Skip to content

Commit 796da93

Browse files
authored
Support approved free plan #3299
ref #3291
2 parents 88f54cd + 773d17e commit 796da93

File tree

10 files changed

+409
-107
lines changed

10 files changed

+409
-107
lines changed

portal/src/graphql/portal/LoginMethodConfigurationScreen.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import {
5858
expandSpecifier,
5959
} from "../../util/resource";
6060
import { clearEmptyObject } from "../../util/misc";
61+
import { isLimitedFreePlan } from "../../util/plan";
6162
import ShowLoading from "../../ShowLoading";
6263
import ShowError from "../../ShowError";
6364
import ScreenContent from "../../ScreenContent";
@@ -85,6 +86,7 @@ import LabelWithTooltip from "../../LabelWithTooltip";
8586
import PhoneInputListWidget from "./PhoneInputListWidget";
8687
import PasswordSettings from "./PasswordSettings";
8788
import ShowOnlyIfSIWEIsDisabled from "./ShowOnlyIfSIWEIsDisabled";
89+
import BlueMessageBar from "../../BlueMessageBar";
8890
import { useTagPickerWithNewTags } from "../../hook/useInput";
8991
import { fixTagPickerStyles } from "../../bugs";
9092
import { useResourceForm } from "../../hook/useResourceForm";
@@ -495,6 +497,7 @@ interface ConfigFormState {
495497
}
496498

497499
interface FeatureConfigFormState {
500+
planName: string | null;
498501
phoneLoginIDDisabled: boolean;
499502
passwordPolicyFeatureConfig: PasswordPolicyFeatureConfig;
500503
}
@@ -517,6 +520,35 @@ interface FormModel {
517520
save: () => Promise<void>;
518521
}
519522

523+
function shouldShowFreePlanWarning(formState: FormState): boolean {
524+
const {
525+
identitiesControl,
526+
loginIDKeyConfigsControl,
527+
primaryAuthenticatorsControl,
528+
planName,
529+
} = formState;
530+
531+
if (planName == null) {
532+
return false;
533+
}
534+
535+
if (!isLimitedFreePlan(planName)) {
536+
return false;
537+
}
538+
539+
// For our purpose, controlListUnwrap is sufficient here.
540+
const identities = controlListUnwrap(identitiesControl);
541+
const loginIDKeyConfigs = controlListUnwrap(loginIDKeyConfigsControl);
542+
const primaryAuthenticators = controlListUnwrap(primaryAuthenticatorsControl);
543+
544+
const loginIDEnabled = identities.includes("login_id");
545+
const phoneEnabled =
546+
loginIDKeyConfigs.find((a) => a.type === "phone") != null;
547+
const oobOTPSMSEnabled = primaryAuthenticators.includes("oob_otp_sms");
548+
549+
return loginIDEnabled && phoneEnabled && oobOTPSMSEnabled;
550+
}
551+
520552
// eslint-disable-next-line complexity
521553
function loginMethodFromFormState(formState: FormState): LoginMethod {
522554
const {
@@ -1548,6 +1580,7 @@ function AuthenticationButton(props: AuthenticationButtonProps) {
15481580

15491581
interface LoginMethodChooserProps {
15501582
loginMethod: LoginMethod;
1583+
showFreePlanWarning: boolean;
15511584
phoneLoginIDDisabled: boolean;
15521585
passkeyChecked: boolean;
15531586
appID: string;
@@ -1558,6 +1591,7 @@ interface LoginMethodChooserProps {
15581591
function LoginMethodChooser(props: LoginMethodChooserProps) {
15591592
const {
15601593
loginMethod,
1594+
showFreePlanWarning,
15611595
phoneLoginIDDisabled,
15621596
appID,
15631597
onChangeLoginMethod,
@@ -1716,6 +1750,11 @@ function LoginMethodChooser(props: LoginMethodChooserProps) {
17161750
onChange={onChangePasskeyChecked}
17171751
/>
17181752
)}
1753+
{showFreePlanWarning ? (
1754+
<BlueMessageBar>
1755+
<FormattedMessage id="warnings.free-plan" />
1756+
</BlueMessageBar>
1757+
) : null}
17191758
</Widget>
17201759
);
17211760
}
@@ -2905,6 +2944,11 @@ const LoginMethodConfigurationContent: React.VFC<LoginMethodConfigurationContent
29052944
resources,
29062945
} = state;
29072946

2947+
const showFreePlanWarning = useMemo(
2948+
() => shouldShowFreePlanWarning(state),
2949+
[state]
2950+
);
2951+
29082952
const isPasswordlessEnabled = useMemo(() => {
29092953
return primaryAuthenticatorsControl
29102954
.filter((c) => c.value === "oob_otp_email" || c.value === "oob_otp_sms")
@@ -3059,6 +3103,7 @@ const LoginMethodConfigurationContent: React.VFC<LoginMethodConfigurationContent
30593103
passkeyChecked={passkeyChecked}
30603104
/>
30613105
<LoginMethodChooser
3106+
showFreePlanWarning={showFreePlanWarning}
30623107
loginMethod={loginMethod}
30633108
phoneLoginIDDisabled={phoneLoginIDDisabled}
30643109
passkeyChecked={passkeyChecked}
@@ -3260,6 +3305,7 @@ const LoginMethodConfigurationScreen: React.VFC =
32603305
const state = useMemo<FormState>(() => {
32613306
return {
32623307
resources: resourceForm.state.resources,
3308+
planName: featureConfig.planName,
32633309
phoneLoginIDDisabled:
32643310
featureConfig.effectiveFeatureConfig?.identity?.login_id?.types?.phone
32653311
?.disabled ?? false,
@@ -3268,6 +3314,7 @@ const LoginMethodConfigurationScreen: React.VFC =
32683314
...configForm.state,
32693315
};
32703316
}, [
3317+
featureConfig.planName,
32713318
resourceForm.state.resources,
32723319
featureConfig.effectiveFeatureConfig?.identity?.login_id?.types?.phone
32733320
?.disabled,

portal/src/graphql/portal/SubscriptionCurrentPlanSummary.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { DateTime } from "luxon";
1919
import { Context, FormattedMessage } from "@oursky/react-messageformat";
2020
import { formatDatetime } from "../../util/formatDatetime";
2121
import LinkButton from "../../LinkButton";
22+
import BlueMessageBar from "../../BlueMessageBar";
2223
import styles from "./SubscriptionCurrentPlanSummary.module.css";
2324

2425
export interface SubscriptionCurrentPlanSummaryProps {
@@ -34,6 +35,7 @@ export interface SubscriptionCurrentPlanSummaryProps {
3435
onClickManageSubscription?: IButtonProps["onClick"];
3536
manageSubscriptionLoading?: boolean;
3637
manageSubscriptionDisabled?: boolean;
38+
showFreePlanWarning?: boolean;
3739
children?: React.ReactNode;
3840
}
3941

@@ -306,6 +308,7 @@ function SubscriptionCurrentPlanSummary(
306308
manageSubscriptionLoading,
307309
manageSubscriptionDisabled,
308310
isCustomPlan,
311+
showFreePlanWarning,
309312
children,
310313
} = props;
311314
return (
@@ -344,6 +347,11 @@ function SubscriptionCurrentPlanSummary(
344347
/>
345348
) : null}
346349
</div>
350+
{showFreePlanWarning === true ? (
351+
<BlueMessageBar className="mt-4 max-w-2xl">
352+
<FormattedMessage id="warnings.free-plan" />
353+
</BlueMessageBar>
354+
) : null}
347355
</div>
348356
);
349357
}

portal/src/graphql/portal/SubscriptionPlanCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ function CTA_(props: CTAProps): React.ReactElement {
469469
className={styles.cta}
470470
disabled={true}
471471
text={
472-
<FormattedMessage id="SubscriptionPlanCard.label.subscribe" />
472+
<FormattedMessage id="SubscriptionPlanCard.label.non-applicable" />
473473
}
474474
/>
475475
</ThemeProvider>

0 commit comments

Comments
 (0)