| |
| |
| |
| |
| |
|
|
| export type PaidPlanSlug = |
| | "lite_monthly" |
| | "lite_annual" |
| | "standard_monthly" |
| | "standard_annual" |
| | "pro_monthly" |
| | "pro_annual"; |
|
|
| export type CurrentSlug = PaidPlanSlug | "free"; |
|
|
| export type CardTier = "lite" | "standard" | "pro"; |
| export type Toggle = "monthly" | "annual"; |
|
|
| export type SwitchAction = |
| | { kind: "current"; targetSlug: PaidPlanSlug } |
| | { kind: "subscribe"; targetSlug: PaidPlanSlug } |
| | { kind: "upgrade"; targetSlug: PaidPlanSlug } |
| | { kind: "downgrade"; targetSlug: PaidPlanSlug } |
| | { |
| kind: "cycle_change"; |
| targetSlug: PaidPlanSlug; |
| direction: "upgrade" | "downgrade"; |
| }; |
|
|
| |
| |
| const PLAN_RANK: Record<PaidPlanSlug, [number, number]> = { |
| lite_monthly: [1, 1], |
| lite_annual: [1, 2], |
| standard_monthly: [2, 1], |
| standard_annual: [2, 2], |
| pro_monthly: [3, 1], |
| pro_annual: [3, 2], |
| }; |
|
|
| function compareRank(a: PaidPlanSlug, b: PaidPlanSlug): number { |
| const [aTier, aCycle] = PLAN_RANK[a]; |
| const [bTier, bCycle] = PLAN_RANK[b]; |
| if (aTier !== bTier) return aTier - bTier; |
| return aCycle - bCycle; |
| } |
|
|
| function tierOf(slug: PaidPlanSlug): CardTier { |
| return slug.split("_")[0] as CardTier; |
| } |
|
|
| export function targetSlug(tier: CardTier, toggle: Toggle): PaidPlanSlug { |
| return `${tier}_${toggle}` as PaidPlanSlug; |
| } |
|
|
| export function getSwitchAction( |
| currentSlug: CurrentSlug, |
| cardTier: CardTier, |
| toggle: Toggle, |
| ): SwitchAction { |
| const target = targetSlug(cardTier, toggle); |
|
|
| if (currentSlug === "free") { |
| return { kind: "subscribe", targetSlug: target }; |
| } |
|
|
| if (currentSlug === target) { |
| return { kind: "current", targetSlug: target }; |
| } |
|
|
| const cmp = compareRank(target, currentSlug); |
|
|
| |
| if (tierOf(currentSlug) === tierOf(target)) { |
| return { |
| kind: "cycle_change", |
| targetSlug: target, |
| direction: cmp > 0 ? "upgrade" : "downgrade", |
| }; |
| } |
|
|
| return { |
| kind: cmp > 0 ? "upgrade" : "downgrade", |
| targetSlug: target, |
| }; |
| } |
|
|
| export function planParts(slug: PaidPlanSlug): { |
| plan: CardTier; |
| cycle: Toggle; |
| } { |
| const [plan, cycle] = slug.split("_") as [CardTier, Toggle]; |
| return { plan, cycle }; |
| } |
|
|
| export function isPaidSlug(slug: string): slug is PaidPlanSlug { |
| return slug in PLAN_RANK; |
| } |
|
|
| export function getSwitchButtonLabel(action: SwitchAction): string { |
| switch (action.kind) { |
| case "current": |
| return "Current plan"; |
| case "subscribe": |
| if (action.targetSlug.startsWith("pro")) return "Upgrade to Pro"; |
| if (action.targetSlug.startsWith("standard")) return "Upgrade to Standard"; |
| return "Upgrade to Lite"; |
| case "upgrade": |
| return "Upgrade"; |
| case "downgrade": |
| return "Downgrade"; |
| case "cycle_change": |
| return action.targetSlug.endsWith("_annual") |
| ? "Switch to Annual" |
| : "Switch to Monthly"; |
| } |
| } |
|
|
| export function isSwitchActionDisabled( |
| action: SwitchAction, |
| options: { |
| paymentBlocked?: boolean; |
| scheduledTargetSlug?: string | null; |
| }, |
| ): boolean { |
| if (action.kind === "current" || action.kind === "subscribe") { |
| return false; |
| } |
| if (options.paymentBlocked) { |
| return true; |
| } |
| const scheduled = options.scheduledTargetSlug; |
| if (!scheduled) { |
| return false; |
| } |
| |
| if (action.kind === "upgrade") { |
| return false; |
| } |
| if (action.targetSlug === scheduled) { |
| return true; |
| } |
| if (action.kind === "downgrade") { |
| return true; |
| } |
| if (action.kind === "cycle_change" && action.direction === "downgrade") { |
| return true; |
| } |
| return false; |
| } |
|
|