formanitor 0.0.47 → 0.0.49
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.cjs +677 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +99 -4
- package/dist/index.d.ts +99 -4
- package/dist/index.mjs +678 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -67,8 +67,25 @@ function fieldMetaRequestsMarMedicationOrdersButton(meta) {
|
|
|
67
67
|
|
|
68
68
|
// src/core/validate.ts
|
|
69
69
|
var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
70
|
-
function validateField(value, field) {
|
|
70
|
+
function validateField(value, field, allValues) {
|
|
71
71
|
if (field.hidden || field.disabled) return null;
|
|
72
|
+
if (field.type === "otp_input") {
|
|
73
|
+
if (field.required) {
|
|
74
|
+
if (!value || typeof value !== "object" || value.verified !== true) {
|
|
75
|
+
return "Please verify your mobile number with OTP";
|
|
76
|
+
}
|
|
77
|
+
const otpField = field;
|
|
78
|
+
const phoneFieldId = otpField.meta?.phoneFieldId ?? "phone";
|
|
79
|
+
if (allValues) {
|
|
80
|
+
const phoneRaw = allValues[phoneFieldId] ?? "";
|
|
81
|
+
const e164 = phoneRaw.startsWith("+") ? phoneRaw : `+91${phoneRaw}`;
|
|
82
|
+
if (value.phone && value.phone !== e164) {
|
|
83
|
+
return "Mobile number changed \u2014 please re-verify";
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
72
89
|
if (field.required) {
|
|
73
90
|
if (value === null || value === void 0 || value === "") {
|
|
74
91
|
return "This field is required";
|
|
@@ -129,7 +146,7 @@ function validateForm(values, fields) {
|
|
|
129
146
|
const errors = {};
|
|
130
147
|
for (const [key, field] of Object.entries(fields)) {
|
|
131
148
|
if (field.type === "static_text") continue;
|
|
132
|
-
const error = validateField(values[key], field);
|
|
149
|
+
const error = validateField(values[key], field, values);
|
|
133
150
|
if (error) {
|
|
134
151
|
errors[key] = error;
|
|
135
152
|
}
|
|
@@ -1206,6 +1223,9 @@ var FormStore = class {
|
|
|
1206
1223
|
persistence;
|
|
1207
1224
|
prefillData = {};
|
|
1208
1225
|
config;
|
|
1226
|
+
// Widget-owned errors that schema validation cannot express (e.g. "OTP not sent yet").
|
|
1227
|
+
// Merged on top of validateForm output in evaluate().
|
|
1228
|
+
externalErrors = {};
|
|
1209
1229
|
// Cache for debounce
|
|
1210
1230
|
saveTimeout = null;
|
|
1211
1231
|
draftCallbackTimeout = null;
|
|
@@ -1325,6 +1345,9 @@ var FormStore = class {
|
|
|
1325
1345
|
getSchema() {
|
|
1326
1346
|
return this.schema;
|
|
1327
1347
|
}
|
|
1348
|
+
getConfig() {
|
|
1349
|
+
return this.config;
|
|
1350
|
+
}
|
|
1328
1351
|
getFieldDef(fieldId) {
|
|
1329
1352
|
return this.fieldMap[fieldId];
|
|
1330
1353
|
}
|
|
@@ -1372,6 +1395,17 @@ var FormStore = class {
|
|
|
1372
1395
|
this.notify();
|
|
1373
1396
|
this.scheduleSave();
|
|
1374
1397
|
}
|
|
1398
|
+
/**
|
|
1399
|
+
* Let a widget report an error that schema validation cannot express
|
|
1400
|
+
* (e.g. "Send OTP to continue" until the API call succeeds).
|
|
1401
|
+
* Pass `null` to clear a previously set external error.
|
|
1402
|
+
*/
|
|
1403
|
+
setExternalError(fieldId, message) {
|
|
1404
|
+
if ((this.externalErrors[fieldId] ?? null) === message) return;
|
|
1405
|
+
this.externalErrors = { ...this.externalErrors, [fieldId]: message };
|
|
1406
|
+
this.evaluate();
|
|
1407
|
+
this.notify();
|
|
1408
|
+
}
|
|
1375
1409
|
setTouched(fieldId) {
|
|
1376
1410
|
if (this.state.touched[fieldId]) return;
|
|
1377
1411
|
this.state.touched = { ...this.state.touched, [fieldId]: true };
|
|
@@ -1500,6 +1534,20 @@ var FormStore = class {
|
|
|
1500
1534
|
result[field.id] = parts.length > 0 ? parts.join("\n\n") : null;
|
|
1501
1535
|
return;
|
|
1502
1536
|
}
|
|
1537
|
+
if (field.type === "otp_input") {
|
|
1538
|
+
const raw = values[field.id];
|
|
1539
|
+
if (typeof raw === "string") {
|
|
1540
|
+
result[field.id] = raw;
|
|
1541
|
+
return;
|
|
1542
|
+
}
|
|
1543
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw) && raw.verified === true) {
|
|
1544
|
+
const code = raw.code;
|
|
1545
|
+
result[field.id] = typeof code === "string" ? code : null;
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1548
|
+
result[field.id] = null;
|
|
1549
|
+
return;
|
|
1550
|
+
}
|
|
1503
1551
|
if (field.type === "image_upload" || field.type === "media_upload" || field.type === "signature") {
|
|
1504
1552
|
const raw = values[field.id];
|
|
1505
1553
|
if (raw !== void 0 && raw !== null && raw !== "") {
|
|
@@ -1616,8 +1664,12 @@ var FormStore = class {
|
|
|
1616
1664
|
};
|
|
1617
1665
|
});
|
|
1618
1666
|
const errors = validateForm(this.state.values, effectiveFields);
|
|
1619
|
-
|
|
1620
|
-
|
|
1667
|
+
const merged = { ...errors };
|
|
1668
|
+
for (const [id, msg] of Object.entries(this.externalErrors)) {
|
|
1669
|
+
if (msg) merged[id] = msg;
|
|
1670
|
+
}
|
|
1671
|
+
this.state.errors = merged;
|
|
1672
|
+
this.state.isValid = Object.keys(merged).length === 0;
|
|
1621
1673
|
}
|
|
1622
1674
|
// --- Workflow ---
|
|
1623
1675
|
getAvailableTransitions() {
|
|
@@ -5874,15 +5926,21 @@ var UNIT_OPTIONS = [
|
|
|
5874
5926
|
{ value: "weeks", label: "Weeks" },
|
|
5875
5927
|
{ value: "months", label: "Months" }
|
|
5876
5928
|
];
|
|
5929
|
+
var DAYS_PER_MONTH = 30;
|
|
5877
5930
|
function durationToDays(valueNum, unit) {
|
|
5878
5931
|
const today = /* @__PURE__ */ new Date();
|
|
5879
5932
|
today.setHours(0, 0, 0, 0);
|
|
5880
5933
|
let target;
|
|
5881
5934
|
if (unit === "days") target = dateFns.addDays(today, valueNum);
|
|
5882
5935
|
else if (unit === "weeks") target = dateFns.addWeeks(today, valueNum);
|
|
5883
|
-
else target = dateFns.
|
|
5936
|
+
else target = dateFns.addDays(today, valueNum * DAYS_PER_MONTH);
|
|
5884
5937
|
return Math.max(0, dateFns.differenceInCalendarDays(target, today));
|
|
5885
5938
|
}
|
|
5939
|
+
function daysToDisplayCount(daysNum, unit) {
|
|
5940
|
+
if (unit === "days") return Math.max(0, daysNum);
|
|
5941
|
+
if (unit === "weeks") return Math.round(daysNum / 7) || 1;
|
|
5942
|
+
return Math.round(daysNum / DAYS_PER_MONTH) || 1;
|
|
5943
|
+
}
|
|
5886
5944
|
function dateToDays(date) {
|
|
5887
5945
|
const today = /* @__PURE__ */ new Date();
|
|
5888
5946
|
today.setHours(0, 0, 0, 0);
|
|
@@ -5950,9 +6008,7 @@ var FollowupWidget = ({ fieldId }) => {
|
|
|
5950
6008
|
const daysNum = hasFollowup ? parseInt(followup.value, 10) || 7 : 7;
|
|
5951
6009
|
const currentDate = hasFollowup ? daysToDate(daysNum) : null;
|
|
5952
6010
|
const followupMode = displayMode;
|
|
5953
|
-
const currentValue = hasFollowup
|
|
5954
|
-
displayUnit === "weeks" ? Math.round(daysNum / 7) || 1 : Math.round(daysNum / 30) || 1
|
|
5955
|
-
) : "7";
|
|
6011
|
+
const currentValue = hasFollowup ? String(daysToDisplayCount(daysNum, displayUnit)) : "7";
|
|
5956
6012
|
const currentUnit = displayUnit;
|
|
5957
6013
|
const handleFollowupTypeChange = React15.useCallback(
|
|
5958
6014
|
(newType) => {
|
|
@@ -5989,9 +6045,18 @@ var FollowupWidget = ({ fieldId }) => {
|
|
|
5989
6045
|
},
|
|
5990
6046
|
[displayUnit, setValue, setTouched]
|
|
5991
6047
|
);
|
|
5992
|
-
const handleUnitChange = React15.useCallback(
|
|
5993
|
-
|
|
5994
|
-
|
|
6048
|
+
const handleUnitChange = React15.useCallback(
|
|
6049
|
+
(newUnit) => {
|
|
6050
|
+
if (newUnit === displayUnit) return;
|
|
6051
|
+
const n = daysToDisplayCount(daysNum, displayUnit);
|
|
6052
|
+
setDisplayUnit(newUnit);
|
|
6053
|
+
if (!hasFollowup) return;
|
|
6054
|
+
setTouched();
|
|
6055
|
+
const newDays = durationToDays(n, newUnit);
|
|
6056
|
+
setValue({ followupType: "one time", value: String(newDays), unit: "days" });
|
|
6057
|
+
},
|
|
6058
|
+
[displayUnit, daysNum, hasFollowup, setValue, setTouched]
|
|
6059
|
+
);
|
|
5995
6060
|
const handleDateSelect = React15.useCallback(
|
|
5996
6061
|
(selected) => {
|
|
5997
6062
|
setTouched();
|
|
@@ -13797,6 +13862,347 @@ var OBGPathwayWidget = ({ fieldId }) => {
|
|
|
13797
13862
|
] })
|
|
13798
13863
|
] });
|
|
13799
13864
|
};
|
|
13865
|
+
var RESEND_COOLDOWN_SECONDS = 30;
|
|
13866
|
+
var PhoneInputWidget = ({ fieldId }) => {
|
|
13867
|
+
const { fieldDef, error, touched, disabled, value: fieldValue } = useField(fieldId);
|
|
13868
|
+
const { state } = useForm();
|
|
13869
|
+
const store = useFormStore();
|
|
13870
|
+
const rawPhone = fieldValue ?? "";
|
|
13871
|
+
const wasAlreadySent = rawPhone.length === 10 && !error;
|
|
13872
|
+
const [status, setStatus] = React15.useState(wasAlreadySent ? "sent" : "idle");
|
|
13873
|
+
const [resendCooldown, setResendCooldown] = React15.useState(0);
|
|
13874
|
+
const [resendError, setResendError] = React15.useState("");
|
|
13875
|
+
const hasSentRef = React15.useRef(wasAlreadySent);
|
|
13876
|
+
const sentPhoneRef = React15.useRef(wasAlreadySent ? rawPhone : "");
|
|
13877
|
+
const isSendingRef = React15.useRef(false);
|
|
13878
|
+
const isResendingRef = React15.useRef(false);
|
|
13879
|
+
const showError = !!error && (touched || state.submitAttempted) && status !== "sending" && status !== "sent";
|
|
13880
|
+
React15.useEffect(() => {
|
|
13881
|
+
if (resendCooldown <= 0) return;
|
|
13882
|
+
const t = setTimeout(() => setResendCooldown((c) => Math.max(0, c - 1)), 1e3);
|
|
13883
|
+
return () => clearTimeout(t);
|
|
13884
|
+
}, [resendCooldown]);
|
|
13885
|
+
if (!fieldDef) return null;
|
|
13886
|
+
const placeholder = typeof fieldDef.placeholder === "string" && fieldDef.placeholder.trim() ? fieldDef.placeholder : "Enter 10-digit mobile number";
|
|
13887
|
+
const handleChange = (e) => {
|
|
13888
|
+
const digits = e.target.value.replace(/\D/g, "").slice(0, 10);
|
|
13889
|
+
if (hasSentRef.current && digits !== sentPhoneRef.current) {
|
|
13890
|
+
hasSentRef.current = false;
|
|
13891
|
+
sentPhoneRef.current = "";
|
|
13892
|
+
setStatus("idle");
|
|
13893
|
+
setResendCooldown(0);
|
|
13894
|
+
setResendError("");
|
|
13895
|
+
store.setExternalError(fieldId, "Send OTP to continue");
|
|
13896
|
+
const otpFieldId = fieldDef.meta?.otpFieldId;
|
|
13897
|
+
if (otpFieldId) {
|
|
13898
|
+
store.setValue(otpFieldId, "");
|
|
13899
|
+
store.setExternalError(otpFieldId, "Verify OTP to continue");
|
|
13900
|
+
}
|
|
13901
|
+
}
|
|
13902
|
+
store.setValue(fieldId, digits);
|
|
13903
|
+
if (digits.length === 10 && !hasSentRef.current && !isSendingRef.current) {
|
|
13904
|
+
sendOtp(digits);
|
|
13905
|
+
}
|
|
13906
|
+
};
|
|
13907
|
+
const sendOtp = async (phone) => {
|
|
13908
|
+
if (isSendingRef.current) return;
|
|
13909
|
+
isSendingRef.current = true;
|
|
13910
|
+
setStatus("sending");
|
|
13911
|
+
store.setExternalError(fieldId, "Sending OTP...");
|
|
13912
|
+
try {
|
|
13913
|
+
const authCfg = store.getConfig().auth;
|
|
13914
|
+
const base = authCfg?.baseUrl ?? ((typeof process !== "undefined" && process.env?.NEXT_PUBLIC_API_URL) ?? "");
|
|
13915
|
+
const sendPath = authCfg?.sendPath ?? "/api/auth/otp/send/";
|
|
13916
|
+
const countryCode = authCfg?.defaultCountryCode ?? "+91";
|
|
13917
|
+
const res = await fetch(`${base}${sendPath}`, {
|
|
13918
|
+
method: "POST",
|
|
13919
|
+
headers: { "Content-Type": "application/json" },
|
|
13920
|
+
body: JSON.stringify({ mobile_number: `${countryCode}${phone}` })
|
|
13921
|
+
});
|
|
13922
|
+
const data = await res.json().catch(() => ({}));
|
|
13923
|
+
if (res.ok) {
|
|
13924
|
+
hasSentRef.current = true;
|
|
13925
|
+
sentPhoneRef.current = phone;
|
|
13926
|
+
setStatus("sent");
|
|
13927
|
+
setResendCooldown(RESEND_COOLDOWN_SECONDS);
|
|
13928
|
+
store.setExternalError(fieldId, null);
|
|
13929
|
+
} else {
|
|
13930
|
+
setStatus("error");
|
|
13931
|
+
store.setExternalError(fieldId, data.error ?? "Failed to send OTP. Please try again.");
|
|
13932
|
+
}
|
|
13933
|
+
} catch {
|
|
13934
|
+
setStatus("error");
|
|
13935
|
+
store.setExternalError(fieldId, "Network error. Please try again.");
|
|
13936
|
+
} finally {
|
|
13937
|
+
isSendingRef.current = false;
|
|
13938
|
+
}
|
|
13939
|
+
};
|
|
13940
|
+
const handleResend = async () => {
|
|
13941
|
+
if (isResendingRef.current || resendCooldown > 0) return;
|
|
13942
|
+
isResendingRef.current = true;
|
|
13943
|
+
setResendError("");
|
|
13944
|
+
try {
|
|
13945
|
+
const authCfg = store.getConfig().auth;
|
|
13946
|
+
const base = authCfg?.baseUrl ?? ((typeof process !== "undefined" && process.env?.NEXT_PUBLIC_API_URL) ?? "");
|
|
13947
|
+
const resendPath = authCfg?.resendPath ?? "/api/auth/otp/resend/";
|
|
13948
|
+
const countryCode = authCfg?.defaultCountryCode ?? "+91";
|
|
13949
|
+
const res = await fetch(`${base}${resendPath}`, {
|
|
13950
|
+
method: "POST",
|
|
13951
|
+
headers: { "Content-Type": "application/json" },
|
|
13952
|
+
body: JSON.stringify({ mobile_number: `${countryCode}${rawPhone}`, retrytype: "text" })
|
|
13953
|
+
});
|
|
13954
|
+
const data = await res.json().catch(() => ({}));
|
|
13955
|
+
if (res.ok) {
|
|
13956
|
+
setResendCooldown(RESEND_COOLDOWN_SECONDS);
|
|
13957
|
+
setResendError("");
|
|
13958
|
+
} else {
|
|
13959
|
+
setResendError(data.error ?? "Failed to resend OTP. Please try again.");
|
|
13960
|
+
}
|
|
13961
|
+
} catch {
|
|
13962
|
+
setResendError("Network error. Please try again.");
|
|
13963
|
+
} finally {
|
|
13964
|
+
isResendingRef.current = false;
|
|
13965
|
+
}
|
|
13966
|
+
};
|
|
13967
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
13968
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Label, { htmlFor: fieldId, children: [
|
|
13969
|
+
fieldDef.label,
|
|
13970
|
+
" ",
|
|
13971
|
+
fieldDef.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500", children: "*" })
|
|
13972
|
+
] }),
|
|
13973
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
13974
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13975
|
+
Input,
|
|
13976
|
+
{
|
|
13977
|
+
id: fieldId,
|
|
13978
|
+
type: "tel",
|
|
13979
|
+
inputMode: "numeric",
|
|
13980
|
+
value: rawPhone,
|
|
13981
|
+
onChange: handleChange,
|
|
13982
|
+
onBlur: () => store.setTouched(fieldId),
|
|
13983
|
+
disabled: disabled || status === "sending",
|
|
13984
|
+
placeholder,
|
|
13985
|
+
maxLength: 10,
|
|
13986
|
+
className: cn(
|
|
13987
|
+
showError && "border-red-500",
|
|
13988
|
+
status === "sent" && "border-green-500 pr-9",
|
|
13989
|
+
status === "sending" && "pr-9"
|
|
13990
|
+
)
|
|
13991
|
+
}
|
|
13992
|
+
),
|
|
13993
|
+
status === "sending" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
13994
|
+
"svg",
|
|
13995
|
+
{
|
|
13996
|
+
className: "animate-spin h-4 w-4 text-muted-foreground",
|
|
13997
|
+
viewBox: "0 0 24 24",
|
|
13998
|
+
fill: "none",
|
|
13999
|
+
children: [
|
|
14000
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14001
|
+
"circle",
|
|
14002
|
+
{
|
|
14003
|
+
className: "opacity-25",
|
|
14004
|
+
cx: "12",
|
|
14005
|
+
cy: "12",
|
|
14006
|
+
r: "10",
|
|
14007
|
+
stroke: "currentColor",
|
|
14008
|
+
strokeWidth: "4"
|
|
14009
|
+
}
|
|
14010
|
+
),
|
|
14011
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14012
|
+
"path",
|
|
14013
|
+
{
|
|
14014
|
+
className: "opacity-75",
|
|
14015
|
+
fill: "currentColor",
|
|
14016
|
+
d: "M4 12a8 8 0 018-8v8H4z"
|
|
14017
|
+
}
|
|
14018
|
+
)
|
|
14019
|
+
]
|
|
14020
|
+
}
|
|
14021
|
+
) }),
|
|
14022
|
+
status === "sent" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
14023
|
+
"svg",
|
|
14024
|
+
{
|
|
14025
|
+
className: "h-4 w-4 text-green-500",
|
|
14026
|
+
viewBox: "0 0 20 20",
|
|
14027
|
+
fill: "currentColor",
|
|
14028
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
14029
|
+
"path",
|
|
14030
|
+
{
|
|
14031
|
+
fillRule: "evenodd",
|
|
14032
|
+
d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z",
|
|
14033
|
+
clipRule: "evenodd"
|
|
14034
|
+
}
|
|
14035
|
+
)
|
|
14036
|
+
}
|
|
14037
|
+
) })
|
|
14038
|
+
] }),
|
|
14039
|
+
status === "sent" && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-green-600", children: [
|
|
14040
|
+
"OTP sent to +91",
|
|
14041
|
+
rawPhone
|
|
14042
|
+
] }),
|
|
14043
|
+
showError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-500", children: error }),
|
|
14044
|
+
status === "sent" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [
|
|
14045
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Didn't receive it?" }),
|
|
14046
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14047
|
+
"button",
|
|
14048
|
+
{
|
|
14049
|
+
type: "button",
|
|
14050
|
+
onClick: handleResend,
|
|
14051
|
+
disabled: resendCooldown > 0,
|
|
14052
|
+
className: cn(
|
|
14053
|
+
"underline transition-colors",
|
|
14054
|
+
resendCooldown > 0 ? "opacity-40 cursor-not-allowed" : "text-primary hover:text-primary/80 cursor-pointer"
|
|
14055
|
+
),
|
|
14056
|
+
children: resendCooldown > 0 ? `Resend in ${resendCooldown}s` : "Resend OTP"
|
|
14057
|
+
}
|
|
14058
|
+
)
|
|
14059
|
+
] }),
|
|
14060
|
+
resendError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-500", children: resendError })
|
|
14061
|
+
] });
|
|
14062
|
+
};
|
|
14063
|
+
var OtpInputWidget = ({ fieldId }) => {
|
|
14064
|
+
const { fieldDef, error, touched, disabled } = useField(fieldId);
|
|
14065
|
+
const { state } = useForm();
|
|
14066
|
+
const store = useFormStore();
|
|
14067
|
+
const phoneFieldId = fieldDef?.meta?.phoneFieldId ?? "phone";
|
|
14068
|
+
const { value: phoneValue } = useField(phoneFieldId);
|
|
14069
|
+
const phone = phoneValue ?? "";
|
|
14070
|
+
const e164Phone = phone.startsWith("+") ? phone : `+91${phone}`;
|
|
14071
|
+
const [otp, setOtp] = React15.useState("");
|
|
14072
|
+
const [status, setStatus] = React15.useState("idle");
|
|
14073
|
+
const isVerifyingRef = React15.useRef(false);
|
|
14074
|
+
const showError = !!error && (touched || state.submitAttempted) && status !== "verifying" && status !== "verified";
|
|
14075
|
+
if (!fieldDef) return null;
|
|
14076
|
+
const placeholder = typeof fieldDef.placeholder === "string" && fieldDef.placeholder.trim() ? fieldDef.placeholder : "Enter 4-digit OTP";
|
|
14077
|
+
const handleChange = (e) => {
|
|
14078
|
+
const digits = e.target.value.replace(/\D/g, "").slice(0, 4);
|
|
14079
|
+
if (status === "failed") {
|
|
14080
|
+
setStatus("idle");
|
|
14081
|
+
store.setExternalError(fieldId, null);
|
|
14082
|
+
}
|
|
14083
|
+
setOtp(digits);
|
|
14084
|
+
store.setValue(fieldId, digits);
|
|
14085
|
+
if (digits.length === 4 && !isVerifyingRef.current) {
|
|
14086
|
+
verifyOtp(digits);
|
|
14087
|
+
}
|
|
14088
|
+
};
|
|
14089
|
+
const verifyOtp = async (code) => {
|
|
14090
|
+
if (isVerifyingRef.current) return;
|
|
14091
|
+
isVerifyingRef.current = true;
|
|
14092
|
+
setStatus("verifying");
|
|
14093
|
+
store.setExternalError(fieldId, "Verifying...");
|
|
14094
|
+
try {
|
|
14095
|
+
const authCfg = store.getConfig().auth;
|
|
14096
|
+
const base = authCfg?.baseUrl ?? ((typeof process !== "undefined" && process.env?.NEXT_PUBLIC_API_URL) ?? "");
|
|
14097
|
+
const verifyPath = authCfg?.verifyPath ?? "/api/auth/otp/verify/";
|
|
14098
|
+
const res = await fetch(`${base}${verifyPath}`, {
|
|
14099
|
+
method: "POST",
|
|
14100
|
+
headers: { "Content-Type": "application/json" },
|
|
14101
|
+
body: JSON.stringify({ mobile_number: e164Phone, otp_code: code })
|
|
14102
|
+
});
|
|
14103
|
+
const data = await res.json().catch(() => ({}));
|
|
14104
|
+
if (res.ok) {
|
|
14105
|
+
setStatus("verified");
|
|
14106
|
+
store.setExternalError(fieldId, null);
|
|
14107
|
+
store.setValue(fieldId, {
|
|
14108
|
+
verified: true,
|
|
14109
|
+
phone: e164Phone,
|
|
14110
|
+
code,
|
|
14111
|
+
...data?.verification_token ? { token: data.verification_token } : {}
|
|
14112
|
+
});
|
|
14113
|
+
} else {
|
|
14114
|
+
setStatus("failed");
|
|
14115
|
+
const msg = data.error ?? "Invalid OTP. Please try again.";
|
|
14116
|
+
store.setExternalError(fieldId, msg);
|
|
14117
|
+
setOtp("");
|
|
14118
|
+
store.setValue(fieldId, "");
|
|
14119
|
+
}
|
|
14120
|
+
} catch {
|
|
14121
|
+
setStatus("failed");
|
|
14122
|
+
store.setExternalError(fieldId, "Network error. Please try again.");
|
|
14123
|
+
setOtp("");
|
|
14124
|
+
store.setValue(fieldId, "");
|
|
14125
|
+
} finally {
|
|
14126
|
+
isVerifyingRef.current = false;
|
|
14127
|
+
}
|
|
14128
|
+
};
|
|
14129
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
14130
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Label, { htmlFor: fieldId, children: [
|
|
14131
|
+
fieldDef.label,
|
|
14132
|
+
" ",
|
|
14133
|
+
fieldDef.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500", children: "*" })
|
|
14134
|
+
] }),
|
|
14135
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
14136
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14137
|
+
Input,
|
|
14138
|
+
{
|
|
14139
|
+
id: fieldId,
|
|
14140
|
+
type: "tel",
|
|
14141
|
+
inputMode: "numeric",
|
|
14142
|
+
value: otp,
|
|
14143
|
+
onChange: handleChange,
|
|
14144
|
+
onBlur: () => store.setTouched(fieldId),
|
|
14145
|
+
disabled: disabled || status === "verifying" || status === "verified",
|
|
14146
|
+
placeholder,
|
|
14147
|
+
maxLength: 4,
|
|
14148
|
+
className: cn(
|
|
14149
|
+
"tracking-widest text-center text-lg",
|
|
14150
|
+
showError && "border-red-500",
|
|
14151
|
+
status === "verified" && "border-green-500 pr-9",
|
|
14152
|
+
status === "verifying" && "pr-9"
|
|
14153
|
+
)
|
|
14154
|
+
}
|
|
14155
|
+
),
|
|
14156
|
+
status === "verifying" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14157
|
+
"svg",
|
|
14158
|
+
{
|
|
14159
|
+
className: "animate-spin h-4 w-4 text-muted-foreground",
|
|
14160
|
+
viewBox: "0 0 24 24",
|
|
14161
|
+
fill: "none",
|
|
14162
|
+
children: [
|
|
14163
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14164
|
+
"circle",
|
|
14165
|
+
{
|
|
14166
|
+
className: "opacity-25",
|
|
14167
|
+
cx: "12",
|
|
14168
|
+
cy: "12",
|
|
14169
|
+
r: "10",
|
|
14170
|
+
stroke: "currentColor",
|
|
14171
|
+
strokeWidth: "4"
|
|
14172
|
+
}
|
|
14173
|
+
),
|
|
14174
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14175
|
+
"path",
|
|
14176
|
+
{
|
|
14177
|
+
className: "opacity-75",
|
|
14178
|
+
fill: "currentColor",
|
|
14179
|
+
d: "M4 12a8 8 0 018-8v8H4z"
|
|
14180
|
+
}
|
|
14181
|
+
)
|
|
14182
|
+
]
|
|
14183
|
+
}
|
|
14184
|
+
) }),
|
|
14185
|
+
status === "verified" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
14186
|
+
"svg",
|
|
14187
|
+
{
|
|
14188
|
+
className: "h-4 w-4 text-green-500",
|
|
14189
|
+
viewBox: "0 0 20 20",
|
|
14190
|
+
fill: "currentColor",
|
|
14191
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
14192
|
+
"path",
|
|
14193
|
+
{
|
|
14194
|
+
fillRule: "evenodd",
|
|
14195
|
+
d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z",
|
|
14196
|
+
clipRule: "evenodd"
|
|
14197
|
+
}
|
|
14198
|
+
)
|
|
14199
|
+
}
|
|
14200
|
+
) })
|
|
14201
|
+
] }),
|
|
14202
|
+
status === "verified" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-green-600", children: "Mobile number verified successfully" }),
|
|
14203
|
+
showError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-500", children: error })
|
|
14204
|
+
] });
|
|
14205
|
+
};
|
|
13800
14206
|
var FieldRenderer = ({ fieldId }) => {
|
|
13801
14207
|
const { fieldDef, visible } = useField(fieldId);
|
|
13802
14208
|
if (!visible || !fieldDef) {
|
|
@@ -13888,6 +14294,10 @@ function renderWidget(fieldId, fieldDef) {
|
|
|
13888
14294
|
return /* @__PURE__ */ jsxRuntime.jsx(OBGExaminationWidget, { fieldId });
|
|
13889
14295
|
case "obg_pathway":
|
|
13890
14296
|
return /* @__PURE__ */ jsxRuntime.jsx(OBGPathwayWidget, { fieldId });
|
|
14297
|
+
case "phone_input":
|
|
14298
|
+
return /* @__PURE__ */ jsxRuntime.jsx(PhoneInputWidget, { fieldId });
|
|
14299
|
+
case "otp_input":
|
|
14300
|
+
return /* @__PURE__ */ jsxRuntime.jsx(OtpInputWidget, { fieldId });
|
|
13891
14301
|
case "toggle": {
|
|
13892
14302
|
const { value, setValue, setTouched, disabled } = useField(fieldId);
|
|
13893
14303
|
const store = useFormStore();
|
|
@@ -14008,16 +14418,6 @@ var DynamicForm = () => {
|
|
|
14008
14418
|
})
|
|
14009
14419
|
] });
|
|
14010
14420
|
};
|
|
14011
|
-
function PlainSection({
|
|
14012
|
-
title,
|
|
14013
|
-
showHeading,
|
|
14014
|
-
children
|
|
14015
|
-
}) {
|
|
14016
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6 space-y-4", children: [
|
|
14017
|
-
showHeading ? /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-base font-medium text-foreground", children: title }) : null,
|
|
14018
|
-
children
|
|
14019
|
-
] });
|
|
14020
|
-
}
|
|
14021
14421
|
function renderSectionChildren(children) {
|
|
14022
14422
|
return children.map((child, index) => {
|
|
14023
14423
|
if (typeof child === "string") {
|
|
@@ -14037,6 +14437,16 @@ function renderSectionChildren(children) {
|
|
|
14037
14437
|
return /* @__PURE__ */ jsxRuntime.jsx(React15__namespace.default.Fragment, {}, index);
|
|
14038
14438
|
});
|
|
14039
14439
|
}
|
|
14440
|
+
function PlainSection({
|
|
14441
|
+
title,
|
|
14442
|
+
showHeading,
|
|
14443
|
+
children
|
|
14444
|
+
}) {
|
|
14445
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6 space-y-4", children: [
|
|
14446
|
+
showHeading ? /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-base font-medium text-foreground", children: title }) : null,
|
|
14447
|
+
children
|
|
14448
|
+
] });
|
|
14449
|
+
}
|
|
14040
14450
|
var DynamicFormV2 = ({
|
|
14041
14451
|
showTitle = true,
|
|
14042
14452
|
sectionMode = "plain",
|
|
@@ -14077,6 +14487,247 @@ var DynamicFormV2 = ({
|
|
|
14077
14487
|
})
|
|
14078
14488
|
] });
|
|
14079
14489
|
};
|
|
14490
|
+
function flattenStepFieldIds(step) {
|
|
14491
|
+
const ids = [];
|
|
14492
|
+
for (const child of step.children) {
|
|
14493
|
+
if (typeof child === "string") {
|
|
14494
|
+
ids.push(child);
|
|
14495
|
+
} else if (child.type === "column_layout") {
|
|
14496
|
+
ids.push(...child.children);
|
|
14497
|
+
}
|
|
14498
|
+
}
|
|
14499
|
+
return ids;
|
|
14500
|
+
}
|
|
14501
|
+
function isFieldComplete(id, state, fieldMap) {
|
|
14502
|
+
if (state.visibility[id] === false) return true;
|
|
14503
|
+
if (state.errors[id]) return false;
|
|
14504
|
+
const def = fieldMap[id];
|
|
14505
|
+
if (def?.required) {
|
|
14506
|
+
const v = state.values[id];
|
|
14507
|
+
if (v === null || v === void 0 || v === "") return false;
|
|
14508
|
+
if (Array.isArray(v) && v.length === 0) return false;
|
|
14509
|
+
}
|
|
14510
|
+
return true;
|
|
14511
|
+
}
|
|
14512
|
+
function isStepComplete(step, state, fieldMap) {
|
|
14513
|
+
return flattenStepFieldIds(step).every(
|
|
14514
|
+
(id) => isFieldComplete(id, state, fieldMap)
|
|
14515
|
+
);
|
|
14516
|
+
}
|
|
14517
|
+
var Stepper2 = ({ steps, statuses, variant }) => {
|
|
14518
|
+
if (variant === "dots") {
|
|
14519
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2 mb-6", children: steps.map((step, i) => {
|
|
14520
|
+
const status = statuses[i];
|
|
14521
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(React15__namespace.default.Fragment, { children: [
|
|
14522
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14523
|
+
"div",
|
|
14524
|
+
{
|
|
14525
|
+
className: cn(
|
|
14526
|
+
"h-2.5 w-2.5 rounded-full transition-colors",
|
|
14527
|
+
status === "complete" && "bg-primary",
|
|
14528
|
+
status === "active" && "bg-primary ring-2 ring-primary/30",
|
|
14529
|
+
status === "locked" && "bg-muted"
|
|
14530
|
+
),
|
|
14531
|
+
title: step.title
|
|
14532
|
+
}
|
|
14533
|
+
),
|
|
14534
|
+
i < steps.length - 1 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
14535
|
+
"div",
|
|
14536
|
+
{
|
|
14537
|
+
className: cn(
|
|
14538
|
+
"flex-1 h-px transition-colors",
|
|
14539
|
+
status === "complete" ? "bg-primary" : "bg-border"
|
|
14540
|
+
)
|
|
14541
|
+
}
|
|
14542
|
+
)
|
|
14543
|
+
] }, step.id);
|
|
14544
|
+
}) });
|
|
14545
|
+
}
|
|
14546
|
+
if (variant === "labels") {
|
|
14547
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-0 mb-6", children: steps.map((step, i) => {
|
|
14548
|
+
const status = statuses[i];
|
|
14549
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(React15__namespace.default.Fragment, { children: [
|
|
14550
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-1", children: [
|
|
14551
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14552
|
+
"div",
|
|
14553
|
+
{
|
|
14554
|
+
className: cn(
|
|
14555
|
+
"h-2 w-2 rounded-full",
|
|
14556
|
+
status === "complete" && "bg-primary",
|
|
14557
|
+
status === "active" && "bg-primary",
|
|
14558
|
+
status === "locked" && "bg-muted"
|
|
14559
|
+
)
|
|
14560
|
+
}
|
|
14561
|
+
),
|
|
14562
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14563
|
+
"span",
|
|
14564
|
+
{
|
|
14565
|
+
className: cn(
|
|
14566
|
+
"text-xs whitespace-nowrap",
|
|
14567
|
+
status === "locked" ? "text-muted-foreground" : "text-foreground"
|
|
14568
|
+
),
|
|
14569
|
+
children: step.title
|
|
14570
|
+
}
|
|
14571
|
+
)
|
|
14572
|
+
] }),
|
|
14573
|
+
i < steps.length - 1 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
14574
|
+
"div",
|
|
14575
|
+
{
|
|
14576
|
+
className: cn(
|
|
14577
|
+
"flex-1 h-px mb-4 transition-colors",
|
|
14578
|
+
status === "complete" ? "bg-primary" : "bg-border"
|
|
14579
|
+
)
|
|
14580
|
+
}
|
|
14581
|
+
)
|
|
14582
|
+
] }, step.id);
|
|
14583
|
+
}) });
|
|
14584
|
+
}
|
|
14585
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-0 mb-6", children: steps.map((step, i) => {
|
|
14586
|
+
const status = statuses[i];
|
|
14587
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(React15__namespace.default.Fragment, { children: [
|
|
14588
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
14589
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14590
|
+
"div",
|
|
14591
|
+
{
|
|
14592
|
+
className: cn(
|
|
14593
|
+
"flex h-7 w-7 items-center justify-center rounded-full text-xs font-semibold border-2 transition-colors",
|
|
14594
|
+
status === "complete" && "border-primary bg-primary text-primary-foreground",
|
|
14595
|
+
status === "active" && "border-primary bg-background text-primary",
|
|
14596
|
+
status === "locked" && "border-border bg-background text-muted-foreground"
|
|
14597
|
+
),
|
|
14598
|
+
children: status === "complete" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-3.5 w-3.5" }) : i + 1
|
|
14599
|
+
}
|
|
14600
|
+
),
|
|
14601
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14602
|
+
"span",
|
|
14603
|
+
{
|
|
14604
|
+
className: cn(
|
|
14605
|
+
"text-sm font-medium hidden sm:block",
|
|
14606
|
+
status === "locked" ? "text-muted-foreground" : "text-foreground"
|
|
14607
|
+
),
|
|
14608
|
+
children: step.title
|
|
14609
|
+
}
|
|
14610
|
+
)
|
|
14611
|
+
] }),
|
|
14612
|
+
i < steps.length - 1 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
14613
|
+
"div",
|
|
14614
|
+
{
|
|
14615
|
+
className: cn(
|
|
14616
|
+
"flex-1 h-px mx-3 transition-colors",
|
|
14617
|
+
status === "complete" ? "bg-primary" : "bg-border"
|
|
14618
|
+
)
|
|
14619
|
+
}
|
|
14620
|
+
)
|
|
14621
|
+
] }, step.id);
|
|
14622
|
+
}) });
|
|
14623
|
+
};
|
|
14624
|
+
var ActiveStep = ({
|
|
14625
|
+
title,
|
|
14626
|
+
children
|
|
14627
|
+
}) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-4 space-y-4", children: [
|
|
14628
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-base font-medium text-foreground", children: title }),
|
|
14629
|
+
children
|
|
14630
|
+
] });
|
|
14631
|
+
var Wizard = ({ node }) => {
|
|
14632
|
+
const store = useFormStore();
|
|
14633
|
+
const [formState, setFormState] = React15.useState(() => ({
|
|
14634
|
+
...store.getState()
|
|
14635
|
+
}));
|
|
14636
|
+
React15.useEffect(() => {
|
|
14637
|
+
return store.subscribe((state) => {
|
|
14638
|
+
console.log("[MultiStepForm] store update \u2192", {
|
|
14639
|
+
values: state.values,
|
|
14640
|
+
errors: state.errors,
|
|
14641
|
+
visibility: state.visibility
|
|
14642
|
+
});
|
|
14643
|
+
setFormState({ ...state });
|
|
14644
|
+
});
|
|
14645
|
+
}, [store]);
|
|
14646
|
+
const schema = store.getSchema();
|
|
14647
|
+
const fieldMap = {};
|
|
14648
|
+
schema.fields.forEach((f) => {
|
|
14649
|
+
fieldMap[f.id] = f;
|
|
14650
|
+
});
|
|
14651
|
+
const { steps, meta } = node;
|
|
14652
|
+
const showStepper = meta?.showStepper !== false;
|
|
14653
|
+
const variant = meta?.stepperVariant ?? "numbered";
|
|
14654
|
+
const collapseCompleted = meta?.collapseCompleted !== false;
|
|
14655
|
+
const stepComplete = steps.map((s) => {
|
|
14656
|
+
const fieldIds = flattenStepFieldIds(s);
|
|
14657
|
+
const complete = isStepComplete(s, formState, fieldMap);
|
|
14658
|
+
console.log(`[MultiStepForm] step "${s.id}" fieldIds=${JSON.stringify(fieldIds)} complete=${complete}`, {
|
|
14659
|
+
errors: fieldIds.map((id) => ({ id, error: formState.errors[id] })),
|
|
14660
|
+
values: fieldIds.map((id) => ({ id, value: formState.values[id] }))
|
|
14661
|
+
});
|
|
14662
|
+
return complete;
|
|
14663
|
+
});
|
|
14664
|
+
const activeIndex = stepComplete.findIndex((c) => !c) === -1 ? steps.length : stepComplete.findIndex((c) => !c);
|
|
14665
|
+
console.log(`[MultiStepForm] activeIndex=${activeIndex} stepComplete=${JSON.stringify(stepComplete)}`);
|
|
14666
|
+
const statuses = steps.map((_, i) => {
|
|
14667
|
+
if (i < activeIndex) return "complete";
|
|
14668
|
+
if (i === activeIndex) return "active";
|
|
14669
|
+
return "locked";
|
|
14670
|
+
});
|
|
14671
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
14672
|
+
node.title && /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold mb-4", children: node.title }),
|
|
14673
|
+
showStepper && /* @__PURE__ */ jsxRuntime.jsx(Stepper2, { steps, statuses, variant }),
|
|
14674
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: steps.map((step, i) => {
|
|
14675
|
+
const status = statuses[i];
|
|
14676
|
+
if (status === "locked") return null;
|
|
14677
|
+
const children = renderSectionChildren(step.children);
|
|
14678
|
+
if (status === "complete") {
|
|
14679
|
+
if (collapseCompleted) {
|
|
14680
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Section, { title: step.title, children }, step.id);
|
|
14681
|
+
}
|
|
14682
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14683
|
+
"div",
|
|
14684
|
+
{
|
|
14685
|
+
className: "mb-4 opacity-60 pointer-events-none select-none space-y-4",
|
|
14686
|
+
children: [
|
|
14687
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-base font-medium text-foreground", children: step.title }),
|
|
14688
|
+
children
|
|
14689
|
+
]
|
|
14690
|
+
},
|
|
14691
|
+
step.id
|
|
14692
|
+
);
|
|
14693
|
+
}
|
|
14694
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ActiveStep, { title: step.title, children }, step.id);
|
|
14695
|
+
}) })
|
|
14696
|
+
] });
|
|
14697
|
+
};
|
|
14698
|
+
var MultiStepForm = ({
|
|
14699
|
+
showTitle = true,
|
|
14700
|
+
className
|
|
14701
|
+
}) => {
|
|
14702
|
+
const store = useFormStore();
|
|
14703
|
+
const schema = store.getSchema();
|
|
14704
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-6", className), children: [
|
|
14705
|
+
showTitle && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4", children: /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold", children: schema.title }) }),
|
|
14706
|
+
schema.layout.map((node) => {
|
|
14707
|
+
if (node.type === "multi_step") {
|
|
14708
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Wizard, { node }, node.id);
|
|
14709
|
+
}
|
|
14710
|
+
if (node.type === "section") {
|
|
14711
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6 space-y-4", children: [
|
|
14712
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-base font-medium text-foreground", children: node.title }),
|
|
14713
|
+
renderSectionChildren(node.children)
|
|
14714
|
+
] }, node.id);
|
|
14715
|
+
}
|
|
14716
|
+
if (node.type === "column_layout") {
|
|
14717
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
14718
|
+
ColumnLayout,
|
|
14719
|
+
{
|
|
14720
|
+
columns: node.columns,
|
|
14721
|
+
label: node.label,
|
|
14722
|
+
children: node.children.map((fieldId) => /* @__PURE__ */ jsxRuntime.jsx(FieldRenderer, { fieldId }, fieldId))
|
|
14723
|
+
},
|
|
14724
|
+
node.id
|
|
14725
|
+
);
|
|
14726
|
+
}
|
|
14727
|
+
return null;
|
|
14728
|
+
})
|
|
14729
|
+
] });
|
|
14730
|
+
};
|
|
14080
14731
|
var SUGGESTION_KEYS = /* @__PURE__ */ new Set(["medications", "investigations", "procedures"]);
|
|
14081
14732
|
function shallowEqualKeys(a, b) {
|
|
14082
14733
|
const keysA = Object.keys(a);
|
|
@@ -15039,7 +15690,8 @@ var FormControls = ({
|
|
|
15039
15690
|
actionsFullWidth = false,
|
|
15040
15691
|
buttonClassName,
|
|
15041
15692
|
onSaveDraft,
|
|
15042
|
-
onSubmit
|
|
15693
|
+
onSubmit,
|
|
15694
|
+
submitLabel
|
|
15043
15695
|
}) => {
|
|
15044
15696
|
const {
|
|
15045
15697
|
state,
|
|
@@ -15128,7 +15780,7 @@ var FormControls = ({
|
|
|
15128
15780
|
disabled: !state.isValid || hasUploadsInFlight,
|
|
15129
15781
|
size: "sm",
|
|
15130
15782
|
className: buttonCn,
|
|
15131
|
-
children: hasUploadsInFlight ? "Upload in progress\u2026" : availableTransitions.length > 1 ? `Submit to ${t.to}` : "Submit"
|
|
15783
|
+
children: hasUploadsInFlight ? "Upload in progress\u2026" : availableTransitions.length > 1 ? `Submit to ${t.to}` : submitLabel ?? "Submit"
|
|
15132
15784
|
},
|
|
15133
15785
|
t.to
|
|
15134
15786
|
)) : onSubmit ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -15141,7 +15793,7 @@ var FormControls = ({
|
|
|
15141
15793
|
disabled: !state.isValid || hasUploadsInFlight,
|
|
15142
15794
|
size: "sm",
|
|
15143
15795
|
className: buttonCn,
|
|
15144
|
-
children: hasUploadsInFlight ? "Upload in progress\u2026" : "Submit"
|
|
15796
|
+
children: hasUploadsInFlight ? "Upload in progress\u2026" : submitLabel ?? "Submit"
|
|
15145
15797
|
}
|
|
15146
15798
|
) : null })
|
|
15147
15799
|
]
|
|
@@ -15195,6 +15847,7 @@ exports.FormStore = FormStore;
|
|
|
15195
15847
|
exports.ImageUploadWidget = ImageUploadWidget;
|
|
15196
15848
|
exports.MAR_MEDICATION_ORDERS_META_KEY = MAR_MEDICATION_ORDERS_META_KEY;
|
|
15197
15849
|
exports.MediaUploadWidget = MediaUploadWidget;
|
|
15850
|
+
exports.MultiStepForm = MultiStepForm;
|
|
15198
15851
|
exports.ReadOnlyForm = ReadOnlyForm;
|
|
15199
15852
|
exports.ReadOnlyImageUpload = ReadOnlyImageUpload;
|
|
15200
15853
|
exports.ReadOnlyMediaUpload = ReadOnlyMediaUpload;
|