feat(web): align pricing cards with subscription page

Bring the public pricing cards in line with the in-app subscription page and
strengthen the CTA:
- Move the "Popüler" badge from 2 Marka to Full Paket (one consistent story
  across both surfaces).
- CTA "Başla" -> "Ücretsiz Başla" (leans on the free trial the flow grants).
- Yearly line now uses a consistent ₺ format ("veya ₺2.000,00/yıl") plus a
  "· 2 ay bedava" savings cue instead of the bare, differently-formatted
  "veya 2.000,00 TL/yıl".
- Plan-decision helper above the grid ("Hangi planı seçmeliyim? …").

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-25 16:10:43 +03:00
parent d49964aac1
commit 34bbb01fed
4 changed files with 38 additions and 11 deletions

View File

@@ -0,0 +1,21 @@
/**
* Guards that the public pricing page emphasises the same plan as the in-app
* subscription page. The "Popüler" badge was moved from 2 Marka to Full Paket
* so both surfaces tell one story.
*/
import { describe, expect, test } from "vitest";
import { plans } from "@/routes/pricing";
describe("pricing plans", () => {
test("Full Paket is the only popular plan (aligned with subscription)", () => {
expect(plans.find((p) => p.key === "full")?.popular).toBe(true);
expect(plans.find((p) => p.key === "brand2")?.popular).toBeFalsy();
expect(plans.filter((p) => p.popular).map((p) => p.key)).toEqual(["full"]);
});
test("still lists all four tiers in ascending brand order", () => {
expect(plans.map((p) => p.key)).toEqual(["brand1", "brand2", "brand3", "full"]);
});
});

View File

@@ -6,13 +6,13 @@ 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 } from "lucide-react";
import { Check, Info } from "lucide-react";
export const Route = createFileRoute("/pricing")({
component: PricingPage,
});
const plans = [
export const plans = [
{
key: "brand1",
brandLimit: 1,
@@ -25,7 +25,6 @@ const plans = [
brandLimit: 2,
priceMonthly: 35000,
priceYearly: 350000,
popular: true,
features: ["brandSelection", "vinSearch", "partsCatalog", "schemaViewer", "prioritySupport"],
},
{
@@ -40,6 +39,7 @@ const plans = [
brandLimit: FULL_PLAN_BRAND_LIMIT,
priceMonthly: 99900,
priceYearly: 999000,
popular: true,
features: [
"allBrands",
"vinSearch",
@@ -86,7 +86,12 @@ function PricingPage() {
</div>
</div>
<div className="mt-12 grid gap-6 md:grid-cols-2 lg:grid-cols-4">
<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}
@@ -107,9 +112,8 @@ function PricingPage() {
<span className="text-muted-foreground">{t("common.perMonth")}</span>
</div>
<p className="text-sm text-muted-foreground">
{t("pricing.orYearly", {
price: formatTRY(plan.priceYearly).replace(/[₺\s]/g, "").trim(),
})}
{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) => {