dev #59

Merged
root merged 7 commits from dev into main 2026-05-27 12:50:18 +03:00
8 changed files with 158 additions and 56 deletions

View File

@@ -2,7 +2,7 @@ import { useEffect } from "react";
const DEFAULT_TITLE = "Şase Numarası Sorgulama & OEM Parça Kataloğu | Sase.tr";
const DEFAULT_DESCRIPTION =
"Araç şase numarasını (VIN) girin, saniyeler içinde OEM parça kodlarına ulaşın. 50+ marka, 1M+ parça. Yanlış sipariş yapmadan doğru parçayı bulun.";
"Araç şase numarasını (VIN) girin, saniyeler içinde OEM parça kodlarına ulaşın. 27 marka, 1M+ parça. Yanlış sipariş yapmadan doğru parçayı bulun.";
const DEFAULT_CANONICAL = "https://sase.tr";
interface PageMetaOptions {

View File

@@ -220,7 +220,7 @@
"brandCountAll": "All",
"planHelper": "Which plan should I pick? Choose the number of brands you work with. For 4+ brands, the Full Package is cheaper per brand.",
"credibility": {
"brands": "50+ brands",
"brands": "27 brands",
"parts": "1M+ parts",
"oem": "OEM + alternative codes",
"speed": "Results in seconds",

View File

@@ -220,7 +220,7 @@
"brandCountAll": "Tümü",
"planHelper": "Hangi planı seçmeliyim? İşine giren marka sayısını seç. 4+ marka için Full Paket marka başına daha avantajlı.",
"credibility": {
"brands": "50+ marka",
"brands": "27 marka",
"parts": "1M+ parça",
"oem": "OEM + alternatif kodlar",
"speed": "Saniyede sonuç",

View File

@@ -109,7 +109,7 @@ function AuthLayout() {
<circle cx="7" cy="17" r="2" />
<circle cx="17" cy="17" r="2" />
</svg>
50+ Marka
27 Marka
</span>
<span className="inline-flex items-center gap-1.5 rounded-full border border-border bg-surface/60 px-3 py-1.5 text-xs text-foreground/80 backdrop-blur-sm">
<Database className="size-3" />

View File

@@ -14,14 +14,21 @@ import { useRef, useState } from "react";
export const Route = createFileRoute("/_auth/register")({
component: RegisterPage,
validateSearch: (search: Record<string, unknown>): { vin?: string; ref?: string } => ({
validateSearch: (
search: Record<string, unknown>,
): { vin?: string; ref?: string; plan?: string } => ({
vin: (search.vin as string) || undefined,
ref: (search.ref as string) || undefined,
plan: (search.plan as string) || undefined,
}),
});
// Carry a plan chosen on the marketing pricing through the value-first
// onboarding detour so the subscription page can preselect it later.
const PENDING_PLAN_KEY = "sase-pending-plan";
function RegisterPage() {
const { vin, ref } = Route.useSearch();
const { vin, ref, plan } = Route.useSearch();
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
@@ -92,6 +99,7 @@ function RegisterPage() {
// (covers both email and Google OAuth signups).
capture("user_signed_up", { method: "email" });
trackMeta("CompleteRegistration", { method: "email" });
if (plan) localStorage.setItem(PENDING_PLAN_KEY, plan);
toast.success("Hesap oluşturuldu!");
window.location.href = redirectUrl;
} catch {
@@ -123,6 +131,7 @@ function RegisterPage() {
startAction("register", { method: "google" });
capture("user_signed_up", { method: "google" });
trackMeta("CompleteRegistration", { method: "google" });
if (plan) localStorage.setItem(PENDING_PLAN_KEY, plan);
signIn.social({ provider: "google", callbackURL: redirectUrl });
}}
>

View File

@@ -252,7 +252,7 @@ const POSTS: Record<string, BlogPost> = {
<p>
Sase.tr, Türkiye'nin yedek parça sektörüne özgü geliştirilen bu dijital dönüşümün
öncüsüdür. VIN/şase numarası sorgulama, çoklu katalog entegrasyonu ve interaktif şema
görüntüleme özellikleriyle geleneksel yapış biçimlerini dönüştürmektedir. Platform, 50+
görüntüleme özellikleriyle geleneksel yapış biçimlerini dönüştürmektedir. Platform, 27
marka ve 1 milyondan fazla OEM parça numarasıyla sektörün en kapsamlı dijital kataloğunu
sunmaktadır.
</p>
@@ -331,7 +331,7 @@ const POSTS: Record<string, BlogPost> = {
Parça bulamadığınızda kategori ağacında bir seviye yukarı çıkarak daha geniş bir arama
yapabilirsiniz.
</li>
<li>50+ markanın tamamına erişmek için Full Paket aboneliği en avantajlı seçenektir.</li>
<li>27 markanın tamamına erişmek için Full Paket aboneliği en avantajlı seçenektir.</li>
</ul>
<h2 className="text-xl font-semibold text-foreground">Sonuç</h2>

View File

@@ -75,6 +75,7 @@ interface SubscriptionSearch {
ref?: string | null;
stripe?: string;
session_id?: string;
plan?: string;
}
export const Route = createFileRoute("/dashboard/subscription/")({
@@ -83,10 +84,15 @@ export const Route = createFileRoute("/dashboard/subscription/")({
ref: search.ref ? String(search.ref) : undefined,
stripe: search.stripe ? String(search.stripe) : undefined,
session_id: search.session_id ? String(search.session_id) : undefined,
plan: search.plan ? String(search.plan) : undefined,
}),
component: SubscriptionPage,
});
/** localStorage carrier so a plan chosen pre-signup survives the value-first
* onboarding detour (register → search) and preselects on the next visit. */
export const PENDING_PLAN_KEY = "sase-pending-plan";
interface SubscriptionBrand {
brandId: string;
brandName: string;
@@ -414,6 +420,24 @@ export function SubscriptionPage() {
setStep("payment");
}, [subscription, selectedPlanKey]);
// Preselect a plan chosen from the marketing pricing — via ?plan= or the
// pre-signup localStorage carrier. Skipped for active/pending subscriptions,
// during the welcome onboarding, or once the user has started selecting.
const hasPreselectedRef = useRef(false);
useEffect(() => {
if (hasPreselectedRef.current || isLoading || welcome || selectedPlanKey) return;
if (subscription?.status === "active" || subscription?.status === "pending") return;
const stored = typeof window !== "undefined" ? localStorage.getItem(PENDING_PLAN_KEY) : null;
const planKey = search.plan ?? stored ?? null;
if (!planKey || !plans.some((p) => p.key === planKey)) return;
hasPreselectedRef.current = true;
localStorage.removeItem(PENDING_PLAN_KEY);
capture("plan_selected", { plan: planKey, source: "marketing_preselect" });
setSelectedPlanKey(planKey);
setSelectedBrandIds([]);
setStep(planKey === "full" ? "payment" : "brands");
}, [isLoading, welcome, selectedPlanKey, subscription, search.plan]);
const cancelPendingMutation = useMutation({
mutationFn: () => api.patch("/subscriptions/cancel-pending"),
onSuccess: () => {

View File

@@ -263,7 +263,7 @@ const BENTO_ITEMS = [
},
{
icon: Car,
title: "50+ Marka",
title: "27 Marka",
description: "Avrupa, Asya ve Amerikan markaları tek çatı altında.",
stat: null,
wide: false,
@@ -317,6 +317,33 @@ const COMPARISON = [
{ label: "Aylık zaman tasarrufu", manual: "—", sase: "~40 saat" },
];
const FAQ = [
{
q: "Hangi markaları destekliyorsunuz?",
a: "Şu an 27 otomobil markası destekleniyor ve liste sürekli genişliyor. Şase numaranızı arama kutusuna girerek aracınızın desteklenip desteklenmediğini hemen görebilirsiniz.",
},
{
q: "Verilerim güvende mi?",
a: "Tüm trafik SSL ile şifrelenir ve verileriniz KVKK'ya uygun şekilde işlenir. Sorgu geçmişiniz yalnızca size özeldir.",
},
{
q: "Kart bilgisi gerekiyor mu?",
a: "Hayır. 30 günlük ücretsiz deneme için kredi kartı bilgisi istemiyoruz; dilediğiniz an vazgeçebilirsiniz.",
},
{
q: "İstediğim zaman iptal edebilir miyim?",
a: "Evet, taahhüt yok. Aboneliğinizi panelden tek tıkla iptal edebilirsiniz.",
},
{
q: "Mevcut e-ticaret veya servis sistemime entegre olur mu?",
a: "Full Paket'te API erişimi ve toplu sorgu ile sistemlerinize entegre edebilirsiniz (talep üzerine kurulum desteği).",
},
{
q: "Şase numarasını bilmiyorsam ne olur?",
a: "Marka ve model üzerinden de katalogda gezebilir, ya da ana sayfadaki örnek aramayı deneyerek platformu test edebilirsiniz.",
},
];
const TESTIMONIALS = [
{
name: "Ahmet Y.",
@@ -392,7 +419,7 @@ export function HomePage() {
usePageMeta({
title: "Şase Numarası Sorgulama & OEM Parça Kataloğu | Sase.tr",
description:
"Araç şase numarasını (VIN) girin, saniyeler içinde OEM parça kodlarına ulaşın. 50+ marka, 1M+ parça. Yanlış sipariş yapmadan doğru parçayı bulun.",
"Araç şase numarasını (VIN) girin, saniyeler içinde OEM parça kodlarına ulaşın. 27 marka, 1M+ parça. Yanlış sipariş yapmadan doğru parçayı bulun.",
canonical: "https://sase.tr",
});
@@ -799,7 +826,7 @@ export function HomePage() {
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-destructive/60 opacity-75" />
<span className="relative inline-flex size-2 rounded-full bg-destructive" />
</span>
Her yanlış parça siparişi ortalama 450 TL'ye mal olur
Her yanlış parça siparişi 200-500 TL'ye mal olur
</div>
{/* Social proof badge */}
@@ -956,7 +983,7 @@ export function HomePage() {
<div className="mt-8 flex flex-wrap items-center justify-center gap-4 sm:gap-6">
{[
{ icon: Zap, label: "Ortalama 1.2sn Sonuç" },
{ icon: Car, label: "50+ Marka Desteği" },
{ icon: Car, label: "27 Marka Desteği" },
{ icon: Database, label: "1M+ OEM Parça" },
].map(({ icon: Icon, label }) => (
<div
@@ -983,7 +1010,7 @@ export function HomePage() {
/>
) : (
<span className="text-sm text-muted-foreground">
Desteklenen 50+ Otomobil Markası
Desteklenen 27 Otomobil Markası
</span>
)}
</p>
@@ -1298,13 +1325,15 @@ export function HomePage() {
Özel entegrasyon veya toplu sorgulama ihtiyacınız varsa bize ulaşın.
</p>
</div>
<Button
variant="outline"
className="mt-4 rounded-full border-border text-muted-foreground hover:bg-muted hover:text-foreground"
>
Bize Ulaşın
<ChevronRight className="ml-1 size-4" />
</Button>
<Link to="/contact" className="mt-4">
<Button
variant="outline"
className="rounded-full border-border text-muted-foreground hover:bg-muted hover:text-foreground"
>
Bize Ulaşın
<ChevronRight className="ml-1 size-4" />
</Button>
</Link>
</div>
</div>
</div>
@@ -1577,6 +1606,7 @@ export function HomePage() {
{[
{
name: "1 Marka",
planKey: "brand1",
description: "Tek marka için erişim",
price: "200",
yearly: "2.000",
@@ -1589,6 +1619,7 @@ export function HomePage() {
},
{
name: "2 Marka",
planKey: "brand2",
description: "İki farklı marka",
price: "350",
yearly: "3.500",
@@ -1601,6 +1632,7 @@ export function HomePage() {
},
{
name: "3 Marka",
planKey: "brand3",
description: "Üç marka kapsamlı",
price: "500",
yearly: "5.000",
@@ -1611,10 +1643,10 @@ export function HomePage() {
"Öncelikli destek",
],
},
].map((plan, idx) => (
].map((plan) => (
<div
key={plan.name}
className={`group relative flex flex-col rounded-2xl border border-border bg-surface p-7 transition-all duration-300 hover:-translate-y-1 hover:border-foreground/20 hover:shadow-[var(--shadow-md)] ${idx === 1 ? "sm:translate-y-3" : ""} ${idx === 2 ? "sm:translate-y-6" : ""}`}
className="group relative flex flex-col rounded-2xl border border-border bg-surface p-7 transition-all duration-300 hover:-translate-y-1 hover:border-foreground/20 hover:shadow-[var(--shadow-md)]"
>
<div className="flex items-baseline justify-between">
<h3 className="font-[family-name:var(--font-display)] text-lg font-semibold">
@@ -1640,11 +1672,23 @@ export function HomePage() {
</li>
))}
</ul>
<Link to="/register" className="mt-7">
<Button variant="outline" className="w-full rounded-full">
30 gün ücretsiz dene
</Button>
</Link>
{isAuthenticated ? (
<Link
to="/dashboard/subscription"
search={{ plan: plan.planKey }}
className="mt-7"
>
<Button variant="outline" className="w-full rounded-full">
30 gün ücretsiz dene
</Button>
</Link>
) : (
<Link to="/register" search={{ plan: plan.planKey }} className="mt-7">
<Button variant="outline" className="w-full rounded-full">
30 gün ücretsiz dene
</Button>
</Link>
)}
</div>
))}
</div>
@@ -1680,7 +1724,11 @@ export function HomePage() {
yıllık 9.990 TL — ayda 832 TL'ye denk gelir
</p>
<Link to="/register" className="mt-8 inline-block">
<Link
to={isAuthenticated ? "/dashboard/subscription" : "/register"}
search={{ plan: "full" }}
className="mt-8 inline-block"
>
<Button variant="brand" size="lg" className="rounded-full">
30 gün ücretsiz dene
<ArrowRight className="ml-2 size-4" />
@@ -1698,7 +1746,7 @@ export function HomePage() {
</p>
<ul className="mt-5 grid grid-cols-1 gap-x-6 gap-y-3 sm:grid-cols-2">
{[
"Tüm 50+ markaya erişim",
"Tüm 27 markaya erişim",
"Sınırsız VIN arama",
"OEM parça kataloğu",
"İnteraktif şema görüntüleyici",
@@ -1730,32 +1778,27 @@ export function HomePage() {
</div>
</section>
{/* ─── 11. REFERRAL BANNER (YENİ) ───────────────────────────────── */}
<section className="px-4 py-10 sm:px-6">
<div className="mx-auto max-w-4xl overflow-hidden rounded-2xl border border-border bg-surface p-8 sm:p-12">
<div className="flex flex-col items-center gap-6 text-center sm:flex-row sm:text-left">
<div className="flex size-14 shrink-0 items-center justify-center rounded-xl bg-muted">
<Gift className="size-6 text-muted-foreground" />
</div>
<div className="flex-1">
<h3 className="font-[family-name:var(--font-display)] text-xl font-bold sm:text-2xl">
Arkadaşını Davet Et
</h3>
<p className="mt-2 text-sm text-muted-foreground">
3 davet = +7 gün ücretsiz, 5 davet = +1 ay ücretsiz
</p>
</div>
<Link to="/register">
<Button className="shrink-0 rounded-full bg-foreground text-background hover:bg-foreground/90">
Davet Kodunuzu Alın
<ArrowRight className="ml-2 size-4" />
</Button>
</Link>
{/* ─── 10.5. SSS / FAQ ──────────────────────────────────────────── */}
<section className="px-4 py-20 sm:px-6">
<div className="mx-auto max-w-4xl">
<div className="text-center">
<SectionBadge>SSS</SectionBadge>
<h2 className="mt-4 font-[family-name:var(--font-display)] text-3xl font-bold tracking-tight sm:text-4xl lg:text-5xl">
Sık Sorulan Sorular
</h2>
</div>
<dl className="mt-12 grid gap-x-10 gap-y-8 sm:grid-cols-2">
{FAQ.map((item) => (
<div key={item.q}>
<dt className="font-semibold text-foreground">{item.q}</dt>
<dd className="mt-2 text-sm leading-relaxed text-muted-foreground">{item.a}</dd>
</div>
))}
</dl>
</div>
</section>
{/* ─── 12. SON CTA ──────────────────────────────────────────────── */}
{/* ─── 11. SON CTA ──────────────────────────────────────────────── */}
<section className="relative overflow-hidden bg-surface-alt px-4 py-20 sm:px-6 sm:py-24">
{/* SVG grid pattern */}
<div className="pointer-events-none absolute inset-0 opacity-[0.03]">
@@ -1783,14 +1826,40 @@ export function HomePage() {
bulun.
</p>
<Link to="/register">
<Button
variant="outline"
className="mt-8 rounded-full border-border px-8 py-3 text-base text-foreground hover:bg-foreground hover:text-background"
>
Ücretsiz Deneyin Kart Gerekmez
<Button variant="brand" size="lg" className="mt-8 rounded-full px-8 text-base">
30 Gün Ücretsiz Başla Kart Gerekmez
<ArrowRight className="ml-2 size-4" />
</Button>
</Link>
<p className="mt-5 flex flex-wrap items-center justify-center gap-2 text-xs text-muted-foreground">
<ShieldCheck className="size-4 text-brand" />
KVKK uyumlu · SSL şifreli · Kart bilgisi gerekmez
</p>
</div>
</section>
{/* ─── 12. REFERRAL BANNER ──────────────────────────────────────── */}
<section className="px-4 py-10 sm:px-6">
<div className="mx-auto max-w-4xl overflow-hidden rounded-2xl border border-border bg-surface p-8 sm:p-12">
<div className="flex flex-col items-center gap-6 text-center sm:flex-row sm:text-left">
<div className="flex size-14 shrink-0 items-center justify-center rounded-xl bg-muted">
<Gift className="size-6 text-muted-foreground" />
</div>
<div className="flex-1">
<h3 className="font-[family-name:var(--font-display)] text-xl font-bold sm:text-2xl">
Arkadaşını Davet Et
</h3>
<p className="mt-2 text-sm text-muted-foreground">
3 davet = +7 gün ücretsiz, 5 davet = +1 ay ücretsiz
</p>
</div>
<Link to="/register">
<Button className="shrink-0 rounded-full bg-foreground text-background hover:bg-foreground/90">
Davet Kodunuzu Alın
<ArrowRight className="ml-2 size-4" />
</Button>
</Link>
</div>
</div>
</section>
</main>