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>
24 lines
760 B
TypeScript
24 lines
760 B
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
|
|
function formatElapsed(ms: number): string {
|
|
const totalSec = Math.max(0, Math.floor(ms / 1000));
|
|
const h = Math.floor(totalSec / 3600);
|
|
const m = Math.floor((totalSec % 3600) / 60);
|
|
const s = totalSec % 60;
|
|
const pad = (n: number) => String(n).padStart(2, "0");
|
|
return h > 0 ? `${h}:${pad(m)}:${pad(s)}` : `${m}:${pad(s)}`;
|
|
}
|
|
|
|
export function SessionTimer({ startedAt }: { startedAt: string }) {
|
|
const [now, setNow] = useState(() => Date.now());
|
|
|
|
useEffect(() => {
|
|
const timer = setInterval(() => setNow(Date.now()), 1000);
|
|
return () => clearInterval(timer);
|
|
}, []);
|
|
|
|
return <span className="font-mono-num">{formatElapsed(now - new Date(startedAt).getTime())}</span>;
|
|
}
|