@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.js
CHANGED
|
@@ -1667,6 +1667,7 @@ var DEFAULT_SHIFT_DATA = {
|
|
|
1667
1667
|
efficiency: 0,
|
|
1668
1668
|
output: 0,
|
|
1669
1669
|
cycleTime: 0,
|
|
1670
|
+
idealCycleTime: 0,
|
|
1670
1671
|
pph: 0,
|
|
1671
1672
|
pphThreshold: 0,
|
|
1672
1673
|
idealOutput: 0,
|
|
@@ -4513,6 +4514,7 @@ var dashboardService = {
|
|
|
4513
4514
|
avg_efficiency: item.avg_efficiency || 0,
|
|
4514
4515
|
total_output: item.total_output || 0,
|
|
4515
4516
|
avg_cycle_time: item.avg_cycle_time || 0,
|
|
4517
|
+
ideal_cycle_time: item.ideal_cycle_time ?? 0,
|
|
4516
4518
|
ideal_output: item.ideal_output || 0,
|
|
4517
4519
|
total_day_output: item.total_day_output ?? item.ideal_output ?? 0,
|
|
4518
4520
|
avg_pph: item.avg_pph || 0,
|
|
@@ -17309,7 +17311,7 @@ var useActiveBreaks = (lineIds) => {
|
|
|
17309
17311
|
const [isLoading, setIsLoading] = React143.useState(true);
|
|
17310
17312
|
const [error, setError] = React143.useState(null);
|
|
17311
17313
|
const supabase = useSupabase();
|
|
17312
|
-
const
|
|
17314
|
+
const parseTimeToMinutes5 = (timeStr) => {
|
|
17313
17315
|
const [hours, minutes] = timeStr.split(":").map(Number);
|
|
17314
17316
|
return hours * 60 + minutes;
|
|
17315
17317
|
};
|
|
@@ -17318,8 +17320,8 @@ var useActiveBreaks = (lineIds) => {
|
|
|
17318
17320
|
return now4.getHours() * 60 + now4.getMinutes();
|
|
17319
17321
|
};
|
|
17320
17322
|
const isTimeInBreak = (breakStart, breakEnd, currentMinutes) => {
|
|
17321
|
-
const startMinutes =
|
|
17322
|
-
const endMinutes =
|
|
17323
|
+
const startMinutes = parseTimeToMinutes5(breakStart);
|
|
17324
|
+
const endMinutes = parseTimeToMinutes5(breakEnd);
|
|
17323
17325
|
if (endMinutes < startMinutes) {
|
|
17324
17326
|
return currentMinutes >= startMinutes || currentMinutes < endMinutes;
|
|
17325
17327
|
} else {
|
|
@@ -17327,8 +17329,8 @@ var useActiveBreaks = (lineIds) => {
|
|
|
17327
17329
|
}
|
|
17328
17330
|
};
|
|
17329
17331
|
const calculateBreakProgress = (breakStart, breakEnd, currentMinutes) => {
|
|
17330
|
-
const startMinutes =
|
|
17331
|
-
const endMinutes =
|
|
17332
|
+
const startMinutes = parseTimeToMinutes5(breakStart);
|
|
17333
|
+
const endMinutes = parseTimeToMinutes5(breakEnd);
|
|
17332
17334
|
let elapsedMinutes = 0;
|
|
17333
17335
|
let remainingMinutes = 0;
|
|
17334
17336
|
if (endMinutes < startMinutes) {
|
|
@@ -17346,8 +17348,8 @@ var useActiveBreaks = (lineIds) => {
|
|
|
17346
17348
|
return { elapsedMinutes, remainingMinutes };
|
|
17347
17349
|
};
|
|
17348
17350
|
const isTimeInShift = (startTime, endTime, currentMinutes) => {
|
|
17349
|
-
const startMinutes =
|
|
17350
|
-
const endMinutes =
|
|
17351
|
+
const startMinutes = parseTimeToMinutes5(startTime);
|
|
17352
|
+
const endMinutes = parseTimeToMinutes5(endTime);
|
|
17351
17353
|
if (endMinutes < startMinutes) {
|
|
17352
17354
|
return currentMinutes >= startMinutes || currentMinutes < endMinutes;
|
|
17353
17355
|
} else {
|
|
@@ -17407,8 +17409,8 @@ var useActiveBreaks = (lineIds) => {
|
|
|
17407
17409
|
const endTime = breakItem.end || breakItem.endTime || "00:00";
|
|
17408
17410
|
let duration = breakItem.duration || 0;
|
|
17409
17411
|
if (!duration || duration === 0) {
|
|
17410
|
-
const startMinutes =
|
|
17411
|
-
const endMinutes =
|
|
17412
|
+
const startMinutes = parseTimeToMinutes5(startTime);
|
|
17413
|
+
const endMinutes = parseTimeToMinutes5(endTime);
|
|
17412
17414
|
duration = endMinutes < startMinutes ? endMinutes + 24 * 60 - startMinutes : endMinutes - startMinutes;
|
|
17413
17415
|
}
|
|
17414
17416
|
return {
|
|
@@ -17424,8 +17426,8 @@ var useActiveBreaks = (lineIds) => {
|
|
|
17424
17426
|
const endTime = breakItem.end || breakItem.endTime || "00:00";
|
|
17425
17427
|
let duration = breakItem.duration || 0;
|
|
17426
17428
|
if (!duration || duration === 0) {
|
|
17427
|
-
const startMinutes =
|
|
17428
|
-
const endMinutes =
|
|
17429
|
+
const startMinutes = parseTimeToMinutes5(startTime);
|
|
17430
|
+
const endMinutes = parseTimeToMinutes5(endTime);
|
|
17429
17431
|
duration = endMinutes < startMinutes ? endMinutes + 24 * 60 - startMinutes : endMinutes - startMinutes;
|
|
17430
17432
|
}
|
|
17431
17433
|
return {
|
|
@@ -33440,13 +33442,13 @@ var CycleTimeOverTimeChart = ({
|
|
|
33440
33442
|
observer.observe(containerRef.current);
|
|
33441
33443
|
return () => observer.disconnect();
|
|
33442
33444
|
}, []);
|
|
33443
|
-
const
|
|
33445
|
+
const parseTimeToMinutes5 = (value) => {
|
|
33444
33446
|
const [hours, minutes] = value.split(":").map(Number);
|
|
33445
33447
|
if (!Number.isFinite(hours) || !Number.isFinite(minutes)) return 0;
|
|
33446
33448
|
return hours * 60 + minutes;
|
|
33447
33449
|
};
|
|
33448
33450
|
const formatHourLabel = (slotIndex) => {
|
|
33449
|
-
const baseMinutes =
|
|
33451
|
+
const baseMinutes = parseTimeToMinutes5(shiftStart);
|
|
33450
33452
|
const absoluteMinutes = baseMinutes + slotIndex * 60;
|
|
33451
33453
|
const hour24 = Math.floor(absoluteMinutes % (24 * 60) / 60);
|
|
33452
33454
|
const ampm = hour24 >= 12 ? "PM" : "AM";
|
|
@@ -47903,6 +47905,7 @@ var LineMonthlyPdfGenerator = ({
|
|
|
47903
47905
|
rangeEnd,
|
|
47904
47906
|
selectedShiftId,
|
|
47905
47907
|
availableShifts,
|
|
47908
|
+
lineAssembly = false,
|
|
47906
47909
|
compact = false,
|
|
47907
47910
|
className
|
|
47908
47911
|
}) => {
|
|
@@ -48186,8 +48189,8 @@ var LineMonthlyPdfGenerator = ({
|
|
|
48186
48189
|
timeZone: "Asia/Kolkata"
|
|
48187
48190
|
});
|
|
48188
48191
|
doc.text(dateStr, 25, yPos);
|
|
48189
|
-
doc.text(`${shift.
|
|
48190
|
-
doc.text(`${shift.
|
|
48192
|
+
doc.text(`${Math.round(shift.output || 0)}`, 60, yPos);
|
|
48193
|
+
doc.text(`${Math.round(shift.idealOutput || 0)}`, 95, yPos);
|
|
48191
48194
|
doc.text(`${shift.avg_efficiency.toFixed(1)}%`, 135, yPos);
|
|
48192
48195
|
const statusColor = getEfficiencyColor(shift.avg_efficiency, effectiveLegend);
|
|
48193
48196
|
if (statusColor === "green") {
|
|
@@ -48217,6 +48220,8 @@ var LineMonthlyPdfGenerator = ({
|
|
|
48217
48220
|
doc.setTextColor(0, 0, 0);
|
|
48218
48221
|
}
|
|
48219
48222
|
const poorestWorkspaces = underperformingWorkspaces[selectedShiftId] || [];
|
|
48223
|
+
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);
|
|
48224
|
+
const showCycleTimePoorestPerformers = !isUptimeMode && poorestWorkspaces.some(isCycleTimeWorkspace);
|
|
48220
48225
|
if (poorestWorkspaces && poorestWorkspaces.length > 0) {
|
|
48221
48226
|
doc.addPage();
|
|
48222
48227
|
doc.setFontSize(14);
|
|
@@ -48242,7 +48247,11 @@ var LineMonthlyPdfGenerator = ({
|
|
|
48242
48247
|
doc.setFillColor(245, 245, 245);
|
|
48243
48248
|
doc.roundedRect(20, 45, 170, 8, 1, 1, "F");
|
|
48244
48249
|
doc.text("Workspace", 25, 50);
|
|
48245
|
-
doc.text(
|
|
48250
|
+
doc.text(
|
|
48251
|
+
isUptimeMode ? "Avg Utilization" : showCycleTimePoorestPerformers ? "Cycle Time" : "Avg Efficiency",
|
|
48252
|
+
120,
|
|
48253
|
+
50
|
|
48254
|
+
);
|
|
48246
48255
|
doc.text("Last 5 Days", 160, 50);
|
|
48247
48256
|
doc.setLineWidth(0.2);
|
|
48248
48257
|
doc.setDrawColor(220, 220, 220);
|
|
@@ -48261,7 +48270,16 @@ var LineMonthlyPdfGenerator = ({
|
|
|
48261
48270
|
);
|
|
48262
48271
|
const workspaceName = rawWorkspaceName.length > 30 ? `${rawWorkspaceName.substring(0, 27)}...` : rawWorkspaceName;
|
|
48263
48272
|
doc.text(workspaceName, 25, yPos2);
|
|
48264
|
-
|
|
48273
|
+
if (isUptimeMode) {
|
|
48274
|
+
doc.text(`${(workspace.avg_efficiency || 0).toFixed(1)}%`, 120, yPos2);
|
|
48275
|
+
} else if (isCycleTimeWorkspace(workspace)) {
|
|
48276
|
+
const actualCycleTime = Number.isFinite(workspace.avg_cycle_time) ? Number(workspace.avg_cycle_time) : null;
|
|
48277
|
+
const targetCycleTime = Number.isFinite(workspace.ideal_cycle_time) ? Number(workspace.ideal_cycle_time) : null;
|
|
48278
|
+
const cycleTimeText = actualCycleTime !== null ? `${actualCycleTime.toFixed(1)}s${targetCycleTime !== null ? ` / ${targetCycleTime.toFixed(1)}s` : ""}` : "-";
|
|
48279
|
+
doc.text(cycleTimeText, 120, yPos2);
|
|
48280
|
+
} else {
|
|
48281
|
+
doc.text(`${(workspace.avg_efficiency || 0).toFixed(1)}%`, 120, yPos2);
|
|
48282
|
+
}
|
|
48265
48283
|
const squareSize = 3;
|
|
48266
48284
|
const squareSpacing = 1;
|
|
48267
48285
|
let squareX = 160;
|
|
@@ -48356,12 +48374,279 @@ Underperforming Workspaces: ${lineInfo.metrics.underperforming_workspaces} / ${l
|
|
|
48356
48374
|
}
|
|
48357
48375
|
);
|
|
48358
48376
|
};
|
|
48377
|
+
|
|
48378
|
+
// src/lib/utils/hourlyTargets.ts
|
|
48379
|
+
var stripSeconds2 = (timeStr) => timeStr ? timeStr.slice(0, 5) : timeStr;
|
|
48380
|
+
var MINUTES_PER_DAY = 24 * 60;
|
|
48381
|
+
var parseTimeToMinutes2 = (timeString) => {
|
|
48382
|
+
const normalized = stripSeconds2(timeString || "");
|
|
48383
|
+
if (!normalized || !/^[0-2]\d:[0-5]\d$/.test(normalized)) return Number.NaN;
|
|
48384
|
+
const [hours, minutes] = normalized.split(":").map(Number);
|
|
48385
|
+
return hours * 60 + minutes;
|
|
48386
|
+
};
|
|
48387
|
+
var normalizeBreaksOnShiftTimeline = (shiftStart, breaks) => {
|
|
48388
|
+
const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
|
|
48389
|
+
if (!Number.isFinite(shiftStartMinutes)) return [];
|
|
48390
|
+
const normalizedBreaks = [];
|
|
48391
|
+
for (const entry of breaks) {
|
|
48392
|
+
const startRaw = parseTimeToMinutes2(entry.startTime);
|
|
48393
|
+
const endRaw = parseTimeToMinutes2(entry.endTime);
|
|
48394
|
+
if (!Number.isFinite(startRaw) || !Number.isFinite(endRaw)) continue;
|
|
48395
|
+
let start = startRaw;
|
|
48396
|
+
let end = endRaw;
|
|
48397
|
+
if (end <= start) {
|
|
48398
|
+
end += 24 * 60;
|
|
48399
|
+
}
|
|
48400
|
+
if (start < shiftStartMinutes) {
|
|
48401
|
+
start += 24 * 60;
|
|
48402
|
+
end += 24 * 60;
|
|
48403
|
+
}
|
|
48404
|
+
const label = entry.remarks?.trim() || "Break";
|
|
48405
|
+
normalizedBreaks.push({ start, end, label });
|
|
48406
|
+
}
|
|
48407
|
+
return normalizedBreaks;
|
|
48408
|
+
};
|
|
48409
|
+
var roundTarget = (value, mode) => {
|
|
48410
|
+
if (!Number.isFinite(value)) return 0;
|
|
48411
|
+
switch (mode) {
|
|
48412
|
+
case "floor":
|
|
48413
|
+
return Math.floor(value);
|
|
48414
|
+
case "ceil":
|
|
48415
|
+
return Math.ceil(value);
|
|
48416
|
+
case "round":
|
|
48417
|
+
default:
|
|
48418
|
+
return Math.round(value);
|
|
48419
|
+
}
|
|
48420
|
+
};
|
|
48421
|
+
var formatDateKey = (date) => {
|
|
48422
|
+
const year = date.getUTCFullYear();
|
|
48423
|
+
const month = `${date.getUTCMonth() + 1}`.padStart(2, "0");
|
|
48424
|
+
const day = `${date.getUTCDate()}`.padStart(2, "0");
|
|
48425
|
+
return `${year}-${month}-${day}`;
|
|
48426
|
+
};
|
|
48427
|
+
var shiftDateKey = (dateKey, deltaDays) => {
|
|
48428
|
+
const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
|
|
48429
|
+
const year = Number.isFinite(yearPart) ? yearPart : 1970;
|
|
48430
|
+
const month = Number.isFinite(monthPart) ? monthPart : 1;
|
|
48431
|
+
const day = Number.isFinite(dayPart) ? dayPart : 1;
|
|
48432
|
+
const date = new Date(Date.UTC(year, month - 1, day));
|
|
48433
|
+
date.setUTCDate(date.getUTCDate() + deltaDays);
|
|
48434
|
+
return formatDateKey(date);
|
|
48435
|
+
};
|
|
48436
|
+
var getZonedNowSnapshot = (timeZone, now4) => {
|
|
48437
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
48438
|
+
timeZone,
|
|
48439
|
+
year: "numeric",
|
|
48440
|
+
month: "2-digit",
|
|
48441
|
+
day: "2-digit",
|
|
48442
|
+
hour: "2-digit",
|
|
48443
|
+
minute: "2-digit",
|
|
48444
|
+
hourCycle: "h23"
|
|
48445
|
+
});
|
|
48446
|
+
const parts = formatter.formatToParts(now4).reduce((acc, part) => {
|
|
48447
|
+
if (part.type !== "literal") {
|
|
48448
|
+
acc[part.type] = part.value;
|
|
48449
|
+
}
|
|
48450
|
+
return acc;
|
|
48451
|
+
}, {});
|
|
48452
|
+
const year = Number(parts.year);
|
|
48453
|
+
const month = Number(parts.month);
|
|
48454
|
+
const day = Number(parts.day);
|
|
48455
|
+
const hour = Number(parts.hour);
|
|
48456
|
+
const minute = Number(parts.minute);
|
|
48457
|
+
return {
|
|
48458
|
+
dateKey: `${String(year).padStart(4, "0")}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`,
|
|
48459
|
+
minutesOfDay: (Number.isFinite(hour) ? hour : 0) * 60 + (Number.isFinite(minute) ? minute : 0)
|
|
48460
|
+
};
|
|
48461
|
+
};
|
|
48462
|
+
var getDateKeyInTimeZone = (timeZone, now4 = /* @__PURE__ */ new Date()) => getZonedNowSnapshot(timeZone, now4).dateKey;
|
|
48463
|
+
var buildHourlyIntervals = ({
|
|
48464
|
+
shiftStart,
|
|
48465
|
+
shiftEnd,
|
|
48466
|
+
bucketMinutes = 60,
|
|
48467
|
+
fallbackHours = 11
|
|
48468
|
+
}) => {
|
|
48469
|
+
const startMinutes = parseTimeToMinutes2(shiftStart);
|
|
48470
|
+
if (!Number.isFinite(startMinutes)) return [];
|
|
48471
|
+
const bucket = Number.isFinite(bucketMinutes) && bucketMinutes > 0 ? Math.floor(bucketMinutes) : 60;
|
|
48472
|
+
let totalMinutes;
|
|
48473
|
+
const endRaw = shiftEnd ? parseTimeToMinutes2(shiftEnd) : Number.NaN;
|
|
48474
|
+
if (!Number.isFinite(endRaw)) {
|
|
48475
|
+
totalMinutes = Math.max(0, Math.round(fallbackHours * 60));
|
|
48476
|
+
} else {
|
|
48477
|
+
let endMinutes = endRaw;
|
|
48478
|
+
if (endMinutes <= startMinutes) {
|
|
48479
|
+
endMinutes += 24 * 60;
|
|
48480
|
+
}
|
|
48481
|
+
totalMinutes = endMinutes - startMinutes;
|
|
48482
|
+
}
|
|
48483
|
+
if (!Number.isFinite(totalMinutes) || totalMinutes <= 0) return [];
|
|
48484
|
+
const count = Math.ceil(totalMinutes / bucket);
|
|
48485
|
+
const shiftEndMinutes = startMinutes + totalMinutes;
|
|
48486
|
+
const intervals = [];
|
|
48487
|
+
for (let i = 0; i < count; i += 1) {
|
|
48488
|
+
const start = startMinutes + i * bucket;
|
|
48489
|
+
const end = Math.min(start + bucket, shiftEndMinutes);
|
|
48490
|
+
const minutes = Math.max(0, end - start);
|
|
48491
|
+
if (minutes <= 0) continue;
|
|
48492
|
+
intervals.push({ start, end, minutes });
|
|
48493
|
+
}
|
|
48494
|
+
return intervals;
|
|
48495
|
+
};
|
|
48496
|
+
var computeBreakMinutesByInterval = ({
|
|
48497
|
+
intervals,
|
|
48498
|
+
shiftStart,
|
|
48499
|
+
breaks
|
|
48500
|
+
}) => {
|
|
48501
|
+
if (!intervals.length || !breaks.length) return intervals.map(() => 0);
|
|
48502
|
+
const normalizedBreaks = normalizeBreaksOnShiftTimeline(shiftStart, breaks);
|
|
48503
|
+
return intervals.map((interval) => {
|
|
48504
|
+
if (!normalizedBreaks.length) return 0;
|
|
48505
|
+
let total = 0;
|
|
48506
|
+
for (const brk of normalizedBreaks) {
|
|
48507
|
+
const overlap = Math.max(0, Math.min(interval.end, brk.end) - Math.max(interval.start, brk.start));
|
|
48508
|
+
total += overlap;
|
|
48509
|
+
if (total >= interval.minutes) return interval.minutes;
|
|
48510
|
+
}
|
|
48511
|
+
return Math.min(interval.minutes, total);
|
|
48512
|
+
});
|
|
48513
|
+
};
|
|
48514
|
+
var computeBreakRemarksByInterval = ({
|
|
48515
|
+
intervals,
|
|
48516
|
+
shiftStart,
|
|
48517
|
+
breaks
|
|
48518
|
+
}) => {
|
|
48519
|
+
if (!intervals.length || !breaks.length) return intervals.map(() => "");
|
|
48520
|
+
const normalizedBreaks = normalizeBreaksOnShiftTimeline(shiftStart, breaks);
|
|
48521
|
+
return intervals.map((interval) => {
|
|
48522
|
+
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);
|
|
48523
|
+
return labels.join(", ");
|
|
48524
|
+
});
|
|
48525
|
+
};
|
|
48526
|
+
var computeEffectiveTargets = ({
|
|
48527
|
+
intervals,
|
|
48528
|
+
breakMinutes,
|
|
48529
|
+
pphThreshold,
|
|
48530
|
+
rounding = "round"
|
|
48531
|
+
}) => {
|
|
48532
|
+
return intervals.map((interval, idx) => {
|
|
48533
|
+
const intervalMinutes = Number(interval?.minutes) || 0;
|
|
48534
|
+
const breakMins = Number(breakMinutes?.[idx]) || 0;
|
|
48535
|
+
const plannedWorkMinutes = Math.max(0, intervalMinutes - breakMins);
|
|
48536
|
+
if (!Number.isFinite(pphThreshold) || pphThreshold <= 0) return 0;
|
|
48537
|
+
if (plannedWorkMinutes <= 0) return 0;
|
|
48538
|
+
return roundTarget(pphThreshold * plannedWorkMinutes / 60, rounding);
|
|
48539
|
+
});
|
|
48540
|
+
};
|
|
48541
|
+
var buildHourlyTargetPlan = ({
|
|
48542
|
+
shiftStart,
|
|
48543
|
+
shiftEnd,
|
|
48544
|
+
breaks = [],
|
|
48545
|
+
pphThreshold,
|
|
48546
|
+
bucketMinutes = 60,
|
|
48547
|
+
fallbackHours = 11,
|
|
48548
|
+
rounding = "round"
|
|
48549
|
+
}) => {
|
|
48550
|
+
const intervals = buildHourlyIntervals({
|
|
48551
|
+
shiftStart,
|
|
48552
|
+
shiftEnd,
|
|
48553
|
+
bucketMinutes,
|
|
48554
|
+
fallbackHours
|
|
48555
|
+
});
|
|
48556
|
+
const breakMinutes = computeBreakMinutesByInterval({
|
|
48557
|
+
intervals,
|
|
48558
|
+
shiftStart,
|
|
48559
|
+
breaks
|
|
48560
|
+
});
|
|
48561
|
+
const breakRemarks = computeBreakRemarksByInterval({
|
|
48562
|
+
intervals,
|
|
48563
|
+
shiftStart,
|
|
48564
|
+
breaks
|
|
48565
|
+
});
|
|
48566
|
+
const productiveMinutes = intervals.map((interval, idx) => Math.max(0, (Number(interval?.minutes) || 0) - (Number(breakMinutes[idx]) || 0)));
|
|
48567
|
+
const targets = computeEffectiveTargets({
|
|
48568
|
+
intervals,
|
|
48569
|
+
breakMinutes,
|
|
48570
|
+
pphThreshold,
|
|
48571
|
+
rounding
|
|
48572
|
+
});
|
|
48573
|
+
return {
|
|
48574
|
+
intervals,
|
|
48575
|
+
breakMinutes,
|
|
48576
|
+
breakRemarks,
|
|
48577
|
+
productiveMinutes,
|
|
48578
|
+
targets
|
|
48579
|
+
};
|
|
48580
|
+
};
|
|
48581
|
+
var isHourlyIntervalComplete = ({
|
|
48582
|
+
reportDate,
|
|
48583
|
+
shiftStart,
|
|
48584
|
+
shiftEnd,
|
|
48585
|
+
interval,
|
|
48586
|
+
timeZone = "Asia/Kolkata",
|
|
48587
|
+
now: now4 = /* @__PURE__ */ new Date()
|
|
48588
|
+
}) => {
|
|
48589
|
+
if (!reportDate) return true;
|
|
48590
|
+
const snapshot = getZonedNowSnapshot(timeZone, now4);
|
|
48591
|
+
const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
|
|
48592
|
+
const shiftEndMinutes = shiftEnd ? parseTimeToMinutes2(shiftEnd) : Number.NaN;
|
|
48593
|
+
const wrapsMidnight = Number.isFinite(shiftStartMinutes) && Number.isFinite(shiftEndMinutes) && shiftEndMinutes <= shiftStartMinutes;
|
|
48594
|
+
if (reportDate === snapshot.dateKey) {
|
|
48595
|
+
return interval.end <= snapshot.minutesOfDay;
|
|
48596
|
+
}
|
|
48597
|
+
if (wrapsMidnight && reportDate === shiftDateKey(snapshot.dateKey, -1)) {
|
|
48598
|
+
return interval.end <= snapshot.minutesOfDay + MINUTES_PER_DAY;
|
|
48599
|
+
}
|
|
48600
|
+
return reportDate < snapshot.dateKey;
|
|
48601
|
+
};
|
|
48602
|
+
var isShiftInProgressForReportDate = ({
|
|
48603
|
+
reportDate,
|
|
48604
|
+
shiftStart,
|
|
48605
|
+
shiftEnd,
|
|
48606
|
+
timeZone = "Asia/Kolkata",
|
|
48607
|
+
now: now4 = /* @__PURE__ */ new Date()
|
|
48608
|
+
}) => {
|
|
48609
|
+
if (!reportDate || !shiftStart || !shiftEnd) return false;
|
|
48610
|
+
const shiftStartMinutes = parseTimeToMinutes2(shiftStart);
|
|
48611
|
+
const shiftEndMinutesRaw = parseTimeToMinutes2(shiftEnd);
|
|
48612
|
+
if (!Number.isFinite(shiftStartMinutes) || !Number.isFinite(shiftEndMinutesRaw)) {
|
|
48613
|
+
return false;
|
|
48614
|
+
}
|
|
48615
|
+
let shiftEndMinutes = shiftEndMinutesRaw;
|
|
48616
|
+
const wrapsMidnight = shiftEndMinutes <= shiftStartMinutes;
|
|
48617
|
+
if (wrapsMidnight) {
|
|
48618
|
+
shiftEndMinutes += MINUTES_PER_DAY;
|
|
48619
|
+
}
|
|
48620
|
+
const snapshot = getZonedNowSnapshot(timeZone, now4);
|
|
48621
|
+
let currentMinutes = null;
|
|
48622
|
+
if (reportDate === snapshot.dateKey) {
|
|
48623
|
+
currentMinutes = snapshot.minutesOfDay;
|
|
48624
|
+
} else if (wrapsMidnight && reportDate === shiftDateKey(snapshot.dateKey, -1)) {
|
|
48625
|
+
currentMinutes = snapshot.minutesOfDay + MINUTES_PER_DAY;
|
|
48626
|
+
}
|
|
48627
|
+
if (currentMinutes === null) {
|
|
48628
|
+
return false;
|
|
48629
|
+
}
|
|
48630
|
+
return shiftStartMinutes <= currentMinutes && currentMinutes < shiftEndMinutes;
|
|
48631
|
+
};
|
|
48632
|
+
var formatOperationalDateKey = (dateKey, options) => {
|
|
48633
|
+
const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
|
|
48634
|
+
const year = Number.isFinite(yearPart) ? yearPart : 1970;
|
|
48635
|
+
const month = Number.isFinite(monthPart) ? monthPart : 1;
|
|
48636
|
+
const day = Number.isFinite(dayPart) ? dayPart : 1;
|
|
48637
|
+
return new Date(Date.UTC(year, month - 1, day, 12)).toLocaleDateString("en-IN", {
|
|
48638
|
+
...options,
|
|
48639
|
+
timeZone: "UTC"
|
|
48640
|
+
});
|
|
48641
|
+
};
|
|
48359
48642
|
var LinePdfGenerator = ({
|
|
48360
48643
|
lineInfo,
|
|
48361
48644
|
workspaceData,
|
|
48362
48645
|
issueResolutionSummary,
|
|
48363
48646
|
shiftName,
|
|
48364
|
-
className
|
|
48647
|
+
className,
|
|
48648
|
+
shiftBreaks = [],
|
|
48649
|
+
reportTimezone = "Asia/Kolkata"
|
|
48365
48650
|
}) => {
|
|
48366
48651
|
const [isGenerating, setIsGenerating] = React143.useState(false);
|
|
48367
48652
|
const formatResolutionDuration2 = (seconds) => {
|
|
@@ -48388,7 +48673,7 @@ var LinePdfGenerator = ({
|
|
|
48388
48673
|
doc.setFontSize(9);
|
|
48389
48674
|
doc.setFont("helvetica", "normal");
|
|
48390
48675
|
doc.setTextColor(100, 100, 100);
|
|
48391
|
-
const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone:
|
|
48676
|
+
const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: reportTimezone })}`;
|
|
48392
48677
|
const generatedTextWidth = doc.getStringUnitWidth(generatedText) * 9 / doc.internal.scaleFactor;
|
|
48393
48678
|
doc.text(generatedText, doc.internal.pageSize.width - 20 - generatedTextWidth, 15);
|
|
48394
48679
|
doc.setDrawColor(200, 200, 200);
|
|
@@ -48406,11 +48691,10 @@ var LinePdfGenerator = ({
|
|
|
48406
48691
|
const isUptimeMode = lineInfo.monitoring_mode === "uptime";
|
|
48407
48692
|
const rawShiftType = shiftName || (lineInfo.shift_id === 0 ? "Day" : lineInfo.shift_id === 1 ? "Night" : `Shift ${lineInfo.shift_id}`);
|
|
48408
48693
|
const shiftType = rawShiftType.toLowerCase().includes("shift") ? rawShiftType : `${rawShiftType} Shift`;
|
|
48409
|
-
const date =
|
|
48694
|
+
const date = formatOperationalDateKey(lineInfo.date, {
|
|
48410
48695
|
weekday: "long",
|
|
48411
48696
|
day: "numeric",
|
|
48412
|
-
month: "long"
|
|
48413
|
-
timeZone: "Asia/Kolkata"
|
|
48697
|
+
month: "long"
|
|
48414
48698
|
});
|
|
48415
48699
|
const shiftStartTime = lineInfo.metrics.shift_start ? (/* @__PURE__ */ new Date(`2000-01-01 ${lineInfo.metrics.shift_start}`)).toLocaleTimeString("en-IN", {
|
|
48416
48700
|
hour: "2-digit",
|
|
@@ -48418,24 +48702,25 @@ var LinePdfGenerator = ({
|
|
|
48418
48702
|
hour12: true,
|
|
48419
48703
|
timeZone: "Asia/Kolkata"
|
|
48420
48704
|
}) : "N/A";
|
|
48421
|
-
const
|
|
48422
|
-
|
|
48423
|
-
|
|
48424
|
-
|
|
48425
|
-
|
|
48426
|
-
|
|
48427
|
-
|
|
48428
|
-
|
|
48429
|
-
|
|
48430
|
-
|
|
48431
|
-
|
|
48432
|
-
|
|
48433
|
-
|
|
48434
|
-
|
|
48435
|
-
|
|
48436
|
-
}
|
|
48705
|
+
const currentTime = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-IN", {
|
|
48706
|
+
hour: "2-digit",
|
|
48707
|
+
minute: "2-digit",
|
|
48708
|
+
timeZone: reportTimezone
|
|
48709
|
+
});
|
|
48710
|
+
const reportEndTime = isShiftInProgressForReportDate({
|
|
48711
|
+
reportDate: lineInfo.date,
|
|
48712
|
+
shiftStart: lineInfo.metrics.shift_start || "",
|
|
48713
|
+
shiftEnd: lineInfo.metrics.shift_end,
|
|
48714
|
+
timeZone: reportTimezone
|
|
48715
|
+
}) ? currentTime : lineInfo.metrics.shift_end ? (/* @__PURE__ */ new Date(`2000-01-01 ${lineInfo.metrics.shift_end}`)).toLocaleTimeString("en-IN", {
|
|
48716
|
+
hour: "2-digit",
|
|
48717
|
+
minute: "2-digit",
|
|
48718
|
+
hour12: true,
|
|
48719
|
+
timeZone: "Asia/Kolkata"
|
|
48720
|
+
}) : "N/A";
|
|
48437
48721
|
if (isUptimeMode) {
|
|
48438
48722
|
const configuredTimezone = "Asia/Kolkata";
|
|
48723
|
+
const effectiveUptimeTimezone = reportTimezone || configuredTimezone;
|
|
48439
48724
|
const lineShiftStart = lineInfo.metrics.shift_start || "06:00";
|
|
48440
48725
|
const lineShiftEnd = lineInfo.metrics.shift_end || "14:00";
|
|
48441
48726
|
const shiftMinutes = getShiftDurationMinutes(lineShiftStart, lineShiftEnd) || 0;
|
|
@@ -48449,7 +48734,7 @@ var LinePdfGenerator = ({
|
|
|
48449
48734
|
shiftStart,
|
|
48450
48735
|
shiftEnd,
|
|
48451
48736
|
shiftDate,
|
|
48452
|
-
timezone:
|
|
48737
|
+
timezone: effectiveUptimeTimezone
|
|
48453
48738
|
});
|
|
48454
48739
|
let activeMinutes = uptimeSeries.activeMinutes;
|
|
48455
48740
|
let idleMinutes = uptimeSeries.idleMinutes;
|
|
@@ -48530,7 +48815,7 @@ var LinePdfGenerator = ({
|
|
|
48530
48815
|
shiftStart: lineShiftStart,
|
|
48531
48816
|
shiftEnd: lineShiftEnd,
|
|
48532
48817
|
shiftDate: lineInfo.date,
|
|
48533
|
-
timezone:
|
|
48818
|
+
timezone: effectiveUptimeTimezone
|
|
48534
48819
|
});
|
|
48535
48820
|
hourlyData = buildHourlyFromSeries(lineUptimeSeries);
|
|
48536
48821
|
}
|
|
@@ -48638,13 +48923,13 @@ var LinePdfGenerator = ({
|
|
|
48638
48923
|
doc.setFontSize(contentFontSize);
|
|
48639
48924
|
doc.setFont("helvetica", "normal");
|
|
48640
48925
|
let yPos2 = headerBottomY2 + 5.5;
|
|
48641
|
-
const
|
|
48642
|
-
const
|
|
48643
|
-
const [
|
|
48644
|
-
const [
|
|
48645
|
-
const
|
|
48926
|
+
const now4 = /* @__PURE__ */ new Date();
|
|
48927
|
+
const currentTimeIST = new Date(now4.toLocaleString("en-US", { timeZone: configuredTimezone }));
|
|
48928
|
+
const [sYear, sMonth, sDay] = lineInfo.date.split("-").map(Number);
|
|
48929
|
+
const [sStartH, sStartM] = (lineShiftStart || "06:00").split(":").map(Number);
|
|
48930
|
+
const shiftStartBase = new Date(sYear, sMonth - 1, sDay, sStartH, sStartM || 0);
|
|
48646
48931
|
hourlyData.forEach((entry, index) => {
|
|
48647
|
-
const bucketStartTime = new Date(
|
|
48932
|
+
const bucketStartTime = new Date(shiftStartBase);
|
|
48648
48933
|
bucketStartTime.setHours(bucketStartTime.getHours() + index);
|
|
48649
48934
|
const bucketEndTime = new Date(bucketStartTime);
|
|
48650
48935
|
bucketEndTime.setHours(bucketEndTime.getHours() + 1);
|
|
@@ -48655,7 +48940,7 @@ var LinePdfGenerator = ({
|
|
|
48655
48940
|
hour: "numeric",
|
|
48656
48941
|
hour12: true
|
|
48657
48942
|
})}`;
|
|
48658
|
-
const dataCollected = bucketEndTime.getTime() <=
|
|
48943
|
+
const dataCollected = bucketEndTime.getTime() <= currentTimeIST.getTime();
|
|
48659
48944
|
if (index < totalRows2 - 1) {
|
|
48660
48945
|
const rowBottomY = headerBottomY2 + (index + 1) * rowHeight;
|
|
48661
48946
|
doc.setDrawColor(200, 200, 200);
|
|
@@ -48666,11 +48951,10 @@ var LinePdfGenerator = ({
|
|
|
48666
48951
|
doc.text(utilizationStr, 147, yPos2);
|
|
48667
48952
|
yPos2 += rowHeight;
|
|
48668
48953
|
});
|
|
48669
|
-
const fileDate2 =
|
|
48954
|
+
const fileDate2 = formatOperationalDateKey(lineInfo.date, {
|
|
48670
48955
|
day: "2-digit",
|
|
48671
48956
|
month: "short",
|
|
48672
|
-
year: "numeric"
|
|
48673
|
-
timeZone: "Asia/Kolkata"
|
|
48957
|
+
year: "numeric"
|
|
48674
48958
|
}).replace(/ /g, "_");
|
|
48675
48959
|
const fileShift2 = shiftType.replace(/ /g, "_");
|
|
48676
48960
|
const fileName2 = `${lineInfo.line_name}_${fileDate2}_${fileShift2}.pdf`;
|
|
@@ -48723,66 +49007,32 @@ var LinePdfGenerator = ({
|
|
|
48723
49007
|
doc.setLineWidth(0.8);
|
|
48724
49008
|
doc.line(20, 123, 190, 123);
|
|
48725
49009
|
const hourlyOverviewStartY = 128;
|
|
48726
|
-
const parseTimeToMinutes4 = (timeStr) => {
|
|
48727
|
-
const [hours, minutes] = timeStr.split(":");
|
|
48728
|
-
const hour = parseInt(hours, 10);
|
|
48729
|
-
const minute = parseInt(minutes || "0", 10);
|
|
48730
|
-
if (Number.isNaN(hour) || Number.isNaN(minute)) {
|
|
48731
|
-
return NaN;
|
|
48732
|
-
}
|
|
48733
|
-
return (hour * 60 + minute) % (24 * 60);
|
|
48734
|
-
};
|
|
48735
49010
|
const formatMinutesLabel = (totalMinutes) => {
|
|
48736
49011
|
const normalized = (totalMinutes % (24 * 60) + 24 * 60) % (24 * 60);
|
|
48737
49012
|
const hour = Math.floor(normalized / 60);
|
|
48738
49013
|
const minute = normalized % 60;
|
|
48739
|
-
const time2 =
|
|
48740
|
-
time2.setHours(hour);
|
|
48741
|
-
time2.setMinutes(minute);
|
|
48742
|
-
time2.setSeconds(0);
|
|
48743
|
-
time2.setMilliseconds(0);
|
|
49014
|
+
const time2 = new Date(Date.UTC(2e3, 0, 1, hour, minute));
|
|
48744
49015
|
return time2.toLocaleTimeString("en-IN", {
|
|
48745
49016
|
hour: "2-digit",
|
|
48746
49017
|
minute: "2-digit",
|
|
48747
49018
|
hour12: true,
|
|
48748
|
-
timeZone: "
|
|
48749
|
-
});
|
|
48750
|
-
};
|
|
48751
|
-
const buildRange = (startMinutes, minutes) => {
|
|
48752
|
-
const endMinutes = startMinutes + minutes;
|
|
48753
|
-
return {
|
|
48754
|
-
label: `${formatMinutesLabel(startMinutes)} - ${formatMinutesLabel(endMinutes)}`,
|
|
48755
|
-
minutes
|
|
48756
|
-
};
|
|
48757
|
-
};
|
|
48758
|
-
const getHourlyTimeRanges = (startTimeStr, endTimeStr) => {
|
|
48759
|
-
const startMinutes = parseTimeToMinutes4(startTimeStr);
|
|
48760
|
-
if (Number.isNaN(startMinutes)) {
|
|
48761
|
-
return [];
|
|
48762
|
-
}
|
|
48763
|
-
if (!endTimeStr) {
|
|
48764
|
-
const defaultHours = 11;
|
|
48765
|
-
return Array.from({ length: defaultHours }, (_, i) => buildRange(startMinutes + i * 60, 60));
|
|
48766
|
-
}
|
|
48767
|
-
const endMinutes = parseTimeToMinutes4(endTimeStr);
|
|
48768
|
-
if (Number.isNaN(endMinutes)) {
|
|
48769
|
-
const fallbackHours = 11;
|
|
48770
|
-
return Array.from({ length: fallbackHours }, (_, i) => buildRange(startMinutes + i * 60, 60));
|
|
48771
|
-
}
|
|
48772
|
-
let durationMinutes = endMinutes - startMinutes;
|
|
48773
|
-
if (durationMinutes <= 0) {
|
|
48774
|
-
durationMinutes += 24 * 60;
|
|
48775
|
-
}
|
|
48776
|
-
const rangeCount = Math.max(1, Math.ceil(durationMinutes / 60));
|
|
48777
|
-
return Array.from({ length: rangeCount }, (_, i) => {
|
|
48778
|
-
const remainingMinutes = durationMinutes - i * 60;
|
|
48779
|
-
const rangeMinutes = remainingMinutes >= 60 ? 60 : remainingMinutes;
|
|
48780
|
-
return buildRange(startMinutes + i * 60, rangeMinutes);
|
|
49019
|
+
timeZone: "UTC"
|
|
48781
49020
|
});
|
|
48782
49021
|
};
|
|
48783
|
-
const hourlyTimeRanges =
|
|
49022
|
+
const hourlyTimeRanges = buildHourlyIntervals({
|
|
49023
|
+
shiftStart: lineInfo.metrics.shift_start || "06:00",
|
|
49024
|
+
shiftEnd: lineInfo.metrics.shift_end,
|
|
49025
|
+
fallbackHours: 11
|
|
49026
|
+
});
|
|
49027
|
+
const targetPlan = buildHourlyTargetPlan({
|
|
49028
|
+
shiftStart: lineInfo.metrics.shift_start || "06:00",
|
|
49029
|
+
shiftEnd: lineInfo.metrics.shift_end,
|
|
49030
|
+
breaks: shiftBreaks,
|
|
49031
|
+
pphThreshold: Number(lineInfo.metrics.threshold_pph ?? 0),
|
|
49032
|
+
fallbackHours: Math.max(hourlyTimeRanges.length, 1),
|
|
49033
|
+
rounding: "floor"
|
|
49034
|
+
});
|
|
48784
49035
|
const shiftDuration = hourlyTimeRanges.length || 11;
|
|
48785
|
-
const targetOutputPerHour = Math.round(lineInfo.metrics.threshold_pph ?? 0);
|
|
48786
49036
|
let hourlyActualOutput = [];
|
|
48787
49037
|
if (lineInfo.metrics.output_hourly && Object.keys(lineInfo.metrics.output_hourly).length > 0) {
|
|
48788
49038
|
const [startHourStr, startMinuteStr] = (lineInfo.metrics.shift_start || "6:00").split(":");
|
|
@@ -48968,29 +49218,29 @@ var LinePdfGenerator = ({
|
|
|
48968
49218
|
doc.text("Remarks", 160, tableHeaderY);
|
|
48969
49219
|
doc.setFont("helvetica", "normal");
|
|
48970
49220
|
let yPos = tableStartY;
|
|
48971
|
-
const now4 = /* @__PURE__ */ new Date();
|
|
48972
|
-
const currentTimeIST = new Date(now4.toLocaleString("en-US", { timeZone: "Asia/Kolkata" }));
|
|
48973
|
-
const [sYear, sMonth, sDay] = lineInfo.date.split("-").map(Number);
|
|
48974
|
-
const [sStartH, sStartM] = (lineInfo.metrics.shift_start || "06:00").split(":").map(Number);
|
|
48975
|
-
const shiftStartBase = new Date(sYear, sMonth - 1, sDay, sStartH, sStartM || 0);
|
|
48976
49221
|
hourlyTimeRanges.forEach((timeRange, index) => {
|
|
48977
49222
|
const actualOutput = hourlyActualOutput[index] || 0;
|
|
48978
|
-
const
|
|
48979
|
-
|
|
48980
|
-
|
|
48981
|
-
|
|
49223
|
+
const dataCollected = isHourlyIntervalComplete({
|
|
49224
|
+
reportDate: lineInfo.date,
|
|
49225
|
+
shiftStart: lineInfo.metrics.shift_start || "06:00",
|
|
49226
|
+
shiftEnd: lineInfo.metrics.shift_end,
|
|
49227
|
+
interval: timeRange,
|
|
49228
|
+
timeZone: reportTimezone
|
|
49229
|
+
});
|
|
48982
49230
|
const outputStr = dataCollected ? actualOutput.toString() : "TBD";
|
|
48983
49231
|
if (index < totalRows - 1) {
|
|
48984
49232
|
const rowBottomY = headerBottomY + (index + 1) * rowSpacing;
|
|
48985
49233
|
doc.setDrawColor(200, 200, 200);
|
|
48986
49234
|
doc.line(20, rowBottomY, 190, rowBottomY);
|
|
48987
49235
|
}
|
|
48988
|
-
const
|
|
48989
|
-
const
|
|
48990
|
-
const
|
|
48991
|
-
doc.text(timeRange.
|
|
49236
|
+
const targetForRange = targetPlan.targets[index] ?? 0;
|
|
49237
|
+
const targetStr = targetForRange.toString();
|
|
49238
|
+
const remarkText = targetPlan.breakRemarks[index] || "";
|
|
49239
|
+
doc.text(`${formatMinutesLabel(timeRange.start)} - ${formatMinutesLabel(timeRange.end)}`, 25, yPos);
|
|
48992
49240
|
doc.text(outputStr, 75, yPos);
|
|
48993
49241
|
doc.text(targetStr, 105, yPos);
|
|
49242
|
+
const remarkDisplay = remarkText ? doc.splitTextToSize(remarkText, 26)[0] : "";
|
|
49243
|
+
doc.text(remarkDisplay, 160, yPos);
|
|
48994
49244
|
if (!dataCollected) {
|
|
48995
49245
|
doc.setTextColor(100, 100, 100);
|
|
48996
49246
|
doc.text("-", 135, yPos);
|
|
@@ -49006,11 +49256,10 @@ var LinePdfGenerator = ({
|
|
|
49006
49256
|
doc.setTextColor(0, 0, 0);
|
|
49007
49257
|
yPos += rowSpacing;
|
|
49008
49258
|
});
|
|
49009
|
-
const fileDate =
|
|
49259
|
+
const fileDate = formatOperationalDateKey(lineInfo.date, {
|
|
49010
49260
|
day: "2-digit",
|
|
49011
49261
|
month: "short",
|
|
49012
|
-
year: "numeric"
|
|
49013
|
-
timeZone: "Asia/Kolkata"
|
|
49262
|
+
year: "numeric"
|
|
49014
49263
|
}).replace(/ /g, "_");
|
|
49015
49264
|
const fileShift = shiftType.replace(/ /g, "_");
|
|
49016
49265
|
const fileName = `${lineInfo.line_name}_${fileDate}_${fileShift}.pdf`;
|
|
@@ -49938,6 +50187,13 @@ var WorkspaceMonthlyHistory = ({
|
|
|
49938
50187
|
}, [analysisMonthlyData, selectedShiftId, isUptimeMode, shiftWorkSeconds]);
|
|
49939
50188
|
const efficiencyDelta = trendSummary?.avg_efficiency?.delta_pp ?? 0;
|
|
49940
50189
|
const efficiencyImproved = efficiencyDelta >= 0;
|
|
50190
|
+
const assemblyRangeCycleTime = React143.useMemo(() => {
|
|
50191
|
+
const trendCycleTime = Number(trendSummary?.avg_cycle_time?.current);
|
|
50192
|
+
if (Number.isFinite(trendCycleTime) && trendCycleTime > 0) {
|
|
50193
|
+
return Math.round(trendCycleTime);
|
|
50194
|
+
}
|
|
50195
|
+
return metrics2?.avgCycleTime ?? 0;
|
|
50196
|
+
}, [trendSummary?.avg_cycle_time?.current, metrics2?.avgCycleTime]);
|
|
49941
50197
|
const cycleDeltaRaw = trendSummary?.avg_cycle_time?.delta_seconds ?? 0;
|
|
49942
50198
|
const cyclePrev = trendSummary?.avg_cycle_time?.previous ?? 0;
|
|
49943
50199
|
const cycleDelta = cyclePrev ? cycleDeltaRaw / cyclePrev * 100 : 0;
|
|
@@ -50123,7 +50379,7 @@ var WorkspaceMonthlyHistory = ({
|
|
|
50123
50379
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-gray-600 mb-1", children: "Avg Cycle Time" }),
|
|
50124
50380
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-nowrap", children: [
|
|
50125
50381
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-2xl font-bold text-gray-900", children: [
|
|
50126
|
-
|
|
50382
|
+
assemblyRangeCycleTime,
|
|
50127
50383
|
"s"
|
|
50128
50384
|
] }),
|
|
50129
50385
|
/* @__PURE__ */ jsxRuntime.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: [
|
|
@@ -50459,7 +50715,25 @@ var WorkspaceWhatsAppShareButton = ({
|
|
|
50459
50715
|
}
|
|
50460
50716
|
);
|
|
50461
50717
|
};
|
|
50462
|
-
var
|
|
50718
|
+
var formatOperationalDateKey2 = (dateKey, options) => {
|
|
50719
|
+
const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
|
|
50720
|
+
const year = Number.isFinite(yearPart) ? yearPart : 1970;
|
|
50721
|
+
const month = Number.isFinite(monthPart) ? monthPart : 1;
|
|
50722
|
+
const day = Number.isFinite(dayPart) ? dayPart : 1;
|
|
50723
|
+
return new Date(Date.UTC(year, month - 1, day, 12)).toLocaleDateString("en-IN", {
|
|
50724
|
+
...options,
|
|
50725
|
+
timeZone: "UTC"
|
|
50726
|
+
});
|
|
50727
|
+
};
|
|
50728
|
+
var WorkspacePdfGenerator = ({
|
|
50729
|
+
workspace,
|
|
50730
|
+
className,
|
|
50731
|
+
idleTimeReasons,
|
|
50732
|
+
efficiencyLegend,
|
|
50733
|
+
hourlyCycleTimes,
|
|
50734
|
+
shiftBreaks = [],
|
|
50735
|
+
reportTimezone = "Asia/Kolkata"
|
|
50736
|
+
}) => {
|
|
50463
50737
|
const [isGenerating, setIsGenerating] = React143.useState(false);
|
|
50464
50738
|
const entityConfig = useEntityConfig();
|
|
50465
50739
|
const effectiveLegend = efficiencyLegend || DEFAULT_EFFICIENCY_LEGEND;
|
|
@@ -50489,7 +50763,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
|
|
|
50489
50763
|
doc.setFontSize(9);
|
|
50490
50764
|
doc.setFont("helvetica", "normal");
|
|
50491
50765
|
doc.setTextColor(100, 100, 100);
|
|
50492
|
-
const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone:
|
|
50766
|
+
const generatedText = `Generated on ${(/* @__PURE__ */ new Date()).toLocaleString("en-IN", { timeZone: reportTimezone })}`;
|
|
50493
50767
|
const generatedTextWidth = doc.getStringUnitWidth(generatedText) * 9 / doc.internal.scaleFactor;
|
|
50494
50768
|
doc.text(generatedText, doc.internal.pageSize.width - 20 - generatedTextWidth, 15);
|
|
50495
50769
|
doc.setDrawColor(200, 200, 200);
|
|
@@ -50508,11 +50782,10 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
|
|
|
50508
50782
|
doc.setFontSize(13);
|
|
50509
50783
|
doc.setFont("helvetica", "normal");
|
|
50510
50784
|
doc.setTextColor(60, 60, 60);
|
|
50511
|
-
const date =
|
|
50785
|
+
const date = formatOperationalDateKey2(workspace.date, {
|
|
50512
50786
|
weekday: "long",
|
|
50513
50787
|
day: "numeric",
|
|
50514
|
-
month: "long"
|
|
50515
|
-
timeZone: "Asia/Kolkata"
|
|
50788
|
+
month: "long"
|
|
50516
50789
|
});
|
|
50517
50790
|
const rawShiftType = workspace.shift_type || (workspace.shift_id === 0 ? "Day" : workspace.shift_id === 1 ? "Night" : `Shift ${workspace.shift_id}`);
|
|
50518
50791
|
const shiftType = rawShiftType.toLowerCase().includes("shift") ? rawShiftType : `${rawShiftType} Shift`;
|
|
@@ -50521,7 +50794,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
|
|
|
50521
50794
|
const currentTime = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-IN", {
|
|
50522
50795
|
hour: "2-digit",
|
|
50523
50796
|
minute: "2-digit",
|
|
50524
|
-
timeZone:
|
|
50797
|
+
timeZone: reportTimezone
|
|
50525
50798
|
});
|
|
50526
50799
|
const shiftStartTime = (/* @__PURE__ */ new Date(`2000-01-01 ${workspace.shift_start}`)).toLocaleTimeString("en-IN", {
|
|
50527
50800
|
hour: "2-digit",
|
|
@@ -50533,29 +50806,12 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
|
|
|
50533
50806
|
minute: "2-digit",
|
|
50534
50807
|
hour12: true
|
|
50535
50808
|
});
|
|
50536
|
-
const
|
|
50537
|
-
|
|
50538
|
-
|
|
50539
|
-
|
|
50540
|
-
|
|
50541
|
-
};
|
|
50542
|
-
const toShiftUtcMs = (dateKey, timeValue) => {
|
|
50543
|
-
const [yearPart, monthPart, dayPart] = dateKey.split("-").map(Number);
|
|
50544
|
-
const year = Number.isFinite(yearPart) ? yearPart : 1970;
|
|
50545
|
-
const month = Number.isFinite(monthPart) ? monthPart : 1;
|
|
50546
|
-
const day = Number.isFinite(dayPart) ? dayPart : 1;
|
|
50547
|
-
const [hourPart, minutePart] = timeValue.split(":").map(Number);
|
|
50548
|
-
const hour = Number.isFinite(hourPart) ? hourPart : 0;
|
|
50549
|
-
const minute = Number.isFinite(minutePart) ? minutePart : 0;
|
|
50550
|
-
const IST_OFFSET_MINUTES = 330;
|
|
50551
|
-
return Date.UTC(year, month - 1, day, hour, minute) - IST_OFFSET_MINUTES * 60 * 1e3;
|
|
50552
|
-
};
|
|
50553
|
-
const shiftStartMinutes = parseTimeToMinutes4(workspace.shift_start);
|
|
50554
|
-
const shiftEndMinutes = parseTimeToMinutes4(workspace.shift_end);
|
|
50555
|
-
const wrapsMidnight = shiftEndMinutes <= shiftStartMinutes;
|
|
50556
|
-
const shiftStartUtcMs = toShiftUtcMs(workspace.date, workspace.shift_start);
|
|
50557
|
-
const shiftEndUtcMs = toShiftUtcMs(workspace.date, workspace.shift_end) + (wrapsMidnight ? 24 * 60 * 60 * 1e3 : 0);
|
|
50558
|
-
const isShiftInProgress = Date.now() >= shiftStartUtcMs && Date.now() < shiftEndUtcMs;
|
|
50809
|
+
const isShiftInProgress = isShiftInProgressForReportDate({
|
|
50810
|
+
reportDate: workspace.date,
|
|
50811
|
+
shiftStart: workspace.shift_start,
|
|
50812
|
+
shiftEnd: workspace.shift_end,
|
|
50813
|
+
timeZone: reportTimezone
|
|
50814
|
+
});
|
|
50559
50815
|
const reportPeriodEndTime = isShiftInProgress ? currentTime : shiftEndTime;
|
|
50560
50816
|
doc.setFontSize(12);
|
|
50561
50817
|
doc.setTextColor(80, 80, 80);
|
|
@@ -50661,7 +50917,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
|
|
|
50661
50917
|
shiftStart: workspace.shift_start,
|
|
50662
50918
|
shiftEnd: workspace.shift_end,
|
|
50663
50919
|
shiftDate: workspace.date,
|
|
50664
|
-
timezone:
|
|
50920
|
+
timezone: reportTimezone
|
|
50665
50921
|
}) : null;
|
|
50666
50922
|
const hourlyUptime = uptimeSeries?.points?.length ? Array.from({ length: Math.ceil(uptimeSeries.shiftMinutes / 60) }, (_, index) => {
|
|
50667
50923
|
const start = index * 60;
|
|
@@ -50676,6 +50932,31 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
|
|
|
50676
50932
|
const hourlyData = isUptimeMode ? hourlyUptime : isAssemblyCycleMode ? hourlyCycleTimes && hourlyCycleTimes.length > 0 ? hourlyCycleTimes : workspace.hourly_action_counts || [] : workspace.hourly_action_counts || [];
|
|
50677
50933
|
const hourlyTarget = workspace.pph_threshold;
|
|
50678
50934
|
const cycleTarget = workspace.ideal_cycle_time || 0;
|
|
50935
|
+
const hourlyIntervals = buildHourlyIntervals({
|
|
50936
|
+
shiftStart: workspace.shift_start,
|
|
50937
|
+
shiftEnd: workspace.shift_end,
|
|
50938
|
+
fallbackHours: Math.max(hourlyData.length, 1)
|
|
50939
|
+
});
|
|
50940
|
+
const outputTargetPlan = !isUptimeMode && !isAssemblyCycleMode ? buildHourlyTargetPlan({
|
|
50941
|
+
shiftStart: workspace.shift_start,
|
|
50942
|
+
shiftEnd: workspace.shift_end,
|
|
50943
|
+
breaks: shiftBreaks,
|
|
50944
|
+
pphThreshold: workspace.pph_threshold,
|
|
50945
|
+
fallbackHours: Math.max(hourlyData.length, 1),
|
|
50946
|
+
rounding: "floor"
|
|
50947
|
+
}) : null;
|
|
50948
|
+
const formatIntervalLabel = (totalMinutes) => {
|
|
50949
|
+
const normalized = (totalMinutes % (24 * 60) + 24 * 60) % (24 * 60);
|
|
50950
|
+
const hour = Math.floor(normalized / 60);
|
|
50951
|
+
const minute = normalized % 60;
|
|
50952
|
+
const time2 = new Date(Date.UTC(2e3, 0, 1, hour, minute));
|
|
50953
|
+
return time2.toLocaleTimeString("en-IN", {
|
|
50954
|
+
hour: "numeric",
|
|
50955
|
+
...minute > 0 ? { minute: "2-digit" } : {},
|
|
50956
|
+
hour12: true,
|
|
50957
|
+
timeZone: "UTC"
|
|
50958
|
+
});
|
|
50959
|
+
};
|
|
50679
50960
|
const pageHeight = doc.internal.pageSize.height;
|
|
50680
50961
|
const maxContentY = pageHeight - 15;
|
|
50681
50962
|
const baseTableStartY = hourlyPerfStartY + 31;
|
|
@@ -50743,36 +51024,30 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
|
|
|
50743
51024
|
doc.setFontSize(contentFontSize);
|
|
50744
51025
|
doc.setFont("helvetica", "normal");
|
|
50745
51026
|
let yPos = headerBottomY + 5.5;
|
|
50746
|
-
const
|
|
50747
|
-
const today = /* @__PURE__ */ new Date();
|
|
50748
|
-
today.setHours(0, 0, 0, 0);
|
|
50749
|
-
workspaceDate.setHours(0, 0, 0, 0);
|
|
50750
|
-
const isToday2 = workspaceDate.getTime() === today.getTime();
|
|
51027
|
+
const isToday2 = getDateKeyInTimeZone(reportTimezone) === workspace.date;
|
|
50751
51028
|
let currentHour = 24;
|
|
50752
51029
|
if (isToday2) {
|
|
50753
51030
|
const now4 = /* @__PURE__ */ new Date();
|
|
50754
|
-
const
|
|
50755
|
-
currentHour =
|
|
51031
|
+
const currentTimeInReportTimezone = new Date(now4.toLocaleString("en-US", { timeZone: reportTimezone }));
|
|
51032
|
+
currentHour = currentTimeInReportTimezone.getHours();
|
|
50756
51033
|
}
|
|
50757
51034
|
hourlyData.forEach((entry, index) => {
|
|
50758
|
-
const
|
|
50759
|
-
|
|
50760
|
-
const
|
|
50761
|
-
|
|
50762
|
-
|
|
50763
|
-
|
|
50764
|
-
|
|
50765
|
-
|
|
50766
|
-
|
|
50767
|
-
hour12: true
|
|
50768
|
-
})}`;
|
|
50769
|
-
const hourNumber = startTime.getHours();
|
|
50770
|
-
const dataCollected = !isToday2 || hourNumber < currentHour;
|
|
51035
|
+
const interval = hourlyIntervals[index];
|
|
51036
|
+
const timeRange = interval ? `${formatIntervalLabel(interval.start)} - ${formatIntervalLabel(interval.end)}` : `${index + 1}`;
|
|
51037
|
+
const dataCollected = interval ? isHourlyIntervalComplete({
|
|
51038
|
+
reportDate: workspace.date,
|
|
51039
|
+
shiftStart: workspace.shift_start,
|
|
51040
|
+
shiftEnd: workspace.shift_end,
|
|
51041
|
+
interval,
|
|
51042
|
+
timeZone: reportTimezone
|
|
51043
|
+
}) : !isToday2 || currentHour >= 24;
|
|
50771
51044
|
const outputValue = isUptimeMode ? entry.activeMinutes ?? 0 : entry;
|
|
50772
51045
|
const idleValue = isUptimeMode ? entry.idleMinutes ?? 0 : 0;
|
|
50773
51046
|
const uptimePercent = isUptimeMode ? entry.uptimePercent ?? 0 : 0;
|
|
50774
51047
|
const outputStr = dataCollected ? outputValue.toString() : "TBD";
|
|
50775
|
-
const
|
|
51048
|
+
const effectiveTarget = outputTargetPlan?.targets[index] ?? hourlyTarget;
|
|
51049
|
+
const remarkText = outputTargetPlan?.breakRemarks[index] || "";
|
|
51050
|
+
const targetStr = isUptimeMode ? dataCollected ? idleValue.toString() : "TBD" : effectiveTarget.toString();
|
|
50776
51051
|
if (index < totalRows - 1) {
|
|
50777
51052
|
const rowBottomY = headerBottomY + (index + 1) * rowHeight;
|
|
50778
51053
|
doc.setDrawColor(200, 200, 200);
|
|
@@ -50804,10 +51079,12 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
|
|
|
50804
51079
|
} else {
|
|
50805
51080
|
doc.text(outputStr, 75, yPos);
|
|
50806
51081
|
doc.text(targetStr, 105, yPos);
|
|
51082
|
+
const remarkDisplay = remarkText ? doc.splitTextToSize(remarkText, 26)[0] : "";
|
|
51083
|
+
doc.text(remarkDisplay, 160, yPos);
|
|
50807
51084
|
if (!dataCollected) {
|
|
50808
51085
|
doc.setTextColor(100, 100, 100);
|
|
50809
51086
|
doc.text("-", 135, yPos);
|
|
50810
|
-
} else if (outputValue >=
|
|
51087
|
+
} else if (outputValue >= effectiveTarget) {
|
|
50811
51088
|
doc.setTextColor(0, 171, 69);
|
|
50812
51089
|
doc.setFont("ZapfDingbats", "normal");
|
|
50813
51090
|
doc.text("4", 135, yPos);
|
|
@@ -50821,11 +51098,10 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
|
|
|
50821
51098
|
yPos += rowHeight;
|
|
50822
51099
|
});
|
|
50823
51100
|
const workspaceDisplayName = getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
|
|
50824
|
-
const fileDate =
|
|
51101
|
+
const fileDate = formatOperationalDateKey2(workspace.date, {
|
|
50825
51102
|
day: "2-digit",
|
|
50826
51103
|
month: "short",
|
|
50827
|
-
year: "numeric"
|
|
50828
|
-
timeZone: "Asia/Kolkata"
|
|
51104
|
+
year: "numeric"
|
|
50829
51105
|
}).replace(/ /g, "_");
|
|
50830
51106
|
const fileShift = shiftType.replace(/ /g, "_");
|
|
50831
51107
|
const fileName = `${workspaceDisplayName}_${fileDate}_${fileShift}.pdf`;
|
|
@@ -50871,12 +51147,25 @@ var WorkspaceMonthlyPdfGenerator = ({
|
|
|
50871
51147
|
availableShifts,
|
|
50872
51148
|
shiftConfig,
|
|
50873
51149
|
efficiencyLegend,
|
|
51150
|
+
trendSummary,
|
|
50874
51151
|
className,
|
|
50875
51152
|
compact = false,
|
|
50876
51153
|
isAssemblyWorkspace = false
|
|
50877
51154
|
}) => {
|
|
50878
51155
|
const [isGenerating, setIsGenerating] = React143.useState(false);
|
|
50879
51156
|
const effectiveLegend = efficiencyLegend || DEFAULT_EFFICIENCY_LEGEND;
|
|
51157
|
+
const drawStatusMark = (doc, x, y, met) => {
|
|
51158
|
+
doc.setLineWidth(0.8);
|
|
51159
|
+
if (met) {
|
|
51160
|
+
doc.setDrawColor(0, 171, 69);
|
|
51161
|
+
doc.line(x - 1.8, y - 0.2, x - 0.5, y + 1.2);
|
|
51162
|
+
doc.line(x - 0.5, y + 1.2, x + 2.1, y - 1.6);
|
|
51163
|
+
return;
|
|
51164
|
+
}
|
|
51165
|
+
doc.setDrawColor(227, 67, 41);
|
|
51166
|
+
doc.line(x - 1.8, y - 1.6, x + 1.8, y + 1.6);
|
|
51167
|
+
doc.line(x - 1.8, y + 1.6, x + 1.8, y - 1.6);
|
|
51168
|
+
};
|
|
50880
51169
|
const generatePDF = async () => {
|
|
50881
51170
|
setIsGenerating(true);
|
|
50882
51171
|
try {
|
|
@@ -51071,12 +51360,13 @@ var WorkspaceMonthlyPdfGenerator = ({
|
|
|
51071
51360
|
doc.setFont("helvetica", "bold");
|
|
51072
51361
|
doc.text(`${outputMetrics.underperformingDays} of ${outputMetrics.totalDays}`, 120, kpiStartY + kpiSpacing * 3);
|
|
51073
51362
|
} else {
|
|
51363
|
+
const medianCycleTime = Number.isFinite(trendSummary?.avg_cycle_time?.current) ? Number(trendSummary?.avg_cycle_time?.current) : outputMetrics.avgCycleTime;
|
|
51074
51364
|
createKPIBox(kpiStartY);
|
|
51075
51365
|
doc.setFontSize(11);
|
|
51076
51366
|
doc.setFont("helvetica", "normal");
|
|
51077
51367
|
doc.text("Average Cycle Time:", 25, kpiStartY);
|
|
51078
51368
|
doc.setFont("helvetica", "bold");
|
|
51079
|
-
doc.text(`${
|
|
51369
|
+
doc.text(`${medianCycleTime.toFixed(1)}s`, 120, kpiStartY);
|
|
51080
51370
|
createKPIBox(kpiStartY + kpiSpacing);
|
|
51081
51371
|
doc.setFont("helvetica", "normal");
|
|
51082
51372
|
doc.text("Average Idle Time:", 25, kpiStartY + kpiSpacing);
|
|
@@ -51111,8 +51401,8 @@ var WorkspaceMonthlyPdfGenerator = ({
|
|
|
51111
51401
|
doc.roundedRect(20, tableHeaderY, 170, 7, 1, 1, "F");
|
|
51112
51402
|
const textY = tableHeaderY + 5;
|
|
51113
51403
|
doc.text("Date", 25, textY);
|
|
51114
|
-
doc.text(isUptimeMode ? "Productive" : isAssemblyWorkspace ? "Cycle Time" : "Actual", 60, textY);
|
|
51115
|
-
doc.text(isUptimeMode ? "Idle" : isAssemblyWorkspace ? "
|
|
51404
|
+
doc.text(isUptimeMode ? "Productive" : isAssemblyWorkspace ? "Actual Cycle Time" : "Actual", 60, textY);
|
|
51405
|
+
doc.text(isUptimeMode ? "Idle" : isAssemblyWorkspace ? "Standard Cycle Time" : "Standard", 95, textY);
|
|
51116
51406
|
doc.text(isUptimeMode ? "Utilization" : "Efficiency", 135, textY);
|
|
51117
51407
|
doc.text("Status", 170, textY);
|
|
51118
51408
|
doc.setLineWidth(0.2);
|
|
@@ -51144,31 +51434,18 @@ var WorkspaceMonthlyPdfGenerator = ({
|
|
|
51144
51434
|
doc.text(formatIdleTime(productiveSeconds), 60, yPos);
|
|
51145
51435
|
doc.text(formatIdleTime(clampedIdleSeconds), 95, yPos);
|
|
51146
51436
|
doc.text(`${utilization}%`, 135, yPos);
|
|
51147
|
-
|
|
51148
|
-
doc.setTextColor(0, 171, 69);
|
|
51149
|
-
doc.text("\u2713", 170, yPos);
|
|
51150
|
-
} else {
|
|
51151
|
-
doc.setTextColor(227, 67, 41);
|
|
51152
|
-
doc.text("\xD7", 170, yPos);
|
|
51153
|
-
}
|
|
51154
|
-
doc.setTextColor(0, 0, 0);
|
|
51437
|
+
drawStatusMark(doc, 171, yPos - 0.3, utilization >= effectiveLegend.green_min);
|
|
51155
51438
|
} else {
|
|
51156
51439
|
if (isAssemblyWorkspace) {
|
|
51440
|
+
const targetCycleTime = Number.isFinite(shift.idealCycleTime) && Number(shift.idealCycleTime) > 0 ? Number(shift.idealCycleTime) : shift.pphThreshold > 0 ? 3600 / shift.pphThreshold : null;
|
|
51157
51441
|
doc.text(`${shift.cycleTime.toFixed(1)}`, 60, yPos);
|
|
51158
|
-
doc.text(
|
|
51442
|
+
doc.text(targetCycleTime !== null ? `${targetCycleTime.toFixed(1)}` : "-", 95, yPos);
|
|
51159
51443
|
} else {
|
|
51160
51444
|
doc.text(`${shift.output}`, 60, yPos);
|
|
51161
51445
|
doc.text(`${shift.targetOutput}`, 95, yPos);
|
|
51162
51446
|
}
|
|
51163
51447
|
doc.text(`${shift.efficiency.toFixed(1)}%`, 135, yPos);
|
|
51164
|
-
|
|
51165
|
-
doc.setTextColor(0, 171, 69);
|
|
51166
|
-
doc.text("\u2713", 170, yPos);
|
|
51167
|
-
} else {
|
|
51168
|
-
doc.setTextColor(227, 67, 41);
|
|
51169
|
-
doc.text("\xD7", 170, yPos);
|
|
51170
|
-
}
|
|
51171
|
-
doc.setTextColor(0, 0, 0);
|
|
51448
|
+
drawStatusMark(doc, 171, yPos - 0.3, shift.efficiency >= effectiveLegend.green_min);
|
|
51172
51449
|
}
|
|
51173
51450
|
yPos += 8;
|
|
51174
51451
|
});
|
|
@@ -58535,6 +58812,50 @@ var UserUsageStats = ({
|
|
|
58535
58812
|
] })
|
|
58536
58813
|
] });
|
|
58537
58814
|
};
|
|
58815
|
+
|
|
58816
|
+
// src/lib/utils/teamUsage.ts
|
|
58817
|
+
var ISO_DATE_KEY_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
58818
|
+
function formatLocalDateKey(date) {
|
|
58819
|
+
const year = date.getFullYear();
|
|
58820
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
58821
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
58822
|
+
return `${year}-${month}-${day}`;
|
|
58823
|
+
}
|
|
58824
|
+
function parseUsageDate(dateLike) {
|
|
58825
|
+
if (ISO_DATE_KEY_REGEX.test(dateLike)) {
|
|
58826
|
+
const [year, month, day] = dateLike.split("-").map(Number);
|
|
58827
|
+
return new Date(year, month - 1, day);
|
|
58828
|
+
}
|
|
58829
|
+
return new Date(dateLike);
|
|
58830
|
+
}
|
|
58831
|
+
function normalizeUsageDateKey(dateLike) {
|
|
58832
|
+
if (!dateLike) return void 0;
|
|
58833
|
+
if (ISO_DATE_KEY_REGEX.test(dateLike)) return dateLike;
|
|
58834
|
+
const parsed = parseUsageDate(dateLike);
|
|
58835
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
58836
|
+
return dateLike.split("T")[0] || dateLike;
|
|
58837
|
+
}
|
|
58838
|
+
return formatLocalDateKey(parsed);
|
|
58839
|
+
}
|
|
58840
|
+
function getCurrentWeekToDateUsageRange(today = /* @__PURE__ */ new Date()) {
|
|
58841
|
+
const normalizedToday = new Date(today);
|
|
58842
|
+
normalizedToday.setHours(0, 0, 0, 0);
|
|
58843
|
+
const dayOfWeek = normalizedToday.getDay();
|
|
58844
|
+
const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
|
|
58845
|
+
const monday = new Date(normalizedToday);
|
|
58846
|
+
monday.setDate(normalizedToday.getDate() - daysSinceMonday);
|
|
58847
|
+
return {
|
|
58848
|
+
startDate: formatLocalDateKey(monday),
|
|
58849
|
+
endDate: formatLocalDateKey(normalizedToday),
|
|
58850
|
+
daysElapsed: daysSinceMonday + 1
|
|
58851
|
+
};
|
|
58852
|
+
}
|
|
58853
|
+
function calculateAverageDailyDuration(durationMs, dayCount) {
|
|
58854
|
+
if (!Number.isFinite(durationMs) || dayCount <= 0) {
|
|
58855
|
+
return 0;
|
|
58856
|
+
}
|
|
58857
|
+
return Math.round(durationMs / dayCount);
|
|
58858
|
+
}
|
|
58538
58859
|
var UserUsageDetailModal = ({
|
|
58539
58860
|
userId,
|
|
58540
58861
|
userName,
|
|
@@ -58544,7 +58865,7 @@ var UserUsageDetailModal = ({
|
|
|
58544
58865
|
endDate
|
|
58545
58866
|
}) => {
|
|
58546
58867
|
const [currentWeekStart, setCurrentWeekStart] = React143.useState(() => {
|
|
58547
|
-
if (startDate) return
|
|
58868
|
+
if (startDate) return parseUsageDate(startDate);
|
|
58548
58869
|
const { start } = getCurrentWeekRange();
|
|
58549
58870
|
return start;
|
|
58550
58871
|
});
|
|
@@ -58554,8 +58875,8 @@ var UserUsageDetailModal = ({
|
|
|
58554
58875
|
return end;
|
|
58555
58876
|
}, [currentWeekStart]);
|
|
58556
58877
|
const { data, isLoading, error } = useUserUsage(userId, {
|
|
58557
|
-
startDate: currentWeekStart
|
|
58558
|
-
endDate: currentWeekEnd
|
|
58878
|
+
startDate: formatLocalDateKey(currentWeekStart),
|
|
58879
|
+
endDate: formatLocalDateKey(currentWeekEnd)
|
|
58559
58880
|
});
|
|
58560
58881
|
const handlePrevWeek = () => {
|
|
58561
58882
|
setCurrentWeekStart((prev) => {
|
|
@@ -58648,8 +58969,8 @@ function UsageContent({
|
|
|
58648
58969
|
} else {
|
|
58649
58970
|
daysToCount = 7;
|
|
58650
58971
|
}
|
|
58651
|
-
const
|
|
58652
|
-
const weekAvgMs =
|
|
58972
|
+
const weekActiveMs = weekData.reduce((sum, d) => sum + d.active_duration_ms, 0);
|
|
58973
|
+
const weekAvgMs = calculateAverageDailyDuration(weekActiveMs, daysToCount);
|
|
58653
58974
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
58654
58975
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center mb-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 bg-gray-50 rounded-lg p-1 border border-gray-100", children: [
|
|
58655
58976
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -58661,7 +58982,7 @@ function UsageContent({
|
|
|
58661
58982
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "w-4 h-4" })
|
|
58662
58983
|
}
|
|
58663
58984
|
),
|
|
58664
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-700 min-w-[140px] text-center", children: formatDateRange(currentWeekStart
|
|
58985
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-700 min-w-[140px] text-center", children: formatDateRange(formatLocalDateKey(currentWeekStart), formatLocalDateKey(currentWeekEnd)) }),
|
|
58665
58986
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
58666
58987
|
"button",
|
|
58667
58988
|
{
|
|
@@ -58693,7 +59014,7 @@ function DailyBarChart({
|
|
|
58693
59014
|
const weekData = React143.useMemo(() => buildWeekData(dailyData, getDateStringsInRange(weekStart, weekEnd)), [dailyData, weekStart, weekEnd]);
|
|
58694
59015
|
const chartData = React143.useMemo(() => {
|
|
58695
59016
|
return weekData.map((day, index) => {
|
|
58696
|
-
const date =
|
|
59017
|
+
const date = parseUsageDate(day.date);
|
|
58697
59018
|
return {
|
|
58698
59019
|
...day,
|
|
58699
59020
|
index,
|
|
@@ -58703,7 +59024,7 @@ function DailyBarChart({
|
|
|
58703
59024
|
// green-500
|
|
58704
59025
|
passiveColor: "#fbbf24",
|
|
58705
59026
|
// amber-400
|
|
58706
|
-
isToday: day.date === (/* @__PURE__ */ new Date())
|
|
59027
|
+
isToday: day.date === formatLocalDateKey(/* @__PURE__ */ new Date())
|
|
58707
59028
|
};
|
|
58708
59029
|
});
|
|
58709
59030
|
}, [weekData]);
|
|
@@ -58723,7 +59044,7 @@ function DailyBarChart({
|
|
|
58723
59044
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
58724
59045
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 rounded-full bg-gray-300" }),
|
|
58725
59046
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-medium text-gray-600", children: [
|
|
58726
|
-
"Average Daily usage: ",
|
|
59047
|
+
"Average Daily active usage: ",
|
|
58727
59048
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-900 font-semibold", children: avgDailyMs > 0 ? formatDuration2(avgDailyMs) : "0m" })
|
|
58728
59049
|
] })
|
|
58729
59050
|
] }),
|
|
@@ -58888,17 +59209,14 @@ function getCurrentWeekRange() {
|
|
|
58888
59209
|
function getDateStringsInRange(start, end) {
|
|
58889
59210
|
const result = [];
|
|
58890
59211
|
for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
|
|
58891
|
-
|
|
58892
|
-
const month = String(d.getMonth() + 1).padStart(2, "0");
|
|
58893
|
-
const day = String(d.getDate()).padStart(2, "0");
|
|
58894
|
-
result.push(`${year}-${month}-${day}`);
|
|
59212
|
+
result.push(formatLocalDateKey(d));
|
|
58895
59213
|
}
|
|
58896
59214
|
return result;
|
|
58897
59215
|
}
|
|
58898
59216
|
function buildWeekData(dailyData, weekDates) {
|
|
58899
59217
|
const dataMap = new Map(
|
|
58900
59218
|
(dailyData || []).map((d) => {
|
|
58901
|
-
const key =
|
|
59219
|
+
const key = normalizeUsageDateKey(d.date);
|
|
58902
59220
|
return [key, { ...d, date: key }];
|
|
58903
59221
|
})
|
|
58904
59222
|
);
|
|
@@ -58913,22 +59231,10 @@ function buildWeekData(dailyData, weekDates) {
|
|
|
58913
59231
|
};
|
|
58914
59232
|
});
|
|
58915
59233
|
}
|
|
58916
|
-
function normalizeDateKey(dateLike) {
|
|
58917
|
-
if (!dateLike) return void 0;
|
|
58918
|
-
try {
|
|
58919
|
-
const date = new Date(dateLike);
|
|
58920
|
-
const year = date.getFullYear();
|
|
58921
|
-
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
58922
|
-
const day = String(date.getDate()).padStart(2, "0");
|
|
58923
|
-
return `${year}-${month}-${day}`;
|
|
58924
|
-
} catch {
|
|
58925
|
-
return dateLike.split("T")[0] || dateLike;
|
|
58926
|
-
}
|
|
58927
|
-
}
|
|
58928
59234
|
function formatDateRange(startDate, endDate) {
|
|
58929
59235
|
try {
|
|
58930
|
-
const start =
|
|
58931
|
-
const end =
|
|
59236
|
+
const start = parseUsageDate(startDate);
|
|
59237
|
+
const end = parseUsageDate(endDate);
|
|
58932
59238
|
const opts = { month: "short", day: "numeric" };
|
|
58933
59239
|
const yearOpts = { year: "numeric" };
|
|
58934
59240
|
return `${start.toLocaleDateString("en-US", opts)} - ${end.toLocaleDateString("en-US", opts)}, ${end.toLocaleDateString("en-US", yearOpts)}`;
|
|
@@ -58939,7 +59245,7 @@ function formatDateRange(startDate, endDate) {
|
|
|
58939
59245
|
function formatDate(dateStr) {
|
|
58940
59246
|
if (!dateStr) return "";
|
|
58941
59247
|
try {
|
|
58942
|
-
const date =
|
|
59248
|
+
const date = parseUsageDate(dateStr);
|
|
58943
59249
|
return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
58944
59250
|
} catch {
|
|
58945
59251
|
return dateStr;
|
|
@@ -59217,7 +59523,7 @@ var UserManagementTable = ({
|
|
|
59217
59523
|
}
|
|
59218
59524
|
),
|
|
59219
59525
|
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Assignments" }),
|
|
59220
|
-
showUsageStats && /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Average Daily
|
|
59526
|
+
showUsageStats && /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Average Daily Active Usage" }),
|
|
59221
59527
|
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Actions" })
|
|
59222
59528
|
] }) }),
|
|
59223
59529
|
/* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "bg-white divide-y divide-gray-200", children: filteredAndSortedUsers.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx("td", { colSpan: showUsageStats ? 5 : 4, className: "px-6 py-12", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center text-gray-400 gap-2", children: [
|
|
@@ -64334,7 +64640,17 @@ var KPIDetailView = ({
|
|
|
64334
64640
|
)
|
|
64335
64641
|
] }),
|
|
64336
64642
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 ml-auto", children: [
|
|
64337
|
-
resolvedLineInfo && activeTab === "overview" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
64643
|
+
resolvedLineInfo && activeTab === "overview" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
64644
|
+
LinePdfGenerator,
|
|
64645
|
+
{
|
|
64646
|
+
lineInfo: resolvedLineInfo,
|
|
64647
|
+
workspaceData: resolvedWorkspaces || [],
|
|
64648
|
+
issueResolutionSummary,
|
|
64649
|
+
shiftName: getShiftName(resolvedLineInfo.shift_id),
|
|
64650
|
+
shiftBreaks: shiftConfig?.shifts?.find((shift) => shift.shiftId === resolvedLineInfo.shift_id)?.breaks || [],
|
|
64651
|
+
reportTimezone: shiftConfig?.timezone || configuredTimezone
|
|
64652
|
+
}
|
|
64653
|
+
),
|
|
64338
64654
|
activeTab === "monthly_history" && !urlDate && !urlShift && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
64339
64655
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
64340
64656
|
MonthlyRangeFilter_default,
|
|
@@ -64363,6 +64679,8 @@ var KPIDetailView = ({
|
|
|
64363
64679
|
rangeStart,
|
|
64364
64680
|
rangeEnd,
|
|
64365
64681
|
selectedShiftId,
|
|
64682
|
+
availableShifts: shiftConfig?.shifts?.map((shift) => ({ id: shift.shiftId, name: shift.shiftName })),
|
|
64683
|
+
lineAssembly: resolvedLineInfo?.assembly === true,
|
|
64366
64684
|
compact: true
|
|
64367
64685
|
}
|
|
64368
64686
|
)
|
|
@@ -64541,7 +64859,7 @@ var KPIDetailViewWithDisplayNames = withSelectedLineDisplayNames(KPIDetailView);
|
|
|
64541
64859
|
var KPIDetailView_default = KPIDetailViewWithDisplayNames;
|
|
64542
64860
|
var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
|
|
64543
64861
|
var resolveCompanyId = (...candidates) => candidates.find(isNonEmptyString);
|
|
64544
|
-
var
|
|
64862
|
+
var parseTimeToMinutes3 = (value) => {
|
|
64545
64863
|
if (!value) return null;
|
|
64546
64864
|
const [hourStr, minuteStr] = value.split(":");
|
|
64547
64865
|
const hour = Number.parseInt(hourStr ?? "", 10);
|
|
@@ -64553,8 +64871,8 @@ var getShiftEndDate = (shift, timezone) => {
|
|
|
64553
64871
|
if (!shift?.date) return null;
|
|
64554
64872
|
const startTime = shift.startTime || "06:00";
|
|
64555
64873
|
const endTime = shift.endTime || "18:00";
|
|
64556
|
-
const startMinutes =
|
|
64557
|
-
const endMinutes =
|
|
64874
|
+
const startMinutes = parseTimeToMinutes3(startTime);
|
|
64875
|
+
const endMinutes = parseTimeToMinutes3(endTime);
|
|
64558
64876
|
if (startMinutes === null || endMinutes === null) return null;
|
|
64559
64877
|
const shiftStartDate = dateFnsTz.fromZonedTime(`${shift.date}T${startTime}:00`, timezone);
|
|
64560
64878
|
let durationMinutes = endMinutes - startMinutes;
|
|
@@ -64592,7 +64910,7 @@ var createKpisOverviewUrl = ({
|
|
|
64592
64910
|
return queryString ? `/kpis?${queryString}` : "/kpis";
|
|
64593
64911
|
};
|
|
64594
64912
|
var getZonedDateAtMidday = (dateKey, timezone) => dateFnsTz.fromZonedTime(`${dateKey}T12:00:00`, timezone);
|
|
64595
|
-
var
|
|
64913
|
+
var formatDateKey2 = (dateKey, timezone, options) => {
|
|
64596
64914
|
try {
|
|
64597
64915
|
return new Intl.DateTimeFormat("en-US", {
|
|
64598
64916
|
...options,
|
|
@@ -65660,7 +65978,7 @@ var KPIsOverviewView = ({
|
|
|
65660
65978
|
setActiveTab(newTab);
|
|
65661
65979
|
}, [activeTab, leaderboardLines.length, lines.length]);
|
|
65662
65980
|
const formatLocalDate2 = React143.useCallback((dateKey) => {
|
|
65663
|
-
return
|
|
65981
|
+
return formatDateKey2(dateKey, configuredTimezone, {
|
|
65664
65982
|
year: "numeric",
|
|
65665
65983
|
month: "long",
|
|
65666
65984
|
day: "numeric"
|
|
@@ -65674,8 +65992,8 @@ var KPIsOverviewView = ({
|
|
|
65674
65992
|
zonedNow.getMonth(),
|
|
65675
65993
|
new Date(zonedNow.getFullYear(), zonedNow.getMonth() + 1, 0).getDate()
|
|
65676
65994
|
);
|
|
65677
|
-
const startLabel =
|
|
65678
|
-
const endLabel =
|
|
65995
|
+
const startLabel = formatDateKey2(startOfMonthKey, configuredTimezone, { month: "short", day: "numeric" });
|
|
65996
|
+
const endLabel = formatDateKey2(endOfMonthKey, configuredTimezone, { month: "short", day: "numeric" });
|
|
65679
65997
|
return `${startLabel} - ${endLabel}, ${zonedNow.getFullYear()}`;
|
|
65680
65998
|
};
|
|
65681
65999
|
const isMonthlyMode = activeTab === "leaderboard" && timeRange === "monthly";
|
|
@@ -71310,6 +71628,7 @@ var WorkspaceDetailView = ({
|
|
|
71310
71628
|
efficiency: monitoringMode === "uptime" ? Number.isFinite(metric.avg_efficiency) ? Number(metric.avg_efficiency) : computedUptimeEfficiency : Number.isFinite(metric.avg_efficiency) ? Number(metric.avg_efficiency) : 0,
|
|
71311
71629
|
output: metric.total_output || 0,
|
|
71312
71630
|
cycleTime: metric.avg_cycle_time || 0,
|
|
71631
|
+
idealCycleTime: Number(metric.ideal_cycle_time || 0),
|
|
71313
71632
|
pph: metric.avg_pph || 0,
|
|
71314
71633
|
pphThreshold: metric.pph_threshold || 0,
|
|
71315
71634
|
idealOutput: Number(metric.ideal_output || 0),
|
|
@@ -71865,7 +72184,9 @@ var WorkspaceDetailView = ({
|
|
|
71865
72184
|
workspace,
|
|
71866
72185
|
idleTimeReasons: idleTimeChartData,
|
|
71867
72186
|
efficiencyLegend,
|
|
71868
|
-
hourlyCycleTimes: cycleTimeChartData
|
|
72187
|
+
hourlyCycleTimes: cycleTimeChartData,
|
|
72188
|
+
shiftBreaks: shiftConfig?.shifts?.find((shift2) => shift2.shiftId === workspace.shift_id)?.breaks || [],
|
|
72189
|
+
reportTimezone: shiftConfig?.timezone || timezone
|
|
71869
72190
|
}
|
|
71870
72191
|
) }),
|
|
71871
72192
|
activeTab === "monthly_history" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
@@ -71900,6 +72221,7 @@ var WorkspaceDetailView = ({
|
|
|
71900
72221
|
workspaceId,
|
|
71901
72222
|
workspaceName: formattedWorkspaceName,
|
|
71902
72223
|
monthlyData,
|
|
72224
|
+
analysisData: analysisMonthlyData,
|
|
71903
72225
|
selectedMonth,
|
|
71904
72226
|
selectedYear,
|
|
71905
72227
|
monitoringMode: workspace?.monitoring_mode,
|
|
@@ -71908,7 +72230,10 @@ var WorkspaceDetailView = ({
|
|
|
71908
72230
|
selectedShiftId: selectedShift,
|
|
71909
72231
|
availableShifts: shiftConfig?.shifts?.map((s) => ({ id: s.shiftId, name: s.shiftName })),
|
|
71910
72232
|
shiftConfig,
|
|
71911
|
-
|
|
72233
|
+
efficiencyLegend,
|
|
72234
|
+
trendSummary: workspaceMonthlyTrend,
|
|
72235
|
+
compact: true,
|
|
72236
|
+
isAssemblyWorkspace
|
|
71912
72237
|
}
|
|
71913
72238
|
)
|
|
71914
72239
|
] })
|
|
@@ -73495,39 +73820,8 @@ var TeamManagementView = ({
|
|
|
73495
73820
|
const [users, setUsers] = React143.useState([]);
|
|
73496
73821
|
const [availableLines, setAvailableLines] = React143.useState([]);
|
|
73497
73822
|
const [availableFactories, setAvailableFactories] = React143.useState([]);
|
|
73498
|
-
const [
|
|
73499
|
-
totalUsers: 0,
|
|
73500
|
-
owners: 0,
|
|
73501
|
-
it: 0,
|
|
73502
|
-
plantHeads: 0,
|
|
73503
|
-
industrialEngineers: 0,
|
|
73504
|
-
supervisors: 0,
|
|
73505
|
-
optifye: 0
|
|
73506
|
-
});
|
|
73823
|
+
const [usageSummaryByUser, setUsageSummaryByUser] = React143.useState({});
|
|
73507
73824
|
const [isAddUserDialogOpen, setIsAddUserDialogOpen] = React143.useState(false);
|
|
73508
|
-
const normalizeIds = React143.useCallback((value) => {
|
|
73509
|
-
if (Array.isArray(value)) {
|
|
73510
|
-
return value.filter((id3) => typeof id3 === "string" && id3.length > 0);
|
|
73511
|
-
}
|
|
73512
|
-
if (typeof value === "string" && value.length > 0) {
|
|
73513
|
-
return [value];
|
|
73514
|
-
}
|
|
73515
|
-
return [];
|
|
73516
|
-
}, []);
|
|
73517
|
-
const factoryScopedRoleFactoryIds = React143.useMemo(() => {
|
|
73518
|
-
if (!isFactoryScopedRole(user?.role_level)) return [];
|
|
73519
|
-
const scopedFactoryIds = normalizeIds(user?.access_scope?.factory_ids);
|
|
73520
|
-
if (scopedFactoryIds.length > 0) {
|
|
73521
|
-
return Array.from(new Set(scopedFactoryIds));
|
|
73522
|
-
}
|
|
73523
|
-
const propertyFactoryIds = normalizeIds(
|
|
73524
|
-
user?.properties?.factory_ids ?? user?.properties?.factory_id
|
|
73525
|
-
);
|
|
73526
|
-
if (propertyFactoryIds.length > 0) {
|
|
73527
|
-
return Array.from(new Set(propertyFactoryIds));
|
|
73528
|
-
}
|
|
73529
|
-
return entityConfig?.factoryId ? [entityConfig.factoryId] : [];
|
|
73530
|
-
}, [user, entityConfig?.factoryId, normalizeIds]);
|
|
73531
73825
|
const notifyScopeRefresh = React143.useCallback(() => {
|
|
73532
73826
|
if (typeof window !== "undefined") {
|
|
73533
73827
|
window.dispatchEvent(new Event("rbac:refresh-scope"));
|
|
@@ -73536,37 +73830,17 @@ var TeamManagementView = ({
|
|
|
73536
73830
|
const canAddUsers = canRoleManageUsers(user?.role_level);
|
|
73537
73831
|
const canViewUsageStats = canRoleViewUsageStats(user?.role_level);
|
|
73538
73832
|
const pageCompanyId = entityConfig?.companyId || user?.properties?.company_id;
|
|
73539
|
-
const
|
|
73540
|
-
const usageDateRange = React143.useMemo(() => {
|
|
73541
|
-
const today = /* @__PURE__ */ new Date();
|
|
73542
|
-
const dayOfWeek = today.getDay();
|
|
73543
|
-
const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
|
|
73544
|
-
const monday = new Date(today);
|
|
73545
|
-
monday.setDate(today.getDate() - daysSinceMonday);
|
|
73546
|
-
return {
|
|
73547
|
-
startDate: monday.toISOString().slice(0, 10),
|
|
73548
|
-
endDate: today.toISOString().slice(0, 10),
|
|
73549
|
-
daysElapsed: daysSinceMonday + 1
|
|
73550
|
-
// Include today
|
|
73551
|
-
};
|
|
73552
|
-
}, []);
|
|
73553
|
-
const {
|
|
73554
|
-
data: usageData,
|
|
73555
|
-
isLoading: isUsageLoading
|
|
73556
|
-
} = useCompanyUsersUsage(canViewUsageStats ? companyIdForUsage : void 0, {
|
|
73557
|
-
startDate: usageDateRange.startDate,
|
|
73558
|
-
endDate: usageDateRange.endDate
|
|
73559
|
-
});
|
|
73833
|
+
const usageDateRange = React143.useMemo(() => getCurrentWeekToDateUsageRange(), []);
|
|
73560
73834
|
const avgDailyUsageMap = React143.useMemo(() => {
|
|
73561
|
-
if (!
|
|
73835
|
+
if (!canViewUsageStats) {
|
|
73562
73836
|
return {};
|
|
73563
73837
|
}
|
|
73564
73838
|
const daysElapsed = usageDateRange.daysElapsed;
|
|
73565
|
-
return
|
|
73566
|
-
acc[
|
|
73839
|
+
return Object.entries(usageSummaryByUser).reduce((acc, [userId, usage]) => {
|
|
73840
|
+
acc[userId] = calculateAverageDailyDuration(usage.active_duration_ms, daysElapsed);
|
|
73567
73841
|
return acc;
|
|
73568
73842
|
}, {});
|
|
73569
|
-
}, [
|
|
73843
|
+
}, [canViewUsageStats, usageSummaryByUser, usageDateRange.daysElapsed]);
|
|
73570
73844
|
const pageTitle = "Team Management";
|
|
73571
73845
|
const pageDescription = canViewUsageStats ? "Manage team members and view their daily usage" : "Manage supervisors in your factory";
|
|
73572
73846
|
const hasAccess = user ? user.role_level === void 0 || canRoleAccessTeamManagement(user.role_level) : false;
|
|
@@ -73590,7 +73864,6 @@ var TeamManagementView = ({
|
|
|
73590
73864
|
setIsLoading(true);
|
|
73591
73865
|
setError(void 0);
|
|
73592
73866
|
try {
|
|
73593
|
-
const userManagementService = createUserManagementService(supabase);
|
|
73594
73867
|
const { data: { session } } = await supabase.auth.getSession();
|
|
73595
73868
|
if (!session?.access_token) {
|
|
73596
73869
|
throw new Error("No authentication token available");
|
|
@@ -73600,116 +73873,31 @@ var TeamManagementView = ({
|
|
|
73600
73873
|
if (!backendUrl) {
|
|
73601
73874
|
throw new Error("Backend URL is not configured. Please set NEXT_PUBLIC_BACKEND_URL in your environment.");
|
|
73602
73875
|
}
|
|
73603
|
-
|
|
73604
|
-
|
|
73605
|
-
|
|
73606
|
-
|
|
73607
|
-
|
|
73608
|
-
|
|
73609
|
-
|
|
73610
|
-
});
|
|
73611
|
-
if (!linesResponse.ok) {
|
|
73612
|
-
throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
|
|
73613
|
-
}
|
|
73614
|
-
const linesData = await linesResponse.json();
|
|
73615
|
-
const lines = linesData.lines || [];
|
|
73616
|
-
setAvailableFactories(factories || []);
|
|
73617
|
-
setAvailableLines(lines);
|
|
73618
|
-
console.log("[TeamManagementView] Company-scoped team view - Company:", companyId, "Loaded factories:", factories?.length, "lines:", lines?.length);
|
|
73619
|
-
} else if (isFactoryScopedRole(user?.role_level)) {
|
|
73620
|
-
if (factoryScopedRoleFactoryIds.length > 0) {
|
|
73621
|
-
if (companyId) {
|
|
73622
|
-
const linesResponse = await fetch(`${backendUrl}/api/lines?company_id=${companyId}`, {
|
|
73623
|
-
headers: {
|
|
73624
|
-
"Authorization": `Bearer ${token}`,
|
|
73625
|
-
"Content-Type": "application/json"
|
|
73626
|
-
}
|
|
73627
|
-
});
|
|
73628
|
-
if (!linesResponse.ok) {
|
|
73629
|
-
throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
|
|
73630
|
-
}
|
|
73631
|
-
const linesData = await linesResponse.json();
|
|
73632
|
-
const allLines = linesData.lines || [];
|
|
73633
|
-
const lines = allLines.filter((line) => factoryScopedRoleFactoryIds.includes(line.factory_id));
|
|
73634
|
-
setAvailableLines(lines);
|
|
73635
|
-
console.log("[TeamManagementView] Factory-scoped team view - Factories:", factoryScopedRoleFactoryIds, "Loaded lines:", lines?.length);
|
|
73636
|
-
}
|
|
73637
|
-
} else if (entityConfig?.factoryId) {
|
|
73638
|
-
const linesResponse = await fetch(`${backendUrl}/api/lines?factory_id=${entityConfig.factoryId}`, {
|
|
73639
|
-
headers: {
|
|
73640
|
-
"Authorization": `Bearer ${token}`,
|
|
73641
|
-
"Content-Type": "application/json"
|
|
73642
|
-
}
|
|
73643
|
-
});
|
|
73644
|
-
if (!linesResponse.ok) {
|
|
73645
|
-
throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
|
|
73646
|
-
}
|
|
73647
|
-
const linesData = await linesResponse.json();
|
|
73648
|
-
const lines = linesData.lines || [];
|
|
73649
|
-
setAvailableLines(lines);
|
|
73650
|
-
console.log("[TeamManagementView] Plant Head - Using entityConfig factory:", entityConfig.factoryId, "Loaded lines:", lines?.length);
|
|
73651
|
-
} else {
|
|
73652
|
-
setAvailableLines([]);
|
|
73653
|
-
console.warn("[TeamManagementView] Plant Head has no factory assignments");
|
|
73654
|
-
}
|
|
73655
|
-
setAvailableFactories([]);
|
|
73656
|
-
} else if (companyId) {
|
|
73657
|
-
const linesResponse = await fetch(`${backendUrl}/api/lines?company_id=${companyId}`, {
|
|
73876
|
+
const params = new URLSearchParams({
|
|
73877
|
+
start_date: usageDateRange.startDate,
|
|
73878
|
+
end_date: usageDateRange.endDate
|
|
73879
|
+
});
|
|
73880
|
+
const bootstrapResponse = await fetch(
|
|
73881
|
+
`${backendUrl}/api/users/company/${companyId}/bootstrap?${params.toString()}`,
|
|
73882
|
+
{
|
|
73658
73883
|
headers: {
|
|
73659
73884
|
"Authorization": `Bearer ${token}`,
|
|
73660
73885
|
"Content-Type": "application/json"
|
|
73661
73886
|
}
|
|
73662
|
-
});
|
|
73663
|
-
if (!linesResponse.ok) {
|
|
73664
|
-
throw new Error(`Failed to fetch lines: ${linesResponse.statusText}`);
|
|
73665
73887
|
}
|
|
73666
|
-
|
|
73667
|
-
|
|
73668
|
-
|
|
73669
|
-
|
|
73670
|
-
|
|
73671
|
-
|
|
73672
|
-
|
|
73673
|
-
|
|
73674
|
-
|
|
73675
|
-
|
|
73676
|
-
|
|
73677
|
-
|
|
73678
|
-
|
|
73679
|
-
const successful = results.filter(
|
|
73680
|
-
(result) => result.status === "fulfilled"
|
|
73681
|
-
).flatMap((result) => result.value || []);
|
|
73682
|
-
if (successful.length > 0) {
|
|
73683
|
-
const byUserId = /* @__PURE__ */ new Map();
|
|
73684
|
-
successful.forEach((u) => {
|
|
73685
|
-
if (u?.user_id) {
|
|
73686
|
-
byUserId.set(u.user_id, u);
|
|
73687
|
-
}
|
|
73688
|
-
});
|
|
73689
|
-
return Array.from(byUserId.values());
|
|
73690
|
-
}
|
|
73691
|
-
const firstRejected = results.find(
|
|
73692
|
-
(result) => result.status === "rejected"
|
|
73693
|
-
);
|
|
73694
|
-
if (firstRejected) {
|
|
73695
|
-
throw firstRejected.reason;
|
|
73696
|
-
}
|
|
73697
|
-
return [];
|
|
73698
|
-
})() : userManagementService.getCompanyUsers(companyId);
|
|
73699
|
-
const [usersData, userStats] = await Promise.all([
|
|
73700
|
-
usersPromise,
|
|
73701
|
-
userManagementService.getUserStats(companyId)
|
|
73702
|
-
]);
|
|
73703
|
-
setUsers(usersData);
|
|
73704
|
-
setStats({
|
|
73705
|
-
totalUsers: userStats.total,
|
|
73706
|
-
owners: userStats.owners,
|
|
73707
|
-
it: userStats.it,
|
|
73708
|
-
plantHeads: userStats.plant_heads,
|
|
73709
|
-
industrialEngineers: userStats.industrial_engineers,
|
|
73710
|
-
supervisors: userStats.supervisors,
|
|
73711
|
-
optifye: 0
|
|
73712
|
-
});
|
|
73888
|
+
);
|
|
73889
|
+
if (!bootstrapResponse.ok) {
|
|
73890
|
+
const errorData = await bootstrapResponse.json().catch(() => ({}));
|
|
73891
|
+
throw new Error(errorData.detail || `Failed to fetch team bootstrap: ${bootstrapResponse.statusText}`);
|
|
73892
|
+
}
|
|
73893
|
+
const bootstrapData = await bootstrapResponse.json();
|
|
73894
|
+
setAvailableFactories((bootstrapData.factories || []).map((factory) => ({
|
|
73895
|
+
id: factory.id,
|
|
73896
|
+
factory_name: factory.factory_name
|
|
73897
|
+
})));
|
|
73898
|
+
setAvailableLines(bootstrapData.lines || []);
|
|
73899
|
+
setUsers(bootstrapData.users || []);
|
|
73900
|
+
setUsageSummaryByUser(bootstrapData.usage_summary_by_user || {});
|
|
73713
73901
|
} catch (err) {
|
|
73714
73902
|
console.error("Error loading team management data:", err);
|
|
73715
73903
|
setError(err instanceof Error ? err.message : "Failed to load data");
|
|
@@ -73717,7 +73905,7 @@ var TeamManagementView = ({
|
|
|
73717
73905
|
} finally {
|
|
73718
73906
|
setIsLoading(false);
|
|
73719
73907
|
}
|
|
73720
|
-
}, [supabase, user, pageCompanyId,
|
|
73908
|
+
}, [supabase, user, pageCompanyId, usageDateRange.endDate, usageDateRange.startDate]);
|
|
73721
73909
|
React143.useEffect(() => {
|
|
73722
73910
|
const companyId = pageCompanyId;
|
|
73723
73911
|
const canLoad = hasAccess && user && !!companyId;
|
|
@@ -73898,7 +74086,7 @@ var TeamManagementView = ({
|
|
|
73898
74086
|
availableLines,
|
|
73899
74087
|
availableFactories,
|
|
73900
74088
|
avgDailyUsage: avgDailyUsageMap,
|
|
73901
|
-
isUsageLoading,
|
|
74089
|
+
isUsageLoading: false,
|
|
73902
74090
|
showUsageStats: canViewUsageStats
|
|
73903
74091
|
}
|
|
73904
74092
|
) }),
|
|
@@ -80123,7 +80311,7 @@ var useOperationsOverviewRefresh = ({
|
|
|
80123
80311
|
};
|
|
80124
80312
|
}, [companyId, enabled, getIsPageActive, isLiveScope, lineIds.length, refreshFromResume, startPolling, stopPolling, supabase]);
|
|
80125
80313
|
};
|
|
80126
|
-
var
|
|
80314
|
+
var parseTimeToMinutes4 = (value) => {
|
|
80127
80315
|
if (!value) return null;
|
|
80128
80316
|
const parts = value.split(":");
|
|
80129
80317
|
if (parts.length < 2) return null;
|
|
@@ -80158,8 +80346,8 @@ var classifyShiftBucket = ({
|
|
|
80158
80346
|
return "day";
|
|
80159
80347
|
}
|
|
80160
80348
|
}
|
|
80161
|
-
const startMinutes =
|
|
80162
|
-
const endMinutes =
|
|
80349
|
+
const startMinutes = parseTimeToMinutes4(startTime);
|
|
80350
|
+
const endMinutes = parseTimeToMinutes4(endTime);
|
|
80163
80351
|
if (startMinutes !== null) {
|
|
80164
80352
|
if (startMinutes >= 4 * 60 && startMinutes < 18 * 60) return "day";
|
|
80165
80353
|
return "night";
|
|
@@ -80218,8 +80406,8 @@ var getShiftWindowsForConfig = (shiftConfig, timezone) => {
|
|
|
80218
80406
|
];
|
|
80219
80407
|
};
|
|
80220
80408
|
var normalizeShiftWindowMinutes = (startTime, endTime) => {
|
|
80221
|
-
const startMinutes =
|
|
80222
|
-
const endMinutesRaw =
|
|
80409
|
+
const startMinutes = parseTimeToMinutes4(startTime);
|
|
80410
|
+
const endMinutesRaw = parseTimeToMinutes4(endTime);
|
|
80223
80411
|
if (startMinutes === null || endMinutesRaw === null) {
|
|
80224
80412
|
return null;
|
|
80225
80413
|
}
|
|
@@ -80385,7 +80573,7 @@ var PlantHeadView = () => {
|
|
|
80385
80573
|
startTime: shift.startTime,
|
|
80386
80574
|
endTime: shift.endTime
|
|
80387
80575
|
});
|
|
80388
|
-
const startMinutes =
|
|
80576
|
+
const startMinutes = parseTimeToMinutes4(shift.startTime);
|
|
80389
80577
|
if (bucket === "day" && startMinutes !== null) {
|
|
80390
80578
|
candidateStarts.push(startMinutes);
|
|
80391
80579
|
}
|
|
@@ -80395,7 +80583,7 @@ var PlantHeadView = () => {
|
|
|
80395
80583
|
scopedLineIds.forEach((lineId) => {
|
|
80396
80584
|
const shiftConfig = shiftConfigMap.get(lineId) || staticShiftConfig;
|
|
80397
80585
|
getShiftWindowsForConfig(shiftConfig).forEach((shift) => {
|
|
80398
|
-
const startMinutes =
|
|
80586
|
+
const startMinutes = parseTimeToMinutes4(shift.startTime);
|
|
80399
80587
|
if (startMinutes !== null) {
|
|
80400
80588
|
candidateStarts.push(startMinutes);
|
|
80401
80589
|
}
|
|
@@ -80486,7 +80674,7 @@ var PlantHeadView = () => {
|
|
|
80486
80674
|
startTime: shift.startTime,
|
|
80487
80675
|
endTime: shift.endTime
|
|
80488
80676
|
});
|
|
80489
|
-
return bucket === "day" ?
|
|
80677
|
+
return bucket === "day" ? parseTimeToMinutes4(shift.startTime) : null;
|
|
80490
80678
|
}).filter((value) => value !== null);
|
|
80491
80679
|
}) : [];
|
|
80492
80680
|
if (dayStartCandidates.length > 0) {
|