feat(FN-263): normalize typography on subscription page (+2 more)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged: - feat(FN-263): normalize typography on subscription page - chore(FN-263): apply Biome formatting fix for long className lines - feat(FN-263): normalize typography on subscription page Files changed: .../src/routes/dashboard/subscription/index.tsx | 116 +++++++++++---------- 1 file changed, 62 insertions(+), 54 deletions(-) Fusion-Task-Id: FN-263
This commit is contained in:
@@ -414,7 +414,7 @@ export function SubscriptionPage() {
|
||||
<CardContent className="relative flex flex-col items-center gap-6 py-10">
|
||||
<div className="flex items-center gap-2">
|
||||
<Sparkles className="h-6 w-6 animate-pulse text-brand" />
|
||||
<h2 className="text-xl font-bold text-foreground">
|
||||
<h2 className="text-xl font-bold leading-8 text-foreground">
|
||||
{t("subscription.onboarding.provisioning")}
|
||||
</h2>
|
||||
</div>
|
||||
@@ -439,7 +439,7 @@ export function SubscriptionPage() {
|
||||
|
||||
{/* If animation finished but mutation still pending */}
|
||||
{animationEnded && trialMutation.isPending && (
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<div className="flex items-center gap-2 text-sm leading-5 text-muted-foreground">
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
{t("subscription.onboarding.step4")}...
|
||||
</div>
|
||||
@@ -448,7 +448,7 @@ export function SubscriptionPage() {
|
||||
{/* Mutation error */}
|
||||
{trialMutation.isError && (
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<p className="text-sm text-red-600 dark:text-red-400">
|
||||
<p className="text-sm leading-5 text-red-600 dark:text-red-400">
|
||||
{t("subscription.onboarding.error")}
|
||||
</p>
|
||||
<Button variant="outline" onClick={() => trialMutation.mutate()}>
|
||||
@@ -472,14 +472,14 @@ export function SubscriptionPage() {
|
||||
<CardContent className="relative flex flex-col items-center gap-6 py-10">
|
||||
<CheckCircle2 className="h-16 w-16 text-brand" />
|
||||
|
||||
<h2 className="text-center text-2xl font-bold text-foreground">
|
||||
<h2 className="text-center text-2xl font-bold leading-9 text-foreground">
|
||||
{t("subscription.onboarding.completed")}
|
||||
</h2>
|
||||
|
||||
{/* Subscription info box */}
|
||||
<div className="w-full max-w-md space-y-4 rounded-xl border border-brand/20 bg-background/60 p-5 backdrop-blur-sm dark:bg-background/30">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
<span className="text-sm leading-5 text-muted-foreground">
|
||||
{t("subscription.currentPlan")}
|
||||
</span>
|
||||
<Badge className="bg-brand text-brand-foreground">
|
||||
@@ -487,23 +487,23 @@ export function SubscriptionPage() {
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
<span className="text-sm leading-5 text-muted-foreground">
|
||||
{t("subscription.billingPeriod")}
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
<span className="text-sm font-medium leading-5">
|
||||
{t("subscription.onboarding.trialDuration")}
|
||||
</span>
|
||||
</div>
|
||||
{freshSub?.endDate && (
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-muted-foreground">{t("subscription.endDate")}</span>
|
||||
<span className="text-sm font-medium">
|
||||
<span className="text-sm leading-5 text-muted-foreground">{t("subscription.endDate")}</span>
|
||||
<span className="text-sm font-medium leading-5">
|
||||
{new Date(freshSub.endDate).toLocaleDateString("tr-TR")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<Separator />
|
||||
<ul className="space-y-2 text-sm">
|
||||
<ul className="space-y-2 text-sm leading-relaxed">
|
||||
{["allBrands", "vinSearch", "partsCatalog", "schemaViewer"].map((f) => (
|
||||
<li key={f} className="flex items-center gap-2 text-foreground/85">
|
||||
<Check className="h-4 w-4 text-brand" />
|
||||
@@ -530,7 +530,7 @@ export function SubscriptionPage() {
|
||||
// ─── Normal subscription page ──────────────────────────────────────────────
|
||||
return (
|
||||
<div className="mx-auto max-w-5xl space-y-8 pb-16">
|
||||
<h2 className="text-2xl font-bold">{t("subscription.title")}</h2>
|
||||
<h2 className="text-2xl font-bold leading-9">{t("subscription.title")}</h2>
|
||||
|
||||
{/* Active Subscription Status */}
|
||||
{subscription && !(eligibleForTrial && subscription.status === "trial") && (
|
||||
@@ -539,13 +539,13 @@ export function SubscriptionPage() {
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Crown className="h-5 w-5 text-primary" />
|
||||
<CardTitle>{t("subscription.currentPlan")}</CardTitle>
|
||||
<CardTitle className="leading-7">{t("subscription.currentPlan")}</CardTitle>
|
||||
</div>
|
||||
<Badge variant={statusVariants[subscription.status] || "secondary"}>
|
||||
{t(`subscription.statusLabels.${subscription.status}`)}
|
||||
</Badge>
|
||||
</div>
|
||||
<CardDescription>
|
||||
<CardDescription className="font-normal leading-5">
|
||||
{subscription.plan?.name} —{" "}
|
||||
{subscription.status === "trial"
|
||||
? t("subscription.onboarding.trialDuration")
|
||||
@@ -557,7 +557,9 @@ export function SubscriptionPage() {
|
||||
<CardContent className="space-y-4">
|
||||
{subscription.brands && subscription.brands.length > 0 && (
|
||||
<div>
|
||||
<p className="mb-2 text-sm font-medium">{t("subscription.accessibleBrands")}:</p>
|
||||
<p className="mb-2 text-sm font-medium leading-5">
|
||||
{t("subscription.accessibleBrands")}:
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{subscription.brands.map((b) => (
|
||||
<Badge key={b.brandId} variant="outline" className="flex items-center gap-1.5">
|
||||
@@ -569,7 +571,7 @@ export function SubscriptionPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-wrap gap-6 text-sm">
|
||||
<div className="flex flex-wrap gap-6 text-sm leading-5">
|
||||
{subscription.startDate && (
|
||||
<div>
|
||||
<span className="text-muted-foreground">{t("subscription.startDate")}: </span>
|
||||
@@ -589,7 +591,7 @@ export function SubscriptionPage() {
|
||||
</div>
|
||||
|
||||
{subscription.status === "trial" && subscription.endDate && (
|
||||
<div className="flex items-center gap-2 rounded-lg bg-brand/10 px-3 py-2 text-sm text-brand">
|
||||
<div className="flex items-center gap-2 rounded-lg bg-brand/10 px-3 py-2 text-sm leading-5 text-brand">
|
||||
<Sparkles className="h-4 w-4" />
|
||||
{(() => {
|
||||
const days = Math.max(
|
||||
@@ -644,18 +646,18 @@ export function SubscriptionPage() {
|
||||
<span className="font-semibold text-foreground">
|
||||
{t(`subscription.plans.${downgradePlan.key}.name`)}
|
||||
</span>
|
||||
<span className="text-lg font-bold">
|
||||
<span className="text-lg font-bold leading-7">
|
||||
{formatTRY(
|
||||
billingPeriod === "yearly"
|
||||
? downgradePlan.priceYearly
|
||||
: downgradePlan.priceMonthly,
|
||||
)}
|
||||
<span className="text-xs font-normal text-muted-foreground">
|
||||
<span className="text-xs font-normal leading-4 text-muted-foreground">
|
||||
{billingPeriod === "yearly" ? t("common.perYear") : t("common.perMonth")}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
<p className="text-sm leading-5 text-muted-foreground">
|
||||
{t(`subscription.plans.${downgradePlan.key}.description`)}
|
||||
</p>
|
||||
{(() => {
|
||||
@@ -670,7 +672,7 @@ export function SubscriptionPage() {
|
||||
const savings = currentPrice - newPrice;
|
||||
if (savings <= 0) return null;
|
||||
return (
|
||||
<p className="text-sm font-medium text-primary">
|
||||
<p className="text-sm font-medium leading-5 text-primary">
|
||||
{t("subscription.downgradeOffer.savingsLabel", {
|
||||
savings: formatTRY(savings),
|
||||
})}
|
||||
@@ -682,7 +684,7 @@ export function SubscriptionPage() {
|
||||
{/* Brand selector — only for non-full plans */}
|
||||
{downgradePlan.brandLimit < FULL_PLAN_BRAND_LIMIT && (
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
<p className="text-sm leading-5 text-muted-foreground">
|
||||
{t("subscription.downgradeOffer.brandsHint")}
|
||||
</p>
|
||||
<Suspense fallback={<BrandSelectorFallback />}>
|
||||
@@ -791,14 +793,16 @@ export function SubscriptionPage() {
|
||||
<CardHeader className="relative">
|
||||
<div className="flex items-center gap-2">
|
||||
<Sparkles className="h-5 w-5 text-brand" />
|
||||
<CardTitle className="text-foreground">{t("subscription.trialTitle")}</CardTitle>
|
||||
<CardTitle className="text-foreground leading-7">
|
||||
{t("subscription.trialTitle")}
|
||||
</CardTitle>
|
||||
</div>
|
||||
<CardDescription className="text-muted-foreground">
|
||||
<CardDescription className="text-muted-foreground font-normal leading-5">
|
||||
{t("subscription.trialDescription")}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="relative space-y-4">
|
||||
<ul className="space-y-2 text-sm">
|
||||
<ul className="space-y-2 text-sm leading-relaxed">
|
||||
{["allBrands", "vinSearch", "partsCatalog", "schemaViewer"].map((f) => (
|
||||
<li key={f} className="flex items-center gap-2 text-foreground/85">
|
||||
<Check className="h-4 w-4 text-brand" />
|
||||
@@ -818,7 +822,7 @@ export function SubscriptionPage() {
|
||||
<ShieldCheck className="mr-2 h-4 w-4" />
|
||||
{trialMutation.isPending ? t("common.loading") : t("subscription.startTrial")}
|
||||
</Button>
|
||||
<div className="flex flex-wrap items-center justify-center gap-x-4 gap-y-1 text-xs text-foreground/70">
|
||||
<div className="flex flex-wrap items-center justify-center gap-x-4 gap-y-1 text-xs leading-5 text-foreground/70">
|
||||
<span className="flex items-center gap-1">
|
||||
<CreditCard className="h-3 w-3 shrink-0" />
|
||||
{t("subscription.trustNoCard")}
|
||||
@@ -840,10 +844,10 @@ export function SubscriptionPage() {
|
||||
{!eligibleForTrial && !subscription && (
|
||||
<Card className="border-dashed">
|
||||
<CardContent className="py-8 text-center">
|
||||
<p className="mb-2 text-lg font-medium text-muted-foreground">
|
||||
<p className="mb-2 text-lg font-medium leading-7 text-muted-foreground">
|
||||
{t("subscription.noSubscription")}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
<p className="text-sm leading-5 text-muted-foreground">
|
||||
{t("subscription.selectBrandsDescription")}
|
||||
</p>
|
||||
</CardContent>
|
||||
@@ -874,7 +878,7 @@ export function SubscriptionPage() {
|
||||
|
||||
{/* Plan Comparison Cards */}
|
||||
<div>
|
||||
<h3 className="mb-4 text-lg font-semibold">{t("subscription.planComparison")}</h3>
|
||||
<h3 className="mb-4 text-xl font-semibold leading-8">{t("subscription.planComparison")}</h3>
|
||||
<div ref={planCardsRef} className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
{plans.map((plan) => {
|
||||
const isSelected = selectedPlanKey === plan.key;
|
||||
@@ -909,16 +913,18 @@ export function SubscriptionPage() {
|
||||
</Badge>
|
||||
)}
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg">
|
||||
<CardTitle className="text-lg leading-7">
|
||||
{t(`subscription.plans.${plan.key}.name`)}
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
<CardDescription className="font-normal leading-5">
|
||||
{t(`subscription.plans.${plan.key}.description`)}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div>
|
||||
<span className="text-3xl font-bold">{formatTRY(price)}</span>
|
||||
<span className="text-xl sm:text-2xl lg:text-3xl font-bold leading-8 sm:leading-9 lg:leading-10">
|
||||
{formatTRY(price)}
|
||||
</span>
|
||||
<span className="text-muted-foreground">
|
||||
{billingPeriod === "monthly" ? t("common.perMonth") : t("common.perYear")}
|
||||
</span>
|
||||
@@ -928,7 +934,7 @@ export function SubscriptionPage() {
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<ul className="space-y-2 text-sm">
|
||||
<ul className="text-sm leading-relaxed space-y-2">
|
||||
{plan.features.map((f) => (
|
||||
<li key={f} className="flex items-center gap-2">
|
||||
<Check className="h-4 w-4 text-primary" />
|
||||
@@ -939,7 +945,7 @@ export function SubscriptionPage() {
|
||||
</CardContent>
|
||||
<CardFooter>
|
||||
{isCurrentPlan ? (
|
||||
<div className="flex w-full items-center justify-center rounded-md bg-muted py-2 text-sm font-medium text-muted-foreground">
|
||||
<div className="flex w-full items-center justify-center rounded-md bg-muted py-2 text-sm font-medium leading-5 text-muted-foreground">
|
||||
<Check className="mr-1.5 h-4 w-4" />
|
||||
{t("subscription.currentPlan")}
|
||||
</div>
|
||||
@@ -977,10 +983,12 @@ export function SubscriptionPage() {
|
||||
<Separator />
|
||||
<Card>
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-lg">{t("subscription.orderSummary")}</CardTitle>
|
||||
<CardTitle className="text-lg leading-7">
|
||||
{t("subscription.orderSummary")}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-2">
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<div className="flex items-center justify-between text-sm leading-5">
|
||||
<span className="text-muted-foreground">
|
||||
{t("subscription.orderSummaryPlan")}
|
||||
</span>
|
||||
@@ -988,7 +996,7 @@ export function SubscriptionPage() {
|
||||
{t(`subscription.plans.${selectedPlanKey}.name`)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<div className="flex items-center justify-between text-sm leading-5">
|
||||
<span className="text-muted-foreground">
|
||||
{t("subscription.orderSummaryPeriod")}
|
||||
</span>
|
||||
@@ -997,7 +1005,7 @@ export function SubscriptionPage() {
|
||||
</span>
|
||||
</div>
|
||||
{selectedBrandIds.length > 0 && (
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<div className="flex items-center justify-between text-sm leading-5">
|
||||
<span className="text-muted-foreground">
|
||||
{t("subscription.orderSummaryBrands")}
|
||||
</span>
|
||||
@@ -1007,11 +1015,11 @@ export function SubscriptionPage() {
|
||||
</div>
|
||||
)}
|
||||
<Separator />
|
||||
<div className="flex items-center justify-between font-semibold">
|
||||
<div className="flex items-center justify-between font-bold">
|
||||
<span>{t("subscription.orderSummaryPrice")}</span>
|
||||
<span>
|
||||
{formatTRY(summaryPrice)}
|
||||
<span className="text-xs font-normal text-muted-foreground">
|
||||
<span className="text-xs font-normal leading-4 text-muted-foreground">
|
||||
{periodLabel}
|
||||
</span>
|
||||
</span>
|
||||
@@ -1027,8 +1035,8 @@ export function SubscriptionPage() {
|
||||
<>
|
||||
<Separator />
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-lg font-semibold">{t("subscription.selectBrands")}</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
<h3 className="text-xl font-semibold leading-8">{t("subscription.selectBrands")}</h3>
|
||||
<p className="text-sm leading-5 text-muted-foreground">
|
||||
{t("subscription.selectBrandsDescription")}
|
||||
</p>
|
||||
<Suspense fallback={<BrandSelectorFallback />}>
|
||||
@@ -1056,7 +1064,7 @@ export function SubscriptionPage() {
|
||||
</div>
|
||||
<section
|
||||
aria-label="Ödeme güvencesi"
|
||||
className="flex flex-wrap items-center justify-end gap-x-3 gap-y-1 text-xs text-foreground/70"
|
||||
className="flex flex-wrap items-center justify-end gap-x-3 gap-y-1 text-xs leading-5 text-foreground/70"
|
||||
>
|
||||
<span aria-hidden="true">🔒</span>
|
||||
<span>{t("subscription.paymentTrustSSL")}</span>
|
||||
@@ -1073,28 +1081,28 @@ export function SubscriptionPage() {
|
||||
>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="px-1.5 py-0 text-[10px] font-normal"
|
||||
className="px-1.5 py-0 text-[0.625rem] leading-3 font-normal"
|
||||
aria-hidden="true"
|
||||
>
|
||||
Visa
|
||||
</Badge>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="px-1.5 py-0 text-[10px] font-normal"
|
||||
className="px-1.5 py-0 text-[0.625rem] leading-3 font-normal"
|
||||
aria-hidden="true"
|
||||
>
|
||||
Mastercard
|
||||
</Badge>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="px-1.5 py-0 text-[10px] font-normal"
|
||||
className="px-1.5 py-0 text-[0.625rem] leading-3 font-normal"
|
||||
aria-hidden="true"
|
||||
>
|
||||
Troy
|
||||
</Badge>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="px-1.5 py-0 text-[10px] font-normal"
|
||||
className="px-1.5 py-0 text-[0.625rem] leading-3 font-normal"
|
||||
aria-hidden="true"
|
||||
>
|
||||
AmEx
|
||||
@@ -1121,12 +1129,12 @@ export function SubscriptionPage() {
|
||||
<div className="fixed inset-x-0 bottom-0 z-50 animate-fade-in-up border-t border-border bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80">
|
||||
<div className="mx-auto flex max-w-5xl items-center justify-between px-6 py-3">
|
||||
<div className="flex items-baseline gap-3">
|
||||
<span className="text-sm font-semibold">
|
||||
<span className="text-sm font-semibold leading-5">
|
||||
{t(`subscription.plans.${selectedPlanKey}.name`)}
|
||||
</span>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
<span className="text-sm leading-5 text-muted-foreground">
|
||||
{formatTRY(barPrice)}
|
||||
<span className="text-xs">{barPeriodLabel}</span>
|
||||
<span className="text-xs leading-4">{barPeriodLabel}</span>
|
||||
</span>
|
||||
</div>
|
||||
<Button size="sm" className="px-6" onClick={handleProceedToPayment}>
|
||||
@@ -1172,16 +1180,16 @@ function FeatureMatrix() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3 className="mb-4 text-lg font-semibold">{t("subscription.featureMatrix")}</h3>
|
||||
<h3 className="mb-4 text-xl font-semibold leading-8">{t("subscription.featureMatrix")}</h3>
|
||||
<div className="overflow-x-auto rounded-lg border">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b bg-muted/50">
|
||||
<th className="w-48 px-4 py-3 text-left font-medium text-muted-foreground" />
|
||||
<th className="w-48 px-4 py-3 text-left font-semibold leading-5 text-muted-foreground" />
|
||||
{plans.map((plan) => (
|
||||
<th
|
||||
key={plan.key}
|
||||
className={`relative px-4 py-3 text-center font-semibold ${
|
||||
className={`relative px-4 py-3 text-center font-semibold leading-5 ${
|
||||
plan.popular ? "text-primary" : "text-foreground"
|
||||
}`}
|
||||
aria-label={t(`subscription.plans.${plan.key}.name`)}
|
||||
@@ -1192,13 +1200,13 @@ function FeatureMatrix() {
|
||||
<tbody>
|
||||
{ALL_FEATURES.map((feature, i) => (
|
||||
<tr key={feature} className={i % 2 === 0 ? "bg-background" : "bg-muted/25"}>
|
||||
<td className="px-4 py-3 font-medium text-foreground">
|
||||
<td className="px-4 py-3 font-medium leading-5 text-foreground">
|
||||
{t(`subscription.features.${feature}`)}
|
||||
</td>
|
||||
{plans.map((plan) => {
|
||||
const has = (plan.features as readonly string[]).includes(feature);
|
||||
return (
|
||||
<td key={plan.key} className="px-4 py-3 text-center">
|
||||
<td key={plan.key} className="px-4 py-3 text-center leading-5">
|
||||
{has ? (
|
||||
<Check
|
||||
className="mx-auto h-4 w-4 text-primary"
|
||||
|
||||
Reference in New Issue
Block a user