@optifye/dashboard-core 6.10.45 → 6.10.47
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.css +3 -6
- package/dist/index.js +587 -192
- package/dist/index.mjs +587 -192
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -30299,37 +30299,13 @@ var HourlyOutputChartComponent = ({
|
|
|
30299
30299
|
}, []);
|
|
30300
30300
|
const xAxisConfig = React26__default.useMemo(() => {
|
|
30301
30301
|
if (containerWidth >= 960) {
|
|
30302
|
-
return { interval: 0, angle: -45, height: 92, tickFont: 10, tickMargin: 12
|
|
30302
|
+
return { interval: 0, angle: -45, height: 92, tickFont: 10, tickMargin: 12 };
|
|
30303
30303
|
}
|
|
30304
30304
|
if (containerWidth >= 760) {
|
|
30305
|
-
return { interval: 0, angle: -35, height: 76, tickFont: 9, tickMargin: 8
|
|
30305
|
+
return { interval: 0, angle: -35, height: 76, tickFont: 9, tickMargin: 8 };
|
|
30306
30306
|
}
|
|
30307
|
-
return { interval: 0, angle: -30, height: 64, tickFont: 9, tickMargin: 6
|
|
30307
|
+
return { interval: 0, angle: -30, height: 64, tickFont: 9, tickMargin: 6 };
|
|
30308
30308
|
}, [containerWidth]);
|
|
30309
|
-
const formatXAxisTick = React26__default.useCallback((raw) => {
|
|
30310
|
-
const label = typeof raw === "string" ? raw : String(raw);
|
|
30311
|
-
if (xAxisConfig.labelMode === "full") return label;
|
|
30312
|
-
const parts = label.split("-");
|
|
30313
|
-
if (parts.length !== 2) return label;
|
|
30314
|
-
const parsePart = (part) => {
|
|
30315
|
-
const match = part.match(/^(\d{1,2})(?::(\d{2}))?(AM|PM)$/);
|
|
30316
|
-
if (!match) return null;
|
|
30317
|
-
const [, hh, mm, meridiem] = match;
|
|
30318
|
-
const time2 = mm ? `${hh}:${mm}` : hh;
|
|
30319
|
-
const merShort = meridiem === "AM" ? "A" : "P";
|
|
30320
|
-
return { time: time2, meridiem, merShort };
|
|
30321
|
-
};
|
|
30322
|
-
const start = parsePart(parts[0]);
|
|
30323
|
-
const end = parsePart(parts[1]);
|
|
30324
|
-
if (!start || !end) return label;
|
|
30325
|
-
if (xAxisConfig.labelMode === "start") {
|
|
30326
|
-
return `${start.time}${start.merShort}`;
|
|
30327
|
-
}
|
|
30328
|
-
if (start.meridiem === end.meridiem) {
|
|
30329
|
-
return `${start.time}-${end.time}${end.merShort}`;
|
|
30330
|
-
}
|
|
30331
|
-
return `${start.time}${start.merShort}-${end.time}${end.merShort}`;
|
|
30332
|
-
}, [xAxisConfig.labelMode]);
|
|
30333
30309
|
const formatHour = React26__default.useCallback((hourIndex) => {
|
|
30334
30310
|
const isLastHour = hourIndex === SHIFT_DURATION - 1;
|
|
30335
30311
|
const startDecimalHour = shiftStartTime.decimalHour + hourIndex;
|
|
@@ -30539,7 +30515,7 @@ var HourlyOutputChartComponent = ({
|
|
|
30539
30515
|
right: 10,
|
|
30540
30516
|
bottom: 10,
|
|
30541
30517
|
// Small bottom margin
|
|
30542
|
-
left:
|
|
30518
|
+
left: 6
|
|
30543
30519
|
},
|
|
30544
30520
|
barCategoryGap: "25%",
|
|
30545
30521
|
children: [
|
|
@@ -30553,8 +30529,7 @@ var HourlyOutputChartComponent = ({
|
|
|
30553
30529
|
angle: xAxisConfig.angle,
|
|
30554
30530
|
textAnchor: "end",
|
|
30555
30531
|
tickMargin: xAxisConfig.tickMargin,
|
|
30556
|
-
height: xAxisConfig.height
|
|
30557
|
-
tickFormatter: formatXAxisTick
|
|
30532
|
+
height: xAxisConfig.height
|
|
30558
30533
|
}
|
|
30559
30534
|
),
|
|
30560
30535
|
/* @__PURE__ */ jsx(
|
|
@@ -30562,7 +30537,7 @@ var HourlyOutputChartComponent = ({
|
|
|
30562
30537
|
{
|
|
30563
30538
|
yAxisId: "default",
|
|
30564
30539
|
tickMargin: 8,
|
|
30565
|
-
width:
|
|
30540
|
+
width: 48,
|
|
30566
30541
|
domain: [0, maxYValue],
|
|
30567
30542
|
ticks: generateYAxisTicks(),
|
|
30568
30543
|
tickFormatter: (value) => value,
|
|
@@ -30571,7 +30546,7 @@ var HourlyOutputChartComponent = ({
|
|
|
30571
30546
|
return /* @__PURE__ */ jsx("g", { transform: `translate(${x},${y})`, children: /* @__PURE__ */ jsx(
|
|
30572
30547
|
"text",
|
|
30573
30548
|
{
|
|
30574
|
-
x:
|
|
30549
|
+
x: -2,
|
|
30575
30550
|
y: 0,
|
|
30576
30551
|
dy: 4,
|
|
30577
30552
|
textAnchor: "end",
|
|
@@ -51450,13 +51425,31 @@ var InviteUserDialog = ({
|
|
|
51450
51425
|
const [selectedRole, setSelectedRole] = useState("supervisor");
|
|
51451
51426
|
const [selectedLines, setSelectedLines] = useState([]);
|
|
51452
51427
|
const [selectedFactories, setSelectedFactories] = useState([]);
|
|
51428
|
+
const [lineSearch, setLineSearch] = useState("");
|
|
51429
|
+
const [factorySearch, setFactorySearch] = useState("");
|
|
51453
51430
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
51454
51431
|
const [error, setError] = useState(null);
|
|
51455
|
-
const [isLinesDropdownOpen, setIsLinesDropdownOpen] = useState(false);
|
|
51456
|
-
const [isFactoriesDropdownOpen, setIsFactoriesDropdownOpen] = useState(false);
|
|
51457
51432
|
const canInviteIT = user?.role_level === "owner" || user?.role_level === "optifye";
|
|
51458
51433
|
const canInvitePlantHead = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "optifye";
|
|
51459
51434
|
const canInviteSupervisor = ["owner", "it", "plant_head", "optifye"].includes(user?.role_level || "");
|
|
51435
|
+
const filteredLines = useMemo(() => {
|
|
51436
|
+
const search = lineSearch.trim().toLowerCase();
|
|
51437
|
+
if (!search) return availableLines;
|
|
51438
|
+
return availableLines.filter((line) => line.name.toLowerCase().includes(search));
|
|
51439
|
+
}, [availableLines, lineSearch]);
|
|
51440
|
+
const filteredFactories = useMemo(() => {
|
|
51441
|
+
const search = factorySearch.trim().toLowerCase();
|
|
51442
|
+
if (!search) return availableFactories;
|
|
51443
|
+
return availableFactories.filter((factory) => factory.name.toLowerCase().includes(search));
|
|
51444
|
+
}, [availableFactories, factorySearch]);
|
|
51445
|
+
const selectedLineItems = useMemo(
|
|
51446
|
+
() => availableLines.filter((line) => selectedLines.includes(line.id)),
|
|
51447
|
+
[availableLines, selectedLines]
|
|
51448
|
+
);
|
|
51449
|
+
const selectedFactoryItems = useMemo(
|
|
51450
|
+
() => availableFactories.filter((factory) => selectedFactories.includes(factory.id)),
|
|
51451
|
+
[availableFactories, selectedFactories]
|
|
51452
|
+
);
|
|
51460
51453
|
useEffect(() => {
|
|
51461
51454
|
if (!isOpen) {
|
|
51462
51455
|
setEmail("");
|
|
@@ -51465,9 +51458,9 @@ var InviteUserDialog = ({
|
|
|
51465
51458
|
setSelectedRole("supervisor");
|
|
51466
51459
|
setSelectedLines([]);
|
|
51467
51460
|
setSelectedFactories([]);
|
|
51461
|
+
setLineSearch("");
|
|
51462
|
+
setFactorySearch("");
|
|
51468
51463
|
setError(null);
|
|
51469
|
-
setIsLinesDropdownOpen(false);
|
|
51470
|
-
setIsFactoriesDropdownOpen(false);
|
|
51471
51464
|
}
|
|
51472
51465
|
}, [isOpen]);
|
|
51473
51466
|
const validateEmail = (email2) => {
|
|
@@ -51536,7 +51529,6 @@ var InviteUserDialog = ({
|
|
|
51536
51529
|
try {
|
|
51537
51530
|
const dashboardUrl = typeof window !== "undefined" ? window.location.origin : "";
|
|
51538
51531
|
if (dashboardUrl) {
|
|
51539
|
-
console.log("Sending welcome email to:", email.trim());
|
|
51540
51532
|
const { data: emailData, error: emailError } = await supabase.functions.invoke("hyper-service", {
|
|
51541
51533
|
body: {
|
|
51542
51534
|
action: "send-welcome-email",
|
|
@@ -51549,13 +51541,8 @@ var InviteUserDialog = ({
|
|
|
51549
51541
|
console.error("Failed to send welcome email:", emailError);
|
|
51550
51542
|
toast.warning("User added successfully, but welcome email could not be sent");
|
|
51551
51543
|
} else if (emailData?.success) {
|
|
51552
|
-
console.log("Welcome email sent successfully:", emailData);
|
|
51553
51544
|
toast.success("User added and welcome email sent!");
|
|
51554
|
-
} else {
|
|
51555
|
-
console.log("Welcome email response:", emailData);
|
|
51556
51545
|
}
|
|
51557
|
-
} else {
|
|
51558
|
-
console.warn("Dashboard URL not available, skipping welcome email");
|
|
51559
51546
|
}
|
|
51560
51547
|
} catch (emailErr) {
|
|
51561
51548
|
console.error("Error sending welcome email:", emailErr);
|
|
@@ -51581,7 +51568,7 @@ var InviteUserDialog = ({
|
|
|
51581
51568
|
children: /* @__PURE__ */ jsxs(
|
|
51582
51569
|
"div",
|
|
51583
51570
|
{
|
|
51584
|
-
className: "bg-white rounded-xl shadow-2xl max-w-
|
|
51571
|
+
className: "bg-white rounded-xl shadow-2xl max-w-2xl w-full max-h-[90vh] overflow-y-auto transform transition-all animate-in fade-in duration-200",
|
|
51585
51572
|
onClick: (e) => e.stopPropagation(),
|
|
51586
51573
|
children: [
|
|
51587
51574
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-6 border-b border-gray-200", children: [
|
|
@@ -51609,7 +51596,7 @@ var InviteUserDialog = ({
|
|
|
51609
51596
|
/* @__PURE__ */ jsx("span", { children: error })
|
|
51610
51597
|
] }),
|
|
51611
51598
|
/* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
|
|
51612
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
51599
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4", children: [
|
|
51613
51600
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
51614
51601
|
/* @__PURE__ */ jsxs("label", { htmlFor: "firstName", className: "block text-sm font-medium text-gray-700 mb-2", children: [
|
|
51615
51602
|
"First Name ",
|
|
@@ -51768,124 +51755,192 @@ var InviteUserDialog = ({
|
|
|
51768
51755
|
)
|
|
51769
51756
|
] })
|
|
51770
51757
|
] }),
|
|
51771
|
-
selectedRole === "plant_head" &&
|
|
51772
|
-
/* @__PURE__ */ jsxs("
|
|
51773
|
-
/* @__PURE__ */
|
|
51774
|
-
|
|
51758
|
+
selectedRole === "plant_head" && /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-slate-200 bg-slate-50 p-4 space-y-3", children: [
|
|
51759
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
51760
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
51761
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm font-medium text-gray-800 flex items-center gap-1.5", children: [
|
|
51762
|
+
/* @__PURE__ */ jsx(Building2, { className: "w-4 h-4 text-blue-600" }),
|
|
51763
|
+
"Factory Access"
|
|
51764
|
+
] }),
|
|
51765
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 mt-1", children: "Choose the factories this plant head will manage." })
|
|
51766
|
+
] }),
|
|
51767
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-medium bg-white border border-gray-200 text-gray-600 rounded-full px-2 py-1", children: [
|
|
51768
|
+
selectedFactories.length,
|
|
51769
|
+
" selected"
|
|
51770
|
+
] })
|
|
51775
51771
|
] }),
|
|
51776
|
-
/* @__PURE__ */ jsx("
|
|
51777
|
-
|
|
51778
|
-
|
|
51779
|
-
|
|
51772
|
+
availableFactories.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-dashed border-gray-300 bg-white px-3 py-4 text-sm text-gray-500", children: "No factories available for assignment." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
51773
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
51774
|
+
/* @__PURE__ */ jsx(
|
|
51775
|
+
"button",
|
|
51776
|
+
{
|
|
51777
|
+
type: "button",
|
|
51778
|
+
onClick: () => setSelectedFactories(availableFactories.map((factory) => factory.id)),
|
|
51779
|
+
className: "px-2.5 py-1 text-xs font-medium text-blue-700 bg-blue-100 rounded-md hover:bg-blue-200 transition-colors",
|
|
51780
|
+
disabled: isSubmitting,
|
|
51781
|
+
children: "Select all"
|
|
51782
|
+
}
|
|
51783
|
+
),
|
|
51784
|
+
/* @__PURE__ */ jsx(
|
|
51785
|
+
"button",
|
|
51786
|
+
{
|
|
51787
|
+
type: "button",
|
|
51788
|
+
onClick: () => setSelectedFactories([]),
|
|
51789
|
+
className: "px-2.5 py-1 text-xs font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 transition-colors",
|
|
51790
|
+
disabled: isSubmitting || selectedFactories.length === 0,
|
|
51791
|
+
children: "Clear"
|
|
51792
|
+
}
|
|
51793
|
+
)
|
|
51794
|
+
] }),
|
|
51795
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
51796
|
+
/* @__PURE__ */ jsx(Search, { className: "w-4 h-4 text-gray-400 absolute left-3 top-1/2 -translate-y-1/2" }),
|
|
51797
|
+
/* @__PURE__ */ jsx(
|
|
51798
|
+
"input",
|
|
51799
|
+
{
|
|
51800
|
+
type: "text",
|
|
51801
|
+
value: factorySearch,
|
|
51802
|
+
onChange: (e) => setFactorySearch(e.target.value),
|
|
51803
|
+
placeholder: "Search factories",
|
|
51804
|
+
className: "w-full pl-9 pr-3 py-2 text-sm border border-gray-300 rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent",
|
|
51805
|
+
disabled: isSubmitting
|
|
51806
|
+
}
|
|
51807
|
+
)
|
|
51808
|
+
] }),
|
|
51809
|
+
/* @__PURE__ */ jsx("div", { className: "max-h-56 overflow-y-auto rounded-lg border border-gray-200 bg-white divide-y divide-gray-100", children: filteredFactories.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-4 py-6 text-sm text-gray-500 text-center", children: "No factories match your search." }) : filteredFactories.map((factory) => /* @__PURE__ */ jsxs(
|
|
51810
|
+
"label",
|
|
51780
51811
|
{
|
|
51781
|
-
|
|
51782
|
-
|
|
51783
|
-
|
|
51784
|
-
|
|
51785
|
-
|
|
51786
|
-
selectedFactories.length === 0 ? /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-500", children: "Select factories..." }) : /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5 flex-1", children: selectedFactories.map((factoryId) => {
|
|
51787
|
-
const factory = availableFactories.find((f) => f.id === factoryId);
|
|
51788
|
-
return factory ? /* @__PURE__ */ jsxs(
|
|
51789
|
-
"span",
|
|
51812
|
+
className: "flex items-center justify-between gap-3 px-4 py-2.5 hover:bg-gray-50 cursor-pointer",
|
|
51813
|
+
children: [
|
|
51814
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
|
|
51815
|
+
/* @__PURE__ */ jsx(
|
|
51816
|
+
"input",
|
|
51790
51817
|
{
|
|
51791
|
-
|
|
51792
|
-
|
|
51793
|
-
|
|
51794
|
-
|
|
51795
|
-
|
|
51796
|
-
|
|
51797
|
-
|
|
51798
|
-
|
|
51799
|
-
|
|
51800
|
-
|
|
51801
|
-
|
|
51802
|
-
|
|
51803
|
-
|
|
51804
|
-
|
|
51805
|
-
|
|
51806
|
-
|
|
51807
|
-
]
|
|
51808
|
-
},
|
|
51809
|
-
factory.id
|
|
51810
|
-
) : null;
|
|
51811
|
-
}) }),
|
|
51812
|
-
/* @__PURE__ */ jsx(ChevronDown, { className: cn("w-4 h-4 text-gray-400 transition-transform flex-shrink-0", isFactoriesDropdownOpen && "rotate-180") })
|
|
51813
|
-
] })
|
|
51814
|
-
}
|
|
51815
|
-
),
|
|
51816
|
-
isFactoriesDropdownOpen && /* @__PURE__ */ jsx("div", { className: "absolute z-10 w-full mt-1 bg-white border border-gray-300 rounded-lg shadow-lg max-h-60 overflow-y-auto", children: availableFactories.map((factory) => /* @__PURE__ */ jsxs(
|
|
51817
|
-
"div",
|
|
51818
|
+
type: "checkbox",
|
|
51819
|
+
checked: selectedFactories.includes(factory.id),
|
|
51820
|
+
onChange: () => toggleFactorySelection(factory.id),
|
|
51821
|
+
className: "h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500",
|
|
51822
|
+
disabled: isSubmitting
|
|
51823
|
+
}
|
|
51824
|
+
),
|
|
51825
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-gray-800 truncate", children: factory.name })
|
|
51826
|
+
] }),
|
|
51827
|
+
selectedFactories.includes(factory.id) && /* @__PURE__ */ jsx(Check, { className: "w-4 h-4 text-blue-600 flex-shrink-0" })
|
|
51828
|
+
]
|
|
51829
|
+
},
|
|
51830
|
+
factory.id
|
|
51831
|
+
)) }),
|
|
51832
|
+
selectedFactoryItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: selectedFactoryItems.map((factory) => /* @__PURE__ */ jsxs(
|
|
51833
|
+
"span",
|
|
51818
51834
|
{
|
|
51819
|
-
|
|
51820
|
-
className: cn(
|
|
51821
|
-
"px-4 py-2.5 cursor-pointer flex items-center justify-between hover:bg-gray-50 transition-colors",
|
|
51822
|
-
selectedFactories.includes(factory.id) && "bg-blue-50"
|
|
51823
|
-
),
|
|
51835
|
+
className: "inline-flex items-center gap-1 px-2 py-1 bg-blue-100 text-blue-700 text-xs rounded-md font-medium",
|
|
51824
51836
|
children: [
|
|
51825
|
-
|
|
51826
|
-
|
|
51837
|
+
factory.name,
|
|
51838
|
+
/* @__PURE__ */ jsx(
|
|
51839
|
+
"button",
|
|
51840
|
+
{
|
|
51841
|
+
type: "button",
|
|
51842
|
+
onClick: () => toggleFactorySelection(factory.id),
|
|
51843
|
+
className: "hover:bg-blue-200 rounded-sm transition-colors",
|
|
51844
|
+
disabled: isSubmitting,
|
|
51845
|
+
children: /* @__PURE__ */ jsx(X, { className: "w-3 h-3" })
|
|
51846
|
+
}
|
|
51847
|
+
)
|
|
51827
51848
|
]
|
|
51828
51849
|
},
|
|
51829
51850
|
factory.id
|
|
51830
51851
|
)) })
|
|
51831
51852
|
] })
|
|
51832
51853
|
] }),
|
|
51833
|
-
selectedRole === "supervisor" &&
|
|
51834
|
-
/* @__PURE__ */ jsxs("
|
|
51835
|
-
/* @__PURE__ */
|
|
51836
|
-
|
|
51854
|
+
selectedRole === "supervisor" && /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-slate-200 bg-slate-50 p-4 space-y-3", children: [
|
|
51855
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
51856
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
51857
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm font-medium text-gray-800 flex items-center gap-1.5", children: [
|
|
51858
|
+
/* @__PURE__ */ jsx(Users, { className: "w-4 h-4 text-blue-600" }),
|
|
51859
|
+
"Line Access"
|
|
51860
|
+
] }),
|
|
51861
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 mt-1", children: "Choose the lines this supervisor will monitor." })
|
|
51862
|
+
] }),
|
|
51863
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-medium bg-white border border-gray-200 text-gray-600 rounded-full px-2 py-1", children: [
|
|
51864
|
+
selectedLines.length,
|
|
51865
|
+
" selected"
|
|
51866
|
+
] })
|
|
51837
51867
|
] }),
|
|
51838
|
-
/* @__PURE__ */ jsx("
|
|
51839
|
-
|
|
51840
|
-
|
|
51841
|
-
|
|
51868
|
+
availableLines.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-dashed border-gray-300 bg-white px-3 py-4 text-sm text-gray-500", children: "No lines available for assignment." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
51869
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
51870
|
+
/* @__PURE__ */ jsx(
|
|
51871
|
+
"button",
|
|
51872
|
+
{
|
|
51873
|
+
type: "button",
|
|
51874
|
+
onClick: () => setSelectedLines(availableLines.map((line) => line.id)),
|
|
51875
|
+
className: "px-2.5 py-1 text-xs font-medium text-blue-700 bg-blue-100 rounded-md hover:bg-blue-200 transition-colors",
|
|
51876
|
+
disabled: isSubmitting,
|
|
51877
|
+
children: "Select all"
|
|
51878
|
+
}
|
|
51879
|
+
),
|
|
51880
|
+
/* @__PURE__ */ jsx(
|
|
51881
|
+
"button",
|
|
51882
|
+
{
|
|
51883
|
+
type: "button",
|
|
51884
|
+
onClick: () => setSelectedLines([]),
|
|
51885
|
+
className: "px-2.5 py-1 text-xs font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 transition-colors",
|
|
51886
|
+
disabled: isSubmitting || selectedLines.length === 0,
|
|
51887
|
+
children: "Clear"
|
|
51888
|
+
}
|
|
51889
|
+
)
|
|
51890
|
+
] }),
|
|
51891
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
51892
|
+
/* @__PURE__ */ jsx(Search, { className: "w-4 h-4 text-gray-400 absolute left-3 top-1/2 -translate-y-1/2" }),
|
|
51893
|
+
/* @__PURE__ */ jsx(
|
|
51894
|
+
"input",
|
|
51895
|
+
{
|
|
51896
|
+
type: "text",
|
|
51897
|
+
value: lineSearch,
|
|
51898
|
+
onChange: (e) => setLineSearch(e.target.value),
|
|
51899
|
+
placeholder: "Search lines",
|
|
51900
|
+
className: "w-full pl-9 pr-3 py-2 text-sm border border-gray-300 rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent",
|
|
51901
|
+
disabled: isSubmitting
|
|
51902
|
+
}
|
|
51903
|
+
)
|
|
51904
|
+
] }),
|
|
51905
|
+
/* @__PURE__ */ jsx("div", { className: "max-h-56 overflow-y-auto rounded-lg border border-gray-200 bg-white divide-y divide-gray-100", children: filteredLines.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-4 py-6 text-sm text-gray-500 text-center", children: "No lines match your search." }) : filteredLines.map((line) => /* @__PURE__ */ jsxs(
|
|
51906
|
+
"label",
|
|
51842
51907
|
{
|
|
51843
|
-
|
|
51844
|
-
|
|
51845
|
-
|
|
51846
|
-
|
|
51847
|
-
|
|
51848
|
-
selectedLines.length === 0 ? /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-500", children: "Select lines..." }) : /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5 flex-1", children: selectedLines.map((lineId) => {
|
|
51849
|
-
const line = availableLines.find((l) => l.id === lineId);
|
|
51850
|
-
return line ? /* @__PURE__ */ jsxs(
|
|
51851
|
-
"span",
|
|
51908
|
+
className: "flex items-center justify-between gap-3 px-4 py-2.5 hover:bg-gray-50 cursor-pointer",
|
|
51909
|
+
children: [
|
|
51910
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
|
|
51911
|
+
/* @__PURE__ */ jsx(
|
|
51912
|
+
"input",
|
|
51852
51913
|
{
|
|
51853
|
-
|
|
51854
|
-
|
|
51855
|
-
|
|
51856
|
-
|
|
51857
|
-
|
|
51858
|
-
|
|
51859
|
-
|
|
51860
|
-
|
|
51861
|
-
|
|
51862
|
-
|
|
51863
|
-
|
|
51864
|
-
|
|
51865
|
-
|
|
51866
|
-
|
|
51867
|
-
|
|
51868
|
-
|
|
51869
|
-
]
|
|
51870
|
-
},
|
|
51871
|
-
line.id
|
|
51872
|
-
) : null;
|
|
51873
|
-
}) }),
|
|
51874
|
-
/* @__PURE__ */ jsx(ChevronDown, { className: cn("w-4 h-4 text-gray-400 transition-transform flex-shrink-0", isLinesDropdownOpen && "rotate-180") })
|
|
51875
|
-
] })
|
|
51876
|
-
}
|
|
51877
|
-
),
|
|
51878
|
-
isLinesDropdownOpen && /* @__PURE__ */ jsx("div", { className: "absolute z-10 w-full mt-1 bg-white border border-gray-300 rounded-lg shadow-lg max-h-60 overflow-y-auto", children: availableLines.map((line) => /* @__PURE__ */ jsxs(
|
|
51879
|
-
"div",
|
|
51914
|
+
type: "checkbox",
|
|
51915
|
+
checked: selectedLines.includes(line.id),
|
|
51916
|
+
onChange: () => toggleLineSelection(line.id),
|
|
51917
|
+
className: "h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500",
|
|
51918
|
+
disabled: isSubmitting
|
|
51919
|
+
}
|
|
51920
|
+
),
|
|
51921
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-gray-800 truncate", children: line.name })
|
|
51922
|
+
] }),
|
|
51923
|
+
selectedLines.includes(line.id) && /* @__PURE__ */ jsx(Check, { className: "w-4 h-4 text-blue-600 flex-shrink-0" })
|
|
51924
|
+
]
|
|
51925
|
+
},
|
|
51926
|
+
line.id
|
|
51927
|
+
)) }),
|
|
51928
|
+
selectedLineItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: selectedLineItems.map((line) => /* @__PURE__ */ jsxs(
|
|
51929
|
+
"span",
|
|
51880
51930
|
{
|
|
51881
|
-
|
|
51882
|
-
className: cn(
|
|
51883
|
-
"px-4 py-2.5 cursor-pointer flex items-center justify-between hover:bg-gray-50 transition-colors",
|
|
51884
|
-
selectedLines.includes(line.id) && "bg-blue-50"
|
|
51885
|
-
),
|
|
51931
|
+
className: "inline-flex items-center gap-1 px-2 py-1 bg-blue-100 text-blue-700 text-xs rounded-md font-medium",
|
|
51886
51932
|
children: [
|
|
51887
|
-
|
|
51888
|
-
|
|
51933
|
+
line.name,
|
|
51934
|
+
/* @__PURE__ */ jsx(
|
|
51935
|
+
"button",
|
|
51936
|
+
{
|
|
51937
|
+
type: "button",
|
|
51938
|
+
onClick: () => toggleLineSelection(line.id),
|
|
51939
|
+
className: "hover:bg-blue-200 rounded-sm transition-colors",
|
|
51940
|
+
disabled: isSubmitting,
|
|
51941
|
+
children: /* @__PURE__ */ jsx(X, { className: "w-3 h-3" })
|
|
51942
|
+
}
|
|
51943
|
+
)
|
|
51889
51944
|
]
|
|
51890
51945
|
},
|
|
51891
51946
|
line.id
|
|
@@ -52420,10 +52475,22 @@ var LineAssignmentDropdown = ({
|
|
|
52420
52475
|
canEdit,
|
|
52421
52476
|
onUpdate
|
|
52422
52477
|
}) => {
|
|
52478
|
+
const VIEWPORT_MARGIN = 8;
|
|
52479
|
+
const DROPDOWN_GAP = 6;
|
|
52480
|
+
const MIN_DROPDOWN_WIDTH = 300;
|
|
52481
|
+
const MAX_DROPDOWN_WIDTH = 420;
|
|
52482
|
+
const PREFERRED_DROPDOWN_HEIGHT = 420;
|
|
52483
|
+
const MIN_DROPDOWN_HEIGHT = 180;
|
|
52423
52484
|
const [isOpen, setIsOpen] = useState(false);
|
|
52424
52485
|
const [selectedIds, setSelectedIds] = useState(currentLineIds);
|
|
52425
52486
|
const [isSaving, setIsSaving] = useState(false);
|
|
52426
|
-
const [position, setPosition] = useState({
|
|
52487
|
+
const [position, setPosition] = useState({
|
|
52488
|
+
top: 0,
|
|
52489
|
+
left: 0,
|
|
52490
|
+
width: MIN_DROPDOWN_WIDTH,
|
|
52491
|
+
maxHeight: PREFERRED_DROPDOWN_HEIGHT,
|
|
52492
|
+
openAbove: false
|
|
52493
|
+
});
|
|
52427
52494
|
const buttonRef = useRef(null);
|
|
52428
52495
|
const dropdownRef = useRef(null);
|
|
52429
52496
|
useEffect(() => {
|
|
@@ -52433,10 +52500,31 @@ var LineAssignmentDropdown = ({
|
|
|
52433
52500
|
const updatePosition = () => {
|
|
52434
52501
|
if (isOpen && buttonRef.current) {
|
|
52435
52502
|
const rect = buttonRef.current.getBoundingClientRect();
|
|
52503
|
+
const viewportWidth = window.innerWidth;
|
|
52504
|
+
const viewportHeight = window.innerHeight;
|
|
52505
|
+
const width = Math.min(
|
|
52506
|
+
Math.max(rect.width, MIN_DROPDOWN_WIDTH),
|
|
52507
|
+
Math.min(MAX_DROPDOWN_WIDTH, viewportWidth - VIEWPORT_MARGIN * 2)
|
|
52508
|
+
);
|
|
52509
|
+
const left = Math.min(
|
|
52510
|
+
Math.max(VIEWPORT_MARGIN, rect.left),
|
|
52511
|
+
viewportWidth - width - VIEWPORT_MARGIN
|
|
52512
|
+
);
|
|
52513
|
+
const spaceBelow = viewportHeight - rect.bottom - VIEWPORT_MARGIN - DROPDOWN_GAP;
|
|
52514
|
+
const spaceAbove = rect.top - VIEWPORT_MARGIN - DROPDOWN_GAP;
|
|
52515
|
+
const openAbove = spaceBelow < 260 && spaceAbove > spaceBelow;
|
|
52516
|
+
const availableSpace = openAbove ? spaceAbove : spaceBelow;
|
|
52517
|
+
const maxHeight = Math.max(
|
|
52518
|
+
MIN_DROPDOWN_HEIGHT,
|
|
52519
|
+
Math.min(PREFERRED_DROPDOWN_HEIGHT, availableSpace > 0 ? availableSpace : viewportHeight - VIEWPORT_MARGIN * 2)
|
|
52520
|
+
);
|
|
52521
|
+
const top = openAbove ? rect.top - DROPDOWN_GAP : Math.min(rect.bottom + DROPDOWN_GAP, viewportHeight - VIEWPORT_MARGIN - maxHeight);
|
|
52436
52522
|
setPosition({
|
|
52437
|
-
top
|
|
52438
|
-
left
|
|
52439
|
-
width
|
|
52523
|
+
top,
|
|
52524
|
+
left,
|
|
52525
|
+
width,
|
|
52526
|
+
maxHeight,
|
|
52527
|
+
openAbove
|
|
52440
52528
|
});
|
|
52441
52529
|
}
|
|
52442
52530
|
};
|
|
@@ -52514,7 +52602,7 @@ var LineAssignmentDropdown = ({
|
|
|
52514
52602
|
assignedLines.length - 2
|
|
52515
52603
|
] });
|
|
52516
52604
|
};
|
|
52517
|
-
const hasChanges = JSON.stringify(selectedIds.sort()) !== JSON.stringify(currentLineIds.sort());
|
|
52605
|
+
const hasChanges = JSON.stringify([...selectedIds].sort()) !== JSON.stringify([...currentLineIds].sort());
|
|
52518
52606
|
if (!canEdit) {
|
|
52519
52607
|
return /* @__PURE__ */ jsx("div", { className: "text-sm", children: getDisplayText() });
|
|
52520
52608
|
}
|
|
@@ -52544,13 +52632,13 @@ var LineAssignmentDropdown = ({
|
|
|
52544
52632
|
"div",
|
|
52545
52633
|
{
|
|
52546
52634
|
ref: dropdownRef,
|
|
52547
|
-
className: "fixed z-[9999] bg-white rounded-lg shadow-2xl border border-gray-200",
|
|
52635
|
+
className: "fixed z-[9999] bg-white rounded-lg shadow-2xl border border-gray-200 flex flex-col overflow-hidden",
|
|
52548
52636
|
style: {
|
|
52549
|
-
top: `${position.top
|
|
52637
|
+
top: `${position.top}px`,
|
|
52550
52638
|
left: `${position.left}px`,
|
|
52551
|
-
|
|
52552
|
-
|
|
52553
|
-
|
|
52639
|
+
width: `${position.width}px`,
|
|
52640
|
+
maxHeight: `${position.maxHeight}px`,
|
|
52641
|
+
transform: position.openAbove ? "translateY(-100%)" : void 0
|
|
52554
52642
|
},
|
|
52555
52643
|
children: [
|
|
52556
52644
|
/* @__PURE__ */ jsx("div", { className: "px-4 py-3 border-b border-gray-200 bg-gray-50", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
@@ -52567,7 +52655,7 @@ var LineAssignmentDropdown = ({
|
|
|
52567
52655
|
}
|
|
52568
52656
|
)
|
|
52569
52657
|
] }) }),
|
|
52570
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
52658
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto", children: availableLines.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-4 py-8 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "No lines available" }) }) : /* @__PURE__ */ jsx("div", { className: "py-1", children: availableLines.map((line) => /* @__PURE__ */ jsxs(
|
|
52571
52659
|
"label",
|
|
52572
52660
|
{
|
|
52573
52661
|
className: "flex items-center gap-3 px-4 py-2.5 hover:bg-gray-50 cursor-pointer transition-colors",
|
|
@@ -52627,10 +52715,22 @@ var FactoryAssignmentDropdown = ({
|
|
|
52627
52715
|
canEdit,
|
|
52628
52716
|
onUpdate
|
|
52629
52717
|
}) => {
|
|
52718
|
+
const VIEWPORT_MARGIN = 8;
|
|
52719
|
+
const DROPDOWN_GAP = 6;
|
|
52720
|
+
const MIN_DROPDOWN_WIDTH = 300;
|
|
52721
|
+
const MAX_DROPDOWN_WIDTH = 420;
|
|
52722
|
+
const PREFERRED_DROPDOWN_HEIGHT = 420;
|
|
52723
|
+
const MIN_DROPDOWN_HEIGHT = 180;
|
|
52630
52724
|
const [isOpen, setIsOpen] = useState(false);
|
|
52631
52725
|
const [selectedIds, setSelectedIds] = useState(currentFactoryIds);
|
|
52632
52726
|
const [isSaving, setIsSaving] = useState(false);
|
|
52633
|
-
const [position, setPosition] = useState({
|
|
52727
|
+
const [position, setPosition] = useState({
|
|
52728
|
+
top: 0,
|
|
52729
|
+
left: 0,
|
|
52730
|
+
width: MIN_DROPDOWN_WIDTH,
|
|
52731
|
+
maxHeight: PREFERRED_DROPDOWN_HEIGHT,
|
|
52732
|
+
openAbove: false
|
|
52733
|
+
});
|
|
52634
52734
|
const buttonRef = useRef(null);
|
|
52635
52735
|
const dropdownRef = useRef(null);
|
|
52636
52736
|
useEffect(() => {
|
|
@@ -52640,10 +52740,31 @@ var FactoryAssignmentDropdown = ({
|
|
|
52640
52740
|
const updatePosition = () => {
|
|
52641
52741
|
if (isOpen && buttonRef.current) {
|
|
52642
52742
|
const rect = buttonRef.current.getBoundingClientRect();
|
|
52743
|
+
const viewportWidth = window.innerWidth;
|
|
52744
|
+
const viewportHeight = window.innerHeight;
|
|
52745
|
+
const width = Math.min(
|
|
52746
|
+
Math.max(rect.width, MIN_DROPDOWN_WIDTH),
|
|
52747
|
+
Math.min(MAX_DROPDOWN_WIDTH, viewportWidth - VIEWPORT_MARGIN * 2)
|
|
52748
|
+
);
|
|
52749
|
+
const left = Math.min(
|
|
52750
|
+
Math.max(VIEWPORT_MARGIN, rect.left),
|
|
52751
|
+
viewportWidth - width - VIEWPORT_MARGIN
|
|
52752
|
+
);
|
|
52753
|
+
const spaceBelow = viewportHeight - rect.bottom - VIEWPORT_MARGIN - DROPDOWN_GAP;
|
|
52754
|
+
const spaceAbove = rect.top - VIEWPORT_MARGIN - DROPDOWN_GAP;
|
|
52755
|
+
const openAbove = spaceBelow < 260 && spaceAbove > spaceBelow;
|
|
52756
|
+
const availableSpace = openAbove ? spaceAbove : spaceBelow;
|
|
52757
|
+
const maxHeight = Math.max(
|
|
52758
|
+
MIN_DROPDOWN_HEIGHT,
|
|
52759
|
+
Math.min(PREFERRED_DROPDOWN_HEIGHT, availableSpace > 0 ? availableSpace : viewportHeight - VIEWPORT_MARGIN * 2)
|
|
52760
|
+
);
|
|
52761
|
+
const top = openAbove ? rect.top - DROPDOWN_GAP : Math.min(rect.bottom + DROPDOWN_GAP, viewportHeight - VIEWPORT_MARGIN - maxHeight);
|
|
52643
52762
|
setPosition({
|
|
52644
|
-
top
|
|
52645
|
-
left
|
|
52646
|
-
width
|
|
52763
|
+
top,
|
|
52764
|
+
left,
|
|
52765
|
+
width,
|
|
52766
|
+
maxHeight,
|
|
52767
|
+
openAbove
|
|
52647
52768
|
});
|
|
52648
52769
|
}
|
|
52649
52770
|
};
|
|
@@ -52721,7 +52842,7 @@ var FactoryAssignmentDropdown = ({
|
|
|
52721
52842
|
assignedFactories.length - 2
|
|
52722
52843
|
] });
|
|
52723
52844
|
};
|
|
52724
|
-
const hasChanges = JSON.stringify(selectedIds.sort()) !== JSON.stringify(currentFactoryIds.sort());
|
|
52845
|
+
const hasChanges = JSON.stringify([...selectedIds].sort()) !== JSON.stringify([...currentFactoryIds].sort());
|
|
52725
52846
|
if (!canEdit) {
|
|
52726
52847
|
return /* @__PURE__ */ jsx("div", { className: "text-sm", children: getDisplayText() });
|
|
52727
52848
|
}
|
|
@@ -52751,13 +52872,13 @@ var FactoryAssignmentDropdown = ({
|
|
|
52751
52872
|
"div",
|
|
52752
52873
|
{
|
|
52753
52874
|
ref: dropdownRef,
|
|
52754
|
-
className: "fixed z-[9999] bg-white rounded-lg shadow-2xl border border-gray-200",
|
|
52875
|
+
className: "fixed z-[9999] bg-white rounded-lg shadow-2xl border border-gray-200 flex flex-col overflow-hidden",
|
|
52755
52876
|
style: {
|
|
52756
|
-
top: `${position.top
|
|
52877
|
+
top: `${position.top}px`,
|
|
52757
52878
|
left: `${position.left}px`,
|
|
52758
|
-
|
|
52759
|
-
|
|
52760
|
-
|
|
52879
|
+
width: `${position.width}px`,
|
|
52880
|
+
maxHeight: `${position.maxHeight}px`,
|
|
52881
|
+
transform: position.openAbove ? "translateY(-100%)" : void 0
|
|
52761
52882
|
},
|
|
52762
52883
|
children: [
|
|
52763
52884
|
/* @__PURE__ */ jsx("div", { className: "px-4 py-3 border-b border-gray-200 bg-gray-50", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
@@ -52774,7 +52895,7 @@ var FactoryAssignmentDropdown = ({
|
|
|
52774
52895
|
}
|
|
52775
52896
|
)
|
|
52776
52897
|
] }) }),
|
|
52777
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
52898
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto", children: availableFactories.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-4 py-8 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "No factories available" }) }) : /* @__PURE__ */ jsx("div", { className: "py-1", children: availableFactories.map((factory) => /* @__PURE__ */ jsxs(
|
|
52778
52899
|
"label",
|
|
52779
52900
|
{
|
|
52780
52901
|
className: "flex items-center gap-3 px-4 py-2.5 hover:bg-gray-50 cursor-pointer transition-colors",
|
|
@@ -60907,6 +61028,20 @@ var ProfileView = () => {
|
|
|
60907
61028
|
] });
|
|
60908
61029
|
};
|
|
60909
61030
|
var ProfileView_default = ProfileView;
|
|
61031
|
+
|
|
61032
|
+
// src/lib/constants/actions.ts
|
|
61033
|
+
var ACTION_NAMES = {
|
|
61034
|
+
/** Assembly operations */
|
|
61035
|
+
ASSEMBLY: "Assembly",
|
|
61036
|
+
/** Packaging operations */
|
|
61037
|
+
PACKAGING: "Packaging",
|
|
61038
|
+
/** Inspection operations */
|
|
61039
|
+
INSPECTION: "Inspection",
|
|
61040
|
+
/** Testing operations */
|
|
61041
|
+
TESTING: "Testing",
|
|
61042
|
+
/** Quality control operations */
|
|
61043
|
+
QUALITY_CONTROL: "Quality Control"
|
|
61044
|
+
};
|
|
60910
61045
|
var calculateShiftHours = (startTime, endTime, breaks = []) => {
|
|
60911
61046
|
if (!startTime || !endTime) return 8;
|
|
60912
61047
|
const [startHour, startMinute] = startTime.split(":").map(Number);
|
|
@@ -60920,6 +61055,43 @@ var calculateShiftHours = (startTime, endTime, breaks = []) => {
|
|
|
60920
61055
|
const hoursDiff = (endMinutes - startMinutes - totalBreakMinutes) / 60;
|
|
60921
61056
|
return Number(hoursDiff.toFixed(1));
|
|
60922
61057
|
};
|
|
61058
|
+
var calculateBreakDuration2 = (startTime, endTime) => {
|
|
61059
|
+
const [startHour, startMinute] = startTime.split(":").map(Number);
|
|
61060
|
+
const [endHour, endMinute] = endTime.split(":").map(Number);
|
|
61061
|
+
let startMinutes = startHour * 60 + startMinute;
|
|
61062
|
+
let endMinutes = endHour * 60 + endMinute;
|
|
61063
|
+
if (endMinutes < startMinutes) {
|
|
61064
|
+
endMinutes += 24 * 60;
|
|
61065
|
+
}
|
|
61066
|
+
return endMinutes - startMinutes;
|
|
61067
|
+
};
|
|
61068
|
+
var parseBreaksFromDB = (dbBreaks) => {
|
|
61069
|
+
if (!dbBreaks) return [];
|
|
61070
|
+
if (Array.isArray(dbBreaks)) {
|
|
61071
|
+
return dbBreaks.map((breakItem) => ({
|
|
61072
|
+
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
61073
|
+
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
61074
|
+
duration: calculateBreakDuration2(
|
|
61075
|
+
breakItem.start || breakItem.startTime || "00:00",
|
|
61076
|
+
breakItem.end || breakItem.endTime || "00:00"
|
|
61077
|
+
),
|
|
61078
|
+
remarks: breakItem.remarks || breakItem.name || ""
|
|
61079
|
+
}));
|
|
61080
|
+
} else if (dbBreaks.breaks && Array.isArray(dbBreaks.breaks)) {
|
|
61081
|
+
return dbBreaks.breaks.map((breakItem) => ({
|
|
61082
|
+
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
61083
|
+
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
61084
|
+
duration: calculateBreakDuration2(
|
|
61085
|
+
breakItem.start || breakItem.startTime || "00:00",
|
|
61086
|
+
breakItem.end || breakItem.endTime || "00:00"
|
|
61087
|
+
),
|
|
61088
|
+
remarks: breakItem.remarks || breakItem.name || ""
|
|
61089
|
+
}));
|
|
61090
|
+
} else {
|
|
61091
|
+
console.warn("Unexpected breaks format:", dbBreaks);
|
|
61092
|
+
return [];
|
|
61093
|
+
}
|
|
61094
|
+
};
|
|
60923
61095
|
var getStoredLineState = (lineId) => {
|
|
60924
61096
|
try {
|
|
60925
61097
|
return JSON.parse(localStorage.getItem(`line_${lineId}_open`) || "false");
|
|
@@ -60936,6 +61108,7 @@ var formatBreaks = (breaks) => {
|
|
|
60936
61108
|
}))
|
|
60937
61109
|
};
|
|
60938
61110
|
};
|
|
61111
|
+
var SHIFT_HOURS_EPSILON = 1e-3;
|
|
60939
61112
|
var BreakRow = memo$1(({
|
|
60940
61113
|
break: breakItem,
|
|
60941
61114
|
onUpdate,
|
|
@@ -61231,16 +61404,22 @@ var ShiftsView = ({
|
|
|
61231
61404
|
);
|
|
61232
61405
|
const [loading, setLoading] = useState(true);
|
|
61233
61406
|
const [error, setError] = useState(null);
|
|
61407
|
+
const [saveStatusMessages, setSaveStatusMessages] = useState(
|
|
61408
|
+
() => lineIds.reduce((acc, id3) => ({ ...acc, [id3]: null }), {})
|
|
61409
|
+
);
|
|
61234
61410
|
const showToast = useCallback((type, message) => {
|
|
61235
61411
|
if (onToast) {
|
|
61236
|
-
|
|
61237
|
-
|
|
61238
|
-
|
|
61239
|
-
|
|
61240
|
-
} else {
|
|
61241
|
-
toast.error(message);
|
|
61412
|
+
try {
|
|
61413
|
+
onToast(type, message);
|
|
61414
|
+
} catch (error2) {
|
|
61415
|
+
console.warn("[ShiftsView] onToast callback failed, falling back to sonner toast:", error2);
|
|
61242
61416
|
}
|
|
61243
61417
|
}
|
|
61418
|
+
if (type === "success") {
|
|
61419
|
+
toast.success(message);
|
|
61420
|
+
} else {
|
|
61421
|
+
toast.error(message);
|
|
61422
|
+
}
|
|
61244
61423
|
}, [onToast]);
|
|
61245
61424
|
useEffect(() => {
|
|
61246
61425
|
const fetchShiftConfigs = async () => {
|
|
@@ -61391,15 +61570,196 @@ var ShiftsView = ({
|
|
|
61391
61570
|
return config;
|
|
61392
61571
|
}));
|
|
61393
61572
|
}, []);
|
|
61573
|
+
const getOperationalDateForLine = useCallback((lineConfig) => {
|
|
61574
|
+
const sortedShifts = [...lineConfig.shifts || []].sort((a, b) => a.shiftId - b.shiftId);
|
|
61575
|
+
const dayBoundaryStart = sortedShifts[0]?.startTime || "06:00";
|
|
61576
|
+
return getOperationalDate(lineConfig.timezone || "UTC", /* @__PURE__ */ new Date(), dayBoundaryStart);
|
|
61577
|
+
}, []);
|
|
61578
|
+
const recalculateTargetsForShiftHourChanges = useCallback(
|
|
61579
|
+
async ({
|
|
61580
|
+
lineId,
|
|
61581
|
+
lineConfig,
|
|
61582
|
+
previousShiftHours,
|
|
61583
|
+
updatedBy
|
|
61584
|
+
}) => {
|
|
61585
|
+
const primaryOperationalDate = getOperationalDate(lineConfig.timezone || "UTC");
|
|
61586
|
+
const alternateOperationalDate = getOperationalDateForLine(lineConfig);
|
|
61587
|
+
const candidateOperationalDates = primaryOperationalDate === alternateOperationalDate ? [primaryOperationalDate] : [primaryOperationalDate, alternateOperationalDate];
|
|
61588
|
+
let recalculatedCount = 0;
|
|
61589
|
+
const { data: lineRow, error: lineRowError } = await supabase.from("lines").select("factory_id").eq("id", lineId).maybeSingle();
|
|
61590
|
+
if (lineRowError) {
|
|
61591
|
+
throw new Error(`Failed to resolve line factory for target recalculation: ${lineRowError.message}`);
|
|
61592
|
+
}
|
|
61593
|
+
const lineFactoryId = lineRow?.factory_id || null;
|
|
61594
|
+
for (const shift of lineConfig.shifts || []) {
|
|
61595
|
+
const oldShiftHours = previousShiftHours[shift.shiftId];
|
|
61596
|
+
const newShiftHours = calculateShiftHours(shift.startTime, shift.endTime, shift.breaks || []);
|
|
61597
|
+
if (oldShiftHours === void 0) continue;
|
|
61598
|
+
if (!Number.isFinite(newShiftHours) || newShiftHours <= 0) {
|
|
61599
|
+
console.warn(
|
|
61600
|
+
`[ShiftsView] Skipping target recalculation for line ${lineId}, shift ${shift.shiftId} due to invalid new shift hours: ${newShiftHours}`
|
|
61601
|
+
);
|
|
61602
|
+
continue;
|
|
61603
|
+
}
|
|
61604
|
+
if (Math.abs(newShiftHours - oldShiftHours) <= SHIFT_HOURS_EPSILON) continue;
|
|
61605
|
+
let thresholdDateForShift = candidateOperationalDates[0];
|
|
61606
|
+
let currentThresholds = [];
|
|
61607
|
+
for (const candidateDate of candidateOperationalDates) {
|
|
61608
|
+
const { data: thresholdRows, error: thresholdsFetchError } = await supabase.from("action_thresholds").select("line_id, shift_id, action_id, workspace_id, date, pph_threshold, ideal_cycle_time, total_day_output, action_name, sku_id").eq("line_id", lineId).eq("date", candidateDate).eq("shift_id", shift.shiftId);
|
|
61609
|
+
if (thresholdsFetchError) {
|
|
61610
|
+
throw new Error(
|
|
61611
|
+
`Failed to fetch action thresholds for line ${lineId}, shift ${shift.shiftId}: ${thresholdsFetchError.message}`
|
|
61612
|
+
);
|
|
61613
|
+
}
|
|
61614
|
+
if ((thresholdRows || []).length > 0) {
|
|
61615
|
+
thresholdDateForShift = candidateDate;
|
|
61616
|
+
currentThresholds = thresholdRows || [];
|
|
61617
|
+
break;
|
|
61618
|
+
}
|
|
61619
|
+
}
|
|
61620
|
+
if (currentThresholds.length === 0) {
|
|
61621
|
+
continue;
|
|
61622
|
+
}
|
|
61623
|
+
const actionIds = Array.from(
|
|
61624
|
+
new Set(currentThresholds.map((threshold) => threshold.action_id).filter(Boolean))
|
|
61625
|
+
);
|
|
61626
|
+
const actionNameById = /* @__PURE__ */ new Map();
|
|
61627
|
+
if (actionIds.length > 0) {
|
|
61628
|
+
const { data: actionRows, error: actionsError } = await supabase.from("actions").select("id, action_name").in("id", actionIds);
|
|
61629
|
+
if (actionsError) {
|
|
61630
|
+
console.warn(
|
|
61631
|
+
`[ShiftsView] Failed to resolve action names for line ${lineId}, shift ${shift.shiftId}: ${actionsError.message}`
|
|
61632
|
+
);
|
|
61633
|
+
} else {
|
|
61634
|
+
(actionRows || []).forEach((actionRow) => {
|
|
61635
|
+
if (actionRow.id && actionRow.action_name) {
|
|
61636
|
+
actionNameById.set(actionRow.id, actionRow.action_name);
|
|
61637
|
+
}
|
|
61638
|
+
});
|
|
61639
|
+
}
|
|
61640
|
+
}
|
|
61641
|
+
const expandedHours = newShiftHours > oldShiftHours;
|
|
61642
|
+
const recalculatedThresholds = currentThresholds.map((threshold) => {
|
|
61643
|
+
const existingPPH = Number(threshold.pph_threshold) || 0;
|
|
61644
|
+
const existingDayOutput = Number(threshold.total_day_output) || 0;
|
|
61645
|
+
const baselineDayOutput = existingDayOutput > 0 ? existingDayOutput : Math.round(existingPPH * Math.max(oldShiftHours, 0));
|
|
61646
|
+
let nextPPH = existingPPH;
|
|
61647
|
+
let nextDayOutput = existingDayOutput;
|
|
61648
|
+
if (expandedHours) {
|
|
61649
|
+
const pphToKeep = existingPPH > 0 ? existingPPH : oldShiftHours > 0 ? Math.round(baselineDayOutput / oldShiftHours) : 0;
|
|
61650
|
+
nextPPH = pphToKeep;
|
|
61651
|
+
nextDayOutput = Math.round(pphToKeep * newShiftHours);
|
|
61652
|
+
} else {
|
|
61653
|
+
const dayOutputToKeep = baselineDayOutput;
|
|
61654
|
+
nextDayOutput = dayOutputToKeep;
|
|
61655
|
+
nextPPH = newShiftHours > 0 ? Math.round(dayOutputToKeep / newShiftHours) : 0;
|
|
61656
|
+
}
|
|
61657
|
+
const resolvedActionName = (typeof threshold.action_name === "string" && threshold.action_name.trim().length > 0 ? threshold.action_name : actionNameById.get(threshold.action_id)) || ACTION_NAMES.ASSEMBLY;
|
|
61658
|
+
return {
|
|
61659
|
+
line_id: threshold.line_id || lineId,
|
|
61660
|
+
shift_id: shift.shiftId,
|
|
61661
|
+
action_id: threshold.action_id,
|
|
61662
|
+
workspace_id: threshold.workspace_id,
|
|
61663
|
+
date: threshold.date || thresholdDateForShift,
|
|
61664
|
+
pph_threshold: Math.max(0, Math.round(Number.isFinite(nextPPH) ? nextPPH : 0)),
|
|
61665
|
+
ideal_cycle_time: Number(threshold.ideal_cycle_time) || 0,
|
|
61666
|
+
total_day_output: Math.max(0, Math.round(Number.isFinite(nextDayOutput) ? nextDayOutput : 0)),
|
|
61667
|
+
action_name: resolvedActionName,
|
|
61668
|
+
updated_by: updatedBy,
|
|
61669
|
+
...threshold.sku_id ? { sku_id: threshold.sku_id } : {}
|
|
61670
|
+
};
|
|
61671
|
+
});
|
|
61672
|
+
const { error: thresholdsUpsertError } = await supabase.from("action_thresholds").upsert(recalculatedThresholds);
|
|
61673
|
+
if (thresholdsUpsertError) {
|
|
61674
|
+
throw new Error(
|
|
61675
|
+
`Failed to update action thresholds for line ${lineId}, shift ${shift.shiftId}: ${thresholdsUpsertError.message}`
|
|
61676
|
+
);
|
|
61677
|
+
}
|
|
61678
|
+
const packagingActionIds = new Set(
|
|
61679
|
+
Array.from(actionNameById.entries()).filter(([, actionName]) => actionName.toLowerCase() === ACTION_NAMES.PACKAGING.toLowerCase()).map(([actionId]) => actionId)
|
|
61680
|
+
);
|
|
61681
|
+
const packagingThresholds = recalculatedThresholds.filter((threshold) => {
|
|
61682
|
+
if (packagingActionIds.has(threshold.action_id)) return true;
|
|
61683
|
+
return typeof threshold.action_name === "string" && threshold.action_name.toLowerCase() === ACTION_NAMES.PACKAGING.toLowerCase();
|
|
61684
|
+
});
|
|
61685
|
+
const thresholdDayOutput = packagingThresholds.reduce(
|
|
61686
|
+
(sum, threshold) => sum + (Number(threshold.total_day_output) || 0),
|
|
61687
|
+
0
|
|
61688
|
+
);
|
|
61689
|
+
const thresholdPPH = packagingThresholds.reduce(
|
|
61690
|
+
(sum, threshold) => sum + (Number(threshold.pph_threshold) || 0),
|
|
61691
|
+
0
|
|
61692
|
+
);
|
|
61693
|
+
const { data: existingLineThreshold, error: existingLineThresholdError } = await supabase.from("line_thresholds").select("factory_id, product_code, sku_id").eq("line_id", lineId).eq("date", thresholdDateForShift).eq("shift_id", shift.shiftId).maybeSingle();
|
|
61694
|
+
if (existingLineThresholdError) {
|
|
61695
|
+
console.warn(
|
|
61696
|
+
`[ShiftsView] Failed to read existing line threshold for line ${lineId}, shift ${shift.shiftId}: ${existingLineThresholdError.message}`
|
|
61697
|
+
);
|
|
61698
|
+
}
|
|
61699
|
+
const factoryId = existingLineThreshold?.factory_id || lineFactoryId;
|
|
61700
|
+
if (factoryId) {
|
|
61701
|
+
const lineThresholdPayload = {
|
|
61702
|
+
factory_id: factoryId,
|
|
61703
|
+
line_id: lineId,
|
|
61704
|
+
date: thresholdDateForShift,
|
|
61705
|
+
shift_id: shift.shiftId,
|
|
61706
|
+
product_code: existingLineThreshold?.product_code || "",
|
|
61707
|
+
threshold_day_output: thresholdDayOutput,
|
|
61708
|
+
threshold_pph: thresholdPPH
|
|
61709
|
+
};
|
|
61710
|
+
if (existingLineThreshold?.sku_id) {
|
|
61711
|
+
lineThresholdPayload.sku_id = existingLineThreshold.sku_id;
|
|
61712
|
+
}
|
|
61713
|
+
const { error: lineThresholdUpsertError } = await supabase.from("line_thresholds").upsert(lineThresholdPayload, { onConflict: "factory_id,line_id,date,shift_id" });
|
|
61714
|
+
if (lineThresholdUpsertError) {
|
|
61715
|
+
throw new Error(
|
|
61716
|
+
`Failed to update line thresholds for line ${lineId}, shift ${shift.shiftId}: ${lineThresholdUpsertError.message}`
|
|
61717
|
+
);
|
|
61718
|
+
}
|
|
61719
|
+
} else {
|
|
61720
|
+
console.warn(
|
|
61721
|
+
`[ShiftsView] Missing factory_id while updating line thresholds for line ${lineId}, shift ${shift.shiftId}`
|
|
61722
|
+
);
|
|
61723
|
+
}
|
|
61724
|
+
recalculatedCount += recalculatedThresholds.length;
|
|
61725
|
+
}
|
|
61726
|
+
return recalculatedCount;
|
|
61727
|
+
},
|
|
61728
|
+
[getOperationalDateForLine, supabase]
|
|
61729
|
+
);
|
|
61394
61730
|
const handleSaveShifts = useCallback(async (lineId) => {
|
|
61731
|
+
const userId = "6bf6f271-1e55-4a95-9b89-1c3820b58739";
|
|
61395
61732
|
setLineConfigs((prev) => prev.map(
|
|
61396
61733
|
(config) => config.id === lineId ? { ...config, isSaving: true, saveSuccess: false } : config
|
|
61397
61734
|
));
|
|
61735
|
+
setSaveStatusMessages((prev) => ({ ...prev, [lineId]: null }));
|
|
61398
61736
|
try {
|
|
61399
61737
|
const lineConfig = lineConfigs.find((config) => config.id === lineId);
|
|
61400
61738
|
if (!lineConfig) {
|
|
61401
61739
|
throw new Error("Line configuration not found");
|
|
61402
61740
|
}
|
|
61741
|
+
const { data: existingRows, error: existingRowsError } = await supabase.from("line_operating_hours").select("shift_id, start_time, end_time, breaks").eq("line_id", lineId);
|
|
61742
|
+
if (existingRowsError) {
|
|
61743
|
+
throw new Error(`Failed to read existing shift timings: ${existingRowsError.message}`);
|
|
61744
|
+
}
|
|
61745
|
+
const previousShiftHours = (existingRows || []).reduce(
|
|
61746
|
+
(acc, row) => {
|
|
61747
|
+
acc[row.shift_id] = calculateShiftHours(
|
|
61748
|
+
row.start_time,
|
|
61749
|
+
row.end_time,
|
|
61750
|
+
parseBreaksFromDB(row.breaks)
|
|
61751
|
+
);
|
|
61752
|
+
return acc;
|
|
61753
|
+
},
|
|
61754
|
+
{}
|
|
61755
|
+
);
|
|
61756
|
+
const changedShiftCount = (lineConfig.shifts || []).reduce((count, shift) => {
|
|
61757
|
+
const oldShiftHours = previousShiftHours[shift.shiftId];
|
|
61758
|
+
if (oldShiftHours === void 0) return count;
|
|
61759
|
+
const newShiftHours = calculateShiftHours(shift.startTime, shift.endTime, shift.breaks || []);
|
|
61760
|
+
if (!Number.isFinite(newShiftHours)) return count;
|
|
61761
|
+
return Math.abs(newShiftHours - oldShiftHours) > SHIFT_HOURS_EPSILON ? count + 1 : count;
|
|
61762
|
+
}, 0);
|
|
61403
61763
|
const allSavedRows = [];
|
|
61404
61764
|
for (const shift of lineConfig.shifts || []) {
|
|
61405
61765
|
const shiftData = {
|
|
@@ -61421,23 +61781,65 @@ var ShiftsView = ({
|
|
|
61421
61781
|
if (allSavedRows.length > 0) {
|
|
61422
61782
|
shiftConfigStore.setFromOperatingHoursRows(lineId, allSavedRows, shiftConfigStore.get(lineId));
|
|
61423
61783
|
}
|
|
61784
|
+
let recalculatedTargetsCount = 0;
|
|
61785
|
+
try {
|
|
61786
|
+
recalculatedTargetsCount = await recalculateTargetsForShiftHourChanges({
|
|
61787
|
+
lineId,
|
|
61788
|
+
lineConfig,
|
|
61789
|
+
previousShiftHours,
|
|
61790
|
+
updatedBy: userId
|
|
61791
|
+
});
|
|
61792
|
+
} catch (recalcError) {
|
|
61793
|
+
console.error("[ShiftsView] Shift timings were saved but target recalculation failed:", recalcError);
|
|
61794
|
+
showToast("error", "Shift timings saved, but target recalculation failed. Please review targets.");
|
|
61795
|
+
setSaveStatusMessages((prev) => ({
|
|
61796
|
+
...prev,
|
|
61797
|
+
[lineId]: {
|
|
61798
|
+
message: "Shift timings saved, but target recalculation failed. Please review targets.",
|
|
61799
|
+
tone: "error"
|
|
61800
|
+
}
|
|
61801
|
+
}));
|
|
61802
|
+
}
|
|
61424
61803
|
setLineConfigs((prev) => prev.map(
|
|
61425
61804
|
(config) => config.id === lineId ? { ...config, isSaving: false, saveSuccess: true } : config
|
|
61426
61805
|
));
|
|
61427
|
-
|
|
61806
|
+
let successMessage = "Shift configurations saved successfully.";
|
|
61807
|
+
if (changedShiftCount > 0 && recalculatedTargetsCount > 0) {
|
|
61808
|
+
successMessage = `Shift configurations saved. Targets updated successfully for ${recalculatedTargetsCount} workspace${recalculatedTargetsCount === 1 ? "" : "s"}.`;
|
|
61809
|
+
} else if (changedShiftCount > 0) {
|
|
61810
|
+
successMessage = "Shift configurations saved. Target recalculation completed successfully.";
|
|
61811
|
+
}
|
|
61812
|
+
showToast("success", successMessage);
|
|
61813
|
+
setSaveStatusMessages((prev) => ({
|
|
61814
|
+
...prev,
|
|
61815
|
+
[lineId]: {
|
|
61816
|
+
message: successMessage,
|
|
61817
|
+
tone: "success"
|
|
61818
|
+
}
|
|
61819
|
+
}));
|
|
61428
61820
|
setTimeout(() => {
|
|
61429
61821
|
setLineConfigs((prev) => prev.map(
|
|
61430
61822
|
(config) => config.id === lineId ? { ...config, saveSuccess: false } : config
|
|
61431
61823
|
));
|
|
61432
61824
|
}, 3e3);
|
|
61825
|
+
setTimeout(() => {
|
|
61826
|
+
setSaveStatusMessages((prev) => ({ ...prev, [lineId]: null }));
|
|
61827
|
+
}, 7e3);
|
|
61433
61828
|
} catch (error2) {
|
|
61434
61829
|
console.error("Error saving shift configurations:", error2);
|
|
61435
61830
|
showToast("error", "Failed to save shift configurations");
|
|
61831
|
+
setSaveStatusMessages((prev) => ({
|
|
61832
|
+
...prev,
|
|
61833
|
+
[lineId]: {
|
|
61834
|
+
message: "Failed to save shift configurations",
|
|
61835
|
+
tone: "error"
|
|
61836
|
+
}
|
|
61837
|
+
}));
|
|
61436
61838
|
setLineConfigs((prev) => prev.map(
|
|
61437
61839
|
(config) => config.id === lineId ? { ...config, isSaving: false, saveSuccess: false } : config
|
|
61438
61840
|
));
|
|
61439
61841
|
}
|
|
61440
|
-
}, [lineConfigs, supabase, showToast]);
|
|
61842
|
+
}, [lineConfigs, recalculateTargetsForShiftHourChanges, supabase, showToast]);
|
|
61441
61843
|
return /* @__PURE__ */ jsxs("div", { className: `min-h-screen bg-slate-50 ${className}`, children: [
|
|
61442
61844
|
/* @__PURE__ */ jsx("div", { className: "sticky top-0 z-10 bg-white border-b border-gray-200/80 shadow-sm", children: /* @__PURE__ */ jsxs("div", { className: "px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4", children: [
|
|
61443
61845
|
/* @__PURE__ */ jsx("div", { className: "sm:hidden", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
@@ -61492,7 +61894,14 @@ var ShiftsView = ({
|
|
|
61492
61894
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col sm:flex-row items-start sm:items-center gap-2 sm:gap-4 w-full sm:w-auto", children: [
|
|
61493
61895
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
61494
61896
|
config.isSaving && /* @__PURE__ */ jsx("span", { className: "text-xs sm:text-sm font-medium text-blue-600", children: "Saving..." }),
|
|
61495
|
-
config.saveSuccess && /* @__PURE__ */ jsx("span", { className: "text-xs sm:text-sm font-medium text-green-600", children: "Saved!" })
|
|
61897
|
+
config.saveSuccess && /* @__PURE__ */ jsx("span", { className: "text-xs sm:text-sm font-medium text-green-600", children: "Saved!" }),
|
|
61898
|
+
saveStatusMessages[config.id]?.message && /* @__PURE__ */ jsx(
|
|
61899
|
+
"span",
|
|
61900
|
+
{
|
|
61901
|
+
className: `text-xs sm:text-sm font-medium ${saveStatusMessages[config.id]?.tone === "error" ? "text-red-600" : saveStatusMessages[config.id]?.tone === "info" ? "text-blue-600" : "text-green-600"}`,
|
|
61902
|
+
children: saveStatusMessages[config.id]?.message
|
|
61903
|
+
}
|
|
61904
|
+
)
|
|
61496
61905
|
] }),
|
|
61497
61906
|
/* @__PURE__ */ jsxs(
|
|
61498
61907
|
"button",
|
|
@@ -61549,20 +61958,6 @@ var ShiftsView = ({
|
|
|
61549
61958
|
var AuthenticatedShiftsView = withAuth(React26__default.memo(ShiftsView));
|
|
61550
61959
|
var ShiftsView_default = ShiftsView;
|
|
61551
61960
|
|
|
61552
|
-
// src/lib/constants/actions.ts
|
|
61553
|
-
var ACTION_NAMES = {
|
|
61554
|
-
/** Assembly operations */
|
|
61555
|
-
ASSEMBLY: "Assembly",
|
|
61556
|
-
/** Packaging operations */
|
|
61557
|
-
PACKAGING: "Packaging",
|
|
61558
|
-
/** Inspection operations */
|
|
61559
|
-
INSPECTION: "Inspection",
|
|
61560
|
-
/** Testing operations */
|
|
61561
|
-
TESTING: "Testing",
|
|
61562
|
-
/** Quality control operations */
|
|
61563
|
-
QUALITY_CONTROL: "Quality Control"
|
|
61564
|
-
};
|
|
61565
|
-
|
|
61566
61961
|
// src/views/TargetsView.utils.ts
|
|
61567
61962
|
var calculatePPH = (cycleTime, breaks = [], shiftHours = 0) => {
|
|
61568
61963
|
if (cycleTime === "" || cycleTime === 0) return "";
|
|
@@ -65979,7 +66374,7 @@ var TeamManagementView = ({
|
|
|
65979
66374
|
optifye: 0
|
|
65980
66375
|
});
|
|
65981
66376
|
const [isAddUserDialogOpen, setIsAddUserDialogOpen] = useState(false);
|
|
65982
|
-
const canAddUsers = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "optifye";
|
|
66377
|
+
const canAddUsers = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "plant_head" || user?.role_level === "optifye";
|
|
65983
66378
|
const canViewUsageStats = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "optifye";
|
|
65984
66379
|
const companyIdForUsage = entityConfig?.companyId || user?.properties?.company_id;
|
|
65985
66380
|
const usageDateRange = useMemo(() => {
|