@optifye/dashboard-core 6.0.2 → 6.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import * as React14 from 'react';
2
- import React14__default, { createContext, useRef, useCallback, useState, useMemo, useEffect, memo, useContext, useLayoutEffect, useId, Children, isValidElement, useInsertionEffect, forwardRef, Fragment as Fragment$1, createElement, Component } from 'react';
1
+ import * as React19 from 'react';
2
+ import React19__default, { createContext, useRef, useCallback, useState, useMemo, useEffect, memo, useContext, useLayoutEffect, useId, Children, isValidElement, useInsertionEffect, forwardRef, Fragment as Fragment$1, createElement, Component } from 'react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import { useRouter } from 'next/router';
5
5
  import { toZonedTime, formatInTimeZone } from 'date-fns-tz';
@@ -10,15 +10,15 @@ import Hls2 from 'hls.js';
10
10
  import useSWR from 'swr';
11
11
  import { noop, warning, invariant, progress, secondsToMilliseconds, millisecondsToSeconds, memo as memo$1 } from 'motion-utils';
12
12
  import { getValueTransition, hover, press, isPrimaryPointer, GroupPlaybackControls, setDragLock, supportsLinearEasing, attachTimeline, isGenerator, calcGeneratorDuration, isWaapiSupportedEasing, mapEasingToNativeEasing, maxGeneratorDuration, generateLinearEasing, isBezierDefinition } from 'motion-dom';
13
- import { Bar, LabelList, ResponsiveContainer, BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, Tooltip, ReferenceLine, Cell, PieChart, Pie, Legend, LineChart as LineChart$1, Line, ComposedChart, Area, ScatterChart, Scatter } from 'recharts';
13
+ import { BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Bar, LabelList, ResponsiveContainer, LineChart as LineChart$1, Line, PieChart, Pie, Cell, ReferenceLine, ComposedChart, Area, ScatterChart, Scatter } from 'recharts';
14
14
  import { Slot } from '@radix-ui/react-slot';
15
- import { Camera, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, X, Coffee, Plus, Clock, Minus, ArrowDown, ArrowUp, Search, CheckCircle, AlertTriangle, Info, Share2, Download, User, XCircle, ChevronLeft, ChevronRight, AlertCircle, Sun, Moon, MessageSquare, Trash2, ArrowLeft, RefreshCw, Menu, Send, Copy, Edit2, UserCheck, Save, LogOut, Calendar, Settings, LifeBuoy, Loader2, ArrowLeftIcon as ArrowLeftIcon$1, Settings2, CheckCircle2, EyeOff, Eye, Zap, UserCircle } from 'lucide-react';
15
+ import { Camera, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, X, Coffee, Plus, Clock, Minus, ArrowDown, ArrowUp, Search, CheckCircle, AlertTriangle, Info, Share2, Download, User, XCircle, ChevronLeft, ChevronRight, AlertCircle, Sun, Moon, MessageSquare, Trash2, ArrowLeft, RefreshCw, Menu, Send, Copy, Edit2, UserCheck, Save, LogOut, Calendar, Package, Settings, LifeBuoy, Loader2, ArrowLeftIcon as ArrowLeftIcon$1, Settings2, CheckCircle2, EyeOff, Eye, Zap, UserCircle } from 'lucide-react';
16
16
  import { DayPicker, useNavigation as useNavigation$1 } from 'react-day-picker';
17
17
  import html2canvas from 'html2canvas';
18
18
  import jsPDF, { jsPDF as jsPDF$1 } from 'jspdf';
19
19
  import * as SelectPrimitive from '@radix-ui/react-select';
20
20
  import { S3Client, ListObjectsV2Command, GetObjectCommand } from '@aws-sdk/client-s3';
21
- import { HomeIcon, TrophyIcon, ChartBarIcon, AdjustmentsHorizontalIcon, ClockIcon, SparklesIcon, QuestionMarkCircleIcon, UserCircleIcon, ArrowRightIcon, ArrowLeftIcon, ChatBubbleLeftRightIcon, CheckCircleIcon, ExclamationCircleIcon, DocumentTextIcon, EnvelopeIcon, InformationCircleIcon, ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/24/outline';
21
+ import { HomeIcon, TrophyIcon, ChartBarIcon, AdjustmentsHorizontalIcon, ClockIcon, CubeIcon, SparklesIcon, QuestionMarkCircleIcon, UserCircleIcon, ArrowRightIcon, ArrowLeftIcon, ChatBubbleLeftRightIcon, CheckCircleIcon, ExclamationCircleIcon, DocumentTextIcon, EnvelopeIcon, InformationCircleIcon, ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/24/outline';
22
22
  import { Readable } from 'stream';
23
23
  import { toast } from 'sonner';
24
24
  import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
@@ -205,14 +205,14 @@ var _getDashboardConfigInstance = () => {
205
205
  }
206
206
  return dashboardConfigInstance;
207
207
  };
208
- var DashboardConfigContext = React14.createContext(void 0);
208
+ var DashboardConfigContext = React19.createContext(void 0);
209
209
  var DashboardProvider = ({ config: userProvidedConfig, children }) => {
210
- const fullConfig = React14.useMemo(() => mergeWithDefaultConfig(userProvidedConfig), [userProvidedConfig]);
210
+ const fullConfig = React19.useMemo(() => mergeWithDefaultConfig(userProvidedConfig), [userProvidedConfig]);
211
211
  _setDashboardConfigInstance(fullConfig);
212
- React14.useEffect(() => {
212
+ React19.useEffect(() => {
213
213
  _setDashboardConfigInstance(fullConfig);
214
214
  }, [fullConfig]);
215
- React14.useEffect(() => {
215
+ React19.useEffect(() => {
216
216
  if (!fullConfig.theme) return;
217
217
  const styleId = "dashboard-core-theme-vars";
218
218
  let styleEl = document.getElementById(styleId);
@@ -238,7 +238,7 @@ var DashboardProvider = ({ config: userProvidedConfig, children }) => {
238
238
  return /* @__PURE__ */ jsx(DashboardConfigContext.Provider, { value: fullConfig, children });
239
239
  };
240
240
  var useDashboardConfig = () => {
241
- const ctx = React14.useContext(DashboardConfigContext);
241
+ const ctx = React19.useContext(DashboardConfigContext);
242
242
  if (!ctx) throw new Error("useDashboardConfig must be used within a DashboardProvider");
243
243
  return ctx;
244
244
  };
@@ -331,6 +331,56 @@ var actionService = {
331
331
  return data || [];
332
332
  }
333
333
  };
334
+ function createMemoizedFunction(fn, getCacheKey) {
335
+ const cache = /* @__PURE__ */ new Map();
336
+ const CACHE_DURATION3 = 5 * 60 * 1e3;
337
+ return (...args) => {
338
+ const key = getCacheKey(...args);
339
+ const cached = cache.get(key);
340
+ if (cached && Date.now() - cached.timestamp < CACHE_DURATION3) {
341
+ return cached.result;
342
+ }
343
+ const result = fn(...args);
344
+ cache.set(key, { result, timestamp: Date.now() });
345
+ if (cache.size > 100) {
346
+ const firstKey = cache.keys().next().value;
347
+ if (firstKey) cache.delete(firstKey);
348
+ }
349
+ return result;
350
+ };
351
+ }
352
+ var memoizedEfficiencyFilter = createMemoizedFunction(
353
+ (data, minEfficiency, maxEfficiency) => {
354
+ return data.filter((item) => {
355
+ const eff = item.efficiency ?? 0;
356
+ return eff >= minEfficiency && (maxEfficiency === void 0 || eff < maxEfficiency);
357
+ });
358
+ },
359
+ (data, min, max = void 0) => `${data.length}-${min}-${max}`
360
+ );
361
+ var memoizedAverageEfficiency = createMemoizedFunction(
362
+ (data, minThreshold = 10) => {
363
+ const validItems = data.filter((item) => (item.efficiency ?? 0) >= minThreshold);
364
+ if (validItems.length === 0) return 0;
365
+ const sum = validItems.reduce((acc, item) => acc + (item.efficiency ?? 0), 0);
366
+ return sum / validItems.length;
367
+ },
368
+ (data, threshold) => `${data.length}-${threshold}`
369
+ );
370
+ var memoizedOutputArrayAggregation = createMemoizedFunction(
371
+ (arrays) => {
372
+ if (arrays.length === 0) return [];
373
+ const maxLength = Math.max(...arrays.map((arr) => arr.length));
374
+ const result = new Array(maxLength).fill(0);
375
+ for (const arr of arrays) {
376
+ for (let i = 0; i < arr.length; i++) {
377
+ result[i] += arr[i];
378
+ }
379
+ }
380
+ return result;
381
+ },
382
+ (arrays) => arrays.map((arr) => arr.length).join("-")
383
+ );
334
384
  var getOperationalDate = (timezone = "Asia/Kolkata", date = /* @__PURE__ */ new Date(), shiftStartTime = "06:00") => {
335
385
  const zonedDate = toZonedTime(date, timezone);
336
386
  const hours = zonedDate.getHours();
@@ -468,22 +518,32 @@ var dashboardService = {
468
518
  if (!defaultLineId || !secondaryLineId || !companyId) {
469
519
  throw new Error("Factory View requires defaultLineId, secondaryLineId, and companyId to be configured.");
470
520
  }
471
- const { data: lineData2, error: lineError2 } = await supabase.from(linesTable).select(`
472
- id,
473
- line_name,
474
- factory_id,
475
- factories!lines_factory_id_fkey(factory_name),
476
- company_id,
477
- companies!lines_company_id_fkey(company_name:name)
478
- `).eq("id", defaultLineId).maybeSingle();
479
- if (lineError2) throw lineError2;
480
- if (!lineData2) throw new Error(`Configured default line (${defaultLineId}) not found`);
481
- const { data: metricsData, error: metricsError } = await supabase.from(lineMetricsTable).select("*").in("line_id", [defaultLineId, secondaryLineId]).eq("shift_id", shiftId).eq("date", date);
482
- if (metricsError) throw metricsError;
483
- const { data: performanceData2, error: performanceError2 } = await supabase.from(metricsTable).select("efficiency").in("line_id", [defaultLineId, secondaryLineId]).eq("shift_id", shiftId).eq("date", date);
484
- if (performanceError2) throw performanceError2;
485
- const underperformingCount2 = performanceData2?.filter((w) => w.efficiency >= 10 && w.efficiency < 70).length || 0;
486
- const totalValidWorkspaces2 = performanceData2?.filter((w) => w.efficiency >= 10).length || 0;
521
+ const [lineResult, metricsResult, performanceResult] = await Promise.all([
522
+ // Get Line 1's info for general factory details
523
+ supabase.from(linesTable).select(`
524
+ id,
525
+ line_name,
526
+ factory_id,
527
+ factories!lines_factory_id_fkey(factory_name),
528
+ company_id,
529
+ companies!lines_company_id_fkey(company_name:name)
530
+ `).eq("id", defaultLineId).maybeSingle(),
531
+ // Get metrics from line_metrics table for both lines
532
+ supabase.from(lineMetricsTable).select("*").in("line_id", [defaultLineId, secondaryLineId]).eq("shift_id", shiftId).eq("date", date),
533
+ // Get performance data from the dynamic metrics table for both lines
534
+ supabase.from(metricsTable).select("efficiency").in("line_id", [defaultLineId, secondaryLineId]).eq("shift_id", shiftId).eq("date", date)
535
+ ]);
536
+ if (lineResult.error) throw lineResult.error;
537
+ if (!lineResult.data) throw new Error(`Configured default line (${defaultLineId}) not found`);
538
+ if (metricsResult.error) throw metricsResult.error;
539
+ if (performanceResult.error) throw performanceResult.error;
540
+ const lineData2 = lineResult.data;
541
+ const metricsData = metricsResult.data;
542
+ const performanceData2 = performanceResult.data;
543
+ const underperformingWorkspaces2 = memoizedEfficiencyFilter(performanceData2 || [], 10, 70);
544
+ const validWorkspaces2 = memoizedEfficiencyFilter(performanceData2 || [], 10);
545
+ const underperformingCount2 = underperformingWorkspaces2.length;
546
+ validWorkspaces2.length;
487
547
  const combinedMetrics = (metricsData || []).reduce((acc, m) => {
488
548
  acc.avg_efficiency += m.efficiency ?? 0;
489
549
  acc.current_output += m.current_output ?? 0;
@@ -503,13 +563,13 @@ var dashboardService = {
503
563
  shift_id: shiftId,
504
564
  date,
505
565
  metrics: {
506
- avg_efficiency: (performanceData2?.reduce((sum, m) => sum + (m.efficiency || 0), 0) || 0) / (totalValidWorkspaces2 || 1),
507
- // Use performance data for avg efficiency
566
+ avg_efficiency: memoizedAverageEfficiency(performanceData2 || [], 10),
567
+ // Use memoized calculation for efficiency
508
568
  avg_cycle_time: 0,
509
569
  // Needs calculation logic if required for factory view
510
570
  current_output: combinedMetrics.current_output,
511
571
  ideal_output: combinedMetrics.ideal_output,
512
- total_workspaces: 44,
572
+ total_workspaces: 0,
513
573
  // SRC ALIGNMENT: Use hardcoded 44 for factory view total_workspaces
514
574
  underperforming_workspaces: underperformingCount2,
515
575
  underperforming_workspace_names: [],
@@ -547,9 +607,11 @@ var dashboardService = {
547
607
  }
548
608
  const { data: performanceData, error: performanceError } = await supabase.from(metricsTable).select("efficiency").eq("line_id", lineId).eq("shift_id", shiftId).eq("date", date);
549
609
  if (performanceError) throw performanceError;
550
- const underperformingCount = performanceData?.filter((w) => w.efficiency >= 10 && w.efficiency < 70).length || 0;
551
- const totalValidWorkspaces = performanceData?.filter((w) => w.efficiency >= 10).length || 0;
552
- const avgEfficiencyFromPerf = (performanceData?.reduce((sum, m) => sum + (m.efficiency || 0), 0) || 0) / (totalValidWorkspaces || 1);
610
+ const underperformingWorkspaces = memoizedEfficiencyFilter(performanceData || [], 10, 70);
611
+ const validWorkspaces = memoizedEfficiencyFilter(performanceData || [], 10);
612
+ const underperformingCount = underperformingWorkspaces.length;
613
+ validWorkspaces.length;
614
+ const avgEfficiencyFromPerf = memoizedAverageEfficiency(performanceData || [], 10);
553
615
  const useFallbackMetrics = !metricsFromDb;
554
616
  let metricsForReturn;
555
617
  if (useFallbackMetrics) {
@@ -864,21 +926,29 @@ var dashboardService = {
864
926
  const queryShiftId = shiftProp !== void 0 ? shiftProp : currentShiftResult.shiftId;
865
927
  try {
866
928
  if (lineIdToQuery === factoryViewId) {
867
- if (!defaultLineId || !secondaryLineId || !companyId) {
868
- throw new Error("Factory View requires defaultLineId, secondaryLineId, and companyId to be configured.");
929
+ if (!defaultLineId || !companyId) {
930
+ throw new Error("Factory View requires at least defaultLineId and companyId to be configured.");
869
931
  }
870
- const { data: line1Data, error: line1Error } = await supabase.from(linesTable).select("id, line_name, factory_id, factories!lines_factory_id_fkey(factory_name), company_id, companies!lines_company_id_fkey(company_name:name)").eq("id", defaultLineId).single();
871
- if (line1Error) throw line1Error;
872
- if (!line1Data) {
932
+ const lineIdsToQuery = [defaultLineId, secondaryLineId].filter((id3) => id3 !== void 0 && id3 !== null);
933
+ const [line1Result, metricsResult2, performanceResult2] = await Promise.all([
934
+ supabase.from(linesTable).select("id, line_name, factory_id, factories!lines_factory_id_fkey(factory_name), company_id, companies!lines_company_id_fkey(company_name:name)").eq("id", defaultLineId).single(),
935
+ supabase.from(lineMetricsTable).select("*").in("line_id", lineIdsToQuery).eq("shift_id", queryShiftId).eq("date", queryDate),
936
+ supabase.from(metricsTable).select("efficiency").in("line_id", lineIdsToQuery).eq("shift_id", queryShiftId).eq("date", queryDate)
937
+ ]);
938
+ if (line1Result.error) throw line1Result.error;
939
+ if (!line1Result.data) {
873
940
  throw new Error(`Default line ${defaultLineId} for Factory View not found.`);
874
941
  }
875
- const { data: metricsData, error: metricsError2 } = await supabase.from(lineMetricsTable).select("*").in("line_id", [defaultLineId, secondaryLineId]).eq("shift_id", queryShiftId).eq("date", queryDate);
876
- if (metricsError2) throw metricsError2;
877
- const { data: performanceData2, error: performanceError2 } = await supabase.from(metricsTable).select("efficiency").in("line_id", [defaultLineId, secondaryLineId]).eq("shift_id", queryShiftId).eq("date", queryDate);
878
- if (performanceError2) throw performanceError2;
879
- const underperformingCount2 = performanceData2?.filter((w) => w.efficiency >= 10 && w.efficiency < 70).length || 0;
880
- const totalValidWorkspaces2 = performanceData2?.filter((w) => w.efficiency >= 10).length || 0;
881
- const avgEfficiencyFromPerf2 = (performanceData2?.reduce((sum, m) => sum + (m.efficiency || 0), 0) || 0) / (totalValidWorkspaces2 || 1);
942
+ if (metricsResult2.error) throw metricsResult2.error;
943
+ if (performanceResult2.error) throw performanceResult2.error;
944
+ const line1Data = line1Result.data;
945
+ const metricsData = metricsResult2.data;
946
+ const performanceData2 = performanceResult2.data;
947
+ const underperformingWorkspaces2 = memoizedEfficiencyFilter(performanceData2 || [], 10, 70);
948
+ const validWorkspaces2 = memoizedEfficiencyFilter(performanceData2 || [], 10);
949
+ const underperformingCount2 = underperformingWorkspaces2.length;
950
+ const totalValidWorkspaces2 = validWorkspaces2.length;
951
+ const avgEfficiencyFromPerf2 = memoizedAverageEfficiency(performanceData2 || [], 10);
882
952
  const initialAccumulator = {
883
953
  avg_cycle_time: 0,
884
954
  current_output: 0,
@@ -886,7 +956,8 @@ var dashboardService = {
886
956
  line_threshold: 0,
887
957
  threshold_pph: 0,
888
958
  total_workspaces: 0,
889
- output_array: []
959
+ output_array: [],
960
+ outputArrays: []
890
961
  };
891
962
  const combinedMetricsData = (metricsData || []).reduce((acc, m) => {
892
963
  acc.avg_cycle_time += m.avg_cycle_time ?? 0;
@@ -896,14 +967,7 @@ var dashboardService = {
896
967
  acc.threshold_pph += m.threshold_pph ?? 0;
897
968
  acc.total_workspaces += m.total_workspaces ?? 0;
898
969
  if (m.output_array && m.output_array.length > 0) {
899
- const currentMOutputArray = m.output_array;
900
- if (!acc.output_array || acc.output_array.length === 0) acc.output_array = [...currentMOutputArray];
901
- else {
902
- acc.output_array = acc.output_array.map((val, i) => val + (currentMOutputArray[i] ?? 0));
903
- if (currentMOutputArray.length > acc.output_array.length) {
904
- acc.output_array.push(...currentMOutputArray.slice(acc.output_array.length));
905
- }
906
- }
970
+ acc.outputArrays.push(m.output_array);
907
971
  }
908
972
  return acc;
909
973
  }, initialAccumulator);
@@ -924,7 +988,7 @@ var dashboardService = {
924
988
  avg_cycle_time: combinedMetricsData.avg_cycle_time / numLines,
925
989
  current_output: combinedMetricsData.current_output,
926
990
  ideal_output: combinedMetricsData.ideal_output,
927
- total_workspaces: combinedMetricsData.total_workspaces || 44,
991
+ total_workspaces: combinedMetricsData.total_workspaces || 0,
928
992
  // Use combined or default
929
993
  underperforming_workspaces: underperformingCount2,
930
994
  line_threshold: combinedMetricsData.line_threshold,
@@ -932,7 +996,7 @@ var dashboardService = {
932
996
  shift_start: metricsData?.[0]?.shift_start || shiftConfig.dayShift?.startTime || "06:00",
933
997
  shift_end: metricsData?.[0]?.shift_end || shiftConfig.dayShift?.endTime || "18:00",
934
998
  last_updated: (/* @__PURE__ */ new Date()).toISOString(),
935
- output_array: combinedMetricsData.output_array,
999
+ output_array: combinedMetricsData.outputArrays.length > 0 ? memoizedOutputArrayAggregation(combinedMetricsData.outputArrays) : [],
936
1000
  underperforming_workspace_names: [],
937
1001
  underperforming_workspace_uuids: [],
938
1002
  poorest_performing_workspaces: []
@@ -942,18 +1006,25 @@ var dashboardService = {
942
1006
  if (!companyId) {
943
1007
  throw new Error("Company ID must be configured for detailed line requests.");
944
1008
  }
945
- const { data: lineData, error: lineError } = await supabase.from(linesTable).select("id, line_name, factory_id, factories!lines_factory_id_fkey(factory_name), company_id, companies!lines_company_id_fkey(company_name:name)").eq("id", lineIdToQuery).single();
946
- if (lineError) throw lineError;
947
- if (!lineData) {
1009
+ const [lineResult, metricsResult, performanceResult] = await Promise.all([
1010
+ supabase.from(linesTable).select("id, line_name, factory_id, factories!lines_factory_id_fkey(factory_name), company_id, companies!lines_company_id_fkey(company_name:name)").eq("id", lineIdToQuery).single(),
1011
+ supabase.from(lineMetricsTable).select("*").eq("line_id", lineIdToQuery).eq("shift_id", queryShiftId).eq("date", queryDate).maybeSingle(),
1012
+ supabase.from(metricsTable).select("efficiency").eq("line_id", lineIdToQuery).eq("shift_id", queryShiftId).eq("date", queryDate)
1013
+ ]);
1014
+ if (lineResult.error) throw lineResult.error;
1015
+ if (!lineResult.data) {
948
1016
  throw new Error(`Line ${lineIdToQuery} not found.`);
949
1017
  }
950
- const { data: metrics2, error: metricsError } = await supabase.from(lineMetricsTable).select("*").eq("line_id", lineIdToQuery).eq("shift_id", queryShiftId).eq("date", queryDate).maybeSingle();
951
- if (metricsError) throw metricsError;
952
- const { data: performanceData, error: performanceError } = await supabase.from(metricsTable).select("efficiency").eq("line_id", lineIdToQuery).eq("shift_id", queryShiftId).eq("date", queryDate);
953
- if (performanceError) throw performanceError;
954
- const underperformingCount = performanceData?.filter((w) => w.efficiency >= 10 && w.efficiency < 70).length || 0;
955
- const totalValidWorkspaces = performanceData?.filter((w) => w.efficiency >= 10).length || 0;
956
- const avgEfficiencyFromPerf = (performanceData?.reduce((sum, m) => sum + (m.efficiency || 0), 0) || 0) / (totalValidWorkspaces || 1);
1018
+ if (metricsResult.error) throw metricsResult.error;
1019
+ if (performanceResult.error) throw performanceResult.error;
1020
+ const lineData = lineResult.data;
1021
+ const metrics2 = metricsResult.data;
1022
+ const performanceData = performanceResult.data;
1023
+ const underperformingWorkspaces = memoizedEfficiencyFilter(performanceData || [], 10, 70);
1024
+ const validWorkspaces = memoizedEfficiencyFilter(performanceData || [], 10);
1025
+ const underperformingCount = underperformingWorkspaces.length;
1026
+ const totalValidWorkspaces = validWorkspaces.length;
1027
+ const avgEfficiencyFromPerf = memoizedAverageEfficiency(performanceData || [], 10);
957
1028
  return {
958
1029
  line_id: lineData.id,
959
1030
  line_name: lineData.line_name,
@@ -1640,6 +1711,128 @@ var workspaceService = {
1640
1711
  }
1641
1712
  };
1642
1713
 
1714
+ // src/lib/services/skuService.ts
1715
+ var getTable4 = (dbConfig, tableName) => {
1716
+ const globalConfig = _getDashboardConfigInstance();
1717
+ const defaults2 = globalConfig?.databaseConfig?.tables;
1718
+ const userValue = dbConfig?.tables?.[tableName];
1719
+ const hardcodedDefaults = {
1720
+ skus: "skus"
1721
+ };
1722
+ return userValue ?? defaults2?.[tableName] ?? hardcodedDefaults[tableName];
1723
+ };
1724
+ var skuService = {
1725
+ /**
1726
+ * Fetch all active SKUs for a company
1727
+ */
1728
+ async getSKUs(companyId) {
1729
+ const supabase = _getSupabaseInstance();
1730
+ if (!supabase) throw new Error("Supabase client not initialized");
1731
+ const config = _getDashboardConfigInstance();
1732
+ const dbConfig = config?.databaseConfig;
1733
+ const skusTable = getTable4(dbConfig, "skus");
1734
+ const { data, error } = await supabase.from(skusTable).select("*").eq("company_id", companyId).eq("is_active", true).order("created_at", { ascending: false });
1735
+ if (error) {
1736
+ console.error(`Error fetching SKUs from ${skusTable}:`, error);
1737
+ throw error;
1738
+ }
1739
+ return data || [];
1740
+ },
1741
+ /**
1742
+ * Get a single SKU by ID
1743
+ */
1744
+ async getSKU(skuId) {
1745
+ const supabase = _getSupabaseInstance();
1746
+ if (!supabase) throw new Error("Supabase client not initialized");
1747
+ const config = _getDashboardConfigInstance();
1748
+ const dbConfig = config?.databaseConfig;
1749
+ const skusTable = getTable4(dbConfig, "skus");
1750
+ const { data, error } = await supabase.from(skusTable).select("*").eq("id", skuId).single();
1751
+ if (error) {
1752
+ console.error(`Error fetching SKU from ${skusTable}:`, error);
1753
+ throw error;
1754
+ }
1755
+ return data;
1756
+ },
1757
+ /**
1758
+ * Create a new SKU
1759
+ */
1760
+ async createSKU(skuData) {
1761
+ const supabase = _getSupabaseInstance();
1762
+ if (!supabase) throw new Error("Supabase client not initialized");
1763
+ const config = _getDashboardConfigInstance();
1764
+ const dbConfig = config?.databaseConfig;
1765
+ const skusTable = getTable4(dbConfig, "skus");
1766
+ const { data, error } = await supabase.from(skusTable).insert([{
1767
+ ...skuData,
1768
+ attributes: skuData.attributes || {},
1769
+ is_active: skuData.is_active ?? true
1770
+ }]).select("*").single();
1771
+ if (error) {
1772
+ console.error(`Error creating SKU in ${skusTable}:`, error);
1773
+ throw error;
1774
+ }
1775
+ return data;
1776
+ },
1777
+ /**
1778
+ * Update an existing SKU
1779
+ */
1780
+ async updateSKU(skuId, updates) {
1781
+ const supabase = _getSupabaseInstance();
1782
+ if (!supabase) throw new Error("Supabase client not initialized");
1783
+ const config = _getDashboardConfigInstance();
1784
+ const dbConfig = config?.databaseConfig;
1785
+ const skusTable = getTable4(dbConfig, "skus");
1786
+ const { data, error } = await supabase.from(skusTable).update({
1787
+ ...updates,
1788
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
1789
+ }).eq("id", skuId).select("*").single();
1790
+ if (error) {
1791
+ console.error(`Error updating SKU in ${skusTable}:`, error);
1792
+ throw error;
1793
+ }
1794
+ return data;
1795
+ },
1796
+ /**
1797
+ * Soft delete a SKU (set is_active to false)
1798
+ */
1799
+ async deleteSKU(skuId) {
1800
+ const supabase = _getSupabaseInstance();
1801
+ if (!supabase) throw new Error("Supabase client not initialized");
1802
+ const config = _getDashboardConfigInstance();
1803
+ const dbConfig = config?.databaseConfig;
1804
+ const skusTable = getTable4(dbConfig, "skus");
1805
+ const { error } = await supabase.from(skusTable).update({
1806
+ is_active: false,
1807
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
1808
+ }).eq("id", skuId);
1809
+ if (error) {
1810
+ console.error(`Error deleting SKU from ${skusTable}:`, error);
1811
+ throw error;
1812
+ }
1813
+ },
1814
+ /**
1815
+ * Check if a SKU ID already exists for a company
1816
+ */
1817
+ async checkSKUExists(companyId, skuId, excludeId) {
1818
+ const supabase = _getSupabaseInstance();
1819
+ if (!supabase) throw new Error("Supabase client not initialized");
1820
+ const config = _getDashboardConfigInstance();
1821
+ const dbConfig = config?.databaseConfig;
1822
+ const skusTable = getTable4(dbConfig, "skus");
1823
+ let query = supabase.from(skusTable).select("id").eq("company_id", companyId).eq("sku_id", skuId).eq("is_active", true);
1824
+ if (excludeId) {
1825
+ query = query.neq("id", excludeId);
1826
+ }
1827
+ const { data, error } = await query;
1828
+ if (error) {
1829
+ console.error(`Error checking SKU existence in ${skusTable}:`, error);
1830
+ throw error;
1831
+ }
1832
+ return data && data.length > 0 || false;
1833
+ }
1834
+ };
1835
+
1643
1836
  // src/lib/services/authCoreService.ts
1644
1837
  var AUTH_ERROR_MESSAGES = {
1645
1838
  "Invalid login credentials": "Invalid email or password",
@@ -2069,6 +2262,349 @@ async function deleteThread(threadId) {
2069
2262
  const { error } = await supabase.schema("ai").from("chat_threads").delete().eq("id", threadId);
2070
2263
  if (error) throw error;
2071
2264
  }
2265
+
2266
+ // src/lib/services/cacheService.ts
2267
+ var CacheService = class {
2268
+ constructor() {
2269
+ this.memoryCache = /* @__PURE__ */ new Map();
2270
+ this.DEFAULT_DURATION = 5 * 60 * 1e3;
2271
+ }
2272
+ // 5 minutes
2273
+ /**
2274
+ * Generate a cache key from multiple parts
2275
+ */
2276
+ generateKey(...parts) {
2277
+ return parts.filter((p) => p !== void 0 && p !== null).join(":");
2278
+ }
2279
+ /**
2280
+ * Get item from cache
2281
+ */
2282
+ get(key, options) {
2283
+ const storage = options?.storage || "memory";
2284
+ try {
2285
+ let cacheItem = null;
2286
+ if (storage === "memory") {
2287
+ cacheItem = this.memoryCache.get(key) || null;
2288
+ } else if (storage === "localStorage" || storage === "sessionStorage") {
2289
+ const stored = window[storage].getItem(key);
2290
+ if (stored) {
2291
+ cacheItem = JSON.parse(stored);
2292
+ }
2293
+ }
2294
+ if (!cacheItem) {
2295
+ return null;
2296
+ }
2297
+ if (Date.now() > cacheItem.expiresAt) {
2298
+ this.delete(key, options);
2299
+ return null;
2300
+ }
2301
+ return cacheItem.data;
2302
+ } catch (error) {
2303
+ console.error(`Error getting cache item ${key}:`, error);
2304
+ return null;
2305
+ }
2306
+ }
2307
+ /**
2308
+ * Set item in cache
2309
+ */
2310
+ set(key, data, options) {
2311
+ const storage = options?.storage || "memory";
2312
+ const duration = options?.duration || this.DEFAULT_DURATION;
2313
+ const cacheItem = {
2314
+ data,
2315
+ timestamp: Date.now(),
2316
+ expiresAt: Date.now() + duration
2317
+ };
2318
+ try {
2319
+ if (storage === "memory") {
2320
+ this.memoryCache.set(key, cacheItem);
2321
+ if (this.memoryCache.size > 100) {
2322
+ const firstKey = this.memoryCache.keys().next().value;
2323
+ if (firstKey) {
2324
+ this.memoryCache.delete(firstKey);
2325
+ }
2326
+ }
2327
+ } else if (storage === "localStorage" || storage === "sessionStorage") {
2328
+ window[storage].setItem(key, JSON.stringify(cacheItem));
2329
+ }
2330
+ } catch (error) {
2331
+ console.error(`Error setting cache item ${key}:`, error);
2332
+ }
2333
+ }
2334
+ /**
2335
+ * Delete item from cache
2336
+ */
2337
+ delete(key, options) {
2338
+ const storage = options?.storage || "memory";
2339
+ try {
2340
+ if (storage === "memory") {
2341
+ this.memoryCache.delete(key);
2342
+ } else if (storage === "localStorage" || storage === "sessionStorage") {
2343
+ window[storage].removeItem(key);
2344
+ }
2345
+ } catch (error) {
2346
+ console.error(`Error deleting cache item ${key}:`, error);
2347
+ }
2348
+ }
2349
+ /**
2350
+ * Clear all items from cache
2351
+ */
2352
+ clear(options) {
2353
+ const storage = options?.storage || "memory";
2354
+ try {
2355
+ if (storage === "memory") {
2356
+ this.memoryCache.clear();
2357
+ } else if (storage === "localStorage" || storage === "sessionStorage") {
2358
+ const keys = Object.keys(window[storage]);
2359
+ keys.forEach((key) => {
2360
+ try {
2361
+ const item = window[storage].getItem(key);
2362
+ if (item) {
2363
+ const parsed = JSON.parse(item);
2364
+ if (parsed.timestamp && parsed.expiresAt && parsed.data !== void 0) {
2365
+ window[storage].removeItem(key);
2366
+ }
2367
+ }
2368
+ } catch {
2369
+ }
2370
+ });
2371
+ }
2372
+ } catch (error) {
2373
+ console.error("Error clearing cache:", error);
2374
+ }
2375
+ }
2376
+ /**
2377
+ * Get or set item in cache with a factory function
2378
+ */
2379
+ async getOrSet(key, factory, options) {
2380
+ const cached = this.get(key, options);
2381
+ if (cached !== null) {
2382
+ return cached;
2383
+ }
2384
+ const data = await factory();
2385
+ this.set(key, data, options);
2386
+ return data;
2387
+ }
2388
+ /**
2389
+ * Invalidate cache entries matching a pattern
2390
+ */
2391
+ invalidatePattern(pattern, options) {
2392
+ const storage = options?.storage || "memory";
2393
+ const regex = typeof pattern === "string" ? new RegExp(pattern) : pattern;
2394
+ try {
2395
+ if (storage === "memory") {
2396
+ const keysToDelete = [];
2397
+ this.memoryCache.forEach((_, key) => {
2398
+ if (regex.test(key)) {
2399
+ keysToDelete.push(key);
2400
+ }
2401
+ });
2402
+ keysToDelete.forEach((key) => this.memoryCache.delete(key));
2403
+ } else if (storage === "localStorage" || storage === "sessionStorage") {
2404
+ const keys = Object.keys(window[storage]);
2405
+ keys.forEach((key) => {
2406
+ if (regex.test(key)) {
2407
+ window[storage].removeItem(key);
2408
+ }
2409
+ });
2410
+ }
2411
+ } catch (error) {
2412
+ console.error("Error invalidating cache pattern:", error);
2413
+ }
2414
+ }
2415
+ /**
2416
+ * Clean up expired items
2417
+ */
2418
+ cleanup(options) {
2419
+ const storage = options?.storage || "memory";
2420
+ const now2 = Date.now();
2421
+ try {
2422
+ if (storage === "memory") {
2423
+ const keysToDelete = [];
2424
+ this.memoryCache.forEach((item, key) => {
2425
+ if (now2 > item.expiresAt) {
2426
+ keysToDelete.push(key);
2427
+ }
2428
+ });
2429
+ keysToDelete.forEach((key) => this.memoryCache.delete(key));
2430
+ } else if (storage === "localStorage" || storage === "sessionStorage") {
2431
+ const keys = Object.keys(window[storage]);
2432
+ keys.forEach((key) => {
2433
+ try {
2434
+ const item = window[storage].getItem(key);
2435
+ if (item) {
2436
+ const parsed = JSON.parse(item);
2437
+ if (parsed.expiresAt && now2 > parsed.expiresAt) {
2438
+ window[storage].removeItem(key);
2439
+ }
2440
+ }
2441
+ } catch {
2442
+ }
2443
+ });
2444
+ }
2445
+ } catch (error) {
2446
+ console.error("Error cleaning up cache:", error);
2447
+ }
2448
+ }
2449
+ };
2450
+ var cacheService = new CacheService();
2451
+ if (typeof window !== "undefined") {
2452
+ setInterval(() => {
2453
+ cacheService.cleanup({ storage: "localStorage" });
2454
+ cacheService.cleanup({ storage: "sessionStorage" });
2455
+ cacheService.cleanup({ storage: "memory" });
2456
+ }, 60 * 1e3);
2457
+ }
2458
+
2459
+ // src/lib/services/subscriptionManager.ts
2460
+ var SubscriptionManager = class {
2461
+ constructor(supabase) {
2462
+ this.subscriptions = /* @__PURE__ */ new Map();
2463
+ this.debounceTimers = /* @__PURE__ */ new Map();
2464
+ this.supabase = supabase;
2465
+ }
2466
+ /**
2467
+ * Generate a unique key for a subscription configuration
2468
+ */
2469
+ generateKey(config) {
2470
+ const { table, schema = "public", event = "*", filter: filter2 = "" } = config;
2471
+ return `${schema}:${table}:${event}:${filter2}`;
2472
+ }
2473
+ /**
2474
+ * Subscribe to real-time changes with automatic deduplication
2475
+ */
2476
+ subscribe(config) {
2477
+ const key = this.generateKey(config);
2478
+ const existing = this.subscriptions.get(key);
2479
+ if (existing) {
2480
+ existing.configs.push(config);
2481
+ existing.refCount++;
2482
+ console.log(`[SubscriptionManager] Reusing existing subscription for ${key}, refCount: ${existing.refCount}`);
2483
+ return () => this.unsubscribe(key, config.callback);
2484
+ }
2485
+ const channelName = `subscription-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
2486
+ const channel = this.supabase.channel(channelName);
2487
+ channel.on(
2488
+ "postgres_changes",
2489
+ {
2490
+ event: config.event || "*",
2491
+ schema: config.schema || "public",
2492
+ table: config.table,
2493
+ ...config.filter && { filter: config.filter }
2494
+ },
2495
+ (payload) => {
2496
+ this.debouncedCallback(key, () => {
2497
+ const subscription = this.subscriptions.get(key);
2498
+ if (subscription) {
2499
+ subscription.configs.forEach((cfg) => {
2500
+ try {
2501
+ cfg.callback(payload);
2502
+ } catch (error) {
2503
+ console.error("[SubscriptionManager] Error in callback:", error);
2504
+ }
2505
+ });
2506
+ }
2507
+ });
2508
+ }
2509
+ );
2510
+ channel.subscribe((status) => {
2511
+ console.log(`[SubscriptionManager] Subscription status for ${key}:`, status);
2512
+ });
2513
+ this.subscriptions.set(key, {
2514
+ channel,
2515
+ configs: [config],
2516
+ refCount: 1
2517
+ });
2518
+ console.log(`[SubscriptionManager] Created new subscription for ${key}`);
2519
+ return () => this.unsubscribe(key, config.callback);
2520
+ }
2521
+ /**
2522
+ * Debounce callbacks to prevent excessive updates
2523
+ */
2524
+ debouncedCallback(key, callback, delay2 = 500) {
2525
+ const existingTimer = this.debounceTimers.get(key);
2526
+ if (existingTimer) {
2527
+ clearTimeout(existingTimer);
2528
+ }
2529
+ const timer = setTimeout(() => {
2530
+ callback();
2531
+ this.debounceTimers.delete(key);
2532
+ }, delay2);
2533
+ this.debounceTimers.set(key, timer);
2534
+ }
2535
+ /**
2536
+ * Unsubscribe from a specific subscription
2537
+ */
2538
+ unsubscribe(key, callback) {
2539
+ const subscription = this.subscriptions.get(key);
2540
+ if (!subscription) {
2541
+ console.warn(`[SubscriptionManager] No subscription found for key: ${key}`);
2542
+ return;
2543
+ }
2544
+ subscription.configs = subscription.configs.filter((cfg) => cfg.callback !== callback);
2545
+ subscription.refCount--;
2546
+ console.log(`[SubscriptionManager] Unsubscribed from ${key}, refCount: ${subscription.refCount}`);
2547
+ if (subscription.refCount <= 0) {
2548
+ this.supabase.removeChannel(subscription.channel);
2549
+ this.subscriptions.delete(key);
2550
+ const timer = this.debounceTimers.get(key);
2551
+ if (timer) {
2552
+ clearTimeout(timer);
2553
+ this.debounceTimers.delete(key);
2554
+ }
2555
+ console.log(`[SubscriptionManager] Removed subscription for ${key}`);
2556
+ }
2557
+ }
2558
+ /**
2559
+ * Subscribe to multiple tables with a single callback
2560
+ */
2561
+ subscribeMultiple(configs, callback) {
2562
+ const unsubscribeFunctions = configs.map(
2563
+ (config) => this.subscribe({ ...config, callback })
2564
+ );
2565
+ return () => {
2566
+ unsubscribeFunctions.forEach((fn) => fn());
2567
+ };
2568
+ }
2569
+ /**
2570
+ * Get current subscription statistics
2571
+ */
2572
+ getStats() {
2573
+ let totalCallbacks = 0;
2574
+ this.subscriptions.forEach((sub) => {
2575
+ totalCallbacks += sub.configs.length;
2576
+ });
2577
+ return {
2578
+ totalSubscriptions: this.subscriptions.size,
2579
+ totalCallbacks
2580
+ };
2581
+ }
2582
+ /**
2583
+ * Clean up all subscriptions
2584
+ */
2585
+ cleanup() {
2586
+ this.debounceTimers.forEach((timer) => clearTimeout(timer));
2587
+ this.debounceTimers.clear();
2588
+ this.subscriptions.forEach((subscription) => {
2589
+ this.supabase.removeChannel(subscription.channel);
2590
+ });
2591
+ this.subscriptions.clear();
2592
+ console.log("[SubscriptionManager] All subscriptions cleaned up");
2593
+ }
2594
+ };
2595
+ var managerInstance = null;
2596
+ function getSubscriptionManager(supabase) {
2597
+ if (!managerInstance) {
2598
+ managerInstance = new SubscriptionManager(supabase);
2599
+ }
2600
+ return managerInstance;
2601
+ }
2602
+ function resetSubscriptionManager() {
2603
+ if (managerInstance) {
2604
+ managerInstance.cleanup();
2605
+ managerInstance = null;
2606
+ }
2607
+ }
2072
2608
  var AuthContext = createContext({
2073
2609
  session: null,
2074
2610
  user: null,
@@ -2288,6 +2824,36 @@ var useSupabase = () => {
2288
2824
  }
2289
2825
  return context.supabase;
2290
2826
  };
2827
+ var SubscriptionManagerContext = createContext({
2828
+ subscriptionManager: null
2829
+ });
2830
+ var SubscriptionManagerProvider = ({ children }) => {
2831
+ const supabase = useSupabase();
2832
+ const subscriptionManager = useMemo(() => {
2833
+ if (!supabase) return null;
2834
+ return getSubscriptionManager(supabase);
2835
+ }, [supabase]);
2836
+ useEffect(() => {
2837
+ return () => {
2838
+ if (subscriptionManager) {
2839
+ console.log("[SubscriptionManagerProvider] Cleaning up subscriptions");
2840
+ subscriptionManager.cleanup();
2841
+ }
2842
+ };
2843
+ }, [subscriptionManager]);
2844
+ return /* @__PURE__ */ jsx(SubscriptionManagerContext.Provider, { value: { subscriptionManager }, children });
2845
+ };
2846
+ var useSubscriptionManager = () => {
2847
+ const { subscriptionManager } = useContext(SubscriptionManagerContext);
2848
+ if (!subscriptionManager) {
2849
+ throw new Error("useSubscriptionManager must be used within a SubscriptionManagerProvider");
2850
+ }
2851
+ return subscriptionManager;
2852
+ };
2853
+ var useSubscriptionManagerSafe = () => {
2854
+ const { subscriptionManager } = useContext(SubscriptionManagerContext);
2855
+ return subscriptionManager;
2856
+ };
2291
2857
  var DEFAULT_COMPANY_ID = "default-company-id";
2292
2858
  var useWorkspaceMetrics = (workspaceId) => {
2293
2859
  const supabase = useSupabase();
@@ -2544,7 +3110,7 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
2544
3110
  const defaultTimezone = dateTimeConfig.defaultTimezone;
2545
3111
  const workspaceMetricsBaseTable = databaseConfig.tables?.workspaces ?? "workspace_metrics";
2546
3112
  const workspaceActionsTable = databaseConfig.tables?.actions ?? "workspace_actions";
2547
- const fetchMetrics = useCallback(async () => {
3113
+ const fetchMetrics = useCallback(async (skipCache = false) => {
2548
3114
  if (!workspaceId || isFetchingRef.current) return;
2549
3115
  try {
2550
3116
  isFetchingRef.current = true;
@@ -2553,6 +3119,28 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
2553
3119
  const queryShiftId = shiftId !== void 0 ? shiftId : currentShift.shiftId;
2554
3120
  console.log("[useWorkspaceDetailedMetrics] Using shift ID:", queryShiftId, "from input shift:", shiftId);
2555
3121
  console.log("[useWorkspaceDetailedMetrics] Using date:", queryDate, "from input date:", date);
3122
+ const cacheKey = cacheService.generateKey(
3123
+ "workspace-detailed-metrics",
3124
+ workspaceId,
3125
+ queryDate,
3126
+ queryShiftId,
3127
+ companyId
3128
+ );
3129
+ if (!skipCache) {
3130
+ const cachedData = cacheService.get(cacheKey, {
3131
+ storage: "memory",
3132
+ duration: 5 * 60 * 1e3
3133
+ // 5 minutes
3134
+ });
3135
+ if (cachedData) {
3136
+ console.log("[useWorkspaceDetailedMetrics] Using cached data for:", cacheKey);
3137
+ setMetrics(cachedData);
3138
+ setIsLoading(false);
3139
+ updateQueueRef.current = false;
3140
+ isFetchingRef.current = false;
3141
+ return;
3142
+ }
3143
+ }
2556
3144
  console.log(`[useWorkspaceDetailedMetrics] Querying ${metricsTable} for workspace: ${workspaceId}, date: ${queryDate}, shift: ${queryShiftId}`);
2557
3145
  const { data, error: fetchError } = await supabase.from(metricsTable).select("*").eq("workspace_id", workspaceId).eq("date", queryDate).eq("shift_id", queryShiftId).maybeSingle();
2558
3146
  if (fetchError) throw fetchError;
@@ -2655,6 +3243,18 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
2655
3243
  setIsLoading(false);
2656
3244
  updateQueueRef.current = false;
2657
3245
  isFetchingRef.current = false;
3246
+ const fallbackCacheKey = cacheService.generateKey(
3247
+ "workspace-detailed-metrics",
3248
+ workspaceId,
3249
+ recentData.date,
3250
+ recentData.shift_id,
3251
+ companyId
3252
+ );
3253
+ cacheService.set(fallbackCacheKey, transformedData2, {
3254
+ storage: "memory",
3255
+ duration: 2 * 60 * 1e3
3256
+ // 2 minutes for fallback data
3257
+ });
2658
3258
  return;
2659
3259
  } else {
2660
3260
  console.warn("[useWorkspaceDetailedMetrics] No data found for workspace:", workspaceId, "at all");
@@ -2768,6 +3368,11 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
2768
3368
  ...data.sop_check !== void 0 && { sop_check: data.sop_check }
2769
3369
  };
2770
3370
  setMetrics(transformedData);
3371
+ cacheService.set(cacheKey, transformedData, {
3372
+ storage: "memory",
3373
+ duration: 5 * 60 * 1e3
3374
+ // 5 minutes
3375
+ });
2771
3376
  } catch (err) {
2772
3377
  console.error("Error fetching workspace metrics:", err);
2773
3378
  setError({ message: err.message, code: err.code });
@@ -2802,7 +3407,7 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
2802
3407
  },
2803
3408
  async (payload) => {
2804
3409
  console.log(`Received ${metricsTablePrefix} update:`, payload);
2805
- await fetchMetrics();
3410
+ await fetchMetrics(true);
2806
3411
  }
2807
3412
  ).subscribe((status) => {
2808
3413
  console.log(`Workspace detailed metrics subscription status:`, status);
@@ -2836,6 +3441,14 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
2836
3441
  matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
2837
3442
  });
2838
3443
  if (payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId) {
3444
+ const cacheKey = cacheService.generateKey(
3445
+ "workspace-detailed-metrics",
3446
+ workspaceId,
3447
+ operationalDate,
3448
+ queryShiftId,
3449
+ companyId
3450
+ );
3451
+ cacheService.delete(cacheKey, { storage: "memory" });
2839
3452
  queueUpdate();
2840
3453
  }
2841
3454
  }
@@ -2861,6 +3474,14 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
2861
3474
  matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
2862
3475
  });
2863
3476
  if (payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId) {
3477
+ const cacheKey = cacheService.generateKey(
3478
+ "workspace-detailed-metrics",
3479
+ workspaceId,
3480
+ operationalDate,
3481
+ queryShiftId,
3482
+ companyId
3483
+ );
3484
+ cacheService.delete(cacheKey, { storage: "memory" });
2864
3485
  queueUpdate();
2865
3486
  }
2866
3487
  }
@@ -2886,6 +3507,14 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
2886
3507
  matches: payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId
2887
3508
  });
2888
3509
  if (payloadData?.date === operationalDate && payloadData?.shift_id === queryShiftId) {
3510
+ const cacheKey = cacheService.generateKey(
3511
+ "workspace-detailed-metrics",
3512
+ workspaceId,
3513
+ operationalDate,
3514
+ queryShiftId,
3515
+ companyId
3516
+ );
3517
+ cacheService.delete(cacheKey, { storage: "memory" });
2889
3518
  queueUpdate();
2890
3519
  }
2891
3520
  }
@@ -2912,7 +3541,8 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
2912
3541
  metrics: metrics2,
2913
3542
  isLoading,
2914
3543
  error,
2915
- refetch: fetchMetrics
3544
+ refetch: () => fetchMetrics(true)
3545
+ // Force refresh without cache
2916
3546
  };
2917
3547
  };
2918
3548
  var useLineWorkspaceMetrics = (lineId, options) => {
@@ -5129,19 +5759,39 @@ var useActiveBreaks = (lineIds) => {
5129
5759
  let breaks = [];
5130
5760
  if (activeShift.breaks) {
5131
5761
  if (Array.isArray(activeShift.breaks)) {
5132
- breaks = activeShift.breaks.map((breakItem) => ({
5133
- startTime: breakItem.start || breakItem.startTime || "00:00",
5134
- endTime: breakItem.end || breakItem.endTime || "00:00",
5135
- duration: breakItem.duration || 0,
5136
- remarks: breakItem.remarks || breakItem.name || ""
5137
- }));
5762
+ breaks = activeShift.breaks.map((breakItem) => {
5763
+ const startTime = breakItem.start || breakItem.startTime || "00:00";
5764
+ const endTime = breakItem.end || breakItem.endTime || "00:00";
5765
+ let duration = breakItem.duration || 0;
5766
+ if (!duration || duration === 0) {
5767
+ const startMinutes = parseTimeToMinutes2(startTime);
5768
+ const endMinutes = parseTimeToMinutes2(endTime);
5769
+ duration = endMinutes < startMinutes ? endMinutes + 24 * 60 - startMinutes : endMinutes - startMinutes;
5770
+ }
5771
+ return {
5772
+ startTime,
5773
+ endTime,
5774
+ duration,
5775
+ remarks: breakItem.remarks || breakItem.name || ""
5776
+ };
5777
+ });
5138
5778
  } else if (activeShift.breaks.breaks && Array.isArray(activeShift.breaks.breaks)) {
5139
- breaks = activeShift.breaks.breaks.map((breakItem) => ({
5140
- startTime: breakItem.start || breakItem.startTime || "00:00",
5141
- endTime: breakItem.end || breakItem.endTime || "00:00",
5142
- duration: breakItem.duration || 0,
5143
- remarks: breakItem.remarks || breakItem.name || ""
5144
- }));
5779
+ breaks = activeShift.breaks.breaks.map((breakItem) => {
5780
+ const startTime = breakItem.start || breakItem.startTime || "00:00";
5781
+ const endTime = breakItem.end || breakItem.endTime || "00:00";
5782
+ let duration = breakItem.duration || 0;
5783
+ if (!duration || duration === 0) {
5784
+ const startMinutes = parseTimeToMinutes2(startTime);
5785
+ const endMinutes = parseTimeToMinutes2(endTime);
5786
+ duration = endMinutes < startMinutes ? endMinutes + 24 * 60 - startMinutes : endMinutes - startMinutes;
5787
+ }
5788
+ return {
5789
+ startTime,
5790
+ endTime,
5791
+ duration,
5792
+ remarks: breakItem.remarks || breakItem.name || ""
5793
+ };
5794
+ });
5145
5795
  }
5146
5796
  }
5147
5797
  for (const breakItem of breaks) {
@@ -5203,12 +5853,32 @@ var useAllWorkspaceMetrics = (options) => {
5203
5853
  return `${metricsTablePrefix}_${companyId.replace(/-/g, "_")}`;
5204
5854
  }, [entityConfig.companyId]);
5205
5855
  const schema = databaseConfig.schema ?? "public";
5206
- const fetchWorkspaceMetrics = useCallback(async () => {
5856
+ const fetchWorkspaceMetrics = useCallback(async (skipCache = false) => {
5207
5857
  if (!initialized) {
5208
5858
  setLoading(true);
5209
5859
  }
5210
5860
  setError(null);
5211
5861
  try {
5862
+ const cacheKey = cacheService.generateKey(
5863
+ "all-workspace-metrics",
5864
+ entityConfig.companyId,
5865
+ queryDate,
5866
+ queryShiftId
5867
+ );
5868
+ if (!skipCache && !loading) {
5869
+ const cachedData = cacheService.get(cacheKey, {
5870
+ storage: "memory",
5871
+ duration: 5 * 60 * 1e3
5872
+ // 5 minutes
5873
+ });
5874
+ if (cachedData) {
5875
+ console.log("[useAllWorkspaceMetrics] Using cached data for:", cacheKey);
5876
+ setWorkspaces(cachedData);
5877
+ setInitialized(true);
5878
+ setLoading(false);
5879
+ return;
5880
+ }
5881
+ }
5212
5882
  console.log("Fetching all workspace metrics with params:", {
5213
5883
  queryDate,
5214
5884
  queryShiftId,
@@ -5245,6 +5915,11 @@ var useAllWorkspaceMetrics = (options) => {
5245
5915
  }));
5246
5916
  setWorkspaces(transformedData);
5247
5917
  setInitialized(true);
5918
+ cacheService.set(cacheKey, transformedData, {
5919
+ storage: "memory",
5920
+ duration: 5 * 60 * 1e3
5921
+ // 5 minutes
5922
+ });
5248
5923
  } catch (err) {
5249
5924
  console.error("Error fetching all workspace metrics:", err);
5250
5925
  setError({ message: err.message, code: err.code || "FETCH_ERROR" });
@@ -5269,7 +5944,14 @@ var useAllWorkspaceMetrics = (options) => {
5269
5944
  },
5270
5945
  async (payload) => {
5271
5946
  console.log("All workspace metrics update received:", payload);
5272
- await fetchWorkspaceMetrics();
5947
+ const cacheKey = cacheService.generateKey(
5948
+ "all-workspace-metrics",
5949
+ entityConfig.companyId,
5950
+ queryDate,
5951
+ queryShiftId
5952
+ );
5953
+ cacheService.delete(cacheKey, { storage: "memory" });
5954
+ await fetchWorkspaceMetrics(true);
5273
5955
  }
5274
5956
  ).subscribe();
5275
5957
  return channel2;
@@ -5280,16 +5962,80 @@ var useAllWorkspaceMetrics = (options) => {
5280
5962
  supabase.removeChannel(channel);
5281
5963
  }
5282
5964
  };
5283
- }, [queryDate, queryShiftId, metricsTable, fetchWorkspaceMetrics, initialized, supabase, schema]);
5965
+ }, [queryDate, queryShiftId, metricsTable, fetchWorkspaceMetrics, initialized, supabase, schema, entityConfig.companyId]);
5284
5966
  useEffect(() => {
5285
5967
  setInitialized(false);
5286
5968
  }, [queryDate, queryShiftId]);
5287
- const refreshWorkspaces = fetchWorkspaceMetrics;
5969
+ const refreshWorkspaces = useCallback(() => fetchWorkspaceMetrics(true), [fetchWorkspaceMetrics]);
5288
5970
  return useMemo(
5289
5971
  () => ({ workspaces, loading, error, refreshWorkspaces }),
5290
5972
  [workspaces, loading, error, refreshWorkspaces]
5291
5973
  );
5292
5974
  };
5975
+ var useSKUs = (companyId) => {
5976
+ const [skus, setSKUs] = useState([]);
5977
+ const [isLoading, setIsLoading] = useState(true);
5978
+ const [error, setError] = useState(null);
5979
+ const supabase = useSupabase();
5980
+ const config = useDashboardConfig();
5981
+ const dbConfig = config?.databaseConfig;
5982
+ const skusTable = dbConfig?.tables?.skus || "skus";
5983
+ const fetchSKUs = useCallback(async () => {
5984
+ if (!companyId) {
5985
+ setIsLoading(false);
5986
+ return;
5987
+ }
5988
+ try {
5989
+ setIsLoading(true);
5990
+ setError(null);
5991
+ const data = await skuService.getSKUs(companyId);
5992
+ setSKUs(data);
5993
+ } catch (err) {
5994
+ setError(err instanceof Error ? err : new Error("Failed to fetch SKUs"));
5995
+ console.error("Error fetching SKUs:", err);
5996
+ } finally {
5997
+ setIsLoading(false);
5998
+ }
5999
+ }, [companyId]);
6000
+ useEffect(() => {
6001
+ fetchSKUs();
6002
+ }, [fetchSKUs]);
6003
+ useEffect(() => {
6004
+ if (!supabase || !companyId) return;
6005
+ const channel = supabase.channel(`skus:${companyId}`).on(
6006
+ "postgres_changes",
6007
+ {
6008
+ event: "*",
6009
+ schema: dbConfig?.schema || "public",
6010
+ table: skusTable,
6011
+ filter: `company_id=eq.${companyId}`
6012
+ },
6013
+ (payload) => {
6014
+ console.log("SKU change received:", payload);
6015
+ if (payload.eventType === "INSERT") {
6016
+ setSKUs((prev) => [...prev, payload.new]);
6017
+ } else if (payload.eventType === "UPDATE") {
6018
+ setSKUs(
6019
+ (prev) => prev.map(
6020
+ (sku) => sku.id === payload.new.id ? payload.new : sku
6021
+ )
6022
+ );
6023
+ } else if (payload.eventType === "DELETE") {
6024
+ setSKUs((prev) => prev.filter((sku) => sku.id !== payload.old.id));
6025
+ }
6026
+ }
6027
+ ).subscribe();
6028
+ return () => {
6029
+ supabase.removeChannel(channel);
6030
+ };
6031
+ }, [supabase, companyId, dbConfig?.schema, skusTable]);
6032
+ return {
6033
+ skus,
6034
+ isLoading,
6035
+ error,
6036
+ refetch: fetchSKUs
6037
+ };
6038
+ };
5293
6039
  var MAX_RETRIES = 10;
5294
6040
  var RETRY_DELAY = 500;
5295
6041
  function useNavigation(customNavigate) {
@@ -8521,7 +9267,7 @@ var MotionConfigContext = createContext({
8521
9267
  });
8522
9268
 
8523
9269
  // ../../node_modules/framer-motion/dist/es/components/AnimatePresence/PopChild.mjs
8524
- var PopChildMeasure = class extends React14.Component {
9270
+ var PopChildMeasure = class extends React19.Component {
8525
9271
  getSnapshotBeforeUpdate(prevProps) {
8526
9272
  const element = this.props.childRef.current;
8527
9273
  if (element && prevProps.isPresent && !this.props.isPresent) {
@@ -8576,7 +9322,7 @@ function PopChild({ children, isPresent }) {
8576
9322
  document.head.removeChild(style);
8577
9323
  };
8578
9324
  }, [isPresent]);
8579
- return jsx(PopChildMeasure, { isPresent, childRef: ref, sizeRef: size, children: React14.cloneElement(children, { ref }) });
9325
+ return jsx(PopChildMeasure, { isPresent, childRef: ref, sizeRef: size, children: React19.cloneElement(children, { ref }) });
8580
9326
  }
8581
9327
 
8582
9328
  // ../../node_modules/framer-motion/dist/es/components/AnimatePresence/PresenceChild.mjs
@@ -8613,7 +9359,7 @@ var PresenceChild = ({ children, initial, isPresent, onExitComplete, custom, pre
8613
9359
  useMemo(() => {
8614
9360
  presenceChildren.forEach((_, key) => presenceChildren.set(key, false));
8615
9361
  }, [isPresent]);
8616
- React14.useEffect(() => {
9362
+ React19.useEffect(() => {
8617
9363
  !isPresent && !presenceChildren.size && onExitComplete && onExitComplete();
8618
9364
  }, [isPresent]);
8619
9365
  if (mode === "popLayout") {
@@ -10020,7 +10766,7 @@ function removeItem(arr, item) {
10020
10766
  }
10021
10767
 
10022
10768
  // ../../node_modules/framer-motion/dist/es/utils/subscription-manager.mjs
10023
- var SubscriptionManager = class {
10769
+ var SubscriptionManager3 = class {
10024
10770
  constructor() {
10025
10771
  this.subscriptions = [];
10026
10772
  }
@@ -10149,7 +10895,7 @@ var MotionValue = class {
10149
10895
  }
10150
10896
  on(eventName, callback) {
10151
10897
  if (!this.events[eventName]) {
10152
- this.events[eventName] = new SubscriptionManager();
10898
+ this.events[eventName] = new SubscriptionManager3();
10153
10899
  }
10154
10900
  const unsubscribe = this.events[eventName].add(callback);
10155
10901
  if (eventName === "change") {
@@ -14143,7 +14889,7 @@ function createProjectionNode2({ attachResizeListener, defaultParent, measureScr
14143
14889
  }
14144
14890
  addEventListener(name, handler) {
14145
14891
  if (!this.eventHandlers.has(name)) {
14146
- this.eventHandlers.set(name, new SubscriptionManager());
14892
+ this.eventHandlers.set(name, new SubscriptionManager3());
14147
14893
  }
14148
14894
  return this.eventHandlers.get(name).add(handler);
14149
14895
  }
@@ -15733,7 +16479,7 @@ var VisualElement = class {
15733
16479
  }
15734
16480
  on(eventName, callback) {
15735
16481
  if (!this.events[eventName]) {
15736
- this.events[eventName] = new SubscriptionManager();
16482
+ this.events[eventName] = new SubscriptionManager3();
15737
16483
  }
15738
16484
  return this.events[eventName].add(callback);
15739
16485
  }
@@ -15882,7 +16628,7 @@ var LoadingPage = ({
15882
16628
  subMessage = "Please wait while we prepare your data",
15883
16629
  className
15884
16630
  }) => {
15885
- React14__default.useEffect(() => {
16631
+ React19__default.useEffect(() => {
15886
16632
  console.log("LoadingPage rendered with message:", message);
15887
16633
  const timeout = setTimeout(() => {
15888
16634
  console.warn("LoadingPage has been visible for more than 8 seconds. This might indicate an issue.");
@@ -15925,10 +16671,10 @@ var withAuth = (WrappedComponent2, options) => {
15925
16671
  return function WithAuthComponent(props) {
15926
16672
  const { session, loading } = useAuth();
15927
16673
  const router = useRouter();
15928
- React14.useEffect(() => {
16674
+ React19.useEffect(() => {
15929
16675
  console.log("withAuth state:", { loading, hasSession: !!session, requireAuth: defaultOptions.requireAuth });
15930
16676
  }, [session, loading]);
15931
- React14.useEffect(() => {
16677
+ React19.useEffect(() => {
15932
16678
  if (!loading && defaultOptions.requireAuth && !session) {
15933
16679
  console.log("Redirecting to login from withAuth");
15934
16680
  router.replace(defaultOptions.redirectTo);
@@ -16256,7 +17002,7 @@ var DebugAuth = () => {
16256
17002
  ] }) });
16257
17003
  };
16258
17004
  var DEFAULT_BAR_RADIUS = [4, 4, 0, 0];
16259
- var BarChart = ({
17005
+ var BarChartComponent = ({
16260
17006
  data,
16261
17007
  bars,
16262
17008
  xAxisDataKey = "name",
@@ -16344,7 +17090,35 @@ var BarChart = ({
16344
17090
  }
16345
17091
  return /* @__PURE__ */ jsx("div", { className: clsx("w-full", className), children: chartContent });
16346
17092
  };
16347
- var LineChart = ({
17093
+ var BarChart = React19__default.memo(BarChartComponent, (prevProps, nextProps) => {
17094
+ if (prevProps.xAxisDataKey !== nextProps.xAxisDataKey || prevProps.xAxisLabel !== nextProps.xAxisLabel || prevProps.yAxisLabel !== nextProps.yAxisLabel || prevProps.yAxisUnit !== nextProps.yAxisUnit || prevProps.layout !== nextProps.layout || prevProps.className !== nextProps.className || prevProps.showGrid !== nextProps.showGrid || prevProps.showLegend !== nextProps.showLegend || prevProps.showTooltip !== nextProps.showTooltip || prevProps.responsive !== nextProps.responsive || prevProps.aspect !== nextProps.aspect) {
17095
+ return false;
17096
+ }
17097
+ if (prevProps.data.length !== nextProps.data.length) {
17098
+ return false;
17099
+ }
17100
+ if (!prevProps.data.every((item, idx) => {
17101
+ const nextItem = nextProps.data[idx];
17102
+ return Object.keys(item).every((key) => item[key] === nextItem[key]);
17103
+ })) {
17104
+ return false;
17105
+ }
17106
+ if (prevProps.bars.length !== nextProps.bars.length) {
17107
+ return false;
17108
+ }
17109
+ if (!prevProps.bars.every((bar, idx) => {
17110
+ const nextBar = nextProps.bars[idx];
17111
+ return bar.dataKey === nextBar.dataKey && bar.name === nextBar.name && bar.fill === nextBar.fill && JSON.stringify(bar.radius) === JSON.stringify(nextBar.radius) && bar.labelList === nextBar.labelList;
17112
+ })) {
17113
+ return false;
17114
+ }
17115
+ if (prevProps.tooltipFormatter !== nextProps.tooltipFormatter || JSON.stringify(prevProps.legendPayload) !== JSON.stringify(nextProps.legendPayload)) {
17116
+ return false;
17117
+ }
17118
+ return true;
17119
+ });
17120
+ BarChart.displayName = "BarChart";
17121
+ var LineChartComponent = ({
16348
17122
  data,
16349
17123
  lines,
16350
17124
  xAxisDataKey = "name",
@@ -16435,7 +17209,35 @@ var LineChart = ({
16435
17209
  }
16436
17210
  return /* @__PURE__ */ jsx("div", { className: clsx("w-full", className), children: chartContent });
16437
17211
  };
16438
- var OutputProgressChart = ({
17212
+ var LineChart = React19__default.memo(LineChartComponent, (prevProps, nextProps) => {
17213
+ if (prevProps.xAxisDataKey !== nextProps.xAxisDataKey || prevProps.xAxisLabel !== nextProps.xAxisLabel || prevProps.yAxisLabel !== nextProps.yAxisLabel || prevProps.yAxisUnit !== nextProps.yAxisUnit || prevProps.className !== nextProps.className || prevProps.showGrid !== nextProps.showGrid || prevProps.showLegend !== nextProps.showLegend || prevProps.showTooltip !== nextProps.showTooltip || prevProps.responsive !== nextProps.responsive || prevProps.aspect !== nextProps.aspect || JSON.stringify(prevProps.yAxisDomain) !== JSON.stringify(nextProps.yAxisDomain)) {
17214
+ return false;
17215
+ }
17216
+ if (prevProps.data.length !== nextProps.data.length) {
17217
+ return false;
17218
+ }
17219
+ if (!prevProps.data.every((item, idx) => {
17220
+ const nextItem = nextProps.data[idx];
17221
+ return Object.keys(item).every((key) => item[key] === nextItem[key]);
17222
+ })) {
17223
+ return false;
17224
+ }
17225
+ if (prevProps.lines.length !== nextProps.lines.length) {
17226
+ return false;
17227
+ }
17228
+ if (!prevProps.lines.every((line, idx) => {
17229
+ const nextLine = nextProps.lines[idx];
17230
+ return line.dataKey === nextLine.dataKey && line.name === nextLine.name && line.stroke === nextLine.stroke && line.strokeWidth === nextLine.strokeWidth && line.type === nextLine.type && JSON.stringify(line.dot) === JSON.stringify(nextLine.dot) && JSON.stringify(line.activeDot) === JSON.stringify(nextLine.activeDot) && line.labelList === nextLine.labelList;
17231
+ })) {
17232
+ return false;
17233
+ }
17234
+ if (prevProps.tooltipFormatter !== nextProps.tooltipFormatter || prevProps.xAxisTickFormatter !== nextProps.xAxisTickFormatter || JSON.stringify(prevProps.legendPayload) !== JSON.stringify(nextProps.legendPayload)) {
17235
+ return false;
17236
+ }
17237
+ return true;
17238
+ });
17239
+ LineChart.displayName = "LineChart";
17240
+ var OutputProgressChartComponent = ({
16439
17241
  currentOutput,
16440
17242
  targetOutput,
16441
17243
  className = ""
@@ -16484,6 +17286,8 @@ var OutputProgressChart = ({
16484
17286
  ] }) })
16485
17287
  ] }) });
16486
17288
  };
17289
+ var OutputProgressChart = React19__default.memo(OutputProgressChartComponent);
17290
+ OutputProgressChart.displayName = "OutputProgressChart";
16487
17291
  var LargeOutputProgressChart = ({
16488
17292
  currentOutput,
16489
17293
  targetOutput,
@@ -16533,7 +17337,7 @@ var LargeOutputProgressChart = ({
16533
17337
  ] }) })
16534
17338
  ] }) });
16535
17339
  };
16536
- var CycleTimeChart = ({
17340
+ var CycleTimeChartComponent = ({
16537
17341
  data = [],
16538
17342
  className = ""
16539
17343
  }) => {
@@ -16598,6 +17402,25 @@ var CycleTimeChart = ({
16598
17402
  }
16599
17403
  ) }) });
16600
17404
  };
17405
+ var CycleTimeChart = React19__default.memo(CycleTimeChartComponent, (prevProps, nextProps) => {
17406
+ if (prevProps.className !== nextProps.className) {
17407
+ return false;
17408
+ }
17409
+ if (!prevProps.data && !nextProps.data) {
17410
+ return true;
17411
+ }
17412
+ if (!prevProps.data || !nextProps.data) {
17413
+ return false;
17414
+ }
17415
+ if (prevProps.data.length !== nextProps.data.length) {
17416
+ return false;
17417
+ }
17418
+ return prevProps.data.every((item, index) => {
17419
+ const nextItem = nextProps.data[index];
17420
+ return item.time === nextItem.time && item.value === nextItem.value;
17421
+ });
17422
+ });
17423
+ CycleTimeChart.displayName = "CycleTimeChart";
16601
17424
  var CycleTimeOverTimeChart = ({
16602
17425
  data,
16603
17426
  idealCycleTime,
@@ -16615,10 +17438,10 @@ var CycleTimeOverTimeChart = ({
16615
17438
  };
16616
17439
  const displayData = getDisplayData(data);
16617
17440
  const DURATION = displayData.length;
16618
- const [animatedData, setAnimatedData] = React14__default.useState(Array(DURATION).fill(0));
16619
- const prevDataRef = React14__default.useRef(Array(DURATION).fill(0));
16620
- const animationFrameRef = React14__default.useRef(null);
16621
- const animateToNewData = React14__default.useCallback((targetData) => {
17441
+ const [animatedData, setAnimatedData] = React19__default.useState(Array(DURATION).fill(0));
17442
+ const prevDataRef = React19__default.useRef(Array(DURATION).fill(0));
17443
+ const animationFrameRef = React19__default.useRef(null);
17444
+ const animateToNewData = React19__default.useCallback((targetData) => {
16622
17445
  const startData = [...prevDataRef.current];
16623
17446
  const startTime = performance.now();
16624
17447
  const duration = 1200;
@@ -16648,7 +17471,7 @@ var CycleTimeOverTimeChart = ({
16648
17471
  }
16649
17472
  animationFrameRef.current = requestAnimationFrame(animate);
16650
17473
  }, []);
16651
- React14__default.useEffect(() => {
17474
+ React19__default.useEffect(() => {
16652
17475
  if (JSON.stringify(data) !== JSON.stringify(prevDataRef.current)) {
16653
17476
  const processedData = getDisplayData(data);
16654
17477
  animateToNewData(processedData);
@@ -16872,7 +17695,7 @@ var CycleTimeOverTimeChart = ({
16872
17695
  renderLegend()
16873
17696
  ] });
16874
17697
  };
16875
- var Card = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
17698
+ var Card = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
16876
17699
  "div",
16877
17700
  {
16878
17701
  ref,
@@ -16884,7 +17707,7 @@ var Card = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
16884
17707
  }
16885
17708
  ));
16886
17709
  Card.displayName = "Card";
16887
- var CardHeader = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
17710
+ var CardHeader = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
16888
17711
  "div",
16889
17712
  {
16890
17713
  ref,
@@ -16893,7 +17716,7 @@ var CardHeader = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE
16893
17716
  }
16894
17717
  ));
16895
17718
  CardHeader.displayName = "CardHeader";
16896
- var CardTitle = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
17719
+ var CardTitle = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
16897
17720
  "h3",
16898
17721
  {
16899
17722
  ref,
@@ -16905,7 +17728,7 @@ var CardTitle = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE_
16905
17728
  }
16906
17729
  ));
16907
17730
  CardTitle.displayName = "CardTitle";
16908
- var CardDescription = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
17731
+ var CardDescription = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
16909
17732
  "p",
16910
17733
  {
16911
17734
  ref,
@@ -16914,9 +17737,9 @@ var CardDescription = React14.forwardRef(({ className, ...props }, ref) => /* @_
16914
17737
  }
16915
17738
  ));
16916
17739
  CardDescription.displayName = "CardDescription";
16917
- var CardContent = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
17740
+ var CardContent = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
16918
17741
  CardContent.displayName = "CardContent";
16919
- var CardFooter = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
17742
+ var CardFooter = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
16920
17743
  "div",
16921
17744
  {
16922
17745
  ref,
@@ -16992,7 +17815,7 @@ var buttonVariants = cva(
16992
17815
  }
16993
17816
  }
16994
17817
  );
16995
- var Button = React14.forwardRef(
17818
+ var Button = React19.forwardRef(
16996
17819
  ({ className, variant, size, asChild = false, ...props }, ref) => {
16997
17820
  const Comp = asChild ? Slot : "button";
16998
17821
  return /* @__PURE__ */ jsx(
@@ -17006,7 +17829,7 @@ var Button = React14.forwardRef(
17006
17829
  }
17007
17830
  );
17008
17831
  Button.displayName = "Button";
17009
- var HourlyOutputChart = ({
17832
+ var HourlyOutputChartComponent = ({
17010
17833
  data,
17011
17834
  pphThreshold,
17012
17835
  shiftStart,
@@ -17023,17 +17846,17 @@ var HourlyOutputChart = ({
17023
17846
  };
17024
17847
  const shiftStartTime = getTimeFromTimeString(shiftStart);
17025
17848
  const SHIFT_DURATION = 11;
17026
- const [animatedData, setAnimatedData] = React14__default.useState(Array(SHIFT_DURATION).fill(0));
17027
- const prevDataRef = React14__default.useRef(Array(SHIFT_DURATION).fill(0));
17028
- const animationFrameRef = React14__default.useRef(null);
17029
- const [idleBarState, setIdleBarState] = React14__default.useState({
17849
+ const [animatedData, setAnimatedData] = React19__default.useState(Array(SHIFT_DURATION).fill(0));
17850
+ const prevDataRef = React19__default.useRef(Array(SHIFT_DURATION).fill(0));
17851
+ const animationFrameRef = React19__default.useRef(null);
17852
+ const [idleBarState, setIdleBarState] = React19__default.useState({
17030
17853
  visible: showIdleTime,
17031
17854
  key: 0,
17032
17855
  shouldAnimate: false
17033
17856
  });
17034
- const prevShowIdleTimeRef = React14__default.useRef(showIdleTime);
17035
- const stateUpdateTimeoutRef = React14__default.useRef(null);
17036
- React14__default.useEffect(() => {
17857
+ const prevShowIdleTimeRef = React19__default.useRef(showIdleTime);
17858
+ const stateUpdateTimeoutRef = React19__default.useRef(null);
17859
+ React19__default.useEffect(() => {
17037
17860
  if (stateUpdateTimeoutRef.current) {
17038
17861
  clearTimeout(stateUpdateTimeoutRef.current);
17039
17862
  }
@@ -17058,7 +17881,7 @@ var HourlyOutputChart = ({
17058
17881
  }
17059
17882
  };
17060
17883
  }, [showIdleTime]);
17061
- const animateToNewData = React14__default.useCallback((targetData) => {
17884
+ const animateToNewData = React19__default.useCallback((targetData) => {
17062
17885
  const startData = [...prevDataRef.current];
17063
17886
  const startTime = performance.now();
17064
17887
  const duration = 1200;
@@ -17088,7 +17911,7 @@ var HourlyOutputChart = ({
17088
17911
  }
17089
17912
  animationFrameRef.current = requestAnimationFrame(animate);
17090
17913
  }, []);
17091
- React14__default.useEffect(() => {
17914
+ React19__default.useEffect(() => {
17092
17915
  if (JSON.stringify(data) !== JSON.stringify(prevDataRef.current)) {
17093
17916
  const shiftData = data.slice(0, SHIFT_DURATION);
17094
17917
  animateToNewData(shiftData);
@@ -17099,7 +17922,7 @@ var HourlyOutputChart = ({
17099
17922
  }
17100
17923
  };
17101
17924
  }, [data, animateToNewData]);
17102
- const formatHour = React14__default.useCallback((hourIndex) => {
17925
+ const formatHour = React19__default.useCallback((hourIndex) => {
17103
17926
  const startDecimalHour = shiftStartTime.decimalHour + hourIndex;
17104
17927
  const startHour = Math.floor(startDecimalHour) % 24;
17105
17928
  const startMinute = Math.round(startDecimalHour % 1 * 60);
@@ -17116,7 +17939,7 @@ var HourlyOutputChart = ({
17116
17939
  };
17117
17940
  return `${formatTime2(startHour, startMinute)}-${formatTime2(endHour, endMinute)}`;
17118
17941
  }, [shiftStartTime.decimalHour]);
17119
- const formatTimeRange = React14__default.useCallback((hourIndex) => {
17942
+ const formatTimeRange = React19__default.useCallback((hourIndex) => {
17120
17943
  const startDecimalHour = shiftStartTime.decimalHour + hourIndex;
17121
17944
  const startHour = Math.floor(startDecimalHour) % 24;
17122
17945
  const startMinute = Math.round(startDecimalHour % 1 * 60);
@@ -17130,7 +17953,7 @@ var HourlyOutputChart = ({
17130
17953
  };
17131
17954
  return `${formatTime2(startHour, startMinute)} - ${formatTime2(endHour, endMinute)}`;
17132
17955
  }, [shiftStartTime.decimalHour]);
17133
- const chartData = React14__default.useMemo(() => {
17956
+ const chartData = React19__default.useMemo(() => {
17134
17957
  return Array.from({ length: SHIFT_DURATION }, (_, i) => {
17135
17958
  const actualHour = (shiftStartTime.hour + i) % 24;
17136
17959
  const idleArray = idleTimeHourly?.[actualHour.toString()] || [];
@@ -17147,7 +17970,7 @@ var HourlyOutputChart = ({
17147
17970
  };
17148
17971
  });
17149
17972
  }, [animatedData, data, pphThreshold, idleTimeHourly, shiftStartTime.hour, formatHour, formatTimeRange]);
17150
- const IdleBar = React14__default.useMemo(() => {
17973
+ const IdleBar = React19__default.useMemo(() => {
17151
17974
  if (!idleBarState.visible) return null;
17152
17975
  return /* @__PURE__ */ jsx(
17153
17976
  Bar,
@@ -17464,6 +18287,33 @@ var HourlyOutputChart = ({
17464
18287
  renderLegend()
17465
18288
  ] });
17466
18289
  };
18290
+ var HourlyOutputChart = React19__default.memo(HourlyOutputChartComponent, (prevProps, nextProps) => {
18291
+ if (prevProps.pphThreshold !== nextProps.pphThreshold || prevProps.shiftStart !== nextProps.shiftStart || prevProps.showIdleTime !== nextProps.showIdleTime || prevProps.className !== nextProps.className) {
18292
+ return false;
18293
+ }
18294
+ if (prevProps.data.length !== nextProps.data.length) {
18295
+ return false;
18296
+ }
18297
+ if (!prevProps.data.every((val, idx) => val === nextProps.data[idx])) {
18298
+ return false;
18299
+ }
18300
+ const prevIdle = prevProps.idleTimeHourly || {};
18301
+ const nextIdle = nextProps.idleTimeHourly || {};
18302
+ const prevKeys = Object.keys(prevIdle);
18303
+ const nextKeys = Object.keys(nextIdle);
18304
+ if (prevKeys.length !== nextKeys.length) {
18305
+ return false;
18306
+ }
18307
+ for (const key of prevKeys) {
18308
+ if (!nextIdle[key]) return false;
18309
+ const prevArray = prevIdle[key];
18310
+ const nextArray = nextIdle[key];
18311
+ if (prevArray.length !== nextArray.length) return false;
18312
+ if (!prevArray.every((val, idx) => val === nextArray[idx])) return false;
18313
+ }
18314
+ return true;
18315
+ });
18316
+ HourlyOutputChart.displayName = "HourlyOutputChart";
17467
18317
  function getTrendArrowAndColor(trend) {
17468
18318
  if (trend > 0) {
17469
18319
  return { arrow: "\u2191", color: "text-green-400" };
@@ -17473,7 +18323,7 @@ function getTrendArrowAndColor(trend) {
17473
18323
  return { arrow: "\u2192", color: "text-gray-400" };
17474
18324
  }
17475
18325
  }
17476
- var VideoCard = React14__default.memo(({
18326
+ var VideoCard = React19__default.memo(({
17477
18327
  workspace,
17478
18328
  hlsUrl,
17479
18329
  shouldPlay,
@@ -17616,7 +18466,7 @@ var VideoCard = React14__default.memo(({
17616
18466
  });
17617
18467
  VideoCard.displayName = "VideoCard";
17618
18468
  var DEFAULT_HLS_URL = "https://192.168.5.9:8443/cam1.m3u8";
17619
- var VideoGridView = React14__default.memo(({
18469
+ var VideoGridView = React19__default.memo(({
17620
18470
  workspaces,
17621
18471
  selectedLine,
17622
18472
  className = "",
@@ -18418,7 +19268,7 @@ var EmptyStateMessage = ({
18418
19268
  iconClassName
18419
19269
  }) => {
18420
19270
  let IconContent = null;
18421
- if (React14__default.isValidElement(iconType)) {
19271
+ if (React19__default.isValidElement(iconType)) {
18422
19272
  IconContent = iconType;
18423
19273
  } else if (typeof iconType === "string") {
18424
19274
  const MappedIcon = IconMap[iconType];
@@ -18551,7 +19401,10 @@ var BreakNotificationPopup = ({
18551
19401
  /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-3 flex-1", children: [
18552
19402
  /* @__PURE__ */ jsx("div", { className: "w-2 h-2 bg-amber-500 rounded-full animate-pulse flex-shrink-0 mt-2" }),
18553
19403
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
18554
- /* @__PURE__ */ jsx("div", { className: "mb-1", children: /* @__PURE__ */ jsx("h4", { className: "font-semibold text-sm text-gray-900", children: breakItem.remarks || "Break" }) }),
19404
+ /* @__PURE__ */ jsxs("div", { className: "mb-1", children: [
19405
+ /* @__PURE__ */ jsx("h4", { className: "font-semibold text-sm text-gray-900", children: breakItem.remarks || "Break" }),
19406
+ (activeBreaks.length > 1 || lineNames[breakItem.lineId]) && /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500 mt-0.5", children: lineNames[breakItem.lineId] || `Line ${breakItem.lineId.substring(0, 8)}` })
19407
+ ] }),
18555
19408
  /* @__PURE__ */ jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-600 font-medium", children: [
18556
19409
  breakItem.startTime,
18557
19410
  " - ",
@@ -18560,8 +19413,7 @@ var BreakNotificationPopup = ({
18560
19413
  /* @__PURE__ */ jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-500", children: [
18561
19414
  formatTime2(breakItem.elapsedMinutes),
18562
19415
  " / ",
18563
- breakItem.duration,
18564
- " min"
19416
+ formatTime2(breakItem.duration)
18565
19417
  ] }) }),
18566
19418
  /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx("div", { className: "w-full bg-gray-200 rounded-full h-1.5", children: /* @__PURE__ */ jsx(
18567
19419
  "div",
@@ -21000,7 +21852,7 @@ function Skeleton({ className, ...props }) {
21000
21852
  var Select = SelectPrimitive.Root;
21001
21853
  var SelectGroup = SelectPrimitive.Group;
21002
21854
  var SelectValue = SelectPrimitive.Value;
21003
- var SelectTrigger = React14.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
21855
+ var SelectTrigger = React19.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
21004
21856
  SelectPrimitive.Trigger,
21005
21857
  {
21006
21858
  ref,
@@ -21016,7 +21868,7 @@ var SelectTrigger = React14.forwardRef(({ className, children, ...props }, ref)
21016
21868
  }
21017
21869
  ));
21018
21870
  SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
21019
- var SelectScrollUpButton = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
21871
+ var SelectScrollUpButton = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
21020
21872
  SelectPrimitive.ScrollUpButton,
21021
21873
  {
21022
21874
  ref,
@@ -21026,7 +21878,7 @@ var SelectScrollUpButton = React14.forwardRef(({ className, ...props }, ref) =>
21026
21878
  }
21027
21879
  ));
21028
21880
  SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
21029
- var SelectScrollDownButton = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
21881
+ var SelectScrollDownButton = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
21030
21882
  SelectPrimitive.ScrollDownButton,
21031
21883
  {
21032
21884
  ref,
@@ -21036,7 +21888,7 @@ var SelectScrollDownButton = React14.forwardRef(({ className, ...props }, ref) =
21036
21888
  }
21037
21889
  ));
21038
21890
  SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
21039
- var SelectContent = React14.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
21891
+ var SelectContent = React19.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
21040
21892
  SelectPrimitive.Content,
21041
21893
  {
21042
21894
  ref,
@@ -21064,7 +21916,7 @@ var SelectContent = React14.forwardRef(({ className, children, position = "poppe
21064
21916
  }
21065
21917
  ) }));
21066
21918
  SelectContent.displayName = SelectPrimitive.Content.displayName;
21067
- var SelectLabel = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
21919
+ var SelectLabel = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
21068
21920
  SelectPrimitive.Label,
21069
21921
  {
21070
21922
  ref,
@@ -21073,7 +21925,7 @@ var SelectLabel = React14.forwardRef(({ className, ...props }, ref) => /* @__PUR
21073
21925
  }
21074
21926
  ));
21075
21927
  SelectLabel.displayName = SelectPrimitive.Label.displayName;
21076
- var SelectItem = React14.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
21928
+ var SelectItem = React19.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
21077
21929
  SelectPrimitive.Item,
21078
21930
  {
21079
21931
  ref,
@@ -21089,7 +21941,7 @@ var SelectItem = React14.forwardRef(({ className, children, ...props }, ref) =>
21089
21941
  }
21090
21942
  ));
21091
21943
  SelectItem.displayName = SelectPrimitive.Item.displayName;
21092
- var SelectSeparator = React14.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
21944
+ var SelectSeparator = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
21093
21945
  SelectPrimitive.Separator,
21094
21946
  {
21095
21947
  ref,
@@ -21454,7 +22306,7 @@ function parseS3Uri(s3Uri, sopCategories) {
21454
22306
  break;
21455
22307
  case "long_cycle_time":
21456
22308
  severity = "high";
21457
- type = "bottleneck";
22309
+ type = "long_cycle_time";
21458
22310
  description = "Long Cycle Time Detected";
21459
22311
  break;
21460
22312
  case "best_cycle_time":
@@ -21498,7 +22350,7 @@ function parseS3Uri(s3Uri, sopCategories) {
21498
22350
  severity = "low";
21499
22351
  description = "Best Cycle Time Performance";
21500
22352
  } else if (normalizedViolationType.includes("long") && normalizedViolationType.includes("cycle")) {
21501
- type = "bottleneck";
22353
+ type = "long_cycle_time";
21502
22354
  severity = "high";
21503
22355
  description = "Long Cycle Time Detected";
21504
22356
  } else if (normalizedViolationType.includes("cycle") && (normalizedViolationType.includes("completion") || normalizedViolationType.includes("complete"))) {
@@ -21964,7 +22816,7 @@ var BottlenecksContent = ({
21964
22816
  className
21965
22817
  }) => {
21966
22818
  const dashboardConfig = useDashboardConfig();
21967
- const sopCategories = React14__default.useMemo(() => {
22819
+ const sopCategories = React19__default.useMemo(() => {
21968
22820
  const sopConfig = dashboardConfig?.s3Config?.sopCategories;
21969
22821
  if (!sopConfig) return null;
21970
22822
  if (sopConfig.workspaceOverrides && sopConfig.workspaceOverrides[workspaceId]) {
@@ -22047,6 +22899,7 @@ var BottlenecksContent = ({
22047
22899
  const firstWorstCycle = videos.find((v) => v.type === "worst_cycle_time");
22048
22900
  const firstSOPDeviation = videos.find((v) => v.type === "missing_quality_check");
22049
22901
  const firstCycleCompletion = videos.find((v) => v.type === "cycle_completions");
22902
+ const firstLongCycleTime = videos.find((v) => v.type === "long_cycle_time");
22050
22903
  preloadVideosUrl2([
22051
22904
  firstHigh?.src,
22052
22905
  firstMed?.src,
@@ -22055,7 +22908,8 @@ var BottlenecksContent = ({
22055
22908
  firstBestCycle?.src,
22056
22909
  firstWorstCycle?.src,
22057
22910
  firstSOPDeviation?.src,
22058
- firstCycleCompletion?.src
22911
+ firstCycleCompletion?.src,
22912
+ firstLongCycleTime?.src
22059
22913
  ].filter(Boolean));
22060
22914
  }
22061
22915
  setAllVideos(videos);
@@ -22081,7 +22935,7 @@ var BottlenecksContent = ({
22081
22935
  if (activeFilter === "worst_cycle_time") return video.type === "worst_cycle_time";
22082
22936
  if (activeFilter === "cycle_completions") return video.type === "cycle_completions";
22083
22937
  if (activeFilter === "long_cycle_time") {
22084
- return video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time");
22938
+ return video.type === "long_cycle_time";
22085
22939
  }
22086
22940
  return video.type === "bottleneck" && video.severity === activeFilter;
22087
22941
  });
@@ -22099,11 +22953,6 @@ var BottlenecksContent = ({
22099
22953
  const selectedCategory = sopCategories.find((cat) => cat.id === activeFilter);
22100
22954
  if (selectedCategory) {
22101
22955
  filtered = allVideos.filter((video) => video.type === selectedCategory.id);
22102
- if (selectedCategory.id === "long_cycle_time") {
22103
- filtered = allVideos.filter(
22104
- (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22105
- );
22106
- }
22107
22956
  }
22108
22957
  } else {
22109
22958
  if (activeFilter === "low_value") {
@@ -22117,9 +22966,7 @@ var BottlenecksContent = ({
22117
22966
  } else if (activeFilter === "cycle_completions") {
22118
22967
  filtered = allVideos.filter((video) => video.type === "cycle_completions");
22119
22968
  } else if (activeFilter === "long_cycle_time") {
22120
- filtered = allVideos.filter(
22121
- (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22122
- );
22969
+ filtered = allVideos.filter((video) => video.type === "long_cycle_time");
22123
22970
  } else {
22124
22971
  filtered = allVideos.filter((video) => video.type === "bottleneck" && video.severity === activeFilter);
22125
22972
  }
@@ -22406,13 +23253,7 @@ var BottlenecksContent = ({
22406
23253
  const counts = { total: allVideos.length };
22407
23254
  if (sopCategories && sopCategories.length > 0) {
22408
23255
  sopCategories.forEach((category) => {
22409
- if (category.id === "long_cycle_time") {
22410
- counts[category.id] = allVideos.filter(
22411
- (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22412
- ).length;
22413
- } else {
22414
- counts[category.id] = allVideos.filter((video) => video.type === category.id).length;
22415
- }
23256
+ counts[category.id] = allVideos.filter((video) => video.type === category.id).length;
22416
23257
  });
22417
23258
  } else {
22418
23259
  counts.bottlenecks = allVideos.filter((video) => video.type === "bottleneck").length;
@@ -22423,9 +23264,7 @@ var BottlenecksContent = ({
22423
23264
  counts.sopDeviations = allVideos.filter((video) => video.type === "missing_quality_check").length;
22424
23265
  counts.bestCycleTimes = allVideos.filter((video) => video.type === "best_cycle_time").length;
22425
23266
  counts.worstCycleTimes = allVideos.filter((video) => video.type === "worst_cycle_time").length;
22426
- counts.longCycleTimes = allVideos.filter(
22427
- (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22428
- ).length;
23267
+ counts.longCycleTimes = allVideos.filter((video) => video.type === "long_cycle_time").length;
22429
23268
  counts.cycleCompletions = allVideos.filter((video) => video.type === "cycle_completions").length;
22430
23269
  }
22431
23270
  return counts;
@@ -22956,7 +23795,7 @@ var arePropsEqual = (prevProps, nextProps) => {
22956
23795
  return prevProps.data.efficiency === nextProps.data.efficiency && prevProps.data.trend_score === nextProps.data.trend_score && prevProps.data.workspace_id === nextProps.data.workspace_id && prevProps.data.workspace_name === nextProps.data.workspace_name && prevProps.isBottleneck === nextProps.isBottleneck && prevProps.isLowEfficiency === nextProps.isLowEfficiency && prevProps.isVeryLowEfficiency === nextProps.isVeryLowEfficiency && // Position doesn't need deep equality check as it's generally static
22957
23796
  prevProps.position.id === nextProps.position.id;
22958
23797
  };
22959
- var WorkspaceGridItem = React14__default.memo(({
23798
+ var WorkspaceGridItem = React19__default.memo(({
22960
23799
  data,
22961
23800
  position,
22962
23801
  isBottleneck = false,
@@ -23049,7 +23888,7 @@ var WorkspaceGridItem = React14__default.memo(({
23049
23888
  );
23050
23889
  }, arePropsEqual);
23051
23890
  WorkspaceGridItem.displayName = "WorkspaceGridItem";
23052
- var WorkspaceGrid = React14__default.memo(({
23891
+ var WorkspaceGrid = React19__default.memo(({
23053
23892
  workspaces,
23054
23893
  isPdfMode = false,
23055
23894
  customWorkspacePositions,
@@ -23239,7 +24078,7 @@ var KPICard = ({
23239
24078
  }) => {
23240
24079
  useThemeConfig();
23241
24080
  const { formatNumber } = useFormatNumber();
23242
- const trendInfo = React14__default.useMemo(() => {
24081
+ const trendInfo = React19__default.useMemo(() => {
23243
24082
  let trendValue = trend || "neutral";
23244
24083
  if (change !== void 0 && trend === void 0) {
23245
24084
  trendValue = change > 0 ? "up" : change < 0 ? "down" : "neutral";
@@ -23262,7 +24101,7 @@ var KPICard = ({
23262
24101
  const shouldShowTrend = !(change === 0 && trend === void 0);
23263
24102
  return { trendValue, Icon: Icon2, colorClass, shouldShowTrend };
23264
24103
  }, [trend, change]);
23265
- const formattedValue = React14__default.useMemo(() => {
24104
+ const formattedValue = React19__default.useMemo(() => {
23266
24105
  if (title === "Quality Compliance" && typeof value === "number") {
23267
24106
  return value.toFixed(1);
23268
24107
  }
@@ -23276,7 +24115,7 @@ var KPICard = ({
23276
24115
  }
23277
24116
  return value;
23278
24117
  }, [value, title]);
23279
- const formattedChange = React14__default.useMemo(() => {
24118
+ const formattedChange = React19__default.useMemo(() => {
23280
24119
  if (change === void 0 || change === 0) return null;
23281
24120
  const absChange = Math.abs(change);
23282
24121
  return formatNumber(absChange, { minimumFractionDigits: 0, maximumFractionDigits: 1 });
@@ -23763,7 +24602,7 @@ var Breadcrumbs = ({ items }) => {
23763
24602
  }
23764
24603
  }
23765
24604
  };
23766
- return /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: "mb-1 flex items-center space-x-1 text-xs font-medium text-gray-500 dark:text-gray-400", children: items.map((item, index) => /* @__PURE__ */ jsxs(React14__default.Fragment, { children: [
24605
+ return /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: "mb-1 flex items-center space-x-1 text-xs font-medium text-gray-500 dark:text-gray-400", children: items.map((item, index) => /* @__PURE__ */ jsxs(React19__default.Fragment, { children: [
23767
24606
  index > 0 && /* @__PURE__ */ jsx(ChevronRight, { className: "h-3 w-3 text-gray-400 dark:text-gray-500" }),
23768
24607
  /* @__PURE__ */ jsxs(
23769
24608
  "span",
@@ -23955,7 +24794,9 @@ var SideNavBar = memo(({
23955
24794
  const router = useRouter();
23956
24795
  const { navigate } = useNavigation();
23957
24796
  const entityConfig = useEntityConfig();
24797
+ const dashboardConfig = useDashboardConfig();
23958
24798
  const lineId = entityConfig.defaultLineId || LINE_1_UUID;
24799
+ const skuEnabled = dashboardConfig?.skuConfig?.enabled || false;
23959
24800
  const pathname = propPathname || router.pathname;
23960
24801
  const getButtonClasses = useCallback((path) => {
23961
24802
  const isActive = pathname === path || pathname.startsWith(path + "/");
@@ -24030,6 +24871,14 @@ var SideNavBar = memo(({
24030
24871
  }
24031
24872
  }
24032
24873
  }), [navigate]);
24874
+ const handleSKUsClick = useCallback(() => navigate("/skus", {
24875
+ trackingEvent: {
24876
+ name: "SKU Management Page Clicked",
24877
+ properties: {
24878
+ source: "side_nav"
24879
+ }
24880
+ }
24881
+ }), [navigate]);
24033
24882
  const homeButtonClasses = useMemo(() => getButtonClasses("/"), [getButtonClasses, pathname]);
24034
24883
  const leaderboardButtonClasses = useMemo(() => getButtonClasses("/leaderboard"), [getButtonClasses, pathname]);
24035
24884
  const kpisButtonClasses = useMemo(() => getButtonClasses("/kpis"), [getButtonClasses, pathname]);
@@ -24038,6 +24887,7 @@ var SideNavBar = memo(({
24038
24887
  const aiAgentButtonClasses = useMemo(() => getButtonClasses("/ai-agent"), [getButtonClasses, pathname]);
24039
24888
  const profileButtonClasses = useMemo(() => getButtonClasses("/profile"), [getButtonClasses, pathname]);
24040
24889
  const helpButtonClasses = useMemo(() => getButtonClasses("/help"), [getButtonClasses, pathname]);
24890
+ const skusButtonClasses = useMemo(() => getButtonClasses("/skus"), [getButtonClasses, pathname]);
24041
24891
  return /* @__PURE__ */ jsxs("aside", { className: `w-20 h-screen bg-white shadow-lg border-r border-gray-100 flex flex-col items-center fixed ${className}`, children: [
24042
24892
  /* @__PURE__ */ jsx("div", { className: "w-full py-6 px-4 flex-shrink-0", children: /* @__PURE__ */ jsx(
24043
24893
  "button",
@@ -24138,6 +24988,21 @@ var SideNavBar = memo(({
24138
24988
  ]
24139
24989
  }
24140
24990
  ),
24991
+ skuEnabled && /* @__PURE__ */ jsxs(
24992
+ "button",
24993
+ {
24994
+ onClick: handleSKUsClick,
24995
+ className: skusButtonClasses,
24996
+ "aria-label": "SKU Management",
24997
+ tabIndex: 0,
24998
+ role: "tab",
24999
+ "aria-selected": pathname === "/skus" || pathname.startsWith("/skus/"),
25000
+ children: [
25001
+ /* @__PURE__ */ jsx(CubeIcon, { className: "w-5 h-5 mb-1" }),
25002
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium leading-tight", children: "SKUs" })
25003
+ ]
25004
+ }
25005
+ ),
24141
25006
  /* @__PURE__ */ jsxs(
24142
25007
  "button",
24143
25008
  {
@@ -24322,6 +25187,103 @@ var Header = ({
24322
25187
  ] })
24323
25188
  ] }) });
24324
25189
  };
25190
+ var LoadingState = ({
25191
+ message = "Loading...",
25192
+ subMessage,
25193
+ size = "lg",
25194
+ className = ""
25195
+ }) => {
25196
+ return /* @__PURE__ */ jsx("div", { className: `flex h-full w-full items-center justify-center bg-gray-50/50 ${className}`, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center space-y-4 text-center", children: [
25197
+ /* @__PURE__ */ jsx(LoadingSpinner_default, { size }),
25198
+ /* @__PURE__ */ jsxs("div", { children: [
25199
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-medium text-gray-900", children: message }),
25200
+ subMessage && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-600", children: subMessage })
25201
+ ] })
25202
+ ] }) });
25203
+ };
25204
+ var LoadingSkeleton = ({
25205
+ type,
25206
+ count = 1,
25207
+ className = "",
25208
+ showLoadingIndicator = true
25209
+ }) => {
25210
+ const renderSkeleton = () => {
25211
+ switch (type) {
25212
+ case "card":
25213
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3 p-4 border rounded-lg relative", children: [
25214
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-3/4" }),
25215
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-1/2" }),
25216
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-full" }),
25217
+ showLoadingIndicator && /* @__PURE__ */ jsx("div", { className: "absolute top-2 right-2", children: /* @__PURE__ */ jsxs("div", { className: "flex space-x-1", children: [
25218
+ /* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 bg-blue-500 rounded-full animate-bounce", style: { animationDelay: "0ms" } }),
25219
+ /* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 bg-blue-500 rounded-full animate-bounce", style: { animationDelay: "150ms" } }),
25220
+ /* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 bg-blue-500 rounded-full animate-bounce", style: { animationDelay: "300ms" } })
25221
+ ] }) })
25222
+ ] });
25223
+ case "chart":
25224
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3 p-4 relative", children: [
25225
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-1/3" }),
25226
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
25227
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-64 w-full" }),
25228
+ showLoadingIndicator && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "bg-white/80 rounded-lg p-3 shadow-sm", children: /* @__PURE__ */ jsxs("svg", { className: "animate-spin h-6 w-6 text-blue-500", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [
25229
+ /* @__PURE__ */ jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
25230
+ /* @__PURE__ */ jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })
25231
+ ] }) }) })
25232
+ ] })
25233
+ ] });
25234
+ case "table":
25235
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
25236
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full" }),
25237
+ [...Array(5)].map((_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-12 w-full" }, i))
25238
+ ] });
25239
+ case "list":
25240
+ return /* @__PURE__ */ jsx("div", { className: "space-y-2", children: [...Array(5)].map((_, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-3", children: [
25241
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-10 rounded-full" }),
25242
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-2", children: [
25243
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-3/4" }),
25244
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-1/2" })
25245
+ ] })
25246
+ ] }, i)) });
25247
+ case "kpi":
25248
+ return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4", children: [...Array(4)].map((_, i) => /* @__PURE__ */ jsxs("div", { className: "p-4 border rounded-lg space-y-2", children: [
25249
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-2/3" }),
25250
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-1/2" }),
25251
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-full" })
25252
+ ] }, i)) });
25253
+ case "workspace-card":
25254
+ return /* @__PURE__ */ jsxs("div", { className: "p-4 border rounded-lg space-y-3", children: [
25255
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-start", children: [
25256
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-1/3" }),
25257
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-5 w-16" })
25258
+ ] }),
25259
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-32 w-full" }),
25260
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
25261
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-full" }),
25262
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-8 w-full" })
25263
+ ] })
25264
+ ] });
25265
+ case "video-grid":
25266
+ return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: [...Array(6)].map((_, i) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
25267
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-48 w-full rounded-lg" }),
25268
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-3/4" }),
25269
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-1/2" })
25270
+ ] }, i)) });
25271
+ default:
25272
+ return /* @__PURE__ */ jsx(Skeleton, { className: "h-20 w-full" });
25273
+ }
25274
+ };
25275
+ return /* @__PURE__ */ jsx("div", { className, children: [...Array(count)].map((_, index) => /* @__PURE__ */ jsx("div", { children: renderSkeleton() }, index)) });
25276
+ };
25277
+ var LoadingInline = ({
25278
+ message,
25279
+ size = "sm",
25280
+ className = ""
25281
+ }) => {
25282
+ return /* @__PURE__ */ jsxs("div", { className: `inline-flex items-center space-x-2 ${className}`, children: [
25283
+ /* @__PURE__ */ jsx(LoadingSpinner_default, { size }),
25284
+ message && /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-600", children: message })
25285
+ ] });
25286
+ };
24325
25287
  var DEFAULT_HLS_CONFIG = {
24326
25288
  maxBufferLength: 15,
24327
25289
  // Moderate buffer length for faster startup
@@ -24581,7 +25543,7 @@ var ThreadSidebar = ({
24581
25543
  ] });
24582
25544
  };
24583
25545
  var axelProfilePng = "/axel-profile.png";
24584
- var ProfilePicture = React14__default.memo(({ alt = "Axel", className = "w-12 h-12" }) => {
25546
+ var ProfilePicture = React19__default.memo(({ alt = "Axel", className = "w-12 h-12" }) => {
24585
25547
  return /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx("div", { className: `${className} rounded-xl overflow-hidden shadow-sm`, children: /* @__PURE__ */ jsx(
24586
25548
  "img",
24587
25549
  {
@@ -24699,6 +25661,7 @@ var AIAgentView = () => {
24699
25661
  const textareaRef = useRef(null);
24700
25662
  const messagesEndRef = useRef(null);
24701
25663
  const containerRef = useRef(null);
25664
+ const renderedContentCache = useRef(/* @__PURE__ */ new Map());
24702
25665
  const { createThread, mutate: mutateThreads } = useThreads();
24703
25666
  const { messages, addMessage, setMessages } = useMessages(activeThreadId);
24704
25667
  const agnoApiUrl = config.endpoints?.agnoApiUrl || "https://optifye-agent-production.up.railway.app";
@@ -25184,6 +26147,10 @@ var AIAgentView = () => {
25184
26147
  });
25185
26148
  }
25186
26149
  const renderAssistantContent = (content) => {
26150
+ const cached = renderedContentCache.current.get(content);
26151
+ if (cached) {
26152
+ return cached;
26153
+ }
25187
26154
  const parseChartPatterns = (text) => {
25188
26155
  const chartElements = [];
25189
26156
  let lastIndex = 0;
@@ -26048,16 +27015,20 @@ var AIAgentView = () => {
26048
27015
  }
26049
27016
  };
26050
27017
  const chartContent = parseChartPatterns(content);
27018
+ let finalNode;
26051
27019
  if (chartContent) {
26052
- return /* @__PURE__ */ jsx("div", { className: "formatted-content", children: chartContent });
27020
+ finalNode = /* @__PURE__ */ jsx("div", { className: "formatted-content", children: chartContent });
27021
+ } else {
27022
+ finalNode = /* @__PURE__ */ jsx(
27023
+ "div",
27024
+ {
27025
+ className: "formatted-content",
27026
+ dangerouslySetInnerHTML: { __html: formatMessage(content) }
27027
+ }
27028
+ );
26053
27029
  }
26054
- return /* @__PURE__ */ jsx(
26055
- "div",
26056
- {
26057
- className: "formatted-content",
26058
- dangerouslySetInnerHTML: { __html: formatMessage(content) }
26059
- }
26060
- );
27030
+ renderedContentCache.current.set(content, finalNode);
27031
+ return finalNode;
26061
27032
  };
26062
27033
  return /* @__PURE__ */ jsxs("div", { className: "flex h-screen bg-white", children: [
26063
27034
  /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: {
@@ -27299,8 +28270,6 @@ var HelpView = ({
27299
28270
  var AuthenticatedHelpView = withAuth(HelpView);
27300
28271
  var HelpView_default = HelpView;
27301
28272
  var KPISection2 = KPISection;
27302
- var LoadingPageCmp = LoadingPage_default;
27303
- var LoadingOverlayCmp = LoadingOverlay_default;
27304
28273
  function HomeView({
27305
28274
  defaultLineId,
27306
28275
  factoryViewId,
@@ -27319,6 +28288,7 @@ function HomeView({
27319
28288
  const [isChangingFilter, setIsChangingFilter] = useState(false);
27320
28289
  const [errorMessage, setErrorMessage] = useState(null);
27321
28290
  const [displayNamesInitialized, setDisplayNamesInitialized] = useState(false);
28291
+ const [hasInitialDataLoaded, setHasInitialDataLoaded] = useState(false);
27322
28292
  useEffect(() => {
27323
28293
  const initDisplayNames = async () => {
27324
28294
  try {
@@ -27369,17 +28339,17 @@ function HomeView({
27369
28339
  lineId: selectedLineId,
27370
28340
  onLineMetricsUpdate
27371
28341
  });
27372
- const lineIdsForBreaks = useMemo(() => {
27373
- if (selectedLineId === factoryViewId) {
27374
- return allLineIds;
27375
- }
27376
- return [selectedLineId];
27377
- }, [selectedLineId, factoryViewId, allLineIds]);
27378
28342
  const {
27379
- activeBreaks,
28343
+ activeBreaks: allActiveBreaks,
27380
28344
  isLoading: breaksLoading,
27381
28345
  error: breaksError
27382
- } = useActiveBreaks(lineIdsForBreaks);
28346
+ } = useActiveBreaks(allLineIds);
28347
+ const activeBreaks = useMemo(() => {
28348
+ if (selectedLineId === factoryViewId) {
28349
+ return allActiveBreaks;
28350
+ }
28351
+ return allActiveBreaks.filter((breakItem) => breakItem.lineId === selectedLineId);
28352
+ }, [allActiveBreaks, selectedLineId, factoryViewId]);
27383
28353
  const memoizedWorkspaceMetrics = useMemo(() => workspaceMetrics, [
27384
28354
  // Only update reference if meaningful properties change
27385
28355
  workspaceMetrics.length,
@@ -27419,6 +28389,11 @@ function HomeView({
27419
28389
  }
27420
28390
  }
27421
28391
  }, [metricsLoading, kpisLoading, workspaceMetrics, isChangingFilter, selectedLineId, factoryViewId]);
28392
+ useEffect(() => {
28393
+ if (!metricsLoading && !kpisLoading && !hasInitialDataLoaded) {
28394
+ setHasInitialDataLoaded(true);
28395
+ }
28396
+ }, [metricsLoading, kpisLoading, hasInitialDataLoaded]);
27422
28397
  const lineTitle = useMemo(() => {
27423
28398
  return factoryName;
27424
28399
  }, [factoryName]);
@@ -27431,9 +28406,56 @@ function HomeView({
27431
28406
  /* @__PURE__ */ jsx(SelectContent, { className: "z-50 bg-white shadow-lg border border-gray-200 rounded-md", children: availableLineIds.map((id3) => /* @__PURE__ */ jsx(SelectItem, { value: id3, children: lineNames[id3] || (id3 === factoryViewId ? "All Lines" : `Line ${id3.substring(0, 4)}`) }, id3)) })
27432
28407
  ] });
27433
28408
  }, [availableLineIds, handleLineChange, selectedLineId, lineNames, factoryViewId, allLineIds.length]);
27434
- const isLoading = !isHydrated || metricsLoading || kpisLoading || isChangingFilter || displayNamesLoading || !displayNamesInitialized;
27435
- if (isLoading) {
27436
- return /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-slate-50", children: /* @__PURE__ */ jsx(LoadingPageCmp, { message: "Loading dashboard..." }) });
28409
+ const isInitialLoading = !isHydrated || !displayNamesInitialized && displayNamesLoading;
28410
+ const isDataLoading = metricsLoading || kpisLoading;
28411
+ if (isInitialLoading) {
28412
+ return /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-gradient-to-br from-slate-50 via-blue-50 to-slate-50 flex items-center justify-center", children: /* @__PURE__ */ jsx(
28413
+ motion.div,
28414
+ {
28415
+ className: "text-center",
28416
+ initial: { opacity: 0, scale: 0.9 },
28417
+ animate: { opacity: 1, scale: 1 },
28418
+ transition: { duration: 0.5 },
28419
+ children: /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-2xl shadow-2xl p-12 max-w-md mx-auto", children: [
28420
+ /* @__PURE__ */ jsx("div", { className: "relative mb-8", children: /* @__PURE__ */ jsxs("div", { className: "w-24 h-24 mx-auto", children: [
28421
+ /* @__PURE__ */ jsxs("svg", { className: "animate-spin", viewBox: "0 0 100 100", xmlns: "http://www.w3.org/2000/svg", children: [
28422
+ /* @__PURE__ */ jsx("circle", { cx: "50", cy: "50", r: "45", fill: "none", stroke: "#e5e7eb", strokeWidth: "8" }),
28423
+ /* @__PURE__ */ jsx(
28424
+ "circle",
28425
+ {
28426
+ cx: "50",
28427
+ cy: "50",
28428
+ r: "45",
28429
+ fill: "none",
28430
+ stroke: "#3b82f6",
28431
+ strokeWidth: "8",
28432
+ strokeDasharray: "150 283",
28433
+ strokeLinecap: "round",
28434
+ className: "transform -rotate-90 origin-center"
28435
+ }
28436
+ )
28437
+ ] }),
28438
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "w-16 h-16 bg-gradient-to-br from-blue-500 to-blue-600 rounded-full animate-pulse" }) })
28439
+ ] }) }),
28440
+ /* @__PURE__ */ jsx("h2", { className: "text-2xl font-bold text-gray-800 mb-2", children: "Loading Dashboard" }),
28441
+ /* @__PURE__ */ jsx("p", { className: "text-gray-600 mb-6", children: "Initializing your workspace..." }),
28442
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
28443
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center space-x-2 text-sm text-gray-500", children: [
28444
+ /* @__PURE__ */ jsx("div", { className: "w-2 h-2 bg-blue-500 rounded-full animate-pulse" }),
28445
+ /* @__PURE__ */ jsx("span", { children: "Connecting to services" })
28446
+ ] }),
28447
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center space-x-2 text-sm text-gray-500", children: [
28448
+ /* @__PURE__ */ jsx("div", { className: "w-2 h-2 bg-blue-500 rounded-full animate-pulse", style: { animationDelay: "0.2s" } }),
28449
+ /* @__PURE__ */ jsx("span", { children: "Loading workspace configurations" })
28450
+ ] }),
28451
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center space-x-2 text-sm text-gray-500", children: [
28452
+ /* @__PURE__ */ jsx("div", { className: "w-2 h-2 bg-blue-500 rounded-full animate-pulse", style: { animationDelay: "0.4s" } }),
28453
+ /* @__PURE__ */ jsx("span", { children: "Preparing dashboard view" })
28454
+ ] })
28455
+ ] })
28456
+ ] })
28457
+ }
28458
+ ) });
27437
28459
  }
27438
28460
  if (errorMessage || displayNamesError) {
27439
28461
  return /* @__PURE__ */ jsx("div", { className: "flex h-screen items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "rounded-lg bg-white p-6 shadow-lg", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-3 text-red-500", children: [
@@ -27444,52 +28466,64 @@ function HomeView({
27444
28466
  ] })
27445
28467
  ] }) }) });
27446
28468
  }
27447
- if ((metricsLoading || kpisLoading) && (!workspaceMetrics || workspaceMetrics.length === 0) && selectedLineId !== factoryViewId) {
27448
- return /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-slate-50", children: /* @__PURE__ */ jsx(LoadingPageCmp, { message: "Loading metrics..." }) });
27449
- }
27450
- return /* @__PURE__ */ jsxs(
28469
+ return /* @__PURE__ */ jsx(
27451
28470
  motion.div,
27452
28471
  {
27453
28472
  className: "flex min-h-screen bg-slate-50",
27454
28473
  initial: { opacity: 1 },
27455
28474
  animate: { opacity: 1 },
27456
- children: [
27457
- /* @__PURE__ */ jsx(LoadingOverlayCmp, { isVisible: isChangingFilter, message: "Loading new metrics..." }),
27458
- /* @__PURE__ */ jsxs("div", { className: "relative flex flex-1", children: [
27459
- /* @__PURE__ */ jsxs("main", { className: "flex flex-1 flex-col", children: [
27460
- /* @__PURE__ */ jsx("div", { className: "sticky top-0 z-30 sm:static bg-white shadow-sm border-b border-gray-200/80", children: /* @__PURE__ */ jsx("div", { className: "flex flex-col sm:flex-row sm:items-center sm:justify-between px-3 sm:px-6 lg:px-8 py-1.5 sm:py-2.5", children: /* @__PURE__ */ jsx(
27461
- DashboardHeader,
27462
- {
27463
- lineTitle,
27464
- className: "w-full",
27465
- headerControls: memoizedKPIs ? /* @__PURE__ */ jsx(KPISection2, { kpis: memoizedKPIs, className: "w-full sm:w-auto" }) : null
27466
- }
27467
- ) }) }),
27468
- /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto sm:overflow-hidden relative", children: [
27469
- lineSelectorComponent && /* @__PURE__ */ jsx("div", { className: "absolute right-3 top-2 sm:right-6 sm:top-3 z-30", children: lineSelectorComponent }),
27470
- memoizedWorkspaceMetrics.length > 0 ? /* @__PURE__ */ jsx("div", { className: "h-full sm:h-full min-h-[calc(100vh-80px)] sm:min-h-0", children: React14__default.createElement(WorkspaceGrid, {
27471
- workspaces: memoizedWorkspaceMetrics,
27472
- lineNames,
27473
- factoryView: factoryViewId,
27474
- videoSources,
27475
- className: "h-full"
27476
- }) }) : /* @__PURE__ */ jsx(NoWorkspaceData, { message: "No workspace data available. Select another line or check configurations." })
27477
- ] })
27478
- ] }),
27479
- /* @__PURE__ */ jsx(
27480
- BreakNotificationPopup,
28475
+ children: /* @__PURE__ */ jsxs("div", { className: "relative flex flex-1", children: [
28476
+ /* @__PURE__ */ jsxs("main", { className: "flex flex-1 flex-col", children: [
28477
+ /* @__PURE__ */ jsx("div", { className: "sticky top-0 z-30 sm:static bg-white shadow-sm border-b border-gray-200/80", children: /* @__PURE__ */ jsx("div", { className: "flex flex-col sm:flex-row sm:items-center sm:justify-between px-3 sm:px-6 lg:px-8 py-1.5 sm:py-2.5", children: /* @__PURE__ */ jsx(
28478
+ DashboardHeader,
27481
28479
  {
27482
- activeBreaks,
27483
- lineNames,
27484
- isVisible: !breaksLoading && !breaksError
28480
+ lineTitle,
28481
+ className: "w-full",
28482
+ headerControls: memoizedKPIs ? /* @__PURE__ */ jsx(KPISection2, { kpis: memoizedKPIs, className: "w-full sm:w-auto" }) : null
27485
28483
  }
27486
- )
27487
- ] })
27488
- ]
28484
+ ) }) }),
28485
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto sm:overflow-hidden relative", children: [
28486
+ lineSelectorComponent && /* @__PURE__ */ jsx("div", { className: "absolute right-3 top-2 sm:right-6 sm:top-3 z-30", children: lineSelectorComponent }),
28487
+ /* @__PURE__ */ jsx("div", { className: "h-full sm:h-full min-h-[calc(100vh-80px)] sm:min-h-0", children: isDataLoading && hasInitialDataLoaded && memoizedWorkspaceMetrics.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-3 sm:px-6 lg:px-8 py-4", children: /* @__PURE__ */ jsx(LoadingSkeleton, { type: "workspace-card", count: 8, className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4" }) }) : memoizedWorkspaceMetrics.length > 0 ? /* @__PURE__ */ jsx(
28488
+ motion.div,
28489
+ {
28490
+ initial: { opacity: 0, scale: 0.98 },
28491
+ animate: { opacity: 1, scale: 1 },
28492
+ transition: { duration: 0.3 },
28493
+ className: "h-full",
28494
+ children: React19__default.createElement(WorkspaceGrid, {
28495
+ workspaces: memoizedWorkspaceMetrics,
28496
+ lineNames,
28497
+ factoryView: factoryViewId,
28498
+ videoSources,
28499
+ className: "h-full"
28500
+ })
28501
+ },
28502
+ selectedLineId
28503
+ ) : /* @__PURE__ */ jsx(
28504
+ motion.div,
28505
+ {
28506
+ initial: { opacity: 0 },
28507
+ animate: { opacity: 1 },
28508
+ transition: { duration: 0.3 },
28509
+ children: /* @__PURE__ */ jsx(NoWorkspaceData, { message: "No workspace data available. Select another line or check configurations." })
28510
+ }
28511
+ ) })
28512
+ ] })
28513
+ ] }),
28514
+ /* @__PURE__ */ jsx(
28515
+ BreakNotificationPopup,
28516
+ {
28517
+ activeBreaks,
28518
+ lineNames,
28519
+ isVisible: !breaksLoading && !breaksError
28520
+ }
28521
+ )
28522
+ ] })
27489
28523
  }
27490
28524
  );
27491
28525
  }
27492
- var AuthenticatedHomeView = withAuth(React14__default.memo(HomeView));
28526
+ var AuthenticatedHomeView = withAuth(React19__default.memo(HomeView));
27493
28527
  var HomeView_default = HomeView;
27494
28528
 
27495
28529
  // src/views/kpi-detail-view.types.ts
@@ -28322,7 +29356,7 @@ var LineCard = ({ line, onClick }) => {
28322
29356
  const { kpis, isLoading, error } = useLineKPIs({ lineId: line.id });
28323
29357
  const shiftConfig = useShiftConfig();
28324
29358
  const dateTimeConfig = useDateTimeConfig();
28325
- const isOnTrack = React14__default.useMemo(() => {
29359
+ const isOnTrack = React19__default.useMemo(() => {
28326
29360
  if (!kpis) return null;
28327
29361
  const currentTime = /* @__PURE__ */ new Date();
28328
29362
  const timezone = dateTimeConfig.defaultTimezone || "Asia/Kolkata";
@@ -30347,7 +31381,7 @@ var BulkConfigureModal = ({
30347
31381
  },
30348
31382
  className: "w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm\n shadow-sm focus:border-blue-500 focus:ring-blue-500 \n transition-all duration-200 hover:border-blue-400",
30349
31383
  min: "0",
30350
- step: "1",
31384
+ step: "0.01",
30351
31385
  placeholder: "Enter cycle time"
30352
31386
  }
30353
31387
  ),
@@ -30468,6 +31502,343 @@ var BulkConfigureModal = ({
30468
31502
  );
30469
31503
  };
30470
31504
  var BulkConfigureModal_default = BulkConfigureModal;
31505
+ var SKUModal = ({
31506
+ isOpen,
31507
+ onClose,
31508
+ onSave,
31509
+ editingSKU
31510
+ }) => {
31511
+ const config = useDashboardConfig();
31512
+ const defaultProductionTarget = config?.skuConfig?.defaultProductionTarget || 1e3;
31513
+ const [formData, setFormData] = useState({
31514
+ sku_id: "",
31515
+ production_target: defaultProductionTarget,
31516
+ attributes: {}
31517
+ });
31518
+ const [isSaving, setIsSaving] = useState(false);
31519
+ const [error, setError] = useState(null);
31520
+ useEffect(() => {
31521
+ if (editingSKU) {
31522
+ setFormData({
31523
+ sku_id: editingSKU.sku_id,
31524
+ production_target: editingSKU.production_target,
31525
+ attributes: editingSKU.attributes || {}
31526
+ });
31527
+ } else {
31528
+ setFormData({
31529
+ sku_id: "",
31530
+ production_target: defaultProductionTarget,
31531
+ attributes: {}
31532
+ });
31533
+ }
31534
+ setError(null);
31535
+ }, [editingSKU, defaultProductionTarget]);
31536
+ const handleSubmit = async (e) => {
31537
+ e.preventDefault();
31538
+ setError(null);
31539
+ if (!formData.sku_id.trim()) {
31540
+ setError("SKU ID is required");
31541
+ return;
31542
+ }
31543
+ if (formData.production_target <= 0) {
31544
+ setError("Production target must be greater than 0");
31545
+ return;
31546
+ }
31547
+ setIsSaving(true);
31548
+ try {
31549
+ const skuData = {
31550
+ sku_id: formData.sku_id.trim(),
31551
+ production_target: formData.production_target,
31552
+ attributes: formData.attributes,
31553
+ company_id: config?.entityConfig?.companyId || ""
31554
+ };
31555
+ await onSave(skuData);
31556
+ onClose();
31557
+ } catch (err) {
31558
+ setError(err instanceof Error ? err.message : "Failed to save SKU");
31559
+ } finally {
31560
+ setIsSaving(false);
31561
+ }
31562
+ };
31563
+ if (!isOpen) return null;
31564
+ return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50 p-4", children: /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-2xl max-w-md w-full transform transition-all", children: [
31565
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-6 border-b border-gray-200", children: [
31566
+ /* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold text-gray-900", children: editingSKU ? "Edit SKU" : "Add New SKU" }),
31567
+ /* @__PURE__ */ jsx(
31568
+ "button",
31569
+ {
31570
+ onClick: onClose,
31571
+ className: "text-gray-400 hover:text-gray-600 transition-colors p-1 hover:bg-gray-100 rounded-lg",
31572
+ disabled: isSaving,
31573
+ children: /* @__PURE__ */ jsx(X, { className: "w-5 h-5" })
31574
+ }
31575
+ )
31576
+ ] }),
31577
+ /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "p-6", children: [
31578
+ error && /* @__PURE__ */ jsx("div", { className: "mb-4 p-3 bg-red-50 border border-red-200 text-red-700 rounded-lg text-sm", children: error }),
31579
+ /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
31580
+ /* @__PURE__ */ jsxs("div", { children: [
31581
+ /* @__PURE__ */ jsxs("label", { htmlFor: "sku_id", className: "block text-sm font-medium text-gray-700 mb-2", children: [
31582
+ "SKU ID ",
31583
+ /* @__PURE__ */ jsx("span", { className: "text-red-500", children: "*" })
31584
+ ] }),
31585
+ /* @__PURE__ */ jsx(
31586
+ "input",
31587
+ {
31588
+ type: "text",
31589
+ id: "sku_id",
31590
+ value: formData.sku_id,
31591
+ onChange: (e) => setFormData({ ...formData, sku_id: e.target.value }),
31592
+ className: "w-full px-4 py-2.5 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200",
31593
+ placeholder: "Enter SKU ID",
31594
+ disabled: isSaving || !!editingSKU,
31595
+ required: true
31596
+ }
31597
+ )
31598
+ ] }),
31599
+ /* @__PURE__ */ jsxs("div", { children: [
31600
+ /* @__PURE__ */ jsxs("label", { htmlFor: "production_target", className: "block text-sm font-medium text-gray-700 mb-2", children: [
31601
+ "Production Target ",
31602
+ /* @__PURE__ */ jsx("span", { className: "text-red-500", children: "*" })
31603
+ ] }),
31604
+ /* @__PURE__ */ jsx(
31605
+ "input",
31606
+ {
31607
+ type: "number",
31608
+ id: "production_target",
31609
+ value: formData.production_target,
31610
+ onChange: (e) => setFormData({ ...formData, production_target: parseInt(e.target.value) || 0 }),
31611
+ className: "w-full px-4 py-2.5 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200",
31612
+ placeholder: "Enter production target",
31613
+ min: "1",
31614
+ disabled: isSaving,
31615
+ required: true
31616
+ }
31617
+ ),
31618
+ /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-sm text-gray-500", children: "Units per day for this SKU" })
31619
+ ] }),
31620
+ /* @__PURE__ */ jsxs("div", { children: [
31621
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Additional Attributes (Optional)" }),
31622
+ /* @__PURE__ */ jsx(
31623
+ "textarea",
31624
+ {
31625
+ value: JSON.stringify(formData.attributes, null, 2),
31626
+ onChange: (e) => {
31627
+ try {
31628
+ const parsed = JSON.parse(e.target.value);
31629
+ setFormData({ ...formData, attributes: parsed });
31630
+ } catch {
31631
+ }
31632
+ },
31633
+ className: "w-full px-4 py-2.5 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent font-mono text-sm transition-all duration-200",
31634
+ rows: 4,
31635
+ placeholder: '{"color": "red", "size": "large"}',
31636
+ disabled: isSaving
31637
+ }
31638
+ ),
31639
+ /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-sm text-gray-500", children: "Enter valid JSON for additional SKU attributes" })
31640
+ ] })
31641
+ ] }),
31642
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-end space-x-3 mt-6 pt-4 border-t border-gray-200", children: [
31643
+ /* @__PURE__ */ jsx(
31644
+ "button",
31645
+ {
31646
+ type: "button",
31647
+ onClick: onClose,
31648
+ className: "px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 transition-all duration-200",
31649
+ disabled: isSaving,
31650
+ children: "Cancel"
31651
+ }
31652
+ ),
31653
+ /* @__PURE__ */ jsx(
31654
+ "button",
31655
+ {
31656
+ type: "submit",
31657
+ className: "px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 shadow-sm",
31658
+ disabled: isSaving,
31659
+ children: isSaving ? "Saving..." : editingSKU ? "Update" : "Create"
31660
+ }
31661
+ )
31662
+ ] })
31663
+ ] })
31664
+ ] }) });
31665
+ };
31666
+ var SKUSelector = ({
31667
+ onSelect,
31668
+ selectedSKU,
31669
+ availableSKUs,
31670
+ className = "",
31671
+ lineId,
31672
+ disabled = false,
31673
+ required = false
31674
+ }) => {
31675
+ const [isOpen, setIsOpen] = useState(false);
31676
+ const [searchTerm, setSearchTerm] = useState("");
31677
+ const dropdownRef = useRef(null);
31678
+ useEffect(() => {
31679
+ const handleClickOutside = (event) => {
31680
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
31681
+ setIsOpen(false);
31682
+ }
31683
+ };
31684
+ document.addEventListener("mousedown", handleClickOutside);
31685
+ return () => {
31686
+ document.removeEventListener("mousedown", handleClickOutside);
31687
+ };
31688
+ }, []);
31689
+ const filteredSKUs = availableSKUs.filter(
31690
+ (sku) => sku.sku_id.toLowerCase().includes(searchTerm.toLowerCase()) || JSON.stringify(sku.attributes).toLowerCase().includes(searchTerm.toLowerCase())
31691
+ );
31692
+ const handleSelect = (sku) => {
31693
+ onSelect(sku);
31694
+ setIsOpen(false);
31695
+ setSearchTerm("");
31696
+ };
31697
+ return /* @__PURE__ */ jsxs("div", { className: `relative ${className}`, ref: dropdownRef, children: [
31698
+ /* @__PURE__ */ jsx(
31699
+ "button",
31700
+ {
31701
+ type: "button",
31702
+ onClick: () => !disabled && setIsOpen(!isOpen),
31703
+ className: `
31704
+ w-full px-3 py-2 text-left bg-white border rounded-md shadow-sm
31705
+ ${disabled ? "bg-gray-100 cursor-not-allowed" : "hover:bg-gray-50 cursor-pointer"}
31706
+ ${required && !selectedSKU ? "border-red-300" : "border-gray-300"}
31707
+ focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500
31708
+ `,
31709
+ disabled,
31710
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
31711
+ /* @__PURE__ */ jsx("span", { className: selectedSKU ? "text-gray-900" : "text-gray-500", children: selectedSKU ? /* @__PURE__ */ jsxs("div", { children: [
31712
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: selectedSKU.sku_id }),
31713
+ /* @__PURE__ */ jsxs("span", { className: "text-sm text-gray-500 ml-2", children: [
31714
+ "(Target: ",
31715
+ selectedSKU.production_target,
31716
+ " units/day)"
31717
+ ] })
31718
+ ] }) : "Select SKU" }),
31719
+ /* @__PURE__ */ jsx(ChevronDown, { className: `w-4 h-4 text-gray-400 transition-transform ${isOpen ? "rotate-180" : ""}` })
31720
+ ] })
31721
+ }
31722
+ ),
31723
+ isOpen && /* @__PURE__ */ jsxs("div", { className: "absolute z-10 w-full mt-1 bg-white border border-gray-300 rounded-md shadow-lg", children: [
31724
+ /* @__PURE__ */ jsx("div", { className: "p-2 border-b", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
31725
+ /* @__PURE__ */ jsx(Search, { className: "absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" }),
31726
+ /* @__PURE__ */ jsx(
31727
+ "input",
31728
+ {
31729
+ type: "text",
31730
+ value: searchTerm,
31731
+ onChange: (e) => setSearchTerm(e.target.value),
31732
+ placeholder: "Search SKUs...",
31733
+ className: "w-full pl-9 pr-3 py-2 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500",
31734
+ onClick: (e) => e.stopPropagation()
31735
+ }
31736
+ )
31737
+ ] }) }),
31738
+ /* @__PURE__ */ jsxs("div", { className: "max-h-60 overflow-y-auto", children: [
31739
+ !required && /* @__PURE__ */ jsx(
31740
+ "button",
31741
+ {
31742
+ onClick: () => handleSelect(null),
31743
+ className: "w-full px-3 py-2 text-left text-sm hover:bg-gray-100 focus:outline-none focus:bg-gray-100",
31744
+ children: /* @__PURE__ */ jsx("span", { className: "text-gray-500", children: "No SKU" })
31745
+ }
31746
+ ),
31747
+ filteredSKUs.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-sm text-gray-500", children: "No SKUs found" }) : filteredSKUs.map((sku) => /* @__PURE__ */ jsx(
31748
+ "button",
31749
+ {
31750
+ onClick: () => handleSelect(sku),
31751
+ className: `
31752
+ w-full px-3 py-2 text-left text-sm hover:bg-gray-100 focus:outline-none focus:bg-gray-100
31753
+ ${selectedSKU?.id === sku.id ? "bg-blue-50" : ""}
31754
+ `,
31755
+ children: /* @__PURE__ */ jsxs("div", { children: [
31756
+ /* @__PURE__ */ jsx("div", { className: "font-medium", children: sku.sku_id }),
31757
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-500", children: [
31758
+ "Target: ",
31759
+ sku.production_target,
31760
+ " units/day",
31761
+ Object.keys(sku.attributes || {}).length > 0 && /* @__PURE__ */ jsxs("span", { className: "ml-2", children: [
31762
+ "\u2022 ",
31763
+ Object.entries(sku.attributes).map(([key, value]) => `${key}: ${value}`).join(", ")
31764
+ ] })
31765
+ ] })
31766
+ ] })
31767
+ },
31768
+ sku.id
31769
+ ))
31770
+ ] })
31771
+ ] })
31772
+ ] });
31773
+ };
31774
+ var SKUList = ({
31775
+ skus,
31776
+ onEdit,
31777
+ onDelete,
31778
+ isLoading = false
31779
+ }) => {
31780
+ if (isLoading) {
31781
+ return /* @__PURE__ */ jsx("div", { className: "bg-white rounded-xl shadow-sm border border-gray-200 p-8", children: /* @__PURE__ */ jsxs("div", { className: "flex justify-center items-center", children: [
31782
+ /* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600" }),
31783
+ /* @__PURE__ */ jsx("span", { className: "ml-3 text-gray-600", children: "Loading SKUs..." })
31784
+ ] }) });
31785
+ }
31786
+ if (skus.length === 0) {
31787
+ return /* @__PURE__ */ jsx("div", { className: "bg-white rounded-xl shadow-sm border border-gray-200 p-12", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
31788
+ /* @__PURE__ */ jsx(Package, { className: "mx-auto h-12 w-12 text-gray-400" }),
31789
+ /* @__PURE__ */ jsx("h3", { className: "mt-2 text-lg font-medium text-gray-900", children: "No SKUs found" }),
31790
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Get started by creating a new SKU for production planning." })
31791
+ ] }) });
31792
+ }
31793
+ return /* @__PURE__ */ jsx("div", { className: "bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "min-w-full divide-y divide-gray-200", children: [
31794
+ /* @__PURE__ */ jsx("thead", { className: "bg-gray-50", children: /* @__PURE__ */ jsxs("tr", { children: [
31795
+ /* @__PURE__ */ jsx("th", { scope: "col", className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "SKU ID" }),
31796
+ /* @__PURE__ */ jsx("th", { scope: "col", className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Production Target" }),
31797
+ /* @__PURE__ */ jsx("th", { scope: "col", className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Attributes" }),
31798
+ /* @__PURE__ */ jsx("th", { scope: "col", className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Status" }),
31799
+ /* @__PURE__ */ jsx("th", { scope: "col", className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Created" }),
31800
+ /* @__PURE__ */ jsx("th", { scope: "col", className: "relative px-6 py-3", children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Actions" }) })
31801
+ ] }) }),
31802
+ /* @__PURE__ */ jsx("tbody", { className: "bg-white divide-y divide-gray-200", children: skus.map((sku) => /* @__PURE__ */ jsxs("tr", { className: "hover:bg-gray-50 transition-colors duration-150", children: [
31803
+ /* @__PURE__ */ jsx("td", { className: "px-6 py-4 whitespace-nowrap", children: /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-gray-900", children: sku.sku_id }) }),
31804
+ /* @__PURE__ */ jsx("td", { className: "px-6 py-4 whitespace-nowrap", children: /* @__PURE__ */ jsxs("div", { className: "text-sm font-medium text-gray-700", children: [
31805
+ sku.production_target.toLocaleString(),
31806
+ " units/day"
31807
+ ] }) }),
31808
+ /* @__PURE__ */ jsx("td", { className: "px-6 py-4", children: /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-500", children: Object.keys(sku.attributes || {}).length === 0 ? /* @__PURE__ */ jsx("span", { className: "text-gray-400", children: "No attributes" }) : /* @__PURE__ */ jsx("div", { className: "max-w-xs truncate", children: Object.entries(sku.attributes).map(([key, value], index) => /* @__PURE__ */ jsxs("span", { children: [
31809
+ index > 0 && ", ",
31810
+ /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
31811
+ key,
31812
+ ":"
31813
+ ] }),
31814
+ " ",
31815
+ String(value)
31816
+ ] }, key)) }) }) }),
31817
+ /* @__PURE__ */ jsx("td", { className: "px-6 py-4 whitespace-nowrap", children: /* @__PURE__ */ jsx("span", { className: `inline-flex px-2 py-1 text-xs font-semibold rounded-full ${sku.is_active ? "bg-green-100 text-green-800" : "bg-gray-100 text-gray-800"}`, children: sku.is_active ? "Active" : "Inactive" }) }),
31818
+ /* @__PURE__ */ jsx("td", { className: "px-6 py-4 whitespace-nowrap text-sm text-gray-500", children: new Date(sku.created_at).toLocaleDateString() }),
31819
+ /* @__PURE__ */ jsxs("td", { className: "px-6 py-4 whitespace-nowrap text-right text-sm font-medium", children: [
31820
+ /* @__PURE__ */ jsx(
31821
+ "button",
31822
+ {
31823
+ onClick: () => onEdit(sku),
31824
+ className: "text-blue-600 hover:text-blue-800 mr-3 transition-colors duration-150 p-1 hover:bg-blue-50 rounded",
31825
+ title: "Edit SKU",
31826
+ children: /* @__PURE__ */ jsx(Edit2, { className: "w-4 h-4" })
31827
+ }
31828
+ ),
31829
+ /* @__PURE__ */ jsx(
31830
+ "button",
31831
+ {
31832
+ onClick: () => onDelete(sku),
31833
+ className: "text-red-600 hover:text-red-800 transition-colors duration-150 p-1 hover:bg-red-50 rounded",
31834
+ title: "Delete SKU",
31835
+ children: /* @__PURE__ */ jsx(Trash2, { className: "w-4 h-4" })
31836
+ }
31837
+ )
31838
+ ] })
31839
+ ] }, sku.id)) })
31840
+ ] }) }) });
31841
+ };
30471
31842
  var TargetsViewUI = ({
30472
31843
  isLoading,
30473
31844
  lineWorkspaces,
@@ -30486,7 +31857,12 @@ var TargetsViewUI = ({
30486
31857
  onShiftChange,
30487
31858
  onSaveLine,
30488
31859
  onToggleBulkConfigure,
30489
- onBulkConfigure
31860
+ onBulkConfigure,
31861
+ // SKU props
31862
+ skuEnabled = false,
31863
+ skus = [],
31864
+ onUpdateSelectedSKU,
31865
+ skuRequired = false
30490
31866
  }) => {
30491
31867
  if (isLoading) {
30492
31868
  return /* @__PURE__ */ jsx("div", { className: "flex h-screen bg-gray-50", children: /* @__PURE__ */ jsx("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsx(Loader2, { className: "w-8 h-8 animate-spin text-blue-600" }) }) });
@@ -30602,6 +31978,26 @@ var TargetsViewUI = ({
30602
31978
  ] })
30603
31979
  ] }) }),
30604
31980
  line.isOpen && /* @__PURE__ */ jsxs("div", { id: `line-${lineId}-content`, className: "border-t border-gray-200", children: [
31981
+ skuEnabled && /* @__PURE__ */ jsx("div", { className: "px-6 py-4 border-b border-gray-200 bg-gray-50/50", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
31982
+ /* @__PURE__ */ jsx("label", { htmlFor: `sku-${lineId}`, className: "text-sm font-medium text-gray-700", children: "Select SKU:" }),
31983
+ /* @__PURE__ */ jsx("div", { className: "flex-1 max-w-md", children: /* @__PURE__ */ jsx(
31984
+ SKUSelector,
31985
+ {
31986
+ onSelect: (sku) => onUpdateSelectedSKU?.(lineId, sku),
31987
+ selectedSKU: line.selectedSKU || null,
31988
+ availableSKUs: skus,
31989
+ lineId,
31990
+ required: skuRequired,
31991
+ className: "w-full"
31992
+ }
31993
+ ) }),
31994
+ line.selectedSKU && /* @__PURE__ */ jsxs("div", { className: "text-sm text-gray-600", children: [
31995
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Production Target:" }),
31996
+ " ",
31997
+ line.selectedSKU.production_target.toLocaleString(),
31998
+ " units/day"
31999
+ ] })
32000
+ ] }) }),
30605
32001
  /* @__PURE__ */ jsx("div", { className: "px-6 py-3 bg-gray-50 border-b border-gray-200", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-12 gap-6 text-sm font-medium text-gray-600", children: [
30606
32002
  /* @__PURE__ */ jsx("div", { className: "col-span-2", children: "Workspace" }),
30607
32003
  /* @__PURE__ */ jsx("div", { className: "col-span-2", children: "Action Type" }),
@@ -30648,7 +32044,7 @@ var TargetsViewUI = ({
30648
32044
  onChange: (e) => onUpdateWorkspaceTarget(lineId, workspace.id, "targetCycleTime", Number(e.target.value) || ""),
30649
32045
  className: "block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm\n shadow-sm focus:border-blue-500 focus:ring-blue-500 \n transition-all duration-200 hover:border-gray-400",
30650
32046
  min: "0",
30651
- step: "1",
32047
+ step: "0.01",
30652
32048
  placeholder: "Enter cycle time"
30653
32049
  }
30654
32050
  ) }),
@@ -30739,6 +32135,9 @@ var TargetsView = ({
30739
32135
  const supabase = useSupabase();
30740
32136
  const auth = useAuth();
30741
32137
  userId || auth?.user?.id;
32138
+ const dashboardConfig = useDashboardConfig();
32139
+ const { skus, isLoading: skusLoading } = useSKUs(companyId);
32140
+ const skuEnabled = dashboardConfig?.skuConfig?.enabled || false;
30742
32141
  useEffect(() => {
30743
32142
  const fetchLineDetails = async () => {
30744
32143
  if (!supabase || lineIds.length === 0) return;
@@ -31106,6 +32505,41 @@ var TargetsView = ({
31106
32505
  }
31107
32506
  }));
31108
32507
  };
32508
+ const updateSelectedSKU = (lineId, sku) => {
32509
+ setLineWorkspaces((prev) => ({
32510
+ ...prev,
32511
+ [lineId]: {
32512
+ ...prev[lineId],
32513
+ selectedSKU: sku,
32514
+ productId: sku?.sku_id || prev[lineId].productId
32515
+ // Update productId with SKU ID
32516
+ }
32517
+ }));
32518
+ if (sku && sku.production_target > 0) {
32519
+ const lineData = lineWorkspaces[lineId];
32520
+ const workspaceCount = lineData.workspaces.length;
32521
+ if (workspaceCount > 0) {
32522
+ const targetPerWorkspace = Math.floor(sku.production_target / workspaceCount);
32523
+ const pphPerWorkspace = calculatePPH(
32524
+ lineData.shiftHours > 0 ? lineData.shiftHours * 3600 / targetPerWorkspace : 0,
32525
+ lineData.breaks,
32526
+ lineData.shiftHours
32527
+ );
32528
+ setLineWorkspaces((prev) => ({
32529
+ ...prev,
32530
+ [lineId]: {
32531
+ ...prev[lineId],
32532
+ workspaces: prev[lineId].workspaces.map((ws) => ({
32533
+ ...ws,
32534
+ targetDayOutput: targetPerWorkspace,
32535
+ targetPPH: pphPerWorkspace,
32536
+ targetCycleTime: lineData.shiftHours > 0 ? parseFloat((lineData.shiftHours * 3600 / targetPerWorkspace).toFixed(2)) : ""
32537
+ }))
32538
+ }
32539
+ }));
32540
+ }
32541
+ }
32542
+ };
31109
32543
  const updateWorkspaceTarget = (lineId, workspaceId, field, value) => {
31110
32544
  setLineWorkspaces((prev) => {
31111
32545
  const shiftHours = prev[lineId].shiftHours;
@@ -31238,6 +32672,11 @@ var TargetsView = ({
31238
32672
  return;
31239
32673
  }
31240
32674
  console.log(`[handleSaveLine] factoryId for ${lineId}: ${lineDataToSave.factoryId}`);
32675
+ if (skuEnabled && dashboardConfig?.skuConfig?.requireSKUSelection && !lineDataToSave.selectedSKU) {
32676
+ console.log(`[handleSaveLine] Exiting: SKU selection required but not selected for lineId: ${lineId}`);
32677
+ toast.error("Please select a SKU before saving.");
32678
+ return;
32679
+ }
31241
32680
  const currentFactoryId = lineDataToSave.factoryId;
31242
32681
  const currentDate = getOperationalDate();
31243
32682
  console.log(`[handleSaveLine] currentDate: ${currentDate}, selectedShift: ${selectedShift}`);
@@ -31251,8 +32690,9 @@ var TargetsView = ({
31251
32690
  ideal_cycle_time: Number(ws.targetCycleTime) || 0,
31252
32691
  total_day_output: Number(ws.targetDayOutput) || 0,
31253
32692
  action_name: ws.actionType === "assembly" ? ACTION_NAMES.ASSEMBLY : ACTION_NAMES.PACKAGING,
31254
- updated_by: currentEffectiveUserId
32693
+ updated_by: currentEffectiveUserId,
31255
32694
  // Use the potentially hardcoded ID
32695
+ ...skuEnabled && lineDataToSave.selectedSKU ? { sku_id: lineDataToSave.selectedSKU.id } : {}
31256
32696
  }));
31257
32697
  console.log(`[handleSaveLine] workspaceThresholdUpdates for ${lineId}:`, workspaceThresholdUpdates);
31258
32698
  await workspaceService.updateActionThresholds(workspaceThresholdUpdates);
@@ -31264,7 +32704,8 @@ var TargetsView = ({
31264
32704
  shift_id: selectedShift,
31265
32705
  product_code: lineDataToSave.productId,
31266
32706
  threshold_day_output: lineDataToSave.workspaces.reduce((acc, ws) => acc + (Number(ws.targetDayOutput) || 0), 0),
31267
- threshold_pph: lineDataToSave.workspaces.reduce((acc, ws) => acc + (Number(ws.targetPPH) || 0), 0)
32707
+ threshold_pph: lineDataToSave.workspaces.reduce((acc, ws) => acc + (Number(ws.targetPPH) || 0), 0),
32708
+ ...skuEnabled && lineDataToSave.selectedSKU ? { sku_id: lineDataToSave.selectedSKU.id } : {}
31268
32709
  };
31269
32710
  console.log(`[handleSaveLine] lineThresholdData for upsert on ${lineId}:`, lineThresholdData);
31270
32711
  const { error: lineUpsertError } = await supabase.from("line_thresholds").upsert(lineThresholdData, { onConflict: "factory_id,line_id,date,shift_id" });
@@ -31359,7 +32800,7 @@ var TargetsView = ({
31359
32800
  return /* @__PURE__ */ jsx(
31360
32801
  TargetsViewUI_default,
31361
32802
  {
31362
- isLoading,
32803
+ isLoading: isLoading || skusLoading,
31363
32804
  lineWorkspaces,
31364
32805
  lineNames,
31365
32806
  savingLines,
@@ -31376,7 +32817,11 @@ var TargetsView = ({
31376
32817
  onShiftChange: handleShiftChange,
31377
32818
  onSaveLine: handleSaveLine,
31378
32819
  onToggleBulkConfigure: handleToggleBulkConfigure,
31379
- onBulkConfigure: handleBulkConfigure
32820
+ onBulkConfigure: handleBulkConfigure,
32821
+ skuEnabled,
32822
+ skus,
32823
+ onUpdateSelectedSKU: updateSelectedSKU,
32824
+ skuRequired: dashboardConfig?.skuConfig?.requireSKUSelection || false
31380
32825
  }
31381
32826
  );
31382
32827
  };
@@ -31635,7 +33080,43 @@ var WorkspaceDetailView = ({
31635
33080
  }
31636
33081
  };
31637
33082
  if (loading) {
31638
- return /* @__PURE__ */ jsx("div", { className: "min-h-screen flex items-center justify-center bg-slate-50", children: /* @__PURE__ */ jsx("div", { className: "text-xl text-gray-600", children: "Loading workspace details..." }) });
33083
+ return /* @__PURE__ */ jsx(
33084
+ motion.div,
33085
+ {
33086
+ className: "min-h-screen bg-slate-50",
33087
+ initial: { opacity: 0 },
33088
+ animate: { opacity: 1 },
33089
+ transition: { duration: 0.3 },
33090
+ children: /* @__PURE__ */ jsxs("div", { className: "min-h-screen w-full flex flex-col bg-slate-50", children: [
33091
+ /* @__PURE__ */ jsx("header", { className: "sticky top-0 z-10 px-2 sm:px-2.5 lg:px-3 py-1.5 sm:py-2 lg:py-3 flex flex-col shadow-sm bg-white", children: /* @__PURE__ */ jsxs("div", { className: "relative flex items-center", children: [
33092
+ /* @__PURE__ */ jsx("div", { className: "absolute left-0 animate-pulse", children: /* @__PURE__ */ jsx("div", { className: "h-8 w-20 bg-gray-200 rounded" }) }),
33093
+ /* @__PURE__ */ jsx("div", { className: "absolute left-1/2 transform -translate-x-1/2 animate-pulse", children: /* @__PURE__ */ jsx("div", { className: "h-6 w-40 bg-gray-200 rounded" }) }),
33094
+ /* @__PURE__ */ jsx("div", { className: "w-full h-8" })
33095
+ ] }) }),
33096
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 p-4 sm:p-6 lg:p-8", children: [
33097
+ /* @__PURE__ */ jsx("div", { className: "mb-6", children: /* @__PURE__ */ jsxs("div", { className: "flex space-x-4 justify-center animate-pulse", children: [
33098
+ /* @__PURE__ */ jsx("div", { className: "h-10 w-32 bg-gray-200 rounded-lg" }),
33099
+ /* @__PURE__ */ jsx("div", { className: "h-10 w-32 bg-gray-200 rounded-lg" }),
33100
+ /* @__PURE__ */ jsx("div", { className: "h-10 w-32 bg-gray-200 rounded-lg" })
33101
+ ] }) }),
33102
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4 mb-6", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ jsx("div", { className: "bg-white rounded-lg p-4 shadow-sm", children: /* @__PURE__ */ jsxs("div", { className: "animate-pulse", children: [
33103
+ /* @__PURE__ */ jsx("div", { className: "h-4 w-20 bg-gray-200 rounded mb-2" }),
33104
+ /* @__PURE__ */ jsx("div", { className: "h-8 w-16 bg-gray-200 rounded" })
33105
+ ] }) }, i)) }),
33106
+ /* @__PURE__ */ jsx("div", { className: "bg-white rounded-lg p-6 shadow-sm", children: /* @__PURE__ */ jsxs("div", { className: "animate-pulse", children: [
33107
+ /* @__PURE__ */ jsx("div", { className: "h-6 w-40 bg-gray-200 rounded mb-4" }),
33108
+ /* @__PURE__ */ jsx("div", { className: "h-64 bg-gray-100 rounded relative overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "bg-white/80 rounded-lg p-4 shadow-sm", children: [
33109
+ /* @__PURE__ */ jsxs("svg", { className: "animate-spin h-8 w-8 text-blue-500", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [
33110
+ /* @__PURE__ */ jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
33111
+ /* @__PURE__ */ jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })
33112
+ ] }),
33113
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-600 mt-2", children: "Loading chart data..." })
33114
+ ] }) }) })
33115
+ ] }) })
33116
+ ] })
33117
+ ] })
33118
+ }
33119
+ );
31639
33120
  }
31640
33121
  if (error) {
31641
33122
  return /* @__PURE__ */ jsxs("div", { className: "min-h-screen p-8 bg-slate-50", children: [
@@ -32075,6 +33556,144 @@ var WorkspaceDetailView = ({
32075
33556
  };
32076
33557
  var WrappedComponent = withAuth(WorkspaceDetailView);
32077
33558
  var WorkspaceDetailView_default = WrappedComponent;
33559
+ var SKUManagementView = () => {
33560
+ const config = useDashboardConfig();
33561
+ const companyId = config?.entityConfig?.companyId || "";
33562
+ const router = useRouter();
33563
+ const { skus, isLoading, error, refetch } = useSKUs(companyId);
33564
+ const [isModalOpen, setIsModalOpen] = useState(false);
33565
+ const [editingSKU, setEditingSKU] = useState(null);
33566
+ const [deleteConfirmSKU, setDeleteConfirmSKU] = useState(null);
33567
+ useEffect(() => {
33568
+ trackCoreEvent("SKU Management Page Viewed");
33569
+ }, []);
33570
+ const handleCreateOrUpdate = async (skuData) => {
33571
+ try {
33572
+ if (editingSKU) {
33573
+ await skuService.updateSKU(editingSKU.id, {
33574
+ production_target: skuData.production_target,
33575
+ attributes: skuData.attributes
33576
+ });
33577
+ trackCoreEvent("SKU Updated", {
33578
+ sku_id: editingSKU.sku_id,
33579
+ production_target: skuData.production_target
33580
+ });
33581
+ } else {
33582
+ const exists = await skuService.checkSKUExists(companyId, skuData.sku_id);
33583
+ if (exists) {
33584
+ throw new Error("SKU ID already exists");
33585
+ }
33586
+ await skuService.createSKU(skuData);
33587
+ trackCoreEvent("SKU Created", {
33588
+ sku_id: skuData.sku_id,
33589
+ production_target: skuData.production_target
33590
+ });
33591
+ }
33592
+ setIsModalOpen(false);
33593
+ setEditingSKU(null);
33594
+ refetch();
33595
+ } catch (error2) {
33596
+ console.error("Error saving SKU:", error2);
33597
+ throw error2;
33598
+ }
33599
+ };
33600
+ const handleEdit = (sku) => {
33601
+ setEditingSKU(sku);
33602
+ setIsModalOpen(true);
33603
+ };
33604
+ const handleDelete = async (sku) => {
33605
+ if (deleteConfirmSKU?.id === sku.id) {
33606
+ try {
33607
+ await skuService.deleteSKU(sku.id);
33608
+ trackCoreEvent("SKU Deleted", { sku_id: sku.sku_id });
33609
+ setDeleteConfirmSKU(null);
33610
+ refetch();
33611
+ } catch (error2) {
33612
+ console.error("Error deleting SKU:", error2);
33613
+ }
33614
+ } else {
33615
+ setDeleteConfirmSKU(sku);
33616
+ setTimeout(() => setDeleteConfirmSKU(null), 3e3);
33617
+ }
33618
+ };
33619
+ const handleAddNew = () => {
33620
+ setEditingSKU(null);
33621
+ setIsModalOpen(true);
33622
+ };
33623
+ const handleBack = () => {
33624
+ router.push("/");
33625
+ };
33626
+ if (!config?.skuConfig?.enabled) {
33627
+ return /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-slate-50 flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
33628
+ /* @__PURE__ */ jsx(Package, { className: "mx-auto h-12 w-12 text-gray-400" }),
33629
+ /* @__PURE__ */ jsx("h3", { className: "mt-2 text-sm font-medium text-gray-900", children: "SKU Management Disabled" }),
33630
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500", children: "SKU management is not enabled for this workspace." })
33631
+ ] }) });
33632
+ }
33633
+ return /* @__PURE__ */ jsxs("div", { className: "min-h-screen bg-slate-50", children: [
33634
+ /* @__PURE__ */ jsx("div", { className: "sticky top-0 z-10 bg-white border-b border-gray-200/80 shadow-sm", children: /* @__PURE__ */ jsx("div", { className: "px-4 sm:px-8 py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center relative", children: [
33635
+ /* @__PURE__ */ jsxs(
33636
+ "button",
33637
+ {
33638
+ onClick: handleBack,
33639
+ className: "absolute left-0 flex items-center gap-2 text-gray-600 hover:text-gray-900 transition-colors",
33640
+ children: [
33641
+ /* @__PURE__ */ jsx(ArrowLeft, { className: "h-5 w-5" }),
33642
+ /* @__PURE__ */ jsx("span", { children: "Back" })
33643
+ ]
33644
+ }
33645
+ ),
33646
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 text-center mx-auto", children: [
33647
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold text-gray-900", children: "SKU Management" }),
33648
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Manage Stock Keeping Units (SKUs) for production planning" })
33649
+ ] }),
33650
+ /* @__PURE__ */ jsxs(
33651
+ "button",
33652
+ {
33653
+ onClick: handleAddNew,
33654
+ className: "absolute right-0 inline-flex items-center px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-all duration-200 shadow-sm",
33655
+ children: [
33656
+ /* @__PURE__ */ jsx(Plus, { className: "w-4 h-4 mr-2" }),
33657
+ "Add SKU"
33658
+ ]
33659
+ }
33660
+ )
33661
+ ] }) }) }),
33662
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto", children: /* @__PURE__ */ jsxs("div", { className: "px-4 sm:px-8 py-6", children: [
33663
+ /* @__PURE__ */ jsx("div", { className: "mb-6 bg-gradient-to-r from-blue-50 to-blue-50/50 p-4 rounded-xl border border-blue-100", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-blue-700 font-medium", children: "Create and manage SKUs with production targets. These will be available for selection in the production targets page." }) }),
33664
+ error ? /* @__PURE__ */ jsxs("div", { className: "bg-red-50 border border-red-200 text-red-700 p-4 rounded-lg", children: [
33665
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Error loading SKUs" }),
33666
+ /* @__PURE__ */ jsx("p", { className: "text-sm mt-1", children: error.message })
33667
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
33668
+ /* @__PURE__ */ jsx(
33669
+ SKUList,
33670
+ {
33671
+ skus,
33672
+ onEdit: handleEdit,
33673
+ onDelete: handleDelete,
33674
+ isLoading
33675
+ }
33676
+ ),
33677
+ deleteConfirmSKU && /* @__PURE__ */ jsx("div", { className: "mt-4 bg-yellow-50 border border-yellow-200 text-yellow-800 p-4 rounded-lg", children: /* @__PURE__ */ jsxs("p", { className: "text-sm", children: [
33678
+ "Click delete again to confirm deletion of SKU: ",
33679
+ /* @__PURE__ */ jsx("strong", { children: deleteConfirmSKU.sku_id })
33680
+ ] }) })
33681
+ ] })
33682
+ ] }) }),
33683
+ /* @__PURE__ */ jsx(
33684
+ SKUModal,
33685
+ {
33686
+ isOpen: isModalOpen,
33687
+ onClose: () => {
33688
+ setIsModalOpen(false);
33689
+ setEditingSKU(null);
33690
+ },
33691
+ onSave: handleCreateOrUpdate,
33692
+ editingSKU
33693
+ }
33694
+ )
33695
+ ] });
33696
+ };
32078
33697
  var S3Service = class {
32079
33698
  constructor(config) {
32080
33699
  this.s3Client = null;
@@ -32313,4 +33932,4 @@ var S3Service = class {
32313
33932
  }
32314
33933
  };
32315
33934
 
32316
- export { ACTION_NAMES, AIAgentView_default as AIAgentView, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedTargetsView, BarChart, BaseHistoryCalendar, BottlenecksContent, BreakNotificationPopup, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, EmptyStateMessage, FactoryView_default as FactoryView, GaugeChart, GridComponentsPlaceholder, Header, HelpView_default as HelpView, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LiveTimer, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSpinner_default as LoadingSpinner, LoginPage, LoginView_default as LoginView, MainLayout, MetricCard_default as MetricCard, NoWorkspaceData, OptifyeAgentClient, OutputProgressChart, PageHeader, PieChart4 as PieChart, ProfileView_default as ProfileView, RegistryProvider, S3Service, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SingleVideoStream_default as SingleVideoStream, Skeleton, SlackAPI, SupabaseProvider, TargetWorkspaceGrid, TargetsView_default as TargetsView, ThreadSidebar, TimeDisplay_default as TimeDisplay, TimePickerDropdown, VideoCard, VideoGridView, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, authCoreService, authOTPService, authRateLimitService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createStreamProxyHandler, createSupabaseClient, createThrottledReload, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatTimeInZone, fromUrlFriendlyName, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultTabForWorkspace, getManufacturingInsights, getMetricsTablePrefix, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isTransitionPeriod, isValidLineInfoPayload, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, mergeWithDefaultConfig, optifyeAgentClient, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, s3VideoPreloader, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, updateThreadTitle, useActiveBreaks, useAllWorkspaceMetrics, useAnalyticsConfig, useAuth, useAuthConfig, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, useRealtimeLineMetrics, useRegistry, useShiftConfig, useShifts, useSupabase, useSupabaseClient, useTargets, useTheme, useThemeConfig, useThreads, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, videoPreloader, whatsappService, withAuth, withRegistry, workspaceService };
33935
+ export { ACTION_NAMES, AIAgentView_default as AIAgentView, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedTargetsView, BarChart, BaseHistoryCalendar, BottlenecksContent, BreakNotificationPopup, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, EmptyStateMessage, FactoryView_default as FactoryView, GaugeChart, GridComponentsPlaceholder, Header, HelpView_default as HelpView, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingSpinner_default as LoadingSpinner, LoadingState, LoginPage, LoginView_default as LoginView, MainLayout, MetricCard_default as MetricCard, NoWorkspaceData, OptifyeAgentClient, OutputProgressChart, PageHeader, PieChart4 as PieChart, ProfileView_default as ProfileView, RegistryProvider, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SingleVideoStream_default as SingleVideoStream, Skeleton, SlackAPI, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, TargetWorkspaceGrid, TargetsView_default as TargetsView, ThreadSidebar, TimeDisplay_default as TimeDisplay, TimePickerDropdown, VideoCard, VideoGridView, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, authCoreService, authOTPService, authRateLimitService, cacheService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createStreamProxyHandler, createSupabaseClient, createThrottledReload, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatTimeInZone, fromUrlFriendlyName, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultTabForWorkspace, getManufacturingInsights, getMetricsTablePrefix, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isTransitionPeriod, isValidLineInfoPayload, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, mergeWithDefaultConfig, optifyeAgentClient, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetSubscriptionManager, s3VideoPreloader, skuService, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, updateThreadTitle, useActiveBreaks, useAllWorkspaceMetrics, useAnalyticsConfig, useAuth, useAuthConfig, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, useRealtimeLineMetrics, useRegistry, useSKUs, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTheme, useThemeConfig, useThreads, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, videoPreloader, whatsappService, withAuth, withRegistry, workspaceService };