Skip to content

Commit

Permalink
Some ui/ux improvements of billings
Browse files Browse the repository at this point in the history
ref DEV-2343
ref DEV-2342
  • Loading branch information
louischan-oursky committed Dec 4, 2024
2 parents d0ff475 + ea74e11 commit 2bb7dcf
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 18 deletions.
58 changes: 58 additions & 0 deletions portal/src/components/billing/CancelSubscriptionSurveyDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import {
Dialog,
DialogFooter,
DialogType,
IDialogContentProps,
} from "@fluentui/react";
import React, { useMemo } from "react";
import { FormattedMessage } from "@oursky/react-messageformat";
import PrimaryButton from "../../PrimaryButton";
import DefaultButton from "../../DefaultButton";

interface CancelSubscriptionSurveyDialogProps {
isHidden: boolean;
onDismiss: () => void;
onConfirm: () => void;
onCancel: () => void;
}

export function CancelSubscriptionSurveyDialog({
isHidden,
onDismiss,
onConfirm,
onCancel,
}: CancelSubscriptionSurveyDialogProps): React.ReactElement {
const dialogContentProps: IDialogContentProps = useMemo(() => {
return {
type: DialogType.normal,
title: <FormattedMessage id="CancelSubscriptionSurveyDialog.title" />,
subText: (
<FormattedMessage id="CancelSubscriptionSurveyDialog.body" />
) as unknown as IDialogContentProps["subText"],
};
}, []);

return (
<Dialog
hidden={isHidden}
onDismiss={onDismiss}
dialogContentProps={dialogContentProps}
>
<DialogFooter>
<PrimaryButton
onClick={onConfirm}
disabled={isHidden}
text={
<FormattedMessage id="CancelSubscriptionSurveyDialog.button.yes" />
}
/>
<DefaultButton
onClick={onCancel}
text={
<FormattedMessage id="CancelSubscriptionSurveyDialog.button.no" />
}
/>
</DialogFooter>
</Dialog>
);
}
31 changes: 21 additions & 10 deletions portal/src/components/billing/CurrentPlanCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useContext, useMemo } from "react";
import React, { useCallback, useContext, useMemo } from "react";
import {
IButtonProps,
ITooltipHostProps,
Expand Down Expand Up @@ -35,6 +35,7 @@ import {
SubscriptionUsage,
Usage,
} from "../../graphql/portal/globalTypes.generated";
import { useNavigate } from "react-router-dom";

interface CurrentPlanCardProps {
planName: string;
Expand Down Expand Up @@ -420,6 +421,11 @@ function MAUUsageSection({
mauLimit: number | undefined;
mauPrevious: number | undefined;
}) {
const navigate = useNavigate();
const onUpgrade = useCallback(() => {
navigate({ hash: "Subscription" });
}, [navigate]);

return (
<section className={styles.card}>
<UsageMeter
Expand All @@ -429,6 +435,7 @@ function MAUUsageSection({
previous={mauPrevious}
warnPercentage={0.8}
tooltip={<FormattedMessage id="CurrentPlanCard.mau.tooltip" />}
onClickUpgrade={onUpgrade}
/>
</section>
);
Expand Down Expand Up @@ -461,7 +468,7 @@ function UsageMeter(props: UsageMeterProps): React.ReactElement {
onClickUpgrade,
} = props;
const percentComplete =
current != null && limit != null ? current / limit : 0;
current != null && limit != null ? current / limit : null;
const id = useId("usage-meter");
const calloutProps = useMemo(() => {
return {
Expand Down Expand Up @@ -495,14 +502,18 @@ function UsageMeter(props: UsageMeterProps): React.ReactElement {
{title}
</Text>
<ThemeProvider theme={theme}>
<ProgressIndicator
className="w-full"
percentComplete={percentComplete}
/>
{percentComplete != null ? (
<ProgressIndicator
className="w-full"
percentComplete={percentComplete}
/>
) : null}
<Text block={true} styles={usageStyles} variant="medium">
{current != null ? `${current}` : "-"}
{" / "}
{limit != null ? `${limit}` : "-"}
{limit != null && current != null
? `${current} / ${limit}`
: limit == null && current != null
? `${current}`
: null}
{previous != null ? (
<FormattedMessage
id="CurrentPlanCard.mau.previous"
Expand All @@ -516,7 +527,7 @@ function UsageMeter(props: UsageMeterProps): React.ReactElement {
<LinkButton onClick={onClickUpgrade}>
<FormattedMessage id="CurrentPlanCard.mau.limitReached" />
</LinkButton>
) : percentComplete >= warnPercentage ? (
) : percentComplete != null && percentComplete >= warnPercentage ? (
<LinkButton onClick={onClickUpgrade}>
<FormattedMessage id="CurrentPlanCard.mau.approachingLimit" />
</LinkButton>
Expand Down
34 changes: 26 additions & 8 deletions portal/src/graphql/portal/SubscriptionScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import LinkButton from "../../LinkButton";
import { useGenerateStripeCustomerPortalSessionMutationMutation } from "./mutations/generateStripeCustomerPortalSessionMutation";
import { CancelSubscriptionReminder } from "../../components/billing/CancelSubscriptionReminder";
import { extractRawID } from "../../util/graphql";
import { CancelSubscriptionSurveyDialog } from "../../components/billing/CancelSubscriptionSurveyDialog";

const CHECK_IS_PROCESSING_SUBSCRIPTION_INTERVAL = 5000;

Expand Down Expand Up @@ -486,6 +487,8 @@ function SubscriptionScreenContent(props: SubscriptionScreenContentProps) {

const [enterpriseDialogHidden, setEnterpriseDialogHidden] = useState(true);
const [cancelDialogHidden, setCancelDialogHidden] = useState(true);
const [cancelSurveyDialogHidden, setCancelSurveyDialogHidden] =
useState(true);

const { selectedKey: selectedTab, onLinkClick } = usePivotNavigation<Tab>([
Tab.Subscription,
Expand Down Expand Up @@ -551,18 +554,23 @@ function SubscriptionScreenContent(props: SubscriptionScreenContentProps) {
e.preventDefault();
e.stopPropagation();
await setSubscriptionCancelledStatus(true);
const projectID = extractRawID(appID);
const cancelSurveyURL = `https://oursky.typeform.com/authgear-cancel#project_id=${projectID}`;
const anchor = document.createElement("A") as HTMLAnchorElement;
anchor.href = cancelSurveyURL;
anchor.target = "_blank";
anchor.click();
anchor.remove();
setCancelDialogHidden(true);
setCancelSurveyDialogHidden(false);
},
[appID, setSubscriptionCancelledStatus]
[setSubscriptionCancelledStatus]
);

const onConfirmCancelSurveyDialog = useCallback(() => {
const projectID = extractRawID(appID);
const cancelSurveyURL = `https://oursky.typeform.com/authgear-cancel#project_id=${projectID}`;
const anchor = document.createElement("A") as HTMLAnchorElement;
anchor.href = cancelSurveyURL;
anchor.target = "_blank";
anchor.click();
anchor.remove();
setCancelSurveyDialogHidden(true);
}, [appID]);

return (
<>
<Dialog
Expand Down Expand Up @@ -596,6 +604,16 @@ function SubscriptionScreenContent(props: SubscriptionScreenContentProps) {
/>
</DialogFooter>
</Dialog>
<CancelSubscriptionSurveyDialog
isHidden={cancelSurveyDialogHidden}
onDismiss={useCallback(() => {
setCancelSurveyDialogHidden(true);
}, [])}
onConfirm={onConfirmCancelSurveyDialog}
onCancel={useCallback(() => {
setCancelSurveyDialogHidden(true);
}, [])}
/>
<ErrorDialog
error={cancelSubscriptionError}
rules={[]}
Expand Down
5 changes: 5 additions & 0 deletions portal/src/locale-data/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2110,6 +2110,11 @@
"PlanCard.action.reactivate": "Reactivate",
"PlanCard.action.contact-us": "Contact Us",

"CancelSubscriptionSurveyDialog.title": "Sorry to see you go",
"CancelSubscriptionSurveyDialog.body": "Do you have 1 minute to tell us why you are cancelling?{br, react}Your feedback helps us improve.",
"CancelSubscriptionSurveyDialog.button.yes": "Yes",
"CancelSubscriptionSurveyDialog.button.no": "No",

"FeatureBanner.title": "Simple pricing with no gotchas!",
"FeatureBanner.subtitle": "There are no feature gate between plans",
"FeatureBanner.features.customizeSignInPage": "Customize Sign-in/Sign-up Page",
Expand Down

0 comments on commit 2bb7dcf

Please sign in to comment.