Compare commits
2 Commits
blog-direc
...
blog-cms-a
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e67f30d6d | |||
| 25ffec5789 |
@@ -4,6 +4,7 @@ import {
|
||||
Controller,
|
||||
Get,
|
||||
Headers,
|
||||
HttpCode,
|
||||
Param,
|
||||
Post,
|
||||
ServiceUnavailableException,
|
||||
@@ -13,6 +14,25 @@ import { Public } from "../common/decorators/public.decorator";
|
||||
import type { CreateBlogPost } from "./blog.dto";
|
||||
import { BlogService } from "./blog.service";
|
||||
|
||||
// Bearer-token check shared by the automation endpoints (n8n pipeline and the
|
||||
// Directus cache-purge Flow). Mirrors the changelog/internal token pattern.
|
||||
function assertAutomationToken(authHeader: string | undefined): void {
|
||||
const token = process.env.BLOG_AUTOMATION_TOKEN;
|
||||
if (!token) {
|
||||
throw new ServiceUnavailableException("Blog automation endpoint is not configured");
|
||||
}
|
||||
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
||||
throw new UnauthorizedException("Missing or invalid Authorization header");
|
||||
}
|
||||
const provided = authHeader.slice(7);
|
||||
if (
|
||||
provided.length !== token.length ||
|
||||
!timingSafeEqual(Buffer.from(provided), Buffer.from(token))
|
||||
) {
|
||||
throw new UnauthorizedException("Invalid token");
|
||||
}
|
||||
}
|
||||
|
||||
@Controller("blog")
|
||||
export class BlogController {
|
||||
constructor(private readonly blogService: BlogService) {}
|
||||
@@ -30,30 +50,27 @@ export class BlogController {
|
||||
}
|
||||
|
||||
// Automation endpoint — published by the Süper Panel content pipeline via n8n.
|
||||
// Mirrors the changelog/internal Bearer-token pattern.
|
||||
@Post("posts/internal")
|
||||
@Public()
|
||||
async createInternal(
|
||||
@Headers("authorization") authHeader: string,
|
||||
@Body() body: CreateBlogPost,
|
||||
) {
|
||||
const token = process.env.BLOG_AUTOMATION_TOKEN;
|
||||
if (!token) {
|
||||
throw new ServiceUnavailableException("Blog automation endpoint is not configured");
|
||||
}
|
||||
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
||||
throw new UnauthorizedException("Missing or invalid Authorization header");
|
||||
}
|
||||
const provided = authHeader.slice(7);
|
||||
if (
|
||||
provided.length !== token.length ||
|
||||
!timingSafeEqual(Buffer.from(provided), Buffer.from(token))
|
||||
) {
|
||||
throw new UnauthorizedException("Invalid token");
|
||||
}
|
||||
assertAutomationToken(authHeader);
|
||||
if (!body?.title || !body?.body_markdown) {
|
||||
throw new UnauthorizedException("title and body_markdown are required");
|
||||
}
|
||||
return this.blogService.create(body);
|
||||
}
|
||||
|
||||
// Called by a Directus Flow whenever a post is created/updated/deleted, so
|
||||
// edits made in the CMS admin appear immediately instead of after cache TTL.
|
||||
@Post("cache/purge")
|
||||
@Public()
|
||||
@HttpCode(200)
|
||||
async purgeCache(@Headers("authorization") authHeader: string) {
|
||||
assertAutomationToken(authHeader);
|
||||
await this.blogService.purgeListCache();
|
||||
return { purged: true };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,13 @@ const DIRECTUS_URL = process.env.DIRECTUS_URL?.replace(/\/+$/, "");
|
||||
const DIRECTUS_TOKEN = process.env.DIRECTUS_TOKEN;
|
||||
const DIRECTUS_PROJECT = process.env.DIRECTUS_PROJECT ?? "sase";
|
||||
|
||||
// Files uploaded in the Directus admin land in the shared MinIO `blog-assets`
|
||||
// bucket (public read), served from the MinIO domain — NOT from Directus,
|
||||
// which is Tailscale-only and unreachable for site visitors.
|
||||
const BLOG_ASSETS_BASE_URL = (
|
||||
process.env.BLOG_ASSETS_BASE_URL ?? "https://storage.sase.tr/blog-assets"
|
||||
).replace(/\/+$/, "");
|
||||
|
||||
// Row shape of the shared `posts` collection in Directus.
|
||||
interface DirectusPost {
|
||||
id: string;
|
||||
@@ -27,6 +34,7 @@ interface DirectusPost {
|
||||
body_markdown: string;
|
||||
tags: string[] | null;
|
||||
cover_image: string | null;
|
||||
cover_file: { filename_disk: string } | null;
|
||||
status: string;
|
||||
source: string;
|
||||
published_at: string;
|
||||
@@ -34,6 +42,16 @@ interface DirectusPost {
|
||||
date_updated: string | null;
|
||||
}
|
||||
|
||||
// cover_image (external URL, e.g. from the content pipeline) wins over
|
||||
// cover_file (admin upload, resolved to its public MinIO URL).
|
||||
function resolveCover(row: Pick<DirectusPost, "cover_image" | "cover_file">): string | null {
|
||||
if (row.cover_image) return row.cover_image;
|
||||
if (row.cover_file?.filename_disk) {
|
||||
return `${BLOG_ASSETS_BASE_URL}/${row.cover_file.filename_disk}`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// URL-safe slug, Turkish-aware. The content_blog prompt already emits a clean
|
||||
// slug, but we slugify defensively (and to derive one if it's missing).
|
||||
function slugify(input: string): string {
|
||||
@@ -91,7 +109,7 @@ export class BlogService {
|
||||
metaDescription: row.meta_description,
|
||||
bodyMarkdown: row.body_markdown,
|
||||
tags: row.tags ?? [],
|
||||
coverImage: row.cover_image,
|
||||
coverImage: resolveCover(row),
|
||||
status: row.status as BlogPost["status"],
|
||||
source: row.source,
|
||||
publishedAt: row.published_at,
|
||||
@@ -100,6 +118,12 @@ export class BlogService {
|
||||
};
|
||||
}
|
||||
|
||||
// Called by the Directus Flow webhook when a post changes, so both envs
|
||||
// drop their 30-minute list cache immediately.
|
||||
async purgeListCache(): Promise<void> {
|
||||
await this.redis.del(LIST_CACHE_KEY);
|
||||
}
|
||||
|
||||
async findAllPublished(): Promise<BlogPostListItem[]> {
|
||||
const cached = await this.redis.getJson<BlogPostListItem[]>(LIST_CACHE_KEY);
|
||||
if (cached) return cached;
|
||||
@@ -112,7 +136,7 @@ export class BlogService {
|
||||
"filter[project][_eq]": DIRECTUS_PROJECT,
|
||||
"filter[status][_eq]": "published",
|
||||
sort: "-published_at",
|
||||
fields: "slug,title,meta_description,tags,cover_image,published_at",
|
||||
fields: "slug,title,meta_description,tags,cover_image,cover_file.filename_disk,published_at",
|
||||
limit: "-1",
|
||||
}),
|
||||
);
|
||||
@@ -127,7 +151,7 @@ export class BlogService {
|
||||
title: r.title,
|
||||
metaDescription: r.meta_description,
|
||||
tags: r.tags ?? [],
|
||||
coverImage: r.cover_image,
|
||||
coverImage: resolveCover(r),
|
||||
publishedAt: r.published_at,
|
||||
}));
|
||||
|
||||
@@ -144,6 +168,7 @@ export class BlogService {
|
||||
"filter[project][_eq]": DIRECTUS_PROJECT,
|
||||
"filter[slug][_eq]": slug,
|
||||
"filter[status][_eq]": "published",
|
||||
fields: "*,cover_file.filename_disk",
|
||||
limit: "1",
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { usePageMeta } from "@/hooks/use-page-meta";
|
||||
import { SiteHeader } from "@/components/site-header";
|
||||
import { useBlogPosts } from "@/hooks/use-blog";
|
||||
import { Button } from "@sase/ui";
|
||||
import { usePageMeta } from "@/hooks/use-page-meta";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@sase/ui";
|
||||
import { Link, createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
@@ -9,41 +8,6 @@ export const Route = createFileRoute("/blog")({
|
||||
component: BlogPage,
|
||||
});
|
||||
|
||||
type PostCard = { title: string; description: string; date: string; slug: string };
|
||||
|
||||
// Original hand-authored posts. Kept so they never disappear; auto-published
|
||||
// posts from the content pipeline are merged in from the API below.
|
||||
const staticPosts: PostCard[] = [
|
||||
{
|
||||
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",
|
||||
slug: "vin-nedir-nasil-okunur",
|
||||
},
|
||||
{
|
||||
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",
|
||||
slug: "orijinal-mi-muadil-mi",
|
||||
},
|
||||
{
|
||||
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",
|
||||
slug: "dijital-donusum",
|
||||
},
|
||||
{
|
||||
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",
|
||||
slug: "dogru-parcayi-bulun",
|
||||
},
|
||||
];
|
||||
|
||||
function BlogPage() {
|
||||
usePageMeta({
|
||||
title: "Blog — Sase.tr | Şase & Yedek Parça Rehberi",
|
||||
@@ -51,21 +15,17 @@ function BlogPage() {
|
||||
canonical: "https://sase.tr/blog",
|
||||
});
|
||||
|
||||
const { data: apiPosts } = useBlogPosts();
|
||||
// All posts come from the central Directus CMS through the API.
|
||||
const { data: apiPosts, isLoading } = useBlogPosts();
|
||||
|
||||
// Merge static + API posts, dedupe by slug (static wins), newest first.
|
||||
const bySlug = new Map<string, PostCard>();
|
||||
for (const p of staticPosts) bySlug.set(p.slug, p);
|
||||
for (const p of apiPosts ?? []) {
|
||||
if (bySlug.has(p.slug)) continue;
|
||||
bySlug.set(p.slug, {
|
||||
const posts = (apiPosts ?? [])
|
||||
.map((p) => ({
|
||||
slug: p.slug,
|
||||
title: p.title,
|
||||
description: p.metaDescription ?? "",
|
||||
date: p.publishedAt.slice(0, 10),
|
||||
});
|
||||
}
|
||||
const posts = [...bySlug.values()].sort((a, b) => b.date.localeCompare(a.date));
|
||||
}))
|
||||
.sort((a, b) => b.date.localeCompare(a.date));
|
||||
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
@@ -77,6 +37,10 @@ function BlogPage() {
|
||||
Yedek parça sektörü, araç bakımı ve Sase.tr hakkında güncel yazılar.
|
||||
</p>
|
||||
|
||||
{isLoading && posts.length === 0 && (
|
||||
<p className="mt-12 text-muted-foreground">Yükleniyor…</p>
|
||||
)}
|
||||
|
||||
<div className="mt-12 grid gap-6 sm:grid-cols-2">
|
||||
{posts.map((post) => (
|
||||
<Link key={post.slug} to="/blog/$slug" params={{ slug: post.slug }}>
|
||||
|
||||
@@ -1,352 +1,13 @@
|
||||
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";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
|
||||
// ─── 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. 50'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 1 milyondan 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. 30 günlük ücretsiz deneme süresiyle platformu bugün deneyin — kredi
|
||||
kartı gerektirmez.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
// ─── MARKDOWN RENDERING (API-backed posts) ─────────────────────────────────────
|
||||
// Maps markdown to the same prose styling the hand-authored posts use.
|
||||
// ─── MARKDOWN RENDERING ───────────────────────────────────────────────────────
|
||||
// Posts live in the central Directus CMS and arrive as markdown via the API.
|
||||
|
||||
const mdComponents = {
|
||||
h2: (props: React.ComponentProps<"h2">) => (
|
||||
@@ -383,12 +44,11 @@ export const Route = createFileRoute("/blog_/$slug")({
|
||||
|
||||
function BlogPostPage() {
|
||||
const { slug } = Route.useParams();
|
||||
const staticPost = POSTS[slug];
|
||||
const { data: apiPost, isLoading, isError } = useBlogPost(slug, !staticPost);
|
||||
const { data: post, isLoading, isError } = useBlogPost(slug);
|
||||
|
||||
const title = staticPost?.title ?? apiPost?.title ?? "Blog";
|
||||
const description = staticPost?.description ?? apiPost?.metaDescription ?? "";
|
||||
const date = staticPost?.date ?? apiPost?.publishedAt?.slice(0, 10) ?? "";
|
||||
const title = post?.title ?? "Blog";
|
||||
const description = post?.metaDescription ?? "";
|
||||
const date = post?.publishedAt?.slice(0, 10) ?? "";
|
||||
|
||||
usePageMeta({
|
||||
title: `${title} | Sase.tr Blog`,
|
||||
@@ -397,8 +57,7 @@ function BlogPostPage() {
|
||||
});
|
||||
|
||||
let body: React.ReactNode;
|
||||
if (staticPost) body = staticPost.content;
|
||||
else if (apiPost) body = <MarkdownBody markdown={apiPost.bodyMarkdown} />;
|
||||
if (post) body = <MarkdownBody markdown={post.bodyMarkdown} />;
|
||||
else if (isLoading) body = <p className="text-muted-foreground">Yükleniyor…</p>;
|
||||
else
|
||||
body = (
|
||||
@@ -424,6 +83,14 @@ function BlogPostPage() {
|
||||
<h1 className="mt-3 text-3xl font-bold leading-tight sm:text-4xl">{title}</h1>
|
||||
{description && <p className="mt-4 text-lg text-muted-foreground">{description}</p>}
|
||||
|
||||
{post?.coverImage && (
|
||||
<img
|
||||
src={post.coverImage}
|
||||
alt={title}
|
||||
className="mt-8 w-full rounded-lg border object-cover"
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="mt-10">{body}</div>
|
||||
</article>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user