demo seed change

This commit is contained in:
Urtzi Alfaro
2025-12-13 23:57:54 +01:00
parent f3688dfb04
commit ff830a3415
299 changed files with 20328 additions and 19485 deletions

View File

@@ -15,6 +15,7 @@ import * as orchestratorService from '../services/orchestrator';
import { suppliersService } from '../services/suppliers';
import { useBatchNotifications, useDeliveryNotifications, useOrchestrationNotifications } from '../../hooks/useEventNotifications';
import { useSSEEvents } from '../../hooks/useSSE';
import { parseISO } from 'date-fns';
// ============================================================
// Types
@@ -27,6 +28,7 @@ export interface DashboardData {
productionBatches: any[];
deliveries: any[];
orchestrationSummary: OrchestrationSummary | null;
aiInsights: any[]; // AI-generated insights for professional/enterprise tiers
// Computed/derived data
preventedIssues: any[];
@@ -70,7 +72,8 @@ export function useDashboardData(tenantId: string) {
queryKey: ['dashboard-data', tenantId],
queryFn: async () => {
const today = new Date().toISOString().split('T')[0];
const now = new Date();
const now = new Date(); // Keep for local time display
const nowUTC = new Date(); // UTC time for accurate comparison with API dates
// Parallel fetch ALL data needed by all 4 blocks (including suppliers for PO enrichment)
const [alertsResponse, pendingPOs, productionResponse, deliveriesResponse, orchestration, suppliers] = await Promise.all([
@@ -158,20 +161,20 @@ export function useDashboardData(tenantId: string) {
const overdueDeliveries = deliveries.filter((d: any) => {
if (!isPending(d.status)) return false;
const expectedDate = new Date(d.expected_delivery_date);
return expectedDate < now;
const expectedDate = parseISO(d.expected_delivery_date); // Proper UTC parsing
return expectedDate < nowUTC;
}).map((d: any) => ({
...d,
hoursOverdue: Math.ceil((now.getTime() - new Date(d.expected_delivery_date).getTime()) / (1000 * 60 * 60)),
hoursOverdue: Math.ceil((nowUTC.getTime() - parseISO(d.expected_delivery_date).getTime()) / (1000 * 60 * 60)),
}));
const pendingDeliveriesFiltered = deliveries.filter((d: any) => {
if (!isPending(d.status)) return false;
const expectedDate = new Date(d.expected_delivery_date);
return expectedDate >= now;
const expectedDate = parseISO(d.expected_delivery_date); // Proper UTC parsing
return expectedDate >= nowUTC;
}).map((d: any) => ({
...d,
hoursUntil: Math.ceil((new Date(d.expected_delivery_date).getTime() - now.getTime()) / (1000 * 60 * 60)),
hoursUntil: Math.ceil((parseISO(d.expected_delivery_date).getTime() - nowUTC.getTime()) / (1000 * 60 * 60)),
}));
// Filter production batches by status
@@ -180,10 +183,10 @@ export function useDashboardData(tenantId: string) {
if (status !== 'PENDING' && status !== 'SCHEDULED') return false;
const plannedStart = b.planned_start_time;
if (!plannedStart) return false;
return new Date(plannedStart) < now;
return parseISO(plannedStart) < nowUTC;
}).map((b: any) => ({
...b,
hoursLate: Math.ceil((now.getTime() - new Date(b.planned_start_time).getTime()) / (1000 * 60 * 60)),
hoursLate: Math.ceil((nowUTC.getTime() - parseISO(b.planned_start_time).getTime()) / (1000 * 60 * 60)),
}));
const runningBatches = productionBatches.filter((b: any) =>
@@ -195,7 +198,32 @@ export function useDashboardData(tenantId: string) {
if (status !== 'PENDING' && status !== 'SCHEDULED') return false;
const plannedStart = b.planned_start_time;
if (!plannedStart) return true; // No planned start, count as pending
return new Date(plannedStart) >= now;
return parseISO(plannedStart) >= nowUTC;
});
// Create set of batch IDs that we already show in the UI (late or running)
const lateBatchIds = new Set(lateToStartBatches.map((b: any) => b.id));
const runningBatchIds = new Set(runningBatches.map((b: any) => b.id));
// Filter alerts to exclude those for batches already shown in the UI
// This prevents duplicate display: batch card + separate alert for the same batch
const deduplicatedAlerts = alerts.filter((a: any) => {
const eventType = a.event_type || '';
const batchId = a.event_metadata?.batch_id || a.entity_links?.production_batch;
if (!batchId) return true; // Keep alerts not related to batches
// Filter out batch_start_delayed alerts for batches shown in "late to start" section
if (eventType.includes('batch_start_delayed') && lateBatchIds.has(batchId)) {
return false; // Already shown as late batch
}
// Filter out production_delay alerts for batches shown in "running" section
if (eventType.includes('production_delay') && runningBatchIds.has(batchId)) {
return false; // Already shown as running batch (with progress bar showing delay)
}
return true;
});
// Build orchestration summary
@@ -218,11 +246,12 @@ export function useDashboardData(tenantId: string) {
return {
// Raw data
alerts,
alerts: deduplicatedAlerts,
pendingPOs: enrichedPendingPOs,
productionBatches,
deliveries,
orchestrationSummary,
aiInsights: [], // AI-generated insights for professional/enterprise tiers
// Computed
preventedIssues,
@@ -283,7 +312,7 @@ export function useDashboardRealtimeSync(tenantId: string) {
if (deliveryNotifications.length === 0 || !tenantId) return;
const latest = deliveryNotifications[0];
if (['delivery_received', 'delivery_overdue'].includes(latest.event_type)) {
if (['delivery_received', 'delivery_overdue', 'delivery_arriving_soon', 'stock_receipt_incomplete'].includes(latest.event_type)) {
queryClient.invalidateQueries({
queryKey: ['dashboard-data', tenantId],
refetchType: 'active',