From cee1c54dc04428f8bf9101e8123a22dfbc6c5bd6 Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Thu, 11 Jul 2024 17:06:21 +0800 Subject: [PATCH] refactor(console): improve branding experience --- .../ConnectorForm/BasicForm/index.tsx | 6 +-- .../components/ImageInputs/LogoAndFavicon.tsx | 2 +- .../components/ImageInputs/index.module.scss | 15 +----- .../src/ds-components/Tip/TipBubble/utils.ts | 4 +- .../Uploader/FileUploader/index.module.scss | 3 +- .../Branding/index.module.scss | 8 +++ .../Branding/index.tsx | 50 ++++++++++++++++--- .../ApplicationDetailsContent/index.tsx | 14 +++--- .../Settings/JitSettings.tsx | 7 +-- .../Settings/index.module.scss | 6 +-- .../OrganizationDetails/Settings/index.tsx | 4 +- .../PlanComparisonTable/index.tsx | 2 +- .../admin-console/subscription/quota-table.ts | 1 - .../admin-console/sign-in-exp/index.ts | 7 +++ .../admin-console/subscription/quota-table.ts | 2 +- .../admin-console/subscription/quota-table.ts | 1 - .../admin-console/subscription/quota-table.ts | 1 - .../admin-console/subscription/quota-table.ts | 1 - .../admin-console/subscription/quota-table.ts | 1 - .../admin-console/subscription/quota-table.ts | 1 - .../admin-console/subscription/quota-table.ts | 1 - .../admin-console/subscription/quota-table.ts | 1 - .../admin-console/subscription/quota-table.ts | 1 - .../admin-console/subscription/quota-table.ts | 1 - .../admin-console/subscription/quota-table.ts | 1 - .../admin-console/subscription/quota-table.ts | 1 - .../admin-console/subscription/quota-table.ts | 1 - .../admin-console/subscription/quota-table.ts | 1 - .../schemas/src/seeds/sign-in-experience.ts | 2 +- 29 files changed, 86 insertions(+), 60 deletions(-) diff --git a/packages/console/src/components/ConnectorForm/BasicForm/index.tsx b/packages/console/src/components/ConnectorForm/BasicForm/index.tsx index 6fe733182..fb872a83e 100644 --- a/packages/console/src/components/ConnectorForm/BasicForm/index.tsx +++ b/packages/console/src/components/ConnectorForm/BasicForm/index.tsx @@ -3,7 +3,7 @@ import { Controller, useFormContext } from 'react-hook-form'; import { Trans, useTranslation } from 'react-i18next'; import Error from '@/assets/icons/toast-error.svg'; -import LogoInputs from '@/components/ImageInputs'; +import ImageInputs from '@/components/ImageInputs'; import UnnamedTrans from '@/components/UnnamedTrans'; import FormField from '@/ds-components/FormField'; import Select from '@/ds-components/Select'; @@ -57,7 +57,7 @@ function BasicForm({ isAllowEditTarget, isStandard, conflictConnectorName }: Pro {...register('name', { required: true })} /> - ({ name: themeToField[theme], error: errors[themeToField[theme]], - type: 'app_logo', + type: 'connector_logo', theme, }))} /> diff --git a/packages/console/src/components/ImageInputs/LogoAndFavicon.tsx b/packages/console/src/components/ImageInputs/LogoAndFavicon.tsx index fc070aec1..b60af048e 100644 --- a/packages/console/src/components/ImageInputs/LogoAndFavicon.tsx +++ b/packages/console/src/components/ImageInputs/LogoAndFavicon.tsx @@ -45,7 +45,7 @@ function LogoAndFavicon({ uploadTitle={ <> {t(`sign_in_exp.branding.with_${theme}`, { - value: t('sign_in_exp.branding.app_logo_and_favicon'), + value: t(`sign_in_exp.branding.${type}_and_favicon`), })} } diff --git a/packages/console/src/components/ImageInputs/index.module.scss b/packages/console/src/components/ImageInputs/index.module.scss index b931a3b5c..4e24be27a 100644 --- a/packages/console/src/components/ImageInputs/index.module.scss +++ b/packages/console/src/components/ImageInputs/index.module.scss @@ -2,24 +2,11 @@ .container { display: flex; + gap: _.unit(2); > * { flex: 1; - &:first-child { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - - &:last-child { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - - &:not(:first-child):not(:last-child) { - border-radius: 0; - } - &.dark { background-color: #111; } diff --git a/packages/console/src/ds-components/Tip/TipBubble/utils.ts b/packages/console/src/ds-components/Tip/TipBubble/utils.ts index 1dace20b1..20071e539 100644 --- a/packages/console/src/ds-components/Tip/TipBubble/utils.ts +++ b/packages/console/src/ds-components/Tip/TipBubble/utils.ts @@ -5,11 +5,11 @@ import type { TipBubblePlacement } from '.'; export const getVerticalOffset = (placement: TipBubblePlacement) => { switch (placement) { case 'top': { - return -16; + return -8; } case 'bottom': { - return 16; + return 8; } default: { diff --git a/packages/console/src/ds-components/Uploader/FileUploader/index.module.scss b/packages/console/src/ds-components/Uploader/FileUploader/index.module.scss index 0e15e5bbe..77569e882 100644 --- a/packages/console/src/ds-components/Uploader/FileUploader/index.module.scss +++ b/packages/console/src/ds-components/Uploader/FileUploader/index.module.scss @@ -28,7 +28,7 @@ .actionDescription { margin-top: _.unit(1); - font: var(--font-body-2); // With font height to be 20px. + font: var(--font-body-3); user-select: none; } } @@ -60,4 +60,3 @@ border-color: var(--color-error); } } - diff --git a/packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/Branding/index.module.scss b/packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/Branding/index.module.scss index f99c87282..57e7fffb7 100644 --- a/packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/Branding/index.module.scss +++ b/packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/Branding/index.module.scss @@ -3,3 +3,11 @@ .colors { margin-top: _.unit(6); } + +.darkModeTip { + display: flex; + align-items: baseline; + font: var(--font-body-2); + color: var(--color-text-secondary); + margin-top: _.unit(1); +} diff --git a/packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/Branding/index.tsx b/packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/Branding/index.tsx index d04875684..7ff5d422e 100644 --- a/packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/Branding/index.tsx +++ b/packages/console/src/pages/ApplicationDetails/ApplicationDetailsContent/Branding/index.tsx @@ -1,16 +1,23 @@ -import { Theme, type Application, type ApplicationSignInExperience } from '@logto/schemas'; -import { useCallback, useEffect } from 'react'; +import { generateDarkColor } from '@logto/core-kit'; +import { + Theme, + defaultPrimaryColor, + type Application, + type ApplicationSignInExperience, +} from '@logto/schemas'; +import { useCallback, useEffect, useMemo } from 'react'; import { useForm, FormProvider, Controller } from 'react-hook-form'; import { toast } from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; import DetailsForm from '@/components/DetailsForm'; import FormCard, { FormCardSkeleton } from '@/components/FormCard'; -import LogoInputs, { themeToLogoName } from '@/components/ImageInputs'; +import ImageInputs, { themeToLogoName } from '@/components/ImageInputs'; import LogoAndFavicon from '@/components/ImageInputs/LogoAndFavicon'; import RequestDataError from '@/components/RequestDataError'; import UnsavedChangesAlertModal from '@/components/UnsavedChangesAlertModal'; import { appSpecificBrandingLink, logtoThirdPartyAppBrandingLink } from '@/consts'; +import Button from '@/ds-components/Button'; import ColorPicker from '@/ds-components/ColorPicker'; import FormField from '@/ds-components/FormField'; import Switch from '@/ds-components/Switch'; @@ -105,10 +112,22 @@ function Branding({ application, isActive }: Props) { // is valid; otherwise, directly save the form will be a no-op. useEffect(() => { if (isBrandingEnabled && Object.values(color).filter(Boolean).length === 0) { - setValue('color', { primaryColor: '#000000', darkPrimaryColor: '#000000' }); + setValue('color', { + primaryColor: defaultPrimaryColor, + darkPrimaryColor: generateDarkColor(defaultPrimaryColor), + }); } }, [color, isBrandingEnabled, setValue]); + const [primaryColor, darkPrimaryColor] = watch(['color.primaryColor', 'color.darkPrimaryColor']); + const calculatedDarkPrimaryColor = useMemo(() => { + return primaryColor && generateDarkColor(primaryColor); + }, [primaryColor]); + + const handleResetColor = useCallback(() => { + setValue('color.darkPrimaryColor', calculatedDarkPrimaryColor); + }, [calculatedDarkPrimaryColor, setValue]); + const NonThirdPartyBrandingForm = useCallback( () => ( <> @@ -153,10 +172,29 @@ function Branding({ application, isActive }: Props) { )} /> + {calculatedDarkPrimaryColor !== darkPrimaryColor && ( +
+ {t('sign_in_exp.color.dark_mode_reset_tip')} +
+ )} ), - [control, errors.branding, register] + [ + control, + errors.branding, + register, + calculatedDarkPrimaryColor, + darkPrimaryColor, + handleResetColor, + t, + ] ); if (isLoading) { @@ -193,7 +231,7 @@ function Branding({ application, isActive }: Props) { - { @@ -189,9 +195,7 @@ function ApplicationDetailsContent({ data, oidcConfig, onApplicationUpdated }: P {t('application_details.permissions.name')} )} - {[ApplicationType.Native, ApplicationType.SPA, ApplicationType.Traditional].includes( - data.type - ) && ( + {hasBranding && ( {t('application_details.branding.name')} @@ -266,9 +270,7 @@ function ApplicationDetailsContent({ data, oidcConfig, onApplicationUpdated }: P )} - {[ApplicationType.Native, ApplicationType.SPA, ApplicationType.Traditional].includes( - data.type - ) && ( + {hasBranding && ( {!allSsoConnectors?.length && ( - + }} @@ -100,7 +100,7 @@ function JitSettings({ form }: Props) { render={({ field: { onChange, value } }) => ( <> {value.length > 0 && ( -
+
{value.map((id) => { const connector = allSsoConnectors?.find( ({ id: connectorId }) => id === connectorId @@ -130,6 +130,7 @@ function JitSettings({ form }: Props) { {Boolean(filteredSsoConnectors?.length) && ( -