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:
@@ -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'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">
|
||||||
|
|||||||
Reference in New Issue
Block a user