formanitor 0.0.48 → 0.0.50
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 +867 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +129 -5
- package/dist/index.d.ts +129 -5
- package/dist/index.mjs +865 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -32,8 +32,25 @@ function fieldMetaRequestsMarMedicationOrdersButton(meta) {
|
|
|
32
32
|
|
|
33
33
|
// src/core/validate.ts
|
|
34
34
|
var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
35
|
-
function validateField(value, field) {
|
|
35
|
+
function validateField(value, field, allValues) {
|
|
36
36
|
if (field.hidden || field.disabled) return null;
|
|
37
|
+
if (field.type === "otp_input") {
|
|
38
|
+
if (field.required) {
|
|
39
|
+
if (!value || typeof value !== "object" || value.verified !== true) {
|
|
40
|
+
return "Please verify your mobile number with OTP";
|
|
41
|
+
}
|
|
42
|
+
const otpField = field;
|
|
43
|
+
const phoneFieldId = otpField.meta?.phoneFieldId ?? "phone";
|
|
44
|
+
if (allValues) {
|
|
45
|
+
const phoneRaw = allValues[phoneFieldId] ?? "";
|
|
46
|
+
const e164 = phoneRaw.startsWith("+") ? phoneRaw : `+91${phoneRaw}`;
|
|
47
|
+
if (value.phone && value.phone !== e164) {
|
|
48
|
+
return "Mobile number changed \u2014 please re-verify";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
37
54
|
if (field.required) {
|
|
38
55
|
if (value === null || value === void 0 || value === "") {
|
|
39
56
|
return "This field is required";
|
|
@@ -94,7 +111,7 @@ function validateForm(values, fields) {
|
|
|
94
111
|
const errors = {};
|
|
95
112
|
for (const [key, field] of Object.entries(fields)) {
|
|
96
113
|
if (field.type === "static_text") continue;
|
|
97
|
-
const error = validateField(values[key], field);
|
|
114
|
+
const error = validateField(values[key], field, values);
|
|
98
115
|
if (error) {
|
|
99
116
|
errors[key] = error;
|
|
100
117
|
}
|
|
@@ -1171,6 +1188,9 @@ var FormStore = class {
|
|
|
1171
1188
|
persistence;
|
|
1172
1189
|
prefillData = {};
|
|
1173
1190
|
config;
|
|
1191
|
+
// Widget-owned errors that schema validation cannot express (e.g. "OTP not sent yet").
|
|
1192
|
+
// Merged on top of validateForm output in evaluate().
|
|
1193
|
+
externalErrors = {};
|
|
1174
1194
|
// Cache for debounce
|
|
1175
1195
|
saveTimeout = null;
|
|
1176
1196
|
draftCallbackTimeout = null;
|
|
@@ -1290,6 +1310,9 @@ var FormStore = class {
|
|
|
1290
1310
|
getSchema() {
|
|
1291
1311
|
return this.schema;
|
|
1292
1312
|
}
|
|
1313
|
+
getConfig() {
|
|
1314
|
+
return this.config;
|
|
1315
|
+
}
|
|
1293
1316
|
getFieldDef(fieldId) {
|
|
1294
1317
|
return this.fieldMap[fieldId];
|
|
1295
1318
|
}
|
|
@@ -1337,6 +1360,17 @@ var FormStore = class {
|
|
|
1337
1360
|
this.notify();
|
|
1338
1361
|
this.scheduleSave();
|
|
1339
1362
|
}
|
|
1363
|
+
/**
|
|
1364
|
+
* Let a widget report an error that schema validation cannot express
|
|
1365
|
+
* (e.g. "Send OTP to continue" until the API call succeeds).
|
|
1366
|
+
* Pass `null` to clear a previously set external error.
|
|
1367
|
+
*/
|
|
1368
|
+
setExternalError(fieldId, message) {
|
|
1369
|
+
if ((this.externalErrors[fieldId] ?? null) === message) return;
|
|
1370
|
+
this.externalErrors = { ...this.externalErrors, [fieldId]: message };
|
|
1371
|
+
this.evaluate();
|
|
1372
|
+
this.notify();
|
|
1373
|
+
}
|
|
1340
1374
|
setTouched(fieldId) {
|
|
1341
1375
|
if (this.state.touched[fieldId]) return;
|
|
1342
1376
|
this.state.touched = { ...this.state.touched, [fieldId]: true };
|
|
@@ -1465,16 +1499,42 @@ var FormStore = class {
|
|
|
1465
1499
|
result[field.id] = parts.length > 0 ? parts.join("\n\n") : null;
|
|
1466
1500
|
return;
|
|
1467
1501
|
}
|
|
1468
|
-
if (field.type === "
|
|
1502
|
+
if (field.type === "otp_input") {
|
|
1469
1503
|
const raw = values[field.id];
|
|
1470
|
-
if (
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1504
|
+
if (typeof raw === "string") {
|
|
1505
|
+
result[field.id] = raw;
|
|
1506
|
+
return;
|
|
1507
|
+
}
|
|
1508
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw) && raw.verified === true) {
|
|
1509
|
+
const code = raw.code;
|
|
1510
|
+
result[field.id] = typeof code === "string" ? code : null;
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1513
|
+
result[field.id] = null;
|
|
1514
|
+
return;
|
|
1515
|
+
}
|
|
1516
|
+
if (field.type === "image_upload" || field.type === "media_upload" || field.type === "signature" || field.type === "file_upload") {
|
|
1517
|
+
const raw = values[field.id];
|
|
1518
|
+
if (raw === void 0 || raw === null || raw === "") {
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
const wrapKey = (key2) => {
|
|
1522
|
+
if (typeof key2 !== "string" || !key2) return null;
|
|
1523
|
+
return { _type: "s3_key", value: key2 };
|
|
1524
|
+
};
|
|
1525
|
+
if (Array.isArray(raw)) {
|
|
1526
|
+
const wrapped2 = raw.map(
|
|
1527
|
+
(item) => typeof item === "string" ? wrapKey(item) : wrapKey(item.s3_key ?? item.value)
|
|
1528
|
+
).filter(Boolean);
|
|
1529
|
+
if (wrapped2.length > 0) {
|
|
1530
|
+
result[field.id] = wrapped2;
|
|
1477
1531
|
}
|
|
1532
|
+
return;
|
|
1533
|
+
}
|
|
1534
|
+
const key = typeof raw === "string" ? raw : raw.s3_key ?? raw.value ?? null;
|
|
1535
|
+
const wrapped = wrapKey(key);
|
|
1536
|
+
if (wrapped) {
|
|
1537
|
+
result[field.id] = wrapped;
|
|
1478
1538
|
}
|
|
1479
1539
|
}
|
|
1480
1540
|
});
|
|
@@ -1581,8 +1641,12 @@ var FormStore = class {
|
|
|
1581
1641
|
};
|
|
1582
1642
|
});
|
|
1583
1643
|
const errors = validateForm(this.state.values, effectiveFields);
|
|
1584
|
-
|
|
1585
|
-
|
|
1644
|
+
const merged = { ...errors };
|
|
1645
|
+
for (const [id, msg] of Object.entries(this.externalErrors)) {
|
|
1646
|
+
if (msg) merged[id] = msg;
|
|
1647
|
+
}
|
|
1648
|
+
this.state.errors = merged;
|
|
1649
|
+
this.state.isValid = Object.keys(merged).length === 0;
|
|
1586
1650
|
}
|
|
1587
1651
|
// --- Workflow ---
|
|
1588
1652
|
getAvailableTransitions() {
|
|
@@ -3708,6 +3772,190 @@ var SignatureUploadWidget = ({ fieldId }) => {
|
|
|
3708
3772
|
showError && /* @__PURE__ */ jsx("p", { className: "text-xs text-red-500", children: error })
|
|
3709
3773
|
] });
|
|
3710
3774
|
};
|
|
3775
|
+
function Upload3({
|
|
3776
|
+
value,
|
|
3777
|
+
accept,
|
|
3778
|
+
multiple,
|
|
3779
|
+
disabled,
|
|
3780
|
+
onChange,
|
|
3781
|
+
className,
|
|
3782
|
+
formatsLabel,
|
|
3783
|
+
placeholder = "Upload File",
|
|
3784
|
+
beforeUploadContent,
|
|
3785
|
+
id: idProp
|
|
3786
|
+
}) {
|
|
3787
|
+
const inputRef = React15__default.useRef(null);
|
|
3788
|
+
const generatedId = React15__default.useId();
|
|
3789
|
+
const inputId = idProp ?? generatedId;
|
|
3790
|
+
const handleFileChange = (e) => {
|
|
3791
|
+
onChange(e.target.files ?? null);
|
|
3792
|
+
};
|
|
3793
|
+
React15__default.useEffect(() => {
|
|
3794
|
+
if (!value && inputRef.current) {
|
|
3795
|
+
inputRef.current.value = "";
|
|
3796
|
+
}
|
|
3797
|
+
}, [value]);
|
|
3798
|
+
const count = value?.length ?? 0;
|
|
3799
|
+
const formatsText = formatsLabel ?? accept;
|
|
3800
|
+
return /* @__PURE__ */ jsxs(
|
|
3801
|
+
"div",
|
|
3802
|
+
{
|
|
3803
|
+
className: cn(
|
|
3804
|
+
"border border-dashed bg-[#F7F7F7] border-gray-200 rounded-lg py-5 px-4 flex items-center justify-center text-[#989898]",
|
|
3805
|
+
disabled && "opacity-50 pointer-events-none",
|
|
3806
|
+
className
|
|
3807
|
+
),
|
|
3808
|
+
children: [
|
|
3809
|
+
/* @__PURE__ */ jsx(
|
|
3810
|
+
"input",
|
|
3811
|
+
{
|
|
3812
|
+
type: "file",
|
|
3813
|
+
ref: inputRef,
|
|
3814
|
+
accept,
|
|
3815
|
+
id: inputId,
|
|
3816
|
+
className: "hidden",
|
|
3817
|
+
multiple,
|
|
3818
|
+
disabled,
|
|
3819
|
+
onChange: handleFileChange
|
|
3820
|
+
}
|
|
3821
|
+
),
|
|
3822
|
+
/* @__PURE__ */ jsx(
|
|
3823
|
+
"label",
|
|
3824
|
+
{
|
|
3825
|
+
htmlFor: inputId,
|
|
3826
|
+
className: cn("cursor-pointer w-full", disabled && "cursor-not-allowed"),
|
|
3827
|
+
children: /* @__PURE__ */ jsx("div", { children: count > 0 ? /* @__PURE__ */ jsxs("div", { className: "text-center text-sm font-semibold text-gray-700", children: [
|
|
3828
|
+
count,
|
|
3829
|
+
" file",
|
|
3830
|
+
count === 1 ? "" : "s",
|
|
3831
|
+
" selected"
|
|
3832
|
+
] }) : beforeUploadContent ? beforeUploadContent : /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center", children: [
|
|
3833
|
+
/* @__PURE__ */ jsx(Upload, { size: 16, className: "rotate-180" }),
|
|
3834
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs font-semibold mt-0.5 text-gray-700", children: placeholder }),
|
|
3835
|
+
formatsText ? /* @__PURE__ */ jsxs("div", { className: "mt-3 text-[10px]", children: [
|
|
3836
|
+
"Supported formats - ",
|
|
3837
|
+
formatsText
|
|
3838
|
+
] }) : null
|
|
3839
|
+
] }) })
|
|
3840
|
+
}
|
|
3841
|
+
)
|
|
3842
|
+
]
|
|
3843
|
+
}
|
|
3844
|
+
);
|
|
3845
|
+
}
|
|
3846
|
+
function fileListFromArray(files) {
|
|
3847
|
+
const dt = new DataTransfer();
|
|
3848
|
+
files.forEach((f) => dt.items.add(f));
|
|
3849
|
+
return dt.files;
|
|
3850
|
+
}
|
|
3851
|
+
function matchesAccept(file, accept) {
|
|
3852
|
+
if (!accept || accept === "*/*") return true;
|
|
3853
|
+
const tokens = accept.split(",").map((t) => t.trim().toLowerCase());
|
|
3854
|
+
const name = file.name.toLowerCase();
|
|
3855
|
+
const type = file.type.toLowerCase();
|
|
3856
|
+
return tokens.some((token) => {
|
|
3857
|
+
if (token.startsWith(".")) return name.endsWith(token);
|
|
3858
|
+
if (token.endsWith("/*")) {
|
|
3859
|
+
const prefix = token.slice(0, -1);
|
|
3860
|
+
return type.startsWith(prefix);
|
|
3861
|
+
}
|
|
3862
|
+
return type === token;
|
|
3863
|
+
});
|
|
3864
|
+
}
|
|
3865
|
+
var FormFileUploadWidget = ({
|
|
3866
|
+
fieldId
|
|
3867
|
+
}) => {
|
|
3868
|
+
const { fieldDef, value, setValue, setTouched, error, disabled, touched } = useField(fieldId);
|
|
3869
|
+
const store = useFormStore();
|
|
3870
|
+
const { state } = useForm();
|
|
3871
|
+
const [localFiles, setLocalFiles] = useState(null);
|
|
3872
|
+
const [isUploading, setIsUploading] = useState(false);
|
|
3873
|
+
const showError = !!error && (touched || state.submitAttempted);
|
|
3874
|
+
if (!fieldDef || fieldDef.type !== "file_upload") return null;
|
|
3875
|
+
const def = fieldDef;
|
|
3876
|
+
const accept = def.accept ?? "*/*";
|
|
3877
|
+
const maxSize = def.maxSize ?? 10 * 1024 * 1024;
|
|
3878
|
+
const allowMultiple = def.multiple === true;
|
|
3879
|
+
const uploadHandler = store.getUploadHandler();
|
|
3880
|
+
if (!uploadHandler) {
|
|
3881
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
3882
|
+
/* @__PURE__ */ jsxs(Label, { htmlFor: fieldId, children: [
|
|
3883
|
+
fieldDef.label,
|
|
3884
|
+
fieldDef.required && /* @__PURE__ */ jsx("span", { className: "text-red-500", children: "*" })
|
|
3885
|
+
] }),
|
|
3886
|
+
/* @__PURE__ */ jsx("div", { className: "border-2 border-red-300 rounded-lg p-4 bg-red-50", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-red-600", children: "Upload handler not configured. Please provide an onUpload function in FormProvider config." }) })
|
|
3887
|
+
] });
|
|
3888
|
+
}
|
|
3889
|
+
const hasStoredValue = value !== null && value !== void 0 && value !== "" && !(Array.isArray(value) && value.length === 0);
|
|
3890
|
+
const handleChange = async (files) => {
|
|
3891
|
+
setTouched();
|
|
3892
|
+
if (!files || files.length === 0) {
|
|
3893
|
+
setLocalFiles(null);
|
|
3894
|
+
setValue(null);
|
|
3895
|
+
return;
|
|
3896
|
+
}
|
|
3897
|
+
const selected = Array.from(files);
|
|
3898
|
+
for (const file of selected) {
|
|
3899
|
+
if (!matchesAccept(file, accept)) {
|
|
3900
|
+
alert(`File type not allowed: ${file.name}`);
|
|
3901
|
+
return;
|
|
3902
|
+
}
|
|
3903
|
+
if (file.size > maxSize) {
|
|
3904
|
+
alert(
|
|
3905
|
+
`File must be less than ${Math.round(maxSize / (1024 * 1024))}MB: ${file.name}`
|
|
3906
|
+
);
|
|
3907
|
+
return;
|
|
3908
|
+
}
|
|
3909
|
+
}
|
|
3910
|
+
const toUpload = allowMultiple ? selected : [selected[0]];
|
|
3911
|
+
setLocalFiles(fileListFromArray(toUpload));
|
|
3912
|
+
setIsUploading(true);
|
|
3913
|
+
store.incrementPendingUploads();
|
|
3914
|
+
try {
|
|
3915
|
+
if (allowMultiple) {
|
|
3916
|
+
const keys = [];
|
|
3917
|
+
for (const file of toUpload) {
|
|
3918
|
+
keys.push(await uploadHandler(file, file.name));
|
|
3919
|
+
}
|
|
3920
|
+
setValue(keys);
|
|
3921
|
+
} else {
|
|
3922
|
+
const key = await uploadHandler(toUpload[0], toUpload[0].name);
|
|
3923
|
+
setValue(key);
|
|
3924
|
+
}
|
|
3925
|
+
} catch (err) {
|
|
3926
|
+
console.error("[FormFileUpload] Upload failed:", err);
|
|
3927
|
+
const message = err instanceof Error ? err.message : "Upload failed. Please try again.";
|
|
3928
|
+
alert(message);
|
|
3929
|
+
setLocalFiles(null);
|
|
3930
|
+
setValue(null);
|
|
3931
|
+
} finally {
|
|
3932
|
+
setIsUploading(false);
|
|
3933
|
+
store.decrementPendingUploads();
|
|
3934
|
+
}
|
|
3935
|
+
};
|
|
3936
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
3937
|
+
/* @__PURE__ */ jsxs(Label, { htmlFor: fieldId, children: [
|
|
3938
|
+
fieldDef.label,
|
|
3939
|
+
fieldDef.required && /* @__PURE__ */ jsx("span", { className: "text-red-500", children: "*" })
|
|
3940
|
+
] }),
|
|
3941
|
+
/* @__PURE__ */ jsx(
|
|
3942
|
+
Upload3,
|
|
3943
|
+
{
|
|
3944
|
+
accept,
|
|
3945
|
+
multiple: allowMultiple,
|
|
3946
|
+
value: localFiles,
|
|
3947
|
+
onChange: handleChange,
|
|
3948
|
+
disabled: disabled || isUploading,
|
|
3949
|
+
placeholder: isUploading ? "Uploading..." : def.uploadPlaceholder ?? "Upload File",
|
|
3950
|
+
formatsLabel: def.uploadFormats,
|
|
3951
|
+
className: isUploading ? "opacity-70" : void 0
|
|
3952
|
+
}
|
|
3953
|
+
),
|
|
3954
|
+
hasStoredValue && /* @__PURE__ */ jsx("p", { className: "text-xs text-green-600", children: allowMultiple && Array.isArray(value) ? `${value.length} file(s) uploaded` : "File uploaded" }),
|
|
3955
|
+
fieldDef.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500", children: fieldDef.description }),
|
|
3956
|
+
showError && /* @__PURE__ */ jsx("p", { className: "text-xs text-red-500", children: error })
|
|
3957
|
+
] });
|
|
3958
|
+
};
|
|
3711
3959
|
var EditableTableWidget = ({
|
|
3712
3960
|
fieldId
|
|
3713
3961
|
}) => {
|
|
@@ -13775,6 +14023,347 @@ var OBGPathwayWidget = ({ fieldId }) => {
|
|
|
13775
14023
|
] })
|
|
13776
14024
|
] });
|
|
13777
14025
|
};
|
|
14026
|
+
var RESEND_COOLDOWN_SECONDS = 30;
|
|
14027
|
+
var PhoneInputWidget = ({ fieldId }) => {
|
|
14028
|
+
const { fieldDef, error, touched, disabled, value: fieldValue } = useField(fieldId);
|
|
14029
|
+
const { state } = useForm();
|
|
14030
|
+
const store = useFormStore();
|
|
14031
|
+
const rawPhone = fieldValue ?? "";
|
|
14032
|
+
const wasAlreadySent = rawPhone.length === 10 && !error;
|
|
14033
|
+
const [status, setStatus] = useState(wasAlreadySent ? "sent" : "idle");
|
|
14034
|
+
const [resendCooldown, setResendCooldown] = useState(0);
|
|
14035
|
+
const [resendError, setResendError] = useState("");
|
|
14036
|
+
const hasSentRef = useRef(wasAlreadySent);
|
|
14037
|
+
const sentPhoneRef = useRef(wasAlreadySent ? rawPhone : "");
|
|
14038
|
+
const isSendingRef = useRef(false);
|
|
14039
|
+
const isResendingRef = useRef(false);
|
|
14040
|
+
const showError = !!error && (touched || state.submitAttempted) && status !== "sending" && status !== "sent";
|
|
14041
|
+
useEffect(() => {
|
|
14042
|
+
if (resendCooldown <= 0) return;
|
|
14043
|
+
const t = setTimeout(() => setResendCooldown((c) => Math.max(0, c - 1)), 1e3);
|
|
14044
|
+
return () => clearTimeout(t);
|
|
14045
|
+
}, [resendCooldown]);
|
|
14046
|
+
if (!fieldDef) return null;
|
|
14047
|
+
const placeholder = typeof fieldDef.placeholder === "string" && fieldDef.placeholder.trim() ? fieldDef.placeholder : "Enter 10-digit mobile number";
|
|
14048
|
+
const handleChange = (e) => {
|
|
14049
|
+
const digits = e.target.value.replace(/\D/g, "").slice(0, 10);
|
|
14050
|
+
if (hasSentRef.current && digits !== sentPhoneRef.current) {
|
|
14051
|
+
hasSentRef.current = false;
|
|
14052
|
+
sentPhoneRef.current = "";
|
|
14053
|
+
setStatus("idle");
|
|
14054
|
+
setResendCooldown(0);
|
|
14055
|
+
setResendError("");
|
|
14056
|
+
store.setExternalError(fieldId, "Send OTP to continue");
|
|
14057
|
+
const otpFieldId = fieldDef.meta?.otpFieldId;
|
|
14058
|
+
if (otpFieldId) {
|
|
14059
|
+
store.setValue(otpFieldId, "");
|
|
14060
|
+
store.setExternalError(otpFieldId, "Verify OTP to continue");
|
|
14061
|
+
}
|
|
14062
|
+
}
|
|
14063
|
+
store.setValue(fieldId, digits);
|
|
14064
|
+
if (digits.length === 10 && !hasSentRef.current && !isSendingRef.current) {
|
|
14065
|
+
sendOtp(digits);
|
|
14066
|
+
}
|
|
14067
|
+
};
|
|
14068
|
+
const sendOtp = async (phone) => {
|
|
14069
|
+
if (isSendingRef.current) return;
|
|
14070
|
+
isSendingRef.current = true;
|
|
14071
|
+
setStatus("sending");
|
|
14072
|
+
store.setExternalError(fieldId, "Sending OTP...");
|
|
14073
|
+
try {
|
|
14074
|
+
const authCfg = store.getConfig().auth;
|
|
14075
|
+
const base = authCfg?.baseUrl ?? ((typeof process !== "undefined" && process.env?.NEXT_PUBLIC_API_URL) ?? "");
|
|
14076
|
+
const sendPath = authCfg?.sendPath ?? "/api/auth/otp/send/";
|
|
14077
|
+
const countryCode = authCfg?.defaultCountryCode ?? "+91";
|
|
14078
|
+
const res = await fetch(`${base}${sendPath}`, {
|
|
14079
|
+
method: "POST",
|
|
14080
|
+
headers: { "Content-Type": "application/json" },
|
|
14081
|
+
body: JSON.stringify({ mobile_number: `${countryCode}${phone}` })
|
|
14082
|
+
});
|
|
14083
|
+
const data = await res.json().catch(() => ({}));
|
|
14084
|
+
if (res.ok) {
|
|
14085
|
+
hasSentRef.current = true;
|
|
14086
|
+
sentPhoneRef.current = phone;
|
|
14087
|
+
setStatus("sent");
|
|
14088
|
+
setResendCooldown(RESEND_COOLDOWN_SECONDS);
|
|
14089
|
+
store.setExternalError(fieldId, null);
|
|
14090
|
+
} else {
|
|
14091
|
+
setStatus("error");
|
|
14092
|
+
store.setExternalError(fieldId, data.error ?? "Failed to send OTP. Please try again.");
|
|
14093
|
+
}
|
|
14094
|
+
} catch {
|
|
14095
|
+
setStatus("error");
|
|
14096
|
+
store.setExternalError(fieldId, "Network error. Please try again.");
|
|
14097
|
+
} finally {
|
|
14098
|
+
isSendingRef.current = false;
|
|
14099
|
+
}
|
|
14100
|
+
};
|
|
14101
|
+
const handleResend = async () => {
|
|
14102
|
+
if (isResendingRef.current || resendCooldown > 0) return;
|
|
14103
|
+
isResendingRef.current = true;
|
|
14104
|
+
setResendError("");
|
|
14105
|
+
try {
|
|
14106
|
+
const authCfg = store.getConfig().auth;
|
|
14107
|
+
const base = authCfg?.baseUrl ?? ((typeof process !== "undefined" && process.env?.NEXT_PUBLIC_API_URL) ?? "");
|
|
14108
|
+
const resendPath = authCfg?.resendPath ?? "/api/auth/otp/resend/";
|
|
14109
|
+
const countryCode = authCfg?.defaultCountryCode ?? "+91";
|
|
14110
|
+
const res = await fetch(`${base}${resendPath}`, {
|
|
14111
|
+
method: "POST",
|
|
14112
|
+
headers: { "Content-Type": "application/json" },
|
|
14113
|
+
body: JSON.stringify({ mobile_number: `${countryCode}${rawPhone}`, retrytype: "text" })
|
|
14114
|
+
});
|
|
14115
|
+
const data = await res.json().catch(() => ({}));
|
|
14116
|
+
if (res.ok) {
|
|
14117
|
+
setResendCooldown(RESEND_COOLDOWN_SECONDS);
|
|
14118
|
+
setResendError("");
|
|
14119
|
+
} else {
|
|
14120
|
+
setResendError(data.error ?? "Failed to resend OTP. Please try again.");
|
|
14121
|
+
}
|
|
14122
|
+
} catch {
|
|
14123
|
+
setResendError("Network error. Please try again.");
|
|
14124
|
+
} finally {
|
|
14125
|
+
isResendingRef.current = false;
|
|
14126
|
+
}
|
|
14127
|
+
};
|
|
14128
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
14129
|
+
/* @__PURE__ */ jsxs(Label, { htmlFor: fieldId, children: [
|
|
14130
|
+
fieldDef.label,
|
|
14131
|
+
" ",
|
|
14132
|
+
fieldDef.required && /* @__PURE__ */ jsx("span", { className: "text-red-500", children: "*" })
|
|
14133
|
+
] }),
|
|
14134
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
14135
|
+
/* @__PURE__ */ jsx(
|
|
14136
|
+
Input,
|
|
14137
|
+
{
|
|
14138
|
+
id: fieldId,
|
|
14139
|
+
type: "tel",
|
|
14140
|
+
inputMode: "numeric",
|
|
14141
|
+
value: rawPhone,
|
|
14142
|
+
onChange: handleChange,
|
|
14143
|
+
onBlur: () => store.setTouched(fieldId),
|
|
14144
|
+
disabled: disabled || status === "sending",
|
|
14145
|
+
placeholder,
|
|
14146
|
+
maxLength: 10,
|
|
14147
|
+
className: cn(
|
|
14148
|
+
showError && "border-red-500",
|
|
14149
|
+
status === "sent" && "border-green-500 pr-9",
|
|
14150
|
+
status === "sending" && "pr-9"
|
|
14151
|
+
)
|
|
14152
|
+
}
|
|
14153
|
+
),
|
|
14154
|
+
status === "sending" && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsxs(
|
|
14155
|
+
"svg",
|
|
14156
|
+
{
|
|
14157
|
+
className: "animate-spin h-4 w-4 text-muted-foreground",
|
|
14158
|
+
viewBox: "0 0 24 24",
|
|
14159
|
+
fill: "none",
|
|
14160
|
+
children: [
|
|
14161
|
+
/* @__PURE__ */ jsx(
|
|
14162
|
+
"circle",
|
|
14163
|
+
{
|
|
14164
|
+
className: "opacity-25",
|
|
14165
|
+
cx: "12",
|
|
14166
|
+
cy: "12",
|
|
14167
|
+
r: "10",
|
|
14168
|
+
stroke: "currentColor",
|
|
14169
|
+
strokeWidth: "4"
|
|
14170
|
+
}
|
|
14171
|
+
),
|
|
14172
|
+
/* @__PURE__ */ jsx(
|
|
14173
|
+
"path",
|
|
14174
|
+
{
|
|
14175
|
+
className: "opacity-75",
|
|
14176
|
+
fill: "currentColor",
|
|
14177
|
+
d: "M4 12a8 8 0 018-8v8H4z"
|
|
14178
|
+
}
|
|
14179
|
+
)
|
|
14180
|
+
]
|
|
14181
|
+
}
|
|
14182
|
+
) }),
|
|
14183
|
+
status === "sent" && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsx(
|
|
14184
|
+
"svg",
|
|
14185
|
+
{
|
|
14186
|
+
className: "h-4 w-4 text-green-500",
|
|
14187
|
+
viewBox: "0 0 20 20",
|
|
14188
|
+
fill: "currentColor",
|
|
14189
|
+
children: /* @__PURE__ */ jsx(
|
|
14190
|
+
"path",
|
|
14191
|
+
{
|
|
14192
|
+
fillRule: "evenodd",
|
|
14193
|
+
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",
|
|
14194
|
+
clipRule: "evenodd"
|
|
14195
|
+
}
|
|
14196
|
+
)
|
|
14197
|
+
}
|
|
14198
|
+
) })
|
|
14199
|
+
] }),
|
|
14200
|
+
status === "sent" && /* @__PURE__ */ jsxs("p", { className: "text-xs text-green-600", children: [
|
|
14201
|
+
"OTP sent to +91",
|
|
14202
|
+
rawPhone
|
|
14203
|
+
] }),
|
|
14204
|
+
showError && /* @__PURE__ */ jsx("p", { className: "text-xs text-red-500", children: error }),
|
|
14205
|
+
status === "sent" && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [
|
|
14206
|
+
/* @__PURE__ */ jsx("span", { children: "Didn't receive it?" }),
|
|
14207
|
+
/* @__PURE__ */ jsx(
|
|
14208
|
+
"button",
|
|
14209
|
+
{
|
|
14210
|
+
type: "button",
|
|
14211
|
+
onClick: handleResend,
|
|
14212
|
+
disabled: resendCooldown > 0,
|
|
14213
|
+
className: cn(
|
|
14214
|
+
"underline transition-colors",
|
|
14215
|
+
resendCooldown > 0 ? "opacity-40 cursor-not-allowed" : "text-primary hover:text-primary/80 cursor-pointer"
|
|
14216
|
+
),
|
|
14217
|
+
children: resendCooldown > 0 ? `Resend in ${resendCooldown}s` : "Resend OTP"
|
|
14218
|
+
}
|
|
14219
|
+
)
|
|
14220
|
+
] }),
|
|
14221
|
+
resendError && /* @__PURE__ */ jsx("p", { className: "text-xs text-red-500", children: resendError })
|
|
14222
|
+
] });
|
|
14223
|
+
};
|
|
14224
|
+
var OtpInputWidget = ({ fieldId }) => {
|
|
14225
|
+
const { fieldDef, error, touched, disabled } = useField(fieldId);
|
|
14226
|
+
const { state } = useForm();
|
|
14227
|
+
const store = useFormStore();
|
|
14228
|
+
const phoneFieldId = fieldDef?.meta?.phoneFieldId ?? "phone";
|
|
14229
|
+
const { value: phoneValue } = useField(phoneFieldId);
|
|
14230
|
+
const phone = phoneValue ?? "";
|
|
14231
|
+
const e164Phone = phone.startsWith("+") ? phone : `+91${phone}`;
|
|
14232
|
+
const [otp, setOtp] = useState("");
|
|
14233
|
+
const [status, setStatus] = useState("idle");
|
|
14234
|
+
const isVerifyingRef = useRef(false);
|
|
14235
|
+
const showError = !!error && (touched || state.submitAttempted) && status !== "verifying" && status !== "verified";
|
|
14236
|
+
if (!fieldDef) return null;
|
|
14237
|
+
const placeholder = typeof fieldDef.placeholder === "string" && fieldDef.placeholder.trim() ? fieldDef.placeholder : "Enter 4-digit OTP";
|
|
14238
|
+
const handleChange = (e) => {
|
|
14239
|
+
const digits = e.target.value.replace(/\D/g, "").slice(0, 4);
|
|
14240
|
+
if (status === "failed") {
|
|
14241
|
+
setStatus("idle");
|
|
14242
|
+
store.setExternalError(fieldId, null);
|
|
14243
|
+
}
|
|
14244
|
+
setOtp(digits);
|
|
14245
|
+
store.setValue(fieldId, digits);
|
|
14246
|
+
if (digits.length === 4 && !isVerifyingRef.current) {
|
|
14247
|
+
verifyOtp(digits);
|
|
14248
|
+
}
|
|
14249
|
+
};
|
|
14250
|
+
const verifyOtp = async (code) => {
|
|
14251
|
+
if (isVerifyingRef.current) return;
|
|
14252
|
+
isVerifyingRef.current = true;
|
|
14253
|
+
setStatus("verifying");
|
|
14254
|
+
store.setExternalError(fieldId, "Verifying...");
|
|
14255
|
+
try {
|
|
14256
|
+
const authCfg = store.getConfig().auth;
|
|
14257
|
+
const base = authCfg?.baseUrl ?? ((typeof process !== "undefined" && process.env?.NEXT_PUBLIC_API_URL) ?? "");
|
|
14258
|
+
const verifyPath = authCfg?.verifyPath ?? "/api/auth/otp/verify/";
|
|
14259
|
+
const res = await fetch(`${base}${verifyPath}`, {
|
|
14260
|
+
method: "POST",
|
|
14261
|
+
headers: { "Content-Type": "application/json" },
|
|
14262
|
+
body: JSON.stringify({ mobile_number: e164Phone, otp_code: code })
|
|
14263
|
+
});
|
|
14264
|
+
const data = await res.json().catch(() => ({}));
|
|
14265
|
+
if (res.ok) {
|
|
14266
|
+
setStatus("verified");
|
|
14267
|
+
store.setExternalError(fieldId, null);
|
|
14268
|
+
store.setValue(fieldId, {
|
|
14269
|
+
verified: true,
|
|
14270
|
+
phone: e164Phone,
|
|
14271
|
+
code,
|
|
14272
|
+
...data?.verification_token ? { token: data.verification_token } : {}
|
|
14273
|
+
});
|
|
14274
|
+
} else {
|
|
14275
|
+
setStatus("failed");
|
|
14276
|
+
const msg = data.error ?? "Invalid OTP. Please try again.";
|
|
14277
|
+
store.setExternalError(fieldId, msg);
|
|
14278
|
+
setOtp("");
|
|
14279
|
+
store.setValue(fieldId, "");
|
|
14280
|
+
}
|
|
14281
|
+
} catch {
|
|
14282
|
+
setStatus("failed");
|
|
14283
|
+
store.setExternalError(fieldId, "Network error. Please try again.");
|
|
14284
|
+
setOtp("");
|
|
14285
|
+
store.setValue(fieldId, "");
|
|
14286
|
+
} finally {
|
|
14287
|
+
isVerifyingRef.current = false;
|
|
14288
|
+
}
|
|
14289
|
+
};
|
|
14290
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
14291
|
+
/* @__PURE__ */ jsxs(Label, { htmlFor: fieldId, children: [
|
|
14292
|
+
fieldDef.label,
|
|
14293
|
+
" ",
|
|
14294
|
+
fieldDef.required && /* @__PURE__ */ jsx("span", { className: "text-red-500", children: "*" })
|
|
14295
|
+
] }),
|
|
14296
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
14297
|
+
/* @__PURE__ */ jsx(
|
|
14298
|
+
Input,
|
|
14299
|
+
{
|
|
14300
|
+
id: fieldId,
|
|
14301
|
+
type: "tel",
|
|
14302
|
+
inputMode: "numeric",
|
|
14303
|
+
value: otp,
|
|
14304
|
+
onChange: handleChange,
|
|
14305
|
+
onBlur: () => store.setTouched(fieldId),
|
|
14306
|
+
disabled: disabled || status === "verifying" || status === "verified",
|
|
14307
|
+
placeholder,
|
|
14308
|
+
maxLength: 4,
|
|
14309
|
+
className: cn(
|
|
14310
|
+
"tracking-widest text-center text-lg",
|
|
14311
|
+
showError && "border-red-500",
|
|
14312
|
+
status === "verified" && "border-green-500 pr-9",
|
|
14313
|
+
status === "verifying" && "pr-9"
|
|
14314
|
+
)
|
|
14315
|
+
}
|
|
14316
|
+
),
|
|
14317
|
+
status === "verifying" && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsxs(
|
|
14318
|
+
"svg",
|
|
14319
|
+
{
|
|
14320
|
+
className: "animate-spin h-4 w-4 text-muted-foreground",
|
|
14321
|
+
viewBox: "0 0 24 24",
|
|
14322
|
+
fill: "none",
|
|
14323
|
+
children: [
|
|
14324
|
+
/* @__PURE__ */ jsx(
|
|
14325
|
+
"circle",
|
|
14326
|
+
{
|
|
14327
|
+
className: "opacity-25",
|
|
14328
|
+
cx: "12",
|
|
14329
|
+
cy: "12",
|
|
14330
|
+
r: "10",
|
|
14331
|
+
stroke: "currentColor",
|
|
14332
|
+
strokeWidth: "4"
|
|
14333
|
+
}
|
|
14334
|
+
),
|
|
14335
|
+
/* @__PURE__ */ jsx(
|
|
14336
|
+
"path",
|
|
14337
|
+
{
|
|
14338
|
+
className: "opacity-75",
|
|
14339
|
+
fill: "currentColor",
|
|
14340
|
+
d: "M4 12a8 8 0 018-8v8H4z"
|
|
14341
|
+
}
|
|
14342
|
+
)
|
|
14343
|
+
]
|
|
14344
|
+
}
|
|
14345
|
+
) }),
|
|
14346
|
+
status === "verified" && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsx(
|
|
14347
|
+
"svg",
|
|
14348
|
+
{
|
|
14349
|
+
className: "h-4 w-4 text-green-500",
|
|
14350
|
+
viewBox: "0 0 20 20",
|
|
14351
|
+
fill: "currentColor",
|
|
14352
|
+
children: /* @__PURE__ */ jsx(
|
|
14353
|
+
"path",
|
|
14354
|
+
{
|
|
14355
|
+
fillRule: "evenodd",
|
|
14356
|
+
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",
|
|
14357
|
+
clipRule: "evenodd"
|
|
14358
|
+
}
|
|
14359
|
+
)
|
|
14360
|
+
}
|
|
14361
|
+
) })
|
|
14362
|
+
] }),
|
|
14363
|
+
status === "verified" && /* @__PURE__ */ jsx("p", { className: "text-xs text-green-600", children: "Mobile number verified successfully" }),
|
|
14364
|
+
showError && /* @__PURE__ */ jsx("p", { className: "text-xs text-red-500", children: error })
|
|
14365
|
+
] });
|
|
14366
|
+
};
|
|
13778
14367
|
var FieldRenderer = ({ fieldId }) => {
|
|
13779
14368
|
const { fieldDef, visible } = useField(fieldId);
|
|
13780
14369
|
if (!visible || !fieldDef) {
|
|
@@ -13814,6 +14403,8 @@ function renderWidget(fieldId, fieldDef) {
|
|
|
13814
14403
|
return /* @__PURE__ */ jsx(ImageUploadWidget, { fieldId });
|
|
13815
14404
|
case "media_upload":
|
|
13816
14405
|
return /* @__PURE__ */ jsx(MediaUploadWidget, { fieldId });
|
|
14406
|
+
case "file_upload":
|
|
14407
|
+
return /* @__PURE__ */ jsx(FormFileUploadWidget, { fieldId });
|
|
13817
14408
|
case "signature":
|
|
13818
14409
|
return /* @__PURE__ */ jsx(SignatureUploadWidget, { fieldId });
|
|
13819
14410
|
case "editable_table":
|
|
@@ -13866,6 +14457,10 @@ function renderWidget(fieldId, fieldDef) {
|
|
|
13866
14457
|
return /* @__PURE__ */ jsx(OBGExaminationWidget, { fieldId });
|
|
13867
14458
|
case "obg_pathway":
|
|
13868
14459
|
return /* @__PURE__ */ jsx(OBGPathwayWidget, { fieldId });
|
|
14460
|
+
case "phone_input":
|
|
14461
|
+
return /* @__PURE__ */ jsx(PhoneInputWidget, { fieldId });
|
|
14462
|
+
case "otp_input":
|
|
14463
|
+
return /* @__PURE__ */ jsx(OtpInputWidget, { fieldId });
|
|
13869
14464
|
case "toggle": {
|
|
13870
14465
|
const { value, setValue, setTouched, disabled } = useField(fieldId);
|
|
13871
14466
|
const store = useFormStore();
|
|
@@ -13986,16 +14581,6 @@ var DynamicForm = () => {
|
|
|
13986
14581
|
})
|
|
13987
14582
|
] });
|
|
13988
14583
|
};
|
|
13989
|
-
function PlainSection({
|
|
13990
|
-
title,
|
|
13991
|
-
showHeading,
|
|
13992
|
-
children
|
|
13993
|
-
}) {
|
|
13994
|
-
return /* @__PURE__ */ jsxs("div", { className: "mb-6 space-y-4", children: [
|
|
13995
|
-
showHeading ? /* @__PURE__ */ jsx("h2", { className: "text-base font-medium text-foreground", children: title }) : null,
|
|
13996
|
-
children
|
|
13997
|
-
] });
|
|
13998
|
-
}
|
|
13999
14584
|
function renderSectionChildren(children) {
|
|
14000
14585
|
return children.map((child, index) => {
|
|
14001
14586
|
if (typeof child === "string") {
|
|
@@ -14015,6 +14600,16 @@ function renderSectionChildren(children) {
|
|
|
14015
14600
|
return /* @__PURE__ */ jsx(React15__default.Fragment, {}, index);
|
|
14016
14601
|
});
|
|
14017
14602
|
}
|
|
14603
|
+
function PlainSection({
|
|
14604
|
+
title,
|
|
14605
|
+
showHeading,
|
|
14606
|
+
children
|
|
14607
|
+
}) {
|
|
14608
|
+
return /* @__PURE__ */ jsxs("div", { className: "mb-6 space-y-4", children: [
|
|
14609
|
+
showHeading ? /* @__PURE__ */ jsx("h2", { className: "text-base font-medium text-foreground", children: title }) : null,
|
|
14610
|
+
children
|
|
14611
|
+
] });
|
|
14612
|
+
}
|
|
14018
14613
|
var DynamicFormV2 = ({
|
|
14019
14614
|
showTitle = true,
|
|
14020
14615
|
sectionMode = "plain",
|
|
@@ -14055,6 +14650,247 @@ var DynamicFormV2 = ({
|
|
|
14055
14650
|
})
|
|
14056
14651
|
] });
|
|
14057
14652
|
};
|
|
14653
|
+
function flattenStepFieldIds(step) {
|
|
14654
|
+
const ids = [];
|
|
14655
|
+
for (const child of step.children) {
|
|
14656
|
+
if (typeof child === "string") {
|
|
14657
|
+
ids.push(child);
|
|
14658
|
+
} else if (child.type === "column_layout") {
|
|
14659
|
+
ids.push(...child.children);
|
|
14660
|
+
}
|
|
14661
|
+
}
|
|
14662
|
+
return ids;
|
|
14663
|
+
}
|
|
14664
|
+
function isFieldComplete(id, state, fieldMap) {
|
|
14665
|
+
if (state.visibility[id] === false) return true;
|
|
14666
|
+
if (state.errors[id]) return false;
|
|
14667
|
+
const def = fieldMap[id];
|
|
14668
|
+
if (def?.required) {
|
|
14669
|
+
const v = state.values[id];
|
|
14670
|
+
if (v === null || v === void 0 || v === "") return false;
|
|
14671
|
+
if (Array.isArray(v) && v.length === 0) return false;
|
|
14672
|
+
}
|
|
14673
|
+
return true;
|
|
14674
|
+
}
|
|
14675
|
+
function isStepComplete(step, state, fieldMap) {
|
|
14676
|
+
return flattenStepFieldIds(step).every(
|
|
14677
|
+
(id) => isFieldComplete(id, state, fieldMap)
|
|
14678
|
+
);
|
|
14679
|
+
}
|
|
14680
|
+
var Stepper2 = ({ steps, statuses, variant }) => {
|
|
14681
|
+
if (variant === "dots") {
|
|
14682
|
+
return /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 mb-6", children: steps.map((step, i) => {
|
|
14683
|
+
const status = statuses[i];
|
|
14684
|
+
return /* @__PURE__ */ jsxs(React15__default.Fragment, { children: [
|
|
14685
|
+
/* @__PURE__ */ jsx(
|
|
14686
|
+
"div",
|
|
14687
|
+
{
|
|
14688
|
+
className: cn(
|
|
14689
|
+
"h-2.5 w-2.5 rounded-full transition-colors",
|
|
14690
|
+
status === "complete" && "bg-primary",
|
|
14691
|
+
status === "active" && "bg-primary ring-2 ring-primary/30",
|
|
14692
|
+
status === "locked" && "bg-muted"
|
|
14693
|
+
),
|
|
14694
|
+
title: step.title
|
|
14695
|
+
}
|
|
14696
|
+
),
|
|
14697
|
+
i < steps.length - 1 && /* @__PURE__ */ jsx(
|
|
14698
|
+
"div",
|
|
14699
|
+
{
|
|
14700
|
+
className: cn(
|
|
14701
|
+
"flex-1 h-px transition-colors",
|
|
14702
|
+
status === "complete" ? "bg-primary" : "bg-border"
|
|
14703
|
+
)
|
|
14704
|
+
}
|
|
14705
|
+
)
|
|
14706
|
+
] }, step.id);
|
|
14707
|
+
}) });
|
|
14708
|
+
}
|
|
14709
|
+
if (variant === "labels") {
|
|
14710
|
+
return /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0 mb-6", children: steps.map((step, i) => {
|
|
14711
|
+
const status = statuses[i];
|
|
14712
|
+
return /* @__PURE__ */ jsxs(React15__default.Fragment, { children: [
|
|
14713
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1", children: [
|
|
14714
|
+
/* @__PURE__ */ jsx(
|
|
14715
|
+
"div",
|
|
14716
|
+
{
|
|
14717
|
+
className: cn(
|
|
14718
|
+
"h-2 w-2 rounded-full",
|
|
14719
|
+
status === "complete" && "bg-primary",
|
|
14720
|
+
status === "active" && "bg-primary",
|
|
14721
|
+
status === "locked" && "bg-muted"
|
|
14722
|
+
)
|
|
14723
|
+
}
|
|
14724
|
+
),
|
|
14725
|
+
/* @__PURE__ */ jsx(
|
|
14726
|
+
"span",
|
|
14727
|
+
{
|
|
14728
|
+
className: cn(
|
|
14729
|
+
"text-xs whitespace-nowrap",
|
|
14730
|
+
status === "locked" ? "text-muted-foreground" : "text-foreground"
|
|
14731
|
+
),
|
|
14732
|
+
children: step.title
|
|
14733
|
+
}
|
|
14734
|
+
)
|
|
14735
|
+
] }),
|
|
14736
|
+
i < steps.length - 1 && /* @__PURE__ */ jsx(
|
|
14737
|
+
"div",
|
|
14738
|
+
{
|
|
14739
|
+
className: cn(
|
|
14740
|
+
"flex-1 h-px mb-4 transition-colors",
|
|
14741
|
+
status === "complete" ? "bg-primary" : "bg-border"
|
|
14742
|
+
)
|
|
14743
|
+
}
|
|
14744
|
+
)
|
|
14745
|
+
] }, step.id);
|
|
14746
|
+
}) });
|
|
14747
|
+
}
|
|
14748
|
+
return /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0 mb-6", children: steps.map((step, i) => {
|
|
14749
|
+
const status = statuses[i];
|
|
14750
|
+
return /* @__PURE__ */ jsxs(React15__default.Fragment, { children: [
|
|
14751
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
14752
|
+
/* @__PURE__ */ jsx(
|
|
14753
|
+
"div",
|
|
14754
|
+
{
|
|
14755
|
+
className: cn(
|
|
14756
|
+
"flex h-7 w-7 items-center justify-center rounded-full text-xs font-semibold border-2 transition-colors",
|
|
14757
|
+
status === "complete" && "border-primary bg-primary text-primary-foreground",
|
|
14758
|
+
status === "active" && "border-primary bg-background text-primary",
|
|
14759
|
+
status === "locked" && "border-border bg-background text-muted-foreground"
|
|
14760
|
+
),
|
|
14761
|
+
children: status === "complete" ? /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" }) : i + 1
|
|
14762
|
+
}
|
|
14763
|
+
),
|
|
14764
|
+
/* @__PURE__ */ jsx(
|
|
14765
|
+
"span",
|
|
14766
|
+
{
|
|
14767
|
+
className: cn(
|
|
14768
|
+
"text-sm font-medium hidden sm:block",
|
|
14769
|
+
status === "locked" ? "text-muted-foreground" : "text-foreground"
|
|
14770
|
+
),
|
|
14771
|
+
children: step.title
|
|
14772
|
+
}
|
|
14773
|
+
)
|
|
14774
|
+
] }),
|
|
14775
|
+
i < steps.length - 1 && /* @__PURE__ */ jsx(
|
|
14776
|
+
"div",
|
|
14777
|
+
{
|
|
14778
|
+
className: cn(
|
|
14779
|
+
"flex-1 h-px mx-3 transition-colors",
|
|
14780
|
+
status === "complete" ? "bg-primary" : "bg-border"
|
|
14781
|
+
)
|
|
14782
|
+
}
|
|
14783
|
+
)
|
|
14784
|
+
] }, step.id);
|
|
14785
|
+
}) });
|
|
14786
|
+
};
|
|
14787
|
+
var ActiveStep = ({
|
|
14788
|
+
title,
|
|
14789
|
+
children
|
|
14790
|
+
}) => /* @__PURE__ */ jsxs("div", { className: "mb-4 space-y-4", children: [
|
|
14791
|
+
/* @__PURE__ */ jsx("h2", { className: "text-base font-medium text-foreground", children: title }),
|
|
14792
|
+
children
|
|
14793
|
+
] });
|
|
14794
|
+
var Wizard = ({ node }) => {
|
|
14795
|
+
const store = useFormStore();
|
|
14796
|
+
const [formState, setFormState] = useState(() => ({
|
|
14797
|
+
...store.getState()
|
|
14798
|
+
}));
|
|
14799
|
+
useEffect(() => {
|
|
14800
|
+
return store.subscribe((state) => {
|
|
14801
|
+
console.log("[MultiStepForm] store update \u2192", {
|
|
14802
|
+
values: state.values,
|
|
14803
|
+
errors: state.errors,
|
|
14804
|
+
visibility: state.visibility
|
|
14805
|
+
});
|
|
14806
|
+
setFormState({ ...state });
|
|
14807
|
+
});
|
|
14808
|
+
}, [store]);
|
|
14809
|
+
const schema = store.getSchema();
|
|
14810
|
+
const fieldMap = {};
|
|
14811
|
+
schema.fields.forEach((f) => {
|
|
14812
|
+
fieldMap[f.id] = f;
|
|
14813
|
+
});
|
|
14814
|
+
const { steps, meta } = node;
|
|
14815
|
+
const showStepper = meta?.showStepper !== false;
|
|
14816
|
+
const variant = meta?.stepperVariant ?? "numbered";
|
|
14817
|
+
const collapseCompleted = meta?.collapseCompleted !== false;
|
|
14818
|
+
const stepComplete = steps.map((s) => {
|
|
14819
|
+
const fieldIds = flattenStepFieldIds(s);
|
|
14820
|
+
const complete = isStepComplete(s, formState, fieldMap);
|
|
14821
|
+
console.log(`[MultiStepForm] step "${s.id}" fieldIds=${JSON.stringify(fieldIds)} complete=${complete}`, {
|
|
14822
|
+
errors: fieldIds.map((id) => ({ id, error: formState.errors[id] })),
|
|
14823
|
+
values: fieldIds.map((id) => ({ id, value: formState.values[id] }))
|
|
14824
|
+
});
|
|
14825
|
+
return complete;
|
|
14826
|
+
});
|
|
14827
|
+
const activeIndex = stepComplete.findIndex((c) => !c) === -1 ? steps.length : stepComplete.findIndex((c) => !c);
|
|
14828
|
+
console.log(`[MultiStepForm] activeIndex=${activeIndex} stepComplete=${JSON.stringify(stepComplete)}`);
|
|
14829
|
+
const statuses = steps.map((_, i) => {
|
|
14830
|
+
if (i < activeIndex) return "complete";
|
|
14831
|
+
if (i === activeIndex) return "active";
|
|
14832
|
+
return "locked";
|
|
14833
|
+
});
|
|
14834
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
14835
|
+
node.title && /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold mb-4", children: node.title }),
|
|
14836
|
+
showStepper && /* @__PURE__ */ jsx(Stepper2, { steps, statuses, variant }),
|
|
14837
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: steps.map((step, i) => {
|
|
14838
|
+
const status = statuses[i];
|
|
14839
|
+
if (status === "locked") return null;
|
|
14840
|
+
const children = renderSectionChildren(step.children);
|
|
14841
|
+
if (status === "complete") {
|
|
14842
|
+
if (collapseCompleted) {
|
|
14843
|
+
return /* @__PURE__ */ jsx(Section, { title: step.title, children }, step.id);
|
|
14844
|
+
}
|
|
14845
|
+
return /* @__PURE__ */ jsxs(
|
|
14846
|
+
"div",
|
|
14847
|
+
{
|
|
14848
|
+
className: "mb-4 opacity-60 pointer-events-none select-none space-y-4",
|
|
14849
|
+
children: [
|
|
14850
|
+
/* @__PURE__ */ jsx("h2", { className: "text-base font-medium text-foreground", children: step.title }),
|
|
14851
|
+
children
|
|
14852
|
+
]
|
|
14853
|
+
},
|
|
14854
|
+
step.id
|
|
14855
|
+
);
|
|
14856
|
+
}
|
|
14857
|
+
return /* @__PURE__ */ jsx(ActiveStep, { title: step.title, children }, step.id);
|
|
14858
|
+
}) })
|
|
14859
|
+
] });
|
|
14860
|
+
};
|
|
14861
|
+
var MultiStepForm = ({
|
|
14862
|
+
showTitle = true,
|
|
14863
|
+
className
|
|
14864
|
+
}) => {
|
|
14865
|
+
const store = useFormStore();
|
|
14866
|
+
const schema = store.getSchema();
|
|
14867
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-6", className), children: [
|
|
14868
|
+
showTitle && /* @__PURE__ */ jsx("div", { className: "mb-4", children: /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold", children: schema.title }) }),
|
|
14869
|
+
schema.layout.map((node) => {
|
|
14870
|
+
if (node.type === "multi_step") {
|
|
14871
|
+
return /* @__PURE__ */ jsx(Wizard, { node }, node.id);
|
|
14872
|
+
}
|
|
14873
|
+
if (node.type === "section") {
|
|
14874
|
+
return /* @__PURE__ */ jsxs("div", { className: "mb-6 space-y-4", children: [
|
|
14875
|
+
/* @__PURE__ */ jsx("h2", { className: "text-base font-medium text-foreground", children: node.title }),
|
|
14876
|
+
renderSectionChildren(node.children)
|
|
14877
|
+
] }, node.id);
|
|
14878
|
+
}
|
|
14879
|
+
if (node.type === "column_layout") {
|
|
14880
|
+
return /* @__PURE__ */ jsx(
|
|
14881
|
+
ColumnLayout,
|
|
14882
|
+
{
|
|
14883
|
+
columns: node.columns,
|
|
14884
|
+
label: node.label,
|
|
14885
|
+
children: node.children.map((fieldId) => /* @__PURE__ */ jsx(FieldRenderer, { fieldId }, fieldId))
|
|
14886
|
+
},
|
|
14887
|
+
node.id
|
|
14888
|
+
);
|
|
14889
|
+
}
|
|
14890
|
+
return null;
|
|
14891
|
+
})
|
|
14892
|
+
] });
|
|
14893
|
+
};
|
|
14058
14894
|
var SUGGESTION_KEYS = /* @__PURE__ */ new Set(["medications", "investigations", "procedures"]);
|
|
14059
14895
|
function shallowEqualKeys(a, b) {
|
|
14060
14896
|
const keysA = Object.keys(a);
|
|
@@ -14667,6 +15503,8 @@ var ReadOnlyFieldRenderer = ({
|
|
|
14667
15503
|
return /* @__PURE__ */ jsx(ReadOnlyImageUpload, { fieldDef, value });
|
|
14668
15504
|
case "media_upload":
|
|
14669
15505
|
return /* @__PURE__ */ jsx(ReadOnlyMediaUpload, { fieldDef, value });
|
|
15506
|
+
case "file_upload":
|
|
15507
|
+
return /* @__PURE__ */ jsx(ReadOnlyMediaUpload, { fieldDef, value });
|
|
14670
15508
|
case "signature":
|
|
14671
15509
|
return /* @__PURE__ */ jsx(ReadOnlySignature, { fieldDef, value });
|
|
14672
15510
|
case "editable_table":
|
|
@@ -15017,7 +15855,8 @@ var FormControls = ({
|
|
|
15017
15855
|
actionsFullWidth = false,
|
|
15018
15856
|
buttonClassName,
|
|
15019
15857
|
onSaveDraft,
|
|
15020
|
-
onSubmit
|
|
15858
|
+
onSubmit,
|
|
15859
|
+
submitLabel
|
|
15021
15860
|
}) => {
|
|
15022
15861
|
const {
|
|
15023
15862
|
state,
|
|
@@ -15106,7 +15945,7 @@ var FormControls = ({
|
|
|
15106
15945
|
disabled: !state.isValid || hasUploadsInFlight,
|
|
15107
15946
|
size: "sm",
|
|
15108
15947
|
className: buttonCn,
|
|
15109
|
-
children: hasUploadsInFlight ? "Upload in progress\u2026" : availableTransitions.length > 1 ? `Submit to ${t.to}` : "Submit"
|
|
15948
|
+
children: hasUploadsInFlight ? "Upload in progress\u2026" : availableTransitions.length > 1 ? `Submit to ${t.to}` : submitLabel ?? "Submit"
|
|
15110
15949
|
},
|
|
15111
15950
|
t.to
|
|
15112
15951
|
)) : onSubmit ? /* @__PURE__ */ jsx(
|
|
@@ -15119,7 +15958,7 @@ var FormControls = ({
|
|
|
15119
15958
|
disabled: !state.isValid || hasUploadsInFlight,
|
|
15120
15959
|
size: "sm",
|
|
15121
15960
|
className: buttonCn,
|
|
15122
|
-
children: hasUploadsInFlight ? "Upload in progress\u2026" : "Submit"
|
|
15961
|
+
children: hasUploadsInFlight ? "Upload in progress\u2026" : submitLabel ?? "Submit"
|
|
15123
15962
|
}
|
|
15124
15963
|
) : null })
|
|
15125
15964
|
]
|
|
@@ -15160,6 +15999,6 @@ function createUploadHandler(options) {
|
|
|
15160
15999
|
};
|
|
15161
16000
|
}
|
|
15162
16001
|
|
|
15163
|
-
export { AddInvestigationField, AddMedicationField, DifferentialDiagnosis, DynamicForm, DynamicFormV2, EMAIL_REGEX, FieldRenderer, FormControls, FormProvider, FormStore, ImageUploadWidget, MAR_MEDICATION_ORDERS_META_KEY, MediaUploadWidget, ReadOnlyForm, ReadOnlyImageUpload, ReadOnlyMediaUpload, ReadOnlySignature, ReadOnlyTable, RichTextWidget, SignatureUploadWidget, SmartForm, SmartTextareaWidget, createUploadHandler, evaluateRules, fieldMetaRequestsMarMedicationOrdersButton, useField, useFieldHandlers, useForm, useFormStore, useFrequentItems, usePackages, useSmartKeywords, validateField, validateForm };
|
|
16002
|
+
export { AddInvestigationField, AddMedicationField, DifferentialDiagnosis, DynamicForm, DynamicFormV2, EMAIL_REGEX, FieldRenderer, FormControls, FormFileUploadWidget, FormProvider, FormStore, ImageUploadWidget, MAR_MEDICATION_ORDERS_META_KEY, MediaUploadWidget, MultiStepForm, ReadOnlyForm, ReadOnlyImageUpload, ReadOnlyMediaUpload, ReadOnlySignature, ReadOnlyTable, RichTextWidget, SignatureUploadWidget, SmartForm, SmartTextareaWidget, Upload3 as Upload, createUploadHandler, evaluateRules, fieldMetaRequestsMarMedicationOrdersButton, useField, useFieldHandlers, useForm, useFormStore, useFrequentItems, usePackages, useSmartKeywords, validateField, validateForm };
|
|
15164
16003
|
//# sourceMappingURL=index.mjs.map
|
|
15165
16004
|
//# sourceMappingURL=index.mjs.map
|