[WEB-1814] chore: admin app UI & UX improvements. (#4999)
This commit is contained in:
parent
26be5dac14
commit
764e08140c
17 changed files with 203 additions and 96 deletions
|
|
@ -10,6 +10,7 @@ import { IFormattedInstanceConfiguration, TInstanceGithubAuthenticationConfigura
|
||||||
import { Button, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui";
|
import { Button, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import {
|
import {
|
||||||
|
CodeBlock,
|
||||||
ConfirmDiscardModal,
|
ConfirmDiscardModal,
|
||||||
ControllerInput,
|
ControllerInput,
|
||||||
CopyField,
|
CopyField,
|
||||||
|
|
@ -102,7 +103,8 @@ export const InstanceGithubConfigForm: FC<Props> = (props) => {
|
||||||
url: originURL,
|
url: originURL,
|
||||||
description: (
|
description: (
|
||||||
<>
|
<>
|
||||||
We will auto-generate this. Paste this into the Authorized origin URL field{" "}
|
We will auto-generate this. Paste this into the{" "}
|
||||||
|
<CodeBlock darkerShade>Authorized origin URL</CodeBlock> field{" "}
|
||||||
<a
|
<a
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
href="https://github.com/settings/applications/new"
|
href="https://github.com/settings/applications/new"
|
||||||
|
|
@ -121,7 +123,8 @@ export const InstanceGithubConfigForm: FC<Props> = (props) => {
|
||||||
url: `${originURL}/auth/github/callback/`,
|
url: `${originURL}/auth/github/callback/`,
|
||||||
description: (
|
description: (
|
||||||
<>
|
<>
|
||||||
We will auto-generate this. Paste this into your Authorized Callback URI field{" "}
|
We will auto-generate this. Paste this into your{" "}
|
||||||
|
<CodeBlock darkerShade>Authorized Callback URI</CodeBlock> field{" "}
|
||||||
<a
|
<a
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
href="https://github.com/settings/applications/new"
|
href="https://github.com/settings/applications/new"
|
||||||
|
|
@ -143,8 +146,8 @@ export const InstanceGithubConfigForm: FC<Props> = (props) => {
|
||||||
.then((response = []) => {
|
.then((response = []) => {
|
||||||
setToast({
|
setToast({
|
||||||
type: TOAST_TYPE.SUCCESS,
|
type: TOAST_TYPE.SUCCESS,
|
||||||
title: "Success",
|
title: "Done!",
|
||||||
message: "Github Configuration Settings updated successfully",
|
message: "Your GitHub authentication is configured. You should test it now.",
|
||||||
});
|
});
|
||||||
reset({
|
reset({
|
||||||
GITHUB_CLIENT_ID: response.find((item) => item.key === "GITHUB_CLIENT_ID")?.value,
|
GITHUB_CLIENT_ID: response.find((item) => item.key === "GITHUB_CLIENT_ID")?.value,
|
||||||
|
|
@ -170,8 +173,8 @@ export const InstanceGithubConfigForm: FC<Props> = (props) => {
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col gap-8">
|
<div className="flex flex-col gap-8">
|
||||||
<div className="grid grid-cols-2 gap-x-12 gap-y-8 w-full">
|
<div className="grid grid-cols-2 gap-x-12 gap-y-8 w-full">
|
||||||
<div className="flex flex-col gap-y-4 col-span-2 md:col-span-1">
|
<div className="flex flex-col gap-y-4 col-span-2 md:col-span-1 pt-1">
|
||||||
<div className="pt-2 text-xl font-medium">Configuration</div>
|
<div className="pt-2.5 text-xl font-medium">GitHub-provided details for Plane</div>
|
||||||
{GITHUB_FORM_FIELDS.map((field) => (
|
{GITHUB_FORM_FIELDS.map((field) => (
|
||||||
<ControllerInput
|
<ControllerInput
|
||||||
key={field.key}
|
key={field.key}
|
||||||
|
|
@ -201,8 +204,8 @@ export const InstanceGithubConfigForm: FC<Props> = (props) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-2 md:col-span-1">
|
<div className="col-span-2 md:col-span-1">
|
||||||
<div className="flex flex-col gap-y-4 px-6 py-4 my-2 bg-custom-background-80/60 rounded-lg">
|
<div className="flex flex-col gap-y-4 px-6 pt-1.5 pb-4 bg-custom-background-80/60 rounded-lg">
|
||||||
<div className="pt-2 text-xl font-medium">Service provider details</div>
|
<div className="pt-2 text-xl font-medium">Plane-provided details for GitHub</div>
|
||||||
{GITHUB_SERVICE_FIELD.map((field) => (
|
{GITHUB_SERVICE_FIELD.map((field) => (
|
||||||
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
|
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ const InstanceGithubAuthenticationPage = observer(() => {
|
||||||
withBorder={false}
|
withBorder={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md p-4">
|
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
|
||||||
{formattedConfig ? (
|
{formattedConfig ? (
|
||||||
<InstanceGithubConfigForm config={formattedConfig} />
|
<InstanceGithubConfigForm config={formattedConfig} />
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { IFormattedInstanceConfiguration, TInstanceGitlabAuthenticationConfigura
|
||||||
import { Button, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui";
|
import { Button, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import {
|
import {
|
||||||
|
CodeBlock,
|
||||||
ConfirmDiscardModal,
|
ConfirmDiscardModal,
|
||||||
ControllerInput,
|
ControllerInput,
|
||||||
CopyField,
|
CopyField,
|
||||||
|
|
@ -54,7 +55,7 @@ export const InstanceGitlabConfigForm: FC<Props> = (props) => {
|
||||||
label: "Host",
|
label: "Host",
|
||||||
description: (
|
description: (
|
||||||
<>
|
<>
|
||||||
This is the <b>GitLab host</b> to use for login, <b>including scheme</b>.
|
This is either https://gitlab.com or the <CodeBlock>domain.tld</CodeBlock> where you host GitLab.
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
placeholder: "https://gitlab.com",
|
placeholder: "https://gitlab.com",
|
||||||
|
|
@ -116,7 +117,8 @@ export const InstanceGitlabConfigForm: FC<Props> = (props) => {
|
||||||
url: `${originURL}/auth/gitlab/callback/`,
|
url: `${originURL}/auth/gitlab/callback/`,
|
||||||
description: (
|
description: (
|
||||||
<>
|
<>
|
||||||
We will auto-generate this. Paste this into the <b>Redirect URI</b> field of your{" "}
|
We will auto-generate this. Paste this into the{" "}
|
||||||
|
<CodeBlock darkerShade>Redirect URI</CodeBlock> field of your{" "}
|
||||||
<a
|
<a
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
href="https://docs.gitlab.com/ee/integration/oauth_provider.html"
|
href="https://docs.gitlab.com/ee/integration/oauth_provider.html"
|
||||||
|
|
@ -139,8 +141,8 @@ export const InstanceGitlabConfigForm: FC<Props> = (props) => {
|
||||||
.then((response = []) => {
|
.then((response = []) => {
|
||||||
setToast({
|
setToast({
|
||||||
type: TOAST_TYPE.SUCCESS,
|
type: TOAST_TYPE.SUCCESS,
|
||||||
title: "Success",
|
title: "Done!",
|
||||||
message: "GitLab Configuration Settings updated successfully",
|
message: "Your GitLab authentication is configured. You should test it now.",
|
||||||
});
|
});
|
||||||
reset({
|
reset({
|
||||||
GITLAB_HOST: response.find((item) => item.key === "GITLAB_HOST")?.value,
|
GITLAB_HOST: response.find((item) => item.key === "GITLAB_HOST")?.value,
|
||||||
|
|
@ -167,8 +169,8 @@ export const InstanceGitlabConfigForm: FC<Props> = (props) => {
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col gap-8">
|
<div className="flex flex-col gap-8">
|
||||||
<div className="grid grid-cols-2 gap-x-12 gap-y-8 w-full">
|
<div className="grid grid-cols-2 gap-x-12 gap-y-8 w-full">
|
||||||
<div className="flex flex-col gap-y-4 col-span-2 md:col-span-1">
|
<div className="flex flex-col gap-y-4 col-span-2 md:col-span-1 pt-1">
|
||||||
<div className="pt-2 text-xl font-medium">Configuration</div>
|
<div className="pt-2.5 text-xl font-medium">GitLab-provided details for Plane</div>
|
||||||
{GITLAB_FORM_FIELDS.map((field) => (
|
{GITLAB_FORM_FIELDS.map((field) => (
|
||||||
<ControllerInput
|
<ControllerInput
|
||||||
key={field.key}
|
key={field.key}
|
||||||
|
|
@ -198,8 +200,8 @@ export const InstanceGitlabConfigForm: FC<Props> = (props) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-2 md:col-span-1">
|
<div className="col-span-2 md:col-span-1">
|
||||||
<div className="flex flex-col gap-y-4 px-6 py-4 my-2 bg-custom-background-80/60 rounded-lg">
|
<div className="flex flex-col gap-y-4 px-6 pt-1.5 pb-4 bg-custom-background-80/60 rounded-lg">
|
||||||
<div className="pt-2 text-xl font-medium">Service provider details</div>
|
<div className="pt-2 text-xl font-medium">Plane-provided details for GitLab</div>
|
||||||
{GITLAB_SERVICE_FIELD.map((field) => (
|
{GITLAB_SERVICE_FIELD.map((field) => (
|
||||||
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
|
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ const InstanceGitlabAuthenticationPage = observer(() => {
|
||||||
withBorder={false}
|
withBorder={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md p-4">
|
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
|
||||||
{formattedConfig ? (
|
{formattedConfig ? (
|
||||||
<InstanceGitlabConfigForm config={formattedConfig} />
|
<InstanceGitlabConfigForm config={formattedConfig} />
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { IFormattedInstanceConfiguration, TInstanceGoogleAuthenticationConfigura
|
||||||
import { Button, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui";
|
import { Button, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import {
|
import {
|
||||||
|
CodeBlock,
|
||||||
ConfirmDiscardModal,
|
ConfirmDiscardModal,
|
||||||
ControllerInput,
|
ControllerInput,
|
||||||
CopyField,
|
CopyField,
|
||||||
|
|
@ -101,7 +102,8 @@ export const InstanceGoogleConfigForm: FC<Props> = (props) => {
|
||||||
url: originURL,
|
url: originURL,
|
||||||
description: (
|
description: (
|
||||||
<p>
|
<p>
|
||||||
We will auto-generate this. Paste this into your Authorized JavaScript origins field. For this OAuth client{" "}
|
We will auto-generate this. Paste this into your{" "}
|
||||||
|
<CodeBlock darkerShade>Authorized JavaScript origins</CodeBlock> field. For this OAuth client{" "}
|
||||||
<a
|
<a
|
||||||
href="https://console.cloud.google.com/apis/credentials/oauthclient"
|
href="https://console.cloud.google.com/apis/credentials/oauthclient"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|
@ -119,7 +121,8 @@ export const InstanceGoogleConfigForm: FC<Props> = (props) => {
|
||||||
url: `${originURL}/auth/google/callback/`,
|
url: `${originURL}/auth/google/callback/`,
|
||||||
description: (
|
description: (
|
||||||
<p>
|
<p>
|
||||||
We will auto-generate this. Paste this into your Authorized Redirect URI field. For this OAuth client{" "}
|
We will auto-generate this. Paste this into your <CodeBlock darkerShade>Authorized Redirect URI</CodeBlock>{" "}
|
||||||
|
field. For this OAuth client{" "}
|
||||||
<a
|
<a
|
||||||
href="https://console.cloud.google.com/apis/credentials/oauthclient"
|
href="https://console.cloud.google.com/apis/credentials/oauthclient"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|
@ -140,8 +143,8 @@ export const InstanceGoogleConfigForm: FC<Props> = (props) => {
|
||||||
.then((response = []) => {
|
.then((response = []) => {
|
||||||
setToast({
|
setToast({
|
||||||
type: TOAST_TYPE.SUCCESS,
|
type: TOAST_TYPE.SUCCESS,
|
||||||
title: "Success",
|
title: "Done!",
|
||||||
message: "Google Configuration Settings updated successfully",
|
message: "Your Google authentication is configured. You should test it now.",
|
||||||
});
|
});
|
||||||
reset({
|
reset({
|
||||||
GOOGLE_CLIENT_ID: response.find((item) => item.key === "GOOGLE_CLIENT_ID")?.value,
|
GOOGLE_CLIENT_ID: response.find((item) => item.key === "GOOGLE_CLIENT_ID")?.value,
|
||||||
|
|
@ -167,8 +170,8 @@ export const InstanceGoogleConfigForm: FC<Props> = (props) => {
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col gap-8">
|
<div className="flex flex-col gap-8">
|
||||||
<div className="grid grid-cols-2 gap-x-12 gap-y-8 w-full">
|
<div className="grid grid-cols-2 gap-x-12 gap-y-8 w-full">
|
||||||
<div className="flex flex-col gap-y-4 col-span-2 md:col-span-1">
|
<div className="flex flex-col gap-y-4 col-span-2 md:col-span-1 pt-1">
|
||||||
<div className="pt-2 text-xl font-medium">Configuration</div>
|
<div className="pt-2.5 text-xl font-medium">Google-provided details for Plane</div>
|
||||||
{GOOGLE_FORM_FIELDS.map((field) => (
|
{GOOGLE_FORM_FIELDS.map((field) => (
|
||||||
<ControllerInput
|
<ControllerInput
|
||||||
key={field.key}
|
key={field.key}
|
||||||
|
|
@ -198,8 +201,8 @@ export const InstanceGoogleConfigForm: FC<Props> = (props) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-2 md:col-span-1">
|
<div className="col-span-2 md:col-span-1">
|
||||||
<div className="flex flex-col gap-y-4 px-6 py-4 my-2 bg-custom-background-80/60 rounded-lg">
|
<div className="flex flex-col gap-y-4 px-6 pt-1.5 pb-4 bg-custom-background-80/60 rounded-lg">
|
||||||
<div className="pt-2 text-xl font-medium">Service provider details</div>
|
<div className="pt-2 text-xl font-medium">Plane-provided details for Google</div>
|
||||||
{GOOGLE_SERVICE_DETAILS.map((field) => (
|
{GOOGLE_SERVICE_DETAILS.map((field) => (
|
||||||
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
|
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ const InstanceGoogleAuthenticationPage = observer(() => {
|
||||||
withBorder={false}
|
withBorder={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md p-4">
|
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
|
||||||
{formattedConfig ? (
|
{formattedConfig ? (
|
||||||
<InstanceGoogleConfigForm config={formattedConfig} />
|
<InstanceGoogleConfigForm config={formattedConfig} />
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -1,84 +1,48 @@
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
import { KeyRound, Mails } from "lucide-react";
|
|
||||||
// types
|
// types
|
||||||
import { TInstanceAuthenticationMethodKeys, TInstanceAuthenticationModes } from "@plane/types";
|
|
||||||
// components
|
|
||||||
import {
|
import {
|
||||||
AuthenticationMethodCard,
|
TGetBaseAuthenticationModeProps,
|
||||||
EmailCodesConfiguration,
|
TInstanceAuthenticationMethodKeys,
|
||||||
GithubConfiguration,
|
TInstanceAuthenticationModes,
|
||||||
GitlabConfiguration,
|
} from "@plane/types";
|
||||||
GoogleConfiguration,
|
// components
|
||||||
PasswordLoginConfiguration,
|
import { AuthenticationMethodCard } from "@/components/authentication";
|
||||||
} from "@/components/authentication";
|
|
||||||
// helpers
|
// helpers
|
||||||
import { resolveGeneralTheme } from "@/helpers/common.helper";
|
import { UpgradeButton } from "@/components/common/upgrade-button";
|
||||||
|
import { getBaseAuthenticationModes } from "@/helpers/authentication.helper";
|
||||||
// images
|
// images
|
||||||
import githubLightModeImage from "@/public/logos/github-black.png";
|
import OIDCLogo from "@/public/logos/oidc-logo.png";
|
||||||
import githubDarkModeImage from "@/public/logos/github-white.png";
|
import SAMLLogo from "@/public/logos/saml-logo.svg";
|
||||||
import GitlabLogo from "@/public/logos/gitlab-logo.svg";
|
|
||||||
import GoogleLogo from "@/public/logos/google-logo.svg";
|
|
||||||
|
|
||||||
export type TAuthenticationModeProps = {
|
export type TAuthenticationModeProps = {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
updateConfig: (key: TInstanceAuthenticationMethodKeys, value: string) => void;
|
updateConfig: (key: TInstanceAuthenticationMethodKeys, value: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TGetAuthenticationModeProps = {
|
|
||||||
disabled: boolean;
|
|
||||||
updateConfig: (key: TInstanceAuthenticationMethodKeys, value: string) => void;
|
|
||||||
resolvedTheme: string | undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Authentication methods
|
// Authentication methods
|
||||||
export const getAuthenticationModes: (props: TGetAuthenticationModeProps) => TInstanceAuthenticationModes[] = ({
|
export const getAuthenticationModes: (props: TGetBaseAuthenticationModeProps) => TInstanceAuthenticationModes[] = ({
|
||||||
disabled,
|
disabled,
|
||||||
updateConfig,
|
updateConfig,
|
||||||
resolvedTheme,
|
resolvedTheme,
|
||||||
}) => [
|
}) => [
|
||||||
|
...getBaseAuthenticationModes({ disabled, updateConfig, resolvedTheme }),
|
||||||
{
|
{
|
||||||
key: "unique-codes",
|
key: "oidc",
|
||||||
name: "Unique codes",
|
name: "OIDC",
|
||||||
description: "Log in or sign up for Plane using codes sent via email. You need to have set up SMTP to use this method.",
|
description: "Authenticate your users via the OpenID Connect protocol.",
|
||||||
icon: <Mails className="h-6 w-6 p-0.5 text-custom-text-300/80" />,
|
icon: <Image src={OIDCLogo} height={20} width={20} alt="OIDC Logo" />,
|
||||||
config: <EmailCodesConfiguration disabled={disabled} updateConfig={updateConfig} />,
|
config: <UpgradeButton />,
|
||||||
|
unavailable: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "passwords-login",
|
key: "saml",
|
||||||
name: "Passwords",
|
name: "SAML",
|
||||||
description: "Allow members to create accounts with passwords and use it with their email addresses to sign in.",
|
description: "Authenticate your users via the Security Assertion Markup Language protocol.",
|
||||||
icon: <KeyRound className="h-6 w-6 p-0.5 text-custom-text-300/80" />,
|
icon: <Image src={SAMLLogo} height={24} width={24} alt="SAML Logo" className="pb-0.5 pl-0.5" />,
|
||||||
config: <PasswordLoginConfiguration disabled={disabled} updateConfig={updateConfig} />,
|
config: <UpgradeButton />,
|
||||||
},
|
unavailable: true,
|
||||||
{
|
|
||||||
key: "google",
|
|
||||||
name: "Google",
|
|
||||||
description: "Allow members to log in or sign up for Plane with their Google accounts.",
|
|
||||||
icon: <Image src={GoogleLogo} height={20} width={20} alt="Google Logo" />,
|
|
||||||
config: <GoogleConfiguration disabled={disabled} updateConfig={updateConfig} />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "github",
|
|
||||||
name: "GitHub",
|
|
||||||
description: "Allow members to log in or sign up for Plane with their GitHub accounts.",
|
|
||||||
icon: (
|
|
||||||
<Image
|
|
||||||
src={resolveGeneralTheme(resolvedTheme) === "dark" ? githubDarkModeImage : githubLightModeImage}
|
|
||||||
height={20}
|
|
||||||
width={20}
|
|
||||||
alt="GitHub Logo"
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
config: <GithubConfiguration disabled={disabled} updateConfig={updateConfig} />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "gitlab",
|
|
||||||
name: "GitLab",
|
|
||||||
description: "Allow members to login or sign up to plane with their GitLab accounts.",
|
|
||||||
icon: <Image src={GitlabLogo} height={20} width={20} alt="GitLab Logo" />,
|
|
||||||
config: <GitlabConfiguration disabled={disabled} updateConfig={updateConfig} />,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -97,6 +61,7 @@ export const AuthenticationModes: React.FC<TAuthenticationModeProps> = observer(
|
||||||
icon={method.icon}
|
icon={method.icon}
|
||||||
config={method.config}
|
config={method.config}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
unavailable={method.unavailable}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -41,10 +41,10 @@ export const InstanceSidebar: FC<IInstanceSidebar> = observer(() => {
|
||||||
<div
|
<div
|
||||||
className={`inset-y-0 z-20 flex h-full flex-shrink-0 flex-grow-0 flex-col border-r border-custom-sidebar-border-200 bg-custom-sidebar-background-100 duration-300
|
className={`inset-y-0 z-20 flex h-full flex-shrink-0 flex-grow-0 flex-col border-r border-custom-sidebar-border-200 bg-custom-sidebar-background-100 duration-300
|
||||||
fixed md:relative
|
fixed md:relative
|
||||||
${isSidebarCollapsed ? "-ml-[280px]" : ""}
|
${isSidebarCollapsed ? "-ml-[290px]" : ""}
|
||||||
sm:${isSidebarCollapsed ? "-ml-[280px]" : ""}
|
sm:${isSidebarCollapsed ? "-ml-[290px]" : ""}
|
||||||
md:ml-0 ${isSidebarCollapsed ? "w-[70px]" : "w-[280px]"}
|
md:ml-0 ${isSidebarCollapsed ? "w-[70px]" : "w-[290px]"}
|
||||||
lg:ml-0 ${isSidebarCollapsed ? "w-[70px]" : "w-[280px]"}
|
lg:ml-0 ${isSidebarCollapsed ? "w-[70px]" : "w-[290px]"}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<div ref={ref} className="flex h-full w-full flex-1 flex-col">
|
<div ref={ref} className="flex h-full w-full flex-1 flex-col">
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,11 @@ type Props = {
|
||||||
config: JSX.Element;
|
config: JSX.Element;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
withBorder?: boolean;
|
withBorder?: boolean;
|
||||||
|
unavailable?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AuthenticationMethodCard: FC<Props> = (props) => {
|
export const AuthenticationMethodCard: FC<Props> = (props) => {
|
||||||
const { name, description, icon, config, disabled = false, withBorder = true } = props;
|
const { name, description, icon, config, disabled = false, withBorder = true, unavailable = false } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
@ -22,7 +23,11 @@ export const AuthenticationMethodCard: FC<Props> = (props) => {
|
||||||
"px-4 py-3 border border-custom-border-200": withBorder,
|
"px-4 py-3 border border-custom-border-200": withBorder,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div className="flex grow items-center gap-4">
|
<div
|
||||||
|
className={cn("flex grow items-center gap-4", {
|
||||||
|
"opacity-50": unavailable,
|
||||||
|
})}
|
||||||
|
>
|
||||||
<div className="shrink-0">
|
<div className="shrink-0">
|
||||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-custom-background-80">{icon}</div>
|
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-custom-background-80">{icon}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
21
admin/core/components/common/code-block.tsx
Normal file
21
admin/core/components/common/code-block.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { cn } from "@/helpers/common.helper";
|
||||||
|
|
||||||
|
type TProps = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
darkerShade?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CodeBlock = ({ children, className, darkerShade }: TProps) => (
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
"px-0.5 text-xs text-custom-text-300 bg-custom-background-90 font-semibold rounded-md border border-custom-border-100",
|
||||||
|
{
|
||||||
|
"text-custom-text-200 bg-custom-background-80 border-custom-border-200": darkerShade,
|
||||||
|
},
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
@ -38,7 +38,7 @@ export const ControllerInput: React.FC<Props> = (props) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<h4 className="text-sm text-custom-text-300">
|
<h4 className="text-sm text-custom-text-300">
|
||||||
{label} {!required && "(optional)"}
|
{label}
|
||||||
</h4>
|
</h4>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Controller
|
<Controller
|
||||||
|
|
@ -80,7 +80,7 @@ export const ControllerInput: React.FC<Props> = (props) => {
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{description && <p className="text-xs text-custom-text-300">{description}</p>}
|
{description && <p className="pt-0.5 text-xs text-custom-text-300">{description}</p>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,5 @@ export * from "./banner";
|
||||||
export * from "./empty-state";
|
export * from "./empty-state";
|
||||||
export * from "./logo-spinner";
|
export * from "./logo-spinner";
|
||||||
export * from "./page-header";
|
export * from "./page-header";
|
||||||
|
export * from "./code-block";
|
||||||
|
export * from "./upgrade-button";
|
||||||
|
|
|
||||||
16
admin/core/components/common/upgrade-button.tsx
Normal file
16
admin/core/components/common/upgrade-button.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
// icons
|
||||||
|
import { SquareArrowOutUpRight } from "lucide-react";
|
||||||
|
// ui
|
||||||
|
import { getButtonStyling } from "@plane/ui";
|
||||||
|
// helpers
|
||||||
|
import { cn } from "@/helpers/common.helper";
|
||||||
|
|
||||||
|
export const UpgradeButton: React.FC = () => (
|
||||||
|
<a href="https://plane.so/one" target="_blank" className={cn(getButtonStyling("primary", "sm"))}>
|
||||||
|
Available on One
|
||||||
|
<SquareArrowOutUpRight className="h-3.5 w-3.5 p-0.5" />
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
|
@ -1,7 +1,24 @@
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { KeyRound, Mails } from "lucide-react";
|
||||||
|
// types
|
||||||
|
import { TGetBaseAuthenticationModeProps, TInstanceAuthenticationModes } from "@plane/types";
|
||||||
|
// components
|
||||||
|
import {
|
||||||
|
EmailCodesConfiguration,
|
||||||
|
GithubConfiguration,
|
||||||
|
GitlabConfiguration,
|
||||||
|
GoogleConfiguration,
|
||||||
|
PasswordLoginConfiguration,
|
||||||
|
} from "@/components/authentication";
|
||||||
// helpers
|
// helpers
|
||||||
import { SUPPORT_EMAIL } from "./common.helper";
|
import { SUPPORT_EMAIL, resolveGeneralTheme } from "@/helpers/common.helper";
|
||||||
|
// images
|
||||||
|
import githubLightModeImage from "@/public/logos/github-black.png";
|
||||||
|
import githubDarkModeImage from "@/public/logos/github-white.png";
|
||||||
|
import GitlabLogo from "@/public/logos/gitlab-logo.svg";
|
||||||
|
import GoogleLogo from "@/public/logos/google-logo.svg";
|
||||||
|
|
||||||
export enum EPageTypes {
|
export enum EPageTypes {
|
||||||
PUBLIC = "PUBLIC",
|
PUBLIC = "PUBLIC",
|
||||||
|
|
@ -134,3 +151,53 @@ export const authErrorHandler = (
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getBaseAuthenticationModes: (props: TGetBaseAuthenticationModeProps) => TInstanceAuthenticationModes[] = ({
|
||||||
|
disabled,
|
||||||
|
updateConfig,
|
||||||
|
resolvedTheme,
|
||||||
|
}) => [
|
||||||
|
{
|
||||||
|
key: "unique-codes",
|
||||||
|
name: "Unique codes",
|
||||||
|
description:
|
||||||
|
"Log in or sign up for Plane using codes sent via email. You need to have set up SMTP to use this method.",
|
||||||
|
icon: <Mails className="h-6 w-6 p-0.5 text-custom-text-300/80" />,
|
||||||
|
config: <EmailCodesConfiguration disabled={disabled} updateConfig={updateConfig} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "passwords-login",
|
||||||
|
name: "Passwords",
|
||||||
|
description: "Allow members to create accounts with passwords and use it with their email addresses to sign in.",
|
||||||
|
icon: <KeyRound className="h-6 w-6 p-0.5 text-custom-text-300/80" />,
|
||||||
|
config: <PasswordLoginConfiguration disabled={disabled} updateConfig={updateConfig} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "google",
|
||||||
|
name: "Google",
|
||||||
|
description: "Allow members to log in or sign up for Plane with their Google accounts.",
|
||||||
|
icon: <Image src={GoogleLogo} height={20} width={20} alt="Google Logo" />,
|
||||||
|
config: <GoogleConfiguration disabled={disabled} updateConfig={updateConfig} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "github",
|
||||||
|
name: "GitHub",
|
||||||
|
description: "Allow members to log in or sign up for Plane with their GitHub accounts.",
|
||||||
|
icon: (
|
||||||
|
<Image
|
||||||
|
src={resolveGeneralTheme(resolvedTheme) === "dark" ? githubDarkModeImage : githubLightModeImage}
|
||||||
|
height={20}
|
||||||
|
width={20}
|
||||||
|
alt="GitHub Logo"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
config: <GithubConfiguration disabled={disabled} updateConfig={updateConfig} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "gitlab",
|
||||||
|
name: "GitLab",
|
||||||
|
description: "Allow members to log in or sign up to plane with their GitLab accounts.",
|
||||||
|
icon: <Image src={GitlabLogo} height={20} width={20} alt="GitLab Logo" />,
|
||||||
|
config: <GitlabConfiguration disabled={disabled} updateConfig={updateConfig} />,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
|
||||||
BIN
admin/public/logos/oidc-logo.png
Normal file
BIN
admin/public/logos/oidc-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
16
admin/public/logos/saml-logo.svg
Normal file
16
admin/public/logos/saml-logo.svg
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="none">
|
||||||
|
|
||||||
|
<g fill="#C22E33">
|
||||||
|
|
||||||
|
<path d="M7.754 2l.463.41c.343.304.687.607 1.026.915C11.44 5.32 13.3 7.565 14.7 10.149c.072.132.137.268.202.403l.098.203-.108.057-.081-.115-.21-.299-.147-.214c-1.019-1.479-2.04-2.96-3.442-4.145a6.563 6.563 0 00-1.393-.904c-1.014-.485-1.916-.291-2.69.505-.736.757-1.118 1.697-1.463 2.653-.045.123-.092.245-.139.367l-.082.215-.172-.055c.1-.348.192-.698.284-1.049.21-.795.42-1.59.712-2.356.31-.816.702-1.603 1.093-2.39.169-.341.338-.682.5-1.025h.092z"/>
|
||||||
|
|
||||||
|
<path d="M8.448 11.822c-1.626.77-5.56 1.564-7.426 1.36C.717 11.576 3.71 4.05 5.18 2.91l-.095.218a4.638 4.638 0 01-.138.303l-.066.129c-.76 1.462-1.519 2.926-1.908 4.53a7.482 7.482 0 00-.228 1.689c-.01 1.34.824 2.252 2.217 2.309.67.027 1.347-.043 2.023-.114.294-.03.587-.061.88-.084.108-.008.214-.021.352-.039l.231-.028z"/>
|
||||||
|
|
||||||
|
<path d="M3.825 14.781c-.445.034-.89.068-1.333.108 4.097.39 8.03-.277 11.91-1.644-1.265-2.23-2.97-3.991-4.952-5.522.026.098.084.169.141.239l.048.06c.17.226.348.448.527.67.409.509.818 1.018 1.126 1.578.778 1.42.356 2.648-1.168 3.296-1.002.427-2.097.718-3.18.892-1.03.164-2.075.243-3.119.323z"/>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
7
packages/types/src/instance/auth.d.ts
vendored
7
packages/types/src/instance/auth.d.ts
vendored
|
|
@ -4,6 +4,7 @@ export type TInstanceAuthenticationModes = {
|
||||||
description: string;
|
description: string;
|
||||||
icon: JSX.Element;
|
icon: JSX.Element;
|
||||||
config: JSX.Element;
|
config: JSX.Element;
|
||||||
|
unavailable?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TInstanceAuthenticationMethodKeys =
|
export type TInstanceAuthenticationMethodKeys =
|
||||||
|
|
@ -35,3 +36,9 @@ type TInstanceAuthenticationConfigurationKeys =
|
||||||
export type TInstanceAuthenticationKeys =
|
export type TInstanceAuthenticationKeys =
|
||||||
| TInstanceAuthenticationMethodKeys
|
| TInstanceAuthenticationMethodKeys
|
||||||
| TInstanceAuthenticationConfigurationKeys;
|
| TInstanceAuthenticationConfigurationKeys;
|
||||||
|
|
||||||
|
export type TGetBaseAuthenticationModeProps = {
|
||||||
|
disabled: boolean;
|
||||||
|
updateConfig: (key: TInstanceAuthenticationMethodKeys, value: string) => void;
|
||||||
|
resolvedTheme: string | undefined;
|
||||||
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue