@optifye/dashboard-core 6.10.31 → 6.10.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7667,6 +7667,27 @@ var weeklyTopPerformerService = {
7667
7667
  return data?.top_performer ?? null;
7668
7668
  }
7669
7669
  };
7670
+
7671
+ // src/lib/services/lineLeaderboardService.ts
7672
+ var lineLeaderboardService = {
7673
+ async getLineLeaderboard(supabase, params) {
7674
+ const searchParams = new URLSearchParams();
7675
+ searchParams.set("company_id", params.companyId);
7676
+ searchParams.set("start_date", params.startDate);
7677
+ searchParams.set("end_date", params.endDate);
7678
+ if (params.lineIds && params.lineIds.length > 0) {
7679
+ searchParams.set("line_ids", params.lineIds.join(","));
7680
+ }
7681
+ if (typeof params.limit === "number") {
7682
+ searchParams.set("limit", params.limit.toString());
7683
+ }
7684
+ const data = await fetchBackendJson(
7685
+ supabase,
7686
+ `/api/dashboard/line-leaderboard?${searchParams.toString()}`
7687
+ );
7688
+ return data?.entries ?? [];
7689
+ }
7690
+ };
7670
7691
  var SupabaseContext = React25.createContext(void 0);
7671
7692
  var SupabaseProvider = ({ client, children }) => {
7672
7693
  _setSupabaseInstance(client);
@@ -10202,9 +10223,25 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
10202
10223
  const inFlightFetchKeyRef = React25.useRef(null);
10203
10224
  const updateQueueRef = React25.useRef(false);
10204
10225
  const onLineMetricsUpdateRef = React25.useRef(onLineMetricsUpdate);
10226
+ const shiftGroupsRef = React25.useRef(shiftGroups);
10227
+ const operationalShiftKeyRef = React25.useRef(operationalShiftKey);
10228
+ const configuredLineIdsRef = React25.useRef(configuredLineIds);
10229
+ const userAccessibleLineIdsRef = React25.useRef(userAccessibleLineIds);
10205
10230
  React25.useEffect(() => {
10206
10231
  onLineMetricsUpdateRef.current = onLineMetricsUpdate;
10207
10232
  }, [onLineMetricsUpdate]);
10233
+ React25.useEffect(() => {
10234
+ shiftGroupsRef.current = shiftGroups;
10235
+ }, [shiftGroups]);
10236
+ React25.useEffect(() => {
10237
+ operationalShiftKeyRef.current = operationalShiftKey;
10238
+ }, [operationalShiftKey]);
10239
+ React25.useEffect(() => {
10240
+ configuredLineIdsRef.current = configuredLineIds;
10241
+ }, [configuredLineIds]);
10242
+ React25.useEffect(() => {
10243
+ userAccessibleLineIdsRef.current = userAccessibleLineIds;
10244
+ }, [userAccessibleLineIds]);
10208
10245
  const companySpecificMetricsTable = React25.useMemo(
10209
10246
  () => getCompanyMetricsTableName(entityConfig.companyId, "performance_metrics"),
10210
10247
  [entityConfig.companyId]
@@ -10535,8 +10572,29 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
10535
10572
  if (!supabase || shiftLoading || isTimezoneLoading) return;
10536
10573
  fetchAllMetrics({ force: true, reason: "shift-change" });
10537
10574
  }, [isFactoryView, shiftGroupsKey, supabase, shiftLoading, isTimezoneLoading, fetchAllMetrics]);
10575
+ const subscriptionKey = React25.useMemo(() => {
10576
+ if (!supabase || !entityConfig?.companyId) return null;
10577
+ if (shiftLoading || isTimezoneLoading) return null;
10578
+ const isFactory = lineId === (entityConfig.factoryViewId || "factory");
10579
+ if (isFactory && shiftGroups.length === 0) return null;
10580
+ const shiftGroupsKeyPart = isFactory ? shiftGroups.map((g) => `${g.date}-${g.shiftId}-${g.lineIds.join("_")}`).join("|") : operationalShiftKey;
10581
+ return `${lineId}|${entityConfig.companyId}|${shiftGroupsKeyPart}`;
10582
+ }, [
10583
+ supabase,
10584
+ entityConfig?.companyId,
10585
+ entityConfig?.factoryViewId,
10586
+ lineId,
10587
+ shiftLoading,
10588
+ isTimezoneLoading,
10589
+ shiftGroups,
10590
+ operationalShiftKey
10591
+ ]);
10538
10592
  React25.useEffect(() => {
10539
10593
  const currentLineIdToUse = lineIdRef.current;
10594
+ if (!subscriptionKey) {
10595
+ logDebug("[useDashboardMetrics] Realtime setup skipped: subscriptionKey not ready (config still loading)");
10596
+ return;
10597
+ }
10540
10598
  if (!currentLineIdToUse || !supabase || companySpecificMetricsTable.includes("unknown_company") || !entityConfig.companyId) {
10541
10599
  logDebug("[useDashboardMetrics] Realtime setup skipped:", {
10542
10600
  lineId: currentLineIdToUse,
@@ -10549,18 +10607,22 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
10549
10607
  const factoryViewIdentifier = entityConfig.factoryViewId || "factory";
10550
10608
  const isFactory = currentLineIdToUse === factoryViewIdentifier;
10551
10609
  const channels = [];
10610
+ const currentShiftGroups = shiftGroupsRef.current;
10611
+ const currentUserAccessibleLineIds = userAccessibleLineIdsRef.current;
10612
+ const currentConfiguredLineIds = configuredLineIdsRef.current;
10552
10613
  logDebug("[useDashboardMetrics] Realtime setup starting:", {
10553
10614
  lineId: currentLineIdToUse,
10554
10615
  isFactory,
10555
- shiftGroupsCount: shiftGroups.length,
10616
+ shiftGroupsCount: currentShiftGroups.length,
10556
10617
  companySpecificMetricsTable,
10557
- configuredLineMetricsTable
10618
+ configuredLineMetricsTable,
10619
+ subscriptionKey
10558
10620
  });
10559
- if (isFactory && shiftGroups.length > 0) {
10621
+ if (isFactory && currentShiftGroups.length > 0) {
10560
10622
  logDebug("[useDashboardMetrics] Setting up group subscriptions:", {
10561
- groupCount: shiftGroups.length
10623
+ groupCount: currentShiftGroups.length
10562
10624
  });
10563
- shiftGroups.forEach((group, index) => {
10625
+ currentShiftGroups.forEach((group, index) => {
10564
10626
  const groupLineIds = group.lineIds.filter((id3) => id3 && id3 !== factoryViewIdentifier);
10565
10627
  if (groupLineIds.length === 0) {
10566
10628
  logDebug("[useDashboardMetrics] Skipping group subscription: no line IDs after filtering", {
@@ -10579,6 +10641,18 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
10579
10641
  { event: "*", schema, table: companySpecificMetricsTable, filter: filter2 },
10580
10642
  (payload) => {
10581
10643
  const payloadData = payload.new || payload.old;
10644
+ console.log("[useDashboardMetrics] \u{1F4E1} WS_METRICS payload received:", {
10645
+ eventType: payload.eventType,
10646
+ lineId: payloadData?.line_id,
10647
+ workspaceId: payloadData?.workspace_id,
10648
+ date: payloadData?.date,
10649
+ shiftId: payloadData?.shift_id,
10650
+ expectedDate: group.date,
10651
+ expectedShiftId: group.shiftId,
10652
+ dateMatch: payloadData?.date === group.date,
10653
+ shiftMatch: payloadData?.shift_id === group.shiftId,
10654
+ efficiency: payloadData?.efficiency
10655
+ });
10582
10656
  logDebug("[useDashboardMetrics] WS realtime payload:", {
10583
10657
  channel: wsChannelName,
10584
10658
  eventType: payload.eventType,
@@ -10589,10 +10663,19 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
10589
10663
  shiftId: payloadData?.shift_id
10590
10664
  });
10591
10665
  if (payloadData?.date === group.date && payloadData?.shift_id === group.shiftId) {
10666
+ console.log("[useDashboardMetrics] \u2705 WS Date/shift match - triggering update");
10592
10667
  queueUpdate();
10668
+ } else {
10669
+ console.log("[useDashboardMetrics] \u274C WS Date/shift mismatch - update SKIPPED");
10593
10670
  }
10594
10671
  }
10595
10672
  ).subscribe((status) => {
10673
+ console.log("[useDashboardMetrics] \u{1F4F6} WS metrics subscription:", {
10674
+ channel: wsChannelName,
10675
+ status,
10676
+ table: companySpecificMetricsTable,
10677
+ filter: filter2
10678
+ });
10596
10679
  logDebug("[useDashboardMetrics] WS subscription status:", {
10597
10680
  channel: wsChannelName,
10598
10681
  status
@@ -10605,6 +10688,17 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
10605
10688
  { event: "*", schema, table: configuredLineMetricsTable, filter: filter2 },
10606
10689
  (payload) => {
10607
10690
  const payloadData = payload.new || payload.old;
10691
+ console.log("[useDashboardMetrics] \u{1F4E1} LINE_METRICS payload received:", {
10692
+ eventType: payload.eventType,
10693
+ lineId: payloadData?.line_id,
10694
+ date: payloadData?.date,
10695
+ shiftId: payloadData?.shift_id,
10696
+ expectedDate: group.date,
10697
+ expectedShiftId: group.shiftId,
10698
+ dateMatch: payloadData?.date === group.date,
10699
+ shiftMatch: payloadData?.shift_id === group.shiftId,
10700
+ avgEfficiency: payloadData?.avg_efficiency
10701
+ });
10608
10702
  logDebug("[useDashboardMetrics] Line metrics realtime payload:", {
10609
10703
  channel: lmChannelName,
10610
10704
  eventType: payload.eventType,
@@ -10614,11 +10708,20 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
10614
10708
  shiftId: payloadData?.shift_id
10615
10709
  });
10616
10710
  if (payloadData?.date === group.date && payloadData?.shift_id === group.shiftId) {
10711
+ console.log("[useDashboardMetrics] \u2705 Date/shift match - triggering update");
10617
10712
  queueUpdate();
10618
10713
  onLineMetricsUpdateRef.current?.();
10714
+ } else {
10715
+ console.log("[useDashboardMetrics] \u274C Date/shift mismatch - update SKIPPED");
10619
10716
  }
10620
10717
  }
10621
10718
  ).subscribe((status) => {
10719
+ console.log("[useDashboardMetrics] \u{1F4F6} Line metrics subscription:", {
10720
+ channel: lmChannelName,
10721
+ status,
10722
+ table: configuredLineMetricsTable,
10723
+ filter: filter2
10724
+ });
10622
10725
  logDebug("[useDashboardMetrics] Line metrics subscription status:", {
10623
10726
  channel: lmChannelName,
10624
10727
  status
@@ -10654,11 +10757,11 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
10654
10757
  } else {
10655
10758
  logDebug("[useDashboardMetrics] Using single-shift subscriptions:", {
10656
10759
  isFactory,
10657
- shiftGroupsCount: shiftGroups.length
10760
+ shiftGroupsCount: currentShiftGroups.length
10658
10761
  });
10659
10762
  const currentShiftDetails = shiftConfig ? getCurrentShift(defaultTimezone, shiftConfig) : getCurrentShift(defaultTimezone, staticShiftConfig);
10660
10763
  const operationalDateForSubscription = currentShiftDetails.date;
10661
- const targetLineIds = isFactory ? userAccessibleLineIds || configuredLineIds : [currentLineIdToUse];
10764
+ const targetLineIds = isFactory ? currentUserAccessibleLineIds || currentConfiguredLineIds : [currentLineIdToUse];
10662
10765
  const filteredLineIds = targetLineIds.filter((id3) => id3 && id3 !== factoryViewIdentifier);
10663
10766
  if (filteredLineIds.length === 0) {
10664
10767
  logDebug("[useDashboardMetrics] Realtime setup skipped: no line IDs after filtering", {
@@ -10683,6 +10786,20 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
10683
10786
  { event: "*", schema, table, filter: filter2 },
10684
10787
  (payload) => {
10685
10788
  const payloadData = payload.new || payload.old;
10789
+ console.log(`[useDashboardMetrics] \u{1F4E1} ${table.toUpperCase()} payload received (single-line):`, {
10790
+ eventType: payload.eventType,
10791
+ table: payload.table,
10792
+ lineId: payloadData?.line_id,
10793
+ workspaceId: payloadData?.workspace_id,
10794
+ date: payloadData?.date,
10795
+ shiftId: payloadData?.shift_id,
10796
+ expectedDate: operationalDateForSubscription,
10797
+ expectedShiftId: currentShiftDetails.shiftId,
10798
+ dateMatch: payloadData?.date === operationalDateForSubscription,
10799
+ shiftMatch: payloadData?.shift_id === currentShiftDetails.shiftId,
10800
+ avgEfficiency: payloadData?.avg_efficiency,
10801
+ efficiency: payloadData?.efficiency
10802
+ });
10686
10803
  logDebug("[useDashboardMetrics] Subscription payload:", {
10687
10804
  channel: channelName,
10688
10805
  eventType: payload.eventType,
@@ -10693,10 +10810,21 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
10693
10810
  shiftId: payloadData?.shift_id
10694
10811
  });
10695
10812
  if (payloadData?.date === operationalDateForSubscription && payloadData?.shift_id === currentShiftDetails.shiftId) {
10813
+ console.log(`[useDashboardMetrics] \u2705 ${table} Date/shift match - triggering update`);
10696
10814
  callback();
10815
+ } else {
10816
+ console.log(`[useDashboardMetrics] \u274C ${table} Date/shift mismatch - update SKIPPED`);
10697
10817
  }
10698
10818
  }
10699
10819
  ).subscribe((status) => {
10820
+ console.log(`[useDashboardMetrics] \u{1F4F6} ${table} subscription:`, {
10821
+ channel: channelName,
10822
+ status,
10823
+ table,
10824
+ filter: filter2,
10825
+ expectedDate: operationalDateForSubscription,
10826
+ expectedShiftId: currentShiftDetails.shiftId
10827
+ });
10700
10828
  logDebug("[useDashboardMetrics] Subscription status:", {
10701
10829
  channel: channelName,
10702
10830
  status
@@ -10718,6 +10846,10 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
10718
10846
  });
10719
10847
  };
10720
10848
  }, [
10849
+ // CRITICAL: Use subscriptionKey as the primary dependency for subscription recreation
10850
+ // This ensures subscriptions are only recreated when the actual subscription parameters change,
10851
+ // not during every transient state update during initial load
10852
+ subscriptionKey,
10721
10853
  supabase,
10722
10854
  queueUpdate,
10723
10855
  companySpecificMetricsTable,
@@ -10728,10 +10860,10 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
10728
10860
  defaultTimezone,
10729
10861
  shiftConfig,
10730
10862
  staticShiftConfig,
10731
- shiftGroups,
10732
- operationalShiftKey,
10733
- lineId,
10734
- userAccessibleLineIds
10863
+ // NOTE: shiftGroups and operationalShiftKey removed - they're accessed via refs
10864
+ // to prevent subscription churn. subscriptionKey encapsulates their stable state.
10865
+ lineId
10866
+ // NOTE: userAccessibleLineIds removed - accessed via ref
10735
10867
  ]);
10736
10868
  const isMetricsForActiveLine = metricsLineId === lineId;
10737
10869
  const safeMetrics = isMetricsForActiveLine ? metrics2 : { workspaceMetrics: [], lineMetrics: [], metadata: void 0, efficiencyLegend: DEFAULT_EFFICIENCY_LEGEND };
@@ -12674,6 +12806,10 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
12674
12806
  return;
12675
12807
  }
12676
12808
  if (data.type === Hls__default.default.ErrorTypes.NETWORK_ERROR && (data.details === Hls__default.default.ErrorDetails.MANIFEST_LOAD_TIMEOUT || data.details === Hls__default.default.ErrorDetails.MANIFEST_LOAD_ERROR)) {
12809
+ if (data.response?.code === 404 && isR2StreamRef.current) {
12810
+ markStaleStream("manifest 404");
12811
+ return;
12812
+ }
12677
12813
  scheduleManifestRetry("manifest load timeout");
12678
12814
  return;
12679
12815
  }
@@ -12682,7 +12818,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
12682
12818
  if (data.response?.code === 404) {
12683
12819
  if (data.details === Hls__default.default.ErrorDetails.MANIFEST_LOAD_ERROR || data.details === Hls__default.default.ErrorDetails.LEVEL_LOAD_ERROR) {
12684
12820
  if (isR2StreamRef.current) {
12685
- scheduleManifestRetry("manifest 404");
12821
+ markStaleStream("manifest 404");
12686
12822
  return;
12687
12823
  }
12688
12824
  hardRestart("404 manifest hard restart");
@@ -13711,7 +13847,7 @@ var useActiveBreaks = (lineIds) => {
13711
13847
  const [isLoading, setIsLoading] = React25.useState(true);
13712
13848
  const [error, setError] = React25.useState(null);
13713
13849
  const supabase = useSupabase();
13714
- const parseTimeToMinutes2 = (timeStr) => {
13850
+ const parseTimeToMinutes3 = (timeStr) => {
13715
13851
  const [hours, minutes] = timeStr.split(":").map(Number);
13716
13852
  return hours * 60 + minutes;
13717
13853
  };
@@ -13720,8 +13856,8 @@ var useActiveBreaks = (lineIds) => {
13720
13856
  return now4.getHours() * 60 + now4.getMinutes();
13721
13857
  };
13722
13858
  const isTimeInBreak = (breakStart, breakEnd, currentMinutes) => {
13723
- const startMinutes = parseTimeToMinutes2(breakStart);
13724
- const endMinutes = parseTimeToMinutes2(breakEnd);
13859
+ const startMinutes = parseTimeToMinutes3(breakStart);
13860
+ const endMinutes = parseTimeToMinutes3(breakEnd);
13725
13861
  if (endMinutes < startMinutes) {
13726
13862
  return currentMinutes >= startMinutes || currentMinutes < endMinutes;
13727
13863
  } else {
@@ -13729,8 +13865,8 @@ var useActiveBreaks = (lineIds) => {
13729
13865
  }
13730
13866
  };
13731
13867
  const calculateBreakProgress = (breakStart, breakEnd, currentMinutes) => {
13732
- const startMinutes = parseTimeToMinutes2(breakStart);
13733
- const endMinutes = parseTimeToMinutes2(breakEnd);
13868
+ const startMinutes = parseTimeToMinutes3(breakStart);
13869
+ const endMinutes = parseTimeToMinutes3(breakEnd);
13734
13870
  let elapsedMinutes = 0;
13735
13871
  let remainingMinutes = 0;
13736
13872
  if (endMinutes < startMinutes) {
@@ -13748,8 +13884,8 @@ var useActiveBreaks = (lineIds) => {
13748
13884
  return { elapsedMinutes, remainingMinutes };
13749
13885
  };
13750
13886
  const isTimeInShift = (startTime, endTime, currentMinutes) => {
13751
- const startMinutes = parseTimeToMinutes2(startTime);
13752
- const endMinutes = parseTimeToMinutes2(endTime);
13887
+ const startMinutes = parseTimeToMinutes3(startTime);
13888
+ const endMinutes = parseTimeToMinutes3(endTime);
13753
13889
  if (endMinutes < startMinutes) {
13754
13890
  return currentMinutes >= startMinutes || currentMinutes < endMinutes;
13755
13891
  } else {
@@ -13809,8 +13945,8 @@ var useActiveBreaks = (lineIds) => {
13809
13945
  const endTime = breakItem.end || breakItem.endTime || "00:00";
13810
13946
  let duration = breakItem.duration || 0;
13811
13947
  if (!duration || duration === 0) {
13812
- const startMinutes = parseTimeToMinutes2(startTime);
13813
- const endMinutes = parseTimeToMinutes2(endTime);
13948
+ const startMinutes = parseTimeToMinutes3(startTime);
13949
+ const endMinutes = parseTimeToMinutes3(endTime);
13814
13950
  duration = endMinutes < startMinutes ? endMinutes + 24 * 60 - startMinutes : endMinutes - startMinutes;
13815
13951
  }
13816
13952
  return {
@@ -13826,8 +13962,8 @@ var useActiveBreaks = (lineIds) => {
13826
13962
  const endTime = breakItem.end || breakItem.endTime || "00:00";
13827
13963
  let duration = breakItem.duration || 0;
13828
13964
  if (!duration || duration === 0) {
13829
- const startMinutes = parseTimeToMinutes2(startTime);
13830
- const endMinutes = parseTimeToMinutes2(endTime);
13965
+ const startMinutes = parseTimeToMinutes3(startTime);
13966
+ const endMinutes = parseTimeToMinutes3(endTime);
13831
13967
  duration = endMinutes < startMinutes ? endMinutes + 24 * 60 - startMinutes : endMinutes - startMinutes;
13832
13968
  }
13833
13969
  return {
@@ -15846,6 +15982,13 @@ var useSupervisorsByLineIds = (lineIds, options) => {
15846
15982
  refetch: fetchSupervisors
15847
15983
  };
15848
15984
  };
15985
+ var DEBUG_DASHBOARD = process.env.NEXT_PUBLIC_DEBUG_DASHBOARD === "true";
15986
+ var logIdleTimeReasonsDebug = (...args) => {
15987
+ if (DEBUG_DASHBOARD) {
15988
+ console.log(...args);
15989
+ }
15990
+ };
15991
+ var nowMs = () => typeof performance !== "undefined" ? performance.now() : Date.now();
15849
15992
  function useIdleTimeReasons({
15850
15993
  workspaceId,
15851
15994
  lineId,
@@ -15865,23 +16008,44 @@ function useIdleTimeReasons({
15865
16008
  const lastRequestKeyRef = React25.useRef("");
15866
16009
  const fetchData = React25.useCallback(async () => {
15867
16010
  if (!workspaceId && !lineId) {
16011
+ logIdleTimeReasonsDebug("[useIdleTimeReasons] Skipping fetch (missing id)", {
16012
+ requestKey,
16013
+ workspaceId,
16014
+ lineId
16015
+ });
15868
16016
  setError("At least one of workspaceId or lineId is required");
15869
16017
  setIsFetching(false);
15870
16018
  return;
15871
16019
  }
15872
16020
  const token = session?.access_token;
15873
16021
  if (!token) {
16022
+ logIdleTimeReasonsDebug("[useIdleTimeReasons] Skipping fetch (no auth token)", {
16023
+ requestKey,
16024
+ workspaceId,
16025
+ lineId
16026
+ });
15874
16027
  setError("Not authenticated");
15875
16028
  setIsFetching(false);
15876
16029
  return;
15877
16030
  }
15878
16031
  const currentRequestKey = requestKey;
15879
16032
  lastRequestKeyRef.current = currentRequestKey;
16033
+ const startedAt = nowMs();
16034
+ logIdleTimeReasonsDebug("[useIdleTimeReasons] Fetch start", {
16035
+ requestKey: currentRequestKey,
16036
+ workspaceId,
16037
+ lineId,
16038
+ date,
16039
+ shiftId,
16040
+ startDate,
16041
+ endDate
16042
+ });
15880
16043
  setIsFetching(true);
15881
16044
  if (!keepPreviousData) {
15882
16045
  setData(null);
15883
16046
  }
15884
16047
  setError(null);
16048
+ let errorMessage = null;
15885
16049
  try {
15886
16050
  const result = await fetchIdleTimeReasons({
15887
16051
  workspaceId,
@@ -15898,22 +16062,32 @@ function useIdleTimeReasons({
15898
16062
  }
15899
16063
  } catch (err) {
15900
16064
  console.error("[useIdleTimeReasons] Error:", err);
16065
+ errorMessage = err instanceof Error ? err.message : "Failed to fetch idle time reasons";
15901
16066
  if (isMountedRef.current && lastRequestKeyRef.current === currentRequestKey) {
15902
- setError(err instanceof Error ? err.message : "Failed to fetch idle time reasons");
16067
+ setError(errorMessage);
15903
16068
  }
15904
16069
  } finally {
15905
16070
  if (isMountedRef.current && lastRequestKeyRef.current === currentRequestKey) {
15906
16071
  setIsFetching(false);
15907
16072
  }
16073
+ const fetchEndAt = nowMs();
16074
+ const elapsedMs = startedAt ? Math.round(fetchEndAt - startedAt) : null;
16075
+ logIdleTimeReasonsDebug("[useIdleTimeReasons] Fetch end", {
16076
+ requestKey: currentRequestKey,
16077
+ elapsedMs,
16078
+ error: errorMessage
16079
+ });
15908
16080
  }
15909
16081
  }, [workspaceId, lineId, date, shiftId, startDate, endDate, session?.access_token, requestKey, keepPreviousData]);
15910
16082
  React25.useEffect(() => {
15911
16083
  isMountedRef.current = true;
15912
16084
  if (!enabled) {
16085
+ logIdleTimeReasonsDebug("[useIdleTimeReasons] Disabled", { requestKey });
15913
16086
  setIsFetching(false);
15914
16087
  } else if (session?.access_token) {
15915
16088
  fetchData();
15916
16089
  } else {
16090
+ logIdleTimeReasonsDebug("[useIdleTimeReasons] Waiting for auth token", { requestKey });
15917
16091
  setError("Not authenticated");
15918
16092
  setIsFetching(false);
15919
16093
  }
@@ -30608,9 +30782,9 @@ var MapGridView = React25__namespace.default.memo(({
30608
30782
  }, [workspaces]);
30609
30783
  const getPerformanceColor = React25.useCallback((efficiency) => {
30610
30784
  const color2 = getEfficiencyColor(efficiency, effectiveLegend);
30611
- if (color2 === "green") return "border-green-500 bg-green-50";
30612
- if (color2 === "yellow") return "border-yellow-500 bg-yellow-50";
30613
- return "border-red-500 bg-red-50";
30785
+ if (color2 === "green") return "bg-green-500 text-white border-green-600";
30786
+ if (color2 === "yellow") return "bg-yellow-400 text-white border-yellow-500";
30787
+ return "bg-red-500 text-white border-red-600";
30614
30788
  }, [effectiveLegend]);
30615
30789
  const prewarmClipsInit = React25.useCallback((workspace) => {
30616
30790
  if (!dashboardConfig?.s3Config) return;
@@ -30678,52 +30852,52 @@ var MapGridView = React25__namespace.default.memo(({
30678
30852
  /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx("pattern", { id: "grid", width: "40", height: "40", patternUnits: "userSpaceOnUse", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M 40 0 L 0 0 0 40", fill: "none", stroke: "gray", strokeWidth: "0.5" }) }) }),
30679
30853
  /* @__PURE__ */ jsxRuntime.jsx("rect", { width: "100%", height: "100%", fill: "url(#grid)" })
30680
30854
  ] }) }),
30681
- activePositions.map((position) => {
30855
+ workspacePositions.map((position) => {
30682
30856
  const wsKey = position.id.toUpperCase();
30683
30857
  const workspace = workspaceMetricsMap.get(wsKey);
30684
- if (!workspace) return null;
30685
- const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
30686
- const workspaceKey = `${workspace.line_id || "unknown"}-${workspaceId}`;
30687
- const performanceColor = getPerformanceColor(workspace.efficiency);
30688
- const showExclamation = workspace.show_exclamation ?? (workspace.efficiency < 50 && workspace.efficiency >= 10);
30689
- const workspaceDisplayName = displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || // Always pass line_id to fallback to ensure correct mapping per line
30690
- getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
30858
+ const isInactivePlaceholder = position.id.startsWith("INACTIVE");
30859
+ const effectiveWorkspace = workspace || {
30860
+ workspace_name: position.id,
30861
+ // Fallback to config ID
30862
+ workspace_uuid: position.id,
30863
+ // Use config ID as temporary UUID
30864
+ line_id: "",
30865
+ efficiency: 0,
30866
+ show_exclamation: false
30867
+ };
30868
+ const workspaceId = effectiveWorkspace.workspace_uuid || effectiveWorkspace.workspace_name;
30869
+ const workspaceKey = `${effectiveWorkspace.line_id || "unknown"}-${workspaceId}`;
30870
+ const isConveyorRow = position.size === "conveyor_row";
30871
+ const performanceColor = isInactivePlaceholder || isConveyorRow ? "bg-slate-200 border-slate-300 text-slate-500" : getPerformanceColor(effectiveWorkspace.efficiency);
30872
+ !isInactivePlaceholder && !isConveyorRow && (effectiveWorkspace.show_exclamation ?? (effectiveWorkspace.efficiency < 50 && effectiveWorkspace.efficiency >= 10));
30873
+ const configLabel = position.label;
30874
+ const workspaceDisplayName = configLabel || (isInactivePlaceholder ? "INACTIVE" : displayNames[`${effectiveWorkspace.line_id}_${effectiveWorkspace.workspace_name}`] || getWorkspaceDisplayName(effectiveWorkspace.workspace_name, effectiveWorkspace.line_id) || position.id);
30691
30875
  return /* @__PURE__ */ jsxRuntime.jsx(
30692
30876
  motion.div,
30693
30877
  {
30694
30878
  initial: { opacity: 0, scale: 0.8 },
30695
30879
  animate: { opacity: 1, scale: 1 },
30696
30880
  transition: { duration: 0.3 },
30697
- className: "absolute cursor-pointer",
30881
+ className: `absolute ${isInactivePlaceholder || isConveyorRow ? "cursor-default" : "cursor-pointer"}`,
30698
30882
  style: {
30699
30883
  left: `${position.x}%`,
30700
30884
  top: `${position.y}%`,
30701
30885
  transform: "translate(-50%, -50%)"
30702
30886
  },
30703
- onClick: () => handleWorkspaceClick(workspace),
30704
- onMouseEnter: () => onWorkspaceHover?.(workspaceId),
30705
- onMouseLeave: () => onWorkspaceHoverEnd?.(workspaceId),
30706
- children: /* @__PURE__ */ jsxRuntime.jsxs(
30887
+ onClick: () => !isInactivePlaceholder && !isConveyorRow && workspace && handleWorkspaceClick(workspace),
30888
+ onMouseEnter: () => !isInactivePlaceholder && !isConveyorRow && onWorkspaceHover?.(workspaceId),
30889
+ onMouseLeave: () => !isInactivePlaceholder && !isConveyorRow && onWorkspaceHoverEnd?.(workspaceId),
30890
+ children: /* @__PURE__ */ jsxRuntime.jsx(
30707
30891
  "div",
30708
30892
  {
30709
30893
  className: `
30710
- relative rounded-lg border-2 shadow-lg hover:shadow-xl
30711
- transition-all duration-200 hover:scale-105 bg-white
30894
+ relative
30895
+ rounded-lg border-2 shadow-sm
30896
+ ${!isInactivePlaceholder && !isConveyorRow ? "shadow-lg hover:shadow-xl transition-all duration-200 hover:scale-105" : ""}
30712
30897
  ${performanceColor}
30713
- ${position.size === "conveyor" ? "w-32 h-24" : position.size === "large" ? "w-40 h-32" : "w-36 h-28"}
30898
+ ${position.size === "conveyor" ? "w-32 h-24" : position.size === "large" ? "w-40 h-32" : position.size === "small_square" ? "w-16 h-16" : position.size === "wide_rectangle" ? "w-32 h-16" : position.size === "conveyor_row" ? "w-3/4 h-16" : "w-36 h-28"}
30714
30899
  `,
30715
- children: [
30716
- showExclamation && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -top-2 -left-2 z-30", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
30717
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -inset-1 bg-red-400/50 rounded-full blur-sm animate-pulse" }),
30718
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -inset-0.5 bg-red-500/30 rounded-full blur-md animate-ping [animation-duration:1.5s]" }),
30719
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-[#E34329] w-5 h-5 rounded-full flex items-center justify-center text-white font-bold text-xs shadow-lg ring-2 ring-red-400/40 border border-red-400/80 animate-pulse", children: "!" })
30720
- ] }) }),
30721
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-0 left-0 right-0 px-2 py-1.5 border-b border-gray-200", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-bold text-gray-800 truncate text-center leading-tight", children: workspaceDisplayName }) }),
30722
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-0 left-0 right-0 px-2 py-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col items-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-2xl font-bold text-gray-900", children: [
30723
- Math.round(workspace.efficiency),
30724
- "%"
30725
- ] }) }) })
30726
- ]
30900
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute inset-0 px-2 py-1.5 flex items-center justify-center`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: `text-xs font-bold ${isInactivePlaceholder ? "text-gray-400" : isConveyorRow ? "text-gray-400 text-lg tracking-widest uppercase" : "text-white"} text-center leading-tight whitespace-normal break-words w-full`, children: workspaceDisplayName }) })
30727
30901
  }
30728
30902
  )
30729
30903
  },
@@ -40654,7 +40828,7 @@ var LineMonthlyHistory = ({
40654
40828
  const averages = (analysisMonthlyData || []).reduce(
40655
40829
  (acc, day) => {
40656
40830
  const shiftData = getShiftData2(day, selectedShiftId);
40657
- if (!shiftData || shiftData?.avg_efficiency < 10) {
40831
+ if (!shiftData || shiftData?.avg_efficiency < 5) {
40658
40832
  return acc;
40659
40833
  }
40660
40834
  return {
@@ -41116,7 +41290,7 @@ var LineMonthlyPdfGenerator = ({
41116
41290
  });
41117
41291
  const validShifts = validDays.map(
41118
41292
  (day) => getLineShiftData2(day, selectedShiftId)
41119
- ).filter((shift) => shift.avg_efficiency > 0);
41293
+ ).filter((shift) => shift.avg_efficiency >= 5);
41120
41294
  const monthlyMetrics = validShifts.length > 0 ? {
41121
41295
  avgEfficiency: validShifts.reduce((sum, shift) => sum + shift.avg_efficiency, 0) / validShifts.length,
41122
41296
  avgUnderperforming: validShifts.reduce((sum, shift) => sum + shift.underperforming_workspaces, 0) / validShifts.length,
@@ -41484,7 +41658,7 @@ var LinePdfGenerator = ({
41484
41658
  doc.setLineWidth(0.8);
41485
41659
  doc.line(20, 118, 190, 118);
41486
41660
  const hourlyOverviewStartY = 123;
41487
- const parseTimeToMinutes2 = (timeStr) => {
41661
+ const parseTimeToMinutes3 = (timeStr) => {
41488
41662
  const [hours, minutes] = timeStr.split(":");
41489
41663
  const hour = parseInt(hours, 10);
41490
41664
  const minute = parseInt(minutes || "0", 10);
@@ -41517,7 +41691,7 @@ var LinePdfGenerator = ({
41517
41691
  };
41518
41692
  };
41519
41693
  const getHourlyTimeRanges = (startTimeStr, endTimeStr) => {
41520
- const startMinutes = parseTimeToMinutes2(startTimeStr);
41694
+ const startMinutes = parseTimeToMinutes3(startTimeStr);
41521
41695
  if (Number.isNaN(startMinutes)) {
41522
41696
  return [];
41523
41697
  }
@@ -41525,7 +41699,7 @@ var LinePdfGenerator = ({
41525
41699
  const defaultHours = 11;
41526
41700
  return Array.from({ length: defaultHours }, (_, i) => buildRange(startMinutes + i * 60, 60));
41527
41701
  }
41528
- const endMinutes = parseTimeToMinutes2(endTimeStr);
41702
+ const endMinutes = parseTimeToMinutes3(endTimeStr);
41529
41703
  if (Number.isNaN(endMinutes)) {
41530
41704
  const fallbackHours = 11;
41531
41705
  return Array.from({ length: fallbackHours }, (_, i) => buildRange(startMinutes + i * 60, 60));
@@ -54572,6 +54746,376 @@ var KPIDetailViewWithDisplayNames = withSelectedLineDisplayNames(KPIDetailView);
54572
54746
  var KPIDetailView_default = KPIDetailViewWithDisplayNames;
54573
54747
  var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
54574
54748
  var resolveCompanyId = (...candidates) => candidates.find(isNonEmptyString);
54749
+ var parseTimeToMinutes2 = (value) => {
54750
+ if (!value) return null;
54751
+ const [hourStr, minuteStr] = value.split(":");
54752
+ const hour = Number.parseInt(hourStr ?? "", 10);
54753
+ const minute = Number.parseInt(minuteStr ?? "", 10);
54754
+ if (!Number.isFinite(hour) || !Number.isFinite(minute)) return null;
54755
+ return hour * 60 + minute;
54756
+ };
54757
+ var getShiftEndDate = (shift, timezone) => {
54758
+ if (!shift?.date) return null;
54759
+ const startTime = shift.startTime || "06:00";
54760
+ const endTime = shift.endTime || "18:00";
54761
+ const startMinutes = parseTimeToMinutes2(startTime);
54762
+ const endMinutes = parseTimeToMinutes2(endTime);
54763
+ if (startMinutes === null || endMinutes === null) return null;
54764
+ const shiftStartDate = dateFnsTz.fromZonedTime(`${shift.date}T${startTime}:00`, timezone);
54765
+ let durationMinutes = endMinutes - startMinutes;
54766
+ if (durationMinutes <= 0) durationMinutes += 24 * 60;
54767
+ return dateFns.addMinutes(shiftStartDate, durationMinutes);
54768
+ };
54769
+ var getMonthDateInfo = (timezone) => {
54770
+ const zonedNow = dateFnsTz.toZonedTime(/* @__PURE__ */ new Date(), timezone);
54771
+ const year = zonedNow.getFullYear();
54772
+ const monthIndex = zonedNow.getMonth();
54773
+ const day = zonedNow.getDate();
54774
+ const startDate = buildDateKey(year, monthIndex, 1);
54775
+ const endDate = buildDateKey(year, monthIndex, day);
54776
+ const lastDay = new Date(year, monthIndex + 1, 0).getDate();
54777
+ const monthEndKey = buildDateKey(year, monthIndex, lastDay);
54778
+ const monthEndDate = dateFnsTz.fromZonedTime(`${monthEndKey}T23:59:59`, timezone);
54779
+ return { startDate, endDate, monthEndDate };
54780
+ };
54781
+ var LeaderboardCountdown = ({ targetDate, format: format7, finishedLabel = "Finished", placeholder = "--", onFinished }) => {
54782
+ const [time2, setTime] = React25.useState("");
54783
+ const hasFinishedRef = React25.useRef(false);
54784
+ React25.useEffect(() => {
54785
+ hasFinishedRef.current = false;
54786
+ }, [targetDate]);
54787
+ React25.useEffect(() => {
54788
+ if (!targetDate) {
54789
+ setTime(placeholder);
54790
+ return;
54791
+ }
54792
+ const tick = () => {
54793
+ const now4 = /* @__PURE__ */ new Date();
54794
+ const diff = targetDate.getTime() - now4.getTime();
54795
+ if (diff <= 0) {
54796
+ setTime(finishedLabel);
54797
+ if (!hasFinishedRef.current && onFinished) {
54798
+ hasFinishedRef.current = true;
54799
+ onFinished();
54800
+ }
54801
+ return;
54802
+ }
54803
+ if (format7 === "days") {
54804
+ const days = Math.floor(diff / (1e3 * 60 * 60 * 24));
54805
+ const hours = Math.floor(diff % (1e3 * 60 * 60 * 24) / (1e3 * 60 * 60));
54806
+ setTime(`${days} days ${hours} hours`);
54807
+ return;
54808
+ }
54809
+ const totalSeconds = Math.floor(diff / 1e3);
54810
+ const h = Math.floor(totalSeconds / 3600);
54811
+ const m = Math.floor(totalSeconds % 3600 / 60);
54812
+ const s = totalSeconds % 60;
54813
+ setTime(`${h.toString().padStart(2, "0")}:${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`);
54814
+ };
54815
+ tick();
54816
+ const interval = setInterval(tick, 1e3);
54817
+ return () => clearInterval(interval);
54818
+ }, [targetDate, format7, finishedLabel, placeholder, onFinished]);
54819
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: time2 });
54820
+ };
54821
+ var LinesLeaderboard = ({
54822
+ lines,
54823
+ onLineClick,
54824
+ timeRange,
54825
+ setTimeRange,
54826
+ todayEfficiencyByLineId,
54827
+ monthlyEfficiencyByLineId,
54828
+ supervisorsByLineId,
54829
+ supervisorNamesByLineId,
54830
+ isLoadingToday,
54831
+ isLoadingMonthly,
54832
+ shiftEndDate,
54833
+ monthEndDate
54834
+ }) => {
54835
+ const formatEfficiency = (value) => typeof value === "number" && Number.isFinite(value) ? `${value.toFixed(1)}%` : "--";
54836
+ const handleTimeRangeChange = React25__namespace.default.useCallback((newRange) => {
54837
+ if (newRange === timeRange) return;
54838
+ trackCoreEvent("Leaderboard Time Range Changed", {
54839
+ from_range: timeRange,
54840
+ to_range: newRange,
54841
+ from_page: "leaderboard",
54842
+ lines_count: lines.length,
54843
+ has_monthly_data: monthlyEfficiencyByLineId ? monthlyEfficiencyByLineId.size > 0 : false
54844
+ });
54845
+ setTimeRange(newRange);
54846
+ }, [timeRange, lines.length, monthlyEfficiencyByLineId, setTimeRange]);
54847
+ const handleLeaderboardLineClick = React25__namespace.default.useCallback((item, clickSource) => {
54848
+ trackCoreEvent("Leaderboard Line Clicked", {
54849
+ line_id: item.line.id,
54850
+ line_name: item.line.line_name,
54851
+ rank: item.rank,
54852
+ efficiency: item.efficiency,
54853
+ time_range: timeRange,
54854
+ click_source: clickSource,
54855
+ from_page: "leaderboard",
54856
+ supervisor_name: item.supervisorName || "Unassigned"
54857
+ });
54858
+ onLineClick(item.line);
54859
+ }, [onLineClick, timeRange]);
54860
+ const viewLoadedTrackedRef = React25__namespace.default.useRef(null);
54861
+ const leaderboardData = React25__namespace.default.useMemo(() => {
54862
+ const loading = timeRange === "today" ? isLoadingToday : isLoadingMonthly;
54863
+ const efficiencyMap = timeRange === "today" ? todayEfficiencyByLineId : monthlyEfficiencyByLineId;
54864
+ return lines.map((line) => {
54865
+ const supervisors = supervisorsByLineId?.get(line.id) || [];
54866
+ const primarySupervisor = supervisors[0];
54867
+ const supervisorName = supervisorNamesByLineId.get(line.id) || primarySupervisor?.displayName || "Unassigned";
54868
+ const supervisorImage = primarySupervisor?.profilePhotoUrl || null;
54869
+ const hasEfficiency = efficiencyMap.has(line.id);
54870
+ const efficiency = loading ? null : hasEfficiency ? efficiencyMap.get(line.id) ?? 0 : timeRange === "monthly" ? 0 : null;
54871
+ const sortValue = typeof efficiency === "number" ? efficiency : -1;
54872
+ return {
54873
+ id: line.id,
54874
+ line,
54875
+ supervisorName,
54876
+ supervisorImage,
54877
+ supervisors,
54878
+ efficiency,
54879
+ sortValue
54880
+ };
54881
+ }).sort((a, b) => b.sortValue - a.sortValue).map((item, index) => ({ ...item, rank: index + 1 }));
54882
+ }, [
54883
+ lines,
54884
+ timeRange,
54885
+ todayEfficiencyByLineId,
54886
+ monthlyEfficiencyByLineId,
54887
+ supervisorsByLineId,
54888
+ supervisorNamesByLineId,
54889
+ isLoadingToday,
54890
+ isLoadingMonthly
54891
+ ]);
54892
+ React25__namespace.default.useEffect(() => {
54893
+ const isLoading = timeRange === "today" ? isLoadingToday : isLoadingMonthly;
54894
+ const trackingKey = `${timeRange}-${leaderboardData.length}`;
54895
+ if (leaderboardData.length > 0 && !isLoading && viewLoadedTrackedRef.current !== trackingKey) {
54896
+ viewLoadedTrackedRef.current = trackingKey;
54897
+ const topLine = leaderboardData[0];
54898
+ const bottomLine = leaderboardData[leaderboardData.length - 1];
54899
+ trackCoreEvent("Leaderboard View Loaded", {
54900
+ time_range: timeRange,
54901
+ lines_count: leaderboardData.length,
54902
+ top_line_id: topLine?.line.id,
54903
+ top_line_name: topLine?.line.line_name,
54904
+ top_efficiency: topLine?.efficiency,
54905
+ bottom_line_id: bottomLine?.line.id,
54906
+ bottom_line_name: bottomLine?.line.line_name,
54907
+ bottom_efficiency: bottomLine?.efficiency,
54908
+ efficiency_spread: topLine && bottomLine && topLine.efficiency !== null && bottomLine.efficiency !== null ? (topLine.efficiency - bottomLine.efficiency).toFixed(1) : null,
54909
+ from_page: "kpis_overview"
54910
+ });
54911
+ }
54912
+ }, [timeRange, leaderboardData, isLoadingToday, isLoadingMonthly]);
54913
+ const topThree = leaderboardData.slice(0, 3);
54914
+ leaderboardData.slice(3);
54915
+ const countdownTarget = timeRange === "monthly" ? monthEndDate : shiftEndDate;
54916
+ const countdownFormat = timeRange === "monthly" ? "days" : "clock";
54917
+ const countdownFinishedLabel = timeRange === "monthly" ? "Finished" : "Shift Ended";
54918
+ const handleCountdownFinished = React25__namespace.default.useCallback(() => {
54919
+ trackCoreEvent("Leaderboard Countdown Finished", {
54920
+ countdown_type: timeRange === "monthly" ? "month_end" : "shift_end",
54921
+ time_range: timeRange,
54922
+ from_page: "leaderboard"
54923
+ });
54924
+ }, [timeRange]);
54925
+ const podiumOrder = [
54926
+ topThree[1],
54927
+ // 2nd
54928
+ topThree[0],
54929
+ // 1st
54930
+ topThree[2]
54931
+ // 3rd
54932
+ ].filter(Boolean);
54933
+ const getRankColor = (rank) => {
54934
+ switch (rank) {
54935
+ case 1:
54936
+ return "from-yellow-50 to-yellow-100 border-yellow-200 text-yellow-800";
54937
+ case 2:
54938
+ return "from-gray-50 to-gray-100 border-gray-200 text-gray-700";
54939
+ case 3:
54940
+ return "from-orange-50 to-orange-100 border-orange-200 text-orange-800";
54941
+ default:
54942
+ return "bg-white border-gray-100";
54943
+ }
54944
+ };
54945
+ React25__namespace.default.useEffect(() => {
54946
+ const style = document.createElement("style");
54947
+ style.innerHTML = `
54948
+ @keyframes float {
54949
+ 0% { transform: translateY(0px); }
54950
+ 50% { transform: translateY(-8px); }
54951
+ 100% { transform: translateY(0px); }
54952
+ }
54953
+ .animate-float-slow {
54954
+ animation: float 4s ease-in-out infinite;
54955
+ }
54956
+ .animate-float-medium {
54957
+ animation: float 3.5s ease-in-out infinite;
54958
+ animation-delay: 0.5s;
54959
+ }
54960
+ .animate-float-fast {
54961
+ animation: float 3s ease-in-out infinite;
54962
+ animation-delay: 1s;
54963
+ }
54964
+ `;
54965
+ document.head.appendChild(style);
54966
+ return () => {
54967
+ document.head.removeChild(style);
54968
+ };
54969
+ }, []);
54970
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col h-full gap-4", children: [
54971
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col h-[70%] min-h-0", children: [
54972
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-none relative flex flex-col md:flex-row justify-center items-center gap-4 pt-2", children: [
54973
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex bg-gray-100/80 p-1 rounded-xl z-0", children: [
54974
+ /* @__PURE__ */ jsxRuntime.jsx(
54975
+ "button",
54976
+ {
54977
+ onClick: () => handleTimeRangeChange("today"),
54978
+ className: `px-8 py-2.5 text-sm font-bold rounded-lg transition-all duration-200 ${timeRange === "today" ? "bg-white text-slate-900 shadow-md ring-1 ring-black/5" : "text-slate-500 hover:text-slate-700"}`,
54979
+ children: "Daily"
54980
+ }
54981
+ ),
54982
+ /* @__PURE__ */ jsxRuntime.jsx(
54983
+ "button",
54984
+ {
54985
+ onClick: () => handleTimeRangeChange("monthly"),
54986
+ className: `px-8 py-2.5 text-sm font-bold rounded-lg transition-all duration-200 ${timeRange === "monthly" ? "bg-white text-slate-900 shadow-md ring-1 ring-black/5" : "text-slate-500 hover:text-slate-700"}`,
54987
+ children: "Monthly"
54988
+ }
54989
+ )
54990
+ ] }),
54991
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "md:absolute md:right-0 md:top-1/2 md:-translate-y-1/2 flex items-center gap-2.5 px-4 py-2 bg-white rounded-full shadow-sm border border-gray-100", children: [
54992
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "w-4 h-4 text-orange-500" }),
54993
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-2", children: [
54994
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-semibold text-gray-400 uppercase tracking-wider", children: "Ends in" }),
54995
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-bold text-gray-900 tabular-nums tracking-tight font-mono", children: /* @__PURE__ */ jsxRuntime.jsx(
54996
+ LeaderboardCountdown,
54997
+ {
54998
+ targetDate: countdownTarget,
54999
+ format: countdownFormat,
55000
+ finishedLabel: countdownFinishedLabel,
55001
+ placeholder: timeRange === "monthly" ? "--" : "--:--:--",
55002
+ onFinished: handleCountdownFinished
55003
+ }
55004
+ ) })
55005
+ ] })
55006
+ ] })
55007
+ ] }),
55008
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 hidden sm:flex justify-center items-end gap-2 md:gap-4 lg:gap-6 xl:gap-10 pb-4 pt-4 min-h-0", children: podiumOrder.map((item, index) => {
55009
+ if (!item) return null;
55010
+ const isFirst = item.rank === 1;
55011
+ const isSecond = item.rank === 2;
55012
+ item.rank === 3;
55013
+ return /* @__PURE__ */ jsxRuntime.jsxs(
55014
+ "div",
55015
+ {
55016
+ onClick: () => handleLeaderboardLineClick(item, "podium"),
55017
+ className: `relative flex flex-col items-center cursor-pointer z-10 transition-transform hover:scale-105`,
55018
+ children: [
55019
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative -mb-4 z-20 ${isFirst ? "animate-float-slow" : isSecond ? "animate-float-medium" : "animate-float-fast"}`, children: [
55020
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute inset-0 rounded-full blur-xl opacity-40 ${isFirst ? "bg-yellow-400" : isSecond ? "bg-gray-400" : "bg-orange-400"}` }),
55021
+ item.supervisors && item.supervisors.length > 1 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
55022
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex -space-x-3 md:-space-x-4", children: [
55023
+ item.supervisors.slice(0, 3).map((supervisor) => /* @__PURE__ */ jsxRuntime.jsx(
55024
+ "div",
55025
+ {
55026
+ className: "relative inline-block w-10 h-10 md:w-12 md:h-12 lg:w-14 lg:h-14 xl:w-16 xl:h-16 rounded-full ring-2 ring-white bg-white shadow-sm z-0 hover:z-10 transition-all hover:scale-110 overflow-hidden",
55027
+ children: supervisor.profilePhotoUrl ? /* @__PURE__ */ jsxRuntime.jsx(
55028
+ "img",
55029
+ {
55030
+ src: supervisor.profilePhotoUrl,
55031
+ alt: supervisor.displayName,
55032
+ className: "w-full h-full object-cover rounded-full"
55033
+ }
55034
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gradient-to-br from-slate-50 to-slate-100", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs md:text-sm lg:text-base font-bold text-gray-600 uppercase", children: getInitials(supervisor.displayName) }) })
55035
+ },
55036
+ supervisor.userId
55037
+ )),
55038
+ item.supervisors.length > 3 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex w-10 h-10 md:w-12 md:h-12 lg:w-14 lg:h-14 xl:w-16 xl:h-16 rounded-full ring-2 ring-white bg-gray-100 items-center justify-center text-xs md:text-sm font-medium text-gray-600 z-0", children: [
55039
+ "+",
55040
+ item.supervisors.length - 3
55041
+ ] })
55042
+ ] }),
55043
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute -bottom-2 md:-bottom-3 left-1/2 -translate-x-1/2 w-6 h-6 md:w-8 md:h-8 xl:w-10 xl:h-10 rounded-full flex items-center justify-center text-[10px] md:text-xs lg:text-sm xl:text-base font-extrabold text-white shadow-lg ${isFirst ? "bg-gradient-to-br from-yellow-400 to-yellow-600 ring-2 md:ring-4 ring-yellow-50" : isSecond ? "bg-gradient-to-br from-gray-400 to-gray-600 ring-2 md:ring-4 ring-gray-50" : "bg-gradient-to-br from-orange-400 to-orange-600 ring-2 md:ring-4 ring-orange-50"}`, children: item.rank })
55044
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative rounded-full p-1.5 bg-gradient-to-br ${isFirst ? "from-yellow-300 via-yellow-400 to-yellow-600 shadow-yellow-500/50" : isSecond ? "from-gray-300 via-gray-400 to-gray-500 shadow-gray-500/50" : "from-orange-300 via-orange-400 to-orange-600 shadow-orange-500/50"} shadow-xl`, children: [
55045
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12 h-12 md:w-16 md:h-16 lg:w-20 lg:h-20 xl:w-24 xl:h-24 rounded-full bg-white flex items-center justify-center overflow-hidden border-2 border-white/50", children: item.supervisorImage ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: item.supervisorImage, alt: item.supervisorName, className: "w-full h-full object-cover" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gradient-to-br from-slate-50 to-slate-100", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-base md:text-lg lg:text-xl xl:text-2xl font-bold ${isFirst ? "text-yellow-600" : isSecond ? "text-gray-600" : "text-orange-600"}`, children: getInitials(item.supervisorName) }) }) }),
55046
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute -bottom-2 md:-bottom-3 left-1/2 -translate-x-1/2 w-6 h-6 md:w-8 md:h-8 xl:w-10 xl:h-10 rounded-full flex items-center justify-center text-[10px] md:text-xs lg:text-sm xl:text-base font-extrabold text-white shadow-lg ${isFirst ? "bg-gradient-to-br from-yellow-400 to-yellow-600 ring-2 md:ring-4 ring-yellow-50" : isSecond ? "bg-gradient-to-br from-gray-400 to-gray-600 ring-2 md:ring-4 ring-gray-50" : "bg-gradient-to-br from-orange-400 to-orange-600 ring-2 md:ring-4 ring-orange-50"}`, children: item.rank })
55047
+ ] })
55048
+ ] }),
55049
+ /* @__PURE__ */ jsxRuntime.jsx(
55050
+ "div",
55051
+ {
55052
+ className: `flex flex-col items-center w-32 md:w-40 lg:w-48 xl:w-60 px-2 md:px-3 xl:px-4 pb-3 md:pb-4 pt-8 md:pt-10 rounded-2xl border bg-gradient-to-b shadow-2xl backdrop-blur-sm ${getRankColor(item.rank)} ${isFirst ? "h-44 md:h-52 lg:h-60 xl:h-64" : isSecond ? "h-36 md:h-44 lg:h-52 xl:h-56" : "h-28 md:h-36 lg:h-44 xl:h-48"}`,
55053
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col items-center justify-center w-full", children: [
55054
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: `font-bold text-gray-900 text-center line-clamp-1 mb-1 ${isFirst ? "text-xs md:text-sm lg:text-base xl:text-lg" : "text-[10px] md:text-xs lg:text-sm xl:text-base"}`, children: item.supervisorName }),
55055
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-gray-600 text-center line-clamp-1 font-medium opacity-80 bg-white/50 px-2 md:px-3 py-0.5 rounded-full ${isFirst ? "text-[9px] md:text-[10px] lg:text-xs xl:text-sm mb-2 md:mb-3" : "text-[8px] md:text-[9px] lg:text-[10px] xl:text-xs mb-1 md:mb-2"}`, children: item.line.line_name }),
55056
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center mt-auto", children: [
55057
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `font-bold uppercase tracking-widest mb-0.5 ${isFirst ? "text-yellow-700/70 text-[8px] md:text-[9px] lg:text-[10px] xl:text-xs" : isSecond ? "text-gray-600/70 text-[7px] md:text-[8px] lg:text-[9px] xl:text-[10px]" : "text-orange-700/70 text-[7px] md:text-[8px] lg:text-[9px] xl:text-[10px]"}`, children: "Efficiency" }),
55058
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `font-black tracking-tight leading-none ${isFirst ? "text-lg md:text-xl lg:text-2xl xl:text-3xl text-transparent bg-clip-text bg-gradient-to-br from-yellow-600 to-yellow-800 drop-shadow-sm" : isSecond ? "text-base md:text-lg lg:text-xl xl:text-2xl text-transparent bg-clip-text bg-gradient-to-br from-gray-600 to-gray-800 drop-shadow-sm" : "text-sm md:text-base lg:text-lg xl:text-xl text-transparent bg-clip-text bg-gradient-to-br from-orange-600 to-orange-800 drop-shadow-sm"}`, children: formatEfficiency(item.efficiency) })
55059
+ ] })
55060
+ ] })
55061
+ }
55062
+ )
55063
+ ]
55064
+ },
55065
+ item.id
55066
+ );
55067
+ }) })
55068
+ ] }),
55069
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-none h-[30%] min-h-0 flex flex-col", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden h-full flex flex-col", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-auto flex-1", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full border-collapse", children: [
55070
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-gray-50 border-b border-gray-200 sticky top-0 z-10", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
55071
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider w-16", children: "Rank" }),
55072
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider", children: "Assigned To" }),
55073
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider", children: "Line" }),
55074
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-right text-xs font-semibold text-gray-500 uppercase tracking-wider", children: "Efficiency" })
55075
+ ] }) }),
55076
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-gray-100", children: leaderboardData.map((item) => {
55077
+ const isTopThree = item.rank <= 3;
55078
+ return /* @__PURE__ */ jsxRuntime.jsxs(
55079
+ "tr",
55080
+ {
55081
+ onClick: () => handleLeaderboardLineClick(item, isTopThree ? "podium" : "table"),
55082
+ className: `hover:bg-gray-50 transition-colors cursor-pointer group ${isTopThree ? "sm:hidden" : ""}`,
55083
+ children: [
55084
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center w-8 h-8 rounded-full bg-gray-100 text-gray-600 font-bold text-sm", children: item.rank }) }),
55085
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
55086
+ item.supervisors && item.supervisors.length > 1 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex -space-x-2", children: [
55087
+ item.supervisors.slice(0, 3).map((supervisor) => /* @__PURE__ */ jsxRuntime.jsx(
55088
+ "div",
55089
+ {
55090
+ className: "relative inline-block w-7 h-7 rounded-full ring-2 ring-white bg-white shadow-sm overflow-hidden",
55091
+ children: supervisor.profilePhotoUrl ? /* @__PURE__ */ jsxRuntime.jsx(
55092
+ "img",
55093
+ {
55094
+ src: supervisor.profilePhotoUrl,
55095
+ alt: supervisor.displayName,
55096
+ className: "w-full h-full object-cover rounded-full"
55097
+ }
55098
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-100 text-[10px] font-bold text-gray-600 uppercase rounded-full", children: getInitials(supervisor.displayName) })
55099
+ },
55100
+ supervisor.userId
55101
+ )),
55102
+ item.supervisors.length > 3 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex w-7 h-7 rounded-full ring-2 ring-white bg-gray-100 items-center justify-center text-[10px] font-medium text-gray-600", children: [
55103
+ "+",
55104
+ item.supervisors.length - 3
55105
+ ] })
55106
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 h-8 rounded-full bg-gray-100 flex items-center justify-center text-gray-500 overflow-hidden border border-gray-200 flex-shrink-0", children: item.supervisorImage ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: item.supervisorImage, alt: item.supervisorName, className: "w-full h-full object-cover" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-bold", children: getInitials(item.supervisorName) }) }),
55107
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-900", children: item.supervisorName })
55108
+ ] }) }),
55109
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3 whitespace-nowrap text-sm text-gray-500", children: item.line.line_name }),
55110
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3 whitespace-nowrap text-right", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col items-end", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-bold text-gray-900", children: formatEfficiency(item.efficiency) }) }) })
55111
+ ]
55112
+ },
55113
+ item.id
55114
+ );
55115
+ }) })
55116
+ ] }) }) }) })
55117
+ ] });
55118
+ };
54575
55119
  var LineCard = ({
54576
55120
  line,
54577
55121
  kpis,
@@ -54730,6 +55274,8 @@ var KPIsOverviewView = ({
54730
55274
  lineIds
54731
55275
  }) => {
54732
55276
  const [lines, setLines] = React25.useState([]);
55277
+ const [activeTab, setActiveTab] = React25.useState("today");
55278
+ const [timeRange, setTimeRange] = React25.useState("today");
54733
55279
  const [loading, setLoading] = React25.useState(true);
54734
55280
  const [error, setError] = React25.useState(null);
54735
55281
  const [topPerformer, setTopPerformer] = React25.useState({
@@ -54743,6 +55289,10 @@ var KPIsOverviewView = ({
54743
55289
  });
54744
55290
  const [topPerformerLoading, setTopPerformerLoading] = React25.useState(true);
54745
55291
  const [topPerformerImageError, setTopPerformerImageError] = React25.useState(false);
55292
+ const [monthlyEfficiencyByLineId, setMonthlyEfficiencyByLineId] = React25.useState(/* @__PURE__ */ new Map());
55293
+ const [monthlyLoading, setMonthlyLoading] = React25.useState(false);
55294
+ const [monthlyError, setMonthlyError] = React25.useState(null);
55295
+ const monthlyRequestKeyRef = React25.useRef(null);
54746
55296
  const supabase = useSupabase();
54747
55297
  const dashboardConfig = useDashboardConfig();
54748
55298
  const entityConfig = useEntityConfig();
@@ -54757,6 +55307,7 @@ var KPIsOverviewView = ({
54757
55307
  const supervisorEnabled = dashboardConfig?.supervisorConfig?.enabled || false;
54758
55308
  const dbTimezone = useAppTimezone();
54759
55309
  const configuredTimezone = dbTimezone || dateTimeConfig.defaultTimezone || "UTC";
55310
+ const { startDate: monthStartDate, endDate: monthEndDateKey, monthEndDate } = getMonthDateInfo(configuredTimezone);
54760
55311
  const factoryViewId = entityConfig.factoryViewId || "factory";
54761
55312
  const {
54762
55313
  lineMetrics,
@@ -54774,6 +55325,16 @@ var KPIsOverviewView = ({
54774
55325
  });
54775
55326
  return map;
54776
55327
  }, [lineMetrics]);
55328
+ const todayEfficiencyByLineId = React25__namespace.default.useMemo(() => {
55329
+ const map = /* @__PURE__ */ new Map();
55330
+ kpisByLineId.forEach((kpis, lineId) => {
55331
+ const value = kpis?.efficiency?.value;
55332
+ if (typeof value === "number" && Number.isFinite(value)) {
55333
+ map.set(lineId, value);
55334
+ }
55335
+ });
55336
+ return map;
55337
+ }, [kpisByLineId]);
54777
55338
  const visibleLineIds = React25__namespace.default.useMemo(() => lines.map((l) => l.id), [lines]);
54778
55339
  const { supervisorNamesByLineId, supervisorsByLineId, allSupervisorsMap } = useSupervisorsByLineIds(visibleLineIds, {
54779
55340
  enabled: supervisorEnabled && visibleLineIds.length > 0
@@ -54837,6 +55398,61 @@ var KPIsOverviewView = ({
54837
55398
  };
54838
55399
  fetchLines();
54839
55400
  }, [supabase, dashboardConfig, lineIds]);
55401
+ const fetchMonthlyLeaderboard = React25.useCallback(async () => {
55402
+ if (!supabase || !resolvedCompanyId || lines.length === 0) return;
55403
+ const targetLineIds = lines.map((line) => line.id);
55404
+ const lineIdsKey = targetLineIds.slice().sort().join(",");
55405
+ const requestKey = `${resolvedCompanyId}|${monthStartDate}|${monthEndDateKey}|${lineIdsKey}`;
55406
+ if (monthlyRequestKeyRef.current === requestKey) return;
55407
+ monthlyRequestKeyRef.current = requestKey;
55408
+ setMonthlyLoading(true);
55409
+ setMonthlyError(null);
55410
+ const fetchStartTime = Date.now();
55411
+ const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
55412
+ const currentMonth = (/* @__PURE__ */ new Date()).getMonth() + 1;
55413
+ try {
55414
+ const entries = await lineLeaderboardService.getLineLeaderboard(supabase, {
55415
+ companyId: resolvedCompanyId,
55416
+ startDate: monthStartDate,
55417
+ endDate: monthEndDateKey,
55418
+ lineIds: targetLineIds
55419
+ });
55420
+ const nextMap = /* @__PURE__ */ new Map();
55421
+ targetLineIds.forEach((lineId) => nextMap.set(lineId, 0));
55422
+ entries.forEach((entry) => {
55423
+ const value = Number(entry.avg_efficiency);
55424
+ nextMap.set(entry.line_id, Number.isFinite(value) ? value : 0);
55425
+ });
55426
+ setMonthlyEfficiencyByLineId(nextMap);
55427
+ trackCoreEvent("Leaderboard Monthly Data Fetched", {
55428
+ success: true,
55429
+ lines_count: entries.length,
55430
+ fetch_duration_ms: Date.now() - fetchStartTime,
55431
+ year: currentYear,
55432
+ month: currentMonth,
55433
+ has_data: entries.length > 0,
55434
+ from_page: "leaderboard"
55435
+ });
55436
+ } catch (err) {
55437
+ console.error("[KPIsOverviewView] Failed to load monthly leaderboard:", err);
55438
+ setMonthlyError("Failed to load monthly leaderboard");
55439
+ monthlyRequestKeyRef.current = null;
55440
+ trackCoreEvent("Leaderboard Monthly Data Fetched", {
55441
+ success: false,
55442
+ error_message: err instanceof Error ? err.message : "Unknown error",
55443
+ fetch_duration_ms: Date.now() - fetchStartTime,
55444
+ year: currentYear,
55445
+ month: currentMonth,
55446
+ from_page: "leaderboard"
55447
+ });
55448
+ } finally {
55449
+ setMonthlyLoading(false);
55450
+ }
55451
+ }, [supabase, resolvedCompanyId, lines, monthStartDate, monthEndDateKey]);
55452
+ React25.useEffect(() => {
55453
+ if (activeTab !== "leaderboard") return;
55454
+ fetchMonthlyLeaderboard();
55455
+ }, [activeTab, timeRange, fetchMonthlyLeaderboard]);
54840
55456
  const formatTopPerformerWeek = (periodStart, periodEnd) => {
54841
55457
  if (!periodStart || !periodEnd) return "Last Week";
54842
55458
  const startDate = /* @__PURE__ */ new Date(`${periodStart}T00:00:00`);
@@ -54847,6 +55463,21 @@ var KPIsOverviewView = ({
54847
55463
  return `${startLabel} - ${endLabel}`;
54848
55464
  };
54849
55465
  const buildTopPerformerDisplay = (record) => {
55466
+ const supervisorAvatars = [];
55467
+ if (record.supervisors && record.supervisors.length > 0) {
55468
+ for (const s of record.supervisors) {
55469
+ let supervisorName = "Supervisor";
55470
+ if (s.first_name) {
55471
+ supervisorName = s.last_name ? `${s.first_name} ${s.last_name}` : s.first_name;
55472
+ }
55473
+ supervisorAvatars.push({
55474
+ userId: s.user_id,
55475
+ name: supervisorName,
55476
+ imageUrl: s.profile_photo_url || null,
55477
+ initials: getInitials(supervisorName)
55478
+ });
55479
+ }
55480
+ }
54850
55481
  let displayName = record.recipient_name || "Supervisor";
54851
55482
  if (record.first_name) {
54852
55483
  displayName = record.last_name ? `${record.first_name} ${record.last_name}` : record.first_name;
@@ -54865,16 +55496,26 @@ var KPIsOverviewView = ({
54865
55496
  displayName = parts[0].charAt(0).toUpperCase() + parts[0].slice(1);
54866
55497
  }
54867
55498
  }
55499
+ if (supervisorAvatars.length === 0 && record.recipient_user_id) {
55500
+ supervisorAvatars.push({
55501
+ userId: record.recipient_user_id,
55502
+ name: displayName,
55503
+ imageUrl: record.profile_photo_url || null,
55504
+ initials: getInitials(displayName)
55505
+ });
55506
+ }
54868
55507
  const efficiencyValue = typeof record.avg_efficiency === "number" ? record.avg_efficiency : Number.parseFloat(String(record.avg_efficiency));
54869
55508
  const efficiency = Number.isFinite(efficiencyValue) ? efficiencyValue : null;
55509
+ const unitLabel = record.winning_line_name || record.unit || "Line";
54870
55510
  return {
54871
55511
  name: displayName,
54872
55512
  role: "Sup.",
54873
- unit: record.unit || "Line",
55513
+ unit: unitLabel,
54874
55514
  periodLabel: formatTopPerformerWeek(record.period_start, record.period_end),
54875
55515
  efficiency,
54876
55516
  imageUrl: record.profile_photo_url || null,
54877
- initials: getInitials(displayName)
55517
+ initials: getInitials(displayName),
55518
+ supervisors: supervisorAvatars.length > 0 ? supervisorAvatars : void 0
54878
55519
  };
54879
55520
  };
54880
55521
  const handleLineClick = (line, kpis) => {
@@ -54905,6 +55546,16 @@ var KPIsOverviewView = ({
54905
55546
  navigation.navigate("/");
54906
55547
  }
54907
55548
  }, [onBackClick, backLinkUrl, navigation]);
55549
+ const handleTabChange = React25.useCallback((newTab) => {
55550
+ if (newTab === activeTab) return;
55551
+ trackCoreEvent("Leaderboard Tab Switched", {
55552
+ from_tab: activeTab,
55553
+ to_tab: newTab,
55554
+ from_page: "kpis_overview",
55555
+ lines_count: lines.length
55556
+ });
55557
+ setActiveTab(newTab);
55558
+ }, [activeTab, lines.length]);
54908
55559
  const formatLocalDate2 = (date) => {
54909
55560
  const options = {
54910
55561
  year: "numeric",
@@ -54913,7 +55564,20 @@ var KPIsOverviewView = ({
54913
55564
  };
54914
55565
  return date.toLocaleDateString("en-US", options);
54915
55566
  };
55567
+ const getMonthRange = () => {
55568
+ const now4 = /* @__PURE__ */ new Date();
55569
+ const startOfMonth2 = new Date(now4.getFullYear(), now4.getMonth(), 1);
55570
+ const endOfMonth2 = new Date(now4.getFullYear(), now4.getMonth() + 1, 0);
55571
+ const startLabel = startOfMonth2.toLocaleDateString("en-US", { month: "short", day: "numeric" });
55572
+ const endLabel = endOfMonth2.toLocaleDateString("en-US", { month: "short", day: "numeric" });
55573
+ return `${startLabel} - ${endLabel}, ${now4.getFullYear()}`;
55574
+ };
55575
+ const isMonthlyMode = activeTab === "leaderboard" && timeRange === "monthly";
54916
55576
  const currentShiftDetails = getCurrentShift(configuredTimezone, shiftConfig);
55577
+ const shiftEndDate = React25__namespace.default.useMemo(
55578
+ () => getShiftEndDate(currentShiftDetails, configuredTimezone),
55579
+ [currentShiftDetails, configuredTimezone]
55580
+ );
54917
55581
  const shiftName = (currentShiftDetails.shiftName || "Day").replace(/ Shift$/i, "");
54918
55582
  const showTopPerformerImage = Boolean(topPerformer.imageUrl) && !topPerformerImageError;
54919
55583
  typeof topPerformer.efficiency === "number" && Number.isFinite(topPerformer.efficiency);
@@ -55021,29 +55685,61 @@ var KPIsOverviewView = ({
55021
55685
  }
55022
55686
  ),
55023
55687
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
55024
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg font-semibold text-gray-900", children: "Overview" }),
55688
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg font-semibold text-gray-900", children: activeTab === "leaderboard" ? "Leaderboard" : "Overview" }),
55025
55689
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-emerald-500 animate-pulse ring-2 ring-emerald-500/20" })
55026
55690
  ] }) }),
55027
55691
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12" })
55028
55692
  ] }),
55029
55693
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex flex-wrap items-center justify-center gap-2", children: [
55030
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-gray-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: formatLocalDate2(/* @__PURE__ */ new Date()) }) }),
55031
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-gray-100 rounded-full", children: [
55032
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon(currentShiftDetails.shiftId) }),
55033
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: shiftName })
55034
- ] }),
55035
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-green-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-green-700", children: /* @__PURE__ */ jsxRuntime.jsx(ISTTimer_default, {}) }) })
55694
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `inline-flex items-center bg-gray-100 rounded-full ${isMonthlyMode ? "px-4 py-1.5 ring-1 ring-gray-200 shadow-sm" : "px-2.5 py-1"}`, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `font-medium text-gray-700 ${isMonthlyMode ? "text-sm" : "text-xs"}`, children: isMonthlyMode ? getMonthRange() : formatLocalDate2(/* @__PURE__ */ new Date()) }) }),
55695
+ !isMonthlyMode && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
55696
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-gray-100 rounded-full", children: [
55697
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon(currentShiftDetails.shiftId) }),
55698
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: shiftName })
55699
+ ] }),
55700
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-green-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-green-700", children: /* @__PURE__ */ jsxRuntime.jsx(ISTTimer_default, {}) }) })
55701
+ ] })
55036
55702
  ] }),
55037
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 bg-white shadow-md hover:shadow-lg transition-all duration-300 ease-out hover:scale-[1.01] relative rounded-2xl border border-amber-100 pl-2 pr-4 py-1.5 flex items-center justify-between group", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4 min-w-0", children: [
55038
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 rounded-full border-2 border-amber-100 overflow-hidden bg-amber-50 shadow-sm transition-transform group-hover:scale-105", children: showTopPerformerImage ? /* @__PURE__ */ jsxRuntime.jsx(
55039
- "img",
55040
- {
55041
- src: topPerformer.imageUrl || "",
55042
- alt: "Top Performer",
55043
- className: "w-full h-full object-cover",
55044
- onError: () => setTopPerformerImageError(true)
55045
- }
55046
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-amber-50 text-sm font-bold text-amber-600", children: topPerformer.initials }) }) }),
55703
+ activeTab !== "leaderboard" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 bg-white shadow-md hover:shadow-lg transition-all duration-300 ease-out hover:scale-[1.01] relative rounded-2xl border border-amber-100 pl-2 pr-4 py-1.5 flex items-center justify-between group", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4 min-w-0", children: [
55704
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-shrink-0", children: [
55705
+ (!topPerformer.supervisors || topPerformer.supervisors.length <= 1) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 rounded-full border-2 border-amber-100 overflow-hidden bg-amber-50 shadow-sm transition-transform group-hover:scale-105", children: showTopPerformerImage ? /* @__PURE__ */ jsxRuntime.jsx(
55706
+ "img",
55707
+ {
55708
+ src: topPerformer.imageUrl || "",
55709
+ alt: "Top Performer",
55710
+ className: "w-full h-full object-cover",
55711
+ onError: () => setTopPerformerImageError(true)
55712
+ }
55713
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-amber-50 text-sm font-bold text-amber-600", children: topPerformer.initials }) }),
55714
+ topPerformer.supervisors && topPerformer.supervisors.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex -space-x-2", children: [
55715
+ topPerformer.supervisors.slice(0, 3).map((supervisor, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
55716
+ "div",
55717
+ {
55718
+ className: "relative inline-block w-9 h-9 rounded-full ring-2 ring-white bg-amber-50 shadow-sm z-0 hover:z-10 transition-all hover:scale-110 group/avatar",
55719
+ style: { zIndex: 3 - idx },
55720
+ children: [
55721
+ supervisor.imageUrl ? /* @__PURE__ */ jsxRuntime.jsx(
55722
+ "img",
55723
+ {
55724
+ src: supervisor.imageUrl,
55725
+ alt: supervisor.name,
55726
+ className: "w-full h-full object-cover rounded-full"
55727
+ }
55728
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center text-xs font-bold text-amber-600 uppercase rounded-full", children: supervisor.initials }),
55729
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 bg-gray-900 text-white text-[10px] font-medium rounded shadow-lg opacity-0 group-hover/avatar:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-20", children: [
55730
+ supervisor.name,
55731
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-full left-1/2 -translate-x-1/2 -mt-[1px] border-4 border-transparent border-t-gray-900" })
55732
+ ] })
55733
+ ]
55734
+ },
55735
+ supervisor.userId
55736
+ )),
55737
+ topPerformer.supervisors.length > 3 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex w-9 h-9 rounded-full ring-2 ring-white bg-amber-100 items-center justify-center text-xs font-medium text-amber-700 z-0", children: [
55738
+ "+",
55739
+ topPerformer.supervisors.length - 3
55740
+ ] })
55741
+ ] })
55742
+ ] }),
55047
55743
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
55048
55744
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 leading-none mb-1", children: [
55049
55745
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-bold text-amber-600 uppercase tracking-widest", children: "Performer of the week" }),
@@ -55069,6 +55765,24 @@ var KPIsOverviewView = ({
55069
55765
  ) })
55070
55766
  ] })
55071
55767
  ] })
55768
+ ] }) }),
55769
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex bg-gray-100/80 p-1 rounded-xl w-full", children: [
55770
+ /* @__PURE__ */ jsxRuntime.jsx(
55771
+ "button",
55772
+ {
55773
+ onClick: () => handleTabChange("today"),
55774
+ className: `flex-1 py-2 text-xs font-medium rounded-lg transition-all duration-200 ${activeTab === "today" ? "bg-white text-blue-600 shadow-sm ring-1 ring-black/5" : "text-gray-600 hover:text-gray-900"}`,
55775
+ children: "Overview"
55776
+ }
55777
+ ),
55778
+ /* @__PURE__ */ jsxRuntime.jsx(
55779
+ "button",
55780
+ {
55781
+ onClick: () => handleTabChange("leaderboard"),
55782
+ className: `flex-1 py-2 text-xs font-medium rounded-lg transition-all duration-200 ${activeTab === "leaderboard" ? "bg-white text-blue-600 shadow-sm ring-1 ring-black/5" : "text-gray-600 hover:text-gray-900"}`,
55783
+ children: "Leaderboard"
55784
+ }
55785
+ )
55072
55786
  ] }) })
55073
55787
  ] }),
55074
55788
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "hidden sm:block", children: [
@@ -55083,19 +55797,49 @@ var KPIsOverviewView = ({
55083
55797
  }
55084
55798
  ) }),
55085
55799
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
55086
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl md:text-3xl lg:text-4xl font-semibold text-gray-900 tracking-tight", children: "Overview" }),
55800
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl md:text-3xl lg:text-4xl font-semibold text-gray-900 tracking-tight", children: activeTab === "leaderboard" ? "Leaderboard" : "Overview" }),
55087
55801
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-emerald-500 animate-pulse ring-4 ring-emerald-500/10 flex-shrink-0" })
55088
55802
  ] }),
55089
- !topPerformerLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-0 top-1/2 -translate-y-1/2 z-10", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-2xl border border-amber-200 shadow-md pl-1.5 pr-4 py-1.5 flex items-center gap-4 transition-all hover:shadow-lg hover:border-amber-300 group", children: [
55090
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 rounded-full ring-2 ring-amber-100 overflow-hidden bg-amber-50 shadow-inner flex-shrink-0 transition-transform group-hover:scale-105", children: showTopPerformerImage ? /* @__PURE__ */ jsxRuntime.jsx(
55091
- "img",
55092
- {
55093
- src: topPerformer.imageUrl || "",
55094
- alt: topPerformer.name,
55095
- className: "w-full h-full object-cover",
55096
- onError: () => setTopPerformerImageError(true)
55097
- }
55098
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center text-sm font-bold text-amber-600 uppercase", children: topPerformer.initials }) }) }),
55803
+ !topPerformerLoading && activeTab !== "leaderboard" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-0 top-1/2 -translate-y-1/2 z-10", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-2xl border border-amber-200 shadow-md pl-1.5 pr-4 py-1.5 flex items-center gap-4 transition-all hover:shadow-lg hover:border-amber-300 group", children: [
55804
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
55805
+ (!topPerformer.supervisors || topPerformer.supervisors.length <= 1) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 rounded-full ring-2 ring-amber-100 overflow-hidden bg-amber-50 shadow-inner flex-shrink-0 transition-transform group-hover:scale-105", children: showTopPerformerImage ? /* @__PURE__ */ jsxRuntime.jsx(
55806
+ "img",
55807
+ {
55808
+ src: topPerformer.imageUrl || "",
55809
+ alt: topPerformer.name,
55810
+ className: "w-full h-full object-cover",
55811
+ onError: () => setTopPerformerImageError(true)
55812
+ }
55813
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center text-sm font-bold text-amber-600 uppercase", children: topPerformer.initials }) }),
55814
+ topPerformer.supervisors && topPerformer.supervisors.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex -space-x-2", children: [
55815
+ topPerformer.supervisors.slice(0, 3).map((supervisor, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
55816
+ "div",
55817
+ {
55818
+ className: "relative inline-block w-10 h-10 rounded-full ring-2 ring-white bg-amber-50 shadow-sm z-0 hover:z-10 transition-all hover:scale-110 group/avatar",
55819
+ style: { zIndex: 3 - idx },
55820
+ children: [
55821
+ supervisor.imageUrl ? /* @__PURE__ */ jsxRuntime.jsx(
55822
+ "img",
55823
+ {
55824
+ src: supervisor.imageUrl,
55825
+ alt: supervisor.name,
55826
+ className: "w-full h-full object-cover rounded-full"
55827
+ }
55828
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center text-sm font-bold text-amber-600 uppercase rounded-full", children: supervisor.initials }),
55829
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-2 py-1 bg-gray-900 text-white text-[10px] font-medium rounded shadow-lg opacity-0 group-hover/avatar:opacity-100 transition-opacity whitespace-nowrap pointer-events-none z-20", children: [
55830
+ supervisor.name,
55831
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-full left-1/2 -translate-x-1/2 -mt-[1px] border-4 border-transparent border-t-gray-900" })
55832
+ ] })
55833
+ ]
55834
+ },
55835
+ supervisor.userId
55836
+ )),
55837
+ topPerformer.supervisors.length > 3 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex w-10 h-10 rounded-full ring-2 ring-white bg-amber-100 items-center justify-center text-sm font-medium text-amber-700 z-0", children: [
55838
+ "+",
55839
+ topPerformer.supervisors.length - 3
55840
+ ] })
55841
+ ] })
55842
+ ] }),
55099
55843
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col min-w-0", children: [
55100
55844
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-[10px] leading-tight mb-1", children: [
55101
55845
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-bold text-amber-600 uppercase tracking-widest", children: "Performer of the week" }),
@@ -55124,40 +55868,84 @@ var KPIsOverviewView = ({
55124
55868
  ] }) })
55125
55869
  ] }),
55126
55870
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-blue-50/50 px-4 py-2 rounded-xl border border-blue-100/50 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-6", children: [
55127
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-700", children: [
55128
- /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4 opacity-70", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
55129
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-semibold tabular-nums", children: /* @__PURE__ */ jsxRuntime.jsx(ISTTimer_default, {}) })
55871
+ !isMonthlyMode && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
55872
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-700", children: [
55873
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4 opacity-70", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
55874
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-semibold tabular-nums", children: /* @__PURE__ */ jsxRuntime.jsx(ISTTimer_default, {}) })
55875
+ ] }),
55876
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" })
55130
55877
  ] }),
55131
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" }),
55132
55878
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
55133
- /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4 opacity-70", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" }) }),
55134
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: formatLocalDate2(/* @__PURE__ */ new Date()) })
55879
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { className: `w-4 h-4 opacity-70 ${isMonthlyMode ? "scale-110" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" }) }),
55880
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${isMonthlyMode ? "text-base font-medium" : "text-sm font-medium"}`, children: isMonthlyMode ? getMonthRange() : formatLocalDate2(/* @__PURE__ */ new Date()) })
55135
55881
  ] }),
55136
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" }),
55137
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
55138
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon(currentShiftDetails.shiftId) }),
55139
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-semibold uppercase tracking-wider", children: [
55140
- shiftName,
55141
- " Shift"
55882
+ !isMonthlyMode && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
55883
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" }),
55884
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
55885
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon(currentShiftDetails.shiftId) }),
55886
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-semibold uppercase tracking-wider", children: [
55887
+ shiftName,
55888
+ " Shift"
55889
+ ] })
55142
55890
  ] })
55143
55891
  ] })
55892
+ ] }) }),
55893
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 flex justify-start", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1.5 lg:gap-2", children: [
55894
+ /* @__PURE__ */ jsxRuntime.jsx(
55895
+ "button",
55896
+ {
55897
+ onClick: () => handleTabChange("today"),
55898
+ className: `px-3 lg:px-4 py-1.5 lg:py-2 text-sm lg:text-base font-medium rounded-lg transition-colors whitespace-nowrap ${activeTab === "today" ? "bg-blue-50 text-blue-600" : "text-gray-600 hover:bg-gray-50"}`,
55899
+ children: "Overview"
55900
+ }
55901
+ ),
55902
+ /* @__PURE__ */ jsxRuntime.jsx(
55903
+ "button",
55904
+ {
55905
+ onClick: () => handleTabChange("leaderboard"),
55906
+ className: `px-3 lg:px-4 py-1.5 lg:py-2 text-sm lg:text-base font-medium rounded-lg transition-colors whitespace-nowrap ${activeTab === "leaderboard" ? "bg-blue-50 text-blue-600" : "text-gray-600 hover:bg-gray-50"}`,
55907
+ children: "Leaderboard"
55908
+ }
55909
+ )
55144
55910
  ] }) })
55145
55911
  ] })
55146
55912
  ] }) }),
55147
- /* @__PURE__ */ jsxRuntime.jsx("main", { className: "flex-1 p-3 sm:p-4 md:p-6 overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3 sm:gap-4 md:gap-6", children: lines.map((line) => /* @__PURE__ */ jsxRuntime.jsx(
55148
- LineCard,
55149
- {
55150
- line,
55151
- kpis: metricsError ? null : kpisByLineId.get(line.id) ?? (metricsLoading ? null : defaultKPIs),
55152
- isLoading: metricsLoading,
55153
- error: metricsError,
55154
- onClick: (kpis) => handleLineClick(line, kpis),
55155
- supervisorEnabled,
55156
- supervisorName: supervisorNamesByLineId.get(line.id) || null,
55157
- supervisors: supervisorsByLineId?.get(line.id)
55158
- },
55159
- line.id
55160
- )) }) })
55913
+ /* @__PURE__ */ jsxRuntime.jsx("main", { className: `flex-1 p-3 sm:p-4 md:p-6 bg-slate-50 ${activeTab === "leaderboard" ? "overflow-hidden flex flex-col" : "overflow-y-auto"}`, children: activeTab === "today" ? (
55914
+ /* Line Cards Grid */
55915
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3 sm:gap-4 md:gap-6", children: lines.map((line) => /* @__PURE__ */ jsxRuntime.jsx(
55916
+ LineCard,
55917
+ {
55918
+ line,
55919
+ kpis: metricsError ? null : kpisByLineId.get(line.id) ?? (metricsLoading ? null : defaultKPIs),
55920
+ isLoading: metricsLoading,
55921
+ error: metricsError,
55922
+ onClick: (kpis) => handleLineClick(line, kpis),
55923
+ supervisorEnabled,
55924
+ supervisorName: supervisorNamesByLineId.get(line.id) || null,
55925
+ supervisors: supervisorsByLineId?.get(line.id)
55926
+ },
55927
+ line.id
55928
+ )) })
55929
+ ) : (
55930
+ /* Leaderboard View */
55931
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full", children: /* @__PURE__ */ jsxRuntime.jsx(
55932
+ LinesLeaderboard,
55933
+ {
55934
+ lines,
55935
+ onLineClick: (line) => handleLineClick(line),
55936
+ timeRange,
55937
+ setTimeRange,
55938
+ todayEfficiencyByLineId,
55939
+ monthlyEfficiencyByLineId,
55940
+ supervisorsByLineId,
55941
+ supervisorNamesByLineId,
55942
+ isLoadingToday: metricsLoading,
55943
+ isLoadingMonthly: monthlyLoading,
55944
+ shiftEndDate,
55945
+ monthEndDate
55946
+ }
55947
+ ) })
55948
+ ) })
55161
55949
  ] });
55162
55950
  };
55163
55951
  var KPIsOverviewView_default = KPIsOverviewView;
@@ -55303,6 +56091,12 @@ var LeaderboardDetailView = React25.memo(({
55303
56091
  const [activeTab, setActiveTab] = React25.useState("today");
55304
56092
  const timezone = useAppTimezone();
55305
56093
  const staticShiftConfig = useShiftConfig();
56094
+ const representativeLineId = lineId || lines[0]?.id;
56095
+ const {
56096
+ shiftConfig: dynamicShiftConfig,
56097
+ isLoading: isDynamicShiftConfigLoading
56098
+ } = useDynamicShiftConfig(representativeLineId);
56099
+ const activeShiftConfig = dynamicShiftConfig ?? staticShiftConfig;
55306
56100
  const shiftId = React25.useMemo(
55307
56101
  () => typeof shift === "number" ? shift : typeof shift === "string" ? parseInt(shift) : void 0,
55308
56102
  [shift]
@@ -55336,7 +56130,7 @@ var LeaderboardDetailView = React25.memo(({
55336
56130
  return shiftId.toString();
55337
56131
  }
55338
56132
  try {
55339
- const current = getCurrentShift(timezone || "Asia/Kolkata", staticShiftConfig);
56133
+ const current = getCurrentShift(timezone || "Asia/Kolkata", activeShiftConfig);
55340
56134
  return current.shiftId.toString();
55341
56135
  } catch (e) {
55342
56136
  return "0";
@@ -55348,13 +56142,13 @@ var LeaderboardDetailView = React25.memo(({
55348
56142
  }
55349
56143
  }, [shiftId]);
55350
56144
  React25.useEffect(() => {
55351
- if (shiftId !== void 0 || !staticShiftConfig) return;
56145
+ if (shiftId !== void 0 || isDynamicShiftConfigLoading || !activeShiftConfig) return;
55352
56146
  try {
55353
- const current = getCurrentShift(timezone || "Asia/Kolkata", staticShiftConfig);
56147
+ const current = getCurrentShift(timezone || "Asia/Kolkata", activeShiftConfig);
55354
56148
  setSelectedShiftFilter((prev) => prev === "0" ? current.shiftId.toString() : prev);
55355
56149
  } catch (e) {
55356
56150
  }
55357
- }, [shiftId, staticShiftConfig, timezone]);
56151
+ }, [shiftId, activeShiftConfig, isDynamicShiftConfigLoading, timezone]);
55358
56152
  const [todayEntries, setTodayEntries] = React25.useState([]);
55359
56153
  const [monthlyEntries, setMonthlyEntries] = React25.useState([]);
55360
56154
  const [todayLoading, setTodayLoading] = React25.useState(false);
@@ -55451,8 +56245,8 @@ var LeaderboardDetailView = React25.memo(({
55451
56245
  return Array.from(uniqueLines.entries()).map(([id3, name]) => ({ id: id3, label: name }));
55452
56246
  }, [configuredLineIds, lines, getLineName]);
55453
56247
  const shiftOptions = React25.useMemo(() => {
55454
- if (staticShiftConfig?.shifts && staticShiftConfig.shifts.length > 0) {
55455
- return staticShiftConfig.shifts.map((shift2) => ({
56248
+ if (activeShiftConfig?.shifts && activeShiftConfig.shifts.length > 0) {
56249
+ return activeShiftConfig.shifts.map((shift2) => ({
55456
56250
  id: shift2.shiftId.toString(),
55457
56251
  label: shift2.shiftName
55458
56252
  }));
@@ -55461,14 +56255,14 @@ var LeaderboardDetailView = React25.memo(({
55461
56255
  { id: "0", label: "Day Shift" },
55462
56256
  { id: "1", label: "Night Shift" }
55463
56257
  ];
55464
- }, [staticShiftConfig]);
56258
+ }, [activeShiftConfig]);
55465
56259
  const currentShiftInfo = React25.useMemo(() => {
55466
56260
  try {
55467
- return getCurrentShift(timezone || "Asia/Kolkata", staticShiftConfig);
56261
+ return getCurrentShift(timezone || "Asia/Kolkata", activeShiftConfig);
55468
56262
  } catch (e) {
55469
56263
  return { shiftId: 0, date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0], shiftName: "Day" };
55470
56264
  }
55471
- }, [timezone, staticShiftConfig]);
56265
+ }, [timezone, activeShiftConfig]);
55472
56266
  const selectedShiftId = React25.useMemo(() => {
55473
56267
  const parsed = Number(selectedShiftFilter);
55474
56268
  return Number.isFinite(parsed) ? parsed : void 0;
@@ -55709,7 +56503,7 @@ var LeaderboardDetailView = React25.memo(({
55709
56503
  ]);
55710
56504
  const getShiftName = React25.useCallback((shiftId2) => {
55711
56505
  if (shiftId2 !== void 0) {
55712
- return getShiftNameById(shiftId2, timezone || "Asia/Kolkata", staticShiftConfig);
56506
+ return getShiftNameById(shiftId2, timezone || "Asia/Kolkata", activeShiftConfig);
55713
56507
  }
55714
56508
  if (shiftGroups.length > 1) {
55715
56509
  const shiftNames = shiftGroups.map((g) => g.shiftName).join(" & ");
@@ -55717,14 +56511,14 @@ var LeaderboardDetailView = React25.memo(({
55717
56511
  } else if (shiftGroups.length === 1) {
55718
56512
  return shiftGroups[0].shiftName;
55719
56513
  }
55720
- const currentShift = getCurrentShift(timezone || "Asia/Kolkata", staticShiftConfig);
55721
- return currentShift.shiftName || getShiftNameById(currentShift.shiftId, timezone || "Asia/Kolkata", staticShiftConfig);
55722
- }, [timezone, staticShiftConfig, shiftGroups]);
56514
+ const currentShift = getCurrentShift(timezone || "Asia/Kolkata", activeShiftConfig);
56515
+ return currentShift.shiftName || getShiftNameById(currentShift.shiftId, timezone || "Asia/Kolkata", activeShiftConfig);
56516
+ }, [timezone, activeShiftConfig, shiftGroups]);
55723
56517
  const getShiftIcon = React25.useCallback((shiftId2) => {
55724
56518
  if (shiftId2 === void 0 && shiftGroups.length > 1) {
55725
56519
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" }) });
55726
56520
  }
55727
- const effectiveShiftId = shiftId2 !== void 0 ? shiftId2 : shiftGroups.length === 1 ? shiftGroups[0].shiftId : getCurrentShift(timezone || "Asia/Kolkata", staticShiftConfig).shiftId;
56521
+ const effectiveShiftId = shiftId2 !== void 0 ? shiftId2 : shiftGroups.length === 1 ? shiftGroups[0].shiftId : getCurrentShift(timezone || "Asia/Kolkata", activeShiftConfig).shiftId;
55728
56522
  const shiftNameLower = getShiftName(effectiveShiftId).toLowerCase();
55729
56523
  if (shiftNameLower.includes("day") || shiftNameLower.includes("morning")) {
55730
56524
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" }) });
@@ -55736,7 +56530,7 @@ var LeaderboardDetailView = React25.memo(({
55736
56530
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" }) });
55737
56531
  }
55738
56532
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" }) });
55739
- }, [getShiftName, shiftGroups, timezone, staticShiftConfig]);
56533
+ }, [getShiftName, shiftGroups, timezone, activeShiftConfig]);
55740
56534
  const formatDate2 = React25.useCallback((date2) => {
55741
56535
  return new Intl.DateTimeFormat("en-US", {
55742
56536
  weekday: "long",
@@ -55970,7 +56764,7 @@ var LeaderboardDetailView = React25.memo(({
55970
56764
  {
55971
56765
  onClick: () => setActiveTab("today"),
55972
56766
  className: `flex-1 px-2 py-1.5 text-xs font-medium rounded-md transition-all duration-200 ${activeTab === "today" ? "bg-white text-gray-900 shadow-sm" : "text-gray-600"}`,
55973
- children: "Today"
56767
+ children: "Daily"
55974
56768
  }
55975
56769
  ),
55976
56770
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -55978,7 +56772,7 @@ var LeaderboardDetailView = React25.memo(({
55978
56772
  {
55979
56773
  onClick: () => setActiveTab("monthly"),
55980
56774
  className: `flex-1 px-2 py-1.5 text-xs font-medium rounded-md transition-all duration-200 ${activeTab === "monthly" ? "bg-white text-gray-900 shadow-sm" : "text-gray-600"}`,
55981
- children: "Monthly History"
56775
+ children: "Monthly"
55982
56776
  }
55983
56777
  )
55984
56778
  ] }),
@@ -56017,7 +56811,7 @@ var LeaderboardDetailView = React25.memo(({
56017
56811
  {
56018
56812
  onClick: () => setActiveTab("today"),
56019
56813
  className: `px-2 lg:px-3 py-1 lg:py-1.5 text-sm lg:text-base font-medium rounded-lg transition-colors whitespace-nowrap ${activeTab === "today" ? "bg-blue-50 text-blue-600" : "text-gray-600 hover:bg-gray-50"}`,
56020
- children: "Today"
56814
+ children: "Daily"
56021
56815
  }
56022
56816
  ),
56023
56817
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -56025,7 +56819,7 @@ var LeaderboardDetailView = React25.memo(({
56025
56819
  {
56026
56820
  onClick: () => setActiveTab("monthly"),
56027
56821
  className: `px-2 lg:px-3 py-1 lg:py-1.5 text-sm lg:text-base font-medium rounded-lg transition-colors whitespace-nowrap ${activeTab === "monthly" ? "bg-blue-50 text-blue-600" : "text-gray-600 hover:bg-gray-50"}`,
56028
- children: "Monthly History"
56822
+ children: "Monthly"
56029
56823
  }
56030
56824
  )
56031
56825
  ] }),
@@ -59098,6 +59892,7 @@ var getInitialTab = (sourceType, defaultTab, fromMonthly, urlDate) => {
59098
59892
  }
59099
59893
  return "overview";
59100
59894
  };
59895
+ var DEBUG_DASHBOARD2 = process.env.NEXT_PUBLIC_DEBUG_DASHBOARD === "true";
59101
59896
  var chartCardVariants = {
59102
59897
  initial: { opacity: 0, y: 10 },
59103
59898
  animate: {
@@ -59534,6 +60329,34 @@ var WorkspaceDetailView = ({
59534
60329
  const idleTimeReasonsEnabled = Boolean(
59535
60330
  workspaceId && idleTimeReasonsDate && idleTimeReasonsShiftId !== void 0
59536
60331
  );
60332
+ React25.useEffect(() => {
60333
+ if (!DEBUG_DASHBOARD2) return;
60334
+ console.log("[WorkspaceDetailView] IdleTimeReasons gate", {
60335
+ workspaceId,
60336
+ idleTimeReasonsDate,
60337
+ idleTimeReasonsShiftId,
60338
+ idleTimeReasonsEnabled,
60339
+ isShiftConfigLoading,
60340
+ calculatedOperationalDate,
60341
+ calculatedShiftId,
60342
+ workspaceDate: workspace?.date,
60343
+ workspaceShiftId: workspace?.shift_id,
60344
+ parsedShiftId,
60345
+ hasWorkspaceSnapshot
60346
+ });
60347
+ }, [
60348
+ workspaceId,
60349
+ idleTimeReasonsDate,
60350
+ idleTimeReasonsShiftId,
60351
+ idleTimeReasonsEnabled,
60352
+ isShiftConfigLoading,
60353
+ calculatedOperationalDate,
60354
+ calculatedShiftId,
60355
+ workspace?.date,
60356
+ workspace?.shift_id,
60357
+ parsedShiftId,
60358
+ hasWorkspaceSnapshot
60359
+ ]);
59537
60360
  const {
59538
60361
  chartData: idleTimeChartData,
59539
60362
  isLoading: idleTimeLoading,
@@ -66883,6 +67706,7 @@ exports.isValidWorkspaceDetailedMetricsPayload = isValidWorkspaceDetailedMetrics
66883
67706
  exports.isValidWorkspaceMetricsPayload = isValidWorkspaceMetricsPayload;
66884
67707
  exports.isWorkspaceDisplayNamesLoaded = isWorkspaceDisplayNamesLoaded;
66885
67708
  exports.isWorkspaceDisplayNamesLoading = isWorkspaceDisplayNamesLoading;
67709
+ exports.lineLeaderboardService = lineLeaderboardService;
66886
67710
  exports.linesService = linesService;
66887
67711
  exports.mergeWithDefaultConfig = mergeWithDefaultConfig;
66888
67712
  exports.migrateLegacyConfiguration = migrateLegacyConfiguration;