@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.js CHANGED
@@ -8867,27 +8867,208 @@ var useLeaderboardMetrics = (date, shiftId, limit = 10, filter2 = "all") => {
8867
8867
  refetch: fetchLeaderboard
8868
8868
  };
8869
8869
  };
8870
+ var useMultiLineShiftConfigs = (lineIds, fallbackConfig) => {
8871
+ const [shiftConfigMap, setShiftConfigMap] = React24.useState(/* @__PURE__ */ new Map());
8872
+ const [isLoading, setIsLoading] = React24.useState(true);
8873
+ const [error, setError] = React24.useState(null);
8874
+ const supabase = useSupabase();
8875
+ const lineIdsKey = React24.useMemo(() => lineIds.sort().join(","), [lineIds]);
8876
+ React24.useEffect(() => {
8877
+ if (!lineIds || lineIds.length === 0) {
8878
+ setShiftConfigMap(/* @__PURE__ */ new Map());
8879
+ setIsLoading(false);
8880
+ return;
8881
+ }
8882
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
8883
+ const validLineIds = lineIds.filter((id3) => uuidRegex.test(id3));
8884
+ if (validLineIds.length === 0) {
8885
+ console.warn("[useMultiLineShiftConfigs] No valid line IDs provided");
8886
+ setShiftConfigMap(/* @__PURE__ */ new Map());
8887
+ setIsLoading(false);
8888
+ return;
8889
+ }
8890
+ let mounted = true;
8891
+ const calculateBreakDuration2 = (startTime, endTime) => {
8892
+ const [sh, sm] = startTime.split(":").map(Number);
8893
+ const [eh, em] = endTime.split(":").map(Number);
8894
+ let startMinutes = sh * 60 + sm;
8895
+ let endMinutes = eh * 60 + em;
8896
+ if (endMinutes < startMinutes) {
8897
+ endMinutes += 24 * 60;
8898
+ }
8899
+ return endMinutes - startMinutes;
8900
+ };
8901
+ const stripSeconds = (timeStr) => {
8902
+ if (!timeStr) return timeStr;
8903
+ return timeStr.substring(0, 5);
8904
+ };
8905
+ const buildShiftConfigFromRows = (shifts, fallback) => {
8906
+ const mapped = shifts.map((shift) => ({
8907
+ shiftId: shift.shift_id,
8908
+ shiftName: shift.shift_name || `Shift ${shift.shift_id}`,
8909
+ startTime: stripSeconds(shift.start_time),
8910
+ endTime: stripSeconds(shift.end_time),
8911
+ breaks: Array.isArray(shift.breaks) ? shift.breaks.map((b) => ({
8912
+ startTime: b.start || b.startTime || "00:00",
8913
+ endTime: b.end || b.endTime || "00:00",
8914
+ duration: calculateBreakDuration2(
8915
+ b.start || b.startTime || "00:00",
8916
+ b.end || b.endTime || "00:00"
8917
+ ),
8918
+ remarks: b.remarks || b.name || ""
8919
+ })) : [],
8920
+ timezone: shift.timezone
8921
+ }));
8922
+ const day = mapped.find((s) => s.shiftId === 0);
8923
+ const night = mapped.find((s) => s.shiftId === 1);
8924
+ return {
8925
+ shifts: mapped,
8926
+ timezone: mapped[0]?.timezone,
8927
+ dayShift: day ? { id: day.shiftId, startTime: day.startTime, endTime: day.endTime, name: day.shiftName } : fallback?.dayShift,
8928
+ nightShift: night ? { id: night.shiftId, startTime: night.startTime, endTime: night.endTime, name: night.shiftName } : fallback?.nightShift,
8929
+ transitionPeriodMinutes: fallback?.transitionPeriodMinutes || 0
8930
+ };
8931
+ };
8932
+ const fetchAllConfigs = async () => {
8933
+ try {
8934
+ setIsLoading(true);
8935
+ setError(null);
8936
+ console.log(`[useMultiLineShiftConfigs] Fetching shift configs for ${validLineIds.length} lines`);
8937
+ 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);
8938
+ if (fetchError) {
8939
+ console.error("[useMultiLineShiftConfigs] Error fetching shift configs:", fetchError);
8940
+ throw new Error(`Failed to fetch shift configs: ${fetchError.message}`);
8941
+ }
8942
+ const lineShiftsMap = /* @__PURE__ */ new Map();
8943
+ data?.forEach((row) => {
8944
+ if (!lineShiftsMap.has(row.line_id)) {
8945
+ lineShiftsMap.set(row.line_id, []);
8946
+ }
8947
+ lineShiftsMap.get(row.line_id).push(row);
8948
+ });
8949
+ const configMap = /* @__PURE__ */ new Map();
8950
+ lineShiftsMap.forEach((shifts, lineId) => {
8951
+ const config = buildShiftConfigFromRows(shifts, fallbackConfig);
8952
+ configMap.set(lineId, config);
8953
+ });
8954
+ validLineIds.forEach((lineId) => {
8955
+ if (!configMap.has(lineId) && fallbackConfig) {
8956
+ console.warn(`[useMultiLineShiftConfigs] No config found for line ${lineId}, using fallback`);
8957
+ configMap.set(lineId, fallbackConfig);
8958
+ }
8959
+ });
8960
+ console.log(`[useMultiLineShiftConfigs] Built configs for ${configMap.size} lines`);
8961
+ if (mounted) {
8962
+ setShiftConfigMap(configMap);
8963
+ setIsLoading(false);
8964
+ }
8965
+ } catch (err) {
8966
+ console.error("[useMultiLineShiftConfigs] Error:", err);
8967
+ if (mounted) {
8968
+ setError(err instanceof Error ? err.message : "Failed to fetch shift configs");
8969
+ if (fallbackConfig) {
8970
+ const fallbackMap = /* @__PURE__ */ new Map();
8971
+ validLineIds.forEach((lineId) => {
8972
+ fallbackMap.set(lineId, fallbackConfig);
8973
+ });
8974
+ setShiftConfigMap(fallbackMap);
8975
+ }
8976
+ setIsLoading(false);
8977
+ }
8978
+ }
8979
+ };
8980
+ fetchAllConfigs();
8981
+ return () => {
8982
+ mounted = false;
8983
+ };
8984
+ }, [lineIdsKey, supabase]);
8985
+ return {
8986
+ shiftConfigMap,
8987
+ isLoading,
8988
+ error
8989
+ };
8990
+ };
8991
+
8992
+ // src/lib/utils/shiftGrouping.ts
8993
+ var getCurrentShiftForLine = (lineId, shiftConfig, timezone, now2 = /* @__PURE__ */ new Date()) => {
8994
+ const currentShift = getCurrentShift(timezone, shiftConfig, now2);
8995
+ return {
8996
+ lineId,
8997
+ shiftId: currentShift.shiftId,
8998
+ date: currentShift.date,
8999
+ shiftName: currentShift.shiftName || `Shift ${currentShift.shiftId}`
9000
+ };
9001
+ };
9002
+ var groupLinesByShift = (shiftConfigMap, timezone, now2 = /* @__PURE__ */ new Date()) => {
9003
+ const lineShiftInfos = [];
9004
+ shiftConfigMap.forEach((shiftConfig, lineId) => {
9005
+ const info = getCurrentShiftForLine(lineId, shiftConfig, timezone, now2);
9006
+ lineShiftInfos.push(info);
9007
+ });
9008
+ const groupMap = /* @__PURE__ */ new Map();
9009
+ lineShiftInfos.forEach((info) => {
9010
+ const key = `${info.shiftId}-${info.date}`;
9011
+ if (!groupMap.has(key)) {
9012
+ groupMap.set(key, {
9013
+ shiftId: info.shiftId,
9014
+ date: info.date,
9015
+ shiftName: info.shiftName,
9016
+ lineIds: []
9017
+ });
9018
+ }
9019
+ groupMap.get(key).lineIds.push(info.lineId);
9020
+ });
9021
+ return Array.from(groupMap.values()).sort((a, b) => a.shiftId - b.shiftId);
9022
+ };
9023
+ var areAllLinesOnSameShift = (shiftConfigMap, timezone, now2 = /* @__PURE__ */ new Date()) => {
9024
+ if (shiftConfigMap.size <= 1) return true;
9025
+ const groups = groupLinesByShift(shiftConfigMap, timezone, now2);
9026
+ return groups.length === 1;
9027
+ };
9028
+ var getUniformShiftGroup = (shiftConfigMap, timezone, now2 = /* @__PURE__ */ new Date()) => {
9029
+ const groups = groupLinesByShift(shiftConfigMap, timezone, now2);
9030
+ return groups.length === 1 ? groups[0] : null;
9031
+ };
9032
+
9033
+ // src/lib/hooks/useDashboardMetrics.ts
8870
9034
  var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds }) => {
8871
9035
  const { supabaseUrl, supabaseKey } = useDashboardConfig();
8872
9036
  const entityConfig = useEntityConfig();
8873
9037
  const databaseConfig = useDatabaseConfig();
8874
9038
  const dateTimeConfig = useDateTimeConfig();
8875
9039
  const isFactoryView = lineId === (entityConfig.factoryViewId || "factory");
8876
- const firstLineId = React24.useMemo(() => {
8877
- const configuredLineIds = getConfiguredLineIds(entityConfig);
8878
- return configuredLineIds.length > 0 ? configuredLineIds[0] : void 0;
9040
+ const appTimezone = useAppTimezone();
9041
+ const defaultTimezone = appTimezone || dateTimeConfig?.defaultTimezone || "UTC";
9042
+ const configuredLineIds = React24.useMemo(() => {
9043
+ return getConfiguredLineIds(entityConfig);
8879
9044
  }, [entityConfig]);
8880
- const lineIdForShiftConfig = isFactoryView ? firstLineId : lineId;
8881
- const { shiftConfig, isLoading: shiftLoading, isFromDatabase } = useDynamicShiftConfig(lineIdForShiftConfig);
9045
+ const { shiftConfig: staticShiftConfig } = useDashboardConfig();
9046
+ const {
9047
+ shiftConfigMap: multiLineShiftConfigMap,
9048
+ isLoading: isMultiLineShiftConfigLoading
9049
+ } = useMultiLineShiftConfigs(
9050
+ isFactoryView ? configuredLineIds : [],
9051
+ staticShiftConfig
9052
+ );
9053
+ const {
9054
+ shiftConfig: singleLineShiftConfig,
9055
+ isLoading: isSingleLineShiftConfigLoading,
9056
+ isFromDatabase
9057
+ } = useDynamicShiftConfig(isFactoryView ? void 0 : lineId);
9058
+ const shiftLoading = isFactoryView ? isMultiLineShiftConfigLoading : isSingleLineShiftConfigLoading;
9059
+ const shiftGroups = React24.useMemo(() => {
9060
+ if (!isFactoryView) return [];
9061
+ if (isMultiLineShiftConfigLoading || multiLineShiftConfigMap.size === 0) return [];
9062
+ return groupLinesByShift(multiLineShiftConfigMap, defaultTimezone);
9063
+ }, [isFactoryView, isMultiLineShiftConfigLoading, multiLineShiftConfigMap, defaultTimezone]);
9064
+ const shiftConfig = isFactoryView ? null : singleLineShiftConfig;
8882
9065
  console.log(`[useDashboardMetrics] \u{1F3AF} Shift config for line ${lineId}:`, {
8883
9066
  isFactoryView,
8884
- lineIdForShiftConfig,
8885
- firstLineId,
8886
9067
  isFromDatabase,
8887
- shiftConfig: shiftConfig ? { shifts: shiftConfig.shifts?.length, timezone: shiftConfig.timezone } : null
9068
+ shiftConfig: shiftConfig ? { shifts: shiftConfig.shifts?.length, timezone: shiftConfig.timezone } : null,
9069
+ shiftGroupsCount: shiftGroups.length,
9070
+ shiftGroups: shiftGroups.map((g) => ({ shiftId: g.shiftId, date: g.date, lineCount: g.lineIds.length }))
8888
9071
  });
8889
- const appTimezone = useAppTimezone();
8890
- const defaultTimezone = appTimezone || dateTimeConfig?.defaultTimezone || "UTC";
8891
9072
  const configuredLineMetricsTable = databaseConfig?.tables?.lineMetrics ?? "line_metrics";
8892
9073
  const schema = databaseConfig?.schema ?? "public";
8893
9074
  const supabase = useSupabase();
@@ -8924,16 +9105,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
8924
9105
  setIsLoading(true);
8925
9106
  setError(null);
8926
9107
  try {
8927
- const currentShiftDetails = getCurrentShift(defaultTimezone, shiftConfig);
8928
- const operationalDate = getOperationalDate(defaultTimezone);
8929
- const targetLineIds = currentLineIdToUse === (entityConfig.factoryViewId || "factory") ? userAccessibleLineIds || getConfiguredLineIds(entityConfig) : [currentLineIdToUse];
8930
- if (targetLineIds.length === 0 && currentLineIdToUse === (entityConfig.factoryViewId || "factory")) {
8931
- throw new Error("Factory view selected, but no lines are configured in entityConfig.");
8932
- }
8933
- if (targetLineIds.length === 0) {
8934
- throw new Error("No target line IDs available for fetching metrics.");
8935
- }
8936
- const isFactoryView2 = currentLineIdToUse === (entityConfig.factoryViewId || "factory");
8937
9108
  const { data: { session } } = await supabase.auth.getSession();
8938
9109
  console.log("[useDashboardMetrics] Session check:", {
8939
9110
  hasSession: !!session,
@@ -8947,42 +9118,98 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
8947
9118
  if (!apiUrl) {
8948
9119
  throw new Error("Backend URL is not configured. Please set NEXT_PUBLIC_BACKEND_URL in your environment.");
8949
9120
  }
8950
- const lineIdsParam = isFactoryView2 ? `line_ids=${targetLineIds.join(",")}` : `line_id=${targetLineIds[0]}`;
8951
- const url = `${apiUrl}/api/dashboard/metrics?${lineIdsParam}&date=${operationalDate}&shift_id=${currentShiftDetails.shiftId}&company_id=${entityConfig.companyId}`;
8952
- console.log("[useDashboardMetrics] Calling backend API:", {
8953
- url,
8954
- apiUrl,
8955
- lineIdsParam,
8956
- operationalDate,
8957
- shiftId: currentShiftDetails.shiftId,
8958
- companyId: entityConfig.companyId
8959
- });
8960
- const response = await fetch(url, {
8961
- method: "GET",
8962
- headers: {
8963
- "Authorization": `Bearer ${session.access_token}`,
8964
- "Content-Type": "application/json"
8965
- }
8966
- });
8967
- console.log("[useDashboardMetrics] Response status:", response.status, response.statusText);
8968
- if (!response.ok) {
8969
- const errorText = await response.text();
8970
- console.error("[useDashboardMetrics] Backend API error response:", errorText);
8971
- throw new Error(`Backend API error (${response.status}): ${errorText}`);
9121
+ const factoryViewIdentifier = entityConfig.factoryViewId || "factory";
9122
+ const isFactory = currentLineIdToUse === factoryViewIdentifier;
9123
+ const targetLineIds = isFactory ? userAccessibleLineIds || configuredLineIds : [currentLineIdToUse];
9124
+ if (targetLineIds.length === 0) {
9125
+ throw new Error("No target line IDs available for fetching metrics.");
8972
9126
  }
8973
- const responseText = await response.text();
8974
- let backendData;
8975
- try {
8976
- backendData = JSON.parse(responseText);
8977
- } catch (parseError) {
8978
- console.error("[useDashboardMetrics] Failed to parse response as JSON. Response text:", responseText.substring(0, 500));
8979
- throw new Error(`Invalid JSON response from backend. Received: ${responseText.substring(0, 100)}...`);
9127
+ let allWorkspaceMetrics = [];
9128
+ let allLineMetrics = [];
9129
+ if (isFactory && shiftGroups.length > 0) {
9130
+ console.log(`[useDashboardMetrics] \u{1F3ED} Factory view: Fetching for ${shiftGroups.length} shift group(s)`);
9131
+ const metricsPromises = shiftGroups.map(async (group) => {
9132
+ const lineIdsParam = `line_ids=${group.lineIds.join(",")}`;
9133
+ const url = `${apiUrl}/api/dashboard/metrics?${lineIdsParam}&date=${group.date}&shift_id=${group.shiftId}&company_id=${entityConfig.companyId}`;
9134
+ console.log(`[useDashboardMetrics] \u{1F4CA} Fetching for shift ${group.shiftId} (${group.shiftName}):`, {
9135
+ lineIds: group.lineIds,
9136
+ date: group.date
9137
+ });
9138
+ const response = await fetch(url, {
9139
+ method: "GET",
9140
+ headers: {
9141
+ "Authorization": `Bearer ${session.access_token}`,
9142
+ "Content-Type": "application/json"
9143
+ }
9144
+ });
9145
+ if (!response.ok) {
9146
+ const errorText = await response.text();
9147
+ console.error(`[useDashboardMetrics] Backend API error for shift ${group.shiftId}:`, errorText);
9148
+ throw new Error(`Backend API error (${response.status}): ${errorText}`);
9149
+ }
9150
+ const responseText = await response.text();
9151
+ try {
9152
+ return JSON.parse(responseText);
9153
+ } catch (parseError) {
9154
+ console.error("[useDashboardMetrics] Failed to parse response:", responseText.substring(0, 500));
9155
+ throw new Error(`Invalid JSON response from backend`);
9156
+ }
9157
+ });
9158
+ const results = await Promise.all(metricsPromises);
9159
+ results.forEach((result) => {
9160
+ if (result.workspace_metrics) {
9161
+ allWorkspaceMetrics.push(...result.workspace_metrics);
9162
+ }
9163
+ if (result.line_metrics) {
9164
+ allLineMetrics.push(...result.line_metrics);
9165
+ }
9166
+ });
9167
+ console.log(`[useDashboardMetrics] \u{1F4CA} Merged metrics from ${results.length} shift groups:`, {
9168
+ workspaceCount: allWorkspaceMetrics.length,
9169
+ lineMetricsCount: allLineMetrics.length
9170
+ });
9171
+ } else {
9172
+ const currentShiftDetails = shiftConfig ? getCurrentShift(defaultTimezone, shiftConfig) : shiftGroups.length === 1 ? { date: shiftGroups[0].date, shiftId: shiftGroups[0].shiftId } : getCurrentShift(defaultTimezone, staticShiftConfig);
9173
+ const operationalDate = currentShiftDetails.date;
9174
+ const lineIdsParam = isFactory ? `line_ids=${targetLineIds.join(",")}` : `line_id=${targetLineIds[0]}`;
9175
+ const url = `${apiUrl}/api/dashboard/metrics?${lineIdsParam}&date=${operationalDate}&shift_id=${currentShiftDetails.shiftId}&company_id=${entityConfig.companyId}`;
9176
+ console.log("[useDashboardMetrics] Calling backend API:", {
9177
+ url,
9178
+ apiUrl,
9179
+ lineIdsParam,
9180
+ operationalDate,
9181
+ shiftId: currentShiftDetails.shiftId,
9182
+ companyId: entityConfig.companyId
9183
+ });
9184
+ const response = await fetch(url, {
9185
+ method: "GET",
9186
+ headers: {
9187
+ "Authorization": `Bearer ${session.access_token}`,
9188
+ "Content-Type": "application/json"
9189
+ }
9190
+ });
9191
+ console.log("[useDashboardMetrics] Response status:", response.status, response.statusText);
9192
+ if (!response.ok) {
9193
+ const errorText = await response.text();
9194
+ console.error("[useDashboardMetrics] Backend API error response:", errorText);
9195
+ throw new Error(`Backend API error (${response.status}): ${errorText}`);
9196
+ }
9197
+ const responseText = await response.text();
9198
+ let backendData;
9199
+ try {
9200
+ backendData = JSON.parse(responseText);
9201
+ } catch (parseError) {
9202
+ console.error("[useDashboardMetrics] Failed to parse response as JSON. Response text:", responseText.substring(0, 500));
9203
+ throw new Error(`Invalid JSON response from backend. Received: ${responseText.substring(0, 100)}...`);
9204
+ }
9205
+ console.log("[useDashboardMetrics] Backend response:", {
9206
+ workspaceCount: backendData.workspace_metrics?.length || 0,
9207
+ lineCount: backendData.line_metrics?.length || 0
9208
+ });
9209
+ allWorkspaceMetrics = backendData.workspace_metrics || [];
9210
+ allLineMetrics = backendData.line_metrics || [];
8980
9211
  }
8981
- console.log("[useDashboardMetrics] Backend response:", {
8982
- workspaceCount: backendData.workspace_metrics?.length || 0,
8983
- lineCount: backendData.line_metrics?.length || 0
8984
- });
8985
- const transformedWorkspaceData = (backendData.workspace_metrics || []).map((item) => ({
9212
+ const transformedWorkspaceData = allWorkspaceMetrics.map((item) => ({
8986
9213
  company_id: item.company_id || entityConfig.companyId,
8987
9214
  line_id: item.line_id,
8988
9215
  shift_id: item.shift_id,
@@ -9005,7 +9232,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
9005
9232
  });
9006
9233
  const newMetricsState = {
9007
9234
  workspaceMetrics: transformedWorkspaceData,
9008
- lineMetrics: backendData.line_metrics || []
9235
+ lineMetrics: allLineMetrics || []
9009
9236
  };
9010
9237
  console.log("[useDashboardMetrics] Setting metrics state:", {
9011
9238
  workspaceMetrics: newMetricsState.workspaceMetrics.length,
@@ -9026,9 +9253,12 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
9026
9253
  metrics2?.lineMetrics?.length || 0,
9027
9254
  companySpecificMetricsTable,
9028
9255
  entityConfig,
9029
- appTimezone,
9030
9256
  defaultTimezone,
9031
9257
  shiftConfig,
9258
+ shiftGroups,
9259
+ configuredLineIds,
9260
+ staticShiftConfig,
9261
+ userAccessibleLineIds,
9032
9262
  shiftLoading
9033
9263
  ]);
9034
9264
  const fetchAllMetricsRef = React24.useRef(fetchAllMetrics);
@@ -9052,32 +9282,68 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
9052
9282
  if (!currentLineIdToUse || !supabase || companySpecificMetricsTable.includes("unknown_company") || !entityConfig.companyId) {
9053
9283
  return;
9054
9284
  }
9055
- const currentShiftDetails = getCurrentShift(defaultTimezone, shiftConfig);
9056
- const operationalDateForSubscription = getOperationalDate(defaultTimezone);
9057
- const targetLineIds = currentLineIdToUse === (entityConfig.factoryViewId || "factory") ? userAccessibleLineIds || getConfiguredLineIds(entityConfig) : [currentLineIdToUse];
9058
- if (targetLineIds.length === 0) return;
9059
- const wsMetricsFilter = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
9060
- const lineMetricsFilter = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
9285
+ const factoryViewIdentifier = entityConfig.factoryViewId || "factory";
9286
+ const isFactory = currentLineIdToUse === factoryViewIdentifier;
9061
9287
  const channels = [];
9062
- const createSubscription = (table, filter2, channelNameBase, callback) => {
9063
- const channelName = `${channelNameBase}-${Date.now()}`.replace(/[^a-zA-Z0-9_-]/g, "");
9064
- const channel = supabase.channel(channelName).on(
9065
- "postgres_changes",
9066
- { event: "*", schema, table, filter: filter2 },
9067
- (payload) => {
9068
- const payloadData = payload.new;
9069
- if (payloadData?.date === operationalDateForSubscription && payloadData?.shift_id === currentShiftDetails.shiftId) {
9070
- callback();
9288
+ if (isFactory && shiftGroups.length > 0) {
9289
+ console.log(`[useDashboardMetrics] \u{1F4E1} Setting up subscriptions for ${shiftGroups.length} shift group(s)`);
9290
+ shiftGroups.forEach((group, index) => {
9291
+ const baseFilterParts = `date=eq.${group.date},shift_id=eq.${group.shiftId}`;
9292
+ const lineIdFilterPart = `line_id=in.(${group.lineIds.map((id3) => `"${id3}"`).join(",")})`;
9293
+ const filter2 = `${baseFilterParts},${lineIdFilterPart}`;
9294
+ const wsChannelName = `dashboard-ws-g${index}-${group.date}-${group.shiftId}`.replace(/[^a-zA-Z0-9_-]/g, "");
9295
+ const wsChannel = supabase.channel(wsChannelName).on(
9296
+ "postgres_changes",
9297
+ { event: "*", schema, table: companySpecificMetricsTable, filter: filter2 },
9298
+ (payload) => {
9299
+ const payloadData = payload.new || payload.old;
9300
+ if (payloadData?.date === group.date && payloadData?.shift_id === group.shiftId) {
9301
+ queueUpdate();
9302
+ }
9071
9303
  }
9072
- }
9073
- ).subscribe();
9074
- channels.push(channel);
9075
- };
9076
- createSubscription(companySpecificMetricsTable, wsMetricsFilter, "dashboard-ws-metrics", queueUpdate);
9077
- createSubscription(configuredLineMetricsTable, lineMetricsFilter, "dashboard-line-metrics", () => {
9078
- queueUpdate();
9079
- onLineMetricsUpdateRef.current?.();
9080
- });
9304
+ ).subscribe();
9305
+ channels.push(wsChannel);
9306
+ const lmChannelName = `dashboard-lm-g${index}-${group.date}-${group.shiftId}`.replace(/[^a-zA-Z0-9_-]/g, "");
9307
+ const lmChannel = supabase.channel(lmChannelName).on(
9308
+ "postgres_changes",
9309
+ { event: "*", schema, table: configuredLineMetricsTable, filter: filter2 },
9310
+ (payload) => {
9311
+ const payloadData = payload.new || payload.old;
9312
+ if (payloadData?.date === group.date && payloadData?.shift_id === group.shiftId) {
9313
+ queueUpdate();
9314
+ onLineMetricsUpdateRef.current?.();
9315
+ }
9316
+ }
9317
+ ).subscribe();
9318
+ channels.push(lmChannel);
9319
+ });
9320
+ } else {
9321
+ const currentShiftDetails = shiftConfig ? getCurrentShift(defaultTimezone, shiftConfig) : getCurrentShift(defaultTimezone, staticShiftConfig);
9322
+ const operationalDateForSubscription = currentShiftDetails.date;
9323
+ const targetLineIds = [currentLineIdToUse];
9324
+ if (targetLineIds.length === 0) return;
9325
+ const wsMetricsFilter = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
9326
+ const lineMetricsFilter = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
9327
+ const createSubscription = (table, filter2, channelNameBase, callback) => {
9328
+ const channelName = `${channelNameBase}-${Date.now()}`.replace(/[^a-zA-Z0-9_-]/g, "");
9329
+ const channel = supabase.channel(channelName).on(
9330
+ "postgres_changes",
9331
+ { event: "*", schema, table, filter: filter2 },
9332
+ (payload) => {
9333
+ const payloadData = payload.new;
9334
+ if (payloadData?.date === operationalDateForSubscription && payloadData?.shift_id === currentShiftDetails.shiftId) {
9335
+ callback();
9336
+ }
9337
+ }
9338
+ ).subscribe();
9339
+ channels.push(channel);
9340
+ };
9341
+ createSubscription(companySpecificMetricsTable, wsMetricsFilter, "dashboard-ws-metrics", queueUpdate);
9342
+ createSubscription(configuredLineMetricsTable, lineMetricsFilter, "dashboard-line-metrics", () => {
9343
+ queueUpdate();
9344
+ onLineMetricsUpdateRef.current?.();
9345
+ });
9346
+ }
9081
9347
  return () => {
9082
9348
  channels.forEach((channel) => {
9083
9349
  supabase?.removeChannel(channel);
@@ -9091,9 +9357,10 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
9091
9357
  schema,
9092
9358
  entityConfig?.companyId,
9093
9359
  entityConfig?.factoryViewId,
9094
- appTimezone,
9095
9360
  defaultTimezone,
9096
9361
  shiftConfig,
9362
+ staticShiftConfig,
9363
+ shiftGroups,
9097
9364
  lineId,
9098
9365
  userAccessibleLineIds
9099
9366
  ]);
@@ -9111,20 +9378,37 @@ var useLineKPIs = ({ lineId, enabled }) => {
9111
9378
  const isFactoryView = lineId === (entityConfig.factoryViewId || "factory");
9112
9379
  const databaseConfig = useDatabaseConfig();
9113
9380
  const dateTimeConfig = useDateTimeConfig();
9114
- useShiftConfig();
9115
- const firstLineId = React24.useMemo(() => {
9116
- const configuredLineIds = getConfiguredLineIds(entityConfig);
9117
- return configuredLineIds.length > 0 ? configuredLineIds[0] : void 0;
9381
+ const staticShiftConfig = useShiftConfig();
9382
+ const appTimezone = useAppTimezone();
9383
+ const timezone = appTimezone || dateTimeConfig.defaultTimezone || "UTC";
9384
+ const configuredLineIds = React24.useMemo(() => {
9385
+ return getConfiguredLineIds(entityConfig);
9118
9386
  }, [entityConfig]);
9119
- const lineIdForShiftConfig = isFactoryView ? firstLineId : lineId;
9120
- const { shiftConfig: dynamicShiftConfig, isFromDatabase, isLoading: isShiftConfigLoading } = useDynamicShiftConfig(lineIdForShiftConfig);
9121
- const shiftConfig = dynamicShiftConfig;
9387
+ const {
9388
+ shiftConfigMap: multiLineShiftConfigMap,
9389
+ isLoading: isMultiLineShiftConfigLoading
9390
+ } = useMultiLineShiftConfigs(
9391
+ isFactoryView ? configuredLineIds : [],
9392
+ staticShiftConfig
9393
+ );
9394
+ const {
9395
+ shiftConfig: singleLineShiftConfig,
9396
+ isFromDatabase,
9397
+ isLoading: isSingleLineShiftConfigLoading
9398
+ } = useDynamicShiftConfig(isFactoryView ? void 0 : lineId);
9399
+ const isShiftConfigLoading = isFactoryView ? isMultiLineShiftConfigLoading : isSingleLineShiftConfigLoading;
9400
+ const shiftGroups = React24.useMemo(() => {
9401
+ if (!isFactoryView) return [];
9402
+ if (isMultiLineShiftConfigLoading || multiLineShiftConfigMap.size === 0) return [];
9403
+ return groupLinesByShift(multiLineShiftConfigMap, timezone);
9404
+ }, [isFactoryView, isMultiLineShiftConfigLoading, multiLineShiftConfigMap, timezone]);
9405
+ const shiftConfig = isFactoryView ? null : singleLineShiftConfig;
9122
9406
  console.log(`[useLineKPIs] \u{1F3AF} Shift config for line ${lineId}:`, {
9123
9407
  isFactoryView,
9124
- lineIdForShiftConfig,
9125
- firstLineId,
9126
9408
  isFromDatabase,
9127
- shiftConfig
9409
+ shiftConfig,
9410
+ shiftGroupsCount: shiftGroups.length,
9411
+ shiftGroups: shiftGroups.map((g) => ({ shiftId: g.shiftId, date: g.date, lineCount: g.lineIds.length }))
9128
9412
  });
9129
9413
  const supabase = useSupabase();
9130
9414
  React24.useMemo(() => {
@@ -9138,8 +9422,6 @@ var useLineKPIs = ({ lineId, enabled }) => {
9138
9422
  const updateQueueRef = React24.useRef(false);
9139
9423
  const updateTimeoutRef = React24.useRef(null);
9140
9424
  const queueUpdateRef = React24.useRef(void 0);
9141
- const appTimezone = useAppTimezone();
9142
- const timezone = appTimezone || dateTimeConfig.defaultTimezone || "UTC";
9143
9425
  const schema = databaseConfig.schema ?? "public";
9144
9426
  const lineMetricsTable = databaseConfig.tables?.lineMetrics ?? "line_metrics";
9145
9427
  const companySpecificMetricsTable = React24.useMemo(
@@ -9158,8 +9440,6 @@ var useLineKPIs = ({ lineId, enabled }) => {
9158
9440
  setIsLoading(true);
9159
9441
  setError(null);
9160
9442
  try {
9161
- const currentShiftDetails = getCurrentShift(timezone, shiftConfig ?? void 0);
9162
- const operationalDate = currentShiftDetails.date;
9163
9443
  const { data: { session } } = await supabase.auth.getSession();
9164
9444
  if (!session?.access_token) {
9165
9445
  throw new Error("No authentication token available");
@@ -9170,33 +9450,68 @@ var useLineKPIs = ({ lineId, enabled }) => {
9170
9450
  }
9171
9451
  const factoryViewIdentifier = entityConfig.factoryViewId || "factory";
9172
9452
  const isFactory = currentLineId === factoryViewIdentifier;
9173
- const lineParam = isFactory ? `line_ids=${getConfiguredLineIds(entityConfig).join(",")}` : `line_id=${currentLineId}`;
9174
- const response = await fetch(
9175
- `${apiUrl}/api/dashboard/line-kpis?${lineParam}&date=${operationalDate}&shift_id=${currentShiftDetails.shiftId}&company_id=${entityConfig.companyId}`,
9176
- {
9177
- headers: {
9178
- "Authorization": `Bearer ${session.access_token}`,
9179
- "Content-Type": "application/json"
9453
+ if (isFactory && shiftGroups.length > 0) {
9454
+ console.log(`[useLineKPIs] \u{1F3ED} Factory view: Fetching KPIs for ${shiftGroups.length} shift group(s)`);
9455
+ const kpiPromises = shiftGroups.map(async (group) => {
9456
+ const lineIdsParam = `line_ids=${group.lineIds.join(",")}`;
9457
+ const url = `${apiUrl}/api/dashboard/line-kpis?${lineIdsParam}&date=${group.date}&shift_id=${group.shiftId}&company_id=${entityConfig.companyId}`;
9458
+ console.log(`[useLineKPIs] \u{1F4CA} Fetching for shift ${group.shiftId} (${group.shiftName}):`, {
9459
+ lineIds: group.lineIds,
9460
+ date: group.date
9461
+ });
9462
+ const response = await fetch(url, {
9463
+ headers: {
9464
+ "Authorization": `Bearer ${session.access_token}`,
9465
+ "Content-Type": "application/json"
9466
+ }
9467
+ });
9468
+ if (!response.ok) {
9469
+ const errorText = await response.text();
9470
+ console.error(`[useLineKPIs] Backend API error for shift ${group.shiftId}:`, errorText);
9471
+ throw new Error(`Backend API error (${response.status}): ${errorText}`);
9180
9472
  }
9181
- }
9182
- );
9183
- if (!response.ok) {
9184
- const errorText = await response.text();
9185
- console.error("[useLineKPIs] Backend API error response:", errorText);
9186
- throw new Error(`Backend API error (${response.status}): ${errorText}`);
9187
- }
9188
- const responseText = await response.text();
9189
- let backendData;
9190
- try {
9191
- backendData = JSON.parse(responseText);
9192
- } catch (parseError) {
9193
- console.error("[useLineKPIs] Failed to parse response as JSON. Response text:", responseText.substring(0, 500));
9194
- throw new Error(`Invalid JSON response from backend. Received: ${responseText.substring(0, 100)}...`);
9195
- }
9196
- if (backendData.kpis) {
9197
- setKPIs(backendData.kpis);
9473
+ const responseText = await response.text();
9474
+ try {
9475
+ return JSON.parse(responseText);
9476
+ } catch (parseError) {
9477
+ console.error("[useLineKPIs] Failed to parse response as JSON:", responseText.substring(0, 500));
9478
+ throw new Error(`Invalid JSON response from backend`);
9479
+ }
9480
+ });
9481
+ const results = await Promise.all(kpiPromises);
9482
+ const aggregatedKPIs = aggregateKPIResults(results);
9483
+ setKPIs(aggregatedKPIs);
9198
9484
  } else {
9199
- setKPIs(null);
9485
+ const currentShiftDetails = shiftConfig ? getCurrentShift(timezone, shiftConfig) : shiftGroups.length === 1 ? { date: shiftGroups[0].date, shiftId: shiftGroups[0].shiftId } : getCurrentShift(timezone, staticShiftConfig);
9486
+ const operationalDate = currentShiftDetails.date;
9487
+ const lineParam = isFactory ? `line_ids=${configuredLineIds.join(",")}` : `line_id=${currentLineId}`;
9488
+ const response = await fetch(
9489
+ `${apiUrl}/api/dashboard/line-kpis?${lineParam}&date=${operationalDate}&shift_id=${currentShiftDetails.shiftId}&company_id=${entityConfig.companyId}`,
9490
+ {
9491
+ headers: {
9492
+ "Authorization": `Bearer ${session.access_token}`,
9493
+ "Content-Type": "application/json"
9494
+ }
9495
+ }
9496
+ );
9497
+ if (!response.ok) {
9498
+ const errorText = await response.text();
9499
+ console.error("[useLineKPIs] Backend API error response:", errorText);
9500
+ throw new Error(`Backend API error (${response.status}): ${errorText}`);
9501
+ }
9502
+ const responseText = await response.text();
9503
+ let backendData;
9504
+ try {
9505
+ backendData = JSON.parse(responseText);
9506
+ } catch (parseError) {
9507
+ console.error("[useLineKPIs] Failed to parse response as JSON. Response text:", responseText.substring(0, 500));
9508
+ throw new Error(`Invalid JSON response from backend. Received: ${responseText.substring(0, 100)}...`);
9509
+ }
9510
+ if (backendData.kpis) {
9511
+ setKPIs(backendData.kpis);
9512
+ } else {
9513
+ setKPIs(null);
9514
+ }
9200
9515
  }
9201
9516
  } catch (err) {
9202
9517
  console.error("[useLineKPIs] Error fetching KPIs:", err);
@@ -9207,7 +9522,80 @@ var useLineKPIs = ({ lineId, enabled }) => {
9207
9522
  isFetchingRef.current = false;
9208
9523
  updateQueueRef.current = false;
9209
9524
  }
9210
- }, [timezone, shiftConfig, entityConfig, supabase, enabled, isShiftConfigLoading]);
9525
+ }, [timezone, shiftConfig, shiftGroups, configuredLineIds, staticShiftConfig, entityConfig, supabase, enabled, isShiftConfigLoading]);
9526
+ const aggregateKPIResults = (results) => {
9527
+ const validResults = results.filter((r2) => r2?.kpis);
9528
+ if (validResults.length === 0) return null;
9529
+ if (validResults.length === 1) return validResults[0].kpis;
9530
+ let totalEfficiency = 0;
9531
+ let totalEfficiencyWeight = 0;
9532
+ let totalOutputCurrent = 0;
9533
+ let totalOutputTarget = 0;
9534
+ let totalIdealOutput = 0;
9535
+ let totalUnderperformingCurrent = 0;
9536
+ let totalUnderperformingTotal = 0;
9537
+ let totalCycleTimeSum = 0;
9538
+ let totalCycleTimeCount = 0;
9539
+ let totalQualitySum = 0;
9540
+ let totalQualityCount = 0;
9541
+ validResults.forEach((result) => {
9542
+ const kpis2 = result.kpis;
9543
+ if (kpis2.efficiency?.value !== void 0) {
9544
+ const weight = kpis2.outputProgress?.current || 1;
9545
+ totalEfficiency += kpis2.efficiency.value * weight;
9546
+ totalEfficiencyWeight += weight;
9547
+ }
9548
+ if (kpis2.outputProgress) {
9549
+ totalOutputCurrent += kpis2.outputProgress.current || 0;
9550
+ totalOutputTarget += kpis2.outputProgress.target || 0;
9551
+ totalIdealOutput += kpis2.outputProgress.idealOutput || 0;
9552
+ }
9553
+ if (kpis2.underperformingWorkers) {
9554
+ totalUnderperformingCurrent += kpis2.underperformingWorkers.current || 0;
9555
+ totalUnderperformingTotal += kpis2.underperformingWorkers.total || 0;
9556
+ }
9557
+ if (kpis2.avgCycleTime?.value !== void 0) {
9558
+ totalCycleTimeSum += kpis2.avgCycleTime.value;
9559
+ totalCycleTimeCount++;
9560
+ }
9561
+ if (kpis2.qualityCompliance?.value !== void 0) {
9562
+ totalQualitySum += kpis2.qualityCompliance.value;
9563
+ totalQualityCount++;
9564
+ }
9565
+ });
9566
+ const aggregated = {
9567
+ efficiency: {
9568
+ value: totalEfficiencyWeight > 0 ? totalEfficiency / totalEfficiencyWeight : 0,
9569
+ change: 0
9570
+ // Change not meaningful when aggregating across shifts
9571
+ },
9572
+ outputProgress: {
9573
+ current: totalOutputCurrent,
9574
+ target: totalOutputTarget,
9575
+ change: 0,
9576
+ // Change not meaningful when aggregating across shifts
9577
+ idealOutput: totalIdealOutput
9578
+ },
9579
+ underperformingWorkers: {
9580
+ current: totalUnderperformingCurrent,
9581
+ total: totalUnderperformingTotal,
9582
+ change: 0
9583
+ // Change not meaningful when aggregating across shifts
9584
+ },
9585
+ avgCycleTime: {
9586
+ value: totalCycleTimeCount > 0 ? totalCycleTimeSum / totalCycleTimeCount : 0,
9587
+ change: 0
9588
+ // Change not meaningful when aggregating across shifts
9589
+ },
9590
+ qualityCompliance: {
9591
+ value: totalQualityCount > 0 ? totalQualitySum / totalQualityCount : 0,
9592
+ change: 0
9593
+ // Change not meaningful when aggregating across shifts
9594
+ }
9595
+ };
9596
+ console.log("[useLineKPIs] \u{1F4CA} Aggregated KPIs from", validResults.length, "shift groups:", aggregated);
9597
+ return aggregated;
9598
+ };
9211
9599
  const queueUpdate = React24.useCallback(() => {
9212
9600
  if (updateTimeoutRef.current) {
9213
9601
  clearTimeout(updateTimeoutRef.current);
@@ -9227,40 +9615,66 @@ var useLineKPIs = ({ lineId, enabled }) => {
9227
9615
  return;
9228
9616
  }
9229
9617
  fetchKPIs();
9230
- const currentShiftDetails = getCurrentShift(timezone, shiftConfig ?? void 0);
9231
- const operationalDate = currentShiftDetails.date;
9232
9618
  const factoryViewIdentifier = entityConfig.factoryViewId || "factory";
9233
- const targetLineIds = currentLineId === factoryViewIdentifier ? getConfiguredLineIds(entityConfig) : [currentLineId];
9234
- if (targetLineIds.length === 0) {
9235
- console.warn("[useLineKPIs] No target line IDs for subscription. LineId:", currentLineId);
9236
- return;
9237
- }
9238
- const baseFilterParts = `date=eq.${operationalDate},shift_id=eq.${currentShiftDetails.shiftId}`;
9239
- const lineIdFilterPart = `line_id=in.(${targetLineIds.join(",")})`;
9240
- const lineMetricsFilter = `${baseFilterParts},${lineIdFilterPart}`;
9241
- const companyTableFilter = `${baseFilterParts},${lineIdFilterPart}`;
9619
+ const isFactory = currentLineId === factoryViewIdentifier;
9242
9620
  const activeChannels = [];
9243
- const lmChannelName = `kpi-lm-${currentLineId}-${operationalDate}-${currentShiftDetails.shiftId}`.replace(/[^a-zA-Z0-9_-]/g, "");
9244
- const lmChannel = supabase.channel(lmChannelName).on(
9245
- "postgres_changes",
9246
- { event: "*", schema, table: lineMetricsTable, filter: lineMetricsFilter },
9247
- (payload) => {
9248
- const payloadData = payload.new || payload.old;
9249
- if (payloadData?.date === operationalDate && payloadData?.shift_id === currentShiftDetails.shiftId) {
9250
- queueUpdateRef.current?.();
9621
+ if (isFactory && shiftGroups.length > 0) {
9622
+ console.log(`[useLineKPIs] \u{1F4E1} Setting up subscriptions for ${shiftGroups.length} shift group(s)`);
9623
+ shiftGroups.forEach((group, index) => {
9624
+ const baseFilterParts = `date=eq.${group.date},shift_id=eq.${group.shiftId}`;
9625
+ const lineIdFilterPart = `line_id=in.(${group.lineIds.join(",")})`;
9626
+ const filter2 = `${baseFilterParts},${lineIdFilterPart}`;
9627
+ const lmChannelName = `kpi-lm-factory-g${index}-${group.date}-${group.shiftId}`.replace(/[^a-zA-Z0-9_-]/g, "");
9628
+ const lmChannel = supabase.channel(lmChannelName).on(
9629
+ "postgres_changes",
9630
+ { event: "*", schema, table: lineMetricsTable, filter: filter2 },
9631
+ (payload) => {
9632
+ const payloadData = payload.new || payload.old;
9633
+ if (payloadData?.date === group.date && payloadData?.shift_id === group.shiftId) {
9634
+ queueUpdateRef.current?.();
9635
+ }
9636
+ }
9637
+ ).subscribe((status, err) => {
9638
+ if (status === supabaseJs.REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR || status === supabaseJs.REALTIME_SUBSCRIBE_STATES.TIMED_OUT) {
9639
+ console.error(`[useLineKPIs] Subscription to ${lineMetricsTable} (group ${index}) FAILED:`, status, err);
9640
+ }
9641
+ });
9642
+ activeChannels.push(lmChannel);
9643
+ if (companySpecificMetricsTable && !companySpecificMetricsTable.includes("unknown_company")) {
9644
+ const csChannelName = `kpi-cs-factory-g${index}-${group.date}-${group.shiftId}`.replace(/[^a-zA-Z0-9_-]/g, "");
9645
+ const csChannel = supabase.channel(csChannelName).on(
9646
+ "postgres_changes",
9647
+ { event: "*", schema, table: companySpecificMetricsTable, filter: filter2 },
9648
+ (payload) => {
9649
+ const payloadData = payload.new || payload.old;
9650
+ if (payloadData?.date === group.date && payloadData?.shift_id === group.shiftId) {
9651
+ queueUpdateRef.current?.();
9652
+ }
9653
+ }
9654
+ ).subscribe((status, err) => {
9655
+ if (status === supabaseJs.REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR || status === supabaseJs.REALTIME_SUBSCRIBE_STATES.TIMED_OUT) {
9656
+ console.error(`[useLineKPIs] Subscription to ${companySpecificMetricsTable} (group ${index}) FAILED:`, status, err);
9657
+ }
9658
+ });
9659
+ activeChannels.push(csChannel);
9251
9660
  }
9661
+ });
9662
+ } else {
9663
+ const currentShiftDetails = shiftConfig ? getCurrentShift(timezone, shiftConfig) : getCurrentShift(timezone, staticShiftConfig);
9664
+ const operationalDate = currentShiftDetails.date;
9665
+ const targetLineIds = [currentLineId];
9666
+ if (targetLineIds.length === 0) {
9667
+ console.warn("[useLineKPIs] No target line IDs for subscription. LineId:", currentLineId);
9668
+ return;
9252
9669
  }
9253
- ).subscribe((status, err) => {
9254
- if (status === supabaseJs.REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR || status === supabaseJs.REALTIME_SUBSCRIBE_STATES.TIMED_OUT) {
9255
- console.error(`[useLineKPIs] Subscription to ${lineMetricsTable} FAILED: ${status}`, err || "");
9256
- }
9257
- });
9258
- activeChannels.push(lmChannel);
9259
- if (companySpecificMetricsTable && !companySpecificMetricsTable.includes("unknown_company")) {
9260
- const csChannelName = `kpi-cs-${currentLineId}-${operationalDate}-${currentShiftDetails.shiftId}`.replace(/[^a-zA-Z0-9_-]/g, "");
9261
- const csChannel = supabase.channel(csChannelName).on(
9670
+ const baseFilterParts = `date=eq.${operationalDate},shift_id=eq.${currentShiftDetails.shiftId}`;
9671
+ const lineIdFilterPart = `line_id=in.(${targetLineIds.join(",")})`;
9672
+ const lineMetricsFilter = `${baseFilterParts},${lineIdFilterPart}`;
9673
+ const companyTableFilter = `${baseFilterParts},${lineIdFilterPart}`;
9674
+ const lmChannelName = `kpi-lm-${currentLineId}-${operationalDate}-${currentShiftDetails.shiftId}`.replace(/[^a-zA-Z0-9_-]/g, "");
9675
+ const lmChannel = supabase.channel(lmChannelName).on(
9262
9676
  "postgres_changes",
9263
- { event: "*", schema, table: companySpecificMetricsTable, filter: companyTableFilter },
9677
+ { event: "*", schema, table: lineMetricsTable, filter: lineMetricsFilter },
9264
9678
  (payload) => {
9265
9679
  const payloadData = payload.new || payload.old;
9266
9680
  if (payloadData?.date === operationalDate && payloadData?.shift_id === currentShiftDetails.shiftId) {
@@ -9269,10 +9683,28 @@ var useLineKPIs = ({ lineId, enabled }) => {
9269
9683
  }
9270
9684
  ).subscribe((status, err) => {
9271
9685
  if (status === supabaseJs.REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR || status === supabaseJs.REALTIME_SUBSCRIBE_STATES.TIMED_OUT) {
9272
- console.error(`[useLineKPIs] Subscription to ${companySpecificMetricsTable} FAILED: ${status}`, err || "");
9686
+ console.error(`[useLineKPIs] Subscription to ${lineMetricsTable} FAILED: ${status}`, err || "");
9273
9687
  }
9274
9688
  });
9275
- activeChannels.push(csChannel);
9689
+ activeChannels.push(lmChannel);
9690
+ if (companySpecificMetricsTable && !companySpecificMetricsTable.includes("unknown_company")) {
9691
+ const csChannelName = `kpi-cs-${currentLineId}-${operationalDate}-${currentShiftDetails.shiftId}`.replace(/[^a-zA-Z0-9_-]/g, "");
9692
+ const csChannel = supabase.channel(csChannelName).on(
9693
+ "postgres_changes",
9694
+ { event: "*", schema, table: companySpecificMetricsTable, filter: companyTableFilter },
9695
+ (payload) => {
9696
+ const payloadData = payload.new || payload.old;
9697
+ if (payloadData?.date === operationalDate && payloadData?.shift_id === currentShiftDetails.shiftId) {
9698
+ queueUpdateRef.current?.();
9699
+ }
9700
+ }
9701
+ ).subscribe((status, err) => {
9702
+ if (status === supabaseJs.REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR || status === supabaseJs.REALTIME_SUBSCRIBE_STATES.TIMED_OUT) {
9703
+ console.error(`[useLineKPIs] Subscription to ${companySpecificMetricsTable} FAILED: ${status}`, err || "");
9704
+ }
9705
+ });
9706
+ activeChannels.push(csChannel);
9707
+ }
9276
9708
  }
9277
9709
  return () => {
9278
9710
  activeChannels.forEach((ch) => supabase.removeChannel(ch).catch((err) => console.error("[useLineKPIs] Error removing KPI channel:", err)));
@@ -9280,7 +9712,7 @@ var useLineKPIs = ({ lineId, enabled }) => {
9280
9712
  clearTimeout(updateTimeoutRef.current);
9281
9713
  }
9282
9714
  };
9283
- }, [lineId, supabase, entityConfig, schema, lineMetricsTable, companySpecificMetricsTable, timezone, shiftConfig, isShiftConfigLoading]);
9715
+ }, [lineId, supabase, entityConfig, schema, lineMetricsTable, companySpecificMetricsTable, timezone, shiftConfig, staticShiftConfig, shiftGroups, isShiftConfigLoading]);
9284
9716
  return {
9285
9717
  kpis,
9286
9718
  isLoading,
@@ -10953,10 +11385,26 @@ var useAllWorkspaceMetrics = (options) => {
10953
11385
  const dateTimeConfig = useDateTimeConfig();
10954
11386
  const staticShiftConfig = useShiftConfig();
10955
11387
  const timezone = useAppTimezone();
10956
- const configuredLineIds = React24.useMemo(() => getConfiguredLineIds(entityConfig), [entityConfig]);
10957
- const representativeLineId = options?.allowedLineIds?.[0] || configuredLineIds[0];
10958
- const { shiftConfig: dynamicShiftConfig, isLoading: isShiftConfigLoading } = useDynamicShiftConfig(representativeLineId);
10959
- const shiftConfig = dynamicShiftConfig || staticShiftConfig;
11388
+ const configuredLineIds = React24.useMemo(() => {
11389
+ const allLineIds = getConfiguredLineIds(entityConfig);
11390
+ if (options?.allowedLineIds) {
11391
+ return allLineIds.filter((id3) => options.allowedLineIds.includes(id3));
11392
+ }
11393
+ return allLineIds;
11394
+ }, [entityConfig, options?.allowedLineIds]);
11395
+ const {
11396
+ shiftConfigMap: multiLineShiftConfigMap,
11397
+ isLoading: isShiftConfigLoading
11398
+ } = useMultiLineShiftConfigs(configuredLineIds, staticShiftConfig);
11399
+ const shiftGroups = React24.useMemo(() => {
11400
+ if (isShiftConfigLoading || multiLineShiftConfigMap.size === 0) return [];
11401
+ return groupLinesByShift(multiLineShiftConfigMap, timezone || dateTimeConfig.defaultTimezone || "Asia/Kolkata");
11402
+ }, [isShiftConfigLoading, multiLineShiftConfigMap, timezone, dateTimeConfig.defaultTimezone]);
11403
+ console.log(`[useAllWorkspaceMetrics] \u{1F4CA} Shift groups:`, shiftGroups.map((g) => ({
11404
+ shiftId: g.shiftId,
11405
+ date: g.date,
11406
+ lineIds: g.lineIds
11407
+ })));
10960
11408
  const supabase = useSupabase();
10961
11409
  const [workspaces, setWorkspaces] = React24.useState([]);
10962
11410
  const [loading, setLoading] = React24.useState(true);
@@ -10964,19 +11412,29 @@ var useAllWorkspaceMetrics = (options) => {
10964
11412
  const [initialized, setInitialized] = React24.useState(false);
10965
11413
  const fetchTimeoutRef = React24.useRef(null);
10966
11414
  const isFetchingRef = React24.useRef(false);
10967
- const queryShiftId = React24.useMemo(() => {
11415
+ const hasSpecificDateShift = options?.initialDate !== void 0 || options?.initialShiftId !== void 0;
11416
+ const fallbackQueryShiftId = React24.useMemo(() => {
10968
11417
  if (options?.initialShiftId !== void 0) {
10969
11418
  return options.initialShiftId;
10970
11419
  }
11420
+ if (shiftGroups.length > 0) {
11421
+ return shiftGroups[0].shiftId;
11422
+ }
10971
11423
  const currentShift = getCurrentShift(
10972
11424
  timezone || dateTimeConfig.defaultTimezone || "Asia/Kolkata",
10973
- shiftConfig
11425
+ staticShiftConfig
10974
11426
  );
10975
11427
  return currentShift.shiftId;
10976
- }, [options?.initialShiftId, timezone, dateTimeConfig.defaultTimezone, shiftConfig]);
10977
- const queryDate = React24.useMemo(() => {
10978
- return options?.initialDate || getOperationalDate(timezone || dateTimeConfig.defaultTimezone || "UTC");
10979
- }, [options?.initialDate, timezone, dateTimeConfig.defaultTimezone]);
11428
+ }, [options?.initialShiftId, shiftGroups, timezone, dateTimeConfig.defaultTimezone, staticShiftConfig]);
11429
+ const fallbackQueryDate = React24.useMemo(() => {
11430
+ if (options?.initialDate) {
11431
+ return options.initialDate;
11432
+ }
11433
+ if (shiftGroups.length > 0) {
11434
+ return shiftGroups[0].date;
11435
+ }
11436
+ return getOperationalDate(timezone || dateTimeConfig.defaultTimezone || "UTC");
11437
+ }, [options?.initialDate, shiftGroups, timezone, dateTimeConfig.defaultTimezone]);
10980
11438
  const metricsTable = React24.useMemo(() => {
10981
11439
  const companyId = entityConfig.companyId;
10982
11440
  if (!companyId) return "";
@@ -10994,49 +11452,99 @@ var useAllWorkspaceMetrics = (options) => {
10994
11452
  }
10995
11453
  setError(null);
10996
11454
  try {
10997
- const allConfiguredLineIds = getConfiguredLineIds(entityConfig);
10998
- const configuredLineIds2 = options?.allowedLineIds ? allConfiguredLineIds.filter((id3) => options.allowedLineIds.includes(id3)) : allConfiguredLineIds;
10999
- let enabledWorkspaceIds = [];
11000
- for (const lineId of configuredLineIds2) {
11001
- const workspaces2 = await workspaceService.getWorkspaces(lineId);
11002
- const enabledIds = workspaces2.filter((ws) => ws.enable === true).map((ws) => ws.id);
11003
- enabledWorkspaceIds.push(...enabledIds);
11004
- }
11005
- if (enabledWorkspaceIds.length === 0) {
11455
+ const lineWorkspaceMap = /* @__PURE__ */ new Map();
11456
+ let allEnabledWorkspaceIds = [];
11457
+ for (const lineId of configuredLineIds) {
11458
+ const lineWorkspaces = await workspaceService.getWorkspaces(lineId);
11459
+ const enabledIds = lineWorkspaces.filter((ws) => ws.enable === true).map((ws) => ws.id);
11460
+ lineWorkspaceMap.set(lineId, enabledIds);
11461
+ allEnabledWorkspaceIds.push(...enabledIds);
11462
+ }
11463
+ if (allEnabledWorkspaceIds.length === 0) {
11006
11464
  setWorkspaces([]);
11007
11465
  setInitialized(true);
11008
11466
  setLoading(false);
11009
11467
  return;
11010
11468
  }
11011
- const { data, error: fetchError } = await supabase.from(metricsTable).select(`
11012
- workspace_name,
11013
- total_output,
11014
- avg_pph,
11015
- efficiency,
11016
- workspace_id,
11017
- avg_cycle_time,
11018
- performance_score,
11019
- trend_score,
11020
- line_id,
11021
- total_day_output
11022
- `).eq("date", queryDate).eq("shift_id", queryShiftId).in("workspace_id", enabledWorkspaceIds).order("efficiency", { ascending: false });
11023
- if (fetchError) throw fetchError;
11024
- const transformedData = (data || []).map((item) => ({
11025
- company_id: entityConfig.companyId || "unknown",
11026
- line_id: item.line_id,
11027
- shift_id: queryShiftId,
11028
- date: queryDate,
11029
- workspace_uuid: item.workspace_id,
11030
- workspace_name: item.workspace_name,
11031
- action_count: item.total_output || 0,
11032
- pph: item.avg_pph || 0,
11033
- performance_score: item.performance_score || 0,
11034
- avg_cycle_time: item.avg_cycle_time || 0,
11035
- trend: item.trend_score === 1 ? 2 : 0,
11036
- predicted_output: 0,
11037
- efficiency: item.efficiency || 0,
11038
- action_threshold: item.total_day_output || 0
11039
- }));
11469
+ let transformedData = [];
11470
+ if (hasSpecificDateShift) {
11471
+ console.log(`[useAllWorkspaceMetrics] \u{1F4CA} Using specific date/shift: ${fallbackQueryDate} / ${fallbackQueryShiftId}`);
11472
+ const { data, error: fetchError } = await supabase.from(metricsTable).select(`
11473
+ workspace_name,
11474
+ total_output,
11475
+ avg_pph,
11476
+ efficiency,
11477
+ workspace_id,
11478
+ avg_cycle_time,
11479
+ performance_score,
11480
+ trend_score,
11481
+ line_id,
11482
+ total_day_output
11483
+ `).eq("date", fallbackQueryDate).eq("shift_id", fallbackQueryShiftId).in("workspace_id", allEnabledWorkspaceIds).order("efficiency", { ascending: false });
11484
+ if (fetchError) throw fetchError;
11485
+ transformedData = (data || []).map((item) => ({
11486
+ company_id: entityConfig.companyId || "unknown",
11487
+ line_id: item.line_id,
11488
+ shift_id: fallbackQueryShiftId,
11489
+ date: fallbackQueryDate,
11490
+ workspace_uuid: item.workspace_id,
11491
+ workspace_name: item.workspace_name,
11492
+ action_count: item.total_output || 0,
11493
+ pph: item.avg_pph || 0,
11494
+ performance_score: item.performance_score || 0,
11495
+ avg_cycle_time: item.avg_cycle_time || 0,
11496
+ trend: item.trend_score === 1 ? 2 : 0,
11497
+ predicted_output: 0,
11498
+ efficiency: item.efficiency || 0,
11499
+ action_threshold: item.total_day_output || 0
11500
+ }));
11501
+ } else if (shiftGroups.length > 0) {
11502
+ console.log(`[useAllWorkspaceMetrics] \u{1F3ED} Fetching per-shift: ${shiftGroups.length} group(s)`);
11503
+ const queryPromises = shiftGroups.map(async (group) => {
11504
+ const groupWorkspaceIds = group.lineIds.flatMap(
11505
+ (lineId) => lineWorkspaceMap.get(lineId) || []
11506
+ );
11507
+ if (groupWorkspaceIds.length === 0) {
11508
+ return [];
11509
+ }
11510
+ console.log(`[useAllWorkspaceMetrics] \u{1F4CA} Fetching shift ${group.shiftId} (${group.shiftName}):`, {
11511
+ lineIds: group.lineIds,
11512
+ date: group.date,
11513
+ workspaceCount: groupWorkspaceIds.length
11514
+ });
11515
+ const { data, error: fetchError } = await supabase.from(metricsTable).select(`
11516
+ workspace_name,
11517
+ total_output,
11518
+ avg_pph,
11519
+ efficiency,
11520
+ workspace_id,
11521
+ avg_cycle_time,
11522
+ performance_score,
11523
+ trend_score,
11524
+ line_id,
11525
+ total_day_output
11526
+ `).eq("date", group.date).eq("shift_id", group.shiftId).in("workspace_id", groupWorkspaceIds);
11527
+ if (fetchError) throw fetchError;
11528
+ return (data || []).map((item) => ({
11529
+ company_id: entityConfig.companyId || "unknown",
11530
+ line_id: item.line_id,
11531
+ shift_id: group.shiftId,
11532
+ date: group.date,
11533
+ workspace_uuid: item.workspace_id,
11534
+ workspace_name: item.workspace_name,
11535
+ action_count: item.total_output || 0,
11536
+ pph: item.avg_pph || 0,
11537
+ performance_score: item.performance_score || 0,
11538
+ avg_cycle_time: item.avg_cycle_time || 0,
11539
+ trend: item.trend_score === 1 ? 2 : 0,
11540
+ predicted_output: 0,
11541
+ efficiency: item.efficiency || 0,
11542
+ action_threshold: item.total_day_output || 0
11543
+ }));
11544
+ });
11545
+ const results = await Promise.all(queryPromises);
11546
+ transformedData = results.flat().sort((a, b) => (b.efficiency || 0) - (a.efficiency || 0));
11547
+ }
11040
11548
  setWorkspaces((prevWorkspaces) => {
11041
11549
  if (prevWorkspaces.length !== transformedData.length) {
11042
11550
  return transformedData;
@@ -11058,11 +11566,19 @@ var useAllWorkspaceMetrics = (options) => {
11058
11566
  setLoading(false);
11059
11567
  isFetchingRef.current = false;
11060
11568
  }
11061
- }, [queryDate, queryShiftId, metricsTable, supabase, entityConfig.companyId, entityConfig, options?.allowedLineIds, options?.enabled, isShiftConfigLoading]);
11569
+ }, [fallbackQueryDate, fallbackQueryShiftId, hasSpecificDateShift, shiftGroups, metricsTable, supabase, entityConfig.companyId, configuredLineIds, options?.enabled, isShiftConfigLoading]);
11062
11570
  React24.useEffect(() => {
11063
11571
  if (!initialized) {
11064
11572
  fetchWorkspaceMetrics();
11065
11573
  }
11574
+ const validDateShiftCombos = /* @__PURE__ */ new Set();
11575
+ if (hasSpecificDateShift) {
11576
+ validDateShiftCombos.add(`${fallbackQueryDate}-${fallbackQueryShiftId}`);
11577
+ } else {
11578
+ shiftGroups.forEach((group) => {
11579
+ validDateShiftCombos.add(`${group.date}-${group.shiftId}`);
11580
+ });
11581
+ }
11066
11582
  const setupSubscription = () => {
11067
11583
  const channel2 = supabase.channel(`all-workspace-metrics-${Date.now()}`).on(
11068
11584
  "postgres_changes",
@@ -11073,7 +11589,8 @@ var useAllWorkspaceMetrics = (options) => {
11073
11589
  },
11074
11590
  async (payload) => {
11075
11591
  const data = payload.new || payload.old;
11076
- if (data?.date !== queryDate || data?.shift_id !== queryShiftId) {
11592
+ const dataKey = `${data?.date}-${data?.shift_id}`;
11593
+ if (!validDateShiftCombos.has(dataKey)) {
11077
11594
  return;
11078
11595
  }
11079
11596
  if (fetchTimeoutRef.current) {
@@ -11099,10 +11616,10 @@ var useAllWorkspaceMetrics = (options) => {
11099
11616
  supabase.removeChannel(channel);
11100
11617
  }
11101
11618
  };
11102
- }, [queryDate, queryShiftId, metricsTable, fetchWorkspaceMetrics, initialized, supabase, schema, entityConfig.companyId]);
11619
+ }, [fallbackQueryDate, fallbackQueryShiftId, hasSpecificDateShift, shiftGroups, metricsTable, fetchWorkspaceMetrics, initialized, supabase, schema, entityConfig.companyId]);
11103
11620
  React24.useEffect(() => {
11104
11621
  setInitialized(false);
11105
- }, [queryDate, queryShiftId]);
11622
+ }, [fallbackQueryDate, fallbackQueryShiftId, shiftGroups]);
11106
11623
  const refreshWorkspaces = React24.useCallback(() => fetchWorkspaceMetrics(), [fetchWorkspaceMetrics]);
11107
11624
  return React24.useMemo(
11108
11625
  () => ({ workspaces, loading, error, refreshWorkspaces }),
@@ -11591,127 +12108,6 @@ function useClipTypesWithCounts(workspaceId, date, shiftId, totalOutput, options
11591
12108
  counts
11592
12109
  };
11593
12110
  }
11594
- var useMultiLineShiftConfigs = (lineIds, fallbackConfig) => {
11595
- const [shiftConfigMap, setShiftConfigMap] = React24.useState(/* @__PURE__ */ new Map());
11596
- const [isLoading, setIsLoading] = React24.useState(true);
11597
- const [error, setError] = React24.useState(null);
11598
- const supabase = useSupabase();
11599
- const lineIdsKey = React24.useMemo(() => lineIds.sort().join(","), [lineIds]);
11600
- React24.useEffect(() => {
11601
- if (!lineIds || lineIds.length === 0) {
11602
- setShiftConfigMap(/* @__PURE__ */ new Map());
11603
- setIsLoading(false);
11604
- return;
11605
- }
11606
- const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
11607
- const validLineIds = lineIds.filter((id3) => uuidRegex.test(id3));
11608
- if (validLineIds.length === 0) {
11609
- console.warn("[useMultiLineShiftConfigs] No valid line IDs provided");
11610
- setShiftConfigMap(/* @__PURE__ */ new Map());
11611
- setIsLoading(false);
11612
- return;
11613
- }
11614
- let mounted = true;
11615
- const calculateBreakDuration2 = (startTime, endTime) => {
11616
- const [sh, sm] = startTime.split(":").map(Number);
11617
- const [eh, em] = endTime.split(":").map(Number);
11618
- let startMinutes = sh * 60 + sm;
11619
- let endMinutes = eh * 60 + em;
11620
- if (endMinutes < startMinutes) {
11621
- endMinutes += 24 * 60;
11622
- }
11623
- return endMinutes - startMinutes;
11624
- };
11625
- const stripSeconds = (timeStr) => {
11626
- if (!timeStr) return timeStr;
11627
- return timeStr.substring(0, 5);
11628
- };
11629
- const buildShiftConfigFromRows = (shifts, fallback) => {
11630
- const mapped = shifts.map((shift) => ({
11631
- shiftId: shift.shift_id,
11632
- shiftName: shift.shift_name || `Shift ${shift.shift_id}`,
11633
- startTime: stripSeconds(shift.start_time),
11634
- endTime: stripSeconds(shift.end_time),
11635
- breaks: Array.isArray(shift.breaks) ? shift.breaks.map((b) => ({
11636
- startTime: b.start || b.startTime || "00:00",
11637
- endTime: b.end || b.endTime || "00:00",
11638
- duration: calculateBreakDuration2(
11639
- b.start || b.startTime || "00:00",
11640
- b.end || b.endTime || "00:00"
11641
- ),
11642
- remarks: b.remarks || b.name || ""
11643
- })) : [],
11644
- timezone: shift.timezone
11645
- }));
11646
- const day = mapped.find((s) => s.shiftId === 0);
11647
- const night = mapped.find((s) => s.shiftId === 1);
11648
- return {
11649
- shifts: mapped,
11650
- timezone: mapped[0]?.timezone,
11651
- dayShift: day ? { id: day.shiftId, startTime: day.startTime, endTime: day.endTime, name: day.shiftName } : fallback?.dayShift,
11652
- nightShift: night ? { id: night.shiftId, startTime: night.startTime, endTime: night.endTime, name: night.shiftName } : fallback?.nightShift,
11653
- transitionPeriodMinutes: fallback?.transitionPeriodMinutes || 0
11654
- };
11655
- };
11656
- const fetchAllConfigs = async () => {
11657
- try {
11658
- setIsLoading(true);
11659
- setError(null);
11660
- console.log(`[useMultiLineShiftConfigs] Fetching shift configs for ${validLineIds.length} lines`);
11661
- 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);
11662
- if (fetchError) {
11663
- console.error("[useMultiLineShiftConfigs] Error fetching shift configs:", fetchError);
11664
- throw new Error(`Failed to fetch shift configs: ${fetchError.message}`);
11665
- }
11666
- const lineShiftsMap = /* @__PURE__ */ new Map();
11667
- data?.forEach((row) => {
11668
- if (!lineShiftsMap.has(row.line_id)) {
11669
- lineShiftsMap.set(row.line_id, []);
11670
- }
11671
- lineShiftsMap.get(row.line_id).push(row);
11672
- });
11673
- const configMap = /* @__PURE__ */ new Map();
11674
- lineShiftsMap.forEach((shifts, lineId) => {
11675
- const config = buildShiftConfigFromRows(shifts, fallbackConfig);
11676
- configMap.set(lineId, config);
11677
- });
11678
- validLineIds.forEach((lineId) => {
11679
- if (!configMap.has(lineId) && fallbackConfig) {
11680
- console.warn(`[useMultiLineShiftConfigs] No config found for line ${lineId}, using fallback`);
11681
- configMap.set(lineId, fallbackConfig);
11682
- }
11683
- });
11684
- console.log(`[useMultiLineShiftConfigs] Built configs for ${configMap.size} lines`);
11685
- if (mounted) {
11686
- setShiftConfigMap(configMap);
11687
- setIsLoading(false);
11688
- }
11689
- } catch (err) {
11690
- console.error("[useMultiLineShiftConfigs] Error:", err);
11691
- if (mounted) {
11692
- setError(err instanceof Error ? err.message : "Failed to fetch shift configs");
11693
- if (fallbackConfig) {
11694
- const fallbackMap = /* @__PURE__ */ new Map();
11695
- validLineIds.forEach((lineId) => {
11696
- fallbackMap.set(lineId, fallbackConfig);
11697
- });
11698
- setShiftConfigMap(fallbackMap);
11699
- }
11700
- setIsLoading(false);
11701
- }
11702
- }
11703
- };
11704
- fetchAllConfigs();
11705
- return () => {
11706
- mounted = false;
11707
- };
11708
- }, [lineIdsKey, supabase]);
11709
- return {
11710
- shiftConfigMap,
11711
- isLoading,
11712
- error
11713
- };
11714
- };
11715
12111
  var MAX_RETRIES = 10;
11716
12112
  var RETRY_DELAY = 500;
11717
12113
  function useNavigation(customNavigate) {
@@ -45266,6 +45662,10 @@ function HomeView({
45266
45662
  const [diagnosisModalOpen, setDiagnosisModalOpen] = React24.useState(false);
45267
45663
  const [diagnoses, setDiagnoses] = React24.useState([]);
45268
45664
  const timezone = useAppTimezone();
45665
+ const { shiftConfigMap: lineShiftConfigs } = useMultiLineShiftConfigs(
45666
+ allLineIds,
45667
+ dashboardConfig?.shiftConfig
45668
+ );
45269
45669
  React24.useEffect(() => {
45270
45670
  const initDisplayNames = async () => {
45271
45671
  try {
@@ -45363,12 +45763,18 @@ function HomeView({
45363
45763
  label: "Diagnose",
45364
45764
  onClick: async () => {
45365
45765
  console.log("\u{1F50D} Investigating bottleneck:", bottleneck.log_number);
45366
- const operationalDate = getOperationalDate(
45367
- timezone || dashboardConfig.dateTimeConfig?.defaultTimezone || "UTC"
45368
- );
45369
45766
  const tz = timezone || dashboardConfig.dateTimeConfig?.defaultTimezone || "UTC";
45370
- const shiftResult = getCurrentShift(tz, dashboardConfig?.shiftConfig);
45767
+ const lineShiftConfig = bottleneck.line_id && lineShiftConfigs.get(bottleneck.line_id);
45768
+ const effectiveShiftConfig = lineShiftConfig || dashboardConfig?.shiftConfig;
45769
+ const shiftResult = getCurrentShift(tz, effectiveShiftConfig);
45371
45770
  const currentShift = shiftResult.shiftId;
45771
+ const operationalDate = shiftResult.date;
45772
+ console.log("\u{1F550} Using shift config for line:", {
45773
+ lineId: bottleneck.line_id,
45774
+ hasLineConfig: !!lineShiftConfig,
45775
+ shiftId: currentShift,
45776
+ date: operationalDate
45777
+ });
45372
45778
  console.log("\u{1F3AC} [Investigate] Opening clips modal with data:", {
45373
45779
  workspaceId: bottleneck.workspace_id,
45374
45780
  ticketId: bottleneck.id,
@@ -45438,12 +45844,18 @@ function HomeView({
45438
45844
  state,
45439
45845
  priority: bottleneck.priority
45440
45846
  });
45441
- const operationalDate = getOperationalDate(
45442
- timezone || dashboardConfig.dateTimeConfig?.defaultTimezone || "UTC"
45443
- );
45444
45847
  const tz = timezone || dashboardConfig.dateTimeConfig?.defaultTimezone || "UTC";
45445
- const shiftResult = getCurrentShift(tz, dashboardConfig?.shiftConfig);
45848
+ const lineShiftConfig = bottleneck.line_id && lineShiftConfigs.get(bottleneck.line_id);
45849
+ const effectiveShiftConfig = lineShiftConfig || dashboardConfig?.shiftConfig;
45850
+ const shiftResult = getCurrentShift(tz, effectiveShiftConfig);
45446
45851
  const currentShift = shiftResult.shiftId;
45852
+ const operationalDate = shiftResult.date;
45853
+ console.log("\u{1F550} Using shift config for line:", {
45854
+ lineId: bottleneck.line_id,
45855
+ hasLineConfig: !!lineShiftConfig,
45856
+ shiftId: currentShift,
45857
+ date: operationalDate
45858
+ });
45447
45859
  setBottleneckModalData({
45448
45860
  workspaceId: bottleneck.workspace_id,
45449
45861
  workspaceName: bottleneck_workspace.workspace_name || "Unknown Workspace",
@@ -45500,7 +45912,7 @@ function HomeView({
45500
45912
  };
45501
45913
  setBottleneckNotification(errorNotification);
45502
45914
  }
45503
- }, [notificationService, timezone, dashboardConfig]);
45915
+ }, [notificationService, timezone, dashboardConfig, lineShiftConfigs]);
45504
45916
  React24.useEffect(() => {
45505
45917
  const ticketsEnabled = dashboardConfig?.ticketsConfig?.enabled ?? true;
45506
45918
  if (!ticketsEnabled) {
@@ -47443,6 +47855,7 @@ var LeaderboardDetailView = React24.memo(({
47443
47855
  const entityConfig = useEntityConfig();
47444
47856
  const [sortAscending, setSortAscending] = React24.useState(false);
47445
47857
  const timezone = useAppTimezone();
47858
+ const staticShiftConfig = useShiftConfig();
47446
47859
  const [isMobile, setIsMobile] = React24.useState(false);
47447
47860
  React24__namespace.default.useEffect(() => {
47448
47861
  const checkMobile = () => setIsMobile(window.innerWidth < 640);
@@ -47471,9 +47884,27 @@ var LeaderboardDetailView = React24.memo(({
47471
47884
  () => typeof shift === "number" ? shift : typeof shift === "string" ? parseInt(shift) : void 0,
47472
47885
  [shift]
47473
47886
  );
47474
- const availableLineId = Object.keys(configuredLineNames)[0];
47475
- const effectiveLineId = lineId || userAccessibleLineIds?.[0] || availableLineId;
47476
- const { shiftConfig, isLoading: isShiftConfigLoading } = useDynamicShiftConfig(effectiveLineId);
47887
+ const configuredLineIds = React24.useMemo(() => {
47888
+ const allLineIds = getConfiguredLineIds(entityConfig);
47889
+ if (userAccessibleLineIds) {
47890
+ return allLineIds.filter((id3) => userAccessibleLineIds.includes(id3));
47891
+ }
47892
+ return allLineIds;
47893
+ }, [entityConfig, userAccessibleLineIds]);
47894
+ const shouldFetchShiftConfigs = !date && shiftId === void 0;
47895
+ const {
47896
+ shiftConfigMap: multiLineShiftConfigMap,
47897
+ isLoading: isShiftConfigLoading
47898
+ } = useMultiLineShiftConfigs(
47899
+ shouldFetchShiftConfigs ? configuredLineIds : [],
47900
+ staticShiftConfig
47901
+ );
47902
+ const shiftGroups = React24.useMemo(() => {
47903
+ if (!shouldFetchShiftConfigs || isShiftConfigLoading || multiLineShiftConfigMap.size === 0) {
47904
+ return [];
47905
+ }
47906
+ return groupLinesByShift(multiLineShiftConfigMap, timezone || "Asia/Kolkata");
47907
+ }, [shouldFetchShiftConfigs, isShiftConfigLoading, multiLineShiftConfigMap, timezone]);
47477
47908
  const {
47478
47909
  workspaces,
47479
47910
  loading: workspacesLoading,
@@ -47481,21 +47912,28 @@ var LeaderboardDetailView = React24.memo(({
47481
47912
  refreshWorkspaces
47482
47913
  } = useAllWorkspaceMetrics({
47483
47914
  initialDate: date,
47484
- initialShiftId: typeof shift === "number" ? shift : typeof shift === "string" ? parseInt(shift) : void 0,
47915
+ initialShiftId: shiftId,
47485
47916
  allowedLineIds: userAccessibleLineIds,
47486
- // Filter to user's accessible lines only
47487
47917
  enabled: !isShiftConfigLoading
47488
- // Pass enabled flag
47489
47918
  });
47490
47919
  const getShiftName = React24.useCallback((shiftId2) => {
47491
47920
  if (shiftId2 !== void 0) {
47492
- return getShiftNameById(shiftId2, timezone || "Asia/Kolkata", shiftConfig);
47921
+ return getShiftNameById(shiftId2, timezone || "Asia/Kolkata", staticShiftConfig);
47493
47922
  }
47494
- const currentShift = getCurrentShift(timezone || "Asia/Kolkata", shiftConfig);
47495
- return currentShift.shiftName || getShiftNameById(currentShift.shiftId, timezone || "Asia/Kolkata", shiftConfig);
47496
- }, [timezone, shiftConfig]);
47923
+ if (shiftGroups.length > 1) {
47924
+ const shiftNames = shiftGroups.map((g) => g.shiftName).join(" & ");
47925
+ return shiftNames;
47926
+ } else if (shiftGroups.length === 1) {
47927
+ return shiftGroups[0].shiftName;
47928
+ }
47929
+ const currentShift = getCurrentShift(timezone || "Asia/Kolkata", staticShiftConfig);
47930
+ return currentShift.shiftName || getShiftNameById(currentShift.shiftId, timezone || "Asia/Kolkata", staticShiftConfig);
47931
+ }, [timezone, staticShiftConfig, shiftGroups]);
47497
47932
  const getShiftIcon = React24.useCallback((shiftId2) => {
47498
- const effectiveShiftId = shiftId2 !== void 0 ? shiftId2 : getCurrentShift(timezone || "Asia/Kolkata", shiftConfig).shiftId;
47933
+ if (shiftId2 === void 0 && shiftGroups.length > 1) {
47934
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" }) });
47935
+ }
47936
+ const effectiveShiftId = shiftId2 !== void 0 ? shiftId2 : shiftGroups.length === 1 ? shiftGroups[0].shiftId : getCurrentShift(timezone || "Asia/Kolkata", staticShiftConfig).shiftId;
47499
47937
  const shiftNameLower = getShiftName(effectiveShiftId).toLowerCase();
47500
47938
  if (shiftNameLower.includes("day") || shiftNameLower.includes("morning")) {
47501
47939
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" }) });
@@ -47507,7 +47945,7 @@ var LeaderboardDetailView = React24.memo(({
47507
47945
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" }) });
47508
47946
  }
47509
47947
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" }) });
47510
- }, [getShiftName]);
47948
+ }, [getShiftName, shiftGroups, timezone, staticShiftConfig]);
47511
47949
  const formatDate2 = React24.useCallback((date2) => {
47512
47950
  return new Intl.DateTimeFormat("en-US", {
47513
47951
  weekday: "long",
@@ -56845,6 +57283,7 @@ exports.WorkspacePdfGenerator = WorkspacePdfGenerator;
56845
57283
  exports.WorkspaceWhatsAppShareButton = WorkspaceWhatsAppShareButton;
56846
57284
  exports.actionService = actionService;
56847
57285
  exports.apiUtils = apiUtils;
57286
+ exports.areAllLinesOnSameShift = areAllLinesOnSameShift;
56848
57287
  exports.authCoreService = authCoreService;
56849
57288
  exports.authOTPService = authOTPService;
56850
57289
  exports.authRateLimitService = authRateLimitService;
@@ -56890,6 +57329,7 @@ exports.getConfiguredLineIds = getConfiguredLineIds;
56890
57329
  exports.getCoreSessionRecordingProperties = getCoreSessionRecordingProperties;
56891
57330
  exports.getCoreSessionReplayUrl = getCoreSessionReplayUrl;
56892
57331
  exports.getCurrentShift = getCurrentShift;
57332
+ exports.getCurrentShiftForLine = getCurrentShiftForLine;
56893
57333
  exports.getCurrentTimeInZone = getCurrentTimeInZone;
56894
57334
  exports.getDashboardHeaderTimeInZone = getDashboardHeaderTimeInZone;
56895
57335
  exports.getDaysDifferenceInZone = getDaysDifferenceInZone;
@@ -56912,6 +57352,7 @@ exports.getShortWorkspaceDisplayNameAsync = getShortWorkspaceDisplayNameAsync;
56912
57352
  exports.getStoredWorkspaceMappings = getStoredWorkspaceMappings;
56913
57353
  exports.getSubscriptionManager = getSubscriptionManager;
56914
57354
  exports.getThreadMessages = getThreadMessages;
57355
+ exports.getUniformShiftGroup = getUniformShiftGroup;
56915
57356
  exports.getUserThreads = getUserThreads;
56916
57357
  exports.getUserThreadsPaginated = getUserThreadsPaginated;
56917
57358
  exports.getWorkspaceDisplayName = getWorkspaceDisplayName;
@@ -56919,6 +57360,7 @@ exports.getWorkspaceDisplayNameAsync = getWorkspaceDisplayNameAsync;
56919
57360
  exports.getWorkspaceDisplayNamesMap = getWorkspaceDisplayNamesMap;
56920
57361
  exports.getWorkspaceFromUrl = getWorkspaceFromUrl;
56921
57362
  exports.getWorkspaceNavigationParams = getWorkspaceNavigationParams;
57363
+ exports.groupLinesByShift = groupLinesByShift;
56922
57364
  exports.hasAnyShiftData = hasAnyShiftData;
56923
57365
  exports.identifyCoreUser = identifyCoreUser;
56924
57366
  exports.initializeCoreMixpanel = initializeCoreMixpanel;