From a9efd06f832857f79b34069743a11e5f061f9d43 Mon Sep 17 00:00:00 2001 From: Peter Zimon Date: Wed, 26 Jul 2023 16:13:07 +0200 Subject: [PATCH] Tiers related cleanup in AdminX settings (#17504) refs. https://github.com/TryGhost/Product/issues/3349 - added thousands separator to numbers in tiers list and preview - added dirty state handling to edit/add tier modal - applied sorting to tiers list - fixed free trial toggle bug. No default was set and didn't keep the trial value and the toggle in sync - applied a little scale down to tier preview for better proportions --- .../components/settings/membership/Tiers.tsx | 16 +++++++-- .../membership/tiers/TierDetailModal.tsx | 19 +++++++++-- .../membership/tiers/TierDetailPreview.tsx | 33 ++++++++++--------- .../settings/membership/tiers/TiersList.tsx | 7 ++-- apps/admin-x-settings/src/utils/helpers.ts | 4 +++ 5 files changed, 57 insertions(+), 22 deletions(-) diff --git a/apps/admin-x-settings/src/components/settings/membership/Tiers.tsx b/apps/admin-x-settings/src/components/settings/membership/Tiers.tsx index 8cba94ec75..082b1d87c1 100644 --- a/apps/admin-x-settings/src/components/settings/membership/Tiers.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/Tiers.tsx @@ -4,6 +4,7 @@ import StripeButton from '../../../admin-x-ds/settings/StripeButton'; import TabView from '../../../admin-x-ds/global/TabView'; import TiersList from './tiers/TiersList'; import useRouting from '../../../hooks/useRouting'; +import {Tier} from '../../../types/api'; import {getActiveTiers, getArchivedTiers} from '../../../utils/helpers'; import {useTiers} from '../../providers/ServiceProvider'; @@ -18,16 +19,27 @@ const Tiers: React.FC<{ keywords: string[] }> = ({keywords}) => { updateRoute('stripe-connect'); }; + const sortTiers = (t: Tier[]) => { + t.sort((a, b) => { + if ((a.monthly_price as number) < (b.monthly_price as number)) { + return -1; + } else { + return 1; + } + }); + return t; + }; + const tabs = [ { id: 'active-tiers', title: 'Active', - contents: () + contents: () }, { id: 'archived-tiers', title: 'Archived', - contents: () + contents: () } ]; diff --git a/apps/admin-x-settings/src/components/settings/membership/tiers/TierDetailModal.tsx b/apps/admin-x-settings/src/components/settings/membership/tiers/TierDetailModal.tsx index e8f4f891c7..692557b062 100644 --- a/apps/admin-x-settings/src/components/settings/membership/tiers/TierDetailModal.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/tiers/TierDetailModal.tsx @@ -47,7 +47,7 @@ const TierDetailModal: React.FC = ({tier}) => { return error; }; - const {formState, updateForm, handleSave} = useForm({ + const {formState, saveState, updateForm, handleSave} = useForm({ initialState: { ...(tier || {}), monthly_price: tier?.monthly_price ? currencyToDecimal(tier.monthly_price).toString() : '', @@ -97,10 +97,21 @@ const TierDetailModal: React.FC = ({tier}) => { return value.match(/[\d]+\.?[\d]{0,2}/)?.[0] || ''; }; + const toggleFreeTrial = (e: React.ChangeEvent) => { + if (e.target.checked) { + setHasFreeTrial(true); + updateForm(state => ({...state, trial_days: tier?.trial_days ? tier?.trial_days.toString() : '7'})); + } else { + setHasFreeTrial(false); + updateForm(state => ({...state, trial_days: '0'})); + } + }; + return { updateRoute('tiers'); }} + dirty={saveState === 'unsaved'} okLabel='Save & close' size='lg' testId='tier-detail-modal' @@ -116,6 +127,10 @@ const TierDetailModal: React.FC = ({tier}) => { } handleSave(); + + if (saveState !== 'unsaved') { + modal.remove(); + } }} >
@@ -186,7 +201,7 @@ const TierDetailModal: React.FC = ({tier}) => {
- setHasFreeTrial(e.target.checked)} /> +
> & { monthly_price: string; @@ -95,23 +96,25 @@ const TierDetailPreview: React.FC = ({tier, isFreeTier})
} -
-
-

{name || 'Bronze'}

-
-
- {currencySymbol} - {showingYearly ? yearlyPrice : monthlyPrice} - {!isFreeTier && /{showingYearly ? 'year' : 'month'}} +
+
+
+

{name || 'Bronze'}

+
+
+ {currencySymbol} + {showingYearly ? numberWithCommas(yearlyPrice) : numberWithCommas(monthlyPrice)} + {!isFreeTier && /{showingYearly ? 'year' : 'month'}} +
+
- + {(showingYearly && yearlyDiscount > 0) && }
- {(showingYearly && yearlyDiscount > 0) && } -
-
-
-
{description || (isFreeTier ? `Free preview of ${siteTitle}` : 'Full access to premium content')}
- +
+
+
{description || (isFreeTier ? `Free preview of ${siteTitle}` : 'Full access to premium content')}
+ +
diff --git a/apps/admin-x-settings/src/components/settings/membership/tiers/TiersList.tsx b/apps/admin-x-settings/src/components/settings/membership/tiers/TiersList.tsx index ff510c4c13..07a32d8ad0 100644 --- a/apps/admin-x-settings/src/components/settings/membership/tiers/TiersList.tsx +++ b/apps/admin-x-settings/src/components/settings/membership/tiers/TiersList.tsx @@ -7,6 +7,7 @@ import TierDetailModal from './TierDetailModal'; import useRouting from '../../../../hooks/useRouting'; import {Tier} from '../../../../types/api'; import {currencyToDecimal, getSymbol} from '../../../../utils/currency'; +import {numberWithCommas} from '../../../../utils/helpers'; interface TiersListProps { tab?: string; @@ -35,11 +36,11 @@ const TierCard: React.FC = ({ }}>
{tier.name}
- {currencySymbol} - {currencyToDecimal(tier.monthly_price || 0)} + {currencySymbol} + {numberWithCommas(currencyToDecimal(tier.monthly_price || 0))} {(tier.monthly_price && tier.monthly_price > 0) && /month}
-
+
{tier.description || No description}
diff --git a/apps/admin-x-settings/src/utils/helpers.ts b/apps/admin-x-settings/src/utils/helpers.ts index dbd5fbc02f..26d97b630a 100644 --- a/apps/admin-x-settings/src/utils/helpers.ts +++ b/apps/admin-x-settings/src/utils/helpers.ts @@ -143,3 +143,7 @@ export function getArchivedTiers(tiers: Tier[]) { return !tier.active; }); } + +export function numberWithCommas(x: number) { + return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); +} \ No newline at end of file