Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-04-17 02:08:33 +00:00
co-authored by renee-png
parent 5fe3d744d1
commit 173f7ba793
+74 -25
View File
@@ -201,31 +201,80 @@ function ClientDetail() {
<Card className="border-border/60 h-fit">
<CardContent className="p-5">
<h3 className="font-serif text-lg mb-3">Cases</h3>
{cases.length === 0 && <p className="text-sm text-muted-foreground">No cases yet.</p>}
<div className="space-y-1">
{cases.map((c) => (
<Link
key={c.id}
to="/cases/$caseId"
params={{ caseId: c.id }}
className="block p-2.5 rounded-md hover:bg-muted/60 -mx-2"
>
<div className="flex items-center justify-between gap-2">
<span className="text-sm font-medium truncate">{c.title}</span>
<Badge variant="outline" className={`text-[10px] ${statusBadgeClass(c.status)}`}>
{c.status.replace("_", " ")}
</Badge>
</div>
<div className="text-xs text-muted-foreground">
{c.case_number} · opened {formatDate(c.opened_at)}
</div>
<div className="text-xs text-muted-foreground mt-0.5">
Billed: {c.billed.hours.toFixed(1)} hr · {formatCurrency(c.billed.amount)}
</div>
</Link>
))}
</div>
<Tabs defaultValue="cases">
<TabsList className="mb-3 w-full">
<TabsTrigger value="cases" className="flex-1"><Briefcase className="h-3.5 w-3.5 mr-1.5" />Cases</TabsTrigger>
<TabsTrigger value="status" className="flex-1"><Activity className="h-3.5 w-3.5 mr-1.5" />Status</TabsTrigger>
</TabsList>
<TabsContent value="cases">
{cases.length === 0 && <p className="text-sm text-muted-foreground">No cases yet.</p>}
<div className="space-y-1">
{cases.map((c) => (
<Link
key={c.id}
to="/cases/$caseId"
params={{ caseId: c.id }}
className="block p-2.5 rounded-md hover:bg-muted/60 -mx-2"
>
<div className="flex items-center justify-between gap-2">
<span className="text-sm font-medium truncate">{c.title}</span>
<Badge variant="outline" className={`text-[10px] ${statusBadgeClass(c.status)}`}>
{c.status.replace("_", " ")}
</Badge>
</div>
<div className="text-xs text-muted-foreground">
{c.case_number} · opened {formatDate(c.opened_at)}
</div>
<div className="text-xs text-muted-foreground mt-0.5">
Billed: {c.billed.hours.toFixed(1)} hr · {formatCurrency(c.billed.amount)}
</div>
</Link>
))}
</div>
</TabsContent>
<TabsContent value="status">
<div className="flex items-center justify-between mb-3">
<p className="text-xs text-muted-foreground">{statusEntries.length} update{statusEntries.length === 1 ? "" : "s"} across all cases</p>
<Button
size="sm"
variant="outline"
disabled={statusEntries.length === 0}
onClick={() => {
downloadStatusReport({
title: "Client Status Report",
subtitle: client.name,
groupByCase: true,
entries: statusEntries.map((u) => ({
title: u.title,
body: u.body,
created_at: u.created_at,
author_name: u.user?.full_name,
author_email: u.user?.email,
case_number: u.case?.case_number,
case_title: u.case?.title,
})),
}, `status-report-${client.name.replace(/[^a-z0-9]+/gi, "-")}.pdf`);
}}
>
<FileDown className="h-3.5 w-3.5 mr-1.5" /> Export
</Button>
</div>
{statusEntries.length === 0 && <p className="text-sm text-muted-foreground">No status updates logged.</p>}
<div className="space-y-3 max-h-[480px] overflow-auto">
{statusEntries.map((u) => (
<div key={u.id} className="border-l-2 border-primary/40 pl-3 py-1">
<div className="text-sm font-medium">{formatDateTime(u.title)}</div>
<div className="text-[11px] text-muted-foreground mb-1">
{u.case?.case_number} · {u.user?.full_name || u.user?.email || "Unknown"}
</div>
<p className="text-xs text-muted-foreground line-clamp-3 whitespace-pre-wrap">{u.body}</p>
</div>
))}
</div>
</TabsContent>
</Tabs>
</CardContent>
</Card>
</div>