feat: SEO altyapısı — pre-render, meta tags, blog posts, robots/sitemap
- vite-plugin-prerender yerine Playwright tabanlı post-build pre-render scripti (apps/web/scripts/prerender.mjs): 10 public route statik HTML'e dönüştürülüyor - usePageMeta hook: her public route'da özgün title/description/canonical/OG tags - index.html: favicon.svg, canonical, OG/Twitter Card, geliştirilmiş title+desc - apps/web/public/: robots.txt, sitemap.xml (10 URL), favicon.svg - nginx HSTS header eklendi (Strict-Transport-Security) - Blog post sayfaları: blog_/$slug.tsx (4 tam yazı, 600-800 kelime) - blog.tsx: post kartlarına Link eklendi - Homepage: Schema.org JSON-LD (Organization + SoftwareApplication) - Footer: placeholder href="#" sosyal linkler kaldırıldı - deploy.sh + deploy.yml: playwright install chromium + prerender adımı Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,24 +7,29 @@ import { signIn, signUp } from "@/lib/auth-client";
|
||||
import { startAction } from "@/lib/faro";
|
||||
import { capture } from "@/lib/posthog";
|
||||
import { toast } from "@/lib/toast";
|
||||
import { api } from "@/lib/api-client";
|
||||
import { ShieldCheck } from "lucide-react";
|
||||
|
||||
export const Route = createFileRoute("/_auth/register")({
|
||||
component: RegisterPage,
|
||||
validateSearch: (search: Record<string, unknown>): { vin?: string } => ({
|
||||
validateSearch: (search: Record<string, unknown>): { vin?: string; ref?: string } => ({
|
||||
vin: (search.vin as string) || undefined,
|
||||
ref: (search.ref as string) || undefined,
|
||||
}),
|
||||
});
|
||||
|
||||
function RegisterPage() {
|
||||
const { vin } = Route.useSearch();
|
||||
const { vin, ref } = Route.useSearch();
|
||||
const [name, setName] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [refCode, setRefCode] = useState(ref || "");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const redirectUrl = vin
|
||||
? `/dashboard/subscription?welcome=1&vin=${encodeURIComponent(vin)}`
|
||||
: "/dashboard/subscription?welcome=1";
|
||||
const redirectUrl = [
|
||||
"/dashboard/subscription?welcome=1",
|
||||
vin ? `&vin=${encodeURIComponent(vin)}` : "",
|
||||
refCode.trim() ? `&ref=${encodeURIComponent(refCode.trim().toUpperCase())}` : "",
|
||||
].join("");
|
||||
|
||||
async function handleSubmit(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
@@ -32,8 +37,15 @@ function RegisterPage() {
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
await signUp.email({ name, email, password });
|
||||
await signUp.email({ name, email, password, callbackURL: redirectUrl });
|
||||
capture("user_signed_up", { method: "email" });
|
||||
if (refCode.trim()) {
|
||||
try {
|
||||
await api.post("/referrals/apply", { code: refCode.trim().toUpperCase() });
|
||||
} catch {
|
||||
// Geçersiz/kullanılmış kod — sessizce geç
|
||||
}
|
||||
}
|
||||
toast.success("Hesap oluşturuldu!");
|
||||
window.location.href = redirectUrl;
|
||||
} catch {
|
||||
@@ -128,6 +140,21 @@ function RegisterPage() {
|
||||
minLength={8}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="refCode">
|
||||
Referans Kodu <span className="text-muted-foreground">(opsiyonel)</span>
|
||||
</Label>
|
||||
<Input
|
||||
id="refCode"
|
||||
type="text"
|
||||
placeholder="örn. MEU8EHF7"
|
||||
value={refCode}
|
||||
onChange={(e) => setRefCode(e.target.value.toUpperCase())}
|
||||
maxLength={20}
|
||||
autoComplete="off"
|
||||
className="font-mono uppercase"
|
||||
/>
|
||||
</div>
|
||||
<Button type="submit" className="w-full" disabled={loading}>
|
||||
{loading ? "Kayıt yapılıyor..." : "Kayıt Ol"}
|
||||
</Button>
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { Button } from "@sase/ui";
|
||||
import { usePageMeta } from "@/hooks/use-page-meta";
|
||||
|
||||
export const Route = createFileRoute("/about")({
|
||||
component: AboutPage,
|
||||
});
|
||||
|
||||
function AboutPage() {
|
||||
usePageMeta({
|
||||
title: "Hakkımızda — Sase.tr | Türkiye'nin Şase Sorgulama Platformu",
|
||||
description:
|
||||
"Sase.tr, Türkiye yedek parça sektörüne yönelik VIN/şase numarası sorgulama ve OEM parça kataloğu platformudur.",
|
||||
canonical: "https://sase.tr/about",
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<header className="border-b">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { Button } from "@sase/ui";
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@sase/ui";
|
||||
import { usePageMeta } from "@/hooks/use-page-meta";
|
||||
|
||||
export const Route = createFileRoute("/blog")({
|
||||
component: BlogPage,
|
||||
@@ -38,6 +39,12 @@ const posts = [
|
||||
];
|
||||
|
||||
function BlogPage() {
|
||||
usePageMeta({
|
||||
title: "Blog — Sase.tr | Şase & Yedek Parça Rehberi",
|
||||
description: "Şase numarası okuma, OEM vs muadil parça, dijital dönüşüm ve daha fazlası.",
|
||||
canonical: "https://sase.tr/blog",
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<header className="border-b">
|
||||
@@ -64,17 +71,19 @@ function BlogPage() {
|
||||
|
||||
<div className="mt-12 grid gap-6 sm:grid-cols-2">
|
||||
{posts.map((post) => (
|
||||
<Card key={post.slug}>
|
||||
<CardHeader>
|
||||
<CardDescription>{post.date}</CardDescription>
|
||||
<CardTitle className="text-lg">{post.title}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{post.description}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Link key={post.slug} to="/blog/$slug" params={{ slug: post.slug }}>
|
||||
<Card className="h-full transition hover:border-foreground/20">
|
||||
<CardHeader>
|
||||
<CardDescription>{post.date}</CardDescription>
|
||||
<CardTitle className="text-lg">{post.title}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{post.description}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
406
apps/web/src/routes/blog_/$slug.tsx
Normal file
406
apps/web/src/routes/blog_/$slug.tsx
Normal file
@@ -0,0 +1,406 @@
|
||||
import { createFileRoute, Link, notFound } from "@tanstack/react-router";
|
||||
import { Button } from "@sase/ui";
|
||||
import { ChevronRight } from "lucide-react";
|
||||
import { usePageMeta } from "@/hooks/use-page-meta";
|
||||
|
||||
// ─── BLOG POST DATA ───────────────────────────────────────────────────────────
|
||||
|
||||
interface BlogPost {
|
||||
slug: string;
|
||||
title: string;
|
||||
description: string;
|
||||
date: string;
|
||||
content: React.ReactNode;
|
||||
}
|
||||
|
||||
const POSTS: Record<string, BlogPost> = {
|
||||
"vin-nedir-nasil-okunur": {
|
||||
slug: "vin-nedir-nasil-okunur",
|
||||
title: "Şase Numarası (VIN) Nedir? Nasıl Okunur?",
|
||||
description:
|
||||
"17 haneli VIN kodunun her bir bölümünün ne anlama geldiğini, araç geçmişini nasıl ortaya çıkarabileceğinizi adım adım anlatıyoruz.",
|
||||
date: "2026-02-10",
|
||||
content: (
|
||||
<div className="space-y-6 leading-relaxed text-muted-foreground">
|
||||
<p>
|
||||
VIN (Vehicle Identification Number), yani Araç Tanımlama Numarası, her motorlu taşıta üretim
|
||||
aşamasında atanan 17 karakterlik benzersiz bir koddur. Türkiye'de "şase numarası" olarak da
|
||||
bilinen bu kod, aracın üretiminden imhasına kadar tüm yaşam döngüsünü takip etmeye yarar.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">VIN Nereden Okunur?</h2>
|
||||
<p>
|
||||
VIN numarasına birçok yerden ulaşabilirsiniz: ön camın sol alt köşesindeki plaka, sürücü
|
||||
kapısının iç kısmındaki etiket, motor bölmesi veya araç ruhsatı ve fatura bunların
|
||||
başında gelir. Bazı araçlarda bagaj kapısı iç kısmında da bulunur.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">17 Karakterin Anlamı</h2>
|
||||
<p>VIN üç ana bölüme ayrılır:</p>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>
|
||||
<strong className="text-foreground">WMI (1-3. karakterler) — Dünya Üretici Kodu:</strong>{" "}
|
||||
Aracın hangi ülkede ve hangi fabrikada üretildiğini gösterir. Örneğin "WVW" Volkswagen
|
||||
Almanya, "ZFA" Fiat İtalya demektir.
|
||||
</li>
|
||||
<li>
|
||||
<strong className="text-foreground">VDS (4-9. karakterler) — Araç Tanımlayıcı Bölüm:</strong>{" "}
|
||||
Model, kasa tipi, motor hacmi, yakıt türü ve güvenlik donanımları hakkında bilgi içerir.
|
||||
9. karakter her zaman kontrol karakteridir ve matematiksel bir doğrulama amacı taşır.
|
||||
</li>
|
||||
<li>
|
||||
<strong className="text-foreground">VIS (10-17. karakterler) — Araç Tanımlama Bölümü:</strong>{" "}
|
||||
Model yılı (10. karakter), üretim fabrikası (11. karakter) ve üretim sıra numarası
|
||||
(12-17. karakterler) bilgilerini içerir.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">Örnek: WVWZZZ1JZ3W597935</h2>
|
||||
<p>
|
||||
Bu VIN'i inceleyelim: <strong className="font-mono text-foreground">WVW</strong> — Volkswagen
|
||||
Almanya, <strong className="font-mono text-foreground">ZZZ</strong> — pazar tanımlayıcı,{" "}
|
||||
<strong className="font-mono text-foreground">1J</strong> — Golf modeli,{" "}
|
||||
<strong className="font-mono text-foreground">Z</strong> — motor tipi,{" "}
|
||||
<strong className="font-mono text-foreground">3</strong> — model yılı 2003,{" "}
|
||||
<strong className="font-mono text-foreground">W</strong> — Wolfsburg fabrikası,{" "}
|
||||
<strong className="font-mono text-foreground">597935</strong> — sıra numarası.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">VIN ile Ne Öğrenebilirsiniz?</h2>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>Aracın üretim yılı ve fabrikası</li>
|
||||
<li>Motor hacmi, yakıt türü ve şanzıman tipi</li>
|
||||
<li>Kasa tipi ve donanım paketi</li>
|
||||
<li>Kaza ve hasar geçmişi (resmi kayıtlarda)</li>
|
||||
<li>Kilometre manipülasyonu şüphesi</li>
|
||||
<li>Orijinal parça uyumluluğu</li>
|
||||
</ul>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">VIN ile Doğru Parça Bulma</h2>
|
||||
<p>
|
||||
VIN'in en kritik kullanım alanlarından biri yedek parça aramadır. Aynı model arabada bile
|
||||
üretim yılı, motor tipi veya donanım paketine göre farklı parçalar kullanılmış olabilir.
|
||||
VIN numarasıyla sorgulama yaparak yalnızca aracınıza tam uyumlu OEM parçalara ulaşabilir,
|
||||
yanlış parça sipariş etme riskini sıfıra indirebilirsiniz.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Sase.tr platformunda VIN numaranızı girerek saniyeler içinde aracınızın tam teknik
|
||||
bilgilerine ve orijinal parça kataloğuna erişebilirsiniz. 27'den fazla markayı destekleyen
|
||||
platformumuzla yanlış parça siparişlerine son verin.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
|
||||
"orijinal-mi-muadil-mi": {
|
||||
slug: "orijinal-mi-muadil-mi",
|
||||
title: "Orijinal Parça mı, Muadil Parça mı?",
|
||||
description:
|
||||
"OEM ve aftermarket parçalar arasındaki farkları, avantaj ve dezavantajlarını karşılaştırmalı olarak inceliyoruz.",
|
||||
date: "2026-01-28",
|
||||
content: (
|
||||
<div className="space-y-6 leading-relaxed text-muted-foreground">
|
||||
<p>
|
||||
Araç bakımı ve onarımında en sık sorulan sorulardan biri şudur: "Orijinal mi alsam, muadil
|
||||
mi?" Bu soru, hem maliyet hem de güvenlik açısından kritik öneme sahiptir. İki seçeneği
|
||||
tüm boyutlarıyla inceleyelim.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">OEM Parça Nedir?</h2>
|
||||
<p>
|
||||
OEM (Original Equipment Manufacturer), aracın üretiminde kullanılan ya da araç üreticisinin
|
||||
onayladığı orijinal parçalardır. Bu parçalar araç fabrikasında kullanılan parçalarla aynı
|
||||
spesifikasyonlara sahiptir ve genellikle aynı tedarikçilerden gelir. Üzerinde araç markasının
|
||||
logosu bulunabilir ya da yalnızca parça numarasıyla satılabilir.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">Aftermarket (Muadil) Parça Nedir?</h2>
|
||||
<p>
|
||||
Muadil parçalar, araç üreticisinden bağımsız üçüncü taraf firmalar tarafından üretilir.
|
||||
Kalite seviyeleri geniş bir yelpazede değişir: premium muadil parçalar OEM kalitesine
|
||||
yaklaşırken, düşük kaliteli kopya ürünler araç güvenliğini tehlikeye atabilir.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">OEM Parçaların Avantajları</h2>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>Araca tam uyum garantisi — montaj sorunu yaşanmaz</li>
|
||||
<li>Fabrika spesifikasyonlarına uygunluk</li>
|
||||
<li>Araç garantisini etkilemez</li>
|
||||
<li>Standart kalite tutarlılığı</li>
|
||||
<li>Orijinal parça numarasıyla kolay temin</li>
|
||||
</ul>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">OEM Parçaların Dezavantajları</h2>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>Genellikle muadile göre %20-50 daha pahalı</li>
|
||||
<li>Her markada ve her bölgede stok bulunmayabilir</li>
|
||||
<li>Bazı modeller için yüksek bekleme süresi</li>
|
||||
</ul>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">Muadil Parçaların Avantajları</h2>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>Genellikle daha düşük fiyat</li>
|
||||
<li>Daha kolay erişilebilirlik</li>
|
||||
<li>Bazı premium muadil markalar üstün performans sunar</li>
|
||||
<li>Üretimi durdurulmuş eski araçlarda tek seçenek olabilir</li>
|
||||
</ul>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">Muadil Parçaların Riskleri</h2>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>Kalite tutarsızlığı — marka ve fiyata göre büyük farklar</li>
|
||||
<li>Uyumsuzluk riski — görünürde aynı ama teknik farklılıklar olabilir</li>
|
||||
<li>Araç garantisini geçersiz kılabilir</li>
|
||||
<li>Güvenlik sistemlerini olumsuz etkileyebilir</li>
|
||||
</ul>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">Hangi Durumlarda Orijinal, Hangisinde Muadil?</h2>
|
||||
<p>
|
||||
Fren sistemi, hava yastığı, direksiyon ve motor parçaları gibi güvenlik kritik
|
||||
bileşenlerde kesinlikle OEM tercih edilmelidir. Kaporta, iç döşeme veya aksesuar
|
||||
niteliğindeki parçalarda güvenilir bir muadil marka değerlendirilebilir.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">Doğru Parçayı Nasıl Bulursunuz?</h2>
|
||||
<p>
|
||||
İster OEM ister muadil tercih edin, en önemli adım doğru OEM parça numarasını bilmektir.
|
||||
Sase.tr ile araç VIN numaranızdan yola çıkarak orijinal parça kodlarına ulaşın. Bu kodu
|
||||
elinizde bulundurmak, hem servisle hem de tedarikçiyle iletişimi kolaylaştırır, yanlış
|
||||
parça siparişini önler.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
|
||||
"dijital-donusum": {
|
||||
slug: "dijital-donusum",
|
||||
title: "Yedek Parça Aramada Dijital Dönüşüm",
|
||||
description:
|
||||
"Geleneksel katalog yöntemlerinden dijital platformlara geçiş sürecini ve sektöre etkilerini ele alıyoruz.",
|
||||
date: "2026-01-15",
|
||||
content: (
|
||||
<div className="space-y-6 leading-relaxed text-muted-foreground">
|
||||
<p>
|
||||
Türkiye'nin otomotiv yedek parça sektörü onlarca yıldır basılı kataloglar, telefon
|
||||
görüşmeleri ve tecrübeye dayalı bilgiyle ayakta durdu. Ancak 2020'lerin ortasına
|
||||
gelindiğinde bu yöntemler artık yetersiz kalmaktadır. Dijital dönüşüm, sektörde kaçınılmaz
|
||||
bir zorunluluk hâline gelmiştir.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">Geleneksel Yöntemlerin Sorunları</h2>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>
|
||||
<strong className="text-foreground">Zaman kaybı:</strong> Tek bir parça için birden fazla
|
||||
katalog taramak ortalama 15-20 dakika sürebilir.
|
||||
</li>
|
||||
<li>
|
||||
<strong className="text-foreground">Hata oranı:</strong> Elle yapılan arama ve karşılaştırma
|
||||
işlemlerinde yanlış parça sipariş riski yüksektir.
|
||||
</li>
|
||||
<li>
|
||||
<strong className="text-foreground">Güncellik sorunu:</strong> Basılı kataloglar yeni
|
||||
model güncellemelerini yavaş takip eder.
|
||||
</li>
|
||||
<li>
|
||||
<strong className="text-foreground">Ölçeklenemezlik:</strong> Büyüyen filo veya artan
|
||||
müşteri sayısıyla geleneksel yöntemler çökmektedir.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">Dijital Dönüşümün Faydaları</h2>
|
||||
<p>
|
||||
Dijital platformlar, yedek parça arama sürecini kökten değiştirmektedir:
|
||||
</p>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>VIN bazlı anlık araç tanımlama — saniyeler içinde doğru araç tespiti</li>
|
||||
<li>Çoklu katalog çapraz sorgulama — tek arayüzde birden fazla kaynak</li>
|
||||
<li>Otomatik parça eşleştirme — insan hatasını minimuma indirir</li>
|
||||
<li>Gerçek zamanlı fiyat karşılaştırma</li>
|
||||
<li>Sipariş geçmişi ve parça takibi</li>
|
||||
</ul>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">Sektörde Sayısal Dönüşüm</h2>
|
||||
<p>
|
||||
Dijital platforma geçiş yapan işletmelerin deneyimlerine göre:
|
||||
</p>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>Parça arama süresi ortalama %85 kısalmaktadır</li>
|
||||
<li>Yanlış parça iade oranları %60-80 düşmektedir</li>
|
||||
<li>Müşteri memnuniyeti belirgin biçimde artmaktadır</li>
|
||||
<li>Personel kapasitesi daha katma değerli işlere yönlendirilebilmektedir</li>
|
||||
</ul>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">Küçük ve Orta Ölçekli İşletmeler İçin Fırsatlar</h2>
|
||||
<p>
|
||||
Dijital dönüşüm artık yalnızca büyük zincirlerin ayrıcalığı değildir. Aylık sabit maliyetli
|
||||
abonelik modelleri sayesinde küçük oto yedek parçacılar ve servisler de kurumsal araçlara
|
||||
erişebilmektedir. Bu durum, rekabet eşitliğini kısmen sağlamaktadır.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">Sase.tr'nin Rolü</h2>
|
||||
<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 iş yapış biçimlerini dönüştürmektedir.
|
||||
Platform, 27+ marka ve 243.000'den fazla OEM parça numarasıyla sektörün en kapsamlı
|
||||
dijital kataloğunu sunmaktadır.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
|
||||
"dogru-parcayi-bulun": {
|
||||
slug: "dogru-parcayi-bulun",
|
||||
title: "Sase.tr ile Doğru Parçayı İlk Seferde Bulun",
|
||||
description:
|
||||
"Platform özelliklerini kullanarak VIN bazlı arama, şema görüntüleme ve parça eşleştirme rehberi.",
|
||||
date: "2026-01-05",
|
||||
content: (
|
||||
<div className="space-y-6 leading-relaxed text-muted-foreground">
|
||||
<p>
|
||||
Yanlış parça sipariş etmek hem zaman hem para kaybettirdiği gibi, müşteri memnuniyetini
|
||||
de olumsuz etkiler. Sase.tr, bu sorunu kökten çözmek için tasarlanmıştır. Bu rehberde
|
||||
platformu nasıl en verimli şekilde kullanacağınızı adım adım anlatıyoruz.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">Adım 1: VIN Numarasını Girin</h2>
|
||||
<p>
|
||||
Ana sayfadaki arama kutusuna aracın 17 haneli şase numarasını (VIN) girin. Numara
|
||||
girilirken anlık doğrulama çubuğu dolmaya başlar — 17. karaktere ulaştığınızda sistem
|
||||
otomatik olarak araç bilgilerini çeker.
|
||||
</p>
|
||||
<p>
|
||||
Sistem saniyeler içinde araç markasını, modelini, yılını ve motor bilgilerini gösterir.
|
||||
Bilgileri doğrulayın ve "Parça Kataloğuna Devam Et" butonuna tıklayın.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">Adım 2: Kategori Seçin</h2>
|
||||
<p>
|
||||
Araç tanımlandıktan sonra karşınıza o araca özgü parça kategorileri çıkar. Motor,
|
||||
Şasi & Süspansiyon, Elektrik, Karoseri, Klima & Isıtma gibi ana kategorilerden
|
||||
ihtiyacınız olan bölümü seçin.
|
||||
</p>
|
||||
<p>
|
||||
Her ana kategorinin altında detaylı alt kategoriler bulunur. Örneğin "Motor" altında
|
||||
Silindir Kapağı, Krank Mili, Piston, Yağ Pompası gibi bölümler yer alır.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">Adım 3: İnteraktif Şemayı Kullanın</h2>
|
||||
<p>
|
||||
Kategori seçildikten sonra o bölgеnin teknik çizimi — interaktif şema — ekranda açılır.
|
||||
Şema üzerindeki parçalara tıklayarak OEM kodunu, açıklamasını ve gerekli miktarını
|
||||
görebilirsiniz.
|
||||
</p>
|
||||
<p>
|
||||
Şemalar zoom ve pan desteğiyle büyütülebilir, yatay veya dikey kaydırılabilir.
|
||||
Karmaşık motor veya şasi bölgelerinde parça konumunu görsel olarak tespit etmek
|
||||
iade oranlarını ciddi ölçüde düşürmektedir.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">Adım 4: OEM Kodunu Kopyalayın</h2>
|
||||
<p>
|
||||
Doğru parçayı bulduktan sonra OEM kodunu panoya kopyalayın. Bu kodu tedarikçinize,
|
||||
servis faturanıza veya online sipariş formunuza yapıştırarak yanlış parça riskini
|
||||
tamamen ortadan kaldırın.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-semibold text-foreground">İpuçları</h2>
|
||||
<ul className="list-disc space-y-2 pl-6">
|
||||
<li>
|
||||
Birden fazla araç için sorgu yapıyorsanız, her birini VIN ile kaydedin; geçmiş
|
||||
aramalarınıza hızla dönebilirsiniz.
|
||||
</li>
|
||||
<li>
|
||||
Aynı OEM kodu birden fazla katalogda farklı fiyatlarla listelenebilir — fiyat
|
||||
karşılaştırma özelliğini kullanın.
|
||||
</li>
|
||||
<li>
|
||||
Parça bulamadığınızda kategori ağacında bir seviye yukarı çıkarak daha geniş
|
||||
bir arama yapabilirsiniz.
|
||||
</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>
|
||||
<p>
|
||||
Sase.tr ile tek bir yanlış parça iadesinden tasarruf ettiğiniz para, aylık abonelik
|
||||
ücretini karşılar. 7 günlük ücretsiz deneme süresiyle platformu bugün deneyin —
|
||||
kredi kartı gerektirmez.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
// ─── ROUTE ───────────────────────────────────────────────────────────────────
|
||||
|
||||
export const Route = createFileRoute("/blog_/$slug")({
|
||||
component: BlogPostPage,
|
||||
beforeLoad: ({ params }) => {
|
||||
if (!POSTS[params.slug]) {
|
||||
throw notFound();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
function BlogPostPage() {
|
||||
const { slug } = Route.useParams();
|
||||
const post = POSTS[slug];
|
||||
|
||||
usePageMeta({
|
||||
title: `${post.title} | Sase.tr Blog`,
|
||||
description: post.description,
|
||||
canonical: `https://sase.tr/blog/${post.slug}`,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<header className="border-b">
|
||||
<div className="container mx-auto flex h-16 items-center justify-between px-4">
|
||||
<Link to="/" className="text-xl font-bold">
|
||||
Sase.tr
|
||||
</Link>
|
||||
<div className="flex items-center gap-4">
|
||||
<Link to="/login">
|
||||
<Button variant="ghost">Giriş Yap</Button>
|
||||
</Link>
|
||||
<Link to="/register">
|
||||
<Button>Kayıt Ol</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="container mx-auto max-w-3xl px-4 py-16">
|
||||
{/* Breadcrumb */}
|
||||
<nav className="mb-8 flex items-center gap-1.5 text-sm text-muted-foreground">
|
||||
<Link to="/blog" className="transition hover:text-foreground">
|
||||
Blog
|
||||
</Link>
|
||||
<ChevronRight className="size-3.5" />
|
||||
<span className="text-foreground">{post.title}</span>
|
||||
</nav>
|
||||
|
||||
<article>
|
||||
<time className="text-sm text-muted-foreground">{post.date}</time>
|
||||
<h1 className="mt-3 text-3xl font-bold leading-tight sm:text-4xl">{post.title}</h1>
|
||||
<p className="mt-4 text-lg text-muted-foreground">{post.description}</p>
|
||||
|
||||
<div className="mt-10">{post.content}</div>
|
||||
</article>
|
||||
|
||||
{/* Back link */}
|
||||
<div className="mt-16 border-t pt-8">
|
||||
<Link
|
||||
to="/blog"
|
||||
className="inline-flex items-center gap-1.5 text-sm text-muted-foreground transition hover:text-foreground"
|
||||
>
|
||||
← Tüm yazılar
|
||||
</Link>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,12 +1,19 @@
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { Button } from "@sase/ui";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@sase/ui";
|
||||
import { usePageMeta } from "@/hooks/use-page-meta";
|
||||
|
||||
export const Route = createFileRoute("/contact")({
|
||||
component: ContactPage,
|
||||
});
|
||||
|
||||
function ContactPage() {
|
||||
usePageMeta({
|
||||
title: "İletişim — Sase.tr",
|
||||
description: "Sase.tr destek ve iletişim. info@sase.tr",
|
||||
canonical: "https://sase.tr/contact",
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<header className="border-b">
|
||||
|
||||
@@ -123,6 +123,11 @@ function SubscriptionPage() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
return params.get("welcome") === "1";
|
||||
});
|
||||
const [initialRef] = useState(() => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
return params.get("ref") || null;
|
||||
});
|
||||
const hasAppliedRefRef = useRef(false);
|
||||
const [selectedPlanKey, setSelectedPlanKey] = useState<string | null>(null);
|
||||
const [selectedBrandIds, setSelectedBrandIds] = useState<string[]>([]);
|
||||
const [billingPeriod, setBillingPeriod] = useState<"monthly" | "yearly">("monthly");
|
||||
@@ -160,6 +165,13 @@ function SubscriptionPage() {
|
||||
}
|
||||
}, [subscription?.status, subscription?.plan?.key]);
|
||||
|
||||
// Apply referral code from Google OAuth callback
|
||||
useEffect(() => {
|
||||
if (!initialRef || !subData || hasAppliedRefRef.current) return;
|
||||
hasAppliedRefRef.current = true;
|
||||
api.post("/referrals/apply", { code: initialRef.toUpperCase().trim() }).catch(() => {});
|
||||
}, [initialRef, subData]);
|
||||
|
||||
const cancelMutation = useMutation({
|
||||
mutationFn: () => api.patch("/subscriptions/cancel"),
|
||||
onSuccess: () => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { lazy, Suspense } from "react";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
||||
import { useCategoryParts } from "@/hooks/use-parts";
|
||||
import { CategoryGrid } from "@/components/categories/category-grid";
|
||||
import { Button } from "@sase/ui";
|
||||
import { Skeleton } from "@sase/ui";
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
@@ -27,14 +28,41 @@ function SchemaViewerFallback() {
|
||||
);
|
||||
}
|
||||
|
||||
function CategoryGridFallback() {
|
||||
return (
|
||||
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{Array.from({ length: 6 }).map((_, i) => (
|
||||
<Skeleton key={`cat-grid-skel-${i}`} className="h-24 w-full rounded-lg" />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const Route = createFileRoute("/dashboard/vehicles_/$id/categories_/$categoryId")({
|
||||
component: VehicleCategoryPage,
|
||||
});
|
||||
|
||||
function VehicleCategoryPage() {
|
||||
const { id, categoryId } = Route.useParams();
|
||||
const navigate = useNavigate();
|
||||
const { data, isLoading, error } = useCategoryParts(id, categoryId);
|
||||
|
||||
const hasChildren = data?.children && data.children.length > 0;
|
||||
|
||||
const handleBack = () => {
|
||||
if (data?.parentId) {
|
||||
navigate({
|
||||
to: "/dashboard/vehicles/$id/categories/$categoryId",
|
||||
params: { id, categoryId: data.parentId },
|
||||
});
|
||||
} else {
|
||||
navigate({
|
||||
to: "/dashboard/vehicles/$id",
|
||||
params: { id },
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* Header */}
|
||||
@@ -42,7 +70,7 @@ function VehicleCategoryPage() {
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => window.history.back()}
|
||||
onClick={handleBack}
|
||||
title="Geri don"
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
@@ -66,17 +94,32 @@ function VehicleCategoryPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Schema Viewer */}
|
||||
<Suspense fallback={<SchemaViewerFallback />}>
|
||||
<SchemaViewer
|
||||
schemaPic={data?.schemaPics?.[0] ?? null}
|
||||
hotspots={data?.hotspots ?? []}
|
||||
parts={data?.parts ?? []}
|
||||
isLoading={isLoading}
|
||||
{/* Loading state */}
|
||||
{isLoading && !data && (
|
||||
<CategoryGridFallback />
|
||||
)}
|
||||
|
||||
{/* Parent category — show children grid */}
|
||||
{hasChildren && (
|
||||
<CategoryGrid
|
||||
categories={data.children!}
|
||||
vehicleId={id}
|
||||
categoryId={categoryId}
|
||||
/>
|
||||
</Suspense>
|
||||
)}
|
||||
|
||||
{/* Leaf category — show schema viewer */}
|
||||
{data && !hasChildren && (
|
||||
<Suspense fallback={<SchemaViewerFallback />}>
|
||||
<SchemaViewer
|
||||
schemaPic={data.schemaPics?.[0] ?? null}
|
||||
hotspots={data.hotspots ?? []}
|
||||
parts={data.parts ?? []}
|
||||
isLoading={isLoading}
|
||||
vehicleId={id}
|
||||
categoryId={categoryId}
|
||||
/>
|
||||
</Suspense>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { Button, Input } from "@sase/ui";
|
||||
import { usePageMeta } from "@/hooks/use-page-meta";
|
||||
import {
|
||||
Search,
|
||||
Car,
|
||||
@@ -34,6 +35,12 @@ const EXAMPLE_SCHEMA_PARTS = [
|
||||
];
|
||||
|
||||
function DemoPage() {
|
||||
usePageMeta({
|
||||
title: "Demo — Sase.tr | Şase Sorgulamayı Deneyin",
|
||||
description: "Ücretsiz demo ile şase numarası sorgulama ve OEM parça kataloğunu keşfedin.",
|
||||
canonical: "https://sase.tr/demo",
|
||||
});
|
||||
|
||||
const [vin, setVin] = useState("");
|
||||
const [vinPreview, setVinPreview] = useState<{
|
||||
make: string;
|
||||
|
||||
@@ -31,6 +31,7 @@ import { DashboardDemo } from "@/remotion/DashboardDemo";
|
||||
import { EcommerceDemo } from "@/remotion/EcommerceDemo";
|
||||
import { getUserSettings, setUserSetting } from "@/lib/user-settings";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { usePageMeta } from "@/hooks/use-page-meta";
|
||||
import { useAuthStore } from "@/stores/auth.store";
|
||||
import { api, ApiError } from "@/lib/api-client";
|
||||
import { toast } from "@/lib/toast";
|
||||
@@ -334,6 +335,53 @@ export const Route = createFileRoute("/")({
|
||||
});
|
||||
|
||||
function HomePage() {
|
||||
usePageMeta({
|
||||
title: "Şase Numarası Sorgulama & OEM Parça Kataloğu | Sase.tr",
|
||||
description:
|
||||
"Araç şase numarasını girin, saniyeler içinde doğru OEM parça kodlarına ulaşın. 27+ marka, 243.000+ parça.",
|
||||
canonical: "https://sase.tr",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const schemas = [
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Organization",
|
||||
name: "Sase.tr",
|
||||
url: "https://sase.tr",
|
||||
email: "info@sase.tr",
|
||||
description:
|
||||
"Türkiye'nin VIN/şase numarası sorgulama ve OEM yedek parça kataloğu platformu.",
|
||||
},
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "SoftwareApplication",
|
||||
name: "Sase.tr",
|
||||
applicationCategory: "BusinessApplication",
|
||||
operatingSystem: "Web",
|
||||
offers: {
|
||||
"@type": "Offer",
|
||||
price: "200",
|
||||
priceCurrency: "TRY",
|
||||
},
|
||||
description: "Araç şase numarası ile OEM yedek parça sorgulama platformu.",
|
||||
},
|
||||
];
|
||||
|
||||
const scripts = schemas.map((schema) => {
|
||||
const el = document.createElement("script");
|
||||
el.type = "application/ld+json";
|
||||
el.textContent = JSON.stringify(schema);
|
||||
el.dataset.seoSchema = "true";
|
||||
document.head.appendChild(el);
|
||||
return el;
|
||||
});
|
||||
|
||||
return () => {
|
||||
scripts.forEach((el) => el.remove());
|
||||
};
|
||||
}, []);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const { isAuthenticated } = useAuth();
|
||||
const [vin, setVin] = useState("");
|
||||
@@ -1519,16 +1567,11 @@ function HomePage() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4 text-muted-foreground/70">
|
||||
{/* Social links - minimal */}
|
||||
<a href="#" className="transition hover:text-foreground">
|
||||
<svg className="size-5" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
|
||||
</svg>
|
||||
</a>
|
||||
<a href="#" className="transition hover:text-foreground">
|
||||
<svg className="size-5" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
|
||||
</svg>
|
||||
<a
|
||||
href="mailto:info@sase.tr"
|
||||
className="text-sm transition hover:text-foreground"
|
||||
>
|
||||
info@sase.tr
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { Button } from "@sase/ui";
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@sase/ui";
|
||||
import { Badge } from "@sase/ui";
|
||||
import { usePageMeta } from "@/hooks/use-page-meta";
|
||||
|
||||
export const Route = createFileRoute("/pricing")({
|
||||
component: PricingPage,
|
||||
@@ -59,6 +60,13 @@ const plans = [
|
||||
];
|
||||
|
||||
function PricingPage() {
|
||||
usePageMeta({
|
||||
title: "Fiyatlandırma — Sase.tr | Şase Sorgulama Planları",
|
||||
description:
|
||||
"200 TL/ay'dan başlayan şase numarası ve OEM parça sorgulama planları. 7 gün ücretsiz deneyin.",
|
||||
canonical: "https://sase.tr/pricing",
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<header className="border-b">
|
||||
|
||||
Reference in New Issue
Block a user