@waypointjs/builder 0.1.4 → 0.1.6

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 CHANGED
@@ -4,6 +4,9 @@ var core = require('@waypointjs/core');
4
4
  var react = require('react');
5
5
  var zustand = require('zustand');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
+ var core$1 = require('@dnd-kit/core');
8
+ var sortable = require('@dnd-kit/sortable');
9
+ var utilities = require('@dnd-kit/utilities');
7
10
  var react$1 = require('@waypointjs/react');
8
11
  var devtools = require('@waypointjs/devtools');
9
12
 
@@ -197,6 +200,44 @@ var useBuilderStore = zustand.create((set, _get) => ({
197
200
  selectedFieldId: s.selectedStepId === stepId ? null : s.selectedFieldId,
198
201
  isDirty: true
199
202
  })),
203
+ duplicateStep: (stepId) => {
204
+ const state = _get();
205
+ const step = state.schema.steps.find((s) => s.id === stepId);
206
+ if (!step) return;
207
+ const newStepId = generateId("step");
208
+ const fieldIdMap = {};
209
+ for (const f of step.fields) {
210
+ fieldIdMap[f.id] = generateId("field");
211
+ }
212
+ const newFields = step.fields.map((f) => ({
213
+ ...f,
214
+ id: fieldIdMap[f.id],
215
+ dependsOn: f.dependsOn?.map((dep) => {
216
+ if (dep.startsWith(`${stepId}.`)) {
217
+ const oldFieldId = dep.slice(stepId.length + 1);
218
+ const newFieldId = fieldIdMap[oldFieldId];
219
+ return newFieldId ? `${newStepId}.${newFieldId}` : dep;
220
+ }
221
+ return dep;
222
+ })
223
+ }));
224
+ const newStep = {
225
+ ...step,
226
+ id: newStepId,
227
+ title: `${step.title} (copy)`,
228
+ url: `${step.url}-copy`,
229
+ fields: newFields
230
+ };
231
+ const stepIndex = state.schema.steps.findIndex((s) => s.id === stepId);
232
+ const newSteps = [...state.schema.steps];
233
+ newSteps.splice(stepIndex + 1, 0, newStep);
234
+ set({
235
+ schema: { ...state.schema, steps: newSteps },
236
+ selectedStepId: newStepId,
237
+ selectedFieldId: null,
238
+ isDirty: true
239
+ });
240
+ },
200
241
  reorderSteps: (fromIndex, toIndex) => set((s) => {
201
242
  const steps = [...s.schema.steps];
202
243
  const [moved] = steps.splice(fromIndex, 1);
@@ -249,6 +290,32 @@ var useBuilderStore = zustand.create((set, _get) => ({
249
290
  selectedFieldId: s.selectedFieldId === fieldId ? null : s.selectedFieldId,
250
291
  isDirty: true
251
292
  })),
293
+ duplicateField: (stepId, fieldId) => {
294
+ const state = _get();
295
+ const step = state.schema.steps.find((s) => s.id === stepId);
296
+ if (!step) return;
297
+ const field = step.fields.find((f) => f.id === fieldId);
298
+ if (!field) return;
299
+ const newFieldId = generateId("field");
300
+ const newField = {
301
+ ...field,
302
+ id: newFieldId,
303
+ label: `${field.label} (copy)`
304
+ };
305
+ const fieldIndex = step.fields.findIndex((f) => f.id === fieldId);
306
+ const newFields = [...step.fields];
307
+ newFields.splice(fieldIndex + 1, 0, newField);
308
+ set((s) => ({
309
+ schema: {
310
+ ...s.schema,
311
+ steps: s.schema.steps.map(
312
+ (st) => st.id === stepId ? { ...st, fields: newFields } : st
313
+ )
314
+ },
315
+ selectedFieldId: newFieldId,
316
+ isDirty: true
317
+ }));
318
+ },
252
319
  reorderFields: (stepId, fromIndex, toIndex) => set((s) => ({
253
320
  schema: {
254
321
  ...s.schema,
@@ -347,6 +414,18 @@ var useBuilderStore = zustand.create((set, _get) => ({
347
414
  isDirty: true
348
415
  }))
349
416
  }));
417
+ var BuilderReadOnlyContext = react.createContext(false);
418
+ function useBuilderReadOnly() {
419
+ return react.useContext(BuilderReadOnlyContext);
420
+ }
421
+ var BuilderCustomTypesContext = react.createContext([]);
422
+ function useBuilderCustomTypes() {
423
+ return react.useContext(BuilderCustomTypesContext);
424
+ }
425
+ var BuilderExternalEnumsContext = react.createContext([]);
426
+ function useBuilderExternalEnums() {
427
+ return react.useContext(BuilderExternalEnumsContext);
428
+ }
350
429
  var BLANK_FORM = {
351
430
  id: "",
352
431
  label: "",
@@ -360,6 +439,7 @@ function ExternalVariablePanel() {
360
439
  updateExternalVariable,
361
440
  removeExternalVariable
362
441
  } = useBuilderStore();
442
+ const readOnly = useBuilderReadOnly();
363
443
  const variables = schema.externalVariables ?? [];
364
444
  const [isAdding, setIsAdding] = react.useState(false);
365
445
  const [editingId, setEditingId] = react.useState(null);
@@ -433,7 +513,7 @@ function ExternalVariablePanel() {
433
513
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: panelStyle, children: [
434
514
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: headerStyle, children: [
435
515
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: titleStyle, children: "External Variables" }),
436
- !isAdding && /* @__PURE__ */ jsxRuntime.jsx(
516
+ !readOnly && !isAdding && /* @__PURE__ */ jsxRuntime.jsx(
437
517
  "button",
438
518
  {
439
519
  style: addBtnStyle,
@@ -488,7 +568,7 @@ function ExternalVariablePanel() {
488
568
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: varLabelStyle, children: v.label }),
489
569
  refs.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: refsStyle, children: refs.map((ref, i) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: refChipStyle, children: ref }, i)) })
490
570
  ] }),
491
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: varActionsStyle, children: [
571
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: varActionsStyle, children: [
492
572
  /* @__PURE__ */ jsxRuntime.jsx("button", { style: actionBtnStyle, onClick: () => startEdit(v), children: "Edit" }),
493
573
  /* @__PURE__ */ jsxRuntime.jsx(
494
574
  "button",
@@ -815,15 +895,23 @@ var submitBtnStyle = {
815
895
  // src/hooks/useAllFieldPaths.ts
816
896
  function useAllFieldPaths(excludeStepId, excludeFieldId) {
817
897
  const { schema } = useBuilderStore();
898
+ const externalEnums = useBuilderExternalEnums();
818
899
  const paths = [];
819
900
  for (const step of schema.steps) {
820
901
  for (const field of step.fields) {
821
902
  if (step.id === excludeStepId && field.id === excludeFieldId) continue;
903
+ let options;
904
+ if (field.externalEnumId) {
905
+ options = externalEnums.find((e) => e.id === field.externalEnumId)?.values;
906
+ } else if (field.options?.length) {
907
+ options = field.options;
908
+ }
822
909
  paths.push({
823
910
  path: `${step.id}.${field.id}`,
824
911
  label: `${step.title} \u2192 ${field.label}`,
825
912
  stepId: step.id,
826
- fieldId: field.id
913
+ fieldId: field.id,
914
+ options
827
915
  });
828
916
  }
829
917
  }
@@ -849,7 +937,9 @@ var OPERATORS = [
849
937
  { value: "notIn", label: "not in (comma list)", hasValue: true },
850
938
  { value: "matches", label: "matches regex", hasValue: true },
851
939
  { value: "exists", label: "exists", hasValue: false },
852
- { value: "notExists", label: "not exists", hasValue: false }
940
+ { value: "notExists", label: "not exists", hasValue: false },
941
+ { value: "inEnum", label: "is in enum", hasValue: true, isEnum: true },
942
+ { value: "notInEnum", label: "not in enum", hasValue: true, isEnum: true }
853
943
  ];
854
944
  function ConditionBuilder({
855
945
  value,
@@ -858,6 +948,7 @@ function ConditionBuilder({
858
948
  excludeFieldId
859
949
  }) {
860
950
  const allPaths = useAllFieldPaths(excludeStepId, excludeFieldId);
951
+ const externalEnums = useBuilderExternalEnums();
861
952
  const group = value ?? { combinator: "and", rules: [] };
862
953
  const updateRule = (index, updates) => {
863
954
  const rules = group.rules.map((r, i) => i === index ? { ...r, ...updates } : r);
@@ -920,15 +1011,48 @@ function ConditionBuilder({
920
1011
  children: OPERATORS.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.value, children: o.label }, o.value))
921
1012
  }
922
1013
  ),
923
- opDef?.hasValue && /* @__PURE__ */ jsxRuntime.jsx(
924
- "input",
1014
+ opDef?.hasValue && (opDef.isEnum ? /* @__PURE__ */ jsxRuntime.jsxs(
1015
+ "select",
925
1016
  {
926
- style: styles.valueInput,
927
- placeholder: "value",
1017
+ style: { ...styles.select, width: 140 },
928
1018
  value: rule.value != null ? String(rule.value) : "",
929
- onChange: (e) => updateRule(index, { value: e.target.value })
1019
+ onChange: (e) => updateRule(index, { value: e.target.value }),
1020
+ children: [
1021
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u2014 pick enum \u2014" }),
1022
+ externalEnums.map((en) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: en.id, children: en.label }, en.id))
1023
+ ]
930
1024
  }
931
- ),
1025
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.valueGroup, children: [
1026
+ /* @__PURE__ */ jsxRuntime.jsx(
1027
+ "input",
1028
+ {
1029
+ style: styles.valueInput,
1030
+ placeholder: "value",
1031
+ value: rule.value != null ? String(rule.value) : "",
1032
+ onChange: (e) => updateRule(index, { value: e.target.value })
1033
+ }
1034
+ ),
1035
+ externalEnums.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
1036
+ "select",
1037
+ {
1038
+ style: styles.enumPicker,
1039
+ title: "Pick a value from an enum",
1040
+ value: "",
1041
+ onChange: (e) => {
1042
+ if (e.target.value) updateRule(index, { value: e.target.value });
1043
+ },
1044
+ children: [
1045
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u229E" }),
1046
+ externalEnums.map((en) => /* @__PURE__ */ jsxRuntime.jsx("optgroup", { label: en.label, children: en.values.map((v) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: String(v.value), children: [
1047
+ v.label,
1048
+ " (",
1049
+ v.value,
1050
+ ")"
1051
+ ] }, String(v.value))) }, en.id))
1052
+ ]
1053
+ }
1054
+ )
1055
+ ] })),
932
1056
  /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles.removeBtn, onClick: () => removeRule(index), children: "\u2715" })
933
1057
  ] }, index);
934
1058
  }),
@@ -1006,6 +1130,17 @@ var styles = {
1006
1130
  alignSelf: "flex-start",
1007
1131
  color: "var(--wp-text-secondary)"
1008
1132
  },
1133
+ valueGroup: { display: "flex", alignItems: "center", gap: 4 },
1134
+ enumPicker: {
1135
+ fontSize: 11,
1136
+ padding: "4px 4px",
1137
+ border: "1px solid var(--wp-border-muted)",
1138
+ borderRadius: "var(--wp-radius)",
1139
+ background: "var(--wp-canvas)",
1140
+ color: "var(--wp-primary)",
1141
+ cursor: "pointer",
1142
+ flexShrink: 0
1143
+ },
1009
1144
  preview: { marginTop: 4 },
1010
1145
  previewLabel: {
1011
1146
  fontSize: 10,
@@ -1027,6 +1162,244 @@ var styles = {
1027
1162
  color: "var(--wp-text-mono)"
1028
1163
  }
1029
1164
  };
1165
+ var VALIDATION_RULES = [
1166
+ { type: "required", label: "Required", hasValue: false },
1167
+ { type: "min", label: "Min value", hasValue: true },
1168
+ { type: "max", label: "Max value", hasValue: true },
1169
+ { type: "minLength", label: "Min length", hasValue: true },
1170
+ { type: "maxLength", label: "Max length", hasValue: true },
1171
+ { type: "email", label: "Email format", hasValue: false },
1172
+ { type: "url", label: "URL format", hasValue: false },
1173
+ { type: "regex", label: "Matches regex", hasValue: true },
1174
+ { type: "equals", label: "equals", hasValue: true },
1175
+ { type: "notEquals", label: "not equals", hasValue: true },
1176
+ { type: "greaterThan", label: ">", hasValue: true },
1177
+ { type: "greaterThanOrEqual", label: ">=", hasValue: true },
1178
+ { type: "lessThan", label: "<", hasValue: true },
1179
+ { type: "lessThanOrEqual", label: "<=", hasValue: true },
1180
+ { type: "contains", label: "contains", hasValue: true },
1181
+ { type: "notContains", label: "not contains", hasValue: true },
1182
+ { type: "matches", label: "matches regex", hasValue: true },
1183
+ { type: "inEnum", label: "is in enum", hasValue: true, isEnum: true },
1184
+ { type: "notInEnum", label: "not in enum", hasValue: true, isEnum: true },
1185
+ { type: "custom", label: "Custom validator", hasValue: false }
1186
+ ];
1187
+ var COMPARATOR_TYPES = /* @__PURE__ */ new Set([
1188
+ "equals",
1189
+ "notEquals",
1190
+ "greaterThan",
1191
+ "greaterThanOrEqual",
1192
+ "lessThan",
1193
+ "lessThanOrEqual",
1194
+ "contains",
1195
+ "notContains"
1196
+ ]);
1197
+ function ValidationBuilder({ value, onChange }) {
1198
+ const externalEnums = useBuilderExternalEnums();
1199
+ const allFieldPaths = useAllFieldPaths();
1200
+ const updateRule = (index, updates) => {
1201
+ onChange(value.map((r, i) => i === index ? { ...r, ...updates } : r));
1202
+ };
1203
+ const addRule = () => {
1204
+ onChange([...value, { type: "required", message: "This field is required" }]);
1205
+ };
1206
+ const removeRule = (index) => {
1207
+ onChange(value.filter((_, i) => i !== index));
1208
+ };
1209
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles2.container, children: [
1210
+ value.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles2.empty, children: "No rules \u2014 field is optional by default." }),
1211
+ value.map((rule, index) => {
1212
+ const def = VALIDATION_RULES.find((r) => r.type === rule.type);
1213
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles2.rule, children: [
1214
+ /* @__PURE__ */ jsxRuntime.jsx(
1215
+ "select",
1216
+ {
1217
+ style: styles2.typeSelect,
1218
+ value: rule.type,
1219
+ onChange: (e) => updateRule(index, { type: e.target.value }),
1220
+ children: VALIDATION_RULES.map((r) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: r.type, children: r.label }, r.type))
1221
+ }
1222
+ ),
1223
+ def?.hasValue && (def.isEnum ? /* @__PURE__ */ jsxRuntime.jsxs(
1224
+ "select",
1225
+ {
1226
+ style: { ...styles2.typeSelect, flex: "0 0 140px" },
1227
+ value: rule.value != null ? String(rule.value) : "",
1228
+ onChange: (e) => updateRule(index, { value: e.target.value }),
1229
+ children: [
1230
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u2014 pick enum \u2014" }),
1231
+ externalEnums.map((en) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: en.id, children: en.label }, en.id))
1232
+ ]
1233
+ }
1234
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles2.valueGroup, children: [
1235
+ COMPARATOR_TYPES.has(rule.type) && /* @__PURE__ */ jsxRuntime.jsx(
1236
+ "button",
1237
+ {
1238
+ type: "button",
1239
+ style: {
1240
+ ...styles2.refToggle,
1241
+ background: rule.refField !== void 0 ? "var(--wp-primary)" : "var(--wp-surface-muted)",
1242
+ color: rule.refField !== void 0 ? "#fff" : "var(--wp-text-secondary)"
1243
+ },
1244
+ title: rule.refField !== void 0 ? "Comparing to field \u2014 click for static value" : "Static value \u2014 click to compare to another field",
1245
+ onClick: () => {
1246
+ if (rule.refField !== void 0) {
1247
+ updateRule(index, { refField: void 0, value: "" });
1248
+ } else {
1249
+ updateRule(index, { refField: "", value: void 0 });
1250
+ }
1251
+ },
1252
+ children: "\u21C4"
1253
+ }
1254
+ ),
1255
+ rule.refField !== void 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
1256
+ "select",
1257
+ {
1258
+ style: { ...styles2.typeSelect, flex: "1 1 auto" },
1259
+ value: rule.refField,
1260
+ onChange: (e) => updateRule(index, { refField: e.target.value }),
1261
+ children: [
1262
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u2014 pick field \u2014" }),
1263
+ allFieldPaths.filter((fp) => !fp.isExternal).map((fp) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: fp.path, children: fp.label }, fp.path))
1264
+ ]
1265
+ }
1266
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1267
+ /* @__PURE__ */ jsxRuntime.jsx(
1268
+ "input",
1269
+ {
1270
+ style: styles2.valueInput,
1271
+ placeholder: "value",
1272
+ value: rule.value != null ? String(rule.value) : "",
1273
+ onChange: (e) => updateRule(index, { value: e.target.value })
1274
+ }
1275
+ ),
1276
+ externalEnums.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
1277
+ "select",
1278
+ {
1279
+ style: styles2.enumPicker,
1280
+ title: "Pick a value from an enum",
1281
+ value: "",
1282
+ onChange: (e) => {
1283
+ if (e.target.value) updateRule(index, { value: e.target.value });
1284
+ },
1285
+ children: [
1286
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u229E" }),
1287
+ externalEnums.map((en) => /* @__PURE__ */ jsxRuntime.jsx("optgroup", { label: en.label, children: en.values.map((v) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: String(v.value), children: [
1288
+ v.label,
1289
+ " (",
1290
+ v.value,
1291
+ ")"
1292
+ ] }, String(v.value))) }, en.id))
1293
+ ]
1294
+ }
1295
+ )
1296
+ ] })
1297
+ ] })),
1298
+ rule.type === "custom" && /* @__PURE__ */ jsxRuntime.jsx(
1299
+ "input",
1300
+ {
1301
+ style: styles2.valueInput,
1302
+ placeholder: "validatorId",
1303
+ value: rule.customValidatorId ?? "",
1304
+ onChange: (e) => updateRule(index, { customValidatorId: e.target.value })
1305
+ }
1306
+ ),
1307
+ /* @__PURE__ */ jsxRuntime.jsx(
1308
+ "input",
1309
+ {
1310
+ style: styles2.messageInput,
1311
+ placeholder: "error message",
1312
+ value: rule.message,
1313
+ onChange: (e) => updateRule(index, { message: e.target.value })
1314
+ }
1315
+ ),
1316
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles2.removeBtn, onClick: () => removeRule(index), children: "\u2715" })
1317
+ ] }, index);
1318
+ }),
1319
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles2.addBtn, onClick: addRule, children: "+ Add rule" })
1320
+ ] });
1321
+ }
1322
+ var styles2 = {
1323
+ container: { display: "flex", flexDirection: "column", gap: 10 },
1324
+ empty: { fontSize: 13, color: "var(--wp-text-subtle)", textAlign: "center", padding: "12px 0" },
1325
+ rule: {
1326
+ display: "flex",
1327
+ alignItems: "center",
1328
+ gap: 8,
1329
+ background: "var(--wp-surface)",
1330
+ border: "1px solid var(--wp-border)",
1331
+ borderRadius: "var(--wp-radius-lg)",
1332
+ padding: "8px 10px"
1333
+ },
1334
+ typeSelect: {
1335
+ flex: "0 0 150px",
1336
+ fontSize: 12,
1337
+ padding: "5px 6px",
1338
+ border: "1px solid var(--wp-border-muted)",
1339
+ borderRadius: "var(--wp-radius)",
1340
+ background: "var(--wp-canvas)",
1341
+ color: "var(--wp-text)"
1342
+ },
1343
+ valueGroup: { display: "flex", alignItems: "center", gap: 4, flex: "1 1 auto" },
1344
+ refToggle: {
1345
+ border: "1px solid var(--wp-border-muted)",
1346
+ borderRadius: "var(--wp-radius)",
1347
+ cursor: "pointer",
1348
+ fontSize: 11,
1349
+ padding: "4px 6px",
1350
+ flexShrink: 0,
1351
+ fontWeight: 600,
1352
+ lineHeight: 1
1353
+ },
1354
+ valueInput: {
1355
+ width: 90,
1356
+ fontSize: 12,
1357
+ padding: "5px 6px",
1358
+ border: "1px solid var(--wp-border-muted)",
1359
+ borderRadius: "var(--wp-radius)",
1360
+ background: "var(--wp-canvas)",
1361
+ color: "var(--wp-text)"
1362
+ },
1363
+ enumPicker: {
1364
+ fontSize: 11,
1365
+ padding: "4px 4px",
1366
+ border: "1px solid var(--wp-border-muted)",
1367
+ borderRadius: "var(--wp-radius)",
1368
+ background: "var(--wp-canvas)",
1369
+ color: "var(--wp-primary)",
1370
+ cursor: "pointer",
1371
+ flexShrink: 0
1372
+ },
1373
+ messageInput: {
1374
+ flex: 1,
1375
+ fontSize: 12,
1376
+ padding: "5px 6px",
1377
+ border: "1px solid var(--wp-border-muted)",
1378
+ borderRadius: "var(--wp-radius)",
1379
+ background: "var(--wp-canvas)",
1380
+ color: "var(--wp-text)",
1381
+ minWidth: 0
1382
+ },
1383
+ removeBtn: {
1384
+ border: "none",
1385
+ background: "transparent",
1386
+ color: "var(--wp-danger)",
1387
+ cursor: "pointer",
1388
+ fontSize: 13,
1389
+ flexShrink: 0
1390
+ },
1391
+ addBtn: {
1392
+ fontSize: 12,
1393
+ padding: "6px 12px",
1394
+ background: "var(--wp-surface-muted)",
1395
+ border: "1px solid var(--wp-border-muted)",
1396
+ borderRadius: "var(--wp-radius)",
1397
+ cursor: "pointer",
1398
+ fontWeight: 500,
1399
+ alignSelf: "flex-start",
1400
+ color: "var(--wp-text-secondary)"
1401
+ }
1402
+ };
1030
1403
  function DependsOnInput({
1031
1404
  value,
1032
1405
  onChange,
@@ -1059,17 +1432,17 @@ function DependsOnInput({
1059
1432
  }, []);
1060
1433
  const getLabel = (path) => allPaths.find((p) => p.path === path)?.label ?? path;
1061
1434
  const isExternal = (path) => path.startsWith("$ext.");
1062
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, style: styles2.container, children: [
1063
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles2.tags, children: [
1064
- value.map((path) => /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { ...styles2.tag, ...isExternal(path) ? styles2.tagExt : {} }, children: [
1435
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, style: styles3.container, children: [
1436
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles3.tags, children: [
1437
+ value.map((path) => /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { ...styles3.tag, ...isExternal(path) ? styles3.tagExt : {} }, children: [
1065
1438
  getLabel(path),
1066
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles2.tagRemove, onClick: () => remove(path), children: "\u2715" })
1439
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles3.tagRemove, onClick: () => remove(path), children: "\u2715" })
1067
1440
  ] }, path)),
1068
1441
  /* @__PURE__ */ jsxRuntime.jsx(
1069
1442
  "input",
1070
1443
  {
1071
1444
  ref: inputRef,
1072
- style: styles2.input,
1445
+ style: styles3.input,
1073
1446
  placeholder: value.length === 0 ? "Search fields or $ext vars\u2026" : "Add more\u2026",
1074
1447
  value: query,
1075
1448
  onChange: (e) => {
@@ -1080,28 +1453,28 @@ function DependsOnInput({
1080
1453
  }
1081
1454
  )
1082
1455
  ] }),
1083
- open && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles2.dropdown, children: [
1084
- available.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles2.noResults, children: allPaths.length === 0 ? "No fields available in the tree yet." : "No matching fields." }),
1456
+ open && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles3.dropdown, children: [
1457
+ available.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles3.noResults, children: allPaths.length === 0 ? "No fields available in the tree yet." : "No matching fields." }),
1085
1458
  available.map((p) => /* @__PURE__ */ jsxRuntime.jsxs(
1086
1459
  "button",
1087
1460
  {
1088
- style: styles2.option,
1461
+ style: styles3.option,
1089
1462
  onMouseDown: (e) => {
1090
1463
  e.preventDefault();
1091
1464
  add(p.path);
1092
1465
  },
1093
1466
  children: [
1094
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles2.optionLabel, children: p.label }),
1095
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles2.optionPath, children: p.path })
1467
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles3.optionLabel, children: p.label }),
1468
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles3.optionPath, children: p.path })
1096
1469
  ]
1097
1470
  },
1098
1471
  p.path
1099
1472
  ))
1100
1473
  ] }),
1101
- value.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles2.hint, children: "This field will be blocked until all dependencies have a value." })
1474
+ value.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles3.hint, children: "This field will be blocked until all dependencies have a value." })
1102
1475
  ] });
1103
1476
  }
1104
- var styles2 = {
1477
+ var styles3 = {
1105
1478
  container: { position: "relative" },
1106
1479
  tags: {
1107
1480
  display: "flex",
@@ -1183,15 +1556,15 @@ function Modal({ title, onClose, children, width = 560 }) {
1183
1556
  document.addEventListener("keydown", handler);
1184
1557
  return () => document.removeEventListener("keydown", handler);
1185
1558
  }, [onClose]);
1186
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles3.overlay, onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...styles3.panel, width }, onClick: (e) => e.stopPropagation(), children: [
1187
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles3.header, children: [
1188
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles3.title, children: title }),
1189
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles3.closeBtn, onClick: onClose, children: "\u2715" })
1559
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles4.overlay, onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...styles4.panel, width }, onClick: (e) => e.stopPropagation(), children: [
1560
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.header, children: [
1561
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles4.title, children: title }),
1562
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles4.closeBtn, onClick: onClose, children: "\u2715" })
1190
1563
  ] }),
1191
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles3.body, children })
1564
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles4.body, children })
1192
1565
  ] }) });
1193
1566
  }
1194
- var styles3 = {
1567
+ var styles4 = {
1195
1568
  overlay: {
1196
1569
  position: "fixed",
1197
1570
  inset: 0,
@@ -1228,16 +1601,7 @@ var styles3 = {
1228
1601
  },
1229
1602
  body: { overflowY: "auto", padding: 20, color: "var(--wp-text)" }
1230
1603
  };
1231
- var VALIDATION_TYPES = [
1232
- { type: "required", label: "Required", hasValue: false },
1233
- { type: "min", label: "Min value", hasValue: true },
1234
- { type: "max", label: "Max value", hasValue: true },
1235
- { type: "minLength", label: "Min length", hasValue: true },
1236
- { type: "maxLength", label: "Max length", hasValue: true },
1237
- { type: "email", label: "Email format", hasValue: false },
1238
- { type: "url", label: "URL format", hasValue: false },
1239
- { type: "regex", label: "Regex pattern", hasValue: true }
1240
- ];
1604
+ var ENUM_FIELD_TYPES = ["select", "multiselect", "radio"];
1241
1605
  function FieldEditor() {
1242
1606
  const {
1243
1607
  schema,
@@ -1246,85 +1610,171 @@ function FieldEditor() {
1246
1610
  updateField,
1247
1611
  setFieldCondition
1248
1612
  } = useBuilderStore();
1249
- const [newValidationType, setNewValidationType] = react.useState("required");
1613
+ const readOnly = useBuilderReadOnly();
1614
+ const externalEnums = useBuilderExternalEnums();
1250
1615
  const [conditionModalOpen, setConditionModalOpen] = react.useState(false);
1616
+ const [validationModalOpen, setValidationModalOpen] = react.useState(false);
1617
+ const [dynDefaultModalOpen, setDynDefaultModalOpen] = react.useState(false);
1618
+ const [editingDynIdx, setEditingDynIdx] = react.useState(null);
1251
1619
  const step = schema.steps.find((s) => s.id === selectedStepId);
1252
1620
  const field = step?.fields.find((f) => f.id === selectedFieldId);
1253
1621
  if (!field || !step) {
1254
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles4.empty, children: "Select a field in the middle panel to edit its properties." });
1622
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.empty, children: "Select a field in the middle panel to edit its properties." });
1255
1623
  }
1256
1624
  const validation = field.validation ?? [];
1257
1625
  const isRequired = validation.some((v) => v.type === "required");
1258
1626
  const hasCondition = !!field.visibleWhen;
1259
1627
  const ruleCount = field.visibleWhen?.rules.length ?? 0;
1260
- const updateValidationRule = (index, updates) => {
1261
- const updated = validation.map((v, i) => i === index ? { ...v, ...updates } : v);
1262
- updateField(step.id, field.id, { validation: updated });
1263
- };
1264
- const removeValidationRule = (index) => {
1265
- const updated = validation.filter((_, i) => i !== index);
1266
- updateField(step.id, field.id, { validation: updated.length ? updated : void 0 });
1267
- };
1268
- const addValidationRule = () => {
1269
- const newRule = {
1270
- type: newValidationType,
1271
- message: `${newValidationType} error`
1272
- };
1273
- updateField(step.id, field.id, { validation: [...validation, newRule] });
1628
+ const handleValidationChange = (rules) => {
1629
+ updateField(step.id, field.id, { validation: rules.length ? rules : void 0 });
1274
1630
  };
1275
1631
  const handleDependsOnChange = (paths) => {
1276
1632
  updateField(step.id, field.id, { dependsOn: paths.length ? paths : void 0 });
1277
1633
  };
1278
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.container, children: [
1279
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.header, children: [
1280
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.headerLeft, children: [
1281
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles4.headerTitle, children: "Field Editor" }),
1282
- !isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles4.optionalBadge, children: "optional" }),
1283
- isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles4.requiredBadge, children: "required" })
1634
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.container, children: [
1635
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.header, children: [
1636
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.headerLeft, children: [
1637
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.headerTitle, children: "Field Editor" }),
1638
+ !isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.optionalBadge, children: "optional" }),
1639
+ isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.requiredBadge, children: "required" })
1284
1640
  ] }),
1285
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.fieldId, children: [
1641
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.fieldId, children: [
1286
1642
  "id: ",
1287
1643
  field.id
1288
1644
  ] })
1289
1645
  ] }),
1290
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.body, children: [
1291
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.group, children: [
1292
- /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles4.label, children: "Label" }),
1646
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.body, children: [
1647
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.group, children: [
1648
+ /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles5.label, children: "Label" }),
1293
1649
  /* @__PURE__ */ jsxRuntime.jsx(
1294
1650
  "input",
1295
1651
  {
1296
- style: styles4.input,
1652
+ style: styles5.input,
1297
1653
  value: field.label,
1298
1654
  onChange: (e) => updateField(step.id, field.id, { label: e.target.value })
1299
1655
  }
1300
1656
  )
1301
1657
  ] }),
1302
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.group, children: [
1303
- /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles4.label, children: "Placeholder" }),
1658
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.group, children: [
1659
+ /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles5.label, children: "Placeholder" }),
1304
1660
  /* @__PURE__ */ jsxRuntime.jsx(
1305
1661
  "input",
1306
1662
  {
1307
- style: styles4.input,
1663
+ style: styles5.input,
1308
1664
  value: field.placeholder ?? "",
1309
1665
  placeholder: "Optional",
1310
1666
  onChange: (e) => updateField(step.id, field.id, { placeholder: e.target.value || void 0 })
1311
1667
  }
1312
1668
  )
1313
1669
  ] }),
1314
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.group, children: [
1315
- /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles4.label, children: "Default value" }),
1670
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.group, children: [
1671
+ /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles5.label, children: "Default value" }),
1316
1672
  /* @__PURE__ */ jsxRuntime.jsx(
1317
1673
  "input",
1318
1674
  {
1319
- style: styles4.input,
1675
+ style: styles5.input,
1320
1676
  value: field.defaultValue != null ? String(field.defaultValue) : "",
1321
1677
  placeholder: "Optional",
1322
1678
  onChange: (e) => updateField(step.id, field.id, { defaultValue: e.target.value || void 0 })
1323
1679
  }
1324
1680
  )
1325
1681
  ] }),
1326
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.group, children: [
1327
- /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles4.label, children: "Depends on" }),
1682
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionRow, children: [
1683
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionInfo, children: [
1684
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.label, children: "Dynamic defaults" }),
1685
+ (field.dynamicDefault?.length ?? 0) > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: field.dynamicDefault.map((rule, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
1686
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles5.conditionBadge, children: [
1687
+ rule.when.rules.length,
1688
+ " rule",
1689
+ rule.when.rules.length !== 1 ? "s" : ""
1690
+ ] }),
1691
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontSize: 11, color: "var(--wp-text-subtle)" }, children: [
1692
+ "\u2192 ",
1693
+ JSON.stringify(rule.value)
1694
+ ] }),
1695
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1696
+ /* @__PURE__ */ jsxRuntime.jsx(
1697
+ "button",
1698
+ {
1699
+ style: { ...styles5.editConditionBtn, fontSize: 10, padding: "2px 6px" },
1700
+ onClick: () => {
1701
+ setEditingDynIdx(i);
1702
+ setDynDefaultModalOpen(true);
1703
+ },
1704
+ children: "Edit"
1705
+ }
1706
+ ),
1707
+ /* @__PURE__ */ jsxRuntime.jsx(
1708
+ "button",
1709
+ {
1710
+ style: { ...styles5.clearConditionBtn, fontSize: 10, padding: "2px 6px" },
1711
+ onClick: () => {
1712
+ const updated = field.dynamicDefault.filter((_, j) => j !== i);
1713
+ updateField(step.id, field.id, { dynamicDefault: updated.length ? updated : void 0 });
1714
+ },
1715
+ children: "\xD7"
1716
+ }
1717
+ )
1718
+ ] })
1719
+ ] }, i)) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.conditionNone, children: "No dynamic defaults" })
1720
+ ] }),
1721
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.conditionActions, children: /* @__PURE__ */ jsxRuntime.jsx(
1722
+ "button",
1723
+ {
1724
+ style: styles5.editConditionBtn,
1725
+ onClick: () => {
1726
+ setEditingDynIdx(null);
1727
+ setDynDefaultModalOpen(true);
1728
+ },
1729
+ children: "Add"
1730
+ }
1731
+ ) })
1732
+ ] }),
1733
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.divider }),
1734
+ ENUM_FIELD_TYPES.includes(field.type) && externalEnums.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.group, children: [
1735
+ /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles5.label, children: "Options source" }),
1736
+ /* @__PURE__ */ jsxRuntime.jsxs(
1737
+ "select",
1738
+ {
1739
+ style: styles5.input,
1740
+ disabled: readOnly,
1741
+ value: field.externalEnumId ?? "",
1742
+ onChange: (e) => {
1743
+ const enumId = e.target.value || void 0;
1744
+ updateField(step.id, field.id, {
1745
+ externalEnumId: enumId,
1746
+ // Clear hardcoded options when switching to an enum
1747
+ options: enumId ? void 0 : field.options
1748
+ });
1749
+ },
1750
+ children: [
1751
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u2014 Hardcoded options \u2014" }),
1752
+ externalEnums.map((en) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: en.id, children: [
1753
+ en.label,
1754
+ " (",
1755
+ en.values.length,
1756
+ " items)"
1757
+ ] }, en.id))
1758
+ ]
1759
+ }
1760
+ ),
1761
+ field.externalEnumId && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.enumInfo, children: (() => {
1762
+ const en = externalEnums.find((e) => e.id === field.externalEnumId);
1763
+ return en ? /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles5.enumBadge, children: [
1764
+ "\u229E ",
1765
+ en.label,
1766
+ " \xB7 ",
1767
+ en.values.length,
1768
+ " options"
1769
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles5.enumMissing, children: [
1770
+ '\u26A0 Enum "',
1771
+ field.externalEnumId,
1772
+ '" not found'
1773
+ ] });
1774
+ })() })
1775
+ ] }),
1776
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.group, children: [
1777
+ /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles5.label, children: "Depends on" }),
1328
1778
  /* @__PURE__ */ jsxRuntime.jsx(
1329
1779
  DependsOnInput,
1330
1780
  {
@@ -1335,92 +1785,101 @@ function FieldEditor() {
1335
1785
  }
1336
1786
  )
1337
1787
  ] }),
1338
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles4.divider }),
1339
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.conditionRow, children: [
1340
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.conditionInfo, children: [
1341
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles4.label, children: "Visibility condition" }),
1342
- hasCondition ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.conditionSummary, children: [
1343
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles4.conditionBadge, children: [
1788
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.divider }),
1789
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionRow, children: [
1790
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionInfo, children: [
1791
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.label, children: "Visibility condition" }),
1792
+ hasCondition ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionSummary, children: [
1793
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles5.conditionBadge, children: [
1344
1794
  ruleCount,
1345
1795
  " rule",
1346
1796
  ruleCount !== 1 ? "s" : "",
1347
1797
  " \xB7 ",
1348
1798
  field.visibleWhen.combinator.toUpperCase()
1349
1799
  ] }),
1350
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles4.conditionDesc, children: "Field is conditional" })
1351
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles4.conditionNone, children: "Always visible" })
1800
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.conditionDesc, children: "Field is conditional" })
1801
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.conditionNone, children: "Always visible" })
1352
1802
  ] }),
1353
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.conditionActions, children: [
1354
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles4.editConditionBtn, onClick: () => setConditionModalOpen(true), children: hasCondition ? "Edit" : "Add" }),
1803
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionActions, children: [
1804
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles5.editConditionBtn, onClick: () => setConditionModalOpen(true), children: hasCondition ? "Edit" : "Add" }),
1355
1805
  hasCondition && /* @__PURE__ */ jsxRuntime.jsx(
1356
1806
  "button",
1357
1807
  {
1358
- style: styles4.clearConditionBtn,
1808
+ style: styles5.clearConditionBtn,
1359
1809
  onClick: () => setFieldCondition(step.id, field.id, void 0),
1360
1810
  children: "Clear"
1361
1811
  }
1362
1812
  )
1363
1813
  ] })
1364
1814
  ] }),
1365
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles4.divider }),
1366
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.sectionTitle, children: [
1367
- "Validation",
1368
- !isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles4.optionalHint, children: '\u2014 no "required" rule \u2192 field is optional' })
1369
- ] }),
1370
- validation.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles4.noRules, children: "No rules \xB7 field is optional by default." }),
1371
- validation.map((rule, index) => {
1372
- const def = VALIDATION_TYPES.find((vt) => vt.type === rule.type);
1373
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.ruleCard, children: [
1374
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.ruleHeader, children: [
1375
- /* @__PURE__ */ jsxRuntime.jsx(
1376
- "span",
1377
- {
1378
- style: {
1379
- ...styles4.ruleBadge,
1380
- ...rule.type === "required" ? styles4.requiredRuleBadge : {}
1381
- },
1382
- children: rule.type
1383
- }
1384
- ),
1385
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles4.removeRuleBtn, onClick: () => removeValidationRule(index), children: "\u2715" })
1386
- ] }),
1387
- def?.hasValue && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.ruleRow, children: [
1388
- /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles4.ruleLabel, children: "Value" }),
1389
- /* @__PURE__ */ jsxRuntime.jsx(
1390
- "input",
1391
- {
1392
- style: styles4.ruleInput,
1393
- value: rule.value != null ? String(rule.value) : "",
1394
- onChange: (e) => updateValidationRule(index, { value: e.target.value })
1395
- }
1396
- )
1397
- ] }),
1398
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.ruleRow, children: [
1399
- /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles4.ruleLabel, children: "Error message" }),
1400
- /* @__PURE__ */ jsxRuntime.jsx(
1401
- "input",
1402
- {
1403
- style: styles4.ruleInput,
1404
- value: rule.message,
1405
- onChange: (e) => updateValidationRule(index, { message: e.target.value })
1406
- }
1407
- )
1408
- ] })
1409
- ] }, index);
1410
- }),
1411
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.addRule, children: [
1412
- /* @__PURE__ */ jsxRuntime.jsx(
1413
- "select",
1414
- {
1415
- style: styles4.ruleSelect,
1416
- value: newValidationType,
1417
- onChange: (e) => setNewValidationType(e.target.value),
1418
- children: VALIDATION_TYPES.map((vt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: vt.type, children: vt.label }, vt.type))
1419
- }
1420
- ),
1421
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles4.addRuleBtn, onClick: addValidationRule, children: "+ Add rule" })
1815
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.divider }),
1816
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionRow, children: [
1817
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionInfo, children: [
1818
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.label, children: "Validation" }),
1819
+ validation.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionSummary, children: [
1820
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles5.validationBadge, children: [
1821
+ validation.length,
1822
+ " rule",
1823
+ validation.length !== 1 ? "s" : "",
1824
+ isRequired ? " \xB7 required" : ""
1825
+ ] }),
1826
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.conditionDesc, children: validation.map((r) => r.type).join(", ") })
1827
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.conditionNone, children: "No rules \xB7 field is optional" })
1828
+ ] }),
1829
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionActions, children: [
1830
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles5.editConditionBtn, onClick: () => setValidationModalOpen(true), children: validation.length > 0 ? "Edit" : "Add" }),
1831
+ validation.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
1832
+ "button",
1833
+ {
1834
+ style: styles5.clearConditionBtn,
1835
+ onClick: () => updateField(step.id, field.id, { validation: void 0 }),
1836
+ children: "Clear"
1837
+ }
1838
+ )
1839
+ ] })
1422
1840
  ] })
1423
1841
  ] }),
1842
+ validationModalOpen && /* @__PURE__ */ jsxRuntime.jsxs(
1843
+ Modal,
1844
+ {
1845
+ title: `Validation \u2014 "${field.label}"`,
1846
+ onClose: () => setValidationModalOpen(false),
1847
+ width: 680,
1848
+ children: [
1849
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: styles5.modalHint, children: "Define validation rules for this field. All rules must pass for the field to be valid." }),
1850
+ /* @__PURE__ */ jsxRuntime.jsx(
1851
+ ValidationBuilder,
1852
+ {
1853
+ value: validation,
1854
+ onChange: handleValidationChange
1855
+ }
1856
+ ),
1857
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.modalFooter, children: /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles5.modalCloseBtn, onClick: () => setValidationModalOpen(false), children: "Done" }) })
1858
+ ]
1859
+ }
1860
+ ),
1861
+ dynDefaultModalOpen && /* @__PURE__ */ jsxRuntime.jsx(
1862
+ DynDefaultModal,
1863
+ {
1864
+ rule: editingDynIdx !== null ? field.dynamicDefault?.[editingDynIdx] : void 0,
1865
+ onSave: (rule) => {
1866
+ const current = field.dynamicDefault ?? [];
1867
+ let updated;
1868
+ if (editingDynIdx !== null) {
1869
+ updated = current.map((r, i) => i === editingDynIdx ? rule : r);
1870
+ } else {
1871
+ updated = [...current, rule];
1872
+ }
1873
+ updateField(step.id, field.id, { dynamicDefault: updated });
1874
+ setDynDefaultModalOpen(false);
1875
+ setEditingDynIdx(null);
1876
+ },
1877
+ onClose: () => {
1878
+ setDynDefaultModalOpen(false);
1879
+ setEditingDynIdx(null);
1880
+ }
1881
+ }
1882
+ ),
1424
1883
  conditionModalOpen && /* @__PURE__ */ jsxRuntime.jsxs(
1425
1884
  Modal,
1426
1885
  {
@@ -1428,7 +1887,7 @@ function FieldEditor() {
1428
1887
  onClose: () => setConditionModalOpen(false),
1429
1888
  width: 620,
1430
1889
  children: [
1431
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: styles4.modalHint, children: "Define when this field is visible within its step." }),
1890
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: styles5.modalHint, children: "Define when this field is visible within its step." }),
1432
1891
  /* @__PURE__ */ jsxRuntime.jsx(
1433
1892
  ConditionBuilder,
1434
1893
  {
@@ -1438,13 +1897,103 @@ function FieldEditor() {
1438
1897
  excludeFieldId: field.id
1439
1898
  }
1440
1899
  ),
1441
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles4.modalFooter, children: /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles4.modalCloseBtn, onClick: () => setConditionModalOpen(false), children: "Done" }) })
1900
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.modalFooter, children: /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles5.modalCloseBtn, onClick: () => setConditionModalOpen(false), children: "Done" }) })
1442
1901
  ]
1443
1902
  }
1444
1903
  )
1445
1904
  ] });
1446
1905
  }
1447
- var styles4 = {
1906
+ function DynDefaultModal({
1907
+ rule,
1908
+ onSave,
1909
+ onClose
1910
+ }) {
1911
+ const [condition, setCondition] = react.useState(rule?.when);
1912
+ const [value, setValue] = react.useState(rule?.value != null ? String(rule.value) : "");
1913
+ const canSave = condition && condition.rules.length > 0 && value !== "";
1914
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1915
+ Modal,
1916
+ {
1917
+ title: rule ? "Edit dynamic default" : "Add dynamic default",
1918
+ onClose,
1919
+ width: 620,
1920
+ children: [
1921
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: 13, color: "var(--wp-text-muted)", margin: "0 0 16px" }, children: "When the condition matches, this value will be used as the field default." }),
1922
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 16 }, children: [
1923
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, fontWeight: 600, color: "var(--wp-text-muted)", textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 6 }, children: "Condition" }),
1924
+ /* @__PURE__ */ jsxRuntime.jsx(ConditionBuilder, { value: condition, onChange: setCondition })
1925
+ ] }),
1926
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 20 }, children: [
1927
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, fontWeight: 600, color: "var(--wp-text-muted)", textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 6 }, children: "Default value when condition matches" }),
1928
+ /* @__PURE__ */ jsxRuntime.jsx(
1929
+ "input",
1930
+ {
1931
+ style: {
1932
+ fontSize: 13,
1933
+ padding: "6px 8px",
1934
+ border: "1px solid var(--wp-border-muted)",
1935
+ borderRadius: "var(--wp-radius)",
1936
+ outline: "none",
1937
+ width: "100%",
1938
+ boxSizing: "border-box",
1939
+ background: "var(--wp-canvas)",
1940
+ color: "var(--wp-text)"
1941
+ },
1942
+ value,
1943
+ placeholder: "Value to set as default",
1944
+ onChange: (e) => setValue(e.target.value)
1945
+ }
1946
+ )
1947
+ ] }),
1948
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8 }, children: [
1949
+ /* @__PURE__ */ jsxRuntime.jsx(
1950
+ "button",
1951
+ {
1952
+ style: {
1953
+ fontSize: 13,
1954
+ padding: "7px 16px",
1955
+ background: "transparent",
1956
+ color: "var(--wp-text-muted)",
1957
+ border: "1px solid var(--wp-border)",
1958
+ borderRadius: "var(--wp-radius-lg)",
1959
+ cursor: "pointer"
1960
+ },
1961
+ onClick: onClose,
1962
+ children: "Cancel"
1963
+ }
1964
+ ),
1965
+ /* @__PURE__ */ jsxRuntime.jsx(
1966
+ "button",
1967
+ {
1968
+ disabled: !canSave,
1969
+ style: {
1970
+ fontSize: 13,
1971
+ padding: "7px 20px",
1972
+ background: canSave ? "var(--wp-primary)" : "var(--wp-border)",
1973
+ color: "var(--wp-canvas)",
1974
+ border: "none",
1975
+ borderRadius: "var(--wp-radius-lg)",
1976
+ cursor: canSave ? "pointer" : "not-allowed",
1977
+ fontWeight: 600,
1978
+ opacity: canSave ? 1 : 0.5
1979
+ },
1980
+ onClick: () => {
1981
+ if (!canSave || !condition) return;
1982
+ let parsed = value;
1983
+ if (value === "true") parsed = true;
1984
+ else if (value === "false") parsed = false;
1985
+ else if (!isNaN(Number(value)) && value.trim() !== "") parsed = Number(value);
1986
+ onSave({ when: condition, value: parsed });
1987
+ },
1988
+ children: rule ? "Update" : "Add"
1989
+ }
1990
+ )
1991
+ ] })
1992
+ ]
1993
+ }
1994
+ );
1995
+ }
1996
+ var styles5 = {
1448
1997
  container: { display: "flex", flexDirection: "column", height: "100%", overflow: "hidden" },
1449
1998
  empty: {
1450
1999
  display: "flex",
@@ -1528,60 +2077,30 @@ var styles4 = {
1528
2077
  borderRadius: "var(--wp-radius)",
1529
2078
  cursor: "pointer"
1530
2079
  },
1531
- sectionTitle: { fontSize: 12, fontWeight: 700, color: "var(--wp-text-secondary)", textTransform: "uppercase", letterSpacing: "0.05em" },
1532
- optionalHint: { fontSize: 10, color: "var(--wp-text-subtle)", fontWeight: 400, textTransform: "none", letterSpacing: 0 },
1533
- noRules: { fontSize: 12, color: "var(--wp-text-subtle)" },
1534
- ruleCard: {
1535
- background: "var(--wp-surface)",
1536
- border: "1px solid var(--wp-border)",
1537
- borderRadius: "var(--wp-radius-lg)",
1538
- padding: 10,
1539
- display: "flex",
1540
- flexDirection: "column",
1541
- gap: 8
1542
- },
1543
- ruleHeader: { display: "flex", justifyContent: "space-between", alignItems: "center" },
1544
- ruleBadge: {
2080
+ validationBadge: {
1545
2081
  fontSize: 11,
1546
2082
  fontWeight: 700,
1547
- color: "var(--wp-primary-dark)",
1548
2083
  background: "var(--wp-primary-bg)",
2084
+ color: "var(--wp-primary-dark)",
1549
2085
  padding: "2px 8px",
1550
- borderRadius: 4
1551
- },
1552
- requiredRuleBadge: { background: "var(--wp-danger-bg-strong)", color: "var(--wp-danger)" },
1553
- removeRuleBtn: { border: "none", background: "transparent", color: "var(--wp-danger)", cursor: "pointer", fontSize: 12 },
1554
- ruleRow: { display: "flex", flexDirection: "column", gap: 3 },
1555
- ruleLabel: { fontSize: 10, fontWeight: 600, color: "var(--wp-text-subtle)", textTransform: "uppercase" },
1556
- ruleInput: {
1557
- fontSize: 12,
1558
- padding: "4px 6px",
1559
- border: "1px solid var(--wp-border-muted)",
1560
- borderRadius: 4,
1561
- outline: "none",
1562
- background: "var(--wp-canvas)",
1563
- color: "var(--wp-text)"
1564
- },
1565
- addRule: { display: "flex", gap: 8, alignItems: "center" },
1566
- ruleSelect: {
1567
- flex: 1,
1568
- fontSize: 12,
1569
- padding: "5px 6px",
1570
- border: "1px solid var(--wp-border-muted)",
1571
- borderRadius: "var(--wp-radius)",
1572
- background: "var(--wp-canvas)",
1573
- color: "var(--wp-text)"
2086
+ borderRadius: 4
1574
2087
  },
1575
- addRuleBtn: {
1576
- fontSize: 12,
1577
- padding: "5px 10px",
1578
- background: "var(--wp-surface-muted)",
1579
- border: "1px solid var(--wp-border-muted)",
1580
- borderRadius: "var(--wp-radius)",
1581
- cursor: "pointer",
1582
- fontWeight: 500,
1583
- whiteSpace: "nowrap",
1584
- color: "var(--wp-text-secondary)"
2088
+ enumInfo: { marginTop: 4 },
2089
+ enumBadge: {
2090
+ fontSize: 11,
2091
+ fontWeight: 600,
2092
+ padding: "2px 8px",
2093
+ background: "var(--wp-info-bg)",
2094
+ color: "var(--wp-info-text)",
2095
+ borderRadius: 4
2096
+ },
2097
+ enumMissing: {
2098
+ fontSize: 11,
2099
+ fontWeight: 600,
2100
+ padding: "2px 8px",
2101
+ background: "var(--wp-warning-bg)",
2102
+ color: "var(--wp-warning)",
2103
+ borderRadius: 4
1585
2104
  },
1586
2105
  modalHint: { fontSize: 13, color: "var(--wp-text-muted)", marginBottom: 16, marginTop: 0 },
1587
2106
  modalFooter: { marginTop: 20, display: "flex", justifyContent: "flex-end" },
@@ -1693,6 +2212,107 @@ function getStepDependencyLabels(stepId, deps, schema) {
1693
2212
  const required = deps.get(stepId) ?? /* @__PURE__ */ new Set();
1694
2213
  return [...required].map((id) => schema.steps.find((s) => s.id === id)?.title ?? id).filter(Boolean);
1695
2214
  }
2215
+ function SortableItem({ id, disabled, children }) {
2216
+ const {
2217
+ attributes,
2218
+ listeners,
2219
+ setNodeRef,
2220
+ transform,
2221
+ transition,
2222
+ isDragging
2223
+ } = sortable.useSortable({ id, disabled });
2224
+ const style = {
2225
+ transform: utilities.CSS.Transform.toString(transform),
2226
+ transition,
2227
+ opacity: isDragging ? 0.4 : 1,
2228
+ position: "relative"
2229
+ };
2230
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: setNodeRef, style, children: children({
2231
+ handleProps: { ...attributes, ...listeners },
2232
+ isDragging
2233
+ }) });
2234
+ }
2235
+ function DragHandle({ handleProps, disabled }) {
2236
+ return /* @__PURE__ */ jsxRuntime.jsx(
2237
+ "button",
2238
+ {
2239
+ type: "button",
2240
+ ...handleProps,
2241
+ style: {
2242
+ ...dragHandleStyle,
2243
+ ...disabled ? dragHandleDisabledStyle : {}
2244
+ },
2245
+ title: "Drag to reorder",
2246
+ "aria-label": "Drag handle",
2247
+ children: "\u283F"
2248
+ }
2249
+ );
2250
+ }
2251
+ var dragHandleStyle = {
2252
+ cursor: "grab",
2253
+ touchAction: "none",
2254
+ border: "none",
2255
+ background: "transparent",
2256
+ color: "var(--wp-text-muted)",
2257
+ fontSize: 16,
2258
+ padding: "2px 4px",
2259
+ borderRadius: 4,
2260
+ display: "flex",
2261
+ alignItems: "center",
2262
+ justifyContent: "center",
2263
+ flexShrink: 0,
2264
+ lineHeight: 1
2265
+ };
2266
+ var dragHandleDisabledStyle = {
2267
+ cursor: "not-allowed",
2268
+ opacity: 0.3
2269
+ };
2270
+ function SortableList({
2271
+ items,
2272
+ disabled,
2273
+ onReorder,
2274
+ renderItem,
2275
+ renderOverlay
2276
+ }) {
2277
+ const [activeId, setActiveId] = react.useState(null);
2278
+ const sensors = core$1.useSensors(
2279
+ core$1.useSensor(core$1.PointerSensor, { activationConstraint: { distance: 8 } }),
2280
+ core$1.useSensor(core$1.TouchSensor, { activationConstraint: { delay: 150, tolerance: 5 } }),
2281
+ core$1.useSensor(core$1.KeyboardSensor)
2282
+ );
2283
+ const handleDragStart = react.useCallback((event) => {
2284
+ setActiveId(String(event.active.id));
2285
+ }, []);
2286
+ const handleDragEnd = react.useCallback(
2287
+ (event) => {
2288
+ setActiveId(null);
2289
+ const { active, over } = event;
2290
+ if (!over || active.id === over.id) return;
2291
+ const fromIndex = items.indexOf(String(active.id));
2292
+ const toIndex = items.indexOf(String(over.id));
2293
+ if (fromIndex === -1 || toIndex === -1) return;
2294
+ onReorder(fromIndex, toIndex);
2295
+ },
2296
+ [items, onReorder]
2297
+ );
2298
+ const handleDragCancel = react.useCallback(() => {
2299
+ setActiveId(null);
2300
+ }, []);
2301
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2302
+ core$1.DndContext,
2303
+ {
2304
+ sensors,
2305
+ collisionDetection: core$1.closestCenter,
2306
+ onDragStart: handleDragStart,
2307
+ onDragEnd: handleDragEnd,
2308
+ onDragCancel: handleDragCancel,
2309
+ children: [
2310
+ /* @__PURE__ */ jsxRuntime.jsx(sortable.SortableContext, { items, strategy: sortable.verticalListSortingStrategy, disabled, children: items.map((id, index) => renderItem(id, index)) }),
2311
+ /* @__PURE__ */ jsxRuntime.jsx(core$1.DragOverlay, { dropAnimation: null, children: activeId && renderOverlay ? renderOverlay(activeId) : null })
2312
+ ]
2313
+ }
2314
+ );
2315
+ }
1696
2316
  var FIELD_TYPES = [
1697
2317
  "text",
1698
2318
  "number",
@@ -1715,10 +2335,14 @@ function FieldList() {
1715
2335
  selectedFieldId,
1716
2336
  addField,
1717
2337
  removeField,
2338
+ duplicateField,
1718
2339
  updateField,
1719
2340
  selectField,
1720
2341
  reorderFields
1721
2342
  } = useBuilderStore();
2343
+ const readOnly = useBuilderReadOnly();
2344
+ const appCustomTypes = useBuilderCustomTypes();
2345
+ const externalEnums = useBuilderExternalEnums();
1722
2346
  const [moveError, setMoveError] = react.useState(null);
1723
2347
  const step = schema.steps.find((s) => s.id === selectedStepId);
1724
2348
  const allDependencyTargets = /* @__PURE__ */ new Set();
@@ -1730,7 +2354,7 @@ function FieldList() {
1730
2354
  }
1731
2355
  }
1732
2356
  if (!step) {
1733
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.empty, children: "Select a step on the left to manage its fields." });
2357
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.empty, children: "Select a step on the left to manage its fields." });
1734
2358
  }
1735
2359
  const tryMove = (fromIndex, toIndex) => {
1736
2360
  const check = isFieldMoveValid(step.fields, step.id, fromIndex, toIndex);
@@ -1742,127 +2366,180 @@ function FieldList() {
1742
2366
  setMoveError(null);
1743
2367
  reorderFields(step.id, fromIndex, toIndex);
1744
2368
  };
1745
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.container, children: [
1746
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.header, children: [
2369
+ const fieldIds = step.fields.map((f) => f.id);
2370
+ const renderOverlay = (id) => {
2371
+ const field = step.fields.find((f) => f.id === id);
2372
+ if (!field) return null;
2373
+ const ct = appCustomTypes.find((c) => c.id === field.type);
2374
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...styles6.card, ...styles6.cardDragOverlay }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.cardTop, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.cardLeft, children: [
2375
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { ...styles6.typeBadge, ...ct ? styles6.typeBadgeCustom : {} }, children: ct ? `${ct.icon ? ct.icon + " " : ""}${ct.label}` : field.type }),
2376
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.fieldLabel, children: field.label })
2377
+ ] }) }) });
2378
+ };
2379
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.container, children: [
2380
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.header, children: [
1747
2381
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1748
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.stepTitle, children: step.title }),
1749
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.stepSub, children: [
2382
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.stepTitle, children: step.title }),
2383
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.stepSub, children: [
1750
2384
  step.fields.length,
1751
2385
  " field",
1752
2386
  step.fields.length !== 1 ? "s" : ""
1753
2387
  ] })
1754
2388
  ] }),
1755
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles5.addBtn, onClick: () => addField(step.id), children: "+ Add field" })
2389
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles6.addBtn, onClick: () => addField(step.id), children: "+ Add field" })
1756
2390
  ] }),
1757
- moveError && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.errorBanner, children: [
2391
+ moveError && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.errorBanner, children: [
1758
2392
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u26A0" }),
1759
2393
  " ",
1760
2394
  moveError
1761
2395
  ] }),
1762
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.list, children: [
1763
- step.fields.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.emptyFields, children: 'No fields yet. Click "Add field" to start.' }),
1764
- step.fields.map((field, index) => {
1765
- const isSelected = field.id === selectedFieldId;
1766
- const isRequired = field.validation?.some((v) => v.type === "required") ?? false;
1767
- const hasCondition = !!field.visibleWhen;
1768
- const hasDeps = (field.dependsOn?.length ?? 0) > 0;
1769
- const isUsedAsDep = allDependencyTargets.has(`${step.id}.${field.id}`);
1770
- const canMoveUp = index > 0 && isFieldMoveValid(step.fields, step.id, index, index - 1).valid;
1771
- const canMoveDown = index < step.fields.length - 1 && isFieldMoveValid(step.fields, step.id, index, index + 1).valid;
1772
- const intraStepDeps = (field.dependsOn ?? []).filter((p) => p.startsWith(`${step.id}.`)).map((p) => {
1773
- const fieldId = p.slice(step.id.length + 1);
1774
- return step.fields.find((f) => f.id === fieldId)?.label ?? fieldId;
1775
- });
1776
- const intraStepDependents = step.fields.filter(
1777
- (f) => f.id !== field.id && (f.dependsOn ?? []).includes(`${step.id}.${field.id}`)
1778
- );
1779
- return /* @__PURE__ */ jsxRuntime.jsxs(
1780
- "div",
1781
- {
1782
- style: { ...styles5.card, ...isSelected ? styles5.cardSelected : {} },
1783
- onClick: () => selectField(field.id),
1784
- children: [
1785
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.cardTop, children: [
1786
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.cardLeft, children: [
1787
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.typeBadge, children: field.type }),
1788
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.fieldLabel, children: field.label })
1789
- ] }),
1790
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.cardRight, children: [
1791
- /* @__PURE__ */ jsxRuntime.jsx(
1792
- "select",
1793
- {
1794
- style: styles5.typeSelect,
1795
- value: field.type,
1796
- onClick: (e) => e.stopPropagation(),
1797
- onChange: (e) => updateField(step.id, field.id, { type: e.target.value }),
1798
- children: FIELD_TYPES.map((t) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: t, children: t }, t))
1799
- }
1800
- ),
1801
- index > 0 && /* @__PURE__ */ jsxRuntime.jsx(
1802
- "button",
1803
- {
1804
- style: { ...styles5.iconBtn, ...canMoveUp ? {} : styles5.iconBtnBlocked },
1805
- title: canMoveUp ? "Move up" : "Can't move \u2014 dependency order required",
1806
- onClick: (e) => {
1807
- e.stopPropagation();
1808
- tryMove(index, index - 1);
1809
- },
1810
- children: "\u2191"
1811
- }
1812
- ),
1813
- index < step.fields.length - 1 && /* @__PURE__ */ jsxRuntime.jsx(
1814
- "button",
1815
- {
1816
- style: { ...styles5.iconBtn, ...canMoveDown ? {} : styles5.iconBtnBlocked },
1817
- title: canMoveDown ? "Move down" : "Can't move \u2014 dependency order required",
1818
- onClick: (e) => {
1819
- e.stopPropagation();
1820
- tryMove(index, index + 1);
1821
- },
1822
- children: "\u2193"
1823
- }
1824
- ),
1825
- /* @__PURE__ */ jsxRuntime.jsx(
1826
- "button",
1827
- {
1828
- style: { ...styles5.iconBtn, color: "var(--wp-danger)" },
1829
- title: "Remove field",
1830
- onClick: (e) => {
1831
- e.stopPropagation();
1832
- removeField(step.id, field.id);
1833
- },
1834
- children: "\u2715"
1835
- }
1836
- )
1837
- ] })
1838
- ] }),
1839
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.badges, children: [
1840
- !isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.badgeOptional, children: "optional" }),
1841
- isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.badgeRequired, children: "required" }),
1842
- hasCondition && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.badgeCondition, children: "conditional" }),
1843
- hasDeps && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles5.badgeDep, children: [
1844
- "depends on ",
1845
- field.dependsOn.length
1846
- ] }),
1847
- isUsedAsDep && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.badgeUsed, children: "\u2190 dependency" })
1848
- ] }),
1849
- intraStepDeps.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.depRow, children: [
1850
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.depLabel, children: "needs:" }),
1851
- intraStepDeps.map((label) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.depBadge, children: label }, label))
1852
- ] }),
1853
- intraStepDependents.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.depRow, children: [
1854
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.depLabelUsed, children: "used by:" }),
1855
- intraStepDependents.map((f) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.depBadgeUsed, children: f.label }, f.id))
1856
- ] })
1857
- ]
1858
- },
1859
- field.id
1860
- );
1861
- })
2396
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.list, children: [
2397
+ step.fields.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.emptyFields, children: 'No fields yet. Click "Add field" to start.' }),
2398
+ /* @__PURE__ */ jsxRuntime.jsx(
2399
+ SortableList,
2400
+ {
2401
+ items: fieldIds,
2402
+ disabled: readOnly,
2403
+ onReorder: tryMove,
2404
+ renderOverlay,
2405
+ renderItem: (id, index) => {
2406
+ const field = step.fields[index];
2407
+ const isSelected = field.id === selectedFieldId;
2408
+ const isRequired = field.validation?.some((v) => v.type === "required") ?? false;
2409
+ const hasCondition = !!field.visibleWhen;
2410
+ const hasDeps = (field.dependsOn?.length ?? 0) > 0;
2411
+ const enumDef = field.externalEnumId ? externalEnums.find((e) => e.id === field.externalEnumId) : void 0;
2412
+ const isUsedAsDep = allDependencyTargets.has(`${step.id}.${field.id}`);
2413
+ const canMoveUp = index > 0 && isFieldMoveValid(step.fields, step.id, index, index - 1).valid;
2414
+ const canMoveDown = index < step.fields.length - 1 && isFieldMoveValid(step.fields, step.id, index, index + 1).valid;
2415
+ const intraStepDeps = (field.dependsOn ?? []).filter((p) => p.startsWith(`${step.id}.`)).map((p) => {
2416
+ const fieldId = p.slice(step.id.length + 1);
2417
+ return step.fields.find((f) => f.id === fieldId)?.label ?? fieldId;
2418
+ });
2419
+ const intraStepDependents = step.fields.filter(
2420
+ (f) => f.id !== field.id && (f.dependsOn ?? []).includes(`${step.id}.${field.id}`)
2421
+ );
2422
+ return /* @__PURE__ */ jsxRuntime.jsx(SortableItem, { id: field.id, disabled: readOnly, children: ({ handleProps }) => /* @__PURE__ */ jsxRuntime.jsxs(
2423
+ "div",
2424
+ {
2425
+ style: { ...styles6.card, ...isSelected ? styles6.cardSelected : {} },
2426
+ onClick: () => selectField(field.id),
2427
+ children: [
2428
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.cardTop, children: [
2429
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.cardLeft, children: [
2430
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx(DragHandle, { handleProps }),
2431
+ (() => {
2432
+ const ct = appCustomTypes.find((c) => c.id === field.type);
2433
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { style: { ...styles6.typeBadge, ...ct ? styles6.typeBadgeCustom : {} }, children: ct ? `${ct.icon ? ct.icon + " " : ""}${ct.label}` : field.type });
2434
+ })(),
2435
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.fieldLabel, children: field.label })
2436
+ ] }),
2437
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.cardRight, children: [
2438
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsxs(
2439
+ "select",
2440
+ {
2441
+ style: styles6.typeSelect,
2442
+ value: field.type,
2443
+ onClick: (e) => e.stopPropagation(),
2444
+ onChange: (e) => {
2445
+ const newType = e.target.value;
2446
+ const customType = appCustomTypes.find((ct) => ct.id === newType);
2447
+ updateField(step.id, field.id, {
2448
+ type: newType,
2449
+ ...customType?.defaultValidation ? { validation: customType.defaultValidation } : {}
2450
+ });
2451
+ },
2452
+ children: [
2453
+ FIELD_TYPES.map((t) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: t, children: t }, t)),
2454
+ appCustomTypes.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("optgroup", { label: "Custom", children: appCustomTypes.map((ct) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: ct.id, children: [
2455
+ ct.icon ? `${ct.icon} ` : "",
2456
+ ct.label
2457
+ ] }, ct.id)) })
2458
+ ]
2459
+ }
2460
+ ),
2461
+ !readOnly && index > 0 && /* @__PURE__ */ jsxRuntime.jsx(
2462
+ "button",
2463
+ {
2464
+ style: { ...styles6.iconBtn, ...canMoveUp ? {} : styles6.iconBtnBlocked },
2465
+ title: canMoveUp ? "Move up" : "Can't move \u2014 dependency order required",
2466
+ onClick: (e) => {
2467
+ e.stopPropagation();
2468
+ tryMove(index, index - 1);
2469
+ },
2470
+ children: "\u2191"
2471
+ }
2472
+ ),
2473
+ !readOnly && index < step.fields.length - 1 && /* @__PURE__ */ jsxRuntime.jsx(
2474
+ "button",
2475
+ {
2476
+ style: { ...styles6.iconBtn, ...canMoveDown ? {} : styles6.iconBtnBlocked },
2477
+ title: canMoveDown ? "Move down" : "Can't move \u2014 dependency order required",
2478
+ onClick: (e) => {
2479
+ e.stopPropagation();
2480
+ tryMove(index, index + 1);
2481
+ },
2482
+ children: "\u2193"
2483
+ }
2484
+ ),
2485
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
2486
+ "button",
2487
+ {
2488
+ style: styles6.iconBtn,
2489
+ title: "Duplicate field",
2490
+ onClick: (e) => {
2491
+ e.stopPropagation();
2492
+ duplicateField(step.id, field.id);
2493
+ },
2494
+ children: "\u29C9"
2495
+ }
2496
+ ),
2497
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
2498
+ "button",
2499
+ {
2500
+ style: { ...styles6.iconBtn, color: "var(--wp-danger)" },
2501
+ title: "Remove field",
2502
+ onClick: (e) => {
2503
+ e.stopPropagation();
2504
+ removeField(step.id, field.id);
2505
+ },
2506
+ children: "\u2715"
2507
+ }
2508
+ )
2509
+ ] })
2510
+ ] }),
2511
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.badges, children: [
2512
+ !isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.badgeOptional, children: "optional" }),
2513
+ isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.badgeRequired, children: "required" }),
2514
+ hasCondition && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.badgeCondition, children: "conditional" }),
2515
+ hasDeps && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles6.badgeDep, children: [
2516
+ "depends on ",
2517
+ field.dependsOn.length
2518
+ ] }),
2519
+ isUsedAsDep && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.badgeUsed, children: "\u2190 dependency" }),
2520
+ field.externalEnumId && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles6.badgeEnum, children: [
2521
+ "\u229E ",
2522
+ enumDef ? enumDef.label : field.externalEnumId
2523
+ ] })
2524
+ ] }),
2525
+ intraStepDeps.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.depRow, children: [
2526
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.depLabel, children: "needs:" }),
2527
+ intraStepDeps.map((label) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.depBadge, children: label }, label))
2528
+ ] }),
2529
+ intraStepDependents.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.depRow, children: [
2530
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.depLabelUsed, children: "used by:" }),
2531
+ intraStepDependents.map((f) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.depBadgeUsed, children: f.label }, f.id))
2532
+ ] })
2533
+ ]
2534
+ }
2535
+ ) }, field.id);
2536
+ }
2537
+ }
2538
+ )
1862
2539
  ] })
1863
2540
  ] });
1864
2541
  }
1865
- var styles5 = {
2542
+ var styles6 = {
1866
2543
  container: { display: "flex", flexDirection: "column", height: "100%" },
1867
2544
  empty: {
1868
2545
  display: "flex",
@@ -1919,6 +2596,12 @@ var styles5 = {
1919
2596
  gap: 6
1920
2597
  },
1921
2598
  cardSelected: { background: "var(--wp-primary-muted)", border: "1px solid var(--wp-primary-border)" },
2599
+ cardDragOverlay: {
2600
+ boxShadow: "0 4px 16px rgba(0,0,0,0.25)",
2601
+ border: "1px solid var(--wp-primary-border)",
2602
+ background: "var(--wp-surface)",
2603
+ opacity: 0.95
2604
+ },
1922
2605
  cardTop: { display: "flex", alignItems: "center", justifyContent: "space-between" },
1923
2606
  cardLeft: { display: "flex", alignItems: "center", gap: 8, minWidth: 0 },
1924
2607
  cardRight: { display: "flex", alignItems: "center", gap: 4, flexShrink: 0 },
@@ -1931,6 +2614,10 @@ var styles5 = {
1931
2614
  fontWeight: 600,
1932
2615
  flexShrink: 0
1933
2616
  },
2617
+ typeBadgeCustom: {
2618
+ background: "var(--wp-success-bg)",
2619
+ color: "var(--wp-success)"
2620
+ },
1934
2621
  fieldLabel: {
1935
2622
  fontSize: 13,
1936
2623
  fontWeight: 600,
@@ -2005,6 +2692,14 @@ var styles5 = {
2005
2692
  borderRadius: 3,
2006
2693
  textTransform: "uppercase"
2007
2694
  },
2695
+ badgeEnum: {
2696
+ fontSize: 9,
2697
+ fontWeight: 600,
2698
+ padding: "1px 6px",
2699
+ background: "var(--wp-info-bg)",
2700
+ color: "var(--wp-info-text)",
2701
+ borderRadius: 3
2702
+ },
2008
2703
  depRow: { display: "flex", alignItems: "center", flexWrap: "wrap", gap: 4 },
2009
2704
  depLabel: { fontSize: 10, fontWeight: 600, color: "var(--wp-text-muted)", textTransform: "uppercase" },
2010
2705
  depLabelUsed: { fontSize: 10, fontWeight: 600, color: "var(--wp-success)", textTransform: "uppercase" },
@@ -2025,12 +2720,28 @@ var styles5 = {
2025
2720
  borderRadius: 4
2026
2721
  }
2027
2722
  };
2028
- function PreviewPanel({ store, schema }) {
2723
+ function PreviewPanel({ store, schema, externalEnums }) {
2029
2724
  const [done, setDone] = react.useState(false);
2030
- const { tree, currentStep, progress } = react$1.useWaypoint(store);
2725
+ const { tree, currentStep, progress } = react$1.useWaypoint(store, externalEnums);
2031
2726
  const stepId = currentStep?.definition.id ?? "";
2032
2727
  const { fields, stepData, setFieldValue } = react$1.useWaypointStep(store, stepId);
2033
2728
  const [errors, setErrors] = react.useState({});
2729
+ const extVarDefs = schema.externalVariables ?? [];
2730
+ const [mockVars, setMockVars] = react.useState(() => {
2731
+ const initial = {};
2732
+ for (const v of extVarDefs) {
2733
+ initial[v.id] = v.type === "boolean" ? false : v.type === "number" ? 0 : "";
2734
+ }
2735
+ return initial;
2736
+ });
2737
+ react.useEffect(() => {
2738
+ for (const [varId, value] of Object.entries(mockVars)) {
2739
+ store.getState().setExternalVar(varId, value);
2740
+ }
2741
+ }, [mockVars]);
2742
+ function handleMockVarChange(varId, value) {
2743
+ setMockVars((prev) => ({ ...prev, [varId]: value }));
2744
+ }
2034
2745
  function handleNext() {
2035
2746
  const newErrors = {};
2036
2747
  for (const field of fields) {
@@ -2050,8 +2761,11 @@ function PreviewPanel({ store, schema }) {
2050
2761
  setErrors({});
2051
2762
  const oldIds = tree.steps.map((s) => s.definition.id).join(",");
2052
2763
  store.getState().setStepData(stepId, stepData);
2764
+ if ((store.getState().skippedSteps ?? []).includes(stepId)) {
2765
+ store.getState().unskipStep(stepId);
2766
+ }
2053
2767
  const newData = store.getState().data;
2054
- const newTree = core.resolveTree(schema, newData, {});
2768
+ const newTree = core.resolveTree(schema, newData, store.getState().externalVars, externalEnums);
2055
2769
  const newIds = newTree.steps.map((s) => s.definition.id).join(",");
2056
2770
  if (oldIds !== newIds) {
2057
2771
  store.getState().truncateHistoryAt(stepId);
@@ -2069,6 +2783,9 @@ function PreviewPanel({ store, schema }) {
2069
2783
  }
2070
2784
  function handleRestart() {
2071
2785
  store.getState().init(schema);
2786
+ for (const [varId, value] of Object.entries(mockVars)) {
2787
+ store.getState().setExternalVar(varId, value);
2788
+ }
2072
2789
  setDone(false);
2073
2790
  setErrors({});
2074
2791
  }
@@ -2076,77 +2793,124 @@ function PreviewPanel({ store, schema }) {
2076
2793
  (s) => s.definition.id === currentStep?.definition.id
2077
2794
  );
2078
2795
  if (done) {
2079
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.panel, children: [
2080
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.leftCol, children: /* @__PURE__ */ jsxRuntime.jsx(
2796
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.panel, children: [
2797
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.leftCol, children: [
2798
+ /* @__PURE__ */ jsxRuntime.jsx(
2799
+ StepList,
2800
+ {
2801
+ tree,
2802
+ currentIdx,
2803
+ onSelect: (id) => store.getState().setCurrentStep(id),
2804
+ skippedStepIds: store.getState().skippedSteps
2805
+ }
2806
+ ),
2807
+ extVarDefs.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
2808
+ MockVarPanel,
2809
+ {
2810
+ extVarDefs,
2811
+ mockVars,
2812
+ onChange: handleMockVarChange
2813
+ }
2814
+ )
2815
+ ] }),
2816
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.divider }),
2817
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.rightCol, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.doneScreen, children: [
2818
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.doneIcon, children: "\u2713" }),
2819
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.doneTitle, children: "Parcours termin\xE9 !" }),
2820
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: styles7.doneText, children: "Toutes les \xE9tapes ont \xE9t\xE9 compl\xE9t\xE9es avec succ\xE8s." }),
2821
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles7.primaryBtn, onClick: handleRestart, children: "Recommencer" })
2822
+ ] }) })
2823
+ ] });
2824
+ }
2825
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.panel, children: [
2826
+ /* @__PURE__ */ jsxRuntime.jsx(devtools.DevPanel, { store }),
2827
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.leftCol, children: [
2828
+ /* @__PURE__ */ jsxRuntime.jsx(
2081
2829
  StepList,
2082
2830
  {
2083
2831
  tree,
2084
2832
  currentIdx,
2085
2833
  onSelect: (id) => store.getState().setCurrentStep(id)
2086
2834
  }
2087
- ) }),
2088
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.divider }),
2089
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.rightCol, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.doneScreen, children: [
2090
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.doneIcon, children: "\u2713" }),
2091
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.doneTitle, children: "Parcours termin\xE9 !" }),
2092
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: styles6.doneText, children: "Toutes les \xE9tapes ont \xE9t\xE9 compl\xE9t\xE9es avec succ\xE8s." }),
2093
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles6.primaryBtn, onClick: handleRestart, children: "Recommencer" })
2094
- ] }) })
2095
- ] });
2096
- }
2097
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.panel, children: [
2098
- /* @__PURE__ */ jsxRuntime.jsx(devtools.DevPanel, { store }),
2099
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.leftCol, children: /* @__PURE__ */ jsxRuntime.jsx(
2100
- StepList,
2101
- {
2102
- tree,
2103
- currentIdx,
2104
- onSelect: (id) => store.getState().setCurrentStep(id)
2105
- }
2106
- ) }),
2107
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.divider }),
2108
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.rightCol, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.stepRenderer, children: [
2109
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.progressTrack, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...styles6.progressFill, width: `${progress}%` } }) }),
2110
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: styles6.stepTitle, children: currentStep?.definition.title ?? "" }),
2111
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.fieldsContainer, children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(
2112
- FieldRenderer,
2835
+ ),
2836
+ extVarDefs.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
2837
+ MockVarPanel,
2113
2838
  {
2114
- field,
2115
- value: stepData[field.definition.id],
2116
- error: errors[field.definition.id],
2117
- onChange: (val) => {
2118
- setFieldValue(field.definition.id, val);
2119
- if (errors[field.definition.id]) {
2120
- setErrors((prev) => {
2121
- const next = { ...prev };
2122
- delete next[field.definition.id];
2123
- return next;
2124
- });
2839
+ extVarDefs,
2840
+ mockVars,
2841
+ onChange: handleMockVarChange
2842
+ }
2843
+ )
2844
+ ] }),
2845
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.divider }),
2846
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.rightCol, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.stepRenderer, children: [
2847
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.progressTrack, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...styles7.progressFill, width: `${progress}%` } }) }),
2848
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: styles7.stepTitle, children: currentStep?.definition.title ?? "" }),
2849
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.fieldsContainer, children: fields.map((field) => {
2850
+ const storedVal = stepData[field.definition.id];
2851
+ const displayVal = storedVal !== void 0 && storedVal !== null && storedVal !== "" ? storedVal : field.resolvedDefaultValue ?? field.definition.defaultValue ?? void 0;
2852
+ return /* @__PURE__ */ jsxRuntime.jsx(
2853
+ FieldRenderer,
2854
+ {
2855
+ field,
2856
+ value: displayVal,
2857
+ error: errors[field.definition.id],
2858
+ onChange: (val) => {
2859
+ setFieldValue(field.definition.id, val);
2860
+ if (errors[field.definition.id]) {
2861
+ setErrors((prev) => {
2862
+ const next = { ...prev };
2863
+ delete next[field.definition.id];
2864
+ return next;
2865
+ });
2866
+ }
2125
2867
  }
2126
- }
2127
- },
2128
- field.definition.id
2129
- )) }),
2130
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.navRow, children: [
2131
- currentIdx > 0 && /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles6.secondaryBtn, onClick: handlePrev, children: "\u2190 Pr\xE9c\xE9dent" }),
2132
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: { ...styles6.primaryBtn, marginLeft: "auto" }, onClick: handleNext, children: core.getNextStep(tree.steps, stepId) ? "Continuer \u2192" : "Terminer \u2713" })
2868
+ },
2869
+ field.definition.id
2870
+ );
2871
+ }) }),
2872
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.navRow, children: [
2873
+ currentIdx > 0 && /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles7.secondaryBtn, onClick: handlePrev, children: "\u2190 Pr\xE9c\xE9dent" }),
2874
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 8, marginLeft: "auto" }, children: [
2875
+ currentStep?.definition.skippable && /* @__PURE__ */ jsxRuntime.jsx(
2876
+ "button",
2877
+ {
2878
+ style: styles7.secondaryBtn,
2879
+ onClick: () => {
2880
+ setErrors({});
2881
+ store.getState().skipStep(stepId);
2882
+ const next = core.getNextStep(tree.steps, stepId);
2883
+ if (next) {
2884
+ store.getState().setCurrentStep(next.definition.id);
2885
+ } else {
2886
+ setDone(true);
2887
+ }
2888
+ },
2889
+ children: "Passer"
2890
+ }
2891
+ ),
2892
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles7.primaryBtn, onClick: handleNext, children: core.getNextStep(tree.steps, stepId) ? "Continuer \u2192" : "Terminer \u2713" })
2893
+ ] })
2133
2894
  ] })
2134
2895
  ] }) })
2135
2896
  ] });
2136
2897
  }
2137
- function StepList({ tree, currentIdx, onSelect }) {
2898
+ function StepList({ tree, currentIdx, onSelect, skippedStepIds }) {
2899
+ const skipped = new Set(skippedStepIds ?? []);
2138
2900
  const allSteps = [
2139
2901
  ...tree.steps.map((s) => ({ ...s, hidden: false })),
2140
2902
  ...tree.hiddenSteps.map((s) => ({ ...s, hidden: true }))
2141
2903
  ];
2142
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.stepList, children: [
2143
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.stepListTitle, children: "\xC9tapes" }),
2904
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.stepList, children: [
2905
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.stepListTitle, children: "\xC9tapes" }),
2144
2906
  allSteps.map((step) => {
2145
- const isVisible = !step.hidden;
2907
+ const isStepVisible = !step.hidden;
2146
2908
  const visIdx = tree.steps.findIndex((s) => s.definition.id === step.definition.id);
2909
+ const isSkipped = skipped.has(step.definition.id);
2147
2910
  let status = "hidden";
2148
- if (isVisible) {
2149
- if (visIdx < currentIdx) status = "done";
2911
+ if (isStepVisible) {
2912
+ if (isSkipped && visIdx < currentIdx) status = "skipped";
2913
+ else if (visIdx < currentIdx) status = "done";
2150
2914
  else if (visIdx === currentIdx) status = "current";
2151
2915
  else status = "upcoming";
2152
2916
  }
@@ -2154,23 +2918,26 @@ function StepList({ tree, currentIdx, onSelect }) {
2154
2918
  "div",
2155
2919
  {
2156
2920
  style: {
2157
- ...styles6.stepItem,
2158
- ...status === "current" ? styles6.stepItemCurrent : {},
2159
- ...status === "hidden" ? styles6.stepItemHidden : {},
2160
- cursor: status === "done" ? "pointer" : "default"
2921
+ ...styles7.stepItem,
2922
+ ...status === "current" ? styles7.stepItemCurrent : {},
2923
+ ...status === "hidden" ? styles7.stepItemHidden : {},
2924
+ ...status === "skipped" ? { opacity: 0.6 } : {},
2925
+ cursor: status === "done" || status === "skipped" ? "pointer" : "default"
2161
2926
  },
2162
2927
  onClick: () => {
2163
- if (status === "done") onSelect(step.definition.id);
2928
+ if (status === "done" || status === "skipped") onSelect(step.definition.id);
2164
2929
  },
2165
2930
  children: [
2166
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles6.stepStatus, children: [
2931
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles7.stepStatus, children: [
2167
2932
  status === "done" && "\u2713",
2933
+ status === "skipped" && "\u23ED",
2168
2934
  status === "current" && "\u2192",
2169
2935
  status === "upcoming" && "\u25CB",
2170
2936
  status === "hidden" && "\u2013"
2171
2937
  ] }),
2172
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.stepName, children: step.definition.title }),
2173
- status === "hidden" && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.hiddenBadge, children: "hidden" })
2938
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles7.stepName, children: step.definition.title }),
2939
+ status === "hidden" && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles7.hiddenBadge, children: "hidden" }),
2940
+ status === "skipped" && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { ...styles7.hiddenBadge, color: "var(--wp-warning)", background: "var(--wp-warning-bg)" }, children: "skipped" })
2174
2941
  ]
2175
2942
  },
2176
2943
  step.definition.id
@@ -2178,13 +2945,51 @@ function StepList({ tree, currentIdx, onSelect }) {
2178
2945
  })
2179
2946
  ] });
2180
2947
  }
2948
+ function MockVarPanel({ extVarDefs, mockVars, onChange }) {
2949
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.mockPanel, children: [
2950
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.mockPanelTitle, children: [
2951
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u26A1 External Variables" }),
2952
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles7.mockPanelHint, children: "mock values" })
2953
+ ] }),
2954
+ extVarDefs.map((v) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.mockVarRow, children: [
2955
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.mockVarLabel, children: [
2956
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles7.mockVarId, children: [
2957
+ "$",
2958
+ `ext.${v.id}`
2959
+ ] }),
2960
+ v.blocking && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles7.mockBlockingBadge, children: "!" })
2961
+ ] }),
2962
+ v.type === "boolean" ? /* @__PURE__ */ jsxRuntime.jsxs("label", { style: styles7.mockCheckRow, children: [
2963
+ /* @__PURE__ */ jsxRuntime.jsx(
2964
+ "input",
2965
+ {
2966
+ type: "checkbox",
2967
+ checked: Boolean(mockVars[v.id]),
2968
+ onChange: (e) => onChange(v.id, e.target.checked)
2969
+ }
2970
+ ),
2971
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 11, color: "var(--wp-text-muted)" }, children: String(mockVars[v.id]) })
2972
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
2973
+ "input",
2974
+ {
2975
+ style: styles7.mockInput,
2976
+ type: v.type === "number" ? "number" : "text",
2977
+ value: String(mockVars[v.id] ?? ""),
2978
+ placeholder: v.label,
2979
+ onChange: (e) => onChange(v.id, v.type === "number" ? Number(e.target.value) : e.target.value)
2980
+ }
2981
+ )
2982
+ ] }, v.id))
2983
+ ] });
2984
+ }
2181
2985
  function FieldRenderer({ field, value, error, onChange }) {
2182
2986
  const { definition } = field;
2183
- const inputStyle2 = { ...styles6.input, ...error ? styles6.inputError : {} };
2184
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.fieldGroup, children: [
2185
- /* @__PURE__ */ jsxRuntime.jsxs("label", { style: styles6.label, children: [
2987
+ const options = field.resolvedOptions ?? definition.options ?? [];
2988
+ const inputStyle2 = { ...styles7.input, ...error ? styles7.inputError : {} };
2989
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.fieldGroup, children: [
2990
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { style: styles7.label, children: [
2186
2991
  definition.label,
2187
- definition.validation?.some((r) => r.type === "required") && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.required, children: " *" })
2992
+ definition.validation?.some((r) => r.type === "required") && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles7.required, children: " *" })
2188
2993
  ] }),
2189
2994
  (definition.type === "text" || definition.type === "email" || definition.type === "tel" || definition.type === "password" || definition.type === "url" || definition.type === "number" || definition.type === "date") && /* @__PURE__ */ jsxRuntime.jsx(
2190
2995
  "input",
@@ -2213,7 +3018,7 @@ function FieldRenderer({ field, value, error, onChange }) {
2213
3018
  onChange: (e) => onChange(e.target.value),
2214
3019
  children: [
2215
3020
  /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u2014 Choisir \u2014" }),
2216
- definition.options?.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: String(opt.value), children: opt.label }, String(opt.value)))
3021
+ options.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: String(opt.value), children: opt.label }, String(opt.value)))
2217
3022
  ]
2218
3023
  }
2219
3024
  ),
@@ -2227,10 +3032,10 @@ function FieldRenderer({ field, value, error, onChange }) {
2227
3032
  const selected = Array.from(e.target.selectedOptions).map((o) => o.value);
2228
3033
  onChange(selected);
2229
3034
  },
2230
- children: definition.options?.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: String(opt.value), children: opt.label }, String(opt.value)))
3035
+ children: options.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: String(opt.value), children: opt.label }, String(opt.value)))
2231
3036
  }
2232
3037
  ),
2233
- definition.type === "radio" && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: definition.options?.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: 8, cursor: "pointer", fontSize: 13 }, children: [
3038
+ definition.type === "radio" && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: options.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: 8, cursor: "pointer", fontSize: 13 }, children: [
2234
3039
  /* @__PURE__ */ jsxRuntime.jsx(
2235
3040
  "input",
2236
3041
  {
@@ -2254,10 +3059,10 @@ function FieldRenderer({ field, value, error, onChange }) {
2254
3059
  ),
2255
3060
  definition.placeholder ?? definition.label
2256
3061
  ] }),
2257
- error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.errorMsg, children: error })
3062
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.errorMsg, children: error })
2258
3063
  ] });
2259
3064
  }
2260
- var styles6 = {
3065
+ var styles7 = {
2261
3066
  panel: {
2262
3067
  display: "flex",
2263
3068
  flex: 1,
@@ -2426,6 +3231,80 @@ var styles6 = {
2426
3231
  letterSpacing: "0.3px",
2427
3232
  flexShrink: 0
2428
3233
  },
3234
+ // Mock var panel
3235
+ mockPanel: {
3236
+ borderTop: "1px solid var(--wp-border)",
3237
+ padding: "12px",
3238
+ display: "flex",
3239
+ flexDirection: "column",
3240
+ gap: 8
3241
+ },
3242
+ mockPanelTitle: {
3243
+ display: "flex",
3244
+ alignItems: "center",
3245
+ justifyContent: "space-between",
3246
+ fontSize: 11,
3247
+ fontWeight: 700,
3248
+ color: "var(--wp-text-muted)",
3249
+ textTransform: "uppercase",
3250
+ letterSpacing: "0.5px",
3251
+ marginBottom: 4
3252
+ },
3253
+ mockPanelHint: {
3254
+ fontSize: 9,
3255
+ fontWeight: 500,
3256
+ color: "var(--wp-warning)",
3257
+ textTransform: "none",
3258
+ background: "var(--wp-warning-bg)",
3259
+ padding: "1px 5px",
3260
+ borderRadius: 4
3261
+ },
3262
+ mockVarRow: {
3263
+ display: "flex",
3264
+ flexDirection: "column",
3265
+ gap: 3
3266
+ },
3267
+ mockVarLabel: {
3268
+ display: "flex",
3269
+ alignItems: "center",
3270
+ gap: 4
3271
+ },
3272
+ mockVarId: {
3273
+ fontSize: 10,
3274
+ fontFamily: "monospace",
3275
+ fontWeight: 600,
3276
+ color: "var(--wp-text-secondary)"
3277
+ },
3278
+ mockBlockingBadge: {
3279
+ fontSize: 9,
3280
+ fontWeight: 700,
3281
+ width: 13,
3282
+ height: 13,
3283
+ borderRadius: "50%",
3284
+ background: "var(--wp-danger-bg-strong)",
3285
+ color: "var(--wp-danger)",
3286
+ display: "flex",
3287
+ alignItems: "center",
3288
+ justifyContent: "center",
3289
+ flexShrink: 0
3290
+ },
3291
+ mockInput: {
3292
+ fontSize: 11,
3293
+ padding: "4px 6px",
3294
+ border: "1px solid var(--wp-border-muted)",
3295
+ borderRadius: "var(--wp-radius)",
3296
+ background: "var(--wp-canvas)",
3297
+ color: "var(--wp-text)",
3298
+ outline: "none",
3299
+ width: "100%",
3300
+ boxSizing: "border-box"
3301
+ },
3302
+ mockCheckRow: {
3303
+ display: "flex",
3304
+ alignItems: "center",
3305
+ gap: 6,
3306
+ cursor: "pointer"
3307
+ },
2429
3308
  // Done screen
2430
3309
  doneScreen: {
2431
3310
  display: "flex",
@@ -2462,83 +3341,100 @@ var styles6 = {
2462
3341
  };
2463
3342
  function StepEditor() {
2464
3343
  const { schema, selectedStepId, updateStep, setStepCondition } = useBuilderStore();
3344
+ const readOnly = useBuilderReadOnly();
2465
3345
  const [conditionModalOpen, setConditionModalOpen] = react.useState(false);
2466
3346
  const step = schema.steps.find((s) => s.id === selectedStepId);
2467
3347
  if (!step) {
2468
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.empty, children: "Select a step to configure its properties." });
3348
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles8.empty, children: "Select a step to configure its properties." });
2469
3349
  }
2470
3350
  const hasCondition = !!step.visibleWhen;
2471
3351
  const ruleCount = step.visibleWhen?.rules.length ?? 0;
2472
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.container, children: [
2473
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.header, children: [
2474
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.headerTitle, children: "Step Config" }),
2475
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.stepId, children: [
3352
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.container, children: [
3353
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.header, children: [
3354
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles8.headerTitle, children: "Step Config" }),
3355
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.stepId, children: [
2476
3356
  "id: ",
2477
3357
  step.id
2478
3358
  ] })
2479
3359
  ] }),
2480
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.body, children: [
2481
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.group, children: [
2482
- /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles7.label, children: "Title" }),
3360
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.body, children: [
3361
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.group, children: [
3362
+ /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles8.label, children: "Title" }),
2483
3363
  /* @__PURE__ */ jsxRuntime.jsx(
2484
3364
  "input",
2485
3365
  {
2486
- style: styles7.input,
3366
+ style: styles8.input,
2487
3367
  value: step.title,
2488
3368
  placeholder: "Step title",
2489
- onChange: (e) => updateStep(step.id, { title: e.target.value })
3369
+ readOnly,
3370
+ onChange: readOnly ? void 0 : (e) => updateStep(step.id, { title: e.target.value })
2490
3371
  }
2491
3372
  )
2492
3373
  ] }),
2493
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.group, children: [
2494
- /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles7.label, children: "URL" }),
3374
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.group, children: [
3375
+ /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles8.label, children: "URL" }),
2495
3376
  /* @__PURE__ */ jsxRuntime.jsx(
2496
3377
  "input",
2497
3378
  {
2498
- style: styles7.input,
3379
+ style: styles8.input,
2499
3380
  value: step.url,
2500
3381
  placeholder: "/onboarding/step-name",
2501
- onChange: (e) => updateStep(step.id, { url: e.target.value })
3382
+ readOnly,
3383
+ onChange: readOnly ? void 0 : (e) => updateStep(step.id, { url: e.target.value })
2502
3384
  }
2503
3385
  ),
2504
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.hint, children: [
3386
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.hint, children: [
2505
3387
  "Supports ",
2506
3388
  "{{PARAM}}",
2507
3389
  " placeholders"
2508
3390
  ] })
2509
3391
  ] }),
2510
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.checkRow, children: [
3392
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.checkRow, children: [
2511
3393
  /* @__PURE__ */ jsxRuntime.jsx(
2512
3394
  "input",
2513
3395
  {
2514
3396
  type: "checkbox",
2515
3397
  id: `resume-${step.id}`,
2516
3398
  checked: !!step.enableResumeFromHere,
2517
- onChange: (e) => updateStep(step.id, { enableResumeFromHere: e.target.checked || void 0 })
3399
+ disabled: readOnly,
3400
+ onChange: readOnly ? void 0 : (e) => updateStep(step.id, { enableResumeFromHere: e.target.checked || void 0 })
2518
3401
  }
2519
3402
  ),
2520
- /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: `resume-${step.id}`, style: styles7.checkLabel, children: "Resume from this step" })
3403
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: `resume-${step.id}`, style: styles8.checkLabel, children: "Resume from this step" })
2521
3404
  ] }),
2522
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.divider }),
2523
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.conditionRow, children: [
2524
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.conditionInfo, children: [
2525
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.label, children: "Visibility condition" }),
2526
- hasCondition ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.conditionSummary, children: [
2527
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles7.conditionBadge, children: [
3405
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.checkRow, children: [
3406
+ /* @__PURE__ */ jsxRuntime.jsx(
3407
+ "input",
3408
+ {
3409
+ type: "checkbox",
3410
+ id: `skippable-${step.id}`,
3411
+ checked: !!step.skippable,
3412
+ disabled: readOnly,
3413
+ onChange: readOnly ? void 0 : (e) => updateStep(step.id, { skippable: e.target.checked || void 0 })
3414
+ }
3415
+ ),
3416
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: `skippable-${step.id}`, style: styles8.checkLabel, children: "Skippable (user can bypass this step)" })
3417
+ ] }),
3418
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles8.divider }),
3419
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.conditionRow, children: [
3420
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.conditionInfo, children: [
3421
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles8.label, children: "Visibility condition" }),
3422
+ hasCondition ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.conditionSummary, children: [
3423
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles8.conditionBadge, children: [
2528
3424
  ruleCount,
2529
3425
  " rule",
2530
3426
  ruleCount !== 1 ? "s" : "",
2531
3427
  " \xB7 ",
2532
3428
  step.visibleWhen.combinator.toUpperCase()
2533
3429
  ] }),
2534
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles7.conditionDesc, children: "Step is conditional" })
2535
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.conditionNone, children: "Always visible" })
3430
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles8.conditionDesc, children: "Step is conditional" })
3431
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles8.conditionNone, children: "Always visible" })
2536
3432
  ] }),
2537
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.conditionActions, children: [
3433
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.conditionActions, children: [
2538
3434
  /* @__PURE__ */ jsxRuntime.jsx(
2539
3435
  "button",
2540
3436
  {
2541
- style: styles7.editConditionBtn,
3437
+ style: styles8.editConditionBtn,
2542
3438
  onClick: () => setConditionModalOpen(true),
2543
3439
  children: hasCondition ? "Edit" : "Add condition"
2544
3440
  }
@@ -2546,7 +3442,7 @@ function StepEditor() {
2546
3442
  hasCondition && /* @__PURE__ */ jsxRuntime.jsx(
2547
3443
  "button",
2548
3444
  {
2549
- style: styles7.clearConditionBtn,
3445
+ style: styles8.clearConditionBtn,
2550
3446
  onClick: () => setStepCondition(step.id, void 0),
2551
3447
  children: "Clear"
2552
3448
  }
@@ -2561,7 +3457,7 @@ function StepEditor() {
2561
3457
  onClose: () => setConditionModalOpen(false),
2562
3458
  width: 620,
2563
3459
  children: [
2564
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: styles7.modalHint, children: "Define when this step is visible. Leave empty to always show it." }),
3460
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: styles8.modalHint, children: "Define when this step is visible. Leave empty to always show it." }),
2565
3461
  /* @__PURE__ */ jsxRuntime.jsx(
2566
3462
  ConditionBuilder,
2567
3463
  {
@@ -2569,10 +3465,10 @@ function StepEditor() {
2569
3465
  onChange: (c) => setStepCondition(step.id, c)
2570
3466
  }
2571
3467
  ),
2572
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.modalFooter, children: /* @__PURE__ */ jsxRuntime.jsx(
3468
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles8.modalFooter, children: /* @__PURE__ */ jsxRuntime.jsx(
2573
3469
  "button",
2574
3470
  {
2575
- style: styles7.modalCloseBtn,
3471
+ style: styles8.modalCloseBtn,
2576
3472
  onClick: () => setConditionModalOpen(false),
2577
3473
  children: "Done"
2578
3474
  }
@@ -2582,7 +3478,7 @@ function StepEditor() {
2582
3478
  )
2583
3479
  ] });
2584
3480
  }
2585
- var styles7 = {
3481
+ var styles8 = {
2586
3482
  container: { display: "flex", flexDirection: "column", height: "100%", overflow: "hidden" },
2587
3483
  empty: {
2588
3484
  display: "flex",
@@ -2671,128 +3567,164 @@ var styles7 = {
2671
3567
  }
2672
3568
  };
2673
3569
  function StepList2() {
2674
- const { schema, selectedStepId, addStep, removeStep, selectStep, reorderSteps } = useBuilderStore();
3570
+ const { schema, selectedStepId, addStep, removeStep, duplicateStep, selectStep, reorderSteps } = useBuilderStore();
3571
+ const readOnly = useBuilderReadOnly();
2675
3572
  const [moveError, setMoveError] = react.useState(null);
2676
3573
  const steps = schema.steps;
2677
3574
  const deps = computeStepDependencies(schema);
2678
- const tryMove = (fromIndex, toIndex) => {
2679
- const check = isMoveValid(steps, deps, fromIndex, toIndex);
2680
- if (!check.valid) {
2681
- setMoveError(check.reason ?? "Invalid move");
2682
- setTimeout(() => setMoveError(null), 3e3);
2683
- return;
2684
- }
2685
- setMoveError(null);
2686
- reorderSteps(fromIndex, toIndex);
3575
+ const tryMove = react.useCallback(
3576
+ (fromIndex, toIndex) => {
3577
+ const check = isMoveValid(steps, deps, fromIndex, toIndex);
3578
+ if (!check.valid) {
3579
+ setMoveError(check.reason ?? "Invalid move");
3580
+ setTimeout(() => setMoveError(null), 3e3);
3581
+ return;
3582
+ }
3583
+ setMoveError(null);
3584
+ reorderSteps(fromIndex, toIndex);
3585
+ },
3586
+ [steps, deps, reorderSteps]
3587
+ );
3588
+ const stepIds = steps.map((s) => s.id);
3589
+ const renderOverlay = (id) => {
3590
+ const step = steps.find((s) => s.id === id);
3591
+ if (!step) return null;
3592
+ const index = steps.indexOf(step);
3593
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...styles9.card, ...styles9.cardDragOverlay }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles9.cardMain, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.cardLeft, children: [
3594
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles9.cardIndex, children: index + 1 }),
3595
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles9.cardTitle, children: step.title })
3596
+ ] }) }) });
2687
3597
  };
2688
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.container, children: [
2689
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.header, children: [
2690
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles8.title, children: [
3598
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.container, children: [
3599
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.header, children: [
3600
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles9.title, children: [
2691
3601
  "Steps (",
2692
3602
  steps.length,
2693
3603
  ")"
2694
3604
  ] }),
2695
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles8.addBtn, onClick: () => addStep(), children: "+ Add step" })
3605
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles9.addBtn, onClick: () => addStep(), children: "+ Add step" })
2696
3606
  ] }),
2697
- moveError && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.errorBanner, children: [
2698
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles8.errorIcon, children: "\u26A0" }),
3607
+ moveError && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.errorBanner, children: [
3608
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.errorIcon, children: "\u26A0" }),
2699
3609
  moveError
2700
3610
  ] }),
2701
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.list, children: [
2702
- steps.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles8.empty, children: 'No steps yet. Click "Add step" to start.' }),
2703
- steps.map((step, index) => {
2704
- const isSelected = step.id === selectedStepId;
2705
- const hasCondition = !!step.visibleWhen;
2706
- const depLabels = getStepDependencyLabels(step.id, deps, schema);
2707
- const canMoveUp = index > 0 && isMoveValid(steps, deps, index, index - 1).valid;
2708
- const canMoveDown = index < steps.length - 1 && isMoveValid(steps, deps, index, index + 1).valid;
2709
- const dependents = steps.filter(
2710
- (s) => (deps.get(s.id) ?? /* @__PURE__ */ new Set()).has(step.id)
2711
- );
2712
- return /* @__PURE__ */ jsxRuntime.jsxs(
2713
- "div",
2714
- {
2715
- style: {
2716
- ...styles8.card,
2717
- ...isSelected ? styles8.cardSelected : {}
2718
- },
2719
- onClick: () => selectStep(step.id),
2720
- children: [
2721
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.cardMain, children: [
2722
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.cardLeft, children: [
2723
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles8.cardIndex, children: index + 1 }),
2724
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { minWidth: 0 }, children: [
2725
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles8.cardTitle, children: step.title }),
2726
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.cardMeta, children: [
2727
- step.fields.length,
2728
- " field",
2729
- step.fields.length !== 1 ? "s" : "",
2730
- hasCondition && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles8.conditionBadge, children: "conditional" })
3611
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.list, children: [
3612
+ steps.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles9.empty, children: 'No steps yet. Click "Add step" to start.' }),
3613
+ /* @__PURE__ */ jsxRuntime.jsx(
3614
+ SortableList,
3615
+ {
3616
+ items: stepIds,
3617
+ disabled: readOnly,
3618
+ onReorder: tryMove,
3619
+ renderOverlay,
3620
+ renderItem: (id, index) => {
3621
+ const step = steps[index];
3622
+ const isSelected = step.id === selectedStepId;
3623
+ const hasCondition = !!step.visibleWhen;
3624
+ const depLabels = getStepDependencyLabels(step.id, deps, schema);
3625
+ const canMoveUp = index > 0 && isMoveValid(steps, deps, index, index - 1).valid;
3626
+ const canMoveDown = index < steps.length - 1 && isMoveValid(steps, deps, index, index + 1).valid;
3627
+ const dependents = steps.filter(
3628
+ (s) => (deps.get(s.id) ?? /* @__PURE__ */ new Set()).has(step.id)
3629
+ );
3630
+ return /* @__PURE__ */ jsxRuntime.jsx(SortableItem, { id: step.id, disabled: readOnly, children: ({ handleProps }) => /* @__PURE__ */ jsxRuntime.jsxs(
3631
+ "div",
3632
+ {
3633
+ style: {
3634
+ ...styles9.card,
3635
+ ...isSelected ? styles9.cardSelected : {}
3636
+ },
3637
+ onClick: () => selectStep(step.id),
3638
+ children: [
3639
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.cardMain, children: [
3640
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.cardLeft, children: [
3641
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx(DragHandle, { handleProps }),
3642
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles9.cardIndex, children: index + 1 }),
3643
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { minWidth: 0 }, children: [
3644
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles9.cardTitle, children: step.title }),
3645
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.cardMeta, children: [
3646
+ step.fields.length,
3647
+ " field",
3648
+ step.fields.length !== 1 ? "s" : "",
3649
+ hasCondition && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.conditionBadge, children: "conditional" })
3650
+ ] })
3651
+ ] })
3652
+ ] }),
3653
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.cardActions, children: [
3654
+ index > 0 && /* @__PURE__ */ jsxRuntime.jsx(
3655
+ "button",
3656
+ {
3657
+ style: {
3658
+ ...styles9.iconBtn,
3659
+ ...canMoveUp ? {} : styles9.iconBtnBlocked
3660
+ },
3661
+ title: canMoveUp ? "Move up" : `Can't move up \u2014 dependency order required`,
3662
+ onClick: (e) => {
3663
+ e.stopPropagation();
3664
+ tryMove(index, index - 1);
3665
+ },
3666
+ children: "\u2191"
3667
+ }
3668
+ ),
3669
+ index < steps.length - 1 && /* @__PURE__ */ jsxRuntime.jsx(
3670
+ "button",
3671
+ {
3672
+ style: {
3673
+ ...styles9.iconBtn,
3674
+ ...canMoveDown ? {} : styles9.iconBtnBlocked
3675
+ },
3676
+ title: canMoveDown ? "Move down" : `Can't move down \u2014 dependency order required`,
3677
+ onClick: (e) => {
3678
+ e.stopPropagation();
3679
+ tryMove(index, index + 1);
3680
+ },
3681
+ children: "\u2193"
3682
+ }
3683
+ ),
3684
+ /* @__PURE__ */ jsxRuntime.jsx(
3685
+ "button",
3686
+ {
3687
+ style: styles9.iconBtn,
3688
+ title: "Duplicate step",
3689
+ onClick: (e) => {
3690
+ e.stopPropagation();
3691
+ duplicateStep(step.id);
3692
+ },
3693
+ children: "\u29C9"
3694
+ }
3695
+ ),
3696
+ /* @__PURE__ */ jsxRuntime.jsx(
3697
+ "button",
3698
+ {
3699
+ style: { ...styles9.iconBtn, ...styles9.deleteBtn },
3700
+ title: "Remove step",
3701
+ onClick: (e) => {
3702
+ e.stopPropagation();
3703
+ removeStep(step.id);
3704
+ },
3705
+ children: "\u2715"
3706
+ }
3707
+ )
2731
3708
  ] })
3709
+ ] }),
3710
+ depLabels.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.depRow, children: [
3711
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.depLabel, children: "needs:" }),
3712
+ depLabels.map((label) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.depBadge, children: label }, label))
3713
+ ] }),
3714
+ dependents.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.depRow, children: [
3715
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.depLabelUsed, children: "used by:" }),
3716
+ dependents.map((s) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.depBadgeUsed, children: s.title }, s.id))
2732
3717
  ] })
2733
- ] }),
2734
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.cardActions, children: [
2735
- index > 0 && /* @__PURE__ */ jsxRuntime.jsx(
2736
- "button",
2737
- {
2738
- style: {
2739
- ...styles8.iconBtn,
2740
- ...canMoveUp ? {} : styles8.iconBtnBlocked
2741
- },
2742
- title: canMoveUp ? "Move up" : `Can't move up \u2014 dependency order required`,
2743
- onClick: (e) => {
2744
- e.stopPropagation();
2745
- tryMove(index, index - 1);
2746
- },
2747
- children: "\u2191"
2748
- }
2749
- ),
2750
- index < steps.length - 1 && /* @__PURE__ */ jsxRuntime.jsx(
2751
- "button",
2752
- {
2753
- style: {
2754
- ...styles8.iconBtn,
2755
- ...canMoveDown ? {} : styles8.iconBtnBlocked
2756
- },
2757
- title: canMoveDown ? "Move down" : `Can't move down \u2014 dependency order required`,
2758
- onClick: (e) => {
2759
- e.stopPropagation();
2760
- tryMove(index, index + 1);
2761
- },
2762
- children: "\u2193"
2763
- }
2764
- ),
2765
- /* @__PURE__ */ jsxRuntime.jsx(
2766
- "button",
2767
- {
2768
- style: { ...styles8.iconBtn, ...styles8.deleteBtn },
2769
- title: "Remove step",
2770
- onClick: (e) => {
2771
- e.stopPropagation();
2772
- removeStep(step.id);
2773
- },
2774
- children: "\u2715"
2775
- }
2776
- )
2777
- ] })
2778
- ] }),
2779
- depLabels.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.depRow, children: [
2780
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles8.depLabel, children: "needs:" }),
2781
- depLabels.map((label) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles8.depBadge, children: label }, label))
2782
- ] }),
2783
- dependents.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.depRow, children: [
2784
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles8.depLabelUsed, children: "used by:" }),
2785
- dependents.map((s) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles8.depBadgeUsed, children: s.title }, s.id))
2786
- ] })
2787
- ]
2788
- },
2789
- step.id
2790
- );
2791
- })
3718
+ ]
3719
+ }
3720
+ ) }, step.id);
3721
+ }
3722
+ }
3723
+ )
2792
3724
  ] })
2793
3725
  ] });
2794
3726
  }
2795
- var styles8 = {
3727
+ var styles9 = {
2796
3728
  container: { display: "flex", flexDirection: "column", height: "100%" },
2797
3729
  header: {
2798
3730
  display: "flex",
@@ -2840,6 +3772,12 @@ var styles8 = {
2840
3772
  gap: 6
2841
3773
  },
2842
3774
  cardSelected: { background: "var(--wp-primary-muted)", border: "1px solid var(--wp-primary-border)" },
3775
+ cardDragOverlay: {
3776
+ boxShadow: "0 4px 16px rgba(0,0,0,0.25)",
3777
+ border: "1px solid var(--wp-primary-border)",
3778
+ background: "var(--wp-surface)",
3779
+ opacity: 0.95
3780
+ },
2843
3781
  cardMain: { display: "flex", alignItems: "center", justifyContent: "space-between" },
2844
3782
  cardLeft: { display: "flex", alignItems: "center", gap: 10, minWidth: 0, flex: 1 },
2845
3783
  cardIndex: {
@@ -2912,7 +3850,7 @@ var styles8 = {
2912
3850
  borderRadius: 4
2913
3851
  }
2914
3852
  };
2915
- function Toolbar({ onSave, previewMode, onTest, isMobile }) {
3853
+ function Toolbar({ onSave, previewMode, onTest, isMobile, readOnly }) {
2916
3854
  const { schema, isDirty, resetSchema } = useBuilderStore();
2917
3855
  const handleExport = () => {
2918
3856
  const json = JSON.stringify(schema, null, 2);
@@ -2952,31 +3890,31 @@ ${result.errors.map((e2) => `\u2022 ${e2}`).join("\n")}`);
2952
3890
  input.click();
2953
3891
  };
2954
3892
  if (previewMode) {
2955
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.toolbar, children: [
2956
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.left, children: [
2957
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.logo, children: "\u25C8 waypoint" }),
2958
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.separator, children: "/" }),
3893
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles10.toolbar, children: [
3894
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles10.left, children: [
3895
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles10.logo, children: "\u25C8 waypoint" }),
3896
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles10.separator, children: "/" }),
2959
3897
  /* @__PURE__ */ jsxRuntime.jsx(
2960
3898
  "button",
2961
3899
  {
2962
- style: { ...styles9.btn, ...styles9.editBtn },
3900
+ style: { ...styles10.btn, ...styles10.editBtn },
2963
3901
  onClick: onTest,
2964
3902
  children: "\u2190 \xC9diter"
2965
3903
  }
2966
3904
  )
2967
3905
  ] }),
2968
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles9.right, children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "var(--wp-text-muted)", fontStyle: "italic" }, children: "Mode aper\xE7u" }) })
3906
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles10.right, children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "var(--wp-text-muted)", fontStyle: "italic" }, children: "Mode aper\xE7u" }) })
2969
3907
  ] });
2970
3908
  }
2971
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.toolbar, children: [
2972
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.left, children: [
2973
- !isMobile && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.logo, children: "\u25C8 waypoint" }),
2974
- !isMobile && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.separator, children: "/" }),
3909
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles10.toolbar, children: [
3910
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles10.left, children: [
3911
+ !isMobile && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles10.logo, children: "\u25C8 waypoint" }),
3912
+ !isMobile && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles10.separator, children: "/" }),
2975
3913
  /* @__PURE__ */ jsxRuntime.jsx(
2976
3914
  "input",
2977
3915
  {
2978
3916
  style: {
2979
- ...styles9.journeyName,
3917
+ ...styles10.journeyName,
2980
3918
  ...isMobile ? { maxWidth: 120, fontSize: 12 } : {}
2981
3919
  },
2982
3920
  value: schema.name,
@@ -2987,25 +3925,26 @@ ${result.errors.map((e2) => `\u2022 ${e2}`).join("\n")}`);
2987
3925
  }))
2988
3926
  }
2989
3927
  ),
2990
- isDirty && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.dirtyDot, title: "Unsaved changes" })
3928
+ isDirty && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles10.dirtyDot, title: "Unsaved changes" })
2991
3929
  ] }),
2992
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.right, children: [
2993
- onTest && /* @__PURE__ */ jsxRuntime.jsx("button", { style: { ...styles9.btn, ...styles9.testBtn }, onClick: onTest, title: "Tester", children: isMobile ? "\u25B6" : "\u25B6 Tester" }),
2994
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles9.btn, onClick: handleImport, title: "Import", children: isMobile ? "\u2193" : "Import" }),
2995
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles9.btn, onClick: handleExport, title: "Export JSON", children: isMobile ? "\u2191" : "Export JSON" }),
2996
- onSave && /* @__PURE__ */ jsxRuntime.jsx(
3930
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles10.right, children: [
3931
+ readOnly && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles10.readOnlyBadge, children: "View only" }),
3932
+ !readOnly && onTest && /* @__PURE__ */ jsxRuntime.jsx("button", { style: { ...styles10.btn, ...styles10.testBtn }, onClick: onTest, title: "Tester", children: isMobile ? "\u25B6" : "\u25B6 Tester" }),
3933
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles10.btn, onClick: handleImport, title: "Import", children: isMobile ? "\u2193" : "Import" }),
3934
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles10.btn, onClick: handleExport, title: "Export JSON", children: isMobile ? "\u2191" : "Export JSON" }),
3935
+ !readOnly && onSave && /* @__PURE__ */ jsxRuntime.jsx(
2997
3936
  "button",
2998
3937
  {
2999
- style: { ...styles9.btn, ...styles9.saveBtn },
3938
+ style: { ...styles10.btn, ...styles10.saveBtn },
3000
3939
  onClick: () => onSave(schema),
3001
3940
  title: "Save",
3002
3941
  children: isMobile ? "\u2713" : "Save"
3003
3942
  }
3004
3943
  ),
3005
- /* @__PURE__ */ jsxRuntime.jsx(
3944
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
3006
3945
  "button",
3007
3946
  {
3008
- style: { ...styles9.btn, color: "var(--wp-danger)" },
3947
+ style: { ...styles10.btn, color: "var(--wp-danger)" },
3009
3948
  title: "Reset",
3010
3949
  onClick: () => {
3011
3950
  if (confirm("Reset the journey? All changes will be lost.")) resetSchema();
@@ -3016,7 +3955,7 @@ ${result.errors.map((e2) => `\u2022 ${e2}`).join("\n")}`);
3016
3955
  ] })
3017
3956
  ] });
3018
3957
  }
3019
- var styles9 = {
3958
+ var styles10 = {
3020
3959
  toolbar: {
3021
3960
  display: "flex",
3022
3961
  alignItems: "center",
@@ -3058,6 +3997,15 @@ var styles9 = {
3058
3997
  fontWeight: 500
3059
3998
  },
3060
3999
  saveBtn: { background: "var(--wp-primary)", color: "var(--wp-canvas)", border: "1px solid var(--wp-primary)" },
4000
+ readOnlyBadge: {
4001
+ fontSize: 11,
4002
+ fontWeight: 600,
4003
+ color: "var(--wp-text-subtle)",
4004
+ background: "var(--wp-surface)",
4005
+ border: "1px solid var(--wp-border)",
4006
+ borderRadius: "var(--wp-radius)",
4007
+ padding: "3px 8px"
4008
+ },
3061
4009
  testBtn: { background: "var(--wp-success, #22c55e)", color: "#fff", border: "1px solid var(--wp-success, #22c55e)", fontWeight: 600 },
3062
4010
  editBtn: { background: "transparent", color: "var(--wp-toolbar-text)", border: "none", fontWeight: 600, cursor: "pointer", fontSize: 13, padding: "5px 0" }
3063
4011
  };
@@ -3067,7 +4015,10 @@ function WaypointBuilder({
3067
4015
  onSave,
3068
4016
  theme,
3069
4017
  className,
3070
- style
4018
+ style,
4019
+ readOnly = false,
4020
+ appCustomTypes = [],
4021
+ externalEnums = []
3071
4022
  }) {
3072
4023
  const { loadSchema, schema, selectedStepId, selectedFieldId } = useBuilderStore();
3073
4024
  const [previewMode, setPreviewMode] = react.useState(false);
@@ -3100,14 +4051,15 @@ function WaypointBuilder({
3100
4051
  setPreviewMode(true);
3101
4052
  }
3102
4053
  const themeVars = buildThemeVars(theme);
3103
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: { ...rootStyle, ...themeVars, ...style }, children: [
4054
+ return /* @__PURE__ */ jsxRuntime.jsx(BuilderReadOnlyContext.Provider, { value: readOnly, children: /* @__PURE__ */ jsxRuntime.jsx(BuilderCustomTypesContext.Provider, { value: appCustomTypes, children: /* @__PURE__ */ jsxRuntime.jsx(BuilderExternalEnumsContext.Provider, { value: externalEnums, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: { ...rootStyle, ...themeVars, ...style }, children: [
3104
4055
  /* @__PURE__ */ jsxRuntime.jsx(
3105
4056
  Toolbar,
3106
4057
  {
3107
4058
  isMobile,
3108
- onSave: !previewMode && onSave ? () => onSave(schema) : void 0,
4059
+ readOnly,
4060
+ onSave: !previewMode && !readOnly && onSave ? () => onSave(schema) : void 0,
3109
4061
  previewMode,
3110
- onTest: previewMode ? () => setPreviewMode(false) : handleTest
4062
+ onTest: !readOnly ? previewMode ? () => setPreviewMode(false) : handleTest : void 0
3111
4063
  }
3112
4064
  ),
3113
4065
  previewMode ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -3115,6 +4067,7 @@ function WaypointBuilder({
3115
4067
  {
3116
4068
  store: previewStoreRef.current,
3117
4069
  schema,
4070
+ externalEnums,
3118
4071
  onEdit: () => setPreviewMode(false)
3119
4072
  }
3120
4073
  ) : isMobile ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -3152,7 +4105,7 @@ function WaypointBuilder({
3152
4105
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, overflow: "hidden" }, children: /* @__PURE__ */ jsxRuntime.jsx(FieldEditor, {}) })
3153
4106
  ] })
3154
4107
  ] })
3155
- ] });
4108
+ ] }) }) }) });
3156
4109
  }
3157
4110
  var rootStyle = {
3158
4111
  display: "flex",