mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
feat(console): add quota paywall for mfa feature (#4538)
This commit is contained in:
parent
9c8b9e4853
commit
bfcc5a2cc9
4 changed files with 47 additions and 8 deletions
|
@ -8,11 +8,7 @@ type Props = {
|
|||
};
|
||||
|
||||
function ContactUsPhraseLink({ children }: Props) {
|
||||
return (
|
||||
<TextLink href={contactEmailLink} target="_blank">
|
||||
{children}
|
||||
</TextLink>
|
||||
);
|
||||
return <TextLink href={contactEmailLink}>{children}</TextLink>;
|
||||
}
|
||||
|
||||
export default ContactUsPhraseLink;
|
||||
|
|
|
@ -44,6 +44,11 @@
|
|||
input:checked + .slider::before {
|
||||
transform: translateX(16px);
|
||||
}
|
||||
|
||||
input:disabled + .slider {
|
||||
background-color: var(--color-disabled);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
|
|
|
@ -22,6 +22,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.unlockMfaNotice {
|
||||
margin-top: _.unit(4);
|
||||
}
|
||||
|
||||
.policyRadio {
|
||||
> div[class$='content'] {
|
||||
> div[class$='indicator'] {
|
||||
|
|
|
@ -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) {
|
|||
<DynamicT forKey="mfa.multi_factors_description" />
|
||||
</div>
|
||||
<div className={styles.factorField}>
|
||||
<Switch label={<FactorLabel type={MfaFactor.TOTP} />} {...register('totpEnabled')} />
|
||||
<Switch
|
||||
disabled={isMfaDisabled}
|
||||
label={<FactorLabel type={MfaFactor.TOTP} />}
|
||||
{...register('totpEnabled')}
|
||||
/>
|
||||
<Switch
|
||||
disabled={isMfaDisabled}
|
||||
label={<FactorLabel type={MfaFactor.WebAuthn} />}
|
||||
{...register('webAuthnEnabled')}
|
||||
/>
|
||||
|
@ -90,6 +105,7 @@ function MfaForm({ data, onMfaUpdated }: Props) {
|
|||
<DynamicT forKey="mfa.backup_code_setup_hint" />
|
||||
</div>
|
||||
<Switch
|
||||
disabled={isMfaDisabled}
|
||||
label={<FactorLabel type={MfaFactor.BackupCode} />}
|
||||
hasError={!isBackupCodeAllowed}
|
||||
{...register('backupCodeEnabled')}
|
||||
|
@ -102,6 +118,23 @@ function MfaForm({ data, onMfaUpdated }: Props) {
|
|||
</div>
|
||||
</div>
|
||||
</FormField>
|
||||
{isMfaDisabled && (
|
||||
<InlineNotification
|
||||
className={styles.unlockMfaNotice}
|
||||
action="mfa.view_plans"
|
||||
onClick={() => {
|
||||
navigate('/tenant-settings/subscription');
|
||||
}}
|
||||
>
|
||||
<Trans
|
||||
components={{
|
||||
a: <ContactUsPhraseLink />,
|
||||
}}
|
||||
>
|
||||
{t('mfa.unlock_reminder')}
|
||||
</Trans>
|
||||
</InlineNotification>
|
||||
)}
|
||||
</FormCard>
|
||||
<FormCard title="mfa.policy">
|
||||
<FormField title="mfa.two_step_sign_in_policy">
|
||||
|
@ -118,6 +151,7 @@ function MfaForm({ data, onMfaUpdated }: Props) {
|
|||
return (
|
||||
<Radio
|
||||
key={policy}
|
||||
isDisabled={isMfaDisabled}
|
||||
className={styles.policyRadio}
|
||||
title={<PolicyOptionTitle {...titleProps} />}
|
||||
value={policy}
|
||||
|
|
Loading…
Reference in a new issue