@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.mjs CHANGED
@@ -2,8 +2,8 @@ import * as React24 from 'react';
2
2
  import React24__default, { createContext, useRef, useCallback, useState, useMemo, useEffect, forwardRef, useImperativeHandle, useLayoutEffect, memo, useContext, useId, Children, isValidElement, useInsertionEffect, Fragment as Fragment$1, createElement, Component } from 'react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import { useRouter } from 'next/router';
5
- import { toZonedTime, formatInTimeZone, fromZonedTime } from 'date-fns-tz';
6
- import { subDays, format, parseISO, isValid, addMinutes, differenceInMinutes, formatDistanceToNow, isToday, isFuture, addDays, startOfDay, isBefore } from 'date-fns';
5
+ import { fromZonedTime, toZonedTime, formatInTimeZone } from 'date-fns-tz';
6
+ import { addMinutes, subDays, format, parseISO, isValid, differenceInMinutes, formatDistanceToNow, isToday, isFuture, addDays, startOfDay, isBefore, startOfMonth, endOfMonth, eachDayOfInterval, getDay, isSameDay, isWithinInterval, subMonths, addMonths } from 'date-fns';
7
7
  import mixpanel from 'mixpanel-browser';
8
8
  import { EventEmitter } from 'events';
9
9
  import { createClient, REALTIME_SUBSCRIBE_STATES } from '@supabase/supabase-js';
@@ -11,13 +11,13 @@ import Hls3, { Events, ErrorTypes } from 'hls.js';
11
11
  import useSWR from 'swr';
12
12
  import { noop, warning, invariant, progress, secondsToMilliseconds, millisecondsToSeconds, memo as memo$1 } from 'motion-utils';
13
13
  import { getValueTransition, hover, press, isPrimaryPointer, GroupPlaybackControls, setDragLock, supportsLinearEasing, attachTimeline, isGenerator, calcGeneratorDuration, isWaapiSupportedEasing, mapEasingToNativeEasing, maxGeneratorDuration, generateLinearEasing, isBezierDefinition } from 'motion-dom';
14
- import { Camera, ChevronDown, ChevronUp, Check, Map as Map$1, Video, ShieldCheck, Star, Award, ArrowLeft, X, Coffee, Plus, Clock, Calendar, Save, AlertCircle, Loader2, Minus, ArrowDown, ArrowUp, ChevronLeft, ChevronRight, Pause, Play, Wrench, XCircle, Package, UserX, Zap, HelpCircle, AlertTriangle, Tag, Sparkles, TrendingUp, Settings2, CheckCircle2, RefreshCw, TrendingDown, FolderOpen, Folder, Sliders, Activity, Layers, Filter, Search, Edit2, CheckCircle, User, Users, Shield, Building2, Mail, Lock, ArrowRight, Info, Share2, Trophy, Target, Download, Sun, Moon, MousePointer, UserPlus, UserCog, Trash2, Eye, MoreVertical, BarChart3, MessageSquare, Menu, Send, Copy, UserCheck, LogOut, Settings, LifeBuoy, EyeOff, UserCircle } from 'lucide-react';
14
+ import { Camera, ChevronDown, ChevronUp, Check, Map as Map$1, Video, ShieldCheck, Star, Award, ArrowLeft, X, Coffee, Plus, Clock, Calendar, Save, AlertCircle, Loader2, Minus, ArrowDown, ArrowUp, ChevronLeft, ChevronRight, TrendingUp, Sparkles, Pause, Play, Wrench, XCircle, Package, UserX, Zap, HelpCircle, AlertTriangle, Tag, Palette, CheckCircle2, RefreshCw, TrendingDown, FolderOpen, Folder, Sliders, Activity, Layers, Filter, Search, Edit2, ArrowRight, CheckCircle, User, Users, Shield, Building2, Mail, Lock, Info, Share2, Trophy, Target, Download, Sun, Moon, MousePointer, UserPlus, UserCog, Trash2, Eye, MoreVertical, BarChart3, MessageSquare, Menu, Send, Copy, UserCheck, LogOut, Settings, LifeBuoy, EyeOff, UserCircle } from 'lucide-react';
15
15
  import { toast } from 'sonner';
16
16
  import { BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, ReferenceLine, Tooltip, Legend, Bar, LabelList, ResponsiveContainer, LineChart as LineChart$1, Line, PieChart, Pie, Cell, ComposedChart, Area, ScatterChart, Scatter } from 'recharts';
17
17
  import { Slot } from '@radix-ui/react-slot';
18
18
  import * as SelectPrimitive from '@radix-ui/react-select';
19
19
  import { DayPicker, useNavigation as useNavigation$1 } from 'react-day-picker';
20
- import { XMarkIcon, ArrowRightIcon, HomeIcon, TrophyIcon, ChartBarIcon, LightBulbIcon, AdjustmentsHorizontalIcon, ClockIcon, UsersIcon, TicketIcon, CubeIcon, SparklesIcon, QuestionMarkCircleIcon, HeartIcon, UserCircleIcon, ExclamationCircleIcon, EnvelopeIcon, DocumentTextIcon, ChevronUpIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, Bars3Icon, CheckCircleIcon, ChatBubbleLeftRightIcon, ArrowLeftIcon, XCircleIcon, CalendarIcon, InformationCircleIcon, PlayCircleIcon } from '@heroicons/react/24/outline';
20
+ import { XMarkIcon, ArrowRightIcon, HomeIcon, TrophyIcon, ChartBarIcon, LightBulbIcon, AdjustmentsHorizontalIcon, ClockIcon, UsersIcon, TicketIcon, CubeIcon, SparklesIcon, QuestionMarkCircleIcon, HeartIcon, UserCircleIcon, ExclamationCircleIcon, EnvelopeIcon, DocumentTextIcon, ChevronUpIcon, ChevronDownIcon, Bars3Icon, CheckCircleIcon, ChatBubbleLeftRightIcon, ArrowLeftIcon, XCircleIcon, FunnelIcon, CalendarIcon, ChevronRightIcon, ChevronLeftIcon, InformationCircleIcon, PlayCircleIcon } from '@heroicons/react/24/outline';
21
21
  import { CheckIcon } from '@heroicons/react/24/solid';
22
22
  import html2canvas from 'html2canvas';
23
23
  import jsPDF, { jsPDF as jsPDF$1 } from 'jspdf';
@@ -1552,7 +1552,7 @@ var dashboardService = {
1552
1552
  };
1553
1553
  const formattedStartDate = formatDate2(startDate);
1554
1554
  const formattedEndDate = formatDate2(endDate);
1555
- let query = supabase.from(lineMetricsTable).select("date, shift_id, avg_efficiency, underperforming_workspaces, total_workspaces").gte("date", formattedStartDate).lte("date", formattedEndDate);
1555
+ 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);
1556
1556
  if (lineIdInput === factoryViewId) {
1557
1557
  if (!isValidFactoryViewConfiguration(entityConfig)) {
1558
1558
  throw new Error("Factory View requires at least one configured line for monthly data.");
@@ -1571,7 +1571,10 @@ var dashboardService = {
1571
1571
  shift_id: item.shift_id,
1572
1572
  avg_efficiency: item.avg_efficiency || 0,
1573
1573
  underperforming_workspaces: item.underperforming_workspaces || 0,
1574
- total_workspaces: item.total_workspaces || 0
1574
+ total_workspaces: item.total_workspaces || 0,
1575
+ current_output: item.current_output || 0,
1576
+ ideal_output: item.ideal_output || 0,
1577
+ line_threshold: item.line_threshold || 0
1575
1578
  }));
1576
1579
  return transformedData;
1577
1580
  } catch (err) {
@@ -9386,7 +9389,56 @@ var getUniformShiftGroup = (shiftConfigMap, timezone, now2 = /* @__PURE__ */ new
9386
9389
  return groups.length === 1 ? groups[0] : null;
9387
9390
  };
9388
9391
 
9392
+ // src/lib/types/efficiencyLegend.ts
9393
+ var DEFAULT_EFFICIENCY_LEGEND = {
9394
+ green_min: 80,
9395
+ green_max: 100,
9396
+ yellow_min: 70,
9397
+ yellow_max: 79,
9398
+ red_min: 0,
9399
+ red_max: 69,
9400
+ critical_threshold: 50
9401
+ };
9402
+ function getEfficiencyColor(efficiency, legend = DEFAULT_EFFICIENCY_LEGEND) {
9403
+ if (efficiency >= legend.green_min) {
9404
+ return "green";
9405
+ } else if (efficiency >= legend.yellow_min) {
9406
+ return "yellow";
9407
+ } else {
9408
+ return "red";
9409
+ }
9410
+ }
9411
+ function getEfficiencyColorClasses(efficiency, legend = DEFAULT_EFFICIENCY_LEGEND) {
9412
+ const color2 = getEfficiencyColor(efficiency, legend);
9413
+ switch (color2) {
9414
+ case "green":
9415
+ return "bg-[#00AB45]/90 hover:bg-[#00AB45]/95";
9416
+ case "yellow":
9417
+ return "bg-[#FFB020]/90 hover:bg-[#FFB020]/95";
9418
+ case "red":
9419
+ return "bg-[#E34329]/90 hover:bg-[#E34329]/95";
9420
+ default:
9421
+ return "bg-gray-300/90";
9422
+ }
9423
+ }
9424
+
9389
9425
  // src/lib/hooks/useDashboardMetrics.ts
9426
+ var parseEfficiencyLegend = (legend) => {
9427
+ if (!legend) return null;
9428
+ const coerce = (value, fallback) => {
9429
+ const num = Number(value);
9430
+ return Number.isFinite(num) ? num : fallback;
9431
+ };
9432
+ return {
9433
+ green_min: coerce(legend.green_min, DEFAULT_EFFICIENCY_LEGEND.green_min),
9434
+ green_max: coerce(legend.green_max, DEFAULT_EFFICIENCY_LEGEND.green_max),
9435
+ yellow_min: coerce(legend.yellow_min, DEFAULT_EFFICIENCY_LEGEND.yellow_min),
9436
+ yellow_max: coerce(legend.yellow_max, DEFAULT_EFFICIENCY_LEGEND.yellow_max),
9437
+ red_min: coerce(legend.red_min, DEFAULT_EFFICIENCY_LEGEND.red_min),
9438
+ red_max: coerce(legend.red_max, DEFAULT_EFFICIENCY_LEGEND.red_max),
9439
+ critical_threshold: coerce(legend.critical_threshold, DEFAULT_EFFICIENCY_LEGEND.critical_threshold)
9440
+ };
9441
+ };
9390
9442
  var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds }) => {
9391
9443
  const { supabaseUrl, supabaseKey } = useDashboardConfig();
9392
9444
  const entityConfig = useEntityConfig();
@@ -9444,7 +9496,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
9444
9496
  );
9445
9497
  useEffect(() => {
9446
9498
  lineIdRef.current = lineId;
9447
- setMetrics({ workspaceMetrics: [], lineMetrics: [] });
9499
+ setMetrics({ workspaceMetrics: [], lineMetrics: [], metadata: void 0 });
9448
9500
  setIsLoading(true);
9449
9501
  }, [lineId]);
9450
9502
  const fetchAllMetrics = useCallback(async () => {
@@ -9482,6 +9534,8 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
9482
9534
  }
9483
9535
  let allWorkspaceMetrics = [];
9484
9536
  let allLineMetrics = [];
9537
+ let hasFlowBuffers = false;
9538
+ let efficiencyLegend;
9485
9539
  if (isFactory && shiftGroups.length > 0) {
9486
9540
  console.log(`[useDashboardMetrics] \u{1F3ED} Factory view: Fetching for ${shiftGroups.length} shift group(s)`);
9487
9541
  const metricsPromises = shiftGroups.map(async (group) => {
@@ -9512,6 +9566,9 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
9512
9566
  }
9513
9567
  });
9514
9568
  const results = await Promise.all(metricsPromises);
9569
+ hasFlowBuffers = results.some((result) => result?.metadata?.has_flow_buffers);
9570
+ const legendCandidate = results.find((result) => result?.efficiency_legend)?.efficiency_legend;
9571
+ efficiencyLegend = parseEfficiencyLegend(legendCandidate) ?? DEFAULT_EFFICIENCY_LEGEND;
9515
9572
  results.forEach((result) => {
9516
9573
  if (result.workspace_metrics) {
9517
9574
  allWorkspaceMetrics.push(...result.workspace_metrics);
@@ -9564,6 +9621,8 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
9564
9621
  });
9565
9622
  allWorkspaceMetrics = backendData.workspace_metrics || [];
9566
9623
  allLineMetrics = backendData.line_metrics || [];
9624
+ hasFlowBuffers = Boolean(backendData?.metadata?.has_flow_buffers);
9625
+ efficiencyLegend = parseEfficiencyLegend(backendData?.efficiency_legend) ?? DEFAULT_EFFICIENCY_LEGEND;
9567
9626
  }
9568
9627
  const transformedWorkspaceData = allWorkspaceMetrics.map((item) => ({
9569
9628
  company_id: item.company_id || entityConfig.companyId,
@@ -9589,7 +9648,9 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
9589
9648
  });
9590
9649
  const newMetricsState = {
9591
9650
  workspaceMetrics: transformedWorkspaceData,
9592
- lineMetrics: allLineMetrics || []
9651
+ lineMetrics: allLineMetrics || [],
9652
+ metadata: { hasFlowBuffers },
9653
+ efficiencyLegend: efficiencyLegend ?? DEFAULT_EFFICIENCY_LEGEND
9593
9654
  };
9594
9655
  console.log("[useDashboardMetrics] Setting metrics state:", {
9595
9656
  workspaceMetrics: newMetricsState.workspaceMetrics.length,
@@ -9741,6 +9802,8 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
9741
9802
  return {
9742
9803
  workspaceMetrics: metrics2?.workspaceMetrics || [],
9743
9804
  lineMetrics: metrics2?.lineMetrics || [],
9805
+ efficiencyLegend: metrics2?.efficiencyLegend || DEFAULT_EFFICIENCY_LEGEND,
9806
+ metadata: metrics2?.metadata,
9744
9807
  isLoading,
9745
9808
  error,
9746
9809
  refetch: fetchAllMetrics
@@ -13757,6 +13820,256 @@ function useIdleTimeReasons({
13757
13820
  refetch: fetchData
13758
13821
  };
13759
13822
  }
13823
+
13824
+ // src/lib/services/clipClassificationService.ts
13825
+ function parseCleanLabel(rawLabel) {
13826
+ if (!rawLabel) return null;
13827
+ const patterns = [
13828
+ /"Clip Label"\s*:\s*"([^"]+)"/,
13829
+ // Old format
13830
+ /"clip_label"\s*:\s*"([^"]+)"/,
13831
+ // New format
13832
+ /'Clip Label'\s*:\s*'([^']+)'/,
13833
+ // Single quotes
13834
+ /'clip_label'\s*:\s*'([^']+)'/
13835
+ // Single quotes
13836
+ ];
13837
+ for (const pattern of patterns) {
13838
+ const match = rawLabel.match(pattern);
13839
+ if (match) {
13840
+ const label = match[1].trim();
13841
+ return label.replace(/ /g, "_");
13842
+ }
13843
+ }
13844
+ return null;
13845
+ }
13846
+ async function fetchClassifications(clipIds, token) {
13847
+ if (!clipIds || clipIds.length === 0) {
13848
+ return {};
13849
+ }
13850
+ try {
13851
+ const CHUNK_SIZE = 50;
13852
+ const chunks = [];
13853
+ for (let i = 0; i < clipIds.length; i += CHUNK_SIZE) {
13854
+ chunks.push(clipIds.slice(i, i + CHUNK_SIZE));
13855
+ }
13856
+ console.log(`[fetchClassifications] Fetching ${clipIds.length} classifications in ${chunks.length} chunks`);
13857
+ const apiBase = process.env.NEXT_PUBLIC_BACKEND_URL || "http://localhost:8000";
13858
+ const chunkResults = await Promise.all(
13859
+ chunks.map(async (chunkIds) => {
13860
+ try {
13861
+ const response = await fetch(
13862
+ `${apiBase}/api/classification/batch?clip_ids=${chunkIds.join(",")}`,
13863
+ {
13864
+ headers: {
13865
+ "Authorization": `Bearer ${token}`,
13866
+ "Content-Type": "application/json"
13867
+ }
13868
+ }
13869
+ );
13870
+ if (!response.ok) {
13871
+ console.error(`Classification API error for chunk: ${response.status}`);
13872
+ return Object.fromEntries(
13873
+ chunkIds.map((id3) => [id3, { status: "processing" }])
13874
+ );
13875
+ }
13876
+ const data = await response.json();
13877
+ return data.classifications || {};
13878
+ } catch (error) {
13879
+ console.error("Error fetching chunk:", error);
13880
+ return Object.fromEntries(
13881
+ chunkIds.map((id3) => [id3, { status: "processing" }])
13882
+ );
13883
+ }
13884
+ })
13885
+ );
13886
+ const allClassifications = Object.assign({}, ...chunkResults);
13887
+ console.log(`[fetchClassifications] Total fetched: ${Object.keys(allClassifications).length} classifications`);
13888
+ return allClassifications;
13889
+ } catch (error) {
13890
+ console.error("Error fetching classifications:", error);
13891
+ return Object.fromEntries(
13892
+ clipIds.map((id3) => [id3, { status: "processing" }])
13893
+ );
13894
+ }
13895
+ }
13896
+ function useClassificationRealtimeUpdates({
13897
+ clipIds,
13898
+ enabled = true,
13899
+ onClassificationUpdate
13900
+ }) {
13901
+ const supabase = useSupabase();
13902
+ useEffect(() => {
13903
+ if (!enabled || !clipIds || clipIds.length === 0 || !supabase) {
13904
+ return;
13905
+ }
13906
+ const channelName = `classification:${clipIds.slice(0, 5).join("-")}`;
13907
+ console.log(`[useClassificationRealtimeUpdates] Subscribing to channel: ${channelName}`);
13908
+ const channel = supabase.channel(channelName).on(
13909
+ "postgres_changes",
13910
+ {
13911
+ event: "INSERT",
13912
+ schema: "public",
13913
+ table: "clip_classification",
13914
+ filter: `clip_id=in.(${clipIds.join(",")})`
13915
+ },
13916
+ (payload) => {
13917
+ console.log("[useClassificationRealtimeUpdates] New classification:", payload);
13918
+ try {
13919
+ const { clip_id, clip_label, confidence_score } = payload.new;
13920
+ const parsedLabel = parseCleanLabel(clip_label);
13921
+ if (parsedLabel) {
13922
+ onClassificationUpdate(clip_id, {
13923
+ status: "classified",
13924
+ label: parsedLabel,
13925
+ confidence: confidence_score || 0
13926
+ });
13927
+ } else {
13928
+ console.warn("[useClassificationRealtimeUpdates] Failed to parse label:", clip_label);
13929
+ }
13930
+ } catch (error) {
13931
+ console.error("[useClassificationRealtimeUpdates] Error processing update:", error);
13932
+ }
13933
+ }
13934
+ ).subscribe((status) => {
13935
+ console.log(`[useClassificationRealtimeUpdates] Subscription status:`, status);
13936
+ });
13937
+ return () => {
13938
+ console.log(`[useClassificationRealtimeUpdates] Unsubscribing from ${channelName}`);
13939
+ supabase.removeChannel(channel);
13940
+ };
13941
+ }, [clipIds, enabled, supabase, onClassificationUpdate]);
13942
+ }
13943
+
13944
+ // src/lib/hooks/useIdleTimeClipClassifications.ts
13945
+ var DEFAULT_LIMIT = 200;
13946
+ var MAX_PAGES = 5;
13947
+ function useIdleTimeClipClassifications({
13948
+ workspaceId,
13949
+ date,
13950
+ shiftId,
13951
+ enabled = true,
13952
+ limit = DEFAULT_LIMIT
13953
+ }) {
13954
+ const { session } = useAuth();
13955
+ const [idleClips, setIdleClips] = useState([]);
13956
+ const [clipClassifications, setClipClassifications] = useState({});
13957
+ const [isLoading, setIsLoading] = useState(false);
13958
+ const [error, setError] = useState(null);
13959
+ const lastRequestKeyRef = useRef("");
13960
+ const requestKey = useMemo(
13961
+ () => `${workspaceId || ""}-${date || ""}-${shiftId ?? ""}`,
13962
+ [workspaceId, date, shiftId]
13963
+ );
13964
+ const fetchIdleClips = useCallback(async () => {
13965
+ if (!enabled) {
13966
+ setIsLoading(false);
13967
+ return;
13968
+ }
13969
+ if (!workspaceId || !date || shiftId === void 0) {
13970
+ setIdleClips([]);
13971
+ setIsLoading(false);
13972
+ return;
13973
+ }
13974
+ if (!session?.access_token) {
13975
+ setError("Not authenticated");
13976
+ setIsLoading(false);
13977
+ return;
13978
+ }
13979
+ setIsLoading(true);
13980
+ setError(null);
13981
+ lastRequestKeyRef.current = requestKey;
13982
+ try {
13983
+ const collected = [];
13984
+ let page = 1;
13985
+ let hasMore = true;
13986
+ while (hasMore && page <= MAX_PAGES && collected.length < limit) {
13987
+ const response = await fetch("/api/clips/supabase", {
13988
+ method: "POST",
13989
+ headers: {
13990
+ "Content-Type": "application/json",
13991
+ "Authorization": `Bearer ${session.access_token}`
13992
+ },
13993
+ body: JSON.stringify({
13994
+ action: "clip-metadata",
13995
+ workspaceId,
13996
+ date,
13997
+ shift: shiftId.toString(),
13998
+ category: "idle_time",
13999
+ page,
14000
+ limit
14001
+ })
14002
+ });
14003
+ if (!response.ok) {
14004
+ throw new Error(`Idle clip metadata request failed: ${response.status}`);
14005
+ }
14006
+ const data = await response.json();
14007
+ const clips = Array.isArray(data?.clips) ? data.clips : [];
14008
+ const mapped = clips.map((clip) => ({
14009
+ id: clip.id,
14010
+ idle_start_time: clip.idle_start_time,
14011
+ idle_end_time: clip.idle_end_time
14012
+ }));
14013
+ collected.push(...mapped);
14014
+ hasMore = Boolean(data?.hasMore);
14015
+ page += 1;
14016
+ }
14017
+ if (lastRequestKeyRef.current === requestKey) {
14018
+ setIdleClips(collected);
14019
+ }
14020
+ } catch (err) {
14021
+ console.error("[useIdleTimeClipClassifications] Error fetching idle clips:", err);
14022
+ if (lastRequestKeyRef.current === requestKey) {
14023
+ setError(err instanceof Error ? err.message : "Failed to fetch idle clips");
14024
+ setIdleClips([]);
14025
+ }
14026
+ } finally {
14027
+ if (lastRequestKeyRef.current === requestKey) {
14028
+ setIsLoading(false);
14029
+ }
14030
+ }
14031
+ }, [enabled, workspaceId, date, shiftId, session?.access_token, requestKey, limit]);
14032
+ useEffect(() => {
14033
+ fetchIdleClips();
14034
+ }, [fetchIdleClips]);
14035
+ useEffect(() => {
14036
+ setClipClassifications({});
14037
+ }, [requestKey]);
14038
+ const idleClipIds = useMemo(
14039
+ () => idleClips.map((clip) => clip.id).filter(Boolean),
14040
+ [idleClips]
14041
+ );
14042
+ useEffect(() => {
14043
+ if (!enabled || idleClipIds.length === 0 || !session?.access_token) {
14044
+ return;
14045
+ }
14046
+ fetchClassifications(idleClipIds, session.access_token).then((classifications) => {
14047
+ setClipClassifications((prev) => ({
14048
+ ...prev,
14049
+ ...classifications
14050
+ }));
14051
+ }).catch((err) => {
14052
+ console.error("[useIdleTimeClipClassifications] Error fetching classifications:", err);
14053
+ });
14054
+ }, [enabled, idleClipIds, session?.access_token]);
14055
+ useClassificationRealtimeUpdates({
14056
+ clipIds: idleClipIds,
14057
+ enabled: enabled && idleClipIds.length > 0,
14058
+ onClassificationUpdate: useCallback((clipId, classification) => {
14059
+ setClipClassifications((prev) => ({
14060
+ ...prev,
14061
+ [clipId]: classification
14062
+ }));
14063
+ }, [])
14064
+ });
14065
+ return {
14066
+ idleClips,
14067
+ clipClassifications,
14068
+ isLoading,
14069
+ error,
14070
+ refetch: fetchIdleClips
14071
+ };
14072
+ }
13760
14073
  function useUserUsage(userId, options = {}) {
13761
14074
  const { startDate, endDate, enabled = true } = options;
13762
14075
  const { session } = useAuth();
@@ -26464,6 +26777,10 @@ var HourlyOutputChartComponent = ({
26464
26777
  shiftEnd,
26465
26778
  showIdleTime = false,
26466
26779
  idleTimeHourly,
26780
+ idleTimeClips,
26781
+ idleTimeClipClassifications,
26782
+ shiftDate,
26783
+ timezone,
26467
26784
  className = ""
26468
26785
  }) => {
26469
26786
  const containerRef = React24__default.useRef(null);
@@ -26476,6 +26793,37 @@ var HourlyOutputChartComponent = ({
26476
26793
  return { hour, minute, decimalHour };
26477
26794
  };
26478
26795
  const shiftStartTime = getTimeFromTimeString(shiftStart);
26796
+ const shiftStartDateTime = React24__default.useMemo(() => {
26797
+ if (!shiftDate || !timezone) return null;
26798
+ const hour = shiftStartTime.hour.toString().padStart(2, "0");
26799
+ const minute = shiftStartTime.minute.toString().padStart(2, "0");
26800
+ return fromZonedTime(`${shiftDate}T${hour}:${minute}:00`, timezone);
26801
+ }, [shiftDate, timezone, shiftStartTime.hour, shiftStartTime.minute]);
26802
+ const idleClipRanges = React24__default.useMemo(() => {
26803
+ if (!idleTimeClips || idleTimeClips.length === 0) return [];
26804
+ return idleTimeClips.map((clip) => ({
26805
+ id: clip.id,
26806
+ start: clip.idle_start_time ? new Date(clip.idle_start_time) : null,
26807
+ end: clip.idle_end_time ? new Date(clip.idle_end_time) : null
26808
+ })).filter((clip) => clip.start && clip.end);
26809
+ }, [idleTimeClips]);
26810
+ const getIdleReasonForRange = React24__default.useCallback((rangeStart, rangeEnd) => {
26811
+ if (!rangeStart || !rangeEnd || idleClipRanges.length === 0) {
26812
+ return "Reason unavailable";
26813
+ }
26814
+ const matchingClip = idleClipRanges.find((clip) => rangeStart >= clip.start && rangeEnd <= clip.end) || idleClipRanges.find((clip) => rangeStart < clip.end && rangeEnd > clip.start);
26815
+ if (!matchingClip) {
26816
+ return "Reason unavailable";
26817
+ }
26818
+ const classification = idleTimeClipClassifications?.[matchingClip.id];
26819
+ if (!classification || classification.status === "processing") {
26820
+ return "Analyzing...";
26821
+ }
26822
+ if (!classification.label) {
26823
+ return "Reason unavailable";
26824
+ }
26825
+ return classification.label.replace(/_/g, " ");
26826
+ }, [idleClipRanges, idleTimeClipClassifications]);
26479
26827
  const { shiftDuration, shiftEndTime, hasPartialLastHour } = React24__default.useMemo(() => {
26480
26828
  console.log("[HourlyOutputChart] Calculating shift duration with:", {
26481
26829
  shiftStart,
@@ -26723,6 +27071,7 @@ var HourlyOutputChartComponent = ({
26723
27071
  }
26724
27072
  idleMinutes = idleArray.filter((val) => val === "1" || val === 1).length;
26725
27073
  return {
27074
+ hourIndex: i,
26726
27075
  hour: formatHour(i),
26727
27076
  timeRange: formatTimeRange2(i),
26728
27077
  output: animatedData[i] || 0,
@@ -26916,9 +27265,10 @@ var HourlyOutputChartComponent = ({
26916
27265
  }
26917
27266
  }
26918
27267
  const formatIdleTimestamp = (minuteIdx) => {
26919
- const hourIndex = chartData.findIndex((item) => item.timeRange === data2.timeRange);
26920
- const hourOffset = hourIndex >= 0 ? hourIndex : 0;
26921
- const totalMinutes = (shiftStartTime.hour + hourOffset) * 60 + shiftStartTime.minute + minuteIdx;
27268
+ const fallbackIndex = chartData.findIndex((item) => item.timeRange === data2.timeRange);
27269
+ const hourOffset = Number.isFinite(data2.hourIndex) ? data2.hourIndex : fallbackIndex;
27270
+ const safeHourOffset = hourOffset >= 0 ? hourOffset : 0;
27271
+ const totalMinutes = (shiftStartTime.hour + safeHourOffset) * 60 + shiftStartTime.minute + minuteIdx;
26922
27272
  const hour = Math.floor(totalMinutes / 60) % 24;
26923
27273
  const minute = totalMinutes % 60;
26924
27274
  const period = hour >= 12 ? "PM" : "AM";
@@ -26949,27 +27299,23 @@ var HourlyOutputChartComponent = ({
26949
27299
  const duration = range.end - range.start + 1;
26950
27300
  const startTime = formatIdleTimestamp(range.start);
26951
27301
  const endTime = formatIdleTimestamp(range.end + 1);
27302
+ const fallbackIndex = chartData.findIndex((item) => item.timeRange === data2.timeRange);
27303
+ const hourOffset = Number.isFinite(data2.hourIndex) ? data2.hourIndex : fallbackIndex;
27304
+ const safeHourOffset = hourOffset >= 0 ? hourOffset : 0;
27305
+ const rangeStartDate = shiftStartDateTime ? addMinutes(shiftStartDateTime, safeHourOffset * 60 + range.start) : null;
27306
+ const rangeEndDate = shiftStartDateTime ? addMinutes(shiftStartDateTime, safeHourOffset * 60 + range.end + 1) : null;
27307
+ const reasonLabel = getIdleReasonForRange(rangeStartDate, rangeEndDate);
26952
27308
  return /* @__PURE__ */ jsxs("div", { className: "text-gray-600 flex items-center gap-2 text-xs", children: [
26953
27309
  /* @__PURE__ */ jsx("span", { className: "inline-block w-1.5 h-1.5 bg-orange-400 rounded-full flex-shrink-0" }),
26954
- /* @__PURE__ */ jsx("span", { children: duration === 1 ? /* @__PURE__ */ jsxs(Fragment, { children: [
26955
- startTime,
26956
- " ",
26957
- /* @__PURE__ */ jsxs("span", { className: "text-gray-400", children: [
26958
- "(",
26959
- duration,
26960
- " min)"
26961
- ] })
26962
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
26963
- startTime,
26964
- " - ",
26965
- endTime,
26966
- " ",
26967
- /* @__PURE__ */ jsxs("span", { className: "text-gray-400", children: [
26968
- "(",
26969
- duration,
26970
- " min)"
26971
- ] })
26972
- ] }) })
27310
+ /* @__PURE__ */ jsxs("span", { children: [
27311
+ duration === 1 ? /* @__PURE__ */ jsx(Fragment, { children: startTime }) : /* @__PURE__ */ jsxs(Fragment, { children: [
27312
+ startTime,
27313
+ " - ",
27314
+ endTime
27315
+ ] }),
27316
+ /* @__PURE__ */ jsx("span", { className: "text-gray-400", children: " | " }),
27317
+ /* @__PURE__ */ jsx("span", { className: "text-gray-600", children: reasonLabel })
27318
+ ] })
26973
27319
  ] }, index);
26974
27320
  }) })
26975
27321
  ] })
@@ -27067,7 +27413,7 @@ var HourlyOutputChartComponent = ({
27067
27413
  );
27068
27414
  };
27069
27415
  var HourlyOutputChart = React24__default.memo(HourlyOutputChartComponent, (prevProps, nextProps) => {
27070
- if (prevProps.pphThreshold !== nextProps.pphThreshold || prevProps.shiftStart !== nextProps.shiftStart || prevProps.showIdleTime !== nextProps.showIdleTime || prevProps.className !== nextProps.className) {
27416
+ 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) {
27071
27417
  return false;
27072
27418
  }
27073
27419
  if (prevProps.data.length !== nextProps.data.length) {
@@ -27090,6 +27436,33 @@ var HourlyOutputChart = React24__default.memo(HourlyOutputChartComponent, (prevP
27090
27436
  if (prevArray.length !== nextArray.length) return false;
27091
27437
  if (!prevArray.every((val, idx) => val === nextArray[idx])) return false;
27092
27438
  }
27439
+ const prevIdleClips = prevProps.idleTimeClips || [];
27440
+ const nextIdleClips = nextProps.idleTimeClips || [];
27441
+ if (prevIdleClips.length !== nextIdleClips.length) {
27442
+ return false;
27443
+ }
27444
+ for (let i = 0; i < prevIdleClips.length; i += 1) {
27445
+ const prevClip = prevIdleClips[i];
27446
+ const nextClip = nextIdleClips[i];
27447
+ if (prevClip.id !== nextClip.id || prevClip.idle_start_time !== nextClip.idle_start_time || prevClip.idle_end_time !== nextClip.idle_end_time) {
27448
+ return false;
27449
+ }
27450
+ }
27451
+ const prevClassifications = prevProps.idleTimeClipClassifications || {};
27452
+ const nextClassifications = nextProps.idleTimeClipClassifications || {};
27453
+ const prevClassKeys = Object.keys(prevClassifications);
27454
+ const nextClassKeys = Object.keys(nextClassifications);
27455
+ if (prevClassKeys.length !== nextClassKeys.length) {
27456
+ return false;
27457
+ }
27458
+ for (const key of prevClassKeys) {
27459
+ const prevClass = prevClassifications[key];
27460
+ const nextClass = nextClassifications[key];
27461
+ if (!nextClass) return false;
27462
+ if (prevClass.status !== nextClass.status || prevClass.label !== nextClass.label || prevClass.confidence !== nextClass.confidence) {
27463
+ return false;
27464
+ }
27465
+ }
27093
27466
  return true;
27094
27467
  });
27095
27468
  HourlyOutputChart.displayName = "HourlyOutputChart";
@@ -27109,6 +27482,7 @@ var VideoCard = React24__default.memo(({
27109
27482
  onClick,
27110
27483
  onFatalError,
27111
27484
  isVeryLowEfficiency = false,
27485
+ legend,
27112
27486
  cropping,
27113
27487
  canvasFps = 30,
27114
27488
  useRAF = true,
@@ -27120,6 +27494,7 @@ var VideoCard = React24__default.memo(({
27120
27494
  }) => {
27121
27495
  const videoRef = useRef(null);
27122
27496
  const canvasRef = useRef(null);
27497
+ const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
27123
27498
  if (cropping) {
27124
27499
  useHlsStreamWithCropping(videoRef, canvasRef, {
27125
27500
  src: hlsUrl,
@@ -27137,39 +27512,23 @@ var VideoCard = React24__default.memo(({
27137
27512
  });
27138
27513
  }
27139
27514
  const workspaceDisplayName = displayName || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
27140
- const getEfficiencyOverlayColor = (efficiency) => {
27141
- if (efficiency >= 80) {
27142
- return "bg-[#00D654]/25";
27143
- } else if (efficiency >= 70) {
27144
- return "bg-[#FFD700]/30";
27145
- } else {
27146
- return "bg-[#FF2D0A]/30";
27147
- }
27148
- };
27149
- const getEfficiencyBarColor = (efficiency) => {
27150
- if (efficiency >= 80) {
27151
- return "bg-[#00AB45]";
27152
- } else if (efficiency >= 70) {
27153
- return "bg-[#FFB020]";
27154
- } else {
27155
- return "bg-[#E34329]";
27156
- }
27157
- };
27158
- const efficiencyOverlayClass = getEfficiencyOverlayColor(workspace.efficiency);
27159
- const efficiencyBarClass = getEfficiencyBarColor(workspace.efficiency);
27515
+ const efficiencyColor = getEfficiencyColor(workspace.efficiency, effectiveLegend);
27516
+ const efficiencyOverlayClass = efficiencyColor === "green" ? "bg-[#00D654]/25" : efficiencyColor === "yellow" ? "bg-[#FFD700]/30" : "bg-[#FF2D0A]/30";
27517
+ const efficiencyBarClass = efficiencyColor === "green" ? "bg-[#00AB45]" : efficiencyColor === "yellow" ? "bg-[#FFB020]" : "bg-[#E34329]";
27518
+ const efficiencyStatus = efficiencyColor === "green" ? "High" : efficiencyColor === "yellow" ? "Medium" : "Low";
27160
27519
  const trendInfo = workspace.trend !== void 0 ? getTrendArrowAndColor(workspace.trend) : null;
27161
27520
  const handleClick = useCallback(() => {
27162
27521
  trackCoreEvent("Workspace Card Clicked", {
27163
27522
  workspace_id: workspace.workspace_uuid,
27164
27523
  workspace_name: workspace.workspace_name,
27165
27524
  efficiency: workspace.efficiency,
27166
- status: workspace.efficiency >= 80 ? "High" : workspace.efficiency >= 70 ? "Medium" : "Low",
27525
+ status: efficiencyStatus,
27167
27526
  trend: workspace.trend
27168
27527
  });
27169
27528
  if (onClick) {
27170
27529
  onClick();
27171
27530
  }
27172
- }, [onClick, workspace]);
27531
+ }, [onClick, workspace, efficiencyStatus]);
27173
27532
  return /* @__PURE__ */ jsxs(
27174
27533
  "div",
27175
27534
  {
@@ -27272,6 +27631,7 @@ var VideoGridView = React24__default.memo(({
27272
27631
  workspaces,
27273
27632
  selectedLine,
27274
27633
  className = "",
27634
+ legend,
27275
27635
  videoSources = {},
27276
27636
  displayNames = {},
27277
27637
  onWorkspaceHover,
@@ -27287,6 +27647,7 @@ var VideoGridView = React24__default.memo(({
27287
27647
  const videoConfig = useVideoConfig();
27288
27648
  const dashboardConfig = useDashboardConfig();
27289
27649
  const { cropping, canvasConfig, hlsUrls } = videoConfig;
27650
+ const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
27290
27651
  const mergedVideoSources = {
27291
27652
  defaultHlsUrl: videoSources.defaultHlsUrl || hlsUrls?.defaultHlsUrl || DEFAULT_HLS_URL,
27292
27653
  workspaceHlsUrls: { ...videoSources.workspaceHlsUrls, ...hlsUrls?.workspaceHlsUrls },
@@ -27481,6 +27842,7 @@ var VideoGridView = React24__default.memo(({
27481
27842
  onClick: () => handleWorkspaceClick(workspace),
27482
27843
  onFatalError: () => handleStreamError(workspaceId),
27483
27844
  isVeryLowEfficiency,
27845
+ legend: effectiveLegend,
27484
27846
  cropping: workspaceCropping,
27485
27847
  canvasFps: canvasConfig?.fps,
27486
27848
  displayName: (
@@ -27508,10 +27870,12 @@ var MapGridView = React24__default.memo(({
27508
27870
  className = "",
27509
27871
  displayNames = {},
27510
27872
  onWorkspaceHover,
27511
- onWorkspaceHoverEnd
27873
+ onWorkspaceHoverEnd,
27874
+ legend
27512
27875
  }) => {
27513
27876
  const router = useRouter();
27514
27877
  const dashboardConfig = useDashboardConfig();
27878
+ const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
27515
27879
  const mapViewConfig = dashboardConfig?.mapViewConfig;
27516
27880
  const workspacePositions = mapViewConfig?.workspacePositions || [];
27517
27881
  const aspectRatio2 = mapViewConfig?.aspectRatio || 16 / 9;
@@ -27523,10 +27887,11 @@ var MapGridView = React24__default.memo(({
27523
27887
  return map;
27524
27888
  }, [workspaces]);
27525
27889
  const getPerformanceColor = useCallback((efficiency) => {
27526
- if (efficiency >= 80) return "border-green-500 bg-green-50";
27527
- if (efficiency >= 50) return "border-yellow-500 bg-yellow-50";
27890
+ const color2 = getEfficiencyColor(efficiency, effectiveLegend);
27891
+ if (color2 === "green") return "border-green-500 bg-green-50";
27892
+ if (color2 === "yellow") return "border-yellow-500 bg-yellow-50";
27528
27893
  return "border-red-500 bg-red-50";
27529
- }, []);
27894
+ }, [effectiveLegend]);
27530
27895
  const handleWorkspaceClick = useCallback((workspace) => {
27531
27896
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
27532
27897
  trackCoreEvent("Workspace Detail Clicked", {
@@ -27566,7 +27931,7 @@ var MapGridView = React24__default.memo(({
27566
27931
  const workspace = workspaceMetricsMap.get(wsKey);
27567
27932
  if (!workspace) return null;
27568
27933
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
27569
- getPerformanceColor(workspace.efficiency);
27934
+ const performanceColor = getPerformanceColor(workspace.efficiency);
27570
27935
  const showExclamation = workspace.show_exclamation ?? (workspace.efficiency < 50 && workspace.efficiency >= 10);
27571
27936
  const workspaceDisplayName = displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || // Always pass line_id to fallback to ensure correct mapping per line
27572
27937
  getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
@@ -27591,7 +27956,7 @@ var MapGridView = React24__default.memo(({
27591
27956
  className: `
27592
27957
  relative rounded-lg border-2 shadow-lg hover:shadow-xl
27593
27958
  transition-all duration-200 hover:scale-105 bg-white
27594
- ${workspace.efficiency >= 80 ? "border-green-500" : workspace.efficiency >= 50 ? "border-yellow-500" : "border-red-500"}
27959
+ ${performanceColor}
27595
27960
  ${position.size === "conveyor" ? "w-32 h-24" : position.size === "large" ? "w-40 h-32" : "w-36 h-28"}
27596
27961
  `,
27597
27962
  children: [
@@ -28172,7 +28537,7 @@ var PieChart4 = ({
28172
28537
  }
28173
28538
  );
28174
28539
  };
28175
- const CustomTooltip3 = ({ active, payload }) => {
28540
+ const CustomTooltip4 = ({ active, payload }) => {
28176
28541
  if (active && payload && payload.length) {
28177
28542
  const data2 = payload[0];
28178
28543
  return /* @__PURE__ */ jsxs("div", { className: "bg-white px-3 py-2 shadow-lg rounded-lg border border-gray-200", children: [
@@ -28203,7 +28568,7 @@ var PieChart4 = ({
28203
28568
  children: dataWithPercentage.map((entry, index) => /* @__PURE__ */ jsx(Cell, { fill: colors[index % colors.length] }, `cell-${index}`))
28204
28569
  }
28205
28570
  ),
28206
- /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip3, {}) }),
28571
+ /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip4, {}) }),
28207
28572
  /* @__PURE__ */ jsx(
28208
28573
  Legend,
28209
28574
  {
@@ -28415,31 +28780,6 @@ var WhatsAppShareButton = ({
28415
28780
  }
28416
28781
  );
28417
28782
  };
28418
- var AxelOrb = ({
28419
- className = "",
28420
- size = "md",
28421
- animate = false
28422
- }) => {
28423
- const sizeClasses = {
28424
- sm: "w-8 h-8",
28425
- md: "w-10 h-10",
28426
- lg: "w-12 h-12",
28427
- xl: "w-16 h-16",
28428
- "2xl": "w-20 h-20"
28429
- };
28430
- return /* @__PURE__ */ jsx(
28431
- "div",
28432
- {
28433
- className: `${sizeClasses[size]} rounded-full ${animate ? "animate-float" : ""} ${className}`,
28434
- style: {
28435
- background: "linear-gradient(to top, #078DDB 0%, #65ADD6 33%, #A3D0E6 66%, #C7E2EC 100%)",
28436
- boxShadow: "0 4px 12px rgba(7, 141, 219, 0.4), 0 0 20px rgba(7, 141, 219, 0.2)"
28437
- },
28438
- "aria-label": "Axel AI",
28439
- role: "img"
28440
- }
28441
- );
28442
- };
28443
28783
  var BreakNotificationPopup = ({
28444
28784
  activeBreaks,
28445
28785
  onDismiss,
@@ -28527,7 +28867,7 @@ var BreakNotificationPopup = ({
28527
28867
  className: "relative z-10",
28528
28868
  children: /* @__PURE__ */ jsx("div", { className: "bg-white text-gray-900 rounded-lg border border-gray-200 shadow-xl overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "p-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
28529
28869
  /* @__PURE__ */ jsxs("div", { className: "flex items-start space-x-3 flex-1", children: [
28530
- /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(AxelOrb, { size: "md" }) }),
28870
+ /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx("div", { className: "w-10 h-10 rounded-full bg-amber-100 flex items-center justify-center", children: /* @__PURE__ */ jsx(Coffee, { className: "w-5 h-5 text-amber-600" }) }) }),
28531
28871
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
28532
28872
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [
28533
28873
  /* @__PURE__ */ jsxs("h4", { className: "font-semibold text-sm text-gray-900", children: [
@@ -28800,7 +29140,11 @@ var AxelNotificationPopup = ({
28800
29140
  className: "p-3",
28801
29141
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
28802
29142
  /* @__PURE__ */ jsxs("div", { className: "flex items-start space-x-3 flex-1", children: [
28803
- /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(AxelOrb, { size: "md" }) }),
29143
+ /* @__PURE__ */ jsxs("div", { className: "flex-shrink-0", children: [
29144
+ suggestion.type === "improvement" && /* @__PURE__ */ jsx("div", { className: "w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center", children: /* @__PURE__ */ jsx(TrendingUp, { className: "w-5 h-5 text-blue-600" }) }),
29145
+ (suggestion.type === "alert" || suggestion.type === "bottleneck_triage") && /* @__PURE__ */ jsx("div", { className: "w-10 h-10 rounded-full bg-red-100 flex items-center justify-center", children: /* @__PURE__ */ jsx(AlertCircle, { className: "w-5 h-5 text-red-600" }) }),
29146
+ suggestion.type === "insight" && /* @__PURE__ */ jsx("div", { className: "w-10 h-10 rounded-full bg-purple-100 flex items-center justify-center", children: /* @__PURE__ */ jsx(Sparkles, { className: "w-5 h-5 text-purple-600" }) })
29147
+ ] }),
28804
29148
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
28805
29149
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [
28806
29150
  /* @__PURE__ */ jsx("h4", { className: "font-semibold text-sm text-gray-900", children: suggestion.title }),
@@ -29535,10 +29879,9 @@ var HlsVideoPlayer = forwardRef(({
29535
29879
  fetchSetup: function(context, initParams) {
29536
29880
  const url = context.url;
29537
29881
  if (isR2WorkerUrl(url, r2WorkerDomain) && authToken) {
29538
- initParams.headers = {
29539
- ...initParams.headers,
29540
- "Authorization": `Bearer ${authToken}`
29541
- };
29882
+ const headers = initParams.headers instanceof Headers ? initParams.headers : new Headers(initParams.headers || {});
29883
+ headers.set("Authorization", `Bearer ${authToken}`);
29884
+ initParams.headers = headers;
29542
29885
  }
29543
29886
  return new Request(url, initParams);
29544
29887
  }
@@ -32861,126 +33204,6 @@ function useClipsRealtimeUpdates({
32861
33204
  hasNewClips: newClipsNotification !== null && newClipsNotification.count > 0
32862
33205
  };
32863
33206
  }
32864
-
32865
- // src/lib/services/clipClassificationService.ts
32866
- function parseCleanLabel(rawLabel) {
32867
- if (!rawLabel) return null;
32868
- const patterns = [
32869
- /"Clip Label"\s*:\s*"([^"]+)"/,
32870
- // Old format
32871
- /"clip_label"\s*:\s*"([^"]+)"/,
32872
- // New format
32873
- /'Clip Label'\s*:\s*'([^']+)'/,
32874
- // Single quotes
32875
- /'clip_label'\s*:\s*'([^']+)'/
32876
- // Single quotes
32877
- ];
32878
- for (const pattern of patterns) {
32879
- const match = rawLabel.match(pattern);
32880
- if (match) {
32881
- const label = match[1].trim();
32882
- return label.replace(/ /g, "_");
32883
- }
32884
- }
32885
- return null;
32886
- }
32887
- async function fetchClassifications(clipIds, token) {
32888
- if (!clipIds || clipIds.length === 0) {
32889
- return {};
32890
- }
32891
- try {
32892
- const CHUNK_SIZE = 50;
32893
- const chunks = [];
32894
- for (let i = 0; i < clipIds.length; i += CHUNK_SIZE) {
32895
- chunks.push(clipIds.slice(i, i + CHUNK_SIZE));
32896
- }
32897
- console.log(`[fetchClassifications] Fetching ${clipIds.length} classifications in ${chunks.length} chunks`);
32898
- const apiBase = process.env.NEXT_PUBLIC_BACKEND_URL || "http://localhost:8000";
32899
- const chunkResults = await Promise.all(
32900
- chunks.map(async (chunkIds) => {
32901
- try {
32902
- const response = await fetch(
32903
- `${apiBase}/api/classification/batch?clip_ids=${chunkIds.join(",")}`,
32904
- {
32905
- headers: {
32906
- "Authorization": `Bearer ${token}`,
32907
- "Content-Type": "application/json"
32908
- }
32909
- }
32910
- );
32911
- if (!response.ok) {
32912
- console.error(`Classification API error for chunk: ${response.status}`);
32913
- return Object.fromEntries(
32914
- chunkIds.map((id3) => [id3, { status: "processing" }])
32915
- );
32916
- }
32917
- const data = await response.json();
32918
- return data.classifications || {};
32919
- } catch (error) {
32920
- console.error("Error fetching chunk:", error);
32921
- return Object.fromEntries(
32922
- chunkIds.map((id3) => [id3, { status: "processing" }])
32923
- );
32924
- }
32925
- })
32926
- );
32927
- const allClassifications = Object.assign({}, ...chunkResults);
32928
- console.log(`[fetchClassifications] Total fetched: ${Object.keys(allClassifications).length} classifications`);
32929
- return allClassifications;
32930
- } catch (error) {
32931
- console.error("Error fetching classifications:", error);
32932
- return Object.fromEntries(
32933
- clipIds.map((id3) => [id3, { status: "processing" }])
32934
- );
32935
- }
32936
- }
32937
- function useClassificationRealtimeUpdates({
32938
- clipIds,
32939
- enabled = true,
32940
- onClassificationUpdate
32941
- }) {
32942
- const supabase = useSupabase();
32943
- useEffect(() => {
32944
- if (!enabled || !clipIds || clipIds.length === 0 || !supabase) {
32945
- return;
32946
- }
32947
- const channelName = `classification:${clipIds.slice(0, 5).join("-")}`;
32948
- console.log(`[useClassificationRealtimeUpdates] Subscribing to channel: ${channelName}`);
32949
- const channel = supabase.channel(channelName).on(
32950
- "postgres_changes",
32951
- {
32952
- event: "INSERT",
32953
- schema: "public",
32954
- table: "clip_classification",
32955
- filter: `clip_id=in.(${clipIds.join(",")})`
32956
- },
32957
- (payload) => {
32958
- console.log("[useClassificationRealtimeUpdates] New classification:", payload);
32959
- try {
32960
- const { clip_id, clip_label, confidence_score } = payload.new;
32961
- const parsedLabel = parseCleanLabel(clip_label);
32962
- if (parsedLabel) {
32963
- onClassificationUpdate(clip_id, {
32964
- status: "classified",
32965
- label: parsedLabel,
32966
- confidence: confidence_score || 0
32967
- });
32968
- } else {
32969
- console.warn("[useClassificationRealtimeUpdates] Failed to parse label:", clip_label);
32970
- }
32971
- } catch (error) {
32972
- console.error("[useClassificationRealtimeUpdates] Error processing update:", error);
32973
- }
32974
- }
32975
- ).subscribe((status) => {
32976
- console.log(`[useClassificationRealtimeUpdates] Subscription status:`, status);
32977
- });
32978
- return () => {
32979
- console.log(`[useClassificationRealtimeUpdates] Unsubscribing from ${channelName}`);
32980
- supabase.removeChannel(channel);
32981
- };
32982
- }, [clipIds, enabled, supabase, onClassificationUpdate]);
32983
- }
32984
33207
  var BottlenecksContent = ({
32985
33208
  workspaceId,
32986
33209
  workspaceName,
@@ -36428,6 +36651,31 @@ var DetailedHealthStatus = ({
36428
36651
  }
36429
36652
  );
36430
36653
  };
36654
+ var AxelOrb = ({
36655
+ className = "",
36656
+ size = "md",
36657
+ animate = false
36658
+ }) => {
36659
+ const sizeClasses = {
36660
+ sm: "w-8 h-8",
36661
+ md: "w-10 h-10",
36662
+ lg: "w-12 h-12",
36663
+ xl: "w-16 h-16",
36664
+ "2xl": "w-20 h-20"
36665
+ };
36666
+ return /* @__PURE__ */ jsx(
36667
+ "div",
36668
+ {
36669
+ className: `${sizeClasses[size]} rounded-full ${animate ? "animate-float" : ""} ${className}`,
36670
+ style: {
36671
+ background: "linear-gradient(to top, #078DDB 0%, #65ADD6 33%, #A3D0E6 66%, #C7E2EC 100%)",
36672
+ boxShadow: "0 4px 12px rgba(7, 141, 219, 0.4), 0 0 20px rgba(7, 141, 219, 0.2)"
36673
+ },
36674
+ "aria-label": "Axel AI",
36675
+ role: "img"
36676
+ }
36677
+ );
36678
+ };
36431
36679
  var LinePdfExportButton = ({
36432
36680
  targetElement,
36433
36681
  fileName = "line-export",
@@ -36545,10 +36793,10 @@ var LineHistoryCalendar = ({
36545
36793
  }, [configuredTimezone]);
36546
36794
  const monthBounds = useMemo(() => getMonthKeyBounds(year, month), [year, month]);
36547
36795
  const calendarData = useMemo(() => {
36548
- const startOfMonth = toZonedTime(new Date(year, month, 1), configuredTimezone);
36549
- const endOfMonth = toZonedTime(new Date(year, month + 1, 0), configuredTimezone);
36550
- const totalDays = endOfMonth.getDate();
36551
- let startOffset = startOfMonth.getDay() - 1;
36796
+ const startOfMonth2 = toZonedTime(new Date(year, month, 1), configuredTimezone);
36797
+ const endOfMonth2 = toZonedTime(new Date(year, month + 1, 0), configuredTimezone);
36798
+ const totalDays = endOfMonth2.getDate();
36799
+ let startOffset = startOfMonth2.getDay() - 1;
36552
36800
  if (startOffset === -1) startOffset = 6;
36553
36801
  const calendar = Array(startOffset).fill(null);
36554
36802
  for (let day = 1; day <= totalDays; day++) {
@@ -36579,12 +36827,13 @@ var LineHistoryCalendar = ({
36579
36827
  if (shift.hasData !== void 0) return shift.hasData;
36580
36828
  return shift.total_workspaces > 0 || shift.avg_efficiency > 0 || shift.underperforming_workspaces > 0;
36581
36829
  };
36582
- const getPerformanceColor = (efficiency, date, hasData) => {
36830
+ const getPerformanceColor = (efficiency, date, hasData, isFilteredOut = false) => {
36583
36831
  const istNow = todayInZone;
36584
36832
  const nowString = `${istNow.getFullYear()}-${String(istNow.getMonth() + 1).padStart(2, "0")}-${String(istNow.getDate()).padStart(2, "0")}`;
36585
36833
  const dateString = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
36586
36834
  if (dateString > nowString) return "bg-gray-200 dark:bg-gray-700";
36587
36835
  if (!hasData) return "bg-gray-300 dark:bg-gray-600";
36836
+ if (isFilteredOut) return "bg-gray-200 dark:bg-gray-700";
36588
36837
  return efficiency >= 75 ? "bg-green-500 dark:bg-green-600" : "bg-red-500 dark:bg-red-600";
36589
36838
  };
36590
36839
  const isCurrentDate = (date) => {
@@ -36629,12 +36878,13 @@ var LineHistoryCalendar = ({
36629
36878
  const showRange = rangeStart && rangeEnd ? !(rangeStart === monthBounds.startKey && rangeEnd === monthBounds.endKey) : false;
36630
36879
  const inRange = showRange ? dateKey >= rangeStart && dateKey <= rangeEnd : false;
36631
36880
  const isRangeEdge = inRange && (dateKey === rangeStart || dateKey === rangeEnd);
36881
+ const isFilteredOut = showRange && !inRange;
36632
36882
  return /* @__PURE__ */ jsx(
36633
36883
  "div",
36634
36884
  {
36635
- className: `group h-full ${isFuture || !hasData ? "cursor-not-allowed" : "cursor-pointer hover:opacity-90"}`,
36885
+ className: `group h-full ${isFuture || !hasData || isFilteredOut ? "cursor-not-allowed" : "cursor-pointer hover:opacity-90"}`,
36636
36886
  onClick: () => {
36637
- if (!isFuture && hasData) {
36887
+ if (!isFuture && hasData && !isFilteredOut) {
36638
36888
  const dateObj2 = day.date instanceof Date ? day.date : new Date(day.date);
36639
36889
  const year2 = dateObj2.getFullYear();
36640
36890
  const month2 = String(dateObj2.getMonth() + 1).padStart(2, "0");
@@ -36666,7 +36916,7 @@ var LineHistoryCalendar = ({
36666
36916
  }
36667
36917
  },
36668
36918
  children: /* @__PURE__ */ jsxs("div", { className: `
36669
- ${getPerformanceColor(shiftData.avg_efficiency || 0, dateObj, hasData)}
36919
+ ${getPerformanceColor(shiftData.avg_efficiency || 0, dateObj, hasData, isFilteredOut)}
36670
36920
  rounded-lg h-full p-2 relative
36671
36921
  ${isToday2 ? "ring-2 ring-blue-500 dark:ring-blue-400 ring-offset-2 dark:ring-offset-gray-800 shadow-md" : ""}
36672
36922
  `, children: [
@@ -36677,10 +36927,11 @@ var LineHistoryCalendar = ({
36677
36927
  }
36678
36928
  ),
36679
36929
  /* @__PURE__ */ jsx("div", { className: `
36680
- text-base font-medium ${hasData ? "text-white" : "text-gray-500"} flex items-center relative z-10
36930
+ text-base font-medium flex items-center relative z-10
36931
+ ${hasData && !isFuture && !isFilteredOut ? "text-white" : "text-gray-400"}
36681
36932
  ${isToday2 ? "bg-blue-500 dark:bg-blue-600 rounded-full w-7 h-7 justify-center" : ""}
36682
36933
  `, children: dateObj.getDate() }),
36683
- !isFuture && hasData && renderStats(shiftData, dateObj)
36934
+ !isFuture && hasData && !isFilteredOut && renderStats(shiftData, dateObj)
36684
36935
  ] })
36685
36936
  }
36686
36937
  );
@@ -36855,54 +37106,19 @@ var IdleTimeReasonChart = ({
36855
37106
  ] });
36856
37107
  };
36857
37108
  var IdleTimeReasonChart_default = IdleTimeReasonChart;
37109
+ var WEEKDAYS2 = ["S", "M", "T", "W", "T", "F", "S"];
36858
37110
  var MonthlyRangeFilter = ({
36859
37111
  month,
36860
37112
  year,
36861
- timezone,
36862
37113
  value,
36863
37114
  onChange,
37115
+ onMonthNavigate,
36864
37116
  className
36865
37117
  }) => {
36866
37118
  const monthBounds = useMemo(() => getMonthKeyBounds(year, month), [year, month]);
36867
- const weekRanges = useMemo(() => getMonthWeekRanges(year, month, timezone), [year, month, timezone]);
36868
37119
  const normalizedRange = useMemo(() => {
36869
37120
  return normalizeDateKeyRange(value.startKey, value.endKey, monthBounds.startKey, monthBounds.endKey);
36870
37121
  }, [value.startKey, value.endKey, monthBounds.startKey, monthBounds.endKey]);
36871
- const monthLabel = useMemo(() => {
36872
- return new Date(year, month).toLocaleString("default", { month: "long", year: "numeric" });
36873
- }, [year, month]);
36874
- const presetOptions = useMemo(() => {
36875
- const fullMonthRangeLabel = `${formatDateKeyForDisplay(monthBounds.startKey, "MMM d")} - ${formatDateKeyForDisplay(monthBounds.endKey, "MMM d")}`;
36876
- const presets = [
36877
- {
36878
- id: "full-month",
36879
- label: "Full month",
36880
- rangeLabel: fullMonthRangeLabel,
36881
- startKey: monthBounds.startKey,
36882
- endKey: monthBounds.endKey
36883
- }
36884
- ];
36885
- weekRanges.forEach((range, index) => {
36886
- const rangeLabel = `${formatDateKeyForDisplay(range.startKey, "MMM d")} - ${formatDateKeyForDisplay(range.endKey, "MMM d")}`;
36887
- presets.push({
36888
- id: `week-${index + 1}`,
36889
- label: `Week ${index + 1}`,
36890
- rangeLabel,
36891
- startKey: range.startKey,
36892
- endKey: range.endKey
36893
- });
36894
- });
36895
- return presets;
36896
- }, [monthBounds.startKey, monthBounds.endKey, weekRanges]);
36897
- const activePreset = useMemo(() => {
36898
- return presetOptions.find(
36899
- (preset) => preset.startKey === normalizedRange.startKey && preset.endKey === normalizedRange.endKey
36900
- );
36901
- }, [presetOptions, normalizedRange.startKey, normalizedRange.endKey]);
36902
- const displayLabel = useMemo(() => {
36903
- if (activePreset) return activePreset.label;
36904
- return "Custom range";
36905
- }, [activePreset]);
36906
37122
  const fullMonthLabel = useMemo(() => {
36907
37123
  const startLabel = formatDateKeyForDisplay(monthBounds.startKey, "MMM d");
36908
37124
  const endLabel = formatDateKeyForDisplay(monthBounds.endKey, "MMM d, yyyy");
@@ -36911,14 +37127,26 @@ var MonthlyRangeFilter = ({
36911
37127
  const displayRange = useMemo(() => {
36912
37128
  return formatRangeLabel(normalizedRange, fullMonthLabel);
36913
37129
  }, [normalizedRange, fullMonthLabel]);
36914
- const [customStart, setCustomStart] = useState(normalizedRange.startKey);
36915
- const [customEnd, setCustomEnd] = useState(normalizedRange.endKey);
36916
37130
  const [isOpen, setIsOpen] = useState(false);
36917
37131
  const dropdownRef = useRef(null);
37132
+ const [calendarMonth, setCalendarMonth] = useState(month);
37133
+ const [calendarYear, setCalendarYear] = useState(year);
37134
+ const [rangeStart, setRangeStart] = useState(parseDateKeyToDate(normalizedRange.startKey));
37135
+ const [rangeEnd, setRangeEnd] = useState(parseDateKeyToDate(normalizedRange.endKey));
37136
+ const [selecting, setSelecting] = useState(false);
36918
37137
  useEffect(() => {
36919
- setCustomStart(normalizedRange.startKey);
36920
- setCustomEnd(normalizedRange.endKey);
36921
- }, [normalizedRange.startKey, normalizedRange.endKey]);
37138
+ setCalendarMonth(month);
37139
+ setCalendarYear(year);
37140
+ }, [month, year]);
37141
+ useEffect(() => {
37142
+ if (isOpen) {
37143
+ setRangeStart(parseDateKeyToDate(normalizedRange.startKey));
37144
+ setRangeEnd(parseDateKeyToDate(normalizedRange.endKey));
37145
+ setSelecting(false);
37146
+ setCalendarMonth(month);
37147
+ setCalendarYear(year);
37148
+ }
37149
+ }, [isOpen, normalizedRange.startKey, normalizedRange.endKey, month, year]);
36922
37150
  useEffect(() => {
36923
37151
  const handleClickOutside = (event) => {
36924
37152
  if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
@@ -36928,107 +37156,211 @@ var MonthlyRangeFilter = ({
36928
37156
  document.addEventListener("mousedown", handleClickOutside);
36929
37157
  return () => document.removeEventListener("mousedown", handleClickOutside);
36930
37158
  }, []);
36931
- const handlePresetSelect = (startKey, endKey) => {
36932
- const normalized = normalizeDateKeyRange(startKey, endKey, monthBounds.startKey, monthBounds.endKey);
36933
- onChange(normalized);
36934
- setIsOpen(false);
37159
+ const handleDayClick = (day) => {
37160
+ if (!selecting || !rangeStart) {
37161
+ setRangeStart(day);
37162
+ setRangeEnd(null);
37163
+ setSelecting(true);
37164
+ } else {
37165
+ if (day < rangeStart) {
37166
+ setRangeEnd(rangeStart);
37167
+ setRangeStart(day);
37168
+ } else {
37169
+ setRangeEnd(day);
37170
+ }
37171
+ setSelecting(false);
37172
+ }
36935
37173
  };
36936
- const handleCustomApply = () => {
36937
- if (!customStart || !customEnd) return;
36938
- const normalized = normalizeDateKeyRange(customStart, customEnd, monthBounds.startKey, monthBounds.endKey);
36939
- onChange(normalized);
36940
- setIsOpen(false);
37174
+ const handleApply = () => {
37175
+ if (rangeStart) {
37176
+ const endDate = rangeEnd || rangeStart;
37177
+ const startKey = format(rangeStart, "yyyy-MM-dd");
37178
+ const endKey = format(endDate, "yyyy-MM-dd");
37179
+ const normalized = normalizeDateKeyRange(startKey, endKey, monthBounds.startKey, monthBounds.endKey);
37180
+ onChange(normalized);
37181
+ setIsOpen(false);
37182
+ }
37183
+ };
37184
+ const handlePreviousMonth = () => {
37185
+ const prevMonthDate = subMonths(new Date(calendarYear, calendarMonth), 1);
37186
+ const newMonth = prevMonthDate.getMonth();
37187
+ const newYear = prevMonthDate.getFullYear();
37188
+ if (newYear < 2023) return;
37189
+ setCalendarMonth(newMonth);
37190
+ setCalendarYear(newYear);
37191
+ onMonthNavigate?.(newMonth, newYear);
36941
37192
  };
37193
+ const handleNextMonth = () => {
37194
+ const nextMonthDate = addMonths(new Date(calendarYear, calendarMonth), 1);
37195
+ const newMonth = nextMonthDate.getMonth();
37196
+ const newYear = nextMonthDate.getFullYear();
37197
+ const currentDate = /* @__PURE__ */ new Date();
37198
+ if (newYear > currentDate.getFullYear() || newYear === currentDate.getFullYear() && newMonth > currentDate.getMonth()) {
37199
+ return;
37200
+ }
37201
+ setCalendarMonth(newMonth);
37202
+ setCalendarYear(newYear);
37203
+ onMonthNavigate?.(newMonth, newYear);
37204
+ };
37205
+ const canGoPrevious = !(calendarYear === 2023 && calendarMonth === 0);
37206
+ const canGoNext = !(calendarYear === (/* @__PURE__ */ new Date()).getFullYear() && calendarMonth === (/* @__PURE__ */ new Date()).getMonth());
37207
+ const monthDate = useMemo(() => new Date(calendarYear, calendarMonth), [calendarYear, calendarMonth]);
37208
+ const calendarDays = useMemo(() => {
37209
+ const start = startOfMonth(monthDate);
37210
+ const end = endOfMonth(monthDate);
37211
+ const days = eachDayOfInterval({ start, end });
37212
+ const startDayOfWeek = getDay(start);
37213
+ const emptySlots = Array(startDayOfWeek).fill(null);
37214
+ return [...emptySlots, ...days];
37215
+ }, [monthDate]);
37216
+ const isInRange = useCallback((day) => {
37217
+ if (!rangeStart) return false;
37218
+ if (!rangeEnd) return isSameDay(day, rangeStart);
37219
+ return isWithinInterval(day, {
37220
+ start: startOfDay(rangeStart),
37221
+ end: startOfDay(rangeEnd)
37222
+ });
37223
+ }, [rangeStart, rangeEnd]);
37224
+ const isRangeStart = useCallback((day) => {
37225
+ return rangeStart && isSameDay(day, rangeStart);
37226
+ }, [rangeStart]);
37227
+ const isRangeEnd = useCallback((day) => {
37228
+ return rangeEnd && isSameDay(day, rangeEnd);
37229
+ }, [rangeEnd]);
37230
+ const monthName = format(monthDate, "MMMM yyyy");
37231
+ const startDisplay = rangeStart ? format(rangeStart, "MMM d, yyyy") : "Start Date";
37232
+ const endDisplay = rangeEnd ? format(rangeEnd, "MMM d, yyyy") : "End Date";
36942
37233
  return /* @__PURE__ */ jsxs("div", { className: `relative ${className ?? ""}`, ref: dropdownRef, children: [
36943
37234
  /* @__PURE__ */ jsxs(
36944
37235
  "button",
36945
37236
  {
36946
37237
  type: "button",
36947
37238
  onClick: () => setIsOpen((prev) => !prev),
36948
- 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",
37239
+ className: clsx(
37240
+ "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",
37241
+ isOpen ? "border-blue-500 ring-2 ring-blue-500/20" : "border-gray-200 hover:border-gray-300 hover:shadow"
37242
+ ),
36949
37243
  children: [
36950
- /* @__PURE__ */ jsx(CalendarIcon, { className: "h-4 w-4 text-slate-500" }),
36951
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
36952
- /* @__PURE__ */ jsx("span", { className: "text-[11px] uppercase tracking-[0.2em] text-slate-500", children: displayLabel }),
36953
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-slate-900", children: displayRange })
36954
- ] }),
37244
+ /* @__PURE__ */ jsx(CalendarIcon, { className: "h-4 w-4 text-gray-400" }),
37245
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-900", children: displayRange }),
36955
37246
  /* @__PURE__ */ jsx(
36956
37247
  ChevronDownIcon,
36957
37248
  {
36958
- className: `ml-2 h-4 w-4 text-slate-400 transition-transform ${isOpen ? "rotate-180" : ""}`
37249
+ className: clsx(
37250
+ "ml-2 h-4 w-4 text-gray-400 transition-transform duration-200",
37251
+ isOpen && "rotate-180"
37252
+ )
36959
37253
  }
36960
37254
  )
36961
37255
  ]
36962
37256
  }
36963
37257
  ),
36964
- isOpen && /* @__PURE__ */ jsx("div", { className: "absolute right-0 z-50 mt-2 w-80 rounded-lg border border-slate-200 bg-white shadow-lg", children: /* @__PURE__ */ jsxs("div", { className: "p-3", children: [
36965
- /* @__PURE__ */ jsx("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-500", children: "Weeks" }),
36966
- /* @__PURE__ */ jsx("div", { className: "mt-2 space-y-1", children: presetOptions.map((preset) => {
36967
- const isActive = preset.startKey === normalizedRange.startKey && preset.endKey === normalizedRange.endKey;
36968
- return /* @__PURE__ */ jsxs(
37258
+ isOpen && /* @__PURE__ */ 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: [
37259
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-4 pb-4 border-b border-gray-100", children: [
37260
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
37261
+ /* @__PURE__ */ jsx("label", { className: "block text-[10px] uppercase tracking-wider font-medium text-gray-500 mb-1", children: "Start" }),
37262
+ /* @__PURE__ */ jsx("div", { className: clsx(
37263
+ "px-2 py-1.5 rounded-md text-sm font-medium border transition-colors",
37264
+ rangeStart ? "bg-blue-50 border-blue-200 text-blue-700" : "bg-gray-50 border-gray-100 text-gray-400"
37265
+ ), children: startDisplay })
37266
+ ] }),
37267
+ /* @__PURE__ */ jsx("div", { className: "pt-4 text-gray-300", children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "h-4 w-4" }) }),
37268
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
37269
+ /* @__PURE__ */ jsx("label", { className: "block text-[10px] uppercase tracking-wider font-medium text-gray-500 mb-1", children: "End" }),
37270
+ /* @__PURE__ */ jsx("div", { className: clsx(
37271
+ "px-2 py-1.5 rounded-md text-sm font-medium border transition-colors",
37272
+ rangeEnd ? "bg-blue-50 border-blue-200 text-blue-700" : "bg-gray-50 border-gray-100 text-gray-400"
37273
+ ), children: endDisplay })
37274
+ ] })
37275
+ ] }),
37276
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
37277
+ /* @__PURE__ */ jsx(
36969
37278
  "button",
36970
37279
  {
36971
37280
  type: "button",
36972
- onClick: () => handlePresetSelect(preset.startKey, preset.endKey),
36973
- 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"}`,
36974
- children: [
36975
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: preset.label }),
36976
- /* @__PURE__ */ jsx("span", { className: "text-xs text-slate-500", children: preset.rangeLabel })
36977
- ]
37281
+ onClick: handlePreviousMonth,
37282
+ disabled: !canGoPrevious,
37283
+ className: clsx(
37284
+ "p-1 rounded-md transition-colors",
37285
+ canGoPrevious ? "hover:bg-gray-100 text-gray-600" : "text-gray-300 cursor-not-allowed"
37286
+ ),
37287
+ "aria-label": "Previous month",
37288
+ children: /* @__PURE__ */ jsx(ChevronLeftIcon, { className: "h-5 w-5" })
37289
+ }
37290
+ ),
37291
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-gray-900", children: monthName }),
37292
+ /* @__PURE__ */ jsx(
37293
+ "button",
37294
+ {
37295
+ type: "button",
37296
+ onClick: handleNextMonth,
37297
+ disabled: !canGoNext,
37298
+ className: clsx(
37299
+ "p-1 rounded-md transition-colors",
37300
+ canGoNext ? "hover:bg-gray-100 text-gray-600" : "text-gray-300 cursor-not-allowed"
37301
+ ),
37302
+ "aria-label": "Next month",
37303
+ children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "h-5 w-5" })
37304
+ }
37305
+ )
37306
+ ] }),
37307
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-1 mb-2", children: WEEKDAYS2.map((day, i) => /* @__PURE__ */ jsx("div", { className: "h-8 flex items-center justify-center text-xs font-medium text-gray-400", children: day }, i)) }),
37308
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-0", children: calendarDays.map((day, i) => {
37309
+ if (!day) {
37310
+ return /* @__PURE__ */ jsx("div", { className: "h-10" }, `empty-${i}`);
37311
+ }
37312
+ const inRange = isInRange(day);
37313
+ const isStart = isRangeStart(day);
37314
+ const isEnd = isRangeEnd(day);
37315
+ const isSingleDaySelection = isStart && (!rangeEnd || rangeEnd && isSameDay(rangeStart, rangeEnd));
37316
+ const hasSelection = rangeStart !== null;
37317
+ const dayNum = day.getDate();
37318
+ return /* @__PURE__ */ jsx(
37319
+ "button",
37320
+ {
37321
+ type: "button",
37322
+ onClick: () => handleDayClick(day),
37323
+ className: clsx(
37324
+ "h-10 w-full flex items-center justify-center text-sm font-medium transition-all duration-150 relative",
37325
+ // Not in range - fade when there's a selection
37326
+ !inRange && hasSelection && "text-gray-300 hover:text-gray-500 hover:bg-gray-50 rounded-md",
37327
+ !inRange && !hasSelection && "text-gray-700 hover:bg-gray-100 rounded-md",
37328
+ // Middle of range - subtle highlight
37329
+ inRange && !isStart && !isEnd && "bg-blue-50 text-blue-600",
37330
+ // Start of range
37331
+ isStart && !isSingleDaySelection && "bg-blue-600 text-white rounded-l-md shadow-sm z-10",
37332
+ // End of range
37333
+ isEnd && !isSingleDaySelection && "bg-blue-600 text-white rounded-r-md shadow-sm z-10",
37334
+ // Single day selection
37335
+ isSingleDaySelection && "bg-blue-600 text-white rounded-md shadow-sm z-10"
37336
+ ),
37337
+ children: dayNum
36978
37338
  },
36979
- preset.id
37339
+ day.toISOString()
36980
37340
  );
36981
37341
  }) }),
36982
- /* @__PURE__ */ jsxs("div", { className: "mt-3 border-t border-slate-200 pt-3", children: [
36983
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
36984
- /* @__PURE__ */ jsx("div", { className: "text-[11px] uppercase tracking-[0.18em] text-slate-500", children: "Custom range" }),
36985
- !activePreset && /* @__PURE__ */ 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" })
36986
- ] }),
36987
- /* @__PURE__ */ jsxs("div", { className: "mt-2 grid grid-cols-1 gap-2 sm:grid-cols-2", children: [
36988
- /* @__PURE__ */ jsxs("div", { children: [
36989
- /* @__PURE__ */ jsx("label", { className: "block text-[11px] font-medium text-slate-600", children: "Start" }),
36990
- /* @__PURE__ */ jsx(
36991
- "input",
36992
- {
36993
- type: "date",
36994
- value: customStart,
36995
- onChange: (event) => setCustomStart(event.target.value),
36996
- min: monthBounds.startKey,
36997
- max: monthBounds.endKey,
36998
- 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"
36999
- }
37000
- )
37001
- ] }),
37002
- /* @__PURE__ */ jsxs("div", { children: [
37003
- /* @__PURE__ */ jsx("label", { className: "block text-[11px] font-medium text-slate-600", children: "End" }),
37004
- /* @__PURE__ */ jsx(
37005
- "input",
37006
- {
37007
- type: "date",
37008
- value: customEnd,
37009
- onChange: (event) => setCustomEnd(event.target.value),
37010
- min: monthBounds.startKey,
37011
- max: monthBounds.endKey,
37012
- 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"
37013
- }
37014
- )
37015
- ] })
37016
- ] }),
37342
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2 mt-4 pt-4 border-t border-gray-100", children: [
37343
+ /* @__PURE__ */ jsx(
37344
+ "button",
37345
+ {
37346
+ type: "button",
37347
+ onClick: () => setIsOpen(false),
37348
+ 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",
37349
+ children: "Cancel"
37350
+ }
37351
+ ),
37017
37352
  /* @__PURE__ */ jsx(
37018
37353
  "button",
37019
37354
  {
37020
37355
  type: "button",
37021
- onClick: handleCustomApply,
37022
- 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",
37023
- children: "Apply range"
37356
+ onClick: handleApply,
37357
+ disabled: !rangeStart,
37358
+ 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",
37359
+ children: "Apply"
37024
37360
  }
37025
37361
  )
37026
- ] }),
37027
- /* @__PURE__ */ jsxs("div", { className: "mt-3 text-xs text-slate-400", children: [
37028
- "Month: ",
37029
- monthLabel
37030
37362
  ] })
37031
- ] }) })
37363
+ ] })
37032
37364
  ] });
37033
37365
  };
37034
37366
  var MonthlyRangeFilter_default = MonthlyRangeFilter;
@@ -37036,7 +37368,36 @@ var DEFAULT_PERFORMANCE_DATA = {
37036
37368
  avg_efficiency: 0,
37037
37369
  underperforming_workspaces: 0,
37038
37370
  total_workspaces: 0,
37039
- hasData: false
37371
+ hasData: false,
37372
+ output: 0,
37373
+ idealOutput: 0
37374
+ };
37375
+ var getOrdinal = (n) => {
37376
+ const suffix = ["th", "st", "nd", "rd"];
37377
+ const v = n % 100;
37378
+ return n + (suffix[(v - 20) % 10] || suffix[v] || suffix[0]);
37379
+ };
37380
+ var CustomTooltip2 = ({ active, payload, label }) => {
37381
+ if (active && payload && payload.length) {
37382
+ return /* @__PURE__ */ jsxs("div", { className: "bg-white/95 backdrop-blur-sm p-3 rounded-lg shadow-lg border border-gray-100", children: [
37383
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-gray-800 mb-1", children: label }),
37384
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
37385
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-600", children: [
37386
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Actual:" }),
37387
+ " ",
37388
+ Math.round(payload[0].value),
37389
+ " units"
37390
+ ] }),
37391
+ payload[0].payload.idealOutput > 0 && /* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-600", children: [
37392
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: "Target:" }),
37393
+ " ",
37394
+ Math.round(payload[0].payload.idealOutput),
37395
+ " units"
37396
+ ] })
37397
+ ] })
37398
+ ] });
37399
+ }
37400
+ return null;
37040
37401
  };
37041
37402
  var getShiftData2 = (day, shiftId) => {
37042
37403
  const shift = day.shifts[shiftId];
@@ -37075,7 +37436,7 @@ var LineMonthlyHistory = ({
37075
37436
  }, [rangeStart, rangeEnd, monthBounds.startKey, monthBounds.endKey]);
37076
37437
  const isFullRange = useMemo(() => isFullMonthRange(normalizedRange, year, month), [normalizedRange, year, month]);
37077
37438
  const monthLabel = useMemo(() => new Date(year, month).toLocaleString("default", { month: "long" }), [year, month]);
37078
- const rangeLabel = useMemo(() => {
37439
+ useMemo(() => {
37079
37440
  return isFullRange ? monthLabel : formatRangeLabel(normalizedRange, monthLabel);
37080
37441
  }, [isFullRange, normalizedRange, monthLabel]);
37081
37442
  const analysisMonthlyData = useMemo(() => {
@@ -37097,15 +37458,6 @@ var LineMonthlyHistory = ({
37097
37458
  shiftId: selectedShiftId,
37098
37459
  enabled: !!lineId
37099
37460
  });
37100
- if (isLoading) {
37101
- return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-[calc(100vh-10rem)]", children: /* @__PURE__ */ jsx(
37102
- OptifyeLogoLoader_default,
37103
- {
37104
- size: "lg",
37105
- message: "Loading monthly performance data..."
37106
- }
37107
- ) });
37108
- }
37109
37461
  const averages = (analysisMonthlyData || []).reduce(
37110
37462
  (acc, day) => {
37111
37463
  const shiftData = getShiftData2(day, selectedShiftId);
@@ -37124,6 +37476,88 @@ var LineMonthlyHistory = ({
37124
37476
  const avgEfficiency = averages.count > 0 ? averages.efficiency / averages.count : 0;
37125
37477
  const avgUnderperforming = averages.count > 0 ? averages.underperforming / averages.count : 0;
37126
37478
  const avgTotalWorkspaces = averages.count > 0 ? averages.totalWorkspaces / averages.count : 0;
37479
+ const hasRealData = (shift) => {
37480
+ if (shift.hasData !== void 0) return shift.hasData;
37481
+ return shift.avg_efficiency > 0 || shift.underperforming_workspaces > 0 || shift.total_workspaces > 0 || (shift.output || 0) > 0 || (shift.idealOutput || 0) > 0;
37482
+ };
37483
+ const chartData = useMemo(() => {
37484
+ const rangeStartDate = parseDateKeyToDate(normalizedRange.startKey);
37485
+ const rangeEndDate = parseDateKeyToDate(normalizedRange.endKey);
37486
+ const dayNumbers = [];
37487
+ for (let d = new Date(rangeStartDate); d <= rangeEndDate; d.setDate(d.getDate() + 1)) {
37488
+ dayNumbers.push(d.getDate());
37489
+ }
37490
+ const dailyData = [];
37491
+ let maxOutput = 0;
37492
+ let lastSetTarget = 0;
37493
+ for (let i = dayNumbers.length - 1; i >= 0; i--) {
37494
+ const day = dayNumbers[i];
37495
+ const dayData = analysisMonthlyData.find((d) => {
37496
+ const date = new Date(d.date);
37497
+ return date.getDate() === day;
37498
+ });
37499
+ const shiftData = dayData ? getShiftData2(dayData, selectedShiftId) : null;
37500
+ const idealOutput = shiftData ? shiftData.idealOutput || 0 : 0;
37501
+ if (idealOutput > 0) {
37502
+ lastSetTarget = idealOutput;
37503
+ break;
37504
+ }
37505
+ }
37506
+ for (const day of dayNumbers) {
37507
+ const dayData = analysisMonthlyData.find((d) => {
37508
+ const date = new Date(d.date);
37509
+ return date.getDate() === day;
37510
+ });
37511
+ const shiftData = dayData ? getShiftData2(dayData, selectedShiftId) : null;
37512
+ const output = shiftData && hasRealData(shiftData) ? shiftData.output || 0 : 0;
37513
+ const idealOutput = shiftData ? shiftData.idealOutput || 0 : 0;
37514
+ if (output > maxOutput) maxOutput = output;
37515
+ const color2 = output >= lastSetTarget ? "#00AB45" : "#E34329";
37516
+ dailyData.push({
37517
+ hour: getOrdinal(day),
37518
+ // Using ordinal format (1st, 2nd, 3rd, etc.)
37519
+ timeRange: `Day ${day}`,
37520
+ output,
37521
+ originalOutput: output,
37522
+ // For label display
37523
+ idealOutput,
37524
+ color: color2
37525
+ });
37526
+ }
37527
+ const calculatedMax = Math.max(maxOutput, lastSetTarget);
37528
+ const yAxisMax = calculatedMax > 0 ? calculatedMax * 1.1 : 100;
37529
+ return { data: dailyData, maxOutput, lastSetTarget, yAxisMax };
37530
+ }, [analysisMonthlyData, normalizedRange.startKey, normalizedRange.endKey, selectedShiftId]);
37531
+ const yAxisTicks = useMemo(() => {
37532
+ const max = chartData.yAxisMax;
37533
+ const target = chartData.lastSetTarget;
37534
+ if (!max || max <= 0) return void 0;
37535
+ const desiredIntervals = 4;
37536
+ const roughStep = max / desiredIntervals;
37537
+ const power = Math.pow(10, Math.floor(Math.log10(roughStep)));
37538
+ const normalized = roughStep / power;
37539
+ let step = 1 * power;
37540
+ if (normalized >= 1.5 && normalized < 3) step = 2 * power;
37541
+ else if (normalized >= 3 && normalized < 7) step = 5 * power;
37542
+ else if (normalized >= 7) step = 10 * power;
37543
+ const ticks = [];
37544
+ for (let v = 0; v <= max; v += step) {
37545
+ ticks.push(Math.round(v));
37546
+ }
37547
+ if (target > 0) {
37548
+ ticks.push(Math.round(target));
37549
+ }
37550
+ return Array.from(new Set(ticks)).filter((v) => v >= 0 && v <= max).sort((a, b) => a - b);
37551
+ }, [chartData.yAxisMax, chartData.lastSetTarget]);
37552
+ if (isLoading) {
37553
+ return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-[calc(100vh-10rem)]", children: /* @__PURE__ */ jsx(
37554
+ OptifyeLogoLoader_default,
37555
+ {
37556
+ size: "lg",
37557
+ message: "Loading monthly performance data..."
37558
+ }
37559
+ ) });
37560
+ }
37127
37561
  const renderPerformanceSquares = (performances) => {
37128
37562
  if (!performances || !Array.isArray(performances)) {
37129
37563
  return null;
@@ -37215,64 +37649,18 @@ var LineMonthlyHistory = ({
37215
37649
  year,
37216
37650
  timezone,
37217
37651
  value: normalizedRange,
37218
- onChange: (nextRange) => onRangeChange?.(nextRange)
37652
+ onChange: (nextRange) => onRangeChange?.(nextRange),
37653
+ onMonthNavigate: (newMonth, newYear) => onCalendarMonthChange?.(new Date(newYear, newMonth))
37219
37654
  }
37220
37655
  ) })
37221
37656
  ] }),
37222
37657
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-6 mt-6", children: [
37223
37658
  /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-6", children: [
37224
- /* @__PURE__ */ jsxs("div", { className: "flex justify-center items-center mb-6 space-x-4", children: [
37225
- /* @__PURE__ */ jsx(
37226
- "button",
37227
- {
37228
- onClick: () => {
37229
- let newMonth = month - 1;
37230
- let newYear = year;
37231
- if (newMonth < 0) {
37232
- newMonth = 11;
37233
- newYear -= 1;
37234
- }
37235
- if (onCalendarMonthChange) {
37236
- onCalendarMonthChange(new Date(newYear, newMonth));
37237
- }
37238
- },
37239
- className: "p-2 rounded-full hover:bg-gray-100",
37240
- "aria-label": "Previous month",
37241
- children: /* @__PURE__ */ jsx(ChevronLeftIcon, { className: "w-5 h-5" })
37242
- }
37243
- ),
37244
- /* @__PURE__ */ jsxs("h2", { className: "text-lg font-semibold text-gray-700", children: [
37245
- new Date(year, month).toLocaleString("default", { month: "long" }),
37246
- " ",
37247
- year
37248
- ] }),
37249
- /* @__PURE__ */ jsx(
37250
- "button",
37251
- {
37252
- onClick: () => {
37253
- let newMonth = month + 1;
37254
- let newYear = year;
37255
- if (newMonth > 11) {
37256
- newMonth = 0;
37257
- newYear += 1;
37258
- }
37259
- const currentDate = /* @__PURE__ */ new Date();
37260
- const currentYear = currentDate.getFullYear();
37261
- const currentMonth = currentDate.getMonth();
37262
- if (newYear > currentYear || newYear === currentYear && newMonth > currentMonth) {
37263
- return;
37264
- }
37265
- if (onCalendarMonthChange) {
37266
- onCalendarMonthChange(new Date(newYear, newMonth));
37267
- }
37268
- },
37269
- 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"}`,
37270
- disabled: (/* @__PURE__ */ new Date()).getFullYear() === year && (/* @__PURE__ */ new Date()).getMonth() === month,
37271
- "aria-label": "Next month",
37272
- children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "w-5 h-5" })
37273
- }
37274
- )
37275
- ] }),
37659
+ /* @__PURE__ */ jsx("div", { className: "flex justify-center items-center mb-6", children: /* @__PURE__ */ jsxs("h2", { className: "text-lg font-semibold text-gray-700", children: [
37660
+ new Date(year, month).toLocaleString("default", { month: "long" }),
37661
+ " ",
37662
+ year
37663
+ ] }) }),
37276
37664
  /* @__PURE__ */ jsx(
37277
37665
  LineHistoryCalendar_default,
37278
37666
  {
@@ -37287,26 +37675,20 @@ var LineMonthlyHistory = ({
37287
37675
  }
37288
37676
  )
37289
37677
  ] }),
37290
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6", children: [
37291
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4", children: [
37292
- /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-6", children: [
37293
- /* @__PURE__ */ jsx("h3", { className: "text-base font-semibold text-gray-700 mb-1 text-center", children: "Efficiency" }),
37294
- /* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-500 mb-4 text-center", children: [
37295
- "Cumulative for ",
37296
- rangeLabel
37297
- ] }),
37298
- /* @__PURE__ */ jsxs("div", { className: "text-4xl font-bold text-center text-gray-900", children: [
37678
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
37679
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
37680
+ /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4 flex flex-col items-center justify-center", children: [
37681
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-700 mb-1 text-center", children: "Efficiency" }),
37682
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] text-gray-400 mb-1 text-center uppercase tracking-wide", children: "Cumulative" }),
37683
+ /* @__PURE__ */ jsxs("div", { className: "text-2xl font-bold text-center text-gray-900", children: [
37299
37684
  avgEfficiency.toFixed(1),
37300
37685
  "%"
37301
37686
  ] })
37302
37687
  ] }),
37303
- /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-6", children: [
37304
- /* @__PURE__ */ jsx("h3", { className: "text-base font-semibold text-gray-700 mb-1 text-center", children: "Avg. Underperforming" }),
37305
- /* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-500 mb-4 text-center", children: [
37306
- "Cumulative for ",
37307
- rangeLabel
37308
- ] }),
37309
- /* @__PURE__ */ jsxs("div", { className: "text-4xl font-bold text-center text-gray-900", children: [
37688
+ /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4 flex flex-col items-center justify-center", children: [
37689
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-700 mb-1 text-center", children: "Avg. Underperforming" }),
37690
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] text-gray-400 mb-1 text-center uppercase tracking-wide", children: "Cumulative" }),
37691
+ /* @__PURE__ */ jsxs("div", { className: "text-2xl font-bold text-center text-gray-900", children: [
37310
37692
  avgUnderperforming.toFixed(1),
37311
37693
  "/",
37312
37694
  avgTotalWorkspaces.toFixed(1)
@@ -37314,9 +37696,9 @@ var LineMonthlyHistory = ({
37314
37696
  ] })
37315
37697
  ] }),
37316
37698
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-4", children: [
37317
- /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4 flex flex-col", children: [
37318
- /* @__PURE__ */ jsx("h3", { className: "text-base font-semibold text-gray-700 mb-3", children: "Idle time Breakdown" }),
37319
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-[200px]", children: /* @__PURE__ */ jsx(
37699
+ /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4 flex flex-col h-[220px]", children: [
37700
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-700 mb-1", children: "Idle time Breakdown" }),
37701
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 relative -ml-4", children: /* @__PURE__ */ jsx(
37320
37702
  IdleTimeReasonChart,
37321
37703
  {
37322
37704
  data: idleReasonsChartData,
@@ -37326,34 +37708,117 @@ var LineMonthlyHistory = ({
37326
37708
  chartKey
37327
37709
  ) })
37328
37710
  ] }),
37329
- /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-6", children: [
37330
- /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-700 mb-4", children: "Top 3 Poorest Performers" }),
37331
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
37711
+ /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4 h-[220px] flex flex-col", children: [
37712
+ /* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold text-gray-700 mb-3", children: "Top 3 Poorest Performers" }),
37713
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto pr-1 space-y-1 custom-scrollbar", children: [
37332
37714
  (underperformingWorkspaces[selectedShiftId] || []).slice(0, 3).map((workspace) => /* @__PURE__ */ jsx(
37333
37715
  "button",
37334
37716
  {
37335
37717
  onClick: () => handleWorkspaceClick(workspace),
37336
- className: "block hover:bg-gray-50 transition-colors rounded-lg w-full text-left",
37337
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between py-2 px-2 border-b border-gray-100 last:border-b-0", children: [
37338
- /* @__PURE__ */ jsxs("div", { className: "font-medium text-gray-900 text-sm", children: [
37718
+ className: "block hover:bg-gray-50 transition-colors rounded-lg w-full text-left group",
37719
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between py-2 px-2 border-b border-gray-50 group-last:border-b-0", children: [
37720
+ /* @__PURE__ */ jsxs("div", { className: "font-medium text-gray-900 text-xs", children: [
37339
37721
  getWorkspaceDisplayName(workspace.workspace_name, lineId),
37340
- workspace.avg_efficiency !== void 0 && /* @__PURE__ */ jsxs("span", { className: "ml-1 text-xs text-gray-500", children: [
37722
+ workspace.avg_efficiency !== void 0 && /* @__PURE__ */ jsxs("span", { className: "ml-1 text-[10px] text-gray-500", children: [
37341
37723
  "(",
37342
37724
  (workspace.avg_efficiency || 0).toFixed(1),
37343
37725
  "%)"
37344
37726
  ] })
37345
37727
  ] }),
37346
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
37347
- /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500 hidden xl:block", children: "Last 5 days:" }),
37348
- renderPerformanceSquares(workspace.last_5_days)
37349
- ] })
37728
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5", children: renderPerformanceSquares(workspace.last_5_days) })
37350
37729
  ] })
37351
37730
  },
37352
37731
  workspace.workspace_uuid
37353
37732
  )),
37354
- (!underperformingWorkspaces || !underperformingWorkspaces[selectedShiftId]?.length) && /* @__PURE__ */ jsx("div", { className: "text-center text-gray-500 py-4 text-sm", children: "No underperforming workspaces found" })
37733
+ (!underperformingWorkspaces || !underperformingWorkspaces[selectedShiftId]?.length) && /* @__PURE__ */ jsx("div", { className: "text-center text-gray-400 py-8 text-xs italic", children: "No underperforming workspaces" })
37355
37734
  ] })
37356
37735
  ] })
37736
+ ] }),
37737
+ /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-4", children: [
37738
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between mb-2", children: /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-700", children: "Daily Output" }) }),
37739
+ /* @__PURE__ */ jsx("div", { style: { height: "220px" }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(
37740
+ BarChart$1,
37741
+ {
37742
+ data: chartData.data,
37743
+ margin: { top: 20, right: 10, bottom: 40, left: 10 },
37744
+ children: [
37745
+ /* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#f3f4f6" }),
37746
+ /* @__PURE__ */ jsx(
37747
+ XAxis,
37748
+ {
37749
+ dataKey: "hour",
37750
+ tick: { fontSize: 10, fill: "#6b7280" },
37751
+ interval: 0,
37752
+ angle: -45,
37753
+ textAnchor: "end",
37754
+ height: 60
37755
+ }
37756
+ ),
37757
+ /* @__PURE__ */ jsx(
37758
+ YAxis,
37759
+ {
37760
+ domain: [0, chartData.yAxisMax],
37761
+ width: 40,
37762
+ ticks: yAxisTicks,
37763
+ tick: (props) => {
37764
+ const { x, y, payload } = props;
37765
+ const value = Math.round(payload.value);
37766
+ const targetValue = Math.round(chartData.lastSetTarget);
37767
+ const isTarget = value === targetValue && targetValue > 0;
37768
+ return /* @__PURE__ */ jsx(
37769
+ "text",
37770
+ {
37771
+ x: x - 5,
37772
+ y: y + 4,
37773
+ textAnchor: "end",
37774
+ fontSize: "10",
37775
+ fill: isTarget ? "#E34329" : "#6b7280",
37776
+ fontWeight: isTarget ? "600" : "normal",
37777
+ children: value < 1e-3 ? "" : value.toLocaleString()
37778
+ }
37779
+ );
37780
+ }
37781
+ }
37782
+ ),
37783
+ /* @__PURE__ */ jsx(
37784
+ Tooltip,
37785
+ {
37786
+ cursor: false,
37787
+ content: CustomTooltip2
37788
+ }
37789
+ ),
37790
+ chartData.lastSetTarget > 0 && /* @__PURE__ */ jsx(
37791
+ ReferenceLine,
37792
+ {
37793
+ y: chartData.lastSetTarget,
37794
+ stroke: "#E34329",
37795
+ strokeDasharray: "5 5",
37796
+ strokeWidth: 2
37797
+ }
37798
+ ),
37799
+ /* @__PURE__ */ jsx(
37800
+ Bar,
37801
+ {
37802
+ dataKey: "output",
37803
+ radius: [4, 4, 0, 0],
37804
+ isAnimationActive: true,
37805
+ animationBegin: 0,
37806
+ animationDuration: 1e3,
37807
+ animationEasing: "ease-out",
37808
+ children: chartData.data.map((entry, index) => /* @__PURE__ */ jsx(
37809
+ Cell,
37810
+ {
37811
+ fill: entry.output === 0 ? "#f3f4f6" : entry.color,
37812
+ className: entry.output > 0 ? "hover:opacity-80 transition-opacity cursor-pointer" : ""
37813
+ },
37814
+ `cell-${index}`
37815
+ ))
37816
+ }
37817
+ )
37818
+ ]
37819
+ },
37820
+ chartKey
37821
+ ) }) })
37357
37822
  ] })
37358
37823
  ] })
37359
37824
  ] })
@@ -38494,7 +38959,7 @@ var styles = `
38494
38959
  transform: scale(1) !important;
38495
38960
  }
38496
38961
  `;
38497
- var WEEKDAYS2 = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
38962
+ var WEEKDAYS3 = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
38498
38963
  var getTimeInZoneAsDate = (timezone) => {
38499
38964
  const time2 = getCurrentTimeInZone(timezone);
38500
38965
  return typeof time2 === "string" ? new Date(time2) : time2;
@@ -38526,10 +38991,10 @@ var WorkspaceHistoryCalendar = ({
38526
38991
  return () => clearTimeout(timer);
38527
38992
  }, [month, year]);
38528
38993
  const calendarData = useMemo(() => {
38529
- const startOfMonth = toZonedTime(new Date(year, month, 1), configuredTimezone);
38530
- const endOfMonth = toZonedTime(new Date(year, month + 1, 0), configuredTimezone);
38531
- const totalDays = endOfMonth.getDate();
38532
- let startOffset = startOfMonth.getDay() - 1;
38994
+ const startOfMonth2 = toZonedTime(new Date(year, month, 1), configuredTimezone);
38995
+ const endOfMonth2 = toZonedTime(new Date(year, month + 1, 0), configuredTimezone);
38996
+ const totalDays = endOfMonth2.getDate();
38997
+ let startOffset = startOfMonth2.getDay() - 1;
38533
38998
  if (startOffset === -1) startOffset = 6;
38534
38999
  const calendar = Array(startOffset).fill(null);
38535
39000
  for (let day = 1; day <= totalDays; day++) {
@@ -38738,7 +39203,7 @@ var WorkspaceHistoryCalendar = ({
38738
39203
  /* @__PURE__ */ jsx("p", { className: "text-xs sm:text-sm text-gray-500 mt-1", children: "Calendar view of daily performance" })
38739
39204
  ] }),
38740
39205
  /* @__PURE__ */ jsxs("div", { className: "grid gap-3 sm:gap-4 lg:gap-6", children: [
38741
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-1 sm:gap-2", children: WEEKDAYS2.map((day) => /* @__PURE__ */ jsx("div", { className: "text-xs sm:text-sm font-medium text-gray-600 text-center", children: day.slice(0, 3) }, day)) }),
39206
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-1 sm:gap-2", children: WEEKDAYS3.map((day) => /* @__PURE__ */ jsx("div", { className: "text-xs sm:text-sm font-medium text-gray-600 text-center", children: day.slice(0, 3) }, day)) }),
38742
39207
  /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-1 sm:gap-2", children: calendarData.calendar.map((day, index) => {
38743
39208
  const startOffset = calendarData.startOffset;
38744
39209
  const dayNumber = index >= startOffset ? index - startOffset + 1 : null;
@@ -38791,13 +39256,13 @@ var WorkspaceHistoryCalendar = ({
38791
39256
  ] })
38792
39257
  ] });
38793
39258
  };
38794
- var WEEKDAYS3 = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
38795
- var getOrdinal = (n) => {
39259
+ var WEEKDAYS4 = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
39260
+ var getOrdinal2 = (n) => {
38796
39261
  const suffix = ["th", "st", "nd", "rd"];
38797
39262
  const v = n % 100;
38798
39263
  return n + (suffix[(v - 20) % 10] || suffix[v] || suffix[0]);
38799
39264
  };
38800
- var CustomTooltip2 = ({ active, payload, label }) => {
39265
+ var CustomTooltip3 = ({ active, payload, label }) => {
38801
39266
  if (active && payload && payload.length) {
38802
39267
  return /* @__PURE__ */ jsxs("div", { className: "bg-white/95 backdrop-blur-sm p-3 rounded-lg shadow-lg border border-gray-100", children: [
38803
39268
  /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-gray-800 mb-1", children: label }),
@@ -38900,7 +39365,7 @@ var WorkspaceMonthlyHistory = ({
38900
39365
  if (output > maxOutput) maxOutput = output;
38901
39366
  const color2 = output >= lastSetTarget ? "#00AB45" : "#E34329";
38902
39367
  dailyData.push({
38903
- hour: getOrdinal(day),
39368
+ hour: getOrdinal2(day),
38904
39369
  // Using ordinal format (1st, 2nd, 3rd, etc.)
38905
39370
  timeRange: `Day ${day}`,
38906
39371
  output,
@@ -38934,9 +39399,26 @@ var WorkspaceMonthlyHistory = ({
38934
39399
  ticks.push(Math.round(v));
38935
39400
  }
38936
39401
  if (target > 0) {
38937
- ticks.push(Math.round(target));
39402
+ const roundedTarget = Math.round(target);
39403
+ if (!ticks.includes(roundedTarget)) {
39404
+ let nearestIndex = -1;
39405
+ let nearestDistance = Number.POSITIVE_INFINITY;
39406
+ ticks.forEach((tick, index) => {
39407
+ if (tick === 0) return;
39408
+ const distance2 = Math.abs(tick - roundedTarget);
39409
+ if (distance2 < nearestDistance) {
39410
+ nearestDistance = distance2;
39411
+ nearestIndex = index;
39412
+ }
39413
+ });
39414
+ if (nearestIndex >= 0) {
39415
+ ticks[nearestIndex] = roundedTarget;
39416
+ } else {
39417
+ ticks.push(roundedTarget);
39418
+ }
39419
+ }
38938
39420
  }
38939
- return Array.from(new Set(ticks)).filter((v) => v >= 0 && v <= max).sort((a, b) => a - b);
39421
+ return ticks.filter((v) => v >= 0 && v <= max * 1.05).sort((a, b) => a - b);
38940
39422
  }, [chartData.yAxisMax, chartData.lastSetTarget]);
38941
39423
  const pieChartData = useMemo(() => {
38942
39424
  const validShifts = analysisMonthlyData.map((d) => getShiftData(d, selectedShiftId)).filter(hasRealData);
@@ -38972,10 +39454,10 @@ var WorkspaceMonthlyHistory = ({
38972
39454
  };
38973
39455
  }, [analysisMonthlyData, selectedShiftId]);
38974
39456
  const calendarData = useMemo(() => {
38975
- const startOfMonth = new Date(year, month, 1);
38976
- const endOfMonth = new Date(year, month + 1, 0);
38977
- const totalDays = endOfMonth.getDate();
38978
- let startOffset = startOfMonth.getDay() - 1;
39457
+ const startOfMonth2 = new Date(year, month, 1);
39458
+ const endOfMonth2 = new Date(year, month + 1, 0);
39459
+ const totalDays = endOfMonth2.getDate();
39460
+ let startOffset = startOfMonth2.getDay() - 1;
38979
39461
  if (startOffset === -1) startOffset = 6;
38980
39462
  const calendar = Array(startOffset).fill(null);
38981
39463
  for (let day = 1; day <= totalDays; day++) {
@@ -39042,68 +39524,19 @@ var WorkspaceMonthlyHistory = ({
39042
39524
  year,
39043
39525
  timezone,
39044
39526
  value: normalizedRange,
39045
- onChange: (nextRange) => onRangeChange?.(nextRange)
39527
+ onChange: (nextRange) => onRangeChange?.(nextRange),
39528
+ onMonthNavigate
39046
39529
  }
39047
39530
  ) })
39048
39531
  ] }),
39049
39532
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-6 mt-6", children: [
39050
39533
  /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-sm border border-gray-100 p-6", children: [
39051
- /* @__PURE__ */ jsxs("div", { className: "flex justify-center items-center mb-6 space-x-4", children: [
39052
- /* @__PURE__ */ jsx(
39053
- "button",
39054
- {
39055
- onClick: () => {
39056
- let newMonth = month - 1;
39057
- let newYear = year;
39058
- if (newMonth < 0) {
39059
- newMonth = 11;
39060
- newYear -= 1;
39061
- }
39062
- if (newYear < 2023) {
39063
- return;
39064
- }
39065
- if (onMonthNavigate) {
39066
- onMonthNavigate(newMonth, newYear);
39067
- }
39068
- },
39069
- className: "p-2 rounded-full hover:bg-gray-100",
39070
- "aria-label": "Previous month",
39071
- children: /* @__PURE__ */ jsx(ChevronLeftIcon, { className: "w-5 h-5" })
39072
- }
39073
- ),
39074
- /* @__PURE__ */ jsxs("h2", { className: "text-lg font-semibold text-gray-700", children: [
39075
- new Date(year, month).toLocaleString("default", { month: "long" }),
39076
- " ",
39077
- year
39078
- ] }),
39079
- /* @__PURE__ */ jsx(
39080
- "button",
39081
- {
39082
- onClick: () => {
39083
- let newMonth = month + 1;
39084
- let newYear = year;
39085
- if (newMonth > 11) {
39086
- newMonth = 0;
39087
- newYear += 1;
39088
- }
39089
- const currentDate = /* @__PURE__ */ new Date();
39090
- const currentYear = currentDate.getFullYear();
39091
- const currentMonth = currentDate.getMonth();
39092
- if (newYear > currentYear || newYear === currentYear && newMonth > currentMonth) {
39093
- return;
39094
- }
39095
- if (onMonthNavigate) {
39096
- onMonthNavigate(newMonth, newYear);
39097
- }
39098
- },
39099
- 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"}`,
39100
- disabled: (/* @__PURE__ */ new Date()).getFullYear() === year && (/* @__PURE__ */ new Date()).getMonth() === month,
39101
- "aria-label": "Next month",
39102
- children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "w-5 h-5" })
39103
- }
39104
- )
39105
- ] }),
39106
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-2", children: WEEKDAYS3.map((day, idx) => /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-gray-500 dark:text-gray-400 text-center", children: day }, day)) }),
39534
+ /* @__PURE__ */ jsx("div", { className: "flex justify-center items-center mb-6", children: /* @__PURE__ */ jsxs("h2", { className: "text-lg font-semibold text-gray-700", children: [
39535
+ new Date(year, month).toLocaleString("default", { month: "long" }),
39536
+ " ",
39537
+ year
39538
+ ] }) }),
39539
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-2", children: WEEKDAYS4.map((day, idx) => /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-gray-500 dark:text-gray-400 text-center", children: day }, day)) }),
39107
39540
  /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-2 mt-6", children: calendarData.calendar.map((day, index) => {
39108
39541
  const dayNumber = index >= calendarData.startOffset ? index - calendarData.startOffset + 1 : null;
39109
39542
  if (!dayNumber || dayNumber > new Date(year, month + 1, 0).getDate()) {
@@ -39120,6 +39553,7 @@ var WorkspaceMonthlyHistory = ({
39120
39553
  const getPerformanceColor = () => {
39121
39554
  if (isFuture) return "bg-gray-200 dark:bg-gray-700";
39122
39555
  if (!hasData || !shiftData) return "bg-gray-300 dark:bg-gray-600";
39556
+ if (showRange && !inRange) return "bg-gray-200 dark:bg-gray-700";
39123
39557
  return shiftData.efficiency >= 75 ? "bg-green-500 dark:bg-green-600" : "bg-red-500 dark:bg-red-600";
39124
39558
  };
39125
39559
  return /* @__PURE__ */ jsx("div", { className: "aspect-square relative", children: /* @__PURE__ */ jsx(
@@ -39139,7 +39573,8 @@ var WorkspaceMonthlyHistory = ({
39139
39573
  }
39140
39574
  ),
39141
39575
  /* @__PURE__ */ jsx("div", { className: `
39142
- text-base font-medium ${hasData && !isFuture ? "text-white" : "text-gray-500"} flex items-center relative z-10
39576
+ text-base font-medium flex items-center relative z-10
39577
+ ${hasData && !isFuture && (!showRange || inRange) ? "text-white" : "text-gray-400"}
39143
39578
  ${isToday2 ? "bg-blue-500 dark:bg-blue-600 rounded-full w-7 h-7 justify-center" : ""}
39144
39579
  `, children: dayNumber }),
39145
39580
  !isFuture && hasData && shiftData && /* @__PURE__ */ 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__ */ jsxs("div", { className: "text-xs space-y-1", children: [
@@ -39295,13 +39730,13 @@ var WorkspaceMonthlyHistory = ({
39295
39730
  YAxis,
39296
39731
  {
39297
39732
  domain: [0, chartData.yAxisMax],
39298
- width: 35,
39733
+ width: 40,
39299
39734
  ticks: yAxisTicks,
39300
39735
  tick: (props) => {
39301
39736
  const { x, y, payload } = props;
39302
39737
  const value = Math.round(payload.value);
39303
39738
  const targetValue = Math.round(chartData.lastSetTarget);
39304
- const isTarget = Math.abs(value - targetValue) < 1 && targetValue > 0;
39739
+ const isTarget = value === targetValue && targetValue > 0;
39305
39740
  return /* @__PURE__ */ jsx(
39306
39741
  "text",
39307
39742
  {
@@ -39311,7 +39746,7 @@ var WorkspaceMonthlyHistory = ({
39311
39746
  fontSize: "10",
39312
39747
  fill: isTarget ? "#E34329" : "#6b7280",
39313
39748
  fontWeight: isTarget ? "600" : "normal",
39314
- children: value < 1e-3 ? "" : value.toString()
39749
+ children: value < 1e-3 ? "" : value.toLocaleString()
39315
39750
  }
39316
39751
  );
39317
39752
  }
@@ -39321,7 +39756,7 @@ var WorkspaceMonthlyHistory = ({
39321
39756
  Tooltip,
39322
39757
  {
39323
39758
  cursor: false,
39324
- content: CustomTooltip2
39759
+ content: CustomTooltip3
39325
39760
  }
39326
39761
  ),
39327
39762
  chartData.lastSetTarget > 0 && /* @__PURE__ */ jsx(
@@ -39950,13 +40385,15 @@ var LiveTimer = () => {
39950
40385
  });
39951
40386
  return /* @__PURE__ */ jsx("span", { children: formatter.format(time2) });
39952
40387
  };
39953
- var getEfficiencyColor = (efficiency) => {
39954
- if (efficiency >= 80) {
39955
- return "bg-[#00AB45]/90 hover:bg-[#00AB45]/95";
39956
- } else if (efficiency >= 70) {
39957
- return "bg-[#FFB020]/90 hover:bg-[#FFB020]/95";
39958
- } else {
39959
- return "bg-[#E34329]/90 hover:bg-[#E34329]/95";
40388
+ var getGradientEfficiencyClasses = (color2) => {
40389
+ switch (color2) {
40390
+ case "green":
40391
+ return "bg-gradient-to-r from-[#00AB45]/90 to-[#00AB45]/95";
40392
+ case "yellow":
40393
+ return "bg-gradient-to-r from-[#FFB020]/90 to-[#FFB020]/95";
40394
+ case "red":
40395
+ default:
40396
+ return "bg-gradient-to-r from-[#E34329]/90 to-[#E34329]/95";
39960
40397
  }
39961
40398
  };
39962
40399
  var TREND_STYLES = {
@@ -40024,30 +40461,40 @@ var getWorkspaceStyles = (position, isPlaceholder = false) => {
40024
40461
  ${additionalStyles}
40025
40462
  ${isPlaceholder ? "cursor-default" : ""}`;
40026
40463
  };
40027
- var Legend6 = () => /* @__PURE__ */ 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: [
40028
- /* @__PURE__ */ jsx("div", { className: "font-medium text-gray-700 hidden sm:block", children: "Efficiency:" }),
40029
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 sm:gap-4", children: [
40030
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
40031
- /* @__PURE__ */ 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" }),
40032
- /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-600", children: "80-100%" })
40033
- ] }),
40034
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
40035
- /* @__PURE__ */ 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" }),
40036
- /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-600", children: "70-79%" })
40464
+ var formatPercentRange = (min, max) => {
40465
+ const format7 = (value) => Number.isInteger(value) ? `${value}` : value.toFixed(1);
40466
+ return `${format7(min)}-${format7(max)}%`;
40467
+ };
40468
+ var Legend6 = ({ useBottleneckLabel = false, legend }) => {
40469
+ const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
40470
+ const exclamationLabel = useBottleneckLabel ? "Bottleneck" : "<50% efficiency";
40471
+ return /* @__PURE__ */ 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: [
40472
+ /* @__PURE__ */ jsx("div", { className: "font-medium text-gray-700 hidden sm:block", children: "Efficiency:" }),
40473
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 sm:gap-4", children: [
40474
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
40475
+ /* @__PURE__ */ 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" }),
40476
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-600", children: formatPercentRange(effectiveLegend.green_min, effectiveLegend.green_max) })
40477
+ ] }),
40478
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
40479
+ /* @__PURE__ */ 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" }),
40480
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-600", children: formatPercentRange(effectiveLegend.yellow_min, effectiveLegend.yellow_max) })
40481
+ ] }),
40482
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
40483
+ /* @__PURE__ */ 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" }),
40484
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-600", children: formatPercentRange(effectiveLegend.red_min, effectiveLegend.red_max) })
40485
+ ] })
40037
40486
  ] }),
40487
+ /* @__PURE__ */ jsx("div", { className: "hidden sm:block w-px h-6 bg-gray-200 mx-1" }),
40038
40488
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
40039
- /* @__PURE__ */ 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" }),
40040
- /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-600", children: "<70%" })
40489
+ /* @__PURE__ */ 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: "!" }),
40490
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-600", children: exclamationLabel })
40041
40491
  ] })
40042
- ] }),
40043
- /* @__PURE__ */ jsx("div", { className: "hidden sm:block w-px h-6 bg-gray-200 mx-1" }),
40044
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
40045
- /* @__PURE__ */ 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: "!" }),
40046
- /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-600", children: "Bottleneck" })
40047
- ] })
40048
- ] });
40492
+ ] });
40493
+ };
40049
40494
  var arePropsEqual = (prevProps, nextProps) => {
40050
- 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
40495
+ const prevLegend = prevProps.legend || DEFAULT_EFFICIENCY_LEGEND;
40496
+ const nextLegend = nextProps.legend || DEFAULT_EFFICIENCY_LEGEND;
40497
+ 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
40051
40498
  prevProps.position.id === nextProps.position.id;
40052
40499
  };
40053
40500
  var WorkspaceGridItem = React24__default.memo(({
@@ -40056,20 +40503,21 @@ var WorkspaceGridItem = React24__default.memo(({
40056
40503
  isBottleneck = false,
40057
40504
  isLowEfficiency = false,
40058
40505
  isVeryLowEfficiency = false,
40059
- onHoverChange
40506
+ onHoverChange,
40507
+ legend
40060
40508
  }) => {
40061
40509
  const { navigate } = useNavigation();
40510
+ const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
40062
40511
  const isInactive = useMemo(() => !data.workspace_id || data.efficiency < 10, [data.workspace_id, data.efficiency]);
40063
40512
  const colorClass = useMemo(() => {
40064
40513
  const isConveyorEnd = position.size === "conveyor" && position.orientation;
40514
+ const efficiencyColor = getEfficiencyColor(data.efficiency, effectiveLegend);
40065
40515
  if (isConveyorEnd) {
40066
40516
  if (isInactive) return "bg-gray-300/90";
40067
- if (data.efficiency >= 80) return "bg-gradient-to-r from-[#00AB45]/90 to-[#00AB45]/95";
40068
- if (data.efficiency >= 70) return "bg-gradient-to-r from-[#FFB020]/90 to-[#FFB020]/95";
40069
- return "bg-gradient-to-r from-[#E34329]/90 to-[#E34329]/95";
40517
+ return getGradientEfficiencyClasses(efficiencyColor);
40070
40518
  }
40071
- return isInactive ? "bg-gray-300/90" : getEfficiencyColor(data.efficiency);
40072
- }, [data.efficiency, isInactive, position.size, position.orientation]);
40519
+ return isInactive ? "bg-gray-300/90" : getEfficiencyColorClasses(data.efficiency, effectiveLegend);
40520
+ }, [data.efficiency, effectiveLegend, isInactive, position.size, position.orientation]);
40073
40521
  const { arrow, color: arrowColor } = useMemo(() => getTrendArrowAndColor2(data.trend_score), [data.trend_score]);
40074
40522
  const workspaceNumber = useMemo(() => getWorkspaceNumber(data.workspace_name), [data.workspace_name]);
40075
40523
  const styles2 = useMemo(() => getWorkspaceStyles(position, isInactive), [position, isInactive]);
@@ -40151,6 +40599,8 @@ var WorkspaceGrid = React24__default.memo(({
40151
40599
  factoryView = "factory",
40152
40600
  line2Uuid = "line-2",
40153
40601
  className = "",
40602
+ hasFlowBuffers = false,
40603
+ legend = DEFAULT_EFFICIENCY_LEGEND,
40154
40604
  videoSources = {},
40155
40605
  displayNames = {},
40156
40606
  onWorkspaceHover,
@@ -40187,7 +40637,7 @@ var WorkspaceGrid = React24__default.memo(({
40187
40637
  return /* @__PURE__ */ jsxs("div", { className: `relative w-full h-full overflow-hidden ${className}`, children: [
40188
40638
  /* @__PURE__ */ jsxs("div", { className: "absolute top-0 left-2 sm:left-4 right-2 sm:right-8 z-20", children: [
40189
40639
  /* @__PURE__ */ jsxs("div", { className: "flex flex-row items-center justify-between py-1 sm:py-1.5 gap-2", children: [
40190
- /* @__PURE__ */ jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsx(Legend6, {}) }),
40640
+ /* @__PURE__ */ jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsx(Legend6, { legend, useBottleneckLabel: hasFlowBuffers }) }),
40191
40641
  mapViewEnabled && /* @__PURE__ */ jsx(
40192
40642
  "button",
40193
40643
  {
@@ -40204,7 +40654,7 @@ var WorkspaceGrid = React24__default.memo(({
40204
40654
  }
40205
40655
  )
40206
40656
  ] }),
40207
- /* @__PURE__ */ jsx("div", { className: "sm:hidden mt-1 mr-32", children: /* @__PURE__ */ jsx(Legend6, {}) })
40657
+ /* @__PURE__ */ jsx("div", { className: "sm:hidden mt-1 mr-32", children: /* @__PURE__ */ jsx(Legend6, { legend, useBottleneckLabel: hasFlowBuffers }) })
40208
40658
  ] }),
40209
40659
  /* @__PURE__ */ jsx("div", { className: "absolute top-14 sm:top-16 left-0 right-0 bottom-0", children: /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: viewMode === "video" ? /* @__PURE__ */ jsx(
40210
40660
  motion.div,
@@ -40220,6 +40670,7 @@ var WorkspaceGrid = React24__default.memo(({
40220
40670
  workspaces,
40221
40671
  videoSources,
40222
40672
  displayNames,
40673
+ legend,
40223
40674
  onWorkspaceHover,
40224
40675
  onWorkspaceHoverEnd
40225
40676
  }
@@ -40239,6 +40690,7 @@ var WorkspaceGrid = React24__default.memo(({
40239
40690
  {
40240
40691
  workspaces,
40241
40692
  displayNames,
40693
+ legend,
40242
40694
  onWorkspaceHover,
40243
40695
  onWorkspaceHoverEnd
40244
40696
  }
@@ -47719,7 +48171,7 @@ var AIAgentView = () => {
47719
48171
  barRadius: [4, 4, 0, 0]
47720
48172
  // Top corners rounded
47721
48173
  };
47722
- const CustomTooltip3 = ({ active, payload, label }) => {
48174
+ const CustomTooltip4 = ({ active, payload, label }) => {
47723
48175
  if (active && payload && payload.length) {
47724
48176
  return /* @__PURE__ */ jsxs("div", { className: "bg-white px-4 py-3 shadow-lg rounded-lg border border-gray-200", children: [
47725
48177
  /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-900 mb-1", children: label }),
@@ -47819,7 +48271,7 @@ var AIAgentView = () => {
47819
48271
  tickFormatter: (value) => formatNumber(value)
47820
48272
  }
47821
48273
  ),
47822
- /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip3, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
48274
+ /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip4, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
47823
48275
  /* @__PURE__ */ jsx(
47824
48276
  Bar,
47825
48277
  {
@@ -47863,7 +48315,7 @@ var AIAgentView = () => {
47863
48315
  tickFormatter: (value) => formatNumber(value)
47864
48316
  }
47865
48317
  ),
47866
- /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip3, {}), cursor: { strokeDasharray: "3 3" } }),
48318
+ /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip4, {}), cursor: { strokeDasharray: "3 3" } }),
47867
48319
  /* @__PURE__ */ jsx(
47868
48320
  Line,
47869
48321
  {
@@ -47983,7 +48435,7 @@ var AIAgentView = () => {
47983
48435
  tickFormatter: (value) => formatNumber(value)
47984
48436
  }
47985
48437
  ),
47986
- /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip3, {}), cursor: { strokeDasharray: "3 3" } }),
48438
+ /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip4, {}), cursor: { strokeDasharray: "3 3" } }),
47987
48439
  /* @__PURE__ */ jsx(
47988
48440
  Legend,
47989
48441
  {
@@ -48043,7 +48495,7 @@ var AIAgentView = () => {
48043
48495
  tickFormatter: (value) => formatNumber(value)
48044
48496
  }
48045
48497
  ),
48046
- /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip3, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
48498
+ /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip4, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
48047
48499
  /* @__PURE__ */ jsx(
48048
48500
  Legend,
48049
48501
  {
@@ -48207,7 +48659,7 @@ var AIAgentView = () => {
48207
48659
  Tooltip,
48208
48660
  {
48209
48661
  cursor: { strokeDasharray: "3 3" },
48210
- content: /* @__PURE__ */ jsx(CustomTooltip3, {})
48662
+ content: /* @__PURE__ */ jsx(CustomTooltip4, {})
48211
48663
  }
48212
48664
  ),
48213
48665
  /* @__PURE__ */ jsx(
@@ -48277,7 +48729,7 @@ var AIAgentView = () => {
48277
48729
  tickFormatter: (value) => formatNumber(value)
48278
48730
  }
48279
48731
  ),
48280
- /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip3, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
48732
+ /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip4, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
48281
48733
  /* @__PURE__ */ jsx(
48282
48734
  Legend,
48283
48735
  {
@@ -48351,7 +48803,7 @@ var AIAgentView = () => {
48351
48803
  tickFormatter: (value) => formatNumber(value)
48352
48804
  }
48353
48805
  ),
48354
- /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip3, {}), cursor: { strokeDasharray: "3 3" } }),
48806
+ /* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip4, {}), cursor: { strokeDasharray: "3 3" } }),
48355
48807
  /* @__PURE__ */ jsx(
48356
48808
  Legend,
48357
48809
  {
@@ -49610,6 +50062,8 @@ function HomeView({
49610
50062
  const {
49611
50063
  workspaceMetrics,
49612
50064
  lineMetrics,
50065
+ efficiencyLegend,
50066
+ metadata: metricsMetadata,
49613
50067
  isLoading: metricsLoading,
49614
50068
  error: metricsError,
49615
50069
  refetch: refetchMetrics
@@ -49618,6 +50072,7 @@ function HomeView({
49618
50072
  userAccessibleLineIds: allLineIds
49619
50073
  // Pass user's accessible lines for supervisor filtering
49620
50074
  });
50075
+ const hasFlowBuffers = Boolean(metricsMetadata?.hasFlowBuffers);
49621
50076
  const kpis = useMemo(() => {
49622
50077
  const lineMetricsRows = lineMetrics || [];
49623
50078
  if (selectedLineId === factoryViewId) {
@@ -50076,8 +50531,10 @@ function HomeView({
50076
50531
  workspaces: memoizedWorkspaceMetrics,
50077
50532
  lineNames,
50078
50533
  factoryView: factoryViewId,
50534
+ legend: efficiencyLegend,
50079
50535
  videoSources,
50080
50536
  displayNames: workspaceDisplayNames,
50537
+ hasFlowBuffers,
50081
50538
  className: "h-full",
50082
50539
  onWorkspaceHover: handleWorkspaceHover,
50083
50540
  onWorkspaceHoverEnd: handleWorkspaceHoverEnd
@@ -50104,8 +50561,10 @@ function HomeView({
50104
50561
  // Show empty grid while loading
50105
50562
  lineNames,
50106
50563
  factoryView: factoryViewId,
50564
+ legend: efficiencyLegend,
50107
50565
  videoSources,
50108
50566
  displayNames: workspaceDisplayNames,
50567
+ hasFlowBuffers,
50109
50568
  className: "h-full",
50110
50569
  onWorkspaceHover: handleWorkspaceHover,
50111
50570
  onWorkspaceHoverEnd: handleWorkspaceHoverEnd
@@ -50726,6 +51185,8 @@ var KPIDetailView = ({
50726
51185
  avg_efficiency: metric.avg_efficiency || 0,
50727
51186
  underperforming_workspaces: metric.underperforming_workspaces || 0,
50728
51187
  total_workspaces: metric.total_workspaces || 0,
51188
+ output: metric.current_output || 0,
51189
+ idealOutput: metric.ideal_output || metric.line_threshold || 0,
50729
51190
  compliance_percentage: 95 + Math.random() * 5,
50730
51191
  // Mock data: random value between 95-100%
50731
51192
  hasData: true
@@ -53181,6 +53642,201 @@ var ACTION_NAMES = {
53181
53642
  QUALITY_CONTROL: "Quality Control"
53182
53643
  };
53183
53644
 
53645
+ // src/lib/services/efficiencyLegendService.ts
53646
+ var EfficiencyLegendService = class {
53647
+ // 5 minutes
53648
+ constructor(supabase) {
53649
+ this.cache = /* @__PURE__ */ new Map();
53650
+ this.cacheExpiry = /* @__PURE__ */ new Map();
53651
+ this.CACHE_DURATION = 5 * 60 * 1e3;
53652
+ this.supabase = supabase;
53653
+ }
53654
+ /**
53655
+ * Get efficiency legend for a company
53656
+ */
53657
+ async getEfficiencyLegend(companyId) {
53658
+ try {
53659
+ const cached = this.cache.get(companyId);
53660
+ const expiry = this.cacheExpiry.get(companyId);
53661
+ if (cached && expiry && Date.now() < expiry) {
53662
+ return cached;
53663
+ }
53664
+ const params = new URLSearchParams({ company_id: companyId });
53665
+ const response = await fetchBackendJson(
53666
+ this.supabase,
53667
+ `/api/efficiency-legend?${params.toString()}`
53668
+ );
53669
+ const normalized = this.normalizeLegend(response?.legend);
53670
+ this.cache.set(companyId, normalized);
53671
+ this.cacheExpiry.set(companyId, Date.now() + this.CACHE_DURATION);
53672
+ return normalized;
53673
+ } catch (error) {
53674
+ console.error("Error fetching efficiency legend:", error);
53675
+ return DEFAULT_EFFICIENCY_LEGEND;
53676
+ }
53677
+ }
53678
+ /**
53679
+ * Update or create efficiency legend for a company
53680
+ */
53681
+ async updateEfficiencyLegend(companyId, legend) {
53682
+ try {
53683
+ const normalizedLegend = this.normalizeLegend(legend);
53684
+ const validation = this.validateLegend(normalizedLegend);
53685
+ if (!validation.valid) {
53686
+ return { success: false, error: validation.error };
53687
+ }
53688
+ const params = new URLSearchParams({ company_id: companyId });
53689
+ await fetchBackendJson(
53690
+ this.supabase,
53691
+ `/api/efficiency-legend?${params.toString()}`,
53692
+ {
53693
+ method: "PUT",
53694
+ body: JSON.stringify(normalizedLegend)
53695
+ }
53696
+ );
53697
+ this.cache.delete(companyId);
53698
+ this.cacheExpiry.delete(companyId);
53699
+ return { success: true };
53700
+ } catch (error) {
53701
+ console.error("Error updating efficiency legend:", error);
53702
+ return { success: false, error: error.message || "Failed to update efficiency legend" };
53703
+ }
53704
+ }
53705
+ /**
53706
+ * Clear cache for a specific company
53707
+ */
53708
+ clearCache(companyId) {
53709
+ this.cache.delete(companyId);
53710
+ this.cacheExpiry.delete(companyId);
53711
+ }
53712
+ /**
53713
+ * Clear all cache
53714
+ */
53715
+ clearAllCache() {
53716
+ this.cache.clear();
53717
+ this.cacheExpiry.clear();
53718
+ }
53719
+ /**
53720
+ * Validate legend configuration
53721
+ */
53722
+ validateLegend(legend) {
53723
+ if (legend.green_min < 0 || legend.green_min > 100) {
53724
+ return { valid: false, error: "Green minimum must be between 0 and 100" };
53725
+ }
53726
+ if (legend.green_max < 0 || legend.green_max > 100) {
53727
+ return { valid: false, error: "Green maximum must be between 0 and 100" };
53728
+ }
53729
+ if (legend.yellow_min < 0 || legend.yellow_min > 100) {
53730
+ return { valid: false, error: "Yellow minimum must be between 0 and 100" };
53731
+ }
53732
+ if (legend.yellow_max < 0 || legend.yellow_max > 100) {
53733
+ return { valid: false, error: "Yellow maximum must be between 0 and 100" };
53734
+ }
53735
+ if (legend.red_min < 0 || legend.red_min > 100) {
53736
+ return { valid: false, error: "Red minimum must be between 0 and 100" };
53737
+ }
53738
+ if (legend.red_max < 0 || legend.red_max > 100) {
53739
+ return { valid: false, error: "Red maximum must be between 0 and 100" };
53740
+ }
53741
+ if (legend.critical_threshold < 0 || legend.critical_threshold > 100) {
53742
+ return { valid: false, error: "Critical threshold must be between 0 and 100" };
53743
+ }
53744
+ if (legend.yellow_min >= legend.green_min) {
53745
+ return { valid: false, error: "Yellow minimum must be less than green minimum" };
53746
+ }
53747
+ if (legend.red_max >= legend.yellow_min) {
53748
+ return { valid: false, error: "Red maximum must be less than yellow minimum" };
53749
+ }
53750
+ if (legend.critical_threshold > legend.red_max) {
53751
+ return { valid: false, error: "Critical threshold must be less than or equal to red maximum" };
53752
+ }
53753
+ return { valid: true };
53754
+ }
53755
+ normalizeLegend(legend) {
53756
+ const fallback = DEFAULT_EFFICIENCY_LEGEND;
53757
+ if (!legend) return fallback;
53758
+ const coerce = (value, fallbackValue) => {
53759
+ const num = Number(value);
53760
+ return Number.isFinite(num) ? num : fallbackValue;
53761
+ };
53762
+ return {
53763
+ green_min: coerce(legend.green_min, fallback.green_min),
53764
+ green_max: coerce(legend.green_max, fallback.green_max),
53765
+ yellow_min: coerce(legend.yellow_min, fallback.yellow_min),
53766
+ yellow_max: coerce(legend.yellow_max, fallback.yellow_max),
53767
+ red_min: coerce(legend.red_min, fallback.red_min),
53768
+ red_max: coerce(legend.red_max, fallback.red_max),
53769
+ critical_threshold: coerce(legend.critical_threshold, fallback.critical_threshold)
53770
+ };
53771
+ }
53772
+ };
53773
+ var efficiencyLegendServiceInstance = null;
53774
+ function getEfficiencyLegendService(supabase) {
53775
+ if (!efficiencyLegendServiceInstance) {
53776
+ efficiencyLegendServiceInstance = new EfficiencyLegendService(supabase);
53777
+ }
53778
+ return efficiencyLegendServiceInstance;
53779
+ }
53780
+
53781
+ // src/lib/hooks/useEfficiencyLegend.ts
53782
+ function useEfficiencyLegend(companyId) {
53783
+ const supabase = useSupabase();
53784
+ const config = useDashboardConfig();
53785
+ const [legend, setLegend] = useState(DEFAULT_EFFICIENCY_LEGEND);
53786
+ const [isLoading, setIsLoading] = useState(true);
53787
+ const [error, setError] = useState(null);
53788
+ const effectiveCompanyId = companyId || config.entityConfig?.companyId;
53789
+ const fetchLegend = useCallback(async () => {
53790
+ if (!supabase || !effectiveCompanyId) {
53791
+ setIsLoading(false);
53792
+ return;
53793
+ }
53794
+ try {
53795
+ setIsLoading(true);
53796
+ setError(null);
53797
+ const service = getEfficiencyLegendService(supabase);
53798
+ const fetchedLegend = await service.getEfficiencyLegend(effectiveCompanyId);
53799
+ setLegend(fetchedLegend);
53800
+ } catch (err) {
53801
+ console.error("Error fetching efficiency legend:", err);
53802
+ setError(err.message || "Failed to fetch efficiency legend");
53803
+ setLegend(DEFAULT_EFFICIENCY_LEGEND);
53804
+ } finally {
53805
+ setIsLoading(false);
53806
+ }
53807
+ }, [supabase, effectiveCompanyId]);
53808
+ useEffect(() => {
53809
+ fetchLegend();
53810
+ }, [fetchLegend]);
53811
+ const updateLegend = useCallback(async (newLegend) => {
53812
+ if (!supabase || !effectiveCompanyId) {
53813
+ return { success: false, error: "Supabase client or company ID not available" };
53814
+ }
53815
+ try {
53816
+ const service = getEfficiencyLegendService(supabase);
53817
+ const result = await service.updateEfficiencyLegend(effectiveCompanyId, newLegend);
53818
+ if (result.success) {
53819
+ setLegend(newLegend);
53820
+ setError(null);
53821
+ } else {
53822
+ setError(result.error || "Failed to update efficiency legend");
53823
+ }
53824
+ return result;
53825
+ } catch (err) {
53826
+ const errorMsg = err.message || "Failed to update efficiency legend";
53827
+ setError(errorMsg);
53828
+ return { success: false, error: errorMsg };
53829
+ }
53830
+ }, [supabase, effectiveCompanyId]);
53831
+ return {
53832
+ legend,
53833
+ isLoading,
53834
+ error,
53835
+ updateLegend,
53836
+ refetch: fetchLegend
53837
+ };
53838
+ }
53839
+
53184
53840
  // src/views/TargetsView.utils.ts
53185
53841
  var calculatePPH = (cycleTime, breaks = [], shiftHours = 0) => {
53186
53842
  if (cycleTime === "" || cycleTime === 0) return "";
@@ -53207,369 +53863,245 @@ var getStoredLineState2 = (lineId) => {
53207
53863
  return false;
53208
53864
  }
53209
53865
  };
53210
- var BulkConfigureModal = ({
53866
+ var ConfigureLegendModal = ({
53211
53867
  isOpen,
53212
53868
  onClose,
53213
- lineWorkspaces,
53214
- lineNames,
53215
53869
  onSave,
53216
- selectedShift
53870
+ legend,
53871
+ canSave = true
53217
53872
  }) => {
53218
- const [selectedLine, setSelectedLine] = useState("");
53219
- const [selectedWorkspaces, setSelectedWorkspaces] = useState([]);
53220
- const [actionType, setActionType] = useState("assembly");
53221
- const [targetPPH, setTargetPPH] = useState("");
53222
- const [targetCycleTime, setTargetCycleTime] = useState("");
53223
- const [targetDayOutput, setTargetDayOutput] = useState("");
53224
- const [productId, setProductId] = useState("");
53225
- const [selectedCount, setSelectedCount] = useState(0);
53226
- const shiftHours = useMemo(() => {
53227
- if (selectedLine && lineWorkspaces[selectedLine]) {
53228
- return lineWorkspaces[selectedLine].shiftHours;
53229
- }
53230
- return 8;
53231
- }, [selectedLine, lineWorkspaces]);
53232
- const selectedLineBreaks = useMemo(() => {
53233
- if (selectedLine && lineWorkspaces[selectedLine]) {
53234
- return lineWorkspaces[selectedLine].breaks;
53235
- }
53236
- return [];
53237
- }, [selectedLine, lineWorkspaces]);
53238
- useEffect(() => {
53239
- if (!isOpen) {
53240
- setTimeout(() => {
53241
- setSelectedLine("");
53242
- setSelectedWorkspaces([]);
53243
- setActionType("assembly");
53244
- setTargetPPH("");
53245
- setTargetCycleTime("");
53246
- setTargetDayOutput("");
53247
- setProductId("");
53248
- }, 200);
53249
- }
53250
- }, [isOpen]);
53873
+ const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
53874
+ const [redThreshold, setRedThreshold] = useState(effectiveLegend.yellow_min);
53875
+ const [greenThreshold, setGreenThreshold] = useState(effectiveLegend.green_min);
53876
+ const [isSaving, setIsSaving] = useState(false);
53877
+ const [saveSuccess, setSaveSuccess] = useState(false);
53251
53878
  useEffect(() => {
53252
- if (selectedLine && lineWorkspaces[selectedLine]) {
53253
- setProductId(lineWorkspaces[selectedLine].productId);
53879
+ if (isOpen) {
53880
+ setRedThreshold(effectiveLegend.yellow_min);
53881
+ setGreenThreshold(effectiveLegend.green_min);
53882
+ setSaveSuccess(false);
53254
53883
  }
53255
- }, [selectedLine, lineWorkspaces]);
53256
- useEffect(() => {
53257
- setSelectedCount(selectedWorkspaces.length);
53258
- }, [selectedWorkspaces]);
53884
+ }, [isOpen, effectiveLegend]);
53259
53885
  const handleSave = async () => {
53260
- if (selectedWorkspaces.length === 0) {
53261
- toast.error("Please select at least one workspace");
53886
+ if (!canSave) {
53887
+ toast.error("You do not have permission to update the legend.");
53262
53888
  return;
53263
53889
  }
53264
- if (targetPPH === "" && targetCycleTime === "" && targetDayOutput === "") {
53265
- toast.error("Please enter at least one target value");
53890
+ if (redThreshold === "" || greenThreshold === "") {
53891
+ toast.error("Please enter valid threshold values");
53266
53892
  return;
53267
53893
  }
53268
- if (!selectedLine) {
53269
- toast.error("Please select a line");
53894
+ if (Number(redThreshold) >= Number(greenThreshold)) {
53895
+ toast.error("Red threshold must be lower than Green threshold");
53270
53896
  return;
53271
53897
  }
53272
- const updates = {
53273
- ...actionType && { actionType },
53274
- ...targetPPH !== "" && { targetPPH },
53275
- ...targetCycleTime !== "" && { targetCycleTime },
53276
- ...targetDayOutput !== "" && { targetDayOutput }
53277
- };
53278
- if (targetCycleTime !== "" && targetPPH === "") {
53279
- const lineBreaks = lineWorkspaces[selectedLine].breaks || [];
53280
- const lineShiftHours = lineWorkspaces[selectedLine].shiftHours;
53281
- const calculatedPPH = calculatePPH(targetCycleTime, lineBreaks, lineShiftHours);
53282
- if (calculatedPPH !== "") {
53283
- updates.targetPPH = calculatedPPH;
53284
- }
53285
- }
53286
- if (updates.targetPPH !== void 0 && targetDayOutput === "") {
53287
- const lineBreaks = lineWorkspaces[selectedLine].breaks || [];
53288
- const lineShiftHours = lineWorkspaces[selectedLine].shiftHours;
53289
- const calculatedOutput = calculateDayOutput(updates.targetPPH, lineShiftHours, lineBreaks);
53290
- if (calculatedOutput !== "") {
53291
- updates.targetDayOutput = calculatedOutput;
53292
- }
53293
- }
53294
- onSave({
53295
- lineId: selectedLine,
53296
- workspaceIds: selectedWorkspaces,
53297
- productId,
53298
- updates
53299
- });
53300
- onClose();
53301
- };
53302
- const toggleAllWorkspaces = (lineId) => {
53303
- if (!lineWorkspaces[lineId]) return;
53304
- const allWorkspaceIds = lineWorkspaces[lineId].workspaces.map((w) => w.id);
53305
- if (selectedWorkspaces.length === allWorkspaceIds.length) {
53306
- setSelectedWorkspaces([]);
53307
- } else {
53308
- setSelectedWorkspaces(allWorkspaceIds);
53898
+ if (Number(redThreshold) < 1 || Number(redThreshold) > 100 || Number(greenThreshold) > 100) {
53899
+ toast.error("Thresholds must be between 1 and 100");
53900
+ return;
53309
53901
  }
53310
- };
53311
- useEffect(() => {
53312
- const handleEscape = (e) => {
53313
- if (e.key === "Escape") {
53314
- onClose();
53902
+ const redMax = Number(redThreshold) - 1;
53903
+ const yellowMax = Number(greenThreshold) - 1;
53904
+ if (redMax < DEFAULT_EFFICIENCY_LEGEND.critical_threshold) {
53905
+ toast.error("Red threshold must be above 50% while critical alerts are fixed at <50%.");
53906
+ return;
53907
+ }
53908
+ setIsSaving(true);
53909
+ setSaveSuccess(false);
53910
+ try {
53911
+ const result = await onSave({
53912
+ green_min: Number(greenThreshold),
53913
+ green_max: 100,
53914
+ yellow_min: Number(redThreshold),
53915
+ yellow_max: yellowMax,
53916
+ red_min: 0,
53917
+ red_max: redMax,
53918
+ critical_threshold: DEFAULT_EFFICIENCY_LEGEND.critical_threshold
53919
+ });
53920
+ if (result.success) {
53921
+ setSaveSuccess(true);
53922
+ setTimeout(() => setSaveSuccess(false), 3e3);
53923
+ } else {
53924
+ toast.error(result.error || "Failed to update legend");
53315
53925
  }
53316
- };
53317
- if (isOpen) {
53318
- document.addEventListener("keydown", handleEscape);
53926
+ } catch (error) {
53927
+ console.error("Error saving legend:", error);
53928
+ toast.error("Failed to update legend");
53929
+ } finally {
53930
+ setIsSaving(false);
53319
53931
  }
53320
- return () => {
53321
- document.removeEventListener("keydown", handleEscape);
53322
- };
53323
- }, [isOpen, onClose]);
53932
+ };
53324
53933
  if (!isOpen) return null;
53325
53934
  return /* @__PURE__ */ jsx(
53326
53935
  "div",
53327
53936
  {
53328
- className: "fixed inset-0 bg-gray-900/50 backdrop-blur-sm z-50 flex items-center justify-center",
53937
+ className: "fixed inset-0 bg-gray-900/40 backdrop-blur-sm z-50 flex items-center justify-center transition-opacity duration-300",
53329
53938
  onClick: (e) => {
53330
53939
  if (e.target === e.currentTarget) {
53331
53940
  onClose();
53332
53941
  }
53333
53942
  },
53334
- children: /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-xl shadow-xl w-full max-w-3xl max-h-[90vh] flex flex-col animate-modal-in", children: [
53335
- /* @__PURE__ */ jsxs("div", { className: "px-6 py-4 border-b border-gray-200 flex items-center justify-between", children: [
53943
+ children: /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg shadow-xl w-full max-w-xl flex flex-col animate-modal-in transform transition-all", children: [
53944
+ /* @__PURE__ */ jsxs("div", { className: "px-6 py-5 border-b border-gray-100 flex items-center justify-between bg-white rounded-t-lg", children: [
53336
53945
  /* @__PURE__ */ jsxs("div", { children: [
53337
- /* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold text-gray-900", children: "Bulk Configure Targets" }),
53338
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Update multiple workspace targets at once" })
53946
+ /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-gray-900", children: "Performance Thresholds" }),
53947
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 mt-0.5", children: "Define acceptance criteria for production metrics." })
53339
53948
  ] }),
53340
53949
  /* @__PURE__ */ jsx(
53341
53950
  "button",
53342
53951
  {
53343
53952
  onClick: onClose,
53344
- className: "text-gray-400 hover:text-gray-500 transition-colors p-1 hover:bg-gray-100 rounded-lg",
53345
- children: /* @__PURE__ */ jsx(X, { className: "w-5 h-5" })
53953
+ className: "text-gray-400 hover:text-gray-600 transition-colors p-1.5 hover:bg-gray-50 rounded-md",
53954
+ children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
53346
53955
  }
53347
53956
  )
53348
53957
  ] }),
53349
- /* @__PURE__ */ jsxs("div", { className: "p-6 space-y-6 overflow-y-scroll flex-1 custom-scrollbar relative", children: [
53350
- /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
53351
- /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
53352
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
53353
- /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700", children: "Select Line" }),
53354
- /* @__PURE__ */ jsxs(
53355
- "select",
53356
- {
53357
- value: selectedLine,
53358
- onChange: (e) => {
53359
- setSelectedLine(e.target.value);
53360
- setSelectedWorkspaces([]);
53361
- },
53362
- 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",
53363
- children: [
53364
- /* @__PURE__ */ jsx("option", { value: "", children: "Select a line" }),
53365
- Object.entries(lineWorkspaces).map(([lineId, line]) => /* @__PURE__ */ jsx("option", { value: lineId, className: "py-2", children: lineNames[lineId] || lineId }, lineId))
53366
- ]
53367
- }
53368
- )
53369
- ] }),
53370
- selectedLine && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
53371
- /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700", children: "Product Code" }),
53372
- /* @__PURE__ */ jsx(
53373
- "input",
53374
- {
53375
- type: "text",
53376
- value: productId,
53377
- onChange: (e) => setProductId(e.target.value),
53378
- placeholder: "e.g., PROD-001",
53379
- 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"
53380
- }
53381
- )
53382
- ] })
53383
- ] }),
53384
- selectedLine && /* @__PURE__ */ jsxs(Fragment, { children: [
53385
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
53386
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
53387
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
53388
- /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700", children: "Select Workspaces" }),
53389
- selectedCount > 0 && /* @__PURE__ */ 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: [
53390
- selectedCount,
53391
- " selected"
53392
- ] })
53393
- ] }),
53394
- /* @__PURE__ */ jsx(
53395
- "button",
53396
- {
53397
- onClick: () => toggleAllWorkspaces(selectedLine),
53398
- className: "text-sm text-blue-600 hover:text-blue-700 font-medium px-2 py-1 rounded hover:bg-blue-50 transition-colors",
53399
- children: selectedWorkspaces.length === lineWorkspaces[selectedLine].workspaces.length ? "Deselect All" : "Select All"
53400
- }
53401
- )
53402
- ] }),
53403
- /* @__PURE__ */ 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__ */ jsxs(
53404
- "label",
53405
- {
53406
- 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"}`,
53407
- children: [
53408
- /* @__PURE__ */ jsx(
53409
- "input",
53410
- {
53411
- type: "checkbox",
53412
- checked: selectedWorkspaces.includes(workspace.id),
53413
- onChange: (e) => {
53414
- if (e.target.checked) {
53415
- setSelectedWorkspaces((prev) => [...prev, workspace.id]);
53416
- } else {
53417
- setSelectedWorkspaces((prev) => prev.filter((id3) => id3 !== workspace.id));
53418
- }
53419
- },
53420
- className: "rounded border-gray-300 text-blue-600 focus:ring-blue-500"
53421
- }
53422
- ),
53423
- /* @__PURE__ */ jsx("span", { className: `text-sm ${selectedWorkspaces.includes(workspace.id) ? "text-blue-900 font-medium" : "text-gray-900"}`, children: formatWorkspaceName(workspace.name, selectedLine) })
53424
- ]
53425
- },
53426
- workspace.id
53427
- )) })
53428
- ] }),
53429
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
53430
- /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700", children: "Action Type" }),
53431
- /* @__PURE__ */ jsxs(
53432
- "select",
53433
- {
53434
- value: actionType,
53435
- onChange: (e) => setActionType(e.target.value),
53436
- 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",
53437
- children: [
53438
- /* @__PURE__ */ jsx("option", { value: "assembly", className: "py-2", children: "Assembly" }),
53439
- /* @__PURE__ */ jsx("option", { value: "packaging", className: "py-2", children: "Packaging" })
53440
- ]
53441
- }
53442
- )
53443
- ] })
53958
+ /* @__PURE__ */ jsxs("div", { className: "p-8 space-y-8", children: [
53959
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2 pt-6", children: [
53960
+ " ",
53961
+ /* @__PURE__ */ jsxs("div", { className: "h-6 w-full rounded-md bg-gray-100 relative overflow-visible ring-1 ring-black/5 flex mt-2", children: [
53962
+ /* @__PURE__ */ jsx(
53963
+ "div",
53964
+ {
53965
+ className: "h-full bg-red-500 transition-all duration-500 ease-in-out relative group first:rounded-l-md",
53966
+ style: { width: `${Math.min(Number(redThreshold) || 0, 100)}%` },
53967
+ children: /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-white/0 group-hover:bg-white/10 transition-colors" })
53968
+ }
53969
+ ),
53970
+ /* @__PURE__ */ jsx(
53971
+ "div",
53972
+ {
53973
+ className: "h-full bg-yellow-400 transition-all duration-500 ease-in-out relative group",
53974
+ style: { width: `${Math.max(0, Math.min((Number(greenThreshold) || 0) - (Number(redThreshold) || 0), 100 - (Number(redThreshold) || 0)))}%` },
53975
+ children: /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-white/0 group-hover:bg-white/10 transition-colors" })
53976
+ }
53977
+ ),
53978
+ /* @__PURE__ */ jsx(
53979
+ "div",
53980
+ {
53981
+ className: "h-full bg-green-500 transition-all duration-500 ease-in-out relative group last:rounded-r-md",
53982
+ style: { width: `${Math.max(0, 100 - (Number(greenThreshold) || 0))}%` },
53983
+ children: /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-white/0 group-hover:bg-white/10 transition-colors" })
53984
+ }
53985
+ ),
53986
+ /* @__PURE__ */ jsx(
53987
+ "div",
53988
+ {
53989
+ className: "absolute -top-8 transition-all duration-500 ease-in-out z-20",
53990
+ style: { left: `${Math.min(Number(redThreshold) || 0, 100)}%`, transform: "translateX(-50%)" },
53991
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center", children: [
53992
+ /* @__PURE__ */ 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: [
53993
+ redThreshold,
53994
+ "%"
53995
+ ] }),
53996
+ /* @__PURE__ */ jsx("div", { className: "w-px h-3 bg-gray-400/50" })
53997
+ ] })
53998
+ }
53999
+ ),
54000
+ /* @__PURE__ */ jsx(
54001
+ "div",
54002
+ {
54003
+ className: "absolute -top-8 transition-all duration-500 ease-in-out z-20",
54004
+ style: { left: `${Math.min(Number(greenThreshold) || 0, 100)}%`, transform: "translateX(-50%)" },
54005
+ children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center", children: [
54006
+ /* @__PURE__ */ 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: [
54007
+ greenThreshold,
54008
+ "%"
54009
+ ] }),
54010
+ /* @__PURE__ */ jsx("div", { className: "w-px h-3 bg-gray-400/50" })
54011
+ ] })
54012
+ }
54013
+ ),
54014
+ /* @__PURE__ */ 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)}%` } }),
54015
+ /* @__PURE__ */ 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)}%` } })
53444
54016
  ] }),
53445
- /* @__PURE__ */ jsx("div", { className: "space-y-4", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-4", children: [
53446
- /* @__PURE__ */ jsxs("div", { children: [
53447
- /* @__PURE__ */ jsxs("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: [
53448
- "Target Cycle Time",
53449
- /* @__PURE__ */ jsx("span", { className: "ml-1 text-xs text-gray-400", children: "seconds per piece" })
53450
- ] }),
53451
- /* @__PURE__ */ jsxs("div", { className: "relative", children: [
54017
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between text-xs font-medium text-gray-400 pt-1", children: [
54018
+ /* @__PURE__ */ jsx("span", { children: "0%" }),
54019
+ /* @__PURE__ */ jsx("span", { children: "100%+" })
54020
+ ] })
54021
+ ] }),
54022
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-8 relative", children: [
54023
+ /* @__PURE__ */ jsx("div", { className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-gray-300", children: /* @__PURE__ */ jsx(ArrowRight, { className: "w-5 h-5" }) }),
54024
+ /* @__PURE__ */ jsxs("div", { children: [
54025
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Critical Limit" }),
54026
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
54027
+ /* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-red-500 mr-2.5" }),
54028
+ /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
53452
54029
  /* @__PURE__ */ jsx(
53453
54030
  "input",
53454
54031
  {
53455
54032
  type: "number",
53456
- value: targetCycleTime,
54033
+ value: redThreshold,
53457
54034
  onChange: (e) => {
53458
- const newValue = e.target.value ? Number(e.target.value) : "";
53459
- setTargetCycleTime(newValue);
53460
- if (newValue !== "" && newValue > 0) {
53461
- const pph = calculatePPH(newValue, selectedLineBreaks, shiftHours);
53462
- setTargetPPH(pph);
53463
- const dayOutput = calculateDayOutput(pph, shiftHours, selectedLineBreaks);
53464
- setTargetDayOutput(dayOutput);
53465
- } else if (newValue === "") {
53466
- setTargetPPH("");
53467
- setTargetDayOutput("");
53468
- }
54035
+ setRedThreshold(e.target.value === "" ? "" : Number(e.target.value));
54036
+ setSaveSuccess(false);
53469
54037
  },
53470
- 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",
53471
- min: "0",
53472
- step: "0.01",
53473
- placeholder: "Enter cycle time"
54038
+ min: 1,
54039
+ max: 100,
54040
+ 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",
54041
+ placeholder: "70"
53474
54042
  }
53475
54043
  ),
53476
- targetCycleTime !== "" && /* @__PURE__ */ jsx(
53477
- "button",
53478
- {
53479
- onClick: () => {
53480
- setTargetCycleTime("");
53481
- setTargetPPH("");
53482
- setTargetDayOutput("");
53483
- },
53484
- className: "absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-500",
53485
- children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
53486
- }
53487
- )
54044
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsx("span", { className: "text-gray-500 sm:text-sm", children: "%" }) })
53488
54045
  ] })
53489
54046
  ] }),
53490
- /* @__PURE__ */ jsxs("div", { children: [
53491
- /* @__PURE__ */ jsxs("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: [
53492
- "Target PPH",
53493
- /* @__PURE__ */ jsx("span", { className: "ml-1 text-xs text-gray-400", children: "pieces per hour" })
54047
+ /* @__PURE__ */ jsxs("p", { className: "mt-2 text-xs text-gray-500", children: [
54048
+ "Efficiency below ",
54049
+ /* @__PURE__ */ jsxs("span", { className: "font-medium text-gray-900", children: [
54050
+ redThreshold,
54051
+ "%"
53494
54052
  ] }),
53495
- /* @__PURE__ */ jsxs("div", { className: "relative", children: [
54053
+ " will show in ",
54054
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-red-600", children: "red" }),
54055
+ "."
54056
+ ] })
54057
+ ] }),
54058
+ /* @__PURE__ */ jsxs("div", { children: [
54059
+ /* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Target Limit" }),
54060
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
54061
+ /* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-green-500 mr-2.5" }),
54062
+ /* @__PURE__ */ jsxs("div", { className: "relative w-full", children: [
53496
54063
  /* @__PURE__ */ jsx(
53497
54064
  "input",
53498
54065
  {
53499
54066
  type: "number",
53500
- value: targetPPH,
54067
+ value: greenThreshold,
53501
54068
  onChange: (e) => {
53502
- const newValue = e.target.value ? Number(e.target.value) : "";
53503
- setTargetPPH(newValue);
53504
- if (newValue !== "") {
53505
- const dayOutput = calculateDayOutput(newValue, shiftHours, selectedLineBreaks);
53506
- setTargetDayOutput(dayOutput);
53507
- }
54069
+ setGreenThreshold(e.target.value === "" ? "" : Number(e.target.value));
54070
+ setSaveSuccess(false);
53508
54071
  },
53509
- 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",
53510
- min: "0",
53511
- step: "0.1",
53512
- placeholder: "Enter PPH"
54072
+ min: 1,
54073
+ max: 100,
54074
+ 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",
54075
+ placeholder: "80"
53513
54076
  }
53514
54077
  ),
53515
- targetPPH !== "" && /* @__PURE__ */ jsx(
53516
- "button",
53517
- {
53518
- onClick: () => {
53519
- setTargetPPH("");
53520
- setTargetDayOutput("");
53521
- },
53522
- className: "absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-500",
53523
- children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
53524
- }
53525
- )
54078
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsx("span", { className: "text-gray-500 sm:text-sm", children: "%" }) })
53526
54079
  ] })
53527
54080
  ] }),
53528
- /* @__PURE__ */ jsxs("div", { children: [
53529
- /* @__PURE__ */ jsxs("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: [
53530
- "Total Day Output",
53531
- /* @__PURE__ */ jsx("span", { className: "ml-1 text-xs text-gray-400", children: "pieces per day" })
54081
+ /* @__PURE__ */ jsxs("p", { className: "mt-2 text-xs text-gray-500", children: [
54082
+ "Efficiency above ",
54083
+ /* @__PURE__ */ jsxs("span", { className: "font-medium text-gray-900", children: [
54084
+ greenThreshold,
54085
+ "%"
53532
54086
  ] }),
53533
- /* @__PURE__ */ jsxs("div", { className: "relative", children: [
53534
- /* @__PURE__ */ jsx(
53535
- "input",
53536
- {
53537
- type: "number",
53538
- value: targetDayOutput,
53539
- onChange: (e) => setTargetDayOutput(e.target.value ? Number(e.target.value) : ""),
53540
- 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",
53541
- min: "0",
53542
- step: "1",
53543
- placeholder: "Enter day output"
53544
- }
53545
- ),
53546
- targetDayOutput !== "" && /* @__PURE__ */ jsx(
53547
- "button",
53548
- {
53549
- onClick: () => setTargetDayOutput(""),
53550
- className: "absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-500",
53551
- children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
53552
- }
53553
- )
53554
- ] })
54087
+ " will show in ",
54088
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-green-600", children: "green" }),
54089
+ "."
53555
54090
  ] })
53556
- ] }) })
53557
- ] }),
53558
- /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 h-10 bg-gradient-to-t from-white to-transparent pointer-events-none" })
54091
+ ] })
54092
+ ] })
53559
54093
  ] }),
53560
- /* @__PURE__ */ jsxs("div", { className: "px-6 py-4 bg-gray-50 border-t border-gray-200 flex justify-between items-center", children: [
53561
- /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-500", children: selectedCount > 0 && /* @__PURE__ */ jsxs("span", { children: [
53562
- "Updating ",
53563
- /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-900", children: selectedCount }),
53564
- " workspace",
53565
- selectedCount !== 1 ? "s" : ""
54094
+ /* @__PURE__ */ 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: [
54095
+ /* @__PURE__ */ jsx("div", { className: "min-h-[20px]", children: saveSuccess && /* @__PURE__ */ jsxs("span", { className: "text-sm text-green-600 font-medium flex items-center gap-1.5", children: [
54096
+ /* @__PURE__ */ jsx(CheckCircle2, { className: "w-4 h-4" }),
54097
+ "Saved successfully"
53566
54098
  ] }) }),
53567
- /* @__PURE__ */ jsxs("div", { className: "flex space-x-3", children: [
54099
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-3", children: [
53568
54100
  /* @__PURE__ */ jsx(
53569
54101
  "button",
53570
54102
  {
53571
54103
  onClick: onClose,
53572
- 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",
54104
+ 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",
53573
54105
  children: "Cancel"
53574
54106
  }
53575
54107
  ),
@@ -53577,10 +54109,9 @@ var BulkConfigureModal = ({
53577
54109
  "button",
53578
54110
  {
53579
54111
  onClick: handleSave,
53580
- disabled: !selectedLine || selectedWorkspaces.length === 0,
53581
- className: `px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200
53582
- ${!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"}`,
53583
- children: "Apply Changes"
54112
+ disabled: !canSave || isSaving,
54113
+ 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",
54114
+ children: isSaving ? "Saving..." : "Save Changes"
53584
54115
  }
53585
54116
  )
53586
54117
  ] })
@@ -53589,7 +54120,7 @@ var BulkConfigureModal = ({
53589
54120
  }
53590
54121
  );
53591
54122
  };
53592
- var BulkConfigureModal_default = BulkConfigureModal;
54123
+ var ConfigureLegendModal_default = ConfigureLegendModal;
53593
54124
  var SKUModal = ({
53594
54125
  isOpen,
53595
54126
  onClose,
@@ -53990,7 +54521,8 @@ var TargetsViewUI = ({
53990
54521
  { id: 0, name: "Day Shift" },
53991
54522
  { id: 1, name: "Night Shift" }
53992
54523
  ],
53993
- isBulkConfigureOpen,
54524
+ isConfigureLegendOpen,
54525
+ legend,
53994
54526
  navItems = [],
53995
54527
  currentPathname = "/targets",
53996
54528
  canSaveTargets = true,
@@ -54001,8 +54533,8 @@ var TargetsViewUI = ({
54001
54533
  onActionTypeChange,
54002
54534
  onShiftChange,
54003
54535
  onSaveLine,
54004
- onToggleBulkConfigure,
54005
- onBulkConfigure,
54536
+ onToggleConfigureLegend,
54537
+ onSaveLegend,
54006
54538
  onUpdateWorkspaceDisplayName,
54007
54539
  // SKU props
54008
54540
  skuEnabled = false,
@@ -54028,11 +54560,13 @@ var TargetsViewUI = ({
54028
54560
  /* @__PURE__ */ jsx("div", { className: "sm:absolute sm:right-0 w-full sm:w-auto", children: /* @__PURE__ */ jsxs(
54029
54561
  "button",
54030
54562
  {
54031
- 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",
54032
- onClick: onToggleBulkConfigure,
54563
+ 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"}`,
54564
+ onClick: onToggleConfigureLegend,
54565
+ disabled: !canSaveTargets,
54566
+ title: !canSaveTargets ? "You do not have permission to update the legend" : "",
54033
54567
  children: [
54034
- /* @__PURE__ */ jsx(Settings2, { className: "w-3 h-3 sm:w-4 sm:h-4 mr-1.5 sm:mr-2 inline-block" }),
54035
- "Bulk Configure"
54568
+ /* @__PURE__ */ jsx(Palette, { className: "w-3 h-3 sm:w-4 sm:h-4 mr-1.5 sm:mr-2 inline-block" }),
54569
+ "Configure Legend"
54036
54570
  ]
54037
54571
  }
54038
54572
  ) }),
@@ -54334,14 +54868,13 @@ var TargetsViewUI = ({
54334
54868
  )) })
54335
54869
  ] }) }),
54336
54870
  /* @__PURE__ */ jsx(
54337
- BulkConfigureModal_default,
54871
+ ConfigureLegendModal_default,
54338
54872
  {
54339
- isOpen: isBulkConfigureOpen,
54340
- onClose: onToggleBulkConfigure,
54341
- lineWorkspaces,
54342
- lineNames,
54343
- onSave: onBulkConfigure,
54344
- selectedShift
54873
+ isOpen: isConfigureLegendOpen,
54874
+ onClose: onToggleConfigureLegend,
54875
+ onSave: onSaveLegend,
54876
+ legend,
54877
+ canSave: canSaveTargets
54345
54878
  }
54346
54879
  )
54347
54880
  ] });
@@ -54387,7 +54920,7 @@ var TargetsView = ({
54387
54920
  () => lineIds.reduce((acc, id3) => ({ ...acc, [id3]: false }), {})
54388
54921
  );
54389
54922
  const [isLoading, setIsLoading] = useState(true);
54390
- const [isBulkConfigureOpen, setIsBulkConfigureOpen] = useState(false);
54923
+ const [isConfigureLegendOpen, setIsConfigureLegendOpen] = useState(false);
54391
54924
  const [selectedWorkspaces, setSelectedWorkspaces] = useState([]);
54392
54925
  const [selectedShift, setSelectedShift] = useState(0);
54393
54926
  const [shiftOptions, setShiftOptions] = useState([]);
@@ -54404,6 +54937,7 @@ var TargetsView = ({
54404
54937
  const supabase = useSupabase();
54405
54938
  const effectiveUserId = userId || "6bf6f271-1e55-4a95-9b89-1c3820b58739";
54406
54939
  const dashboardConfig = useDashboardConfig();
54940
+ const { legend, updateLegend } = useEfficiencyLegend(companyId);
54407
54941
  const { skus, isLoading: skusLoading } = useSKUs(companyId);
54408
54942
  const skuEnabled = dashboardConfig?.skuConfig?.enabled || false;
54409
54943
  useEffect(() => {
@@ -54839,41 +55373,23 @@ var TargetsView = ({
54839
55373
  console.log(`[handleSaveLine] Set savingLines to false for ${lineId} in finally block`);
54840
55374
  }
54841
55375
  }, [canSaveTargets, supabase, lineWorkspaces, selectedShift, lineNames, onSaveChanges, skuEnabled, dashboardConfig]);
54842
- const handleBulkConfigure = async (updates) => {
54843
- if (!actionIds) return;
54844
- if (updates.productId !== void 0) {
54845
- setLineWorkspaces((prev) => ({
54846
- ...prev,
54847
- [updates.lineId]: {
54848
- ...prev[updates.lineId],
54849
- productId: updates.productId || ""
54850
- }
54851
- }));
55376
+ const handleSaveLegend = useCallback(async (nextLegend) => {
55377
+ if (!canSaveTargets) {
55378
+ toast.error("You do not have permission to update the legend.");
55379
+ return { success: false, error: "Permission denied" };
55380
+ }
55381
+ const result = await updateLegend(nextLegend);
55382
+ if (result.success) {
55383
+ toast.success("Efficiency legend updated successfully");
55384
+ } else if (result.error) {
55385
+ toast.error(result.error);
55386
+ } else {
55387
+ toast.error("Failed to update efficiency legend");
54852
55388
  }
54853
- setLineWorkspaces((prev) => ({
54854
- ...prev,
54855
- [updates.lineId]: {
54856
- ...prev[updates.lineId],
54857
- workspaces: prev[updates.lineId].workspaces.map((ws) => {
54858
- if (!updates.workspaceIds.includes(ws.id)) return ws;
54859
- return {
54860
- ...ws,
54861
- ...updates.updates.actionType && {
54862
- actionType: updates.updates.actionType,
54863
- actionId: actionIds[updates.updates.actionType]
54864
- },
54865
- ...updates.updates.targetPPH !== void 0 && { targetPPH: updates.updates.targetPPH },
54866
- ...updates.updates.targetCycleTime !== void 0 && { targetCycleTime: updates.updates.targetCycleTime },
54867
- ...updates.updates.targetDayOutput !== void 0 && { targetDayOutput: updates.updates.targetDayOutput }
54868
- };
54869
- })
54870
- }
54871
- }));
54872
- toast.success(`Updated ${updates.workspaceIds.length} workspaces in ${lineNames[updates.lineId] || updates.lineId}`);
54873
- setIsBulkConfigureOpen(false);
54874
- };
54875
- const handleToggleBulkConfigure = () => {
54876
- setIsBulkConfigureOpen((prev) => !prev);
55389
+ return result;
55390
+ }, [canSaveTargets, updateLegend]);
55391
+ const handleToggleConfigureLegend = () => {
55392
+ setIsConfigureLegendOpen((prev) => !prev);
54877
55393
  };
54878
55394
  const handleNavigateBack = () => {
54879
55395
  if (router && router.push) {
@@ -54911,7 +55427,7 @@ var TargetsView = ({
54911
55427
  saveSuccess,
54912
55428
  selectedShift,
54913
55429
  shiftOptions,
54914
- isBulkConfigureOpen,
55430
+ isConfigureLegendOpen,
54915
55431
  navItems: [],
54916
55432
  currentPathname: "/targets",
54917
55433
  canSaveTargets,
@@ -54922,8 +55438,9 @@ var TargetsView = ({
54922
55438
  onActionTypeChange: handleActionTypeChange,
54923
55439
  onShiftChange: handleShiftChange,
54924
55440
  onSaveLine: handleSaveLine,
54925
- onToggleBulkConfigure: handleToggleBulkConfigure,
54926
- onBulkConfigure: handleBulkConfigure,
55441
+ onToggleConfigureLegend: handleToggleConfigureLegend,
55442
+ onSaveLegend: handleSaveLegend,
55443
+ legend,
54927
55444
  onUpdateWorkspaceDisplayName: handleUpdateWorkspaceDisplayName,
54928
55445
  skuEnabled,
54929
55446
  skus,
@@ -55304,6 +55821,20 @@ var WorkspaceDetailView = ({
55304
55821
  }, [monthlyData, range]);
55305
55822
  const formattedWorkspaceName = displayName || formatWorkspaceName3(workspace?.workspace_name || "", effectiveLineId);
55306
55823
  const shouldShowCycleTimeChart = showCycleTimeChart ?? formattedWorkspaceName.startsWith("FINAL ASSY");
55824
+ const idleClipDate = date || workspace?.date || calculatedOperationalDate || getOperationalDate(timezone);
55825
+ const idleClipShiftId = parsedShiftId ?? workspace?.shift_id;
55826
+ const idleClipFetchEnabled = Boolean(
55827
+ workspaceId && idleClipDate && idleClipShiftId !== void 0 && activeTab === "overview" && showIdleTime && !shouldShowCycleTimeChart
55828
+ );
55829
+ const {
55830
+ idleClips: idleTimeClips,
55831
+ clipClassifications: idleTimeClipClassifications
55832
+ } = useIdleTimeClipClassifications({
55833
+ workspaceId,
55834
+ date: idleClipDate,
55835
+ shiftId: idleClipShiftId,
55836
+ enabled: idleClipFetchEnabled
55837
+ });
55307
55838
  const handleBackNavigation = () => {
55308
55839
  if (returnUrl) {
55309
55840
  if (onNavigate) {
@@ -55690,7 +56221,11 @@ var WorkspaceDetailView = ({
55690
56221
  shiftStart: workspace.shift_start || "06:00",
55691
56222
  shiftEnd: workspace.shift_end,
55692
56223
  showIdleTime,
55693
- idleTimeHourly: workspace.idle_time_hourly
56224
+ idleTimeHourly: workspace.idle_time_hourly,
56225
+ idleTimeClips,
56226
+ idleTimeClipClassifications,
56227
+ shiftDate: idleClipDate,
56228
+ timezone
55694
56229
  }
55695
56230
  )
55696
56231
  }
@@ -55816,7 +56351,11 @@ var WorkspaceDetailView = ({
55816
56351
  shiftStart: workspace.shift_start || "06:00",
55817
56352
  shiftEnd: workspace.shift_end,
55818
56353
  showIdleTime,
55819
- idleTimeHourly: workspace.idle_time_hourly
56354
+ idleTimeHourly: workspace.idle_time_hourly,
56355
+ idleTimeClips,
56356
+ idleTimeClipClassifications,
56357
+ shiftDate: idleClipDate,
56358
+ timezone
55820
56359
  }
55821
56360
  )
55822
56361
  }
@@ -58950,7 +59489,12 @@ var ImprovementCenterView = () => {
58950
59489
  const { user } = useAuth();
58951
59490
  const dashboardConfig = useDashboardConfig();
58952
59491
  const entityConfig = useEntityConfig();
58953
- const [currentDate, setCurrentDate] = useState(/* @__PURE__ */ new Date());
59492
+ const timezone = useAppTimezone();
59493
+ const today = new Date((/* @__PURE__ */ new Date()).toLocaleString("en-US", { timeZone: timezone }));
59494
+ const [selectedMonth, setSelectedMonth] = useState(today.getMonth());
59495
+ const [selectedYear, setSelectedYear] = useState(today.getFullYear());
59496
+ const monthBounds = useMemo(() => getMonthKeyBounds(selectedYear, selectedMonth), [selectedYear, selectedMonth]);
59497
+ const [dateRange, setDateRange] = useState(monthBounds);
58954
59498
  const [selectedLineId, setSelectedLineId] = useState("all");
58955
59499
  const [selectedStatus, setSelectedStatus] = useState("all");
58956
59500
  const [selectedShift, setSelectedShift] = useState("all");
@@ -58961,14 +59505,36 @@ var ImprovementCenterView = () => {
58961
59505
  const [loadError, setLoadError] = useState(null);
58962
59506
  const [teamMembers, setTeamMembers] = useState([]);
58963
59507
  const [companyLines, setCompanyLines] = useState([]);
59508
+ const [isFilterOpen, setIsFilterOpen] = useState(false);
59509
+ const filterRef = useRef(null);
59510
+ useEffect(() => {
59511
+ const handleClickOutside = (event) => {
59512
+ if (filterRef.current && !filterRef.current.contains(event.target)) {
59513
+ setIsFilterOpen(false);
59514
+ }
59515
+ };
59516
+ document.addEventListener("mousedown", handleClickOutside);
59517
+ return () => document.removeEventListener("mousedown", handleClickOutside);
59518
+ }, []);
58964
59519
  const computeWeeksOpen = (firstSeenAt) => {
58965
59520
  if (!firstSeenAt) return void 0;
58966
- const firstSeen = new Date(firstSeenAt);
59521
+ const firstSeen = new Date(new Date(firstSeenAt).toLocaleString("en-US", { timeZone: timezone }));
58967
59522
  if (Number.isNaN(firstSeen.getTime())) return void 0;
58968
- const now2 = /* @__PURE__ */ new Date();
59523
+ const now2 = new Date((/* @__PURE__ */ new Date()).toLocaleString("en-US", { timeZone: timezone }));
58969
59524
  const diffDays = Math.max(0, Math.floor((now2.getTime() - firstSeen.getTime()) / (1e3 * 60 * 60 * 24)));
58970
59525
  return Math.max(1, Math.ceil(diffDays / 7));
58971
59526
  };
59527
+ const formatOpenedDate = (firstSeenAt) => {
59528
+ if (!firstSeenAt) return void 0;
59529
+ const raw = new Date(firstSeenAt);
59530
+ if (Number.isNaN(raw.getTime())) return void 0;
59531
+ const zoned = new Date(raw.toLocaleString("en-US", { timeZone: timezone }));
59532
+ const day = zoned.getDate();
59533
+ const month = zoned.toLocaleString("en-US", { month: "short" });
59534
+ const dayMod = day % 100;
59535
+ const suffix = dayMod >= 11 && dayMod <= 13 ? "th" : day % 10 === 1 ? "st" : day % 10 === 2 ? "nd" : day % 10 === 3 ? "rd" : "th";
59536
+ return `${day}${suffix} ${month}`;
59537
+ };
58972
59538
  const configuredLines = useMemo(() => {
58973
59539
  return entityConfig.lines || entityConfig.lineNames || {};
58974
59540
  }, [entityConfig.lines, entityConfig.lineNames]);
@@ -59077,14 +59643,38 @@ var ImprovementCenterView = () => {
59077
59643
  cancelled = true;
59078
59644
  };
59079
59645
  }, [supabase, companyId]);
59080
- const nextMonth = () => {
59081
- setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1));
59082
- };
59083
- const prevMonth = () => {
59084
- setCurrentDate(new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1));
59646
+ useEffect(() => {
59647
+ setDateRange(monthBounds);
59648
+ }, [monthBounds]);
59649
+ const handleRangeChange = (newRange) => {
59650
+ setDateRange(newRange);
59651
+ trackCoreEvent("Improvement Center Date Range Changed", {
59652
+ start_date: newRange.startKey,
59653
+ end_date: newRange.endKey
59654
+ });
59085
59655
  };
59086
- const formatMonth = (date) => {
59087
- return date.toLocaleDateString("en-US", { month: "long", year: "numeric" });
59656
+ const handleMonthNavigate = (newMonth, newYear) => {
59657
+ let validMonth = newMonth;
59658
+ let validYear = newYear;
59659
+ if (validMonth < 0) {
59660
+ validMonth = 11;
59661
+ validYear -= 1;
59662
+ } else if (validMonth > 11) {
59663
+ validMonth = 0;
59664
+ validYear += 1;
59665
+ }
59666
+ if (validYear < 2023) return;
59667
+ if (validYear > today.getFullYear() || validYear === today.getFullYear() && validMonth > today.getMonth()) {
59668
+ return;
59669
+ }
59670
+ const nextBounds = getMonthKeyBounds(validYear, validMonth);
59671
+ setSelectedMonth(validMonth);
59672
+ setSelectedYear(validYear);
59673
+ setDateRange(nextBounds);
59674
+ trackCoreEvent("Improvement Center Month Changed", {
59675
+ direction: validMonth > selectedMonth || validYear > selectedYear ? "next" : "previous",
59676
+ new_month: `${validYear}-${String(validMonth + 1).padStart(2, "0")}`
59677
+ });
59088
59678
  };
59089
59679
  useEffect(() => {
59090
59680
  let cancelled = false;
@@ -59102,7 +59692,9 @@ var ImprovementCenterView = () => {
59102
59692
  status: "all",
59103
59693
  limit: "500"
59104
59694
  });
59105
- params.set("month", `${currentDate.getFullYear()}-${String(currentDate.getMonth() + 1).padStart(2, "0")}`);
59695
+ params.set("month", `${selectedYear}-${String(selectedMonth + 1).padStart(2, "0")}`);
59696
+ params.set("start_date", dateRange.startKey);
59697
+ params.set("end_date", dateRange.endKey);
59106
59698
  if (scopeLineIds.length > 0) {
59107
59699
  params.set("line_ids", scopeLineIds.join(","));
59108
59700
  }
@@ -59133,7 +59725,7 @@ var ImprovementCenterView = () => {
59133
59725
  return () => {
59134
59726
  cancelled = true;
59135
59727
  };
59136
- }, [supabase, companyId, scopeLineIdsKey, currentDate]);
59728
+ }, [supabase, companyId, scopeLineIdsKey, selectedMonth, selectedYear, dateRange.startKey, dateRange.endKey]);
59137
59729
  const teamMembersById = useMemo(() => {
59138
59730
  return new Map(teamMembers.map((member) => [member.id, member]));
59139
59731
  }, [teamMembers]);
@@ -59175,6 +59767,15 @@ var ImprovementCenterView = () => {
59175
59767
  };
59176
59768
  }, [recommendations, selectedLineId, selectedShift, selectedWeeksRange, selectedMemberId]);
59177
59769
  const clearFilters = () => {
59770
+ trackCoreEvent("Improvement Center Filters Cleared", {
59771
+ previous_filters: {
59772
+ line_id: selectedLineId,
59773
+ status: selectedStatus,
59774
+ shift: selectedShift,
59775
+ weeks_range: selectedWeeksRange,
59776
+ member_id: selectedMemberId
59777
+ }
59778
+ });
59178
59779
  setSelectedLineId("all");
59179
59780
  setSelectedStatus("all");
59180
59781
  setSelectedShift("all");
@@ -59208,8 +59809,60 @@ var ImprovementCenterView = () => {
59208
59809
  setSelectedLineId("all");
59209
59810
  }
59210
59811
  }, [scopeLineIds, selectedLineId]);
59812
+ useEffect(() => {
59813
+ trackCoreEvent("Improvement Center Viewed", {
59814
+ company_id: companyId,
59815
+ user_role: user?.role_level,
59816
+ initial_month: `${selectedYear}-${String(selectedMonth + 1).padStart(2, "0")}`,
59817
+ total_scope_lines: scopeLineIds.length
59818
+ });
59819
+ }, []);
59820
+ const handleStatusFilterChange = (status) => {
59821
+ trackCoreEvent("Improvement Center Filter Applied", {
59822
+ filter_type: "status",
59823
+ filter_value: status,
59824
+ previous_value: selectedStatus
59825
+ });
59826
+ setSelectedStatus(status);
59827
+ };
59828
+ const handleShiftFilterChange = (shift) => {
59829
+ trackCoreEvent("Improvement Center Filter Applied", {
59830
+ filter_type: "shift",
59831
+ filter_value: shift,
59832
+ previous_value: selectedShift
59833
+ });
59834
+ setSelectedShift(shift);
59835
+ };
59836
+ const handleWeeksFilterChange = (weeksRange) => {
59837
+ trackCoreEvent("Improvement Center Filter Applied", {
59838
+ filter_type: "weeks_open",
59839
+ filter_value: weeksRange,
59840
+ previous_value: selectedWeeksRange
59841
+ });
59842
+ setSelectedWeeksRange(weeksRange);
59843
+ };
59844
+ const handleMemberFilterChange = (memberId) => {
59845
+ const memberName = memberId === "all" ? "all" : teamMembers.find((m) => m.id === memberId)?.name || memberId;
59846
+ trackCoreEvent("Improvement Center Filter Applied", {
59847
+ filter_type: "member",
59848
+ filter_value: memberName,
59849
+ member_id: memberId,
59850
+ previous_value: selectedMemberId
59851
+ });
59852
+ setSelectedMemberId(memberId);
59853
+ };
59854
+ const handleLineFilterChange = (lineId) => {
59855
+ const lineName = lineId === "all" ? "all" : lineNameById.get(lineId) || lineId;
59856
+ trackCoreEvent("Improvement Center Filter Applied", {
59857
+ filter_type: "line",
59858
+ filter_value: lineName,
59859
+ line_id: lineId,
59860
+ previous_value: selectedLineId
59861
+ });
59862
+ setSelectedLineId(lineId);
59863
+ };
59211
59864
  return /* @__PURE__ */ jsxs("div", { className: "min-h-screen bg-gray-50 flex flex-col", children: [
59212
- /* @__PURE__ */ jsx("header", { className: "sticky top-0 z-10 bg-white border-b border-gray-200 flex-shrink-0", children: /* @__PURE__ */ jsx("div", { className: "px-4 sm:px-6 lg:px-8 py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
59865
+ /* @__PURE__ */ jsx("header", { className: "sticky top-0 z-30 bg-white border-b border-gray-200 flex-shrink-0", children: /* @__PURE__ */ jsx("div", { className: "px-4 sm:px-6 lg:px-8 py-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
59213
59866
  /* @__PURE__ */ jsx("div", { className: "flex items-center gap-4 min-w-[120px]", children: /* @__PURE__ */ jsx(
59214
59867
  BackButtonMinimal,
59215
59868
  {
@@ -59221,187 +59874,173 @@ var ImprovementCenterView = () => {
59221
59874
  /* @__PURE__ */ jsx("h1", { className: "text-2xl lg:text-3xl font-semibold text-gray-900 text-center", children: "Improvement Center" }),
59222
59875
  /* @__PURE__ */ 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" })
59223
59876
  ] }),
59224
- /* @__PURE__ */ jsx("div", { className: "min-w-[120px]" })
59877
+ /* @__PURE__ */ jsx("div", { className: "min-w-[180px] flex justify-end", children: /* @__PURE__ */ jsx(
59878
+ MonthlyRangeFilter_default,
59879
+ {
59880
+ month: selectedMonth,
59881
+ year: selectedYear,
59882
+ timezone,
59883
+ value: dateRange,
59884
+ onChange: handleRangeChange,
59885
+ onMonthNavigate: handleMonthNavigate
59886
+ }
59887
+ ) })
59225
59888
  ] }) }) }),
59226
59889
  /* @__PURE__ */ jsxs("main", { className: "flex-1 p-4 sm:p-6 max-w-7xl mx-auto w-full", children: [
59227
- /* @__PURE__ */ jsx("div", { className: "flex justify-center mb-6", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 bg-white rounded-full px-4 py-2 border border-gray-200 shadow-sm", children: [
59228
- /* @__PURE__ */ jsx(
59229
- "button",
59230
- {
59231
- onClick: prevMonth,
59232
- className: "p-1 rounded-full hover:bg-gray-100 text-gray-500 transition-colors",
59233
- children: /* @__PURE__ */ jsx(ChevronLeftIcon, { className: "w-5 h-5" })
59234
- }
59235
- ),
59236
- /* @__PURE__ */ jsx("span", { className: "text-base font-semibold text-gray-900 min-w-[160px] text-center", children: formatMonth(currentDate) }),
59237
- /* @__PURE__ */ jsx(
59890
+ /* @__PURE__ */ 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: [
59891
+ /* @__PURE__ */ jsx("div", { className: "flex p-1 bg-gray-50 border border-gray-200 rounded-lg", children: [
59892
+ { id: "all", label: "All", count: stats.all },
59893
+ { id: "resolved", label: "Resolved", count: stats.resolved, icon: CheckCircleIcon },
59894
+ { id: "unresolved", label: "Unresolved", count: stats.unresolved, icon: XCircleIcon }
59895
+ ].map((tab) => /* @__PURE__ */ jsxs(
59238
59896
  "button",
59239
59897
  {
59240
- onClick: nextMonth,
59241
- className: "p-1 rounded-full hover:bg-gray-100 text-gray-500 transition-colors",
59242
- children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "w-5 h-5" })
59243
- }
59244
- )
59245
- ] }) }),
59246
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col md:flex-row items-center justify-between gap-4 mb-6", children: [
59247
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
59248
- /* @__PURE__ */ jsxs(
59249
- "button",
59250
- {
59251
- onClick: () => setSelectedStatus("all"),
59252
- 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"}`,
59253
- children: [
59254
- "All ",
59255
- /* @__PURE__ */ jsx("span", { className: "ml-1.5 bg-gray-100 px-2 py-0.5 rounded-full text-xs text-gray-600", children: stats.all })
59256
- ]
59257
- }
59258
- ),
59259
- /* @__PURE__ */ jsx("div", { className: "h-4 w-px bg-gray-300 mx-1" }),
59260
- /* @__PURE__ */ jsxs(
59261
- "button",
59262
- {
59263
- onClick: () => setSelectedStatus("resolved"),
59264
- 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"}`,
59265
- children: [
59266
- /* @__PURE__ */ jsx(CheckCircleIcon, { className: "w-4 h-4 text-green-500" }),
59267
- "Resolved ",
59268
- stats.resolved
59269
- ]
59270
- }
59271
- ),
59898
+ onClick: () => handleStatusFilterChange(tab.id),
59899
+ 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"}`,
59900
+ children: [
59901
+ tab.icon && /* @__PURE__ */ jsx(tab.icon, { className: `w-4 h-4 ${selectedStatus === tab.id ? "text-blue-600" : "text-gray-400"}` }),
59902
+ tab.label,
59903
+ /* @__PURE__ */ 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 })
59904
+ ]
59905
+ },
59906
+ tab.id
59907
+ )) }),
59908
+ /* @__PURE__ */ jsxs("div", { className: "relative", ref: filterRef, children: [
59272
59909
  /* @__PURE__ */ jsxs(
59273
59910
  "button",
59274
59911
  {
59275
- onClick: () => setSelectedStatus("unresolved"),
59276
- 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"}`,
59277
- children: [
59278
- /* @__PURE__ */ jsx(XCircleIcon, { className: "w-4 h-4 text-red-500" }),
59279
- "Unresolved ",
59280
- stats.unresolved
59281
- ]
59282
- }
59283
- )
59284
- ] }),
59285
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 w-full md:w-auto", children: [
59286
- /* @__PURE__ */ jsxs(
59287
- "select",
59288
- {
59289
- value: selectedShift,
59290
- onChange: (e) => setSelectedShift(e.target.value),
59291
- 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",
59292
- 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` },
59293
- children: [
59294
- /* @__PURE__ */ jsx("option", { value: "all", children: "All Shifts" }),
59295
- shiftOptions.filter((s) => s !== "all").map((shift) => /* @__PURE__ */ jsx("option", { value: shift, children: shift }, shift))
59296
- ]
59297
- }
59298
- ),
59299
- /* @__PURE__ */ jsx(
59300
- "select",
59301
- {
59302
- value: selectedWeeksRange,
59303
- onChange: (e) => setSelectedWeeksRange(e.target.value),
59304
- 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",
59305
- 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` },
59306
- children: weekOptions.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.id, children: opt.label }, opt.id))
59307
- }
59308
- ),
59309
- /* @__PURE__ */ jsxs(
59310
- "select",
59311
- {
59312
- value: selectedMemberId,
59313
- onChange: (e) => setSelectedMemberId(e.target.value),
59314
- 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",
59315
- 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` },
59912
+ onClick: () => setIsFilterOpen(!isFilterOpen),
59913
+ 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"}`,
59316
59914
  children: [
59317
- /* @__PURE__ */ jsx("option", { value: "all", children: "All Members" }),
59318
- teamMembers.map((member) => /* @__PURE__ */ jsx("option", { value: member.id, children: member.name }, member.id))
59915
+ /* @__PURE__ */ jsx(FunnelIcon, { className: "w-4 h-4" }),
59916
+ /* @__PURE__ */ jsx("span", { children: "Filters" }),
59917
+ (selectedShift !== "all" || selectedWeeksRange !== "all" || selectedMemberId !== "all" || selectedLineId !== "all") && /* @__PURE__ */ 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 }),
59918
+ /* @__PURE__ */ jsx(ChevronDownIcon, { className: `w-3 h-3 ml-1 transition-transform ${isFilterOpen ? "rotate-180" : ""}` })
59319
59919
  ]
59320
59920
  }
59321
59921
  ),
59322
- /* @__PURE__ */ jsx(
59323
- "select",
59324
- {
59325
- value: selectedLineId,
59326
- onChange: (e) => setSelectedLineId(e.target.value),
59327
- 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",
59328
- 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` },
59329
- children: lineOptions.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.id, children: opt.label }, opt.id))
59330
- }
59331
- )
59922
+ isFilterOpen && /* @__PURE__ */ 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: [
59923
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
59924
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-900", children: "Filter View" }),
59925
+ (selectedShift !== "all" || selectedWeeksRange !== "all" || selectedMemberId !== "all" || selectedLineId !== "all") && /* @__PURE__ */ jsx(
59926
+ "button",
59927
+ {
59928
+ onClick: clearFilters,
59929
+ className: "text-xs text-red-600 hover:text-red-700 font-medium",
59930
+ children: "Clear all"
59931
+ }
59932
+ )
59933
+ ] }),
59934
+ /* @__PURE__ */ jsx("div", { className: "space-y-3", children: [
59935
+ { value: selectedShift, onChange: handleShiftFilterChange, options: shiftOptions, label: "Shift" },
59936
+ { value: selectedWeeksRange, onChange: handleWeeksFilterChange, options: weekOptions.map((o) => o.id), labels: weekOptions, label: "Duration" },
59937
+ { value: selectedMemberId, onChange: handleMemberFilterChange, options: ["all", ...teamMembers.map((m) => m.id)], labels: teamMembers, label: "Member" },
59938
+ { value: selectedLineId, onChange: handleLineFilterChange, options: lineOptions.map((o) => o.id), labels: lineOptions, label: "Line" }
59939
+ ].map((filter2, idx) => /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
59940
+ /* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide ml-1", children: filter2.label }),
59941
+ /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsx(
59942
+ "select",
59943
+ {
59944
+ value: filter2.value,
59945
+ onChange: (e) => filter2.onChange(e.target.value),
59946
+ 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",
59947
+ 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` },
59948
+ children: filter2.options.map((opt) => {
59949
+ const val = typeof opt === "string" ? opt : opt.id;
59950
+ let label = val;
59951
+ if (filter2.labels) {
59952
+ const found = filter2.labels.find((l) => (l.id || l.user_id || l) === val);
59953
+ label = found ? found.label || found.name || found : val;
59954
+ }
59955
+ if (val === "all") label = `All ${filter2.label}s`;
59956
+ return /* @__PURE__ */ jsx("option", { value: val, children: label }, val);
59957
+ })
59958
+ }
59959
+ ) })
59960
+ ] }, idx)) })
59961
+ ] })
59332
59962
  ] })
59333
59963
  ] }),
59334
59964
  /* @__PURE__ */ jsxs("div", { className: "grid gap-6", children: [
59335
- filteredRecommendations.length > 0 ? filteredRecommendations.map((rec, index) => /* @__PURE__ */ jsx(
59336
- motion.div,
59337
- {
59338
- initial: { opacity: 0, y: 20 },
59339
- animate: { opacity: 1, y: 0 },
59340
- transition: { delay: index * 0.1 },
59341
- className: `bg-white rounded-xl shadow-sm border overflow-hidden ${rec.ticket_status === "solved" ? "border-green-200" : "border-gray-200"}`,
59342
- children: /* @__PURE__ */ jsx("div", { className: "p-6 relative", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col md:flex-row gap-6", children: [
59343
- /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-4", children: [
59344
- /* @__PURE__ */ jsx("div", { className: "flex items-start justify-between", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3 w-full", children: [
59345
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
59346
- rec.ticket_status === "solved" ? /* @__PURE__ */ 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: [
59347
- /* @__PURE__ */ jsx(CheckCircleIcon, { className: "w-3.5 h-3.5" }),
59348
- "Resolved"
59349
- ] }) : /* @__PURE__ */ 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: [
59350
- /* @__PURE__ */ jsx(ClockIcon, { className: "w-3.5 h-3.5" }),
59351
- "Open for ",
59352
- rec.weeks_open || 1,
59353
- " week",
59354
- (rec.weeks_open || 1) !== 1 ? "s" : ""
59355
- ] }),
59356
- rec.assigned_user_ids && rec.assigned_user_ids.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
59357
- /* @__PURE__ */ jsx("div", { className: "h-3 w-px bg-gray-200" }),
59358
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: rec.assigned_user_ids.map((uid) => {
59359
- const user2 = teamMembersById.get(uid);
59360
- if (!user2) {
59965
+ filteredRecommendations.length > 0 ? filteredRecommendations.map((rec, index) => {
59966
+ const weeksOpen = rec.weeks_open || 1;
59967
+ const openLabel = weeksOpen <= 1 ? "Opened this week" : `Opened for ${weeksOpen} weeks`;
59968
+ const openedOnLabel = formatOpenedDate(rec.first_seen_at);
59969
+ return /* @__PURE__ */ jsx(
59970
+ motion.div,
59971
+ {
59972
+ initial: { opacity: 0, y: 20 },
59973
+ animate: { opacity: 1, y: 0 },
59974
+ transition: { delay: index * 0.1 },
59975
+ className: `bg-white rounded-xl shadow-sm border overflow-hidden ${rec.ticket_status === "solved" ? "border-green-200" : "border-gray-200"}`,
59976
+ children: /* @__PURE__ */ jsx("div", { className: "p-6 relative", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col md:flex-row gap-6", children: [
59977
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-4", children: [
59978
+ /* @__PURE__ */ jsx("div", { className: "flex items-start justify-between", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3 w-full", children: [
59979
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
59980
+ /* @__PURE__ */ 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: [
59981
+ "#",
59982
+ rec.issue_number ?? index + 1
59983
+ ] }),
59984
+ rec.ticket_status === "solved" ? /* @__PURE__ */ 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: [
59985
+ /* @__PURE__ */ jsx(CheckCircleIcon, { className: "w-3.5 h-3.5" }),
59986
+ "Resolved"
59987
+ ] }) : /* @__PURE__ */ 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: [
59988
+ /* @__PURE__ */ jsx(ClockIcon, { className: "w-3.5 h-3.5" }),
59989
+ openLabel
59990
+ ] }),
59991
+ openedOnLabel && /* @__PURE__ */ 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: [
59992
+ "Opened on ",
59993
+ openedOnLabel
59994
+ ] }),
59995
+ rec.assigned_user_ids && rec.assigned_user_ids.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
59996
+ /* @__PURE__ */ jsx("div", { className: "h-3 w-px bg-gray-200" }),
59997
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: rec.assigned_user_ids.map((uid) => {
59998
+ const user2 = teamMembersById.get(uid);
59999
+ if (!user2) {
60000
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
60001
+ /* @__PURE__ */ 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: "??" }),
60002
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-500", children: "Unknown" })
60003
+ ] }, uid);
60004
+ }
59361
60005
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
59362
- /* @__PURE__ */ 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: "??" }),
59363
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-500", children: "Unknown" })
59364
- ] }, uid);
59365
- }
59366
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
59367
- /* @__PURE__ */ 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 }),
59368
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-600", children: user2.name })
59369
- ] }, user2.id);
59370
- }) })
59371
- ] })
59372
- ] }),
59373
- /* @__PURE__ */ jsxs("div", { children: [
59374
- /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-900 pr-12", children: rec.title }),
59375
- /* @__PURE__ */ jsxs("p", { className: "text-sm font-medium text-gray-500 mt-1 flex items-center gap-2", children: [
59376
- rec.location,
59377
- " \u2022 ",
59378
- rec.line,
59379
- (rec.shift_label || rec.shift_id !== void 0) && /* @__PURE__ */ jsxs(Fragment, { children: [
59380
- /* @__PURE__ */ jsx("span", { children: "\u2022" }),
59381
- /* @__PURE__ */ jsx("span", { className: "uppercase tracking-wide text-xs pt-0.5 text-gray-500 font-medium", children: rec.shift_label || `Shift ${rec.shift_id}` })
60006
+ /* @__PURE__ */ 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 }),
60007
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-600", children: user2.name })
60008
+ ] }, user2.id);
60009
+ }) })
60010
+ ] })
60011
+ ] }),
60012
+ /* @__PURE__ */ jsxs("div", { children: [
60013
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-900 pr-12", children: rec.title }),
60014
+ /* @__PURE__ */ jsxs("p", { className: "text-sm font-medium text-gray-500 mt-1 flex items-center gap-2", children: [
60015
+ rec.location,
60016
+ " \u2022 ",
60017
+ rec.line,
60018
+ (rec.shift_label || rec.shift_id !== void 0) && /* @__PURE__ */ jsxs(Fragment, { children: [
60019
+ /* @__PURE__ */ jsx("span", { children: "\u2022" }),
60020
+ /* @__PURE__ */ jsx("span", { className: "uppercase tracking-wide text-xs pt-0.5 text-gray-500 font-medium", children: rec.shift_label || `Shift ${rec.shift_id}` })
60021
+ ] })
59382
60022
  ] })
59383
60023
  ] })
59384
- ] })
59385
- ] }) }),
59386
- /* @__PURE__ */ jsx("div", { className: "prose prose-sm text-gray-600", children: /* @__PURE__ */ jsx("p", { children: rec.description }) }),
59387
- /* @__PURE__ */ jsx("div", { className: "bg-amber-50 border border-amber-100 rounded-lg p-3", children: /* @__PURE__ */ jsxs("p", { className: "text-sm text-amber-800 font-medium flex items-start gap-2", children: [
59388
- /* @__PURE__ */ jsx(ChartBarIcon, { className: "w-5 h-5 flex-shrink-0" }),
59389
- "Impact: ",
59390
- rec.impact
59391
- ] }) })
59392
- ] }),
59393
- /* @__PURE__ */ 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__ */ jsx(
59394
- EvidenceCarousel,
59395
- {
59396
- evidence: rec.evidence,
59397
- recId: rec.id,
59398
- clipsService
59399
- }
59400
- ) : /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500", children: isLoading ? "Loading evidence\u2026" : loadError ? loadError : "No evidence available" }) })
59401
- ] }) })
59402
- },
59403
- rec.id
59404
- )) : (
60024
+ ] }) }),
60025
+ /* @__PURE__ */ jsx("div", { className: "prose prose-sm text-gray-600", children: /* @__PURE__ */ jsx("p", { children: rec.description }) }),
60026
+ rec.resolution_instructions && /* @__PURE__ */ jsx("div", { className: "bg-blue-50 border border-blue-100 rounded-lg p-3", children: /* @__PURE__ */ jsxs("p", { className: "text-sm text-blue-800 font-medium flex items-start gap-2", children: [
60027
+ /* @__PURE__ */ jsx(CheckCircleIcon, { className: "w-5 h-5 flex-shrink-0" }),
60028
+ rec.resolution_instructions
60029
+ ] }) })
60030
+ ] }),
60031
+ /* @__PURE__ */ 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__ */ jsx(
60032
+ EvidenceCarousel,
60033
+ {
60034
+ evidence: rec.evidence,
60035
+ recId: rec.id,
60036
+ clipsService
60037
+ }
60038
+ ) : /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500", children: isLoading ? "Loading evidence\u2026" : loadError ? loadError : "No evidence available" }) })
60039
+ ] }) })
60040
+ },
60041
+ rec.id
60042
+ );
60043
+ }) : (
59405
60044
  // Success State (Zero Tickets)
59406
60045
  /* @__PURE__ */ jsxs("div", { className: "text-center py-16 bg-white rounded-xl border border-gray-200 shadow-sm", children: [
59407
60046
  /* @__PURE__ */ jsx("div", { className: "mx-auto flex items-center justify-center w-16 h-16 rounded-full bg-green-50 mb-4", children: /* @__PURE__ */ jsx(CheckCircleIcon, { className: "w-8 h-8 text-green-500" }) }),
@@ -59878,4 +60517,4 @@ var streamProxyConfig = {
59878
60517
  }
59879
60518
  };
59880
60519
 
59881
- export { ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ChangeRoleDialog, ClipFilterProvider, CompactWorkspaceHealthCard, ConfirmRemoveUserDialog, CongratulationsOverlay, CroppedHlsVideoPlayer, CroppedVideoPlayer, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_HOME_VIEW_CONFIG, DEFAULT_MAP_VIEW_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_SHIFT_DATA, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, DetailedHealthStatus, DiagnosisVideoModal, EmptyStateMessage, EncouragementOverlay, FactoryAssignmentDropdown, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthDateShiftSelector, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HlsVideoPlayer, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, ImprovementCenterView_default as ImprovementCenterView, InlineEditableText, InteractiveOnboardingTour, InvitationService, InvitationsTable, InviteUserDialog, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineAssignmentDropdown, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LinesService, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlayPauseIndicator, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, RoleBadge, S3ClipsSupabaseService as S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SessionTracker, SessionTrackingContext, SessionTrackingProvider, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, TeamUsagePdfGenerator, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UserManagementService, UserManagementTable, UserService, UserUsageDetailModal, UserUsageStats, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyHistory, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, aggregateKPIsFromLineMetricsRows, apiUtils, areAllLinesOnSameShift, authCoreService, authOTPService, authRateLimitService, buildDateKey, buildKPIsFromLineMetricsRow, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createDefaultKPIs, createInvitationService, createLinesService, createSessionTracker, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, fetchIdleTimeReasons, filterDataByDateKeyRange, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateKeyForDisplay, formatDateTimeInZone, formatDuration, formatISTDate, formatIdleTime, formatRangeLabel, formatReasonLabel, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAllWorkspaceDisplayNamesSnapshot, getAnonClient, getAvailableShiftIds, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentShiftForLine, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDateKeyFromDate, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getMonthKeyBounds, getMonthWeekRanges, getNextUpdateInterval, getOperationalDate, getReasonColor, getS3SignedUrl, getS3VideoSrc, getShiftData, getShiftNameById, getShortShiftName, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUniformShiftGroup, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, groupLinesByShift, hasAnyShiftData, identifyCoreUser, initializeCoreMixpanel, isFullMonthRange, isLegacyConfiguration, isPrefetchError, isSafari, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, normalizeDateKeyRange, optifyeAgentClient, parseDateKeyToDate, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, subscribeWorkspaceDisplayNames, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, transformToChartData, updateThreadTitle, upsertWorkspaceDisplayNameInCache, useAccessControl, useActiveBreaks, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useCompanyUsersUsage, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useIdleTimeReasons, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useLines, useMessages, useMetrics, useMultiLineShiftConfigs, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useSessionTracking, useSessionTrackingContext, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useSupervisorsByLineIds, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useUserUsage, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthStatus, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, useWorkspaceUptimeTimeline, userService, videoPrefetchManager, videoPreloader, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };
60520
+ export { ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ChangeRoleDialog, ClipFilterProvider, CompactWorkspaceHealthCard, ConfirmRemoveUserDialog, CongratulationsOverlay, CroppedHlsVideoPlayer, CroppedVideoPlayer, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_HOME_VIEW_CONFIG, DEFAULT_MAP_VIEW_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_SHIFT_DATA, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, DetailedHealthStatus, DiagnosisVideoModal, EmptyStateMessage, EncouragementOverlay, FactoryAssignmentDropdown, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthDateShiftSelector, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HlsVideoPlayer, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, ImprovementCenterView_default as ImprovementCenterView, InlineEditableText, InteractiveOnboardingTour, InvitationService, InvitationsTable, InviteUserDialog, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineAssignmentDropdown, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LinesService, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlayPauseIndicator, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, RoleBadge, S3ClipsSupabaseService as S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SessionTracker, SessionTrackingContext, SessionTrackingProvider, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, TeamUsagePdfGenerator, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UserManagementService, UserManagementTable, UserService, UserUsageDetailModal, UserUsageStats, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyHistory, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, aggregateKPIsFromLineMetricsRows, apiUtils, areAllLinesOnSameShift, authCoreService, authOTPService, authRateLimitService, buildDateKey, buildKPIsFromLineMetricsRow, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createDefaultKPIs, createInvitationService, createLinesService, createSessionTracker, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, fetchIdleTimeReasons, filterDataByDateKeyRange, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateKeyForDisplay, formatDateTimeInZone, formatDuration, formatISTDate, formatIdleTime, formatRangeLabel, formatReasonLabel, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAllWorkspaceDisplayNamesSnapshot, getAnonClient, getAvailableShiftIds, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentShiftForLine, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDateKeyFromDate, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getMonthKeyBounds, getMonthWeekRanges, getNextUpdateInterval, getOperationalDate, getReasonColor, getS3SignedUrl, getS3VideoSrc, getShiftData, getShiftNameById, getShortShiftName, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUniformShiftGroup, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, groupLinesByShift, hasAnyShiftData, identifyCoreUser, initializeCoreMixpanel, isFullMonthRange, isLegacyConfiguration, isPrefetchError, isSafari, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, normalizeDateKeyRange, optifyeAgentClient, parseDateKeyToDate, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, subscribeWorkspaceDisplayNames, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, transformToChartData, updateThreadTitle, upsertWorkspaceDisplayNameInCache, useAccessControl, useActiveBreaks, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useCompanyUsersUsage, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useIdleTimeClipClassifications, useIdleTimeReasons, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useLines, useMessages, useMetrics, useMultiLineShiftConfigs, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useSessionTracking, useSessionTrackingContext, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useSupervisorsByLineIds, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useUserUsage, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthStatus, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, useWorkspaceUptimeTimeline, userService, videoPrefetchManager, videoPreloader, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };