@optifye/dashboard-core 6.10.34 → 6.10.35

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.d.mts CHANGED
@@ -4674,6 +4674,8 @@ interface UseSupervisorsByLineIdsResult {
4674
4674
  }
4675
4675
  declare const useSupervisorsByLineIds: (lineIds: string[] | undefined, options?: {
4676
4676
  enabled?: boolean;
4677
+ companyId?: string;
4678
+ useBackend?: boolean;
4677
4679
  }) => UseSupervisorsByLineIdsResult;
4678
4680
 
4679
4681
  /**
@@ -6084,6 +6086,10 @@ type LineLeaderboardEntry = {
6084
6086
  days_with_output: number;
6085
6087
  total_output: number;
6086
6088
  };
6089
+ type DailyLineLeaderboardEntry = {
6090
+ line_id: string;
6091
+ avg_efficiency: number | null;
6092
+ };
6087
6093
  declare const lineLeaderboardService: {
6088
6094
  getLineLeaderboard(supabase: SupabaseClient$1, params: {
6089
6095
  companyId: string;
@@ -6092,6 +6098,13 @@ declare const lineLeaderboardService: {
6092
6098
  lineIds?: string[];
6093
6099
  limit?: number;
6094
6100
  }): Promise<LineLeaderboardEntry[]>;
6101
+ getDailyLineLeaderboard(supabase: SupabaseClient$1, params: {
6102
+ companyId: string;
6103
+ date: string;
6104
+ shiftId: number;
6105
+ lineIds?: string[];
6106
+ limit?: number;
6107
+ }): Promise<DailyLineLeaderboardEntry[]>;
6095
6108
  };
6096
6109
 
6097
6110
  /**
package/dist/index.d.ts CHANGED
@@ -4674,6 +4674,8 @@ interface UseSupervisorsByLineIdsResult {
4674
4674
  }
4675
4675
  declare const useSupervisorsByLineIds: (lineIds: string[] | undefined, options?: {
4676
4676
  enabled?: boolean;
4677
+ companyId?: string;
4678
+ useBackend?: boolean;
4677
4679
  }) => UseSupervisorsByLineIdsResult;
4678
4680
 
4679
4681
  /**
@@ -6084,6 +6086,10 @@ type LineLeaderboardEntry = {
6084
6086
  days_with_output: number;
6085
6087
  total_output: number;
6086
6088
  };
6089
+ type DailyLineLeaderboardEntry = {
6090
+ line_id: string;
6091
+ avg_efficiency: number | null;
6092
+ };
6087
6093
  declare const lineLeaderboardService: {
6088
6094
  getLineLeaderboard(supabase: SupabaseClient$1, params: {
6089
6095
  companyId: string;
@@ -6092,6 +6098,13 @@ declare const lineLeaderboardService: {
6092
6098
  lineIds?: string[];
6093
6099
  limit?: number;
6094
6100
  }): Promise<LineLeaderboardEntry[]>;
6101
+ getDailyLineLeaderboard(supabase: SupabaseClient$1, params: {
6102
+ companyId: string;
6103
+ date: string;
6104
+ shiftId: number;
6105
+ lineIds?: string[];
6106
+ limit?: number;
6107
+ }): Promise<DailyLineLeaderboardEntry[]>;
6095
6108
  };
6096
6109
 
6097
6110
  /**
package/dist/index.js CHANGED
@@ -7754,6 +7754,23 @@ var lineLeaderboardService = {
7754
7754
  `/api/dashboard/line-leaderboard?${searchParams.toString()}`
7755
7755
  );
7756
7756
  return data?.entries ?? [];
7757
+ },
7758
+ async getDailyLineLeaderboard(supabase, params) {
7759
+ const searchParams = new URLSearchParams();
7760
+ searchParams.set("company_id", params.companyId);
7761
+ searchParams.set("date", params.date);
7762
+ searchParams.set("shift_id", params.shiftId.toString());
7763
+ if (params.lineIds && params.lineIds.length > 0) {
7764
+ searchParams.set("line_ids", params.lineIds.join(","));
7765
+ }
7766
+ if (typeof params.limit === "number") {
7767
+ searchParams.set("limit", params.limit.toString());
7768
+ }
7769
+ const data = await fetchBackendJson(
7770
+ supabase,
7771
+ `/api/dashboard/line-leaderboard-daily?${searchParams.toString()}`
7772
+ );
7773
+ return data?.entries ?? [];
7757
7774
  }
7758
7775
  };
7759
7776
  var SupabaseContext = React26.createContext(void 0);
@@ -15979,7 +15996,10 @@ function useLineSupervisor(lineId) {
15979
15996
  }
15980
15997
  var useSupervisorsByLineIds = (lineIds, options) => {
15981
15998
  const supabase = useSupabase();
15999
+ const entityConfig = useEntityConfig();
15982
16000
  const enabled = options?.enabled ?? true;
16001
+ const useBackend = options?.useBackend ?? false;
16002
+ const resolvedCompanyId = options?.companyId || entityConfig.companyId;
15983
16003
  const lineIdsKey = React26.useMemo(() => (lineIds || []).slice().sort().join(","), [lineIds]);
15984
16004
  const targetLineIdSet = React26.useMemo(() => new Set(lineIds || []), [lineIdsKey]);
15985
16005
  const [supervisorsByLineId, setSupervisorsByLineId] = React26.useState(/* @__PURE__ */ new Map());
@@ -15995,37 +16015,72 @@ var useSupervisorsByLineIds = (lineIds, options) => {
15995
16015
  try {
15996
16016
  setIsLoading(true);
15997
16017
  setError(null);
15998
- const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, first_name, last_name, properties, profile_photo_url").eq("role_level", "supervisor");
15999
- if (fetchError) {
16000
- throw fetchError;
16001
- }
16002
16018
  const nextSupervisorsByLineId = /* @__PURE__ */ new Map();
16003
16019
  const nextAllSupervisorsMap = /* @__PURE__ */ new Map();
16004
- (data || []).forEach((row) => {
16005
- const assignedLineIds = row?.properties?.line_id || row?.properties?.line_ids || [];
16006
- const email = row.email || "";
16007
- let displayName;
16008
- if (row.first_name) {
16009
- displayName = row.last_name ? `${row.first_name} ${row.last_name}` : row.first_name;
16010
- } else {
16011
- displayName = (typeof email === "string" && email.includes("@") ? email.split("@")[0] : email) || email;
16012
- }
16013
- const supervisor = {
16014
- userId: row.user_id,
16015
- email,
16016
- displayName,
16017
- profilePhotoUrl: row.profile_photo_url
16018
- };
16019
- nextAllSupervisorsMap.set(supervisor.userId, supervisor);
16020
- if (!Array.isArray(assignedLineIds) || assignedLineIds.length === 0) return;
16021
- assignedLineIds.forEach((lineId) => {
16022
- if (typeof lineId !== "string") return;
16023
- if (targetLineIdSet.size > 0 && !targetLineIdSet.has(lineId)) return;
16024
- const existing = nextSupervisorsByLineId.get(lineId) || [];
16025
- existing.push(supervisor);
16026
- nextSupervisorsByLineId.set(lineId, existing);
16020
+ if (useBackend && resolvedCompanyId) {
16021
+ const searchParams = new URLSearchParams({
16022
+ company_id: resolvedCompanyId
16027
16023
  });
16028
- });
16024
+ if (lineIdsKey) {
16025
+ searchParams.set("line_ids", lineIdsKey);
16026
+ }
16027
+ const data = await fetchBackendJson(supabase, `/api/dashboard/line-supervisors?${searchParams.toString()}`);
16028
+ (data?.supervisors || []).forEach((row) => {
16029
+ const assignedLineIds = Array.isArray(row.line_ids) ? row.line_ids : [];
16030
+ if (!assignedLineIds.length) return;
16031
+ const email = row.email || "";
16032
+ let displayName;
16033
+ if (row.first_name) {
16034
+ displayName = row.last_name ? `${row.first_name} ${row.last_name}` : row.first_name;
16035
+ } else {
16036
+ displayName = (typeof email === "string" && email.includes("@") ? email.split("@")[0] : email) || email;
16037
+ }
16038
+ const supervisor = {
16039
+ userId: row.user_id,
16040
+ email,
16041
+ displayName,
16042
+ profilePhotoUrl: row.profile_photo_url ?? null
16043
+ };
16044
+ nextAllSupervisorsMap.set(supervisor.userId, supervisor);
16045
+ assignedLineIds.forEach((lineId) => {
16046
+ if (typeof lineId !== "string") return;
16047
+ if (targetLineIdSet.size > 0 && !targetLineIdSet.has(lineId)) return;
16048
+ const existing = nextSupervisorsByLineId.get(lineId) || [];
16049
+ existing.push(supervisor);
16050
+ nextSupervisorsByLineId.set(lineId, existing);
16051
+ });
16052
+ });
16053
+ } else {
16054
+ const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, first_name, last_name, properties, profile_photo_url").eq("role_level", "supervisor");
16055
+ if (fetchError) {
16056
+ throw fetchError;
16057
+ }
16058
+ (data || []).forEach((row) => {
16059
+ const assignedLineIds = row?.properties?.line_id || row?.properties?.line_ids || [];
16060
+ const email = row.email || "";
16061
+ let displayName;
16062
+ if (row.first_name) {
16063
+ displayName = row.last_name ? `${row.first_name} ${row.last_name}` : row.first_name;
16064
+ } else {
16065
+ displayName = (typeof email === "string" && email.includes("@") ? email.split("@")[0] : email) || email;
16066
+ }
16067
+ const supervisor = {
16068
+ userId: row.user_id,
16069
+ email,
16070
+ displayName,
16071
+ profilePhotoUrl: row.profile_photo_url
16072
+ };
16073
+ nextAllSupervisorsMap.set(supervisor.userId, supervisor);
16074
+ if (!Array.isArray(assignedLineIds) || assignedLineIds.length === 0) return;
16075
+ assignedLineIds.forEach((lineId) => {
16076
+ if (typeof lineId !== "string") return;
16077
+ if (targetLineIdSet.size > 0 && !targetLineIdSet.has(lineId)) return;
16078
+ const existing = nextSupervisorsByLineId.get(lineId) || [];
16079
+ existing.push(supervisor);
16080
+ nextSupervisorsByLineId.set(lineId, existing);
16081
+ });
16082
+ });
16083
+ }
16029
16084
  const nextSupervisorNamesByLineId = /* @__PURE__ */ new Map();
16030
16085
  nextSupervisorsByLineId.forEach((supervisors, lineId) => {
16031
16086
  nextSupervisorNamesByLineId.set(lineId, supervisors.map((s) => s.displayName).join(", "));
@@ -16041,7 +16096,7 @@ var useSupervisorsByLineIds = (lineIds, options) => {
16041
16096
  } finally {
16042
16097
  setIsLoading(false);
16043
16098
  }
16044
- }, [enabled, supabase, targetLineIdSet]);
16099
+ }, [enabled, supabase, targetLineIdSet, useBackend, resolvedCompanyId, lineIdsKey]);
16045
16100
  React26.useEffect(() => {
16046
16101
  fetchSupervisors();
16047
16102
  }, [fetchSupervisors, lineIdsKey]);
@@ -55012,6 +55067,7 @@ var LeaderboardCountdown = ({ targetDate, format: format7, finishedLabel = "Fini
55012
55067
  var LinesLeaderboard = ({
55013
55068
  lines,
55014
55069
  onLineClick,
55070
+ assignedLineIds,
55015
55071
  timeRange,
55016
55072
  setTimeRange,
55017
55073
  todayEfficiencyByLineId,
@@ -55024,6 +55080,17 @@ var LinesLeaderboard = ({
55024
55080
  monthEndDate
55025
55081
  }) => {
55026
55082
  const formatEfficiency = (value) => typeof value === "number" && Number.isFinite(value) ? `${value.toFixed(1)}%` : "--";
55083
+ const assignedLineIdSet = React26__namespace.default.useMemo(
55084
+ () => new Set(assignedLineIds || []),
55085
+ [assignedLineIds]
55086
+ );
55087
+ const canClickLine = React26__namespace.default.useCallback(
55088
+ (lineId) => {
55089
+ if (!assignedLineIds) return true;
55090
+ return assignedLineIdSet.has(lineId);
55091
+ },
55092
+ [assignedLineIds, assignedLineIdSet]
55093
+ );
55027
55094
  const handleTimeRangeChange = React26__namespace.default.useCallback((newRange) => {
55028
55095
  if (newRange === timeRange) return;
55029
55096
  trackCoreEvent("Leaderboard Time Range Changed", {
@@ -55036,6 +55103,7 @@ var LinesLeaderboard = ({
55036
55103
  setTimeRange(newRange);
55037
55104
  }, [timeRange, lines.length, monthlyEfficiencyByLineId, setTimeRange]);
55038
55105
  const handleLeaderboardLineClick = React26__namespace.default.useCallback((item, clickSource) => {
55106
+ if (!canClickLine(item.line.id)) return;
55039
55107
  trackCoreEvent("Leaderboard Line Clicked", {
55040
55108
  line_id: item.line.id,
55041
55109
  line_name: item.line.line_name,
@@ -55047,7 +55115,7 @@ var LinesLeaderboard = ({
55047
55115
  supervisor_name: item.supervisorName || "Unassigned"
55048
55116
  });
55049
55117
  onLineClick(item.line);
55050
- }, [onLineClick, timeRange]);
55118
+ }, [canClickLine, onLineClick, timeRange]);
55051
55119
  const viewLoadedTrackedRef = React26__namespace.default.useRef(null);
55052
55120
  const leaderboardData = React26__namespace.default.useMemo(() => {
55053
55121
  const loading = timeRange === "today" ? isLoadingToday : isLoadingMonthly;
@@ -55201,11 +55269,12 @@ var LinesLeaderboard = ({
55201
55269
  const isFirst = item.rank === 1;
55202
55270
  const isSecond = item.rank === 2;
55203
55271
  item.rank === 3;
55272
+ const isClickable = canClickLine(item.line.id);
55204
55273
  return /* @__PURE__ */ jsxRuntime.jsxs(
55205
55274
  "div",
55206
55275
  {
55207
55276
  onClick: () => handleLeaderboardLineClick(item, "podium"),
55208
- className: `relative flex flex-col items-center cursor-pointer z-10 transition-transform hover:scale-105`,
55277
+ className: `relative flex flex-col items-center z-10 transition-transform ${isClickable ? "cursor-pointer hover:scale-105" : "cursor-not-allowed"}`,
55209
55278
  children: [
55210
55279
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative -mb-4 z-20 ${isFirst ? "animate-float-slow" : isSecond ? "animate-float-medium" : "animate-float-fast"}`, children: [
55211
55280
  /* @__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"}` }),
@@ -55266,11 +55335,12 @@ var LinesLeaderboard = ({
55266
55335
  ] }) }),
55267
55336
  /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-gray-100", children: leaderboardData.map((item) => {
55268
55337
  const isTopThree = item.rank <= 3;
55338
+ const isClickable = canClickLine(item.line.id);
55269
55339
  return /* @__PURE__ */ jsxRuntime.jsxs(
55270
55340
  "tr",
55271
55341
  {
55272
55342
  onClick: () => handleLeaderboardLineClick(item, isTopThree ? "podium" : "table"),
55273
- className: `hover:bg-gray-50 transition-colors cursor-pointer group ${isTopThree ? "sm:hidden" : ""}`,
55343
+ className: `transition-colors ${isTopThree ? "sm:hidden" : ""} ${isClickable ? "hover:bg-gray-50 cursor-pointer group" : "cursor-not-allowed"}`,
55274
55344
  children: [
55275
55345
  /* @__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 }) }),
55276
55346
  /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
@@ -55465,6 +55535,7 @@ var KPIsOverviewView = ({
55465
55535
  lineIds
55466
55536
  }) => {
55467
55537
  const [lines, setLines] = React26.useState([]);
55538
+ const [leaderboardLines, setLeaderboardLines] = React26.useState([]);
55468
55539
  const [activeTab, setActiveTab] = React26.useState("today");
55469
55540
  const [timeRange, setTimeRange] = React26.useState("today");
55470
55541
  const [loading, setLoading] = React26.useState(true);
@@ -55480,11 +55551,16 @@ var KPIsOverviewView = ({
55480
55551
  });
55481
55552
  const [topPerformerLoading, setTopPerformerLoading] = React26.useState(true);
55482
55553
  const [topPerformerImageError, setTopPerformerImageError] = React26.useState(false);
55554
+ const [todayEfficiencyByLineId, setTodayEfficiencyByLineId] = React26.useState(/* @__PURE__ */ new Map());
55555
+ const [dailyLoading, setDailyLoading] = React26.useState(false);
55556
+ const [dailyError, setDailyError] = React26.useState(null);
55483
55557
  const [monthlyEfficiencyByLineId, setMonthlyEfficiencyByLineId] = React26.useState(/* @__PURE__ */ new Map());
55484
55558
  const [monthlyLoading, setMonthlyLoading] = React26.useState(false);
55485
55559
  const [monthlyError, setMonthlyError] = React26.useState(null);
55560
+ const dailyRequestKeyRef = React26.useRef(null);
55486
55561
  const monthlyRequestKeyRef = React26.useRef(null);
55487
55562
  const supabase = useSupabase();
55563
+ const { user } = useAuth();
55488
55564
  const dashboardConfig = useDashboardConfig();
55489
55565
  const entityConfig = useEntityConfig();
55490
55566
  const configCompanyId = (dashboardConfig && typeof dashboardConfig === "object" && "company" in dashboardConfig ? dashboardConfig.company?.id : void 0) || dashboardConfig.customConfig?.companyId || dashboardConfig.customConfig?.company_id;
@@ -55499,6 +55575,16 @@ var KPIsOverviewView = ({
55499
55575
  const dbTimezone = useAppTimezone();
55500
55576
  const configuredTimezone = dbTimezone || dateTimeConfig.defaultTimezone || "UTC";
55501
55577
  const { startDate: monthStartDate, endDate: monthEndDateKey, monthEndDate } = getMonthDateInfo(configuredTimezone);
55578
+ const isSupervisor = user?.role_level === "supervisor";
55579
+ const assignedLineIdsForLeaderboard = isSupervisor ? lineIds : void 0;
55580
+ const currentShiftDetails = getCurrentShift(configuredTimezone, shiftConfig);
55581
+ const currentShiftDate = currentShiftDetails.date;
55582
+ const currentShiftId = currentShiftDetails.shiftId;
55583
+ const shiftEndDate = React26__namespace.default.useMemo(
55584
+ () => getShiftEndDate(currentShiftDetails, configuredTimezone),
55585
+ [currentShiftDetails, configuredTimezone]
55586
+ );
55587
+ const shiftName = (currentShiftDetails.shiftName || "Day").replace(/ Shift$/i, "");
55502
55588
  const factoryViewId = entityConfig.factoryViewId || "factory";
55503
55589
  const {
55504
55590
  lineMetrics,
@@ -55516,19 +55602,14 @@ var KPIsOverviewView = ({
55516
55602
  });
55517
55603
  return map;
55518
55604
  }, [lineMetrics]);
55519
- const todayEfficiencyByLineId = React26__namespace.default.useMemo(() => {
55520
- const map = /* @__PURE__ */ new Map();
55521
- kpisByLineId.forEach((kpis, lineId) => {
55522
- const value = kpis?.efficiency?.value;
55523
- if (typeof value === "number" && Number.isFinite(value)) {
55524
- map.set(lineId, value);
55525
- }
55526
- });
55527
- return map;
55528
- }, [kpisByLineId]);
55529
- const visibleLineIds = React26__namespace.default.useMemo(() => lines.map((l) => l.id), [lines]);
55530
- const { supervisorNamesByLineId, supervisorsByLineId, allSupervisorsMap } = useSupervisorsByLineIds(visibleLineIds, {
55531
- enabled: supervisorEnabled && visibleLineIds.length > 0
55605
+ const supervisorLineIds = React26__namespace.default.useMemo(
55606
+ () => (leaderboardLines.length > 0 ? leaderboardLines : lines).map((l) => l.id),
55607
+ [leaderboardLines, lines]
55608
+ );
55609
+ const { supervisorNamesByLineId, supervisorsByLineId, allSupervisorsMap } = useSupervisorsByLineIds(supervisorLineIds, {
55610
+ enabled: supervisorEnabled && supervisorLineIds.length > 0,
55611
+ companyId: resolvedCompanyId,
55612
+ useBackend: true
55532
55613
  });
55533
55614
  React26.useEffect(() => {
55534
55615
  let isMounted = true;
@@ -55589,9 +55670,41 @@ var KPIsOverviewView = ({
55589
55670
  };
55590
55671
  fetchLines();
55591
55672
  }, [supabase, dashboardConfig, lineIds]);
55673
+ React26.useEffect(() => {
55674
+ let isMounted = true;
55675
+ const fetchLeaderboardLines = async () => {
55676
+ if (!supabase || !resolvedCompanyId) {
55677
+ setLeaderboardLines(lines);
55678
+ return;
55679
+ }
55680
+ try {
55681
+ const linesService2 = new LinesService(supabase);
55682
+ const companyLines = await linesService2.getLinesByCompanyId(resolvedCompanyId);
55683
+ if (!isMounted) return;
55684
+ const transformed = companyLines.map((line) => ({
55685
+ id: line.id,
55686
+ line_name: line.name,
55687
+ factory_id: line.factoryId || "",
55688
+ factory_name: "N/A",
55689
+ company_id: line.companyId,
55690
+ company_name: "",
55691
+ enable: line.isActive ?? true
55692
+ }));
55693
+ setLeaderboardLines(transformed);
55694
+ } catch (err) {
55695
+ console.error("[KPIsOverviewView] Failed to load leaderboard lines:", err);
55696
+ if (!isMounted) return;
55697
+ setLeaderboardLines(lines);
55698
+ }
55699
+ };
55700
+ fetchLeaderboardLines();
55701
+ return () => {
55702
+ isMounted = false;
55703
+ };
55704
+ }, [supabase, resolvedCompanyId, lines]);
55592
55705
  const fetchMonthlyLeaderboard = React26.useCallback(async () => {
55593
- if (!supabase || !resolvedCompanyId || lines.length === 0) return;
55594
- const targetLineIds = lines.map((line) => line.id);
55706
+ if (!supabase || !resolvedCompanyId || leaderboardLines.length === 0) return;
55707
+ const targetLineIds = leaderboardLines.map((line) => line.id);
55595
55708
  const lineIdsKey = targetLineIds.slice().sort().join(",");
55596
55709
  const requestKey = `${resolvedCompanyId}|${monthStartDate}|${monthEndDateKey}|${lineIdsKey}`;
55597
55710
  if (monthlyRequestKeyRef.current === requestKey) return;
@@ -55605,8 +55718,7 @@ var KPIsOverviewView = ({
55605
55718
  const entries = await lineLeaderboardService.getLineLeaderboard(supabase, {
55606
55719
  companyId: resolvedCompanyId,
55607
55720
  startDate: monthStartDate,
55608
- endDate: monthEndDateKey,
55609
- lineIds: targetLineIds
55721
+ endDate: monthEndDateKey
55610
55722
  });
55611
55723
  const nextMap = /* @__PURE__ */ new Map();
55612
55724
  targetLineIds.forEach((lineId) => nextMap.set(lineId, 0));
@@ -55639,11 +55751,47 @@ var KPIsOverviewView = ({
55639
55751
  } finally {
55640
55752
  setMonthlyLoading(false);
55641
55753
  }
55642
- }, [supabase, resolvedCompanyId, lines, monthStartDate, monthEndDateKey]);
55754
+ }, [supabase, resolvedCompanyId, leaderboardLines, monthStartDate, monthEndDateKey]);
55755
+ const fetchDailyLeaderboard = React26.useCallback(async () => {
55756
+ if (!supabase || !resolvedCompanyId || leaderboardLines.length === 0) return;
55757
+ if (!currentShiftDate) return;
55758
+ const targetLineIds = leaderboardLines.map((line) => line.id);
55759
+ const lineIdsKey = targetLineIds.slice().sort().join(",");
55760
+ const requestKey = `${resolvedCompanyId}|${currentShiftDate}|${currentShiftId}|${lineIdsKey}`;
55761
+ if (dailyRequestKeyRef.current === requestKey) return;
55762
+ dailyRequestKeyRef.current = requestKey;
55763
+ setDailyLoading(true);
55764
+ setDailyError(null);
55765
+ try {
55766
+ const entries = await lineLeaderboardService.getDailyLineLeaderboard(supabase, {
55767
+ companyId: resolvedCompanyId,
55768
+ date: currentShiftDate,
55769
+ shiftId: currentShiftId
55770
+ });
55771
+ const nextMap = /* @__PURE__ */ new Map();
55772
+ entries.forEach((entry) => {
55773
+ const value = Number(entry.avg_efficiency);
55774
+ if (Number.isFinite(value)) {
55775
+ nextMap.set(entry.line_id, value);
55776
+ }
55777
+ });
55778
+ setTodayEfficiencyByLineId(nextMap);
55779
+ } catch (err) {
55780
+ console.error("[KPIsOverviewView] Failed to load daily leaderboard:", err);
55781
+ setDailyError("Failed to load daily leaderboard");
55782
+ dailyRequestKeyRef.current = null;
55783
+ } finally {
55784
+ setDailyLoading(false);
55785
+ }
55786
+ }, [supabase, resolvedCompanyId, leaderboardLines, currentShiftDate, currentShiftId]);
55643
55787
  React26.useEffect(() => {
55644
55788
  if (activeTab !== "leaderboard") return;
55645
55789
  fetchMonthlyLeaderboard();
55646
55790
  }, [activeTab, timeRange, fetchMonthlyLeaderboard]);
55791
+ React26.useEffect(() => {
55792
+ if (activeTab !== "leaderboard" || timeRange !== "today") return;
55793
+ fetchDailyLeaderboard();
55794
+ }, [activeTab, timeRange, fetchDailyLeaderboard]);
55647
55795
  const formatTopPerformerWeek = (periodStart, periodEnd) => {
55648
55796
  if (!periodStart || !periodEnd) return "Last Week";
55649
55797
  const startDate = /* @__PURE__ */ new Date(`${periodStart}T00:00:00`);
@@ -55739,14 +55887,15 @@ var KPIsOverviewView = ({
55739
55887
  }, [onBackClick, backLinkUrl, navigation]);
55740
55888
  const handleTabChange = React26.useCallback((newTab) => {
55741
55889
  if (newTab === activeTab) return;
55890
+ const trackedLineCount = newTab === "leaderboard" ? leaderboardLines.length : lines.length;
55742
55891
  trackCoreEvent("Leaderboard Tab Switched", {
55743
55892
  from_tab: activeTab,
55744
55893
  to_tab: newTab,
55745
55894
  from_page: "kpis_overview",
55746
- lines_count: lines.length
55895
+ lines_count: trackedLineCount
55747
55896
  });
55748
55897
  setActiveTab(newTab);
55749
- }, [activeTab, lines.length]);
55898
+ }, [activeTab, leaderboardLines.length, lines.length]);
55750
55899
  const formatLocalDate2 = (date) => {
55751
55900
  const options = {
55752
55901
  year: "numeric",
@@ -55764,12 +55913,6 @@ var KPIsOverviewView = ({
55764
55913
  return `${startLabel} - ${endLabel}, ${now4.getFullYear()}`;
55765
55914
  };
55766
55915
  const isMonthlyMode = activeTab === "leaderboard" && timeRange === "monthly";
55767
- const currentShiftDetails = getCurrentShift(configuredTimezone, shiftConfig);
55768
- const shiftEndDate = React26__namespace.default.useMemo(
55769
- () => getShiftEndDate(currentShiftDetails, configuredTimezone),
55770
- [currentShiftDetails, configuredTimezone]
55771
- );
55772
- const shiftName = (currentShiftDetails.shiftName || "Day").replace(/ Shift$/i, "");
55773
55916
  const showTopPerformerImage = Boolean(topPerformer.imageUrl) && !topPerformerImageError;
55774
55917
  typeof topPerformer.efficiency === "number" && Number.isFinite(topPerformer.efficiency);
55775
55918
  topPerformerLoading ? "--" : typeof topPerformer.efficiency === "number" && Number.isFinite(topPerformer.efficiency) ? `${topPerformer.efficiency.toFixed(1)}%` : "--";
@@ -56122,15 +56265,16 @@ var KPIsOverviewView = ({
56122
56265
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full", children: /* @__PURE__ */ jsxRuntime.jsx(
56123
56266
  LinesLeaderboard,
56124
56267
  {
56125
- lines,
56268
+ lines: leaderboardLines,
56126
56269
  onLineClick: (line) => handleLineClick(line),
56270
+ assignedLineIds: assignedLineIdsForLeaderboard,
56127
56271
  timeRange,
56128
56272
  setTimeRange,
56129
56273
  todayEfficiencyByLineId,
56130
56274
  monthlyEfficiencyByLineId,
56131
56275
  supervisorsByLineId,
56132
56276
  supervisorNamesByLineId,
56133
- isLoadingToday: metricsLoading,
56277
+ isLoadingToday: dailyLoading,
56134
56278
  isLoadingMonthly: monthlyLoading,
56135
56279
  shiftEndDate,
56136
56280
  monthEndDate
package/dist/index.mjs CHANGED
@@ -7725,6 +7725,23 @@ var lineLeaderboardService = {
7725
7725
  `/api/dashboard/line-leaderboard?${searchParams.toString()}`
7726
7726
  );
7727
7727
  return data?.entries ?? [];
7728
+ },
7729
+ async getDailyLineLeaderboard(supabase, params) {
7730
+ const searchParams = new URLSearchParams();
7731
+ searchParams.set("company_id", params.companyId);
7732
+ searchParams.set("date", params.date);
7733
+ searchParams.set("shift_id", params.shiftId.toString());
7734
+ if (params.lineIds && params.lineIds.length > 0) {
7735
+ searchParams.set("line_ids", params.lineIds.join(","));
7736
+ }
7737
+ if (typeof params.limit === "number") {
7738
+ searchParams.set("limit", params.limit.toString());
7739
+ }
7740
+ const data = await fetchBackendJson(
7741
+ supabase,
7742
+ `/api/dashboard/line-leaderboard-daily?${searchParams.toString()}`
7743
+ );
7744
+ return data?.entries ?? [];
7728
7745
  }
7729
7746
  };
7730
7747
  var SupabaseContext = createContext(void 0);
@@ -15950,7 +15967,10 @@ function useLineSupervisor(lineId) {
15950
15967
  }
15951
15968
  var useSupervisorsByLineIds = (lineIds, options) => {
15952
15969
  const supabase = useSupabase();
15970
+ const entityConfig = useEntityConfig();
15953
15971
  const enabled = options?.enabled ?? true;
15972
+ const useBackend = options?.useBackend ?? false;
15973
+ const resolvedCompanyId = options?.companyId || entityConfig.companyId;
15954
15974
  const lineIdsKey = useMemo(() => (lineIds || []).slice().sort().join(","), [lineIds]);
15955
15975
  const targetLineIdSet = useMemo(() => new Set(lineIds || []), [lineIdsKey]);
15956
15976
  const [supervisorsByLineId, setSupervisorsByLineId] = useState(/* @__PURE__ */ new Map());
@@ -15966,37 +15986,72 @@ var useSupervisorsByLineIds = (lineIds, options) => {
15966
15986
  try {
15967
15987
  setIsLoading(true);
15968
15988
  setError(null);
15969
- const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, first_name, last_name, properties, profile_photo_url").eq("role_level", "supervisor");
15970
- if (fetchError) {
15971
- throw fetchError;
15972
- }
15973
15989
  const nextSupervisorsByLineId = /* @__PURE__ */ new Map();
15974
15990
  const nextAllSupervisorsMap = /* @__PURE__ */ new Map();
15975
- (data || []).forEach((row) => {
15976
- const assignedLineIds = row?.properties?.line_id || row?.properties?.line_ids || [];
15977
- const email = row.email || "";
15978
- let displayName;
15979
- if (row.first_name) {
15980
- displayName = row.last_name ? `${row.first_name} ${row.last_name}` : row.first_name;
15981
- } else {
15982
- displayName = (typeof email === "string" && email.includes("@") ? email.split("@")[0] : email) || email;
15983
- }
15984
- const supervisor = {
15985
- userId: row.user_id,
15986
- email,
15987
- displayName,
15988
- profilePhotoUrl: row.profile_photo_url
15989
- };
15990
- nextAllSupervisorsMap.set(supervisor.userId, supervisor);
15991
- if (!Array.isArray(assignedLineIds) || assignedLineIds.length === 0) return;
15992
- assignedLineIds.forEach((lineId) => {
15993
- if (typeof lineId !== "string") return;
15994
- if (targetLineIdSet.size > 0 && !targetLineIdSet.has(lineId)) return;
15995
- const existing = nextSupervisorsByLineId.get(lineId) || [];
15996
- existing.push(supervisor);
15997
- nextSupervisorsByLineId.set(lineId, existing);
15991
+ if (useBackend && resolvedCompanyId) {
15992
+ const searchParams = new URLSearchParams({
15993
+ company_id: resolvedCompanyId
15998
15994
  });
15999
- });
15995
+ if (lineIdsKey) {
15996
+ searchParams.set("line_ids", lineIdsKey);
15997
+ }
15998
+ const data = await fetchBackendJson(supabase, `/api/dashboard/line-supervisors?${searchParams.toString()}`);
15999
+ (data?.supervisors || []).forEach((row) => {
16000
+ const assignedLineIds = Array.isArray(row.line_ids) ? row.line_ids : [];
16001
+ if (!assignedLineIds.length) return;
16002
+ const email = row.email || "";
16003
+ let displayName;
16004
+ if (row.first_name) {
16005
+ displayName = row.last_name ? `${row.first_name} ${row.last_name}` : row.first_name;
16006
+ } else {
16007
+ displayName = (typeof email === "string" && email.includes("@") ? email.split("@")[0] : email) || email;
16008
+ }
16009
+ const supervisor = {
16010
+ userId: row.user_id,
16011
+ email,
16012
+ displayName,
16013
+ profilePhotoUrl: row.profile_photo_url ?? null
16014
+ };
16015
+ nextAllSupervisorsMap.set(supervisor.userId, supervisor);
16016
+ assignedLineIds.forEach((lineId) => {
16017
+ if (typeof lineId !== "string") return;
16018
+ if (targetLineIdSet.size > 0 && !targetLineIdSet.has(lineId)) return;
16019
+ const existing = nextSupervisorsByLineId.get(lineId) || [];
16020
+ existing.push(supervisor);
16021
+ nextSupervisorsByLineId.set(lineId, existing);
16022
+ });
16023
+ });
16024
+ } else {
16025
+ const { data, error: fetchError } = await supabase.from("user_roles").select("user_id, email, first_name, last_name, properties, profile_photo_url").eq("role_level", "supervisor");
16026
+ if (fetchError) {
16027
+ throw fetchError;
16028
+ }
16029
+ (data || []).forEach((row) => {
16030
+ const assignedLineIds = row?.properties?.line_id || row?.properties?.line_ids || [];
16031
+ const email = row.email || "";
16032
+ let displayName;
16033
+ if (row.first_name) {
16034
+ displayName = row.last_name ? `${row.first_name} ${row.last_name}` : row.first_name;
16035
+ } else {
16036
+ displayName = (typeof email === "string" && email.includes("@") ? email.split("@")[0] : email) || email;
16037
+ }
16038
+ const supervisor = {
16039
+ userId: row.user_id,
16040
+ email,
16041
+ displayName,
16042
+ profilePhotoUrl: row.profile_photo_url
16043
+ };
16044
+ nextAllSupervisorsMap.set(supervisor.userId, supervisor);
16045
+ if (!Array.isArray(assignedLineIds) || assignedLineIds.length === 0) return;
16046
+ assignedLineIds.forEach((lineId) => {
16047
+ if (typeof lineId !== "string") return;
16048
+ if (targetLineIdSet.size > 0 && !targetLineIdSet.has(lineId)) return;
16049
+ const existing = nextSupervisorsByLineId.get(lineId) || [];
16050
+ existing.push(supervisor);
16051
+ nextSupervisorsByLineId.set(lineId, existing);
16052
+ });
16053
+ });
16054
+ }
16000
16055
  const nextSupervisorNamesByLineId = /* @__PURE__ */ new Map();
16001
16056
  nextSupervisorsByLineId.forEach((supervisors, lineId) => {
16002
16057
  nextSupervisorNamesByLineId.set(lineId, supervisors.map((s) => s.displayName).join(", "));
@@ -16012,7 +16067,7 @@ var useSupervisorsByLineIds = (lineIds, options) => {
16012
16067
  } finally {
16013
16068
  setIsLoading(false);
16014
16069
  }
16015
- }, [enabled, supabase, targetLineIdSet]);
16070
+ }, [enabled, supabase, targetLineIdSet, useBackend, resolvedCompanyId, lineIdsKey]);
16016
16071
  useEffect(() => {
16017
16072
  fetchSupervisors();
16018
16073
  }, [fetchSupervisors, lineIdsKey]);
@@ -54983,6 +55038,7 @@ var LeaderboardCountdown = ({ targetDate, format: format7, finishedLabel = "Fini
54983
55038
  var LinesLeaderboard = ({
54984
55039
  lines,
54985
55040
  onLineClick,
55041
+ assignedLineIds,
54986
55042
  timeRange,
54987
55043
  setTimeRange,
54988
55044
  todayEfficiencyByLineId,
@@ -54995,6 +55051,17 @@ var LinesLeaderboard = ({
54995
55051
  monthEndDate
54996
55052
  }) => {
54997
55053
  const formatEfficiency = (value) => typeof value === "number" && Number.isFinite(value) ? `${value.toFixed(1)}%` : "--";
55054
+ const assignedLineIdSet = React26__default.useMemo(
55055
+ () => new Set(assignedLineIds || []),
55056
+ [assignedLineIds]
55057
+ );
55058
+ const canClickLine = React26__default.useCallback(
55059
+ (lineId) => {
55060
+ if (!assignedLineIds) return true;
55061
+ return assignedLineIdSet.has(lineId);
55062
+ },
55063
+ [assignedLineIds, assignedLineIdSet]
55064
+ );
54998
55065
  const handleTimeRangeChange = React26__default.useCallback((newRange) => {
54999
55066
  if (newRange === timeRange) return;
55000
55067
  trackCoreEvent("Leaderboard Time Range Changed", {
@@ -55007,6 +55074,7 @@ var LinesLeaderboard = ({
55007
55074
  setTimeRange(newRange);
55008
55075
  }, [timeRange, lines.length, monthlyEfficiencyByLineId, setTimeRange]);
55009
55076
  const handleLeaderboardLineClick = React26__default.useCallback((item, clickSource) => {
55077
+ if (!canClickLine(item.line.id)) return;
55010
55078
  trackCoreEvent("Leaderboard Line Clicked", {
55011
55079
  line_id: item.line.id,
55012
55080
  line_name: item.line.line_name,
@@ -55018,7 +55086,7 @@ var LinesLeaderboard = ({
55018
55086
  supervisor_name: item.supervisorName || "Unassigned"
55019
55087
  });
55020
55088
  onLineClick(item.line);
55021
- }, [onLineClick, timeRange]);
55089
+ }, [canClickLine, onLineClick, timeRange]);
55022
55090
  const viewLoadedTrackedRef = React26__default.useRef(null);
55023
55091
  const leaderboardData = React26__default.useMemo(() => {
55024
55092
  const loading = timeRange === "today" ? isLoadingToday : isLoadingMonthly;
@@ -55172,11 +55240,12 @@ var LinesLeaderboard = ({
55172
55240
  const isFirst = item.rank === 1;
55173
55241
  const isSecond = item.rank === 2;
55174
55242
  item.rank === 3;
55243
+ const isClickable = canClickLine(item.line.id);
55175
55244
  return /* @__PURE__ */ jsxs(
55176
55245
  "div",
55177
55246
  {
55178
55247
  onClick: () => handleLeaderboardLineClick(item, "podium"),
55179
- className: `relative flex flex-col items-center cursor-pointer z-10 transition-transform hover:scale-105`,
55248
+ className: `relative flex flex-col items-center z-10 transition-transform ${isClickable ? "cursor-pointer hover:scale-105" : "cursor-not-allowed"}`,
55180
55249
  children: [
55181
55250
  /* @__PURE__ */ jsxs("div", { className: `relative -mb-4 z-20 ${isFirst ? "animate-float-slow" : isSecond ? "animate-float-medium" : "animate-float-fast"}`, children: [
55182
55251
  /* @__PURE__ */ jsx("div", { className: `absolute inset-0 rounded-full blur-xl opacity-40 ${isFirst ? "bg-yellow-400" : isSecond ? "bg-gray-400" : "bg-orange-400"}` }),
@@ -55237,11 +55306,12 @@ var LinesLeaderboard = ({
55237
55306
  ] }) }),
55238
55307
  /* @__PURE__ */ jsx("tbody", { className: "divide-y divide-gray-100", children: leaderboardData.map((item) => {
55239
55308
  const isTopThree = item.rank <= 3;
55309
+ const isClickable = canClickLine(item.line.id);
55240
55310
  return /* @__PURE__ */ jsxs(
55241
55311
  "tr",
55242
55312
  {
55243
55313
  onClick: () => handleLeaderboardLineClick(item, isTopThree ? "podium" : "table"),
55244
- className: `hover:bg-gray-50 transition-colors cursor-pointer group ${isTopThree ? "sm:hidden" : ""}`,
55314
+ className: `transition-colors ${isTopThree ? "sm:hidden" : ""} ${isClickable ? "hover:bg-gray-50 cursor-pointer group" : "cursor-not-allowed"}`,
55245
55315
  children: [
55246
55316
  /* @__PURE__ */ jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: /* @__PURE__ */ 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 }) }),
55247
55317
  /* @__PURE__ */ jsx("td", { className: "px-4 py-3 whitespace-nowrap", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
@@ -55436,6 +55506,7 @@ var KPIsOverviewView = ({
55436
55506
  lineIds
55437
55507
  }) => {
55438
55508
  const [lines, setLines] = useState([]);
55509
+ const [leaderboardLines, setLeaderboardLines] = useState([]);
55439
55510
  const [activeTab, setActiveTab] = useState("today");
55440
55511
  const [timeRange, setTimeRange] = useState("today");
55441
55512
  const [loading, setLoading] = useState(true);
@@ -55451,11 +55522,16 @@ var KPIsOverviewView = ({
55451
55522
  });
55452
55523
  const [topPerformerLoading, setTopPerformerLoading] = useState(true);
55453
55524
  const [topPerformerImageError, setTopPerformerImageError] = useState(false);
55525
+ const [todayEfficiencyByLineId, setTodayEfficiencyByLineId] = useState(/* @__PURE__ */ new Map());
55526
+ const [dailyLoading, setDailyLoading] = useState(false);
55527
+ const [dailyError, setDailyError] = useState(null);
55454
55528
  const [monthlyEfficiencyByLineId, setMonthlyEfficiencyByLineId] = useState(/* @__PURE__ */ new Map());
55455
55529
  const [monthlyLoading, setMonthlyLoading] = useState(false);
55456
55530
  const [monthlyError, setMonthlyError] = useState(null);
55531
+ const dailyRequestKeyRef = useRef(null);
55457
55532
  const monthlyRequestKeyRef = useRef(null);
55458
55533
  const supabase = useSupabase();
55534
+ const { user } = useAuth();
55459
55535
  const dashboardConfig = useDashboardConfig();
55460
55536
  const entityConfig = useEntityConfig();
55461
55537
  const configCompanyId = (dashboardConfig && typeof dashboardConfig === "object" && "company" in dashboardConfig ? dashboardConfig.company?.id : void 0) || dashboardConfig.customConfig?.companyId || dashboardConfig.customConfig?.company_id;
@@ -55470,6 +55546,16 @@ var KPIsOverviewView = ({
55470
55546
  const dbTimezone = useAppTimezone();
55471
55547
  const configuredTimezone = dbTimezone || dateTimeConfig.defaultTimezone || "UTC";
55472
55548
  const { startDate: monthStartDate, endDate: monthEndDateKey, monthEndDate } = getMonthDateInfo(configuredTimezone);
55549
+ const isSupervisor = user?.role_level === "supervisor";
55550
+ const assignedLineIdsForLeaderboard = isSupervisor ? lineIds : void 0;
55551
+ const currentShiftDetails = getCurrentShift(configuredTimezone, shiftConfig);
55552
+ const currentShiftDate = currentShiftDetails.date;
55553
+ const currentShiftId = currentShiftDetails.shiftId;
55554
+ const shiftEndDate = React26__default.useMemo(
55555
+ () => getShiftEndDate(currentShiftDetails, configuredTimezone),
55556
+ [currentShiftDetails, configuredTimezone]
55557
+ );
55558
+ const shiftName = (currentShiftDetails.shiftName || "Day").replace(/ Shift$/i, "");
55473
55559
  const factoryViewId = entityConfig.factoryViewId || "factory";
55474
55560
  const {
55475
55561
  lineMetrics,
@@ -55487,19 +55573,14 @@ var KPIsOverviewView = ({
55487
55573
  });
55488
55574
  return map;
55489
55575
  }, [lineMetrics]);
55490
- const todayEfficiencyByLineId = React26__default.useMemo(() => {
55491
- const map = /* @__PURE__ */ new Map();
55492
- kpisByLineId.forEach((kpis, lineId) => {
55493
- const value = kpis?.efficiency?.value;
55494
- if (typeof value === "number" && Number.isFinite(value)) {
55495
- map.set(lineId, value);
55496
- }
55497
- });
55498
- return map;
55499
- }, [kpisByLineId]);
55500
- const visibleLineIds = React26__default.useMemo(() => lines.map((l) => l.id), [lines]);
55501
- const { supervisorNamesByLineId, supervisorsByLineId, allSupervisorsMap } = useSupervisorsByLineIds(visibleLineIds, {
55502
- enabled: supervisorEnabled && visibleLineIds.length > 0
55576
+ const supervisorLineIds = React26__default.useMemo(
55577
+ () => (leaderboardLines.length > 0 ? leaderboardLines : lines).map((l) => l.id),
55578
+ [leaderboardLines, lines]
55579
+ );
55580
+ const { supervisorNamesByLineId, supervisorsByLineId, allSupervisorsMap } = useSupervisorsByLineIds(supervisorLineIds, {
55581
+ enabled: supervisorEnabled && supervisorLineIds.length > 0,
55582
+ companyId: resolvedCompanyId,
55583
+ useBackend: true
55503
55584
  });
55504
55585
  useEffect(() => {
55505
55586
  let isMounted = true;
@@ -55560,9 +55641,41 @@ var KPIsOverviewView = ({
55560
55641
  };
55561
55642
  fetchLines();
55562
55643
  }, [supabase, dashboardConfig, lineIds]);
55644
+ useEffect(() => {
55645
+ let isMounted = true;
55646
+ const fetchLeaderboardLines = async () => {
55647
+ if (!supabase || !resolvedCompanyId) {
55648
+ setLeaderboardLines(lines);
55649
+ return;
55650
+ }
55651
+ try {
55652
+ const linesService2 = new LinesService(supabase);
55653
+ const companyLines = await linesService2.getLinesByCompanyId(resolvedCompanyId);
55654
+ if (!isMounted) return;
55655
+ const transformed = companyLines.map((line) => ({
55656
+ id: line.id,
55657
+ line_name: line.name,
55658
+ factory_id: line.factoryId || "",
55659
+ factory_name: "N/A",
55660
+ company_id: line.companyId,
55661
+ company_name: "",
55662
+ enable: line.isActive ?? true
55663
+ }));
55664
+ setLeaderboardLines(transformed);
55665
+ } catch (err) {
55666
+ console.error("[KPIsOverviewView] Failed to load leaderboard lines:", err);
55667
+ if (!isMounted) return;
55668
+ setLeaderboardLines(lines);
55669
+ }
55670
+ };
55671
+ fetchLeaderboardLines();
55672
+ return () => {
55673
+ isMounted = false;
55674
+ };
55675
+ }, [supabase, resolvedCompanyId, lines]);
55563
55676
  const fetchMonthlyLeaderboard = useCallback(async () => {
55564
- if (!supabase || !resolvedCompanyId || lines.length === 0) return;
55565
- const targetLineIds = lines.map((line) => line.id);
55677
+ if (!supabase || !resolvedCompanyId || leaderboardLines.length === 0) return;
55678
+ const targetLineIds = leaderboardLines.map((line) => line.id);
55566
55679
  const lineIdsKey = targetLineIds.slice().sort().join(",");
55567
55680
  const requestKey = `${resolvedCompanyId}|${monthStartDate}|${monthEndDateKey}|${lineIdsKey}`;
55568
55681
  if (monthlyRequestKeyRef.current === requestKey) return;
@@ -55576,8 +55689,7 @@ var KPIsOverviewView = ({
55576
55689
  const entries = await lineLeaderboardService.getLineLeaderboard(supabase, {
55577
55690
  companyId: resolvedCompanyId,
55578
55691
  startDate: monthStartDate,
55579
- endDate: monthEndDateKey,
55580
- lineIds: targetLineIds
55692
+ endDate: monthEndDateKey
55581
55693
  });
55582
55694
  const nextMap = /* @__PURE__ */ new Map();
55583
55695
  targetLineIds.forEach((lineId) => nextMap.set(lineId, 0));
@@ -55610,11 +55722,47 @@ var KPIsOverviewView = ({
55610
55722
  } finally {
55611
55723
  setMonthlyLoading(false);
55612
55724
  }
55613
- }, [supabase, resolvedCompanyId, lines, monthStartDate, monthEndDateKey]);
55725
+ }, [supabase, resolvedCompanyId, leaderboardLines, monthStartDate, monthEndDateKey]);
55726
+ const fetchDailyLeaderboard = useCallback(async () => {
55727
+ if (!supabase || !resolvedCompanyId || leaderboardLines.length === 0) return;
55728
+ if (!currentShiftDate) return;
55729
+ const targetLineIds = leaderboardLines.map((line) => line.id);
55730
+ const lineIdsKey = targetLineIds.slice().sort().join(",");
55731
+ const requestKey = `${resolvedCompanyId}|${currentShiftDate}|${currentShiftId}|${lineIdsKey}`;
55732
+ if (dailyRequestKeyRef.current === requestKey) return;
55733
+ dailyRequestKeyRef.current = requestKey;
55734
+ setDailyLoading(true);
55735
+ setDailyError(null);
55736
+ try {
55737
+ const entries = await lineLeaderboardService.getDailyLineLeaderboard(supabase, {
55738
+ companyId: resolvedCompanyId,
55739
+ date: currentShiftDate,
55740
+ shiftId: currentShiftId
55741
+ });
55742
+ const nextMap = /* @__PURE__ */ new Map();
55743
+ entries.forEach((entry) => {
55744
+ const value = Number(entry.avg_efficiency);
55745
+ if (Number.isFinite(value)) {
55746
+ nextMap.set(entry.line_id, value);
55747
+ }
55748
+ });
55749
+ setTodayEfficiencyByLineId(nextMap);
55750
+ } catch (err) {
55751
+ console.error("[KPIsOverviewView] Failed to load daily leaderboard:", err);
55752
+ setDailyError("Failed to load daily leaderboard");
55753
+ dailyRequestKeyRef.current = null;
55754
+ } finally {
55755
+ setDailyLoading(false);
55756
+ }
55757
+ }, [supabase, resolvedCompanyId, leaderboardLines, currentShiftDate, currentShiftId]);
55614
55758
  useEffect(() => {
55615
55759
  if (activeTab !== "leaderboard") return;
55616
55760
  fetchMonthlyLeaderboard();
55617
55761
  }, [activeTab, timeRange, fetchMonthlyLeaderboard]);
55762
+ useEffect(() => {
55763
+ if (activeTab !== "leaderboard" || timeRange !== "today") return;
55764
+ fetchDailyLeaderboard();
55765
+ }, [activeTab, timeRange, fetchDailyLeaderboard]);
55618
55766
  const formatTopPerformerWeek = (periodStart, periodEnd) => {
55619
55767
  if (!periodStart || !periodEnd) return "Last Week";
55620
55768
  const startDate = /* @__PURE__ */ new Date(`${periodStart}T00:00:00`);
@@ -55710,14 +55858,15 @@ var KPIsOverviewView = ({
55710
55858
  }, [onBackClick, backLinkUrl, navigation]);
55711
55859
  const handleTabChange = useCallback((newTab) => {
55712
55860
  if (newTab === activeTab) return;
55861
+ const trackedLineCount = newTab === "leaderboard" ? leaderboardLines.length : lines.length;
55713
55862
  trackCoreEvent("Leaderboard Tab Switched", {
55714
55863
  from_tab: activeTab,
55715
55864
  to_tab: newTab,
55716
55865
  from_page: "kpis_overview",
55717
- lines_count: lines.length
55866
+ lines_count: trackedLineCount
55718
55867
  });
55719
55868
  setActiveTab(newTab);
55720
- }, [activeTab, lines.length]);
55869
+ }, [activeTab, leaderboardLines.length, lines.length]);
55721
55870
  const formatLocalDate2 = (date) => {
55722
55871
  const options = {
55723
55872
  year: "numeric",
@@ -55735,12 +55884,6 @@ var KPIsOverviewView = ({
55735
55884
  return `${startLabel} - ${endLabel}, ${now4.getFullYear()}`;
55736
55885
  };
55737
55886
  const isMonthlyMode = activeTab === "leaderboard" && timeRange === "monthly";
55738
- const currentShiftDetails = getCurrentShift(configuredTimezone, shiftConfig);
55739
- const shiftEndDate = React26__default.useMemo(
55740
- () => getShiftEndDate(currentShiftDetails, configuredTimezone),
55741
- [currentShiftDetails, configuredTimezone]
55742
- );
55743
- const shiftName = (currentShiftDetails.shiftName || "Day").replace(/ Shift$/i, "");
55744
55887
  const showTopPerformerImage = Boolean(topPerformer.imageUrl) && !topPerformerImageError;
55745
55888
  typeof topPerformer.efficiency === "number" && Number.isFinite(topPerformer.efficiency);
55746
55889
  topPerformerLoading ? "--" : typeof topPerformer.efficiency === "number" && Number.isFinite(topPerformer.efficiency) ? `${topPerformer.efficiency.toFixed(1)}%` : "--";
@@ -56093,15 +56236,16 @@ var KPIsOverviewView = ({
56093
56236
  /* @__PURE__ */ jsx("div", { className: "w-full h-full", children: /* @__PURE__ */ jsx(
56094
56237
  LinesLeaderboard,
56095
56238
  {
56096
- lines,
56239
+ lines: leaderboardLines,
56097
56240
  onLineClick: (line) => handleLineClick(line),
56241
+ assignedLineIds: assignedLineIdsForLeaderboard,
56098
56242
  timeRange,
56099
56243
  setTimeRange,
56100
56244
  todayEfficiencyByLineId,
56101
56245
  monthlyEfficiencyByLineId,
56102
56246
  supervisorsByLineId,
56103
56247
  supervisorNamesByLineId,
56104
- isLoadingToday: metricsLoading,
56248
+ isLoadingToday: dailyLoading,
56105
56249
  isLoadingMonthly: monthlyLoading,
56106
56250
  shiftEndDate,
56107
56251
  monthEndDate
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optifye/dashboard-core",
3
- "version": "6.10.34",
3
+ "version": "6.10.35",
4
4
  "description": "Reusable UI & logic for Optifye dashboard",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",