diff --git a/frontend/src/components/dashboard/ActionQueueCard.tsx b/frontend/src/components/dashboard/ActionQueueCard.tsx index 131a784f..700b9a67 100644 --- a/frontend/src/components/dashboard/ActionQueueCard.tsx +++ b/frontend/src/components/dashboard/ActionQueueCard.tsx @@ -65,7 +65,7 @@ function ActionItemCard({ onModify?: (id: string) => void; }) { const [expanded, setExpanded] = useState(false); - const config = urgencyConfig[action.urgency]; + const config = urgencyConfig[action.urgency as keyof typeof urgencyConfig] || urgencyConfig.normal; const UrgencyIcon = config.icon; const { formatPOAction } = useReasoningFormatter(); const { t } = useTranslation('reasoning'); @@ -84,12 +84,12 @@ function ActionItemCard({
-

{action.title}

+

{action.title || 'Action Required'}

- {action.urgency} + {action.urgency || 'normal'}
-

{action.subtitle}

+

{action.subtitle || ''}

@@ -139,13 +139,13 @@ function ActionItemCard({
- {t('jtbd.action_queue.estimated_time')}: {action.estimatedTimeMinutes} min + {t('jtbd.action_queue.estimated_time')}: {action.estimatedTimeMinutes || 5} min
{/* Action Buttons */}
- {action.actions.map((button, index) => { + {(action.actions || []).map((button, index) => { const buttonStyles = { primary: 'bg-blue-600 hover:bg-blue-700 text-white', secondary: 'bg-gray-200 hover:bg-gray-300 text-gray-800', @@ -188,10 +188,9 @@ export function ActionQueueCard({ onModify, }: ActionQueueCardProps) { const [showAll, setShowAll] = useState(false); - const displayedActions = showAll ? actionQueue.actions : actionQueue.actions.slice(0, 3); const { t } = useTranslation('reasoning'); - if (loading) { + if (loading || !actionQueue) { return (
@@ -203,7 +202,7 @@ export function ActionQueueCard({ ); } - if (actionQueue.actions.length === 0) { + if (!actionQueue.actions || actionQueue.actions.length === 0) { return (
@@ -215,29 +214,31 @@ export function ActionQueueCard({ ); } + const displayedActions = showAll ? actionQueue.actions : actionQueue.actions.slice(0, 3); + return (
{/* Header */}

{t('jtbd.action_queue.title')}

- {actionQueue.totalActions > 3 && ( + {(actionQueue.totalActions || 0) > 3 && ( - {actionQueue.totalActions} {t('jtbd.action_queue.total')} + {actionQueue.totalActions || 0} {t('jtbd.action_queue.total')} )}
{/* Summary Badges */} - {(actionQueue.criticalCount > 0 || actionQueue.importantCount > 0) && ( + {((actionQueue.criticalCount || 0) > 0 || (actionQueue.importantCount || 0) > 0) && (
- {actionQueue.criticalCount > 0 && ( + {(actionQueue.criticalCount || 0) > 0 && ( - {actionQueue.criticalCount} {t('jtbd.action_queue.critical')} + {actionQueue.criticalCount || 0} {t('jtbd.action_queue.critical')} )} - {actionQueue.importantCount > 0 && ( + {(actionQueue.importantCount || 0) > 0 && ( - {actionQueue.importantCount} {t('jtbd.action_queue.important')} + {actionQueue.importantCount || 0} {t('jtbd.action_queue.important')} )}
@@ -257,14 +258,14 @@ export function ActionQueueCard({
{/* Show More/Less */} - {actionQueue.totalActions > 3 && ( + {(actionQueue.totalActions || 0) > 3 && ( )}
diff --git a/frontend/src/components/dashboard/HealthStatusCard.tsx b/frontend/src/components/dashboard/HealthStatusCard.tsx index 97a5dfcb..ebe709c8 100644 --- a/frontend/src/components/dashboard/HealthStatusCard.tsx +++ b/frontend/src/components/dashboard/HealthStatusCard.tsx @@ -50,11 +50,9 @@ const iconMap = { }; export function HealthStatusCard({ healthStatus, loading }: HealthStatusCardProps) { - const config = statusConfig[healthStatus.status]; - const StatusIcon = config.icon; const { t } = useTranslation('reasoning'); - if (loading) { + if (loading || !healthStatus) { return (
@@ -63,6 +61,10 @@ export function HealthStatusCard({ healthStatus, loading }: HealthStatusCardProp ); } + const status = healthStatus.status || 'green'; + const config = statusConfig[status]; + const StatusIcon = config.icon; + return (

- {healthStatus.headline} + {healthStatus.headline || t(`jtbd.health_status.${status}`)}

{/* Last Update */} @@ -91,19 +93,22 @@ export function HealthStatusCard({ healthStatus, loading }: HealthStatusCardProp
{/* Next Check */} -
- - - {t('jtbd.health_status.next_check')}:{' '} - {formatDistanceToNow(new Date(healthStatus.nextScheduledRun), { addSuffix: true })} - -
+ {healthStatus.nextScheduledRun && ( +
+ + + {t('jtbd.health_status.next_check')}:{' '} + {formatDistanceToNow(new Date(healthStatus.nextScheduledRun), { addSuffix: true })} + +
+ )}
{/* Status Checklist */} -
- {healthStatus.checklistItems.map((item, index) => { + {healthStatus.checklistItems && healthStatus.checklistItems.length > 0 && ( +
+ {healthStatus.checklistItems.map((item, index) => { const ItemIcon = iconMap[item.icon]; const iconColorClass = item.actionRequired ? 'text-amber-600' : 'text-green-600'; @@ -116,12 +121,13 @@ export function HealthStatusCard({ healthStatus, loading }: HealthStatusCardProp > - {item.text} + {item.text || ''}
); })}
+ )} {/* Summary Footer */} {(healthStatus.criticalIssues > 0 || healthStatus.pendingActions > 0) && ( diff --git a/frontend/src/components/dashboard/InsightsGrid.tsx b/frontend/src/components/dashboard/InsightsGrid.tsx index c606d4ce..9483ff8c 100644 --- a/frontend/src/components/dashboard/InsightsGrid.tsx +++ b/frontend/src/components/dashboard/InsightsGrid.tsx @@ -81,31 +81,36 @@ export function InsightsGrid({ insights, loading }: InsightsGridProps) { ); } + // Guard against undefined values + if (!insights || !insights.savings || !insights.inventory || !insights.waste || !insights.deliveries) { + return null; + } + return (
); diff --git a/frontend/src/components/dashboard/OrchestrationSummaryCard.tsx b/frontend/src/components/dashboard/OrchestrationSummaryCard.tsx index b2d2682b..3b89e847 100644 --- a/frontend/src/components/dashboard/OrchestrationSummaryCard.tsx +++ b/frontend/src/components/dashboard/OrchestrationSummaryCard.tsx @@ -35,7 +35,7 @@ export function OrchestrationSummaryCard({ summary, loading }: OrchestrationSumm const [expanded, setExpanded] = useState(false); const { t } = useTranslation('reasoning'); - if (loading) { + if (loading || !summary) { return (
@@ -62,7 +62,7 @@ export function OrchestrationSummaryCard({ summary, loading }: OrchestrationSumm

{t('jtbd.orchestration_summary.ready_to_plan')}

-

{summary.message}

+

{summary.message || ''}

@@ -90,7 +90,7 @@ export function OrchestrationSummaryCard({ summary, loading }: OrchestrationSumm
- {t('jtbd.orchestration_summary.run_info', { runNumber: summary.runNumber })} • {runTime} + {t('jtbd.orchestration_summary.run_info', { runNumber: summary.runNumber || 0 })} • {runTime} {summary.durationSeconds && ( @@ -111,16 +111,16 @@ export function OrchestrationSummaryCard({ summary, loading }: OrchestrationSumm
- {summary.purchaseOrdersSummary.length > 0 && ( + {summary.purchaseOrdersSummary && summary.purchaseOrdersSummary.length > 0 && (
    {summary.purchaseOrdersSummary.map((po, index) => (
  • - {po.supplierName} + {po.supplierName || 'Unknown Supplier'} {' • '} - {po.itemCategories.slice(0, 2).join(', ')} - {po.itemCategories.length > 2 && ` +${po.itemCategories.length - 2} more`} + {(po.itemCategories || []).slice(0, 2).join(', ') || 'Items'} + {(po.itemCategories || []).length > 2 && ` +${po.itemCategories.length - 2} more`} {' • '} - €{po.totalAmount.toFixed(2)} + €{(po.totalAmount || 0).toFixed(2)}
  • ))}
@@ -140,25 +140,25 @@ export function OrchestrationSummaryCard({ summary, loading }: OrchestrationSumm
- {summary.productionBatchesSummary.length > 0 && ( + {summary.productionBatchesSummary && summary.productionBatchesSummary.length > 0 && (
    {summary.productionBatchesSummary.slice(0, expanded ? undefined : 3).map((batch, index) => (
  • - {batch.quantity} {batch.productName} + {batch.quantity || 0} {batch.productName || 'Product'} {' • '} - ready by {new Date(batch.readyByTime).toLocaleTimeString('en-US', { + ready by {batch.readyByTime ? new Date(batch.readyByTime).toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true, - })} + }) : 'TBD'}
  • ))}
)} - {summary.productionBatchesSummary.length > 3 && ( + {summary.productionBatchesSummary && summary.productionBatchesSummary.length > 3 && (