@optifye/dashboard-core 6.11.35 → 6.11.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +592 -404
- package/dist/index.mjs +592 -404
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1638,6 +1638,7 @@ var DEFAULT_SHIFT_DATA = {
|
|
|
1638
1638
|
efficiency: 0,
|
|
1639
1639
|
output: 0,
|
|
1640
1640
|
cycleTime: 0,
|
|
1641
|
+
idealCycleTime: 0,
|
|
1641
1642
|
pph: 0,
|
|
1642
1643
|
pphThreshold: 0,
|
|
1643
1644
|
idealOutput: 0,
|
|
@@ -4484,6 +4485,7 @@ var dashboardService = {
|
|
|
4484
4485
|
avg_efficiency: item.avg_efficiency || 0,
|
|
4485
4486
|
total_output: item.total_output || 0,
|
|
4486
4487
|
avg_cycle_time: item.avg_cycle_time || 0,
|
|
4488
|
+
ideal_cycle_time: item.ideal_cycle_time ?? 0,
|
|
4487
4489
|
ideal_output: item.ideal_output || 0,
|
|
4488
4490
|
total_day_output: item.total_day_output ?? item.ideal_output ?? 0,
|
|
4489
4491
|
avg_pph: item.avg_pph || 0,
|
|
@@ -17280,7 +17282,7 @@ var useActiveBreaks = (lineIds) => {
|
|
|
17280
17282
|
const [isLoading, setIsLoading] = useState(true);
|
|
17281
17283
|
const [error, setError] = useState(null);
|
|
17282
17284
|
const supabase = useSupabase();
|
|
17283
|
-
const
|
|
17285
|
+
const parseTimeToMinutes5 = (timeStr) => {
|
|
17284
17286
|
const [hours, minutes] = timeStr.split(":").map(Number);
|
|
17285
17287
|
return hours * 60 + minutes;
|
|
17286
17288
|
};
|
|
@@ -17289,8 +17291,8 @@ var useActiveBreaks = (lineIds) => {
|
|
|
17289
17291
|
return now4.getHours() * 60 + now4.getMinutes();
|
|
17290
17292
|
};
|
|
17291
17293
|
const isTimeInBreak = (breakStart, breakEnd, currentMinutes) => {
|
|
17292
|
-
const startMinutes =
|
|
17293
|
-
const endMinutes =
|
|
17294
|
+
const startMinutes = parseTimeToMinutes5(breakStart);
|
|
17295
|
+
const endMinutes = parseTimeToMinutes5(breakEnd);
|
|
17294
17296
|
if (endMinutes < startMinutes) {
|
|
17295
17297
|
return currentMinutes >= startMinutes || currentMinutes < endMinutes;
|
|
17296
17298
|
} else {
|
|
@@ -17298,8 +17300,8 @@ var useActiveBreaks = (lineIds) => {
|
|
|
17298
17300
|
}
|
|
17299
17301
|
};
|
|
17300
17302
|
const calculateBreakProgress = (breakStart, breakEnd, currentMinutes) => {
|
|
17301
|
-
const startMinutes =
|
|
17302
|
-
const endMinutes =
|
|
17303
|
+
const startMinutes = parseTimeToMinutes5(breakStart);
|
|
17304
|
+
const endMinutes = parseTimeToMinutes5(breakEnd);
|
|
17303
17305
|
let elapsedMinutes = 0;
|
|
17304
17306
|
let remainingMinutes = 0;
|
|
17305
17307
|
if (endMinutes < startMinutes) {
|
|
@@ -17317,8 +17319,8 @@ var useActiveBreaks = (lineIds) => {
|
|
|
17317
17319
|
return { elapsedMinutes, remainingMinutes };
|
|
17318
17320
|
};
|
|
17319
17321
|
const isTimeInShift = (startTime, endTime, currentMinutes) => {
|
|
17320
|
-
const startMinutes =
|
|
17321
|
-
const endMinutes =
|
|
17322
|
+
const startMinutes = parseTimeToMinutes5(startTime);
|
|
17323
|
+
const endMinutes = parseTimeToMinutes5(endTime);
|
|
17322
17324
|
if (endMinutes < startMinutes) {
|
|
17323
17325
|
return currentMinutes >= startMinutes || currentMinutes < endMinutes;
|
|
17324
17326
|
} else {
|
|
@@ -17378,8 +17380,8 @@ var useActiveBreaks = (lineIds) => {
|
|
|
17378
17380
|
const endTime = breakItem.end || breakItem.endTime || "00:00";
|
|
17379
17381
|
let duration = breakItem.duration || 0;
|
|
17380
17382
|
if (!duration || duration === 0) {
|
|
17381
|
-
const startMinutes =
|
|
17382
|
-
const endMinutes =
|
|
17383
|
+
const startMinutes = parseTimeToMinutes5(startTime);
|
|
17384
|
+
const endMinutes = parseTimeToMinutes5(endTime);
|
|
17383
17385
|
duration = endMinutes < startMinutes ? endMinutes + 24 * 60 - startMinutes : endMinutes - startMinutes;
|
|
17384
17386
|
}
|
|
17385
17387
|
return {
|
|
@@ -17395,8 +17397,8 @@ var useActiveBreaks = (lineIds) => {
|
|
|
17395
17397
|
const endTime = breakItem.end || breakItem.endTime || "00:00";
|
|
17396
17398
|
let duration = breakItem.duration || 0;
|
|
17397
17399
|
if (!duration || duration === 0) {
|
|
17398
|
-
const startMinutes =
|
|
17399
|
-
const endMinutes =
|
|
17400
|
+
const startMinutes = parseTimeToMinutes5(startTime);
|
|
17401
|
+
const endMinutes = parseTimeToMinutes5(endTime);
|
|
17400
17402
|
duration = endMinutes < startMinutes ? endMinutes + 24 * 60 - startMinutes : endMinutes - startMinutes;
|
|
17401
17403
|
}
|
|
17402
17404
|
return {
|
|
@@ -33411,13 +33413,13 @@ var CycleTimeOverTimeChart = ({
|
|
|
33411
33413
|
observer.observe(containerRef.current);
|
|
33412
33414
|
return () => observer.disconnect();
|
|
33413
33415
|
}, []);
|
|
33414
|
-
const
|
|
33416
|
+
const parseTimeToMinutes5 = (value) => {
|
|
33415
33417
|
const [hours, minutes] = value.split(":").map(Number);
|
|
33416
33418
|
if (!Number.isFinite(hours) || !Number.isFinite(minutes)) return 0;
|
|
33417
33419
|
return hours * 60 + minutes;
|
|
33418
33420
|
};
|
|
33419
33421
|
const formatHourLabel = (slotIndex) => {
|
|
33420
|
-
const baseMinutes =
|
|
33422
|
+
const baseMinutes = parseTimeToMinutes5(shiftStart);
|
|
33421
33423
|
const absoluteMinutes = baseMinutes + slotIndex * 60;
|
|
33422
33424
|
const hour24 = Math.floor(absoluteMinutes % (24 * 60) / 60);
|
|
33423
33425
|
const ampm = hour24 >= 12 ? "PM" : "AM";
|
|
@@ -47874,6 +47876,7 @@ var LineMonthlyPdfGenerator = ({
|
|
|
47874
47876
|
rangeEnd,
|
|
47875
47877
|
selectedShiftId,
|
|
47876
47878
|
availableShifts,
|
|
47879
|
+
lineAssembly = false,
|
|
47877
47880
|
compact = false,
|
|
47878
47881
|
className
|
|
47879
47882
|
}) => {
|
|
@@ -48157,8 +48160,8 @@ var LineMonthlyPdfGenerator = ({
|
|
|
48157
48160
|
timeZone: "Asia/Kolkata"
|
|
48158
48161
|
});
|
|
48159
48162
|
doc.text(dateStr, 25, yPos);
|
|
48160
|
-
doc.text(`${shift.
|
|
48161
|
-
doc.text(`${shift.
|
|
48163
|
+
doc.text(`${Math.round(shift.output || 0)}`, 60, yPos);
|
|
48164
|
+
doc.text(`${Math.round(shift.idealOutput || 0)}`, 95, yPos);
|
|
48162
48165
|
doc.text(`${shift.avg_efficiency.toFixed(1)}%`, 135, yPos);
|
|
48163
48166
|
const statusColor = getEfficiencyColor(shift.avg_efficiency, effectiveLegend);
|
|
48164
48167
|
if (statusColor === "green") {
|
|
@@ -48188,6 +48191,8 @@ var LineMonthlyPdfGenerator = ({
|
|
|
48188
48191
|
doc.setTextColor(0, 0, 0);
|
|
48189
48192
|
}
|
|
48190
48193
|
const poorestWorkspaces = underperformingWorkspaces[selectedShiftId] || [];
|
|
48194
|
+
const isCycleTimeWorkspace = (workspace) => workspace.metric_mode === "cycle_time" || lineAssembly && (workspace.avg_cycle_time !== void 0 || workspace.ideal_cycle_time !== void 0 || workspace.cycle_ratio !== void 0);
|
|
48195
|
+
const showCycleTimePoorestPerformers = !isUptimeMode && poorestWorkspaces.some(isCycleTimeWorkspace);
|
|
48191
48196
|
if (poorestWorkspaces && poorestWorkspaces.length > 0) {
|
|
48192
48197
|
doc.addPage();
|
|
48193
48198
|
doc.setFontSize(14);
|
|
@@ -48213,7 +48218,11 @@ var LineMonthlyPdfGenerator = ({
|
|
|
48213
48218
|
doc.setFillColor(245, 245, 245);
|
|
48214
48219
|
doc.roundedRect(20, 45, 170, 8, 1, 1, "F");
|
|
48215
48220
|
doc.text("Workspace", 25, 50);
|
|
48216
|
-
doc.text(
|
|
48221
|
+
doc.text(
|
|
48222
|
+
isUptimeMode ? "Avg Utilization" : showCycleTimePoorestPerformers ? "Cycle Time" : "Avg Efficiency",
|
|
48223
|
+
120,
|
|
48224
|
+
50
|
|
48225
|
+
);
|
|
48217
48226
|
doc.text("Last 5 Days", 160, 50);
|
|
48218
48227
|
doc.setLineWidth(0.2);
|
|
48219
48228
|
doc.setDrawColor(220, 220, 220);
|
|
@@ -48232,7 +48241,16 @@ var LineMonthlyPdfGenerator = ({
|
|
|
48232
48241
|
);
|
|
48233
48242
|
const workspaceName = rawWorkspaceName.length > 30 ? `${rawWorkspaceName.substring(0, 27)}...` : rawWorkspaceName;
|
|
48234
48243
|
doc.text(workspaceName, 25, yPos2);
|
|
48235
|
-
|
|
48244
|
+
if (isUptimeMode) {
|
|
48245
|
+
doc.text(`${(workspace.avg_efficiency || 0).toFixed(1)}%`, 120, yPos2);
|
|
48246
|
+
} else if (isCycleTimeWorkspace(workspace)) {
|
|
48247
|
+
const actualCycleTime = Number.isFinite(workspace.avg_cycle_time) ? Number(workspace.avg_cycle_time) : null;
|
|
48248
|
+
const targetCycleTime = Number.isFinite(workspace.ideal_cycle_time) ? Number(workspace.ideal_cycle_time) : null;
|
|
48249
|
+
const cycleTimeText = actualCycleTime !== null ? `${actualCycleTime.toFixed(1)}s${targetCycleTime !== null ? ` / ${targetCycleTime.toFixed(1)}s` : ""}` : "-";
|
|
48250
|
+
doc.text(cycleTimeText, 120, yPos2);
|
|
48251
|
+
} else {
|
|
48252
|
+
doc.text(`${(workspace.avg_efficiency || 0).toFixed(1)}%`, 120, yPos2);
|
|
48253
|
+
}
|
|
48236
48254
|
const squareSize = 3;
|
|
48237
48255
|
const squareSpacing = 1;
|
|
48238
48256
|
let squareX = 160;
|
|
@@ -48327,12 +48345,279 @@ Underperforming Workspaces: ${lineInfo.metrics.underperforming_workspaces} / ${l
|
|
|
48327
48345
|
}
|
|
48328
48346
|
);
|
|
48329
48347
|
};
|
|
48348
|
+
|
|
48349
|
+
// src/lib/utils/hourlyTargets.ts
|
|
48350
|
+
var stripSeconds2 = (timeStr) => timeStr ? timeStr.slice(0, 5) : timeStr;
|
|
48351
|
+
var MINUTES_PER_DAY = 24 * 60;
|
|
48352
|
+
var parseTimeToMinutes2 = (timeString) => {
|
|
48353
|
+
const normalized = stripSeconds2(timeString || "");
|
|
48354
|
+
if (!normalized || !/^[0-2]\d:[0-5]\d$/.test(normalized)) return Number.NaN;
|
|
48355
|
+
const [hours, minutes] = normalized.split(":").map(Number);
|
|
48356
|
+
return hours * 60 + minutes;
|
|
48357
|
+
};
|
|
48358
|
+
var normalizeBreaksOnShiftTimeline = (shiftStart, breaks) => {
|
|
48359
|
+
const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
|
|
48360
|
+
if (!Number.isFinite(shiftStartMinutes)) return [];
|
|
48361
|
+
const normalizedBreaks = [];
|
|
48362
|
+
for (const entry of breaks) {
|
|
48363
|
+
const startRaw = parseTimeToMinutes2(entry.startTime);
|
|
48364
|
+
const endRaw = parseTimeToMinutes2(entry.endTime);
|
|
48365
|
+
if (!Number.isFinite(startRaw) || !Number.isFinite(endRaw)) continue;
|
|
48366
|
+
let start = startRaw;
|
|
48367
|
+
let end = endRaw;
|
|
48368
|
+
if (end <= start) {
|
|
48369
|
+
end += 24 * 60;
|
|
48370
|
+
}
|
|
48371
|
+
if (start < shiftStartMinutes) {
|
|
48372
|
+
start += 24 * 60;
|
|
48373
|
+
end += 24 * 60;
|
|
48374
|
+
}
|
|
48375
|
+
const label = entry.remarks?.trim() || "Break";
|
|
48376
|
+
normalizedBreaks.push({ start, end, label });
|
|
48377
|
+
}
|
|
48378
|
+
return normalizedBreaks;
|
|
48379
|
+
};
|
|
48380
|
+
var roundTarget = (value, mode) => {
|
|
48381
|
+
if (!Number.isFinite(value)) return 0;
|
|
48382
|
+
switch (mode) {
|
|
48383
|
+
case "floor":
|
|
48384
|
+
return Math.floor(value);
|
|
48385
|
+
case "ceil":
|
|
48386
|
+
return Math.ceil(value);
|
|
48387
|
+
case "round":
|
|
48388
|
+
default:
|
|
48389
|
+
return Math.round(value);
|
|
48390
|
+
}
|
|
48391
|
+
};
|
|
48392
|
+
var formatDateKey = (date) => {
|
|
48393
|
+
const year = date.getUTCFullYear();
|
|
48394
|
+
const month = `${date.getUTCMonth() + 1}`.padStart(2, "0");
|
|
48395
|
+
const day = `${date.getUTCDate()}`.padStart(2, "0");
|
|
48396
|
+
return `${year}-${month}-${day}`;
|
|
48397
|
+
};
|
|
48398
|
+
var shiftDateKey = (dateKey, deltaDays) => {
|
|
48399
|
+
const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
|
|
48400
|
+
const year = Number.isFinite(yearPart) ? yearPart : 1970;
|
|
48401
|
+
const month = Number.isFinite(monthPart) ? monthPart : 1;
|
|
48402
|
+
const day = Number.isFinite(dayPart) ? dayPart : 1;
|
|
48403
|
+
const date = new Date(Date.UTC(year, month - 1, day));
|
|
48404
|
+
date.setUTCDate(date.getUTCDate() + deltaDays);
|
|
48405
|
+
return formatDateKey(date);
|
|
48406
|
+
};
|
|
48407
|
+
var getZonedNowSnapshot = (timeZone, now4) => {
|
|
48408
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
48409
|
+
timeZone,
|
|
48410
|
+
year: "numeric",
|
|
48411
|
+
month: "2-digit",
|
|
48412
|
+
day: "2-digit",
|
|
48413
|
+
hour: "2-digit",
|
|
48414
|
+
minute: "2-digit",
|
|
48415
|
+
hourCycle: "h23"
|
|
48416
|
+
});
|
|
48417
|
+
const parts = formatter.formatToParts(now4).reduce((acc, part) => {
|
|
48418
|
+
if (part.type !== "literal") {
|
|
48419
|
+
acc[part.type] = part.value;
|
|
48420
|
+
}
|
|
48421
|
+
return acc;
|
|
48422
|
+
}, {});
|
|
48423
|
+
const year = Number(parts.year);
|
|
48424
|
+
const month = Number(parts.month);
|
|
48425
|
+
const day = Number(parts.day);
|
|
48426
|
+
const hour = Number(parts.hour);
|
|
48427
|
+
const minute = Number(parts.minute);
|
|
48428
|
+
return {
|
|
48429
|
+
dateKey: `${String(year).padStart(4, "0")}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`,
|
|
48430
|
+
minutesOfDay: (Number.isFinite(hour) ? hour : 0) * 60 + (Number.isFinite(minute) ? minute : 0)
|
|
48431
|
+
};
|
|
48432
|
+
};
|
|
48433
|
+
var getDateKeyInTimeZone = (timeZone, now4 = /* @__PURE__ */ new Date()) => getZonedNowSnapshot(timeZone, now4).dateKey;
|
|
48434
|
+
var buildHourlyIntervals = ({
|
|
48435
|
+
shiftStart,
|
|
48436
|
+
shiftEnd,
|
|
48437
|
+
bucketMinutes = 60,
|
|
48438
|
+
fallbackHours = 11
|
|
48439
|
+
}) => {
|
|
48440
|
+
const startMinutes = parseTimeToMinutes2(shiftStart);
|
|
48441
|
+
if (!Number.isFinite(startMinutes)) return [];
|
|
48442
|
+
const bucket = Number.isFinite(bucketMinutes) && bucketMinutes > 0 ? Math.floor(bucketMinutes) : 60;
|
|
48443
|
+
let totalMinutes;
|
|
48444
|
+
const endRaw = shiftEnd ? parseTimeToMinutes2(shiftEnd) : Number.NaN;
|
|
48445
|
+
if (!Number.isFinite(endRaw)) {
|
|
48446
|
+
totalMinutes = Math.max(0, Math.round(fallbackHours * 60));
|
|
48447
|
+
} else {
|
|
48448
|
+
let endMinutes = endRaw;
|
|
48449
|
+
if (endMinutes <= startMinutes) {
|
|
48450
|
+
endMinutes += 24 * 60;
|
|
48451
|
+
}
|
|
48452
|
+
totalMinutes = endMinutes - startMinutes;
|
|
48453
|
+
}
|
|
48454
|
+
if (!Number.isFinite(totalMinutes) || totalMinutes <= 0) return [];
|
|
48455
|
+
const count = Math.ceil(totalMinutes / bucket);
|
|
48456
|
+
const shiftEndMinutes = startMinutes + totalMinutes;
|
|
48457
|
+
const intervals = [];
|
|
48458
|
+
for (let i = 0; i < count; i += 1) {
|
|
48459
|
+
const start = startMinutes + i * bucket;
|
|
48460
|
+
const end = Math.min(start + bucket, shiftEndMinutes);
|
|
48461
|
+
const minutes = Math.max(0, end - start);
|
|
48462
|
+
if (minutes <= 0) continue;
|
|
48463
|
+
intervals.push({ start, end, minutes });
|
|
48464
|
+
}
|
|
48465
|
+
return intervals;
|
|
48466
|
+
};
|
|
48467
|
+
var computeBreakMinutesByInterval = ({
|
|
48468
|
+
intervals,
|
|
48469
|
+
shiftStart,
|
|
48470
|
+
breaks
|
|
48471
|
+
}) => {
|
|
48472
|
+
if (!intervals.length || !breaks.length) return intervals.map(() => 0);
|
|
48473
|
+
const normalizedBreaks = normalizeBreaksOnShiftTimeline(shiftStart, breaks);
|
|
48474
|
+
return intervals.map((interval) => {
|
|
48475
|
+
if (!normalizedBreaks.length) return 0;
|
|
48476
|
+
let total = 0;
|
|
48477
|
+
for (const brk of normalizedBreaks) {
|
|
48478
|
+
const overlap = Math.max(0, Math.min(interval.end, brk.end) - Math.max(interval.start, brk.start));
|
|
48479
|
+
total += overlap;
|
|
48480
|
+
if (total >= interval.minutes) return interval.minutes;
|
|
48481
|
+
}
|
|
48482
|
+
return Math.min(interval.minutes, total);
|
|
48483
|
+
});
|
|
48484
|
+
};
|
|
48485
|
+
var computeBreakRemarksByInterval = ({
|
|
48486
|
+
intervals,
|
|
48487
|
+
shiftStart,
|
|
48488
|
+
breaks
|
|
48489
|
+
}) => {
|
|
48490
|
+
if (!intervals.length || !breaks.length) return intervals.map(() => "");
|
|
48491
|
+
const normalizedBreaks = normalizeBreaksOnShiftTimeline(shiftStart, breaks);
|
|
48492
|
+
return intervals.map((interval) => {
|
|
48493
|
+
const labels = normalizedBreaks.filter((brk) => Math.max(0, Math.min(interval.end, brk.end) - Math.max(interval.start, brk.start)) > 0).map((brk) => brk.label).filter((label, index, values) => label && values.indexOf(label) === index);
|
|
48494
|
+
return labels.join(", ");
|
|
48495
|
+
});
|
|
48496
|
+
};
|
|
48497
|
+
var computeEffectiveTargets = ({
|
|
48498
|
+
intervals,
|
|
48499
|
+
breakMinutes,
|
|
48500
|
+
pphThreshold,
|
|
48501
|
+
rounding = "round"
|
|
48502
|
+
}) => {
|
|
48503
|
+
return intervals.map((interval, idx) => {
|
|
48504
|
+
const intervalMinutes = Number(interval?.minutes) || 0;
|
|
48505
|
+
const breakMins = Number(breakMinutes?.[idx]) || 0;
|
|
48506
|
+
const plannedWorkMinutes = Math.max(0, intervalMinutes - breakMins);
|
|
48507
|
+
if (!Number.isFinite(pphThreshold) || pphThreshold <= 0) return 0;
|
|
48508
|
+
if (plannedWorkMinutes <= 0) return 0;
|
|
48509
|
+
return roundTarget(pphThreshold * plannedWorkMinutes / 60, rounding);
|
|
48510
|
+
});
|
|
48511
|
+
};
|
|
48512
|
+
var buildHourlyTargetPlan = ({
|
|
48513
|
+
shiftStart,
|
|
48514
|
+
shiftEnd,
|
|
48515
|
+
breaks = [],
|
|
48516
|
+
pphThreshold,
|
|
48517
|
+
bucketMinutes = 60,
|
|
48518
|
+
fallbackHours = 11,
|
|
48519
|
+
rounding = "round"
|
|
48520
|
+
}) => {
|
|
48521
|
+
const intervals = buildHourlyIntervals({
|
|
48522
|
+
shiftStart,
|
|
48523
|
+
shiftEnd,
|
|
48524
|
+
bucketMinutes,
|
|
48525
|
+
fallbackHours
|
|
48526
|
+
});
|
|
48527
|
+
const breakMinutes = computeBreakMinutesByInterval({
|
|
48528
|
+
intervals,
|
|
48529
|
+
shiftStart,
|
|
48530
|
+
breaks
|
|
48531
|
+
});
|
|
48532
|
+
const breakRemarks = computeBreakRemarksByInterval({
|
|
48533
|
+
intervals,
|
|
48534
|
+
shiftStart,
|
|
48535
|
+
breaks
|
|
48536
|
+
});
|
|
48537
|
+
const productiveMinutes = intervals.map((interval, idx) => Math.max(0, (Number(interval?.minutes) || 0) - (Number(breakMinutes[idx]) || 0)));
|
|
48538
|
+
const targets = computeEffectiveTargets({
|
|
48539
|
+
intervals,
|
|
48540
|
+
breakMinutes,
|
|
48541
|
+
pphThreshold,
|
|
48542
|
+
rounding
|
|
48543
|
+
});
|
|
48544
|
+
return {
|
|
48545
|
+
intervals,
|
|
48546
|
+
breakMinutes,
|
|
48547
|
+
breakRemarks,
|
|
48548
|
+
productiveMinutes,
|
|
48549
|
+
targets
|
|
48550
|
+
};
|
|
48551
|
+
};
|
|
48552
|
+
var isHourlyIntervalComplete = ({
|
|
48553
|
+
reportDate,
|
|
48554
|
+
shiftStart,
|
|
48555
|
+
shiftEnd,
|
|
48556
|
+
interval,
|
|
48557
|
+
timeZone = "Asia/Kolkata",
|
|
48558
|
+
now: now4 = /* @__PURE__ */ new Date()
|
|
48559
|
+
}) => {
|
|
48560
|
+
if (!reportDate) return true;
|
|
48561
|
+
const snapshot = getZonedNowSnapshot(timeZone, now4);
|
|
48562
|
+
const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
|
|
48563
|
+
const shiftEndMinutes = shiftEnd ? parseTimeToMinutes2(shiftEnd) : Number.NaN;
|
|
48564
|
+
const wrapsMidnight = Number.isFinite(shiftStartMinutes) && Number.isFinite(shiftEndMinutes) && shiftEndMinutes <= shiftStartMinutes;
|
|
48565
|
+
if (reportDate === snapshot.dateKey) {
|
|
48566
|
+
return interval.end <= snapshot.minutesOfDay;
|
|
48567
|
+
}
|
|
48568
|
+
if (wrapsMidnight && reportDate === shiftDateKey(snapshot.dateKey, -1)) {
|
|
48569
|
+
return interval.end <= snapshot.minutesOfDay + MINUTES_PER_DAY;
|
|
48570
|
+
}
|
|
48571
|
+
return reportDate < snapshot.dateKey;
|
|
48572
|
+
};
|
|
48573
|
+
var isShiftInProgressForReportDate = ({
|
|
48574
|
+
reportDate,
|
|
48575
|
+
shiftStart,
|
|
48576
|
+
shiftEnd,
|
|
48577
|
+
timeZone = "Asia/Kolkata",
|
|
48578
|
+
now: now4 = /* @__PURE__ */ new Date()
|
|
48579
|
+
}) => {
|
|
48580
|
+
if (!reportDate || !shiftStart || !shiftEnd) return false;
|
|
48581
|
+
const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
|
|
48582
|
+
const shiftEndMinutesRaw = parseTimeToMinutes2(shiftEnd);
|
|
48583
|
+
if (!Number.isFinite(shiftStartMinutes) || !Number.isFinite(shiftEndMinutesRaw)) {
|
|
48584
|
+
return false;
|
|
48585
|
+
}
|
|
48586
|
+
let shiftEndMinutes = shiftEndMinutesRaw;
|
|
48587
|
+
const wrapsMidnight = shiftEndMinutes <= shiftStartMinutes;
|
|
48588
|
+
if (wrapsMidnight) {
|
|
48589
|
+
shiftEndMinutes += MINUTES_PER_DAY;
|
|
48590
|
+
}
|
|
48591
|
+
const snapshot = getZonedNowSnapshot(timeZone, now4);
|
|
48592
|
+
let currentMinutes = null;
|
|
48593
|
+
if (reportDate === snapshot.dateKey) {
|
|
48594
|
+
currentMinutes = snapshot.minutesOfDay;
|
|
48595
|
+
} else if (wrapsMidnight && reportDate === shiftDateKey(snapshot.dateKey, -1)) {
|
|
48596
|
+
currentMinutes = snapshot.minutesOfDay + MINUTES_PER_DAY;
|
|
48597
|
+
}
|
|
48598
|
+
if (currentMinutes === null) {
|
|
48599
|
+
return false;
|
|
48600
|
+
}
|
|
48601
|
+
return shiftStartMinutes <= currentMinutes && currentMinutes < shiftEndMinutes;
|
|
48602
|
+
};
|
|
48603
|
+
var formatOperationalDateKey = (dateKey, options) => {
|
|
48604
|
+
const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
|
|
48605
|
+
const year = Number.isFinite(yearPart) ? yearPart : 1970;
|
|
48606
|
+
const month = Number.isFinite(monthPart) ? monthPart : 1;
|
|
48607
|
+
const day = Number.isFinite(dayPart) ? dayPart : 1;
|
|
48608
|
+
return new Date(Date.UTC(year, month - 1, day, 12)).toLocaleDateString("en-IN", {
|
|
48609
|
+
...options,
|
|
48610
|
+
timeZone: "UTC"
|
|
48611
|
+
});
|
|
48612
|
+
};
|
|
48330
48613
|
var LinePdfGenerator = ({
|
|
48331
48614
|
lineInfo,
|
|
48332
48615
|
workspaceData,
|
|
48333
48616
|
issueResolutionSummary,
|
|
48334
48617
|
shiftName,
|
|
48335
|
-
className
|
|
48618
|
+
className,
|
|
48619
|
+
shiftBreaks = [],
|
|
48620
|
+
reportTimezone = "Asia/Kolkata"
|
|
48336
48621
|
}) => {
|
|
48337
48622
|
const [isGenerating, setIsGenerating] = useState(false);
|
|
48338
48623
|
const formatResolutionDuration2 = (seconds) => {
|
|
@@ -48359,7 +48644,7 @@ var LinePdfGenerator = ({
|
|
|
48359
48644
|
doc.setFontSize(9);
|
|
48360
48645
|
doc.setFont("helvetica", "normal");
|
|
48361
48646
|
doc.setTextColor(100, 100, 100);
|
|
48362
|
-
const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone:
|
|
48647
|
+
const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: reportTimezone })}`;
|
|
48363
48648
|
const generatedTextWidth = doc.getStringUnitWidth(generatedText) * 9 / doc.internal.scaleFactor;
|
|
48364
48649
|
doc.text(generatedText, doc.internal.pageSize.width - 20 - generatedTextWidth, 15);
|
|
48365
48650
|
doc.setDrawColor(200, 200, 200);
|
|
@@ -48377,11 +48662,10 @@ var LinePdfGenerator = ({
|
|
|
48377
48662
|
const isUptimeMode = lineInfo.monitoring_mode === "uptime";
|
|
48378
48663
|
const rawShiftType = shiftName || (lineInfo.shift_id === 0 ? "Day" : lineInfo.shift_id === 1 ? "Night" : `Shift ${lineInfo.shift_id}`);
|
|
48379
48664
|
const shiftType = rawShiftType.toLowerCase().includes("shift") ? rawShiftType : `${rawShiftType} Shift`;
|
|
48380
|
-
const date =
|
|
48665
|
+
const date = formatOperationalDateKey(lineInfo.date, {
|
|
48381
48666
|
weekday: "long",
|
|
48382
48667
|
day: "numeric",
|
|
48383
|
-
month: "long"
|
|
48384
|
-
timeZone: "Asia/Kolkata"
|
|
48668
|
+
month: "long"
|
|
48385
48669
|
});
|
|
48386
48670
|
const shiftStartTime = lineInfo.metrics.shift_start ? (/* @__PURE__ */ new Date(`2000-01-01 ${lineInfo.metrics.shift_start}`)).toLocaleTimeString("en-IN", {
|
|
48387
48671
|
hour: "2-digit",
|
|
@@ -48389,24 +48673,25 @@ var LinePdfGenerator = ({
|
|
|
48389
48673
|
hour12: true,
|
|
48390
48674
|
timeZone: "Asia/Kolkata"
|
|
48391
48675
|
}) : "N/A";
|
|
48392
|
-
const
|
|
48393
|
-
|
|
48394
|
-
|
|
48395
|
-
|
|
48396
|
-
|
|
48397
|
-
|
|
48398
|
-
|
|
48399
|
-
|
|
48400
|
-
|
|
48401
|
-
|
|
48402
|
-
|
|
48403
|
-
|
|
48404
|
-
|
|
48405
|
-
|
|
48406
|
-
|
|
48407
|
-
}
|
|
48676
|
+
const currentTime = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-IN", {
|
|
48677
|
+
hour: "2-digit",
|
|
48678
|
+
minute: "2-digit",
|
|
48679
|
+
timeZone: reportTimezone
|
|
48680
|
+
});
|
|
48681
|
+
const reportEndTime = isShiftInProgressForReportDate({
|
|
48682
|
+
reportDate: lineInfo.date,
|
|
48683
|
+
shiftStart: lineInfo.metrics.shift_start || "",
|
|
48684
|
+
shiftEnd: lineInfo.metrics.shift_end,
|
|
48685
|
+
timeZone: reportTimezone
|
|
48686
|
+
}) ? currentTime : lineInfo.metrics.shift_end ? (/* @__PURE__ */ new Date(`2000-01-01 ${lineInfo.metrics.shift_end}`)).toLocaleTimeString("en-IN", {
|
|
48687
|
+
hour: "2-digit",
|
|
48688
|
+
minute: "2-digit",
|
|
48689
|
+
hour12: true,
|
|
48690
|
+
timeZone: "Asia/Kolkata"
|
|
48691
|
+
}) : "N/A";
|
|
48408
48692
|
if (isUptimeMode) {
|
|
48409
48693
|
const configuredTimezone = "Asia/Kolkata";
|
|
48694
|
+
const effectiveUptimeTimezone = reportTimezone || configuredTimezone;
|
|
48410
48695
|
const lineShiftStart = lineInfo.metrics.shift_start || "06:00";
|
|
48411
48696
|
const lineShiftEnd = lineInfo.metrics.shift_end || "14:00";
|
|
48412
48697
|
const shiftMinutes = getShiftDurationMinutes(lineShiftStart, lineShiftEnd) || 0;
|
|
@@ -48420,7 +48705,7 @@ var LinePdfGenerator = ({
|
|
|
48420
48705
|
shiftStart,
|
|
48421
48706
|
shiftEnd,
|
|
48422
48707
|
shiftDate,
|
|
48423
|
-
timezone:
|
|
48708
|
+
timezone: effectiveUptimeTimezone
|
|
48424
48709
|
});
|
|
48425
48710
|
let activeMinutes = uptimeSeries.activeMinutes;
|
|
48426
48711
|
let idleMinutes = uptimeSeries.idleMinutes;
|
|
@@ -48501,7 +48786,7 @@ var LinePdfGenerator = ({
|
|
|
48501
48786
|
shiftStart: lineShiftStart,
|
|
48502
48787
|
shiftEnd: lineShiftEnd,
|
|
48503
48788
|
shiftDate: lineInfo.date,
|
|
48504
|
-
timezone:
|
|
48789
|
+
timezone: effectiveUptimeTimezone
|
|
48505
48790
|
});
|
|
48506
48791
|
hourlyData = buildHourlyFromSeries(lineUptimeSeries);
|
|
48507
48792
|
}
|
|
@@ -48609,13 +48894,13 @@ var LinePdfGenerator = ({
|
|
|
48609
48894
|
doc.setFontSize(contentFontSize);
|
|
48610
48895
|
doc.setFont("helvetica", "normal");
|
|
48611
48896
|
let yPos2 = headerBottomY2 + 5.5;
|
|
48612
|
-
const
|
|
48613
|
-
const
|
|
48614
|
-
const [
|
|
48615
|
-
const [
|
|
48616
|
-
const
|
|
48897
|
+
const now4 = /* @__PURE__ */ new Date();
|
|
48898
|
+
const currentTimeIST = new Date(now4.toLocaleString("en-US", { timeZone: configuredTimezone }));
|
|
48899
|
+
const [sYear, sMonth, sDay] = lineInfo.date.split("-").map(Number);
|
|
48900
|
+
const [sStartH, sStartM] = (lineShiftStart || "06:00").split(":").map(Number);
|
|
48901
|
+
const shiftStartBase = new Date(sYear, sMonth - 1, sDay, sStartH, sStartM || 0);
|
|
48617
48902
|
hourlyData.forEach((entry, index) => {
|
|
48618
|
-
const bucketStartTime = new Date(
|
|
48903
|
+
const bucketStartTime = new Date(shiftStartBase);
|
|
48619
48904
|
bucketStartTime.setHours(bucketStartTime.getHours() + index);
|
|
48620
48905
|
const bucketEndTime = new Date(bucketStartTime);
|
|
48621
48906
|
bucketEndTime.setHours(bucketEndTime.getHours() + 1);
|
|
@@ -48626,7 +48911,7 @@ var LinePdfGenerator = ({
|
|
|
48626
48911
|
hour: "numeric",
|
|
48627
48912
|
hour12: true
|
|
48628
48913
|
})}`;
|
|
48629
|
-
const dataCollected = bucketEndTime.getTime() <=
|
|
48914
|
+
const dataCollected = bucketEndTime.getTime() <= currentTimeIST.getTime();
|
|
48630
48915
|
if (index < totalRows2 - 1) {
|
|
48631
48916
|
const rowBottomY = headerBottomY2 + (index + 1) * rowHeight;
|
|
48632
48917
|
doc.setDrawColor(200, 200, 200);
|
|
@@ -48637,11 +48922,10 @@ var LinePdfGenerator = ({
|
|
|
48637
48922
|
doc.text(utilizationStr, 147, yPos2);
|
|
48638
48923
|
yPos2 += rowHeight;
|
|
48639
48924
|
});
|
|
48640
|
-
const fileDate2 =
|
|
48925
|
+
const fileDate2 = formatOperationalDateKey(lineInfo.date, {
|
|
48641
48926
|
day: "2-digit",
|
|
48642
48927
|
month: "short",
|
|
48643
|
-
year: "numeric"
|
|
48644
|
-
timeZone: "Asia/Kolkata"
|
|
48928
|
+
year: "numeric"
|
|
48645
48929
|
}).replace(/ /g, "_");
|
|
48646
48930
|
const fileShift2 = shiftType.replace(/ /g, "_");
|
|
48647
48931
|
const fileName2 = `${lineInfo.line_name}_${fileDate2}_${fileShift2}.pdf`;
|
|
@@ -48694,66 +48978,32 @@ var LinePdfGenerator = ({
|
|
|
48694
48978
|
doc.setLineWidth(0.8);
|
|
48695
48979
|
doc.line(20, 123, 190, 123);
|
|
48696
48980
|
const hourlyOverviewStartY = 128;
|
|
48697
|
-
const parseTimeToMinutes4 = (timeStr) => {
|
|
48698
|
-
const [hours, minutes] = timeStr.split(":");
|
|
48699
|
-
const hour = parseInt(hours, 10);
|
|
48700
|
-
const minute = parseInt(minutes || "0", 10);
|
|
48701
|
-
if (Number.isNaN(hour) || Number.isNaN(minute)) {
|
|
48702
|
-
return NaN;
|
|
48703
|
-
}
|
|
48704
|
-
return (hour * 60 + minute) % (24 * 60);
|
|
48705
|
-
};
|
|
48706
48981
|
const formatMinutesLabel = (totalMinutes) => {
|
|
48707
48982
|
const normalized = (totalMinutes % (24 * 60) + 24 * 60) % (24 * 60);
|
|
48708
48983
|
const hour = Math.floor(normalized / 60);
|
|
48709
48984
|
const minute = normalized % 60;
|
|
48710
|
-
const time2 =
|
|
48711
|
-
time2.setHours(hour);
|
|
48712
|
-
time2.setMinutes(minute);
|
|
48713
|
-
time2.setSeconds(0);
|
|
48714
|
-
time2.setMilliseconds(0);
|
|
48985
|
+
const time2 = new Date(Date.UTC(2e3, 0, 1, hour, minute));
|
|
48715
48986
|
return time2.toLocaleTimeString("en-IN", {
|
|
48716
48987
|
hour: "2-digit",
|
|
48717
48988
|
minute: "2-digit",
|
|
48718
48989
|
hour12: true,
|
|
48719
|
-
timeZone: "
|
|
48720
|
-
});
|
|
48721
|
-
};
|
|
48722
|
-
const buildRange = (startMinutes, minutes) => {
|
|
48723
|
-
const endMinutes = startMinutes + minutes;
|
|
48724
|
-
return {
|
|
48725
|
-
label: `${formatMinutesLabel(startMinutes)} - ${formatMinutesLabel(endMinutes)}`,
|
|
48726
|
-
minutes
|
|
48727
|
-
};
|
|
48728
|
-
};
|
|
48729
|
-
const getHourlyTimeRanges = (startTimeStr, endTimeStr) => {
|
|
48730
|
-
const startMinutes = parseTimeToMinutes4(startTimeStr);
|
|
48731
|
-
if (Number.isNaN(startMinutes)) {
|
|
48732
|
-
return [];
|
|
48733
|
-
}
|
|
48734
|
-
if (!endTimeStr) {
|
|
48735
|
-
const defaultHours = 11;
|
|
48736
|
-
return Array.from({ length: defaultHours }, (_, i) => buildRange(startMinutes + i * 60, 60));
|
|
48737
|
-
}
|
|
48738
|
-
const endMinutes = parseTimeToMinutes4(endTimeStr);
|
|
48739
|
-
if (Number.isNaN(endMinutes)) {
|
|
48740
|
-
const fallbackHours = 11;
|
|
48741
|
-
return Array.from({ length: fallbackHours }, (_, i) => buildRange(startMinutes + i * 60, 60));
|
|
48742
|
-
}
|
|
48743
|
-
let durationMinutes = endMinutes - startMinutes;
|
|
48744
|
-
if (durationMinutes <= 0) {
|
|
48745
|
-
durationMinutes += 24 * 60;
|
|
48746
|
-
}
|
|
48747
|
-
const rangeCount = Math.max(1, Math.ceil(durationMinutes / 60));
|
|
48748
|
-
return Array.from({ length: rangeCount }, (_, i) => {
|
|
48749
|
-
const remainingMinutes = durationMinutes - i * 60;
|
|
48750
|
-
const rangeMinutes = remainingMinutes >= 60 ? 60 : remainingMinutes;
|
|
48751
|
-
return buildRange(startMinutes + i * 60, rangeMinutes);
|
|
48990
|
+
timeZone: "UTC"
|
|
48752
48991
|
});
|
|
48753
48992
|
};
|
|
48754
|
-
const hourlyTimeRanges =
|
|
48993
|
+
const hourlyTimeRanges = buildHourlyIntervals({
|
|
48994
|
+
shiftStart: lineInfo.metrics.shift_start || "06:00",
|
|
48995
|
+
shiftEnd: lineInfo.metrics.shift_end,
|
|
48996
|
+
fallbackHours: 11
|
|
48997
|
+
});
|
|
48998
|
+
const targetPlan = buildHourlyTargetPlan({
|
|
48999
|
+
shiftStart: lineInfo.metrics.shift_start || "06:00",
|
|
49000
|
+
shiftEnd: lineInfo.metrics.shift_end,
|
|
49001
|
+
breaks: shiftBreaks,
|
|
49002
|
+
pphThreshold: Number(lineInfo.metrics.threshold_pph ?? 0),
|
|
49003
|
+
fallbackHours: Math.max(hourlyTimeRanges.length, 1),
|
|
49004
|
+
rounding: "floor"
|
|
49005
|
+
});
|
|
48755
49006
|
const shiftDuration = hourlyTimeRanges.length || 11;
|
|
48756
|
-
const targetOutputPerHour = Math.round(lineInfo.metrics.threshold_pph ?? 0);
|
|
48757
49007
|
let hourlyActualOutput = [];
|
|
48758
49008
|
if (lineInfo.metrics.output_hourly && Object.keys(lineInfo.metrics.output_hourly).length > 0) {
|
|
48759
49009
|
const [startHourStr, startMinuteStr] = (lineInfo.metrics.shift_start || "6:00").split(":");
|
|
@@ -48939,29 +49189,29 @@ var LinePdfGenerator = ({
|
|
|
48939
49189
|
doc.text("Remarks", 160, tableHeaderY);
|
|
48940
49190
|
doc.setFont("helvetica", "normal");
|
|
48941
49191
|
let yPos = tableStartY;
|
|
48942
|
-
const now4 = /* @__PURE__ */ new Date();
|
|
48943
|
-
const currentTimeIST = new Date(now4.toLocaleString("en-US", { timeZone: "Asia/Kolkata" }));
|
|
48944
|
-
const [sYear, sMonth, sDay] = lineInfo.date.split("-").map(Number);
|
|
48945
|
-
const [sStartH, sStartM] = (lineInfo.metrics.shift_start || "06:00").split(":").map(Number);
|
|
48946
|
-
const shiftStartBase = new Date(sYear, sMonth - 1, sDay, sStartH, sStartM || 0);
|
|
48947
49192
|
hourlyTimeRanges.forEach((timeRange, index) => {
|
|
48948
49193
|
const actualOutput = hourlyActualOutput[index] || 0;
|
|
48949
|
-
const
|
|
48950
|
-
|
|
48951
|
-
|
|
48952
|
-
|
|
49194
|
+
const dataCollected = isHourlyIntervalComplete({
|
|
49195
|
+
reportDate: lineInfo.date,
|
|
49196
|
+
shiftStart: lineInfo.metrics.shift_start || "06:00",
|
|
49197
|
+
shiftEnd: lineInfo.metrics.shift_end,
|
|
49198
|
+
interval: timeRange,
|
|
49199
|
+
timeZone: reportTimezone
|
|
49200
|
+
});
|
|
48953
49201
|
const outputStr = dataCollected ? actualOutput.toString() : "TBD";
|
|
48954
49202
|
if (index < totalRows - 1) {
|
|
48955
49203
|
const rowBottomY = headerBottomY + (index + 1) * rowSpacing;
|
|
48956
49204
|
doc.setDrawColor(200, 200, 200);
|
|
48957
49205
|
doc.line(20, rowBottomY, 190, rowBottomY);
|
|
48958
49206
|
}
|
|
48959
|
-
const
|
|
48960
|
-
const
|
|
48961
|
-
const
|
|
48962
|
-
doc.text(timeRange.
|
|
49207
|
+
const targetForRange = targetPlan.targets[index] ?? 0;
|
|
49208
|
+
const targetStr = targetForRange.toString();
|
|
49209
|
+
const remarkText = targetPlan.breakRemarks[index] || "";
|
|
49210
|
+
doc.text(`${formatMinutesLabel(timeRange.start)} - ${formatMinutesLabel(timeRange.end)}`, 25, yPos);
|
|
48963
49211
|
doc.text(outputStr, 75, yPos);
|
|
48964
49212
|
doc.text(targetStr, 105, yPos);
|
|
49213
|
+
const remarkDisplay = remarkText ? doc.splitTextToSize(remarkText, 26)[0] : "";
|
|
49214
|
+
doc.text(remarkDisplay, 160, yPos);
|
|
48965
49215
|
if (!dataCollected) {
|
|
48966
49216
|
doc.setTextColor(100, 100, 100);
|
|
48967
49217
|
doc.text("-", 135, yPos);
|
|
@@ -48977,11 +49227,10 @@ var LinePdfGenerator = ({
|
|
|
48977
49227
|
doc.setTextColor(0, 0, 0);
|
|
48978
49228
|
yPos += rowSpacing;
|
|
48979
49229
|
});
|
|
48980
|
-
const fileDate =
|
|
49230
|
+
const fileDate = formatOperationalDateKey(lineInfo.date, {
|
|
48981
49231
|
day: "2-digit",
|
|
48982
49232
|
month: "short",
|
|
48983
|
-
year: "numeric"
|
|
48984
|
-
timeZone: "Asia/Kolkata"
|
|
49233
|
+
year: "numeric"
|
|
48985
49234
|
}).replace(/ /g, "_");
|
|
48986
49235
|
const fileShift = shiftType.replace(/ /g, "_");
|
|
48987
49236
|
const fileName = `${lineInfo.line_name}_${fileDate}_${fileShift}.pdf`;
|
|
@@ -49909,6 +50158,13 @@ var WorkspaceMonthlyHistory = ({
|
|
|
49909
50158
|
}, [analysisMonthlyData, selectedShiftId, isUptimeMode, shiftWorkSeconds]);
|
|
49910
50159
|
const efficiencyDelta = trendSummary?.avg_efficiency?.delta_pp ?? 0;
|
|
49911
50160
|
const efficiencyImproved = efficiencyDelta >= 0;
|
|
50161
|
+
const assemblyRangeCycleTime = useMemo(() => {
|
|
50162
|
+
const trendCycleTime = Number(trendSummary?.avg_cycle_time?.current);
|
|
50163
|
+
if (Number.isFinite(trendCycleTime) && trendCycleTime > 0) {
|
|
50164
|
+
return Math.round(trendCycleTime);
|
|
50165
|
+
}
|
|
50166
|
+
return metrics2?.avgCycleTime ?? 0;
|
|
50167
|
+
}, [trendSummary?.avg_cycle_time?.current, metrics2?.avgCycleTime]);
|
|
49912
50168
|
const cycleDeltaRaw = trendSummary?.avg_cycle_time?.delta_seconds ?? 0;
|
|
49913
50169
|
const cyclePrev = trendSummary?.avg_cycle_time?.previous ?? 0;
|
|
49914
50170
|
const cycleDelta = cyclePrev ? cycleDeltaRaw / cyclePrev * 100 : 0;
|
|
@@ -50094,7 +50350,7 @@ var WorkspaceMonthlyHistory = ({
|
|
|
50094
50350
|
/* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-gray-600 mb-1", children: "Avg Cycle Time" }),
|
|
50095
50351
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [
|
|
50096
50352
|
/* @__PURE__ */ jsxs("div", { className: "text-2xl font-bold text-gray-900", children: [
|
|
50097
|
-
|
|
50353
|
+
assemblyRangeCycleTime,
|
|
50098
50354
|
"s"
|
|
50099
50355
|
] }),
|
|
50100
50356
|
/* @__PURE__ */ jsxs("div", { className: `flex items-center gap-1 ${cycleWorsened ? "bg-red-50 text-red-600" : "bg-emerald-50 text-emerald-600"} px-2 py-0.5 rounded-full text-[10px] font-medium whitespace-nowrap flex-shrink-0`, children: [
|
|
@@ -50430,7 +50686,25 @@ var WorkspaceWhatsAppShareButton = ({
|
|
|
50430
50686
|
}
|
|
50431
50687
|
);
|
|
50432
50688
|
};
|
|
50433
|
-
var
|
|
50689
|
+
var formatOperationalDateKey2 = (dateKey, options) => {
|
|
50690
|
+
const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
|
|
50691
|
+
const year = Number.isFinite(yearPart) ? yearPart : 1970;
|
|
50692
|
+
const month = Number.isFinite(monthPart) ? monthPart : 1;
|
|
50693
|
+
const day = Number.isFinite(dayPart) ? dayPart : 1;
|
|
50694
|
+
return new Date(Date.UTC(year, month - 1, day, 12)).toLocaleDateString("en-IN", {
|
|
50695
|
+
...options,
|
|
50696
|
+
timeZone: "UTC"
|
|
50697
|
+
});
|
|
50698
|
+
};
|
|
50699
|
+
var WorkspacePdfGenerator = ({
|
|
50700
|
+
workspace,
|
|
50701
|
+
className,
|
|
50702
|
+
idleTimeReasons,
|
|
50703
|
+
efficiencyLegend,
|
|
50704
|
+
hourlyCycleTimes,
|
|
50705
|
+
shiftBreaks = [],
|
|
50706
|
+
reportTimezone = "Asia/Kolkata"
|
|
50707
|
+
}) => {
|
|
50434
50708
|
const [isGenerating, setIsGenerating] = useState(false);
|
|
50435
50709
|
const entityConfig = useEntityConfig();
|
|
50436
50710
|
const effectiveLegend = efficiencyLegend || DEFAULT_EFFICIENCY_LEGEND;
|
|
@@ -50460,7 +50734,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
|
|
|
50460
50734
|
doc.setFontSize(9);
|
|
50461
50735
|
doc.setFont("helvetica", "normal");
|
|
50462
50736
|
doc.setTextColor(100, 100, 100);
|
|
50463
|
-
const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone:
|
|
50737
|
+
const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: reportTimezone })}`;
|
|
50464
50738
|
const generatedTextWidth = doc.getStringUnitWidth(generatedText) * 9 / doc.internal.scaleFactor;
|
|
50465
50739
|
doc.text(generatedText, doc.internal.pageSize.width - 20 - generatedTextWidth, 15);
|
|
50466
50740
|
doc.setDrawColor(200, 200, 200);
|
|
@@ -50479,11 +50753,10 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
|
|
|
50479
50753
|
doc.setFontSize(13);
|
|
50480
50754
|
doc.setFont("helvetica", "normal");
|
|
50481
50755
|
doc.setTextColor(60, 60, 60);
|
|
50482
|
-
const date =
|
|
50756
|
+
const date = formatOperationalDateKey2(workspace.date, {
|
|
50483
50757
|
weekday: "long",
|
|
50484
50758
|
day: "numeric",
|
|
50485
|
-
month: "long"
|
|
50486
|
-
timeZone: "Asia/Kolkata"
|
|
50759
|
+
month: "long"
|
|
50487
50760
|
});
|
|
50488
50761
|
const rawShiftType = workspace.shift_type || (workspace.shift_id === 0 ? "Day" : workspace.shift_id === 1 ? "Night" : `Shift ${workspace.shift_id}`);
|
|
50489
50762
|
const shiftType = rawShiftType.toLowerCase().includes("shift") ? rawShiftType : `${rawShiftType} Shift`;
|
|
@@ -50492,7 +50765,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
|
|
|
50492
50765
|
const currentTime = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-IN", {
|
|
50493
50766
|
hour: "2-digit",
|
|
50494
50767
|
minute: "2-digit",
|
|
50495
|
-
timeZone:
|
|
50768
|
+
timeZone: reportTimezone
|
|
50496
50769
|
});
|
|
50497
50770
|
const shiftStartTime = (/* @__PURE__ */ new Date(`2000-01-01 ${workspace.shift_start}`)).toLocaleTimeString("en-IN", {
|
|
50498
50771
|
hour: "2-digit",
|
|
@@ -50504,29 +50777,12 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
|
|
|
50504
50777
|
minute: "2-digit",
|
|
50505
50778
|
hour12: true
|
|
50506
50779
|
});
|
|
50507
|
-
const
|
|
50508
|
-
|
|
50509
|
-
|
|
50510
|
-
|
|
50511
|
-
|
|
50512
|
-
};
|
|
50513
|
-
const toShiftUtcMs = (dateKey, timeValue) => {
|
|
50514
|
-
const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
|
|
50515
|
-
const year = Number.isFinite(yearPart) ? yearPart : 1970;
|
|
50516
|
-
const month = Number.isFinite(monthPart) ? monthPart : 1;
|
|
50517
|
-
const day = Number.isFinite(dayPart) ? dayPart : 1;
|
|
50518
|
-
const [hourPart, minutePart] = timeValue.split(":").map(Number);
|
|
50519
|
-
const hour = Number.isFinite(hourPart) ? hourPart : 0;
|
|
50520
|
-
const minute = Number.isFinite(minutePart) ? minutePart : 0;
|
|
50521
|
-
const IST_OFFSET_MINUTES = 330;
|
|
50522
|
-
return Date.UTC(year, month - 1, day, hour, minute) - IST_OFFSET_MINUTES * 60 * 1e3;
|
|
50523
|
-
};
|
|
50524
|
-
const shiftStartMinutes = parseTimeToMinutes4(workspace.shift_start);
|
|
50525
|
-
const shiftEndMinutes = parseTimeToMinutes4(workspace.shift_end);
|
|
50526
|
-
const wrapsMidnight = shiftEndMinutes <= shiftStartMinutes;
|
|
50527
|
-
const shiftStartUtcMs = toShiftUtcMs(workspace.date, workspace.shift_start);
|
|
50528
|
-
const shiftEndUtcMs = toShiftUtcMs(workspace.date, workspace.shift_end) + (wrapsMidnight ? 24 * 60 * 60 * 1e3 : 0);
|
|
50529
|
-
const isShiftInProgress = Date.now() >= shiftStartUtcMs && Date.now() < shiftEndUtcMs;
|
|
50780
|
+
const isShiftInProgress = isShiftInProgressForReportDate({
|
|
50781
|
+
reportDate: workspace.date,
|
|
50782
|
+
shiftStart: workspace.shift_start,
|
|
50783
|
+
shiftEnd: workspace.shift_end,
|
|
50784
|
+
timeZone: reportTimezone
|
|
50785
|
+
});
|
|
50530
50786
|
const reportPeriodEndTime = isShiftInProgress ? currentTime : shiftEndTime;
|
|
50531
50787
|
doc.setFontSize(12);
|
|
50532
50788
|
doc.setTextColor(80, 80, 80);
|
|
@@ -50632,7 +50888,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
|
|
|
50632
50888
|
shiftStart: workspace.shift_start,
|
|
50633
50889
|
shiftEnd: workspace.shift_end,
|
|
50634
50890
|
shiftDate: workspace.date,
|
|
50635
|
-
timezone:
|
|
50891
|
+
timezone: reportTimezone
|
|
50636
50892
|
}) : null;
|
|
50637
50893
|
const hourlyUptime = uptimeSeries?.points?.length ? Array.from({ length: Math.ceil(uptimeSeries.shiftMinutes / 60) }, (_, index) => {
|
|
50638
50894
|
const start = index * 60;
|
|
@@ -50647,6 +50903,31 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
|
|
|
50647
50903
|
const hourlyData = isUptimeMode ? hourlyUptime : isAssemblyCycleMode ? hourlyCycleTimes && hourlyCycleTimes.length > 0 ? hourlyCycleTimes : workspace.hourly_action_counts || [] : workspace.hourly_action_counts || [];
|
|
50648
50904
|
const hourlyTarget = workspace.pph_threshold;
|
|
50649
50905
|
const cycleTarget = workspace.ideal_cycle_time || 0;
|
|
50906
|
+
const hourlyIntervals = buildHourlyIntervals({
|
|
50907
|
+
shiftStart: workspace.shift_start,
|
|
50908
|
+
shiftEnd: workspace.shift_end,
|
|
50909
|
+
fallbackHours: Math.max(hourlyData.length, 1)
|
|
50910
|
+
});
|
|
50911
|
+
const outputTargetPlan = !isUptimeMode && !isAssemblyCycleMode ? buildHourlyTargetPlan({
|
|
50912
|
+
shiftStart: workspace.shift_start,
|
|
50913
|
+
shiftEnd: workspace.shift_end,
|
|
50914
|
+
breaks: shiftBreaks,
|
|
50915
|
+
pphThreshold: workspace.pph_threshold,
|
|
50916
|
+
fallbackHours: Math.max(hourlyData.length, 1),
|
|
50917
|
+
rounding: "floor"
|
|
50918
|
+
}) : null;
|
|
50919
|
+
const formatIntervalLabel = (totalMinutes) => {
|
|
50920
|
+
const normalized = (totalMinutes % (24 * 60) + 24 * 60) % (24 * 60);
|
|
50921
|
+
const hour = Math.floor(normalized / 60);
|
|
50922
|
+
const minute = normalized % 60;
|
|
50923
|
+
const time2 = new Date(Date.UTC(2e3, 0, 1, hour, minute));
|
|
50924
|
+
return time2.toLocaleTimeString("en-IN", {
|
|
50925
|
+
hour: "numeric",
|
|
50926
|
+
...minute > 0 ? { minute: "2-digit" } : {},
|
|
50927
|
+
hour12: true,
|
|
50928
|
+
timeZone: "UTC"
|
|
50929
|
+
});
|
|
50930
|
+
};
|
|
50650
50931
|
const pageHeight = doc.internal.pageSize.height;
|
|
50651
50932
|
const maxContentY = pageHeight - 15;
|
|
50652
50933
|
const baseTableStartY = hourlyPerfStartY + 31;
|
|
@@ -50714,36 +50995,30 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
|
|
|
50714
50995
|
doc.setFontSize(contentFontSize);
|
|
50715
50996
|
doc.setFont("helvetica", "normal");
|
|
50716
50997
|
let yPos = headerBottomY + 5.5;
|
|
50717
|
-
const
|
|
50718
|
-
const today = /* @__PURE__ */ new Date();
|
|
50719
|
-
today.setHours(0, 0, 0, 0);
|
|
50720
|
-
workspaceDate.setHours(0, 0, 0, 0);
|
|
50721
|
-
const isToday2 = workspaceDate.getTime() === today.getTime();
|
|
50998
|
+
const isToday2 = getDateKeyInTimeZone(reportTimezone) === workspace.date;
|
|
50722
50999
|
let currentHour = 24;
|
|
50723
51000
|
if (isToday2) {
|
|
50724
51001
|
const now4 = /* @__PURE__ */ new Date();
|
|
50725
|
-
const
|
|
50726
|
-
currentHour =
|
|
51002
|
+
const currentTimeInReportTimezone = new Date(now4.toLocaleString("en-US", { timeZone: reportTimezone }));
|
|
51003
|
+
currentHour = currentTimeInReportTimezone.getHours();
|
|
50727
51004
|
}
|
|
50728
51005
|
hourlyData.forEach((entry, index) => {
|
|
50729
|
-
const
|
|
50730
|
-
|
|
50731
|
-
const
|
|
50732
|
-
|
|
50733
|
-
|
|
50734
|
-
|
|
50735
|
-
|
|
50736
|
-
|
|
50737
|
-
|
|
50738
|
-
hour12: true
|
|
50739
|
-
})}`;
|
|
50740
|
-
const hourNumber = startTime.getHours();
|
|
50741
|
-
const dataCollected = !isToday2 || hourNumber < currentHour;
|
|
51006
|
+
const interval = hourlyIntervals[index];
|
|
51007
|
+
const timeRange = interval ? `${formatIntervalLabel(interval.start)} - ${formatIntervalLabel(interval.end)}` : `${index + 1}`;
|
|
51008
|
+
const dataCollected = interval ? isHourlyIntervalComplete({
|
|
51009
|
+
reportDate: workspace.date,
|
|
51010
|
+
shiftStart: workspace.shift_start,
|
|
51011
|
+
shiftEnd: workspace.shift_end,
|
|
51012
|
+
interval,
|
|
51013
|
+
timeZone: reportTimezone
|
|
51014
|
+
}) : !isToday2 || currentHour >= 24;
|
|
50742
51015
|
const outputValue = isUptimeMode ? entry.activeMinutes ?? 0 : entry;
|
|
50743
51016
|
const idleValue = isUptimeMode ? entry.idleMinutes ?? 0 : 0;
|
|
50744
51017
|
const uptimePercent = isUptimeMode ? entry.uptimePercent ?? 0 : 0;
|
|
50745
51018
|
const outputStr = dataCollected ? outputValue.toString() : "TBD";
|
|
50746
|
-
const
|
|
51019
|
+
const effectiveTarget = outputTargetPlan?.targets[index] ?? hourlyTarget;
|
|
51020
|
+
const remarkText = outputTargetPlan?.breakRemarks[index] || "";
|
|
51021
|
+
const targetStr = isUptimeMode ? dataCollected ? idleValue.toString() : "TBD" : effectiveTarget.toString();
|
|
50747
51022
|
if (index < totalRows - 1) {
|
|
50748
51023
|
const rowBottomY = headerBottomY + (index + 1) * rowHeight;
|
|
50749
51024
|
doc.setDrawColor(200, 200, 200);
|
|
@@ -50775,10 +51050,12 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
|
|
|
50775
51050
|
} else {
|
|
50776
51051
|
doc.text(outputStr, 75, yPos);
|
|
50777
51052
|
doc.text(targetStr, 105, yPos);
|
|
51053
|
+
const remarkDisplay = remarkText ? doc.splitTextToSize(remarkText, 26)[0] : "";
|
|
51054
|
+
doc.text(remarkDisplay, 160, yPos);
|
|
50778
51055
|
if (!dataCollected) {
|
|
50779
51056
|
doc.setTextColor(100, 100, 100);
|
|
50780
51057
|
doc.text("-", 135, yPos);
|
|
50781
|
-
} else if (outputValue >=
|
|
51058
|
+
} else if (outputValue >= effectiveTarget) {
|
|
50782
51059
|
doc.setTextColor(0, 171, 69);
|
|
50783
51060
|
doc.setFont("ZapfDingbats", "normal");
|
|
50784
51061
|
doc.text("4", 135, yPos);
|
|
@@ -50792,11 +51069,10 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
|
|
|
50792
51069
|
yPos += rowHeight;
|
|
50793
51070
|
});
|
|
50794
51071
|
const workspaceDisplayName = getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
|
|
50795
|
-
const fileDate =
|
|
51072
|
+
const fileDate = formatOperationalDateKey2(workspace.date, {
|
|
50796
51073
|
day: "2-digit",
|
|
50797
51074
|
month: "short",
|
|
50798
|
-
year: "numeric"
|
|
50799
|
-
timeZone: "Asia/Kolkata"
|
|
51075
|
+
year: "numeric"
|
|
50800
51076
|
}).replace(/ /g, "_");
|
|
50801
51077
|
const fileShift = shiftType.replace(/ /g, "_");
|
|
50802
51078
|
const fileName = `${workspaceDisplayName}_${fileDate}_${fileShift}.pdf`;
|
|
@@ -50842,12 +51118,25 @@ var WorkspaceMonthlyPdfGenerator = ({
|
|
|
50842
51118
|
availableShifts,
|
|
50843
51119
|
shiftConfig,
|
|
50844
51120
|
efficiencyLegend,
|
|
51121
|
+
trendSummary,
|
|
50845
51122
|
className,
|
|
50846
51123
|
compact = false,
|
|
50847
51124
|
isAssemblyWorkspace = false
|
|
50848
51125
|
}) => {
|
|
50849
51126
|
const [isGenerating, setIsGenerating] = useState(false);
|
|
50850
51127
|
const effectiveLegend = efficiencyLegend || DEFAULT_EFFICIENCY_LEGEND;
|
|
51128
|
+
const drawStatusMark = (doc, x, y, met) => {
|
|
51129
|
+
doc.setLineWidth(0.8);
|
|
51130
|
+
if (met) {
|
|
51131
|
+
doc.setDrawColor(0, 171, 69);
|
|
51132
|
+
doc.line(x - 1.8, y - 0.2, x - 0.5, y + 1.2);
|
|
51133
|
+
doc.line(x - 0.5, y + 1.2, x + 2.1, y - 1.6);
|
|
51134
|
+
return;
|
|
51135
|
+
}
|
|
51136
|
+
doc.setDrawColor(227, 67, 41);
|
|
51137
|
+
doc.line(x - 1.8, y - 1.6, x + 1.8, y + 1.6);
|
|
51138
|
+
doc.line(x - 1.8, y + 1.6, x + 1.8, y - 1.6);
|
|
51139
|
+
};
|
|
50851
51140
|
const generatePDF = async () => {
|
|
50852
51141
|
setIsGenerating(true);
|
|
50853
51142
|
try {
|
|
@@ -51042,12 +51331,13 @@ var WorkspaceMonthlyPdfGenerator = ({
|
|
|
51042
51331
|
doc.setFont("helvetica", "bold");
|
|
51043
51332
|
doc.text(`${outputMetrics.underperformingDays} of ${outputMetrics.totalDays}`, 120, kpiStartY + kpiSpacing * 3);
|
|
51044
51333
|
} else {
|
|
51334
|
+
const medianCycleTime = Number.isFinite(trendSummary?.avg_cycle_time?.current) ? Number(trendSummary?.avg_cycle_time?.current) : outputMetrics.avgCycleTime;
|
|
51045
51335
|
createKPIBox(kpiStartY);
|
|
51046
51336
|
doc.setFontSize(11);
|
|
51047
51337
|
doc.setFont("helvetica", "normal");
|
|
51048
51338
|
doc.text("Average Cycle Time:", 25, kpiStartY);
|
|
51049
51339
|
doc.setFont("helvetica", "bold");
|
|
51050
|
-
doc.text(`${
|
|
51340
|
+
doc.text(`${medianCycleTime.toFixed(1)}s`, 120, kpiStartY);
|
|
51051
51341
|
createKPIBox(kpiStartY + kpiSpacing);
|
|
51052
51342
|
doc.setFont("helvetica", "normal");
|
|
51053
51343
|
doc.text("Average Idle Time:", 25, kpiStartY + kpiSpacing);
|
|
@@ -51082,8 +51372,8 @@ var WorkspaceMonthlyPdfGenerator = ({
|
|
|
51082
51372
|
doc.roundedRect(20, tableHeaderY, 170, 7, 1, 1, "F");
|
|
51083
51373
|
const textY = tableHeaderY + 5;
|
|
51084
51374
|
doc.text("Date", 25, textY);
|
|
51085
|
-
doc.text(isUptimeMode ? "Productive" : isAssemblyWorkspace ? "Cycle Time" : "Actual", 60, textY);
|
|
51086
|
-
doc.text(isUptimeMode ? "Idle" : isAssemblyWorkspace ? "
|
|
51375
|
+
doc.text(isUptimeMode ? "Productive" : isAssemblyWorkspace ? "Actual Cycle Time" : "Actual", 60, textY);
|
|
51376
|
+
doc.text(isUptimeMode ? "Idle" : isAssemblyWorkspace ? "Standard Cycle Time" : "Standard", 95, textY);
|
|
51087
51377
|
doc.text(isUptimeMode ? "Utilization" : "Efficiency", 135, textY);
|
|
51088
51378
|
doc.text("Status", 170, textY);
|
|
51089
51379
|
doc.setLineWidth(0.2);
|
|
@@ -51115,31 +51405,18 @@ var WorkspaceMonthlyPdfGenerator = ({
|
|
|
51115
51405
|
doc.text(formatIdleTime(productiveSeconds), 60, yPos);
|
|
51116
51406
|
doc.text(formatIdleTime(clampedIdleSeconds), 95, yPos);
|
|
51117
51407
|
doc.text(`${utilization}%`, 135, yPos);
|
|
51118
|
-
|
|
51119
|
-
doc.setTextColor(0, 171, 69);
|
|
51120
|
-
doc.text("\u2713", 170, yPos);
|
|
51121
|
-
} else {
|
|
51122
|
-
doc.setTextColor(227, 67, 41);
|
|
51123
|
-
doc.text("\xD7", 170, yPos);
|
|
51124
|
-
}
|
|
51125
|
-
doc.setTextColor(0, 0, 0);
|
|
51408
|
+
drawStatusMark(doc, 171, yPos - 0.3, utilization >= effectiveLegend.green_min);
|
|
51126
51409
|
} else {
|
|
51127
51410
|
if (isAssemblyWorkspace) {
|
|
51411
|
+
const targetCycleTime = Number.isFinite(shift.idealCycleTime) && Number(shift.idealCycleTime) > 0 ? Number(shift.idealCycleTime) : shift.pphThreshold > 0 ? 3600 / shift.pphThreshold : null;
|
|
51128
51412
|
doc.text(`${shift.cycleTime.toFixed(1)}`, 60, yPos);
|
|
51129
|
-
doc.text(
|
|
51413
|
+
doc.text(targetCycleTime !== null ? `${targetCycleTime.toFixed(1)}` : "-", 95, yPos);
|
|
51130
51414
|
} else {
|
|
51131
51415
|
doc.text(`${shift.output}`, 60, yPos);
|
|
51132
51416
|
doc.text(`${shift.targetOutput}`, 95, yPos);
|
|
51133
51417
|
}
|
|
51134
51418
|
doc.text(`${shift.efficiency.toFixed(1)}%`, 135, yPos);
|
|
51135
|
-
|
|
51136
|
-
doc.setTextColor(0, 171, 69);
|
|
51137
|
-
doc.text("\u2713", 170, yPos);
|
|
51138
|
-
} else {
|
|
51139
|
-
doc.setTextColor(227, 67, 41);
|
|
51140
|
-
doc.text("\xD7", 170, yPos);
|
|
51141
|
-
}
|
|
51142
|
-
doc.setTextColor(0, 0, 0);
|
|
51419
|
+
drawStatusMark(doc, 171, yPos - 0.3, shift.efficiency >= effectiveLegend.green_min);
|
|
51143
51420
|
}
|
|
51144
51421
|
yPos += 8;
|
|
51145
51422
|
});
|
|
@@ -58506,6 +58783,50 @@ var UserUsageStats = ({
|
|
|
58506
58783
|
] })
|
|
58507
58784
|
] });
|
|
58508
58785
|
};
|
|
58786
|
+
|
|
58787
|
+
// src/lib/utils/teamUsage.ts
|
|
58788
|
+
var ISO_DATE_KEY_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
58789
|
+
function formatLocalDateKey(date) {
|
|
58790
|
+
const year = date.getFullYear();
|
|
58791
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
58792
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
58793
|
+
return `${year}-${month}-${day}`;
|
|
58794
|
+
}
|
|
58795
|
+
function parseUsageDate(dateLike) {
|
|
58796
|
+
if (ISO_DATE_KEY_REGEX.test(dateLike)) {
|
|
58797
|
+
const [year, month, day] = dateLike.split("-").map(Number);
|
|
58798
|
+
return new Date(year, month - 1, day);
|
|
58799
|
+
}
|
|
58800
|
+
return new Date(dateLike);
|
|
58801
|
+
}
|
|
58802
|
+
function normalizeUsageDateKey(dateLike) {
|
|
58803
|
+
if (!dateLike) return void 0;
|
|
58804
|
+
if (ISO_DATE_KEY_REGEX.test(dateLike)) return dateLike;
|
|
58805
|
+
const parsed = parseUsageDate(dateLike);
|
|
58806
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
58807
|
+
return dateLike.split("T")[0] || dateLike;
|
|
58808
|
+
}
|
|
58809
|
+
return formatLocalDateKey(parsed);
|
|
58810
|
+
}
|
|
58811
|
+
function getCurrentWeekToDateUsageRange(today = /* @__PURE__ */ new Date()) {
|
|
58812
|
+
const normalizedToday = new Date(today);
|
|
58813
|
+
normalizedToday.setHours(0, 0, 0, 0);
|
|
58814
|
+
const dayOfWeek = normalizedToday.getDay();
|
|
58815
|
+
const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
|
|
58816
|
+
const monday = new Date(normalizedToday);
|
|
58817
|
+
monday.setDate(normalizedToday.getDate() - daysSinceMonday);
|
|
58818
|
+
return {
|
|
58819
|
+
startDate: formatLocalDateKey(monday),
|
|
58820
|
+
endDate: formatLocalDateKey(normalizedToday),
|
|
58821
|
+
daysElapsed: daysSinceMonday + 1
|
|
58822
|
+
};
|
|
58823
|
+
}
|
|
58824
|
+
function calculateAverageDailyDuration(durationMs, dayCount) {
|
|
58825
|
+
if (!Number.isFinite(durationMs) || dayCount <= 0) {
|
|
58826
|
+
return 0;
|
|
58827
|
+
}
|
|
58828
|
+
return Math.round(durationMs / dayCount);
|
|
58829
|
+
}
|
|
58509
58830
|
var UserUsageDetailModal = ({
|
|
58510
58831
|
userId,
|
|
58511
58832
|
userName,
|
|
@@ -58515,7 +58836,7 @@ var UserUsageDetailModal = ({
|
|
|
58515
58836
|
endDate
|
|
58516
58837
|
}) => {
|
|
58517
58838
|
const [currentWeekStart, setCurrentWeekStart] = useState(() => {
|
|
58518
|
-
if (startDate) return
|
|
58839
|
+
if (startDate) return parseUsageDate(startDate);
|
|
58519
58840
|
const { start } = getCurrentWeekRange();
|
|
58520
58841
|
return start;
|
|
58521
58842
|
});
|
|
@@ -58525,8 +58846,8 @@ var UserUsageDetailModal = ({
|
|
|
58525
58846
|
return end;
|
|
58526
58847
|
}, [currentWeekStart]);
|
|
58527
58848
|
const { data, isLoading, error } = useUserUsage(userId, {
|
|
58528
|
-
startDate: currentWeekStart
|
|
58529
|
-
endDate: currentWeekEnd
|
|
58849
|
+
startDate: formatLocalDateKey(currentWeekStart),
|
|
58850
|
+
endDate: formatLocalDateKey(currentWeekEnd)
|
|
58530
58851
|
});
|
|
58531
58852
|
const handlePrevWeek = () => {
|
|
58532
58853
|
setCurrentWeekStart((prev) => {
|
|
@@ -58619,8 +58940,8 @@ function UsageContent({
|
|
|
58619
58940
|
} else {
|
|
58620
58941
|
daysToCount = 7;
|
|
58621
58942
|
}
|
|
58622
|
-
const
|
|
58623
|
-
const weekAvgMs =
|
|
58943
|
+
const weekActiveMs = weekData.reduce((sum, d) => sum + d.active_duration_ms, 0);
|
|
58944
|
+
const weekAvgMs = calculateAverageDailyDuration(weekActiveMs, daysToCount);
|
|
58624
58945
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
58625
58946
|
/* @__PURE__ */ jsx("div", { className: "flex justify-center mb-6", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 bg-gray-50 rounded-lg p-1 border border-gray-100", children: [
|
|
58626
58947
|
/* @__PURE__ */ jsx(
|
|
@@ -58632,7 +58953,7 @@ function UsageContent({
|
|
|
58632
58953
|
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "w-4 h-4" })
|
|
58633
58954
|
}
|
|
58634
58955
|
),
|
|
58635
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-700 min-w-[140px] text-center", children: formatDateRange(currentWeekStart
|
|
58956
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-700 min-w-[140px] text-center", children: formatDateRange(formatLocalDateKey(currentWeekStart), formatLocalDateKey(currentWeekEnd)) }),
|
|
58636
58957
|
/* @__PURE__ */ jsx(
|
|
58637
58958
|
"button",
|
|
58638
58959
|
{
|
|
@@ -58664,7 +58985,7 @@ function DailyBarChart({
|
|
|
58664
58985
|
const weekData = useMemo(() => buildWeekData(dailyData, getDateStringsInRange(weekStart, weekEnd)), [dailyData, weekStart, weekEnd]);
|
|
58665
58986
|
const chartData = useMemo(() => {
|
|
58666
58987
|
return weekData.map((day, index) => {
|
|
58667
|
-
const date =
|
|
58988
|
+
const date = parseUsageDate(day.date);
|
|
58668
58989
|
return {
|
|
58669
58990
|
...day,
|
|
58670
58991
|
index,
|
|
@@ -58674,7 +58995,7 @@ function DailyBarChart({
|
|
|
58674
58995
|
// green-500
|
|
58675
58996
|
passiveColor: "#fbbf24",
|
|
58676
58997
|
// amber-400
|
|
58677
|
-
isToday: day.date === (/* @__PURE__ */ new Date())
|
|
58998
|
+
isToday: day.date === formatLocalDateKey(/* @__PURE__ */ new Date())
|
|
58678
58999
|
};
|
|
58679
59000
|
});
|
|
58680
59001
|
}, [weekData]);
|
|
@@ -58694,7 +59015,7 @@ function DailyBarChart({
|
|
|
58694
59015
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
58695
59016
|
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 rounded-full bg-gray-300" }),
|
|
58696
59017
|
/* @__PURE__ */ jsxs("span", { className: "text-sm font-medium text-gray-600", children: [
|
|
58697
|
-
"Average Daily usage: ",
|
|
59018
|
+
"Average Daily active usage: ",
|
|
58698
59019
|
/* @__PURE__ */ jsx("span", { className: "text-gray-900 font-semibold", children: avgDailyMs > 0 ? formatDuration2(avgDailyMs) : "0m" })
|
|
58699
59020
|
] })
|
|
58700
59021
|
] }),
|
|
@@ -58859,17 +59180,14 @@ function getCurrentWeekRange() {
|
|
|
58859
59180
|
function getDateStringsInRange(start, end) {
|
|
58860
59181
|
const result = [];
|
|
58861
59182
|
for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
|
|
58862
|
-
|
|
58863
|
-
const month = String(d.getMonth() + 1).padStart(2, "0");
|
|
58864
|
-
const day = String(d.getDate()).padStart(2, "0");
|
|
58865
|
-
result.push(`${year}-${month}-${day}`);
|
|
59183
|
+
result.push(formatLocalDateKey(d));
|
|
58866
59184
|
}
|
|
58867
59185
|
return result;
|
|
58868
59186
|
}
|
|
58869
59187
|
function buildWeekData(dailyData, weekDates) {
|
|
58870
59188
|
const dataMap = new Map(
|
|
58871
59189
|
(dailyData || []).map((d) => {
|
|
58872
|
-
const key =
|
|
59190
|
+
const key = normalizeUsageDateKey(d.date);
|
|
58873
59191
|
return [key, { ...d, date: key }];
|
|
58874
59192
|
})
|
|
58875
59193
|
);
|
|
@@ -58884,22 +59202,10 @@ function buildWeekData(dailyData, weekDates) {
|
|
|
58884
59202
|
};
|
|
58885
59203
|
});
|
|
58886
59204
|
}
|
|
58887
|
-
function normalizeDateKey(dateLike) {
|
|
58888
|
-
if (!dateLike) return void 0;
|
|
58889
|
-
try {
|
|
58890
|
-
const date = new Date(dateLike);
|
|
58891
|
-
const year = date.getFullYear();
|
|
58892
|
-
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
58893
|
-
const day = String(date.getDate()).padStart(2, "0");
|
|
58894
|
-
return `${year}-${month}-${day}`;
|
|
58895
|
-
} catch {
|
|
58896
|
-
return dateLike.split("T")[0] || dateLike;
|
|
58897
|
-
}
|
|
58898
|
-
}
|
|
58899
59205
|
function formatDateRange(startDate, endDate) {
|
|
58900
59206
|
try {
|
|
58901
|
-
const start =
|
|
58902
|
-
const end =
|
|
59207
|
+
const start = parseUsageDate(startDate);
|
|
59208
|
+
const end = parseUsageDate(endDate);
|
|
58903
59209
|
const opts = { month: "short", day: "numeric" };
|
|
58904
59210
|
const yearOpts = { year: "numeric" };
|
|
58905
59211
|
return `${start.toLocaleDateString("en-US", opts)} - ${end.toLocaleDateString("en-US", opts)}, ${end.toLocaleDateString("en-US", yearOpts)}`;
|
|
@@ -58910,7 +59216,7 @@ function formatDateRange(startDate, endDate) {
|
|
|
58910
59216
|
function formatDate(dateStr) {
|
|
58911
59217
|
if (!dateStr) return "";
|
|
58912
59218
|
try {
|
|
58913
|
-
const date =
|
|
59219
|
+
const date = parseUsageDate(dateStr);
|
|
58914
59220
|
return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
58915
59221
|
} catch {
|
|
58916
59222
|
return dateStr;
|
|
@@ -59188,7 +59494,7 @@ var UserManagementTable = ({
|
|
|
59188
59494
|
}
|
|
59189
59495
|
),
|
|
59190
59496
|
/* @__PURE__ */ jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Assignments" }),
|
|
59191
|
-
showUsageStats && /* @__PURE__ */ jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Average Daily
|
|
59497
|
+
showUsageStats && /* @__PURE__ */ jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Average Daily Active Usage" }),
|
|
59192
59498
|
/* @__PURE__ */ jsx("th", { className: "px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Actions" })
|
|
59193
59499
|
] }) }),
|
|
59194
59500
|
/* @__PURE__ */ jsx("tbody", { className: "bg-white divide-y divide-gray-200", children: filteredAndSortedUsers.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: showUsageStats ? 5 : 4, className: "px-6 py-12", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center text-gray-400 gap-2", children: [
|
|
@@ -64305,7 +64611,17 @@ var KPIDetailView = ({
|
|
|
64305
64611
|
)
|
|
64306
64612
|
] }),
|
|
64307
64613
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 ml-auto", children: [
|
|
64308
|
-
resolvedLineInfo && activeTab === "overview" && /* @__PURE__ */ jsx(
|
|
64614
|
+
resolvedLineInfo && activeTab === "overview" && /* @__PURE__ */ jsx(
|
|
64615
|
+
LinePdfGenerator,
|
|
64616
|
+
{
|
|
64617
|
+
lineInfo: resolvedLineInfo,
|
|
64618
|
+
workspaceData: resolvedWorkspaces || [],
|
|
64619
|
+
issueResolutionSummary,
|
|
64620
|
+
shiftName: getShiftName(resolvedLineInfo.shift_id),
|
|
64621
|
+
shiftBreaks: shiftConfig?.shifts?.find((shift) => shift.shiftId === resolvedLineInfo.shift_id)?.breaks || [],
|
|
64622
|
+
reportTimezone: shiftConfig?.timezone || configuredTimezone
|
|
64623
|
+
}
|
|
64624
|
+
),
|
|
64309
64625
|
activeTab === "monthly_history" && !urlDate && !urlShift && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
64310
64626
|
/* @__PURE__ */ jsx(
|
|
64311
64627
|
MonthlyRangeFilter_default,
|
|
@@ -64334,6 +64650,8 @@ var KPIDetailView = ({
|
|
|
64334
64650
|
rangeStart,
|
|
64335
64651
|
rangeEnd,
|
|
64336
64652
|
selectedShiftId,
|
|
64653
|
+
availableShifts: shiftConfig?.shifts?.map((shift) => ({ id: shift.shiftId, name: shift.shiftName })),
|
|
64654
|
+
lineAssembly: resolvedLineInfo?.assembly === true,
|
|
64337
64655
|
compact: true
|
|
64338
64656
|
}
|
|
64339
64657
|
)
|
|
@@ -64512,7 +64830,7 @@ var KPIDetailViewWithDisplayNames = withSelectedLineDisplayNames(KPIDetailView);
|
|
|
64512
64830
|
var KPIDetailView_default = KPIDetailViewWithDisplayNames;
|
|
64513
64831
|
var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
|
|
64514
64832
|
var resolveCompanyId = (...candidates) => candidates.find(isNonEmptyString);
|
|
64515
|
-
var
|
|
64833
|
+
var parseTimeToMinutes3 = (value) => {
|
|
64516
64834
|
if (!value) return null;
|
|
64517
64835
|
const [hourStr, minuteStr] = value.split(":");
|
|
64518
64836
|
const hour = Number.parseInt(hourStr ?? "", 10);
|
|
@@ -64524,8 +64842,8 @@ var getShiftEndDate = (shift, timezone) => {
|
|
|
64524
64842
|
if (!shift?.date) return null;
|
|
64525
64843
|
const startTime = shift.startTime || "06:00";
|
|
64526
64844
|
const endTime = shift.endTime || "18:00";
|
|
64527
|
-
const startMinutes =
|
|
64528
|
-
const endMinutes =
|
|
64845
|
+
const startMinutes = parseTimeToMinutes3(startTime);
|
|
64846
|
+
const endMinutes = parseTimeToMinutes3(endTime);
|
|
64529
64847
|
if (startMinutes === null || endMinutes === null) return null;
|
|
64530
64848
|
const shiftStartDate = fromZonedTime(`${shift.date}T${startTime}:00`, timezone);
|
|
64531
64849
|
let durationMinutes = endMinutes - startMinutes;
|
|
@@ -64563,7 +64881,7 @@ var createKpisOverviewUrl = ({
|
|
|
64563
64881
|
return queryString ? `/kpis?${queryString}` : "/kpis";
|
|
64564
64882
|
};
|
|
64565
64883
|
var getZonedDateAtMidday = (dateKey, timezone) => fromZonedTime(`${dateKey}T12:00:00`, timezone);
|
|
64566
|
-
var
|
|
64884
|
+
var formatDateKey2 = (dateKey, timezone, options) => {
|
|
64567
64885
|
try {
|
|
64568
64886
|
return new Intl.DateTimeFormat("en-US", {
|
|
64569
64887
|
...options,
|
|
@@ -65631,7 +65949,7 @@ var KPIsOverviewView = ({
|
|
|
65631
65949
|
setActiveTab(newTab);
|
|
65632
65950
|
}, [activeTab, leaderboardLines.length, lines.length]);
|
|
65633
65951
|
const formatLocalDate2 = useCallback((dateKey) => {
|
|
65634
|
-
return
|
|
65952
|
+
return formatDateKey2(dateKey, configuredTimezone, {
|
|
65635
65953
|
year: "numeric",
|
|
65636
65954
|
month: "long",
|
|
65637
65955
|
day: "numeric"
|
|
@@ -65645,8 +65963,8 @@ var KPIsOverviewView = ({
|
|
|
65645
65963
|
zonedNow.getMonth(),
|
|
65646
65964
|
new Date(zonedNow.getFullYear(), zonedNow.getMonth() + 1, 0).getDate()
|
|
65647
65965
|
);
|
|
65648
|
-
const startLabel =
|
|
65649
|
-
const endLabel =
|
|
65966
|
+
const startLabel = formatDateKey2(startOfMonthKey, configuredTimezone, { month: "short", day: "numeric" });
|
|
65967
|
+
const endLabel = formatDateKey2(endOfMonthKey, configuredTimezone, { month: "short", day: "numeric" });
|
|
65650
65968
|
return `${startLabel} - ${endLabel}, ${zonedNow.getFullYear()}`;
|
|
65651
65969
|
};
|
|
65652
65970
|
const isMonthlyMode = activeTab === "leaderboard" && timeRange === "monthly";
|
|
@@ -71281,6 +71599,7 @@ var WorkspaceDetailView = ({
|
|
|
71281
71599
|
efficiency: monitoringMode === "uptime" ? Number.isFinite(metric.avg_efficiency) ? Number(metric.avg_efficiency) : computedUptimeEfficiency : Number.isFinite(metric.avg_efficiency) ? Number(metric.avg_efficiency) : 0,
|
|
71282
71600
|
output: metric.total_output || 0,
|
|
71283
71601
|
cycleTime: metric.avg_cycle_time || 0,
|
|
71602
|
+
idealCycleTime: Number(metric.ideal_cycle_time || 0),
|
|
71284
71603
|
pph: metric.avg_pph || 0,
|
|
71285
71604
|
pphThreshold: metric.pph_threshold || 0,
|
|
71286
71605
|
idealOutput: Number(metric.ideal_output || 0),
|
|
@@ -71836,7 +72155,9 @@ var WorkspaceDetailView = ({
|
|
|
71836
72155
|
workspace,
|
|
71837
72156
|
idleTimeReasons: idleTimeChartData,
|
|
71838
72157
|
efficiencyLegend,
|
|
71839
|
-
hourlyCycleTimes: cycleTimeChartData
|
|
72158
|
+
hourlyCycleTimes: cycleTimeChartData,
|
|
72159
|
+
shiftBreaks: shiftConfig?.shifts?.find((shift2) => shift2.shiftId === workspace.shift_id)?.breaks || [],
|
|
72160
|
+
reportTimezone: shiftConfig?.timezone || timezone
|
|
71840
72161
|
}
|
|
71841
72162
|
) }),
|
|
71842
72163
|
activeTab === "monthly_history" && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
@@ -71871,6 +72192,7 @@ var WorkspaceDetailView = ({
|
|
|
71871
72192
|
workspaceId,
|
|
71872
72193
|
workspaceName: formattedWorkspaceName,
|
|
71873
72194
|
monthlyData,
|
|
72195
|
+
analysisData: analysisMonthlyData,
|
|
71874
72196
|
selectedMonth,
|
|
71875
72197
|
selectedYear,
|
|
71876
72198
|
monitoringMode: workspace?.monitoring_mode,
|
|
@@ -71879,7 +72201,10 @@ var WorkspaceDetailView = ({
|
|
|
71879
72201
|
selectedShiftId: selectedShift,
|
|
71880
72202
|
availableShifts: shiftConfig?.shifts?.map((s) => ({ id: s.shiftId, name: s.shiftName })),
|
|
71881
72203
|
shiftConfig,
|
|
71882
|
-
|
|
72204
|
+
efficiencyLegend,
|
|
72205
|
+
trendSummary: workspaceMonthlyTrend,
|
|
72206
|
+
compact: true,
|
|
72207
|
+
isAssemblyWorkspace
|
|
71883
72208
|
}
|
|
71884
72209
|
)
|
|
71885
72210
|
] })
|
|
@@ -73466,39 +73791,8 @@ var TeamManagementView = ({
|
|
|
73466
73791
|
const [users, setUsers] = useState([]);
|
|
73467
73792
|
const [availableLines, setAvailableLines] = useState([]);
|
|
73468
73793
|
const [availableFactories, setAvailableFactories] = useState([]);
|
|
73469
|
-
const [
|
|
73470
|
-
totalUsers: 0,
|
|
73471
|
-
owners: 0,
|
|
73472
|
-
it: 0,
|
|
73473
|
-
plantHeads: 0,
|
|
73474
|
-
industrialEngineers: 0,
|
|
73475
|
-
supervisors: 0,
|
|
73476
|
-
optifye: 0
|
|
73477
|
-
});
|
|
73794
|
+
const [usageSummaryByUser, setUsageSummaryByUser] = useState({});
|
|
73478
73795
|
const [isAddUserDialogOpen, setIsAddUserDialogOpen] = useState(false);
|
|
73479
|
-
const normalizeIds = useCallback((value) => {
|
|
73480
|
-
if (Array.isArray(value)) {
|
|
73481
|
-
return value.filter((id3) => typeof id3 === "string" && id3.length > 0);
|
|
73482
|
-
}
|
|
73483
|
-
if (typeof value === "string" && value.length > 0) {
|
|
73484
|
-
return [value];
|
|
73485
|
-
}
|
|
73486
|
-
return [];
|
|
73487
|
-
}, []);
|
|
73488
|
-
const factoryScopedRoleFactoryIds = useMemo(() => {
|
|
73489
|
-
if (!isFactoryScopedRole(user?.role_level)) return [];
|
|
73490
|
-
const scopedFactoryIds = normalizeIds(user?.access_scope?.factory_ids);
|
|
73491
|
-
if (scopedFactoryIds.length > 0) {
|
|
73492
|
-
return Array.from(new Set(scopedFactoryIds));
|
|
73493
|
-
}
|
|
73494
|
-
const propertyFactoryIds = normalizeIds(
|
|
73495
|
-
user?.properties?.factory_ids ?? user?.properties?.factory_id
|
|
73496
|
-
);
|
|
73497
|
-
if (propertyFactoryIds.length > 0) {
|
|
73498
|
-
return Array.from(new Set(propertyFactoryIds));
|
|
73499
|
-
}
|
|
73500
|
-
return entityConfig?.factoryId ? [entityConfig.factoryId] : [];
|
|
73501
|
-
}, [user, entityConfig?.factoryId, normalizeIds]);
|
|
73502
73796
|
const notifyScopeRefresh = useCallback(() => {
|
|
73503
73797
|
if (typeof window !== "undefined") {
|
|
73504
73798
|
window.dispatchEvent(new Event("rbac:refresh-scope"));
|
|
@@ -73507,37 +73801,17 @@ var TeamManagementView = ({
|
|
|
73507
73801
|
const canAddUsers = canRoleManageUsers(user?.role_level);
|
|
73508
73802
|
const canViewUsageStats = canRoleViewUsageStats(user?.role_level);
|
|
73509
73803
|
const pageCompanyId = entityConfig?.companyId || user?.properties?.company_id;
|
|
73510
|
-
const
|
|
73511
|
-
const usageDateRange = useMemo(() => {
|
|
73512
|
-
const today = /* @__PURE__ */ new Date();
|
|
73513
|
-
const dayOfWeek = today.getDay();
|
|
73514
|
-
const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
|
|
73515
|
-
const monday = new Date(today);
|
|
73516
|
-
monday.setDate(today.getDate() - daysSinceMonday);
|
|
73517
|
-
return {
|
|
73518
|
-
startDate: monday.toISOString().slice(0, 10),
|
|
73519
|
-
endDate: today.toISOString().slice(0, 10),
|
|
73520
|
-
daysElapsed: daysSinceMonday + 1
|
|
73521
|
-
// Include today
|
|
73522
|
-
};
|
|
73523
|
-
}, []);
|
|
73524
|
-
const {
|
|
73525
|
-
data: usageData,
|
|
73526
|
-
isLoading: isUsageLoading
|
|
73527
|
-
} = useCompanyUsersUsage(canViewUsageStats ? companyIdForUsage : void 0, {
|
|
73528
|
-
startDate: usageDateRange.startDate,
|
|
73529
|
-
endDate: usageDateRange.endDate
|
|
73530
|
-
});
|
|
73804
|
+
const usageDateRange = useMemo(() => getCurrentWeekToDateUsageRange(), []);
|
|
73531
73805
|
const avgDailyUsageMap = useMemo(() => {
|
|
73532
|
-
if (!
|
|
73806
|
+
if (!canViewUsageStats) {
|
|
73533
73807
|
return {};
|
|
73534
73808
|
}
|
|
73535
73809
|
const daysElapsed = usageDateRange.daysElapsed;
|
|
73536
|
-
return
|
|
73537
|
-
acc[
|
|
73810
|
+
return Object.entries(usageSummaryByUser).reduce((acc, [userId, usage]) => {
|
|
73811
|
+
acc[userId] = calculateAverageDailyDuration(usage.active_duration_ms, daysElapsed);
|
|
73538
73812
|
return acc;
|
|
73539
73813
|
}, {});
|
|
73540
|
-
}, [
|
|
73814
|
+
}, [canViewUsageStats, usageSummaryByUser, usageDateRange.daysElapsed]);
|
|
73541
73815
|
const pageTitle = "Team Management";
|
|
73542
73816
|
const pageDescription = canViewUsageStats ? "Manage team members and view their daily usage" : "Manage supervisors in your factory";
|
|
73543
73817
|
const hasAccess = user ? user.role_level === void 0 || canRoleAccessTeamManagement(user.role_level) : false;
|
|
@@ -73561,7 +73835,6 @@ var TeamManagementView = ({
|
|
|
73561
73835
|
setIsLoading(true);
|
|
73562
73836
|
setError(void 0);
|
|
73563
73837
|
try {
|
|
73564
|
-
const userManagementService = createUserManagementService(supabase);
|
|
73565
73838
|
const { data: { session } } = await supabase.auth.getSession();
|
|
73566
73839
|
if (!session?.access_token) {
|
|
73567
73840
|
throw new Error("No authentication token available");
|
|
@@ -73571,116 +73844,31 @@ var TeamManagementView = ({
|
|
|
73571
73844
|
if (!backendUrl) {
|
|
73572
73845
|
throw new Error("Backend URL is not configured. Please set NEXT_PUBLIC_BACKEND_URL in your environment.");
|
|
73573
73846
|
}
|
|
73574
|
-
|
|
73575
|
-
|
|
73576
|
-
|
|
73577
|
-
|
|
73578
|
-
|
|
73579
|
-
|
|
73580
|
-
|
|
73581
|
-
});
|
|
73582
|
-
if (!linesResponse.ok) {
|
|
73583
|
-
throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
|
|
73584
|
-
}
|
|
73585
|
-
const linesData = await linesResponse.json();
|
|
73586
|
-
const lines = linesData.lines || [];
|
|
73587
|
-
setAvailableFactories(factories || []);
|
|
73588
|
-
setAvailableLines(lines);
|
|
73589
|
-
console.log("[TeamManagementView] Company-scoped team view - Company:", companyId, "Loaded factories:", factories?.length, "lines:", lines?.length);
|
|
73590
|
-
} else if (isFactoryScopedRole(user?.role_level)) {
|
|
73591
|
-
if (factoryScopedRoleFactoryIds.length > 0) {
|
|
73592
|
-
if (companyId) {
|
|
73593
|
-
const linesResponse = await fetch(`${backendUrl}/api/lines?company_id=${companyId}`, {
|
|
73594
|
-
headers: {
|
|
73595
|
-
"Authorization": `Bearer ${token}`,
|
|
73596
|
-
"Content-Type": "application/json"
|
|
73597
|
-
}
|
|
73598
|
-
});
|
|
73599
|
-
if (!linesResponse.ok) {
|
|
73600
|
-
throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
|
|
73601
|
-
}
|
|
73602
|
-
const linesData = await linesResponse.json();
|
|
73603
|
-
const allLines = linesData.lines || [];
|
|
73604
|
-
const lines = allLines.filter((line) => factoryScopedRoleFactoryIds.includes(line.factory_id));
|
|
73605
|
-
setAvailableLines(lines);
|
|
73606
|
-
console.log("[TeamManagementView] Factory-scoped team view - Factories:", factoryScopedRoleFactoryIds, "Loaded lines:", lines?.length);
|
|
73607
|
-
}
|
|
73608
|
-
} else if (entityConfig?.factoryId) {
|
|
73609
|
-
const linesResponse = await fetch(`${backendUrl}/api/lines?factory_id=${entityConfig.factoryId}`, {
|
|
73610
|
-
headers: {
|
|
73611
|
-
"Authorization": `Bearer ${token}`,
|
|
73612
|
-
"Content-Type": "application/json"
|
|
73613
|
-
}
|
|
73614
|
-
});
|
|
73615
|
-
if (!linesResponse.ok) {
|
|
73616
|
-
throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
|
|
73617
|
-
}
|
|
73618
|
-
const linesData = await linesResponse.json();
|
|
73619
|
-
const lines = linesData.lines || [];
|
|
73620
|
-
setAvailableLines(lines);
|
|
73621
|
-
console.log("[TeamManagementView] Plant Head - Using entityConfig factory:", entityConfig.factoryId, "Loaded lines:", lines?.length);
|
|
73622
|
-
} else {
|
|
73623
|
-
setAvailableLines([]);
|
|
73624
|
-
console.warn("[TeamManagementView] Plant Head has no factory assignments");
|
|
73625
|
-
}
|
|
73626
|
-
setAvailableFactories([]);
|
|
73627
|
-
} else if (companyId) {
|
|
73628
|
-
const linesResponse = await fetch(`${backendUrl}/api/lines?company_id=${companyId}`, {
|
|
73847
|
+
const params = new URLSearchParams({
|
|
73848
|
+
start_date: usageDateRange.startDate,
|
|
73849
|
+
end_date: usageDateRange.endDate
|
|
73850
|
+
});
|
|
73851
|
+
const bootstrapResponse = await fetch(
|
|
73852
|
+
`${backendUrl}/api/users/company/${companyId}/bootstrap?${params.toString()}`,
|
|
73853
|
+
{
|
|
73629
73854
|
headers: {
|
|
73630
73855
|
"Authorization": `Bearer ${token}`,
|
|
73631
73856
|
"Content-Type": "application/json"
|
|
73632
73857
|
}
|
|
73633
|
-
});
|
|
73634
|
-
if (!linesResponse.ok) {
|
|
73635
|
-
throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
|
|
73636
73858
|
}
|
|
73637
|
-
|
|
73638
|
-
|
|
73639
|
-
|
|
73640
|
-
|
|
73641
|
-
|
|
73642
|
-
|
|
73643
|
-
|
|
73644
|
-
|
|
73645
|
-
|
|
73646
|
-
|
|
73647
|
-
|
|
73648
|
-
|
|
73649
|
-
|
|
73650
|
-
const successful = results.filter(
|
|
73651
|
-
(result) => result.status === "fulfilled"
|
|
73652
|
-
).flatMap((result) => result.value || []);
|
|
73653
|
-
if (successful.length > 0) {
|
|
73654
|
-
const byUserId = /* @__PURE__ */ new Map();
|
|
73655
|
-
successful.forEach((u) => {
|
|
73656
|
-
if (u?.user_id) {
|
|
73657
|
-
byUserId.set(u.user_id, u);
|
|
73658
|
-
}
|
|
73659
|
-
});
|
|
73660
|
-
return Array.from(byUserId.values());
|
|
73661
|
-
}
|
|
73662
|
-
const firstRejected = results.find(
|
|
73663
|
-
(result) => result.status === "rejected"
|
|
73664
|
-
);
|
|
73665
|
-
if (firstRejected) {
|
|
73666
|
-
throw firstRejected.reason;
|
|
73667
|
-
}
|
|
73668
|
-
return [];
|
|
73669
|
-
})() : userManagementService.getCompanyUsers(companyId);
|
|
73670
|
-
const [usersData, userStats] = await Promise.all([
|
|
73671
|
-
usersPromise,
|
|
73672
|
-
userManagementService.getUserStats(companyId)
|
|
73673
|
-
]);
|
|
73674
|
-
setUsers(usersData);
|
|
73675
|
-
setStats({
|
|
73676
|
-
totalUsers: userStats.total,
|
|
73677
|
-
owners: userStats.owners,
|
|
73678
|
-
it: userStats.it,
|
|
73679
|
-
plantHeads: userStats.plant_heads,
|
|
73680
|
-
industrialEngineers: userStats.industrial_engineers,
|
|
73681
|
-
supervisors: userStats.supervisors,
|
|
73682
|
-
optifye: 0
|
|
73683
|
-
});
|
|
73859
|
+
);
|
|
73860
|
+
if (!bootstrapResponse.ok) {
|
|
73861
|
+
const errorData = await bootstrapResponse.json().catch(() => ({}));
|
|
73862
|
+
throw new Error(errorData.detail || `Failed to fetch team bootstrap: ${bootstrapResponse.statusText}`);
|
|
73863
|
+
}
|
|
73864
|
+
const bootstrapData = await bootstrapResponse.json();
|
|
73865
|
+
setAvailableFactories((bootstrapData.factories || []).map((factory) => ({
|
|
73866
|
+
id: factory.id,
|
|
73867
|
+
factory_name: factory.factory_name
|
|
73868
|
+
})));
|
|
73869
|
+
setAvailableLines(bootstrapData.lines || []);
|
|
73870
|
+
setUsers(bootstrapData.users || []);
|
|
73871
|
+
setUsageSummaryByUser(bootstrapData.usage_summary_by_user || {});
|
|
73684
73872
|
} catch (err) {
|
|
73685
73873
|
console.error("Error loading team management data:", err);
|
|
73686
73874
|
setError(err instanceof Error ? err.message : "Failed to load data");
|
|
@@ -73688,7 +73876,7 @@ var TeamManagementView = ({
|
|
|
73688
73876
|
} finally {
|
|
73689
73877
|
setIsLoading(false);
|
|
73690
73878
|
}
|
|
73691
|
-
}, [supabase, user, pageCompanyId,
|
|
73879
|
+
}, [supabase, user, pageCompanyId, usageDateRange.endDate, usageDateRange.startDate]);
|
|
73692
73880
|
useEffect(() => {
|
|
73693
73881
|
const companyId = pageCompanyId;
|
|
73694
73882
|
const canLoad = hasAccess && user && !!companyId;
|
|
@@ -73869,7 +74057,7 @@ var TeamManagementView = ({
|
|
|
73869
74057
|
availableLines,
|
|
73870
74058
|
availableFactories,
|
|
73871
74059
|
avgDailyUsage: avgDailyUsageMap,
|
|
73872
|
-
isUsageLoading,
|
|
74060
|
+
isUsageLoading: false,
|
|
73873
74061
|
showUsageStats: canViewUsageStats
|
|
73874
74062
|
}
|
|
73875
74063
|
) }),
|
|
@@ -80094,7 +80282,7 @@ var useOperationsOverviewRefresh = ({
|
|
|
80094
80282
|
};
|
|
80095
80283
|
}, [companyId, enabled, getIsPageActive, isLiveScope, lineIds.length, refreshFromResume, startPolling, stopPolling, supabase]);
|
|
80096
80284
|
};
|
|
80097
|
-
var
|
|
80285
|
+
var parseTimeToMinutes4 = (value) => {
|
|
80098
80286
|
if (!value) return null;
|
|
80099
80287
|
const parts = value.split(":");
|
|
80100
80288
|
if (parts.length < 2) return null;
|
|
@@ -80129,8 +80317,8 @@ var classifyShiftBucket = ({
|
|
|
80129
80317
|
return "day";
|
|
80130
80318
|
}
|
|
80131
80319
|
}
|
|
80132
|
-
const startMinutes =
|
|
80133
|
-
const endMinutes =
|
|
80320
|
+
const startMinutes = parseTimeToMinutes4(startTime);
|
|
80321
|
+
const endMinutes = parseTimeToMinutes4(endTime);
|
|
80134
80322
|
if (startMinutes !== null) {
|
|
80135
80323
|
if (startMinutes >= 4 * 60 && startMinutes < 18 * 60) return "day";
|
|
80136
80324
|
return "night";
|
|
@@ -80189,8 +80377,8 @@ var getShiftWindowsForConfig = (shiftConfig, timezone) => {
|
|
|
80189
80377
|
];
|
|
80190
80378
|
};
|
|
80191
80379
|
var normalizeShiftWindowMinutes = (startTime, endTime) => {
|
|
80192
|
-
const startMinutes =
|
|
80193
|
-
const endMinutesRaw =
|
|
80380
|
+
const startMinutes = parseTimeToMinutes4(startTime);
|
|
80381
|
+
const endMinutesRaw = parseTimeToMinutes4(endTime);
|
|
80194
80382
|
if (startMinutes === null || endMinutesRaw === null) {
|
|
80195
80383
|
return null;
|
|
80196
80384
|
}
|
|
@@ -80356,7 +80544,7 @@ var PlantHeadView = () => {
|
|
|
80356
80544
|
startTime: shift.startTime,
|
|
80357
80545
|
endTime: shift.endTime
|
|
80358
80546
|
});
|
|
80359
|
-
const startMinutes =
|
|
80547
|
+
const startMinutes = parseTimeToMinutes4(shift.startTime);
|
|
80360
80548
|
if (bucket === "day" && startMinutes !== null) {
|
|
80361
80549
|
candidateStarts.push(startMinutes);
|
|
80362
80550
|
}
|
|
@@ -80366,7 +80554,7 @@ var PlantHeadView = () => {
|
|
|
80366
80554
|
scopedLineIds.forEach((lineId) => {
|
|
80367
80555
|
const shiftConfig = shiftConfigMap.get(lineId) || staticShiftConfig;
|
|
80368
80556
|
getShiftWindowsForConfig(shiftConfig).forEach((shift) => {
|
|
80369
|
-
const startMinutes =
|
|
80557
|
+
const startMinutes = parseTimeToMinutes4(shift.startTime);
|
|
80370
80558
|
if (startMinutes !== null) {
|
|
80371
80559
|
candidateStarts.push(startMinutes);
|
|
80372
80560
|
}
|
|
@@ -80457,7 +80645,7 @@ var PlantHeadView = () => {
|
|
|
80457
80645
|
startTime: shift.startTime,
|
|
80458
80646
|
endTime: shift.endTime
|
|
80459
80647
|
});
|
|
80460
|
-
return bucket === "day" ?
|
|
80648
|
+
return bucket === "day" ? parseTimeToMinutes4(shift.startTime) : null;
|
|
80461
80649
|
}).filter((value) => value !== null);
|
|
80462
80650
|
}) : [];
|
|
80463
80651
|
if (dayStartCandidates.length > 0) {
|