import Link from "next/link"; import { PanelShell } from "@/components/panel-shell"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { buttonVariants } from "@/components/ui/button"; import { ALL_RANGES, type TimeRange, getOperationalHealth, getWinningProviders, getProviderAttempts, getFallbackChains, getErrorBreakdown, getDailyTrend, getBrandBreakdown, } from "@/lib/sase/vin-decode"; import { listSaseDeploys, analyzeDeployRegressions, } from "@/lib/sase/deploy-timeline"; import { getVinRelatedInsights } from "@/lib/sase/vin-insights"; export const dynamic = "force-dynamic"; const RANGE_LABEL: Record = { "1h": "Son 1 saat", "24h": "Son 24 saat", "7d": "Son 7 gün", "30d": "Son 30 gün", }; function isRange(v: string | undefined): v is TimeRange { return v === "1h" || v === "24h" || v === "7d" || v === "30d"; } export default async function VinDecodePage({ searchParams, }: { searchParams: Promise<{ range?: string }>; }) { const sp = await searchParams; const range: TimeRange = isRange(sp.range) ? sp.range : "24h"; const [health, winning, attempts, chains, errors, trend, brands, deploys, insights] = await Promise.all([ getOperationalHealth(range), getWinningProviders(range), getProviderAttempts(range), getFallbackChains(range), getErrorBreakdown(range), getDailyTrend(30), getBrandBreakdown(range, 12), listSaseDeploys(10), getVinRelatedInsights(8), ]); const regressions = deploys.length > 0 ? await analyzeDeployRegressions(deploys) : []; const trendMax = Math.max(1, ...trend.map((t) => t.total)); return (
← Sase

VIN Decode Observability

{RANGE_LABEL[range]} · {health.totalCount.toLocaleString("tr-TR")} sorgu

{ALL_RANGES.map((r) => ( {r} ))}
VIN list → Business → Trends → WMI fırsatları →
= 0.9 ? "ok" : health.successRate >= 0.75 ? "warn" : "bad" } />
Kazanan provider (decode'u sonuçlandıran) ({ label: w.provider, href: `/projects/sase/vin-decode/providers/${encodeURIComponent(w.provider)}`, count: w.successCount, meta: `Ø ${ms(w.avgResponseMs)} · %${(w.share * 100).toFixed(1)}`, }))} /> Provider deneme (timings'ten — chain'de yer alan) ({ label: a.provider, href: `/projects/sase/vin-decode/providers/${encodeURIComponent(a.provider === "pcat" ? "parts-catalogs" : a.provider === "vin_api" ? "vin-api" : a.provider)}`, count: a.attemptCount, meta: `P50 ${ms(a.p50Ms)} · P95 ${ms(a.p95Ms)}`, }))} />
Fallback zincirleri (timings keys) {chains.length === 0 ? (

Bu pencerede provider chain kullanılmamış.

) : ( Chain Adet {chains.map((c) => ( {c.chain} {c.occurrences.toLocaleString("tr-TR")} ))}
)}
Hata kodu breakdown {errors.length === 0 ? (

Hata yok.

) : (
    {errors.map((e) => (
  • {e.errorKey} {e.count.toLocaleString("tr-TR")}

    {e.exampleMessage}

  • ))}
)}
En çok sorgulanan markalar (success rate'li) {brands.length === 0 ? (

Marka eşleşmiş sorgu yok.

) : ( Marka Sorgu Başarı {brands.map((b) => ( {b.brandSlug ?? "?"} {b.brandName && ( {b.brandName} )} {b.total.toLocaleString("tr-TR")} = 0.9 ? "text-emerald-600" : b.successRate >= 0.75 ? "" : "text-destructive") } > {pct(b.successRate)} ))}
)}
BIP'ten VIN'le ilgili insight'lar (derin kök neden) {insights.length === 0 ? (

VIN-tag'li aktif insight yok.{" "} /insights {" "} tam listede.

) : ( )}
Son deploy'lar + regresyon analizi (30dk önce/sonra) {deploys.length === 0 ? (

Coolify deploy bilgisi yok (COOLIFY_API_TOKEN gerekli).

) : ( Tarih Commit Önce (success) Sonra (success) Δ {regressions.length === 0 && ( Henüz post-deploy penceresi geçmemiş. )} {regressions.map((r) => { const deltaTone = r.regressed ? "text-destructive font-semibold" : r.successRateDeltaPp > 5 ? "text-emerald-600" : ""; return ( {r.deploy.startedAt.toISOString().slice(5, 16).replace("T", " ")} {r.deploy.commit?.slice(0, 8) ?? "—"} {r.regressed && ( regression )} {(r.before.successRate * 100).toFixed(1)}%{" "} (n={r.before.total}) {(r.after.successRate * 100).toFixed(1)}%{" "} (n={r.after.total}) {r.successRateDeltaPp > 0 ? "+" : ""} {r.successRateDeltaPp.toFixed(1)}pp ); })}
)}
Son 30 gün — günlük sorgu hacmi
{trend.map((t) => (
{t.succeeded > 0 && (
)}
))}
{trend[0]?.date} maks {trendMax}/gün {trend[trend.length - 1]?.date}
); } function Kpi({ label, value, hint, tone, }: { label: string; value: string; hint?: string; tone?: "ok" | "warn" | "bad"; }) { const toneColor = tone === "ok" ? "text-emerald-600" : tone === "warn" ? "text-yellow-600" : tone === "bad" ? "text-destructive" : ""; return ( {label} {value} {hint && ( {hint} )} ); } function DistList({ items, }: { items: Array<{ label: string; count: number; meta?: string; href?: string }>; }) { if (items.length === 0) { return

Veri yok.

; } const max = Math.max(1, ...items.map((i) => i.count)); return (
    {items.map((i) => { const labelNode = i.href ? ( {i.label} ) : ( {i.label} ); return (
  • {labelNode} {i.meta} {i.count.toLocaleString("tr-TR")}
  • ); })}
); } function pct(v: number): string { return `${(v * 100).toFixed(1)}%`; } function ms(v: number | null): string { if (v == null) return "—"; if (v >= 1000) return `${(v / 1000).toFixed(1)}s`; return `${v}ms`; }