feat: panel UI'ını Tailwind v4 + shadcn/ui ile yeniden tasarla (Faz 4)
Elle yazılmış tek-dosya CSS (sabit koyu tema, ham table/button, window.confirm ile silme onayı) yerine tutarlı bir bileşen sistemi: Card/Table/Badge/Button/ Input/Switch/AlertDialog primitifleri, Hanken Grotesk + JetBrains Mono tipografi çifti, "broadcast/monitör" temalı özel bir renk paleti (camgöbeği accent, ok/warn/err/live durum rengi sözlüğü). Tüm sayfalar (dashboard, kanal detay, segmentler, ayarlar, istatistik, kullanıcılar, login) aynı oturumda taşındı — silme onayları artık gerçek AlertDialog, native confirm() değil. Veri akışı/server action'lar değişmedi, tamamen görsel katman. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { prisma } from "@streamclipper/db";
|
||||
import { Card } from "@/components/ui/card";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
@@ -9,6 +10,15 @@ function fmt(n: number, digits = 1): string {
|
||||
return n.toLocaleString("tr-TR", { minimumFractionDigits: digits, maximumFractionDigits: digits });
|
||||
}
|
||||
|
||||
function StatCard({ label, value }: { label: string; value: string }) {
|
||||
return (
|
||||
<Card className="gap-1 px-5 py-4">
|
||||
<span className="font-mono-num text-xs tracking-wide text-muted-foreground uppercase">{label}</span>
|
||||
<span className="font-mono-num text-2xl font-semibold">{value}</span>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default async function StatsPage() {
|
||||
const [transcribed, durationSum, readyShorts, failedShorts, totalCandidates] = await Promise.all([
|
||||
prisma.candidateSegment.findMany({
|
||||
@@ -30,43 +40,24 @@ export default async function StatsPage() {
|
||||
const estimatedGb = captureHours * GB_PER_HOUR_ESTIMATE;
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Kullanım & Maliyet</h1>
|
||||
<p className="empty" style={{ marginBottom: "1rem" }}>
|
||||
Şimdiye kadarki toplam kullanım — tüm zamanlar. STT maliyeti ve disk kullanımı tahmini
|
||||
(bkz. maliyet analizi), gerçek faturayla küçük farklar olabilir.
|
||||
</p>
|
||||
|
||||
<div className="card" style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: "1rem" }}>
|
||||
<div>
|
||||
<div className="mono" style={{ fontSize: "0.75rem", opacity: 0.7 }}>TOPLAM KAYIT SÜRESİ</div>
|
||||
<div style={{ fontSize: "1.4rem", fontWeight: 600 }}>{fmt(captureHours)} sa</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="mono" style={{ fontSize: "0.75rem", opacity: 0.7 }}>TOPLAM STT SÜRESİ</div>
|
||||
<div style={{ fontSize: "1.4rem", fontWeight: 600 }}>{fmt(sttMinutes)} dk</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="mono" style={{ fontSize: "0.75rem", opacity: 0.7 }}>TAHMİNİ WHISPER MALİYETİ</div>
|
||||
<div style={{ fontSize: "1.4rem", fontWeight: 600 }}>${fmt(sttCost, 2)}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="mono" style={{ fontSize: "0.75rem", opacity: 0.7 }}>TAHMİNİ DİSK KULLANIMI</div>
|
||||
<div style={{ fontSize: "1.4rem", fontWeight: 600 }}>{fmt(estimatedGb)} GB</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="mono" style={{ fontSize: "0.75rem", opacity: 0.7 }}>ÜRETİLEN 9:16 KLİP</div>
|
||||
<div style={{ fontSize: "1.4rem", fontWeight: 600 }}>{readyShorts}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="mono" style={{ fontSize: "0.75rem", opacity: 0.7 }}>BAŞARISIZ RENDER</div>
|
||||
<div style={{ fontSize: "1.4rem", fontWeight: 600 }}>{failedShorts}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="mono" style={{ fontSize: "0.75rem", opacity: 0.7 }}>TOPLAM ADAY KLİP</div>
|
||||
<div style={{ fontSize: "1.4rem", fontWeight: 600 }}>{totalCandidates}</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold tracking-tight">Kullanım & Maliyet</h1>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
Şimdiye kadarki toplam kullanım — tüm zamanlar. STT maliyeti ve disk kullanımı tahmini, gerçek faturayla
|
||||
küçük farklar olabilir.
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3">
|
||||
<StatCard label="Toplam Kayıt Süresi" value={`${fmt(captureHours)} sa`} />
|
||||
<StatCard label="Toplam STT Süresi" value={`${fmt(sttMinutes)} dk`} />
|
||||
<StatCard label="Tahmini Whisper Maliyeti" value={`$${fmt(sttCost, 2)}`} />
|
||||
<StatCard label="Tahmini Disk Kullanımı" value={`${fmt(estimatedGb)} GB`} />
|
||||
<StatCard label="Üretilen 9:16 Klip" value={String(readyShorts)} />
|
||||
<StatCard label="Başarısız Render" value={String(failedShorts)} />
|
||||
<StatCard label="Toplam Aday Klip" value={String(totalCandidates)} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user