@optifye/dashboard-core 6.5.8 → 6.5.10

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.
Files changed (3) hide show
  1. package/dist/index.js +699 -190
  2. package/dist/index.mjs +699 -190
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -889,40 +889,140 @@ var dashboardService = {
889
889
  if (hasOutputHourlyData) {
890
890
  console.log("Using new output_hourly column for workspace:", data.workspace_name);
891
891
  console.log("Raw output_hourly data:", outputHourly);
892
- const isNightShift = data.shift_id === 1;
893
- const shiftStart = data.shift_start || (isNightShift ? "22:00" : "06:00");
894
- const shiftEnd = data.shift_end || (isNightShift ? "06:00" : "14:00");
892
+ const isNightShift = data.shift_id === (shiftConfig.nightShift?.id ?? 1);
893
+ const shiftStart = data.shift_start || (isNightShift ? shiftConfig.nightShift?.startTime || "22:00" : shiftConfig.dayShift?.startTime || "06:00");
894
+ const shiftEnd = data.shift_end || (isNightShift ? shiftConfig.nightShift?.endTime || "06:00" : shiftConfig.dayShift?.endTime || "14:00");
895
895
  const startHour = parseInt(shiftStart.split(":")[0]);
896
896
  const startMinute = parseInt(shiftStart.split(":")[1] || "0");
897
897
  const endHour = parseInt(shiftEnd.split(":")[0]);
898
898
  const endMinute = parseInt(shiftEnd.split(":")[1] || "0");
899
899
  let shiftDuration = endHour - startHour;
900
- if (endMinute > startMinute) {
901
- shiftDuration += 1;
902
- }
903
900
  if (shiftDuration <= 0) {
904
901
  shiftDuration += 24;
905
902
  }
903
+ const hasPartialLastHour = endMinute > 0 && endMinute < 60;
904
+ const hourCount = hasPartialLastHour ? Math.ceil(shiftDuration + endMinute / 60) : shiftDuration;
906
905
  let expectedHours = [];
907
- for (let i = 0; i < shiftDuration; i++) {
906
+ for (let i = 0; i < hourCount; i++) {
908
907
  expectedHours.push((startHour + i) % 24);
909
908
  }
910
909
  console.log("Expected shift hours:", expectedHours);
911
910
  console.log("Available data hours:", Object.keys(outputHourly));
912
- hourlyActionCounts = expectedHours.map((expectedHour) => {
913
- let hourData = outputHourly[expectedHour.toString()];
914
- if (!hourData && isNightShift) {
915
- for (const [storedHour, data2] of Object.entries(outputHourly)) {
916
- if (Array.isArray(data2) && data2.length > 0 && data2.some((val) => val > 0)) {
917
- if (storedHour === "18" && expectedHour === 1) {
918
- hourData = data2;
919
- console.log(`Mapping stored hour ${storedHour} to expected hour ${expectedHour}`);
920
- break;
911
+ hourlyActionCounts = expectedHours.map((expectedHour, index) => {
912
+ let hourSum = 0;
913
+ const hourData = outputHourly[expectedHour.toString()] || [];
914
+ if (startMinute > 0) {
915
+ if (index === 0) {
916
+ if (Array.isArray(hourData)) {
917
+ for (let i = startMinute; i < hourData.length; i++) {
918
+ const val = hourData[i];
919
+ if (val !== "x" && typeof val === "number") {
920
+ hourSum += val;
921
+ }
922
+ }
923
+ }
924
+ const nextHour = (expectedHour + 1) % 24;
925
+ const nextHourData = outputHourly[nextHour.toString()] || [];
926
+ if (Array.isArray(nextHourData)) {
927
+ for (let i = 0; i < startMinute && i < nextHourData.length; i++) {
928
+ const val = nextHourData[i];
929
+ if (val !== "x" && typeof val === "number") {
930
+ hourSum += val;
931
+ }
932
+ }
933
+ }
934
+ } else if (index < expectedHours.length - 1) {
935
+ if (Array.isArray(hourData)) {
936
+ for (let i = startMinute; i < hourData.length; i++) {
937
+ const val = hourData[i];
938
+ if (val !== "x" && typeof val === "number") {
939
+ hourSum += val;
940
+ }
941
+ }
942
+ }
943
+ const nextHour = (expectedHour + 1) % 24;
944
+ const nextHourData = outputHourly[nextHour.toString()] || [];
945
+ if (Array.isArray(nextHourData)) {
946
+ for (let i = 0; i < startMinute && i < nextHourData.length; i++) {
947
+ const val = nextHourData[i];
948
+ if (val !== "x" && typeof val === "number") {
949
+ hourSum += val;
950
+ }
951
+ }
952
+ }
953
+ } else {
954
+ if (hasPartialLastHour && endMinute > 0) {
955
+ if (startMinute > 0) {
956
+ if (Array.isArray(hourData)) {
957
+ for (let i = startMinute; i < hourData.length; i++) {
958
+ const val = hourData[i];
959
+ if (val !== "x" && typeof val === "number") {
960
+ hourSum += val;
961
+ }
962
+ }
963
+ }
964
+ const nextHour = (expectedHour + 1) % 24;
965
+ const nextHourData = outputHourly[nextHour.toString()] || [];
966
+ if (Array.isArray(nextHourData)) {
967
+ for (let i = 0; i < endMinute && i < nextHourData.length; i++) {
968
+ const val = nextHourData[i];
969
+ if (val !== "x" && typeof val === "number") {
970
+ hourSum += val;
971
+ }
972
+ }
973
+ }
974
+ } else {
975
+ if (Array.isArray(hourData)) {
976
+ for (let i = 0; i < endMinute && i < hourData.length; i++) {
977
+ const val = hourData[i];
978
+ if (val !== "x" && typeof val === "number") {
979
+ hourSum += val;
980
+ }
981
+ }
982
+ }
983
+ }
984
+ } else {
985
+ if (Array.isArray(hourData)) {
986
+ for (let i = startMinute; i < hourData.length; i++) {
987
+ const val = hourData[i];
988
+ if (val !== "x" && typeof val === "number") {
989
+ hourSum += val;
990
+ }
991
+ }
992
+ }
993
+ const nextHour = (expectedHour + 1) % 24;
994
+ const nextHourData = outputHourly[nextHour.toString()] || [];
995
+ if (Array.isArray(nextHourData)) {
996
+ for (let i = 0; i < startMinute && i < nextHourData.length; i++) {
997
+ const val = nextHourData[i];
998
+ if (val !== "x" && typeof val === "number") {
999
+ hourSum += val;
1000
+ }
1001
+ }
1002
+ }
1003
+ }
1004
+ }
1005
+ } else {
1006
+ if (Array.isArray(hourData)) {
1007
+ hourData.forEach((val) => {
1008
+ if (val !== "x" && typeof val === "number") {
1009
+ hourSum += val;
1010
+ }
1011
+ });
1012
+ }
1013
+ if (index === expectedHours.length - 1 && hasPartialLastHour && endMinute > 0) {
1014
+ hourSum = 0;
1015
+ if (Array.isArray(hourData)) {
1016
+ for (let i = 0; i < endMinute && i < hourData.length; i++) {
1017
+ const val = hourData[i];
1018
+ if (val !== "x" && typeof val === "number") {
1019
+ hourSum += val;
1020
+ }
921
1021
  }
922
1022
  }
923
1023
  }
924
1024
  }
925
- return Array.isArray(hourData) ? hourData.reduce((sum, count) => sum + (count || 0), 0) : 0;
1025
+ return hourSum;
926
1026
  });
927
1027
  console.log("Final hourly action counts:", hourlyActionCounts);
928
1028
  } else {
@@ -2123,136 +2223,142 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
2123
2223
  const now2 = /* @__PURE__ */ new Date();
2124
2224
  const currentHour = now2.getHours();
2125
2225
  const currentMinute = now2.getMinutes();
2126
- let shiftStartHour;
2127
- let shiftEndHour;
2226
+ const dayShiftStart = shiftConfig?.dayShift?.startTime || "08:00";
2227
+ const dayShiftEnd = shiftConfig?.dayShift?.endTime || "19:30";
2228
+ const nightShiftStart = shiftConfig?.nightShift?.startTime || "19:30";
2229
+ const nightShiftEnd = shiftConfig?.nightShift?.endTime || "06:00";
2230
+ const parseShiftTime = (timeStr) => {
2231
+ const [hour, minute] = timeStr.split(":").map(Number);
2232
+ return { hour, minute };
2233
+ };
2234
+ let shiftStart, shiftEnd;
2128
2235
  if (currentShiftId === 0) {
2129
- shiftStartHour = 9;
2130
- shiftEndHour = 18;
2236
+ shiftStart = parseShiftTime(dayShiftStart);
2237
+ shiftEnd = parseShiftTime(dayShiftEnd);
2131
2238
  } else {
2132
- shiftStartHour = 20;
2133
- shiftEndHour = 3;
2239
+ shiftStart = parseShiftTime(nightShiftStart);
2240
+ shiftEnd = parseShiftTime(nightShiftEnd);
2134
2241
  }
2135
2242
  let elapsedMinutes = 0;
2136
2243
  if (currentShiftId === 0) {
2137
- if (currentHour >= shiftStartHour && currentHour < shiftEndHour) {
2138
- elapsedMinutes = (currentHour - shiftStartHour) * 60 + currentMinute;
2139
- } else if (currentHour >= shiftEndHour) {
2140
- elapsedMinutes = (shiftEndHour - shiftStartHour) * 60;
2244
+ const shiftStartTotalMinutes = shiftStart.hour * 60 + shiftStart.minute;
2245
+ const currentTotalMinutes = currentHour * 60 + currentMinute;
2246
+ const shiftEndTotalMinutes = shiftEnd.hour * 60 + shiftEnd.minute;
2247
+ if (currentTotalMinutes >= shiftStartTotalMinutes && currentTotalMinutes < shiftEndTotalMinutes) {
2248
+ elapsedMinutes = currentTotalMinutes - shiftStartTotalMinutes;
2249
+ } else if (currentTotalMinutes >= shiftEndTotalMinutes) {
2250
+ elapsedMinutes = shiftEndTotalMinutes - shiftStartTotalMinutes;
2141
2251
  }
2142
2252
  } else {
2143
- if (currentHour >= shiftStartHour) {
2144
- elapsedMinutes = (currentHour - shiftStartHour) * 60 + currentMinute;
2145
- } else if (currentHour < shiftEndHour) {
2146
- elapsedMinutes = (24 - shiftStartHour + currentHour) * 60 + currentMinute;
2253
+ const shiftStartTotalMinutes = shiftStart.hour * 60 + shiftStart.minute;
2254
+ const currentTotalMinutes = currentHour * 60 + currentMinute;
2255
+ const shiftEndTotalMinutes = shiftEnd.hour * 60 + shiftEnd.minute;
2256
+ if (currentHour >= shiftStart.hour || currentHour < shiftEnd.hour) {
2257
+ if (currentHour >= shiftStart.hour) {
2258
+ elapsedMinutes = currentTotalMinutes - shiftStartTotalMinutes;
2259
+ } else {
2260
+ elapsedMinutes = 24 * 60 - shiftStartTotalMinutes + currentTotalMinutes;
2261
+ }
2262
+ if (currentHour >= shiftEnd.hour && currentTotalMinutes >= shiftEndTotalMinutes) {
2263
+ const totalShiftMinutes = 24 * 60 - shiftStartTotalMinutes + shiftEndTotalMinutes;
2264
+ elapsedMinutes = Math.min(elapsedMinutes, totalShiftMinutes);
2265
+ }
2147
2266
  }
2148
2267
  }
2149
2268
  const tableName = `performance_metrics_${companyId.replace(/-/g, "_")}`;
2150
- const query = `
2151
- WITH workspace_uptime AS (
2152
- SELECT
2153
- workspace_id,
2154
- workspace_display_name,
2155
- idle_time_hourly,
2156
- output_hourly,
2157
- shift_start,
2158
- shift_end
2159
- FROM ${tableName}
2160
- WHERE date = $1::date
2161
- AND shift_id = $2
2162
- ),
2163
- calculated_uptime AS (
2164
- SELECT
2165
- workspace_id,
2166
- workspace_display_name,
2167
- -- Calculate actual minutes from hourly data
2168
- (
2169
- SELECT COALESCE(SUM(
2170
- CASE
2171
- WHEN jsonb_array_length(idle_time_hourly->key::text) >= 58 THEN 60
2172
- WHEN jsonb_array_length(idle_time_hourly->key::text) > 0 THEN jsonb_array_length(idle_time_hourly->key::text)
2173
- ELSE 0
2174
- END
2175
- ), 0)
2176
- FROM jsonb_object_keys(idle_time_hourly) AS key
2177
- WHERE key::int >= $3 AND key::int < $4
2178
- ) +
2179
- -- Add current hour's data if applicable
2180
- CASE
2181
- WHEN $4::int >= $3 AND $4::int < $5 THEN
2182
- LEAST($6::int,
2183
- COALESCE(jsonb_array_length(idle_time_hourly->$4::text), 0))
2184
- ELSE 0
2185
- END as actual_minutes
2186
- FROM workspace_uptime
2187
- )
2188
- SELECT
2189
- workspace_id,
2190
- workspace_display_name,
2191
- actual_minutes,
2192
- $7::int as expected_minutes,
2193
- ROUND(
2194
- CASE
2195
- WHEN $7::int > 0 THEN (actual_minutes::numeric / $7::numeric) * 100
2196
- ELSE 100
2197
- END, 1
2198
- ) as uptime_percentage
2199
- FROM calculated_uptime
2200
- `;
2201
2269
  try {
2202
- const { data, error } = await supabase.rpc("sql_query", {
2203
- query_text: query,
2204
- params: [
2205
- currentDate,
2206
- currentShiftId,
2207
- shiftStartHour,
2208
- currentHour,
2209
- shiftEndHour,
2210
- currentMinute,
2211
- elapsedMinutes
2212
- ]
2213
- }).single();
2270
+ const { data: queryData, error } = await supabase.from(tableName).select("workspace_id, workspace_display_name, output_hourly").eq("date", currentDate).eq("shift_id", currentShiftId);
2214
2271
  if (error) {
2215
- const { data: queryData, error: queryError } = await supabase.from(tableName).select("workspace_id, workspace_display_name, idle_time_hourly, output_hourly").eq("date", currentDate).eq("shift_id", currentShiftId);
2216
- if (queryError) {
2217
- console.error("Error fetching performance metrics:", queryError);
2218
- return /* @__PURE__ */ new Map();
2219
- }
2220
- const uptimeMap2 = /* @__PURE__ */ new Map();
2221
- for (const record of queryData || []) {
2222
- let actualMinutes = 0;
2223
- const idleTimeHourly = record.idle_time_hourly || {};
2224
- if (idleTimeHourly && typeof idleTimeHourly === "object") {
2225
- for (const [hour, dataArray] of Object.entries(idleTimeHourly)) {
2226
- const hourNum = parseInt(hour);
2227
- if (hourNum >= shiftStartHour && hourNum < currentHour) {
2228
- const arrayLength = Array.isArray(dataArray) ? dataArray.length : 0;
2229
- actualMinutes += arrayLength >= 58 ? 60 : arrayLength;
2230
- } else if (hourNum === currentHour && currentHour >= shiftStartHour) {
2231
- const arrayLength = Array.isArray(dataArray) ? dataArray.length : 0;
2232
- actualMinutes += Math.min(currentMinute, arrayLength);
2272
+ console.error("Error fetching performance metrics:", error);
2273
+ return /* @__PURE__ */ new Map();
2274
+ }
2275
+ const uptimeMap = /* @__PURE__ */ new Map();
2276
+ for (const record of queryData || []) {
2277
+ let actualMinutes = 0;
2278
+ let totalPossibleMinutes = 0;
2279
+ const outputHourly = record.output_hourly || {};
2280
+ const hoursElapsed = Math.ceil(elapsedMinutes / 60);
2281
+ for (let hourIndex = 0; hourIndex < hoursElapsed; hourIndex++) {
2282
+ const actualHour = (shiftStart.hour + hourIndex) % 24;
2283
+ let hourData = [];
2284
+ let minutesInThisHour = 60;
2285
+ if (shiftStart.minute > 0) {
2286
+ if (hourIndex === 0) {
2287
+ const firstHourData = outputHourly[actualHour.toString()] || [];
2288
+ const nextHour = (actualHour + 1) % 24;
2289
+ const nextHourData = outputHourly[nextHour.toString()] || [];
2290
+ hourData = [
2291
+ ...Array.isArray(firstHourData) ? firstHourData.slice(shiftStart.minute) : [],
2292
+ ...Array.isArray(nextHourData) ? nextHourData.slice(0, shiftStart.minute) : []
2293
+ ];
2294
+ } else if (hourIndex < hoursElapsed - 1) {
2295
+ const currentHourData = outputHourly[actualHour.toString()] || [];
2296
+ const nextHour = (actualHour + 1) % 24;
2297
+ const nextHourData = outputHourly[nextHour.toString()] || [];
2298
+ hourData = [
2299
+ ...Array.isArray(currentHourData) ? currentHourData.slice(shiftStart.minute) : [],
2300
+ ...Array.isArray(nextHourData) ? nextHourData.slice(0, shiftStart.minute) : []
2301
+ ];
2302
+ } else {
2303
+ const isLastHourPartial = hourIndex === hoursElapsed - 1 && elapsedMinutes % 60 > 0;
2304
+ if (isLastHourPartial) {
2305
+ minutesInThisHour = elapsedMinutes % 60;
2306
+ const currentHourData = outputHourly[actualHour.toString()] || [];
2307
+ if (shiftStart.minute > 0) {
2308
+ const nextHour = (actualHour + 1) % 24;
2309
+ const nextHourData = outputHourly[nextHour.toString()] || [];
2310
+ const firstPart = Array.isArray(currentHourData) ? currentHourData.slice(shiftStart.minute) : [];
2311
+ const secondPart = Array.isArray(nextHourData) ? nextHourData.slice(0, Math.min(shiftStart.minute, minutesInThisHour)) : [];
2312
+ hourData = [...firstPart, ...secondPart].slice(0, minutesInThisHour);
2313
+ } else {
2314
+ hourData = Array.isArray(currentHourData) ? currentHourData.slice(0, minutesInThisHour) : [];
2315
+ }
2316
+ } else {
2317
+ const currentHourData = outputHourly[actualHour.toString()] || [];
2318
+ const nextHour = (actualHour + 1) % 24;
2319
+ const nextHourData = outputHourly[nextHour.toString()] || [];
2320
+ hourData = [
2321
+ ...Array.isArray(currentHourData) ? currentHourData.slice(shiftStart.minute) : [],
2322
+ ...Array.isArray(nextHourData) ? nextHourData.slice(0, shiftStart.minute) : []
2323
+ ];
2324
+ }
2325
+ }
2326
+ } else {
2327
+ hourData = outputHourly[actualHour.toString()] || [];
2328
+ if (hourIndex === hoursElapsed - 1) {
2329
+ const remainingMinutes = elapsedMinutes % 60;
2330
+ if (remainingMinutes > 0) {
2331
+ minutesInThisHour = remainingMinutes;
2332
+ hourData = Array.isArray(hourData) ? hourData.slice(0, minutesInThisHour) : [];
2233
2333
  }
2234
2334
  }
2235
2335
  }
2236
- const percentage = elapsedMinutes > 0 ? Math.round(actualMinutes / elapsedMinutes * 1e3) / 10 : 100;
2237
- uptimeMap2.set(record.workspace_id, {
2238
- expectedMinutes: elapsedMinutes,
2239
- actualMinutes,
2240
- percentage,
2241
- lastCalculated: (/* @__PURE__ */ new Date()).toISOString()
2242
- });
2243
- }
2244
- return uptimeMap2;
2245
- }
2246
- const uptimeMap = /* @__PURE__ */ new Map();
2247
- if (Array.isArray(data)) {
2248
- for (const record of data) {
2249
- uptimeMap.set(record.workspace_id, {
2250
- expectedMinutes: record.expected_minutes,
2251
- actualMinutes: record.actual_minutes,
2252
- percentage: record.uptime_percentage,
2253
- lastCalculated: (/* @__PURE__ */ new Date()).toISOString()
2254
- });
2336
+ const hasXArchitecture = Array.isArray(hourData) && hourData.some((val) => val === "x");
2337
+ if (hasXArchitecture) {
2338
+ const xCount = hourData.filter((val) => val === "x").length;
2339
+ if (xCount <= 2) {
2340
+ actualMinutes += minutesInThisHour;
2341
+ } else {
2342
+ const uptimeCount = hourData.filter((val) => val !== "x").length;
2343
+ actualMinutes += Math.min(uptimeCount, minutesInThisHour);
2344
+ }
2345
+ } else {
2346
+ const arrayLength = Array.isArray(hourData) ? hourData.length : 0;
2347
+ if (arrayLength >= Math.min(58, minutesInThisHour - 2)) {
2348
+ actualMinutes += minutesInThisHour;
2349
+ } else {
2350
+ actualMinutes += Math.min(arrayLength, minutesInThisHour);
2351
+ }
2352
+ }
2353
+ totalPossibleMinutes += minutesInThisHour;
2255
2354
  }
2355
+ const percentage = totalPossibleMinutes > 0 ? Math.round(actualMinutes / totalPossibleMinutes * 1e3) / 10 : 100;
2356
+ uptimeMap.set(record.workspace_id, {
2357
+ expectedMinutes: totalPossibleMinutes,
2358
+ actualMinutes,
2359
+ percentage,
2360
+ lastCalculated: (/* @__PURE__ */ new Date()).toISOString()
2361
+ });
2256
2362
  }
2257
2363
  return uptimeMap;
2258
2364
  } catch (error) {
@@ -5703,39 +5809,139 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5703
5809
  console.log("Using new output_hourly column for workspace (fallback):", recentData.workspace_name);
5704
5810
  console.log("Raw output_hourly data (fallback):", outputHourly2);
5705
5811
  const isNightShift = recentData.shift_id === 1;
5706
- const shiftStart = recentData.shift_start || (isNightShift ? "22:00" : "06:00");
5707
- const shiftEnd = recentData.shift_end || (isNightShift ? "06:00" : "14:00");
5812
+ const shiftStart = recentData.shift_start || (isNightShift ? shiftConfig.nightShift?.startTime || "22:00" : shiftConfig.dayShift?.startTime || "06:00");
5813
+ const shiftEnd = recentData.shift_end || (isNightShift ? shiftConfig.nightShift?.endTime || "06:00" : shiftConfig.dayShift?.endTime || "14:00");
5708
5814
  const startHour = parseInt(shiftStart.split(":")[0]);
5709
5815
  const startMinute = parseInt(shiftStart.split(":")[1] || "0");
5710
5816
  const endHour = parseInt(shiftEnd.split(":")[0]);
5711
5817
  const endMinute = parseInt(shiftEnd.split(":")[1] || "0");
5712
5818
  let shiftDuration = endHour - startHour;
5713
- if (endMinute > startMinute) {
5714
- shiftDuration += 1;
5715
- }
5716
5819
  if (shiftDuration <= 0) {
5717
5820
  shiftDuration += 24;
5718
5821
  }
5822
+ const hasPartialLastHour = endMinute > 0 && endMinute < 60;
5823
+ const hourCount = hasPartialLastHour ? Math.ceil(shiftDuration + endMinute / 60) : shiftDuration;
5719
5824
  let expectedHours = [];
5720
- for (let i = 0; i < shiftDuration; i++) {
5825
+ for (let i = 0; i < hourCount; i++) {
5721
5826
  expectedHours.push((startHour + i) % 24);
5722
5827
  }
5723
5828
  console.log("Expected shift hours (fallback):", expectedHours);
5724
5829
  console.log("Available data hours (fallback):", Object.keys(outputHourly2));
5725
- hourlyActionCounts2 = expectedHours.map((expectedHour) => {
5726
- let hourData = outputHourly2[expectedHour.toString()];
5727
- if (!hourData && isNightShift) {
5728
- for (const [storedHour, data2] of Object.entries(outputHourly2)) {
5729
- if (Array.isArray(data2) && data2.length > 0 && data2.some((val) => val > 0)) {
5730
- if (storedHour === "18" && expectedHour === 1) {
5731
- hourData = data2;
5732
- console.log(`Mapping stored hour ${storedHour} to expected hour ${expectedHour} (fallback)`);
5733
- break;
5830
+ hourlyActionCounts2 = expectedHours.map((expectedHour, index) => {
5831
+ let hourSum = 0;
5832
+ const hourData = outputHourly2[expectedHour.toString()] || [];
5833
+ if (startMinute > 0) {
5834
+ if (index === 0) {
5835
+ if (Array.isArray(hourData)) {
5836
+ for (let i = startMinute; i < hourData.length; i++) {
5837
+ const val = hourData[i];
5838
+ if (val !== "x" && typeof val === "number") {
5839
+ hourSum += val;
5840
+ }
5841
+ }
5842
+ }
5843
+ const nextHour = (expectedHour + 1) % 24;
5844
+ const nextHourData = outputHourly2[nextHour.toString()] || [];
5845
+ if (Array.isArray(nextHourData)) {
5846
+ for (let i = 0; i < startMinute && i < nextHourData.length; i++) {
5847
+ const val = nextHourData[i];
5848
+ if (val !== "x" && typeof val === "number") {
5849
+ hourSum += val;
5850
+ }
5851
+ }
5852
+ }
5853
+ } else if (index < expectedHours.length - 1) {
5854
+ if (Array.isArray(hourData)) {
5855
+ for (let i = startMinute; i < hourData.length; i++) {
5856
+ const val = hourData[i];
5857
+ if (val !== "x" && typeof val === "number") {
5858
+ hourSum += val;
5859
+ }
5860
+ }
5861
+ }
5862
+ const nextHour = (expectedHour + 1) % 24;
5863
+ const nextHourData = outputHourly2[nextHour.toString()] || [];
5864
+ if (Array.isArray(nextHourData)) {
5865
+ for (let i = 0; i < startMinute && i < nextHourData.length; i++) {
5866
+ const val = nextHourData[i];
5867
+ if (val !== "x" && typeof val === "number") {
5868
+ hourSum += val;
5869
+ }
5870
+ }
5871
+ }
5872
+ } else {
5873
+ if (hasPartialLastHour && endMinute > 0) {
5874
+ if (startMinute > 0) {
5875
+ if (Array.isArray(hourData)) {
5876
+ for (let i = startMinute; i < hourData.length; i++) {
5877
+ const val = hourData[i];
5878
+ if (val !== "x" && typeof val === "number") {
5879
+ hourSum += val;
5880
+ }
5881
+ }
5882
+ }
5883
+ const nextHour = (expectedHour + 1) % 24;
5884
+ const nextHourData = outputHourly2[nextHour.toString()] || [];
5885
+ if (Array.isArray(nextHourData)) {
5886
+ for (let i = 0; i < endMinute && i < nextHourData.length; i++) {
5887
+ const val = nextHourData[i];
5888
+ if (val !== "x" && typeof val === "number") {
5889
+ hourSum += val;
5890
+ }
5891
+ }
5892
+ }
5893
+ } else {
5894
+ if (Array.isArray(hourData)) {
5895
+ for (let i = 0; i < endMinute && i < hourData.length; i++) {
5896
+ const val = hourData[i];
5897
+ if (val !== "x" && typeof val === "number") {
5898
+ hourSum += val;
5899
+ }
5900
+ }
5901
+ }
5902
+ }
5903
+ } else {
5904
+ if (Array.isArray(hourData)) {
5905
+ for (let i = startMinute; i < hourData.length; i++) {
5906
+ const val = hourData[i];
5907
+ if (val !== "x" && typeof val === "number") {
5908
+ hourSum += val;
5909
+ }
5910
+ }
5911
+ }
5912
+ const nextHour = (expectedHour + 1) % 24;
5913
+ const nextHourData = outputHourly2[nextHour.toString()] || [];
5914
+ if (Array.isArray(nextHourData)) {
5915
+ for (let i = 0; i < startMinute && i < nextHourData.length; i++) {
5916
+ const val = nextHourData[i];
5917
+ if (val !== "x" && typeof val === "number") {
5918
+ hourSum += val;
5919
+ }
5920
+ }
5921
+ }
5922
+ }
5923
+ }
5924
+ } else {
5925
+ if (Array.isArray(hourData)) {
5926
+ hourData.forEach((val) => {
5927
+ if (val !== "x" && typeof val === "number") {
5928
+ hourSum += val;
5929
+ }
5930
+ });
5931
+ }
5932
+ if (index === expectedHours.length - 1 && hasPartialLastHour && endMinute > 0) {
5933
+ hourSum = 0;
5934
+ if (Array.isArray(hourData)) {
5935
+ for (let i = 0; i < endMinute && i < hourData.length; i++) {
5936
+ const val = hourData[i];
5937
+ if (val !== "x" && typeof val === "number") {
5938
+ hourSum += val;
5939
+ }
5734
5940
  }
5735
5941
  }
5736
5942
  }
5737
5943
  }
5738
- return Array.isArray(hourData) ? hourData.reduce((sum, count) => sum + (count || 0), 0) : 0;
5944
+ return hourSum;
5739
5945
  });
5740
5946
  console.log("Final hourly action counts (fallback):", hourlyActionCounts2);
5741
5947
  } else {
@@ -5762,8 +5968,8 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5762
5968
  date: recentData.date,
5763
5969
  shift_id: recentData.shift_id,
5764
5970
  action_name: recentData.action_name || "",
5765
- shift_start: recentData.shift_start || "06:00",
5766
- shift_end: recentData.shift_end || "14:00",
5971
+ shift_start: recentData.shift_start || (recentData.shift_id === 1 ? shiftConfig.nightShift?.startTime || "22:00" : shiftConfig.dayShift?.startTime || "06:00"),
5972
+ shift_end: recentData.shift_end || (recentData.shift_id === 1 ? shiftConfig.nightShift?.endTime || "06:00" : shiftConfig.dayShift?.endTime || "14:00"),
5767
5973
  shift_type: recentData.shift_type || (recentData.shift_id === 0 ? "Day" : "Night"),
5768
5974
  pph_threshold: recentData.pph_threshold || 0,
5769
5975
  target_output: recentData.total_day_output || 0,
@@ -5819,39 +6025,139 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5819
6025
  console.log("\u2705 Using new output_hourly column for workspace:", data.workspace_name);
5820
6026
  console.log("Raw output_hourly data:", JSON.stringify(outputHourly));
5821
6027
  const isNightShift = data.shift_id === 1;
5822
- const shiftStart = data.shift_start || (isNightShift ? "22:00" : "06:00");
5823
- const shiftEnd = data.shift_end || (isNightShift ? "06:00" : "14:00");
6028
+ const shiftStart = data.shift_start || (isNightShift ? shiftConfig.nightShift?.startTime || "22:00" : shiftConfig.dayShift?.startTime || "06:00");
6029
+ const shiftEnd = data.shift_end || (isNightShift ? shiftConfig.nightShift?.endTime || "06:00" : shiftConfig.dayShift?.endTime || "14:00");
5824
6030
  const startHour = parseInt(shiftStart.split(":")[0]);
5825
6031
  const startMinute = parseInt(shiftStart.split(":")[1] || "0");
5826
6032
  const endHour = parseInt(shiftEnd.split(":")[0]);
5827
6033
  const endMinute = parseInt(shiftEnd.split(":")[1] || "0");
5828
6034
  let shiftDuration = endHour - startHour;
5829
- if (endMinute > startMinute) {
5830
- shiftDuration += 1;
5831
- }
5832
6035
  if (shiftDuration <= 0) {
5833
6036
  shiftDuration += 24;
5834
6037
  }
6038
+ const hasPartialLastHour = endMinute > 0 && endMinute < 60;
6039
+ const hourCount = hasPartialLastHour ? Math.ceil(shiftDuration + endMinute / 60) : shiftDuration;
5835
6040
  let expectedHours = [];
5836
- for (let i = 0; i < shiftDuration; i++) {
6041
+ for (let i = 0; i < hourCount; i++) {
5837
6042
  expectedHours.push((startHour + i) % 24);
5838
6043
  }
5839
6044
  console.log("Expected shift hours:", expectedHours);
5840
6045
  console.log("Available data hours:", Object.keys(outputHourly));
5841
- hourlyActionCounts = expectedHours.map((expectedHour) => {
5842
- let hourData = outputHourly[expectedHour.toString()];
5843
- if (!hourData && isNightShift) {
5844
- for (const [storedHour, data2] of Object.entries(outputHourly)) {
5845
- if (Array.isArray(data2) && data2.length > 0 && data2.some((val) => val > 0)) {
5846
- if (storedHour === "18" && expectedHour === 1) {
5847
- hourData = data2;
5848
- console.log(`Mapping stored hour ${storedHour} to expected hour ${expectedHour}`);
5849
- break;
6046
+ hourlyActionCounts = expectedHours.map((expectedHour, index) => {
6047
+ let hourSum = 0;
6048
+ const hourData = outputHourly[expectedHour.toString()] || [];
6049
+ if (startMinute > 0) {
6050
+ if (index === 0) {
6051
+ if (Array.isArray(hourData)) {
6052
+ for (let i = startMinute; i < hourData.length; i++) {
6053
+ const val = hourData[i];
6054
+ if (val !== "x" && typeof val === "number") {
6055
+ hourSum += val;
6056
+ }
6057
+ }
6058
+ }
6059
+ const nextHour = (expectedHour + 1) % 24;
6060
+ const nextHourData = outputHourly[nextHour.toString()] || [];
6061
+ if (Array.isArray(nextHourData)) {
6062
+ for (let i = 0; i < startMinute && i < nextHourData.length; i++) {
6063
+ const val = nextHourData[i];
6064
+ if (val !== "x" && typeof val === "number") {
6065
+ hourSum += val;
6066
+ }
6067
+ }
6068
+ }
6069
+ } else if (index < expectedHours.length - 1) {
6070
+ if (Array.isArray(hourData)) {
6071
+ for (let i = startMinute; i < hourData.length; i++) {
6072
+ const val = hourData[i];
6073
+ if (val !== "x" && typeof val === "number") {
6074
+ hourSum += val;
6075
+ }
6076
+ }
6077
+ }
6078
+ const nextHour = (expectedHour + 1) % 24;
6079
+ const nextHourData = outputHourly[nextHour.toString()] || [];
6080
+ if (Array.isArray(nextHourData)) {
6081
+ for (let i = 0; i < startMinute && i < nextHourData.length; i++) {
6082
+ const val = nextHourData[i];
6083
+ if (val !== "x" && typeof val === "number") {
6084
+ hourSum += val;
6085
+ }
6086
+ }
6087
+ }
6088
+ } else {
6089
+ if (hasPartialLastHour && endMinute > 0) {
6090
+ if (startMinute > 0) {
6091
+ if (Array.isArray(hourData)) {
6092
+ for (let i = startMinute; i < hourData.length; i++) {
6093
+ const val = hourData[i];
6094
+ if (val !== "x" && typeof val === "number") {
6095
+ hourSum += val;
6096
+ }
6097
+ }
6098
+ }
6099
+ const nextHour = (expectedHour + 1) % 24;
6100
+ const nextHourData = outputHourly[nextHour.toString()] || [];
6101
+ if (Array.isArray(nextHourData)) {
6102
+ for (let i = 0; i < endMinute && i < nextHourData.length; i++) {
6103
+ const val = nextHourData[i];
6104
+ if (val !== "x" && typeof val === "number") {
6105
+ hourSum += val;
6106
+ }
6107
+ }
6108
+ }
6109
+ } else {
6110
+ if (Array.isArray(hourData)) {
6111
+ for (let i = 0; i < endMinute && i < hourData.length; i++) {
6112
+ const val = hourData[i];
6113
+ if (val !== "x" && typeof val === "number") {
6114
+ hourSum += val;
6115
+ }
6116
+ }
6117
+ }
6118
+ }
6119
+ } else {
6120
+ if (Array.isArray(hourData)) {
6121
+ for (let i = startMinute; i < hourData.length; i++) {
6122
+ const val = hourData[i];
6123
+ if (val !== "x" && typeof val === "number") {
6124
+ hourSum += val;
6125
+ }
6126
+ }
6127
+ }
6128
+ const nextHour = (expectedHour + 1) % 24;
6129
+ const nextHourData = outputHourly[nextHour.toString()] || [];
6130
+ if (Array.isArray(nextHourData)) {
6131
+ for (let i = 0; i < startMinute && i < nextHourData.length; i++) {
6132
+ const val = nextHourData[i];
6133
+ if (val !== "x" && typeof val === "number") {
6134
+ hourSum += val;
6135
+ }
6136
+ }
6137
+ }
6138
+ }
6139
+ }
6140
+ } else {
6141
+ if (Array.isArray(hourData)) {
6142
+ hourData.forEach((val) => {
6143
+ if (val !== "x" && typeof val === "number") {
6144
+ hourSum += val;
6145
+ }
6146
+ });
6147
+ }
6148
+ if (index === expectedHours.length - 1 && hasPartialLastHour && endMinute > 0) {
6149
+ hourSum = 0;
6150
+ if (Array.isArray(hourData)) {
6151
+ for (let i = 0; i < endMinute && i < hourData.length; i++) {
6152
+ const val = hourData[i];
6153
+ if (val !== "x" && typeof val === "number") {
6154
+ hourSum += val;
6155
+ }
5850
6156
  }
5851
6157
  }
5852
6158
  }
5853
6159
  }
5854
- return Array.isArray(hourData) ? hourData.reduce((sum, count) => sum + (count || 0), 0) : 0;
6160
+ return hourSum;
5855
6161
  });
5856
6162
  console.log("Final hourly action counts:", hourlyActionCounts);
5857
6163
  } else {
@@ -5881,8 +6187,8 @@ var useWorkspaceDetailedMetrics = (workspaceId, date, shiftId) => {
5881
6187
  date: data.date,
5882
6188
  shift_id: data.shift_id,
5883
6189
  action_name: data.action_name || "",
5884
- shift_start: data.shift_start || "06:00",
5885
- shift_end: data.shift_end || "14:00",
6190
+ shift_start: data.shift_start || (data.shift_id === 1 ? shiftConfig.nightShift?.startTime || "22:00" : shiftConfig.dayShift?.startTime || "06:00"),
6191
+ shift_end: data.shift_end || (data.shift_id === 1 ? shiftConfig.nightShift?.endTime || "06:00" : shiftConfig.dayShift?.endTime || "14:00"),
5886
6192
  shift_type: data.shift_type || (data.shift_id === 0 ? "Day" : "Night"),
5887
6193
  pph_threshold: data.pph_threshold || 0,
5888
6194
  target_output: data.total_day_output || 0,
@@ -21273,18 +21579,41 @@ var HourlyOutputChartComponent = ({
21273
21579
  return { hour, minute, decimalHour };
21274
21580
  };
21275
21581
  const shiftStartTime = getTimeFromTimeString(shiftStart);
21276
- const calculateShiftDuration = React19__namespace.default.useMemo(() => {
21582
+ const { shiftDuration, shiftEndTime, hasPartialLastHour } = React19__namespace.default.useMemo(() => {
21583
+ console.log("[HourlyOutputChart] Calculating shift duration with:", {
21584
+ shiftStart,
21585
+ shiftEnd,
21586
+ shiftStartTime
21587
+ });
21277
21588
  if (!shiftEnd) {
21278
- return 11;
21589
+ console.log("[HourlyOutputChart] No shiftEnd provided, using default 11 hours");
21590
+ return {
21591
+ shiftDuration: 11,
21592
+ shiftEndTime: null,
21593
+ hasPartialLastHour: false
21594
+ };
21279
21595
  }
21280
21596
  const endTime = getTimeFromTimeString(shiftEnd);
21281
21597
  let duration = endTime.decimalHour - shiftStartTime.decimalHour;
21282
21598
  if (duration <= 0) {
21283
21599
  duration += 24;
21284
21600
  }
21285
- return Math.round(duration);
21601
+ const hasPartial = endTime.minute > 0 && endTime.minute < 60;
21602
+ const hourCount = hasPartial ? Math.ceil(duration) : Math.round(duration);
21603
+ console.log("[HourlyOutputChart] Shift calculation results:", {
21604
+ endTime,
21605
+ duration,
21606
+ hasPartial,
21607
+ hourCount
21608
+ });
21609
+ return {
21610
+ shiftDuration: hourCount,
21611
+ shiftEndTime: endTime,
21612
+ hasPartialLastHour: hasPartial
21613
+ };
21286
21614
  }, [shiftEnd, shiftStartTime.decimalHour]);
21287
- const SHIFT_DURATION = calculateShiftDuration;
21615
+ const SHIFT_DURATION = shiftDuration;
21616
+ shiftEndTime ? shiftEndTime.hour : (shiftStartTime.hour + SHIFT_DURATION) % 24;
21288
21617
  const [animatedData, setAnimatedData] = React19__namespace.default.useState(
21289
21618
  () => Array(SHIFT_DURATION).fill(0)
21290
21619
  );
@@ -21395,12 +21724,19 @@ var HourlyOutputChartComponent = ({
21395
21724
  };
21396
21725
  }, []);
21397
21726
  const formatHour = React19__namespace.default.useCallback((hourIndex) => {
21727
+ const isLastHour = hourIndex === SHIFT_DURATION - 1;
21398
21728
  const startDecimalHour = shiftStartTime.decimalHour + hourIndex;
21399
21729
  const startHour = Math.floor(startDecimalHour) % 24;
21400
21730
  const startMinute = Math.round(startDecimalHour % 1 * 60);
21401
- const endDecimalHour = startDecimalHour + 1;
21402
- const endHour = Math.floor(endDecimalHour) % 24;
21403
- const endMinute = Math.round(endDecimalHour % 1 * 60);
21731
+ let endHour, endMinute;
21732
+ if (isLastHour && shiftEndTime) {
21733
+ endHour = shiftEndTime.hour;
21734
+ endMinute = shiftEndTime.minute;
21735
+ } else {
21736
+ const endDecimalHour = startDecimalHour + 1;
21737
+ endHour = Math.floor(endDecimalHour) % 24;
21738
+ endMinute = Math.round(endDecimalHour % 1 * 60);
21739
+ }
21404
21740
  const formatTime3 = (h, m) => {
21405
21741
  const period = h >= 12 ? "PM" : "AM";
21406
21742
  const hour12 = h === 0 ? 12 : h > 12 ? h - 12 : h;
@@ -21410,26 +21746,85 @@ var HourlyOutputChartComponent = ({
21410
21746
  return `${hour12}:${m.toString().padStart(2, "0")}${period}`;
21411
21747
  };
21412
21748
  return `${formatTime3(startHour, startMinute)}-${formatTime3(endHour, endMinute)}`;
21413
- }, [shiftStartTime.decimalHour]);
21749
+ }, [shiftStartTime.decimalHour, SHIFT_DURATION, shiftEndTime]);
21414
21750
  const formatTimeRange = React19__namespace.default.useCallback((hourIndex) => {
21751
+ const isLastHour = hourIndex === SHIFT_DURATION - 1;
21415
21752
  const startDecimalHour = shiftStartTime.decimalHour + hourIndex;
21416
21753
  const startHour = Math.floor(startDecimalHour) % 24;
21417
21754
  const startMinute = Math.round(startDecimalHour % 1 * 60);
21418
- const endDecimalHour = startDecimalHour + 1;
21419
- const endHour = Math.floor(endDecimalHour) % 24;
21420
- const endMinute = Math.round(endDecimalHour % 1 * 60);
21755
+ let endHour, endMinute;
21756
+ if (isLastHour && shiftEndTime) {
21757
+ endHour = shiftEndTime.hour;
21758
+ endMinute = shiftEndTime.minute;
21759
+ } else {
21760
+ const endDecimalHour = startDecimalHour + 1;
21761
+ endHour = Math.floor(endDecimalHour) % 24;
21762
+ endMinute = Math.round(endDecimalHour % 1 * 60);
21763
+ }
21421
21764
  const formatTime3 = (h, m) => {
21422
21765
  const period = h >= 12 ? "PM" : "AM";
21423
21766
  const hour12 = h === 0 ? 12 : h > 12 ? h - 12 : h;
21424
21767
  return `${hour12}:${m.toString().padStart(2, "0")} ${period}`;
21425
21768
  };
21426
21769
  return `${formatTime3(startHour, startMinute)} - ${formatTime3(endHour, endMinute)}`;
21427
- }, [shiftStartTime.decimalHour]);
21770
+ }, [shiftStartTime.decimalHour, SHIFT_DURATION, shiftEndTime]);
21428
21771
  const chartData = React19__namespace.default.useMemo(() => {
21429
21772
  return Array.from({ length: SHIFT_DURATION }, (_, i) => {
21430
21773
  const actualHour = (shiftStartTime.hour + i) % 24;
21431
- const idleArray = idleTimeHourly?.[actualHour.toString()] || [];
21432
- const idleMinutes = idleArray.filter((val) => val === "1").length;
21774
+ const startMinute = shiftStartTime.minute;
21775
+ let idleArray = [];
21776
+ let idleMinutes = 0;
21777
+ if (idleTimeHourly) {
21778
+ if (startMinute > 0) {
21779
+ if (i === 0) {
21780
+ const firstHourData = idleTimeHourly[actualHour.toString()] || [];
21781
+ const nextHour = (actualHour + 1) % 24;
21782
+ const nextHourData = idleTimeHourly[nextHour.toString()] || [];
21783
+ idleArray = [
21784
+ ...firstHourData.slice(startMinute) || [],
21785
+ ...nextHourData.slice(0, startMinute) || []
21786
+ ];
21787
+ } else if (i < SHIFT_DURATION - 1) {
21788
+ const currentHourData = idleTimeHourly[actualHour.toString()] || [];
21789
+ const nextHour = (actualHour + 1) % 24;
21790
+ const nextHourData = idleTimeHourly[nextHour.toString()] || [];
21791
+ idleArray = [
21792
+ ...currentHourData.slice(startMinute) || [],
21793
+ ...nextHourData.slice(0, startMinute) || []
21794
+ ];
21795
+ } else {
21796
+ const hasPartialLastHour2 = shiftEndTime && shiftEndTime.minute > 0 && shiftEndTime.minute < 60;
21797
+ if (hasPartialLastHour2) {
21798
+ const currentHourData = idleTimeHourly[actualHour.toString()] || [];
21799
+ const nextHour = (actualHour + 1) % 24;
21800
+ const nextHourData = idleTimeHourly[nextHour.toString()] || [];
21801
+ if (startMinute > 0) {
21802
+ const firstPart = currentHourData.slice(startMinute) || [];
21803
+ const secondPart = nextHourData.slice(0, shiftEndTime.minute) || [];
21804
+ idleArray = [...firstPart, ...secondPart];
21805
+ } else {
21806
+ idleArray = currentHourData.slice(0, shiftEndTime.minute) || [];
21807
+ }
21808
+ } else {
21809
+ const currentHourData = idleTimeHourly[actualHour.toString()] || [];
21810
+ const nextHour = (actualHour + 1) % 24;
21811
+ const nextHourData = idleTimeHourly[nextHour.toString()] || [];
21812
+ idleArray = [
21813
+ ...currentHourData.slice(startMinute) || [],
21814
+ ...nextHourData.slice(0, startMinute) || []
21815
+ ];
21816
+ }
21817
+ }
21818
+ } else {
21819
+ const currentHourData = idleTimeHourly[actualHour.toString()] || [];
21820
+ if (i === SHIFT_DURATION - 1 && shiftEndTime && shiftEndTime.minute > 0 && shiftEndTime.minute < 60) {
21821
+ idleArray = currentHourData.slice(0, shiftEndTime.minute) || [];
21822
+ } else {
21823
+ idleArray = currentHourData || [];
21824
+ }
21825
+ }
21826
+ }
21827
+ idleMinutes = idleArray.filter((val) => val === "1" || val === 1).length;
21433
21828
  return {
21434
21829
  hour: formatHour(i),
21435
21830
  timeRange: formatTimeRange(i),
@@ -21441,7 +21836,7 @@ var HourlyOutputChartComponent = ({
21441
21836
  idleArray
21442
21837
  };
21443
21838
  });
21444
- }, [animatedData, data, pphThreshold, idleTimeHourly, shiftStartTime.hour, formatHour, formatTimeRange]);
21839
+ }, [animatedData, data, pphThreshold, idleTimeHourly, shiftStartTime.hour, shiftStartTime.minute, shiftEndTime, formatHour, formatTimeRange, SHIFT_DURATION]);
21445
21840
  const IdleBar = React19__namespace.default.useMemo(() => {
21446
21841
  if (!idleBarState.visible) return null;
21447
21842
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -21601,13 +21996,13 @@ var HourlyOutputChartComponent = ({
21601
21996
  if (showIdleTime && data2.idleArray) {
21602
21997
  let currentRange = null;
21603
21998
  data2.idleArray.forEach((val, idx) => {
21604
- if (val === "1") {
21999
+ if (val === "1" || val === 1) {
21605
22000
  if (!currentRange) {
21606
22001
  currentRange = { start: idx, end: idx };
21607
22002
  } else {
21608
22003
  currentRange.end = idx;
21609
22004
  }
21610
- } else if (currentRange) {
22005
+ } else if (val !== "x" && currentRange) {
21611
22006
  idleRanges.push(currentRange);
21612
22007
  currentRange = null;
21613
22008
  }
@@ -34149,21 +34544,135 @@ var KPIDetailView = ({
34149
34544
  const shiftStart = metrics2.shift_start || (isNightShift ? "22:00" : "06:00");
34150
34545
  const shiftEnd = metrics2.shift_end || (isNightShift ? "06:00" : "14:00");
34151
34546
  const startHour = parseInt(shiftStart.split(":")[0]);
34547
+ const startMinute = parseInt(shiftStart.split(":")[1] || "0");
34152
34548
  const endHour = parseInt(shiftEnd.split(":")[0]);
34549
+ const endMinute = parseInt(shiftEnd.split(":")[1] || "0");
34153
34550
  let shiftDuration = endHour - startHour;
34154
34551
  if (shiftDuration <= 0) {
34155
34552
  shiftDuration += 24;
34156
34553
  }
34554
+ const hasPartialLastHour = endMinute > 0 && endMinute < 60;
34555
+ const hourCount = hasPartialLastHour ? Math.ceil(shiftDuration + endMinute / 60) : shiftDuration;
34157
34556
  let expectedHours = [];
34158
- for (let i = 0; i < shiftDuration; i++) {
34557
+ for (let i = 0; i < hourCount; i++) {
34159
34558
  expectedHours.push((startHour + i) % 24);
34160
34559
  }
34161
- return expectedHours.map((hour) => {
34162
- const hourData = metrics2.output_hourly[hour.toString()];
34163
- if (hourData && Array.isArray(hourData)) {
34164
- return hourData.reduce((sum, val) => sum + (val || 0), 0);
34560
+ return expectedHours.map((expectedHour, index) => {
34561
+ let hourSum = 0;
34562
+ const outputHourly = metrics2.output_hourly;
34563
+ const hourData = outputHourly[expectedHour.toString()] || [];
34564
+ if (startMinute > 0) {
34565
+ if (index === 0) {
34566
+ if (Array.isArray(hourData)) {
34567
+ for (let i = startMinute; i < hourData.length; i++) {
34568
+ const val = hourData[i];
34569
+ if (val !== "x" && typeof val === "number") {
34570
+ hourSum += val;
34571
+ }
34572
+ }
34573
+ }
34574
+ const nextHour = (expectedHour + 1) % 24;
34575
+ const nextHourData = outputHourly[nextHour.toString()] || [];
34576
+ if (Array.isArray(nextHourData)) {
34577
+ for (let i = 0; i < startMinute && i < nextHourData.length; i++) {
34578
+ const val = nextHourData[i];
34579
+ if (val !== "x" && typeof val === "number") {
34580
+ hourSum += val;
34581
+ }
34582
+ }
34583
+ }
34584
+ } else if (index < expectedHours.length - 1) {
34585
+ if (Array.isArray(hourData)) {
34586
+ for (let i = startMinute; i < hourData.length; i++) {
34587
+ const val = hourData[i];
34588
+ if (val !== "x" && typeof val === "number") {
34589
+ hourSum += val;
34590
+ }
34591
+ }
34592
+ }
34593
+ const nextHour = (expectedHour + 1) % 24;
34594
+ const nextHourData = outputHourly[nextHour.toString()] || [];
34595
+ if (Array.isArray(nextHourData)) {
34596
+ for (let i = 0; i < startMinute && i < nextHourData.length; i++) {
34597
+ const val = nextHourData[i];
34598
+ if (val !== "x" && typeof val === "number") {
34599
+ hourSum += val;
34600
+ }
34601
+ }
34602
+ }
34603
+ } else {
34604
+ if (hasPartialLastHour && endMinute > 0) {
34605
+ if (startMinute > 0) {
34606
+ if (Array.isArray(hourData)) {
34607
+ for (let i = startMinute; i < hourData.length; i++) {
34608
+ const val = hourData[i];
34609
+ if (val !== "x" && typeof val === "number") {
34610
+ hourSum += val;
34611
+ }
34612
+ }
34613
+ }
34614
+ const nextHour = (expectedHour + 1) % 24;
34615
+ const nextHourData = outputHourly[nextHour.toString()] || [];
34616
+ if (Array.isArray(nextHourData)) {
34617
+ for (let i = 0; i < endMinute && i < nextHourData.length; i++) {
34618
+ const val = nextHourData[i];
34619
+ if (val !== "x" && typeof val === "number") {
34620
+ hourSum += val;
34621
+ }
34622
+ }
34623
+ }
34624
+ } else {
34625
+ if (Array.isArray(hourData)) {
34626
+ for (let i = 0; i < endMinute && i < hourData.length; i++) {
34627
+ const val = hourData[i];
34628
+ if (val !== "x" && typeof val === "number") {
34629
+ hourSum += val;
34630
+ }
34631
+ }
34632
+ }
34633
+ }
34634
+ } else {
34635
+ if (Array.isArray(hourData)) {
34636
+ for (let i = startMinute; i < hourData.length; i++) {
34637
+ const val = hourData[i];
34638
+ if (val !== "x" && typeof val === "number") {
34639
+ hourSum += val;
34640
+ }
34641
+ }
34642
+ }
34643
+ const nextHour = (expectedHour + 1) % 24;
34644
+ const nextHourData = outputHourly[nextHour.toString()] || [];
34645
+ if (Array.isArray(nextHourData)) {
34646
+ for (let i = 0; i < startMinute && i < nextHourData.length; i++) {
34647
+ const val = nextHourData[i];
34648
+ if (val !== "x" && typeof val === "number") {
34649
+ hourSum += val;
34650
+ }
34651
+ }
34652
+ }
34653
+ }
34654
+ }
34655
+ } else {
34656
+ if (Array.isArray(hourData)) {
34657
+ hourData.forEach((val) => {
34658
+ if (val !== "x" && typeof val === "number") {
34659
+ hourSum += val;
34660
+ }
34661
+ });
34662
+ }
34663
+ if (index === expectedHours.length - 1 && hasPartialLastHour && endMinute > 0) {
34664
+ hourSum = 0;
34665
+ if (Array.isArray(hourData)) {
34666
+ for (let i = 0; i < endMinute && i < hourData.length; i++) {
34667
+ const val = hourData[i];
34668
+ if (val !== "x" && typeof val === "number") {
34669
+ hourSum += val;
34670
+ }
34671
+ }
34672
+ }
34673
+ }
34165
34674
  }
34166
- return 0;
34675
+ return hourSum;
34167
34676
  });
34168
34677
  }
34169
34678
  if (metrics2.output_array) {