diff --git a/admin/app/authentication/github/form.tsx b/admin/app/authentication/github/form.tsx index e9359a68b..afab9a3c5 100644 --- a/admin/app/authentication/github/form.tsx +++ b/admin/app/authentication/github/form.tsx @@ -10,6 +10,7 @@ import { IFormattedInstanceConfiguration, TInstanceGithubAuthenticationConfigura import { Button, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui"; // components import { + CodeBlock, ConfirmDiscardModal, ControllerInput, CopyField, @@ -102,7 +103,8 @@ export const InstanceGithubConfigForm: FC = (props) => { url: originURL, description: ( <> - We will auto-generate this. Paste this into the Authorized origin URL field{" "} + We will auto-generate this. Paste this into the{" "} + Authorized origin URL field{" "} = (props) => { url: `${originURL}/auth/github/callback/`, description: ( <> - We will auto-generate this. Paste this into your Authorized Callback URI field{" "} + We will auto-generate this. Paste this into your{" "} + Authorized Callback URI field{" "} = (props) => { .then((response = []) => { setToast({ type: TOAST_TYPE.SUCCESS, - title: "Success", - message: "Github Configuration Settings updated successfully", + title: "Done!", + message: "Your GitHub authentication is configured. You should test it now.", }); reset({ GITHUB_CLIENT_ID: response.find((item) => item.key === "GITHUB_CLIENT_ID")?.value, @@ -170,8 +173,8 @@ export const InstanceGithubConfigForm: FC = (props) => { />
-
-
Configuration
+
+
GitHub-provided details for Plane
{GITHUB_FORM_FIELDS.map((field) => ( = (props) => {
-
-
Service provider details
+
+
Plane-provided details for GitHub
{GITHUB_SERVICE_FIELD.map((field) => ( ))} diff --git a/admin/app/authentication/github/page.tsx b/admin/app/authentication/github/page.tsx index b00def973..7991fb95f 100644 --- a/admin/app/authentication/github/page.tsx +++ b/admin/app/authentication/github/page.tsx @@ -93,7 +93,7 @@ const InstanceGithubAuthenticationPage = observer(() => { withBorder={false} />
-
+
{formattedConfig ? ( ) : ( diff --git a/admin/app/authentication/gitlab/form.tsx b/admin/app/authentication/gitlab/form.tsx index 2cb46baf9..2d5782e10 100644 --- a/admin/app/authentication/gitlab/form.tsx +++ b/admin/app/authentication/gitlab/form.tsx @@ -8,6 +8,7 @@ import { IFormattedInstanceConfiguration, TInstanceGitlabAuthenticationConfigura import { Button, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui"; // components import { + CodeBlock, ConfirmDiscardModal, ControllerInput, CopyField, @@ -54,7 +55,7 @@ export const InstanceGitlabConfigForm: FC = (props) => { label: "Host", description: ( <> - This is the GitLab host to use for login, including scheme. + This is either https://gitlab.com or the domain.tld where you host GitLab. ), placeholder: "https://gitlab.com", @@ -116,7 +117,8 @@ export const InstanceGitlabConfigForm: FC = (props) => { url: `${originURL}/auth/gitlab/callback/`, description: ( <> - We will auto-generate this. Paste this into the Redirect URI field of your{" "} + We will auto-generate this. Paste this into the{" "} + Redirect URI field of your{" "} = (props) => { .then((response = []) => { setToast({ type: TOAST_TYPE.SUCCESS, - title: "Success", - message: "GitLab Configuration Settings updated successfully", + title: "Done!", + message: "Your GitLab authentication is configured. You should test it now.", }); reset({ GITLAB_HOST: response.find((item) => item.key === "GITLAB_HOST")?.value, @@ -167,8 +169,8 @@ export const InstanceGitlabConfigForm: FC = (props) => { />
-
-
Configuration
+
+
GitLab-provided details for Plane
{GITLAB_FORM_FIELDS.map((field) => ( = (props) => {
-
-
Service provider details
+
+
Plane-provided details for GitLab
{GITLAB_SERVICE_FIELD.map((field) => ( ))} diff --git a/admin/app/authentication/gitlab/page.tsx b/admin/app/authentication/gitlab/page.tsx index b6441d9b7..7a4d8248e 100644 --- a/admin/app/authentication/gitlab/page.tsx +++ b/admin/app/authentication/gitlab/page.tsx @@ -80,7 +80,7 @@ const InstanceGitlabAuthenticationPage = observer(() => { withBorder={false} />
-
+
{formattedConfig ? ( ) : ( diff --git a/admin/app/authentication/google/form.tsx b/admin/app/authentication/google/form.tsx index 42dd15e4d..cf5797895 100644 --- a/admin/app/authentication/google/form.tsx +++ b/admin/app/authentication/google/form.tsx @@ -9,6 +9,7 @@ import { IFormattedInstanceConfiguration, TInstanceGoogleAuthenticationConfigura import { Button, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui"; // components import { + CodeBlock, ConfirmDiscardModal, ControllerInput, CopyField, @@ -101,7 +102,8 @@ export const InstanceGoogleConfigForm: FC = (props) => { url: originURL, description: (

- 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{" "} + Authorized JavaScript origins field. For this OAuth client{" "} = (props) => { url: `${originURL}/auth/google/callback/`, description: (

- 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 Authorized Redirect URI{" "} + field. For this OAuth client{" "} = (props) => { .then((response = []) => { setToast({ type: TOAST_TYPE.SUCCESS, - title: "Success", - message: "Google Configuration Settings updated successfully", + title: "Done!", + message: "Your Google authentication is configured. You should test it now.", }); reset({ GOOGLE_CLIENT_ID: response.find((item) => item.key === "GOOGLE_CLIENT_ID")?.value, @@ -167,8 +170,8 @@ export const InstanceGoogleConfigForm: FC = (props) => { />

-
-
Configuration
+
+
Google-provided details for Plane
{GOOGLE_FORM_FIELDS.map((field) => ( = (props) => {
-
-
Service provider details
+
+
Plane-provided details for Google
{GOOGLE_SERVICE_DETAILS.map((field) => ( ))} diff --git a/admin/app/authentication/google/page.tsx b/admin/app/authentication/google/page.tsx index af2005e40..992c7a8a7 100644 --- a/admin/app/authentication/google/page.tsx +++ b/admin/app/authentication/google/page.tsx @@ -81,7 +81,7 @@ const InstanceGoogleAuthenticationPage = observer(() => { withBorder={false} />
-
+
{formattedConfig ? ( ) : ( diff --git a/admin/ce/components/authentication/authentication-modes.tsx b/admin/ce/components/authentication/authentication-modes.tsx index e0b37a146..31a807cec 100644 --- a/admin/ce/components/authentication/authentication-modes.tsx +++ b/admin/ce/components/authentication/authentication-modes.tsx @@ -1,84 +1,48 @@ import { observer } from "mobx-react"; import Image from "next/image"; import { useTheme } from "next-themes"; -import { KeyRound, Mails } from "lucide-react"; // types -import { TInstanceAuthenticationMethodKeys, TInstanceAuthenticationModes } from "@plane/types"; -// components import { - AuthenticationMethodCard, - EmailCodesConfiguration, - GithubConfiguration, - GitlabConfiguration, - GoogleConfiguration, - PasswordLoginConfiguration, -} from "@/components/authentication"; + TGetBaseAuthenticationModeProps, + TInstanceAuthenticationMethodKeys, + TInstanceAuthenticationModes, +} from "@plane/types"; +// components +import { AuthenticationMethodCard } from "@/components/authentication"; // helpers -import { resolveGeneralTheme } from "@/helpers/common.helper"; +import { UpgradeButton } from "@/components/common/upgrade-button"; +import { getBaseAuthenticationModes } from "@/helpers/authentication.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"; +import OIDCLogo from "@/public/logos/oidc-logo.png"; +import SAMLLogo from "@/public/logos/saml-logo.svg"; export type TAuthenticationModeProps = { disabled: boolean; updateConfig: (key: TInstanceAuthenticationMethodKeys, value: string) => void; }; -export type TGetAuthenticationModeProps = { - disabled: boolean; - updateConfig: (key: TInstanceAuthenticationMethodKeys, value: string) => void; - resolvedTheme: string | undefined; -}; - // Authentication methods -export const getAuthenticationModes: (props: TGetAuthenticationModeProps) => TInstanceAuthenticationModes[] = ({ +export const getAuthenticationModes: (props: TGetBaseAuthenticationModeProps) => TInstanceAuthenticationModes[] = ({ disabled, updateConfig, resolvedTheme, }) => [ + ...getBaseAuthenticationModes({ 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: , - config: , + key: "oidc", + name: "OIDC", + description: "Authenticate your users via the OpenID Connect protocol.", + icon: OIDC Logo, + config: , + unavailable: true, }, { - key: "passwords-login", - name: "Passwords", - description: "Allow members to create accounts with passwords and use it with their email addresses to sign in.", - icon: , - config: , - }, - { - key: "google", - name: "Google", - description: "Allow members to log in or sign up for Plane with their Google accounts.", - icon: Google Logo, - config: , - }, - { - key: "github", - name: "GitHub", - description: "Allow members to log in or sign up for Plane with their GitHub accounts.", - icon: ( - GitHub Logo - ), - config: , - }, - { - key: "gitlab", - name: "GitLab", - description: "Allow members to login or sign up to plane with their GitLab accounts.", - icon: GitLab Logo, - config: , + key: "saml", + name: "SAML", + description: "Authenticate your users via the Security Assertion Markup Language protocol.", + icon: SAML Logo, + config: , + unavailable: true, }, ]; @@ -97,6 +61,7 @@ export const AuthenticationModes: React.FC = observer( icon={method.icon} config={method.config} disabled={disabled} + unavailable={method.unavailable} /> ))} diff --git a/admin/core/components/admin-sidebar/root.tsx b/admin/core/components/admin-sidebar/root.tsx index 6e992b06a..c2a90daa3 100644 --- a/admin/core/components/admin-sidebar/root.tsx +++ b/admin/core/components/admin-sidebar/root.tsx @@ -41,10 +41,10 @@ export const InstanceSidebar: FC = observer(() => {
diff --git a/admin/core/components/authentication/authentication-method-card.tsx b/admin/core/components/authentication/authentication-method-card.tsx index 1346a730e..50895a459 100644 --- a/admin/core/components/authentication/authentication-method-card.tsx +++ b/admin/core/components/authentication/authentication-method-card.tsx @@ -11,10 +11,11 @@ type Props = { config: JSX.Element; disabled?: boolean; withBorder?: boolean; + unavailable?: boolean; }; export const AuthenticationMethodCard: FC = (props) => { - const { name, description, icon, config, disabled = false, withBorder = true } = props; + const { name, description, icon, config, disabled = false, withBorder = true, unavailable = false } = props; return (
= (props) => { "px-4 py-3 border border-custom-border-200": withBorder, })} > -
+
{icon}
diff --git a/admin/core/components/common/code-block.tsx b/admin/core/components/common/code-block.tsx new file mode 100644 index 000000000..55f8b4afb --- /dev/null +++ b/admin/core/components/common/code-block.tsx @@ -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) => ( + + {children} + +); diff --git a/admin/core/components/common/controller-input.tsx b/admin/core/components/common/controller-input.tsx index 0eb215095..4d0eade08 100644 --- a/admin/core/components/common/controller-input.tsx +++ b/admin/core/components/common/controller-input.tsx @@ -38,7 +38,7 @@ export const ControllerInput: React.FC = (props) => { return (

- {label} {!required && "(optional)"} + {label}

= (props) => { ))}
- {description &&

{description}

} + {description &&

{description}

}
); }; diff --git a/admin/core/components/common/index.ts b/admin/core/components/common/index.ts index ef376f622..2043926ac 100644 --- a/admin/core/components/common/index.ts +++ b/admin/core/components/common/index.ts @@ -7,3 +7,5 @@ export * from "./banner"; export * from "./empty-state"; export * from "./logo-spinner"; export * from "./page-header"; +export * from "./code-block"; +export * from "./upgrade-button"; diff --git a/admin/core/components/common/upgrade-button.tsx b/admin/core/components/common/upgrade-button.tsx new file mode 100644 index 000000000..aa3c95fdb --- /dev/null +++ b/admin/core/components/common/upgrade-button.tsx @@ -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 = () => ( + + Available on One + + +); diff --git a/admin/helpers/authentication.helper.tsx b/admin/helpers/authentication.helper.tsx index cc9058611..627ff182c 100644 --- a/admin/helpers/authentication.helper.tsx +++ b/admin/helpers/authentication.helper.tsx @@ -1,7 +1,24 @@ import { ReactNode } from "react"; +import Image from "next/image"; 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 -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 { PUBLIC = "PUBLIC", @@ -134,3 +151,53 @@ export const authErrorHandler = ( 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: , + config: , + }, + { + key: "passwords-login", + name: "Passwords", + description: "Allow members to create accounts with passwords and use it with their email addresses to sign in.", + icon: , + config: , + }, + { + key: "google", + name: "Google", + description: "Allow members to log in or sign up for Plane with their Google accounts.", + icon: Google Logo, + config: , + }, + { + key: "github", + name: "GitHub", + description: "Allow members to log in or sign up for Plane with their GitHub accounts.", + icon: ( + GitHub Logo + ), + config: , + }, + { + key: "gitlab", + name: "GitLab", + description: "Allow members to log in or sign up to plane with their GitLab accounts.", + icon: GitLab Logo, + config: , + }, + ]; diff --git a/admin/public/logos/oidc-logo.png b/admin/public/logos/oidc-logo.png new file mode 100644 index 000000000..154fb9bbb Binary files /dev/null and b/admin/public/logos/oidc-logo.png differ diff --git a/admin/public/logos/saml-logo.svg b/admin/public/logos/saml-logo.svg new file mode 100644 index 000000000..36526fc21 --- /dev/null +++ b/admin/public/logos/saml-logo.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/types/src/instance/auth.d.ts b/packages/types/src/instance/auth.d.ts index df8179cf1..d71cfa0bb 100644 --- a/packages/types/src/instance/auth.d.ts +++ b/packages/types/src/instance/auth.d.ts @@ -4,6 +4,7 @@ export type TInstanceAuthenticationModes = { description: string; icon: JSX.Element; config: JSX.Element; + unavailable?: boolean; }; export type TInstanceAuthenticationMethodKeys = @@ -35,3 +36,9 @@ type TInstanceAuthenticationConfigurationKeys = export type TInstanceAuthenticationKeys = | TInstanceAuthenticationMethodKeys | TInstanceAuthenticationConfigurationKeys; + +export type TGetBaseAuthenticationModeProps = { + disabled: boolean; + updateConfig: (key: TInstanceAuthenticationMethodKeys, value: string) => void; + resolvedTheme: string | undefined; +};