@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.js
CHANGED
|
@@ -30328,37 +30328,13 @@ var HourlyOutputChartComponent = ({
|
|
|
30328
30328
|
}, []);
|
|
30329
30329
|
const xAxisConfig = React26__namespace.default.useMemo(() => {
|
|
30330
30330
|
if (containerWidth >= 960) {
|
|
30331
|
-
return { interval: 0, angle: -45, height: 92, tickFont: 10, tickMargin: 12
|
|
30331
|
+
return { interval: 0, angle: -45, height: 92, tickFont: 10, tickMargin: 12 };
|
|
30332
30332
|
}
|
|
30333
30333
|
if (containerWidth >= 760) {
|
|
30334
|
-
return { interval: 0, angle: -35, height: 76, tickFont: 9, tickMargin: 8
|
|
30334
|
+
return { interval: 0, angle: -35, height: 76, tickFont: 9, tickMargin: 8 };
|
|
30335
30335
|
}
|
|
30336
|
-
return { interval: 0, angle: -30, height: 64, tickFont: 9, tickMargin: 6
|
|
30336
|
+
return { interval: 0, angle: -30, height: 64, tickFont: 9, tickMargin: 6 };
|
|
30337
30337
|
}, [containerWidth]);
|
|
30338
|
-
const formatXAxisTick = React26__namespace.default.useCallback((raw) => {
|
|
30339
|
-
const label = typeof raw === "string" ? raw : String(raw);
|
|
30340
|
-
if (xAxisConfig.labelMode === "full") return label;
|
|
30341
|
-
const parts = label.split("-");
|
|
30342
|
-
if (parts.length !== 2) return label;
|
|
30343
|
-
const parsePart = (part) => {
|
|
30344
|
-
const match = part.match(/^(\d{1,2})(?::(\d{2}))?(AM|PM)$/);
|
|
30345
|
-
if (!match) return null;
|
|
30346
|
-
const [, hh, mm, meridiem] = match;
|
|
30347
|
-
const time2 = mm ? `${hh}:${mm}` : hh;
|
|
30348
|
-
const merShort = meridiem === "AM" ? "A" : "P";
|
|
30349
|
-
return { time: time2, meridiem, merShort };
|
|
30350
|
-
};
|
|
30351
|
-
const start = parsePart(parts[0]);
|
|
30352
|
-
const end = parsePart(parts[1]);
|
|
30353
|
-
if (!start || !end) return label;
|
|
30354
|
-
if (xAxisConfig.labelMode === "start") {
|
|
30355
|
-
return `${start.time}${start.merShort}`;
|
|
30356
|
-
}
|
|
30357
|
-
if (start.meridiem === end.meridiem) {
|
|
30358
|
-
return `${start.time}-${end.time}${end.merShort}`;
|
|
30359
|
-
}
|
|
30360
|
-
return `${start.time}${start.merShort}-${end.time}${end.merShort}`;
|
|
30361
|
-
}, [xAxisConfig.labelMode]);
|
|
30362
30338
|
const formatHour = React26__namespace.default.useCallback((hourIndex) => {
|
|
30363
30339
|
const isLastHour = hourIndex === SHIFT_DURATION - 1;
|
|
30364
30340
|
const startDecimalHour = shiftStartTime.decimalHour + hourIndex;
|
|
@@ -30568,7 +30544,7 @@ var HourlyOutputChartComponent = ({
|
|
|
30568
30544
|
right: 10,
|
|
30569
30545
|
bottom: 10,
|
|
30570
30546
|
// Small bottom margin
|
|
30571
|
-
left:
|
|
30547
|
+
left: 6
|
|
30572
30548
|
},
|
|
30573
30549
|
barCategoryGap: "25%",
|
|
30574
30550
|
children: [
|
|
@@ -30582,8 +30558,7 @@ var HourlyOutputChartComponent = ({
|
|
|
30582
30558
|
angle: xAxisConfig.angle,
|
|
30583
30559
|
textAnchor: "end",
|
|
30584
30560
|
tickMargin: xAxisConfig.tickMargin,
|
|
30585
|
-
height: xAxisConfig.height
|
|
30586
|
-
tickFormatter: formatXAxisTick
|
|
30561
|
+
height: xAxisConfig.height
|
|
30587
30562
|
}
|
|
30588
30563
|
),
|
|
30589
30564
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -30591,7 +30566,7 @@ var HourlyOutputChartComponent = ({
|
|
|
30591
30566
|
{
|
|
30592
30567
|
yAxisId: "default",
|
|
30593
30568
|
tickMargin: 8,
|
|
30594
|
-
width:
|
|
30569
|
+
width: 48,
|
|
30595
30570
|
domain: [0, maxYValue],
|
|
30596
30571
|
ticks: generateYAxisTicks(),
|
|
30597
30572
|
tickFormatter: (value) => value,
|
|
@@ -30600,7 +30575,7 @@ var HourlyOutputChartComponent = ({
|
|
|
30600
30575
|
return /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(${x},${y})`, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
30601
30576
|
"text",
|
|
30602
30577
|
{
|
|
30603
|
-
x:
|
|
30578
|
+
x: -2,
|
|
30604
30579
|
y: 0,
|
|
30605
30580
|
dy: 4,
|
|
30606
30581
|
textAnchor: "end",
|
|
@@ -51479,13 +51454,31 @@ var InviteUserDialog = ({
|
|
|
51479
51454
|
const [selectedRole, setSelectedRole] = React26.useState("supervisor");
|
|
51480
51455
|
const [selectedLines, setSelectedLines] = React26.useState([]);
|
|
51481
51456
|
const [selectedFactories, setSelectedFactories] = React26.useState([]);
|
|
51457
|
+
const [lineSearch, setLineSearch] = React26.useState("");
|
|
51458
|
+
const [factorySearch, setFactorySearch] = React26.useState("");
|
|
51482
51459
|
const [isSubmitting, setIsSubmitting] = React26.useState(false);
|
|
51483
51460
|
const [error, setError] = React26.useState(null);
|
|
51484
|
-
const [isLinesDropdownOpen, setIsLinesDropdownOpen] = React26.useState(false);
|
|
51485
|
-
const [isFactoriesDropdownOpen, setIsFactoriesDropdownOpen] = React26.useState(false);
|
|
51486
51461
|
const canInviteIT = user?.role_level === "owner" || user?.role_level === "optifye";
|
|
51487
51462
|
const canInvitePlantHead = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "optifye";
|
|
51488
51463
|
const canInviteSupervisor = ["owner", "it", "plant_head", "optifye"].includes(user?.role_level || "");
|
|
51464
|
+
const filteredLines = React26.useMemo(() => {
|
|
51465
|
+
const search = lineSearch.trim().toLowerCase();
|
|
51466
|
+
if (!search) return availableLines;
|
|
51467
|
+
return availableLines.filter((line) => line.name.toLowerCase().includes(search));
|
|
51468
|
+
}, [availableLines, lineSearch]);
|
|
51469
|
+
const filteredFactories = React26.useMemo(() => {
|
|
51470
|
+
const search = factorySearch.trim().toLowerCase();
|
|
51471
|
+
if (!search) return availableFactories;
|
|
51472
|
+
return availableFactories.filter((factory) => factory.name.toLowerCase().includes(search));
|
|
51473
|
+
}, [availableFactories, factorySearch]);
|
|
51474
|
+
const selectedLineItems = React26.useMemo(
|
|
51475
|
+
() => availableLines.filter((line) => selectedLines.includes(line.id)),
|
|
51476
|
+
[availableLines, selectedLines]
|
|
51477
|
+
);
|
|
51478
|
+
const selectedFactoryItems = React26.useMemo(
|
|
51479
|
+
() => availableFactories.filter((factory) => selectedFactories.includes(factory.id)),
|
|
51480
|
+
[availableFactories, selectedFactories]
|
|
51481
|
+
);
|
|
51489
51482
|
React26.useEffect(() => {
|
|
51490
51483
|
if (!isOpen) {
|
|
51491
51484
|
setEmail("");
|
|
@@ -51494,9 +51487,9 @@ var InviteUserDialog = ({
|
|
|
51494
51487
|
setSelectedRole("supervisor");
|
|
51495
51488
|
setSelectedLines([]);
|
|
51496
51489
|
setSelectedFactories([]);
|
|
51490
|
+
setLineSearch("");
|
|
51491
|
+
setFactorySearch("");
|
|
51497
51492
|
setError(null);
|
|
51498
|
-
setIsLinesDropdownOpen(false);
|
|
51499
|
-
setIsFactoriesDropdownOpen(false);
|
|
51500
51493
|
}
|
|
51501
51494
|
}, [isOpen]);
|
|
51502
51495
|
const validateEmail = (email2) => {
|
|
@@ -51565,7 +51558,6 @@ var InviteUserDialog = ({
|
|
|
51565
51558
|
try {
|
|
51566
51559
|
const dashboardUrl = typeof window !== "undefined" ? window.location.origin : "";
|
|
51567
51560
|
if (dashboardUrl) {
|
|
51568
|
-
console.log("Sending welcome email to:", email.trim());
|
|
51569
51561
|
const { data: emailData, error: emailError } = await supabase.functions.invoke("hyper-service", {
|
|
51570
51562
|
body: {
|
|
51571
51563
|
action: "send-welcome-email",
|
|
@@ -51578,13 +51570,8 @@ var InviteUserDialog = ({
|
|
|
51578
51570
|
console.error("Failed to send welcome email:", emailError);
|
|
51579
51571
|
sonner.toast.warning("User added successfully, but welcome email could not be sent");
|
|
51580
51572
|
} else if (emailData?.success) {
|
|
51581
|
-
console.log("Welcome email sent successfully:", emailData);
|
|
51582
51573
|
sonner.toast.success("User added and welcome email sent!");
|
|
51583
|
-
} else {
|
|
51584
|
-
console.log("Welcome email response:", emailData);
|
|
51585
51574
|
}
|
|
51586
|
-
} else {
|
|
51587
|
-
console.warn("Dashboard URL not available, skipping welcome email");
|
|
51588
51575
|
}
|
|
51589
51576
|
} catch (emailErr) {
|
|
51590
51577
|
console.error("Error sending welcome email:", emailErr);
|
|
@@ -51610,7 +51597,7 @@ var InviteUserDialog = ({
|
|
|
51610
51597
|
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
51611
51598
|
"div",
|
|
51612
51599
|
{
|
|
51613
|
-
className: "bg-white rounded-xl shadow-2xl max-w-
|
|
51600
|
+
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",
|
|
51614
51601
|
onClick: (e) => e.stopPropagation(),
|
|
51615
51602
|
children: [
|
|
51616
51603
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between p-6 border-b border-gray-200", children: [
|
|
@@ -51638,7 +51625,7 @@ var InviteUserDialog = ({
|
|
|
51638
51625
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: error })
|
|
51639
51626
|
] }),
|
|
51640
51627
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-5", children: [
|
|
51641
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
51628
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4", children: [
|
|
51642
51629
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
51643
51630
|
/* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: "firstName", className: "block text-sm font-medium text-gray-700 mb-2", children: [
|
|
51644
51631
|
"First Name ",
|
|
@@ -51797,124 +51784,192 @@ var InviteUserDialog = ({
|
|
|
51797
51784
|
)
|
|
51798
51785
|
] })
|
|
51799
51786
|
] }),
|
|
51800
|
-
selectedRole === "plant_head" &&
|
|
51801
|
-
/* @__PURE__ */ jsxRuntime.jsxs("
|
|
51802
|
-
/* @__PURE__ */ jsxRuntime.
|
|
51803
|
-
|
|
51787
|
+
selectedRole === "plant_head" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-xl border border-slate-200 bg-slate-50 p-4 space-y-3", children: [
|
|
51788
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
51789
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
51790
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm font-medium text-gray-800 flex items-center gap-1.5", children: [
|
|
51791
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Building2, { className: "w-4 h-4 text-blue-600" }),
|
|
51792
|
+
"Factory Access"
|
|
51793
|
+
] }),
|
|
51794
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 mt-1", children: "Choose the factories this plant head will manage." })
|
|
51795
|
+
] }),
|
|
51796
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium bg-white border border-gray-200 text-gray-600 rounded-full px-2 py-1", children: [
|
|
51797
|
+
selectedFactories.length,
|
|
51798
|
+
" selected"
|
|
51799
|
+
] })
|
|
51804
51800
|
] }),
|
|
51805
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
51806
|
-
|
|
51807
|
-
|
|
51808
|
-
|
|
51801
|
+
availableFactories.length === 0 ? /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
51802
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
51803
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51804
|
+
"button",
|
|
51805
|
+
{
|
|
51806
|
+
type: "button",
|
|
51807
|
+
onClick: () => setSelectedFactories(availableFactories.map((factory) => factory.id)),
|
|
51808
|
+
className: "px-2.5 py-1 text-xs font-medium text-blue-700 bg-blue-100 rounded-md hover:bg-blue-200 transition-colors",
|
|
51809
|
+
disabled: isSubmitting,
|
|
51810
|
+
children: "Select all"
|
|
51811
|
+
}
|
|
51812
|
+
),
|
|
51813
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51814
|
+
"button",
|
|
51815
|
+
{
|
|
51816
|
+
type: "button",
|
|
51817
|
+
onClick: () => setSelectedFactories([]),
|
|
51818
|
+
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",
|
|
51819
|
+
disabled: isSubmitting || selectedFactories.length === 0,
|
|
51820
|
+
children: "Clear"
|
|
51821
|
+
}
|
|
51822
|
+
)
|
|
51823
|
+
] }),
|
|
51824
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
51825
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "w-4 h-4 text-gray-400 absolute left-3 top-1/2 -translate-y-1/2" }),
|
|
51826
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51827
|
+
"input",
|
|
51828
|
+
{
|
|
51829
|
+
type: "text",
|
|
51830
|
+
value: factorySearch,
|
|
51831
|
+
onChange: (e) => setFactorySearch(e.target.value),
|
|
51832
|
+
placeholder: "Search factories",
|
|
51833
|
+
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",
|
|
51834
|
+
disabled: isSubmitting
|
|
51835
|
+
}
|
|
51836
|
+
)
|
|
51837
|
+
] }),
|
|
51838
|
+
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("p", { className: "px-4 py-6 text-sm text-gray-500 text-center", children: "No factories match your search." }) : filteredFactories.map((factory) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
51839
|
+
"label",
|
|
51809
51840
|
{
|
|
51810
|
-
|
|
51811
|
-
|
|
51812
|
-
|
|
51813
|
-
|
|
51814
|
-
|
|
51815
|
-
selectedFactories.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-500", children: "Select factories..." }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5 flex-1", children: selectedFactories.map((factoryId) => {
|
|
51816
|
-
const factory = availableFactories.find((f) => f.id === factoryId);
|
|
51817
|
-
return factory ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
51818
|
-
"span",
|
|
51841
|
+
className: "flex items-center justify-between gap-3 px-4 py-2.5 hover:bg-gray-50 cursor-pointer",
|
|
51842
|
+
children: [
|
|
51843
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
|
|
51844
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51845
|
+
"input",
|
|
51819
51846
|
{
|
|
51820
|
-
|
|
51821
|
-
|
|
51822
|
-
|
|
51823
|
-
|
|
51824
|
-
|
|
51825
|
-
|
|
51826
|
-
|
|
51827
|
-
|
|
51828
|
-
|
|
51829
|
-
|
|
51830
|
-
|
|
51831
|
-
|
|
51832
|
-
|
|
51833
|
-
|
|
51834
|
-
|
|
51835
|
-
|
|
51836
|
-
]
|
|
51837
|
-
},
|
|
51838
|
-
factory.id
|
|
51839
|
-
) : null;
|
|
51840
|
-
}) }),
|
|
51841
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: cn("w-4 h-4 text-gray-400 transition-transform flex-shrink-0", isFactoriesDropdownOpen && "rotate-180") })
|
|
51842
|
-
] })
|
|
51843
|
-
}
|
|
51844
|
-
),
|
|
51845
|
-
isFactoriesDropdownOpen && /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs(
|
|
51846
|
-
"div",
|
|
51847
|
+
type: "checkbox",
|
|
51848
|
+
checked: selectedFactories.includes(factory.id),
|
|
51849
|
+
onChange: () => toggleFactorySelection(factory.id),
|
|
51850
|
+
className: "h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500",
|
|
51851
|
+
disabled: isSubmitting
|
|
51852
|
+
}
|
|
51853
|
+
),
|
|
51854
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-800 truncate", children: factory.name })
|
|
51855
|
+
] }),
|
|
51856
|
+
selectedFactories.includes(factory.id) && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-4 h-4 text-blue-600 flex-shrink-0" })
|
|
51857
|
+
]
|
|
51858
|
+
},
|
|
51859
|
+
factory.id
|
|
51860
|
+
)) }),
|
|
51861
|
+
selectedFactoryItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5", children: selectedFactoryItems.map((factory) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
51862
|
+
"span",
|
|
51847
51863
|
{
|
|
51848
|
-
|
|
51849
|
-
className: cn(
|
|
51850
|
-
"px-4 py-2.5 cursor-pointer flex items-center justify-between hover:bg-gray-50 transition-colors",
|
|
51851
|
-
selectedFactories.includes(factory.id) && "bg-blue-50"
|
|
51852
|
-
),
|
|
51864
|
+
className: "inline-flex items-center gap-1 px-2 py-1 bg-blue-100 text-blue-700 text-xs rounded-md font-medium",
|
|
51853
51865
|
children: [
|
|
51854
|
-
|
|
51855
|
-
|
|
51866
|
+
factory.name,
|
|
51867
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51868
|
+
"button",
|
|
51869
|
+
{
|
|
51870
|
+
type: "button",
|
|
51871
|
+
onClick: () => toggleFactorySelection(factory.id),
|
|
51872
|
+
className: "hover:bg-blue-200 rounded-sm transition-colors",
|
|
51873
|
+
disabled: isSubmitting,
|
|
51874
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-3 h-3" })
|
|
51875
|
+
}
|
|
51876
|
+
)
|
|
51856
51877
|
]
|
|
51857
51878
|
},
|
|
51858
51879
|
factory.id
|
|
51859
51880
|
)) })
|
|
51860
51881
|
] })
|
|
51861
51882
|
] }),
|
|
51862
|
-
selectedRole === "supervisor" &&
|
|
51863
|
-
/* @__PURE__ */ jsxRuntime.jsxs("
|
|
51864
|
-
/* @__PURE__ */ jsxRuntime.
|
|
51865
|
-
|
|
51883
|
+
selectedRole === "supervisor" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-xl border border-slate-200 bg-slate-50 p-4 space-y-3", children: [
|
|
51884
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
51885
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
51886
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm font-medium text-gray-800 flex items-center gap-1.5", children: [
|
|
51887
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Users, { className: "w-4 h-4 text-blue-600" }),
|
|
51888
|
+
"Line Access"
|
|
51889
|
+
] }),
|
|
51890
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 mt-1", children: "Choose the lines this supervisor will monitor." })
|
|
51891
|
+
] }),
|
|
51892
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium bg-white border border-gray-200 text-gray-600 rounded-full px-2 py-1", children: [
|
|
51893
|
+
selectedLines.length,
|
|
51894
|
+
" selected"
|
|
51895
|
+
] })
|
|
51866
51896
|
] }),
|
|
51867
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
51868
|
-
|
|
51869
|
-
|
|
51870
|
-
|
|
51897
|
+
availableLines.length === 0 ? /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
51898
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
51899
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51900
|
+
"button",
|
|
51901
|
+
{
|
|
51902
|
+
type: "button",
|
|
51903
|
+
onClick: () => setSelectedLines(availableLines.map((line) => line.id)),
|
|
51904
|
+
className: "px-2.5 py-1 text-xs font-medium text-blue-700 bg-blue-100 rounded-md hover:bg-blue-200 transition-colors",
|
|
51905
|
+
disabled: isSubmitting,
|
|
51906
|
+
children: "Select all"
|
|
51907
|
+
}
|
|
51908
|
+
),
|
|
51909
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51910
|
+
"button",
|
|
51911
|
+
{
|
|
51912
|
+
type: "button",
|
|
51913
|
+
onClick: () => setSelectedLines([]),
|
|
51914
|
+
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",
|
|
51915
|
+
disabled: isSubmitting || selectedLines.length === 0,
|
|
51916
|
+
children: "Clear"
|
|
51917
|
+
}
|
|
51918
|
+
)
|
|
51919
|
+
] }),
|
|
51920
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
51921
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "w-4 h-4 text-gray-400 absolute left-3 top-1/2 -translate-y-1/2" }),
|
|
51922
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51923
|
+
"input",
|
|
51924
|
+
{
|
|
51925
|
+
type: "text",
|
|
51926
|
+
value: lineSearch,
|
|
51927
|
+
onChange: (e) => setLineSearch(e.target.value),
|
|
51928
|
+
placeholder: "Search lines",
|
|
51929
|
+
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",
|
|
51930
|
+
disabled: isSubmitting
|
|
51931
|
+
}
|
|
51932
|
+
)
|
|
51933
|
+
] }),
|
|
51934
|
+
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("p", { className: "px-4 py-6 text-sm text-gray-500 text-center", children: "No lines match your search." }) : filteredLines.map((line) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
51935
|
+
"label",
|
|
51871
51936
|
{
|
|
51872
|
-
|
|
51873
|
-
|
|
51874
|
-
|
|
51875
|
-
|
|
51876
|
-
|
|
51877
|
-
selectedLines.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-500", children: "Select lines..." }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5 flex-1", children: selectedLines.map((lineId) => {
|
|
51878
|
-
const line = availableLines.find((l) => l.id === lineId);
|
|
51879
|
-
return line ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
51880
|
-
"span",
|
|
51937
|
+
className: "flex items-center justify-between gap-3 px-4 py-2.5 hover:bg-gray-50 cursor-pointer",
|
|
51938
|
+
children: [
|
|
51939
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
|
|
51940
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51941
|
+
"input",
|
|
51881
51942
|
{
|
|
51882
|
-
|
|
51883
|
-
|
|
51884
|
-
|
|
51885
|
-
|
|
51886
|
-
|
|
51887
|
-
|
|
51888
|
-
|
|
51889
|
-
|
|
51890
|
-
|
|
51891
|
-
|
|
51892
|
-
|
|
51893
|
-
|
|
51894
|
-
|
|
51895
|
-
|
|
51896
|
-
|
|
51897
|
-
|
|
51898
|
-
]
|
|
51899
|
-
},
|
|
51900
|
-
line.id
|
|
51901
|
-
) : null;
|
|
51902
|
-
}) }),
|
|
51903
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: cn("w-4 h-4 text-gray-400 transition-transform flex-shrink-0", isLinesDropdownOpen && "rotate-180") })
|
|
51904
|
-
] })
|
|
51905
|
-
}
|
|
51906
|
-
),
|
|
51907
|
-
isLinesDropdownOpen && /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs(
|
|
51908
|
-
"div",
|
|
51943
|
+
type: "checkbox",
|
|
51944
|
+
checked: selectedLines.includes(line.id),
|
|
51945
|
+
onChange: () => toggleLineSelection(line.id),
|
|
51946
|
+
className: "h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500",
|
|
51947
|
+
disabled: isSubmitting
|
|
51948
|
+
}
|
|
51949
|
+
),
|
|
51950
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-800 truncate", children: line.name })
|
|
51951
|
+
] }),
|
|
51952
|
+
selectedLines.includes(line.id) && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "w-4 h-4 text-blue-600 flex-shrink-0" })
|
|
51953
|
+
]
|
|
51954
|
+
},
|
|
51955
|
+
line.id
|
|
51956
|
+
)) }),
|
|
51957
|
+
selectedLineItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5", children: selectedLineItems.map((line) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
51958
|
+
"span",
|
|
51909
51959
|
{
|
|
51910
|
-
|
|
51911
|
-
className: cn(
|
|
51912
|
-
"px-4 py-2.5 cursor-pointer flex items-center justify-between hover:bg-gray-50 transition-colors",
|
|
51913
|
-
selectedLines.includes(line.id) && "bg-blue-50"
|
|
51914
|
-
),
|
|
51960
|
+
className: "inline-flex items-center gap-1 px-2 py-1 bg-blue-100 text-blue-700 text-xs rounded-md font-medium",
|
|
51915
51961
|
children: [
|
|
51916
|
-
|
|
51917
|
-
|
|
51962
|
+
line.name,
|
|
51963
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51964
|
+
"button",
|
|
51965
|
+
{
|
|
51966
|
+
type: "button",
|
|
51967
|
+
onClick: () => toggleLineSelection(line.id),
|
|
51968
|
+
className: "hover:bg-blue-200 rounded-sm transition-colors",
|
|
51969
|
+
disabled: isSubmitting,
|
|
51970
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-3 h-3" })
|
|
51971
|
+
}
|
|
51972
|
+
)
|
|
51918
51973
|
]
|
|
51919
51974
|
},
|
|
51920
51975
|
line.id
|
|
@@ -52449,10 +52504,22 @@ var LineAssignmentDropdown = ({
|
|
|
52449
52504
|
canEdit,
|
|
52450
52505
|
onUpdate
|
|
52451
52506
|
}) => {
|
|
52507
|
+
const VIEWPORT_MARGIN = 8;
|
|
52508
|
+
const DROPDOWN_GAP = 6;
|
|
52509
|
+
const MIN_DROPDOWN_WIDTH = 300;
|
|
52510
|
+
const MAX_DROPDOWN_WIDTH = 420;
|
|
52511
|
+
const PREFERRED_DROPDOWN_HEIGHT = 420;
|
|
52512
|
+
const MIN_DROPDOWN_HEIGHT = 180;
|
|
52452
52513
|
const [isOpen, setIsOpen] = React26.useState(false);
|
|
52453
52514
|
const [selectedIds, setSelectedIds] = React26.useState(currentLineIds);
|
|
52454
52515
|
const [isSaving, setIsSaving] = React26.useState(false);
|
|
52455
|
-
const [position, setPosition] = React26.useState({
|
|
52516
|
+
const [position, setPosition] = React26.useState({
|
|
52517
|
+
top: 0,
|
|
52518
|
+
left: 0,
|
|
52519
|
+
width: MIN_DROPDOWN_WIDTH,
|
|
52520
|
+
maxHeight: PREFERRED_DROPDOWN_HEIGHT,
|
|
52521
|
+
openAbove: false
|
|
52522
|
+
});
|
|
52456
52523
|
const buttonRef = React26.useRef(null);
|
|
52457
52524
|
const dropdownRef = React26.useRef(null);
|
|
52458
52525
|
React26.useEffect(() => {
|
|
@@ -52462,10 +52529,31 @@ var LineAssignmentDropdown = ({
|
|
|
52462
52529
|
const updatePosition = () => {
|
|
52463
52530
|
if (isOpen && buttonRef.current) {
|
|
52464
52531
|
const rect = buttonRef.current.getBoundingClientRect();
|
|
52532
|
+
const viewportWidth = window.innerWidth;
|
|
52533
|
+
const viewportHeight = window.innerHeight;
|
|
52534
|
+
const width = Math.min(
|
|
52535
|
+
Math.max(rect.width, MIN_DROPDOWN_WIDTH),
|
|
52536
|
+
Math.min(MAX_DROPDOWN_WIDTH, viewportWidth - VIEWPORT_MARGIN * 2)
|
|
52537
|
+
);
|
|
52538
|
+
const left = Math.min(
|
|
52539
|
+
Math.max(VIEWPORT_MARGIN, rect.left),
|
|
52540
|
+
viewportWidth - width - VIEWPORT_MARGIN
|
|
52541
|
+
);
|
|
52542
|
+
const spaceBelow = viewportHeight - rect.bottom - VIEWPORT_MARGIN - DROPDOWN_GAP;
|
|
52543
|
+
const spaceAbove = rect.top - VIEWPORT_MARGIN - DROPDOWN_GAP;
|
|
52544
|
+
const openAbove = spaceBelow < 260 && spaceAbove > spaceBelow;
|
|
52545
|
+
const availableSpace = openAbove ? spaceAbove : spaceBelow;
|
|
52546
|
+
const maxHeight = Math.max(
|
|
52547
|
+
MIN_DROPDOWN_HEIGHT,
|
|
52548
|
+
Math.min(PREFERRED_DROPDOWN_HEIGHT, availableSpace > 0 ? availableSpace : viewportHeight - VIEWPORT_MARGIN * 2)
|
|
52549
|
+
);
|
|
52550
|
+
const top = openAbove ? rect.top - DROPDOWN_GAP : Math.min(rect.bottom + DROPDOWN_GAP, viewportHeight - VIEWPORT_MARGIN - maxHeight);
|
|
52465
52551
|
setPosition({
|
|
52466
|
-
top
|
|
52467
|
-
left
|
|
52468
|
-
width
|
|
52552
|
+
top,
|
|
52553
|
+
left,
|
|
52554
|
+
width,
|
|
52555
|
+
maxHeight,
|
|
52556
|
+
openAbove
|
|
52469
52557
|
});
|
|
52470
52558
|
}
|
|
52471
52559
|
};
|
|
@@ -52543,7 +52631,7 @@ var LineAssignmentDropdown = ({
|
|
|
52543
52631
|
assignedLines.length - 2
|
|
52544
52632
|
] });
|
|
52545
52633
|
};
|
|
52546
|
-
const hasChanges = JSON.stringify(selectedIds.sort()) !== JSON.stringify(currentLineIds.sort());
|
|
52634
|
+
const hasChanges = JSON.stringify([...selectedIds].sort()) !== JSON.stringify([...currentLineIds].sort());
|
|
52547
52635
|
if (!canEdit) {
|
|
52548
52636
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm", children: getDisplayText() });
|
|
52549
52637
|
}
|
|
@@ -52573,13 +52661,13 @@ var LineAssignmentDropdown = ({
|
|
|
52573
52661
|
"div",
|
|
52574
52662
|
{
|
|
52575
52663
|
ref: dropdownRef,
|
|
52576
|
-
className: "fixed z-[9999] bg-white rounded-lg shadow-2xl border border-gray-200",
|
|
52664
|
+
className: "fixed z-[9999] bg-white rounded-lg shadow-2xl border border-gray-200 flex flex-col overflow-hidden",
|
|
52577
52665
|
style: {
|
|
52578
|
-
top: `${position.top
|
|
52666
|
+
top: `${position.top}px`,
|
|
52579
52667
|
left: `${position.left}px`,
|
|
52580
|
-
|
|
52581
|
-
|
|
52582
|
-
|
|
52668
|
+
width: `${position.width}px`,
|
|
52669
|
+
maxHeight: `${position.maxHeight}px`,
|
|
52670
|
+
transform: position.openAbove ? "translateY(-100%)" : void 0
|
|
52583
52671
|
},
|
|
52584
52672
|
children: [
|
|
52585
52673
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 border-b border-gray-200 bg-gray-50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
@@ -52596,7 +52684,7 @@ var LineAssignmentDropdown = ({
|
|
|
52596
52684
|
}
|
|
52597
52685
|
)
|
|
52598
52686
|
] }) }),
|
|
52599
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "
|
|
52687
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto", children: availableLines.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-8 text-center", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "No lines available" }) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-1", children: availableLines.map((line) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
52600
52688
|
"label",
|
|
52601
52689
|
{
|
|
52602
52690
|
className: "flex items-center gap-3 px-4 py-2.5 hover:bg-gray-50 cursor-pointer transition-colors",
|
|
@@ -52656,10 +52744,22 @@ var FactoryAssignmentDropdown = ({
|
|
|
52656
52744
|
canEdit,
|
|
52657
52745
|
onUpdate
|
|
52658
52746
|
}) => {
|
|
52747
|
+
const VIEWPORT_MARGIN = 8;
|
|
52748
|
+
const DROPDOWN_GAP = 6;
|
|
52749
|
+
const MIN_DROPDOWN_WIDTH = 300;
|
|
52750
|
+
const MAX_DROPDOWN_WIDTH = 420;
|
|
52751
|
+
const PREFERRED_DROPDOWN_HEIGHT = 420;
|
|
52752
|
+
const MIN_DROPDOWN_HEIGHT = 180;
|
|
52659
52753
|
const [isOpen, setIsOpen] = React26.useState(false);
|
|
52660
52754
|
const [selectedIds, setSelectedIds] = React26.useState(currentFactoryIds);
|
|
52661
52755
|
const [isSaving, setIsSaving] = React26.useState(false);
|
|
52662
|
-
const [position, setPosition] = React26.useState({
|
|
52756
|
+
const [position, setPosition] = React26.useState({
|
|
52757
|
+
top: 0,
|
|
52758
|
+
left: 0,
|
|
52759
|
+
width: MIN_DROPDOWN_WIDTH,
|
|
52760
|
+
maxHeight: PREFERRED_DROPDOWN_HEIGHT,
|
|
52761
|
+
openAbove: false
|
|
52762
|
+
});
|
|
52663
52763
|
const buttonRef = React26.useRef(null);
|
|
52664
52764
|
const dropdownRef = React26.useRef(null);
|
|
52665
52765
|
React26.useEffect(() => {
|
|
@@ -52669,10 +52769,31 @@ var FactoryAssignmentDropdown = ({
|
|
|
52669
52769
|
const updatePosition = () => {
|
|
52670
52770
|
if (isOpen && buttonRef.current) {
|
|
52671
52771
|
const rect = buttonRef.current.getBoundingClientRect();
|
|
52772
|
+
const viewportWidth = window.innerWidth;
|
|
52773
|
+
const viewportHeight = window.innerHeight;
|
|
52774
|
+
const width = Math.min(
|
|
52775
|
+
Math.max(rect.width, MIN_DROPDOWN_WIDTH),
|
|
52776
|
+
Math.min(MAX_DROPDOWN_WIDTH, viewportWidth - VIEWPORT_MARGIN * 2)
|
|
52777
|
+
);
|
|
52778
|
+
const left = Math.min(
|
|
52779
|
+
Math.max(VIEWPORT_MARGIN, rect.left),
|
|
52780
|
+
viewportWidth - width - VIEWPORT_MARGIN
|
|
52781
|
+
);
|
|
52782
|
+
const spaceBelow = viewportHeight - rect.bottom - VIEWPORT_MARGIN - DROPDOWN_GAP;
|
|
52783
|
+
const spaceAbove = rect.top - VIEWPORT_MARGIN - DROPDOWN_GAP;
|
|
52784
|
+
const openAbove = spaceBelow < 260 && spaceAbove > spaceBelow;
|
|
52785
|
+
const availableSpace = openAbove ? spaceAbove : spaceBelow;
|
|
52786
|
+
const maxHeight = Math.max(
|
|
52787
|
+
MIN_DROPDOWN_HEIGHT,
|
|
52788
|
+
Math.min(PREFERRED_DROPDOWN_HEIGHT, availableSpace > 0 ? availableSpace : viewportHeight - VIEWPORT_MARGIN * 2)
|
|
52789
|
+
);
|
|
52790
|
+
const top = openAbove ? rect.top - DROPDOWN_GAP : Math.min(rect.bottom + DROPDOWN_GAP, viewportHeight - VIEWPORT_MARGIN - maxHeight);
|
|
52672
52791
|
setPosition({
|
|
52673
|
-
top
|
|
52674
|
-
left
|
|
52675
|
-
width
|
|
52792
|
+
top,
|
|
52793
|
+
left,
|
|
52794
|
+
width,
|
|
52795
|
+
maxHeight,
|
|
52796
|
+
openAbove
|
|
52676
52797
|
});
|
|
52677
52798
|
}
|
|
52678
52799
|
};
|
|
@@ -52750,7 +52871,7 @@ var FactoryAssignmentDropdown = ({
|
|
|
52750
52871
|
assignedFactories.length - 2
|
|
52751
52872
|
] });
|
|
52752
52873
|
};
|
|
52753
|
-
const hasChanges = JSON.stringify(selectedIds.sort()) !== JSON.stringify(currentFactoryIds.sort());
|
|
52874
|
+
const hasChanges = JSON.stringify([...selectedIds].sort()) !== JSON.stringify([...currentFactoryIds].sort());
|
|
52754
52875
|
if (!canEdit) {
|
|
52755
52876
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm", children: getDisplayText() });
|
|
52756
52877
|
}
|
|
@@ -52780,13 +52901,13 @@ var FactoryAssignmentDropdown = ({
|
|
|
52780
52901
|
"div",
|
|
52781
52902
|
{
|
|
52782
52903
|
ref: dropdownRef,
|
|
52783
|
-
className: "fixed z-[9999] bg-white rounded-lg shadow-2xl border border-gray-200",
|
|
52904
|
+
className: "fixed z-[9999] bg-white rounded-lg shadow-2xl border border-gray-200 flex flex-col overflow-hidden",
|
|
52784
52905
|
style: {
|
|
52785
|
-
top: `${position.top
|
|
52906
|
+
top: `${position.top}px`,
|
|
52786
52907
|
left: `${position.left}px`,
|
|
52787
|
-
|
|
52788
|
-
|
|
52789
|
-
|
|
52908
|
+
width: `${position.width}px`,
|
|
52909
|
+
maxHeight: `${position.maxHeight}px`,
|
|
52910
|
+
transform: position.openAbove ? "translateY(-100%)" : void 0
|
|
52790
52911
|
},
|
|
52791
52912
|
children: [
|
|
52792
52913
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 border-b border-gray-200 bg-gray-50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
@@ -52803,7 +52924,7 @@ var FactoryAssignmentDropdown = ({
|
|
|
52803
52924
|
}
|
|
52804
52925
|
)
|
|
52805
52926
|
] }) }),
|
|
52806
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "
|
|
52927
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto", children: availableFactories.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-8 text-center", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "No factories available" }) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-1", children: availableFactories.map((factory) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
52807
52928
|
"label",
|
|
52808
52929
|
{
|
|
52809
52930
|
className: "flex items-center gap-3 px-4 py-2.5 hover:bg-gray-50 cursor-pointer transition-colors",
|
|
@@ -60936,6 +61057,20 @@ var ProfileView = () => {
|
|
|
60936
61057
|
] });
|
|
60937
61058
|
};
|
|
60938
61059
|
var ProfileView_default = ProfileView;
|
|
61060
|
+
|
|
61061
|
+
// src/lib/constants/actions.ts
|
|
61062
|
+
var ACTION_NAMES = {
|
|
61063
|
+
/** Assembly operations */
|
|
61064
|
+
ASSEMBLY: "Assembly",
|
|
61065
|
+
/** Packaging operations */
|
|
61066
|
+
PACKAGING: "Packaging",
|
|
61067
|
+
/** Inspection operations */
|
|
61068
|
+
INSPECTION: "Inspection",
|
|
61069
|
+
/** Testing operations */
|
|
61070
|
+
TESTING: "Testing",
|
|
61071
|
+
/** Quality control operations */
|
|
61072
|
+
QUALITY_CONTROL: "Quality Control"
|
|
61073
|
+
};
|
|
60939
61074
|
var calculateShiftHours = (startTime, endTime, breaks = []) => {
|
|
60940
61075
|
if (!startTime || !endTime) return 8;
|
|
60941
61076
|
const [startHour, startMinute] = startTime.split(":").map(Number);
|
|
@@ -60949,6 +61084,43 @@ var calculateShiftHours = (startTime, endTime, breaks = []) => {
|
|
|
60949
61084
|
const hoursDiff = (endMinutes - startMinutes - totalBreakMinutes) / 60;
|
|
60950
61085
|
return Number(hoursDiff.toFixed(1));
|
|
60951
61086
|
};
|
|
61087
|
+
var calculateBreakDuration2 = (startTime, endTime) => {
|
|
61088
|
+
const [startHour, startMinute] = startTime.split(":").map(Number);
|
|
61089
|
+
const [endHour, endMinute] = endTime.split(":").map(Number);
|
|
61090
|
+
let startMinutes = startHour * 60 + startMinute;
|
|
61091
|
+
let endMinutes = endHour * 60 + endMinute;
|
|
61092
|
+
if (endMinutes < startMinutes) {
|
|
61093
|
+
endMinutes += 24 * 60;
|
|
61094
|
+
}
|
|
61095
|
+
return endMinutes - startMinutes;
|
|
61096
|
+
};
|
|
61097
|
+
var parseBreaksFromDB = (dbBreaks) => {
|
|
61098
|
+
if (!dbBreaks) return [];
|
|
61099
|
+
if (Array.isArray(dbBreaks)) {
|
|
61100
|
+
return dbBreaks.map((breakItem) => ({
|
|
61101
|
+
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
61102
|
+
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
61103
|
+
duration: calculateBreakDuration2(
|
|
61104
|
+
breakItem.start || breakItem.startTime || "00:00",
|
|
61105
|
+
breakItem.end || breakItem.endTime || "00:00"
|
|
61106
|
+
),
|
|
61107
|
+
remarks: breakItem.remarks || breakItem.name || ""
|
|
61108
|
+
}));
|
|
61109
|
+
} else if (dbBreaks.breaks && Array.isArray(dbBreaks.breaks)) {
|
|
61110
|
+
return dbBreaks.breaks.map((breakItem) => ({
|
|
61111
|
+
startTime: breakItem.start || breakItem.startTime || "00:00",
|
|
61112
|
+
endTime: breakItem.end || breakItem.endTime || "00:00",
|
|
61113
|
+
duration: calculateBreakDuration2(
|
|
61114
|
+
breakItem.start || breakItem.startTime || "00:00",
|
|
61115
|
+
breakItem.end || breakItem.endTime || "00:00"
|
|
61116
|
+
),
|
|
61117
|
+
remarks: breakItem.remarks || breakItem.name || ""
|
|
61118
|
+
}));
|
|
61119
|
+
} else {
|
|
61120
|
+
console.warn("Unexpected breaks format:", dbBreaks);
|
|
61121
|
+
return [];
|
|
61122
|
+
}
|
|
61123
|
+
};
|
|
60952
61124
|
var getStoredLineState = (lineId) => {
|
|
60953
61125
|
try {
|
|
60954
61126
|
return JSON.parse(localStorage.getItem(`line_${lineId}_open`) || "false");
|
|
@@ -60965,6 +61137,7 @@ var formatBreaks = (breaks) => {
|
|
|
60965
61137
|
}))
|
|
60966
61138
|
};
|
|
60967
61139
|
};
|
|
61140
|
+
var SHIFT_HOURS_EPSILON = 1e-3;
|
|
60968
61141
|
var BreakRow = React26.memo(({
|
|
60969
61142
|
break: breakItem,
|
|
60970
61143
|
onUpdate,
|
|
@@ -61260,16 +61433,22 @@ var ShiftsView = ({
|
|
|
61260
61433
|
);
|
|
61261
61434
|
const [loading, setLoading] = React26.useState(true);
|
|
61262
61435
|
const [error, setError] = React26.useState(null);
|
|
61436
|
+
const [saveStatusMessages, setSaveStatusMessages] = React26.useState(
|
|
61437
|
+
() => lineIds.reduce((acc, id3) => ({ ...acc, [id3]: null }), {})
|
|
61438
|
+
);
|
|
61263
61439
|
const showToast = React26.useCallback((type, message) => {
|
|
61264
61440
|
if (onToast) {
|
|
61265
|
-
|
|
61266
|
-
|
|
61267
|
-
|
|
61268
|
-
sonner
|
|
61269
|
-
} else {
|
|
61270
|
-
sonner.toast.error(message);
|
|
61441
|
+
try {
|
|
61442
|
+
onToast(type, message);
|
|
61443
|
+
} catch (error2) {
|
|
61444
|
+
console.warn("[ShiftsView] onToast callback failed, falling back to sonner toast:", error2);
|
|
61271
61445
|
}
|
|
61272
61446
|
}
|
|
61447
|
+
if (type === "success") {
|
|
61448
|
+
sonner.toast.success(message);
|
|
61449
|
+
} else {
|
|
61450
|
+
sonner.toast.error(message);
|
|
61451
|
+
}
|
|
61273
61452
|
}, [onToast]);
|
|
61274
61453
|
React26.useEffect(() => {
|
|
61275
61454
|
const fetchShiftConfigs = async () => {
|
|
@@ -61420,15 +61599,196 @@ var ShiftsView = ({
|
|
|
61420
61599
|
return config;
|
|
61421
61600
|
}));
|
|
61422
61601
|
}, []);
|
|
61602
|
+
const getOperationalDateForLine = React26.useCallback((lineConfig) => {
|
|
61603
|
+
const sortedShifts = [...lineConfig.shifts || []].sort((a, b) => a.shiftId - b.shiftId);
|
|
61604
|
+
const dayBoundaryStart = sortedShifts[0]?.startTime || "06:00";
|
|
61605
|
+
return getOperationalDate(lineConfig.timezone || "UTC", /* @__PURE__ */ new Date(), dayBoundaryStart);
|
|
61606
|
+
}, []);
|
|
61607
|
+
const recalculateTargetsForShiftHourChanges = React26.useCallback(
|
|
61608
|
+
async ({
|
|
61609
|
+
lineId,
|
|
61610
|
+
lineConfig,
|
|
61611
|
+
previousShiftHours,
|
|
61612
|
+
updatedBy
|
|
61613
|
+
}) => {
|
|
61614
|
+
const primaryOperationalDate = getOperationalDate(lineConfig.timezone || "UTC");
|
|
61615
|
+
const alternateOperationalDate = getOperationalDateForLine(lineConfig);
|
|
61616
|
+
const candidateOperationalDates = primaryOperationalDate === alternateOperationalDate ? [primaryOperationalDate] : [primaryOperationalDate, alternateOperationalDate];
|
|
61617
|
+
let recalculatedCount = 0;
|
|
61618
|
+
const { data: lineRow, error: lineRowError } = await supabase.from("lines").select("factory_id").eq("id", lineId).maybeSingle();
|
|
61619
|
+
if (lineRowError) {
|
|
61620
|
+
throw new Error(`Failed to resolve line factory for target recalculation: ${lineRowError.message}`);
|
|
61621
|
+
}
|
|
61622
|
+
const lineFactoryId = lineRow?.factory_id || null;
|
|
61623
|
+
for (const shift of lineConfig.shifts || []) {
|
|
61624
|
+
const oldShiftHours = previousShiftHours[shift.shiftId];
|
|
61625
|
+
const newShiftHours = calculateShiftHours(shift.startTime, shift.endTime, shift.breaks || []);
|
|
61626
|
+
if (oldShiftHours === void 0) continue;
|
|
61627
|
+
if (!Number.isFinite(newShiftHours) || newShiftHours <= 0) {
|
|
61628
|
+
console.warn(
|
|
61629
|
+
`[ShiftsView] Skipping target recalculation for line ${lineId}, shift ${shift.shiftId} due to invalid new shift hours: ${newShiftHours}`
|
|
61630
|
+
);
|
|
61631
|
+
continue;
|
|
61632
|
+
}
|
|
61633
|
+
if (Math.abs(newShiftHours - oldShiftHours) <= SHIFT_HOURS_EPSILON) continue;
|
|
61634
|
+
let thresholdDateForShift = candidateOperationalDates[0];
|
|
61635
|
+
let currentThresholds = [];
|
|
61636
|
+
for (const candidateDate of candidateOperationalDates) {
|
|
61637
|
+
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);
|
|
61638
|
+
if (thresholdsFetchError) {
|
|
61639
|
+
throw new Error(
|
|
61640
|
+
`Failed to fetch action thresholds for line ${lineId}, shift ${shift.shiftId}: ${thresholdsFetchError.message}`
|
|
61641
|
+
);
|
|
61642
|
+
}
|
|
61643
|
+
if ((thresholdRows || []).length > 0) {
|
|
61644
|
+
thresholdDateForShift = candidateDate;
|
|
61645
|
+
currentThresholds = thresholdRows || [];
|
|
61646
|
+
break;
|
|
61647
|
+
}
|
|
61648
|
+
}
|
|
61649
|
+
if (currentThresholds.length === 0) {
|
|
61650
|
+
continue;
|
|
61651
|
+
}
|
|
61652
|
+
const actionIds = Array.from(
|
|
61653
|
+
new Set(currentThresholds.map((threshold) => threshold.action_id).filter(Boolean))
|
|
61654
|
+
);
|
|
61655
|
+
const actionNameById = /* @__PURE__ */ new Map();
|
|
61656
|
+
if (actionIds.length > 0) {
|
|
61657
|
+
const { data: actionRows, error: actionsError } = await supabase.from("actions").select("id, action_name").in("id", actionIds);
|
|
61658
|
+
if (actionsError) {
|
|
61659
|
+
console.warn(
|
|
61660
|
+
`[ShiftsView] Failed to resolve action names for line ${lineId}, shift ${shift.shiftId}: ${actionsError.message}`
|
|
61661
|
+
);
|
|
61662
|
+
} else {
|
|
61663
|
+
(actionRows || []).forEach((actionRow) => {
|
|
61664
|
+
if (actionRow.id && actionRow.action_name) {
|
|
61665
|
+
actionNameById.set(actionRow.id, actionRow.action_name);
|
|
61666
|
+
}
|
|
61667
|
+
});
|
|
61668
|
+
}
|
|
61669
|
+
}
|
|
61670
|
+
const expandedHours = newShiftHours > oldShiftHours;
|
|
61671
|
+
const recalculatedThresholds = currentThresholds.map((threshold) => {
|
|
61672
|
+
const existingPPH = Number(threshold.pph_threshold) || 0;
|
|
61673
|
+
const existingDayOutput = Number(threshold.total_day_output) || 0;
|
|
61674
|
+
const baselineDayOutput = existingDayOutput > 0 ? existingDayOutput : Math.round(existingPPH * Math.max(oldShiftHours, 0));
|
|
61675
|
+
let nextPPH = existingPPH;
|
|
61676
|
+
let nextDayOutput = existingDayOutput;
|
|
61677
|
+
if (expandedHours) {
|
|
61678
|
+
const pphToKeep = existingPPH > 0 ? existingPPH : oldShiftHours > 0 ? Math.round(baselineDayOutput / oldShiftHours) : 0;
|
|
61679
|
+
nextPPH = pphToKeep;
|
|
61680
|
+
nextDayOutput = Math.round(pphToKeep * newShiftHours);
|
|
61681
|
+
} else {
|
|
61682
|
+
const dayOutputToKeep = baselineDayOutput;
|
|
61683
|
+
nextDayOutput = dayOutputToKeep;
|
|
61684
|
+
nextPPH = newShiftHours > 0 ? Math.round(dayOutputToKeep / newShiftHours) : 0;
|
|
61685
|
+
}
|
|
61686
|
+
const resolvedActionName = (typeof threshold.action_name === "string" && threshold.action_name.trim().length > 0 ? threshold.action_name : actionNameById.get(threshold.action_id)) || ACTION_NAMES.ASSEMBLY;
|
|
61687
|
+
return {
|
|
61688
|
+
line_id: threshold.line_id || lineId,
|
|
61689
|
+
shift_id: shift.shiftId,
|
|
61690
|
+
action_id: threshold.action_id,
|
|
61691
|
+
workspace_id: threshold.workspace_id,
|
|
61692
|
+
date: threshold.date || thresholdDateForShift,
|
|
61693
|
+
pph_threshold: Math.max(0, Math.round(Number.isFinite(nextPPH) ? nextPPH : 0)),
|
|
61694
|
+
ideal_cycle_time: Number(threshold.ideal_cycle_time) || 0,
|
|
61695
|
+
total_day_output: Math.max(0, Math.round(Number.isFinite(nextDayOutput) ? nextDayOutput : 0)),
|
|
61696
|
+
action_name: resolvedActionName,
|
|
61697
|
+
updated_by: updatedBy,
|
|
61698
|
+
...threshold.sku_id ? { sku_id: threshold.sku_id } : {}
|
|
61699
|
+
};
|
|
61700
|
+
});
|
|
61701
|
+
const { error: thresholdsUpsertError } = await supabase.from("action_thresholds").upsert(recalculatedThresholds);
|
|
61702
|
+
if (thresholdsUpsertError) {
|
|
61703
|
+
throw new Error(
|
|
61704
|
+
`Failed to update action thresholds for line ${lineId}, shift ${shift.shiftId}: ${thresholdsUpsertError.message}`
|
|
61705
|
+
);
|
|
61706
|
+
}
|
|
61707
|
+
const packagingActionIds = new Set(
|
|
61708
|
+
Array.from(actionNameById.entries()).filter(([, actionName]) => actionName.toLowerCase() === ACTION_NAMES.PACKAGING.toLowerCase()).map(([actionId]) => actionId)
|
|
61709
|
+
);
|
|
61710
|
+
const packagingThresholds = recalculatedThresholds.filter((threshold) => {
|
|
61711
|
+
if (packagingActionIds.has(threshold.action_id)) return true;
|
|
61712
|
+
return typeof threshold.action_name === "string" && threshold.action_name.toLowerCase() === ACTION_NAMES.PACKAGING.toLowerCase();
|
|
61713
|
+
});
|
|
61714
|
+
const thresholdDayOutput = packagingThresholds.reduce(
|
|
61715
|
+
(sum, threshold) => sum + (Number(threshold.total_day_output) || 0),
|
|
61716
|
+
0
|
|
61717
|
+
);
|
|
61718
|
+
const thresholdPPH = packagingThresholds.reduce(
|
|
61719
|
+
(sum, threshold) => sum + (Number(threshold.pph_threshold) || 0),
|
|
61720
|
+
0
|
|
61721
|
+
);
|
|
61722
|
+
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();
|
|
61723
|
+
if (existingLineThresholdError) {
|
|
61724
|
+
console.warn(
|
|
61725
|
+
`[ShiftsView] Failed to read existing line threshold for line ${lineId}, shift ${shift.shiftId}: ${existingLineThresholdError.message}`
|
|
61726
|
+
);
|
|
61727
|
+
}
|
|
61728
|
+
const factoryId = existingLineThreshold?.factory_id || lineFactoryId;
|
|
61729
|
+
if (factoryId) {
|
|
61730
|
+
const lineThresholdPayload = {
|
|
61731
|
+
factory_id: factoryId,
|
|
61732
|
+
line_id: lineId,
|
|
61733
|
+
date: thresholdDateForShift,
|
|
61734
|
+
shift_id: shift.shiftId,
|
|
61735
|
+
product_code: existingLineThreshold?.product_code || "",
|
|
61736
|
+
threshold_day_output: thresholdDayOutput,
|
|
61737
|
+
threshold_pph: thresholdPPH
|
|
61738
|
+
};
|
|
61739
|
+
if (existingLineThreshold?.sku_id) {
|
|
61740
|
+
lineThresholdPayload.sku_id = existingLineThreshold.sku_id;
|
|
61741
|
+
}
|
|
61742
|
+
const { error: lineThresholdUpsertError } = await supabase.from("line_thresholds").upsert(lineThresholdPayload, { onConflict: "factory_id,line_id,date,shift_id" });
|
|
61743
|
+
if (lineThresholdUpsertError) {
|
|
61744
|
+
throw new Error(
|
|
61745
|
+
`Failed to update line thresholds for line ${lineId}, shift ${shift.shiftId}: ${lineThresholdUpsertError.message}`
|
|
61746
|
+
);
|
|
61747
|
+
}
|
|
61748
|
+
} else {
|
|
61749
|
+
console.warn(
|
|
61750
|
+
`[ShiftsView] Missing factory_id while updating line thresholds for line ${lineId}, shift ${shift.shiftId}`
|
|
61751
|
+
);
|
|
61752
|
+
}
|
|
61753
|
+
recalculatedCount += recalculatedThresholds.length;
|
|
61754
|
+
}
|
|
61755
|
+
return recalculatedCount;
|
|
61756
|
+
},
|
|
61757
|
+
[getOperationalDateForLine, supabase]
|
|
61758
|
+
);
|
|
61423
61759
|
const handleSaveShifts = React26.useCallback(async (lineId) => {
|
|
61760
|
+
const userId = "6bf6f271-1e55-4a95-9b89-1c3820b58739";
|
|
61424
61761
|
setLineConfigs((prev) => prev.map(
|
|
61425
61762
|
(config) => config.id === lineId ? { ...config, isSaving: true, saveSuccess: false } : config
|
|
61426
61763
|
));
|
|
61764
|
+
setSaveStatusMessages((prev) => ({ ...prev, [lineId]: null }));
|
|
61427
61765
|
try {
|
|
61428
61766
|
const lineConfig = lineConfigs.find((config) => config.id === lineId);
|
|
61429
61767
|
if (!lineConfig) {
|
|
61430
61768
|
throw new Error("Line configuration not found");
|
|
61431
61769
|
}
|
|
61770
|
+
const { data: existingRows, error: existingRowsError } = await supabase.from("line_operating_hours").select("shift_id, start_time, end_time, breaks").eq("line_id", lineId);
|
|
61771
|
+
if (existingRowsError) {
|
|
61772
|
+
throw new Error(`Failed to read existing shift timings: ${existingRowsError.message}`);
|
|
61773
|
+
}
|
|
61774
|
+
const previousShiftHours = (existingRows || []).reduce(
|
|
61775
|
+
(acc, row) => {
|
|
61776
|
+
acc[row.shift_id] = calculateShiftHours(
|
|
61777
|
+
row.start_time,
|
|
61778
|
+
row.end_time,
|
|
61779
|
+
parseBreaksFromDB(row.breaks)
|
|
61780
|
+
);
|
|
61781
|
+
return acc;
|
|
61782
|
+
},
|
|
61783
|
+
{}
|
|
61784
|
+
);
|
|
61785
|
+
const changedShiftCount = (lineConfig.shifts || []).reduce((count, shift) => {
|
|
61786
|
+
const oldShiftHours = previousShiftHours[shift.shiftId];
|
|
61787
|
+
if (oldShiftHours === void 0) return count;
|
|
61788
|
+
const newShiftHours = calculateShiftHours(shift.startTime, shift.endTime, shift.breaks || []);
|
|
61789
|
+
if (!Number.isFinite(newShiftHours)) return count;
|
|
61790
|
+
return Math.abs(newShiftHours - oldShiftHours) > SHIFT_HOURS_EPSILON ? count + 1 : count;
|
|
61791
|
+
}, 0);
|
|
61432
61792
|
const allSavedRows = [];
|
|
61433
61793
|
for (const shift of lineConfig.shifts || []) {
|
|
61434
61794
|
const shiftData = {
|
|
@@ -61450,23 +61810,65 @@ var ShiftsView = ({
|
|
|
61450
61810
|
if (allSavedRows.length > 0) {
|
|
61451
61811
|
shiftConfigStore.setFromOperatingHoursRows(lineId, allSavedRows, shiftConfigStore.get(lineId));
|
|
61452
61812
|
}
|
|
61813
|
+
let recalculatedTargetsCount = 0;
|
|
61814
|
+
try {
|
|
61815
|
+
recalculatedTargetsCount = await recalculateTargetsForShiftHourChanges({
|
|
61816
|
+
lineId,
|
|
61817
|
+
lineConfig,
|
|
61818
|
+
previousShiftHours,
|
|
61819
|
+
updatedBy: userId
|
|
61820
|
+
});
|
|
61821
|
+
} catch (recalcError) {
|
|
61822
|
+
console.error("[ShiftsView] Shift timings were saved but target recalculation failed:", recalcError);
|
|
61823
|
+
showToast("error", "Shift timings saved, but target recalculation failed. Please review targets.");
|
|
61824
|
+
setSaveStatusMessages((prev) => ({
|
|
61825
|
+
...prev,
|
|
61826
|
+
[lineId]: {
|
|
61827
|
+
message: "Shift timings saved, but target recalculation failed. Please review targets.",
|
|
61828
|
+
tone: "error"
|
|
61829
|
+
}
|
|
61830
|
+
}));
|
|
61831
|
+
}
|
|
61453
61832
|
setLineConfigs((prev) => prev.map(
|
|
61454
61833
|
(config) => config.id === lineId ? { ...config, isSaving: false, saveSuccess: true } : config
|
|
61455
61834
|
));
|
|
61456
|
-
|
|
61835
|
+
let successMessage = "Shift configurations saved successfully.";
|
|
61836
|
+
if (changedShiftCount > 0 && recalculatedTargetsCount > 0) {
|
|
61837
|
+
successMessage = `Shift configurations saved. Targets updated successfully for ${recalculatedTargetsCount} workspace${recalculatedTargetsCount === 1 ? "" : "s"}.`;
|
|
61838
|
+
} else if (changedShiftCount > 0) {
|
|
61839
|
+
successMessage = "Shift configurations saved. Target recalculation completed successfully.";
|
|
61840
|
+
}
|
|
61841
|
+
showToast("success", successMessage);
|
|
61842
|
+
setSaveStatusMessages((prev) => ({
|
|
61843
|
+
...prev,
|
|
61844
|
+
[lineId]: {
|
|
61845
|
+
message: successMessage,
|
|
61846
|
+
tone: "success"
|
|
61847
|
+
}
|
|
61848
|
+
}));
|
|
61457
61849
|
setTimeout(() => {
|
|
61458
61850
|
setLineConfigs((prev) => prev.map(
|
|
61459
61851
|
(config) => config.id === lineId ? { ...config, saveSuccess: false } : config
|
|
61460
61852
|
));
|
|
61461
61853
|
}, 3e3);
|
|
61854
|
+
setTimeout(() => {
|
|
61855
|
+
setSaveStatusMessages((prev) => ({ ...prev, [lineId]: null }));
|
|
61856
|
+
}, 7e3);
|
|
61462
61857
|
} catch (error2) {
|
|
61463
61858
|
console.error("Error saving shift configurations:", error2);
|
|
61464
61859
|
showToast("error", "Failed to save shift configurations");
|
|
61860
|
+
setSaveStatusMessages((prev) => ({
|
|
61861
|
+
...prev,
|
|
61862
|
+
[lineId]: {
|
|
61863
|
+
message: "Failed to save shift configurations",
|
|
61864
|
+
tone: "error"
|
|
61865
|
+
}
|
|
61866
|
+
}));
|
|
61465
61867
|
setLineConfigs((prev) => prev.map(
|
|
61466
61868
|
(config) => config.id === lineId ? { ...config, isSaving: false, saveSuccess: false } : config
|
|
61467
61869
|
));
|
|
61468
61870
|
}
|
|
61469
|
-
}, [lineConfigs, supabase, showToast]);
|
|
61871
|
+
}, [lineConfigs, recalculateTargetsForShiftHourChanges, supabase, showToast]);
|
|
61470
61872
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `min-h-screen bg-slate-50 ${className}`, children: [
|
|
61471
61873
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-0 z-10 bg-white border-b border-gray-200/80 shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-3 sm:px-4 md:px-6 lg:px-8 py-3 sm:py-4", children: [
|
|
61472
61874
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
|
|
@@ -61521,7 +61923,14 @@ var ShiftsView = ({
|
|
|
61521
61923
|
/* @__PURE__ */ jsxRuntime.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: [
|
|
61522
61924
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
61523
61925
|
config.isSaving && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-sm font-medium text-blue-600", children: "Saving..." }),
|
|
61524
|
-
config.saveSuccess && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-sm font-medium text-green-600", children: "Saved!" })
|
|
61926
|
+
config.saveSuccess && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-sm font-medium text-green-600", children: "Saved!" }),
|
|
61927
|
+
saveStatusMessages[config.id]?.message && /* @__PURE__ */ jsxRuntime.jsx(
|
|
61928
|
+
"span",
|
|
61929
|
+
{
|
|
61930
|
+
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"}`,
|
|
61931
|
+
children: saveStatusMessages[config.id]?.message
|
|
61932
|
+
}
|
|
61933
|
+
)
|
|
61525
61934
|
] }),
|
|
61526
61935
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
61527
61936
|
"button",
|
|
@@ -61578,20 +61987,6 @@ var ShiftsView = ({
|
|
|
61578
61987
|
var AuthenticatedShiftsView = withAuth(React26__namespace.default.memo(ShiftsView));
|
|
61579
61988
|
var ShiftsView_default = ShiftsView;
|
|
61580
61989
|
|
|
61581
|
-
// src/lib/constants/actions.ts
|
|
61582
|
-
var ACTION_NAMES = {
|
|
61583
|
-
/** Assembly operations */
|
|
61584
|
-
ASSEMBLY: "Assembly",
|
|
61585
|
-
/** Packaging operations */
|
|
61586
|
-
PACKAGING: "Packaging",
|
|
61587
|
-
/** Inspection operations */
|
|
61588
|
-
INSPECTION: "Inspection",
|
|
61589
|
-
/** Testing operations */
|
|
61590
|
-
TESTING: "Testing",
|
|
61591
|
-
/** Quality control operations */
|
|
61592
|
-
QUALITY_CONTROL: "Quality Control"
|
|
61593
|
-
};
|
|
61594
|
-
|
|
61595
61990
|
// src/views/TargetsView.utils.ts
|
|
61596
61991
|
var calculatePPH = (cycleTime, breaks = [], shiftHours = 0) => {
|
|
61597
61992
|
if (cycleTime === "" || cycleTime === 0) return "";
|
|
@@ -66008,7 +66403,7 @@ var TeamManagementView = ({
|
|
|
66008
66403
|
optifye: 0
|
|
66009
66404
|
});
|
|
66010
66405
|
const [isAddUserDialogOpen, setIsAddUserDialogOpen] = React26.useState(false);
|
|
66011
|
-
const canAddUsers = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "optifye";
|
|
66406
|
+
const canAddUsers = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "plant_head" || user?.role_level === "optifye";
|
|
66012
66407
|
const canViewUsageStats = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "optifye";
|
|
66013
66408
|
const companyIdForUsage = entityConfig?.companyId || user?.properties?.company_id;
|
|
66014
66409
|
const usageDateRange = React26.useMemo(() => {
|