@optifye/dashboard-core 6.10.0 → 6.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -8838,27 +8838,208 @@ var useLeaderboardMetrics = (date, shiftId, limit = 10, filter2 = "all") => {
8838
8838
  refetch: fetchLeaderboard
8839
8839
  };
8840
8840
  };
8841
+ var useMultiLineShiftConfigs = (lineIds, fallbackConfig) => {
8842
+ const [shiftConfigMap, setShiftConfigMap] = useState(/* @__PURE__ */ new Map());
8843
+ const [isLoading, setIsLoading] = useState(true);
8844
+ const [error, setError] = useState(null);
8845
+ const supabase = useSupabase();
8846
+ const lineIdsKey = useMemo(() => lineIds.sort().join(","), [lineIds]);
8847
+ useEffect(() => {
8848
+ if (!lineIds || lineIds.length === 0) {
8849
+ setShiftConfigMap(/* @__PURE__ */ new Map());
8850
+ setIsLoading(false);
8851
+ return;
8852
+ }
8853
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
8854
+ const validLineIds = lineIds.filter((id3) => uuidRegex.test(id3));
8855
+ if (validLineIds.length === 0) {
8856
+ console.warn("[useMultiLineShiftConfigs] No valid line IDs provided");
8857
+ setShiftConfigMap(/* @__PURE__ */ new Map());
8858
+ setIsLoading(false);
8859
+ return;
8860
+ }
8861
+ let mounted = true;
8862
+ const calculateBreakDuration2 = (startTime, endTime) => {
8863
+ const [sh, sm] = startTime.split(":").map(Number);
8864
+ const [eh, em] = endTime.split(":").map(Number);
8865
+ let startMinutes = sh * 60 + sm;
8866
+ let endMinutes = eh * 60 + em;
8867
+ if (endMinutes < startMinutes) {
8868
+ endMinutes += 24 * 60;
8869
+ }
8870
+ return endMinutes - startMinutes;
8871
+ };
8872
+ const stripSeconds = (timeStr) => {
8873
+ if (!timeStr) return timeStr;
8874
+ return timeStr.substring(0, 5);
8875
+ };
8876
+ const buildShiftConfigFromRows = (shifts, fallback) => {
8877
+ const mapped = shifts.map((shift) => ({
8878
+ shiftId: shift.shift_id,
8879
+ shiftName: shift.shift_name || `Shift ${shift.shift_id}`,
8880
+ startTime: stripSeconds(shift.start_time),
8881
+ endTime: stripSeconds(shift.end_time),
8882
+ breaks: Array.isArray(shift.breaks) ? shift.breaks.map((b) => ({
8883
+ startTime: b.start || b.startTime || "00:00",
8884
+ endTime: b.end || b.endTime || "00:00",
8885
+ duration: calculateBreakDuration2(
8886
+ b.start || b.startTime || "00:00",
8887
+ b.end || b.endTime || "00:00"
8888
+ ),
8889
+ remarks: b.remarks || b.name || ""
8890
+ })) : [],
8891
+ timezone: shift.timezone
8892
+ }));
8893
+ const day = mapped.find((s) => s.shiftId === 0);
8894
+ const night = mapped.find((s) => s.shiftId === 1);
8895
+ return {
8896
+ shifts: mapped,
8897
+ timezone: mapped[0]?.timezone,
8898
+ dayShift: day ? { id: day.shiftId, startTime: day.startTime, endTime: day.endTime, name: day.shiftName } : fallback?.dayShift,
8899
+ nightShift: night ? { id: night.shiftId, startTime: night.startTime, endTime: night.endTime, name: night.shiftName } : fallback?.nightShift,
8900
+ transitionPeriodMinutes: fallback?.transitionPeriodMinutes || 0
8901
+ };
8902
+ };
8903
+ const fetchAllConfigs = async () => {
8904
+ try {
8905
+ setIsLoading(true);
8906
+ setError(null);
8907
+ console.log(`[useMultiLineShiftConfigs] Fetching shift configs for ${validLineIds.length} lines`);
8908
+ const { data, error: fetchError } = await supabase.from("line_operating_hours").select("line_id, shift_id, shift_name, start_time, end_time, breaks, timezone").in("line_id", validLineIds);
8909
+ if (fetchError) {
8910
+ console.error("[useMultiLineShiftConfigs] Error fetching shift configs:", fetchError);
8911
+ throw new Error(`Failed to fetch shift configs: ${fetchError.message}`);
8912
+ }
8913
+ const lineShiftsMap = /* @__PURE__ */ new Map();
8914
+ data?.forEach((row) => {
8915
+ if (!lineShiftsMap.has(row.line_id)) {
8916
+ lineShiftsMap.set(row.line_id, []);
8917
+ }
8918
+ lineShiftsMap.get(row.line_id).push(row);
8919
+ });
8920
+ const configMap = /* @__PURE__ */ new Map();
8921
+ lineShiftsMap.forEach((shifts, lineId) => {
8922
+ const config = buildShiftConfigFromRows(shifts, fallbackConfig);
8923
+ configMap.set(lineId, config);
8924
+ });
8925
+ validLineIds.forEach((lineId) => {
8926
+ if (!configMap.has(lineId) && fallbackConfig) {
8927
+ console.warn(`[useMultiLineShiftConfigs] No config found for line ${lineId}, using fallback`);
8928
+ configMap.set(lineId, fallbackConfig);
8929
+ }
8930
+ });
8931
+ console.log(`[useMultiLineShiftConfigs] Built configs for ${configMap.size} lines`);
8932
+ if (mounted) {
8933
+ setShiftConfigMap(configMap);
8934
+ setIsLoading(false);
8935
+ }
8936
+ } catch (err) {
8937
+ console.error("[useMultiLineShiftConfigs] Error:", err);
8938
+ if (mounted) {
8939
+ setError(err instanceof Error ? err.message : "Failed to fetch shift configs");
8940
+ if (fallbackConfig) {
8941
+ const fallbackMap = /* @__PURE__ */ new Map();
8942
+ validLineIds.forEach((lineId) => {
8943
+ fallbackMap.set(lineId, fallbackConfig);
8944
+ });
8945
+ setShiftConfigMap(fallbackMap);
8946
+ }
8947
+ setIsLoading(false);
8948
+ }
8949
+ }
8950
+ };
8951
+ fetchAllConfigs();
8952
+ return () => {
8953
+ mounted = false;
8954
+ };
8955
+ }, [lineIdsKey, supabase]);
8956
+ return {
8957
+ shiftConfigMap,
8958
+ isLoading,
8959
+ error
8960
+ };
8961
+ };
8962
+
8963
+ // src/lib/utils/shiftGrouping.ts
8964
+ var getCurrentShiftForLine = (lineId, shiftConfig, timezone, now2 = /* @__PURE__ */ new Date()) => {
8965
+ const currentShift = getCurrentShift(timezone, shiftConfig, now2);
8966
+ return {
8967
+ lineId,
8968
+ shiftId: currentShift.shiftId,
8969
+ date: currentShift.date,
8970
+ shiftName: currentShift.shiftName || `Shift ${currentShift.shiftId}`
8971
+ };
8972
+ };
8973
+ var groupLinesByShift = (shiftConfigMap, timezone, now2 = /* @__PURE__ */ new Date()) => {
8974
+ const lineShiftInfos = [];
8975
+ shiftConfigMap.forEach((shiftConfig, lineId) => {
8976
+ const info = getCurrentShiftForLine(lineId, shiftConfig, timezone, now2);
8977
+ lineShiftInfos.push(info);
8978
+ });
8979
+ const groupMap = /* @__PURE__ */ new Map();
8980
+ lineShiftInfos.forEach((info) => {
8981
+ const key = `${info.shiftId}-${info.date}`;
8982
+ if (!groupMap.has(key)) {
8983
+ groupMap.set(key, {
8984
+ shiftId: info.shiftId,
8985
+ date: info.date,
8986
+ shiftName: info.shiftName,
8987
+ lineIds: []
8988
+ });
8989
+ }
8990
+ groupMap.get(key).lineIds.push(info.lineId);
8991
+ });
8992
+ return Array.from(groupMap.values()).sort((a, b) => a.shiftId - b.shiftId);
8993
+ };
8994
+ var areAllLinesOnSameShift = (shiftConfigMap, timezone, now2 = /* @__PURE__ */ new Date()) => {
8995
+ if (shiftConfigMap.size <= 1) return true;
8996
+ const groups = groupLinesByShift(shiftConfigMap, timezone, now2);
8997
+ return groups.length === 1;
8998
+ };
8999
+ var getUniformShiftGroup = (shiftConfigMap, timezone, now2 = /* @__PURE__ */ new Date()) => {
9000
+ const groups = groupLinesByShift(shiftConfigMap, timezone, now2);
9001
+ return groups.length === 1 ? groups[0] : null;
9002
+ };
9003
+
9004
+ // src/lib/hooks/useDashboardMetrics.ts
8841
9005
  var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds }) => {
8842
9006
  const { supabaseUrl, supabaseKey } = useDashboardConfig();
8843
9007
  const entityConfig = useEntityConfig();
8844
9008
  const databaseConfig = useDatabaseConfig();
8845
9009
  const dateTimeConfig = useDateTimeConfig();
8846
9010
  const isFactoryView = lineId === (entityConfig.factoryViewId || "factory");
8847
- const firstLineId = useMemo(() => {
8848
- const configuredLineIds = getConfiguredLineIds(entityConfig);
8849
- return configuredLineIds.length > 0 ? configuredLineIds[0] : void 0;
9011
+ const appTimezone = useAppTimezone();
9012
+ const defaultTimezone = appTimezone || dateTimeConfig?.defaultTimezone || "UTC";
9013
+ const configuredLineIds = useMemo(() => {
9014
+ return getConfiguredLineIds(entityConfig);
8850
9015
  }, [entityConfig]);
8851
- const lineIdForShiftConfig = isFactoryView ? firstLineId : lineId;
8852
- const { shiftConfig, isLoading: shiftLoading, isFromDatabase } = useDynamicShiftConfig(lineIdForShiftConfig);
9016
+ const { shiftConfig: staticShiftConfig } = useDashboardConfig();
9017
+ const {
9018
+ shiftConfigMap: multiLineShiftConfigMap,
9019
+ isLoading: isMultiLineShiftConfigLoading
9020
+ } = useMultiLineShiftConfigs(
9021
+ isFactoryView ? configuredLineIds : [],
9022
+ staticShiftConfig
9023
+ );
9024
+ const {
9025
+ shiftConfig: singleLineShiftConfig,
9026
+ isLoading: isSingleLineShiftConfigLoading,
9027
+ isFromDatabase
9028
+ } = useDynamicShiftConfig(isFactoryView ? void 0 : lineId);
9029
+ const shiftLoading = isFactoryView ? isMultiLineShiftConfigLoading : isSingleLineShiftConfigLoading;
9030
+ const shiftGroups = useMemo(() => {
9031
+ if (!isFactoryView) return [];
9032
+ if (isMultiLineShiftConfigLoading || multiLineShiftConfigMap.size === 0) return [];
9033
+ return groupLinesByShift(multiLineShiftConfigMap, defaultTimezone);
9034
+ }, [isFactoryView, isMultiLineShiftConfigLoading, multiLineShiftConfigMap, defaultTimezone]);
9035
+ const shiftConfig = isFactoryView ? null : singleLineShiftConfig;
8853
9036
  console.log(`[useDashboardMetrics] \u{1F3AF} Shift config for line ${lineId}:`, {
8854
9037
  isFactoryView,
8855
- lineIdForShiftConfig,
8856
- firstLineId,
8857
9038
  isFromDatabase,
8858
- shiftConfig: shiftConfig ? { shifts: shiftConfig.shifts?.length, timezone: shiftConfig.timezone } : null
9039
+ shiftConfig: shiftConfig ? { shifts: shiftConfig.shifts?.length, timezone: shiftConfig.timezone } : null,
9040
+ shiftGroupsCount: shiftGroups.length,
9041
+ shiftGroups: shiftGroups.map((g) => ({ shiftId: g.shiftId, date: g.date, lineCount: g.lineIds.length }))
8859
9042
  });
8860
- const appTimezone = useAppTimezone();
8861
- const defaultTimezone = appTimezone || dateTimeConfig?.defaultTimezone || "UTC";
8862
9043
  const configuredLineMetricsTable = databaseConfig?.tables?.lineMetrics ?? "line_metrics";
8863
9044
  const schema = databaseConfig?.schema ?? "public";
8864
9045
  const supabase = useSupabase();
@@ -8895,16 +9076,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
8895
9076
  setIsLoading(true);
8896
9077
  setError(null);
8897
9078
  try {
8898
- const currentShiftDetails = getCurrentShift(defaultTimezone, shiftConfig);
8899
- const operationalDate = getOperationalDate(defaultTimezone);
8900
- const targetLineIds = currentLineIdToUse === (entityConfig.factoryViewId || "factory") ? userAccessibleLineIds || getConfiguredLineIds(entityConfig) : [currentLineIdToUse];
8901
- if (targetLineIds.length === 0 && currentLineIdToUse === (entityConfig.factoryViewId || "factory")) {
8902
- throw new Error("Factory view selected, but no lines are configured in entityConfig.");
8903
- }
8904
- if (targetLineIds.length === 0) {
8905
- throw new Error("No target line IDs available for fetching metrics.");
8906
- }
8907
- const isFactoryView2 = currentLineIdToUse === (entityConfig.factoryViewId || "factory");
8908
9079
  const { data: { session } } = await supabase.auth.getSession();
8909
9080
  console.log("[useDashboardMetrics] Session check:", {
8910
9081
  hasSession: !!session,
@@ -8918,42 +9089,98 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
8918
9089
  if (!apiUrl) {
8919
9090
  throw new Error("Backend URL is not configured. Please set NEXT_PUBLIC_BACKEND_URL in your environment.");
8920
9091
  }
8921
- const lineIdsParam = isFactoryView2 ? `line_ids=${targetLineIds.join(",")}` : `line_id=${targetLineIds[0]}`;
8922
- const url = `${apiUrl}/api/dashboard/metrics?${lineIdsParam}&date=${operationalDate}&shift_id=${currentShiftDetails.shiftId}&company_id=${entityConfig.companyId}`;
8923
- console.log("[useDashboardMetrics] Calling backend API:", {
8924
- url,
8925
- apiUrl,
8926
- lineIdsParam,
8927
- operationalDate,
8928
- shiftId: currentShiftDetails.shiftId,
8929
- companyId: entityConfig.companyId
8930
- });
8931
- const response = await fetch(url, {
8932
- method: "GET",
8933
- headers: {
8934
- "Authorization": `Bearer ${session.access_token}`,
8935
- "Content-Type": "application/json"
8936
- }
8937
- });
8938
- console.log("[useDashboardMetrics] Response status:", response.status, response.statusText);
8939
- if (!response.ok) {
8940
- const errorText = await response.text();
8941
- console.error("[useDashboardMetrics] Backend API error response:", errorText);
8942
- throw new Error(`Backend API error (${response.status}): ${errorText}`);
9092
+ const factoryViewIdentifier = entityConfig.factoryViewId || "factory";
9093
+ const isFactory = currentLineIdToUse === factoryViewIdentifier;
9094
+ const targetLineIds = isFactory ? userAccessibleLineIds || configuredLineIds : [currentLineIdToUse];
9095
+ if (targetLineIds.length === 0) {
9096
+ throw new Error("No target line IDs available for fetching metrics.");
8943
9097
  }
8944
- const responseText = await response.text();
8945
- let backendData;
8946
- try {
8947
- backendData = JSON.parse(responseText);
8948
- } catch (parseError) {
8949
- console.error("[useDashboardMetrics] Failed to parse response as JSON. Response text:", responseText.substring(0, 500));
8950
- throw new Error(`Invalid JSON response from backend. Received: ${responseText.substring(0, 100)}...`);
9098
+ let allWorkspaceMetrics = [];
9099
+ let allLineMetrics = [];
9100
+ if (isFactory && shiftGroups.length > 0) {
9101
+ console.log(`[useDashboardMetrics] \u{1F3ED} Factory view: Fetching for ${shiftGroups.length} shift group(s)`);
9102
+ const metricsPromises = shiftGroups.map(async (group) => {
9103
+ const lineIdsParam = `line_ids=${group.lineIds.join(",")}`;
9104
+ const url = `${apiUrl}/api/dashboard/metrics?${lineIdsParam}&date=${group.date}&shift_id=${group.shiftId}&company_id=${entityConfig.companyId}`;
9105
+ console.log(`[useDashboardMetrics] \u{1F4CA} Fetching for shift ${group.shiftId} (${group.shiftName}):`, {
9106
+ lineIds: group.lineIds,
9107
+ date: group.date
9108
+ });
9109
+ const response = await fetch(url, {
9110
+ method: "GET",
9111
+ headers: {
9112
+ "Authorization": `Bearer ${session.access_token}`,
9113
+ "Content-Type": "application/json"
9114
+ }
9115
+ });
9116
+ if (!response.ok) {
9117
+ const errorText = await response.text();
9118
+ console.error(`[useDashboardMetrics] Backend API error for shift ${group.shiftId}:`, errorText);
9119
+ throw new Error(`Backend API error (${response.status}): ${errorText}`);
9120
+ }
9121
+ const responseText = await response.text();
9122
+ try {
9123
+ return JSON.parse(responseText);
9124
+ } catch (parseError) {
9125
+ console.error("[useDashboardMetrics] Failed to parse response:", responseText.substring(0, 500));
9126
+ throw new Error(`Invalid JSON response from backend`);
9127
+ }
9128
+ });
9129
+ const results = await Promise.all(metricsPromises);
9130
+ results.forEach((result) => {
9131
+ if (result.workspace_metrics) {
9132
+ allWorkspaceMetrics.push(...result.workspace_metrics);
9133
+ }
9134
+ if (result.line_metrics) {
9135
+ allLineMetrics.push(...result.line_metrics);
9136
+ }
9137
+ });
9138
+ console.log(`[useDashboardMetrics] \u{1F4CA} Merged metrics from ${results.length} shift groups:`, {
9139
+ workspaceCount: allWorkspaceMetrics.length,
9140
+ lineMetricsCount: allLineMetrics.length
9141
+ });
9142
+ } else {
9143
+ const currentShiftDetails = shiftConfig ? getCurrentShift(defaultTimezone, shiftConfig) : shiftGroups.length === 1 ? { date: shiftGroups[0].date, shiftId: shiftGroups[0].shiftId } : getCurrentShift(defaultTimezone, staticShiftConfig);
9144
+ const operationalDate = currentShiftDetails.date;
9145
+ const lineIdsParam = isFactory ? `line_ids=${targetLineIds.join(",")}` : `line_id=${targetLineIds[0]}`;
9146
+ const url = `${apiUrl}/api/dashboard/metrics?${lineIdsParam}&date=${operationalDate}&shift_id=${currentShiftDetails.shiftId}&company_id=${entityConfig.companyId}`;
9147
+ console.log("[useDashboardMetrics] Calling backend API:", {
9148
+ url,
9149
+ apiUrl,
9150
+ lineIdsParam,
9151
+ operationalDate,
9152
+ shiftId: currentShiftDetails.shiftId,
9153
+ companyId: entityConfig.companyId
9154
+ });
9155
+ const response = await fetch(url, {
9156
+ method: "GET",
9157
+ headers: {
9158
+ "Authorization": `Bearer ${session.access_token}`,
9159
+ "Content-Type": "application/json"
9160
+ }
9161
+ });
9162
+ console.log("[useDashboardMetrics] Response status:", response.status, response.statusText);
9163
+ if (!response.ok) {
9164
+ const errorText = await response.text();
9165
+ console.error("[useDashboardMetrics] Backend API error response:", errorText);
9166
+ throw new Error(`Backend API error (${response.status}): ${errorText}`);
9167
+ }
9168
+ const responseText = await response.text();
9169
+ let backendData;
9170
+ try {
9171
+ backendData = JSON.parse(responseText);
9172
+ } catch (parseError) {
9173
+ console.error("[useDashboardMetrics] Failed to parse response as JSON. Response text:", responseText.substring(0, 500));
9174
+ throw new Error(`Invalid JSON response from backend. Received: ${responseText.substring(0, 100)}...`);
9175
+ }
9176
+ console.log("[useDashboardMetrics] Backend response:", {
9177
+ workspaceCount: backendData.workspace_metrics?.length || 0,
9178
+ lineCount: backendData.line_metrics?.length || 0
9179
+ });
9180
+ allWorkspaceMetrics = backendData.workspace_metrics || [];
9181
+ allLineMetrics = backendData.line_metrics || [];
8951
9182
  }
8952
- console.log("[useDashboardMetrics] Backend response:", {
8953
- workspaceCount: backendData.workspace_metrics?.length || 0,
8954
- lineCount: backendData.line_metrics?.length || 0
8955
- });
8956
- const transformedWorkspaceData = (backendData.workspace_metrics || []).map((item) => ({
9183
+ const transformedWorkspaceData = allWorkspaceMetrics.map((item) => ({
8957
9184
  company_id: item.company_id || entityConfig.companyId,
8958
9185
  line_id: item.line_id,
8959
9186
  shift_id: item.shift_id,
@@ -8976,7 +9203,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
8976
9203
  });
8977
9204
  const newMetricsState = {
8978
9205
  workspaceMetrics: transformedWorkspaceData,
8979
- lineMetrics: backendData.line_metrics || []
9206
+ lineMetrics: allLineMetrics || []
8980
9207
  };
8981
9208
  console.log("[useDashboardMetrics] Setting metrics state:", {
8982
9209
  workspaceMetrics: newMetricsState.workspaceMetrics.length,
@@ -8997,9 +9224,12 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
8997
9224
  metrics2?.lineMetrics?.length || 0,
8998
9225
  companySpecificMetricsTable,
8999
9226
  entityConfig,
9000
- appTimezone,
9001
9227
  defaultTimezone,
9002
9228
  shiftConfig,
9229
+ shiftGroups,
9230
+ configuredLineIds,
9231
+ staticShiftConfig,
9232
+ userAccessibleLineIds,
9003
9233
  shiftLoading
9004
9234
  ]);
9005
9235
  const fetchAllMetricsRef = useRef(fetchAllMetrics);
@@ -9023,32 +9253,68 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
9023
9253
  if (!currentLineIdToUse || !supabase || companySpecificMetricsTable.includes("unknown_company") || !entityConfig.companyId) {
9024
9254
  return;
9025
9255
  }
9026
- const currentShiftDetails = getCurrentShift(defaultTimezone, shiftConfig);
9027
- const operationalDateForSubscription = getOperationalDate(defaultTimezone);
9028
- const targetLineIds = currentLineIdToUse === (entityConfig.factoryViewId || "factory") ? userAccessibleLineIds || getConfiguredLineIds(entityConfig) : [currentLineIdToUse];
9029
- if (targetLineIds.length === 0) return;
9030
- const wsMetricsFilter = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
9031
- const lineMetricsFilter = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
9256
+ const factoryViewIdentifier = entityConfig.factoryViewId || "factory";
9257
+ const isFactory = currentLineIdToUse === factoryViewIdentifier;
9032
9258
  const channels = [];
9033
- const createSubscription = (table, filter2, channelNameBase, callback) => {
9034
- const channelName = `${channelNameBase}-${Date.now()}`.replace(/[^a-zA-Z0-9_-]/g, "");
9035
- const channel = supabase.channel(channelName).on(
9036
- "postgres_changes",
9037
- { event: "*", schema, table, filter: filter2 },
9038
- (payload) => {
9039
- const payloadData = payload.new;
9040
- if (payloadData?.date === operationalDateForSubscription && payloadData?.shift_id === currentShiftDetails.shiftId) {
9041
- callback();
9259
+ if (isFactory && shiftGroups.length > 0) {
9260
+ console.log(`[useDashboardMetrics] \u{1F4E1} Setting up subscriptions for ${shiftGroups.length} shift group(s)`);
9261
+ shiftGroups.forEach((group, index) => {
9262
+ const baseFilterParts = `date=eq.${group.date},shift_id=eq.${group.shiftId}`;
9263
+ const lineIdFilterPart = `line_id=in.(${group.lineIds.map((id3) => `"${id3}"`).join(",")})`;
9264
+ const filter2 = `${baseFilterParts},${lineIdFilterPart}`;
9265
+ const wsChannelName = `dashboard-ws-g${index}-${group.date}-${group.shiftId}`.replace(/[^a-zA-Z0-9_-]/g, "");
9266
+ const wsChannel = supabase.channel(wsChannelName).on(
9267
+ "postgres_changes",
9268
+ { event: "*", schema, table: companySpecificMetricsTable, filter: filter2 },
9269
+ (payload) => {
9270
+ const payloadData = payload.new || payload.old;
9271
+ if (payloadData?.date === group.date && payloadData?.shift_id === group.shiftId) {
9272
+ queueUpdate();
9273
+ }
9042
9274
  }
9043
- }
9044
- ).subscribe();
9045
- channels.push(channel);
9046
- };
9047
- createSubscription(companySpecificMetricsTable, wsMetricsFilter, "dashboard-ws-metrics", queueUpdate);
9048
- createSubscription(configuredLineMetricsTable, lineMetricsFilter, "dashboard-line-metrics", () => {
9049
- queueUpdate();
9050
- onLineMetricsUpdateRef.current?.();
9051
- });
9275
+ ).subscribe();
9276
+ channels.push(wsChannel);
9277
+ const lmChannelName = `dashboard-lm-g${index}-${group.date}-${group.shiftId}`.replace(/[^a-zA-Z0-9_-]/g, "");
9278
+ const lmChannel = supabase.channel(lmChannelName).on(
9279
+ "postgres_changes",
9280
+ { event: "*", schema, table: configuredLineMetricsTable, filter: filter2 },
9281
+ (payload) => {
9282
+ const payloadData = payload.new || payload.old;
9283
+ if (payloadData?.date === group.date && payloadData?.shift_id === group.shiftId) {
9284
+ queueUpdate();
9285
+ onLineMetricsUpdateRef.current?.();
9286
+ }
9287
+ }
9288
+ ).subscribe();
9289
+ channels.push(lmChannel);
9290
+ });
9291
+ } else {
9292
+ const currentShiftDetails = shiftConfig ? getCurrentShift(defaultTimezone, shiftConfig) : getCurrentShift(defaultTimezone, staticShiftConfig);
9293
+ const operationalDateForSubscription = currentShiftDetails.date;
9294
+ const targetLineIds = [currentLineIdToUse];
9295
+ if (targetLineIds.length === 0) return;
9296
+ const wsMetricsFilter = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
9297
+ const lineMetricsFilter = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
9298
+ const createSubscription = (table, filter2, channelNameBase, callback) => {
9299
+ const channelName = `${channelNameBase}-${Date.now()}`.replace(/[^a-zA-Z0-9_-]/g, "");
9300
+ const channel = supabase.channel(channelName).on(
9301
+ "postgres_changes",
9302
+ { event: "*", schema, table, filter: filter2 },
9303
+ (payload) => {
9304
+ const payloadData = payload.new;
9305
+ if (payloadData?.date === operationalDateForSubscription && payloadData?.shift_id === currentShiftDetails.shiftId) {
9306
+ callback();
9307
+ }
9308
+ }
9309
+ ).subscribe();
9310
+ channels.push(channel);
9311
+ };
9312
+ createSubscription(companySpecificMetricsTable, wsMetricsFilter, "dashboard-ws-metrics", queueUpdate);
9313
+ createSubscription(configuredLineMetricsTable, lineMetricsFilter, "dashboard-line-metrics", () => {
9314
+ queueUpdate();
9315
+ onLineMetricsUpdateRef.current?.();
9316
+ });
9317
+ }
9052
9318
  return () => {
9053
9319
  channels.forEach((channel) => {
9054
9320
  supabase?.removeChannel(channel);
@@ -9062,9 +9328,10 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
9062
9328
  schema,
9063
9329
  entityConfig?.companyId,
9064
9330
  entityConfig?.factoryViewId,
9065
- appTimezone,
9066
9331
  defaultTimezone,
9067
9332
  shiftConfig,
9333
+ staticShiftConfig,
9334
+ shiftGroups,
9068
9335
  lineId,
9069
9336
  userAccessibleLineIds
9070
9337
  ]);
@@ -9082,20 +9349,37 @@ var useLineKPIs = ({ lineId, enabled }) => {
9082
9349
  const isFactoryView = lineId === (entityConfig.factoryViewId || "factory");
9083
9350
  const databaseConfig = useDatabaseConfig();
9084
9351
  const dateTimeConfig = useDateTimeConfig();
9085
- useShiftConfig();
9086
- const firstLineId = useMemo(() => {
9087
- const configuredLineIds = getConfiguredLineIds(entityConfig);
9088
- return configuredLineIds.length > 0 ? configuredLineIds[0] : void 0;
9352
+ const staticShiftConfig = useShiftConfig();
9353
+ const appTimezone = useAppTimezone();
9354
+ const timezone = appTimezone || dateTimeConfig.defaultTimezone || "UTC";
9355
+ const configuredLineIds = useMemo(() => {
9356
+ return getConfiguredLineIds(entityConfig);
9089
9357
  }, [entityConfig]);
9090
- const lineIdForShiftConfig = isFactoryView ? firstLineId : lineId;
9091
- const { shiftConfig: dynamicShiftConfig, isFromDatabase, isLoading: isShiftConfigLoading } = useDynamicShiftConfig(lineIdForShiftConfig);
9092
- const shiftConfig = dynamicShiftConfig;
9358
+ const {
9359
+ shiftConfigMap: multiLineShiftConfigMap,
9360
+ isLoading: isMultiLineShiftConfigLoading
9361
+ } = useMultiLineShiftConfigs(
9362
+ isFactoryView ? configuredLineIds : [],
9363
+ staticShiftConfig
9364
+ );
9365
+ const {
9366
+ shiftConfig: singleLineShiftConfig,
9367
+ isFromDatabase,
9368
+ isLoading: isSingleLineShiftConfigLoading
9369
+ } = useDynamicShiftConfig(isFactoryView ? void 0 : lineId);
9370
+ const isShiftConfigLoading = isFactoryView ? isMultiLineShiftConfigLoading : isSingleLineShiftConfigLoading;
9371
+ const shiftGroups = useMemo(() => {
9372
+ if (!isFactoryView) return [];
9373
+ if (isMultiLineShiftConfigLoading || multiLineShiftConfigMap.size === 0) return [];
9374
+ return groupLinesByShift(multiLineShiftConfigMap, timezone);
9375
+ }, [isFactoryView, isMultiLineShiftConfigLoading, multiLineShiftConfigMap, timezone]);
9376
+ const shiftConfig = isFactoryView ? null : singleLineShiftConfig;
9093
9377
  console.log(`[useLineKPIs] \u{1F3AF} Shift config for line ${lineId}:`, {
9094
9378
  isFactoryView,
9095
- lineIdForShiftConfig,
9096
- firstLineId,
9097
9379
  isFromDatabase,
9098
- shiftConfig
9380
+ shiftConfig,
9381
+ shiftGroupsCount: shiftGroups.length,
9382
+ shiftGroups: shiftGroups.map((g) => ({ shiftId: g.shiftId, date: g.date, lineCount: g.lineIds.length }))
9099
9383
  });
9100
9384
  const supabase = useSupabase();
9101
9385
  useMemo(() => {
@@ -9109,8 +9393,6 @@ var useLineKPIs = ({ lineId, enabled }) => {
9109
9393
  const updateQueueRef = useRef(false);
9110
9394
  const updateTimeoutRef = useRef(null);
9111
9395
  const queueUpdateRef = useRef(void 0);
9112
- const appTimezone = useAppTimezone();
9113
- const timezone = appTimezone || dateTimeConfig.defaultTimezone || "UTC";
9114
9396
  const schema = databaseConfig.schema ?? "public";
9115
9397
  const lineMetricsTable = databaseConfig.tables?.lineMetrics ?? "line_metrics";
9116
9398
  const companySpecificMetricsTable = useMemo(
@@ -9129,8 +9411,6 @@ var useLineKPIs = ({ lineId, enabled }) => {
9129
9411
  setIsLoading(true);
9130
9412
  setError(null);
9131
9413
  try {
9132
- const currentShiftDetails = getCurrentShift(timezone, shiftConfig ?? void 0);
9133
- const operationalDate = currentShiftDetails.date;
9134
9414
  const { data: { session } } = await supabase.auth.getSession();
9135
9415
  if (!session?.access_token) {
9136
9416
  throw new Error("No authentication token available");
@@ -9141,33 +9421,68 @@ var useLineKPIs = ({ lineId, enabled }) => {
9141
9421
  }
9142
9422
  const factoryViewIdentifier = entityConfig.factoryViewId || "factory";
9143
9423
  const isFactory = currentLineId === factoryViewIdentifier;
9144
- const lineParam = isFactory ? `line_ids=${getConfiguredLineIds(entityConfig).join(",")}` : `line_id=${currentLineId}`;
9145
- const response = await fetch(
9146
- `${apiUrl}/api/dashboard/line-kpis?${lineParam}&date=${operationalDate}&shift_id=${currentShiftDetails.shiftId}&company_id=${entityConfig.companyId}`,
9147
- {
9148
- headers: {
9149
- "Authorization": `Bearer ${session.access_token}`,
9150
- "Content-Type": "application/json"
9424
+ if (isFactory && shiftGroups.length > 0) {
9425
+ console.log(`[useLineKPIs] \u{1F3ED} Factory view: Fetching KPIs for ${shiftGroups.length} shift group(s)`);
9426
+ const kpiPromises = shiftGroups.map(async (group) => {
9427
+ const lineIdsParam = `line_ids=${group.lineIds.join(",")}`;
9428
+ const url = `${apiUrl}/api/dashboard/line-kpis?${lineIdsParam}&date=${group.date}&shift_id=${group.shiftId}&company_id=${entityConfig.companyId}`;
9429
+ console.log(`[useLineKPIs] \u{1F4CA} Fetching for shift ${group.shiftId} (${group.shiftName}):`, {
9430
+ lineIds: group.lineIds,
9431
+ date: group.date
9432
+ });
9433
+ const response = await fetch(url, {
9434
+ headers: {
9435
+ "Authorization": `Bearer ${session.access_token}`,
9436
+ "Content-Type": "application/json"
9437
+ }
9438
+ });
9439
+ if (!response.ok) {
9440
+ const errorText = await response.text();
9441
+ console.error(`[useLineKPIs] Backend API error for shift ${group.shiftId}:`, errorText);
9442
+ throw new Error(`Backend API error (${response.status}): ${errorText}`);
9151
9443
  }
9152
- }
9153
- );
9154
- if (!response.ok) {
9155
- const errorText = await response.text();
9156
- console.error("[useLineKPIs] Backend API error response:", errorText);
9157
- throw new Error(`Backend API error (${response.status}): ${errorText}`);
9158
- }
9159
- const responseText = await response.text();
9160
- let backendData;
9161
- try {
9162
- backendData = JSON.parse(responseText);
9163
- } catch (parseError) {
9164
- console.error("[useLineKPIs] Failed to parse response as JSON. Response text:", responseText.substring(0, 500));
9165
- throw new Error(`Invalid JSON response from backend. Received: ${responseText.substring(0, 100)}...`);
9166
- }
9167
- if (backendData.kpis) {
9168
- setKPIs(backendData.kpis);
9444
+ const responseText = await response.text();
9445
+ try {
9446
+ return JSON.parse(responseText);
9447
+ } catch (parseError) {
9448
+ console.error("[useLineKPIs] Failed to parse response as JSON:", responseText.substring(0, 500));
9449
+ throw new Error(`Invalid JSON response from backend`);
9450
+ }
9451
+ });
9452
+ const results = await Promise.all(kpiPromises);
9453
+ const aggregatedKPIs = aggregateKPIResults(results);
9454
+ setKPIs(aggregatedKPIs);
9169
9455
  } else {
9170
- setKPIs(null);
9456
+ const currentShiftDetails = shiftConfig ? getCurrentShift(timezone, shiftConfig) : shiftGroups.length === 1 ? { date: shiftGroups[0].date, shiftId: shiftGroups[0].shiftId } : getCurrentShift(timezone, staticShiftConfig);
9457
+ const operationalDate = currentShiftDetails.date;
9458
+ const lineParam = isFactory ? `line_ids=${configuredLineIds.join(",")}` : `line_id=${currentLineId}`;
9459
+ const response = await fetch(
9460
+ `${apiUrl}/api/dashboard/line-kpis?${lineParam}&date=${operationalDate}&shift_id=${currentShiftDetails.shiftId}&company_id=${entityConfig.companyId}`,
9461
+ {
9462
+ headers: {
9463
+ "Authorization": `Bearer ${session.access_token}`,
9464
+ "Content-Type": "application/json"
9465
+ }
9466
+ }
9467
+ );
9468
+ if (!response.ok) {
9469
+ const errorText = await response.text();
9470
+ console.error("[useLineKPIs] Backend API error response:", errorText);
9471
+ throw new Error(`Backend API error (${response.status}): ${errorText}`);
9472
+ }
9473
+ const responseText = await response.text();
9474
+ let backendData;
9475
+ try {
9476
+ backendData = JSON.parse(responseText);
9477
+ } catch (parseError) {
9478
+ console.error("[useLineKPIs] Failed to parse response as JSON. Response text:", responseText.substring(0, 500));
9479
+ throw new Error(`Invalid JSON response from backend. Received: ${responseText.substring(0, 100)}...`);
9480
+ }
9481
+ if (backendData.kpis) {
9482
+ setKPIs(backendData.kpis);
9483
+ } else {
9484
+ setKPIs(null);
9485
+ }
9171
9486
  }
9172
9487
  } catch (err) {
9173
9488
  console.error("[useLineKPIs] Error fetching KPIs:", err);
@@ -9178,7 +9493,80 @@ var useLineKPIs = ({ lineId, enabled }) => {
9178
9493
  isFetchingRef.current = false;
9179
9494
  updateQueueRef.current = false;
9180
9495
  }
9181
- }, [timezone, shiftConfig, entityConfig, supabase, enabled, isShiftConfigLoading]);
9496
+ }, [timezone, shiftConfig, shiftGroups, configuredLineIds, staticShiftConfig, entityConfig, supabase, enabled, isShiftConfigLoading]);
9497
+ const aggregateKPIResults = (results) => {
9498
+ const validResults = results.filter((r2) => r2?.kpis);
9499
+ if (validResults.length === 0) return null;
9500
+ if (validResults.length === 1) return validResults[0].kpis;
9501
+ let totalEfficiency = 0;
9502
+ let totalEfficiencyWeight = 0;
9503
+ let totalOutputCurrent = 0;
9504
+ let totalOutputTarget = 0;
9505
+ let totalIdealOutput = 0;
9506
+ let totalUnderperformingCurrent = 0;
9507
+ let totalUnderperformingTotal = 0;
9508
+ let totalCycleTimeSum = 0;
9509
+ let totalCycleTimeCount = 0;
9510
+ let totalQualitySum = 0;
9511
+ let totalQualityCount = 0;
9512
+ validResults.forEach((result) => {
9513
+ const kpis2 = result.kpis;
9514
+ if (kpis2.efficiency?.value !== void 0) {
9515
+ const weight = kpis2.outputProgress?.current || 1;
9516
+ totalEfficiency += kpis2.efficiency.value * weight;
9517
+ totalEfficiencyWeight += weight;
9518
+ }
9519
+ if (kpis2.outputProgress) {
9520
+ totalOutputCurrent += kpis2.outputProgress.current || 0;
9521
+ totalOutputTarget += kpis2.outputProgress.target || 0;
9522
+ totalIdealOutput += kpis2.outputProgress.idealOutput || 0;
9523
+ }
9524
+ if (kpis2.underperformingWorkers) {
9525
+ totalUnderperformingCurrent += kpis2.underperformingWorkers.current || 0;
9526
+ totalUnderperformingTotal += kpis2.underperformingWorkers.total || 0;
9527
+ }
9528
+ if (kpis2.avgCycleTime?.value !== void 0) {
9529
+ totalCycleTimeSum += kpis2.avgCycleTime.value;
9530
+ totalCycleTimeCount++;
9531
+ }
9532
+ if (kpis2.qualityCompliance?.value !== void 0) {
9533
+ totalQualitySum += kpis2.qualityCompliance.value;
9534
+ totalQualityCount++;
9535
+ }
9536
+ });
9537
+ const aggregated = {
9538
+ efficiency: {
9539
+ value: totalEfficiencyWeight > 0 ? totalEfficiency / totalEfficiencyWeight : 0,
9540
+ change: 0
9541
+ // Change not meaningful when aggregating across shifts
9542
+ },
9543
+ outputProgress: {
9544
+ current: totalOutputCurrent,
9545
+ target: totalOutputTarget,
9546
+ change: 0,
9547
+ // Change not meaningful when aggregating across shifts
9548
+ idealOutput: totalIdealOutput
9549
+ },
9550
+ underperformingWorkers: {
9551
+ current: totalUnderperformingCurrent,
9552
+ total: totalUnderperformingTotal,
9553
+ change: 0
9554
+ // Change not meaningful when aggregating across shifts
9555
+ },
9556
+ avgCycleTime: {
9557
+ value: totalCycleTimeCount > 0 ? totalCycleTimeSum / totalCycleTimeCount : 0,
9558
+ change: 0
9559
+ // Change not meaningful when aggregating across shifts
9560
+ },
9561
+ qualityCompliance: {
9562
+ value: totalQualityCount > 0 ? totalQualitySum / totalQualityCount : 0,
9563
+ change: 0
9564
+ // Change not meaningful when aggregating across shifts
9565
+ }
9566
+ };
9567
+ console.log("[useLineKPIs] \u{1F4CA} Aggregated KPIs from", validResults.length, "shift groups:", aggregated);
9568
+ return aggregated;
9569
+ };
9182
9570
  const queueUpdate = useCallback(() => {
9183
9571
  if (updateTimeoutRef.current) {
9184
9572
  clearTimeout(updateTimeoutRef.current);
@@ -9198,40 +9586,66 @@ var useLineKPIs = ({ lineId, enabled }) => {
9198
9586
  return;
9199
9587
  }
9200
9588
  fetchKPIs();
9201
- const currentShiftDetails = getCurrentShift(timezone, shiftConfig ?? void 0);
9202
- const operationalDate = currentShiftDetails.date;
9203
9589
  const factoryViewIdentifier = entityConfig.factoryViewId || "factory";
9204
- const targetLineIds = currentLineId === factoryViewIdentifier ? getConfiguredLineIds(entityConfig) : [currentLineId];
9205
- if (targetLineIds.length === 0) {
9206
- console.warn("[useLineKPIs] No target line IDs for subscription. LineId:", currentLineId);
9207
- return;
9208
- }
9209
- const baseFilterParts = `date=eq.${operationalDate},shift_id=eq.${currentShiftDetails.shiftId}`;
9210
- const lineIdFilterPart = `line_id=in.(${targetLineIds.join(",")})`;
9211
- const lineMetricsFilter = `${baseFilterParts},${lineIdFilterPart}`;
9212
- const companyTableFilter = `${baseFilterParts},${lineIdFilterPart}`;
9590
+ const isFactory = currentLineId === factoryViewIdentifier;
9213
9591
  const activeChannels = [];
9214
- const lmChannelName = `kpi-lm-${currentLineId}-${operationalDate}-${currentShiftDetails.shiftId}`.replace(/[^a-zA-Z0-9_-]/g, "");
9215
- const lmChannel = supabase.channel(lmChannelName).on(
9216
- "postgres_changes",
9217
- { event: "*", schema, table: lineMetricsTable, filter: lineMetricsFilter },
9218
- (payload) => {
9219
- const payloadData = payload.new || payload.old;
9220
- if (payloadData?.date === operationalDate && payloadData?.shift_id === currentShiftDetails.shiftId) {
9221
- queueUpdateRef.current?.();
9592
+ if (isFactory && shiftGroups.length > 0) {
9593
+ console.log(`[useLineKPIs] \u{1F4E1} Setting up subscriptions for ${shiftGroups.length} shift group(s)`);
9594
+ shiftGroups.forEach((group, index) => {
9595
+ const baseFilterParts = `date=eq.${group.date},shift_id=eq.${group.shiftId}`;
9596
+ const lineIdFilterPart = `line_id=in.(${group.lineIds.join(",")})`;
9597
+ const filter2 = `${baseFilterParts},${lineIdFilterPart}`;
9598
+ const lmChannelName = `kpi-lm-factory-g${index}-${group.date}-${group.shiftId}`.replace(/[^a-zA-Z0-9_-]/g, "");
9599
+ const lmChannel = supabase.channel(lmChannelName).on(
9600
+ "postgres_changes",
9601
+ { event: "*", schema, table: lineMetricsTable, filter: filter2 },
9602
+ (payload) => {
9603
+ const payloadData = payload.new || payload.old;
9604
+ if (payloadData?.date === group.date && payloadData?.shift_id === group.shiftId) {
9605
+ queueUpdateRef.current?.();
9606
+ }
9607
+ }
9608
+ ).subscribe((status, err) => {
9609
+ if (status === REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR || status === REALTIME_SUBSCRIBE_STATES.TIMED_OUT) {
9610
+ console.error(`[useLineKPIs] Subscription to ${lineMetricsTable} (group ${index}) FAILED:`, status, err);
9611
+ }
9612
+ });
9613
+ activeChannels.push(lmChannel);
9614
+ if (companySpecificMetricsTable && !companySpecificMetricsTable.includes("unknown_company")) {
9615
+ const csChannelName = `kpi-cs-factory-g${index}-${group.date}-${group.shiftId}`.replace(/[^a-zA-Z0-9_-]/g, "");
9616
+ const csChannel = supabase.channel(csChannelName).on(
9617
+ "postgres_changes",
9618
+ { event: "*", schema, table: companySpecificMetricsTable, filter: filter2 },
9619
+ (payload) => {
9620
+ const payloadData = payload.new || payload.old;
9621
+ if (payloadData?.date === group.date && payloadData?.shift_id === group.shiftId) {
9622
+ queueUpdateRef.current?.();
9623
+ }
9624
+ }
9625
+ ).subscribe((status, err) => {
9626
+ if (status === REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR || status === REALTIME_SUBSCRIBE_STATES.TIMED_OUT) {
9627
+ console.error(`[useLineKPIs] Subscription to ${companySpecificMetricsTable} (group ${index}) FAILED:`, status, err);
9628
+ }
9629
+ });
9630
+ activeChannels.push(csChannel);
9222
9631
  }
9632
+ });
9633
+ } else {
9634
+ const currentShiftDetails = shiftConfig ? getCurrentShift(timezone, shiftConfig) : getCurrentShift(timezone, staticShiftConfig);
9635
+ const operationalDate = currentShiftDetails.date;
9636
+ const targetLineIds = [currentLineId];
9637
+ if (targetLineIds.length === 0) {
9638
+ console.warn("[useLineKPIs] No target line IDs for subscription. LineId:", currentLineId);
9639
+ return;
9223
9640
  }
9224
- ).subscribe((status, err) => {
9225
- if (status === REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR || status === REALTIME_SUBSCRIBE_STATES.TIMED_OUT) {
9226
- console.error(`[useLineKPIs] Subscription to ${lineMetricsTable} FAILED: ${status}`, err || "");
9227
- }
9228
- });
9229
- activeChannels.push(lmChannel);
9230
- if (companySpecificMetricsTable && !companySpecificMetricsTable.includes("unknown_company")) {
9231
- const csChannelName = `kpi-cs-${currentLineId}-${operationalDate}-${currentShiftDetails.shiftId}`.replace(/[^a-zA-Z0-9_-]/g, "");
9232
- const csChannel = supabase.channel(csChannelName).on(
9641
+ const baseFilterParts = `date=eq.${operationalDate},shift_id=eq.${currentShiftDetails.shiftId}`;
9642
+ const lineIdFilterPart = `line_id=in.(${targetLineIds.join(",")})`;
9643
+ const lineMetricsFilter = `${baseFilterParts},${lineIdFilterPart}`;
9644
+ const companyTableFilter = `${baseFilterParts},${lineIdFilterPart}`;
9645
+ const lmChannelName = `kpi-lm-${currentLineId}-${operationalDate}-${currentShiftDetails.shiftId}`.replace(/[^a-zA-Z0-9_-]/g, "");
9646
+ const lmChannel = supabase.channel(lmChannelName).on(
9233
9647
  "postgres_changes",
9234
- { event: "*", schema, table: companySpecificMetricsTable, filter: companyTableFilter },
9648
+ { event: "*", schema, table: lineMetricsTable, filter: lineMetricsFilter },
9235
9649
  (payload) => {
9236
9650
  const payloadData = payload.new || payload.old;
9237
9651
  if (payloadData?.date === operationalDate && payloadData?.shift_id === currentShiftDetails.shiftId) {
@@ -9240,10 +9654,28 @@ var useLineKPIs = ({ lineId, enabled }) => {
9240
9654
  }
9241
9655
  ).subscribe((status, err) => {
9242
9656
  if (status === REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR || status === REALTIME_SUBSCRIBE_STATES.TIMED_OUT) {
9243
- console.error(`[useLineKPIs] Subscription to ${companySpecificMetricsTable} FAILED: ${status}`, err || "");
9657
+ console.error(`[useLineKPIs] Subscription to ${lineMetricsTable} FAILED: ${status}`, err || "");
9244
9658
  }
9245
9659
  });
9246
- activeChannels.push(csChannel);
9660
+ activeChannels.push(lmChannel);
9661
+ if (companySpecificMetricsTable && !companySpecificMetricsTable.includes("unknown_company")) {
9662
+ const csChannelName = `kpi-cs-${currentLineId}-${operationalDate}-${currentShiftDetails.shiftId}`.replace(/[^a-zA-Z0-9_-]/g, "");
9663
+ const csChannel = supabase.channel(csChannelName).on(
9664
+ "postgres_changes",
9665
+ { event: "*", schema, table: companySpecificMetricsTable, filter: companyTableFilter },
9666
+ (payload) => {
9667
+ const payloadData = payload.new || payload.old;
9668
+ if (payloadData?.date === operationalDate && payloadData?.shift_id === currentShiftDetails.shiftId) {
9669
+ queueUpdateRef.current?.();
9670
+ }
9671
+ }
9672
+ ).subscribe((status, err) => {
9673
+ if (status === REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR || status === REALTIME_SUBSCRIBE_STATES.TIMED_OUT) {
9674
+ console.error(`[useLineKPIs] Subscription to ${companySpecificMetricsTable} FAILED: ${status}`, err || "");
9675
+ }
9676
+ });
9677
+ activeChannels.push(csChannel);
9678
+ }
9247
9679
  }
9248
9680
  return () => {
9249
9681
  activeChannels.forEach((ch) => supabase.removeChannel(ch).catch((err) => console.error("[useLineKPIs] Error removing KPI channel:", err)));
@@ -9251,7 +9683,7 @@ var useLineKPIs = ({ lineId, enabled }) => {
9251
9683
  clearTimeout(updateTimeoutRef.current);
9252
9684
  }
9253
9685
  };
9254
- }, [lineId, supabase, entityConfig, schema, lineMetricsTable, companySpecificMetricsTable, timezone, shiftConfig, isShiftConfigLoading]);
9686
+ }, [lineId, supabase, entityConfig, schema, lineMetricsTable, companySpecificMetricsTable, timezone, shiftConfig, staticShiftConfig, shiftGroups, isShiftConfigLoading]);
9255
9687
  return {
9256
9688
  kpis,
9257
9689
  isLoading,
@@ -10924,10 +11356,26 @@ var useAllWorkspaceMetrics = (options) => {
10924
11356
  const dateTimeConfig = useDateTimeConfig();
10925
11357
  const staticShiftConfig = useShiftConfig();
10926
11358
  const timezone = useAppTimezone();
10927
- const configuredLineIds = useMemo(() => getConfiguredLineIds(entityConfig), [entityConfig]);
10928
- const representativeLineId = options?.allowedLineIds?.[0] || configuredLineIds[0];
10929
- const { shiftConfig: dynamicShiftConfig, isLoading: isShiftConfigLoading } = useDynamicShiftConfig(representativeLineId);
10930
- const shiftConfig = dynamicShiftConfig || staticShiftConfig;
11359
+ const configuredLineIds = useMemo(() => {
11360
+ const allLineIds = getConfiguredLineIds(entityConfig);
11361
+ if (options?.allowedLineIds) {
11362
+ return allLineIds.filter((id3) => options.allowedLineIds.includes(id3));
11363
+ }
11364
+ return allLineIds;
11365
+ }, [entityConfig, options?.allowedLineIds]);
11366
+ const {
11367
+ shiftConfigMap: multiLineShiftConfigMap,
11368
+ isLoading: isShiftConfigLoading
11369
+ } = useMultiLineShiftConfigs(configuredLineIds, staticShiftConfig);
11370
+ const shiftGroups = useMemo(() => {
11371
+ if (isShiftConfigLoading || multiLineShiftConfigMap.size === 0) return [];
11372
+ return groupLinesByShift(multiLineShiftConfigMap, timezone || dateTimeConfig.defaultTimezone || "Asia/Kolkata");
11373
+ }, [isShiftConfigLoading, multiLineShiftConfigMap, timezone, dateTimeConfig.defaultTimezone]);
11374
+ console.log(`[useAllWorkspaceMetrics] \u{1F4CA} Shift groups:`, shiftGroups.map((g) => ({
11375
+ shiftId: g.shiftId,
11376
+ date: g.date,
11377
+ lineIds: g.lineIds
11378
+ })));
10931
11379
  const supabase = useSupabase();
10932
11380
  const [workspaces, setWorkspaces] = useState([]);
10933
11381
  const [loading, setLoading] = useState(true);
@@ -10935,19 +11383,29 @@ var useAllWorkspaceMetrics = (options) => {
10935
11383
  const [initialized, setInitialized] = useState(false);
10936
11384
  const fetchTimeoutRef = useRef(null);
10937
11385
  const isFetchingRef = useRef(false);
10938
- const queryShiftId = useMemo(() => {
11386
+ const hasSpecificDateShift = options?.initialDate !== void 0 || options?.initialShiftId !== void 0;
11387
+ const fallbackQueryShiftId = useMemo(() => {
10939
11388
  if (options?.initialShiftId !== void 0) {
10940
11389
  return options.initialShiftId;
10941
11390
  }
11391
+ if (shiftGroups.length > 0) {
11392
+ return shiftGroups[0].shiftId;
11393
+ }
10942
11394
  const currentShift = getCurrentShift(
10943
11395
  timezone || dateTimeConfig.defaultTimezone || "Asia/Kolkata",
10944
- shiftConfig
11396
+ staticShiftConfig
10945
11397
  );
10946
11398
  return currentShift.shiftId;
10947
- }, [options?.initialShiftId, timezone, dateTimeConfig.defaultTimezone, shiftConfig]);
10948
- const queryDate = useMemo(() => {
10949
- return options?.initialDate || getOperationalDate(timezone || dateTimeConfig.defaultTimezone || "UTC");
10950
- }, [options?.initialDate, timezone, dateTimeConfig.defaultTimezone]);
11399
+ }, [options?.initialShiftId, shiftGroups, timezone, dateTimeConfig.defaultTimezone, staticShiftConfig]);
11400
+ const fallbackQueryDate = useMemo(() => {
11401
+ if (options?.initialDate) {
11402
+ return options.initialDate;
11403
+ }
11404
+ if (shiftGroups.length > 0) {
11405
+ return shiftGroups[0].date;
11406
+ }
11407
+ return getOperationalDate(timezone || dateTimeConfig.defaultTimezone || "UTC");
11408
+ }, [options?.initialDate, shiftGroups, timezone, dateTimeConfig.defaultTimezone]);
10951
11409
  const metricsTable = useMemo(() => {
10952
11410
  const companyId = entityConfig.companyId;
10953
11411
  if (!companyId) return "";
@@ -10965,49 +11423,99 @@ var useAllWorkspaceMetrics = (options) => {
10965
11423
  }
10966
11424
  setError(null);
10967
11425
  try {
10968
- const allConfiguredLineIds = getConfiguredLineIds(entityConfig);
10969
- const configuredLineIds2 = options?.allowedLineIds ? allConfiguredLineIds.filter((id3) => options.allowedLineIds.includes(id3)) : allConfiguredLineIds;
10970
- let enabledWorkspaceIds = [];
10971
- for (const lineId of configuredLineIds2) {
10972
- const workspaces2 = await workspaceService.getWorkspaces(lineId);
10973
- const enabledIds = workspaces2.filter((ws) => ws.enable === true).map((ws) => ws.id);
10974
- enabledWorkspaceIds.push(...enabledIds);
10975
- }
10976
- if (enabledWorkspaceIds.length === 0) {
11426
+ const lineWorkspaceMap = /* @__PURE__ */ new Map();
11427
+ let allEnabledWorkspaceIds = [];
11428
+ for (const lineId of configuredLineIds) {
11429
+ const lineWorkspaces = await workspaceService.getWorkspaces(lineId);
11430
+ const enabledIds = lineWorkspaces.filter((ws) => ws.enable === true).map((ws) => ws.id);
11431
+ lineWorkspaceMap.set(lineId, enabledIds);
11432
+ allEnabledWorkspaceIds.push(...enabledIds);
11433
+ }
11434
+ if (allEnabledWorkspaceIds.length === 0) {
10977
11435
  setWorkspaces([]);
10978
11436
  setInitialized(true);
10979
11437
  setLoading(false);
10980
11438
  return;
10981
11439
  }
10982
- const { data, error: fetchError } = await supabase.from(metricsTable).select(`
10983
- workspace_name,
10984
- total_output,
10985
- avg_pph,
10986
- efficiency,
10987
- workspace_id,
10988
- avg_cycle_time,
10989
- performance_score,
10990
- trend_score,
10991
- line_id,
10992
- total_day_output
10993
- `).eq("date", queryDate).eq("shift_id", queryShiftId).in("workspace_id", enabledWorkspaceIds).order("efficiency", { ascending: false });
10994
- if (fetchError) throw fetchError;
10995
- const transformedData = (data || []).map((item) => ({
10996
- company_id: entityConfig.companyId || "unknown",
10997
- line_id: item.line_id,
10998
- shift_id: queryShiftId,
10999
- date: queryDate,
11000
- workspace_uuid: item.workspace_id,
11001
- workspace_name: item.workspace_name,
11002
- action_count: item.total_output || 0,
11003
- pph: item.avg_pph || 0,
11004
- performance_score: item.performance_score || 0,
11005
- avg_cycle_time: item.avg_cycle_time || 0,
11006
- trend: item.trend_score === 1 ? 2 : 0,
11007
- predicted_output: 0,
11008
- efficiency: item.efficiency || 0,
11009
- action_threshold: item.total_day_output || 0
11010
- }));
11440
+ let transformedData = [];
11441
+ if (hasSpecificDateShift) {
11442
+ console.log(`[useAllWorkspaceMetrics] \u{1F4CA} Using specific date/shift: ${fallbackQueryDate} / ${fallbackQueryShiftId}`);
11443
+ const { data, error: fetchError } = await supabase.from(metricsTable).select(`
11444
+ workspace_name,
11445
+ total_output,
11446
+ avg_pph,
11447
+ efficiency,
11448
+ workspace_id,
11449
+ avg_cycle_time,
11450
+ performance_score,
11451
+ trend_score,
11452
+ line_id,
11453
+ total_day_output
11454
+ `).eq("date", fallbackQueryDate).eq("shift_id", fallbackQueryShiftId).in("workspace_id", allEnabledWorkspaceIds).order("efficiency", { ascending: false });
11455
+ if (fetchError) throw fetchError;
11456
+ transformedData = (data || []).map((item) => ({
11457
+ company_id: entityConfig.companyId || "unknown",
11458
+ line_id: item.line_id,
11459
+ shift_id: fallbackQueryShiftId,
11460
+ date: fallbackQueryDate,
11461
+ workspace_uuid: item.workspace_id,
11462
+ workspace_name: item.workspace_name,
11463
+ action_count: item.total_output || 0,
11464
+ pph: item.avg_pph || 0,
11465
+ performance_score: item.performance_score || 0,
11466
+ avg_cycle_time: item.avg_cycle_time || 0,
11467
+ trend: item.trend_score === 1 ? 2 : 0,
11468
+ predicted_output: 0,
11469
+ efficiency: item.efficiency || 0,
11470
+ action_threshold: item.total_day_output || 0
11471
+ }));
11472
+ } else if (shiftGroups.length > 0) {
11473
+ console.log(`[useAllWorkspaceMetrics] \u{1F3ED} Fetching per-shift: ${shiftGroups.length} group(s)`);
11474
+ const queryPromises = shiftGroups.map(async (group) => {
11475
+ const groupWorkspaceIds = group.lineIds.flatMap(
11476
+ (lineId) => lineWorkspaceMap.get(lineId) || []
11477
+ );
11478
+ if (groupWorkspaceIds.length === 0) {
11479
+ return [];
11480
+ }
11481
+ console.log(`[useAllWorkspaceMetrics] \u{1F4CA} Fetching shift ${group.shiftId} (${group.shiftName}):`, {
11482
+ lineIds: group.lineIds,
11483
+ date: group.date,
11484
+ workspaceCount: groupWorkspaceIds.length
11485
+ });
11486
+ const { data, error: fetchError } = await supabase.from(metricsTable).select(`
11487
+ workspace_name,
11488
+ total_output,
11489
+ avg_pph,
11490
+ efficiency,
11491
+ workspace_id,
11492
+ avg_cycle_time,
11493
+ performance_score,
11494
+ trend_score,
11495
+ line_id,
11496
+ total_day_output
11497
+ `).eq("date", group.date).eq("shift_id", group.shiftId).in("workspace_id", groupWorkspaceIds);
11498
+ if (fetchError) throw fetchError;
11499
+ return (data || []).map((item) => ({
11500
+ company_id: entityConfig.companyId || "unknown",
11501
+ line_id: item.line_id,
11502
+ shift_id: group.shiftId,
11503
+ date: group.date,
11504
+ workspace_uuid: item.workspace_id,
11505
+ workspace_name: item.workspace_name,
11506
+ action_count: item.total_output || 0,
11507
+ pph: item.avg_pph || 0,
11508
+ performance_score: item.performance_score || 0,
11509
+ avg_cycle_time: item.avg_cycle_time || 0,
11510
+ trend: item.trend_score === 1 ? 2 : 0,
11511
+ predicted_output: 0,
11512
+ efficiency: item.efficiency || 0,
11513
+ action_threshold: item.total_day_output || 0
11514
+ }));
11515
+ });
11516
+ const results = await Promise.all(queryPromises);
11517
+ transformedData = results.flat().sort((a, b) => (b.efficiency || 0) - (a.efficiency || 0));
11518
+ }
11011
11519
  setWorkspaces((prevWorkspaces) => {
11012
11520
  if (prevWorkspaces.length !== transformedData.length) {
11013
11521
  return transformedData;
@@ -11029,11 +11537,19 @@ var useAllWorkspaceMetrics = (options) => {
11029
11537
  setLoading(false);
11030
11538
  isFetchingRef.current = false;
11031
11539
  }
11032
- }, [queryDate, queryShiftId, metricsTable, supabase, entityConfig.companyId, entityConfig, options?.allowedLineIds, options?.enabled, isShiftConfigLoading]);
11540
+ }, [fallbackQueryDate, fallbackQueryShiftId, hasSpecificDateShift, shiftGroups, metricsTable, supabase, entityConfig.companyId, configuredLineIds, options?.enabled, isShiftConfigLoading]);
11033
11541
  useEffect(() => {
11034
11542
  if (!initialized) {
11035
11543
  fetchWorkspaceMetrics();
11036
11544
  }
11545
+ const validDateShiftCombos = /* @__PURE__ */ new Set();
11546
+ if (hasSpecificDateShift) {
11547
+ validDateShiftCombos.add(`${fallbackQueryDate}-${fallbackQueryShiftId}`);
11548
+ } else {
11549
+ shiftGroups.forEach((group) => {
11550
+ validDateShiftCombos.add(`${group.date}-${group.shiftId}`);
11551
+ });
11552
+ }
11037
11553
  const setupSubscription = () => {
11038
11554
  const channel2 = supabase.channel(`all-workspace-metrics-${Date.now()}`).on(
11039
11555
  "postgres_changes",
@@ -11044,7 +11560,8 @@ var useAllWorkspaceMetrics = (options) => {
11044
11560
  },
11045
11561
  async (payload) => {
11046
11562
  const data = payload.new || payload.old;
11047
- if (data?.date !== queryDate || data?.shift_id !== queryShiftId) {
11563
+ const dataKey = `${data?.date}-${data?.shift_id}`;
11564
+ if (!validDateShiftCombos.has(dataKey)) {
11048
11565
  return;
11049
11566
  }
11050
11567
  if (fetchTimeoutRef.current) {
@@ -11070,10 +11587,10 @@ var useAllWorkspaceMetrics = (options) => {
11070
11587
  supabase.removeChannel(channel);
11071
11588
  }
11072
11589
  };
11073
- }, [queryDate, queryShiftId, metricsTable, fetchWorkspaceMetrics, initialized, supabase, schema, entityConfig.companyId]);
11590
+ }, [fallbackQueryDate, fallbackQueryShiftId, hasSpecificDateShift, shiftGroups, metricsTable, fetchWorkspaceMetrics, initialized, supabase, schema, entityConfig.companyId]);
11074
11591
  useEffect(() => {
11075
11592
  setInitialized(false);
11076
- }, [queryDate, queryShiftId]);
11593
+ }, [fallbackQueryDate, fallbackQueryShiftId, shiftGroups]);
11077
11594
  const refreshWorkspaces = useCallback(() => fetchWorkspaceMetrics(), [fetchWorkspaceMetrics]);
11078
11595
  return useMemo(
11079
11596
  () => ({ workspaces, loading, error, refreshWorkspaces }),
@@ -11562,127 +12079,6 @@ function useClipTypesWithCounts(workspaceId, date, shiftId, totalOutput, options
11562
12079
  counts
11563
12080
  };
11564
12081
  }
11565
- var useMultiLineShiftConfigs = (lineIds, fallbackConfig) => {
11566
- const [shiftConfigMap, setShiftConfigMap] = useState(/* @__PURE__ */ new Map());
11567
- const [isLoading, setIsLoading] = useState(true);
11568
- const [error, setError] = useState(null);
11569
- const supabase = useSupabase();
11570
- const lineIdsKey = useMemo(() => lineIds.sort().join(","), [lineIds]);
11571
- useEffect(() => {
11572
- if (!lineIds || lineIds.length === 0) {
11573
- setShiftConfigMap(/* @__PURE__ */ new Map());
11574
- setIsLoading(false);
11575
- return;
11576
- }
11577
- const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
11578
- const validLineIds = lineIds.filter((id3) => uuidRegex.test(id3));
11579
- if (validLineIds.length === 0) {
11580
- console.warn("[useMultiLineShiftConfigs] No valid line IDs provided");
11581
- setShiftConfigMap(/* @__PURE__ */ new Map());
11582
- setIsLoading(false);
11583
- return;
11584
- }
11585
- let mounted = true;
11586
- const calculateBreakDuration2 = (startTime, endTime) => {
11587
- const [sh, sm] = startTime.split(":").map(Number);
11588
- const [eh, em] = endTime.split(":").map(Number);
11589
- let startMinutes = sh * 60 + sm;
11590
- let endMinutes = eh * 60 + em;
11591
- if (endMinutes < startMinutes) {
11592
- endMinutes += 24 * 60;
11593
- }
11594
- return endMinutes - startMinutes;
11595
- };
11596
- const stripSeconds = (timeStr) => {
11597
- if (!timeStr) return timeStr;
11598
- return timeStr.substring(0, 5);
11599
- };
11600
- const buildShiftConfigFromRows = (shifts, fallback) => {
11601
- const mapped = shifts.map((shift) => ({
11602
- shiftId: shift.shift_id,
11603
- shiftName: shift.shift_name || `Shift ${shift.shift_id}`,
11604
- startTime: stripSeconds(shift.start_time),
11605
- endTime: stripSeconds(shift.end_time),
11606
- breaks: Array.isArray(shift.breaks) ? shift.breaks.map((b) => ({
11607
- startTime: b.start || b.startTime || "00:00",
11608
- endTime: b.end || b.endTime || "00:00",
11609
- duration: calculateBreakDuration2(
11610
- b.start || b.startTime || "00:00",
11611
- b.end || b.endTime || "00:00"
11612
- ),
11613
- remarks: b.remarks || b.name || ""
11614
- })) : [],
11615
- timezone: shift.timezone
11616
- }));
11617
- const day = mapped.find((s) => s.shiftId === 0);
11618
- const night = mapped.find((s) => s.shiftId === 1);
11619
- return {
11620
- shifts: mapped,
11621
- timezone: mapped[0]?.timezone,
11622
- dayShift: day ? { id: day.shiftId, startTime: day.startTime, endTime: day.endTime, name: day.shiftName } : fallback?.dayShift,
11623
- nightShift: night ? { id: night.shiftId, startTime: night.startTime, endTime: night.endTime, name: night.shiftName } : fallback?.nightShift,
11624
- transitionPeriodMinutes: fallback?.transitionPeriodMinutes || 0
11625
- };
11626
- };
11627
- const fetchAllConfigs = async () => {
11628
- try {
11629
- setIsLoading(true);
11630
- setError(null);
11631
- console.log(`[useMultiLineShiftConfigs] Fetching shift configs for ${validLineIds.length} lines`);
11632
- const { data, error: fetchError } = await supabase.from("line_operating_hours").select("line_id, shift_id, shift_name, start_time, end_time, breaks, timezone").in("line_id", validLineIds);
11633
- if (fetchError) {
11634
- console.error("[useMultiLineShiftConfigs] Error fetching shift configs:", fetchError);
11635
- throw new Error(`Failed to fetch shift configs: ${fetchError.message}`);
11636
- }
11637
- const lineShiftsMap = /* @__PURE__ */ new Map();
11638
- data?.forEach((row) => {
11639
- if (!lineShiftsMap.has(row.line_id)) {
11640
- lineShiftsMap.set(row.line_id, []);
11641
- }
11642
- lineShiftsMap.get(row.line_id).push(row);
11643
- });
11644
- const configMap = /* @__PURE__ */ new Map();
11645
- lineShiftsMap.forEach((shifts, lineId) => {
11646
- const config = buildShiftConfigFromRows(shifts, fallbackConfig);
11647
- configMap.set(lineId, config);
11648
- });
11649
- validLineIds.forEach((lineId) => {
11650
- if (!configMap.has(lineId) && fallbackConfig) {
11651
- console.warn(`[useMultiLineShiftConfigs] No config found for line ${lineId}, using fallback`);
11652
- configMap.set(lineId, fallbackConfig);
11653
- }
11654
- });
11655
- console.log(`[useMultiLineShiftConfigs] Built configs for ${configMap.size} lines`);
11656
- if (mounted) {
11657
- setShiftConfigMap(configMap);
11658
- setIsLoading(false);
11659
- }
11660
- } catch (err) {
11661
- console.error("[useMultiLineShiftConfigs] Error:", err);
11662
- if (mounted) {
11663
- setError(err instanceof Error ? err.message : "Failed to fetch shift configs");
11664
- if (fallbackConfig) {
11665
- const fallbackMap = /* @__PURE__ */ new Map();
11666
- validLineIds.forEach((lineId) => {
11667
- fallbackMap.set(lineId, fallbackConfig);
11668
- });
11669
- setShiftConfigMap(fallbackMap);
11670
- }
11671
- setIsLoading(false);
11672
- }
11673
- }
11674
- };
11675
- fetchAllConfigs();
11676
- return () => {
11677
- mounted = false;
11678
- };
11679
- }, [lineIdsKey, supabase]);
11680
- return {
11681
- shiftConfigMap,
11682
- isLoading,
11683
- error
11684
- };
11685
- };
11686
12082
  var MAX_RETRIES = 10;
11687
12083
  var RETRY_DELAY = 500;
11688
12084
  function useNavigation(customNavigate) {
@@ -45237,6 +45633,10 @@ function HomeView({
45237
45633
  const [diagnosisModalOpen, setDiagnosisModalOpen] = useState(false);
45238
45634
  const [diagnoses, setDiagnoses] = useState([]);
45239
45635
  const timezone = useAppTimezone();
45636
+ const { shiftConfigMap: lineShiftConfigs } = useMultiLineShiftConfigs(
45637
+ allLineIds,
45638
+ dashboardConfig?.shiftConfig
45639
+ );
45240
45640
  useEffect(() => {
45241
45641
  const initDisplayNames = async () => {
45242
45642
  try {
@@ -45334,12 +45734,18 @@ function HomeView({
45334
45734
  label: "Diagnose",
45335
45735
  onClick: async () => {
45336
45736
  console.log("\u{1F50D} Investigating bottleneck:", bottleneck.log_number);
45337
- const operationalDate = getOperationalDate(
45338
- timezone || dashboardConfig.dateTimeConfig?.defaultTimezone || "UTC"
45339
- );
45340
45737
  const tz = timezone || dashboardConfig.dateTimeConfig?.defaultTimezone || "UTC";
45341
- const shiftResult = getCurrentShift(tz, dashboardConfig?.shiftConfig);
45738
+ const lineShiftConfig = bottleneck.line_id && lineShiftConfigs.get(bottleneck.line_id);
45739
+ const effectiveShiftConfig = lineShiftConfig || dashboardConfig?.shiftConfig;
45740
+ const shiftResult = getCurrentShift(tz, effectiveShiftConfig);
45342
45741
  const currentShift = shiftResult.shiftId;
45742
+ const operationalDate = shiftResult.date;
45743
+ console.log("\u{1F550} Using shift config for line:", {
45744
+ lineId: bottleneck.line_id,
45745
+ hasLineConfig: !!lineShiftConfig,
45746
+ shiftId: currentShift,
45747
+ date: operationalDate
45748
+ });
45343
45749
  console.log("\u{1F3AC} [Investigate] Opening clips modal with data:", {
45344
45750
  workspaceId: bottleneck.workspace_id,
45345
45751
  ticketId: bottleneck.id,
@@ -45409,12 +45815,18 @@ function HomeView({
45409
45815
  state,
45410
45816
  priority: bottleneck.priority
45411
45817
  });
45412
- const operationalDate = getOperationalDate(
45413
- timezone || dashboardConfig.dateTimeConfig?.defaultTimezone || "UTC"
45414
- );
45415
45818
  const tz = timezone || dashboardConfig.dateTimeConfig?.defaultTimezone || "UTC";
45416
- const shiftResult = getCurrentShift(tz, dashboardConfig?.shiftConfig);
45819
+ const lineShiftConfig = bottleneck.line_id && lineShiftConfigs.get(bottleneck.line_id);
45820
+ const effectiveShiftConfig = lineShiftConfig || dashboardConfig?.shiftConfig;
45821
+ const shiftResult = getCurrentShift(tz, effectiveShiftConfig);
45417
45822
  const currentShift = shiftResult.shiftId;
45823
+ const operationalDate = shiftResult.date;
45824
+ console.log("\u{1F550} Using shift config for line:", {
45825
+ lineId: bottleneck.line_id,
45826
+ hasLineConfig: !!lineShiftConfig,
45827
+ shiftId: currentShift,
45828
+ date: operationalDate
45829
+ });
45418
45830
  setBottleneckModalData({
45419
45831
  workspaceId: bottleneck.workspace_id,
45420
45832
  workspaceName: bottleneck_workspace.workspace_name || "Unknown Workspace",
@@ -45471,7 +45883,7 @@ function HomeView({
45471
45883
  };
45472
45884
  setBottleneckNotification(errorNotification);
45473
45885
  }
45474
- }, [notificationService, timezone, dashboardConfig]);
45886
+ }, [notificationService, timezone, dashboardConfig, lineShiftConfigs]);
45475
45887
  useEffect(() => {
45476
45888
  const ticketsEnabled = dashboardConfig?.ticketsConfig?.enabled ?? true;
45477
45889
  if (!ticketsEnabled) {
@@ -47414,6 +47826,7 @@ var LeaderboardDetailView = memo(({
47414
47826
  const entityConfig = useEntityConfig();
47415
47827
  const [sortAscending, setSortAscending] = useState(false);
47416
47828
  const timezone = useAppTimezone();
47829
+ const staticShiftConfig = useShiftConfig();
47417
47830
  const [isMobile, setIsMobile] = useState(false);
47418
47831
  React24__default.useEffect(() => {
47419
47832
  const checkMobile = () => setIsMobile(window.innerWidth < 640);
@@ -47442,9 +47855,27 @@ var LeaderboardDetailView = memo(({
47442
47855
  () => typeof shift === "number" ? shift : typeof shift === "string" ? parseInt(shift) : void 0,
47443
47856
  [shift]
47444
47857
  );
47445
- const availableLineId = Object.keys(configuredLineNames)[0];
47446
- const effectiveLineId = lineId || userAccessibleLineIds?.[0] || availableLineId;
47447
- const { shiftConfig, isLoading: isShiftConfigLoading } = useDynamicShiftConfig(effectiveLineId);
47858
+ const configuredLineIds = useMemo(() => {
47859
+ const allLineIds = getConfiguredLineIds(entityConfig);
47860
+ if (userAccessibleLineIds) {
47861
+ return allLineIds.filter((id3) => userAccessibleLineIds.includes(id3));
47862
+ }
47863
+ return allLineIds;
47864
+ }, [entityConfig, userAccessibleLineIds]);
47865
+ const shouldFetchShiftConfigs = !date && shiftId === void 0;
47866
+ const {
47867
+ shiftConfigMap: multiLineShiftConfigMap,
47868
+ isLoading: isShiftConfigLoading
47869
+ } = useMultiLineShiftConfigs(
47870
+ shouldFetchShiftConfigs ? configuredLineIds : [],
47871
+ staticShiftConfig
47872
+ );
47873
+ const shiftGroups = useMemo(() => {
47874
+ if (!shouldFetchShiftConfigs || isShiftConfigLoading || multiLineShiftConfigMap.size === 0) {
47875
+ return [];
47876
+ }
47877
+ return groupLinesByShift(multiLineShiftConfigMap, timezone || "Asia/Kolkata");
47878
+ }, [shouldFetchShiftConfigs, isShiftConfigLoading, multiLineShiftConfigMap, timezone]);
47448
47879
  const {
47449
47880
  workspaces,
47450
47881
  loading: workspacesLoading,
@@ -47452,21 +47883,28 @@ var LeaderboardDetailView = memo(({
47452
47883
  refreshWorkspaces
47453
47884
  } = useAllWorkspaceMetrics({
47454
47885
  initialDate: date,
47455
- initialShiftId: typeof shift === "number" ? shift : typeof shift === "string" ? parseInt(shift) : void 0,
47886
+ initialShiftId: shiftId,
47456
47887
  allowedLineIds: userAccessibleLineIds,
47457
- // Filter to user's accessible lines only
47458
47888
  enabled: !isShiftConfigLoading
47459
- // Pass enabled flag
47460
47889
  });
47461
47890
  const getShiftName = useCallback((shiftId2) => {
47462
47891
  if (shiftId2 !== void 0) {
47463
- return getShiftNameById(shiftId2, timezone || "Asia/Kolkata", shiftConfig);
47892
+ return getShiftNameById(shiftId2, timezone || "Asia/Kolkata", staticShiftConfig);
47464
47893
  }
47465
- const currentShift = getCurrentShift(timezone || "Asia/Kolkata", shiftConfig);
47466
- return currentShift.shiftName || getShiftNameById(currentShift.shiftId, timezone || "Asia/Kolkata", shiftConfig);
47467
- }, [timezone, shiftConfig]);
47894
+ if (shiftGroups.length > 1) {
47895
+ const shiftNames = shiftGroups.map((g) => g.shiftName).join(" & ");
47896
+ return shiftNames;
47897
+ } else if (shiftGroups.length === 1) {
47898
+ return shiftGroups[0].shiftName;
47899
+ }
47900
+ const currentShift = getCurrentShift(timezone || "Asia/Kolkata", staticShiftConfig);
47901
+ return currentShift.shiftName || getShiftNameById(currentShift.shiftId, timezone || "Asia/Kolkata", staticShiftConfig);
47902
+ }, [timezone, staticShiftConfig, shiftGroups]);
47468
47903
  const getShiftIcon = useCallback((shiftId2) => {
47469
- const effectiveShiftId = shiftId2 !== void 0 ? shiftId2 : getCurrentShift(timezone || "Asia/Kolkata", shiftConfig).shiftId;
47904
+ if (shiftId2 === void 0 && shiftGroups.length > 1) {
47905
+ return /* @__PURE__ */ jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" }) });
47906
+ }
47907
+ const effectiveShiftId = shiftId2 !== void 0 ? shiftId2 : shiftGroups.length === 1 ? shiftGroups[0].shiftId : getCurrentShift(timezone || "Asia/Kolkata", staticShiftConfig).shiftId;
47470
47908
  const shiftNameLower = getShiftName(effectiveShiftId).toLowerCase();
47471
47909
  if (shiftNameLower.includes("day") || shiftNameLower.includes("morning")) {
47472
47910
  return /* @__PURE__ */ jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ 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" }) });
@@ -47478,7 +47916,7 @@ var LeaderboardDetailView = memo(({
47478
47916
  return /* @__PURE__ */ jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ 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" }) });
47479
47917
  }
47480
47918
  return /* @__PURE__ */ jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" }) });
47481
- }, [getShiftName]);
47919
+ }, [getShiftName, shiftGroups, timezone, staticShiftConfig]);
47482
47920
  const formatDate2 = useCallback((date2) => {
47483
47921
  return new Intl.DateTimeFormat("en-US", {
47484
47922
  weekday: "long",
@@ -56608,4 +57046,4 @@ function shuffleArray(array) {
56608
57046
  return shuffled;
56609
57047
  }
56610
57048
 
56611
- export { ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ClipFilterProvider, CompactWorkspaceHealthCard, CongratulationsOverlay, CroppedHlsVideoPlayer, CroppedVideoPlayer, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_MAP_VIEW_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_SHIFT_DATA, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, DetailedHealthStatus, DiagnosisVideoModal, EmptyStateMessage, EncouragementOverlay, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthDateShiftSelector, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HlsVideoPlayer, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, InlineEditableText, InteractiveOnboardingTour, InvitationService, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LinesService, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlayPauseIndicator, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, S3ClipsSupabaseService as S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SessionTracker, SessionTrackingContext, SessionTrackingProvider, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UserManagementService, UserService, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyHistory, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, authCoreService, authOTPService, authRateLimitService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createInvitationService, createLinesService, createSessionTracker, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, fetchIdleTimeReasons, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatReasonLabel, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getAvailableShiftIds, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getNextUpdateInterval, getOperationalDate, getReasonColor, getS3SignedUrl, getS3VideoSrc, getShiftData, getShiftNameById, getShortShiftName, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, hasAnyShiftData, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isPrefetchError, isSafari, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, optifyeAgentClient, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, transformToChartData, updateThreadTitle, useAccessControl, useActiveBreaks, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useCompanyUsersUsage, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useIdleTimeReasons, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useMessages, useMetrics, useMultiLineShiftConfigs, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useSessionTracking, useSessionTrackingContext, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useUserUsage, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthStatus, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, useWorkspaceUptimeTimeline, userService, videoPrefetchManager, videoPreloader, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };
57049
+ export { ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ClipFilterProvider, CompactWorkspaceHealthCard, CongratulationsOverlay, CroppedHlsVideoPlayer, CroppedVideoPlayer, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_MAP_VIEW_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_SHIFT_DATA, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, DetailedHealthStatus, DiagnosisVideoModal, EmptyStateMessage, EncouragementOverlay, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthDateShiftSelector, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HlsVideoPlayer, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, InlineEditableText, InteractiveOnboardingTour, InvitationService, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LinesService, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlayPauseIndicator, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, S3ClipsSupabaseService as S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SessionTracker, SessionTrackingContext, SessionTrackingProvider, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UserManagementService, UserService, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyHistory, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, areAllLinesOnSameShift, authCoreService, authOTPService, authRateLimitService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createInvitationService, createLinesService, createSessionTracker, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, fetchIdleTimeReasons, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatReasonLabel, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getAvailableShiftIds, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentShiftForLine, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getNextUpdateInterval, getOperationalDate, getReasonColor, getS3SignedUrl, getS3VideoSrc, getShiftData, getShiftNameById, getShortShiftName, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUniformShiftGroup, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, groupLinesByShift, hasAnyShiftData, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isPrefetchError, isSafari, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, optifyeAgentClient, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, transformToChartData, updateThreadTitle, useAccessControl, useActiveBreaks, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useCompanyUsersUsage, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useIdleTimeReasons, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useMessages, useMetrics, useMultiLineShiftConfigs, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useSessionTracking, useSessionTrackingContext, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useUserUsage, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthStatus, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, useWorkspaceUptimeTimeline, userService, videoPrefetchManager, videoPreloader, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };