Files
sase.tr/apps/web/src/routes/pricing.tsx
Semih Yesilyurt 1685ee4b44
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
feat(web): add "Nasıl çalışır" 3-step band to pricing page
Cold visitors landing straight on /pricing had no product context. Added a
compact 3-step band between the credibility strip and FAQ: enter the chassis
number -> see the parts (OEM codes, diagrams, compatible parts) -> pick the
right part by comparing prices. Grounded in the actual product, no ordering
claims.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 16:16:46 +03:00

200 lines
7.1 KiB
TypeScript

import { CredibilityStrip } from "@/components/credibility-strip";
import { SiteHeader } from "@/components/site-header";
import { usePageMeta } from "@/hooks/use-page-meta";
import { useTranslation } from "@/lib/i18n";
import { FULL_PLAN_BRAND_LIMIT, formatTRY } from "@sase/shared";
import { Button } from "@sase/ui";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@sase/ui";
import { Badge } from "@sase/ui";
import { Link, createFileRoute } from "@tanstack/react-router";
import { Check, ChevronDown, Info } from "lucide-react";
export const Route = createFileRoute("/pricing")({
component: PricingPage,
});
export const plans = [
{
key: "brand1",
brandLimit: 1,
priceMonthly: 20000,
priceYearly: 200000,
features: ["brandSelection", "vinSearch", "partsCatalog", "schemaViewer"],
},
{
key: "brand2",
brandLimit: 2,
priceMonthly: 35000,
priceYearly: 350000,
features: ["brandSelection", "vinSearch", "partsCatalog", "schemaViewer", "prioritySupport"],
},
{
key: "brand3",
brandLimit: 3,
priceMonthly: 50000,
priceYearly: 500000,
features: ["brandSelection", "vinSearch", "partsCatalog", "schemaViewer", "prioritySupport"],
},
{
key: "full",
brandLimit: FULL_PLAN_BRAND_LIMIT,
priceMonthly: 99900,
priceYearly: 999000,
popular: true,
features: [
"allBrands",
"vinSearch",
"partsCatalog",
"schemaViewer",
"prioritySupport",
"oemSearch",
],
},
];
function PricingPage() {
const { t } = useTranslation();
usePageMeta({
title: t("pricing.pageTitle"),
description: t("pricing.metaDescription"),
canonical: "https://sase.tr/pricing",
});
return (
<div className="min-h-screen">
<SiteHeader />
<main id="main-content" className="container mx-auto px-4 py-24">
<div className="mx-auto max-w-2xl text-center">
<p className="text-sm font-semibold uppercase tracking-wider text-primary">
{t("pricing.title")}
</p>
<h1 className="mt-3 text-4xl font-bold leading-tight">{t("pricing.heroTitle")}</h1>
<p className="mt-4 text-lg text-muted-foreground">{t("pricing.heroSubtitle")}</p>
<div className="mt-6 flex flex-wrap items-center justify-center gap-x-5 gap-y-2 text-sm text-muted-foreground">
{[
"pricing.trialFree",
"subscription.trustNoCard",
"subscription.trustCancelAnytime",
"subscription.trustRefund",
].map((k) => (
<span key={k} className="flex items-center gap-1.5">
<Check className="h-4 w-4 text-primary" />
{t(k)}
</span>
))}
</div>
</div>
<div className="mx-auto mt-10 flex max-w-2xl items-start justify-center gap-2 rounded-lg border border-border bg-muted/30 px-4 py-2.5 text-sm text-muted-foreground">
<Info className="mt-0.5 h-4 w-4 shrink-0 text-primary" />
<span>{t("subscription.planHelper")}</span>
</div>
<div className="mt-8 grid gap-6 md:grid-cols-2 lg:grid-cols-4">
{plans.map((plan) => (
<Card
key={plan.key}
className={plan.popular ? "border-primary shadow-lg relative" : ""}
>
{plan.popular && (
<Badge className="absolute -top-3 left-1/2 -translate-x-1/2">
{t("subscription.popular")}
</Badge>
)}
<CardHeader>
<CardTitle>{t(`subscription.plans.${plan.key}.name`)}</CardTitle>
<CardDescription>{t(`subscription.plans.${plan.key}.description`)}</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div>
<span className="text-3xl font-bold">{formatTRY(plan.priceMonthly)}</span>
<span className="text-muted-foreground">{t("common.perMonth")}</span>
</div>
<p className="text-sm text-muted-foreground">
{t("pricing.orYearly", { price: formatTRY(plan.priceYearly) })}{" "}
<span className="font-medium text-primary">{t("pricing.yearlySave")}</span>
</p>
<ul className="space-y-2 text-sm">
{plan.features.map((f) => {
const featureLabel =
f === "brandSelection"
? t(`subscription.features.${f}`, { count: plan.brandLimit })
: t(`subscription.features.${f}`);
return (
<li key={f} className="flex items-center gap-2">
<Check className="h-4 w-4 text-primary" />
{featureLabel}
</li>
);
})}
</ul>
<Link to="/register">
<Button className="w-full" variant={plan.popular ? "default" : "outline"}>
{t("pricing.getStarted")}
</Button>
</Link>
</CardContent>
</Card>
))}
</div>
<div className="mx-auto mt-12 max-w-5xl">
<CredibilityStrip />
</div>
<PricingHowItWorks />
<PricingFaq />
</main>
</div>
);
}
export function PricingHowItWorks() {
const { t } = useTranslation();
const steps = [1, 2, 3] as const;
return (
<section className="mx-auto mt-20 max-w-5xl">
<h2 className="text-center text-2xl font-bold">{t("pricing.how.title")}</h2>
<div className="mt-8 grid gap-6 sm:grid-cols-3">
{steps.map((i) => (
<div key={i} className="rounded-2xl border border-border bg-muted/20 p-6 text-center">
<div className="mx-auto flex h-10 w-10 items-center justify-center rounded-full bg-primary font-bold text-primary-foreground">
{i}
</div>
<h3 className="mt-4 font-semibold">{t(`pricing.how.s${i}t`)}</h3>
<p className="mt-2 text-sm leading-6 text-muted-foreground">
{t(`pricing.how.s${i}d`)}
</p>
</div>
))}
</div>
</section>
);
}
export function PricingFaq() {
const { t } = useTranslation();
const ids = [1, 2, 3, 4, 5, 6] as const;
return (
<section className="mx-auto mt-20 max-w-3xl">
<h2 className="text-center text-2xl font-bold">{t("pricing.faq.title")}</h2>
<div className="mt-8 divide-y divide-border overflow-hidden rounded-2xl border border-border">
{ids.map((i) => (
<details key={i} className="group">
<summary className="flex cursor-pointer list-none items-center justify-between gap-4 p-5 font-medium">
{t(`pricing.faq.q${i}`)}
<ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground transition-transform group-open:rotate-180" />
</summary>
<p className="px-5 pb-5 text-sm leading-6 text-muted-foreground">
{t(`pricing.faq.a${i}`)}
</p>
</details>
))}
</div>
</section>
);
}