From f67d8d3797d522ef12c52490b5048311ac51322b Mon Sep 17 00:00:00 2001 From: Kenneth Obsequio Date: Wed, 9 Sep 2026 13:55:33 +0800 Subject: [PATCH] added asset adjustments Signed-off-by: Kenneth Obsequio --- apps/api/providers/paypal.provider.js | 4 +- .../modules/client/layout/ClientLayout.jsx | 113 +++++++++++++++++- .../web/src/modules/client/pages/PlanList.jsx | 13 +- apps/web/src/utils/tierBadge.util.js | 21 ++++ 4 files changed, 135 insertions(+), 16 deletions(-) diff --git a/apps/api/providers/paypal.provider.js b/apps/api/providers/paypal.provider.js index 79c9de6..975557a 100644 --- a/apps/api/providers/paypal.provider.js +++ b/apps/api/providers/paypal.provider.js @@ -40,8 +40,8 @@ exports.createOrder = async ({ amount, currency = 'USD', referenceId, returnUrl, amount: { currency_code: currency, value: String(amount) }, }], application_context: { - return_url: returnUrl ?? `${process.env.FRONTEND_URL}/plans/checkout`, - cancel_url: cancelUrl ?? `${process.env.FRONTEND_URL}/plans/checkout?cancelled=true`, + return_url: returnUrl ?? `${process.env.FRONTEND_URL}/subscriptions/checkout`, + cancel_url: cancelUrl ?? `${process.env.FRONTEND_URL}/subscriptions/checkout?cancelled=true`, brand_name: process.env.PAYPAL_BRAND_NAME ?? 'STARR', user_action: 'PAY_NOW', }, diff --git a/apps/web/src/modules/client/layout/ClientLayout.jsx b/apps/web/src/modules/client/layout/ClientLayout.jsx index b921ca6..4e5e94c 100644 --- a/apps/web/src/modules/client/layout/ClientLayout.jsx +++ b/apps/web/src/modules/client/layout/ClientLayout.jsx @@ -15,6 +15,7 @@ import { DialogHeader, DialogTitle, DialogDescription, + DialogFooter, } from "@/components/ui/dialog" import { User, Settings, LogOut, SquareArrowOutUpRight, TableOfContents, @@ -29,7 +30,7 @@ import api from "@/utils/api.util" import { ClientProvider } from "@/contexts/provider/ClientProvider" import { useProfile } from "@/contexts/ProfileProvider" import { useClientTiers } from "@/contexts/ClientTiersProvider" -import { resolveTierBadge } from "@/utils/tierBadge.util" +import { resolveTierBadge, formatPlanDuration, formatTimeRemaining } from "@/utils/tierBadge.util" import { useGroup } from "@/contexts/ClientGroupContext" import { resolveAssetSrc } from "@/utils/media.util" import { useEffect, useRef, useState } from "react" @@ -176,6 +177,99 @@ function ExtraTiersDialog({ open, onOpenChange, tiers, tierMap }) { ) } +// ─── My plan dialog (opens from the tier badge in the header) ───────────────── + +function MyTierDialog({ open, onOpenChange, myTier, tierBadge, badgeImgUrl, TierIcon }) { + const navigate = useNavigate() + const { fmtDate, fmtCurrency } = useDateFormat() + + const isActive = myTier?.status === 'active' && myTier?.tier !== 'free' + const plan = myTier?.plan ?? null + const planId = myTier?.plan_id ?? plan?.plan_id ?? null + const duration = plan ? formatPlanDuration(plan.duration_days, plan.duration_unit) : null + const timeRemaining = isActive ? formatTimeRemaining(myTier?.expires_at) : null + + return ( + + + +
+ {badgeImgUrl + ? + : TierIcon && + } + {tierBadge.label} +
+ {plan?.label ?? (isActive ? tierBadge.label : "Free plan")} + + {plan?.description || (isActive + ? "Your current active subscription." + : "You're on the Free plan. Upgrade to unlock more content.")} + +
+ +
+ {plan && ( +
+ {fmtCurrency(plan.price, plan.currency)} + {duration && / {duration}} +
+ )} + + {isActive && ( +
+
+ Started + + {myTier?.starts_at ? fmtDate(myTier.starts_at) : "—"} + +
+
+ Expires + + {myTier?.expires_at ? fmtDate(myTier.expires_at) : "Never"} + +
+ {timeRemaining && ( +
+ Time remaining + {timeRemaining} +
+ )} +
+ )} + + {plan?.features?.length > 0 && ( +
+

Includes

+ {plan.features.map(({ text }, i) => ( +
+ + {text} +
+ ))} +
+ )} +
+ + + + +
+
+ ) +} + // ─── Helpers ────────────────────────────────────────────────────────────────── function getInitials(name = "") { @@ -194,6 +288,7 @@ function ClientNav() { const [referOpen, setReferOpen] = useState(false) const [extraTiersOpen, setExtraTiersOpen] = useState(false) + const [myTierOpen, setMyTierOpen] = useState(false) const [badgeImgUrl, setBadgeImgUrl] = useState(null) // Caches the resolved stream URL per asset_id — avoids re-hitting /media/token // for the same asset on re-renders. Token TTL is 4h so safe within a session. @@ -314,13 +409,17 @@ function ClientNav() { ) : tierBadge && (
-
+
+ {extraActiveTiers.length > 0 && (