feat(jobs): step-by-step timeline on the detail page

The job_status_history table was already being populated on every
transition; the detail page just rendered a flat list with date only.
Replaced it with a proper vertical timeline:

  - Card title moved from 'Aşama Geçmişi' to 'Akış Geçmişi' since we
    now include side-trips (revision requests), not just forward steps.
  - Vertical guide line with a coloured node per entry: emerald for
    a normal step completion, rose for a revision request. Spotting a
    bounced prova in the history is a glance.
  - Revision rows get an inline 'Düzeltme talebi' pill; the '[Düzeltme
    talebi]' prefix is stripped from the visible note so the actual
    feedback text reads cleanly.
  - Always rendered (with an empty-state line) so the card position
    doesn't move around as the case progresses.
This commit is contained in:
kovakmedya
2026-05-22 16:05:07 +03:00
parent 53e443b4f1
commit 94e9dffaef
+48 -26
View File
@@ -259,33 +259,55 @@ export default async function JobDetailPage({
</CardContent> </CardContent>
</Card> </Card>
{history.length > 0 && ( <Card>
<Card> <CardHeader>
<CardHeader> <CardTitle>Akış Geçmişi</CardTitle>
<CardTitle>Aşama Geçmişi</CardTitle> <CardDescription>
<CardDescription>Tamamlanan aşamaların kaydı.</CardDescription> İşin aşama transition&apos;ları, kim yaptı ve hangi notla.
</CardHeader> </CardDescription>
<CardContent> </CardHeader>
<ol className="space-y-3"> <CardContent>
{history.map((h) => ( {history.length === 0 ? (
<li key={h.$id} className="border-l-2 border-primary/30 pl-4"> <p className="text-muted-foreground text-sm">
<div className="flex flex-wrap items-baseline gap-2"> Henüz aşama tamamlanmadı.
<span className="font-medium">{JOB_STEP_LABELS[h.step]}</span> </p>
<span className="text-muted-foreground text-xs"> ) : (
{dateFormatter.format(new Date(h.completedAt))} <ol className="relative space-y-4 border-l-2 border-border pl-6">
</span> {history.map((h) => {
</div> const isRevision = h.note?.startsWith("[Düzeltme talebi]");
{h.note && ( return (
<p className="text-muted-foreground mt-1 whitespace-pre-wrap text-sm"> <li key={h.$id} className="relative">
{h.note} <span
</p> className={`absolute -left-[1.85rem] mt-1.5 size-3 rounded-full ring-2 ring-background ${
)} isRevision ? "bg-rose-500" : "bg-emerald-500"
</li> }`}
))} aria-hidden
/>
<div className="flex flex-wrap items-baseline gap-2">
<span className="font-medium">
{JOB_STEP_LABELS[h.step]}
</span>
{isRevision && (
<span className="rounded bg-rose-100 px-1.5 py-0.5 text-xs font-medium text-rose-700 dark:bg-rose-950 dark:text-rose-300">
Düzeltme talebi
</span>
)}
<span className="text-muted-foreground text-xs tabular-nums">
{dateFormatter.format(new Date(h.completedAt))}
</span>
</div>
{h.note && (
<p className="text-muted-foreground mt-1 whitespace-pre-wrap text-sm">
{h.note.replace(/^\[Düzeltme talebi\]\s*/, "")}
</p>
)}
</li>
);
})}
</ol> </ol>
</CardContent> )}
</Card> </CardContent>
)} </Card>
<div> <div>
<Button asChild variant="outline"> <Button asChild variant="outline">