From 208eb219886303e05daf8bee9a9efacf04b7df63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hartmut=20N=C3=B6renberg?= Date: Fri, 6 Mar 2026 22:06:55 +0100 Subject: [PATCH] fix(dashboard): fix widget crashes on /worker/activity response shape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - QueueStatusWidget + WorkerStatusWidget expected res.data to be ActivityEntry[] but /api/worker/activity returns {cad_processing: [...], render_jobs: [...]} → TypeError: entries.filter is not a function → blank screen (no error boundary) - Both widgets now use ActivityResponse interface and read data?.cad_processing - Field names updated: id→cad_file_id, filename→original_name, status→processing_status - AdminDashboard: fix duplicate React key in top_products table (pim_id can repeat) → use index suffix to guarantee unique keys Co-Authored-By: Claude Sonnet 4.6 --- .../components/dashboard/AdminDashboard.tsx | 4 +-- .../dashboard/widgets/QueueStatusWidget.tsx | 35 +++++++++++-------- .../dashboard/widgets/WorkerStatusWidget.tsx | 23 +++++++----- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/frontend/src/components/dashboard/AdminDashboard.tsx b/frontend/src/components/dashboard/AdminDashboard.tsx index d1edb15..42cc1af 100644 --- a/frontend/src/components/dashboard/AdminDashboard.tsx +++ b/frontend/src/components/dashboard/AdminDashboard.tsx @@ -501,8 +501,8 @@ export default function AdminDashboard() { - {top_products.map((p) => ( - + {top_products.map((p, i) => ( + {p.pim_id} {p.product_name || '—'} {p.category} diff --git a/frontend/src/components/dashboard/widgets/QueueStatusWidget.tsx b/frontend/src/components/dashboard/widgets/QueueStatusWidget.tsx index dc9f373..6815b1d 100644 --- a/frontend/src/components/dashboard/widgets/QueueStatusWidget.tsx +++ b/frontend/src/components/dashboard/widgets/QueueStatusWidget.tsx @@ -3,12 +3,17 @@ import { Activity } from 'lucide-react' import api from '../../../api/client' interface ActivityEntry { - id: string - filename: string - status: string + cad_file_id: string + original_name: string + processing_status: string created_at: string } +interface ActivityResponse { + cad_processing: ActivityEntry[] + render_jobs: ActivityEntry[] +} + function Skeleton() { return (
@@ -20,11 +25,11 @@ function Skeleton() { } export default function QueueStatusWidget() { - const { data, isLoading, error } = useQuery({ + const { data, isLoading, error } = useQuery({ queryKey: ['worker-activity-widget'], queryFn: async () => { const res = await api.get('/worker/activity') - return res.data as ActivityEntry[] + return res.data as ActivityResponse }, refetchInterval: 15_000, staleTime: 10_000, @@ -36,9 +41,9 @@ export default function QueueStatusWidget() { return

Failed to load queue status

} - const entries = data ?? [] - const processing = entries.filter((e) => e.status === 'processing').length - const failed = entries.filter((e) => e.status === 'failed').length + const entries = data?.cad_processing ?? [] + const processing = entries.filter((e) => e.processing_status === 'processing').length + const failed = entries.filter((e) => e.processing_status === 'failed').length const recent = entries.slice(0, 5) const statusDot = processing > 0 @@ -71,26 +76,26 @@ export default function QueueStatusWidget() { )} {recent.map((entry) => (
- - {entry.filename} + + {entry.original_name} - {entry.status} + {entry.processing_status}
))} diff --git a/frontend/src/components/dashboard/widgets/WorkerStatusWidget.tsx b/frontend/src/components/dashboard/widgets/WorkerStatusWidget.tsx index 11e5390..fae8021 100644 --- a/frontend/src/components/dashboard/widgets/WorkerStatusWidget.tsx +++ b/frontend/src/components/dashboard/widgets/WorkerStatusWidget.tsx @@ -3,13 +3,18 @@ import { Cpu } from 'lucide-react' import api from '../../../api/client' interface ActivityEntry { - id: string - filename: string - status: string + cad_file_id: string + original_name: string + processing_status: string created_at: string updated_at?: string } +interface ActivityResponse { + cad_processing: ActivityEntry[] + render_jobs: ActivityEntry[] +} + function Skeleton() { return (
@@ -20,11 +25,11 @@ function Skeleton() { } export default function WorkerStatusWidget() { - const { data, isLoading, error } = useQuery({ + const { data, isLoading, error } = useQuery({ queryKey: ['worker-status-widget'], queryFn: async () => { const res = await api.get('/worker/activity') - return res.data as ActivityEntry[] + return res.data as ActivityResponse }, refetchInterval: 15_000, staleTime: 10_000, @@ -36,10 +41,10 @@ export default function WorkerStatusWidget() { return

Failed to load worker status

} - const entries = data ?? [] - const processing = entries.filter((e) => e.status === 'processing') - const failed = entries.filter((e) => e.status === 'failed') - const completed = entries.filter((e) => e.status === 'completed') + const entries = data?.cad_processing ?? [] + const processing = entries.filter((e) => e.processing_status === 'processing') + const failed = entries.filter((e) => e.processing_status === 'failed') + const completed = entries.filter((e) => e.processing_status === 'completed') const overallStatus = processing.length > 0