@burdenoff/microfe-crewfoundry 2026.625.2 → 2026.703.1
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/crewfoundry/components/forms/ConfirmDialog.js +43 -0
- package/dist/crewfoundry/components/forms/ConfirmDialog.js.map +1 -0
- package/dist/crewfoundry/components/forms/FormModal.js +220 -0
- package/dist/crewfoundry/components/forms/FormModal.js.map +1 -0
- package/dist/crewfoundry/components/forms/formValues.js +20 -0
- package/dist/crewfoundry/components/forms/formValues.js.map +1 -0
- package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js +255 -65
- package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsCallsPagePage.js +28 -17
- package/dist/crewfoundry/pages/CommunicationsCallsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsFilesPagePage.js +57 -53
- package/dist/crewfoundry/pages/CommunicationsFilesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js +32 -23
- package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsVideoCallsPagePage.js +44 -29
- package/dist/crewfoundry/pages/CommunicationsVideoCallsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js +183 -57
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyCompanyDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js +214 -92
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardDashboardPagePage.js +165 -131
- package/dist/crewfoundry/pages/DashboardDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardEnhancedDashboardPagePage.js +163 -133
- package/dist/crewfoundry/pages/DashboardEnhancedDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.js +269 -54
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringCreateJobPagePage.js +225 -78
- package/dist/crewfoundry/pages/HiringCreateJobPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringInterviewsPagePage.js +213 -59
- package/dist/crewfoundry/pages/HiringInterviewsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js +287 -90
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobsPagePage.js +49 -47
- package/dist/crewfoundry/pages/HiringJobsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.js +166 -59
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrEmployeesPagePage.js +274 -83
- package/dist/crewfoundry/pages/HrEmployeesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js +198 -88
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrPayrollPagePage.js +216 -88
- package/dist/crewfoundry/pages/HrPayrollPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js +216 -66
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningAssessmentsPagePage.js +6 -7
- package/dist/crewfoundry/pages/LearningAssessmentsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js +83 -68
- package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js +167 -46
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js +75 -55
- package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js +152 -79
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningMyLearningPagePage.js +75 -63
- package/dist/crewfoundry/pages/LearningMyLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceBookingManagementPagePage.js +3 -1
- package/dist/crewfoundry/pages/MarketplaceBookingManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js +235 -87
- package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js +199 -86
- package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.js +21 -40
- package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsCreateProjectPagePage.js +117 -45
- package/dist/crewfoundry/pages/ProjectsCreateProjectPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsProjectDetailsPagePage.js +249 -90
- package/dist/crewfoundry/pages/ProjectsProjectDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js +47 -45
- package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamSettingsPagePage.js +262 -133
- package/dist/crewfoundry/pages/TeamsTeamSettingsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamsPagePage.js +144 -77
- package/dist/crewfoundry/pages/TeamsTeamsPagePage.js.map +1 -1
- package/dist/crewfoundry/store/crewfoundryStore.js +306 -51
- package/dist/crewfoundry/store/crewfoundryStore.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useState as e } from "react";
|
|
2
|
+
import { jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
3
|
+
import { Button as r, ResponsiveDialog as i, ResponsiveDialogContent as a, ResponsiveDialogDescription as o, ResponsiveDialogFooter as s, ResponsiveDialogHeader as c, ResponsiveDialogTitle as l } from "@burdenoff/fe-libs/ui";
|
|
4
|
+
import { useGlobalUi as u } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
5
|
+
//#region src/crewfoundry/components/forms/ConfirmDialog.tsx
|
|
6
|
+
function d({ open: d, onOpenChange: f, title: p, description: m, confirmLabel: h = "Confirm", destructive: g = !1, onConfirm: _ }) {
|
|
7
|
+
let { showToast: v } = u(), [y, b] = e(!1);
|
|
8
|
+
return /* @__PURE__ */ t(i, {
|
|
9
|
+
open: d,
|
|
10
|
+
onOpenChange: f,
|
|
11
|
+
children: /* @__PURE__ */ n(a, {
|
|
12
|
+
className: "sm:max-w-[420px]",
|
|
13
|
+
children: [/* @__PURE__ */ n(c, { children: [/* @__PURE__ */ t(l, { children: p }), m ? /* @__PURE__ */ t(o, { children: m }) : null] }), /* @__PURE__ */ n(s, { children: [/* @__PURE__ */ t(r, {
|
|
14
|
+
type: "button",
|
|
15
|
+
variant: "outline",
|
|
16
|
+
onClick: () => f(!1),
|
|
17
|
+
disabled: y,
|
|
18
|
+
children: "Cancel"
|
|
19
|
+
}), /* @__PURE__ */ t(r, {
|
|
20
|
+
type: "button",
|
|
21
|
+
variant: g ? "destructive" : "default",
|
|
22
|
+
onClick: async () => {
|
|
23
|
+
if (!y) {
|
|
24
|
+
b(!0);
|
|
25
|
+
try {
|
|
26
|
+
await _(), f(!1);
|
|
27
|
+
} catch (e) {
|
|
28
|
+
v(e instanceof Error ? e.message : "Something went wrong", "error");
|
|
29
|
+
} finally {
|
|
30
|
+
b(!1);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
disabled: y,
|
|
35
|
+
children: y ? "Working…" : h
|
|
36
|
+
})] })]
|
|
37
|
+
})
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { d as ConfirmDialog };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=ConfirmDialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfirmDialog.js","names":[],"sources":["../../../../src/crewfoundry/components/forms/ConfirmDialog.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n Button,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\n\nexport interface ConfirmDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n title: string;\n description?: string;\n confirmLabel?: string;\n destructive?: boolean;\n onConfirm: () => Promise<void>;\n}\n\n/**\n * Small accessible confirm dialog built on the shared ResponsiveDialog so\n * destructive actions (delete) never rely on native window.confirm.\n */\nexport function ConfirmDialog({\n open,\n onOpenChange,\n title,\n description,\n confirmLabel = 'Confirm',\n destructive = false,\n onConfirm,\n}: ConfirmDialogProps) {\n const { showToast } = useGlobalUi();\n const [pending, setPending] = useState(false);\n\n const handleConfirm = async () => {\n if (pending) {\n return;\n }\n setPending(true);\n try {\n await onConfirm();\n onOpenChange(false);\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Something went wrong', 'error');\n } finally {\n setPending(false);\n }\n };\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"sm:max-w-[420px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{title}</ResponsiveDialogTitle>\n {description ? (\n <ResponsiveDialogDescription>{description}</ResponsiveDialogDescription>\n ) : null}\n </ResponsiveDialogHeader>\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n disabled={pending}\n >\n Cancel\n </Button>\n <Button\n type=\"button\"\n variant={destructive ? 'destructive' : 'default'}\n onClick={handleConfirm}\n disabled={pending}\n >\n {pending ? 'Working…' : confirmLabel}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":";;;;;AA0BA,SAAgB,EAAc,EAC5B,SACA,iBACA,UACA,gBACA,kBAAe,WACf,iBAAc,IACd,gBACqB;CACrB,IAAM,EAAE,iBAAc,GAAa,EAC7B,CAAC,GAAS,KAAc,EAAS,GAAM;AAiB7C,QACE,kBAAC,GAAD;EAAwB;EAAoB;YAC1C,kBAAC,GAAD;GAAyB,WAAU;aAAnC,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAwB,GAA8B,CAAA,EACrD,IACC,kBAAC,GAAD,EAAA,UAA8B,GAA0C,CAAA,GACtE,KACmB,EAAA,CAAA,EACzB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;IACE,MAAK;IACL,SAAQ;IACR,eAAe,EAAa,GAAM;IAClC,UAAU;cACX;IAEQ,CAAA,EACT,kBAAC,GAAD;IACE,MAAK;IACL,SAAS,IAAc,gBAAgB;IACvC,SApCY,YAAY;AAC5B,aAGJ;QAAW,GAAK;AAChB,UAAI;AAEF,OADA,MAAM,GAAW,EACjB,EAAa,GAAM;eACZ,GAAO;AACd,SAAU,aAAiB,QAAQ,EAAM,UAAU,wBAAwB,QAAQ;gBAC3E;AACR,SAAW,GAAM;;;;IA0BX,UAAU;cAET,IAAU,aAAa;IACjB,CAAA,CACc,EAAA,CAAA,CACD;;EACT,CAAA"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { useCallback as e, useEffect as t, useRef as n, useState as r } from "react";
|
|
2
|
+
import { X as i } from "lucide-react";
|
|
3
|
+
import { Fragment as a, jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
4
|
+
import { Button as c, Input as l, Label as u, ResponsiveDialog as d, ResponsiveDialogContent as f, ResponsiveDialogDescription as p, ResponsiveDialogFooter as m, ResponsiveDialogHeader as h, ResponsiveDialogTitle as g, Switch as _, Textarea as v } from "@burdenoff/fe-libs/ui";
|
|
5
|
+
import { useGlobalUi as y } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
6
|
+
//#region src/crewfoundry/components/forms/FormModal.tsx
|
|
7
|
+
var b = (e, t) => {
|
|
8
|
+
let n = {};
|
|
9
|
+
for (let r of e) {
|
|
10
|
+
let e = t?.[r.name];
|
|
11
|
+
r.type === "switch" ? n[r.name] = !!e : r.type === "tags" ? Array.isArray(e) ? n[r.name] = e.map((e) => String(e)) : typeof e == "string" && e.length > 0 ? n[r.name] = e.split(",").map((e) => e.trim()).filter(Boolean) : n[r.name] = [] : n[r.name] = e == null ? "" : String(e);
|
|
12
|
+
}
|
|
13
|
+
return n;
|
|
14
|
+
}, x = (e, t) => e.type === "switch" ? !1 : e.type === "tags" ? !Array.isArray(t) || t.length === 0 : typeof t != "string" || t.trim() === "", S = (e, t) => {
|
|
15
|
+
let n = {};
|
|
16
|
+
for (let r of e) {
|
|
17
|
+
let e = t[r.name];
|
|
18
|
+
if (r.type === "switch") n[r.name] = !!e;
|
|
19
|
+
else if (r.type === "tags") n[r.name] = Array.isArray(e) ? e : [];
|
|
20
|
+
else if (r.type === "number") {
|
|
21
|
+
let t = typeof e == "string" ? e.trim() : "";
|
|
22
|
+
n[r.name] = t === "" ? "" : Number(t);
|
|
23
|
+
} else n[r.name] = typeof e == "string" ? e : "";
|
|
24
|
+
}
|
|
25
|
+
return n;
|
|
26
|
+
}, C = "flex h-10 w-full rounded-input border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary transition-colors duration-200 focus-visible:border-border-focus focus-visible:outline-none focus-visible:shadow-[var(--shadow-focus)] focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50";
|
|
27
|
+
function w({ open: w, onOpenChange: T, title: E, description: D, fields: O, initialValues: k, submitLabel: A = "Save", onSubmit: j }) {
|
|
28
|
+
let { showToast: M } = y(), [N, P] = r(() => b(O, k)), [F, I] = r({}), [L, R] = r(!1), z = n(O), B = n(k), V = n(!1);
|
|
29
|
+
t(() => {
|
|
30
|
+
z.current = O, B.current = k;
|
|
31
|
+
}, [O, k]), t(() => {
|
|
32
|
+
w && !V.current && (P(b(z.current, B.current)), I({}), R(!1)), V.current = w;
|
|
33
|
+
}, [w]);
|
|
34
|
+
let H = e((e, t) => {
|
|
35
|
+
P((n) => ({
|
|
36
|
+
...n,
|
|
37
|
+
[e]: t
|
|
38
|
+
}));
|
|
39
|
+
}, []), U = e((e) => {
|
|
40
|
+
I((t) => {
|
|
41
|
+
let n = (t[e] ?? "").trim();
|
|
42
|
+
return n && P((t) => {
|
|
43
|
+
let r = Array.isArray(t[e]) ? t[e] : [];
|
|
44
|
+
return r.includes(n) ? t : {
|
|
45
|
+
...t,
|
|
46
|
+
[e]: [...r, n]
|
|
47
|
+
};
|
|
48
|
+
}), {
|
|
49
|
+
...t,
|
|
50
|
+
[e]: ""
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
}, []), W = e((e, t) => {
|
|
54
|
+
P((n) => {
|
|
55
|
+
let r = Array.isArray(n[e]) ? n[e] : [];
|
|
56
|
+
return {
|
|
57
|
+
...n,
|
|
58
|
+
[e]: r.filter((e) => e !== t)
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
}, []), G = e(async (e) => {
|
|
62
|
+
if (e.preventDefault(), L) return;
|
|
63
|
+
let t = { ...N };
|
|
64
|
+
for (let e of O) if (e.type === "tags") {
|
|
65
|
+
let n = (F[e.name] ?? "").trim();
|
|
66
|
+
if (n) {
|
|
67
|
+
let r = Array.isArray(t[e.name]) ? t[e.name] : [];
|
|
68
|
+
r.includes(n) || (t[e.name] = [...r, n]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
let n = O.find((e) => e.required && x(e, t[e.name]));
|
|
72
|
+
if (n) {
|
|
73
|
+
M(`${n.label} is required`, "warning");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
R(!0);
|
|
77
|
+
try {
|
|
78
|
+
await j(S(O, t)), T(!1);
|
|
79
|
+
} catch (e) {
|
|
80
|
+
M(e instanceof Error ? e.message : "Something went wrong", "error");
|
|
81
|
+
} finally {
|
|
82
|
+
R(!1);
|
|
83
|
+
}
|
|
84
|
+
}, [
|
|
85
|
+
O,
|
|
86
|
+
T,
|
|
87
|
+
j,
|
|
88
|
+
M,
|
|
89
|
+
L,
|
|
90
|
+
F,
|
|
91
|
+
N
|
|
92
|
+
]);
|
|
93
|
+
return /* @__PURE__ */ o(d, {
|
|
94
|
+
open: w,
|
|
95
|
+
onOpenChange: T,
|
|
96
|
+
children: /* @__PURE__ */ s(f, {
|
|
97
|
+
className: "max-h-[85vh] overflow-y-auto sm:max-w-[560px]",
|
|
98
|
+
children: [/* @__PURE__ */ s(h, { children: [/* @__PURE__ */ o(g, { children: E }), D ? /* @__PURE__ */ o(p, { children: D }) : null] }), /* @__PURE__ */ s("form", {
|
|
99
|
+
onSubmit: G,
|
|
100
|
+
className: "space-y-4",
|
|
101
|
+
children: [/* @__PURE__ */ o("div", {
|
|
102
|
+
className: "grid grid-cols-1 gap-4 md:grid-cols-2",
|
|
103
|
+
children: O.map((e) => {
|
|
104
|
+
let t = `formmodal-${e.name}`, n = e.colSpan === 2 || e.colSpan === void 0 && (e.type === "textarea" || e.type === "tags"), r = N[e.name];
|
|
105
|
+
return /* @__PURE__ */ o("div", {
|
|
106
|
+
className: `flex flex-col gap-1.5 ${n ? "md:col-span-2" : ""}`,
|
|
107
|
+
children: e.type === "switch" ? /* @__PURE__ */ s("div", {
|
|
108
|
+
className: "flex items-center justify-between gap-3 rounded-input border border-border-default bg-bg-surface px-3 py-2",
|
|
109
|
+
children: [/* @__PURE__ */ s(u, {
|
|
110
|
+
htmlFor: t,
|
|
111
|
+
children: [e.label, e.required ? /* @__PURE__ */ o("span", {
|
|
112
|
+
className: "text-status-error-fg",
|
|
113
|
+
children: " *"
|
|
114
|
+
}) : null]
|
|
115
|
+
}), /* @__PURE__ */ o(_, {
|
|
116
|
+
id: t,
|
|
117
|
+
checked: !!r,
|
|
118
|
+
onCheckedChange: (t) => H(e.name, t),
|
|
119
|
+
disabled: L
|
|
120
|
+
})]
|
|
121
|
+
}) : /* @__PURE__ */ s(a, { children: [/* @__PURE__ */ s(u, {
|
|
122
|
+
htmlFor: t,
|
|
123
|
+
children: [e.label, e.required ? /* @__PURE__ */ o("span", {
|
|
124
|
+
className: "text-status-error-fg",
|
|
125
|
+
children: " *"
|
|
126
|
+
}) : null]
|
|
127
|
+
}), e.type === "textarea" ? /* @__PURE__ */ o(v, {
|
|
128
|
+
id: t,
|
|
129
|
+
value: typeof r == "string" ? r : "",
|
|
130
|
+
placeholder: e.placeholder,
|
|
131
|
+
disabled: L,
|
|
132
|
+
onChange: (t) => H(e.name, t.target.value)
|
|
133
|
+
}) : e.type === "select" ? /* @__PURE__ */ s("select", {
|
|
134
|
+
id: t,
|
|
135
|
+
className: C,
|
|
136
|
+
value: typeof r == "string" ? r : "",
|
|
137
|
+
disabled: L,
|
|
138
|
+
onChange: (t) => H(e.name, t.target.value),
|
|
139
|
+
children: [/* @__PURE__ */ o("option", {
|
|
140
|
+
value: "",
|
|
141
|
+
disabled: !0,
|
|
142
|
+
children: e.placeholder ?? "Select…"
|
|
143
|
+
}), (e.options ?? []).map((e) => /* @__PURE__ */ o("option", {
|
|
144
|
+
value: e.value,
|
|
145
|
+
children: e.label
|
|
146
|
+
}, e.value))]
|
|
147
|
+
}) : e.type === "tags" ? /* @__PURE__ */ s("div", {
|
|
148
|
+
className: "flex min-h-10 flex-wrap items-center gap-1.5 rounded-input border border-border-default bg-bg-surface px-2 py-1.5",
|
|
149
|
+
children: [(Array.isArray(r) ? r : []).map((t) => /* @__PURE__ */ s("span", {
|
|
150
|
+
className: "inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-primary",
|
|
151
|
+
children: [t, /* @__PURE__ */ o("button", {
|
|
152
|
+
type: "button",
|
|
153
|
+
"aria-label": `Remove ${t}`,
|
|
154
|
+
className: "text-text-secondary hover:text-text-primary",
|
|
155
|
+
onClick: () => W(e.name, t),
|
|
156
|
+
disabled: L,
|
|
157
|
+
children: /* @__PURE__ */ o(i, { className: "h-3 w-3" })
|
|
158
|
+
})]
|
|
159
|
+
}, t)), /* @__PURE__ */ o("input", {
|
|
160
|
+
id: t,
|
|
161
|
+
className: "min-w-[6rem] flex-1 bg-transparent text-sm text-text-primary outline-none placeholder:text-text-placeholder",
|
|
162
|
+
value: F[e.name] ?? "",
|
|
163
|
+
placeholder: e.placeholder ?? "Add and press Enter",
|
|
164
|
+
disabled: L,
|
|
165
|
+
onChange: (t) => I((n) => ({
|
|
166
|
+
...n,
|
|
167
|
+
[e.name]: t.target.value
|
|
168
|
+
})),
|
|
169
|
+
onKeyDown: (t) => {
|
|
170
|
+
if (t.key === "Enter" || t.key === ",") t.preventDefault(), U(e.name);
|
|
171
|
+
else if (t.key === "Backspace" && (F[e.name] ?? "") === "") {
|
|
172
|
+
let t = Array.isArray(r) ? r : [];
|
|
173
|
+
t.length > 0 && W(e.name, t[t.length - 1]);
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
onBlur: () => U(e.name)
|
|
177
|
+
})]
|
|
178
|
+
}) : /* @__PURE__ */ o(l, {
|
|
179
|
+
id: t,
|
|
180
|
+
type: e.type === "number" ? "number" : e.type === "date" ? "date" : e.type === "email" ? "email" : "text",
|
|
181
|
+
value: typeof r == "string" ? r : "",
|
|
182
|
+
placeholder: e.placeholder,
|
|
183
|
+
disabled: L,
|
|
184
|
+
onChange: (t) => H(e.name, t.target.value)
|
|
185
|
+
})] })
|
|
186
|
+
}, e.name);
|
|
187
|
+
})
|
|
188
|
+
}), /* @__PURE__ */ s(m, { children: [/* @__PURE__ */ o(c, {
|
|
189
|
+
type: "button",
|
|
190
|
+
variant: "outline",
|
|
191
|
+
onClick: () => T(!1),
|
|
192
|
+
disabled: L,
|
|
193
|
+
children: "Cancel"
|
|
194
|
+
}), /* @__PURE__ */ o(c, {
|
|
195
|
+
type: "submit",
|
|
196
|
+
disabled: L,
|
|
197
|
+
children: L ? "Saving…" : A
|
|
198
|
+
})] })]
|
|
199
|
+
})]
|
|
200
|
+
})
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
function T() {
|
|
204
|
+
let [t, n] = r(!1), [i, a] = r(null);
|
|
205
|
+
return {
|
|
206
|
+
open: t,
|
|
207
|
+
context: i,
|
|
208
|
+
openModal: e((e) => {
|
|
209
|
+
a(e ?? null), n(!0);
|
|
210
|
+
}, []),
|
|
211
|
+
closeModal: e(() => n(!1), []),
|
|
212
|
+
onOpenChange: e((e) => {
|
|
213
|
+
n(e), e || a(null);
|
|
214
|
+
}, [])
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
//#endregion
|
|
218
|
+
export { w as FormModal, T as useFormModal };
|
|
219
|
+
|
|
220
|
+
//# sourceMappingURL=FormModal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormModal.js","names":[],"sources":["../../../../src/crewfoundry/components/forms/FormModal.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n Button,\n Input,\n Label,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n Switch,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { X } from 'lucide-react';\n\n// ── Public API ──────────────────────────────────────────────────\nexport type FormFieldType =\n | 'text'\n | 'number'\n | 'date'\n | 'email'\n | 'textarea'\n | 'select'\n | 'tags'\n | 'switch';\n\nexport type FormValue = string | number | boolean | string[];\nexport type FormValues = Record<string, FormValue>;\n\nexport interface FieldOption {\n label: string;\n value: string;\n}\n\nexport interface FieldConfig {\n name: string;\n label: string;\n type: FormFieldType;\n required?: boolean;\n placeholder?: string;\n options?: FieldOption[];\n /** Column span in the 2-col grid on md+ (defaults to 1; textarea/tags default to 2). */\n colSpan?: 1 | 2;\n}\n\nexport interface FormModalProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n title: string;\n description?: string;\n fields: FieldConfig[];\n initialValues?: FormValues;\n submitLabel?: string;\n onSubmit: (values: FormValues) => Promise<void>;\n}\n\n// ── Internal state helpers ──────────────────────────────────────\n// Text-like inputs are held as strings for controlled inputs; switches as\n// booleans; tags as string[]. Values are coerced back to their public shapes\n// on submit.\ntype InternalValue = string | boolean | string[];\ntype InternalValues = Record<string, InternalValue>;\n\n\nconst buildInitialState = (fields: FieldConfig[], initialValues?: FormValues): InternalValues => {\n const state: InternalValues = {};\n for (const field of fields) {\n const raw = initialValues?.[field.name];\n if (field.type === 'switch') {\n state[field.name] = Boolean(raw);\n } else if (field.type === 'tags') {\n if (Array.isArray(raw)) {\n state[field.name] = raw.map((item) => String(item));\n } else if (typeof raw === 'string' && raw.length > 0) {\n state[field.name] = raw\n .split(',')\n .map((item) => item.trim())\n .filter(Boolean);\n } else {\n state[field.name] = [];\n }\n } else {\n state[field.name] = raw === undefined || raw === null ? '' : String(raw);\n }\n }\n return state;\n};\n\nconst isEmptyValue = (field: FieldConfig, value: InternalValue): boolean => {\n if (field.type === 'switch') {\n return false;\n }\n if (field.type === 'tags') {\n return !Array.isArray(value) || value.length === 0;\n }\n return typeof value !== 'string' || value.trim() === '';\n};\n\nconst toOutput = (fields: FieldConfig[], state: InternalValues): FormValues => {\n const output: FormValues = {};\n for (const field of fields) {\n const value = state[field.name];\n if (field.type === 'switch') {\n output[field.name] = Boolean(value);\n } else if (field.type === 'tags') {\n output[field.name] = Array.isArray(value) ? value : [];\n } else if (field.type === 'number') {\n const raw = typeof value === 'string' ? value.trim() : '';\n output[field.name] = raw === '' ? '' : Number(raw);\n } else {\n output[field.name] = typeof value === 'string' ? value : '';\n }\n }\n return output;\n};\n\n// Native <select> styled with the same semantic tokens as fe-libs Input.\nconst SELECT_CLASS =\n 'flex h-10 w-full rounded-input border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary transition-colors duration-200 focus-visible:border-border-focus focus-visible:outline-none focus-visible:shadow-[var(--shadow-focus)] focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50';\n\n// ── Component ───────────────────────────────────────────────────\nexport function FormModal({\n open,\n onOpenChange,\n title,\n description,\n fields,\n initialValues,\n submitLabel = 'Save',\n onSubmit,\n}: FormModalProps) {\n const { showToast } = useGlobalUi();\n const [values, setValues] = useState<InternalValues>(() =>\n buildInitialState(fields, initialValues)\n );\n const [tagDrafts, setTagDrafts] = useState<Record<string, string>>({});\n const [submitting, setSubmitting] = useState(false);\n\n // Keep the latest field config / initial values reachable from the reset\n // effect without making them effect dependencies (page callers rarely\n // memoize these, and depending on them would loop or clobber edits).\n const fieldsRef = useRef(fields);\n const initialValuesRef = useRef(initialValues);\n const wasOpenRef = useRef(false);\n\n // Sync the latest field config / initial values into refs after each commit\n // (never during render). Declared before the reset effect so that on a rising\n // edge of `open` the refs are refreshed first, then read by the reset below.\n useEffect(() => {\n fieldsRef.current = fields;\n initialValuesRef.current = initialValues;\n }, [fields, initialValues]);\n\n // Reset the working copy on each rising edge of `open` so edit vs create\n // contexts never leak into each other.\n useEffect(() => {\n if (open && !wasOpenRef.current) {\n setValues(buildInitialState(fieldsRef.current, initialValuesRef.current));\n setTagDrafts({});\n setSubmitting(false);\n }\n wasOpenRef.current = open;\n }, [open]);\n\n const setFieldValue = useCallback((name: string, value: InternalValue) => {\n setValues((prev) => ({ ...prev, [name]: value }));\n }, []);\n\n const commitTag = useCallback(\n (name: string) => {\n setTagDrafts((prevDrafts) => {\n const draft = (prevDrafts[name] ?? '').trim();\n if (draft) {\n setValues((prev) => {\n const current = Array.isArray(prev[name]) ? (prev[name] as string[]) : [];\n if (current.includes(draft)) {\n return prev;\n }\n return { ...prev, [name]: [...current, draft] };\n });\n }\n return { ...prevDrafts, [name]: '' };\n });\n },\n []\n );\n\n const removeTag = useCallback((name: string, tag: string) => {\n setValues((prev) => {\n const current = Array.isArray(prev[name]) ? (prev[name] as string[]) : [];\n return { ...prev, [name]: current.filter((item) => item !== tag) };\n });\n }, []);\n\n const handleSubmit = useCallback(\n async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n if (submitting) {\n return;\n }\n\n // Fold any un-committed tag draft into its field before validating.\n const staged: InternalValues = { ...values };\n for (const field of fields) {\n if (field.type === 'tags') {\n const draft = (tagDrafts[field.name] ?? '').trim();\n if (draft) {\n const current = Array.isArray(staged[field.name])\n ? (staged[field.name] as string[])\n : [];\n if (!current.includes(draft)) {\n staged[field.name] = [...current, draft];\n }\n }\n }\n }\n\n const missing = fields.find((field) => field.required && isEmptyValue(field, staged[field.name]));\n if (missing) {\n showToast(`${missing.label} is required`, 'warning');\n return;\n }\n\n setSubmitting(true);\n try {\n await onSubmit(toOutput(fields, staged));\n onOpenChange(false);\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Something went wrong', 'error');\n } finally {\n setSubmitting(false);\n }\n },\n [fields, onOpenChange, onSubmit, showToast, submitting, tagDrafts, values]\n );\n\n return (\n <ResponsiveDialog open={open} onOpenChange={onOpenChange}>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-[560px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{title}</ResponsiveDialogTitle>\n {description ? (\n <ResponsiveDialogDescription>{description}</ResponsiveDialogDescription>\n ) : null}\n </ResponsiveDialogHeader>\n\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n {fields.map((field) => {\n const fieldId = `formmodal-${field.name}`;\n const spanTwo =\n field.colSpan === 2 ||\n (field.colSpan === undefined && (field.type === 'textarea' || field.type === 'tags'));\n const value = values[field.name];\n\n return (\n <div\n key={field.name}\n className={`flex flex-col gap-1.5 ${spanTwo ? 'md:col-span-2' : ''}`}\n >\n {field.type === 'switch' ? (\n <div className=\"flex items-center justify-between gap-3 rounded-input border border-border-default bg-bg-surface px-3 py-2\">\n <Label htmlFor={fieldId}>\n {field.label}\n {field.required ? <span className=\"text-status-error-fg\"> *</span> : null}\n </Label>\n <Switch\n id={fieldId}\n checked={Boolean(value)}\n onCheckedChange={(checked) => setFieldValue(field.name, checked)}\n disabled={submitting}\n />\n </div>\n ) : (\n <>\n <Label htmlFor={fieldId}>\n {field.label}\n {field.required ? <span className=\"text-status-error-fg\"> *</span> : null}\n </Label>\n\n {field.type === 'textarea' ? (\n <Textarea\n id={fieldId}\n value={typeof value === 'string' ? value : ''}\n placeholder={field.placeholder}\n disabled={submitting}\n onChange={(event) => setFieldValue(field.name, event.target.value)}\n />\n ) : field.type === 'select' ? (\n <select\n id={fieldId}\n className={SELECT_CLASS}\n value={typeof value === 'string' ? value : ''}\n disabled={submitting}\n onChange={(event) => setFieldValue(field.name, event.target.value)}\n >\n <option value=\"\" disabled>\n {field.placeholder ?? 'Select…'}\n </option>\n {(field.options ?? []).map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n ) : field.type === 'tags' ? (\n <div className=\"flex min-h-10 flex-wrap items-center gap-1.5 rounded-input border border-border-default bg-bg-surface px-2 py-1.5\">\n {(Array.isArray(value) ? value : []).map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-primary\"\n >\n {tag}\n <button\n type=\"button\"\n aria-label={`Remove ${tag}`}\n className=\"text-text-secondary hover:text-text-primary\"\n onClick={() => removeTag(field.name, tag)}\n disabled={submitting}\n >\n <X className=\"h-3 w-3\" />\n </button>\n </span>\n ))}\n <input\n id={fieldId}\n className=\"min-w-[6rem] flex-1 bg-transparent text-sm text-text-primary outline-none placeholder:text-text-placeholder\"\n value={tagDrafts[field.name] ?? ''}\n placeholder={field.placeholder ?? 'Add and press Enter'}\n disabled={submitting}\n onChange={(event) =>\n setTagDrafts((prev) => ({ ...prev, [field.name]: event.target.value }))\n }\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ',') {\n event.preventDefault();\n commitTag(field.name);\n } else if (\n event.key === 'Backspace' &&\n (tagDrafts[field.name] ?? '') === ''\n ) {\n const current = Array.isArray(value) ? value : [];\n if (current.length > 0) {\n removeTag(field.name, current[current.length - 1]);\n }\n }\n }}\n onBlur={() => commitTag(field.name)}\n />\n </div>\n ) : (\n <Input\n id={fieldId}\n type={\n field.type === 'number'\n ? 'number'\n : field.type === 'date'\n ? 'date'\n : field.type === 'email'\n ? 'email'\n : 'text'\n }\n value={typeof value === 'string' ? value : ''}\n placeholder={field.placeholder}\n disabled={submitting}\n onChange={(event) => setFieldValue(field.name, event.target.value)}\n />\n )}\n </>\n )}\n </div>\n );\n })}\n </div>\n\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => onOpenChange(false)}\n disabled={submitting}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={submitting}>\n {submitting ? 'Saving…' : submitLabel}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\n// ── Optional open/close helper ──────────────────────────────────\nexport interface UseFormModalResult<TContext> {\n open: boolean;\n /** The row/context passed to openModal (e.g. the entity being edited). */\n context: TContext | null;\n openModal: (context?: TContext) => void;\n closeModal: () => void;\n onOpenChange: (open: boolean) => void;\n}\n\n/**\n * Tiny helper for pages that toggle a single FormModal and want to remember\n * which row is being edited. `context` is null for a create flow.\n */\nexport function useFormModal<TContext = void>(): UseFormModalResult<TContext> {\n const [open, setOpen] = useState(false);\n const [context, setContext] = useState<TContext | null>(null);\n\n const openModal = useCallback((next?: TContext) => {\n setContext(next ?? null);\n setOpen(true);\n }, []);\n\n const closeModal = useCallback(() => setOpen(false), []);\n\n const onOpenChange = useCallback((nextOpen: boolean) => {\n setOpen(nextOpen);\n if (!nextOpen) {\n setContext(null);\n }\n }, []);\n\n return { open, context, openModal, closeModal, onOpenChange };\n}\n"],"mappings":";;;;;;AAkEA,IAAM,KAAqB,GAAuB,MAA+C;CAC/F,IAAM,IAAwB,EAAE;AAChC,MAAK,IAAM,KAAS,GAAQ;EAC1B,IAAM,IAAM,IAAgB,EAAM;AAClC,EAAI,EAAM,SAAS,WACjB,EAAM,EAAM,QAAQ,EAAQ,IACnB,EAAM,SAAS,SACpB,MAAM,QAAQ,EAAI,GACpB,EAAM,EAAM,QAAQ,EAAI,KAAK,MAAS,OAAO,EAAK,CAAC,GAC1C,OAAO,KAAQ,YAAY,EAAI,SAAS,IACjD,EAAM,EAAM,QAAQ,EACjB,MAAM,IAAI,CACV,KAAK,MAAS,EAAK,MAAM,CAAC,CAC1B,OAAO,QAAQ,GAElB,EAAM,EAAM,QAAQ,EAAE,GAGxB,EAAM,EAAM,QAAQ,KAA6B,OAAO,KAAK,OAAO,EAAI;;AAG5E,QAAO;GAGH,KAAgB,GAAoB,MACpC,EAAM,SAAS,WACV,KAEL,EAAM,SAAS,SACV,CAAC,MAAM,QAAQ,EAAM,IAAI,EAAM,WAAW,IAE5C,OAAO,KAAU,YAAY,EAAM,MAAM,KAAK,IAGjD,KAAY,GAAuB,MAAsC;CAC7E,IAAM,IAAqB,EAAE;AAC7B,MAAK,IAAM,KAAS,GAAQ;EAC1B,IAAM,IAAQ,EAAM,EAAM;AAC1B,MAAI,EAAM,SAAS,SACjB,GAAO,EAAM,QAAQ,EAAQ;WACpB,EAAM,SAAS,OACxB,GAAO,EAAM,QAAQ,MAAM,QAAQ,EAAM,GAAG,IAAQ,EAAE;WAC7C,EAAM,SAAS,UAAU;GAClC,IAAM,IAAM,OAAO,KAAU,WAAW,EAAM,MAAM,GAAG;AACvD,KAAO,EAAM,QAAQ,MAAQ,KAAK,KAAK,OAAO,EAAI;QAElD,GAAO,EAAM,QAAQ,OAAO,KAAU,WAAW,IAAQ;;AAG7D,QAAO;GAIH,IACJ;AAGF,SAAgB,EAAU,EACxB,SACA,iBACA,UACA,gBACA,WACA,kBACA,iBAAc,QACd,eACiB;CACjB,IAAM,EAAE,iBAAc,GAAa,EAC7B,CAAC,GAAQ,KAAa,QAC1B,EAAkB,GAAQ,EAAc,CACzC,EACK,CAAC,GAAW,KAAgB,EAAiC,EAAE,CAAC,EAChE,CAAC,GAAY,KAAiB,EAAS,GAAM,EAK7C,IAAY,EAAO,EAAO,EAC1B,IAAmB,EAAO,EAAc,EACxC,IAAa,EAAO,GAAM;AAYhC,CAPA,QAAgB;AAEd,EADA,EAAU,UAAU,GACpB,EAAiB,UAAU;IAC1B,CAAC,GAAQ,EAAc,CAAC,EAI3B,QAAgB;AAMd,EALI,KAAQ,CAAC,EAAW,YACtB,EAAU,EAAkB,EAAU,SAAS,EAAiB,QAAQ,CAAC,EACzE,EAAa,EAAE,CAAC,EAChB,EAAc,GAAM,GAEtB,EAAW,UAAU;IACpB,CAAC,EAAK,CAAC;CAEV,IAAM,IAAgB,GAAa,GAAc,MAAyB;AACxE,KAAW,OAAU;GAAE,GAAG;IAAO,IAAO;GAAO,EAAE;IAChD,EAAE,CAAC,EAEA,IAAY,GACf,MAAiB;AAChB,KAAc,MAAe;GAC3B,IAAM,KAAS,EAAW,MAAS,IAAI,MAAM;AAU7C,UATI,KACF,GAAW,MAAS;IAClB,IAAM,IAAU,MAAM,QAAQ,EAAK,GAAM,GAAI,EAAK,KAAqB,EAAE;AAIzE,WAHI,EAAQ,SAAS,EAAM,GAClB,IAEF;KAAE,GAAG;MAAO,IAAO,CAAC,GAAG,GAAS,EAAM;KAAE;KAC/C,EAEG;IAAE,GAAG;KAAa,IAAO;IAAI;IACpC;IAEJ,EAAE,CACH,EAEK,IAAY,GAAa,GAAc,MAAgB;AAC3D,KAAW,MAAS;GAClB,IAAM,IAAU,MAAM,QAAQ,EAAK,GAAM,GAAI,EAAK,KAAqB,EAAE;AACzE,UAAO;IAAE,GAAG;KAAO,IAAO,EAAQ,QAAQ,MAAS,MAAS,EAAI;IAAE;IAClE;IACD,EAAE,CAAC,EAEA,IAAe,EACnB,OAAO,MAA4C;AAEjD,MADA,EAAM,gBAAgB,EAClB,EACF;EAIF,IAAM,IAAyB,EAAE,GAAG,GAAQ;AAC5C,OAAK,IAAM,KAAS,EAClB,KAAI,EAAM,SAAS,QAAQ;GACzB,IAAM,KAAS,EAAU,EAAM,SAAS,IAAI,MAAM;AAClD,OAAI,GAAO;IACT,IAAM,IAAU,MAAM,QAAQ,EAAO,EAAM,MAAM,GAC5C,EAAO,EAAM,QACd,EAAE;AACN,IAAK,EAAQ,SAAS,EAAM,KAC1B,EAAO,EAAM,QAAQ,CAAC,GAAG,GAAS,EAAM;;;EAMhD,IAAM,IAAU,EAAO,MAAM,MAAU,EAAM,YAAY,EAAa,GAAO,EAAO,EAAM,MAAM,CAAC;AACjG,MAAI,GAAS;AACX,KAAU,GAAG,EAAQ,MAAM,eAAe,UAAU;AACpD;;AAGF,IAAc,GAAK;AACnB,MAAI;AAEF,GADA,MAAM,EAAS,EAAS,GAAQ,EAAO,CAAC,EACxC,EAAa,GAAM;WACZ,GAAO;AACd,KAAU,aAAiB,QAAQ,EAAM,UAAU,wBAAwB,QAAQ;YAC3E;AACR,KAAc,GAAM;;IAGxB;EAAC;EAAQ;EAAc;EAAU;EAAW;EAAY;EAAW;EAAO,CAC3E;AAED,QACE,kBAAC,GAAD;EAAwB;EAAoB;YAC1C,kBAAC,GAAD;GAAyB,WAAU;aAAnC,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAwB,GAA8B,CAAA,EACrD,IACC,kBAAC,GAAD,EAAA,UAA8B,GAA0C,CAAA,GACtE,KACmB,EAAA,CAAA,EAEzB,kBAAC,QAAD;IAAM,UAAU;IAAc,WAAU;cAAxC,CACE,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAO,KAAK,MAAU;MACrB,IAAM,IAAU,aAAa,EAAM,QAC7B,IACJ,EAAM,YAAY,KACjB,EAAM,YAAY,KAAA,MAAc,EAAM,SAAS,cAAc,EAAM,SAAS,SACzE,IAAQ,EAAO,EAAM;AAE3B,aACE,kBAAC,OAAD;OAEE,WAAW,yBAAyB,IAAU,kBAAkB;iBAE/D,EAAM,SAAS,WACd,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAO,SAAS;mBAAhB,CACG,EAAM,OACN,EAAM,WAAW,kBAAC,QAAD;UAAM,WAAU;oBAAuB;UAAS,CAAA,GAAG,KAC/D;YACR,kBAAC,GAAD;SACE,IAAI;SACJ,SAAS,EAAQ;SACjB,kBAAkB,MAAY,EAAc,EAAM,MAAM,EAAQ;SAChE,UAAU;SACV,CAAA,CACE;YAEN,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;QAAO,SAAS;kBAAhB,CACG,EAAM,OACN,EAAM,WAAW,kBAAC,QAAD;SAAM,WAAU;mBAAuB;SAAS,CAAA,GAAG,KAC/D;WAEP,EAAM,SAAS,aACd,kBAAC,GAAD;QACE,IAAI;QACJ,OAAO,OAAO,KAAU,WAAW,IAAQ;QAC3C,aAAa,EAAM;QACnB,UAAU;QACV,WAAW,MAAU,EAAc,EAAM,MAAM,EAAM,OAAO,MAAM;QAClE,CAAA,GACA,EAAM,SAAS,WACjB,kBAAC,UAAD;QACE,IAAI;QACJ,WAAW;QACX,OAAO,OAAO,KAAU,WAAW,IAAQ;QAC3C,UAAU;QACV,WAAW,MAAU,EAAc,EAAM,MAAM,EAAM,OAAO,MAAM;kBALpE,CAOE,kBAAC,UAAD;SAAQ,OAAM;SAAG,UAAA;mBACd,EAAM,eAAe;SACf,CAAA,GACP,EAAM,WAAW,EAAE,EAAE,KAAK,MAC1B,kBAAC,UAAD;SAA2B,OAAO,EAAO;mBACtC,EAAO;SACD,EAFI,EAAO,MAEX,CACT,CACK;YACP,EAAM,SAAS,SACjB,kBAAC,OAAD;QAAK,WAAU;kBAAf,EACI,MAAM,QAAQ,EAAM,GAAG,IAAQ,EAAE,EAAE,KAAK,MACxC,kBAAC,QAAD;SAEE,WAAU;mBAFZ,CAIG,GACD,kBAAC,UAAD;UACE,MAAK;UACL,cAAY,UAAU;UACtB,WAAU;UACV,eAAe,EAAU,EAAM,MAAM,EAAI;UACzC,UAAU;oBAEV,kBAAC,GAAD,EAAG,WAAU,WAAY,CAAA;UAClB,CAAA,CACJ;WAbA,EAaA,CACP,EACF,kBAAC,SAAD;SACE,IAAI;SACJ,WAAU;SACV,OAAO,EAAU,EAAM,SAAS;SAChC,aAAa,EAAM,eAAe;SAClC,UAAU;SACV,WAAW,MACT,GAAc,OAAU;UAAE,GAAG;WAAO,EAAM,OAAO,EAAM,OAAO;UAAO,EAAE;SAEzE,YAAY,MAAU;AACpB,cAAI,EAAM,QAAQ,WAAW,EAAM,QAAQ,IAEzC,CADA,EAAM,gBAAgB,EACtB,EAAU,EAAM,KAAK;mBAErB,EAAM,QAAQ,gBACb,EAAU,EAAM,SAAS,QAAQ,IAClC;WACA,IAAM,IAAU,MAAM,QAAQ,EAAM,GAAG,IAAQ,EAAE;AACjD,WAAI,EAAQ,SAAS,KACnB,EAAU,EAAM,MAAM,EAAQ,EAAQ,SAAS,GAAG;;;SAIxD,cAAc,EAAU,EAAM,KAAK;SACnC,CAAA,CACE;YAEN,kBAAC,GAAD;QACE,IAAI;QACJ,MACE,EAAM,SAAS,WACX,WACA,EAAM,SAAS,SACb,SACA,EAAM,SAAS,UACb,UACA;QAEV,OAAO,OAAO,KAAU,WAAW,IAAQ;QAC3C,aAAa,EAAM;QACnB,UAAU;QACV,WAAW,MAAU,EAAc,EAAM,MAAM,EAAM,OAAO,MAAM;QAClE,CAAA,CAEH,EAAA,CAAA;OAED,EAjHC,EAAM,KAiHP;OAER;KACE,CAAA,EAEN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;KACE,MAAK;KACL,SAAQ;KACR,eAAe,EAAa,GAAM;KAClC,UAAU;eACX;KAEQ,CAAA,EACT,kBAAC,GAAD;KAAQ,MAAK;KAAS,UAAU;eAC7B,IAAa,YAAY;KACnB,CAAA,CACc,EAAA,CAAA,CACpB;MACiB;;EACT,CAAA;;AAkBvB,SAAgB,IAA8D;CAC5E,IAAM,CAAC,GAAM,KAAW,EAAS,GAAM,EACjC,CAAC,GAAS,KAAc,EAA0B,KAAK;AAgB7D,QAAO;EAAE;EAAM;EAAS,WAdN,GAAa,MAAoB;AAEjD,GADA,EAAW,KAAQ,KAAK,EACxB,EAAQ,GAAK;KACZ,EAAE,CAAC;EAW6B,YAThB,QAAkB,EAAQ,GAAM,EAAE,EAAE,CAAC;EAST,cAP1B,GAAa,MAAsB;AAEtD,GADA,EAAQ,EAAS,EACZ,KACH,EAAW,KAAK;KAEjB,EAAE,CAAC;EAEuD"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region src/crewfoundry/components/forms/formValues.ts
|
|
2
|
+
var e = (e, t) => {
|
|
3
|
+
let n = e[t];
|
|
4
|
+
return typeof n == "string" ? n : "";
|
|
5
|
+
}, t = (e, t) => {
|
|
6
|
+
let n = e[t];
|
|
7
|
+
if (typeof n == "number") return Number.isFinite(n) ? n : 0;
|
|
8
|
+
if (typeof n == "string" && n.trim() !== "") {
|
|
9
|
+
let e = Number(n);
|
|
10
|
+
return Number.isFinite(e) ? e : 0;
|
|
11
|
+
}
|
|
12
|
+
return 0;
|
|
13
|
+
}, n = (e, t) => {
|
|
14
|
+
let n = e[t];
|
|
15
|
+
return Array.isArray(n) ? n : [];
|
|
16
|
+
}, r = () => typeof crypto < "u" && "randomUUID" in crypto ? crypto.randomUUID() : `tmp-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { t as getNumber, n as getTags, e as getText, r as newClientId };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=formValues.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formValues.js","names":[],"sources":["../../../../src/crewfoundry/components/forms/formValues.ts"],"sourcesContent":["import type { FormValues } from './FormModal';\n\n// Coercion helpers for reading typed values back out of a FormModal submit.\n// FormModal emits text-like fields as strings, number fields as number | '',\n// tags as string[], and switches as boolean.\n\nexport const getText = (values: FormValues, key: string): string => {\n const value = values[key];\n return typeof value === 'string' ? value : '';\n};\n\nexport const getNumber = (values: FormValues, key: string): number => {\n const value = values[key];\n if (typeof value === 'number') {\n return Number.isFinite(value) ? value : 0;\n }\n if (typeof value === 'string' && value.trim() !== '') {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : 0;\n }\n return 0;\n};\n\nexport const getTags = (values: FormValues, key: string): string[] => {\n const value = values[key];\n return Array.isArray(value) ? value : [];\n};\n\nexport const getBool = (values: FormValues, key: string): boolean => {\n const value = values[key];\n return typeof value === 'boolean' ? value : false;\n};\n\n// A stable client-generated id for optimistic rows. The server returns the\n// authoritative row (which replaces this) on success.\nexport const newClientId = (): string =>\n typeof crypto !== 'undefined' && 'randomUUID' in crypto\n ? crypto.randomUUID()\n : `tmp-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n"],"mappings":";AAMA,IAAa,KAAW,GAAoB,MAAwB;CAClE,IAAM,IAAQ,EAAO;AACrB,QAAO,OAAO,KAAU,WAAW,IAAQ;GAGhC,KAAa,GAAoB,MAAwB;CACpE,IAAM,IAAQ,EAAO;AACrB,KAAI,OAAO,KAAU,SACnB,QAAO,OAAO,SAAS,EAAM,GAAG,IAAQ;AAE1C,KAAI,OAAO,KAAU,YAAY,EAAM,MAAM,KAAK,IAAI;EACpD,IAAM,IAAS,OAAO,EAAM;AAC5B,SAAO,OAAO,SAAS,EAAO,GAAG,IAAS;;AAE5C,QAAO;GAGI,KAAW,GAAoB,MAA0B;CACpE,IAAM,IAAQ,EAAO;AACrB,QAAO,MAAM,QAAQ,EAAM,GAAG,IAAQ,EAAE;GAU7B,UACX,OAAO,SAAW,OAAe,gBAAgB,SAC7C,OAAO,YAAY,GACnB,OAAO,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE"}
|