@optifye/dashboard-core 6.10.46 → 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 +580 -160
- package/dist/index.mjs +580 -160
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -51425,13 +51425,31 @@ var InviteUserDialog = ({
|
|
|
51425
51425
|
const [selectedRole, setSelectedRole] = useState("supervisor");
|
|
51426
51426
|
const [selectedLines, setSelectedLines] = useState([]);
|
|
51427
51427
|
const [selectedFactories, setSelectedFactories] = useState([]);
|
|
51428
|
+
const [lineSearch, setLineSearch] = useState("");
|
|
51429
|
+
const [factorySearch, setFactorySearch] = useState("");
|
|
51428
51430
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
51429
51431
|
const [error, setError] = useState(null);
|
|
51430
|
-
const [isLinesDropdownOpen, setIsLinesDropdownOpen] = useState(false);
|
|
51431
|
-
const [isFactoriesDropdownOpen, setIsFactoriesDropdownOpen] = useState(false);
|
|
51432
51432
|
const canInviteIT = user?.role_level === "owner" || user?.role_level === "optifye";
|
|
51433
51433
|
const canInvitePlantHead = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "optifye";
|
|
51434
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
|
+
);
|
|
51435
51453
|
useEffect(() => {
|
|
51436
51454
|
if (!isOpen) {
|
|
51437
51455
|
setEmail("");
|
|
@@ -51440,9 +51458,9 @@ var InviteUserDialog = ({
|
|
|
51440
51458
|
setSelectedRole("supervisor");
|
|
51441
51459
|
setSelectedLines([]);
|
|
51442
51460
|
setSelectedFactories([]);
|
|
51461
|
+
setLineSearch("");
|
|
51462
|
+
setFactorySearch("");
|
|
51443
51463
|
setError(null);
|
|
51444
|
-
setIsLinesDropdownOpen(false);
|
|
51445
|
-
setIsFactoriesDropdownOpen(false);
|
|
51446
51464
|
}
|
|
51447
51465
|
}, [isOpen]);
|
|
51448
51466
|
const validateEmail = (email2) => {
|
|
@@ -51511,7 +51529,6 @@ var InviteUserDialog = ({
|
|
|
51511
51529
|
try {
|
|
51512
51530
|
const dashboardUrl = typeof window !== "undefined" ? window.location.origin : "";
|
|
51513
51531
|
if (dashboardUrl) {
|
|
51514
|
-
console.log("Sending welcome email to:", email.trim());
|
|
51515
51532
|
const { data: emailData, error: emailError } = await supabase.functions.invoke("hyper-service", {
|
|
51516
51533
|
body: {
|
|
51517
51534
|
action: "send-welcome-email",
|
|
@@ -51524,13 +51541,8 @@ var InviteUserDialog = ({
|
|
|
51524
51541
|
console.error("Failed to send welcome email:", emailError);
|
|
51525
51542
|
toast.warning("User added successfully, but welcome email could not be sent");
|
|
51526
51543
|
} else if (emailData?.success) {
|
|
51527
|
-
console.log("Welcome email sent successfully:", emailData);
|
|
51528
51544
|
toast.success("User added and welcome email sent!");
|
|
51529
|
-
} else {
|
|
51530
|
-
console.log("Welcome email response:", emailData);
|
|
51531
51545
|
}
|
|
51532
|
-
} else {
|
|
51533
|
-
console.warn("Dashboard URL not available, skipping welcome email");
|
|
51534
51546
|
}
|
|
51535
51547
|
} catch (emailErr) {
|
|
51536
51548
|
console.error("Error sending welcome email:", emailErr);
|
|
@@ -51556,7 +51568,7 @@ var InviteUserDialog = ({
|
|
|
51556
51568
|
children: /* @__PURE__ */ jsxs(
|
|
51557
51569
|
"div",
|
|
51558
51570
|
{
|
|
51559
|
-
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",
|
|
51560
51572
|
onClick: (e) => e.stopPropagation(),
|
|
51561
51573
|
children: [
|
|
51562
51574
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between p-6 border-b border-gray-200", children: [
|
|
@@ -51584,7 +51596,7 @@ var InviteUserDialog = ({
|
|
|
51584
51596
|
/* @__PURE__ */ jsx("span", { children: error })
|
|
51585
51597
|
] }),
|
|
51586
51598
|
/* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
|
|
51587
|
-
/* @__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: [
|
|
51588
51600
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
51589
51601
|
/* @__PURE__ */ jsxs("label", { htmlFor: "firstName", className: "block text-sm font-medium text-gray-700 mb-2", children: [
|
|
51590
51602
|
"First Name ",
|
|
@@ -51743,124 +51755,192 @@ var InviteUserDialog = ({
|
|
|
51743
51755
|
)
|
|
51744
51756
|
] })
|
|
51745
51757
|
] }),
|
|
51746
|
-
selectedRole === "plant_head" &&
|
|
51747
|
-
/* @__PURE__ */ jsxs("
|
|
51748
|
-
/* @__PURE__ */
|
|
51749
|
-
|
|
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
|
+
] })
|
|
51750
51771
|
] }),
|
|
51751
|
-
/* @__PURE__ */ jsx("
|
|
51752
|
-
|
|
51753
|
-
|
|
51754
|
-
|
|
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",
|
|
51755
51811
|
{
|
|
51756
|
-
|
|
51757
|
-
|
|
51758
|
-
|
|
51759
|
-
|
|
51760
|
-
|
|
51761
|
-
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) => {
|
|
51762
|
-
const factory = availableFactories.find((f) => f.id === factoryId);
|
|
51763
|
-
return factory ? /* @__PURE__ */ jsxs(
|
|
51764
|
-
"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",
|
|
51765
51817
|
{
|
|
51766
|
-
|
|
51767
|
-
|
|
51768
|
-
|
|
51769
|
-
|
|
51770
|
-
|
|
51771
|
-
|
|
51772
|
-
|
|
51773
|
-
|
|
51774
|
-
|
|
51775
|
-
|
|
51776
|
-
|
|
51777
|
-
|
|
51778
|
-
|
|
51779
|
-
|
|
51780
|
-
|
|
51781
|
-
|
|
51782
|
-
]
|
|
51783
|
-
},
|
|
51784
|
-
factory.id
|
|
51785
|
-
) : null;
|
|
51786
|
-
}) }),
|
|
51787
|
-
/* @__PURE__ */ jsx(ChevronDown, { className: cn("w-4 h-4 text-gray-400 transition-transform flex-shrink-0", isFactoriesDropdownOpen && "rotate-180") })
|
|
51788
|
-
] })
|
|
51789
|
-
}
|
|
51790
|
-
),
|
|
51791
|
-
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(
|
|
51792
|
-
"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",
|
|
51793
51834
|
{
|
|
51794
|
-
|
|
51795
|
-
className: cn(
|
|
51796
|
-
"px-4 py-2.5 cursor-pointer flex items-center justify-between hover:bg-gray-50 transition-colors",
|
|
51797
|
-
selectedFactories.includes(factory.id) && "bg-blue-50"
|
|
51798
|
-
),
|
|
51835
|
+
className: "inline-flex items-center gap-1 px-2 py-1 bg-blue-100 text-blue-700 text-xs rounded-md font-medium",
|
|
51799
51836
|
children: [
|
|
51800
|
-
|
|
51801
|
-
|
|
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
|
+
)
|
|
51802
51848
|
]
|
|
51803
51849
|
},
|
|
51804
51850
|
factory.id
|
|
51805
51851
|
)) })
|
|
51806
51852
|
] })
|
|
51807
51853
|
] }),
|
|
51808
|
-
selectedRole === "supervisor" &&
|
|
51809
|
-
/* @__PURE__ */ jsxs("
|
|
51810
|
-
/* @__PURE__ */
|
|
51811
|
-
|
|
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
|
+
] })
|
|
51812
51867
|
] }),
|
|
51813
|
-
/* @__PURE__ */ jsx("
|
|
51814
|
-
|
|
51815
|
-
|
|
51816
|
-
|
|
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",
|
|
51817
51907
|
{
|
|
51818
|
-
|
|
51819
|
-
|
|
51820
|
-
|
|
51821
|
-
|
|
51822
|
-
|
|
51823
|
-
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) => {
|
|
51824
|
-
const line = availableLines.find((l) => l.id === lineId);
|
|
51825
|
-
return line ? /* @__PURE__ */ jsxs(
|
|
51826
|
-
"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",
|
|
51827
51913
|
{
|
|
51828
|
-
|
|
51829
|
-
|
|
51830
|
-
|
|
51831
|
-
|
|
51832
|
-
|
|
51833
|
-
|
|
51834
|
-
|
|
51835
|
-
|
|
51836
|
-
|
|
51837
|
-
|
|
51838
|
-
|
|
51839
|
-
|
|
51840
|
-
|
|
51841
|
-
|
|
51842
|
-
|
|
51843
|
-
|
|
51844
|
-
]
|
|
51845
|
-
},
|
|
51846
|
-
line.id
|
|
51847
|
-
) : null;
|
|
51848
|
-
}) }),
|
|
51849
|
-
/* @__PURE__ */ jsx(ChevronDown, { className: cn("w-4 h-4 text-gray-400 transition-transform flex-shrink-0", isLinesDropdownOpen && "rotate-180") })
|
|
51850
|
-
] })
|
|
51851
|
-
}
|
|
51852
|
-
),
|
|
51853
|
-
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(
|
|
51854
|
-
"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",
|
|
51855
51930
|
{
|
|
51856
|
-
|
|
51857
|
-
className: cn(
|
|
51858
|
-
"px-4 py-2.5 cursor-pointer flex items-center justify-between hover:bg-gray-50 transition-colors",
|
|
51859
|
-
selectedLines.includes(line.id) && "bg-blue-50"
|
|
51860
|
-
),
|
|
51931
|
+
className: "inline-flex items-center gap-1 px-2 py-1 bg-blue-100 text-blue-700 text-xs rounded-md font-medium",
|
|
51861
51932
|
children: [
|
|
51862
|
-
|
|
51863
|
-
|
|
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
|
+
)
|
|
51864
51944
|
]
|
|
51865
51945
|
},
|
|
51866
51946
|
line.id
|
|
@@ -52395,10 +52475,22 @@ var LineAssignmentDropdown = ({
|
|
|
52395
52475
|
canEdit,
|
|
52396
52476
|
onUpdate
|
|
52397
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;
|
|
52398
52484
|
const [isOpen, setIsOpen] = useState(false);
|
|
52399
52485
|
const [selectedIds, setSelectedIds] = useState(currentLineIds);
|
|
52400
52486
|
const [isSaving, setIsSaving] = useState(false);
|
|
52401
|
-
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
|
+
});
|
|
52402
52494
|
const buttonRef = useRef(null);
|
|
52403
52495
|
const dropdownRef = useRef(null);
|
|
52404
52496
|
useEffect(() => {
|
|
@@ -52408,10 +52500,31 @@ var LineAssignmentDropdown = ({
|
|
|
52408
52500
|
const updatePosition = () => {
|
|
52409
52501
|
if (isOpen && buttonRef.current) {
|
|
52410
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);
|
|
52411
52522
|
setPosition({
|
|
52412
|
-
top
|
|
52413
|
-
left
|
|
52414
|
-
width
|
|
52523
|
+
top,
|
|
52524
|
+
left,
|
|
52525
|
+
width,
|
|
52526
|
+
maxHeight,
|
|
52527
|
+
openAbove
|
|
52415
52528
|
});
|
|
52416
52529
|
}
|
|
52417
52530
|
};
|
|
@@ -52489,7 +52602,7 @@ var LineAssignmentDropdown = ({
|
|
|
52489
52602
|
assignedLines.length - 2
|
|
52490
52603
|
] });
|
|
52491
52604
|
};
|
|
52492
|
-
const hasChanges = JSON.stringify(selectedIds.sort()) !== JSON.stringify(currentLineIds.sort());
|
|
52605
|
+
const hasChanges = JSON.stringify([...selectedIds].sort()) !== JSON.stringify([...currentLineIds].sort());
|
|
52493
52606
|
if (!canEdit) {
|
|
52494
52607
|
return /* @__PURE__ */ jsx("div", { className: "text-sm", children: getDisplayText() });
|
|
52495
52608
|
}
|
|
@@ -52519,13 +52632,13 @@ var LineAssignmentDropdown = ({
|
|
|
52519
52632
|
"div",
|
|
52520
52633
|
{
|
|
52521
52634
|
ref: dropdownRef,
|
|
52522
|
-
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",
|
|
52523
52636
|
style: {
|
|
52524
|
-
top: `${position.top
|
|
52637
|
+
top: `${position.top}px`,
|
|
52525
52638
|
left: `${position.left}px`,
|
|
52526
|
-
|
|
52527
|
-
|
|
52528
|
-
|
|
52639
|
+
width: `${position.width}px`,
|
|
52640
|
+
maxHeight: `${position.maxHeight}px`,
|
|
52641
|
+
transform: position.openAbove ? "translateY(-100%)" : void 0
|
|
52529
52642
|
},
|
|
52530
52643
|
children: [
|
|
52531
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: [
|
|
@@ -52542,7 +52655,7 @@ var LineAssignmentDropdown = ({
|
|
|
52542
52655
|
}
|
|
52543
52656
|
)
|
|
52544
52657
|
] }) }),
|
|
52545
|
-
/* @__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(
|
|
52546
52659
|
"label",
|
|
52547
52660
|
{
|
|
52548
52661
|
className: "flex items-center gap-3 px-4 py-2.5 hover:bg-gray-50 cursor-pointer transition-colors",
|
|
@@ -52602,10 +52715,22 @@ var FactoryAssignmentDropdown = ({
|
|
|
52602
52715
|
canEdit,
|
|
52603
52716
|
onUpdate
|
|
52604
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;
|
|
52605
52724
|
const [isOpen, setIsOpen] = useState(false);
|
|
52606
52725
|
const [selectedIds, setSelectedIds] = useState(currentFactoryIds);
|
|
52607
52726
|
const [isSaving, setIsSaving] = useState(false);
|
|
52608
|
-
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
|
+
});
|
|
52609
52734
|
const buttonRef = useRef(null);
|
|
52610
52735
|
const dropdownRef = useRef(null);
|
|
52611
52736
|
useEffect(() => {
|
|
@@ -52615,10 +52740,31 @@ var FactoryAssignmentDropdown = ({
|
|
|
52615
52740
|
const updatePosition = () => {
|
|
52616
52741
|
if (isOpen && buttonRef.current) {
|
|
52617
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);
|
|
52618
52762
|
setPosition({
|
|
52619
|
-
top
|
|
52620
|
-
left
|
|
52621
|
-
width
|
|
52763
|
+
top,
|
|
52764
|
+
left,
|
|
52765
|
+
width,
|
|
52766
|
+
maxHeight,
|
|
52767
|
+
openAbove
|
|
52622
52768
|
});
|
|
52623
52769
|
}
|
|
52624
52770
|
};
|
|
@@ -52696,7 +52842,7 @@ var FactoryAssignmentDropdown = ({
|
|
|
52696
52842
|
assignedFactories.length - 2
|
|
52697
52843
|
] });
|
|
52698
52844
|
};
|
|
52699
|
-
const hasChanges = JSON.stringify(selectedIds.sort()) !== JSON.stringify(currentFactoryIds.sort());
|
|
52845
|
+
const hasChanges = JSON.stringify([...selectedIds].sort()) !== JSON.stringify([...currentFactoryIds].sort());
|
|
52700
52846
|
if (!canEdit) {
|
|
52701
52847
|
return /* @__PURE__ */ jsx("div", { className: "text-sm", children: getDisplayText() });
|
|
52702
52848
|
}
|
|
@@ -52726,13 +52872,13 @@ var FactoryAssignmentDropdown = ({
|
|
|
52726
52872
|
"div",
|
|
52727
52873
|
{
|
|
52728
52874
|
ref: dropdownRef,
|
|
52729
|
-
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",
|
|
52730
52876
|
style: {
|
|
52731
|
-
top: `${position.top
|
|
52877
|
+
top: `${position.top}px`,
|
|
52732
52878
|
left: `${position.left}px`,
|
|
52733
|
-
|
|
52734
|
-
|
|
52735
|
-
|
|
52879
|
+
width: `${position.width}px`,
|
|
52880
|
+
maxHeight: `${position.maxHeight}px`,
|
|
52881
|
+
transform: position.openAbove ? "translateY(-100%)" : void 0
|
|
52736
52882
|
},
|
|
52737
52883
|
children: [
|
|
52738
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: [
|
|
@@ -52749,7 +52895,7 @@ var FactoryAssignmentDropdown = ({
|
|
|
52749
52895
|
}
|
|
52750
52896
|
)
|
|
52751
52897
|
] }) }),
|
|
52752
|
-
/* @__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(
|
|
52753
52899
|
"label",
|
|
52754
52900
|
{
|
|
52755
52901
|
className: "flex items-center gap-3 px-4 py-2.5 hover:bg-gray-50 cursor-pointer transition-colors",
|
|
@@ -60882,6 +61028,20 @@ var ProfileView = () => {
|
|
|
60882
61028
|
] });
|
|
60883
61029
|
};
|
|
60884
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
|
+
};
|
|
60885
61045
|
var calculateShiftHours = (startTime, endTime, breaks = []) => {
|
|
60886
61046
|
if (!startTime || !endTime) return 8;
|
|
60887
61047
|
const [startHour, startMinute] = startTime.split(":").map(Number);
|
|
@@ -60895,6 +61055,43 @@ var calculateShiftHours = (startTime, endTime, breaks = []) => {
|
|
|
60895
61055
|
const hoursDiff = (endMinutes - startMinutes - totalBreakMinutes) / 60;
|
|
60896
61056
|
return Number(hoursDiff.toFixed(1));
|
|
60897
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
|
+
};
|
|
60898
61095
|
var getStoredLineState = (lineId) => {
|
|
60899
61096
|
try {
|
|
60900
61097
|
return JSON.parse(localStorage.getItem(`line_${lineId}_open`) || "false");
|
|
@@ -60911,6 +61108,7 @@ var formatBreaks = (breaks) => {
|
|
|
60911
61108
|
}))
|
|
60912
61109
|
};
|
|
60913
61110
|
};
|
|
61111
|
+
var SHIFT_HOURS_EPSILON = 1e-3;
|
|
60914
61112
|
var BreakRow = memo$1(({
|
|
60915
61113
|
break: breakItem,
|
|
60916
61114
|
onUpdate,
|
|
@@ -61206,16 +61404,22 @@ var ShiftsView = ({
|
|
|
61206
61404
|
);
|
|
61207
61405
|
const [loading, setLoading] = useState(true);
|
|
61208
61406
|
const [error, setError] = useState(null);
|
|
61407
|
+
const [saveStatusMessages, setSaveStatusMessages] = useState(
|
|
61408
|
+
() => lineIds.reduce((acc, id3) => ({ ...acc, [id3]: null }), {})
|
|
61409
|
+
);
|
|
61209
61410
|
const showToast = useCallback((type, message) => {
|
|
61210
61411
|
if (onToast) {
|
|
61211
|
-
|
|
61212
|
-
|
|
61213
|
-
|
|
61214
|
-
|
|
61215
|
-
} else {
|
|
61216
|
-
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);
|
|
61217
61416
|
}
|
|
61218
61417
|
}
|
|
61418
|
+
if (type === "success") {
|
|
61419
|
+
toast.success(message);
|
|
61420
|
+
} else {
|
|
61421
|
+
toast.error(message);
|
|
61422
|
+
}
|
|
61219
61423
|
}, [onToast]);
|
|
61220
61424
|
useEffect(() => {
|
|
61221
61425
|
const fetchShiftConfigs = async () => {
|
|
@@ -61366,15 +61570,196 @@ var ShiftsView = ({
|
|
|
61366
61570
|
return config;
|
|
61367
61571
|
}));
|
|
61368
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
|
+
);
|
|
61369
61730
|
const handleSaveShifts = useCallback(async (lineId) => {
|
|
61731
|
+
const userId = "6bf6f271-1e55-4a95-9b89-1c3820b58739";
|
|
61370
61732
|
setLineConfigs((prev) => prev.map(
|
|
61371
61733
|
(config) => config.id === lineId ? { ...config, isSaving: true, saveSuccess: false } : config
|
|
61372
61734
|
));
|
|
61735
|
+
setSaveStatusMessages((prev) => ({ ...prev, [lineId]: null }));
|
|
61373
61736
|
try {
|
|
61374
61737
|
const lineConfig = lineConfigs.find((config) => config.id === lineId);
|
|
61375
61738
|
if (!lineConfig) {
|
|
61376
61739
|
throw new Error("Line configuration not found");
|
|
61377
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);
|
|
61378
61763
|
const allSavedRows = [];
|
|
61379
61764
|
for (const shift of lineConfig.shifts || []) {
|
|
61380
61765
|
const shiftData = {
|
|
@@ -61396,23 +61781,65 @@ var ShiftsView = ({
|
|
|
61396
61781
|
if (allSavedRows.length > 0) {
|
|
61397
61782
|
shiftConfigStore.setFromOperatingHoursRows(lineId, allSavedRows, shiftConfigStore.get(lineId));
|
|
61398
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
|
+
}
|
|
61399
61803
|
setLineConfigs((prev) => prev.map(
|
|
61400
61804
|
(config) => config.id === lineId ? { ...config, isSaving: false, saveSuccess: true } : config
|
|
61401
61805
|
));
|
|
61402
|
-
|
|
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
|
+
}));
|
|
61403
61820
|
setTimeout(() => {
|
|
61404
61821
|
setLineConfigs((prev) => prev.map(
|
|
61405
61822
|
(config) => config.id === lineId ? { ...config, saveSuccess: false } : config
|
|
61406
61823
|
));
|
|
61407
61824
|
}, 3e3);
|
|
61825
|
+
setTimeout(() => {
|
|
61826
|
+
setSaveStatusMessages((prev) => ({ ...prev, [lineId]: null }));
|
|
61827
|
+
}, 7e3);
|
|
61408
61828
|
} catch (error2) {
|
|
61409
61829
|
console.error("Error saving shift configurations:", error2);
|
|
61410
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
|
+
}));
|
|
61411
61838
|
setLineConfigs((prev) => prev.map(
|
|
61412
61839
|
(config) => config.id === lineId ? { ...config, isSaving: false, saveSuccess: false } : config
|
|
61413
61840
|
));
|
|
61414
61841
|
}
|
|
61415
|
-
}, [lineConfigs, supabase, showToast]);
|
|
61842
|
+
}, [lineConfigs, recalculateTargetsForShiftHourChanges, supabase, showToast]);
|
|
61416
61843
|
return /* @__PURE__ */ jsxs("div", { className: `min-h-screen bg-slate-50 ${className}`, children: [
|
|
61417
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: [
|
|
61418
61845
|
/* @__PURE__ */ jsx("div", { className: "sm:hidden", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
@@ -61467,7 +61894,14 @@ var ShiftsView = ({
|
|
|
61467
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: [
|
|
61468
61895
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
61469
61896
|
config.isSaving && /* @__PURE__ */ jsx("span", { className: "text-xs sm:text-sm font-medium text-blue-600", children: "Saving..." }),
|
|
61470
|
-
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
|
+
)
|
|
61471
61905
|
] }),
|
|
61472
61906
|
/* @__PURE__ */ jsxs(
|
|
61473
61907
|
"button",
|
|
@@ -61524,20 +61958,6 @@ var ShiftsView = ({
|
|
|
61524
61958
|
var AuthenticatedShiftsView = withAuth(React26__default.memo(ShiftsView));
|
|
61525
61959
|
var ShiftsView_default = ShiftsView;
|
|
61526
61960
|
|
|
61527
|
-
// src/lib/constants/actions.ts
|
|
61528
|
-
var ACTION_NAMES = {
|
|
61529
|
-
/** Assembly operations */
|
|
61530
|
-
ASSEMBLY: "Assembly",
|
|
61531
|
-
/** Packaging operations */
|
|
61532
|
-
PACKAGING: "Packaging",
|
|
61533
|
-
/** Inspection operations */
|
|
61534
|
-
INSPECTION: "Inspection",
|
|
61535
|
-
/** Testing operations */
|
|
61536
|
-
TESTING: "Testing",
|
|
61537
|
-
/** Quality control operations */
|
|
61538
|
-
QUALITY_CONTROL: "Quality Control"
|
|
61539
|
-
};
|
|
61540
|
-
|
|
61541
61961
|
// src/views/TargetsView.utils.ts
|
|
61542
61962
|
var calculatePPH = (cycleTime, breaks = [], shiftHours = 0) => {
|
|
61543
61963
|
if (cycleTime === "" || cycleTime === 0) return "";
|
|
@@ -65954,7 +66374,7 @@ var TeamManagementView = ({
|
|
|
65954
66374
|
optifye: 0
|
|
65955
66375
|
});
|
|
65956
66376
|
const [isAddUserDialogOpen, setIsAddUserDialogOpen] = useState(false);
|
|
65957
|
-
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";
|
|
65958
66378
|
const canViewUsageStats = user?.role_level === "owner" || user?.role_level === "it" || user?.role_level === "optifye";
|
|
65959
66379
|
const companyIdForUsage = entityConfig?.companyId || user?.properties?.company_id;
|
|
65960
66380
|
const usageDateRange = useMemo(() => {
|