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:
CCS Admin 2026-02-27 17:28:23 +00:00
parent 35b9d5a7c9
commit d5b357b60d

View file

@ -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">
<h1 className="text-2xl font-bold">Coding-Warteschlange</h1> <div>
<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>
)} )}