@duke333/builder-ui 0.1.0 → 0.1.3

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.js CHANGED
@@ -1,12 +1,3 @@
1
- // src/admin/BuilderEditorPage.tsx
2
- import { useEffect } from "react";
3
- import {
4
- FormProvider,
5
- useFieldArray,
6
- useForm,
7
- useWatch
8
- } from "react-hook-form";
9
-
10
1
  // src/admin/editorState.ts
11
2
  var BUILDER_EDITOR_NONE_VALUE = "__none";
12
3
  function builderEditorOptionsToText(options) {
@@ -175,15 +166,15 @@ function builderListEditorValuesFromEntity(list, emptyValues) {
175
166
  version: list.version
176
167
  };
177
168
  }
178
- function builderViewEditorValuesFromEntity(view, emptyValues, defaultPermission) {
169
+ function builderViewEditorValuesFromEntity(view, emptyValues, defaultPermission = "") {
179
170
  if (!view) return emptyValues;
180
171
  return {
181
172
  title: view.title,
182
173
  description: view.description ?? "",
183
174
  formId: view.formId ?? BUILDER_EDITOR_NONE_VALUE,
184
175
  listId: view.listId ?? BUILDER_EDITOR_NONE_VALUE,
185
- permissionRead: view.permissionRead ?? defaultPermission,
186
- permissionWrite: view.permissionWrite ?? defaultPermission,
176
+ permissionRead: view.permissionRead || defaultPermission,
177
+ permissionWrite: view.permissionWrite || defaultPermission,
187
178
  triggersText: JSON.stringify(view.triggers ?? [], null, 2),
188
179
  version: view.version
189
180
  };
@@ -303,22 +294,1001 @@ function builderViewEditorInputFromValues(values, parseTriggers) {
303
294
  description: values.description || null,
304
295
  formId: values.formId === BUILDER_EDITOR_NONE_VALUE ? null : values.formId,
305
296
  listId: values.listId === BUILDER_EDITOR_NONE_VALUE ? null : values.listId,
306
- permissionRead: values.permissionRead,
307
- permissionWrite: values.permissionWrite,
297
+ permissionRead: values.permissionRead || null,
298
+ permissionWrite: values.permissionWrite || null,
308
299
  triggers: values.triggersText?.trim() ? parseTriggers(values.triggersText) : [],
309
300
  version: values.version
310
301
  };
311
302
  }
312
303
 
304
+ // src/admin/BuilderDefaultEditorBodies.tsx
305
+ import { jsx, jsxs } from "react/jsx-runtime";
306
+ var styles = {
307
+ page: {
308
+ display: "grid",
309
+ gap: "1rem"
310
+ },
311
+ header: {
312
+ display: "flex",
313
+ justifyContent: "space-between",
314
+ gap: "1rem",
315
+ alignItems: "flex-start",
316
+ flexWrap: "wrap"
317
+ },
318
+ title: {
319
+ margin: 0,
320
+ fontSize: "1.5rem",
321
+ fontWeight: 650
322
+ },
323
+ description: {
324
+ margin: "0.25rem 0 0",
325
+ color: "var(--muted-foreground, #64748b)"
326
+ },
327
+ actions: {
328
+ display: "flex",
329
+ gap: "0.5rem",
330
+ flexWrap: "wrap"
331
+ },
332
+ section: {
333
+ border: "1px solid var(--border, #d4d4d8)",
334
+ borderRadius: "0.5rem",
335
+ background: "var(--background, #fff)",
336
+ overflow: "hidden"
337
+ },
338
+ sectionHeader: {
339
+ display: "flex",
340
+ justifyContent: "space-between",
341
+ gap: "1rem",
342
+ alignItems: "center",
343
+ padding: "0.75rem",
344
+ borderBottom: "1px solid var(--border, #d4d4d8)",
345
+ flexWrap: "wrap"
346
+ },
347
+ sectionTitle: {
348
+ margin: 0,
349
+ fontSize: "1rem",
350
+ fontWeight: 650
351
+ },
352
+ grid: {
353
+ display: "grid",
354
+ gap: "0.75rem",
355
+ padding: "0.75rem"
356
+ },
357
+ twoColumn: {
358
+ display: "grid",
359
+ gridTemplateColumns: "repeat(auto-fit, minmax(14rem, 1fr))",
360
+ gap: "0.75rem"
361
+ },
362
+ row: {
363
+ display: "grid",
364
+ gridTemplateColumns: "repeat(auto-fit, minmax(10rem, 1fr))",
365
+ gap: "0.5rem",
366
+ padding: "0.75rem",
367
+ borderBottom: "1px solid var(--border, #d4d4d8)"
368
+ },
369
+ field: {
370
+ display: "grid",
371
+ gap: "0.25rem",
372
+ fontSize: "0.875rem"
373
+ },
374
+ label: {
375
+ color: "var(--muted-foreground, #64748b)",
376
+ fontSize: "0.75rem",
377
+ fontWeight: 600
378
+ },
379
+ input: {
380
+ minWidth: 0,
381
+ border: "1px solid var(--border, #d4d4d8)",
382
+ borderRadius: "0.375rem",
383
+ padding: "0.5rem 0.625rem",
384
+ background: "var(--background, #fff)",
385
+ color: "inherit"
386
+ },
387
+ textarea: {
388
+ minWidth: 0,
389
+ minHeight: "5rem",
390
+ border: "1px solid var(--border, #d4d4d8)",
391
+ borderRadius: "0.375rem",
392
+ padding: "0.5rem 0.625rem",
393
+ background: "var(--background, #fff)",
394
+ color: "inherit",
395
+ resize: "vertical"
396
+ },
397
+ checkboxField: {
398
+ display: "flex",
399
+ alignItems: "center",
400
+ gap: "0.5rem",
401
+ fontSize: "0.875rem",
402
+ minHeight: "2.25rem"
403
+ },
404
+ button: {
405
+ border: "1px solid var(--border, #d4d4d8)",
406
+ borderRadius: "0.375rem",
407
+ background: "var(--background, #fff)",
408
+ color: "inherit",
409
+ cursor: "pointer",
410
+ padding: "0.5rem 0.75rem",
411
+ textDecoration: "none",
412
+ fontSize: "0.875rem"
413
+ },
414
+ primaryButton: {
415
+ border: "1px solid var(--primary, #18181b)",
416
+ borderRadius: "0.375rem",
417
+ background: "var(--primary, #18181b)",
418
+ color: "var(--primary-foreground, #fff)",
419
+ cursor: "pointer",
420
+ padding: "0.5rem 0.75rem",
421
+ textDecoration: "none",
422
+ fontSize: "0.875rem"
423
+ }
424
+ };
425
+ function optionItems(options) {
426
+ return options.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value));
427
+ }
428
+ function createBuilderDefaultFormEditorBody(options) {
429
+ return function BuilderDefaultFormEditorBody({
430
+ formId,
431
+ form,
432
+ fieldArray,
433
+ subformArray,
434
+ triggerArray,
435
+ fields,
436
+ sourceType,
437
+ sourceTable,
438
+ tables,
439
+ emptyField,
440
+ emptyTrigger,
441
+ isSaving,
442
+ importFieldsFromTable
443
+ }) {
444
+ return /* @__PURE__ */ jsxs("div", { style: styles.page, children: [
445
+ /* @__PURE__ */ jsxs("div", { style: styles.header, children: [
446
+ /* @__PURE__ */ jsxs("div", { children: [
447
+ /* @__PURE__ */ jsx("h1", { style: styles.title, children: formId ? "Edit Dynamic Form" : "New Dynamic Form" }),
448
+ /* @__PURE__ */ jsx("p", { style: styles.description, children: "Configure fields, source mapping, layout, and triggers." })
449
+ ] }),
450
+ /* @__PURE__ */ jsxs("div", { style: styles.actions, children: [
451
+ /* @__PURE__ */ jsx("a", { href: options.backHref, style: styles.button, children: "Cancel" }),
452
+ /* @__PURE__ */ jsx("button", { type: "submit", disabled: isSaving, style: styles.primaryButton, children: isSaving ? "Saving..." : "Save" })
453
+ ] })
454
+ ] }),
455
+ /* @__PURE__ */ jsxs("section", { style: styles.section, children: [
456
+ /* @__PURE__ */ jsx("div", { style: styles.sectionHeader, children: /* @__PURE__ */ jsx("h2", { style: styles.sectionTitle, children: "Details" }) }),
457
+ /* @__PURE__ */ jsxs("div", { style: { ...styles.grid, ...styles.twoColumn }, children: [
458
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
459
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Title" }),
460
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register("title", { required: true }) })
461
+ ] }),
462
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
463
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Description" }),
464
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register("description") })
465
+ ] }),
466
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
467
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Datasource" }),
468
+ /* @__PURE__ */ jsxs("select", { style: styles.input, ...form.register("sourceType"), children: [
469
+ /* @__PURE__ */ jsx("option", { value: "none", children: "None" }),
470
+ /* @__PURE__ */ jsx("option", { value: "dbTable", children: "DB table" }),
471
+ /* @__PURE__ */ jsx("option", { value: "apiEndpoint", children: "API endpoint" })
472
+ ] })
473
+ ] }),
474
+ sourceType === "dbTable" ? /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
475
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Table" }),
476
+ /* @__PURE__ */ jsxs("select", { style: styles.input, ...form.register("sourceTable"), children: [
477
+ /* @__PURE__ */ jsx("option", { value: BUILDER_EDITOR_NONE_VALUE, children: "Select table" }),
478
+ tables.map((table) => /* @__PURE__ */ jsx("option", { value: table.name, children: table.label ?? table.name }, table.name))
479
+ ] })
480
+ ] }) : null,
481
+ sourceType === "apiEndpoint" ? /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
482
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "API base path" }),
483
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register("apiBasePath") })
484
+ ] }) : null,
485
+ sourceType === "dbTable" ? /* @__PURE__ */ jsx("div", { style: { ...styles.field, justifyContent: "end" }, children: /* @__PURE__ */ jsx(
486
+ "button",
487
+ {
488
+ type: "button",
489
+ style: styles.button,
490
+ disabled: sourceTable === BUILDER_EDITOR_NONE_VALUE,
491
+ onClick: importFieldsFromTable,
492
+ children: "Import fields"
493
+ }
494
+ ) }) : null
495
+ ] })
496
+ ] }),
497
+ /* @__PURE__ */ jsxs("section", { style: styles.section, children: [
498
+ /* @__PURE__ */ jsxs("div", { style: styles.sectionHeader, children: [
499
+ /* @__PURE__ */ jsxs("div", { children: [
500
+ /* @__PURE__ */ jsx("h2", { style: styles.sectionTitle, children: "Fields" }),
501
+ /* @__PURE__ */ jsx("p", { style: styles.description, children: "Runtime form inputs." })
502
+ ] }),
503
+ /* @__PURE__ */ jsx(
504
+ "button",
505
+ {
506
+ type: "button",
507
+ style: styles.button,
508
+ onClick: () => {
509
+ const key = `field${fieldArray.fields.length + 1}`;
510
+ fieldArray.append({
511
+ ...emptyField,
512
+ key,
513
+ label: `Field ${fieldArray.fields.length + 1}`,
514
+ type: options.fieldTypeOptions[0]?.value
515
+ });
516
+ },
517
+ children: "Add field"
518
+ }
519
+ )
520
+ ] }),
521
+ fieldArray.fields.map((field, index) => /* @__PURE__ */ jsxs("div", { style: styles.row, children: [
522
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
523
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Key" }),
524
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`fields.${index}.key`) })
525
+ ] }),
526
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
527
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Label" }),
528
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`fields.${index}.label`) })
529
+ ] }),
530
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
531
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Type" }),
532
+ /* @__PURE__ */ jsx(
533
+ "select",
534
+ {
535
+ style: styles.input,
536
+ ...form.register(`fields.${index}.type`),
537
+ children: optionItems(options.fieldTypeOptions)
538
+ }
539
+ )
540
+ ] }),
541
+ /* @__PURE__ */ jsxs("label", { style: styles.checkboxField, children: [
542
+ /* @__PURE__ */ jsx("input", { type: "checkbox", ...form.register(`fields.${index}.required`) }),
543
+ "Required"
544
+ ] }),
545
+ /* @__PURE__ */ jsxs("label", { style: styles.checkboxField, children: [
546
+ /* @__PURE__ */ jsx("input", { type: "checkbox", ...form.register(`fields.${index}.hidden`) }),
547
+ "Hidden"
548
+ ] }),
549
+ /* @__PURE__ */ jsxs("label", { style: styles.checkboxField, children: [
550
+ /* @__PURE__ */ jsx("input", { type: "checkbox", ...form.register(`fields.${index}.disabled`) }),
551
+ "Disabled"
552
+ ] }),
553
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
554
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Placeholder" }),
555
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`fields.${index}.placeholder`) })
556
+ ] }),
557
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
558
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Static options" }),
559
+ /* @__PURE__ */ jsx("textarea", { style: styles.textarea, ...form.register(`fields.${index}.optionsText`) })
560
+ ] }),
561
+ /* @__PURE__ */ jsx("div", { style: styles.actions, children: /* @__PURE__ */ jsx(
562
+ "button",
563
+ {
564
+ type: "button",
565
+ style: styles.button,
566
+ onClick: () => fieldArray.remove(index),
567
+ disabled: fieldArray.fields.length <= 1,
568
+ children: "Remove"
569
+ }
570
+ ) })
571
+ ] }, field.id))
572
+ ] }),
573
+ /* @__PURE__ */ jsxs("section", { style: styles.section, children: [
574
+ /* @__PURE__ */ jsxs("div", { style: styles.sectionHeader, children: [
575
+ /* @__PURE__ */ jsxs("div", { children: [
576
+ /* @__PURE__ */ jsx("h2", { style: styles.sectionTitle, children: "Layout" }),
577
+ /* @__PURE__ */ jsx("p", { style: styles.description, children: "Default layout can be regenerated from current fields." })
578
+ ] }),
579
+ /* @__PURE__ */ jsx(
580
+ "button",
581
+ {
582
+ type: "button",
583
+ style: styles.button,
584
+ onClick: () => form.setValue("layoutGroups", builderFormEditorDefaultLayoutGroups(fields), {
585
+ shouldDirty: true
586
+ }),
587
+ children: "Reset layout"
588
+ }
589
+ )
590
+ ] }),
591
+ form.watch("layoutGroups").map((group, groupIndex) => /* @__PURE__ */ jsxs("div", { style: styles.row, children: [
592
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
593
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Group title" }),
594
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`layoutGroups.${groupIndex}.title`) })
595
+ ] }),
596
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
597
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Columns" }),
598
+ /* @__PURE__ */ jsxs(
599
+ "select",
600
+ {
601
+ style: styles.input,
602
+ value: group.columns,
603
+ onChange: (event) => form.setValue(
604
+ `layoutGroups.${groupIndex}.columns`,
605
+ Number(event.target.value),
606
+ { shouldDirty: true }
607
+ ),
608
+ children: [
609
+ /* @__PURE__ */ jsx("option", { value: 6, children: "6" }),
610
+ /* @__PURE__ */ jsx("option", { value: 8, children: "8" }),
611
+ /* @__PURE__ */ jsx("option", { value: 12, children: "12" })
612
+ ]
613
+ }
614
+ )
615
+ ] }),
616
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
617
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Fields" }),
618
+ /* @__PURE__ */ jsx(
619
+ "input",
620
+ {
621
+ style: styles.input,
622
+ value: group.fields.map((item) => item.key).join(", "),
623
+ readOnly: true
624
+ }
625
+ )
626
+ ] })
627
+ ] }, group.id))
628
+ ] }),
629
+ /* @__PURE__ */ jsxs("section", { style: styles.section, children: [
630
+ /* @__PURE__ */ jsxs("div", { style: styles.sectionHeader, children: [
631
+ /* @__PURE__ */ jsxs("div", { children: [
632
+ /* @__PURE__ */ jsx("h2", { style: styles.sectionTitle, children: "Subforms" }),
633
+ /* @__PURE__ */ jsx("p", { style: styles.description, children: "Optional nested runtime view bindings." })
634
+ ] }),
635
+ /* @__PURE__ */ jsx(
636
+ "button",
637
+ {
638
+ type: "button",
639
+ style: styles.button,
640
+ onClick: () => subformArray.append({
641
+ key: `subform${subformArray.fields.length + 1}`,
642
+ label: `Subform ${subformArray.fields.length + 1}`,
643
+ viewId: BUILDER_EDITOR_NONE_VALUE,
644
+ parentField: "id",
645
+ childField: "parentId",
646
+ buttonLabel: "",
647
+ icon: ""
648
+ }),
649
+ children: "Add subform"
650
+ }
651
+ )
652
+ ] }),
653
+ subformArray.fields.map((subform, index) => /* @__PURE__ */ jsxs("div", { style: styles.row, children: [
654
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
655
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Key" }),
656
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`subforms.${index}.key`) })
657
+ ] }),
658
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
659
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Label" }),
660
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`subforms.${index}.label`) })
661
+ ] }),
662
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
663
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "View ID" }),
664
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`subforms.${index}.viewId`) })
665
+ ] }),
666
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
667
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Parent field" }),
668
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`subforms.${index}.parentField`) })
669
+ ] }),
670
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
671
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Child field" }),
672
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`subforms.${index}.childField`) })
673
+ ] }),
674
+ /* @__PURE__ */ jsx("button", { type: "button", style: styles.button, onClick: () => subformArray.remove(index), children: "Remove" })
675
+ ] }, subform.id))
676
+ ] }),
677
+ /* @__PURE__ */ jsxs("section", { style: styles.section, children: [
678
+ /* @__PURE__ */ jsxs("div", { style: styles.sectionHeader, children: [
679
+ /* @__PURE__ */ jsxs("div", { children: [
680
+ /* @__PURE__ */ jsx("h2", { style: styles.sectionTitle, children: "Form Triggers" }),
681
+ /* @__PURE__ */ jsx("p", { style: styles.description, children: "Optional API callbacks for lifecycle events." })
682
+ ] }),
683
+ /* @__PURE__ */ jsx("button", { type: "button", style: styles.button, onClick: () => triggerArray.append(emptyTrigger), children: "Add trigger" })
684
+ ] }),
685
+ triggerArray.fields.map((trigger, index) => /* @__PURE__ */ jsxs("div", { style: styles.row, children: [
686
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
687
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Event" }),
688
+ /* @__PURE__ */ jsx(
689
+ "select",
690
+ {
691
+ style: styles.input,
692
+ ...form.register(
693
+ `triggers.${index}.event`
694
+ ),
695
+ children: optionItems(options.formTriggerEventOptions)
696
+ }
697
+ )
698
+ ] }),
699
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
700
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Route" }),
701
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`triggers.${index}.route`) })
702
+ ] }),
703
+ /* @__PURE__ */ jsxs("label", { style: styles.checkboxField, children: [
704
+ /* @__PURE__ */ jsx("input", { type: "checkbox", ...form.register(`triggers.${index}.enabled`) }),
705
+ "Enabled"
706
+ ] }),
707
+ /* @__PURE__ */ jsxs("label", { style: styles.checkboxField, children: [
708
+ /* @__PURE__ */ jsx("input", { type: "checkbox", ...form.register(`triggers.${index}.blocking`) }),
709
+ "Blocking"
710
+ ] }),
711
+ /* @__PURE__ */ jsx("button", { type: "button", style: styles.button, onClick: () => triggerArray.remove(index), children: "Remove" })
712
+ ] }, trigger.id))
713
+ ] })
714
+ ] });
715
+ };
716
+ }
717
+ function createBuilderDefaultListEditorBody(options) {
718
+ return function BuilderDefaultListEditorBody({
719
+ listId,
720
+ form,
721
+ columnArray,
722
+ triggerArray,
723
+ sourceType,
724
+ sourceTable,
725
+ tables,
726
+ emptyColumn,
727
+ emptyTrigger,
728
+ isSaving,
729
+ importColumnsFromTable
730
+ }) {
731
+ return /* @__PURE__ */ jsxs("div", { style: styles.page, children: [
732
+ /* @__PURE__ */ jsxs("div", { style: styles.header, children: [
733
+ /* @__PURE__ */ jsxs("div", { children: [
734
+ /* @__PURE__ */ jsx("h1", { style: styles.title, children: listId ? "Edit Dynamic List" : "New Dynamic List" }),
735
+ /* @__PURE__ */ jsx("p", { style: styles.description, children: "Configure list columns, source mapping, and triggers." })
736
+ ] }),
737
+ /* @__PURE__ */ jsxs("div", { style: styles.actions, children: [
738
+ /* @__PURE__ */ jsx("a", { href: options.backHref, style: styles.button, children: "Cancel" }),
739
+ /* @__PURE__ */ jsx("button", { type: "submit", disabled: isSaving, style: styles.primaryButton, children: isSaving ? "Saving..." : "Save" })
740
+ ] })
741
+ ] }),
742
+ /* @__PURE__ */ jsxs("section", { style: styles.section, children: [
743
+ /* @__PURE__ */ jsx("div", { style: styles.sectionHeader, children: /* @__PURE__ */ jsx("h2", { style: styles.sectionTitle, children: "Details" }) }),
744
+ /* @__PURE__ */ jsxs("div", { style: { ...styles.grid, ...styles.twoColumn }, children: [
745
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
746
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Title" }),
747
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register("title", { required: true }) })
748
+ ] }),
749
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
750
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Description" }),
751
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register("description") })
752
+ ] }),
753
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
754
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Datasource" }),
755
+ /* @__PURE__ */ jsxs("select", { style: styles.input, ...form.register("sourceType"), children: [
756
+ /* @__PURE__ */ jsx("option", { value: "none", children: "None" }),
757
+ /* @__PURE__ */ jsx("option", { value: "dbTable", children: "DB table" }),
758
+ /* @__PURE__ */ jsx("option", { value: "apiEndpoint", children: "API endpoint" })
759
+ ] })
760
+ ] }),
761
+ sourceType === "dbTable" ? /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
762
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Table" }),
763
+ /* @__PURE__ */ jsxs("select", { style: styles.input, ...form.register("sourceTable"), children: [
764
+ /* @__PURE__ */ jsx("option", { value: BUILDER_EDITOR_NONE_VALUE, children: "Select table" }),
765
+ tables.map((table) => /* @__PURE__ */ jsx("option", { value: table.name, children: table.label ?? table.name }, table.name))
766
+ ] })
767
+ ] }) : null,
768
+ sourceType === "apiEndpoint" ? /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
769
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "API base path" }),
770
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register("apiBasePath") })
771
+ ] }) : null,
772
+ sourceType === "dbTable" ? /* @__PURE__ */ jsx("div", { style: { ...styles.field, justifyContent: "end" }, children: /* @__PURE__ */ jsx(
773
+ "button",
774
+ {
775
+ type: "button",
776
+ style: styles.button,
777
+ disabled: sourceTable === BUILDER_EDITOR_NONE_VALUE,
778
+ onClick: importColumnsFromTable,
779
+ children: "Import columns"
780
+ }
781
+ ) }) : null
782
+ ] })
783
+ ] }),
784
+ /* @__PURE__ */ jsxs("section", { style: styles.section, children: [
785
+ /* @__PURE__ */ jsxs("div", { style: styles.sectionHeader, children: [
786
+ /* @__PURE__ */ jsxs("div", { children: [
787
+ /* @__PURE__ */ jsx("h2", { style: styles.sectionTitle, children: "Columns" }),
788
+ /* @__PURE__ */ jsx("p", { style: styles.description, children: "Runtime list columns." })
789
+ ] }),
790
+ /* @__PURE__ */ jsx(
791
+ "button",
792
+ {
793
+ type: "button",
794
+ style: styles.button,
795
+ onClick: () => columnArray.append({
796
+ ...emptyColumn,
797
+ key: `field${columnArray.fields.length + 1}`,
798
+ label: `Column ${columnArray.fields.length + 1}`
799
+ }),
800
+ children: "Add column"
801
+ }
802
+ )
803
+ ] }),
804
+ columnArray.fields.map((column, index) => /* @__PURE__ */ jsxs("div", { style: styles.row, children: [
805
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
806
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Key" }),
807
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`columns.${index}.key`) })
808
+ ] }),
809
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
810
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Label" }),
811
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`columns.${index}.label`) })
812
+ ] }),
813
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
814
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Align" }),
815
+ /* @__PURE__ */ jsxs("select", { style: styles.input, ...form.register(`columns.${index}.align`), children: [
816
+ /* @__PURE__ */ jsx("option", { value: "left", children: "Left" }),
817
+ /* @__PURE__ */ jsx("option", { value: "right", children: "Right" })
818
+ ] })
819
+ ] }),
820
+ /* @__PURE__ */ jsxs("label", { style: styles.checkboxField, children: [
821
+ /* @__PURE__ */ jsx("input", { type: "checkbox", ...form.register(`columns.${index}.sortable`) }),
822
+ "Sortable"
823
+ ] }),
824
+ /* @__PURE__ */ jsx(
825
+ "button",
826
+ {
827
+ type: "button",
828
+ style: styles.button,
829
+ onClick: () => columnArray.remove(index),
830
+ disabled: columnArray.fields.length <= 1,
831
+ children: "Remove"
832
+ }
833
+ )
834
+ ] }, column.id))
835
+ ] }),
836
+ /* @__PURE__ */ jsxs("section", { style: styles.section, children: [
837
+ /* @__PURE__ */ jsxs("div", { style: styles.sectionHeader, children: [
838
+ /* @__PURE__ */ jsxs("div", { children: [
839
+ /* @__PURE__ */ jsx("h2", { style: styles.sectionTitle, children: "List Triggers" }),
840
+ /* @__PURE__ */ jsx("p", { style: styles.description, children: "Optional API callbacks for list lifecycle events." })
841
+ ] }),
842
+ /* @__PURE__ */ jsx("button", { type: "button", style: styles.button, onClick: () => triggerArray.append(emptyTrigger), children: "Add trigger" })
843
+ ] }),
844
+ triggerArray.fields.map((trigger, index) => /* @__PURE__ */ jsxs("div", { style: styles.row, children: [
845
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
846
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Event" }),
847
+ /* @__PURE__ */ jsx(
848
+ "select",
849
+ {
850
+ style: styles.input,
851
+ ...form.register(
852
+ `triggers.${index}.event`
853
+ ),
854
+ children: optionItems(options.listTriggerEventOptions)
855
+ }
856
+ )
857
+ ] }),
858
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
859
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Route" }),
860
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`triggers.${index}.route`) })
861
+ ] }),
862
+ /* @__PURE__ */ jsxs("label", { style: styles.checkboxField, children: [
863
+ /* @__PURE__ */ jsx("input", { type: "checkbox", ...form.register(`triggers.${index}.enabled`) }),
864
+ "Enabled"
865
+ ] }),
866
+ /* @__PURE__ */ jsxs("label", { style: styles.checkboxField, children: [
867
+ /* @__PURE__ */ jsx("input", { type: "checkbox", ...form.register(`triggers.${index}.blocking`) }),
868
+ "Blocking"
869
+ ] }),
870
+ /* @__PURE__ */ jsx("button", { type: "button", style: styles.button, onClick: () => triggerArray.remove(index), children: "Remove" })
871
+ ] }, trigger.id))
872
+ ] })
873
+ ] });
874
+ };
875
+ }
876
+
877
+ // src/admin/BuilderDefaultEntityManager.tsx
878
+ import { useMemo, useState } from "react";
879
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
880
+ var styles2 = {
881
+ page: {
882
+ display: "grid",
883
+ gap: "1rem"
884
+ },
885
+ header: {
886
+ display: "flex",
887
+ justifyContent: "space-between",
888
+ gap: "1rem",
889
+ alignItems: "flex-start",
890
+ flexWrap: "wrap"
891
+ },
892
+ title: {
893
+ margin: 0,
894
+ fontSize: "1.5rem",
895
+ fontWeight: 650
896
+ },
897
+ description: {
898
+ margin: "0.25rem 0 0",
899
+ color: "var(--muted-foreground, #64748b)"
900
+ },
901
+ button: {
902
+ border: "1px solid var(--border, #d4d4d8)",
903
+ borderRadius: "0.375rem",
904
+ background: "var(--background, #fff)",
905
+ color: "inherit",
906
+ cursor: "pointer",
907
+ padding: "0.5rem 0.75rem",
908
+ textDecoration: "none",
909
+ fontSize: "0.875rem"
910
+ },
911
+ primaryButton: {
912
+ border: "1px solid var(--primary, #18181b)",
913
+ borderRadius: "0.375rem",
914
+ background: "var(--primary, #18181b)",
915
+ color: "var(--primary-foreground, #fff)",
916
+ cursor: "pointer",
917
+ padding: "0.5rem 0.75rem",
918
+ textDecoration: "none",
919
+ fontSize: "0.875rem"
920
+ },
921
+ card: {
922
+ border: "1px solid var(--border, #d4d4d8)",
923
+ borderRadius: "0.5rem",
924
+ overflow: "hidden",
925
+ background: "var(--background, #fff)"
926
+ },
927
+ toolbar: {
928
+ padding: "0.75rem",
929
+ borderBottom: "1px solid var(--border, #d4d4d8)"
930
+ },
931
+ input: {
932
+ width: "100%",
933
+ maxWidth: "24rem",
934
+ border: "1px solid var(--border, #d4d4d8)",
935
+ borderRadius: "0.375rem",
936
+ padding: "0.5rem 0.75rem",
937
+ background: "var(--background, #fff)",
938
+ color: "inherit"
939
+ },
940
+ tableWrap: {
941
+ overflowX: "auto"
942
+ },
943
+ table: {
944
+ width: "100%",
945
+ borderCollapse: "collapse",
946
+ fontSize: "0.875rem"
947
+ },
948
+ th: {
949
+ padding: "0.75rem",
950
+ borderBottom: "1px solid var(--border, #d4d4d8)",
951
+ textAlign: "left",
952
+ color: "var(--muted-foreground, #64748b)",
953
+ fontWeight: 600,
954
+ whiteSpace: "nowrap"
955
+ },
956
+ td: {
957
+ padding: "0.75rem",
958
+ borderBottom: "1px solid var(--border, #d4d4d8)",
959
+ verticalAlign: "top"
960
+ },
961
+ actions: {
962
+ display: "flex",
963
+ justifyContent: "flex-end",
964
+ gap: "0.5rem",
965
+ flexWrap: "wrap"
966
+ },
967
+ empty: {
968
+ padding: "2rem",
969
+ textAlign: "center",
970
+ color: "var(--muted-foreground, #64748b)"
971
+ }
972
+ };
973
+ function createBuilderDefaultEntityManagerComponent(deps) {
974
+ return function BuilderDefaultEntityManager() {
975
+ const [state, setState] = useState({
976
+ page: 1,
977
+ pageSize: 20,
978
+ search: ""
979
+ });
980
+ const query = deps.hooks.useEntities(useMemo(() => deps.buildQuery(state), [state]));
981
+ const remove = deps.hooks.useDelete();
982
+ const replicate = deps.hooks.useReplicate();
983
+ const items = query.data?.items ?? [];
984
+ const onReplicate = async (entity) => {
985
+ const title = window.prompt(
986
+ deps.labels.replicatePrompt(entity),
987
+ deps.labels.replicateDefaultTitle(entity)
988
+ );
989
+ if (!title) return;
990
+ await replicate.mutateAsync({ id: entity.id, input: { title } });
991
+ };
992
+ const onDelete = async (entity) => {
993
+ if (!window.confirm(deps.labels.deleteConfirm(entity))) return;
994
+ await remove.mutateAsync(entity.id);
995
+ };
996
+ return /* @__PURE__ */ jsxs2("section", { style: styles2.page, children: [
997
+ /* @__PURE__ */ jsxs2("div", { style: styles2.header, children: [
998
+ /* @__PURE__ */ jsxs2("div", { children: [
999
+ /* @__PURE__ */ jsx2("h1", { style: styles2.title, children: deps.labels.title }),
1000
+ /* @__PURE__ */ jsx2("p", { style: styles2.description, children: deps.labels.description })
1001
+ ] }),
1002
+ deps.createHref ? /* @__PURE__ */ jsx2("a", { href: deps.createHref, style: styles2.primaryButton, children: deps.labels.addLabel }) : null
1003
+ ] }),
1004
+ /* @__PURE__ */ jsxs2("div", { style: styles2.card, children: [
1005
+ /* @__PURE__ */ jsx2("div", { style: styles2.toolbar, children: /* @__PURE__ */ jsx2(
1006
+ "input",
1007
+ {
1008
+ style: styles2.input,
1009
+ placeholder: deps.labels.searchPlaceholder,
1010
+ value: state.search,
1011
+ onChange: (event) => setState((current) => ({ ...current, page: 1, search: event.target.value }))
1012
+ }
1013
+ ) }),
1014
+ /* @__PURE__ */ jsx2("div", { style: styles2.tableWrap, children: /* @__PURE__ */ jsxs2("table", { style: styles2.table, children: [
1015
+ /* @__PURE__ */ jsx2("thead", { children: /* @__PURE__ */ jsxs2("tr", { children: [
1016
+ /* @__PURE__ */ jsx2("th", { style: styles2.th, children: deps.labels.title }),
1017
+ /* @__PURE__ */ jsx2("th", { style: styles2.th, children: "Detail" }),
1018
+ /* @__PURE__ */ jsx2("th", { style: styles2.th, children: "Description" }),
1019
+ /* @__PURE__ */ jsx2("th", { style: { ...styles2.th, textAlign: "right" }, children: "Actions" })
1020
+ ] }) }),
1021
+ /* @__PURE__ */ jsx2("tbody", { children: items.map((entity) => /* @__PURE__ */ jsxs2("tr", { children: [
1022
+ /* @__PURE__ */ jsx2("td", { style: styles2.td, children: /* @__PURE__ */ jsx2("a", { href: deps.editHref(entity), children: entity.title }) }),
1023
+ /* @__PURE__ */ jsx2("td", { style: styles2.td, children: deps.getDetail?.(entity) ?? "-" }),
1024
+ /* @__PURE__ */ jsx2("td", { style: styles2.td, children: entity.description || "-" }),
1025
+ /* @__PURE__ */ jsx2("td", { style: styles2.td, children: /* @__PURE__ */ jsxs2("div", { style: styles2.actions, children: [
1026
+ deps.runtimeHref ? /* @__PURE__ */ jsx2("a", { href: deps.runtimeHref(entity), style: styles2.button, children: deps.labels.runtimeLabel }) : null,
1027
+ /* @__PURE__ */ jsx2("a", { href: deps.editHref(entity), style: styles2.button, children: deps.labels.editLabel }),
1028
+ /* @__PURE__ */ jsx2(
1029
+ "button",
1030
+ {
1031
+ type: "button",
1032
+ style: styles2.button,
1033
+ onClick: () => void onReplicate(entity),
1034
+ children: deps.labels.replicateLabel
1035
+ }
1036
+ ),
1037
+ /* @__PURE__ */ jsx2(
1038
+ "button",
1039
+ {
1040
+ type: "button",
1041
+ style: styles2.button,
1042
+ disabled: remove.isPending,
1043
+ onClick: () => void onDelete(entity),
1044
+ children: deps.labels.deleteLabel
1045
+ }
1046
+ )
1047
+ ] }) })
1048
+ ] }, entity.id)) })
1049
+ ] }) }),
1050
+ query.isPending ? /* @__PURE__ */ jsx2("div", { style: styles2.empty, children: "Loading..." }) : null,
1051
+ query.isError ? /* @__PURE__ */ jsx2("div", { style: styles2.empty, children: "Failed to load." }) : null,
1052
+ !query.isPending && !query.isError && items.length === 0 ? /* @__PURE__ */ jsxs2("div", { style: styles2.empty, children: [
1053
+ /* @__PURE__ */ jsx2("strong", { children: deps.labels.emptyTitle }),
1054
+ /* @__PURE__ */ jsx2("div", { children: deps.labels.emptyDescription })
1055
+ ] }) : null
1056
+ ] })
1057
+ ] });
1058
+ };
1059
+ }
1060
+
1061
+ // src/admin/BuilderDefaultViewEditorPage.tsx
1062
+ import { useEffect } from "react";
1063
+ import { useForm } from "react-hook-form";
1064
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1065
+ var styles3 = {
1066
+ page: {
1067
+ display: "grid",
1068
+ gap: "1rem"
1069
+ },
1070
+ header: {
1071
+ display: "flex",
1072
+ justifyContent: "space-between",
1073
+ gap: "1rem",
1074
+ alignItems: "flex-start",
1075
+ flexWrap: "wrap"
1076
+ },
1077
+ title: {
1078
+ margin: 0,
1079
+ fontSize: "1.5rem",
1080
+ fontWeight: 650
1081
+ },
1082
+ description: {
1083
+ margin: "0.25rem 0 0",
1084
+ color: "var(--muted-foreground, #64748b)"
1085
+ },
1086
+ actions: {
1087
+ display: "flex",
1088
+ gap: "0.5rem",
1089
+ flexWrap: "wrap"
1090
+ },
1091
+ section: {
1092
+ border: "1px solid var(--border, #d4d4d8)",
1093
+ borderRadius: "0.5rem",
1094
+ background: "var(--background, #fff)",
1095
+ overflow: "hidden"
1096
+ },
1097
+ grid: {
1098
+ display: "grid",
1099
+ gridTemplateColumns: "repeat(auto-fit, minmax(14rem, 1fr))",
1100
+ gap: "0.75rem",
1101
+ padding: "0.75rem"
1102
+ },
1103
+ field: {
1104
+ display: "grid",
1105
+ gap: "0.25rem",
1106
+ fontSize: "0.875rem"
1107
+ },
1108
+ label: {
1109
+ color: "var(--muted-foreground, #64748b)",
1110
+ fontSize: "0.75rem",
1111
+ fontWeight: 600
1112
+ },
1113
+ input: {
1114
+ minWidth: 0,
1115
+ border: "1px solid var(--border, #d4d4d8)",
1116
+ borderRadius: "0.375rem",
1117
+ padding: "0.5rem 0.625rem",
1118
+ background: "var(--background, #fff)",
1119
+ color: "inherit"
1120
+ },
1121
+ textarea: {
1122
+ minWidth: 0,
1123
+ minHeight: "8rem",
1124
+ border: "1px solid var(--border, #d4d4d8)",
1125
+ borderRadius: "0.375rem",
1126
+ padding: "0.5rem 0.625rem",
1127
+ background: "var(--background, #fff)",
1128
+ color: "inherit",
1129
+ resize: "vertical"
1130
+ },
1131
+ button: {
1132
+ border: "1px solid var(--border, #d4d4d8)",
1133
+ borderRadius: "0.375rem",
1134
+ background: "var(--background, #fff)",
1135
+ color: "inherit",
1136
+ cursor: "pointer",
1137
+ padding: "0.5rem 0.75rem",
1138
+ textDecoration: "none",
1139
+ fontSize: "0.875rem"
1140
+ },
1141
+ primaryButton: {
1142
+ border: "1px solid var(--primary, #18181b)",
1143
+ borderRadius: "0.375rem",
1144
+ background: "var(--primary, #18181b)",
1145
+ color: "var(--primary-foreground, #fff)",
1146
+ cursor: "pointer",
1147
+ padding: "0.5rem 0.75rem",
1148
+ textDecoration: "none",
1149
+ fontSize: "0.875rem"
1150
+ },
1151
+ state: {
1152
+ color: "var(--muted-foreground, #64748b)",
1153
+ fontSize: "0.875rem"
1154
+ }
1155
+ };
1156
+ var defaultEmptyValues = {
1157
+ title: "",
1158
+ description: "",
1159
+ formId: BUILDER_EDITOR_NONE_VALUE,
1160
+ listId: BUILDER_EDITOR_NONE_VALUE,
1161
+ permissionRead: "",
1162
+ permissionWrite: "",
1163
+ triggersText: "[]",
1164
+ version: 0
1165
+ };
1166
+ function createBuilderDefaultViewEditorPage(options) {
1167
+ const defaultPermission = options.defaultPermission ?? "";
1168
+ const showPermissions = options.permissionOptions.length > 0;
1169
+ return function BuilderDefaultViewEditorPage({ viewId }) {
1170
+ const view = options.hooks.useBuilderView(viewId);
1171
+ const forms = options.hooks.useBuilderFormOptions();
1172
+ const lists = options.hooks.useBuilderListOptions();
1173
+ const save = options.hooks.useSaveBuilderView();
1174
+ const form = useForm({
1175
+ defaultValues: {
1176
+ ...defaultEmptyValues,
1177
+ permissionRead: defaultPermission,
1178
+ permissionWrite: defaultPermission
1179
+ }
1180
+ });
1181
+ useEffect(() => {
1182
+ if (viewId && !view.data) return;
1183
+ form.reset(
1184
+ builderViewEditorValuesFromEntity(
1185
+ view.data ?? null,
1186
+ {
1187
+ ...defaultEmptyValues,
1188
+ permissionRead: defaultPermission,
1189
+ permissionWrite: defaultPermission
1190
+ },
1191
+ defaultPermission
1192
+ )
1193
+ );
1194
+ }, [form, view.data, viewId]);
1195
+ const onSubmit = form.handleSubmit(async (values) => {
1196
+ try {
1197
+ const input = builderViewEditorInputFromValues(values, options.parseTriggers);
1198
+ const saved = await save.mutateAsync({
1199
+ id: viewId,
1200
+ input
1201
+ });
1202
+ options.notifySuccess?.(viewId ? "View saved" : "View created");
1203
+ if (!viewId) {
1204
+ window.location.assign(options.editHref(saved.id));
1205
+ }
1206
+ } catch (error) {
1207
+ if (options.notifyError) options.notifyError(error);
1208
+ else throw error;
1209
+ }
1210
+ });
1211
+ if (viewId && view.isPending) {
1212
+ return /* @__PURE__ */ jsx3("div", { style: styles3.state, children: "Loading..." });
1213
+ }
1214
+ if (viewId && view.isError) {
1215
+ return /* @__PURE__ */ jsx3("div", { style: styles3.state, children: "Failed to load view." });
1216
+ }
1217
+ return /* @__PURE__ */ jsxs3("form", { onSubmit, noValidate: true, style: styles3.page, children: [
1218
+ /* @__PURE__ */ jsxs3("div", { style: styles3.header, children: [
1219
+ /* @__PURE__ */ jsxs3("div", { children: [
1220
+ /* @__PURE__ */ jsx3("h1", { style: styles3.title, children: viewId ? "Edit Dynamic View" : "New Dynamic View" }),
1221
+ /* @__PURE__ */ jsx3("p", { style: styles3.description, children: "Connect a form and list into a runtime screen." })
1222
+ ] }),
1223
+ /* @__PURE__ */ jsxs3("div", { style: styles3.actions, children: [
1224
+ /* @__PURE__ */ jsx3("a", { href: options.backHref, style: styles3.button, children: "Cancel" }),
1225
+ /* @__PURE__ */ jsx3("button", { type: "submit", disabled: save.isPending, style: styles3.primaryButton, children: save.isPending ? "Saving..." : "Save" })
1226
+ ] })
1227
+ ] }),
1228
+ /* @__PURE__ */ jsx3("section", { style: styles3.section, children: /* @__PURE__ */ jsxs3("div", { style: styles3.grid, children: [
1229
+ /* @__PURE__ */ jsxs3("label", { style: styles3.field, children: [
1230
+ /* @__PURE__ */ jsx3("span", { style: styles3.label, children: "Title" }),
1231
+ /* @__PURE__ */ jsx3("input", { style: styles3.input, ...form.register("title", { required: true }) })
1232
+ ] }),
1233
+ /* @__PURE__ */ jsxs3("label", { style: styles3.field, children: [
1234
+ /* @__PURE__ */ jsx3("span", { style: styles3.label, children: "Description" }),
1235
+ /* @__PURE__ */ jsx3("input", { style: styles3.input, ...form.register("description") })
1236
+ ] }),
1237
+ /* @__PURE__ */ jsxs3("label", { style: styles3.field, children: [
1238
+ /* @__PURE__ */ jsx3("span", { style: styles3.label, children: "Form" }),
1239
+ /* @__PURE__ */ jsxs3("select", { style: styles3.input, ...form.register("formId"), children: [
1240
+ /* @__PURE__ */ jsx3("option", { value: BUILDER_EDITOR_NONE_VALUE, children: "No form" }),
1241
+ (forms.data ?? []).map((item) => /* @__PURE__ */ jsx3("option", { value: item.id, children: item.title }, item.id))
1242
+ ] })
1243
+ ] }),
1244
+ /* @__PURE__ */ jsxs3("label", { style: styles3.field, children: [
1245
+ /* @__PURE__ */ jsx3("span", { style: styles3.label, children: "List" }),
1246
+ /* @__PURE__ */ jsxs3("select", { style: styles3.input, ...form.register("listId"), children: [
1247
+ /* @__PURE__ */ jsx3("option", { value: BUILDER_EDITOR_NONE_VALUE, children: "No list" }),
1248
+ (lists.data ?? []).map((item) => /* @__PURE__ */ jsx3("option", { value: item.id, children: item.title }, item.id))
1249
+ ] })
1250
+ ] }),
1251
+ showPermissions ? /* @__PURE__ */ jsxs3(Fragment, { children: [
1252
+ /* @__PURE__ */ jsxs3("label", { style: styles3.field, children: [
1253
+ /* @__PURE__ */ jsx3("span", { style: styles3.label, children: "Read permission" }),
1254
+ /* @__PURE__ */ jsxs3("select", { style: styles3.input, ...form.register("permissionRead"), children: [
1255
+ /* @__PURE__ */ jsx3("option", { value: "", children: "Authenticated users" }),
1256
+ options.permissionOptions.map((permission) => /* @__PURE__ */ jsx3("option", { value: permission.value, children: permission.label }, permission.value))
1257
+ ] })
1258
+ ] }),
1259
+ /* @__PURE__ */ jsxs3("label", { style: styles3.field, children: [
1260
+ /* @__PURE__ */ jsx3("span", { style: styles3.label, children: "Write permission" }),
1261
+ /* @__PURE__ */ jsxs3("select", { style: styles3.input, ...form.register("permissionWrite"), children: [
1262
+ /* @__PURE__ */ jsx3("option", { value: "", children: "Authenticated users" }),
1263
+ options.permissionOptions.map((permission) => /* @__PURE__ */ jsx3("option", { value: permission.value, children: permission.label }, permission.value))
1264
+ ] })
1265
+ ] })
1266
+ ] }) : null,
1267
+ /* @__PURE__ */ jsxs3("label", { style: { ...styles3.field, gridColumn: "1 / -1" }, children: [
1268
+ /* @__PURE__ */ jsx3("span", { style: styles3.label, children: "Triggers JSON" }),
1269
+ /* @__PURE__ */ jsx3("textarea", { style: styles3.textarea, ...form.register("triggersText") })
1270
+ ] })
1271
+ ] }) })
1272
+ ] });
1273
+ };
1274
+ }
1275
+
313
1276
  // src/admin/BuilderEditorPage.tsx
314
- import { jsx } from "react/jsx-runtime";
1277
+ import { useEffect as useEffect2 } from "react";
1278
+ import {
1279
+ FormProvider,
1280
+ useFieldArray,
1281
+ useForm as useForm2,
1282
+ useWatch
1283
+ } from "react-hook-form";
1284
+ import { jsx as jsx4 } from "react/jsx-runtime";
315
1285
  function createBuilderFormEditorPageComponent(deps) {
316
1286
  return function BuilderFormEditorPage({ formId }) {
317
1287
  const Body = deps.Body;
318
1288
  const entityQuery = deps.hooks.useEntity(formId);
319
1289
  const tables = deps.hooks.useTables();
320
1290
  const save = deps.hooks.useSave();
321
- const form = useForm({
1291
+ const form = useForm2({
322
1292
  resolver: deps.resolver,
323
1293
  defaultValues: deps.emptyValues
324
1294
  });
@@ -329,7 +1299,7 @@ function createBuilderFormEditorPageComponent(deps) {
329
1299
  const subforms = useWatch({ control: form.control, name: "subforms" }) ?? [];
330
1300
  const sourceType = useWatch({ control: form.control, name: "sourceType" });
331
1301
  const sourceTable = useWatch({ control: form.control, name: "sourceTable" });
332
- useEffect(() => {
1302
+ useEffect2(() => {
333
1303
  if (formId && !entityQuery.data) return;
334
1304
  form.reset(builderFormEditorValuesFromEntity(entityQuery.data ?? null, deps.emptyValues));
335
1305
  }, [entityQuery.data, form, formId]);
@@ -352,12 +1322,12 @@ function createBuilderFormEditorPageComponent(deps) {
352
1322
  }
353
1323
  });
354
1324
  if (formId && entityQuery.isPending) {
355
- return /* @__PURE__ */ jsx("div", { className: "text-muted-foreground text-sm", children: deps.states.loading });
1325
+ return /* @__PURE__ */ jsx4("div", { className: "text-muted-foreground text-sm", children: deps.states.loading });
356
1326
  }
357
1327
  if (formId && entityQuery.isError) {
358
- return /* @__PURE__ */ jsx("div", { className: "text-destructive text-sm", children: deps.states.error });
1328
+ return /* @__PURE__ */ jsx4("div", { className: "text-destructive text-sm", children: deps.states.error });
359
1329
  }
360
- return /* @__PURE__ */ jsx(FormProvider, { ...form, children: /* @__PURE__ */ jsx("form", { onSubmit, noValidate: true, className: "space-y-6", children: /* @__PURE__ */ jsx(
1330
+ return /* @__PURE__ */ jsx4(FormProvider, { ...form, children: /* @__PURE__ */ jsx4("form", { onSubmit, noValidate: true, className: "space-y-6", children: /* @__PURE__ */ jsx4(
361
1331
  Body,
362
1332
  {
363
1333
  formId,
@@ -384,7 +1354,7 @@ function createBuilderListEditorPageComponent(deps) {
384
1354
  const entityQuery = deps.hooks.useEntity(listId);
385
1355
  const tables = deps.hooks.useTables();
386
1356
  const save = deps.hooks.useSave();
387
- const form = useForm({
1357
+ const form = useForm2({
388
1358
  resolver: deps.resolver,
389
1359
  defaultValues: deps.emptyValues
390
1360
  });
@@ -392,7 +1362,7 @@ function createBuilderListEditorPageComponent(deps) {
392
1362
  const triggerArray = useFieldArray({ control: form.control, name: "triggers" });
393
1363
  const sourceType = useWatch({ control: form.control, name: "sourceType" });
394
1364
  const sourceTable = useWatch({ control: form.control, name: "sourceTable" });
395
- useEffect(() => {
1365
+ useEffect2(() => {
396
1366
  if (listId && !entityQuery.data) return;
397
1367
  form.reset(builderListEditorValuesFromEntity(entityQuery.data ?? null, deps.emptyValues));
398
1368
  }, [entityQuery.data, form, listId]);
@@ -411,12 +1381,12 @@ function createBuilderListEditorPageComponent(deps) {
411
1381
  }
412
1382
  });
413
1383
  if (listId && entityQuery.isPending) {
414
- return /* @__PURE__ */ jsx("div", { className: "text-muted-foreground text-sm", children: deps.states.loading });
1384
+ return /* @__PURE__ */ jsx4("div", { className: "text-muted-foreground text-sm", children: deps.states.loading });
415
1385
  }
416
1386
  if (listId && entityQuery.isError) {
417
- return /* @__PURE__ */ jsx("div", { className: "text-destructive text-sm", children: deps.states.error });
1387
+ return /* @__PURE__ */ jsx4("div", { className: "text-destructive text-sm", children: deps.states.error });
418
1388
  }
419
- return /* @__PURE__ */ jsx(FormProvider, { ...form, children: /* @__PURE__ */ jsx("form", { onSubmit, noValidate: true, className: "space-y-6", children: /* @__PURE__ */ jsx(
1389
+ return /* @__PURE__ */ jsx4(FormProvider, { ...form, children: /* @__PURE__ */ jsx4("form", { onSubmit, noValidate: true, className: "space-y-6", children: /* @__PURE__ */ jsx4(
420
1390
  Body,
421
1391
  {
422
1392
  listId,
@@ -435,17 +1405,405 @@ function createBuilderListEditorPageComponent(deps) {
435
1405
  };
436
1406
  }
437
1407
 
1408
+ // src/admin/BuilderAdminPages.tsx
1409
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1410
+ var defaultFieldTypeOptions = [
1411
+ { value: "text", label: "Text" },
1412
+ { value: "textarea", label: "Textarea" },
1413
+ { value: "number", label: "Number" },
1414
+ { value: "money", label: "Money" },
1415
+ { value: "select", label: "Select" },
1416
+ { value: "checkbox", label: "Checkbox" },
1417
+ { value: "date", label: "Date" }
1418
+ ];
1419
+ var defaultFormTriggerEventOptions = [
1420
+ { value: "beforeCreate", label: "Before create" },
1421
+ { value: "afterCreate", label: "After create" },
1422
+ { value: "beforeUpdate", label: "Before update" },
1423
+ { value: "afterUpdate", label: "After update" }
1424
+ ];
1425
+ var defaultListTriggerEventOptions = [
1426
+ { value: "beforeRead", label: "Before read" },
1427
+ { value: "afterRead", label: "After read" },
1428
+ { value: "beforeDelete", label: "Before delete" },
1429
+ { value: "afterDelete", label: "After delete" }
1430
+ ];
1431
+ var defaultFieldTriggerEventOptions = [
1432
+ { value: "onChange", label: "On change" },
1433
+ { value: "onBlur", label: "On blur" }
1434
+ ];
1435
+ var defaultPermissionOptions = [];
1436
+ function parseJsonArray(value) {
1437
+ const parsed = JSON.parse(value);
1438
+ return Array.isArray(parsed) ? parsed : [];
1439
+ }
1440
+ function joinPath(base, path) {
1441
+ return `${base.replace(/\/$/, "")}/${path.replace(/^\//, "")}`;
1442
+ }
1443
+ function defaultQuery(state) {
1444
+ return {
1445
+ page: state.page,
1446
+ pageSize: state.pageSize,
1447
+ search: state.search || void 0
1448
+ };
1449
+ }
1450
+ function detail(entity) {
1451
+ return entity.key ?? entity.id;
1452
+ }
1453
+ var homeStyles = {
1454
+ page: {
1455
+ display: "grid",
1456
+ gap: "1rem"
1457
+ },
1458
+ headerTitle: {
1459
+ margin: 0,
1460
+ fontSize: "1.5rem",
1461
+ fontWeight: 650
1462
+ },
1463
+ description: {
1464
+ margin: "0.25rem 0 0",
1465
+ color: "var(--muted-foreground, #64748b)"
1466
+ },
1467
+ grid: {
1468
+ display: "grid",
1469
+ gridTemplateColumns: "repeat(auto-fit, minmax(13rem, 1fr))",
1470
+ gap: "0.75rem"
1471
+ },
1472
+ card: {
1473
+ border: "1px solid var(--border, #d4d4d8)",
1474
+ borderRadius: "0.5rem",
1475
+ padding: "1rem",
1476
+ background: "var(--background, #fff)",
1477
+ color: "inherit",
1478
+ textDecoration: "none"
1479
+ },
1480
+ cardTitle: {
1481
+ margin: 0,
1482
+ fontSize: "1rem",
1483
+ fontWeight: 650
1484
+ }
1485
+ };
1486
+ function createBuilderAdminPages({
1487
+ hooks,
1488
+ routes = {},
1489
+ fieldTypeOptions = defaultFieldTypeOptions,
1490
+ formTriggerEventOptions = defaultFormTriggerEventOptions,
1491
+ listTriggerEventOptions = defaultListTriggerEventOptions,
1492
+ fieldTriggerEventOptions = defaultFieldTriggerEventOptions,
1493
+ permissionOptions = defaultPermissionOptions,
1494
+ defaultPermission = "",
1495
+ notifySuccess,
1496
+ notifyError,
1497
+ parseViewTriggers = parseJsonArray
1498
+ }) {
1499
+ const adminBasePath = routes.adminBasePath ?? "/admin/dev/builder";
1500
+ const runtimeBasePath = routes.runtimeBasePath ?? "/admin/winu";
1501
+ const formsPath = routes.formsPath ?? joinPath(adminBasePath, "forms");
1502
+ const newFormPath = routes.newFormPath ?? joinPath(formsPath, "new");
1503
+ const editFormPath = routes.editFormPath ?? ((id) => `${joinPath(formsPath, "edit")}?id=${encodeURIComponent(id)}`);
1504
+ const runtimeFormPath = routes.runtimeFormPath ?? ((id) => `${joinPath(runtimeBasePath, "form")}?id=${encodeURIComponent(id)}`);
1505
+ const listsPath = routes.listsPath ?? joinPath(adminBasePath, "lists");
1506
+ const newListPath = routes.newListPath ?? joinPath(listsPath, "new");
1507
+ const editListPath = routes.editListPath ?? ((id) => `${joinPath(listsPath, "edit")}?id=${encodeURIComponent(id)}`);
1508
+ const runtimeListPath = routes.runtimeListPath ?? ((id) => `${joinPath(runtimeBasePath, "list")}?id=${encodeURIComponent(id)}`);
1509
+ const viewsPath = routes.viewsPath ?? joinPath(adminBasePath, "views");
1510
+ const newViewPath = routes.newViewPath ?? joinPath(viewsPath, "new");
1511
+ const editViewPath = routes.editViewPath ?? ((id) => `${joinPath(viewsPath, "edit")}?id=${encodeURIComponent(id)}`);
1512
+ const runtimeViewPath = routes.runtimeViewPath ?? ((id) => `${joinPath(runtimeBasePath, "view")}?id=${encodeURIComponent(id)}`);
1513
+ const formFieldType = fieldTypeOptions[0]?.value ?? "text";
1514
+ const formTriggerEvent = formTriggerEventOptions[0]?.value ?? "beforeCreate";
1515
+ const listTriggerEvent = listTriggerEventOptions[0]?.value ?? "beforeRead";
1516
+ const emptyField = {
1517
+ key: "name",
1518
+ label: "Name",
1519
+ type: formFieldType,
1520
+ required: true,
1521
+ hidden: false,
1522
+ disabled: false,
1523
+ placeholder: "",
1524
+ optionsText: "",
1525
+ optionsMode: "static",
1526
+ optionsTable: BUILDER_EDITOR_NONE_VALUE,
1527
+ optionsValueField: BUILDER_EDITOR_NONE_VALUE,
1528
+ optionsLabelField: BUILDER_EDITOR_NONE_VALUE,
1529
+ optionsSearchFieldsText: "",
1530
+ optionsDependsOn: "",
1531
+ optionsFilterField: "",
1532
+ triggers: []
1533
+ };
1534
+ const emptyFormValues = {
1535
+ title: "",
1536
+ description: "",
1537
+ sourceType: "none",
1538
+ sourceTable: BUILDER_EDITOR_NONE_VALUE,
1539
+ apiBasePath: "",
1540
+ fields: [emptyField],
1541
+ layoutGroups: builderFormEditorDefaultLayoutGroups([emptyField]),
1542
+ subforms: [],
1543
+ triggers: [],
1544
+ version: 0
1545
+ };
1546
+ const emptyFormTrigger = {
1547
+ event: formTriggerEvent,
1548
+ route: "/api/v1/",
1549
+ enabled: false,
1550
+ blocking: true
1551
+ };
1552
+ const emptyColumn = {
1553
+ key: "name",
1554
+ label: "Name",
1555
+ sortable: true,
1556
+ align: "left"
1557
+ };
1558
+ const emptyListValues = {
1559
+ title: "",
1560
+ description: "",
1561
+ sourceType: "none",
1562
+ sourceTable: BUILDER_EDITOR_NONE_VALUE,
1563
+ apiBasePath: "",
1564
+ columns: [emptyColumn],
1565
+ triggers: [],
1566
+ version: 0
1567
+ };
1568
+ const emptyListTrigger = {
1569
+ event: listTriggerEvent,
1570
+ route: "/api/v1/",
1571
+ enabled: false,
1572
+ blocking: true
1573
+ };
1574
+ const FormEditorPage = createBuilderFormEditorPageComponent({
1575
+ emptyValues: emptyFormValues,
1576
+ emptyField,
1577
+ emptyTrigger: emptyFormTrigger,
1578
+ labels: {
1579
+ created: "Form created",
1580
+ saved: "Form saved"
1581
+ },
1582
+ states: {
1583
+ loading: "Loading...",
1584
+ error: "Failed to load form."
1585
+ },
1586
+ hooks: {
1587
+ useEntity: hooks.useBuilderForm,
1588
+ useTables: hooks.useBuilderDbTables,
1589
+ useSave: hooks.useSaveBuilderForm
1590
+ },
1591
+ buildInput: (values) => builderFormEditorInputFromValues(values),
1592
+ navigateAfterCreate: (entity) => window.location.assign(editFormPath(entity.id)),
1593
+ notifications: {
1594
+ success: (message) => notifySuccess?.(message),
1595
+ error: (error) => {
1596
+ if (notifyError) notifyError(error);
1597
+ else throw error;
1598
+ }
1599
+ },
1600
+ Body: createBuilderDefaultFormEditorBody({
1601
+ backHref: formsPath,
1602
+ fieldTypeOptions,
1603
+ formTriggerEventOptions,
1604
+ fieldTriggerEventOptions
1605
+ })
1606
+ });
1607
+ const ListEditorPage = createBuilderListEditorPageComponent({
1608
+ emptyValues: emptyListValues,
1609
+ emptyColumn,
1610
+ emptyTrigger: emptyListTrigger,
1611
+ labels: {
1612
+ created: "List created",
1613
+ saved: "List saved"
1614
+ },
1615
+ states: {
1616
+ loading: "Loading...",
1617
+ error: "Failed to load list."
1618
+ },
1619
+ hooks: {
1620
+ useEntity: hooks.useBuilderList,
1621
+ useTables: hooks.useBuilderDbTables,
1622
+ useSave: hooks.useSaveBuilderList
1623
+ },
1624
+ buildInput: (values) => builderListEditorInputFromValues(values),
1625
+ navigateAfterCreate: (entity) => window.location.assign(editListPath(entity.id)),
1626
+ notifications: {
1627
+ success: (message) => notifySuccess?.(message),
1628
+ error: (error) => {
1629
+ if (notifyError) notifyError(error);
1630
+ else throw error;
1631
+ }
1632
+ },
1633
+ Body: createBuilderDefaultListEditorBody({
1634
+ backHref: listsPath,
1635
+ listTriggerEventOptions
1636
+ })
1637
+ });
1638
+ const ViewEditorPage = createBuilderDefaultViewEditorPage({
1639
+ hooks,
1640
+ backHref: viewsPath,
1641
+ editHref: editViewPath,
1642
+ permissionOptions,
1643
+ defaultPermission,
1644
+ parseTriggers: parseViewTriggers,
1645
+ notifySuccess,
1646
+ notifyError
1647
+ });
1648
+ const AdminHomePage = function BuilderAdminHomePage() {
1649
+ const items = [
1650
+ {
1651
+ title: "Dynamic Forms",
1652
+ description: "Reusable form schemas.",
1653
+ href: formsPath
1654
+ },
1655
+ {
1656
+ title: "Dynamic Lists",
1657
+ description: "Reusable list and grid schemas.",
1658
+ href: listsPath
1659
+ },
1660
+ {
1661
+ title: "Dynamic Views",
1662
+ description: "Runtime screens composed from form and list schemas.",
1663
+ href: viewsPath
1664
+ }
1665
+ ];
1666
+ return /* @__PURE__ */ jsxs4("section", { style: homeStyles.page, children: [
1667
+ /* @__PURE__ */ jsxs4("div", { children: [
1668
+ /* @__PURE__ */ jsx5("h1", { style: homeStyles.headerTitle, children: "Builder" }),
1669
+ /* @__PURE__ */ jsx5("p", { style: homeStyles.description, children: "Manage dynamic forms, lists, and views." })
1670
+ ] }),
1671
+ /* @__PURE__ */ jsx5("div", { style: homeStyles.grid, children: items.map((item) => /* @__PURE__ */ jsxs4("a", { href: item.href, style: homeStyles.card, children: [
1672
+ /* @__PURE__ */ jsx5("h2", { style: homeStyles.cardTitle, children: item.title }),
1673
+ /* @__PURE__ */ jsx5("p", { style: homeStyles.description, children: item.description })
1674
+ ] }, item.href)) })
1675
+ ] });
1676
+ };
1677
+ const AdminFormsPage = createBuilderDefaultEntityManagerComponent({
1678
+ labels: {
1679
+ title: "Dynamic Forms",
1680
+ description: "Reusable form schemas.",
1681
+ searchPlaceholder: "Search forms...",
1682
+ emptyTitle: "No forms",
1683
+ emptyDescription: "Create your first dynamic form.",
1684
+ addLabel: "Add form",
1685
+ editLabel: "Edit",
1686
+ replicateLabel: "Replicate",
1687
+ deleteLabel: "Delete",
1688
+ runtimeLabel: "Open",
1689
+ deleteConfirm: (entity) => `Delete ${entity.title}?`,
1690
+ replicatePrompt: () => "New form title",
1691
+ replicateDefaultTitle: (entity) => `${entity.title} copy`
1692
+ },
1693
+ createHref: newFormPath,
1694
+ editHref: (entity) => editFormPath(entity.id),
1695
+ runtimeHref: (entity) => runtimeFormPath(entity.id),
1696
+ getDetail: detail,
1697
+ buildQuery: (state) => defaultQuery(state),
1698
+ hooks: {
1699
+ useEntities: (query) => hooks.useBuilderForms(query),
1700
+ useDelete: hooks.useDeleteBuilderForm,
1701
+ useReplicate: hooks.useReplicateBuilderForm
1702
+ }
1703
+ });
1704
+ const AdminListsPage = createBuilderDefaultEntityManagerComponent({
1705
+ labels: {
1706
+ title: "Dynamic Lists",
1707
+ description: "Reusable list and grid schemas.",
1708
+ searchPlaceholder: "Search lists...",
1709
+ emptyTitle: "No lists",
1710
+ emptyDescription: "Create your first dynamic list.",
1711
+ addLabel: "Add list",
1712
+ editLabel: "Edit",
1713
+ replicateLabel: "Replicate",
1714
+ deleteLabel: "Delete",
1715
+ runtimeLabel: "Open",
1716
+ deleteConfirm: (entity) => `Delete ${entity.title}?`,
1717
+ replicatePrompt: () => "New list title",
1718
+ replicateDefaultTitle: (entity) => `${entity.title} copy`
1719
+ },
1720
+ createHref: newListPath,
1721
+ editHref: (entity) => editListPath(entity.id),
1722
+ runtimeHref: (entity) => runtimeListPath(entity.id),
1723
+ getDetail: detail,
1724
+ buildQuery: (state) => defaultQuery(state),
1725
+ hooks: {
1726
+ useEntities: (query) => hooks.useBuilderLists(query),
1727
+ useDelete: hooks.useDeleteBuilderList,
1728
+ useReplicate: hooks.useReplicateBuilderList
1729
+ }
1730
+ });
1731
+ const AdminViewsPage = createBuilderDefaultEntityManagerComponent({
1732
+ labels: {
1733
+ title: "Dynamic Views",
1734
+ description: "Runtime screens composed from form and list schemas.",
1735
+ searchPlaceholder: "Search views...",
1736
+ emptyTitle: "No views",
1737
+ emptyDescription: "Create a dynamic view after preparing a form and list.",
1738
+ addLabel: "Add view",
1739
+ editLabel: "Edit",
1740
+ replicateLabel: "Replicate",
1741
+ deleteLabel: "Delete",
1742
+ runtimeLabel: "Open",
1743
+ deleteConfirm: (entity) => `Delete ${entity.title}?`,
1744
+ replicatePrompt: () => "New view title",
1745
+ replicateDefaultTitle: (entity) => `${entity.title} copy`
1746
+ },
1747
+ createHref: newViewPath,
1748
+ editHref: (entity) => editViewPath(entity.id),
1749
+ runtimeHref: (entity) => runtimeViewPath(entity.id),
1750
+ getDetail: detail,
1751
+ buildQuery: (state) => defaultQuery(state),
1752
+ hooks: {
1753
+ useEntities: (query) => hooks.useBuilderViews(query),
1754
+ useDelete: hooks.useDeleteBuilderView,
1755
+ useReplicate: hooks.useReplicateBuilderView
1756
+ }
1757
+ });
1758
+ return {
1759
+ AdminHomePage,
1760
+ AdminFormsPage,
1761
+ AdminNewFormPage: function BuilderAdminNewFormPage() {
1762
+ return /* @__PURE__ */ jsx5(FormEditorPage, {});
1763
+ },
1764
+ AdminEditFormPage: function BuilderAdminEditFormPage({ formId }) {
1765
+ return /* @__PURE__ */ jsx5(FormEditorPage, { formId });
1766
+ },
1767
+ AdminListsPage,
1768
+ AdminNewListPage: function BuilderAdminNewListPage() {
1769
+ return /* @__PURE__ */ jsx5(ListEditorPage, {});
1770
+ },
1771
+ AdminEditListPage: function BuilderAdminEditListPage({ listId }) {
1772
+ return /* @__PURE__ */ jsx5(ListEditorPage, { listId });
1773
+ },
1774
+ AdminViewsPage,
1775
+ AdminNewViewPage: function BuilderAdminNewViewPage() {
1776
+ return /* @__PURE__ */ jsx5(ViewEditorPage, {});
1777
+ },
1778
+ AdminEditViewPage: function BuilderAdminEditViewPage({ viewId }) {
1779
+ return /* @__PURE__ */ jsx5(ViewEditorPage, { viewId });
1780
+ },
1781
+ routes: {
1782
+ adminBasePath,
1783
+ formsPath,
1784
+ newFormPath,
1785
+ editFormPath,
1786
+ listsPath,
1787
+ newListPath,
1788
+ editListPath,
1789
+ viewsPath,
1790
+ newViewPath,
1791
+ editViewPath
1792
+ }
1793
+ };
1794
+ }
1795
+
438
1796
  // src/admin/BuilderEntityManager.tsx
439
- import { useCallback, useMemo, useState } from "react";
440
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
1797
+ import { useCallback, useMemo as useMemo2, useState as useState2 } from "react";
1798
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
441
1799
  function createBuilderEntityManagerComponent(deps) {
442
1800
  return function BuilderEntityManager() {
443
- const [pendingDelete, setPendingDelete] = useState(null);
1801
+ const [pendingDelete, setPendingDelete] = useState2(null);
444
1802
  const state = deps.useDataTableState({ pageSize: 20 });
445
1803
  const query = deps.hooks.useEntities(state.query);
446
1804
  const remove = deps.hooks.useDelete();
447
1805
  const replicate = deps.hooks.useReplicate();
448
- const columns = useMemo(
1806
+ const columns = useMemo2(
449
1807
  () => deps.buildColumns({
450
1808
  onEdit: deps.navigateToEdit,
451
1809
  onReplicate: async (entity) => {
@@ -465,8 +1823,8 @@ function createBuilderEntityManagerComponent(deps) {
465
1823
  state,
466
1824
  getRowId: (row) => row.id
467
1825
  });
468
- return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
469
- /* @__PURE__ */ jsx2(
1826
+ return /* @__PURE__ */ jsxs5("div", { className: "space-y-6", children: [
1827
+ /* @__PURE__ */ jsx6(
470
1828
  deps.DataGrid,
471
1829
  {
472
1830
  title: deps.labels.title,
@@ -477,10 +1835,10 @@ function createBuilderEntityManagerComponent(deps) {
477
1835
  emptyTitle: deps.labels.emptyTitle,
478
1836
  emptyDescription: deps.labels.emptyDescription,
479
1837
  onRowClick: deps.navigateToEdit,
480
- toolbarExtra: /* @__PURE__ */ jsx2(deps.AddButton, { href: deps.createHref, label: deps.labels.addLabel })
1838
+ toolbarExtra: /* @__PURE__ */ jsx6(deps.AddButton, { href: deps.createHref, label: deps.labels.addLabel })
481
1839
  }
482
1840
  ),
483
- /* @__PURE__ */ jsx2(
1841
+ /* @__PURE__ */ jsx6(
484
1842
  deps.DeleteDialog,
485
1843
  {
486
1844
  entity: pendingDelete,
@@ -500,9 +1858,9 @@ function createBuilderEntityManagerComponent(deps) {
500
1858
  }
501
1859
  function createBuilderEntityDialogManagerComponent(deps) {
502
1860
  return function BuilderEntityDialogManager() {
503
- const [editing, setEditing] = useState(null);
504
- const [isEditorOpen, setEditorOpen] = useState(false);
505
- const [pendingDelete, setPendingDelete] = useState(null);
1861
+ const [editing, setEditing] = useState2(null);
1862
+ const [isEditorOpen, setEditorOpen] = useState2(false);
1863
+ const [pendingDelete, setPendingDelete] = useState2(null);
506
1864
  const state = deps.useDataTableState({ pageSize: 20 });
507
1865
  const query = deps.hooks.useEntities(state.query);
508
1866
  const remove = deps.hooks.useDelete();
@@ -511,7 +1869,7 @@ function createBuilderEntityDialogManagerComponent(deps) {
511
1869
  setEditing(entity);
512
1870
  setEditorOpen(true);
513
1871
  }, []);
514
- const columns = useMemo(
1872
+ const columns = useMemo2(
515
1873
  () => deps.buildColumns({
516
1874
  onEdit: (entity) => openEditor(entity),
517
1875
  onReplicate: async (entity) => {
@@ -531,8 +1889,8 @@ function createBuilderEntityDialogManagerComponent(deps) {
531
1889
  state,
532
1890
  getRowId: (row) => row.id
533
1891
  });
534
- return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
535
- /* @__PURE__ */ jsx2(
1892
+ return /* @__PURE__ */ jsxs5("div", { className: "space-y-6", children: [
1893
+ /* @__PURE__ */ jsx6(
536
1894
  deps.DataGrid,
537
1895
  {
538
1896
  title: deps.labels.title,
@@ -543,10 +1901,10 @@ function createBuilderEntityDialogManagerComponent(deps) {
543
1901
  emptyTitle: deps.labels.emptyTitle,
544
1902
  emptyDescription: deps.labels.emptyDescription,
545
1903
  onRowClick: openEditor,
546
- toolbarExtra: /* @__PURE__ */ jsx2(deps.AddButton, { label: deps.labels.addLabel, onAdd: () => openEditor(null) })
1904
+ toolbarExtra: /* @__PURE__ */ jsx6(deps.AddButton, { label: deps.labels.addLabel, onAdd: () => openEditor(null) })
547
1905
  }
548
1906
  ),
549
- /* @__PURE__ */ jsx2(
1907
+ /* @__PURE__ */ jsx6(
550
1908
  deps.EditorDialog,
551
1909
  {
552
1910
  open: isEditorOpen,
@@ -554,7 +1912,7 @@ function createBuilderEntityDialogManagerComponent(deps) {
554
1912
  onClose: () => setEditorOpen(false)
555
1913
  }
556
1914
  ),
557
- /* @__PURE__ */ jsx2(
1915
+ /* @__PURE__ */ jsx6(
558
1916
  deps.DeleteDialog,
559
1917
  {
560
1918
  entity: pendingDelete,
@@ -574,9 +1932,9 @@ function createBuilderEntityDialogManagerComponent(deps) {
574
1932
  }
575
1933
 
576
1934
  // src/admin/BuilderViewDialog.tsx
577
- import { useEffect as useEffect2 } from "react";
578
- import { FormProvider as FormProvider2, useForm as useForm2 } from "react-hook-form";
579
- import { jsx as jsx3 } from "react/jsx-runtime";
1935
+ import { useEffect as useEffect3 } from "react";
1936
+ import { FormProvider as FormProvider2, useForm as useForm3 } from "react-hook-form";
1937
+ import { jsx as jsx7 } from "react/jsx-runtime";
580
1938
  function createBuilderViewDialogComponent(deps) {
581
1939
  return function BuilderViewDialog({
582
1940
  open,
@@ -587,11 +1945,11 @@ function createBuilderViewDialogComponent(deps) {
587
1945
  onSave,
588
1946
  isSaving
589
1947
  }) {
590
- const form = useForm2({
1948
+ const form = useForm3({
591
1949
  resolver: deps.resolver,
592
1950
  defaultValues: deps.emptyValues
593
1951
  });
594
- useEffect2(() => {
1952
+ useEffect3(() => {
595
1953
  if (!open) return;
596
1954
  form.reset(
597
1955
  builderViewEditorValuesFromEntity(editing, deps.emptyValues, deps.defaultPermission)
@@ -606,7 +1964,7 @@ function createBuilderViewDialogComponent(deps) {
606
1964
  deps.notifications.error(error);
607
1965
  }
608
1966
  });
609
- return /* @__PURE__ */ jsx3(deps.Shell, { open, editing, onClose, children: /* @__PURE__ */ jsx3(FormProvider2, { ...form, children: /* @__PURE__ */ jsx3("form", { onSubmit, noValidate: true, children: deps.Body({
1967
+ return /* @__PURE__ */ jsx7(deps.Shell, { open, editing, onClose, children: /* @__PURE__ */ jsx7(FormProvider2, { ...form, children: /* @__PURE__ */ jsx7("form", { onSubmit, noValidate: true, children: deps.Body({
610
1968
  form,
611
1969
  editing,
612
1970
  forms,
@@ -619,6 +1977,175 @@ function createBuilderViewDialogComponent(deps) {
619
1977
  };
620
1978
  }
621
1979
 
1980
+ // src/admin/formLayoutEditorState.ts
1981
+ import { useMemo as useMemo3 } from "react";
1982
+ import { useWatch as useWatch2 } from "react-hook-form";
1983
+ var builderFormLayoutDragDataType = "application/x-winu-builder-layout-field";
1984
+ function clampBuilderFormLayoutSpan(span, columns) {
1985
+ return Math.max(1, Math.min(columns, span));
1986
+ }
1987
+ function swapBuilderFormLayoutItems(items, from, to) {
1988
+ if (from < 0 || from >= items.length || to < 0 || to >= items.length) return items;
1989
+ const next = [...items];
1990
+ const item = next[from];
1991
+ next[from] = next[to];
1992
+ next[to] = item;
1993
+ return next;
1994
+ }
1995
+ function nextBuilderFormLayoutGroupId(groups) {
1996
+ let index = groups.length + 1;
1997
+ let id = `group${index}`;
1998
+ const ids = new Set(groups.map((group) => group.id));
1999
+ while (ids.has(id)) {
2000
+ index += 1;
2001
+ id = `group${index}`;
2002
+ }
2003
+ return id;
2004
+ }
2005
+ function serializeBuilderFormLayoutDragPayload(payload) {
2006
+ return JSON.stringify(payload);
2007
+ }
2008
+ function parseBuilderFormLayoutDragPayload(raw) {
2009
+ if (!raw) return null;
2010
+ try {
2011
+ const value = JSON.parse(raw);
2012
+ if (value.type === "palette" && typeof value.key === "string" && value.key) {
2013
+ return { type: value.type, key: value.key };
2014
+ }
2015
+ if (value.type === "layout" && typeof value.key === "string" && value.key && Number.isInteger(value.groupIndex) && Number.isInteger(value.fieldIndex) && (value.groupIndex ?? -1) >= 0 && (value.fieldIndex ?? -1) >= 0) {
2016
+ return {
2017
+ type: value.type,
2018
+ groupIndex: value.groupIndex,
2019
+ fieldIndex: value.fieldIndex,
2020
+ key: value.key
2021
+ };
2022
+ }
2023
+ return null;
2024
+ } catch {
2025
+ return null;
2026
+ }
2027
+ }
2028
+ function useBuilderFormLayoutEditor({ form, fields, subforms = [] }) {
2029
+ const layoutGroups = useWatch2({ control: form.control, name: "layoutGroups" }) ?? [];
2030
+ const layoutItems = useMemo3(
2031
+ () => [
2032
+ ...fields.map((field) => ({
2033
+ key: field.key,
2034
+ kind: "field",
2035
+ meta: field.type,
2036
+ span: builderFormEditorDefaultSpan(field)
2037
+ })),
2038
+ ...subforms.map((subform) => ({
2039
+ key: subform.key,
2040
+ kind: "subform",
2041
+ meta: "subform",
2042
+ span: 12
2043
+ }))
2044
+ ],
2045
+ [fields, subforms]
2046
+ );
2047
+ const assignedKeys = useMemo3(
2048
+ () => new Set(layoutGroups.flatMap((group) => group.fields.map((field) => field.key))),
2049
+ [layoutGroups]
2050
+ );
2051
+ const unassignedItems = useMemo3(
2052
+ () => layoutItems.filter((item) => !assignedKeys.has(item.key)),
2053
+ [assignedKeys, layoutItems]
2054
+ );
2055
+ const setGroups = (groups) => {
2056
+ form.setValue("layoutGroups", groups, { shouldDirty: true, shouldValidate: true });
2057
+ };
2058
+ const updateGroup = (groupIndex, group) => {
2059
+ setGroups(layoutGroups.map((item, index) => index === groupIndex ? group : item));
2060
+ };
2061
+ const updateGroupFields = (groupIndex, updater) => {
2062
+ const group = layoutGroups[groupIndex];
2063
+ if (!group) return;
2064
+ updateGroup(groupIndex, { ...group, fields: updater(group.fields) });
2065
+ };
2066
+ const addGroup = () => {
2067
+ setGroups([
2068
+ ...layoutGroups,
2069
+ {
2070
+ id: nextBuilderFormLayoutGroupId(layoutGroups),
2071
+ title: `Group ${layoutGroups.length + 1}`,
2072
+ columns: 12,
2073
+ fields: []
2074
+ }
2075
+ ]);
2076
+ };
2077
+ const addItemToNewGroup = (itemKey) => {
2078
+ if (assignedKeys.has(itemKey)) return;
2079
+ const item = layoutItems.find((candidate) => candidate.key === itemKey);
2080
+ if (!item) return;
2081
+ setGroups([
2082
+ ...layoutGroups,
2083
+ {
2084
+ id: nextBuilderFormLayoutGroupId(layoutGroups),
2085
+ title: layoutGroups.length === 0 ? "\u04AE\u043D\u0434\u0441\u044D\u043D" : `Group ${layoutGroups.length + 1}`,
2086
+ columns: 12,
2087
+ fields: [{ key: itemKey, span: item.span }]
2088
+ }
2089
+ ]);
2090
+ };
2091
+ const addItemToGroup = (groupIndex, itemKey, insertAt) => {
2092
+ if (assignedKeys.has(itemKey)) return;
2093
+ const item = layoutItems.find((candidate) => candidate.key === itemKey);
2094
+ const group = layoutGroups[groupIndex];
2095
+ if (!item || !group) return;
2096
+ const nextField = {
2097
+ key: itemKey,
2098
+ span: clampBuilderFormLayoutSpan(item.span, group.columns)
2099
+ };
2100
+ updateGroupFields(groupIndex, (groupFields) => {
2101
+ const next = [...groupFields];
2102
+ next.splice(insertAt ?? next.length, 0, nextField);
2103
+ return next;
2104
+ });
2105
+ };
2106
+ const moveLayoutField = (payload, targetGroupIndex, targetFieldIndex) => {
2107
+ const sourceGroup = layoutGroups[payload.groupIndex];
2108
+ const targetGroup = layoutGroups[targetGroupIndex];
2109
+ if (!sourceGroup || !targetGroup) return;
2110
+ const movingField = sourceGroup.fields[payload.fieldIndex];
2111
+ if (!movingField) return;
2112
+ const nextGroups = layoutGroups.map((group) => ({ ...group, fields: [...group.fields] }));
2113
+ nextGroups[payload.groupIndex].fields.splice(payload.fieldIndex, 1);
2114
+ let insertAt = targetFieldIndex ?? nextGroups[targetGroupIndex].fields.length;
2115
+ if (payload.groupIndex === targetGroupIndex && payload.fieldIndex < insertAt) {
2116
+ insertAt -= 1;
2117
+ }
2118
+ nextGroups[targetGroupIndex].fields.splice(insertAt, 0, {
2119
+ ...movingField,
2120
+ span: clampBuilderFormLayoutSpan(movingField.span, targetGroup.columns)
2121
+ });
2122
+ setGroups(nextGroups);
2123
+ };
2124
+ const applyDragPayload = (payload, targetGroupIndex, targetFieldIndex) => {
2125
+ if (!payload) return;
2126
+ if (payload.type === "palette") {
2127
+ addItemToGroup(targetGroupIndex, payload.key, targetFieldIndex);
2128
+ return;
2129
+ }
2130
+ moveLayoutField(payload, targetGroupIndex, targetFieldIndex);
2131
+ };
2132
+ const resetDefaultLayout = () => setGroups(builderFormEditorDefaultLayoutGroups(fields, subforms));
2133
+ return {
2134
+ assignedKeys,
2135
+ layoutGroups,
2136
+ layoutItems,
2137
+ unassignedItems,
2138
+ addGroup,
2139
+ addItemToGroup,
2140
+ addItemToNewGroup,
2141
+ applyDragPayload,
2142
+ resetDefaultLayout,
2143
+ setGroups,
2144
+ updateGroup,
2145
+ updateGroupFields
2146
+ };
2147
+ }
2148
+
622
2149
  // src/api/createBuilderApiClient.ts
623
2150
  function queryParams(query) {
624
2151
  return { ...query };
@@ -709,8 +2236,8 @@ function createBuilderApiClient(transport) {
709
2236
  }
710
2237
 
711
2238
  // src/components/DynamicForm.tsx
712
- import { useMemo as useMemo2 } from "react";
713
- import { FormProvider as FormProvider3, useForm as useForm3 } from "react-hook-form";
2239
+ import { useMemo as useMemo4 } from "react";
2240
+ import { FormProvider as FormProvider3, useForm as useForm4 } from "react-hook-form";
714
2241
 
715
2242
  // src/runtime/runtimeUtils.ts
716
2243
  function emptyFieldValue(field) {
@@ -739,16 +2266,16 @@ function formatBuilderRuntimeValue(value, locale = "mn-MN") {
739
2266
  }
740
2267
 
741
2268
  // src/components/DynamicForm.tsx
742
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
2269
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
743
2270
  function createDynamicFormComponent(deps) {
744
2271
  return function DynamicForm({ formId }) {
745
2272
  const query = deps.hooks.useRuntimeBuilderForm(formId);
746
2273
  const save = deps.hooks.useSaveRuntimeBuilderFormEntry(formId);
747
- const formValues = useMemo2(
2274
+ const formValues = useMemo4(
748
2275
  () => query.data ? createBuilderFormDefaultValues(query.data.schema) : void 0,
749
2276
  [query.data]
750
2277
  );
751
- const form = useForm3({
2278
+ const form = useForm4({
752
2279
  defaultValues: createBuilderFormDefaultValues(query.data?.schema),
753
2280
  values: formValues
754
2281
  });
@@ -762,7 +2289,7 @@ function createDynamicFormComponent(deps) {
762
2289
  }
763
2290
  });
764
2291
  if (query.isError) {
765
- return /* @__PURE__ */ jsx4(
2292
+ return /* @__PURE__ */ jsx8(
766
2293
  deps.ErrorState,
767
2294
  {
768
2295
  title: "Form \u043D\u044D\u044D\u0433\u0434\u0441\u044D\u043D\u0433\u04AF\u0439",
@@ -771,22 +2298,22 @@ function createDynamicFormComponent(deps) {
771
2298
  );
772
2299
  }
773
2300
  if (!query.data) return null;
774
- return /* @__PURE__ */ jsx4(deps.FormShell, { title: query.data.title, children: /* @__PURE__ */ jsx4(FormProvider3, { ...form, children: /* @__PURE__ */ jsxs2("form", { onSubmit, noValidate: true, className: "space-y-6", children: [
775
- /* @__PURE__ */ jsx4(deps.FormFields, { formId, schema: query.data.schema, namePrefix: "values" }),
776
- /* @__PURE__ */ jsx4(deps.SubmitButton, { isSaving: save.isPending })
2301
+ return /* @__PURE__ */ jsx8(deps.FormShell, { title: query.data.title, children: /* @__PURE__ */ jsx8(FormProvider3, { ...form, children: /* @__PURE__ */ jsxs6("form", { onSubmit, noValidate: true, className: "space-y-6", children: [
2302
+ /* @__PURE__ */ jsx8(deps.FormFields, { formId, schema: query.data.schema, namePrefix: "values" }),
2303
+ /* @__PURE__ */ jsx8(deps.SubmitButton, { isSaving: save.isPending })
777
2304
  ] }) }) });
778
2305
  };
779
2306
  }
780
2307
 
781
2308
  // src/components/DynamicList.tsx
782
- import { useMemo as useMemo3 } from "react";
783
- import { jsx as jsx5 } from "react/jsx-runtime";
2309
+ import { useMemo as useMemo5 } from "react";
2310
+ import { jsx as jsx9 } from "react/jsx-runtime";
784
2311
  function createDynamicListComponent(deps) {
785
2312
  return function DynamicList({ listId }) {
786
2313
  const query = deps.hooks.useRuntimeBuilderList(listId);
787
2314
  const state = deps.useDataTableState({ pageSize: 20 });
788
2315
  const entriesQuery = deps.hooks.useRuntimeBuilderListEntries(listId, state.query);
789
- const columns = useMemo3(() => query.data ? deps.buildColumns(query.data) : [], [query.data]);
2316
+ const columns = useMemo5(() => query.data ? deps.buildColumns(query.data) : [], [query.data]);
790
2317
  const entries = entriesQuery.data?.items ?? [];
791
2318
  const controller = deps.useDataTable({
792
2319
  columns,
@@ -796,7 +2323,7 @@ function createDynamicListComponent(deps) {
796
2323
  getRowId: (row) => row.id
797
2324
  });
798
2325
  if (query.isError) {
799
- return /* @__PURE__ */ jsx5(
2326
+ return /* @__PURE__ */ jsx9(
800
2327
  deps.ErrorState,
801
2328
  {
802
2329
  title: "List \u043D\u044D\u044D\u0433\u0434\u0441\u044D\u043D\u0433\u04AF\u0439",
@@ -804,7 +2331,7 @@ function createDynamicListComponent(deps) {
804
2331
  }
805
2332
  );
806
2333
  }
807
- return /* @__PURE__ */ jsx5(
2334
+ return /* @__PURE__ */ jsx9(
808
2335
  deps.DataGrid,
809
2336
  {
810
2337
  title: query.data?.title ?? "Dynamic List",
@@ -820,20 +2347,20 @@ function createDynamicListComponent(deps) {
820
2347
  }
821
2348
 
822
2349
  // src/components/DynamicView.tsx
823
- import { useMemo as useMemo4, useState as useState2 } from "react";
824
- import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
2350
+ import { useMemo as useMemo6, useState as useState3 } from "react";
2351
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
825
2352
  function createDynamicViewComponent(deps) {
826
2353
  return function DynamicView({ viewId }) {
827
- const [editing, setEditing] = useState2(null);
828
- const [isDialogOpen, setDialogOpen] = useState2(false);
829
- const [pendingDelete, setPendingDelete] = useState2(null);
2354
+ const [editing, setEditing] = useState3(null);
2355
+ const [isDialogOpen, setDialogOpen] = useState3(false);
2356
+ const [pendingDelete, setPendingDelete] = useState3(null);
830
2357
  const viewQuery = deps.hooks.useRuntimeBuilderView(viewId);
831
2358
  const state = deps.useDataTableState({ pageSize: 20 });
832
2359
  const entriesQuery = deps.hooks.useBuilderViewEntries(viewId, state.query);
833
2360
  const save = deps.hooks.useSaveBuilderViewEntry(viewId);
834
2361
  const remove = deps.hooks.useDeleteBuilderViewEntry(viewId);
835
2362
  const view = viewQuery.data;
836
- const columns = useMemo4(
2363
+ const columns = useMemo6(
837
2364
  () => view ? deps.buildColumns(view, {
838
2365
  onEdit: (entry) => {
839
2366
  setEditing(entry);
@@ -851,7 +2378,7 @@ function createDynamicViewComponent(deps) {
851
2378
  getRowId: (row) => row.id
852
2379
  });
853
2380
  if (viewQuery.isError) {
854
- return /* @__PURE__ */ jsx6(
2381
+ return /* @__PURE__ */ jsx10(
855
2382
  deps.ErrorState,
856
2383
  {
857
2384
  title: "View \u043D\u044D\u044D\u0433\u0434\u0441\u044D\u043D\u0433\u04AF\u0439",
@@ -859,8 +2386,8 @@ function createDynamicViewComponent(deps) {
859
2386
  }
860
2387
  );
861
2388
  }
862
- return /* @__PURE__ */ jsxs3("div", { className: "space-y-6", children: [
863
- /* @__PURE__ */ jsx6(
2389
+ return /* @__PURE__ */ jsxs7("div", { className: "space-y-6", children: [
2390
+ /* @__PURE__ */ jsx10(
864
2391
  deps.DataGrid,
865
2392
  {
866
2393
  title: view?.title ?? "Dynamic View",
@@ -874,7 +2401,7 @@ function createDynamicViewComponent(deps) {
874
2401
  setEditing(entry);
875
2402
  setDialogOpen(true);
876
2403
  },
877
- toolbarExtra: /* @__PURE__ */ jsx6(
2404
+ toolbarExtra: /* @__PURE__ */ jsx10(
878
2405
  deps.AddButton,
879
2406
  {
880
2407
  disabled: !view?.form,
@@ -886,7 +2413,7 @@ function createDynamicViewComponent(deps) {
886
2413
  )
887
2414
  }
888
2415
  ),
889
- view ? /* @__PURE__ */ jsx6(
2416
+ view ? /* @__PURE__ */ jsx10(
890
2417
  deps.EntryDialog,
891
2418
  {
892
2419
  open: isDialogOpen,
@@ -897,7 +2424,7 @@ function createDynamicViewComponent(deps) {
897
2424
  isSaving: save.isPending
898
2425
  }
899
2426
  ) : null,
900
- /* @__PURE__ */ jsx6(
2427
+ /* @__PURE__ */ jsx10(
901
2428
  deps.DeleteDialog,
902
2429
  {
903
2430
  entry: pendingDelete,
@@ -915,9 +2442,9 @@ function createDynamicViewComponent(deps) {
915
2442
  }
916
2443
 
917
2444
  // src/components/RuntimeEntryDialog.tsx
918
- import { useEffect as useEffect3, useMemo as useMemo5, useState as useState3 } from "react";
919
- import { FormProvider as FormProvider4, useForm as useForm4 } from "react-hook-form";
920
- import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
2445
+ import { useEffect as useEffect4, useMemo as useMemo7, useState as useState4 } from "react";
2446
+ import { FormProvider as FormProvider4, useForm as useForm5 } from "react-hook-form";
2447
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
921
2448
  function isDbTableSource(source) {
922
2449
  return source?.type === "dbTable" && "tableName" in source;
923
2450
  }
@@ -950,19 +2477,19 @@ function createRuntimeEntryDialogComponent(deps) {
950
2477
  onSave,
951
2478
  isSaving
952
2479
  }) {
953
- const [draftSubforms, setDraftSubforms] = useState3({});
2480
+ const [draftSubforms, setDraftSubforms] = useState4({});
954
2481
  const compositeCreate = deps.useCreateCompositeEntry?.(view.id);
955
2482
  const compositeUpdate = deps.useUpdateCompositeEntry?.(view.id);
956
- const form = useForm4({
2483
+ const form = useForm5({
957
2484
  defaultValues: createBuilderEntryDefaultValues(view, editing),
958
2485
  values: open ? createBuilderEntryDefaultValues(view, editing) : void 0
959
2486
  });
960
2487
  const mainTableName = runtimeViewTableName(view);
961
- const hasDraftRows = useMemo5(
2488
+ const hasDraftRows = useMemo7(
962
2489
  () => Object.values(draftSubforms).some((draft) => draft.rows.length > 0),
963
2490
  [draftSubforms]
964
2491
  );
965
- useEffect3(() => {
2492
+ useEffect4(() => {
966
2493
  if (open) setDraftSubforms({});
967
2494
  }, [open]);
968
2495
  const onSubmit = form.handleSubmit(async (values) => {
@@ -1014,7 +2541,7 @@ function createRuntimeEntryDialogComponent(deps) {
1014
2541
  const saving = isSaving || Boolean(compositeCreate?.isPending || compositeUpdate?.isPending);
1015
2542
  const extraItems = deps.SubformField && view.form?.schema.subforms?.length ? view.form.schema.subforms.map((subform) => ({
1016
2543
  key: subform.key,
1017
- render: () => deps.SubformField ? /* @__PURE__ */ jsx7(
2544
+ render: () => deps.SubformField ? /* @__PURE__ */ jsx11(
1018
2545
  deps.SubformField,
1019
2546
  {
1020
2547
  subform,
@@ -1029,15 +2556,15 @@ function createRuntimeEntryDialogComponent(deps) {
1029
2556
  }
1030
2557
  ) : null
1031
2558
  })) : void 0;
1032
- return /* @__PURE__ */ jsx7(
2559
+ return /* @__PURE__ */ jsx11(
1033
2560
  deps.DialogShell,
1034
2561
  {
1035
2562
  open,
1036
2563
  title: editing ? "\u041C\u04E9\u0440 \u0437\u0430\u0441\u0430\u0445" : "\u041C\u04E9\u0440 \u043D\u044D\u043C\u044D\u0445",
1037
2564
  description: view.title,
1038
2565
  onClose,
1039
- children: /* @__PURE__ */ jsx7(FormProvider4, { ...form, children: /* @__PURE__ */ jsxs4("form", { onSubmit, noValidate: true, className: deps.formClassName, children: [
1040
- view.form ? /* @__PURE__ */ jsx7(
2566
+ children: /* @__PURE__ */ jsx11(FormProvider4, { ...form, children: /* @__PURE__ */ jsxs8("form", { onSubmit, noValidate: true, className: deps.formClassName, children: [
2567
+ view.form ? /* @__PURE__ */ jsx11(
1041
2568
  deps.FormFields,
1042
2569
  {
1043
2570
  formId: view.form.id,
@@ -1048,18 +2575,13 @@ function createRuntimeEntryDialogComponent(deps) {
1048
2575
  extraItems
1049
2576
  }
1050
2577
  ) : null,
1051
- /* @__PURE__ */ jsx7(deps.Actions, { isSaving: saving, onClose })
2578
+ /* @__PURE__ */ jsx11(deps.Actions, { isSaving: saving, onClose })
1052
2579
  ] }) })
1053
2580
  }
1054
2581
  );
1055
2582
  };
1056
2583
  }
1057
2584
 
1058
- // src/components/RuntimeSubformField.tsx
1059
- import { useEffect as useEffect4, useMemo as useMemo6, useRef, useState as useState4 } from "react";
1060
- import { FormProvider as FormProvider5, useForm as useForm5 } from "react-hook-form";
1061
- import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
1062
-
1063
2585
  // src/hooks/createBuilderHooks.ts
1064
2586
  import { keepPreviousData, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
1065
2587
  function createBuilderKeys() {
@@ -1075,6 +2597,7 @@ function createBuilderKeys() {
1075
2597
  listOptions: () => [...builderKeys.lists, "options"],
1076
2598
  views: ["builder", "views"],
1077
2599
  viewList: (query) => [...builderKeys.views, "list", query],
2600
+ view: (id) => [...builderKeys.views, "detail", id],
1078
2601
  runtimeForm: (id) => ["builder", "runtime", "form", id],
1079
2602
  runtimeFieldOptions: (formId, fieldKey, query) => [...builderKeys.runtimeForm(formId), "fields", fieldKey, "options", query],
1080
2603
  runtimeFormEntries: (id) => [...builderKeys.runtimeForm(id), "entries"],
@@ -1193,6 +2716,13 @@ function createBuilderHooks(client) {
1193
2716
  placeholderData: keepPreviousData
1194
2717
  });
1195
2718
  }
2719
+ function useBuilderView(id) {
2720
+ return useQuery({
2721
+ queryKey: builderKeys.view(id ?? ""),
2722
+ queryFn: () => client.getBuilderView(id ?? ""),
2723
+ enabled: Boolean(id)
2724
+ });
2725
+ }
1196
2726
  function useSaveBuilderView() {
1197
2727
  const queryClient = useQueryClient();
1198
2728
  return useMutation({
@@ -1333,6 +2863,7 @@ function createBuilderHooks(client) {
1333
2863
  useBuilderListOptions,
1334
2864
  useBuilderLists,
1335
2865
  useBuilderViewEntries,
2866
+ useBuilderView,
1336
2867
  useBuilderViews,
1337
2868
  useDeleteBuilderForm,
1338
2869
  useDeleteBuilderList,
@@ -1357,10 +2888,59 @@ function createBuilderHooks(client) {
1357
2888
  };
1358
2889
  }
1359
2890
 
2891
+ // src/integration/createBuilderIntegration.ts
2892
+ function normalizeBasePath(path) {
2893
+ const trimmed = path.trim().replace(/\/+$/g, "");
2894
+ if (!trimmed) return "/builder";
2895
+ return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
2896
+ }
2897
+ function withQuery(path, query) {
2898
+ const params = new URLSearchParams();
2899
+ for (const [key, value] of Object.entries(query)) {
2900
+ if (value) params.set(key, value);
2901
+ }
2902
+ const queryString = params.toString();
2903
+ return queryString ? `${path}?${queryString}` : path;
2904
+ }
2905
+ function createBuilderIntegration(config) {
2906
+ const adminBasePath = normalizeBasePath(config.adminBasePath ?? "/admin/dev/builder");
2907
+ const runtimeBasePath = normalizeBasePath(config.runtimeBasePath ?? "/admin/winu");
2908
+ const api = createBuilderApiClient(config.transport);
2909
+ const hooks = createBuilderHooks(api);
2910
+ const formsPath = `${adminBasePath}/forms`;
2911
+ const listsPath = `${adminBasePath}/lists`;
2912
+ const routes = {
2913
+ adminBasePath,
2914
+ runtimeBasePath,
2915
+ adminHomePath: adminBasePath,
2916
+ formsPath,
2917
+ newFormPath: `${formsPath}/new`,
2918
+ editFormPath: (id) => withQuery(`${formsPath}/edit`, { id }),
2919
+ listsPath,
2920
+ newListPath: `${listsPath}/new`,
2921
+ editListPath: (id) => withQuery(`${listsPath}/edit`, { id }),
2922
+ viewsPath: `${adminBasePath}/views`
2923
+ };
2924
+ const adminPages = config.adminPages ? createBuilderAdminPages({
2925
+ ...config.adminPages,
2926
+ hooks,
2927
+ routes: {
2928
+ adminBasePath,
2929
+ runtimeBasePath
2930
+ }
2931
+ }) : void 0;
2932
+ return {
2933
+ api,
2934
+ hooks,
2935
+ routes,
2936
+ adminPages
2937
+ };
2938
+ }
2939
+
1360
2940
  // src/index.ts
1361
2941
  var builderUiPackage = {
1362
2942
  name: "@duke333/builder-ui",
1363
- version: "0.1.0"
2943
+ version: "0.1.1"
1364
2944
  };
1365
2945
  export {
1366
2946
  BUILDER_EDITOR_NONE_VALUE,
@@ -1371,19 +2951,27 @@ export {
1371
2951
  builderFormEditorFieldsFromTable,
1372
2952
  builderFormEditorInputFromValues,
1373
2953
  builderFormEditorValuesFromEntity,
2954
+ builderFormLayoutDragDataType,
1374
2955
  builderListEditorColumnsFromTable,
1375
2956
  builderListEditorInputFromValues,
1376
2957
  builderListEditorValuesFromEntity,
1377
2958
  builderUiPackage,
1378
2959
  builderViewEditorInputFromValues,
1379
2960
  builderViewEditorValuesFromEntity,
2961
+ clampBuilderFormLayoutSpan,
2962
+ createBuilderAdminPages,
1380
2963
  createBuilderApiClient,
2964
+ createBuilderDefaultEntityManagerComponent,
2965
+ createBuilderDefaultFormEditorBody,
2966
+ createBuilderDefaultListEditorBody,
2967
+ createBuilderDefaultViewEditorPage,
1381
2968
  createBuilderEntityDialogManagerComponent,
1382
2969
  createBuilderEntityManagerComponent,
1383
2970
  createBuilderEntryDefaultValues,
1384
2971
  createBuilderFormDefaultValues,
1385
2972
  createBuilderFormEditorPageComponent,
1386
2973
  createBuilderHooks,
2974
+ createBuilderIntegration,
1387
2975
  createBuilderKeys,
1388
2976
  createBuilderListEditorPageComponent,
1389
2977
  createBuilderViewDialogComponent,
@@ -1392,6 +2980,11 @@ export {
1392
2980
  createDynamicViewComponent,
1393
2981
  createRuntimeEntryDialogComponent,
1394
2982
  formatBuilderRuntimeValue,
1395
- getBuilderEditorTriggerRoute
2983
+ getBuilderEditorTriggerRoute,
2984
+ nextBuilderFormLayoutGroupId,
2985
+ parseBuilderFormLayoutDragPayload,
2986
+ serializeBuilderFormLayoutDragPayload,
2987
+ swapBuilderFormLayoutItems,
2988
+ useBuilderFormLayoutEditor
1396
2989
  };
1397
2990
  //# sourceMappingURL=index.js.map