@optifye/dashboard-core 6.10.13 → 6.10.14

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.js CHANGED
@@ -1581,7 +1581,7 @@ var dashboardService = {
1581
1581
  };
1582
1582
  const formattedStartDate = formatDate2(startDate);
1583
1583
  const formattedEndDate = formatDate2(endDate);
1584
- let query = supabase.from(lineMetricsTable).select("date, shift_id, avg_efficiency, underperforming_workspaces, total_workspaces").gte("date", formattedStartDate).lte("date", formattedEndDate);
1584
+ let query = supabase.from(lineMetricsTable).select("date, shift_id, avg_efficiency, underperforming_workspaces, total_workspaces, current_output, ideal_output, line_threshold").gte("date", formattedStartDate).lte("date", formattedEndDate);
1585
1585
  if (lineIdInput === factoryViewId) {
1586
1586
  if (!isValidFactoryViewConfiguration(entityConfig)) {
1587
1587
  throw new Error("Factory View requires at least one configured line for monthly data.");
@@ -1600,7 +1600,10 @@ var dashboardService = {
1600
1600
  shift_id: item.shift_id,
1601
1601
  avg_efficiency: item.avg_efficiency || 0,
1602
1602
  underperforming_workspaces: item.underperforming_workspaces || 0,
1603
- total_workspaces: item.total_workspaces || 0
1603
+ total_workspaces: item.total_workspaces || 0,
1604
+ current_output: item.current_output || 0,
1605
+ ideal_output: item.ideal_output || 0,
1606
+ line_threshold: item.line_threshold || 0
1604
1607
  }));
1605
1608
  return transformedData;
1606
1609
  } catch (err) {
@@ -9415,7 +9418,56 @@ var getUniformShiftGroup = (shiftConfigMap, timezone, now2 = /* @__PURE__ */ new
9415
9418
  return groups.length === 1 ? groups[0] : null;
9416
9419
  };
9417
9420
 
9421
+ // src/lib/types/efficiencyLegend.ts
9422
+ var DEFAULT_EFFICIENCY_LEGEND = {
9423
+ green_min: 80,
9424
+ green_max: 100,
9425
+ yellow_min: 70,
9426
+ yellow_max: 79,
9427
+ red_min: 0,
9428
+ red_max: 69,
9429
+ critical_threshold: 50
9430
+ };
9431
+ function getEfficiencyColor(efficiency, legend = DEFAULT_EFFICIENCY_LEGEND) {
9432
+ if (efficiency >= legend.green_min) {
9433
+ return "green";
9434
+ } else if (efficiency >= legend.yellow_min) {
9435
+ return "yellow";
9436
+ } else {
9437
+ return "red";
9438
+ }
9439
+ }
9440
+ function getEfficiencyColorClasses(efficiency, legend = DEFAULT_EFFICIENCY_LEGEND) {
9441
+ const color2 = getEfficiencyColor(efficiency, legend);
9442
+ switch (color2) {
9443
+ case "green":
9444
+ return "bg-[#00AB45]/90 hover:bg-[#00AB45]/95";
9445
+ case "yellow":
9446
+ return "bg-[#FFB020]/90 hover:bg-[#FFB020]/95";
9447
+ case "red":
9448
+ return "bg-[#E34329]/90 hover:bg-[#E34329]/95";
9449
+ default:
9450
+ return "bg-gray-300/90";
9451
+ }
9452
+ }
9453
+
9418
9454
  // src/lib/hooks/useDashboardMetrics.ts
9455
+ var parseEfficiencyLegend = (legend) => {
9456
+ if (!legend) return null;
9457
+ const coerce = (value, fallback) => {
9458
+ const num = Number(value);
9459
+ return Number.isFinite(num) ? num : fallback;
9460
+ };
9461
+ return {
9462
+ green_min: coerce(legend.green_min, DEFAULT_EFFICIENCY_LEGEND.green_min),
9463
+ green_max: coerce(legend.green_max, DEFAULT_EFFICIENCY_LEGEND.green_max),
9464
+ yellow_min: coerce(legend.yellow_min, DEFAULT_EFFICIENCY_LEGEND.yellow_min),
9465
+ yellow_max: coerce(legend.yellow_max, DEFAULT_EFFICIENCY_LEGEND.yellow_max),
9466
+ red_min: coerce(legend.red_min, DEFAULT_EFFICIENCY_LEGEND.red_min),
9467
+ red_max: coerce(legend.red_max, DEFAULT_EFFICIENCY_LEGEND.red_max),
9468
+ critical_threshold: coerce(legend.critical_threshold, DEFAULT_EFFICIENCY_LEGEND.critical_threshold)
9469
+ };
9470
+ };
9419
9471
  var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds }) => {
9420
9472
  const { supabaseUrl, supabaseKey } = useDashboardConfig();
9421
9473
  const entityConfig = useEntityConfig();
@@ -9473,7 +9525,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
9473
9525
  );
9474
9526
  React24.useEffect(() => {
9475
9527
  lineIdRef.current = lineId;
9476
- setMetrics({ workspaceMetrics: [], lineMetrics: [] });
9528
+ setMetrics({ workspaceMetrics: [], lineMetrics: [], metadata: void 0 });
9477
9529
  setIsLoading(true);
9478
9530
  }, [lineId]);
9479
9531
  const fetchAllMetrics = React24.useCallback(async () => {
@@ -9511,6 +9563,8 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
9511
9563
  }
9512
9564
  let allWorkspaceMetrics = [];
9513
9565
  let allLineMetrics = [];
9566
+ let hasFlowBuffers = false;
9567
+ let efficiencyLegend;
9514
9568
  if (isFactory && shiftGroups.length > 0) {
9515
9569
  console.log(`[useDashboardMetrics] \u{1F3ED} Factory view: Fetching for ${shiftGroups.length} shift group(s)`);
9516
9570
  const metricsPromises = shiftGroups.map(async (group) => {
@@ -9541,6 +9595,9 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
9541
9595
  }
9542
9596
  });
9543
9597
  const results = await Promise.all(metricsPromises);
9598
+ hasFlowBuffers = results.some((result) => result?.metadata?.has_flow_buffers);
9599
+ const legendCandidate = results.find((result) => result?.efficiency_legend)?.efficiency_legend;
9600
+ efficiencyLegend = parseEfficiencyLegend(legendCandidate) ?? DEFAULT_EFFICIENCY_LEGEND;
9544
9601
  results.forEach((result) => {
9545
9602
  if (result.workspace_metrics) {
9546
9603
  allWorkspaceMetrics.push(...result.workspace_metrics);
@@ -9593,6 +9650,8 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
9593
9650
  });
9594
9651
  allWorkspaceMetrics = backendData.workspace_metrics || [];
9595
9652
  allLineMetrics = backendData.line_metrics || [];
9653
+ hasFlowBuffers = Boolean(backendData?.metadata?.has_flow_buffers);
9654
+ efficiencyLegend = parseEfficiencyLegend(backendData?.efficiency_legend) ?? DEFAULT_EFFICIENCY_LEGEND;
9596
9655
  }
9597
9656
  const transformedWorkspaceData = allWorkspaceMetrics.map((item) => ({
9598
9657
  company_id: item.company_id || entityConfig.companyId,
@@ -9618,7 +9677,9 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
9618
9677
  });
9619
9678
  const newMetricsState = {
9620
9679
  workspaceMetrics: transformedWorkspaceData,
9621
- lineMetrics: allLineMetrics || []
9680
+ lineMetrics: allLineMetrics || [],
9681
+ metadata: { hasFlowBuffers },
9682
+ efficiencyLegend: efficiencyLegend ?? DEFAULT_EFFICIENCY_LEGEND
9622
9683
  };
9623
9684
  console.log("[useDashboardMetrics] Setting metrics state:", {
9624
9685
  workspaceMetrics: newMetricsState.workspaceMetrics.length,
@@ -9770,6 +9831,8 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
9770
9831
  return {
9771
9832
  workspaceMetrics: metrics2?.workspaceMetrics || [],
9772
9833
  lineMetrics: metrics2?.lineMetrics || [],
9834
+ efficiencyLegend: metrics2?.efficiencyLegend || DEFAULT_EFFICIENCY_LEGEND,
9835
+ metadata: metrics2?.metadata,
9773
9836
  isLoading,
9774
9837
  error,
9775
9838
  refetch: fetchAllMetrics
@@ -13786,6 +13849,256 @@ function useIdleTimeReasons({
13786
13849
  refetch: fetchData
13787
13850
  };
13788
13851
  }
13852
+
13853
+ // src/lib/services/clipClassificationService.ts
13854
+ function parseCleanLabel(rawLabel) {
13855
+ if (!rawLabel) return null;
13856
+ const patterns = [
13857
+ /"Clip Label"\s*:\s*"([^"]+)"/,
13858
+ // Old format
13859
+ /"clip_label"\s*:\s*"([^"]+)"/,
13860
+ // New format
13861
+ /'Clip Label'\s*:\s*'([^']+)'/,
13862
+ // Single quotes
13863
+ /'clip_label'\s*:\s*'([^']+)'/
13864
+ // Single quotes
13865
+ ];
13866
+ for (const pattern of patterns) {
13867
+ const match = rawLabel.match(pattern);
13868
+ if (match) {
13869
+ const label = match[1].trim();
13870
+ return label.replace(/ /g, "_");
13871
+ }
13872
+ }
13873
+ return null;
13874
+ }
13875
+ async function fetchClassifications(clipIds, token) {
13876
+ if (!clipIds || clipIds.length === 0) {
13877
+ return {};
13878
+ }
13879
+ try {
13880
+ const CHUNK_SIZE = 50;
13881
+ const chunks = [];
13882
+ for (let i = 0; i < clipIds.length; i += CHUNK_SIZE) {
13883
+ chunks.push(clipIds.slice(i, i + CHUNK_SIZE));
13884
+ }
13885
+ console.log(`[fetchClassifications] Fetching ${clipIds.length} classifications in ${chunks.length} chunks`);
13886
+ const apiBase = process.env.NEXT_PUBLIC_BACKEND_URL || "http://localhost:8000";
13887
+ const chunkResults = await Promise.all(
13888
+ chunks.map(async (chunkIds) => {
13889
+ try {
13890
+ const response = await fetch(
13891
+ `${apiBase}/api/classification/batch?clip_ids=${chunkIds.join(",")}`,
13892
+ {
13893
+ headers: {
13894
+ "Authorization": `Bearer ${token}`,
13895
+ "Content-Type": "application/json"
13896
+ }
13897
+ }
13898
+ );
13899
+ if (!response.ok) {
13900
+ console.error(`Classification API error for chunk: ${response.status}`);
13901
+ return Object.fromEntries(
13902
+ chunkIds.map((id3) => [id3, { status: "processing" }])
13903
+ );
13904
+ }
13905
+ const data = await response.json();
13906
+ return data.classifications || {};
13907
+ } catch (error) {
13908
+ console.error("Error fetching chunk:", error);
13909
+ return Object.fromEntries(
13910
+ chunkIds.map((id3) => [id3, { status: "processing" }])
13911
+ );
13912
+ }
13913
+ })
13914
+ );
13915
+ const allClassifications = Object.assign({}, ...chunkResults);
13916
+ console.log(`[fetchClassifications] Total fetched: ${Object.keys(allClassifications).length} classifications`);
13917
+ return allClassifications;
13918
+ } catch (error) {
13919
+ console.error("Error fetching classifications:", error);
13920
+ return Object.fromEntries(
13921
+ clipIds.map((id3) => [id3, { status: "processing" }])
13922
+ );
13923
+ }
13924
+ }
13925
+ function useClassificationRealtimeUpdates({
13926
+ clipIds,
13927
+ enabled = true,
13928
+ onClassificationUpdate
13929
+ }) {
13930
+ const supabase = useSupabase();
13931
+ React24.useEffect(() => {
13932
+ if (!enabled || !clipIds || clipIds.length === 0 || !supabase) {
13933
+ return;
13934
+ }
13935
+ const channelName = `classification:${clipIds.slice(0, 5).join("-")}`;
13936
+ console.log(`[useClassificationRealtimeUpdates] Subscribing to channel: ${channelName}`);
13937
+ const channel = supabase.channel(channelName).on(
13938
+ "postgres_changes",
13939
+ {
13940
+ event: "INSERT",
13941
+ schema: "public",
13942
+ table: "clip_classification",
13943
+ filter: `clip_id=in.(${clipIds.join(",")})`
13944
+ },
13945
+ (payload) => {
13946
+ console.log("[useClassificationRealtimeUpdates] New classification:", payload);
13947
+ try {
13948
+ const { clip_id, clip_label, confidence_score } = payload.new;
13949
+ const parsedLabel = parseCleanLabel(clip_label);
13950
+ if (parsedLabel) {
13951
+ onClassificationUpdate(clip_id, {
13952
+ status: "classified",
13953
+ label: parsedLabel,
13954
+ confidence: confidence_score || 0
13955
+ });
13956
+ } else {
13957
+ console.warn("[useClassificationRealtimeUpdates] Failed to parse label:", clip_label);
13958
+ }
13959
+ } catch (error) {
13960
+ console.error("[useClassificationRealtimeUpdates] Error processing update:", error);
13961
+ }
13962
+ }
13963
+ ).subscribe((status) => {
13964
+ console.log(`[useClassificationRealtimeUpdates] Subscription status:`, status);
13965
+ });
13966
+ return () => {
13967
+ console.log(`[useClassificationRealtimeUpdates] Unsubscribing from ${channelName}`);
13968
+ supabase.removeChannel(channel);
13969
+ };
13970
+ }, [clipIds, enabled, supabase, onClassificationUpdate]);
13971
+ }
13972
+
13973
+ // src/lib/hooks/useIdleTimeClipClassifications.ts
13974
+ var DEFAULT_LIMIT = 200;
13975
+ var MAX_PAGES = 5;
13976
+ function useIdleTimeClipClassifications({
13977
+ workspaceId,
13978
+ date,
13979
+ shiftId,
13980
+ enabled = true,
13981
+ limit = DEFAULT_LIMIT
13982
+ }) {
13983
+ const { session } = useAuth();
13984
+ const [idleClips, setIdleClips] = React24.useState([]);
13985
+ const [clipClassifications, setClipClassifications] = React24.useState({});
13986
+ const [isLoading, setIsLoading] = React24.useState(false);
13987
+ const [error, setError] = React24.useState(null);
13988
+ const lastRequestKeyRef = React24.useRef("");
13989
+ const requestKey = React24.useMemo(
13990
+ () => `${workspaceId || ""}-${date || ""}-${shiftId ?? ""}`,
13991
+ [workspaceId, date, shiftId]
13992
+ );
13993
+ const fetchIdleClips = React24.useCallback(async () => {
13994
+ if (!enabled) {
13995
+ setIsLoading(false);
13996
+ return;
13997
+ }
13998
+ if (!workspaceId || !date || shiftId === void 0) {
13999
+ setIdleClips([]);
14000
+ setIsLoading(false);
14001
+ return;
14002
+ }
14003
+ if (!session?.access_token) {
14004
+ setError("Not authenticated");
14005
+ setIsLoading(false);
14006
+ return;
14007
+ }
14008
+ setIsLoading(true);
14009
+ setError(null);
14010
+ lastRequestKeyRef.current = requestKey;
14011
+ try {
14012
+ const collected = [];
14013
+ let page = 1;
14014
+ let hasMore = true;
14015
+ while (hasMore && page <= MAX_PAGES && collected.length < limit) {
14016
+ const response = await fetch("/api/clips/supabase", {
14017
+ method: "POST",
14018
+ headers: {
14019
+ "Content-Type": "application/json",
14020
+ "Authorization": `Bearer ${session.access_token}`
14021
+ },
14022
+ body: JSON.stringify({
14023
+ action: "clip-metadata",
14024
+ workspaceId,
14025
+ date,
14026
+ shift: shiftId.toString(),
14027
+ category: "idle_time",
14028
+ page,
14029
+ limit
14030
+ })
14031
+ });
14032
+ if (!response.ok) {
14033
+ throw new Error(`Idle clip metadata request failed: ${response.status}`);
14034
+ }
14035
+ const data = await response.json();
14036
+ const clips = Array.isArray(data?.clips) ? data.clips : [];
14037
+ const mapped = clips.map((clip) => ({
14038
+ id: clip.id,
14039
+ idle_start_time: clip.idle_start_time,
14040
+ idle_end_time: clip.idle_end_time
14041
+ }));
14042
+ collected.push(...mapped);
14043
+ hasMore = Boolean(data?.hasMore);
14044
+ page += 1;
14045
+ }
14046
+ if (lastRequestKeyRef.current === requestKey) {
14047
+ setIdleClips(collected);
14048
+ }
14049
+ } catch (err) {
14050
+ console.error("[useIdleTimeClipClassifications] Error fetching idle clips:", err);
14051
+ if (lastRequestKeyRef.current === requestKey) {
14052
+ setError(err instanceof Error ? err.message : "Failed to fetch idle clips");
14053
+ setIdleClips([]);
14054
+ }
14055
+ } finally {
14056
+ if (lastRequestKeyRef.current === requestKey) {
14057
+ setIsLoading(false);
14058
+ }
14059
+ }
14060
+ }, [enabled, workspaceId, date, shiftId, session?.access_token, requestKey, limit]);
14061
+ React24.useEffect(() => {
14062
+ fetchIdleClips();
14063
+ }, [fetchIdleClips]);
14064
+ React24.useEffect(() => {
14065
+ setClipClassifications({});
14066
+ }, [requestKey]);
14067
+ const idleClipIds = React24.useMemo(
14068
+ () => idleClips.map((clip) => clip.id).filter(Boolean),
14069
+ [idleClips]
14070
+ );
14071
+ React24.useEffect(() => {
14072
+ if (!enabled || idleClipIds.length === 0 || !session?.access_token) {
14073
+ return;
14074
+ }
14075
+ fetchClassifications(idleClipIds, session.access_token).then((classifications) => {
14076
+ setClipClassifications((prev) => ({
14077
+ ...prev,
14078
+ ...classifications
14079
+ }));
14080
+ }).catch((err) => {
14081
+ console.error("[useIdleTimeClipClassifications] Error fetching classifications:", err);
14082
+ });
14083
+ }, [enabled, idleClipIds, session?.access_token]);
14084
+ useClassificationRealtimeUpdates({
14085
+ clipIds: idleClipIds,
14086
+ enabled: enabled && idleClipIds.length > 0,
14087
+ onClassificationUpdate: React24.useCallback((clipId, classification) => {
14088
+ setClipClassifications((prev) => ({
14089
+ ...prev,
14090
+ [clipId]: classification
14091
+ }));
14092
+ }, [])
14093
+ });
14094
+ return {
14095
+ idleClips,
14096
+ clipClassifications,
14097
+ isLoading,
14098
+ error,
14099
+ refetch: fetchIdleClips
14100
+ };
14101
+ }
13789
14102
  function useUserUsage(userId, options = {}) {
13790
14103
  const { startDate, endDate, enabled = true } = options;
13791
14104
  const { session } = useAuth();
@@ -26493,6 +26806,10 @@ var HourlyOutputChartComponent = ({
26493
26806
  shiftEnd,
26494
26807
  showIdleTime = false,
26495
26808
  idleTimeHourly,
26809
+ idleTimeClips,
26810
+ idleTimeClipClassifications,
26811
+ shiftDate,
26812
+ timezone,
26496
26813
  className = ""
26497
26814
  }) => {
26498
26815
  const containerRef = React24__namespace.default.useRef(null);
@@ -26505,6 +26822,37 @@ var HourlyOutputChartComponent = ({
26505
26822
  return { hour, minute, decimalHour };
26506
26823
  };
26507
26824
  const shiftStartTime = getTimeFromTimeString(shiftStart);
26825
+ const shiftStartDateTime = React24__namespace.default.useMemo(() => {
26826
+ if (!shiftDate || !timezone) return null;
26827
+ const hour = shiftStartTime.hour.toString().padStart(2, "0");
26828
+ const minute = shiftStartTime.minute.toString().padStart(2, "0");
26829
+ return dateFnsTz.fromZonedTime(`${shiftDate}T${hour}:${minute}:00`, timezone);
26830
+ }, [shiftDate, timezone, shiftStartTime.hour, shiftStartTime.minute]);
26831
+ const idleClipRanges = React24__namespace.default.useMemo(() => {
26832
+ if (!idleTimeClips || idleTimeClips.length === 0) return [];
26833
+ return idleTimeClips.map((clip) => ({
26834
+ id: clip.id,
26835
+ start: clip.idle_start_time ? new Date(clip.idle_start_time) : null,
26836
+ end: clip.idle_end_time ? new Date(clip.idle_end_time) : null
26837
+ })).filter((clip) => clip.start && clip.end);
26838
+ }, [idleTimeClips]);
26839
+ const getIdleReasonForRange = React24__namespace.default.useCallback((rangeStart, rangeEnd) => {
26840
+ if (!rangeStart || !rangeEnd || idleClipRanges.length === 0) {
26841
+ return "Reason unavailable";
26842
+ }
26843
+ const matchingClip = idleClipRanges.find((clip) => rangeStart >= clip.start && rangeEnd <= clip.end) || idleClipRanges.find((clip) => rangeStart < clip.end && rangeEnd > clip.start);
26844
+ if (!matchingClip) {
26845
+ return "Reason unavailable";
26846
+ }
26847
+ const classification = idleTimeClipClassifications?.[matchingClip.id];
26848
+ if (!classification || classification.status === "processing") {
26849
+ return "Analyzing...";
26850
+ }
26851
+ if (!classification.label) {
26852
+ return "Reason unavailable";
26853
+ }
26854
+ return classification.label.replace(/_/g, " ");
26855
+ }, [idleClipRanges, idleTimeClipClassifications]);
26508
26856
  const { shiftDuration, shiftEndTime, hasPartialLastHour } = React24__namespace.default.useMemo(() => {
26509
26857
  console.log("[HourlyOutputChart] Calculating shift duration with:", {
26510
26858
  shiftStart,
@@ -26752,6 +27100,7 @@ var HourlyOutputChartComponent = ({
26752
27100
  }
26753
27101
  idleMinutes = idleArray.filter((val) => val === "1" || val === 1).length;
26754
27102
  return {
27103
+ hourIndex: i,
26755
27104
  hour: formatHour(i),
26756
27105
  timeRange: formatTimeRange2(i),
26757
27106
  output: animatedData[i] || 0,
@@ -26945,9 +27294,10 @@ var HourlyOutputChartComponent = ({
26945
27294
  }
26946
27295
  }
26947
27296
  const formatIdleTimestamp = (minuteIdx) => {
26948
- const hourIndex = chartData.findIndex((item) => item.timeRange === data2.timeRange);
26949
- const hourOffset = hourIndex >= 0 ? hourIndex : 0;
26950
- const totalMinutes = (shiftStartTime.hour + hourOffset) * 60 + shiftStartTime.minute + minuteIdx;
27297
+ const fallbackIndex = chartData.findIndex((item) => item.timeRange === data2.timeRange);
27298
+ const hourOffset = Number.isFinite(data2.hourIndex) ? data2.hourIndex : fallbackIndex;
27299
+ const safeHourOffset = hourOffset >= 0 ? hourOffset : 0;
27300
+ const totalMinutes = (shiftStartTime.hour + safeHourOffset) * 60 + shiftStartTime.minute + minuteIdx;
26951
27301
  const hour = Math.floor(totalMinutes / 60) % 24;
26952
27302
  const minute = totalMinutes % 60;
26953
27303
  const period = hour >= 12 ? "PM" : "AM";
@@ -26978,27 +27328,23 @@ var HourlyOutputChartComponent = ({
26978
27328
  const duration = range.end - range.start + 1;
26979
27329
  const startTime = formatIdleTimestamp(range.start);
26980
27330
  const endTime = formatIdleTimestamp(range.end + 1);
27331
+ const fallbackIndex = chartData.findIndex((item) => item.timeRange === data2.timeRange);
27332
+ const hourOffset = Number.isFinite(data2.hourIndex) ? data2.hourIndex : fallbackIndex;
27333
+ const safeHourOffset = hourOffset >= 0 ? hourOffset : 0;
27334
+ const rangeStartDate = shiftStartDateTime ? dateFns.addMinutes(shiftStartDateTime, safeHourOffset * 60 + range.start) : null;
27335
+ const rangeEndDate = shiftStartDateTime ? dateFns.addMinutes(shiftStartDateTime, safeHourOffset * 60 + range.end + 1) : null;
27336
+ const reasonLabel = getIdleReasonForRange(rangeStartDate, rangeEndDate);
26981
27337
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-gray-600 flex items-center gap-2 text-xs", children: [
26982
27338
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block w-1.5 h-1.5 bg-orange-400 rounded-full flex-shrink-0" }),
26983
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: duration === 1 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
26984
- startTime,
26985
- " ",
26986
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-gray-400", children: [
26987
- "(",
26988
- duration,
26989
- " min)"
26990
- ] })
26991
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
26992
- startTime,
26993
- " - ",
26994
- endTime,
26995
- " ",
26996
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-gray-400", children: [
26997
- "(",
26998
- duration,
26999
- " min)"
27000
- ] })
27001
- ] }) })
27339
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
27340
+ duration === 1 ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: startTime }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
27341
+ startTime,
27342
+ " - ",
27343
+ endTime
27344
+ ] }),
27345
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400", children: " | " }),
27346
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-600", children: reasonLabel })
27347
+ ] })
27002
27348
  ] }, index);
27003
27349
  }) })
27004
27350
  ] })
@@ -27096,7 +27442,7 @@ var HourlyOutputChartComponent = ({
27096
27442
  );
27097
27443
  };
27098
27444
  var HourlyOutputChart = React24__namespace.default.memo(HourlyOutputChartComponent, (prevProps, nextProps) => {
27099
- if (prevProps.pphThreshold !== nextProps.pphThreshold || prevProps.shiftStart !== nextProps.shiftStart || prevProps.showIdleTime !== nextProps.showIdleTime || prevProps.className !== nextProps.className) {
27445
+ if (prevProps.pphThreshold !== nextProps.pphThreshold || prevProps.shiftStart !== nextProps.shiftStart || prevProps.shiftEnd !== nextProps.shiftEnd || prevProps.shiftDate !== nextProps.shiftDate || prevProps.timezone !== nextProps.timezone || prevProps.showIdleTime !== nextProps.showIdleTime || prevProps.className !== nextProps.className) {
27100
27446
  return false;
27101
27447
  }
27102
27448
  if (prevProps.data.length !== nextProps.data.length) {
@@ -27119,6 +27465,33 @@ var HourlyOutputChart = React24__namespace.default.memo(HourlyOutputChartCompone
27119
27465
  if (prevArray.length !== nextArray.length) return false;
27120
27466
  if (!prevArray.every((val, idx) => val === nextArray[idx])) return false;
27121
27467
  }
27468
+ const prevIdleClips = prevProps.idleTimeClips || [];
27469
+ const nextIdleClips = nextProps.idleTimeClips || [];
27470
+ if (prevIdleClips.length !== nextIdleClips.length) {
27471
+ return false;
27472
+ }
27473
+ for (let i = 0; i < prevIdleClips.length; i += 1) {
27474
+ const prevClip = prevIdleClips[i];
27475
+ const nextClip = nextIdleClips[i];
27476
+ if (prevClip.id !== nextClip.id || prevClip.idle_start_time !== nextClip.idle_start_time || prevClip.idle_end_time !== nextClip.idle_end_time) {
27477
+ return false;
27478
+ }
27479
+ }
27480
+ const prevClassifications = prevProps.idleTimeClipClassifications || {};
27481
+ const nextClassifications = nextProps.idleTimeClipClassifications || {};
27482
+ const prevClassKeys = Object.keys(prevClassifications);
27483
+ const nextClassKeys = Object.keys(nextClassifications);
27484
+ if (prevClassKeys.length !== nextClassKeys.length) {
27485
+ return false;
27486
+ }
27487
+ for (const key of prevClassKeys) {
27488
+ const prevClass = prevClassifications[key];
27489
+ const nextClass = nextClassifications[key];
27490
+ if (!nextClass) return false;
27491
+ if (prevClass.status !== nextClass.status || prevClass.label !== nextClass.label || prevClass.confidence !== nextClass.confidence) {
27492
+ return false;
27493
+ }
27494
+ }
27122
27495
  return true;
27123
27496
  });
27124
27497
  HourlyOutputChart.displayName = "HourlyOutputChart";
@@ -27138,6 +27511,7 @@ var VideoCard = React24__namespace.default.memo(({
27138
27511
  onClick,
27139
27512
  onFatalError,
27140
27513
  isVeryLowEfficiency = false,
27514
+ legend,
27141
27515
  cropping,
27142
27516
  canvasFps = 30,
27143
27517
  useRAF = true,
@@ -27149,6 +27523,7 @@ var VideoCard = React24__namespace.default.memo(({
27149
27523
  }) => {
27150
27524
  const videoRef = React24.useRef(null);
27151
27525
  const canvasRef = React24.useRef(null);
27526
+ const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
27152
27527
  if (cropping) {
27153
27528
  useHlsStreamWithCropping(videoRef, canvasRef, {
27154
27529
  src: hlsUrl,
@@ -27166,39 +27541,23 @@ var VideoCard = React24__namespace.default.memo(({
27166
27541
  });
27167
27542
  }
27168
27543
  const workspaceDisplayName = displayName || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
27169
- const getEfficiencyOverlayColor = (efficiency) => {
27170
- if (efficiency >= 80) {
27171
- return "bg-[#00D654]/25";
27172
- } else if (efficiency >= 70) {
27173
- return "bg-[#FFD700]/30";
27174
- } else {
27175
- return "bg-[#FF2D0A]/30";
27176
- }
27177
- };
27178
- const getEfficiencyBarColor = (efficiency) => {
27179
- if (efficiency >= 80) {
27180
- return "bg-[#00AB45]";
27181
- } else if (efficiency >= 70) {
27182
- return "bg-[#FFB020]";
27183
- } else {
27184
- return "bg-[#E34329]";
27185
- }
27186
- };
27187
- const efficiencyOverlayClass = getEfficiencyOverlayColor(workspace.efficiency);
27188
- const efficiencyBarClass = getEfficiencyBarColor(workspace.efficiency);
27544
+ const efficiencyColor = getEfficiencyColor(workspace.efficiency, effectiveLegend);
27545
+ const efficiencyOverlayClass = efficiencyColor === "green" ? "bg-[#00D654]/25" : efficiencyColor === "yellow" ? "bg-[#FFD700]/30" : "bg-[#FF2D0A]/30";
27546
+ const efficiencyBarClass = efficiencyColor === "green" ? "bg-[#00AB45]" : efficiencyColor === "yellow" ? "bg-[#FFB020]" : "bg-[#E34329]";
27547
+ const efficiencyStatus = efficiencyColor === "green" ? "High" : efficiencyColor === "yellow" ? "Medium" : "Low";
27189
27548
  const trendInfo = workspace.trend !== void 0 ? getTrendArrowAndColor(workspace.trend) : null;
27190
27549
  const handleClick = React24.useCallback(() => {
27191
27550
  trackCoreEvent("Workspace Card Clicked", {
27192
27551
  workspace_id: workspace.workspace_uuid,
27193
27552
  workspace_name: workspace.workspace_name,
27194
27553
  efficiency: workspace.efficiency,
27195
- status: workspace.efficiency >= 80 ? "High" : workspace.efficiency >= 70 ? "Medium" : "Low",
27554
+ status: efficiencyStatus,
27196
27555
  trend: workspace.trend
27197
27556
  });
27198
27557
  if (onClick) {
27199
27558
  onClick();
27200
27559
  }
27201
- }, [onClick, workspace]);
27560
+ }, [onClick, workspace, efficiencyStatus]);
27202
27561
  return /* @__PURE__ */ jsxRuntime.jsxs(
27203
27562
  "div",
27204
27563
  {
@@ -27301,6 +27660,7 @@ var VideoGridView = React24__namespace.default.memo(({
27301
27660
  workspaces,
27302
27661
  selectedLine,
27303
27662
  className = "",
27663
+ legend,
27304
27664
  videoSources = {},
27305
27665
  displayNames = {},
27306
27666
  onWorkspaceHover,
@@ -27316,6 +27676,7 @@ var VideoGridView = React24__namespace.default.memo(({
27316
27676
  const videoConfig = useVideoConfig();
27317
27677
  const dashboardConfig = useDashboardConfig();
27318
27678
  const { cropping, canvasConfig, hlsUrls } = videoConfig;
27679
+ const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
27319
27680
  const mergedVideoSources = {
27320
27681
  defaultHlsUrl: videoSources.defaultHlsUrl || hlsUrls?.defaultHlsUrl || DEFAULT_HLS_URL,
27321
27682
  workspaceHlsUrls: { ...videoSources.workspaceHlsUrls, ...hlsUrls?.workspaceHlsUrls },
@@ -27510,6 +27871,7 @@ var VideoGridView = React24__namespace.default.memo(({
27510
27871
  onClick: () => handleWorkspaceClick(workspace),
27511
27872
  onFatalError: () => handleStreamError(workspaceId),
27512
27873
  isVeryLowEfficiency,
27874
+ legend: effectiveLegend,
27513
27875
  cropping: workspaceCropping,
27514
27876
  canvasFps: canvasConfig?.fps,
27515
27877
  displayName: (
@@ -27537,10 +27899,12 @@ var MapGridView = React24__namespace.default.memo(({
27537
27899
  className = "",
27538
27900
  displayNames = {},
27539
27901
  onWorkspaceHover,
27540
- onWorkspaceHoverEnd
27902
+ onWorkspaceHoverEnd,
27903
+ legend
27541
27904
  }) => {
27542
27905
  const router$1 = router.useRouter();
27543
27906
  const dashboardConfig = useDashboardConfig();
27907
+ const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
27544
27908
  const mapViewConfig = dashboardConfig?.mapViewConfig;
27545
27909
  const workspacePositions = mapViewConfig?.workspacePositions || [];
27546
27910
  const aspectRatio2 = mapViewConfig?.aspectRatio || 16 / 9;
@@ -27552,10 +27916,11 @@ var MapGridView = React24__namespace.default.memo(({
27552
27916
  return map;
27553
27917
  }, [workspaces]);
27554
27918
  const getPerformanceColor = React24.useCallback((efficiency) => {
27555
- if (efficiency >= 80) return "border-green-500 bg-green-50";
27556
- if (efficiency >= 50) return "border-yellow-500 bg-yellow-50";
27919
+ const color2 = getEfficiencyColor(efficiency, effectiveLegend);
27920
+ if (color2 === "green") return "border-green-500 bg-green-50";
27921
+ if (color2 === "yellow") return "border-yellow-500 bg-yellow-50";
27557
27922
  return "border-red-500 bg-red-50";
27558
- }, []);
27923
+ }, [effectiveLegend]);
27559
27924
  const handleWorkspaceClick = React24.useCallback((workspace) => {
27560
27925
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
27561
27926
  trackCoreEvent("Workspace Detail Clicked", {
@@ -27595,7 +27960,7 @@ var MapGridView = React24__namespace.default.memo(({
27595
27960
  const workspace = workspaceMetricsMap.get(wsKey);
27596
27961
  if (!workspace) return null;
27597
27962
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
27598
- getPerformanceColor(workspace.efficiency);
27963
+ const performanceColor = getPerformanceColor(workspace.efficiency);
27599
27964
  const showExclamation = workspace.show_exclamation ?? (workspace.efficiency < 50 && workspace.efficiency >= 10);
27600
27965
  const workspaceDisplayName = displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || // Always pass line_id to fallback to ensure correct mapping per line
27601
27966
  getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
@@ -27620,7 +27985,7 @@ var MapGridView = React24__namespace.default.memo(({
27620
27985
  className: `
27621
27986
  relative rounded-lg border-2 shadow-lg hover:shadow-xl
27622
27987
  transition-all duration-200 hover:scale-105 bg-white
27623
- ${workspace.efficiency >= 80 ? "border-green-500" : workspace.efficiency >= 50 ? "border-yellow-500" : "border-red-500"}
27988
+ ${performanceColor}
27624
27989
  ${position.size === "conveyor" ? "w-32 h-24" : position.size === "large" ? "w-40 h-32" : "w-36 h-28"}
27625
27990
  `,
27626
27991
  children: [
@@ -28201,7 +28566,7 @@ var PieChart4 = ({
28201
28566
  }
28202
28567
  );
28203
28568
  };
28204
- const CustomTooltip3 = ({ active, payload }) => {
28569
+ const CustomTooltip4 = ({ active, payload }) => {
28205
28570
  if (active && payload && payload.length) {
28206
28571
  const data2 = payload[0];
28207
28572
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white px-3 py-2 shadow-lg rounded-lg border border-gray-200", children: [
@@ -28232,7 +28597,7 @@ var PieChart4 = ({
28232
28597
  children: dataWithPercentage.map((entry, index) => /* @__PURE__ */ jsxRuntime.jsx(recharts.Cell, { fill: colors[index % colors.length] }, `cell-${index}`))
28233
28598
  }
28234
28599
  ),
28235
- /* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip3, {}) }),
28600
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip4, {}) }),
28236
28601
  /* @__PURE__ */ jsxRuntime.jsx(
28237
28602
  recharts.Legend,
28238
28603
  {
@@ -28444,31 +28809,6 @@ var WhatsAppShareButton = ({
28444
28809
  }
28445
28810
  );
28446
28811
  };
28447
- var AxelOrb = ({
28448
- className = "",
28449
- size = "md",
28450
- animate = false
28451
- }) => {
28452
- const sizeClasses = {
28453
- sm: "w-8 h-8",
28454
- md: "w-10 h-10",
28455
- lg: "w-12 h-12",
28456
- xl: "w-16 h-16",
28457
- "2xl": "w-20 h-20"
28458
- };
28459
- return /* @__PURE__ */ jsxRuntime.jsx(
28460
- "div",
28461
- {
28462
- className: `${sizeClasses[size]} rounded-full ${animate ? "animate-float" : ""} ${className}`,
28463
- style: {
28464
- background: "linear-gradient(to top, #078DDB 0%, #65ADD6 33%, #A3D0E6 66%, #C7E2EC 100%)",
28465
- boxShadow: "0 4px 12px rgba(7, 141, 219, 0.4), 0 0 20px rgba(7, 141, 219, 0.2)"
28466
- },
28467
- "aria-label": "Axel AI",
28468
- role: "img"
28469
- }
28470
- );
28471
- };
28472
28812
  var BreakNotificationPopup = ({
28473
28813
  activeBreaks,
28474
28814
  onDismiss,
@@ -28556,7 +28896,7 @@ var BreakNotificationPopup = ({
28556
28896
  className: "relative z-10",
28557
28897
  children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white text-gray-900 rounded-lg border border-gray-200 shadow-xl overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
28558
28898
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start space-x-3 flex-1", children: [
28559
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(AxelOrb, { size: "md" }) }),
28899
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 rounded-full bg-amber-100 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Coffee, { className: "w-5 h-5 text-amber-600" }) }) }),
28560
28900
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
28561
28901
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [
28562
28902
  /* @__PURE__ */ jsxRuntime.jsxs("h4", { className: "font-semibold text-sm text-gray-900", children: [
@@ -28829,7 +29169,11 @@ var AxelNotificationPopup = ({
28829
29169
  className: "p-3",
28830
29170
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
28831
29171
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start space-x-3 flex-1", children: [
28832
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(AxelOrb, { size: "md" }) }),
29172
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-shrink-0", children: [
29173
+ suggestion.type === "improvement" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingUp, { className: "w-5 h-5 text-blue-600" }) }),
29174
+ (suggestion.type === "alert" || suggestion.type === "bottleneck_triage") && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 rounded-full bg-red-100 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "w-5 h-5 text-red-600" }) }),
29175
+ suggestion.type === "insight" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 rounded-full bg-purple-100 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "w-5 h-5 text-purple-600" }) })
29176
+ ] }),
28833
29177
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
28834
29178
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [
28835
29179
  /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-sm text-gray-900", children: suggestion.title }),
@@ -29564,10 +29908,9 @@ var HlsVideoPlayer = React24.forwardRef(({
29564
29908
  fetchSetup: function(context, initParams) {
29565
29909
  const url = context.url;
29566
29910
  if (isR2WorkerUrl(url, r2WorkerDomain) && authToken) {
29567
- initParams.headers = {
29568
- ...initParams.headers,
29569
- "Authorization": `Bearer ${authToken}`
29570
- };
29911
+ const headers = initParams.headers instanceof Headers ? initParams.headers : new Headers(initParams.headers || {});
29912
+ headers.set("Authorization", `Bearer ${authToken}`);
29913
+ initParams.headers = headers;
29571
29914
  }
29572
29915
  return new Request(url, initParams);
29573
29916
  }
@@ -32890,126 +33233,6 @@ function useClipsRealtimeUpdates({
32890
33233
  hasNewClips: newClipsNotification !== null && newClipsNotification.count > 0
32891
33234
  };
32892
33235
  }
32893
-
32894
- // src/lib/services/clipClassificationService.ts
32895
- function parseCleanLabel(rawLabel) {
32896
- if (!rawLabel) return null;
32897
- const patterns = [
32898
- /"Clip Label"\s*:\s*"([^"]+)"/,
32899
- // Old format
32900
- /"clip_label"\s*:\s*"([^"]+)"/,
32901
- // New format
32902
- /'Clip Label'\s*:\s*'([^']+)'/,
32903
- // Single quotes
32904
- /'clip_label'\s*:\s*'([^']+)'/
32905
- // Single quotes
32906
- ];
32907
- for (const pattern of patterns) {
32908
- const match = rawLabel.match(pattern);
32909
- if (match) {
32910
- const label = match[1].trim();
32911
- return label.replace(/ /g, "_");
32912
- }
32913
- }
32914
- return null;
32915
- }
32916
- async function fetchClassifications(clipIds, token) {
32917
- if (!clipIds || clipIds.length === 0) {
32918
- return {};
32919
- }
32920
- try {
32921
- const CHUNK_SIZE = 50;
32922
- const chunks = [];
32923
- for (let i = 0; i < clipIds.length; i += CHUNK_SIZE) {
32924
- chunks.push(clipIds.slice(i, i + CHUNK_SIZE));
32925
- }
32926
- console.log(`[fetchClassifications] Fetching ${clipIds.length} classifications in ${chunks.length} chunks`);
32927
- const apiBase = process.env.NEXT_PUBLIC_BACKEND_URL || "http://localhost:8000";
32928
- const chunkResults = await Promise.all(
32929
- chunks.map(async (chunkIds) => {
32930
- try {
32931
- const response = await fetch(
32932
- `${apiBase}/api/classification/batch?clip_ids=${chunkIds.join(",")}`,
32933
- {
32934
- headers: {
32935
- "Authorization": `Bearer ${token}`,
32936
- "Content-Type": "application/json"
32937
- }
32938
- }
32939
- );
32940
- if (!response.ok) {
32941
- console.error(`Classification API error for chunk: ${response.status}`);
32942
- return Object.fromEntries(
32943
- chunkIds.map((id3) => [id3, { status: "processing" }])
32944
- );
32945
- }
32946
- const data = await response.json();
32947
- return data.classifications || {};
32948
- } catch (error) {
32949
- console.error("Error fetching chunk:", error);
32950
- return Object.fromEntries(
32951
- chunkIds.map((id3) => [id3, { status: "processing" }])
32952
- );
32953
- }
32954
- })
32955
- );
32956
- const allClassifications = Object.assign({}, ...chunkResults);
32957
- console.log(`[fetchClassifications] Total fetched: ${Object.keys(allClassifications).length} classifications`);
32958
- return allClassifications;
32959
- } catch (error) {
32960
- console.error("Error fetching classifications:", error);
32961
- return Object.fromEntries(
32962
- clipIds.map((id3) => [id3, { status: "processing" }])
32963
- );
32964
- }
32965
- }
32966
- function useClassificationRealtimeUpdates({
32967
- clipIds,
32968
- enabled = true,
32969
- onClassificationUpdate
32970
- }) {
32971
- const supabase = useSupabase();
32972
- React24.useEffect(() => {
32973
- if (!enabled || !clipIds || clipIds.length === 0 || !supabase) {
32974
- return;
32975
- }
32976
- const channelName = `classification:${clipIds.slice(0, 5).join("-")}`;
32977
- console.log(`[useClassificationRealtimeUpdates] Subscribing to channel: ${channelName}`);
32978
- const channel = supabase.channel(channelName).on(
32979
- "postgres_changes",
32980
- {
32981
- event: "INSERT",
32982
- schema: "public",
32983
- table: "clip_classification",
32984
- filter: `clip_id=in.(${clipIds.join(",")})`
32985
- },
32986
- (payload) => {
32987
- console.log("[useClassificationRealtimeUpdates] New classification:", payload);
32988
- try {
32989
- const { clip_id, clip_label, confidence_score } = payload.new;
32990
- const parsedLabel = parseCleanLabel(clip_label);
32991
- if (parsedLabel) {
32992
- onClassificationUpdate(clip_id, {
32993
- status: "classified",
32994
- label: parsedLabel,
32995
- confidence: confidence_score || 0
32996
- });
32997
- } else {
32998
- console.warn("[useClassificationRealtimeUpdates] Failed to parse label:", clip_label);
32999
- }
33000
- } catch (error) {
33001
- console.error("[useClassificationRealtimeUpdates] Error processing update:", error);
33002
- }
33003
- }
33004
- ).subscribe((status) => {
33005
- console.log(`[useClassificationRealtimeUpdates] Subscription status:`, status);
33006
- });
33007
- return () => {
33008
- console.log(`[useClassificationRealtimeUpdates] Unsubscribing from ${channelName}`);
33009
- supabase.removeChannel(channel);
33010
- };
33011
- }, [clipIds, enabled, supabase, onClassificationUpdate]);
33012
- }
33013
33236
  var BottlenecksContent = ({
33014
33237
  workspaceId,
33015
33238
  workspaceName,
@@ -36457,6 +36680,31 @@ var DetailedHealthStatus = ({
36457
36680
  }
36458
36681
  );
36459
36682
  };
36683
+ var AxelOrb = ({
36684
+ className = "",
36685
+ size = "md",
36686
+ animate = false
36687
+ }) => {
36688
+ const sizeClasses = {
36689
+ sm: "w-8 h-8",
36690
+ md: "w-10 h-10",
36691
+ lg: "w-12 h-12",
36692
+ xl: "w-16 h-16",
36693
+ "2xl": "w-20 h-20"
36694
+ };
36695
+ return /* @__PURE__ */ jsxRuntime.jsx(
36696
+ "div",
36697
+ {
36698
+ className: `${sizeClasses[size]} rounded-full ${animate ? "animate-float" : ""} ${className}`,
36699
+ style: {
36700
+ background: "linear-gradient(to top, #078DDB 0%, #65ADD6 33%, #A3D0E6 66%, #C7E2EC 100%)",
36701
+ boxShadow: "0 4px 12px rgba(7, 141, 219, 0.4), 0 0 20px rgba(7, 141, 219, 0.2)"
36702
+ },
36703
+ "aria-label": "Axel AI",
36704
+ role: "img"
36705
+ }
36706
+ );
36707
+ };
36460
36708
  var LinePdfExportButton = ({
36461
36709
  targetElement,
36462
36710
  fileName = "line-export",
@@ -36574,10 +36822,10 @@ var LineHistoryCalendar = ({
36574
36822
  }, [configuredTimezone]);
36575
36823
  const monthBounds = React24.useMemo(() => getMonthKeyBounds(year, month), [year, month]);
36576
36824
  const calendarData = React24.useMemo(() => {
36577
- const startOfMonth = dateFnsTz.toZonedTime(new Date(year, month, 1), configuredTimezone);
36578
- const endOfMonth = dateFnsTz.toZonedTime(new Date(year, month + 1, 0), configuredTimezone);
36579
- const totalDays = endOfMonth.getDate();
36580
- let startOffset = startOfMonth.getDay() - 1;
36825
+ const startOfMonth2 = dateFnsTz.toZonedTime(new Date(year, month, 1), configuredTimezone);
36826
+ const endOfMonth2 = dateFnsTz.toZonedTime(new Date(year, month + 1, 0), configuredTimezone);
36827
+ const totalDays = endOfMonth2.getDate();
36828
+ let startOffset = startOfMonth2.getDay() - 1;
36581
36829
  if (startOffset === -1) startOffset = 6;
36582
36830
  const calendar = Array(startOffset).fill(null);
36583
36831
  for (let day = 1; day <= totalDays; day++) {
@@ -36608,12 +36856,13 @@ var LineHistoryCalendar = ({
36608
36856
  if (shift.hasData !== void 0) return shift.hasData;
36609
36857
  return shift.total_workspaces > 0 || shift.avg_efficiency > 0 || shift.underperforming_workspaces > 0;
36610
36858
  };
36611
- const getPerformanceColor = (efficiency, date, hasData) => {
36859
+ const getPerformanceColor = (efficiency, date, hasData, isFilteredOut = false) => {
36612
36860
  const istNow = todayInZone;
36613
36861
  const nowString = `${istNow.getFullYear()}-${String(istNow.getMonth() + 1).padStart(2, "0")}-${String(istNow.getDate()).padStart(2, "0")}`;
36614
36862
  const dateString = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
36615
36863
  if (dateString > nowString) return "bg-gray-200 dark:bg-gray-700";
36616
36864
  if (!hasData) return "bg-gray-300 dark:bg-gray-600";
36865
+ if (isFilteredOut) return "bg-gray-200 dark:bg-gray-700";
36617
36866
  return efficiency >= 75 ? "bg-green-500 dark:bg-green-600" : "bg-red-500 dark:bg-red-600";
36618
36867
  };
36619
36868
  const isCurrentDate = (date) => {
@@ -36658,12 +36907,13 @@ var LineHistoryCalendar = ({
36658
36907
  const showRange = rangeStart && rangeEnd ? !(rangeStart === monthBounds.startKey && rangeEnd === monthBounds.endKey) : false;
36659
36908
  const inRange = showRange ? dateKey >= rangeStart && dateKey <= rangeEnd : false;
36660
36909
  const isRangeEdge = inRange && (dateKey === rangeStart || dateKey === rangeEnd);
36910
+ const isFilteredOut = showRange && !inRange;
36661
36911
  return /* @__PURE__ */ jsxRuntime.jsx(
36662
36912
  "div",
36663
36913
  {
36664
- className: `group h-full ${isFuture || !hasData ? "cursor-not-allowed" : "cursor-pointer hover:opacity-90"}`,
36914
+ className: `group h-full ${isFuture || !hasData || isFilteredOut ? "cursor-not-allowed" : "cursor-pointer hover:opacity-90"}`,
36665
36915
  onClick: () => {
36666
- if (!isFuture && hasData) {
36916
+ if (!isFuture && hasData && !isFilteredOut) {
36667
36917
  const dateObj2 = day.date instanceof Date ? day.date : new Date(day.date);
36668
36918
  const year2 = dateObj2.getFullYear();
36669
36919
  const month2 = String(dateObj2.getMonth() + 1).padStart(2, "0");
@@ -36695,7 +36945,7 @@ var LineHistoryCalendar = ({
36695
36945
  }
36696
36946
  },
36697
36947
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `
36698
- ${getPerformanceColor(shiftData.avg_efficiency || 0, dateObj, hasData)}
36948
+ ${getPerformanceColor(shiftData.avg_efficiency || 0, dateObj, hasData, isFilteredOut)}
36699
36949
  rounded-lg h-full p-2 relative
36700
36950
  ${isToday2 ? "ring-2 ring-blue-500 dark:ring-blue-400 ring-offset-2 dark:ring-offset-gray-800 shadow-md" : ""}
36701
36951
  `, children: [
@@ -36706,10 +36956,11 @@ var LineHistoryCalendar = ({
36706
36956
  }
36707
36957
  ),
36708
36958
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: `
36709
- text-base font-medium ${hasData ? "text-white" : "text-gray-500"} flex items-center relative z-10
36959
+ text-base font-medium flex items-center relative z-10
36960
+ ${hasData && !isFuture && !isFilteredOut ? "text-white" : "text-gray-400"}
36710
36961
  ${isToday2 ? "bg-blue-500 dark:bg-blue-600 rounded-full w-7 h-7 justify-center" : ""}
36711
36962
  `, children: dateObj.getDate() }),
36712
- !isFuture && hasData && renderStats(shiftData, dateObj)
36963
+ !isFuture && hasData && !isFilteredOut && renderStats(shiftData, dateObj)
36713
36964
  ] })
36714
36965
  }
36715
36966
  );
@@ -36884,54 +37135,19 @@ var IdleTimeReasonChart = ({
36884
37135
  ] });
36885
37136
  };
36886
37137
  var IdleTimeReasonChart_default = IdleTimeReasonChart;
37138
+ var WEEKDAYS2 = ["S", "M", "T", "W", "T", "F", "S"];
36887
37139
  var MonthlyRangeFilter = ({
36888
37140
  month,
36889
37141
  year,
36890
- timezone,
36891
37142
  value,
36892
37143
  onChange,
37144
+ onMonthNavigate,
36893
37145
  className
36894
37146
  }) => {
36895
37147
  const monthBounds = React24.useMemo(() => getMonthKeyBounds(year, month), [year, month]);
36896
- const weekRanges = React24.useMemo(() => getMonthWeekRanges(year, month, timezone), [year, month, timezone]);
36897
37148
  const normalizedRange = React24.useMemo(() => {
36898
37149
  return normalizeDateKeyRange(value.startKey, value.endKey, monthBounds.startKey, monthBounds.endKey);
36899
37150
  }, [value.startKey, value.endKey, monthBounds.startKey, monthBounds.endKey]);
36900
- const monthLabel = React24.useMemo(() => {
36901
- return new Date(year, month).toLocaleString("default", { month: "long", year: "numeric" });
36902
- }, [year, month]);
36903
- const presetOptions = React24.useMemo(() => {
36904
- const fullMonthRangeLabel = `${formatDateKeyForDisplay(monthBounds.startKey, "MMM d")} - ${formatDateKeyForDisplay(monthBounds.endKey, "MMM d")}`;
36905
- const presets = [
36906
- {
36907
- id: "full-month",
36908
- label: "Full month",
36909
- rangeLabel: fullMonthRangeLabel,
36910
- startKey: monthBounds.startKey,
36911
- endKey: monthBounds.endKey
36912
- }
36913
- ];
36914
- weekRanges.forEach((range, index) => {
36915
- const rangeLabel = `${formatDateKeyForDisplay(range.startKey, "MMM d")} - ${formatDateKeyForDisplay(range.endKey, "MMM d")}`;
36916
- presets.push({
36917
- id: `week-${index + 1}`,
36918
- label: `Week ${index + 1}`,
36919
- rangeLabel,
36920
- startKey: range.startKey,
36921
- endKey: range.endKey
36922
- });
36923
- });
36924
- return presets;
36925
- }, [monthBounds.startKey, monthBounds.endKey, weekRanges]);
36926
- const activePreset = React24.useMemo(() => {
36927
- return presetOptions.find(
36928
- (preset) => preset.startKey === normalizedRange.startKey && preset.endKey === normalizedRange.endKey
36929
- );
36930
- }, [presetOptions, normalizedRange.startKey, normalizedRange.endKey]);
36931
- const displayLabel = React24.useMemo(() => {
36932
- if (activePreset) return activePreset.label;
36933
- return "Custom range";
36934
- }, [activePreset]);
36935
37151
  const fullMonthLabel = React24.useMemo(() => {
36936
37152
  const startLabel = formatDateKeyForDisplay(monthBounds.startKey, "MMM d");
36937
37153
  const endLabel = formatDateKeyForDisplay(monthBounds.endKey, "MMM d, yyyy");
@@ -36940,14 +37156,26 @@ var MonthlyRangeFilter = ({
36940
37156
  const displayRange = React24.useMemo(() => {
36941
37157
  return formatRangeLabel(normalizedRange, fullMonthLabel);
36942
37158
  }, [normalizedRange, fullMonthLabel]);
36943
- const [customStart, setCustomStart] = React24.useState(normalizedRange.startKey);
36944
- const [customEnd, setCustomEnd] = React24.useState(normalizedRange.endKey);
36945
37159
  const [isOpen, setIsOpen] = React24.useState(false);
36946
37160
  const dropdownRef = React24.useRef(null);
37161
+ const [calendarMonth, setCalendarMonth] = React24.useState(month);
37162
+ const [calendarYear, setCalendarYear] = React24.useState(year);
37163
+ const [rangeStart, setRangeStart] = React24.useState(parseDateKeyToDate(normalizedRange.startKey));
37164
+ const [rangeEnd, setRangeEnd] = React24.useState(parseDateKeyToDate(normalizedRange.endKey));
37165
+ const [selecting, setSelecting] = React24.useState(false);
36947
37166
  React24.useEffect(() => {
36948
- setCustomStart(normalizedRange.startKey);
36949
- setCustomEnd(normalizedRange.endKey);
36950
- }, [normalizedRange.startKey, normalizedRange.endKey]);
37167
+ setCalendarMonth(month);
37168
+ setCalendarYear(year);
37169
+ }, [month, year]);
37170
+ React24.useEffect(() => {
37171
+ if (isOpen) {
37172
+ setRangeStart(parseDateKeyToDate(normalizedRange.startKey));
37173
+ setRangeEnd(parseDateKeyToDate(normalizedRange.endKey));
37174
+ setSelecting(false);
37175
+ setCalendarMonth(month);
37176
+ setCalendarYear(year);
37177
+ }
37178
+ }, [isOpen, normalizedRange.startKey, normalizedRange.endKey, month, year]);
36951
37179
  React24.useEffect(() => {
36952
37180
  const handleClickOutside = (event) => {
36953
37181
  if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
@@ -36957,107 +37185,211 @@ var MonthlyRangeFilter = ({
36957
37185
  document.addEventListener("mousedown", handleClickOutside);
36958
37186
  return () => document.removeEventListener("mousedown", handleClickOutside);
36959
37187
  }, []);
36960
- const handlePresetSelect = (startKey, endKey) => {
36961
- const normalized = normalizeDateKeyRange(startKey, endKey, monthBounds.startKey, monthBounds.endKey);
36962
- onChange(normalized);
36963
- setIsOpen(false);
37188
+ const handleDayClick = (day) => {
37189
+ if (!selecting || !rangeStart) {
37190
+ setRangeStart(day);
37191
+ setRangeEnd(null);
37192
+ setSelecting(true);
37193
+ } else {
37194
+ if (day < rangeStart) {
37195
+ setRangeEnd(rangeStart);
37196
+ setRangeStart(day);
37197
+ } else {
37198
+ setRangeEnd(day);
37199
+ }
37200
+ setSelecting(false);
37201
+ }
36964
37202
  };
36965
- const handleCustomApply = () => {
36966
- if (!customStart || !customEnd) return;
36967
- const normalized = normalizeDateKeyRange(customStart, customEnd, monthBounds.startKey, monthBounds.endKey);
36968
- onChange(normalized);
36969
- setIsOpen(false);
37203
+ const handleApply = () => {
37204
+ if (rangeStart) {
37205
+ const endDate = rangeEnd || rangeStart;
37206
+ const startKey = dateFns.format(rangeStart, "yyyy-MM-dd");
37207
+ const endKey = dateFns.format(endDate, "yyyy-MM-dd");
37208
+ const normalized = normalizeDateKeyRange(startKey, endKey, monthBounds.startKey, monthBounds.endKey);
37209
+ onChange(normalized);
37210
+ setIsOpen(false);
37211
+ }
37212
+ };
37213
+ const handlePreviousMonth = () => {
37214
+ const prevMonthDate = dateFns.subMonths(new Date(calendarYear, calendarMonth), 1);
37215
+ const newMonth = prevMonthDate.getMonth();
37216
+ const newYear = prevMonthDate.getFullYear();
37217
+ if (newYear < 2023) return;
37218
+ setCalendarMonth(newMonth);
37219
+ setCalendarYear(newYear);
37220
+ onMonthNavigate?.(newMonth, newYear);
36970
37221
  };
37222
+ const handleNextMonth = () => {
37223
+ const nextMonthDate = dateFns.addMonths(new Date(calendarYear, calendarMonth), 1);
37224
+ const newMonth = nextMonthDate.getMonth();
37225
+ const newYear = nextMonthDate.getFullYear();
37226
+ const currentDate = /* @__PURE__ */ new Date();
37227
+ if (newYear > currentDate.getFullYear() || newYear === currentDate.getFullYear() && newMonth > currentDate.getMonth()) {
37228
+ return;
37229
+ }
37230
+ setCalendarMonth(newMonth);
37231
+ setCalendarYear(newYear);
37232
+ onMonthNavigate?.(newMonth, newYear);
37233
+ };
37234
+ const canGoPrevious = !(calendarYear === 2023 && calendarMonth === 0);
37235
+ const canGoNext = !(calendarYear === (/* @__PURE__ */ new Date()).getFullYear() && calendarMonth === (/* @__PURE__ */ new Date()).getMonth());
37236
+ const monthDate = React24.useMemo(() => new Date(calendarYear, calendarMonth), [calendarYear, calendarMonth]);
37237
+ const calendarDays = React24.useMemo(() => {
37238
+ const start = dateFns.startOfMonth(monthDate);
37239
+ const end = dateFns.endOfMonth(monthDate);
37240
+ const days = dateFns.eachDayOfInterval({ start, end });
37241
+ const startDayOfWeek = dateFns.getDay(start);
37242
+ const emptySlots = Array(startDayOfWeek).fill(null);
37243
+ return [...emptySlots, ...days];
37244
+ }, [monthDate]);
37245
+ const isInRange = React24.useCallback((day) => {
37246
+ if (!rangeStart) return false;
37247
+ if (!rangeEnd) return dateFns.isSameDay(day, rangeStart);
37248
+ return dateFns.isWithinInterval(day, {
37249
+ start: dateFns.startOfDay(rangeStart),
37250
+ end: dateFns.startOfDay(rangeEnd)
37251
+ });
37252
+ }, [rangeStart, rangeEnd]);
37253
+ const isRangeStart = React24.useCallback((day) => {
37254
+ return rangeStart && dateFns.isSameDay(day, rangeStart);
37255
+ }, [rangeStart]);
37256
+ const isRangeEnd = React24.useCallback((day) => {
37257
+ return rangeEnd && dateFns.isSameDay(day, rangeEnd);
37258
+ }, [rangeEnd]);
37259
+ const monthName = dateFns.format(monthDate, "MMMM yyyy");
37260
+ const startDisplay = rangeStart ? dateFns.format(rangeStart, "MMM d, yyyy") : "Start Date";
37261
+ const endDisplay = rangeEnd ? dateFns.format(rangeEnd, "MMM d, yyyy") : "End Date";
36971
37262
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative ${className ?? ""}`, ref: dropdownRef, children: [
36972
37263
  /* @__PURE__ */ jsxRuntime.jsxs(
36973
37264
  "button",
36974
37265
  {
36975
37266
  type: "button",
36976
37267
  onClick: () => setIsOpen((prev) => !prev),
36977
- className: "flex items-center gap-3 rounded-lg border border-slate-200 bg-white px-3 py-2 text-left shadow-sm transition-colors hover:border-slate-300 focus:outline-none focus:ring-2 focus:ring-blue-500",
37268
+ className: clsx(
37269
+ "flex items-center gap-2 rounded-lg border bg-white px-3 py-2 text-left shadow-sm transition-all duration-200 focus:outline-none",
37270
+ isOpen ? "border-blue-500 ring-2 ring-blue-500/20" : "border-gray-200 hover:border-gray-300 hover:shadow"
37271
+ ),
36978
37272
  children: [
36979
- /* @__PURE__ */ jsxRuntime.jsx(outline.CalendarIcon, { className: "h-4 w-4 text-slate-500" }),
36980
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
36981
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] uppercase tracking-[0.2em] text-slate-500", children: displayLabel }),
36982
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold text-slate-900", children: displayRange })
36983
- ] }),
37273
+ /* @__PURE__ */ jsxRuntime.jsx(outline.CalendarIcon, { className: "h-4 w-4 text-gray-400" }),
37274
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-900", children: displayRange }),
36984
37275
  /* @__PURE__ */ jsxRuntime.jsx(
36985
37276
  outline.ChevronDownIcon,
36986
37277
  {
36987
- className: `ml-2 h-4 w-4 text-slate-400 transition-transform ${isOpen ? "rotate-180" : ""}`
37278
+ className: clsx(
37279
+ "ml-2 h-4 w-4 text-gray-400 transition-transform duration-200",
37280
+ isOpen && "rotate-180"
37281
+ )
36988
37282
  }
36989
37283
  )
36990
37284
  ]
36991
37285
  }
36992
37286
  ),
36993
- isOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-0 z-50 mt-2 w-80 rounded-lg border border-slate-200 bg-white shadow-lg", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3", children: [
36994
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-500", children: "Weeks" }),
36995
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 space-y-1", children: presetOptions.map((preset) => {
36996
- const isActive = preset.startKey === normalizedRange.startKey && preset.endKey === normalizedRange.endKey;
36997
- return /* @__PURE__ */ jsxRuntime.jsxs(
37287
+ isOpen && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-0 z-50 mt-2 w-[320px] overflow-hidden rounded-lg border border-gray-200 bg-white shadow-xl p-4 animate-in fade-in zoom-in-95 duration-200", children: [
37288
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-4 pb-4 border-b border-gray-100", children: [
37289
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
37290
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-[10px] uppercase tracking-wider font-medium text-gray-500 mb-1", children: "Start" }),
37291
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx(
37292
+ "px-2 py-1.5 rounded-md text-sm font-medium border transition-colors",
37293
+ rangeStart ? "bg-blue-50 border-blue-200 text-blue-700" : "bg-gray-50 border-gray-100 text-gray-400"
37294
+ ), children: startDisplay })
37295
+ ] }),
37296
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-4 text-gray-300", children: /* @__PURE__ */ jsxRuntime.jsx(outline.ChevronRightIcon, { className: "h-4 w-4" }) }),
37297
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
37298
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-[10px] uppercase tracking-wider font-medium text-gray-500 mb-1", children: "End" }),
37299
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx(
37300
+ "px-2 py-1.5 rounded-md text-sm font-medium border transition-colors",
37301
+ rangeEnd ? "bg-blue-50 border-blue-200 text-blue-700" : "bg-gray-50 border-gray-100 text-gray-400"
37302
+ ), children: endDisplay })
37303
+ ] })
37304
+ ] }),
37305
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-4", children: [
37306
+ /* @__PURE__ */ jsxRuntime.jsx(
36998
37307
  "button",
36999
37308
  {
37000
37309
  type: "button",
37001
- onClick: () => handlePresetSelect(preset.startKey, preset.endKey),
37002
- className: `flex w-full items-center justify-between rounded-md px-2 py-2 text-left text-sm transition-colors ${isActive ? "bg-blue-50 text-blue-700" : "text-slate-700 hover:bg-slate-50"}`,
37003
- children: [
37004
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: preset.label }),
37005
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-slate-500", children: preset.rangeLabel })
37006
- ]
37310
+ onClick: handlePreviousMonth,
37311
+ disabled: !canGoPrevious,
37312
+ className: clsx(
37313
+ "p-1 rounded-md transition-colors",
37314
+ canGoPrevious ? "hover:bg-gray-100 text-gray-600" : "text-gray-300 cursor-not-allowed"
37315
+ ),
37316
+ "aria-label": "Previous month",
37317
+ children: /* @__PURE__ */ jsxRuntime.jsx(outline.ChevronLeftIcon, { className: "h-5 w-5" })
37318
+ }
37319
+ ),
37320
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold text-gray-900", children: monthName }),
37321
+ /* @__PURE__ */ jsxRuntime.jsx(
37322
+ "button",
37323
+ {
37324
+ type: "button",
37325
+ onClick: handleNextMonth,
37326
+ disabled: !canGoNext,
37327
+ className: clsx(
37328
+ "p-1 rounded-md transition-colors",
37329
+ canGoNext ? "hover:bg-gray-100 text-gray-600" : "text-gray-300 cursor-not-allowed"
37330
+ ),
37331
+ "aria-label": "Next month",
37332
+ children: /* @__PURE__ */ jsxRuntime.jsx(outline.ChevronRightIcon, { className: "h-5 w-5" })
37333
+ }
37334
+ )
37335
+ ] }),
37336
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-1 mb-2", children: WEEKDAYS2.map((day, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 flex items-center justify-center text-xs font-medium text-gray-400", children: day }, i)) }),
37337
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-0", children: calendarDays.map((day, i) => {
37338
+ if (!day) {
37339
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-10" }, `empty-${i}`);
37340
+ }
37341
+ const inRange = isInRange(day);
37342
+ const isStart = isRangeStart(day);
37343
+ const isEnd = isRangeEnd(day);
37344
+ const isSingleDaySelection = isStart && (!rangeEnd || rangeEnd && dateFns.isSameDay(rangeStart, rangeEnd));
37345
+ const hasSelection = rangeStart !== null;
37346
+ const dayNum = day.getDate();
37347
+ return /* @__PURE__ */ jsxRuntime.jsx(
37348
+ "button",
37349
+ {
37350
+ type: "button",
37351
+ onClick: () => handleDayClick(day),
37352
+ className: clsx(
37353
+ "h-10 w-full flex items-center justify-center text-sm font-medium transition-all duration-150 relative",
37354
+ // Not in range - fade when there's a selection
37355
+ !inRange && hasSelection && "text-gray-300 hover:text-gray-500 hover:bg-gray-50 rounded-md",
37356
+ !inRange && !hasSelection && "text-gray-700 hover:bg-gray-100 rounded-md",
37357
+ // Middle of range - subtle highlight
37358
+ inRange && !isStart && !isEnd && "bg-blue-50 text-blue-600",
37359
+ // Start of range
37360
+ isStart && !isSingleDaySelection && "bg-blue-600 text-white rounded-l-md shadow-sm z-10",
37361
+ // End of range
37362
+ isEnd && !isSingleDaySelection && "bg-blue-600 text-white rounded-r-md shadow-sm z-10",
37363
+ // Single day selection
37364
+ isSingleDaySelection && "bg-blue-600 text-white rounded-md shadow-sm z-10"
37365
+ ),
37366
+ children: dayNum
37007
37367
  },
37008
- preset.id
37368
+ day.toISOString()
37009
37369
  );
37010
37370
  }) }),
37011
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 border-t border-slate-200 pt-3", children: [
37012
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
37013
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-500", children: "Custom range" }),
37014
- !activePreset && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-full border border-blue-100 bg-blue-50 px-2 py-0.5 text-[10px] uppercase tracking-wide text-blue-700", children: "Selected" })
37015
- ] }),
37016
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 grid grid-cols-1 gap-2 sm:grid-cols-2", children: [
37017
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
37018
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-[11px] font-medium text-slate-600", children: "Start" }),
37019
- /* @__PURE__ */ jsxRuntime.jsx(
37020
- "input",
37021
- {
37022
- type: "date",
37023
- value: customStart,
37024
- onChange: (event) => setCustomStart(event.target.value),
37025
- min: monthBounds.startKey,
37026
- max: monthBounds.endKey,
37027
- className: "mt-1 w-full rounded-md border border-slate-200 bg-white px-2 py-2 text-sm text-slate-700 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-200"
37028
- }
37029
- )
37030
- ] }),
37031
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
37032
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-[11px] font-medium text-slate-600", children: "End" }),
37033
- /* @__PURE__ */ jsxRuntime.jsx(
37034
- "input",
37035
- {
37036
- type: "date",
37037
- value: customEnd,
37038
- onChange: (event) => setCustomEnd(event.target.value),
37039
- min: monthBounds.startKey,
37040
- max: monthBounds.endKey,
37041
- className: "mt-1 w-full rounded-md border border-slate-200 bg-white px-2 py-2 text-sm text-slate-700 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-200"
37042
- }
37043
- )
37044
- ] })
37045
- ] }),
37371
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-2 mt-4 pt-4 border-t border-gray-100", children: [
37372
+ /* @__PURE__ */ jsxRuntime.jsx(
37373
+ "button",
37374
+ {
37375
+ type: "button",
37376
+ onClick: () => setIsOpen(false),
37377
+ className: "rounded-lg px-3 py-1.5 text-sm font-medium text-gray-600 hover:bg-gray-50 hover:text-gray-900 transition-colors",
37378
+ children: "Cancel"
37379
+ }
37380
+ ),
37046
37381
  /* @__PURE__ */ jsxRuntime.jsx(
37047
37382
  "button",
37048
37383
  {
37049
37384
  type: "button",
37050
- onClick: handleCustomApply,
37051
- className: "mt-3 w-full rounded-md bg-blue-600 px-3 py-2 text-sm font-semibold text-white transition-colors hover:bg-blue-700",
37052
- children: "Apply range"
37385
+ onClick: handleApply,
37386
+ disabled: !rangeStart,
37387
+ className: "rounded-lg bg-blue-600 px-4 py-1.5 text-sm font-medium text-white shadow-sm hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all",
37388
+ children: "Apply"
37053
37389
  }
37054
37390
  )
37055
- ] }),
37056
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 text-xs text-slate-400", children: [
37057
- "Month: ",
37058
- monthLabel
37059
37391
  ] })
37060
- ] }) })
37392
+ ] })
37061
37393
  ] });
37062
37394
  };
37063
37395
  var MonthlyRangeFilter_default = MonthlyRangeFilter;
@@ -37065,7 +37397,36 @@ var DEFAULT_PERFORMANCE_DATA = {
37065
37397
  avg_efficiency: 0,
37066
37398
  underperforming_workspaces: 0,
37067
37399
  total_workspaces: 0,
37068
- hasData: false
37400
+ hasData: false,
37401
+ output: 0,
37402
+ idealOutput: 0
37403
+ };
37404
+ var getOrdinal = (n) => {
37405
+ const suffix = ["th", "st", "nd", "rd"];
37406
+ const v = n % 100;
37407
+ return n + (suffix[(v - 20) % 10] || suffix[v] || suffix[0]);
37408
+ };
37409
+ var CustomTooltip2 = ({ active, payload, label }) => {
37410
+ if (active && payload && payload.length) {
37411
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white/95 backdrop-blur-sm p-3 rounded-lg shadow-lg border border-gray-100", children: [
37412
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-gray-800 mb-1", children: label }),
37413
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
37414
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-600", children: [
37415
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Actual:" }),
37416
+ " ",
37417
+ Math.round(payload[0].value),
37418
+ " units"
37419
+ ] }),
37420
+ payload[0].payload.idealOutput > 0 && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-600", children: [
37421
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Target:" }),
37422
+ " ",
37423
+ Math.round(payload[0].payload.idealOutput),
37424
+ " units"
37425
+ ] })
37426
+ ] })
37427
+ ] });
37428
+ }
37429
+ return null;
37069
37430
  };
37070
37431
  var getShiftData2 = (day, shiftId) => {
37071
37432
  const shift = day.shifts[shiftId];
@@ -37104,7 +37465,7 @@ var LineMonthlyHistory = ({
37104
37465
  }, [rangeStart, rangeEnd, monthBounds.startKey, monthBounds.endKey]);
37105
37466
  const isFullRange = React24.useMemo(() => isFullMonthRange(normalizedRange, year, month), [normalizedRange, year, month]);
37106
37467
  const monthLabel = React24.useMemo(() => new Date(year, month).toLocaleString("default", { month: "long" }), [year, month]);
37107
- const rangeLabel = React24.useMemo(() => {
37468
+ React24.useMemo(() => {
37108
37469
  return isFullRange ? monthLabel : formatRangeLabel(normalizedRange, monthLabel);
37109
37470
  }, [isFullRange, normalizedRange, monthLabel]);
37110
37471
  const analysisMonthlyData = React24.useMemo(() => {
@@ -37126,15 +37487,6 @@ var LineMonthlyHistory = ({
37126
37487
  shiftId: selectedShiftId,
37127
37488
  enabled: !!lineId
37128
37489
  });
37129
- if (isLoading) {
37130
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[calc(100vh-10rem)]", children: /* @__PURE__ */ jsxRuntime.jsx(
37131
- OptifyeLogoLoader_default,
37132
- {
37133
- size: "lg",
37134
- message: "Loading monthly performance data..."
37135
- }
37136
- ) });
37137
- }
37138
37490
  const averages = (analysisMonthlyData || []).reduce(
37139
37491
  (acc, day) => {
37140
37492
  const shiftData = getShiftData2(day, selectedShiftId);
@@ -37153,6 +37505,88 @@ var LineMonthlyHistory = ({
37153
37505
  const avgEfficiency = averages.count > 0 ? averages.efficiency / averages.count : 0;
37154
37506
  const avgUnderperforming = averages.count > 0 ? averages.underperforming / averages.count : 0;
37155
37507
  const avgTotalWorkspaces = averages.count > 0 ? averages.totalWorkspaces / averages.count : 0;
37508
+ const hasRealData = (shift) => {
37509
+ if (shift.hasData !== void 0) return shift.hasData;
37510
+ return shift.avg_efficiency > 0 || shift.underperforming_workspaces > 0 || shift.total_workspaces > 0 || (shift.output || 0) > 0 || (shift.idealOutput || 0) > 0;
37511
+ };
37512
+ const chartData = React24.useMemo(() => {
37513
+ const rangeStartDate = parseDateKeyToDate(normalizedRange.startKey);
37514
+ const rangeEndDate = parseDateKeyToDate(normalizedRange.endKey);
37515
+ const dayNumbers = [];
37516
+ for (let d = new Date(rangeStartDate); d <= rangeEndDate; d.setDate(d.getDate() + 1)) {
37517
+ dayNumbers.push(d.getDate());
37518
+ }
37519
+ const dailyData = [];
37520
+ let maxOutput = 0;
37521
+ let lastSetTarget = 0;
37522
+ for (let i = dayNumbers.length - 1; i >= 0; i--) {
37523
+ const day = dayNumbers[i];
37524
+ const dayData = analysisMonthlyData.find((d) => {
37525
+ const date = new Date(d.date);
37526
+ return date.getDate() === day;
37527
+ });
37528
+ const shiftData = dayData ? getShiftData2(dayData, selectedShiftId) : null;
37529
+ const idealOutput = shiftData ? shiftData.idealOutput || 0 : 0;
37530
+ if (idealOutput > 0) {
37531
+ lastSetTarget = idealOutput;
37532
+ break;
37533
+ }
37534
+ }
37535
+ for (const day of dayNumbers) {
37536
+ const dayData = analysisMonthlyData.find((d) => {
37537
+ const date = new Date(d.date);
37538
+ return date.getDate() === day;
37539
+ });
37540
+ const shiftData = dayData ? getShiftData2(dayData, selectedShiftId) : null;
37541
+ const output = shiftData && hasRealData(shiftData) ? shiftData.output || 0 : 0;
37542
+ const idealOutput = shiftData ? shiftData.idealOutput || 0 : 0;
37543
+ if (output > maxOutput) maxOutput = output;
37544
+ const color2 = output >= lastSetTarget ? "#00AB45" : "#E34329";
37545
+ dailyData.push({
37546
+ hour: getOrdinal(day),
37547
+ // Using ordinal format (1st, 2nd, 3rd, etc.)
37548
+ timeRange: `Day ${day}`,
37549
+ output,
37550
+ originalOutput: output,
37551
+ // For label display
37552
+ idealOutput,
37553
+ color: color2
37554
+ });
37555
+ }
37556
+ const calculatedMax = Math.max(maxOutput, lastSetTarget);
37557
+ const yAxisMax = calculatedMax > 0 ? calculatedMax * 1.1 : 100;
37558
+ return { data: dailyData, maxOutput, lastSetTarget, yAxisMax };
37559
+ }, [analysisMonthlyData, normalizedRange.startKey, normalizedRange.endKey, selectedShiftId]);
37560
+ const yAxisTicks = React24.useMemo(() => {
37561
+ const max = chartData.yAxisMax;
37562
+ const target = chartData.lastSetTarget;
37563
+ if (!max || max <= 0) return void 0;
37564
+ const desiredIntervals = 4;
37565
+ const roughStep = max / desiredIntervals;
37566
+ const power = Math.pow(10, Math.floor(Math.log10(roughStep)));
37567
+ const normalized = roughStep / power;
37568
+ let step = 1 * power;
37569
+ if (normalized >= 1.5 && normalized < 3) step = 2 * power;
37570
+ else if (normalized >= 3 && normalized < 7) step = 5 * power;
37571
+ else if (normalized >= 7) step = 10 * power;
37572
+ const ticks = [];
37573
+ for (let v = 0; v <= max; v += step) {
37574
+ ticks.push(Math.round(v));
37575
+ }
37576
+ if (target > 0) {
37577
+ ticks.push(Math.round(target));
37578
+ }
37579
+ return Array.from(new Set(ticks)).filter((v) => v >= 0 && v <= max).sort((a, b) => a - b);
37580
+ }, [chartData.yAxisMax, chartData.lastSetTarget]);
37581
+ if (isLoading) {
37582
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[calc(100vh-10rem)]", children: /* @__PURE__ */ jsxRuntime.jsx(
37583
+ OptifyeLogoLoader_default,
37584
+ {
37585
+ size: "lg",
37586
+ message: "Loading monthly performance data..."
37587
+ }
37588
+ ) });
37589
+ }
37156
37590
  const renderPerformanceSquares = (performances) => {
37157
37591
  if (!performances || !Array.isArray(performances)) {
37158
37592
  return null;
@@ -37244,64 +37678,18 @@ var LineMonthlyHistory = ({
37244
37678
  year,
37245
37679
  timezone,
37246
37680
  value: normalizedRange,
37247
- onChange: (nextRange) => onRangeChange?.(nextRange)
37681
+ onChange: (nextRange) => onRangeChange?.(nextRange),
37682
+ onMonthNavigate: (newMonth, newYear) => onCalendarMonthChange?.(new Date(newYear, newMonth))
37248
37683
  }
37249
37684
  ) })
37250
37685
  ] }),
37251
37686
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-6 mt-6", children: [
37252
37687
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-6", children: [
37253
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-center items-center mb-6 space-x-4", children: [
37254
- /* @__PURE__ */ jsxRuntime.jsx(
37255
- "button",
37256
- {
37257
- onClick: () => {
37258
- let newMonth = month - 1;
37259
- let newYear = year;
37260
- if (newMonth < 0) {
37261
- newMonth = 11;
37262
- newYear -= 1;
37263
- }
37264
- if (onCalendarMonthChange) {
37265
- onCalendarMonthChange(new Date(newYear, newMonth));
37266
- }
37267
- },
37268
- className: "p-2 rounded-full hover:bg-gray-100",
37269
- "aria-label": "Previous month",
37270
- children: /* @__PURE__ */ jsxRuntime.jsx(outline.ChevronLeftIcon, { className: "w-5 h-5" })
37271
- }
37272
- ),
37273
- /* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "text-lg font-semibold text-gray-700", children: [
37274
- new Date(year, month).toLocaleString("default", { month: "long" }),
37275
- " ",
37276
- year
37277
- ] }),
37278
- /* @__PURE__ */ jsxRuntime.jsx(
37279
- "button",
37280
- {
37281
- onClick: () => {
37282
- let newMonth = month + 1;
37283
- let newYear = year;
37284
- if (newMonth > 11) {
37285
- newMonth = 0;
37286
- newYear += 1;
37287
- }
37288
- const currentDate = /* @__PURE__ */ new Date();
37289
- const currentYear = currentDate.getFullYear();
37290
- const currentMonth = currentDate.getMonth();
37291
- if (newYear > currentYear || newYear === currentYear && newMonth > currentMonth) {
37292
- return;
37293
- }
37294
- if (onCalendarMonthChange) {
37295
- onCalendarMonthChange(new Date(newYear, newMonth));
37296
- }
37297
- },
37298
- className: `p-2 rounded-full ${(/* @__PURE__ */ new Date()).getFullYear() === year && (/* @__PURE__ */ new Date()).getMonth() === month ? "text-gray-300 cursor-not-allowed" : "hover:bg-gray-100 text-gray-600"}`,
37299
- disabled: (/* @__PURE__ */ new Date()).getFullYear() === year && (/* @__PURE__ */ new Date()).getMonth() === month,
37300
- "aria-label": "Next month",
37301
- children: /* @__PURE__ */ jsxRuntime.jsx(outline.ChevronRightIcon, { className: "w-5 h-5" })
37302
- }
37303
- )
37304
- ] }),
37688
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center items-center mb-6", children: /* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "text-lg font-semibold text-gray-700", children: [
37689
+ new Date(year, month).toLocaleString("default", { month: "long" }),
37690
+ " ",
37691
+ year
37692
+ ] }) }),
37305
37693
  /* @__PURE__ */ jsxRuntime.jsx(
37306
37694
  LineHistoryCalendar_default,
37307
37695
  {
@@ -37316,26 +37704,20 @@ var LineMonthlyHistory = ({
37316
37704
  }
37317
37705
  )
37318
37706
  ] }),
37319
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-6", children: [
37320
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4", children: [
37321
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-6", children: [
37322
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-semibold text-gray-700 mb-1 text-center", children: "Efficiency" }),
37323
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-500 mb-4 text-center", children: [
37324
- "Cumulative for ",
37325
- rangeLabel
37326
- ] }),
37327
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-4xl font-bold text-center text-gray-900", children: [
37707
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
37708
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
37709
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4 flex flex-col items-center justify-center", children: [
37710
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-gray-700 mb-1 text-center", children: "Efficiency" }),
37711
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] text-gray-400 mb-1 text-center uppercase tracking-wide", children: "Cumulative" }),
37712
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-2xl font-bold text-center text-gray-900", children: [
37328
37713
  avgEfficiency.toFixed(1),
37329
37714
  "%"
37330
37715
  ] })
37331
37716
  ] }),
37332
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-6", children: [
37333
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-semibold text-gray-700 mb-1 text-center", children: "Avg. Underperforming" }),
37334
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-500 mb-4 text-center", children: [
37335
- "Cumulative for ",
37336
- rangeLabel
37337
- ] }),
37338
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-4xl font-bold text-center text-gray-900", children: [
37717
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4 flex flex-col items-center justify-center", children: [
37718
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-gray-700 mb-1 text-center", children: "Avg. Underperforming" }),
37719
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] text-gray-400 mb-1 text-center uppercase tracking-wide", children: "Cumulative" }),
37720
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-2xl font-bold text-center text-gray-900", children: [
37339
37721
  avgUnderperforming.toFixed(1),
37340
37722
  "/",
37341
37723
  avgTotalWorkspaces.toFixed(1)
@@ -37343,9 +37725,9 @@ var LineMonthlyHistory = ({
37343
37725
  ] })
37344
37726
  ] }),
37345
37727
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-4", children: [
37346
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4 flex flex-col", children: [
37347
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-semibold text-gray-700 mb-3", children: "Idle time Breakdown" }),
37348
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-[200px]", children: /* @__PURE__ */ jsxRuntime.jsx(
37728
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4 flex flex-col h-[220px]", children: [
37729
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-gray-700 mb-1", children: "Idle time Breakdown" }),
37730
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 relative -ml-4", children: /* @__PURE__ */ jsxRuntime.jsx(
37349
37731
  IdleTimeReasonChart,
37350
37732
  {
37351
37733
  data: idleReasonsChartData,
@@ -37355,34 +37737,117 @@ var LineMonthlyHistory = ({
37355
37737
  chartKey
37356
37738
  ) })
37357
37739
  ] }),
37358
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-6", children: [
37359
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-gray-700 mb-4", children: "Top 3 Poorest Performers" }),
37360
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
37740
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4 h-[220px] flex flex-col", children: [
37741
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm font-semibold text-gray-700 mb-3", children: "Top 3 Poorest Performers" }),
37742
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto pr-1 space-y-1 custom-scrollbar", children: [
37361
37743
  (underperformingWorkspaces[selectedShiftId] || []).slice(0, 3).map((workspace) => /* @__PURE__ */ jsxRuntime.jsx(
37362
37744
  "button",
37363
37745
  {
37364
37746
  onClick: () => handleWorkspaceClick(workspace),
37365
- className: "block hover:bg-gray-50 transition-colors rounded-lg w-full text-left",
37366
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between py-2 px-2 border-b border-gray-100 last:border-b-0", children: [
37367
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "font-medium text-gray-900 text-sm", children: [
37747
+ className: "block hover:bg-gray-50 transition-colors rounded-lg w-full text-left group",
37748
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between py-2 px-2 border-b border-gray-50 group-last:border-b-0", children: [
37749
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "font-medium text-gray-900 text-xs", children: [
37368
37750
  getWorkspaceDisplayName(workspace.workspace_name, lineId),
37369
- workspace.avg_efficiency !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-1 text-xs text-gray-500", children: [
37751
+ workspace.avg_efficiency !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-1 text-[10px] text-gray-500", children: [
37370
37752
  "(",
37371
37753
  (workspace.avg_efficiency || 0).toFixed(1),
37372
37754
  "%)"
37373
37755
  ] })
37374
37756
  ] }),
37375
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
37376
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500 hidden xl:block", children: "Last 5 days:" }),
37377
- renderPerformanceSquares(workspace.last_5_days)
37378
- ] })
37757
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1.5", children: renderPerformanceSquares(workspace.last_5_days) })
37379
37758
  ] })
37380
37759
  },
37381
37760
  workspace.workspace_uuid
37382
37761
  )),
37383
- (!underperformingWorkspaces || !underperformingWorkspaces[selectedShiftId]?.length) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center text-gray-500 py-4 text-sm", children: "No underperforming workspaces found" })
37762
+ (!underperformingWorkspaces || !underperformingWorkspaces[selectedShiftId]?.length) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center text-gray-400 py-8 text-xs italic", children: "No underperforming workspaces" })
37384
37763
  ] })
37385
37764
  ] })
37765
+ ] }),
37766
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4", children: [
37767
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between mb-2", children: /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-gray-700", children: "Daily Output" }) }),
37768
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: "220px" }, children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
37769
+ recharts.BarChart,
37770
+ {
37771
+ data: chartData.data,
37772
+ margin: { top: 20, right: 10, bottom: 40, left: 10 },
37773
+ children: [
37774
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#f3f4f6" }),
37775
+ /* @__PURE__ */ jsxRuntime.jsx(
37776
+ recharts.XAxis,
37777
+ {
37778
+ dataKey: "hour",
37779
+ tick: { fontSize: 10, fill: "#6b7280" },
37780
+ interval: 0,
37781
+ angle: -45,
37782
+ textAnchor: "end",
37783
+ height: 60
37784
+ }
37785
+ ),
37786
+ /* @__PURE__ */ jsxRuntime.jsx(
37787
+ recharts.YAxis,
37788
+ {
37789
+ domain: [0, chartData.yAxisMax],
37790
+ width: 40,
37791
+ ticks: yAxisTicks,
37792
+ tick: (props) => {
37793
+ const { x, y, payload } = props;
37794
+ const value = Math.round(payload.value);
37795
+ const targetValue = Math.round(chartData.lastSetTarget);
37796
+ const isTarget = value === targetValue && targetValue > 0;
37797
+ return /* @__PURE__ */ jsxRuntime.jsx(
37798
+ "text",
37799
+ {
37800
+ x: x - 5,
37801
+ y: y + 4,
37802
+ textAnchor: "end",
37803
+ fontSize: "10",
37804
+ fill: isTarget ? "#E34329" : "#6b7280",
37805
+ fontWeight: isTarget ? "600" : "normal",
37806
+ children: value < 1e-3 ? "" : value.toLocaleString()
37807
+ }
37808
+ );
37809
+ }
37810
+ }
37811
+ ),
37812
+ /* @__PURE__ */ jsxRuntime.jsx(
37813
+ recharts.Tooltip,
37814
+ {
37815
+ cursor: false,
37816
+ content: CustomTooltip2
37817
+ }
37818
+ ),
37819
+ chartData.lastSetTarget > 0 && /* @__PURE__ */ jsxRuntime.jsx(
37820
+ recharts.ReferenceLine,
37821
+ {
37822
+ y: chartData.lastSetTarget,
37823
+ stroke: "#E34329",
37824
+ strokeDasharray: "5 5",
37825
+ strokeWidth: 2
37826
+ }
37827
+ ),
37828
+ /* @__PURE__ */ jsxRuntime.jsx(
37829
+ recharts.Bar,
37830
+ {
37831
+ dataKey: "output",
37832
+ radius: [4, 4, 0, 0],
37833
+ isAnimationActive: true,
37834
+ animationBegin: 0,
37835
+ animationDuration: 1e3,
37836
+ animationEasing: "ease-out",
37837
+ children: chartData.data.map((entry, index) => /* @__PURE__ */ jsxRuntime.jsx(
37838
+ recharts.Cell,
37839
+ {
37840
+ fill: entry.output === 0 ? "#f3f4f6" : entry.color,
37841
+ className: entry.output > 0 ? "hover:opacity-80 transition-opacity cursor-pointer" : ""
37842
+ },
37843
+ `cell-${index}`
37844
+ ))
37845
+ }
37846
+ )
37847
+ ]
37848
+ },
37849
+ chartKey
37850
+ ) }) })
37386
37851
  ] })
37387
37852
  ] })
37388
37853
  ] })
@@ -38523,7 +38988,7 @@ var styles = `
38523
38988
  transform: scale(1) !important;
38524
38989
  }
38525
38990
  `;
38526
- var WEEKDAYS2 = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
38991
+ var WEEKDAYS3 = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
38527
38992
  var getTimeInZoneAsDate = (timezone) => {
38528
38993
  const time2 = getCurrentTimeInZone(timezone);
38529
38994
  return typeof time2 === "string" ? new Date(time2) : time2;
@@ -38555,10 +39020,10 @@ var WorkspaceHistoryCalendar = ({
38555
39020
  return () => clearTimeout(timer);
38556
39021
  }, [month, year]);
38557
39022
  const calendarData = React24.useMemo(() => {
38558
- const startOfMonth = dateFnsTz.toZonedTime(new Date(year, month, 1), configuredTimezone);
38559
- const endOfMonth = dateFnsTz.toZonedTime(new Date(year, month + 1, 0), configuredTimezone);
38560
- const totalDays = endOfMonth.getDate();
38561
- let startOffset = startOfMonth.getDay() - 1;
39023
+ const startOfMonth2 = dateFnsTz.toZonedTime(new Date(year, month, 1), configuredTimezone);
39024
+ const endOfMonth2 = dateFnsTz.toZonedTime(new Date(year, month + 1, 0), configuredTimezone);
39025
+ const totalDays = endOfMonth2.getDate();
39026
+ let startOffset = startOfMonth2.getDay() - 1;
38562
39027
  if (startOffset === -1) startOffset = 6;
38563
39028
  const calendar = Array(startOffset).fill(null);
38564
39029
  for (let day = 1; day <= totalDays; day++) {
@@ -38767,7 +39232,7 @@ var WorkspaceHistoryCalendar = ({
38767
39232
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs sm:text-sm text-gray-500 mt-1", children: "Calendar view of daily performance" })
38768
39233
  ] }),
38769
39234
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 sm:gap-4 lg:gap-6", children: [
38770
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-1 sm:gap-2", children: WEEKDAYS2.map((day) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs sm:text-sm font-medium text-gray-600 text-center", children: day.slice(0, 3) }, day)) }),
39235
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-1 sm:gap-2", children: WEEKDAYS3.map((day) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs sm:text-sm font-medium text-gray-600 text-center", children: day.slice(0, 3) }, day)) }),
38771
39236
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-1 sm:gap-2", children: calendarData.calendar.map((day, index) => {
38772
39237
  const startOffset = calendarData.startOffset;
38773
39238
  const dayNumber = index >= startOffset ? index - startOffset + 1 : null;
@@ -38820,13 +39285,13 @@ var WorkspaceHistoryCalendar = ({
38820
39285
  ] })
38821
39286
  ] });
38822
39287
  };
38823
- var WEEKDAYS3 = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
38824
- var getOrdinal = (n) => {
39288
+ var WEEKDAYS4 = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
39289
+ var getOrdinal2 = (n) => {
38825
39290
  const suffix = ["th", "st", "nd", "rd"];
38826
39291
  const v = n % 100;
38827
39292
  return n + (suffix[(v - 20) % 10] || suffix[v] || suffix[0]);
38828
39293
  };
38829
- var CustomTooltip2 = ({ active, payload, label }) => {
39294
+ var CustomTooltip3 = ({ active, payload, label }) => {
38830
39295
  if (active && payload && payload.length) {
38831
39296
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white/95 backdrop-blur-sm p-3 rounded-lg shadow-lg border border-gray-100", children: [
38832
39297
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-gray-800 mb-1", children: label }),
@@ -38929,7 +39394,7 @@ var WorkspaceMonthlyHistory = ({
38929
39394
  if (output > maxOutput) maxOutput = output;
38930
39395
  const color2 = output >= lastSetTarget ? "#00AB45" : "#E34329";
38931
39396
  dailyData.push({
38932
- hour: getOrdinal(day),
39397
+ hour: getOrdinal2(day),
38933
39398
  // Using ordinal format (1st, 2nd, 3rd, etc.)
38934
39399
  timeRange: `Day ${day}`,
38935
39400
  output,
@@ -38963,9 +39428,26 @@ var WorkspaceMonthlyHistory = ({
38963
39428
  ticks.push(Math.round(v));
38964
39429
  }
38965
39430
  if (target > 0) {
38966
- ticks.push(Math.round(target));
39431
+ const roundedTarget = Math.round(target);
39432
+ if (!ticks.includes(roundedTarget)) {
39433
+ let nearestIndex = -1;
39434
+ let nearestDistance = Number.POSITIVE_INFINITY;
39435
+ ticks.forEach((tick, index) => {
39436
+ if (tick === 0) return;
39437
+ const distance2 = Math.abs(tick - roundedTarget);
39438
+ if (distance2 < nearestDistance) {
39439
+ nearestDistance = distance2;
39440
+ nearestIndex = index;
39441
+ }
39442
+ });
39443
+ if (nearestIndex >= 0) {
39444
+ ticks[nearestIndex] = roundedTarget;
39445
+ } else {
39446
+ ticks.push(roundedTarget);
39447
+ }
39448
+ }
38967
39449
  }
38968
- return Array.from(new Set(ticks)).filter((v) => v >= 0 && v <= max).sort((a, b) => a - b);
39450
+ return ticks.filter((v) => v >= 0 && v <= max * 1.05).sort((a, b) => a - b);
38969
39451
  }, [chartData.yAxisMax, chartData.lastSetTarget]);
38970
39452
  const pieChartData = React24.useMemo(() => {
38971
39453
  const validShifts = analysisMonthlyData.map((d) => getShiftData(d, selectedShiftId)).filter(hasRealData);
@@ -39001,10 +39483,10 @@ var WorkspaceMonthlyHistory = ({
39001
39483
  };
39002
39484
  }, [analysisMonthlyData, selectedShiftId]);
39003
39485
  const calendarData = React24.useMemo(() => {
39004
- const startOfMonth = new Date(year, month, 1);
39005
- const endOfMonth = new Date(year, month + 1, 0);
39006
- const totalDays = endOfMonth.getDate();
39007
- let startOffset = startOfMonth.getDay() - 1;
39486
+ const startOfMonth2 = new Date(year, month, 1);
39487
+ const endOfMonth2 = new Date(year, month + 1, 0);
39488
+ const totalDays = endOfMonth2.getDate();
39489
+ let startOffset = startOfMonth2.getDay() - 1;
39008
39490
  if (startOffset === -1) startOffset = 6;
39009
39491
  const calendar = Array(startOffset).fill(null);
39010
39492
  for (let day = 1; day <= totalDays; day++) {
@@ -39071,68 +39553,19 @@ var WorkspaceMonthlyHistory = ({
39071
39553
  year,
39072
39554
  timezone,
39073
39555
  value: normalizedRange,
39074
- onChange: (nextRange) => onRangeChange?.(nextRange)
39556
+ onChange: (nextRange) => onRangeChange?.(nextRange),
39557
+ onMonthNavigate
39075
39558
  }
39076
39559
  ) })
39077
39560
  ] }),
39078
39561
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-6 mt-6", children: [
39079
39562
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-6", children: [
39080
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-center items-center mb-6 space-x-4", children: [
39081
- /* @__PURE__ */ jsxRuntime.jsx(
39082
- "button",
39083
- {
39084
- onClick: () => {
39085
- let newMonth = month - 1;
39086
- let newYear = year;
39087
- if (newMonth < 0) {
39088
- newMonth = 11;
39089
- newYear -= 1;
39090
- }
39091
- if (newYear < 2023) {
39092
- return;
39093
- }
39094
- if (onMonthNavigate) {
39095
- onMonthNavigate(newMonth, newYear);
39096
- }
39097
- },
39098
- className: "p-2 rounded-full hover:bg-gray-100",
39099
- "aria-label": "Previous month",
39100
- children: /* @__PURE__ */ jsxRuntime.jsx(outline.ChevronLeftIcon, { className: "w-5 h-5" })
39101
- }
39102
- ),
39103
- /* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "text-lg font-semibold text-gray-700", children: [
39104
- new Date(year, month).toLocaleString("default", { month: "long" }),
39105
- " ",
39106
- year
39107
- ] }),
39108
- /* @__PURE__ */ jsxRuntime.jsx(
39109
- "button",
39110
- {
39111
- onClick: () => {
39112
- let newMonth = month + 1;
39113
- let newYear = year;
39114
- if (newMonth > 11) {
39115
- newMonth = 0;
39116
- newYear += 1;
39117
- }
39118
- const currentDate = /* @__PURE__ */ new Date();
39119
- const currentYear = currentDate.getFullYear();
39120
- const currentMonth = currentDate.getMonth();
39121
- if (newYear > currentYear || newYear === currentYear && newMonth > currentMonth) {
39122
- return;
39123
- }
39124
- if (onMonthNavigate) {
39125
- onMonthNavigate(newMonth, newYear);
39126
- }
39127
- },
39128
- className: `p-2 rounded-full ${(/* @__PURE__ */ new Date()).getFullYear() === year && (/* @__PURE__ */ new Date()).getMonth() === month ? "text-gray-300 cursor-not-allowed" : "hover:bg-gray-100 text-gray-600"}`,
39129
- disabled: (/* @__PURE__ */ new Date()).getFullYear() === year && (/* @__PURE__ */ new Date()).getMonth() === month,
39130
- "aria-label": "Next month",
39131
- children: /* @__PURE__ */ jsxRuntime.jsx(outline.ChevronRightIcon, { className: "w-5 h-5" })
39132
- }
39133
- )
39134
- ] }),
39135
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-2", children: WEEKDAYS3.map((day, idx) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium text-gray-500 dark:text-gray-400 text-center", children: day }, day)) }),
39563
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center items-center mb-6", children: /* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "text-lg font-semibold text-gray-700", children: [
39564
+ new Date(year, month).toLocaleString("default", { month: "long" }),
39565
+ " ",
39566
+ year
39567
+ ] }) }),
39568
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-2", children: WEEKDAYS4.map((day, idx) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium text-gray-500 dark:text-gray-400 text-center", children: day }, day)) }),
39136
39569
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-2 mt-6", children: calendarData.calendar.map((day, index) => {
39137
39570
  const dayNumber = index >= calendarData.startOffset ? index - calendarData.startOffset + 1 : null;
39138
39571
  if (!dayNumber || dayNumber > new Date(year, month + 1, 0).getDate()) {
@@ -39149,6 +39582,7 @@ var WorkspaceMonthlyHistory = ({
39149
39582
  const getPerformanceColor = () => {
39150
39583
  if (isFuture) return "bg-gray-200 dark:bg-gray-700";
39151
39584
  if (!hasData || !shiftData) return "bg-gray-300 dark:bg-gray-600";
39585
+ if (showRange && !inRange) return "bg-gray-200 dark:bg-gray-700";
39152
39586
  return shiftData.efficiency >= 75 ? "bg-green-500 dark:bg-green-600" : "bg-red-500 dark:bg-red-600";
39153
39587
  };
39154
39588
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-square relative", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -39168,7 +39602,8 @@ var WorkspaceMonthlyHistory = ({
39168
39602
  }
39169
39603
  ),
39170
39604
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: `
39171
- text-base font-medium ${hasData && !isFuture ? "text-white" : "text-gray-500"} flex items-center relative z-10
39605
+ text-base font-medium flex items-center relative z-10
39606
+ ${hasData && !isFuture && (!showRange || inRange) ? "text-white" : "text-gray-400"}
39172
39607
  ${isToday2 ? "bg-blue-500 dark:bg-blue-600 rounded-full w-7 h-7 justify-center" : ""}
39173
39608
  `, children: dayNumber }),
39174
39609
  !isFuture && hasData && shiftData && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-black/80 rounded-lg p-2 text-white opacity-0 group-hover:opacity-100 transition-opacity", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs space-y-1", children: [
@@ -39324,13 +39759,13 @@ var WorkspaceMonthlyHistory = ({
39324
39759
  recharts.YAxis,
39325
39760
  {
39326
39761
  domain: [0, chartData.yAxisMax],
39327
- width: 35,
39762
+ width: 40,
39328
39763
  ticks: yAxisTicks,
39329
39764
  tick: (props) => {
39330
39765
  const { x, y, payload } = props;
39331
39766
  const value = Math.round(payload.value);
39332
39767
  const targetValue = Math.round(chartData.lastSetTarget);
39333
- const isTarget = Math.abs(value - targetValue) < 1 && targetValue > 0;
39768
+ const isTarget = value === targetValue && targetValue > 0;
39334
39769
  return /* @__PURE__ */ jsxRuntime.jsx(
39335
39770
  "text",
39336
39771
  {
@@ -39340,7 +39775,7 @@ var WorkspaceMonthlyHistory = ({
39340
39775
  fontSize: "10",
39341
39776
  fill: isTarget ? "#E34329" : "#6b7280",
39342
39777
  fontWeight: isTarget ? "600" : "normal",
39343
- children: value < 1e-3 ? "" : value.toString()
39778
+ children: value < 1e-3 ? "" : value.toLocaleString()
39344
39779
  }
39345
39780
  );
39346
39781
  }
@@ -39350,7 +39785,7 @@ var WorkspaceMonthlyHistory = ({
39350
39785
  recharts.Tooltip,
39351
39786
  {
39352
39787
  cursor: false,
39353
- content: CustomTooltip2
39788
+ content: CustomTooltip3
39354
39789
  }
39355
39790
  ),
39356
39791
  chartData.lastSetTarget > 0 && /* @__PURE__ */ jsxRuntime.jsx(
@@ -39979,13 +40414,15 @@ var LiveTimer = () => {
39979
40414
  });
39980
40415
  return /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatter.format(time2) });
39981
40416
  };
39982
- var getEfficiencyColor = (efficiency) => {
39983
- if (efficiency >= 80) {
39984
- return "bg-[#00AB45]/90 hover:bg-[#00AB45]/95";
39985
- } else if (efficiency >= 70) {
39986
- return "bg-[#FFB020]/90 hover:bg-[#FFB020]/95";
39987
- } else {
39988
- return "bg-[#E34329]/90 hover:bg-[#E34329]/95";
40417
+ var getGradientEfficiencyClasses = (color2) => {
40418
+ switch (color2) {
40419
+ case "green":
40420
+ return "bg-gradient-to-r from-[#00AB45]/90 to-[#00AB45]/95";
40421
+ case "yellow":
40422
+ return "bg-gradient-to-r from-[#FFB020]/90 to-[#FFB020]/95";
40423
+ case "red":
40424
+ default:
40425
+ return "bg-gradient-to-r from-[#E34329]/90 to-[#E34329]/95";
39989
40426
  }
39990
40427
  };
39991
40428
  var TREND_STYLES = {
@@ -40053,30 +40490,40 @@ var getWorkspaceStyles = (position, isPlaceholder = false) => {
40053
40490
  ${additionalStyles}
40054
40491
  ${isPlaceholder ? "cursor-default" : ""}`;
40055
40492
  };
40056
- var Legend6 = () => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-1.5 sm:gap-3 bg-white/95 rounded-lg shadow-sm px-2 sm:px-4 py-1 sm:py-1.5 border border-gray-200/60 backdrop-blur-sm text-[10px] sm:text-sm", children: [
40057
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium text-gray-700 hidden sm:block", children: "Efficiency:" }),
40058
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 sm:gap-4", children: [
40059
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
40060
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-1.5 h-1.5 sm:w-2.5 sm:h-2.5 rounded-[12px] bg-[#00AB45]/90 ring-1 ring-[#00AB45]/20" }),
40061
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-600", children: "80-100%" })
40062
- ] }),
40063
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
40064
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-1.5 h-1.5 sm:w-2.5 sm:h-2.5 rounded-[12px] bg-[#FFB020]/90 ring-1 ring-[#FFB020]/20" }),
40065
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-600", children: "70-79%" })
40493
+ var formatPercentRange = (min, max) => {
40494
+ const format7 = (value) => Number.isInteger(value) ? `${value}` : value.toFixed(1);
40495
+ return `${format7(min)}-${format7(max)}%`;
40496
+ };
40497
+ var Legend6 = ({ useBottleneckLabel = false, legend }) => {
40498
+ const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
40499
+ const exclamationLabel = useBottleneckLabel ? "Bottleneck" : "<50% efficiency";
40500
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-1.5 sm:gap-3 bg-white/95 rounded-lg shadow-sm px-2 sm:px-4 py-1 sm:py-1.5 border border-gray-200/60 backdrop-blur-sm text-[10px] sm:text-sm", children: [
40501
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium text-gray-700 hidden sm:block", children: "Efficiency:" }),
40502
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 sm:gap-4", children: [
40503
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
40504
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-1.5 h-1.5 sm:w-2.5 sm:h-2.5 rounded-[12px] bg-[#00AB45]/90 ring-1 ring-[#00AB45]/20" }),
40505
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-600", children: formatPercentRange(effectiveLegend.green_min, effectiveLegend.green_max) })
40506
+ ] }),
40507
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
40508
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-1.5 h-1.5 sm:w-2.5 sm:h-2.5 rounded-[12px] bg-[#FFB020]/90 ring-1 ring-[#FFB020]/20" }),
40509
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-600", children: formatPercentRange(effectiveLegend.yellow_min, effectiveLegend.yellow_max) })
40510
+ ] }),
40511
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
40512
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-1.5 h-1.5 sm:w-2.5 sm:h-2.5 rounded-[12px] bg-[#E34329]/90 ring-1 ring-[#E34329]/20" }),
40513
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-600", children: formatPercentRange(effectiveLegend.red_min, effectiveLegend.red_max) })
40514
+ ] })
40066
40515
  ] }),
40516
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block w-px h-6 bg-gray-200 mx-1" }),
40067
40517
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
40068
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-1.5 h-1.5 sm:w-2.5 sm:h-2.5 rounded-[12px] bg-[#E34329]/90 ring-1 ring-[#E34329]/20" }),
40069
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-600", children: "<70%" })
40518
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center w-3 h-3 sm:w-5 sm:h-5 bg-[#E34329]/90 rounded-full ring-1 ring-[#E34329]/20 text-white font-bold text-[8px] sm:text-xs", children: "!" }),
40519
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-600", children: exclamationLabel })
40070
40520
  ] })
40071
- ] }),
40072
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block w-px h-6 bg-gray-200 mx-1" }),
40073
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
40074
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center w-3 h-3 sm:w-5 sm:h-5 bg-[#E34329]/90 rounded-full ring-1 ring-[#E34329]/20 text-white font-bold text-[8px] sm:text-xs", children: "!" }),
40075
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-600", children: "Bottleneck" })
40076
- ] })
40077
- ] });
40521
+ ] });
40522
+ };
40078
40523
  var arePropsEqual = (prevProps, nextProps) => {
40079
- 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
40524
+ const prevLegend = prevProps.legend || DEFAULT_EFFICIENCY_LEGEND;
40525
+ const nextLegend = nextProps.legend || DEFAULT_EFFICIENCY_LEGEND;
40526
+ 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 && prevLegend.green_min === nextLegend.green_min && prevLegend.green_max === nextLegend.green_max && prevLegend.yellow_min === nextLegend.yellow_min && prevLegend.yellow_max === nextLegend.yellow_max && prevLegend.red_min === nextLegend.red_min && prevLegend.red_max === nextLegend.red_max && prevLegend.critical_threshold === nextLegend.critical_threshold && // Position doesn't need deep equality check as it's generally static
40080
40527
  prevProps.position.id === nextProps.position.id;
40081
40528
  };
40082
40529
  var WorkspaceGridItem = React24__namespace.default.memo(({
@@ -40085,20 +40532,21 @@ var WorkspaceGridItem = React24__namespace.default.memo(({
40085
40532
  isBottleneck = false,
40086
40533
  isLowEfficiency = false,
40087
40534
  isVeryLowEfficiency = false,
40088
- onHoverChange
40535
+ onHoverChange,
40536
+ legend
40089
40537
  }) => {
40090
40538
  const { navigate } = useNavigation();
40539
+ const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
40091
40540
  const isInactive = React24.useMemo(() => !data.workspace_id || data.efficiency < 10, [data.workspace_id, data.efficiency]);
40092
40541
  const colorClass = React24.useMemo(() => {
40093
40542
  const isConveyorEnd = position.size === "conveyor" && position.orientation;
40543
+ const efficiencyColor = getEfficiencyColor(data.efficiency, effectiveLegend);
40094
40544
  if (isConveyorEnd) {
40095
40545
  if (isInactive) return "bg-gray-300/90";
40096
- if (data.efficiency >= 80) return "bg-gradient-to-r from-[#00AB45]/90 to-[#00AB45]/95";
40097
- if (data.efficiency >= 70) return "bg-gradient-to-r from-[#FFB020]/90 to-[#FFB020]/95";
40098
- return "bg-gradient-to-r from-[#E34329]/90 to-[#E34329]/95";
40546
+ return getGradientEfficiencyClasses(efficiencyColor);
40099
40547
  }
40100
- return isInactive ? "bg-gray-300/90" : getEfficiencyColor(data.efficiency);
40101
- }, [data.efficiency, isInactive, position.size, position.orientation]);
40548
+ return isInactive ? "bg-gray-300/90" : getEfficiencyColorClasses(data.efficiency, effectiveLegend);
40549
+ }, [data.efficiency, effectiveLegend, isInactive, position.size, position.orientation]);
40102
40550
  const { arrow, color: arrowColor } = React24.useMemo(() => getTrendArrowAndColor2(data.trend_score), [data.trend_score]);
40103
40551
  const workspaceNumber = React24.useMemo(() => getWorkspaceNumber(data.workspace_name), [data.workspace_name]);
40104
40552
  const styles2 = React24.useMemo(() => getWorkspaceStyles(position, isInactive), [position, isInactive]);
@@ -40180,6 +40628,8 @@ var WorkspaceGrid = React24__namespace.default.memo(({
40180
40628
  factoryView = "factory",
40181
40629
  line2Uuid = "line-2",
40182
40630
  className = "",
40631
+ hasFlowBuffers = false,
40632
+ legend = DEFAULT_EFFICIENCY_LEGEND,
40183
40633
  videoSources = {},
40184
40634
  displayNames = {},
40185
40635
  onWorkspaceHover,
@@ -40216,7 +40666,7 @@ var WorkspaceGrid = React24__namespace.default.memo(({
40216
40666
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative w-full h-full overflow-hidden ${className}`, children: [
40217
40667
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-0 left-2 sm:left-4 right-2 sm:right-8 z-20", children: [
40218
40668
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-row items-center justify-between py-1 sm:py-1.5 gap-2", children: [
40219
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxRuntime.jsx(Legend6, {}) }),
40669
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxRuntime.jsx(Legend6, { legend, useBottleneckLabel: hasFlowBuffers }) }),
40220
40670
  mapViewEnabled && /* @__PURE__ */ jsxRuntime.jsx(
40221
40671
  "button",
40222
40672
  {
@@ -40233,7 +40683,7 @@ var WorkspaceGrid = React24__namespace.default.memo(({
40233
40683
  }
40234
40684
  )
40235
40685
  ] }),
40236
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden mt-1 mr-32", children: /* @__PURE__ */ jsxRuntime.jsx(Legend6, {}) })
40686
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden mt-1 mr-32", children: /* @__PURE__ */ jsxRuntime.jsx(Legend6, { legend, useBottleneckLabel: hasFlowBuffers }) })
40237
40687
  ] }),
40238
40688
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-14 sm:top-16 left-0 right-0 bottom-0", children: /* @__PURE__ */ jsxRuntime.jsx(AnimatePresence, { mode: "wait", children: viewMode === "video" ? /* @__PURE__ */ jsxRuntime.jsx(
40239
40689
  motion.div,
@@ -40249,6 +40699,7 @@ var WorkspaceGrid = React24__namespace.default.memo(({
40249
40699
  workspaces,
40250
40700
  videoSources,
40251
40701
  displayNames,
40702
+ legend,
40252
40703
  onWorkspaceHover,
40253
40704
  onWorkspaceHoverEnd
40254
40705
  }
@@ -40268,6 +40719,7 @@ var WorkspaceGrid = React24__namespace.default.memo(({
40268
40719
  {
40269
40720
  workspaces,
40270
40721
  displayNames,
40722
+ legend,
40271
40723
  onWorkspaceHover,
40272
40724
  onWorkspaceHoverEnd
40273
40725
  }
@@ -47748,7 +48200,7 @@ var AIAgentView = () => {
47748
48200
  barRadius: [4, 4, 0, 0]
47749
48201
  // Top corners rounded
47750
48202
  };
47751
- const CustomTooltip3 = ({ active, payload, label }) => {
48203
+ const CustomTooltip4 = ({ active, payload, label }) => {
47752
48204
  if (active && payload && payload.length) {
47753
48205
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white px-4 py-3 shadow-lg rounded-lg border border-gray-200", children: [
47754
48206
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-900 mb-1", children: label }),
@@ -47848,7 +48300,7 @@ var AIAgentView = () => {
47848
48300
  tickFormatter: (value) => formatNumber(value)
47849
48301
  }
47850
48302
  ),
47851
- /* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip3, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
48303
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip4, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
47852
48304
  /* @__PURE__ */ jsxRuntime.jsx(
47853
48305
  recharts.Bar,
47854
48306
  {
@@ -47892,7 +48344,7 @@ var AIAgentView = () => {
47892
48344
  tickFormatter: (value) => formatNumber(value)
47893
48345
  }
47894
48346
  ),
47895
- /* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip3, {}), cursor: { strokeDasharray: "3 3" } }),
48347
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip4, {}), cursor: { strokeDasharray: "3 3" } }),
47896
48348
  /* @__PURE__ */ jsxRuntime.jsx(
47897
48349
  recharts.Line,
47898
48350
  {
@@ -48012,7 +48464,7 @@ var AIAgentView = () => {
48012
48464
  tickFormatter: (value) => formatNumber(value)
48013
48465
  }
48014
48466
  ),
48015
- /* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip3, {}), cursor: { strokeDasharray: "3 3" } }),
48467
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip4, {}), cursor: { strokeDasharray: "3 3" } }),
48016
48468
  /* @__PURE__ */ jsxRuntime.jsx(
48017
48469
  recharts.Legend,
48018
48470
  {
@@ -48072,7 +48524,7 @@ var AIAgentView = () => {
48072
48524
  tickFormatter: (value) => formatNumber(value)
48073
48525
  }
48074
48526
  ),
48075
- /* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip3, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
48527
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip4, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
48076
48528
  /* @__PURE__ */ jsxRuntime.jsx(
48077
48529
  recharts.Legend,
48078
48530
  {
@@ -48236,7 +48688,7 @@ var AIAgentView = () => {
48236
48688
  recharts.Tooltip,
48237
48689
  {
48238
48690
  cursor: { strokeDasharray: "3 3" },
48239
- content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip3, {})
48691
+ content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip4, {})
48240
48692
  }
48241
48693
  ),
48242
48694
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -48306,7 +48758,7 @@ var AIAgentView = () => {
48306
48758
  tickFormatter: (value) => formatNumber(value)
48307
48759
  }
48308
48760
  ),
48309
- /* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip3, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
48761
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip4, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
48310
48762
  /* @__PURE__ */ jsxRuntime.jsx(
48311
48763
  recharts.Legend,
48312
48764
  {
@@ -48380,7 +48832,7 @@ var AIAgentView = () => {
48380
48832
  tickFormatter: (value) => formatNumber(value)
48381
48833
  }
48382
48834
  ),
48383
- /* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip3, {}), cursor: { strokeDasharray: "3 3" } }),
48835
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomTooltip4, {}), cursor: { strokeDasharray: "3 3" } }),
48384
48836
  /* @__PURE__ */ jsxRuntime.jsx(
48385
48837
  recharts.Legend,
48386
48838
  {
@@ -49639,6 +50091,8 @@ function HomeView({
49639
50091
  const {
49640
50092
  workspaceMetrics,
49641
50093
  lineMetrics,
50094
+ efficiencyLegend,
50095
+ metadata: metricsMetadata,
49642
50096
  isLoading: metricsLoading,
49643
50097
  error: metricsError,
49644
50098
  refetch: refetchMetrics
@@ -49647,6 +50101,7 @@ function HomeView({
49647
50101
  userAccessibleLineIds: allLineIds
49648
50102
  // Pass user's accessible lines for supervisor filtering
49649
50103
  });
50104
+ const hasFlowBuffers = Boolean(metricsMetadata?.hasFlowBuffers);
49650
50105
  const kpis = React24.useMemo(() => {
49651
50106
  const lineMetricsRows = lineMetrics || [];
49652
50107
  if (selectedLineId === factoryViewId) {
@@ -50105,8 +50560,10 @@ function HomeView({
50105
50560
  workspaces: memoizedWorkspaceMetrics,
50106
50561
  lineNames,
50107
50562
  factoryView: factoryViewId,
50563
+ legend: efficiencyLegend,
50108
50564
  videoSources,
50109
50565
  displayNames: workspaceDisplayNames,
50566
+ hasFlowBuffers,
50110
50567
  className: "h-full",
50111
50568
  onWorkspaceHover: handleWorkspaceHover,
50112
50569
  onWorkspaceHoverEnd: handleWorkspaceHoverEnd
@@ -50133,8 +50590,10 @@ function HomeView({
50133
50590
  // Show empty grid while loading
50134
50591
  lineNames,
50135
50592
  factoryView: factoryViewId,
50593
+ legend: efficiencyLegend,
50136
50594
  videoSources,
50137
50595
  displayNames: workspaceDisplayNames,
50596
+ hasFlowBuffers,
50138
50597
  className: "h-full",
50139
50598
  onWorkspaceHover: handleWorkspaceHover,
50140
50599
  onWorkspaceHoverEnd: handleWorkspaceHoverEnd
@@ -50755,6 +51214,8 @@ var KPIDetailView = ({
50755
51214
  avg_efficiency: metric.avg_efficiency || 0,
50756
51215
  underperforming_workspaces: metric.underperforming_workspaces || 0,
50757
51216
  total_workspaces: metric.total_workspaces || 0,
51217
+ output: metric.current_output || 0,
51218
+ idealOutput: metric.ideal_output || metric.line_threshold || 0,
50758
51219
  compliance_percentage: 95 + Math.random() * 5,
50759
51220
  // Mock data: random value between 95-100%
50760
51221
  hasData: true
@@ -53210,6 +53671,201 @@ var ACTION_NAMES = {
53210
53671
  QUALITY_CONTROL: "Quality Control"
53211
53672
  };
53212
53673
 
53674
+ // src/lib/services/efficiencyLegendService.ts
53675
+ var EfficiencyLegendService = class {
53676
+ // 5 minutes
53677
+ constructor(supabase) {
53678
+ this.cache = /* @__PURE__ */ new Map();
53679
+ this.cacheExpiry = /* @__PURE__ */ new Map();
53680
+ this.CACHE_DURATION = 5 * 60 * 1e3;
53681
+ this.supabase = supabase;
53682
+ }
53683
+ /**
53684
+ * Get efficiency legend for a company
53685
+ */
53686
+ async getEfficiencyLegend(companyId) {
53687
+ try {
53688
+ const cached = this.cache.get(companyId);
53689
+ const expiry = this.cacheExpiry.get(companyId);
53690
+ if (cached && expiry && Date.now() < expiry) {
53691
+ return cached;
53692
+ }
53693
+ const params = new URLSearchParams({ company_id: companyId });
53694
+ const response = await fetchBackendJson(
53695
+ this.supabase,
53696
+ `/api/efficiency-legend?${params.toString()}`
53697
+ );
53698
+ const normalized = this.normalizeLegend(response?.legend);
53699
+ this.cache.set(companyId, normalized);
53700
+ this.cacheExpiry.set(companyId, Date.now() + this.CACHE_DURATION);
53701
+ return normalized;
53702
+ } catch (error) {
53703
+ console.error("Error fetching efficiency legend:", error);
53704
+ return DEFAULT_EFFICIENCY_LEGEND;
53705
+ }
53706
+ }
53707
+ /**
53708
+ * Update or create efficiency legend for a company
53709
+ */
53710
+ async updateEfficiencyLegend(companyId, legend) {
53711
+ try {
53712
+ const normalizedLegend = this.normalizeLegend(legend);
53713
+ const validation = this.validateLegend(normalizedLegend);
53714
+ if (!validation.valid) {
53715
+ return { success: false, error: validation.error };
53716
+ }
53717
+ const params = new URLSearchParams({ company_id: companyId });
53718
+ await fetchBackendJson(
53719
+ this.supabase,
53720
+ `/api/efficiency-legend?${params.toString()}`,
53721
+ {
53722
+ method: "PUT",
53723
+ body: JSON.stringify(normalizedLegend)
53724
+ }
53725
+ );
53726
+ this.cache.delete(companyId);
53727
+ this.cacheExpiry.delete(companyId);
53728
+ return { success: true };
53729
+ } catch (error) {
53730
+ console.error("Error updating efficiency legend:", error);
53731
+ return { success: false, error: error.message || "Failed to update efficiency legend" };
53732
+ }
53733
+ }
53734
+ /**
53735
+ * Clear cache for a specific company
53736
+ */
53737
+ clearCache(companyId) {
53738
+ this.cache.delete(companyId);
53739
+ this.cacheExpiry.delete(companyId);
53740
+ }
53741
+ /**
53742
+ * Clear all cache
53743
+ */
53744
+ clearAllCache() {
53745
+ this.cache.clear();
53746
+ this.cacheExpiry.clear();
53747
+ }
53748
+ /**
53749
+ * Validate legend configuration
53750
+ */
53751
+ validateLegend(legend) {
53752
+ if (legend.green_min < 0 || legend.green_min > 100) {
53753
+ return { valid: false, error: "Green minimum must be between 0 and 100" };
53754
+ }
53755
+ if (legend.green_max < 0 || legend.green_max > 100) {
53756
+ return { valid: false, error: "Green maximum must be between 0 and 100" };
53757
+ }
53758
+ if (legend.yellow_min < 0 || legend.yellow_min > 100) {
53759
+ return { valid: false, error: "Yellow minimum must be between 0 and 100" };
53760
+ }
53761
+ if (legend.yellow_max < 0 || legend.yellow_max > 100) {
53762
+ return { valid: false, error: "Yellow maximum must be between 0 and 100" };
53763
+ }
53764
+ if (legend.red_min < 0 || legend.red_min > 100) {
53765
+ return { valid: false, error: "Red minimum must be between 0 and 100" };
53766
+ }
53767
+ if (legend.red_max < 0 || legend.red_max > 100) {
53768
+ return { valid: false, error: "Red maximum must be between 0 and 100" };
53769
+ }
53770
+ if (legend.critical_threshold < 0 || legend.critical_threshold > 100) {
53771
+ return { valid: false, error: "Critical threshold must be between 0 and 100" };
53772
+ }
53773
+ if (legend.yellow_min >= legend.green_min) {
53774
+ return { valid: false, error: "Yellow minimum must be less than green minimum" };
53775
+ }
53776
+ if (legend.red_max >= legend.yellow_min) {
53777
+ return { valid: false, error: "Red maximum must be less than yellow minimum" };
53778
+ }
53779
+ if (legend.critical_threshold > legend.red_max) {
53780
+ return { valid: false, error: "Critical threshold must be less than or equal to red maximum" };
53781
+ }
53782
+ return { valid: true };
53783
+ }
53784
+ normalizeLegend(legend) {
53785
+ const fallback = DEFAULT_EFFICIENCY_LEGEND;
53786
+ if (!legend) return fallback;
53787
+ const coerce = (value, fallbackValue) => {
53788
+ const num = Number(value);
53789
+ return Number.isFinite(num) ? num : fallbackValue;
53790
+ };
53791
+ return {
53792
+ green_min: coerce(legend.green_min, fallback.green_min),
53793
+ green_max: coerce(legend.green_max, fallback.green_max),
53794
+ yellow_min: coerce(legend.yellow_min, fallback.yellow_min),
53795
+ yellow_max: coerce(legend.yellow_max, fallback.yellow_max),
53796
+ red_min: coerce(legend.red_min, fallback.red_min),
53797
+ red_max: coerce(legend.red_max, fallback.red_max),
53798
+ critical_threshold: coerce(legend.critical_threshold, fallback.critical_threshold)
53799
+ };
53800
+ }
53801
+ };
53802
+ var efficiencyLegendServiceInstance = null;
53803
+ function getEfficiencyLegendService(supabase) {
53804
+ if (!efficiencyLegendServiceInstance) {
53805
+ efficiencyLegendServiceInstance = new EfficiencyLegendService(supabase);
53806
+ }
53807
+ return efficiencyLegendServiceInstance;
53808
+ }
53809
+
53810
+ // src/lib/hooks/useEfficiencyLegend.ts
53811
+ function useEfficiencyLegend(companyId) {
53812
+ const supabase = useSupabase();
53813
+ const config = useDashboardConfig();
53814
+ const [legend, setLegend] = React24.useState(DEFAULT_EFFICIENCY_LEGEND);
53815
+ const [isLoading, setIsLoading] = React24.useState(true);
53816
+ const [error, setError] = React24.useState(null);
53817
+ const effectiveCompanyId = companyId || config.entityConfig?.companyId;
53818
+ const fetchLegend = React24.useCallback(async () => {
53819
+ if (!supabase || !effectiveCompanyId) {
53820
+ setIsLoading(false);
53821
+ return;
53822
+ }
53823
+ try {
53824
+ setIsLoading(true);
53825
+ setError(null);
53826
+ const service = getEfficiencyLegendService(supabase);
53827
+ const fetchedLegend = await service.getEfficiencyLegend(effectiveCompanyId);
53828
+ setLegend(fetchedLegend);
53829
+ } catch (err) {
53830
+ console.error("Error fetching efficiency legend:", err);
53831
+ setError(err.message || "Failed to fetch efficiency legend");
53832
+ setLegend(DEFAULT_EFFICIENCY_LEGEND);
53833
+ } finally {
53834
+ setIsLoading(false);
53835
+ }
53836
+ }, [supabase, effectiveCompanyId]);
53837
+ React24.useEffect(() => {
53838
+ fetchLegend();
53839
+ }, [fetchLegend]);
53840
+ const updateLegend = React24.useCallback(async (newLegend) => {
53841
+ if (!supabase || !effectiveCompanyId) {
53842
+ return { success: false, error: "Supabase client or company ID not available" };
53843
+ }
53844
+ try {
53845
+ const service = getEfficiencyLegendService(supabase);
53846
+ const result = await service.updateEfficiencyLegend(effectiveCompanyId, newLegend);
53847
+ if (result.success) {
53848
+ setLegend(newLegend);
53849
+ setError(null);
53850
+ } else {
53851
+ setError(result.error || "Failed to update efficiency legend");
53852
+ }
53853
+ return result;
53854
+ } catch (err) {
53855
+ const errorMsg = err.message || "Failed to update efficiency legend";
53856
+ setError(errorMsg);
53857
+ return { success: false, error: errorMsg };
53858
+ }
53859
+ }, [supabase, effectiveCompanyId]);
53860
+ return {
53861
+ legend,
53862
+ isLoading,
53863
+ error,
53864
+ updateLegend,
53865
+ refetch: fetchLegend
53866
+ };
53867
+ }
53868
+
53213
53869
  // src/views/TargetsView.utils.ts
53214
53870
  var calculatePPH = (cycleTime, breaks = [], shiftHours = 0) => {
53215
53871
  if (cycleTime === "" || cycleTime === 0) return "";
@@ -53236,369 +53892,245 @@ var getStoredLineState2 = (lineId) => {
53236
53892
  return false;
53237
53893
  }
53238
53894
  };
53239
- var BulkConfigureModal = ({
53895
+ var ConfigureLegendModal = ({
53240
53896
  isOpen,
53241
53897
  onClose,
53242
- lineWorkspaces,
53243
- lineNames,
53244
53898
  onSave,
53245
- selectedShift
53899
+ legend,
53900
+ canSave = true
53246
53901
  }) => {
53247
- const [selectedLine, setSelectedLine] = React24.useState("");
53248
- const [selectedWorkspaces, setSelectedWorkspaces] = React24.useState([]);
53249
- const [actionType, setActionType] = React24.useState("assembly");
53250
- const [targetPPH, setTargetPPH] = React24.useState("");
53251
- const [targetCycleTime, setTargetCycleTime] = React24.useState("");
53252
- const [targetDayOutput, setTargetDayOutput] = React24.useState("");
53253
- const [productId, setProductId] = React24.useState("");
53254
- const [selectedCount, setSelectedCount] = React24.useState(0);
53255
- const shiftHours = React24.useMemo(() => {
53256
- if (selectedLine && lineWorkspaces[selectedLine]) {
53257
- return lineWorkspaces[selectedLine].shiftHours;
53258
- }
53259
- return 8;
53260
- }, [selectedLine, lineWorkspaces]);
53261
- const selectedLineBreaks = React24.useMemo(() => {
53262
- if (selectedLine && lineWorkspaces[selectedLine]) {
53263
- return lineWorkspaces[selectedLine].breaks;
53264
- }
53265
- return [];
53266
- }, [selectedLine, lineWorkspaces]);
53267
- React24.useEffect(() => {
53268
- if (!isOpen) {
53269
- setTimeout(() => {
53270
- setSelectedLine("");
53271
- setSelectedWorkspaces([]);
53272
- setActionType("assembly");
53273
- setTargetPPH("");
53274
- setTargetCycleTime("");
53275
- setTargetDayOutput("");
53276
- setProductId("");
53277
- }, 200);
53278
- }
53279
- }, [isOpen]);
53902
+ const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
53903
+ const [redThreshold, setRedThreshold] = React24.useState(effectiveLegend.yellow_min);
53904
+ const [greenThreshold, setGreenThreshold] = React24.useState(effectiveLegend.green_min);
53905
+ const [isSaving, setIsSaving] = React24.useState(false);
53906
+ const [saveSuccess, setSaveSuccess] = React24.useState(false);
53280
53907
  React24.useEffect(() => {
53281
- if (selectedLine && lineWorkspaces[selectedLine]) {
53282
- setProductId(lineWorkspaces[selectedLine].productId);
53908
+ if (isOpen) {
53909
+ setRedThreshold(effectiveLegend.yellow_min);
53910
+ setGreenThreshold(effectiveLegend.green_min);
53911
+ setSaveSuccess(false);
53283
53912
  }
53284
- }, [selectedLine, lineWorkspaces]);
53285
- React24.useEffect(() => {
53286
- setSelectedCount(selectedWorkspaces.length);
53287
- }, [selectedWorkspaces]);
53913
+ }, [isOpen, effectiveLegend]);
53288
53914
  const handleSave = async () => {
53289
- if (selectedWorkspaces.length === 0) {
53290
- sonner.toast.error("Please select at least one workspace");
53915
+ if (!canSave) {
53916
+ sonner.toast.error("You do not have permission to update the legend.");
53291
53917
  return;
53292
53918
  }
53293
- if (targetPPH === "" && targetCycleTime === "" && targetDayOutput === "") {
53294
- sonner.toast.error("Please enter at least one target value");
53919
+ if (redThreshold === "" || greenThreshold === "") {
53920
+ sonner.toast.error("Please enter valid threshold values");
53295
53921
  return;
53296
53922
  }
53297
- if (!selectedLine) {
53298
- sonner.toast.error("Please select a line");
53923
+ if (Number(redThreshold) >= Number(greenThreshold)) {
53924
+ sonner.toast.error("Red threshold must be lower than Green threshold");
53299
53925
  return;
53300
53926
  }
53301
- const updates = {
53302
- ...actionType && { actionType },
53303
- ...targetPPH !== "" && { targetPPH },
53304
- ...targetCycleTime !== "" && { targetCycleTime },
53305
- ...targetDayOutput !== "" && { targetDayOutput }
53306
- };
53307
- if (targetCycleTime !== "" && targetPPH === "") {
53308
- const lineBreaks = lineWorkspaces[selectedLine].breaks || [];
53309
- const lineShiftHours = lineWorkspaces[selectedLine].shiftHours;
53310
- const calculatedPPH = calculatePPH(targetCycleTime, lineBreaks, lineShiftHours);
53311
- if (calculatedPPH !== "") {
53312
- updates.targetPPH = calculatedPPH;
53313
- }
53314
- }
53315
- if (updates.targetPPH !== void 0 && targetDayOutput === "") {
53316
- const lineBreaks = lineWorkspaces[selectedLine].breaks || [];
53317
- const lineShiftHours = lineWorkspaces[selectedLine].shiftHours;
53318
- const calculatedOutput = calculateDayOutput(updates.targetPPH, lineShiftHours, lineBreaks);
53319
- if (calculatedOutput !== "") {
53320
- updates.targetDayOutput = calculatedOutput;
53321
- }
53322
- }
53323
- onSave({
53324
- lineId: selectedLine,
53325
- workspaceIds: selectedWorkspaces,
53326
- productId,
53327
- updates
53328
- });
53329
- onClose();
53330
- };
53331
- const toggleAllWorkspaces = (lineId) => {
53332
- if (!lineWorkspaces[lineId]) return;
53333
- const allWorkspaceIds = lineWorkspaces[lineId].workspaces.map((w) => w.id);
53334
- if (selectedWorkspaces.length === allWorkspaceIds.length) {
53335
- setSelectedWorkspaces([]);
53336
- } else {
53337
- setSelectedWorkspaces(allWorkspaceIds);
53927
+ if (Number(redThreshold) < 1 || Number(redThreshold) > 100 || Number(greenThreshold) > 100) {
53928
+ sonner.toast.error("Thresholds must be between 1 and 100");
53929
+ return;
53338
53930
  }
53339
- };
53340
- React24.useEffect(() => {
53341
- const handleEscape = (e) => {
53342
- if (e.key === "Escape") {
53343
- onClose();
53931
+ const redMax = Number(redThreshold) - 1;
53932
+ const yellowMax = Number(greenThreshold) - 1;
53933
+ if (redMax < DEFAULT_EFFICIENCY_LEGEND.critical_threshold) {
53934
+ sonner.toast.error("Red threshold must be above 50% while critical alerts are fixed at <50%.");
53935
+ return;
53936
+ }
53937
+ setIsSaving(true);
53938
+ setSaveSuccess(false);
53939
+ try {
53940
+ const result = await onSave({
53941
+ green_min: Number(greenThreshold),
53942
+ green_max: 100,
53943
+ yellow_min: Number(redThreshold),
53944
+ yellow_max: yellowMax,
53945
+ red_min: 0,
53946
+ red_max: redMax,
53947
+ critical_threshold: DEFAULT_EFFICIENCY_LEGEND.critical_threshold
53948
+ });
53949
+ if (result.success) {
53950
+ setSaveSuccess(true);
53951
+ setTimeout(() => setSaveSuccess(false), 3e3);
53952
+ } else {
53953
+ sonner.toast.error(result.error || "Failed to update legend");
53344
53954
  }
53345
- };
53346
- if (isOpen) {
53347
- document.addEventListener("keydown", handleEscape);
53955
+ } catch (error) {
53956
+ console.error("Error saving legend:", error);
53957
+ sonner.toast.error("Failed to update legend");
53958
+ } finally {
53959
+ setIsSaving(false);
53348
53960
  }
53349
- return () => {
53350
- document.removeEventListener("keydown", handleEscape);
53351
- };
53352
- }, [isOpen, onClose]);
53961
+ };
53353
53962
  if (!isOpen) return null;
53354
53963
  return /* @__PURE__ */ jsxRuntime.jsx(
53355
53964
  "div",
53356
53965
  {
53357
- className: "fixed inset-0 bg-gray-900/50 backdrop-blur-sm z-50 flex items-center justify-center",
53966
+ className: "fixed inset-0 bg-gray-900/40 backdrop-blur-sm z-50 flex items-center justify-center transition-opacity duration-300",
53358
53967
  onClick: (e) => {
53359
53968
  if (e.target === e.currentTarget) {
53360
53969
  onClose();
53361
53970
  }
53362
53971
  },
53363
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-xl w-full max-w-3xl max-h-[90vh] flex flex-col animate-modal-in", children: [
53364
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4 border-b border-gray-200 flex items-center justify-between", children: [
53972
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-xl w-full max-w-xl flex flex-col animate-modal-in transform transition-all", children: [
53973
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-5 border-b border-gray-100 flex items-center justify-between bg-white rounded-t-lg", children: [
53365
53974
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
53366
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl font-semibold text-gray-900", children: "Bulk Configure Targets" }),
53367
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Update multiple workspace targets at once" })
53975
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-base font-semibold text-gray-900", children: "Performance Thresholds" }),
53976
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 mt-0.5", children: "Define acceptance criteria for production metrics." })
53368
53977
  ] }),
53369
53978
  /* @__PURE__ */ jsxRuntime.jsx(
53370
53979
  "button",
53371
53980
  {
53372
53981
  onClick: onClose,
53373
- className: "text-gray-400 hover:text-gray-500 transition-colors p-1 hover:bg-gray-100 rounded-lg",
53374
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-5 h-5" })
53982
+ className: "text-gray-400 hover:text-gray-600 transition-colors p-1.5 hover:bg-gray-50 rounded-md",
53983
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4" })
53375
53984
  }
53376
53985
  )
53377
53986
  ] }),
53378
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 space-y-6 overflow-y-scroll flex-1 custom-scrollbar relative", children: [
53379
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
53380
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
53381
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
53382
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700", children: "Select Line" }),
53383
- /* @__PURE__ */ jsxRuntime.jsxs(
53384
- "select",
53385
- {
53386
- value: selectedLine,
53387
- onChange: (e) => {
53388
- setSelectedLine(e.target.value);
53389
- setSelectedWorkspaces([]);
53390
- },
53391
- 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-blue-400\n appearance-none bg-no-repeat bg-right pr-10\n hover:bg-blue-50/50",
53392
- children: [
53393
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Select a line" }),
53394
- Object.entries(lineWorkspaces).map(([lineId, line]) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: lineId, className: "py-2", children: lineNames[lineId] || lineId }, lineId))
53395
- ]
53396
- }
53397
- )
53398
- ] }),
53399
- selectedLine && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
53400
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700", children: "Product Code" }),
53401
- /* @__PURE__ */ jsxRuntime.jsx(
53402
- "input",
53403
- {
53404
- type: "text",
53405
- value: productId,
53406
- onChange: (e) => setProductId(e.target.value),
53407
- placeholder: "e.g., PROD-001",
53408
- 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"
53409
- }
53410
- )
53411
- ] })
53412
- ] }),
53413
- selectedLine && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
53414
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
53415
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
53416
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
53417
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700", children: "Select Workspaces" }),
53418
- selectedCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800", children: [
53419
- selectedCount,
53420
- " selected"
53421
- ] })
53422
- ] }),
53423
- /* @__PURE__ */ jsxRuntime.jsx(
53424
- "button",
53425
- {
53426
- onClick: () => toggleAllWorkspaces(selectedLine),
53427
- className: "text-sm text-blue-600 hover:text-blue-700 font-medium px-2 py-1 rounded hover:bg-blue-50 transition-colors",
53428
- children: selectedWorkspaces.length === lineWorkspaces[selectedLine].workspaces.length ? "Deselect All" : "Select All"
53429
- }
53430
- )
53431
- ] }),
53432
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-4 gap-2 max-h-48 overflow-y-auto p-2 border border-gray-200 rounded-lg bg-gray-50", children: lineWorkspaces[selectedLine].workspaces.map((workspace) => /* @__PURE__ */ jsxRuntime.jsxs(
53433
- "label",
53434
- {
53435
- className: `flex items-center space-x-2 p-2 rounded cursor-pointer transition-all duration-200 ${selectedWorkspaces.includes(workspace.id) ? "bg-blue-50 border border-blue-200" : "bg-white border border-gray-200 hover:border-gray-300"}`,
53436
- children: [
53437
- /* @__PURE__ */ jsxRuntime.jsx(
53438
- "input",
53439
- {
53440
- type: "checkbox",
53441
- checked: selectedWorkspaces.includes(workspace.id),
53442
- onChange: (e) => {
53443
- if (e.target.checked) {
53444
- setSelectedWorkspaces((prev) => [...prev, workspace.id]);
53445
- } else {
53446
- setSelectedWorkspaces((prev) => prev.filter((id3) => id3 !== workspace.id));
53447
- }
53448
- },
53449
- className: "rounded border-gray-300 text-blue-600 focus:ring-blue-500"
53450
- }
53451
- ),
53452
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-sm ${selectedWorkspaces.includes(workspace.id) ? "text-blue-900 font-medium" : "text-gray-900"}`, children: formatWorkspaceName(workspace.name, selectedLine) })
53453
- ]
53454
- },
53455
- workspace.id
53456
- )) })
53457
- ] }),
53458
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
53459
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700", children: "Action Type" }),
53460
- /* @__PURE__ */ jsxRuntime.jsxs(
53461
- "select",
53462
- {
53463
- value: actionType,
53464
- onChange: (e) => setActionType(e.target.value),
53465
- 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-blue-400\n appearance-none bg-no-repeat bg-right pr-10\n hover:bg-blue-50/50",
53466
- children: [
53467
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "assembly", className: "py-2", children: "Assembly" }),
53468
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "packaging", className: "py-2", children: "Packaging" })
53469
- ]
53470
- }
53471
- )
53472
- ] })
53987
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-8 space-y-8", children: [
53988
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 pt-6", children: [
53989
+ " ",
53990
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-6 w-full rounded-md bg-gray-100 relative overflow-visible ring-1 ring-black/5 flex mt-2", children: [
53991
+ /* @__PURE__ */ jsxRuntime.jsx(
53992
+ "div",
53993
+ {
53994
+ className: "h-full bg-red-500 transition-all duration-500 ease-in-out relative group first:rounded-l-md",
53995
+ style: { width: `${Math.min(Number(redThreshold) || 0, 100)}%` },
53996
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-white/0 group-hover:bg-white/10 transition-colors" })
53997
+ }
53998
+ ),
53999
+ /* @__PURE__ */ jsxRuntime.jsx(
54000
+ "div",
54001
+ {
54002
+ className: "h-full bg-yellow-400 transition-all duration-500 ease-in-out relative group",
54003
+ style: { width: `${Math.max(0, Math.min((Number(greenThreshold) || 0) - (Number(redThreshold) || 0), 100 - (Number(redThreshold) || 0)))}%` },
54004
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-white/0 group-hover:bg-white/10 transition-colors" })
54005
+ }
54006
+ ),
54007
+ /* @__PURE__ */ jsxRuntime.jsx(
54008
+ "div",
54009
+ {
54010
+ className: "h-full bg-green-500 transition-all duration-500 ease-in-out relative group last:rounded-r-md",
54011
+ style: { width: `${Math.max(0, 100 - (Number(greenThreshold) || 0))}%` },
54012
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-white/0 group-hover:bg-white/10 transition-colors" })
54013
+ }
54014
+ ),
54015
+ /* @__PURE__ */ jsxRuntime.jsx(
54016
+ "div",
54017
+ {
54018
+ className: "absolute -top-8 transition-all duration-500 ease-in-out z-20",
54019
+ style: { left: `${Math.min(Number(redThreshold) || 0, 100)}%`, transform: "translateX(-50%)" },
54020
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center", children: [
54021
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-bold text-gray-900 bg-white px-1.5 py-0.5 rounded shadow-sm border border-gray-200 mb-1 whitespace-nowrap", children: [
54022
+ redThreshold,
54023
+ "%"
54024
+ ] }),
54025
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-3 bg-gray-400/50" })
54026
+ ] })
54027
+ }
54028
+ ),
54029
+ /* @__PURE__ */ jsxRuntime.jsx(
54030
+ "div",
54031
+ {
54032
+ className: "absolute -top-8 transition-all duration-500 ease-in-out z-20",
54033
+ style: { left: `${Math.min(Number(greenThreshold) || 0, 100)}%`, transform: "translateX(-50%)" },
54034
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center", children: [
54035
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-bold text-gray-900 bg-white px-1.5 py-0.5 rounded shadow-sm border border-gray-200 mb-1 whitespace-nowrap", children: [
54036
+ greenThreshold,
54037
+ "%"
54038
+ ] }),
54039
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-3 bg-gray-400/50" })
54040
+ ] })
54041
+ }
54042
+ ),
54043
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-0 bottom-0 w-px bg-white/50 z-10 pointer-events-none", style: { left: `${Math.min(Number(redThreshold) || 0, 100)}%` } }),
54044
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-0 bottom-0 w-px bg-white/50 z-10 pointer-events-none", style: { left: `${Math.min(Number(greenThreshold) || 0, 100)}%` } })
53473
54045
  ] }),
53474
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-3 gap-4", children: [
53475
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
53476
- /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: [
53477
- "Target Cycle Time",
53478
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 text-xs text-gray-400", children: "seconds per piece" })
53479
- ] }),
53480
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
54046
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between text-xs font-medium text-gray-400 pt-1", children: [
54047
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "0%" }),
54048
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "100%+" })
54049
+ ] })
54050
+ ] }),
54051
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-8 relative", children: [
54052
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-gray-300", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRight, { className: "w-5 h-5" }) }),
54053
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
54054
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Critical Limit" }),
54055
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
54056
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 rounded-full bg-red-500 mr-2.5" }),
54057
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
53481
54058
  /* @__PURE__ */ jsxRuntime.jsx(
53482
54059
  "input",
53483
54060
  {
53484
54061
  type: "number",
53485
- value: targetCycleTime,
54062
+ value: redThreshold,
53486
54063
  onChange: (e) => {
53487
- const newValue = e.target.value ? Number(e.target.value) : "";
53488
- setTargetCycleTime(newValue);
53489
- if (newValue !== "" && newValue > 0) {
53490
- const pph = calculatePPH(newValue, selectedLineBreaks, shiftHours);
53491
- setTargetPPH(pph);
53492
- const dayOutput = calculateDayOutput(pph, shiftHours, selectedLineBreaks);
53493
- setTargetDayOutput(dayOutput);
53494
- } else if (newValue === "") {
53495
- setTargetPPH("");
53496
- setTargetDayOutput("");
53497
- }
54064
+ setRedThreshold(e.target.value === "" ? "" : Number(e.target.value));
54065
+ setSaveSuccess(false);
53498
54066
  },
53499
- 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",
53500
- min: "0",
53501
- step: "0.01",
53502
- placeholder: "Enter cycle time"
54067
+ min: 1,
54068
+ max: 100,
54069
+ className: "block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm py-2 px-3 bg-white no-spinner",
54070
+ placeholder: "70"
53503
54071
  }
53504
54072
  ),
53505
- targetCycleTime !== "" && /* @__PURE__ */ jsxRuntime.jsx(
53506
- "button",
53507
- {
53508
- onClick: () => {
53509
- setTargetCycleTime("");
53510
- setTargetPPH("");
53511
- setTargetDayOutput("");
53512
- },
53513
- className: "absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-500",
53514
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4" })
53515
- }
53516
- )
54073
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-500 sm:text-sm", children: "%" }) })
53517
54074
  ] })
53518
54075
  ] }),
53519
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
53520
- /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: [
53521
- "Target PPH",
53522
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 text-xs text-gray-400", children: "pieces per hour" })
54076
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-2 text-xs text-gray-500", children: [
54077
+ "Efficiency below ",
54078
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium text-gray-900", children: [
54079
+ redThreshold,
54080
+ "%"
53523
54081
  ] }),
53524
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
54082
+ " will show in ",
54083
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-red-600", children: "red" }),
54084
+ "."
54085
+ ] })
54086
+ ] }),
54087
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
54088
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Target Limit" }),
54089
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
54090
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 rounded-full bg-green-500 mr-2.5" }),
54091
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
53525
54092
  /* @__PURE__ */ jsxRuntime.jsx(
53526
54093
  "input",
53527
54094
  {
53528
54095
  type: "number",
53529
- value: targetPPH,
54096
+ value: greenThreshold,
53530
54097
  onChange: (e) => {
53531
- const newValue = e.target.value ? Number(e.target.value) : "";
53532
- setTargetPPH(newValue);
53533
- if (newValue !== "") {
53534
- const dayOutput = calculateDayOutput(newValue, shiftHours, selectedLineBreaks);
53535
- setTargetDayOutput(dayOutput);
53536
- }
54098
+ setGreenThreshold(e.target.value === "" ? "" : Number(e.target.value));
54099
+ setSaveSuccess(false);
53537
54100
  },
53538
- 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",
53539
- min: "0",
53540
- step: "0.1",
53541
- placeholder: "Enter PPH"
54101
+ min: 1,
54102
+ max: 100,
54103
+ className: "block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm py-2 px-3 bg-white no-spinner",
54104
+ placeholder: "80"
53542
54105
  }
53543
54106
  ),
53544
- targetPPH !== "" && /* @__PURE__ */ jsxRuntime.jsx(
53545
- "button",
53546
- {
53547
- onClick: () => {
53548
- setTargetPPH("");
53549
- setTargetDayOutput("");
53550
- },
53551
- className: "absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-500",
53552
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4" })
53553
- }
53554
- )
54107
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-500 sm:text-sm", children: "%" }) })
53555
54108
  ] })
53556
54109
  ] }),
53557
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
53558
- /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: [
53559
- "Total Day Output",
53560
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 text-xs text-gray-400", children: "pieces per day" })
54110
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-2 text-xs text-gray-500", children: [
54111
+ "Efficiency above ",
54112
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium text-gray-900", children: [
54113
+ greenThreshold,
54114
+ "%"
53561
54115
  ] }),
53562
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
53563
- /* @__PURE__ */ jsxRuntime.jsx(
53564
- "input",
53565
- {
53566
- type: "number",
53567
- value: targetDayOutput,
53568
- onChange: (e) => setTargetDayOutput(e.target.value ? Number(e.target.value) : ""),
53569
- 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",
53570
- min: "0",
53571
- step: "1",
53572
- placeholder: "Enter day output"
53573
- }
53574
- ),
53575
- targetDayOutput !== "" && /* @__PURE__ */ jsxRuntime.jsx(
53576
- "button",
53577
- {
53578
- onClick: () => setTargetDayOutput(""),
53579
- className: "absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-500",
53580
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4" })
53581
- }
53582
- )
53583
- ] })
54116
+ " will show in ",
54117
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-green-600", children: "green" }),
54118
+ "."
53584
54119
  ] })
53585
- ] }) })
53586
- ] }),
53587
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-0 left-0 right-0 h-10 bg-gradient-to-t from-white to-transparent pointer-events-none" })
54120
+ ] })
54121
+ ] })
53588
54122
  ] }),
53589
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4 bg-gray-50 border-t border-gray-200 flex justify-between items-center", children: [
53590
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-gray-500", children: selectedCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
53591
- "Updating ",
53592
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-gray-900", children: selectedCount }),
53593
- " workspace",
53594
- selectedCount !== 1 ? "s" : ""
54123
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4 bg-gray-50 border-t border-gray-100 flex items-center justify-between gap-3 rounded-b-lg", children: [
54124
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-[20px]", children: saveSuccess && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-green-600 font-medium flex items-center gap-1.5", children: [
54125
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle2, { className: "w-4 h-4" }),
54126
+ "Saved successfully"
53595
54127
  ] }) }),
53596
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex space-x-3", children: [
54128
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-3", children: [
53597
54129
  /* @__PURE__ */ jsxRuntime.jsx(
53598
54130
  "button",
53599
54131
  {
53600
54132
  onClick: onClose,
53601
- className: "px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 \n rounded-lg hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 \n focus:ring-blue-500 transition-all duration-200",
54133
+ className: "px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 shadow-sm transition-all",
53602
54134
  children: "Cancel"
53603
54135
  }
53604
54136
  ),
@@ -53606,10 +54138,9 @@ var BulkConfigureModal = ({
53606
54138
  "button",
53607
54139
  {
53608
54140
  onClick: handleSave,
53609
- disabled: !selectedLine || selectedWorkspaces.length === 0,
53610
- className: `px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200
53611
- ${!selectedLine || selectedWorkspaces.length === 0 ? "bg-gray-100 text-gray-400 cursor-not-allowed" : "text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"}`,
53612
- children: "Apply Changes"
54141
+ disabled: !canSave || isSaving,
54142
+ className: "px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 shadow-sm transition-all disabled:bg-gray-300 disabled:cursor-not-allowed",
54143
+ children: isSaving ? "Saving..." : "Save Changes"
53613
54144
  }
53614
54145
  )
53615
54146
  ] })
@@ -53618,7 +54149,7 @@ var BulkConfigureModal = ({
53618
54149
  }
53619
54150
  );
53620
54151
  };
53621
- var BulkConfigureModal_default = BulkConfigureModal;
54152
+ var ConfigureLegendModal_default = ConfigureLegendModal;
53622
54153
  var SKUModal = ({
53623
54154
  isOpen,
53624
54155
  onClose,
@@ -54019,7 +54550,8 @@ var TargetsViewUI = ({
54019
54550
  { id: 0, name: "Day Shift" },
54020
54551
  { id: 1, name: "Night Shift" }
54021
54552
  ],
54022
- isBulkConfigureOpen,
54553
+ isConfigureLegendOpen,
54554
+ legend,
54023
54555
  navItems = [],
54024
54556
  currentPathname = "/targets",
54025
54557
  canSaveTargets = true,
@@ -54030,8 +54562,8 @@ var TargetsViewUI = ({
54030
54562
  onActionTypeChange,
54031
54563
  onShiftChange,
54032
54564
  onSaveLine,
54033
- onToggleBulkConfigure,
54034
- onBulkConfigure,
54565
+ onToggleConfigureLegend,
54566
+ onSaveLegend,
54035
54567
  onUpdateWorkspaceDisplayName,
54036
54568
  // SKU props
54037
54569
  skuEnabled = false,
@@ -54057,11 +54589,13 @@ var TargetsViewUI = ({
54057
54589
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:absolute sm:right-0 w-full sm:w-auto", children: /* @__PURE__ */ jsxRuntime.jsxs(
54058
54590
  "button",
54059
54591
  {
54060
- className: "w-full sm:w-auto px-3 sm:px-4 py-1.5 sm:py-2 text-xs sm: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-blue-500 transition-all duration-200",
54061
- onClick: onToggleBulkConfigure,
54592
+ className: `w-full sm:w-auto px-3 sm:px-4 py-1.5 sm:py-2 text-xs sm:text-sm font-medium rounded-lg transition-all duration-200 ${canSaveTargets ? "text-gray-700 bg-white border border-gray-300 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" : "text-gray-400 bg-gray-100 border border-gray-200 cursor-not-allowed"}`,
54593
+ onClick: onToggleConfigureLegend,
54594
+ disabled: !canSaveTargets,
54595
+ title: !canSaveTargets ? "You do not have permission to update the legend" : "",
54062
54596
  children: [
54063
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings2, { className: "w-3 h-3 sm:w-4 sm:h-4 mr-1.5 sm:mr-2 inline-block" }),
54064
- "Bulk Configure"
54597
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Palette, { className: "w-3 h-3 sm:w-4 sm:h-4 mr-1.5 sm:mr-2 inline-block" }),
54598
+ "Configure Legend"
54065
54599
  ]
54066
54600
  }
54067
54601
  ) }),
@@ -54363,14 +54897,13 @@ var TargetsViewUI = ({
54363
54897
  )) })
54364
54898
  ] }) }),
54365
54899
  /* @__PURE__ */ jsxRuntime.jsx(
54366
- BulkConfigureModal_default,
54900
+ ConfigureLegendModal_default,
54367
54901
  {
54368
- isOpen: isBulkConfigureOpen,
54369
- onClose: onToggleBulkConfigure,
54370
- lineWorkspaces,
54371
- lineNames,
54372
- onSave: onBulkConfigure,
54373
- selectedShift
54902
+ isOpen: isConfigureLegendOpen,
54903
+ onClose: onToggleConfigureLegend,
54904
+ onSave: onSaveLegend,
54905
+ legend,
54906
+ canSave: canSaveTargets
54374
54907
  }
54375
54908
  )
54376
54909
  ] });
@@ -54416,7 +54949,7 @@ var TargetsView = ({
54416
54949
  () => lineIds.reduce((acc, id3) => ({ ...acc, [id3]: false }), {})
54417
54950
  );
54418
54951
  const [isLoading, setIsLoading] = React24.useState(true);
54419
- const [isBulkConfigureOpen, setIsBulkConfigureOpen] = React24.useState(false);
54952
+ const [isConfigureLegendOpen, setIsConfigureLegendOpen] = React24.useState(false);
54420
54953
  const [selectedWorkspaces, setSelectedWorkspaces] = React24.useState([]);
54421
54954
  const [selectedShift, setSelectedShift] = React24.useState(0);
54422
54955
  const [shiftOptions, setShiftOptions] = React24.useState([]);
@@ -54433,6 +54966,7 @@ var TargetsView = ({
54433
54966
  const supabase = useSupabase();
54434
54967
  const effectiveUserId = userId || "6bf6f271-1e55-4a95-9b89-1c3820b58739";
54435
54968
  const dashboardConfig = useDashboardConfig();
54969
+ const { legend, updateLegend } = useEfficiencyLegend(companyId);
54436
54970
  const { skus, isLoading: skusLoading } = useSKUs(companyId);
54437
54971
  const skuEnabled = dashboardConfig?.skuConfig?.enabled || false;
54438
54972
  React24.useEffect(() => {
@@ -54868,41 +55402,23 @@ var TargetsView = ({
54868
55402
  console.log(`[handleSaveLine] Set savingLines to false for ${lineId} in finally block`);
54869
55403
  }
54870
55404
  }, [canSaveTargets, supabase, lineWorkspaces, selectedShift, lineNames, onSaveChanges, skuEnabled, dashboardConfig]);
54871
- const handleBulkConfigure = async (updates) => {
54872
- if (!actionIds) return;
54873
- if (updates.productId !== void 0) {
54874
- setLineWorkspaces((prev) => ({
54875
- ...prev,
54876
- [updates.lineId]: {
54877
- ...prev[updates.lineId],
54878
- productId: updates.productId || ""
54879
- }
54880
- }));
55405
+ const handleSaveLegend = React24.useCallback(async (nextLegend) => {
55406
+ if (!canSaveTargets) {
55407
+ sonner.toast.error("You do not have permission to update the legend.");
55408
+ return { success: false, error: "Permission denied" };
55409
+ }
55410
+ const result = await updateLegend(nextLegend);
55411
+ if (result.success) {
55412
+ sonner.toast.success("Efficiency legend updated successfully");
55413
+ } else if (result.error) {
55414
+ sonner.toast.error(result.error);
55415
+ } else {
55416
+ sonner.toast.error("Failed to update efficiency legend");
54881
55417
  }
54882
- setLineWorkspaces((prev) => ({
54883
- ...prev,
54884
- [updates.lineId]: {
54885
- ...prev[updates.lineId],
54886
- workspaces: prev[updates.lineId].workspaces.map((ws) => {
54887
- if (!updates.workspaceIds.includes(ws.id)) return ws;
54888
- return {
54889
- ...ws,
54890
- ...updates.updates.actionType && {
54891
- actionType: updates.updates.actionType,
54892
- actionId: actionIds[updates.updates.actionType]
54893
- },
54894
- ...updates.updates.targetPPH !== void 0 && { targetPPH: updates.updates.targetPPH },
54895
- ...updates.updates.targetCycleTime !== void 0 && { targetCycleTime: updates.updates.targetCycleTime },
54896
- ...updates.updates.targetDayOutput !== void 0 && { targetDayOutput: updates.updates.targetDayOutput }
54897
- };
54898
- })
54899
- }
54900
- }));
54901
- sonner.toast.success(`Updated ${updates.workspaceIds.length} workspaces in ${lineNames[updates.lineId] || updates.lineId}`);
54902
- setIsBulkConfigureOpen(false);
54903
- };
54904
- const handleToggleBulkConfigure = () => {
54905
- setIsBulkConfigureOpen((prev) => !prev);
55418
+ return result;
55419
+ }, [canSaveTargets, updateLegend]);
55420
+ const handleToggleConfigureLegend = () => {
55421
+ setIsConfigureLegendOpen((prev) => !prev);
54906
55422
  };
54907
55423
  const handleNavigateBack = () => {
54908
55424
  if (router && router.push) {
@@ -54940,7 +55456,7 @@ var TargetsView = ({
54940
55456
  saveSuccess,
54941
55457
  selectedShift,
54942
55458
  shiftOptions,
54943
- isBulkConfigureOpen,
55459
+ isConfigureLegendOpen,
54944
55460
  navItems: [],
54945
55461
  currentPathname: "/targets",
54946
55462
  canSaveTargets,
@@ -54951,8 +55467,9 @@ var TargetsView = ({
54951
55467
  onActionTypeChange: handleActionTypeChange,
54952
55468
  onShiftChange: handleShiftChange,
54953
55469
  onSaveLine: handleSaveLine,
54954
- onToggleBulkConfigure: handleToggleBulkConfigure,
54955
- onBulkConfigure: handleBulkConfigure,
55470
+ onToggleConfigureLegend: handleToggleConfigureLegend,
55471
+ onSaveLegend: handleSaveLegend,
55472
+ legend,
54956
55473
  onUpdateWorkspaceDisplayName: handleUpdateWorkspaceDisplayName,
54957
55474
  skuEnabled,
54958
55475
  skus,
@@ -55333,6 +55850,20 @@ var WorkspaceDetailView = ({
55333
55850
  }, [monthlyData, range]);
55334
55851
  const formattedWorkspaceName = displayName || formatWorkspaceName3(workspace?.workspace_name || "", effectiveLineId);
55335
55852
  const shouldShowCycleTimeChart = showCycleTimeChart ?? formattedWorkspaceName.startsWith("FINAL ASSY");
55853
+ const idleClipDate = date || workspace?.date || calculatedOperationalDate || getOperationalDate(timezone);
55854
+ const idleClipShiftId = parsedShiftId ?? workspace?.shift_id;
55855
+ const idleClipFetchEnabled = Boolean(
55856
+ workspaceId && idleClipDate && idleClipShiftId !== void 0 && activeTab === "overview" && showIdleTime && !shouldShowCycleTimeChart
55857
+ );
55858
+ const {
55859
+ idleClips: idleTimeClips,
55860
+ clipClassifications: idleTimeClipClassifications
55861
+ } = useIdleTimeClipClassifications({
55862
+ workspaceId,
55863
+ date: idleClipDate,
55864
+ shiftId: idleClipShiftId,
55865
+ enabled: idleClipFetchEnabled
55866
+ });
55336
55867
  const handleBackNavigation = () => {
55337
55868
  if (returnUrl) {
55338
55869
  if (onNavigate) {
@@ -55719,7 +56250,11 @@ var WorkspaceDetailView = ({
55719
56250
  shiftStart: workspace.shift_start || "06:00",
55720
56251
  shiftEnd: workspace.shift_end,
55721
56252
  showIdleTime,
55722
- idleTimeHourly: workspace.idle_time_hourly
56253
+ idleTimeHourly: workspace.idle_time_hourly,
56254
+ idleTimeClips,
56255
+ idleTimeClipClassifications,
56256
+ shiftDate: idleClipDate,
56257
+ timezone
55723
56258
  }
55724
56259
  )
55725
56260
  }
@@ -55845,7 +56380,11 @@ var WorkspaceDetailView = ({
55845
56380
  shiftStart: workspace.shift_start || "06:00",
55846
56381
  shiftEnd: workspace.shift_end,
55847
56382
  showIdleTime,
55848
- idleTimeHourly: workspace.idle_time_hourly
56383
+ idleTimeHourly: workspace.idle_time_hourly,
56384
+ idleTimeClips,
56385
+ idleTimeClipClassifications,
56386
+ shiftDate: idleClipDate,
56387
+ timezone
55849
56388
  }
55850
56389
  )
55851
56390
  }
@@ -58979,7 +59518,12 @@ var ImprovementCenterView = () => {
58979
59518
  const { user } = useAuth();
58980
59519
  const dashboardConfig = useDashboardConfig();
58981
59520
  const entityConfig = useEntityConfig();
58982
- const [currentDate, setCurrentDate] = React24.useState(/* @__PURE__ */ new Date());
59521
+ const timezone = useAppTimezone();
59522
+ const today = new Date((/* @__PURE__ */ new Date()).toLocaleString("en-US", { timeZone: timezone }));
59523
+ const [selectedMonth, setSelectedMonth] = React24.useState(today.getMonth());
59524
+ const [selectedYear, setSelectedYear] = React24.useState(today.getFullYear());
59525
+ const monthBounds = React24.useMemo(() => getMonthKeyBounds(selectedYear, selectedMonth), [selectedYear, selectedMonth]);
59526
+ const [dateRange, setDateRange] = React24.useState(monthBounds);
58983
59527
  const [selectedLineId, setSelectedLineId] = React24.useState("all");
58984
59528
  const [selectedStatus, setSelectedStatus] = React24.useState("all");
58985
59529
  const [selectedShift, setSelectedShift] = React24.useState("all");
@@ -58990,14 +59534,36 @@ var ImprovementCenterView = () => {
58990
59534
  const [loadError, setLoadError] = React24.useState(null);
58991
59535
  const [teamMembers, setTeamMembers] = React24.useState([]);
58992
59536
  const [companyLines, setCompanyLines] = React24.useState([]);
59537
+ const [isFilterOpen, setIsFilterOpen] = React24.useState(false);
59538
+ const filterRef = React24.useRef(null);
59539
+ React24.useEffect(() => {
59540
+ const handleClickOutside = (event) => {
59541
+ if (filterRef.current && !filterRef.current.contains(event.target)) {
59542
+ setIsFilterOpen(false);
59543
+ }
59544
+ };
59545
+ document.addEventListener("mousedown", handleClickOutside);
59546
+ return () => document.removeEventListener("mousedown", handleClickOutside);
59547
+ }, []);
58993
59548
  const computeWeeksOpen = (firstSeenAt) => {
58994
59549
  if (!firstSeenAt) return void 0;
58995
- const firstSeen = new Date(firstSeenAt);
59550
+ const firstSeen = new Date(new Date(firstSeenAt).toLocaleString("en-US", { timeZone: timezone }));
58996
59551
  if (Number.isNaN(firstSeen.getTime())) return void 0;
58997
- const now2 = /* @__PURE__ */ new Date();
59552
+ const now2 = new Date((/* @__PURE__ */ new Date()).toLocaleString("en-US", { timeZone: timezone }));
58998
59553
  const diffDays = Math.max(0, Math.floor((now2.getTime() - firstSeen.getTime()) / (1e3 * 60 * 60 * 24)));
58999
59554
  return Math.max(1, Math.ceil(diffDays / 7));
59000
59555
  };
59556
+ const formatOpenedDate = (firstSeenAt) => {
59557
+ if (!firstSeenAt) return void 0;
59558
+ const raw = new Date(firstSeenAt);
59559
+ if (Number.isNaN(raw.getTime())) return void 0;
59560
+ const zoned = new Date(raw.toLocaleString("en-US", { timeZone: timezone }));
59561
+ const day = zoned.getDate();
59562
+ const month = zoned.toLocaleString("en-US", { month: "short" });
59563
+ const dayMod = day % 100;
59564
+ const suffix = dayMod >= 11 && dayMod <= 13 ? "th" : day % 10 === 1 ? "st" : day % 10 === 2 ? "nd" : day % 10 === 3 ? "rd" : "th";
59565
+ return `${day}${suffix} ${month}`;
59566
+ };
59001
59567
  const configuredLines = React24.useMemo(() => {
59002
59568
  return entityConfig.lines || entityConfig.lineNames || {};
59003
59569
  }, [entityConfig.lines, entityConfig.lineNames]);
@@ -59106,14 +59672,38 @@ var ImprovementCenterView = () => {
59106
59672
  cancelled = true;
59107
59673
  };
59108
59674
  }, [supabase, companyId]);
59109
- const nextMonth = () => {
59110
- setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1));
59111
- };
59112
- const prevMonth = () => {
59113
- setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1));
59675
+ React24.useEffect(() => {
59676
+ setDateRange(monthBounds);
59677
+ }, [monthBounds]);
59678
+ const handleRangeChange = (newRange) => {
59679
+ setDateRange(newRange);
59680
+ trackCoreEvent("Improvement Center Date Range Changed", {
59681
+ start_date: newRange.startKey,
59682
+ end_date: newRange.endKey
59683
+ });
59114
59684
  };
59115
- const formatMonth = (date) => {
59116
- return date.toLocaleDateString("en-US", { month: "long", year: "numeric" });
59685
+ const handleMonthNavigate = (newMonth, newYear) => {
59686
+ let validMonth = newMonth;
59687
+ let validYear = newYear;
59688
+ if (validMonth < 0) {
59689
+ validMonth = 11;
59690
+ validYear -= 1;
59691
+ } else if (validMonth > 11) {
59692
+ validMonth = 0;
59693
+ validYear += 1;
59694
+ }
59695
+ if (validYear < 2023) return;
59696
+ if (validYear > today.getFullYear() || validYear === today.getFullYear() && validMonth > today.getMonth()) {
59697
+ return;
59698
+ }
59699
+ const nextBounds = getMonthKeyBounds(validYear, validMonth);
59700
+ setSelectedMonth(validMonth);
59701
+ setSelectedYear(validYear);
59702
+ setDateRange(nextBounds);
59703
+ trackCoreEvent("Improvement Center Month Changed", {
59704
+ direction: validMonth > selectedMonth || validYear > selectedYear ? "next" : "previous",
59705
+ new_month: `${validYear}-${String(validMonth + 1).padStart(2, "0")}`
59706
+ });
59117
59707
  };
59118
59708
  React24.useEffect(() => {
59119
59709
  let cancelled = false;
@@ -59131,7 +59721,9 @@ var ImprovementCenterView = () => {
59131
59721
  status: "all",
59132
59722
  limit: "500"
59133
59723
  });
59134
- params.set("month", `${currentDate.getFullYear()}-${String(currentDate.getMonth() + 1).padStart(2, "0")}`);
59724
+ params.set("month", `${selectedYear}-${String(selectedMonth + 1).padStart(2, "0")}`);
59725
+ params.set("start_date", dateRange.startKey);
59726
+ params.set("end_date", dateRange.endKey);
59135
59727
  if (scopeLineIds.length > 0) {
59136
59728
  params.set("line_ids", scopeLineIds.join(","));
59137
59729
  }
@@ -59162,7 +59754,7 @@ var ImprovementCenterView = () => {
59162
59754
  return () => {
59163
59755
  cancelled = true;
59164
59756
  };
59165
- }, [supabase, companyId, scopeLineIdsKey, currentDate]);
59757
+ }, [supabase, companyId, scopeLineIdsKey, selectedMonth, selectedYear, dateRange.startKey, dateRange.endKey]);
59166
59758
  const teamMembersById = React24.useMemo(() => {
59167
59759
  return new Map(teamMembers.map((member) => [member.id, member]));
59168
59760
  }, [teamMembers]);
@@ -59204,6 +59796,15 @@ var ImprovementCenterView = () => {
59204
59796
  };
59205
59797
  }, [recommendations, selectedLineId, selectedShift, selectedWeeksRange, selectedMemberId]);
59206
59798
  const clearFilters = () => {
59799
+ trackCoreEvent("Improvement Center Filters Cleared", {
59800
+ previous_filters: {
59801
+ line_id: selectedLineId,
59802
+ status: selectedStatus,
59803
+ shift: selectedShift,
59804
+ weeks_range: selectedWeeksRange,
59805
+ member_id: selectedMemberId
59806
+ }
59807
+ });
59207
59808
  setSelectedLineId("all");
59208
59809
  setSelectedStatus("all");
59209
59810
  setSelectedShift("all");
@@ -59237,8 +59838,60 @@ var ImprovementCenterView = () => {
59237
59838
  setSelectedLineId("all");
59238
59839
  }
59239
59840
  }, [scopeLineIds, selectedLineId]);
59841
+ React24.useEffect(() => {
59842
+ trackCoreEvent("Improvement Center Viewed", {
59843
+ company_id: companyId,
59844
+ user_role: user?.role_level,
59845
+ initial_month: `${selectedYear}-${String(selectedMonth + 1).padStart(2, "0")}`,
59846
+ total_scope_lines: scopeLineIds.length
59847
+ });
59848
+ }, []);
59849
+ const handleStatusFilterChange = (status) => {
59850
+ trackCoreEvent("Improvement Center Filter Applied", {
59851
+ filter_type: "status",
59852
+ filter_value: status,
59853
+ previous_value: selectedStatus
59854
+ });
59855
+ setSelectedStatus(status);
59856
+ };
59857
+ const handleShiftFilterChange = (shift) => {
59858
+ trackCoreEvent("Improvement Center Filter Applied", {
59859
+ filter_type: "shift",
59860
+ filter_value: shift,
59861
+ previous_value: selectedShift
59862
+ });
59863
+ setSelectedShift(shift);
59864
+ };
59865
+ const handleWeeksFilterChange = (weeksRange) => {
59866
+ trackCoreEvent("Improvement Center Filter Applied", {
59867
+ filter_type: "weeks_open",
59868
+ filter_value: weeksRange,
59869
+ previous_value: selectedWeeksRange
59870
+ });
59871
+ setSelectedWeeksRange(weeksRange);
59872
+ };
59873
+ const handleMemberFilterChange = (memberId) => {
59874
+ const memberName = memberId === "all" ? "all" : teamMembers.find((m) => m.id === memberId)?.name || memberId;
59875
+ trackCoreEvent("Improvement Center Filter Applied", {
59876
+ filter_type: "member",
59877
+ filter_value: memberName,
59878
+ member_id: memberId,
59879
+ previous_value: selectedMemberId
59880
+ });
59881
+ setSelectedMemberId(memberId);
59882
+ };
59883
+ const handleLineFilterChange = (lineId) => {
59884
+ const lineName = lineId === "all" ? "all" : lineNameById.get(lineId) || lineId;
59885
+ trackCoreEvent("Improvement Center Filter Applied", {
59886
+ filter_type: "line",
59887
+ filter_value: lineName,
59888
+ line_id: lineId,
59889
+ previous_value: selectedLineId
59890
+ });
59891
+ setSelectedLineId(lineId);
59892
+ };
59240
59893
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-screen bg-gray-50 flex flex-col", children: [
59241
- /* @__PURE__ */ jsxRuntime.jsx("header", { className: "sticky top-0 z-10 bg-white border-b border-gray-200 flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 sm:px-6 lg:px-8 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
59894
+ /* @__PURE__ */ jsxRuntime.jsx("header", { className: "sticky top-0 z-30 bg-white border-b border-gray-200 flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 sm:px-6 lg:px-8 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
59242
59895
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-4 min-w-[120px]", children: /* @__PURE__ */ jsxRuntime.jsx(
59243
59896
  BackButtonMinimal,
59244
59897
  {
@@ -59250,187 +59903,173 @@ var ImprovementCenterView = () => {
59250
59903
  /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl lg:text-3xl font-semibold text-gray-900 text-center", children: "Improvement Center" }),
59251
59904
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 mt-1 text-center px-4 hidden sm:block", children: "Track and resolve persistent issues across your production lines" })
59252
59905
  ] }),
59253
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-[120px]" })
59906
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-[180px] flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsx(
59907
+ MonthlyRangeFilter_default,
59908
+ {
59909
+ month: selectedMonth,
59910
+ year: selectedYear,
59911
+ timezone,
59912
+ value: dateRange,
59913
+ onChange: handleRangeChange,
59914
+ onMonthNavigate: handleMonthNavigate
59915
+ }
59916
+ ) })
59254
59917
  ] }) }) }),
59255
59918
  /* @__PURE__ */ jsxRuntime.jsxs("main", { className: "flex-1 p-4 sm:p-6 max-w-7xl mx-auto w-full", children: [
59256
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center mb-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 bg-white rounded-full px-4 py-2 border border-gray-200 shadow-sm", children: [
59257
- /* @__PURE__ */ jsxRuntime.jsx(
59258
- "button",
59259
- {
59260
- onClick: prevMonth,
59261
- className: "p-1 rounded-full hover:bg-gray-100 text-gray-500 transition-colors",
59262
- children: /* @__PURE__ */ jsxRuntime.jsx(outline.ChevronLeftIcon, { className: "w-5 h-5" })
59263
- }
59264
- ),
59265
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-semibold text-gray-900 min-w-[160px] text-center", children: formatMonth(currentDate) }),
59266
- /* @__PURE__ */ jsxRuntime.jsx(
59919
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "-mx-4 sm:-mx-6 px-4 sm:px-6 py-3 border-b border-gray-200 mb-6 flex flex-col lg:flex-row items-start lg:items-center justify-between gap-4 transition-all", children: [
59920
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex p-1 bg-gray-50 border border-gray-200 rounded-lg", children: [
59921
+ { id: "all", label: "All", count: stats.all },
59922
+ { id: "resolved", label: "Resolved", count: stats.resolved, icon: outline.CheckCircleIcon },
59923
+ { id: "unresolved", label: "Unresolved", count: stats.unresolved, icon: outline.XCircleIcon }
59924
+ ].map((tab) => /* @__PURE__ */ jsxRuntime.jsxs(
59267
59925
  "button",
59268
59926
  {
59269
- onClick: nextMonth,
59270
- className: "p-1 rounded-full hover:bg-gray-100 text-gray-500 transition-colors",
59271
- children: /* @__PURE__ */ jsxRuntime.jsx(outline.ChevronRightIcon, { className: "w-5 h-5" })
59272
- }
59273
- )
59274
- ] }) }),
59275
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col md:flex-row items-center justify-between gap-4 mb-6", children: [
59276
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
59277
- /* @__PURE__ */ jsxRuntime.jsxs(
59278
- "button",
59279
- {
59280
- onClick: () => setSelectedStatus("all"),
59281
- className: `px-4 py-2 rounded-lg text-sm font-medium transition-all cursor-pointer ${selectedStatus === "all" ? "bg-white text-gray-900 shadow-sm ring-1 ring-black/5" : "text-gray-500 hover:text-gray-700 hover:bg-gray-100"}`,
59282
- children: [
59283
- "All ",
59284
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1.5 bg-gray-100 px-2 py-0.5 rounded-full text-xs text-gray-600", children: stats.all })
59285
- ]
59286
- }
59287
- ),
59288
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-px bg-gray-300 mx-1" }),
59289
- /* @__PURE__ */ jsxRuntime.jsxs(
59290
- "button",
59291
- {
59292
- onClick: () => setSelectedStatus("resolved"),
59293
- className: `flex items-center gap-1.5 px-3 py-2 rounded-lg text-sm transition-all cursor-pointer ${selectedStatus === "resolved" ? "bg-green-50 text-green-700 ring-1 ring-green-200" : "text-gray-600 hover:bg-gray-100"}`,
59294
- children: [
59295
- /* @__PURE__ */ jsxRuntime.jsx(outline.CheckCircleIcon, { className: "w-4 h-4 text-green-500" }),
59296
- "Resolved ",
59297
- stats.resolved
59298
- ]
59299
- }
59300
- ),
59927
+ onClick: () => handleStatusFilterChange(tab.id),
59928
+ className: `flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-medium transition-all duration-200 ${selectedStatus === tab.id ? "bg-white text-blue-600 shadow-sm ring-1 ring-gray-200" : "text-gray-600 hover:text-gray-900 hover:bg-gray-100"}`,
59929
+ children: [
59930
+ tab.icon && /* @__PURE__ */ jsxRuntime.jsx(tab.icon, { className: `w-4 h-4 ${selectedStatus === tab.id ? "text-blue-600" : "text-gray-400"}` }),
59931
+ tab.label,
59932
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-xs px-1.5 py-0.5 rounded-full ${selectedStatus === tab.id ? "bg-blue-50 text-blue-600" : "bg-gray-200/50 text-gray-500"}`, children: tab.count })
59933
+ ]
59934
+ },
59935
+ tab.id
59936
+ )) }),
59937
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", ref: filterRef, children: [
59301
59938
  /* @__PURE__ */ jsxRuntime.jsxs(
59302
59939
  "button",
59303
59940
  {
59304
- onClick: () => setSelectedStatus("unresolved"),
59305
- className: `flex items-center gap-1.5 px-3 py-2 rounded-lg text-sm transition-all cursor-pointer ${selectedStatus === "unresolved" ? "bg-red-50 text-red-700 ring-1 ring-red-200" : "text-gray-600 hover:bg-gray-100"}`,
59306
- children: [
59307
- /* @__PURE__ */ jsxRuntime.jsx(outline.XCircleIcon, { className: "w-4 h-4 text-red-500" }),
59308
- "Unresolved ",
59309
- stats.unresolved
59310
- ]
59311
- }
59312
- )
59313
- ] }),
59314
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2 w-full md:w-auto", children: [
59315
- /* @__PURE__ */ jsxRuntime.jsxs(
59316
- "select",
59317
- {
59318
- value: selectedShift,
59319
- onChange: (e) => setSelectedShift(e.target.value),
59320
- className: "flex-1 md:flex-none appearance-none pl-3 pr-8 py-1.5 text-sm border border-gray-300 rounded-lg bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer text-gray-700 shadow-sm",
59321
- style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.5rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.5em 1.5em` },
59322
- children: [
59323
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "all", children: "All Shifts" }),
59324
- shiftOptions.filter((s) => s !== "all").map((shift) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: shift, children: shift }, shift))
59325
- ]
59326
- }
59327
- ),
59328
- /* @__PURE__ */ jsxRuntime.jsx(
59329
- "select",
59330
- {
59331
- value: selectedWeeksRange,
59332
- onChange: (e) => setSelectedWeeksRange(e.target.value),
59333
- className: "flex-1 md:flex-none appearance-none pl-3 pr-8 py-1.5 text-sm border border-gray-300 rounded-lg bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer text-gray-700 shadow-sm",
59334
- style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.5rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.5em 1.5em` },
59335
- children: weekOptions.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.id, children: opt.label }, opt.id))
59336
- }
59337
- ),
59338
- /* @__PURE__ */ jsxRuntime.jsxs(
59339
- "select",
59340
- {
59341
- value: selectedMemberId,
59342
- onChange: (e) => setSelectedMemberId(e.target.value),
59343
- className: "flex-1 md:flex-none appearance-none pl-3 pr-8 py-1.5 text-sm border border-gray-300 rounded-lg bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer text-gray-700 shadow-sm",
59344
- style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.5rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.5em 1.5em` },
59941
+ onClick: () => setIsFilterOpen(!isFilterOpen),
59942
+ className: `flex items-center gap-2 px-3 py-1.5 rounded-lg border text-sm font-medium transition-all shadow-sm ${isFilterOpen || (selectedShift !== "all" || selectedWeeksRange !== "all" || selectedMemberId !== "all" || selectedLineId !== "all") ? "bg-blue-50 border-blue-200 text-blue-700" : "bg-white border-gray-200 text-gray-700 hover:bg-gray-50"}`,
59345
59943
  children: [
59346
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "all", children: "All Members" }),
59347
- teamMembers.map((member) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: member.id, children: member.name }, member.id))
59944
+ /* @__PURE__ */ jsxRuntime.jsx(outline.FunnelIcon, { className: "w-4 h-4" }),
59945
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Filters" }),
59946
+ (selectedShift !== "all" || selectedWeeksRange !== "all" || selectedMemberId !== "all" || selectedLineId !== "all") && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex items-center justify-center w-5 h-5 bg-blue-100 text-blue-700 text-xs rounded-full font-bold ml-1", children: [selectedShift, selectedWeeksRange, selectedMemberId, selectedLineId].filter((v) => v !== "all").length }),
59947
+ /* @__PURE__ */ jsxRuntime.jsx(outline.ChevronDownIcon, { className: `w-3 h-3 ml-1 transition-transform ${isFilterOpen ? "rotate-180" : ""}` })
59348
59948
  ]
59349
59949
  }
59350
59950
  ),
59351
- /* @__PURE__ */ jsxRuntime.jsx(
59352
- "select",
59353
- {
59354
- value: selectedLineId,
59355
- onChange: (e) => setSelectedLineId(e.target.value),
59356
- className: "flex-1 md:flex-none appearance-none pl-3 pr-8 py-1.5 text-sm border border-gray-300 rounded-lg bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer text-gray-700 shadow-sm",
59357
- style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.5rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.5em 1.5em` },
59358
- children: lineOptions.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.id, children: opt.label }, opt.id))
59359
- }
59360
- )
59951
+ isFilterOpen && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-0 top-full mt-2 w-72 bg-white rounded-xl shadow-xl border border-gray-100 p-4 z-50 animate-in fade-in zoom-in-95 duration-100", children: [
59952
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-3", children: [
59953
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-gray-900", children: "Filter View" }),
59954
+ (selectedShift !== "all" || selectedWeeksRange !== "all" || selectedMemberId !== "all" || selectedLineId !== "all") && /* @__PURE__ */ jsxRuntime.jsx(
59955
+ "button",
59956
+ {
59957
+ onClick: clearFilters,
59958
+ className: "text-xs text-red-600 hover:text-red-700 font-medium",
59959
+ children: "Clear all"
59960
+ }
59961
+ )
59962
+ ] }),
59963
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: [
59964
+ { value: selectedShift, onChange: handleShiftFilterChange, options: shiftOptions, label: "Shift" },
59965
+ { value: selectedWeeksRange, onChange: handleWeeksFilterChange, options: weekOptions.map((o) => o.id), labels: weekOptions, label: "Duration" },
59966
+ { value: selectedMemberId, onChange: handleMemberFilterChange, options: ["all", ...teamMembers.map((m) => m.id)], labels: teamMembers, label: "Member" },
59967
+ { value: selectedLineId, onChange: handleLineFilterChange, options: lineOptions.map((o) => o.id), labels: lineOptions, label: "Line" }
59968
+ ].map((filter2, idx) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
59969
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide ml-1", children: filter2.label }),
59970
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsx(
59971
+ "select",
59972
+ {
59973
+ value: filter2.value,
59974
+ onChange: (e) => filter2.onChange(e.target.value),
59975
+ className: "w-full appearance-none pl-3 pr-8 py-2 text-sm bg-gray-50 border border-gray-200 hover:border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all cursor-pointer",
59976
+ style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.75rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.2em 1.2em` },
59977
+ children: filter2.options.map((opt) => {
59978
+ const val = typeof opt === "string" ? opt : opt.id;
59979
+ let label = val;
59980
+ if (filter2.labels) {
59981
+ const found = filter2.labels.find((l) => (l.id || l.user_id || l) === val);
59982
+ label = found ? found.label || found.name || found : val;
59983
+ }
59984
+ if (val === "all") label = `All ${filter2.label}s`;
59985
+ return /* @__PURE__ */ jsxRuntime.jsx("option", { value: val, children: label }, val);
59986
+ })
59987
+ }
59988
+ ) })
59989
+ ] }, idx)) })
59990
+ ] })
59361
59991
  ] })
59362
59992
  ] }),
59363
59993
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-6", children: [
59364
- filteredRecommendations.length > 0 ? filteredRecommendations.map((rec, index) => /* @__PURE__ */ jsxRuntime.jsx(
59365
- motion.div,
59366
- {
59367
- initial: { opacity: 0, y: 20 },
59368
- animate: { opacity: 1, y: 0 },
59369
- transition: { delay: index * 0.1 },
59370
- className: `bg-white rounded-xl shadow-sm border overflow-hidden ${rec.ticket_status === "solved" ? "border-green-200" : "border-gray-200"}`,
59371
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 relative", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col md:flex-row gap-6", children: [
59372
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 space-y-4", children: [
59373
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-start justify-between", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3 w-full", children: [
59374
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
59375
- rec.ticket_status === "solved" ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium bg-emerald-50 text-emerald-700 border border-emerald-100", children: [
59376
- /* @__PURE__ */ jsxRuntime.jsx(outline.CheckCircleIcon, { className: "w-3.5 h-3.5" }),
59377
- "Resolved"
59378
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium border ${(rec.weeks_open || 0) > 2 ? "bg-red-50 text-red-700 border-red-100" : "bg-amber-50 text-amber-700 border-amber-100"}`, children: [
59379
- /* @__PURE__ */ jsxRuntime.jsx(outline.ClockIcon, { className: "w-3.5 h-3.5" }),
59380
- "Open for ",
59381
- rec.weeks_open || 1,
59382
- " week",
59383
- (rec.weeks_open || 1) !== 1 ? "s" : ""
59384
- ] }),
59385
- rec.assigned_user_ids && rec.assigned_user_ids.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
59386
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 w-px bg-gray-200" }),
59387
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3", children: rec.assigned_user_ids.map((uid) => {
59388
- const user2 = teamMembersById.get(uid);
59389
- if (!user2) {
59994
+ filteredRecommendations.length > 0 ? filteredRecommendations.map((rec, index) => {
59995
+ const weeksOpen = rec.weeks_open || 1;
59996
+ const openLabel = weeksOpen <= 1 ? "Opened this week" : `Opened for ${weeksOpen} weeks`;
59997
+ const openedOnLabel = formatOpenedDate(rec.first_seen_at);
59998
+ return /* @__PURE__ */ jsxRuntime.jsx(
59999
+ motion.div,
60000
+ {
60001
+ initial: { opacity: 0, y: 20 },
60002
+ animate: { opacity: 1, y: 0 },
60003
+ transition: { delay: index * 0.1 },
60004
+ className: `bg-white rounded-xl shadow-sm border overflow-hidden ${rec.ticket_status === "solved" ? "border-green-200" : "border-gray-200"}`,
60005
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 relative", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col md:flex-row gap-6", children: [
60006
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 space-y-4", children: [
60007
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-start justify-between", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3 w-full", children: [
60008
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
60009
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-semibold text-gray-600 bg-gray-100 border border-gray-200", children: [
60010
+ "#",
60011
+ rec.issue_number ?? index + 1
60012
+ ] }),
60013
+ rec.ticket_status === "solved" ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium bg-emerald-50 text-emerald-700 border border-emerald-100", children: [
60014
+ /* @__PURE__ */ jsxRuntime.jsx(outline.CheckCircleIcon, { className: "w-3.5 h-3.5" }),
60015
+ "Resolved"
60016
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium border ${weeksOpen > 2 ? "bg-red-50 text-red-700 border-red-100" : "bg-amber-50 text-amber-700 border-amber-100"}`, children: [
60017
+ /* @__PURE__ */ jsxRuntime.jsx(outline.ClockIcon, { className: "w-3.5 h-3.5" }),
60018
+ openLabel
60019
+ ] }),
60020
+ openedOnLabel && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-xs font-medium text-gray-600 bg-gray-100 border border-gray-200", children: [
60021
+ "Opened on ",
60022
+ openedOnLabel
60023
+ ] }),
60024
+ rec.assigned_user_ids && rec.assigned_user_ids.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
60025
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 w-px bg-gray-200" }),
60026
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3", children: rec.assigned_user_ids.map((uid) => {
60027
+ const user2 = teamMembersById.get(uid);
60028
+ if (!user2) {
60029
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
60030
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-5 h-5 rounded-full bg-gray-100 text-gray-500 flex items-center justify-center text-[9px] font-bold border border-gray-200", children: "??" }),
60031
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-500", children: "Unknown" })
60032
+ ] }, uid);
60033
+ }
59390
60034
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
59391
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-5 h-5 rounded-full bg-gray-100 text-gray-500 flex items-center justify-center text-[9px] font-bold border border-gray-200", children: "??" }),
59392
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-500", children: "Unknown" })
59393
- ] }, uid);
59394
- }
59395
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
59396
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-5 h-5 rounded-full bg-gray-100 text-gray-600 flex items-center justify-center text-[9px] font-bold border border-gray-200", children: user2.initials }),
59397
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-600", children: user2.name })
59398
- ] }, user2.id);
59399
- }) })
59400
- ] })
59401
- ] }),
59402
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
59403
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-900 pr-12", children: rec.title }),
59404
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm font-medium text-gray-500 mt-1 flex items-center gap-2", children: [
59405
- rec.location,
59406
- " \u2022 ",
59407
- rec.line,
59408
- (rec.shift_label || rec.shift_id !== void 0) && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
59409
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u2022" }),
59410
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "uppercase tracking-wide text-xs pt-0.5 text-gray-500 font-medium", children: rec.shift_label || `Shift ${rec.shift_id}` })
60035
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-5 h-5 rounded-full bg-gray-100 text-gray-600 flex items-center justify-center text-[9px] font-bold border border-gray-200", children: user2.initials }),
60036
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-600", children: user2.name })
60037
+ ] }, user2.id);
60038
+ }) })
60039
+ ] })
60040
+ ] }),
60041
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
60042
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-900 pr-12", children: rec.title }),
60043
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm font-medium text-gray-500 mt-1 flex items-center gap-2", children: [
60044
+ rec.location,
60045
+ " \u2022 ",
60046
+ rec.line,
60047
+ (rec.shift_label || rec.shift_id !== void 0) && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
60048
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u2022" }),
60049
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "uppercase tracking-wide text-xs pt-0.5 text-gray-500 font-medium", children: rec.shift_label || `Shift ${rec.shift_id}` })
60050
+ ] })
59411
60051
  ] })
59412
60052
  ] })
59413
- ] })
59414
- ] }) }),
59415
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "prose prose-sm text-gray-600", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: rec.description }) }),
59416
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-amber-50 border border-amber-100 rounded-lg p-3", children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-amber-800 font-medium flex items-start gap-2", children: [
59417
- /* @__PURE__ */ jsxRuntime.jsx(outline.ChartBarIcon, { className: "w-5 h-5 flex-shrink-0" }),
59418
- "Impact: ",
59419
- rec.impact
59420
- ] }) })
59421
- ] }),
59422
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full md:w-1/2 lg:w-5/12 bg-gray-50 rounded-lg p-4 border border-gray-100", children: Array.isArray(rec.evidence) && rec.evidence.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
59423
- EvidenceCarousel,
59424
- {
59425
- evidence: rec.evidence,
59426
- recId: rec.id,
59427
- clipsService
59428
- }
59429
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: isLoading ? "Loading evidence\u2026" : loadError ? loadError : "No evidence available" }) })
59430
- ] }) })
59431
- },
59432
- rec.id
59433
- )) : (
60053
+ ] }) }),
60054
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "prose prose-sm text-gray-600", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: rec.description }) }),
60055
+ rec.resolution_instructions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-blue-50 border border-blue-100 rounded-lg p-3", children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-blue-800 font-medium flex items-start gap-2", children: [
60056
+ /* @__PURE__ */ jsxRuntime.jsx(outline.CheckCircleIcon, { className: "w-5 h-5 flex-shrink-0" }),
60057
+ rec.resolution_instructions
60058
+ ] }) })
60059
+ ] }),
60060
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full md:w-1/2 lg:w-5/12 bg-gray-50 rounded-lg p-4 border border-gray-100", children: Array.isArray(rec.evidence) && rec.evidence.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
60061
+ EvidenceCarousel,
60062
+ {
60063
+ evidence: rec.evidence,
60064
+ recId: rec.id,
60065
+ clipsService
60066
+ }
60067
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: isLoading ? "Loading evidence\u2026" : loadError ? loadError : "No evidence available" }) })
60068
+ ] }) })
60069
+ },
60070
+ rec.id
60071
+ );
60072
+ }) : (
59434
60073
  // Success State (Zero Tickets)
59435
60074
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-16 bg-white rounded-xl border border-gray-200 shadow-sm", children: [
59436
60075
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto flex items-center justify-center w-16 h-16 rounded-full bg-green-50 mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(outline.CheckCircleIcon, { className: "w-8 h-8 text-green-500" }) }),
@@ -60306,6 +60945,7 @@ exports.useHookOverride = useHookOverride;
60306
60945
  exports.useHourEndTimer = useHourEndTimer;
60307
60946
  exports.useHourlyTargetAchievements = useHourlyTargetAchievements;
60308
60947
  exports.useHourlyTargetMisses = useHourlyTargetMisses;
60948
+ exports.useIdleTimeClipClassifications = useIdleTimeClipClassifications;
60309
60949
  exports.useIdleTimeReasons = useIdleTimeReasons;
60310
60950
  exports.useLeaderboardMetrics = useLeaderboardMetrics;
60311
60951
  exports.useLineDetailedMetrics = useLineDetailedMetrics;