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:
2026-09-02 14:33:24 +03:00
co-authored by Claude Sonnet 5
parent d045b24c85
commit 7d504d1938
30 changed files with 3436 additions and 801 deletions
+88 -128
View File
@@ -1,5 +1,12 @@
import { prisma } from "@streamclipper/db";
import { updateYtdlpCookies, updateSystemSettings, updateNotificationPrefs } from "../actions";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Switch } from "@/components/ui/switch";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent, CardHeader, CardTitle, CardDescription, CardFooter } from "@/components/ui/card";
export const dynamic = "force-dynamic";
@@ -22,139 +29,92 @@ export default async function SettingsPage() {
const notifyRenderDone = notifyRender?.value !== "false";
return (
<>
<h1>Ayarlar</h1>
<div className="flex flex-col gap-6">
<h1 className="text-2xl font-semibold tracking-tight">Ayarlar</h1>
<div className="card">
<div className="card-head">
<span>YouTube Cookie&apos;leri</span>
<span className="mono">
<Card>
<CardHeader className="flex-row items-center justify-between">
<CardTitle className="text-sm font-medium text-muted-foreground">YouTube Cookie&apos;leri</CardTitle>
<span className="font-mono-num text-xs text-muted-foreground">
{setting ? `son güncelleme: ${setting.updatedAt.toLocaleString("tr-TR")}` : "hiç ayarlanmadı"}
</span>
</div>
{cookieStale && (
<p style={{ marginBottom: "0.5rem" }}>
<span className="badge warn">
{Math.round(cookieAgeHours!)} saattir güncellenmedi büyük/popüler kanallarda bot-check
hatası görülebilir, taze bir cookies.txt ile güncelle
</span>
</p>
)}
<p className="empty" style={{ marginBottom: "0.5rem" }}>
YouTube canlı yayın kontrolü ve kayıt için kullanılıyor. Google, oturum çerezlerinin bir
kısmını birkaç saatte bir yeniliyor burada eskidiğini fark edersen (kanal detay
sayfasında &quot;son poll hatası&quot; olarak görürsün) taze bir cookies.txt ile
güncelle. Ana hesabın yerine ayrı, önemsiz bir Google hesabı kullanman önerilir.
</p>
<form action={updateYtdlpCookies}>
<textarea
name="cookies"
required
placeholder="Netscape formatlı cookies.txt içeriğini buraya yapıştır"
rows={10}
style={{
width: "100%",
background: "var(--bg)",
border: "1px solid var(--border)",
borderRadius: "6px",
padding: "0.6rem 0.7rem",
color: "var(--text)",
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
fontSize: "0.75rem",
resize: "vertical",
}}
/>
<button
type="submit"
style={{
marginTop: "0.6rem",
background: "var(--accent)",
border: "none",
borderRadius: "6px",
padding: "0.5rem 1rem",
color: "#fff",
fontWeight: 600,
fontSize: "0.9rem",
cursor: "pointer",
}}
>
Kaydet
</button>
</form>
</div>
</CardHeader>
<CardContent className="flex flex-col gap-3">
{cookieStale && (
<Badge variant="warn" className="w-fit">
{Math.round(cookieAgeHours!)} saattir güncellenmedi büyük/popüler kanallarda bot-check hatası
görülebilir
</Badge>
)}
<CardDescription>
YouTube canlı yayın kontrolü ve kayıt için kullanılıyor. Google, oturum çerezlerinin bir kısmını birkaç
saatte bir yeniliyor burada eskidiğini fark edersen (kanal detay sayfasında &quot;son poll hatası&quot;
olarak görürsün) taze bir cookies.txt ile güncelle. Ana hesabın yerine ayrı, önemsiz bir Google hesabı
kullanman önerilir.
</CardDescription>
<form action={updateYtdlpCookies} className="flex flex-col gap-3">
<Textarea
name="cookies"
required
placeholder="Netscape formatlı cookies.txt içeriğini buraya yapıştır"
rows={10}
className="font-mono-num text-xs"
/>
<Button type="submit" className="self-start">
Kaydet
</Button>
</form>
</CardContent>
</Card>
<div className="card">
<div className="card-head">
<span>Sistem Parametreleri</span>
</div>
<p className="empty" style={{ marginBottom: "0.5rem" }}>
Bir sonraki döngüden itibaren geçerli olur, redeploy gerekmez.
</p>
<form action={updateSystemSettings} style={{ display: "flex", flexDirection: "column", gap: "0.6rem" }}>
<label className="mono" style={{ fontSize: "0.8rem" }}>
Poll aralığı (saniye)
<input name="pollIntervalSec" type="number" min={5} defaultValue={pollIntervalSec} style={{ display: "block", marginTop: "0.25rem" }} />
</label>
<label className="mono" style={{ fontSize: "0.8rem" }}>
Ham segment TTL (saat)
<input name="ttlHours" type="number" min={1} defaultValue={ttlHours} style={{ display: "block", marginTop: "0.25rem" }} />
</label>
<p className="empty" style={{ fontSize: "0.75rem", margin: 0 }}>
Eşzamanlı capture limiti ve render eşzamanlılığı BullMQ worker&apos;ları başlatılırken
sabitleniyor bunları değiştirmek için Coolify&apos;deki `MAX_CONCURRENT_CAPTURES` /
`VIDEO_RENDER_CONCURRENCY` env var&apos;larını güncelleyip redeploy etmek gerekiyor.
</p>
<button
type="submit"
style={{
alignSelf: "flex-start",
background: "var(--accent)",
border: "none",
borderRadius: "6px",
padding: "0.5rem 1rem",
color: "#fff",
fontWeight: 600,
fontSize: "0.9rem",
cursor: "pointer",
}}
>
Kaydet
</button>
</form>
</div>
<Card>
<CardHeader>
<CardTitle className="text-sm font-medium text-muted-foreground">Sistem Parametreleri</CardTitle>
<CardDescription>Bir sonraki döngüden itibaren geçerli olur, redeploy gerekmez.</CardDescription>
</CardHeader>
<CardContent>
<form action={updateSystemSettings} className="flex flex-col gap-4">
<div className="flex flex-col gap-1.5">
<Label htmlFor="pollIntervalSec">Poll aralığı (saniye)</Label>
<Input id="pollIntervalSec" name="pollIntervalSec" type="number" min={5} defaultValue={pollIntervalSec} className="max-w-40" />
</div>
<div className="flex flex-col gap-1.5">
<Label htmlFor="ttlHours">Ham segment TTL (saat)</Label>
<Input id="ttlHours" name="ttlHours" type="number" min={1} defaultValue={ttlHours} className="max-w-40" />
</div>
<p className="max-w-prose text-xs text-muted-foreground">
Eşzamanlı capture limiti ve render eşzamanlılığı BullMQ worker&apos;ları başlatılırken sabitleniyor
bunları değiştirmek için Coolify&apos;deki <code className="font-mono-num">MAX_CONCURRENT_CAPTURES</code>{" "}
/ <code className="font-mono-num">VIDEO_RENDER_CONCURRENCY</code> env var&apos;larını güncelleyip
redeploy etmek gerekiyor.
</p>
<Button type="submit" className="self-start">
Kaydet
</Button>
</form>
</CardContent>
</Card>
<div className="card">
<div className="card-head">
<span>Bildirimler</span>
</div>
<form action={updateNotificationPrefs} style={{ display: "flex", flexDirection: "column", gap: "0.5rem" }}>
<label className="mono" style={{ fontSize: "0.85rem", display: "flex", alignItems: "center", gap: "0.4rem" }}>
<input type="checkbox" name="notifyStreamStart" defaultChecked={notifyStreamStart} />
Yayın başladığında Telegram bildirimi
</label>
<label className="mono" style={{ fontSize: "0.85rem", display: "flex", alignItems: "center", gap: "0.4rem" }}>
<input type="checkbox" name="notifyRenderDone" defaultChecked={notifyRenderDone} />
9:16 render tamamlandığında Telegram bildirimi
</label>
<button
type="submit"
style={{
alignSelf: "flex-start",
marginTop: "0.3rem",
background: "var(--accent)",
border: "none",
borderRadius: "6px",
padding: "0.5rem 1rem",
color: "#fff",
fontWeight: 600,
fontSize: "0.9rem",
cursor: "pointer",
}}
>
Kaydet
</button>
<Card>
<CardHeader>
<CardTitle className="text-sm font-medium text-muted-foreground">Bildirimler</CardTitle>
</CardHeader>
<form action={updateNotificationPrefs}>
<CardContent className="flex flex-col gap-4">
<Label className="flex items-center justify-between gap-4 text-sm font-normal">
Yayın başladığında Telegram bildirimi
<Switch name="notifyStreamStart" defaultChecked={notifyStreamStart} />
</Label>
<Label className="flex items-center justify-between gap-4 text-sm font-normal">
9:16 render tamamlandığında Telegram bildirimi
<Switch name="notifyRenderDone" defaultChecked={notifyRenderDone} />
</Label>
</CardContent>
<CardFooter>
<Button type="submit">Kaydet</Button>
</CardFooter>
</form>
</div>
</>
</Card>
</div>
);
}