diff --git a/packages/console/src/components/ContactUsPhraseLink/index.tsx b/packages/console/src/components/ContactUsPhraseLink/index.tsx index e87bf38a2..6deaa4fa1 100644 --- a/packages/console/src/components/ContactUsPhraseLink/index.tsx +++ b/packages/console/src/components/ContactUsPhraseLink/index.tsx @@ -8,11 +8,7 @@ type Props = { }; function ContactUsPhraseLink({ children }: Props) { - return ( - - {children} - - ); + return {children}; } export default ContactUsPhraseLink; diff --git a/packages/console/src/ds-components/Switch/index.module.scss b/packages/console/src/ds-components/Switch/index.module.scss index 2605536d2..5e559f240 100644 --- a/packages/console/src/ds-components/Switch/index.module.scss +++ b/packages/console/src/ds-components/Switch/index.module.scss @@ -44,6 +44,11 @@ input:checked + .slider::before { transform: translateX(16px); } + + input:disabled + .slider { + background-color: var(--color-disabled); + cursor: not-allowed; + } } .wrapper { diff --git a/packages/console/src/pages/Mfa/MfaForm/index.module.scss b/packages/console/src/pages/Mfa/MfaForm/index.module.scss index 0226d6c7a..26b608c60 100644 --- a/packages/console/src/pages/Mfa/MfaForm/index.module.scss +++ b/packages/console/src/pages/Mfa/MfaForm/index.module.scss @@ -22,6 +22,10 @@ } } +.unlockMfaNotice { + margin-top: _.unit(4); +} + .policyRadio { > div[class$='content'] { > div[class$='indicator'] { diff --git a/packages/console/src/pages/Mfa/MfaForm/index.tsx b/packages/console/src/pages/Mfa/MfaForm/index.tsx index bb5d2e853..bc418dc4b 100644 --- a/packages/console/src/pages/Mfa/MfaForm/index.tsx +++ b/packages/console/src/pages/Mfa/MfaForm/index.tsx @@ -1,19 +1,24 @@ import { MfaFactor, MfaPolicy, type SignInExperience } from '@logto/schemas'; import classNames from 'classnames'; -import { useMemo } from 'react'; +import { useContext, useMemo } from 'react'; import { Controller, useForm } from 'react-hook-form'; import { toast } from 'react-hot-toast'; -import { useTranslation } from 'react-i18next'; +import { Trans, useTranslation } from 'react-i18next'; +import ContactUsPhraseLink from '@/components/ContactUsPhraseLink'; import DetailsForm from '@/components/DetailsForm'; import FormCard from '@/components/FormCard'; import UnsavedChangesAlertModal from '@/components/UnsavedChangesAlertModal'; +import { isCloud } from '@/consts/env'; +import { TenantsContext } from '@/contexts/TenantsProvider'; import DynamicT from '@/ds-components/DynamicT'; import FormField from '@/ds-components/FormField'; import InlineNotification from '@/ds-components/InlineNotification'; import RadioGroup, { Radio } from '@/ds-components/RadioGroup'; import Switch from '@/ds-components/Switch'; import useApi from '@/hooks/use-api'; +import useSubscriptionPlan from '@/hooks/use-subscription-plan'; +import useTenantPathname from '@/hooks/use-tenant-pathname'; import { trySubmitSafe } from '@/utils/form'; import { type MfaConfigForm, type MfaConfig } from '../types'; @@ -30,6 +35,11 @@ type Props = { }; function MfaForm({ data, onMfaUpdated }: Props) { + const { currentTenantId } = useContext(TenantsContext); + const { data: currentPlan } = useSubscriptionPlan(currentTenantId); + const { navigate } = useTenantPathname(); + const isMfaDisabled = isCloud && !currentPlan?.quota.mfaEnabled; + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { register, @@ -80,8 +90,13 @@ function MfaForm({ data, onMfaUpdated }: Props) {
- } {...register('totpEnabled')} /> } + {...register('totpEnabled')} + /> + } {...register('webAuthnEnabled')} /> @@ -90,6 +105,7 @@ function MfaForm({ data, onMfaUpdated }: Props) {
} hasError={!isBackupCodeAllowed} {...register('backupCodeEnabled')} @@ -102,6 +118,23 @@ function MfaForm({ data, onMfaUpdated }: Props) { + {isMfaDisabled && ( + { + navigate('/tenant-settings/subscription'); + }} + > + , + }} + > + {t('mfa.unlock_reminder')} + + + )} @@ -118,6 +151,7 @@ function MfaForm({ data, onMfaUpdated }: Props) { return ( } value={policy}