mirror of
https://github.com/complexcaresolutions/dak.c2s.git
synced 2026-03-17 17:13:42 +00:00
fix: improve CodingPage filter UX and prevent race conditions
- Show grand total count (data.total) instead of items.length - Display active fallgruppe filter label prominently - Add AbortController to cancel stale requests on rapid filter changes - Clarify badge to show "X / Y auf dieser Seite codiert" Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
35b9d5a7c9
commit
d5b357b60d
1 changed files with 29 additions and 9 deletions
|
|
@ -59,11 +59,12 @@ export function CodingPage() {
|
||||||
|
|
||||||
// Fetch coding queue
|
// Fetch coding queue
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const controller = new AbortController()
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
const params: Record<string, string | number> = { page, per_page: 50 }
|
const params: Record<string, string | number> = { page, per_page: 50 }
|
||||||
if (fallgruppe !== '__all__') params.fallgruppe = fallgruppe
|
if (fallgruppe !== '__all__') params.fallgruppe = fallgruppe
|
||||||
|
|
||||||
api.get<CaseListResponse>('/coding/queue', { params })
|
api.get<CaseListResponse>('/coding/queue', { params, signal: controller.signal })
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setData(res.data)
|
setData(res.data)
|
||||||
// Initialize form states for each case
|
// Initialize form states for each case
|
||||||
|
|
@ -75,8 +76,14 @@ export function CodingPage() {
|
||||||
setSavedIds(new Set())
|
setSavedIds(new Set())
|
||||||
setErrors({})
|
setErrors({})
|
||||||
})
|
})
|
||||||
.catch(() => setData(null))
|
.catch(() => {
|
||||||
.finally(() => setLoading(false))
|
if (!controller.signal.aborted) setData(null)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
if (!controller.signal.aborted) setLoading(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => controller.abort()
|
||||||
}, [page, fallgruppe])
|
}, [page, fallgruppe])
|
||||||
|
|
||||||
const updateFormField = (caseId: number, field: keyof CodingFormState, value: unknown) => {
|
const updateFormField = (caseId: number, field: keyof CodingFormState, value: unknown) => {
|
||||||
|
|
@ -124,19 +131,32 @@ export function CodingPage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count coded cases
|
// Count coded cases on current page
|
||||||
const codedCount = data
|
const codedCount = data
|
||||||
? data.items.filter((c) => savedIds.has(c.id) || c.gutachten_typ !== null).length
|
? data.items.filter((c) => savedIds.has(c.id) || c.gutachten_typ !== null).length
|
||||||
: 0
|
: 0
|
||||||
const totalCount = data?.items.length ?? 0
|
const grandTotal = data?.total ?? 0
|
||||||
|
const activeFilterLabel = fallgruppe !== '__all__'
|
||||||
|
? FALLGRUPPEN_LABELS[fallgruppe] || fallgruppe
|
||||||
|
: null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-6 space-y-4">
|
<div className="p-6 space-y-4">
|
||||||
<div className="flex flex-wrap items-center justify-between gap-4">
|
<div className="flex flex-wrap items-center justify-between gap-4">
|
||||||
|
<div>
|
||||||
<h1 className="text-2xl font-bold">Coding-Warteschlange</h1>
|
<h1 className="text-2xl font-bold">Coding-Warteschlange</h1>
|
||||||
|
{!loading && data && (
|
||||||
|
<p className="text-sm text-muted-foreground mt-0.5">
|
||||||
|
{grandTotal} {grandTotal === 1 ? 'Fall' : 'Fälle'} offen
|
||||||
|
{activeFilterLabel && (
|
||||||
|
<> — gefiltert nach <span className="font-medium text-foreground">{activeFilterLabel}</span></>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Badge variant="outline" className="text-sm py-1 px-3">
|
<Badge variant="outline" className="text-sm py-1 px-3">
|
||||||
{codedCount} von {totalCount} Fälle codiert
|
{codedCount} / {data?.items.length ?? 0} auf dieser Seite codiert
|
||||||
</Badge>
|
</Badge>
|
||||||
<Select value={fallgruppe} onValueChange={(v) => { setFallgruppe(v); setPage(1) }}>
|
<Select value={fallgruppe} onValueChange={(v) => { setFallgruppe(v); setPage(1) }}>
|
||||||
<SelectTrigger className="w-[180px]">
|
<SelectTrigger className="w-[180px]">
|
||||||
|
|
@ -152,11 +172,11 @@ export function CodingPage() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Progress bar */}
|
{/* Progress bar */}
|
||||||
{data && totalCount > 0 && (
|
{data && grandTotal > 0 && (
|
||||||
<div className="h-2 w-full rounded-full bg-muted overflow-hidden">
|
<div className="h-2 w-full rounded-full bg-muted overflow-hidden">
|
||||||
<div
|
<div
|
||||||
className="h-full rounded-full bg-primary transition-all duration-300"
|
className="h-full rounded-full bg-primary transition-all duration-300"
|
||||||
style={{ width: `${(codedCount / totalCount) * 100}%` }}
|
style={{ width: `${(codedCount / (data.items.length || 1)) * 100}%` }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue