dev #51
@@ -6,7 +6,7 @@
|
||||
<title>Şase Numarası Sorgulama & OEM Parça Kataloğu | Sase.tr</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Araç şase numarasını (VIN) girin, saniyeler içinde OEM parça kodlarına ulaşın. 27+ marka, 243.000+ parça. Yanlış sipariş yapmadan doğru parçayı bulun."
|
||||
content="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."
|
||||
/>
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
@@ -16,7 +16,7 @@
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://sase.tr" />
|
||||
<meta property="og:title" content="Şase Numarası Sorgulama & OEM Parça Kataloğu | Sase.tr" />
|
||||
<meta property="og:description" content="Araç şase numarasını girin, saniyeler içinde doğru OEM parça kodlarına ulaşın. 27+ marka, 243.000+ parça." />
|
||||
<meta property="og:description" content="Araç şase numarasını girin, saniyeler içinde doğru OEM parça kodlarına ulaşın. 50+ marka, 1M+ parça." />
|
||||
<meta property="og:image" content="https://sase.tr/og-image.png" />
|
||||
<meta property="og:locale" content="tr_TR" />
|
||||
<meta property="og:site_name" content="Sase.tr" />
|
||||
@@ -24,7 +24,7 @@
|
||||
<!-- Twitter Card -->
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="Şase Numarası Sorgulama & OEM Parça Kataloğu | Sase.tr" />
|
||||
<meta name="twitter:description" content="Araç şase numarasını girin, saniyeler içinde doğru OEM parça kodlarına ulaşın. 27+ marka, 243.000+ parça." />
|
||||
<meta name="twitter:description" content="Araç şase numarasını girin, saniyeler içinde doğru OEM parça kodlarına ulaşın. 50+ marka, 1M+ parça." />
|
||||
<meta name="twitter:image" content="https://sase.tr/og-image.png" />
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
|
||||
@@ -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. 27+ marka, 243.000+ 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. 50+ marka, 1M+ parça. Yanlış sipariş yapmadan doğru parçayı bulun.";
|
||||
const DEFAULT_CANONICAL = "https://sase.tr";
|
||||
|
||||
interface PageMetaOptions {
|
||||
|
||||
@@ -109,11 +109,11 @@ function AuthLayout() {
|
||||
<circle cx="7" cy="17" r="2" />
|
||||
<circle cx="17" cy="17" r="2" />
|
||||
</svg>
|
||||
27 Marka
|
||||
50+ 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" />
|
||||
<span className="tabular">243K+ OEM Parça</span>
|
||||
<span className="tabular">1M+ OEM Parça</span>
|
||||
</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">
|
||||
<ShieldCheck className="size-3" />
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { api } from "@/lib/api-client";
|
||||
import { signIn, signUp } from "@/lib/auth-client";
|
||||
import { startAction } from "@/lib/faro";
|
||||
import { track as trackMeta } from "@/lib/meta-pixel";
|
||||
@@ -7,7 +8,7 @@ import { Button } from "@sase/ui";
|
||||
import { Input } from "@sase/ui";
|
||||
import { Label } from "@sase/ui";
|
||||
import { Link, createFileRoute } from "@tanstack/react-router";
|
||||
import { ShieldCheck } from "lucide-react";
|
||||
import { Check, Eye, EyeOff, ShieldCheck } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
export const Route = createFileRoute("/_auth/register")({
|
||||
@@ -25,6 +26,13 @@ function RegisterPage() {
|
||||
const [password, setPassword] = useState("");
|
||||
const [refCode, setRefCode] = useState(ref || "");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [showRefCode, setShowRefCode] = useState(!!ref);
|
||||
const passwordRules = [
|
||||
{ ok: password.length >= 8, label: "8+ karakter" },
|
||||
{ ok: /[A-Z]/.test(password), label: "1 büyük harf" },
|
||||
{ ok: /[0-9]/.test(password), label: "1 rakam" },
|
||||
];
|
||||
// Land new users straight on the VIN search (value-first). The activation
|
||||
// "motion" + trial provisioning now runs as a modal on the search page
|
||||
// (see WelcomeOnboardingModal), so we carry welcome/vin/ref across.
|
||||
@@ -40,16 +48,40 @@ function RegisterPage() {
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
await signUp.email({ name, email, password, callbackURL: redirectUrl });
|
||||
capture("user_signed_up", { method: "email" });
|
||||
trackMeta("CompleteRegistration", { method: "email" });
|
||||
// better-auth returns { error } rather than throwing (see login.tsx).
|
||||
// The old code awaited without checking error, so failed signups still
|
||||
// reported success, fired user_signed_up, and redirected to the dashboard.
|
||||
const { error } = await signUp.email({ name, email, password, callbackURL: redirectUrl });
|
||||
|
||||
if (error) {
|
||||
let exists = false;
|
||||
try {
|
||||
({ exists } = await api.post<{ exists: boolean }>("/users/check-email", { email }));
|
||||
} catch {
|
||||
// fall through to a generic message
|
||||
}
|
||||
if (exists) {
|
||||
toast.error("Bu e-posta zaten kayıtlı", {
|
||||
description: "Bunun yerine giriş yapmayı deneyin.",
|
||||
});
|
||||
} else {
|
||||
toast.error("Kayıt başarısız", {
|
||||
description:
|
||||
error.message ?? "Şifre en az 8 karakter, 1 büyük harf ve 1 rakam içermelidir.",
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// The referral code travels via `?ref=` in redirectUrl; the welcome
|
||||
// onboarding modal on the search page is the single place that applies it
|
||||
// (covers both email and Google OAuth signups).
|
||||
capture("user_signed_up", { method: "email" });
|
||||
trackMeta("CompleteRegistration", { method: "email" });
|
||||
toast.success("Hesap oluşturuldu!");
|
||||
window.location.href = redirectUrl;
|
||||
} catch {
|
||||
toast.error("Kayıt başarısız. Bu e-posta zaten kullanılıyor olabilir.");
|
||||
toast.error("Bağlantı hatası", { description: "Lütfen tekrar deneyin." });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -137,38 +169,74 @@ function RegisterPage() {
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Şifre</Label>
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
placeholder="En az 8 karakter, 1 büyük harf, 1 rakam"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
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 className="relative">
|
||||
<Input
|
||||
id="password"
|
||||
type={showPassword ? "text" : "password"}
|
||||
placeholder="En az 8 karakter"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
minLength={8}
|
||||
className="pr-10"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPassword((v) => !v)}
|
||||
className="absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground transition-colors hover:text-foreground"
|
||||
aria-label={showPassword ? "Şifreyi gizle" : "Şifreyi göster"}
|
||||
>
|
||||
{showPassword ? <EyeOff className="size-4" /> : <Eye className="size-4" />}
|
||||
</button>
|
||||
</div>
|
||||
{password.length > 0 && (
|
||||
<ul className="flex flex-wrap gap-x-3 gap-y-1 text-xs">
|
||||
{passwordRules.map((rule) => (
|
||||
<li
|
||||
key={rule.label}
|
||||
className={`flex items-center gap-1 ${
|
||||
rule.ok ? "text-emerald-600 dark:text-emerald-400" : "text-muted-foreground"
|
||||
}`}
|
||||
>
|
||||
<Check className={`size-3 ${rule.ok ? "opacity-100" : "opacity-30"}`} />
|
||||
{rule.label}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
{showRefCode ? (
|
||||
<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="button"
|
||||
onClick={() => setShowRefCode(true)}
|
||||
className="text-sm text-muted-foreground transition-colors hover:text-foreground hover:underline"
|
||||
>
|
||||
Referans kodun var mı?
|
||||
</button>
|
||||
)}
|
||||
<Button type="submit" className="w-full" disabled={loading}>
|
||||
{loading ? "Kayıt yapılıyor..." : "Kayıt Ol"}
|
||||
{loading ? "Kayıt yapılıyor..." : "Ücretsiz Başla"}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
<p className="text-center text-xs text-muted-foreground">
|
||||
Kayıt olunca hemen VIN aramaya başlayın
|
||||
Kayıt olunca hemen VIN aramaya başlarsın · Spam göndermeyiz
|
||||
</p>
|
||||
|
||||
<p className="text-center text-sm">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { usePageMeta } from "@/hooks/use-page-meta";
|
||||
import { SiteHeader } from "@/components/site-header";
|
||||
import { useBlogPost } from "@/hooks/use-blog";
|
||||
import { usePageMeta } from "@/hooks/use-page-meta";
|
||||
import { Button } from "@sase/ui";
|
||||
import { Link, createFileRoute } from "@tanstack/react-router";
|
||||
import { ChevronRight } from "lucide-react";
|
||||
@@ -97,7 +97,7 @@ const POSTS: Record<string, BlogPost> = {
|
||||
|
||||
<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
|
||||
bilgilerine ve orijinal parça kataloğuna erişebilirsiniz. 50'den fazla markayı destekleyen
|
||||
platformumuzla yanlış parça siparişlerine son verin.
|
||||
</p>
|
||||
</div>
|
||||
@@ -252,8 +252,8 @@ 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 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
|
||||
görüntüleme özellikleriyle geleneksel iş yapış biçimlerini dönüştürmektedir. Platform, 50+
|
||||
marka ve 1 milyondan fazla OEM parça numarasıyla sektörün en kapsamlı dijital kataloğunu
|
||||
sunmaktadır.
|
||||
</p>
|
||||
</div>
|
||||
@@ -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>27+ markanın tamamına erişmek için Full Paket aboneliği en avantajlı seçenektir.</li>
|
||||
<li>50+ 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>
|
||||
@@ -355,18 +355,14 @@ const mdComponents = {
|
||||
h3: (props: React.ComponentProps<"h3">) => (
|
||||
<h3 className="text-lg font-semibold text-foreground" {...props} />
|
||||
),
|
||||
ul: (props: React.ComponentProps<"ul">) => (
|
||||
<ul className="list-disc space-y-2 pl-6" {...props} />
|
||||
),
|
||||
ul: (props: React.ComponentProps<"ul">) => <ul className="list-disc space-y-2 pl-6" {...props} />,
|
||||
ol: (props: React.ComponentProps<"ol">) => (
|
||||
<ol className="list-decimal space-y-2 pl-6" {...props} />
|
||||
),
|
||||
strong: (props: React.ComponentProps<"strong">) => (
|
||||
<strong className="text-foreground" {...props} />
|
||||
),
|
||||
a: (props: React.ComponentProps<"a">) => (
|
||||
<a className="text-foreground underline" {...props} />
|
||||
),
|
||||
a: (props: React.ComponentProps<"a">) => <a className="text-foreground underline" {...props} />,
|
||||
};
|
||||
|
||||
function MarkdownBody({ markdown }: { markdown: string }) {
|
||||
@@ -406,9 +402,7 @@ function BlogPostPage() {
|
||||
else if (isLoading) body = <p className="text-muted-foreground">Yükleniyor…</p>;
|
||||
else
|
||||
body = (
|
||||
<p className="text-muted-foreground">
|
||||
{isError ? "Yazı bulunamadı." : "Yazı yüklenemedi."}
|
||||
</p>
|
||||
<p className="text-muted-foreground">{isError ? "Yazı bulunamadı." : "Yazı yüklenemedi."}</p>
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -256,7 +256,7 @@ const BENTO_ITEMS = [
|
||||
},
|
||||
{
|
||||
icon: Car,
|
||||
title: "27+ Marka",
|
||||
title: "50+ Marka",
|
||||
description: "Avrupa, Asya ve Amerikan markaları tek çatı altında.",
|
||||
stat: null,
|
||||
wide: false,
|
||||
@@ -294,7 +294,7 @@ const BENTO_ITEMS = [
|
||||
const STATS = [
|
||||
{ value: "1.2sn", label: "Ortalama sorgu süresi" },
|
||||
{ value: "27", label: "Desteklenen marka" },
|
||||
{ value: "243K+", label: "OEM parça numarası" },
|
||||
{ value: "1M+", label: "OEM parça numarası" },
|
||||
{ value: "%99.9", label: "Platform erişilebilirlik" },
|
||||
];
|
||||
|
||||
@@ -385,7 +385,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. 27+ marka, 243.000+ 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. 50+ marka, 1M+ parça. Yanlış sipariş yapmadan doğru parçayı bulun.",
|
||||
canonical: "https://sase.tr",
|
||||
});
|
||||
|
||||
@@ -947,8 +947,8 @@ 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: "27 Marka Desteği" },
|
||||
{ icon: Database, label: "243.000+ OEM Parça" },
|
||||
{ icon: Car, label: "50+ Marka Desteği" },
|
||||
{ icon: Database, label: "1M+ OEM Parça" },
|
||||
].map(({ icon: Icon, label }) => (
|
||||
<div
|
||||
key={label}
|
||||
@@ -966,7 +966,7 @@ export function HomePage() {
|
||||
<section className="border-y border-border py-12">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6">
|
||||
<p className="mb-8 text-center text-sm text-muted-foreground">
|
||||
Desteklenen 27+ Otomobil Markası
|
||||
Desteklenen 50+ Otomobil Markası
|
||||
</p>
|
||||
</div>
|
||||
<div className="carousel-track relative overflow-hidden">
|
||||
@@ -1669,7 +1669,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 27+ markaya erişim",
|
||||
"Tüm 50+ markaya erişim",
|
||||
"Sınırsız VIN arama",
|
||||
"OEM parça kataloğu",
|
||||
"İnteraktif şema görüntüleyici",
|
||||
|
||||
Reference in New Issue
Block a user