@duke333/builder-ui 0.1.1 → 0.1.4

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.
@@ -87,6 +87,1801 @@ function createBuilderApiClient(transport) {
87
87
  };
88
88
  }
89
89
 
90
+ // src/admin/editorState.ts
91
+ var BUILDER_EDITOR_NONE_VALUE = "__none";
92
+ function builderEditorOptionsToText(options) {
93
+ return (options ?? []).map((option) => `${option.label}:${option.value}`).join("\n");
94
+ }
95
+ function builderEditorTextToOptions(value) {
96
+ const rows = (value ?? "").split("\n").map((line) => line.trim()).filter(Boolean);
97
+ if (rows.length === 0) return void 0;
98
+ return rows.map((line) => {
99
+ const [label, ...rest] = line.split(":");
100
+ return { label: label.trim(), value: rest.join(":").trim() || label.trim() };
101
+ });
102
+ }
103
+ function builderEditorSearchFieldsToText(fields) {
104
+ return (fields ?? []).join("\n");
105
+ }
106
+ function builderEditorTextToSearchFields(value) {
107
+ const fields = (value ?? "").split("\n").map((line) => line.trim()).filter(Boolean);
108
+ return fields.length > 0 ? fields : void 0;
109
+ }
110
+ function getBuilderEditorTriggerRoute(config) {
111
+ const route = config.route ?? config.path ?? config.url;
112
+ return typeof route === "string" ? route : "";
113
+ }
114
+ function valuesFromTriggers(triggers) {
115
+ return triggers.filter((trigger) => trigger.action === "callApi" || trigger.action === "webhook").map((trigger) => ({
116
+ event: trigger.event,
117
+ route: getBuilderEditorTriggerRoute(trigger.config),
118
+ enabled: trigger.enabled,
119
+ blocking: trigger.blocking
120
+ })).filter((trigger) => trigger.route);
121
+ }
122
+ function valuesFromFieldTriggers(triggers) {
123
+ return (triggers ?? []).map((trigger) => ({
124
+ event: trigger.event,
125
+ action: trigger.action,
126
+ enabled: trigger.enabled,
127
+ debounceMs: trigger.debounceMs
128
+ }));
129
+ }
130
+ function clampLayoutSpan(span, columns) {
131
+ if (!span || Number.isNaN(span)) return columns;
132
+ return Math.max(1, Math.min(columns, Math.trunc(span)));
133
+ }
134
+ function builderFormEditorDefaultSpan(field) {
135
+ if (field.type === "textarea" || field.type === "checkbox") return 12;
136
+ return 6;
137
+ }
138
+ function builderFormEditorDefaultLayoutGroups(fields, subforms = []) {
139
+ return [
140
+ {
141
+ id: "main",
142
+ title: "\u04AE\u043D\u0434\u0441\u044D\u043D",
143
+ columns: 12,
144
+ fields: [
145
+ ...fields.map((field) => ({
146
+ key: field.key,
147
+ span: builderFormEditorDefaultSpan(field)
148
+ })),
149
+ ...subforms.map((subform) => ({ key: subform.key, span: 12 }))
150
+ ]
151
+ }
152
+ ];
153
+ }
154
+ function normalizeBuilderFormEditorLayoutGroups(groups, fields, subforms = []) {
155
+ if (fields.length === 0 && subforms.length === 0) return [];
156
+ const layoutItems = [
157
+ ...fields.map((field) => ({
158
+ key: field.key,
159
+ span: builderFormEditorDefaultSpan(field)
160
+ })),
161
+ ...subforms.map((subform) => ({ key: subform.key, span: 12 }))
162
+ ];
163
+ const itemKeys = new Set(layoutItems.map((item) => item.key));
164
+ const usedKeys = /* @__PURE__ */ new Set();
165
+ const normalized = (groups ?? []).map((group, index) => {
166
+ const columns = group.columns === 6 || group.columns === 8 ? group.columns : 12;
167
+ const groupFields = (group.fields ?? []).filter((field) => itemKeys.has(field.key) && !usedKeys.has(field.key)).map((field) => {
168
+ usedKeys.add(field.key);
169
+ return { key: field.key, span: clampLayoutSpan(field.span, columns) };
170
+ });
171
+ return {
172
+ id: group.id || `group${index + 1}`,
173
+ title: group.title || `Group ${index + 1}`,
174
+ columns,
175
+ fields: groupFields
176
+ };
177
+ }).filter((group) => group.fields.length > 0 || group.title.trim());
178
+ const missingFields = layoutItems.filter((item) => !usedKeys.has(item.key));
179
+ if (normalized.length === 0) {
180
+ return builderFormEditorDefaultLayoutGroups(fields, subforms);
181
+ }
182
+ if (missingFields.length > 0) {
183
+ normalized.push({
184
+ id: "ungrouped",
185
+ title: "\u0411\u0443\u0441\u0430\u0434",
186
+ columns: 12,
187
+ fields: missingFields
188
+ });
189
+ }
190
+ return normalized;
191
+ }
192
+ function builderFormEditorValuesFromEntity(form, emptyValues) {
193
+ if (!form) return emptyValues;
194
+ const source = form.source;
195
+ const fields = form.schema.fields.map((field) => ({
196
+ key: field.key,
197
+ label: field.label,
198
+ type: field.type,
199
+ required: field.required,
200
+ hidden: field.hidden ?? false,
201
+ disabled: field.disabled ?? false,
202
+ placeholder: field.placeholder ?? "",
203
+ optionsText: builderEditorOptionsToText(field.options),
204
+ optionsMode: field.optionsSource?.type === "dbTable" ? "table" : "static",
205
+ optionsTable: field.optionsSource?.type === "dbTable" ? field.optionsSource.tableName : BUILDER_EDITOR_NONE_VALUE,
206
+ optionsValueField: field.optionsSource?.type === "dbTable" ? field.optionsSource.valueField : BUILDER_EDITOR_NONE_VALUE,
207
+ optionsLabelField: field.optionsSource?.type === "dbTable" ? field.optionsSource.labelField : BUILDER_EDITOR_NONE_VALUE,
208
+ optionsSearchFieldsText: field.optionsSource?.type === "dbTable" ? builderEditorSearchFieldsToText(field.optionsSource.searchFields) : "",
209
+ optionsDependsOn: field.optionsSource?.type === "dbTable" ? field.optionsSource.filterBy?.valueFromField ?? "" : "",
210
+ optionsFilterField: field.optionsSource?.type === "dbTable" ? field.optionsSource.filterBy?.field ?? "" : "",
211
+ triggers: valuesFromFieldTriggers(field.triggers)
212
+ }));
213
+ const subforms = (form.schema.subforms ?? []).map((subform) => ({
214
+ key: subform.key,
215
+ label: subform.label,
216
+ viewId: subform.viewId,
217
+ parentField: subform.parentField,
218
+ childField: subform.childField,
219
+ buttonLabel: subform.buttonLabel ?? "",
220
+ icon: subform.icon ?? ""
221
+ }));
222
+ return {
223
+ title: form.title,
224
+ description: form.description ?? "",
225
+ sourceType: source?.type ?? "none",
226
+ sourceTable: source?.type === "dbTable" ? source.tableName : BUILDER_EDITOR_NONE_VALUE,
227
+ apiBasePath: source?.type === "apiEndpoint" ? source.basePath : "",
228
+ fields,
229
+ layoutGroups: normalizeBuilderFormEditorLayoutGroups(
230
+ form.schema.layout?.groups,
231
+ fields,
232
+ subforms
233
+ ),
234
+ subforms,
235
+ triggers: valuesFromTriggers(form.triggers),
236
+ version: form.version
237
+ };
238
+ }
239
+ function builderListEditorValuesFromEntity(list, emptyValues) {
240
+ if (!list) return emptyValues;
241
+ const source = list.source;
242
+ return {
243
+ title: list.title,
244
+ description: list.description ?? "",
245
+ sourceType: source?.type ?? "none",
246
+ sourceTable: source?.type === "dbTable" ? source.tableName : BUILDER_EDITOR_NONE_VALUE,
247
+ apiBasePath: source?.type === "apiEndpoint" ? source.basePath : "",
248
+ columns: list.schema.columns.map((column) => ({
249
+ key: column.key,
250
+ label: column.label,
251
+ sortable: column.sortable,
252
+ align: column.align
253
+ })),
254
+ triggers: valuesFromTriggers(list.triggers),
255
+ version: list.version
256
+ };
257
+ }
258
+ function builderViewEditorValuesFromEntity(view, emptyValues, defaultPermission = "") {
259
+ if (!view) return emptyValues;
260
+ return {
261
+ title: view.title,
262
+ description: view.description ?? "",
263
+ formId: view.formId ?? BUILDER_EDITOR_NONE_VALUE,
264
+ listId: view.listId ?? BUILDER_EDITOR_NONE_VALUE,
265
+ permissionRead: view.permissionRead || defaultPermission,
266
+ permissionWrite: view.permissionWrite || defaultPermission,
267
+ triggersText: JSON.stringify(view.triggers ?? [], null, 2),
268
+ version: view.version
269
+ };
270
+ }
271
+ function builderFormEditorFieldsFromTable(table) {
272
+ return table.columns.map((column) => ({
273
+ key: column.name,
274
+ label: column.label,
275
+ type: column.fieldType,
276
+ required: !column.nullable,
277
+ hidden: false,
278
+ disabled: false,
279
+ placeholder: column.label,
280
+ optionsText: "",
281
+ optionsMode: "static",
282
+ optionsTable: BUILDER_EDITOR_NONE_VALUE,
283
+ optionsValueField: BUILDER_EDITOR_NONE_VALUE,
284
+ optionsLabelField: BUILDER_EDITOR_NONE_VALUE,
285
+ optionsSearchFieldsText: "",
286
+ optionsDependsOn: "",
287
+ optionsFilterField: "",
288
+ triggers: []
289
+ }));
290
+ }
291
+ function builderListEditorColumnsFromTable(table) {
292
+ return table.columns.map((column) => ({
293
+ key: column.name,
294
+ label: column.label,
295
+ sortable: column.sortable,
296
+ align: column.fieldType === "number" || column.fieldType === "money" ? "right" : "left"
297
+ }));
298
+ }
299
+ function builderFormEditorInputFromValues(values) {
300
+ const source = values.sourceType === "dbTable" && values.sourceTable !== BUILDER_EDITOR_NONE_VALUE ? { type: "dbTable", tableName: values.sourceTable } : values.sourceType === "apiEndpoint" && values.apiBasePath ? { type: "apiEndpoint", basePath: values.apiBasePath } : null;
301
+ return {
302
+ title: values.title,
303
+ description: values.description || null,
304
+ source,
305
+ schema: {
306
+ fields: values.fields.map((field) => ({
307
+ key: field.key,
308
+ label: field.label,
309
+ type: field.type,
310
+ required: field.required,
311
+ hidden: field.hidden,
312
+ disabled: field.disabled,
313
+ placeholder: field.placeholder?.trim() || field.label,
314
+ options: field.type === "select" && field.optionsMode === "static" ? builderEditorTextToOptions(field.optionsText) : void 0,
315
+ optionsSource: field.type === "select" && field.optionsMode === "table" && field.optionsTable !== BUILDER_EDITOR_NONE_VALUE && field.optionsValueField !== BUILDER_EDITOR_NONE_VALUE && field.optionsLabelField !== BUILDER_EDITOR_NONE_VALUE ? {
316
+ type: "dbTable",
317
+ tableName: field.optionsTable,
318
+ valueField: field.optionsValueField,
319
+ labelField: field.optionsLabelField,
320
+ searchFields: builderEditorTextToSearchFields(field.optionsSearchFieldsText),
321
+ filterBy: field.optionsDependsOn && field.optionsFilterField ? {
322
+ field: field.optionsFilterField,
323
+ valueFromField: field.optionsDependsOn
324
+ } : void 0
325
+ } : void 0,
326
+ triggers: field.triggers.map((trigger) => ({
327
+ event: trigger.event,
328
+ action: trigger.action,
329
+ enabled: trigger.enabled,
330
+ debounceMs: trigger.debounceMs,
331
+ config: {}
332
+ }))
333
+ })),
334
+ layout: {
335
+ groups: normalizeBuilderFormEditorLayoutGroups(
336
+ values.layoutGroups,
337
+ values.fields,
338
+ values.subforms ?? []
339
+ )
340
+ },
341
+ subforms: (values.subforms ?? []).filter(
342
+ (subform) => subform.key && subform.label && subform.viewId !== BUILDER_EDITOR_NONE_VALUE && subform.parentField && subform.childField
343
+ ).map((subform) => ({
344
+ key: subform.key,
345
+ label: subform.label,
346
+ viewId: subform.viewId,
347
+ parentField: subform.parentField,
348
+ childField: subform.childField,
349
+ buttonLabel: subform.buttonLabel?.trim() || void 0,
350
+ icon: subform.icon?.trim() || void 0
351
+ }))
352
+ },
353
+ triggers: values.triggers.map((trigger) => ({
354
+ event: trigger.event,
355
+ action: "callApi",
356
+ enabled: trigger.enabled,
357
+ blocking: trigger.blocking,
358
+ config: { route: trigger.route }
359
+ })),
360
+ version: values.version
361
+ };
362
+ }
363
+ function builderListEditorInputFromValues(values) {
364
+ const source = values.sourceType === "dbTable" && values.sourceTable !== BUILDER_EDITOR_NONE_VALUE ? { type: "dbTable", tableName: values.sourceTable } : values.sourceType === "apiEndpoint" && values.apiBasePath ? { type: "apiEndpoint", basePath: values.apiBasePath } : null;
365
+ return {
366
+ title: values.title,
367
+ description: values.description || null,
368
+ source,
369
+ schema: { columns: values.columns },
370
+ triggers: values.triggers.map((trigger) => ({
371
+ event: trigger.event,
372
+ action: "callApi",
373
+ enabled: trigger.enabled,
374
+ blocking: trigger.blocking,
375
+ config: { route: trigger.route }
376
+ })),
377
+ version: values.version
378
+ };
379
+ }
380
+ function builderViewEditorInputFromValues(values, parseTriggers) {
381
+ return {
382
+ title: values.title,
383
+ description: values.description || null,
384
+ formId: values.formId === BUILDER_EDITOR_NONE_VALUE ? null : values.formId,
385
+ listId: values.listId === BUILDER_EDITOR_NONE_VALUE ? null : values.listId,
386
+ permissionRead: values.permissionRead || null,
387
+ permissionWrite: values.permissionWrite || null,
388
+ triggers: values.triggersText?.trim() ? parseTriggers(values.triggersText) : [],
389
+ version: values.version
390
+ };
391
+ }
392
+
393
+ // src/admin/BuilderDefaultEditorBodies.tsx
394
+ import { jsx, jsxs } from "react/jsx-runtime";
395
+ var styles = {
396
+ page: {
397
+ display: "grid",
398
+ gap: "1rem"
399
+ },
400
+ header: {
401
+ display: "flex",
402
+ justifyContent: "space-between",
403
+ gap: "1rem",
404
+ alignItems: "flex-start",
405
+ flexWrap: "wrap"
406
+ },
407
+ title: {
408
+ margin: 0,
409
+ fontSize: "1.5rem",
410
+ fontWeight: 650
411
+ },
412
+ description: {
413
+ margin: "0.25rem 0 0",
414
+ color: "var(--muted-foreground, #64748b)"
415
+ },
416
+ actions: {
417
+ display: "flex",
418
+ gap: "0.5rem",
419
+ flexWrap: "wrap"
420
+ },
421
+ section: {
422
+ border: "1px solid var(--border, #d4d4d8)",
423
+ borderRadius: "0.5rem",
424
+ background: "var(--background, #fff)",
425
+ overflow: "hidden"
426
+ },
427
+ sectionHeader: {
428
+ display: "flex",
429
+ justifyContent: "space-between",
430
+ gap: "1rem",
431
+ alignItems: "center",
432
+ padding: "0.75rem",
433
+ borderBottom: "1px solid var(--border, #d4d4d8)",
434
+ flexWrap: "wrap"
435
+ },
436
+ sectionTitle: {
437
+ margin: 0,
438
+ fontSize: "1rem",
439
+ fontWeight: 650
440
+ },
441
+ grid: {
442
+ display: "grid",
443
+ gap: "0.75rem",
444
+ padding: "0.75rem"
445
+ },
446
+ twoColumn: {
447
+ display: "grid",
448
+ gridTemplateColumns: "repeat(auto-fit, minmax(14rem, 1fr))",
449
+ gap: "0.75rem"
450
+ },
451
+ row: {
452
+ display: "grid",
453
+ gridTemplateColumns: "repeat(auto-fit, minmax(10rem, 1fr))",
454
+ gap: "0.5rem",
455
+ padding: "0.75rem",
456
+ borderBottom: "1px solid var(--border, #d4d4d8)"
457
+ },
458
+ field: {
459
+ display: "grid",
460
+ gap: "0.25rem",
461
+ fontSize: "0.875rem"
462
+ },
463
+ label: {
464
+ color: "var(--muted-foreground, #64748b)",
465
+ fontSize: "0.75rem",
466
+ fontWeight: 600
467
+ },
468
+ input: {
469
+ minWidth: 0,
470
+ border: "1px solid var(--border, #d4d4d8)",
471
+ borderRadius: "0.375rem",
472
+ padding: "0.5rem 0.625rem",
473
+ background: "var(--background, #fff)",
474
+ color: "inherit"
475
+ },
476
+ textarea: {
477
+ minWidth: 0,
478
+ minHeight: "5rem",
479
+ border: "1px solid var(--border, #d4d4d8)",
480
+ borderRadius: "0.375rem",
481
+ padding: "0.5rem 0.625rem",
482
+ background: "var(--background, #fff)",
483
+ color: "inherit",
484
+ resize: "vertical"
485
+ },
486
+ checkboxField: {
487
+ display: "flex",
488
+ alignItems: "center",
489
+ gap: "0.5rem",
490
+ fontSize: "0.875rem",
491
+ minHeight: "2.25rem"
492
+ },
493
+ button: {
494
+ border: "1px solid var(--border, #d4d4d8)",
495
+ borderRadius: "0.375rem",
496
+ background: "var(--background, #fff)",
497
+ color: "inherit",
498
+ cursor: "pointer",
499
+ padding: "0.5rem 0.75rem",
500
+ textDecoration: "none",
501
+ fontSize: "0.875rem"
502
+ },
503
+ primaryButton: {
504
+ border: "1px solid var(--primary, #18181b)",
505
+ borderRadius: "0.375rem",
506
+ background: "var(--primary, #18181b)",
507
+ color: "var(--primary-foreground, #fff)",
508
+ cursor: "pointer",
509
+ padding: "0.5rem 0.75rem",
510
+ textDecoration: "none",
511
+ fontSize: "0.875rem"
512
+ }
513
+ };
514
+ function optionItems(options) {
515
+ return options.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value));
516
+ }
517
+ function createBuilderDefaultFormEditorBody(options) {
518
+ return function BuilderDefaultFormEditorBody({
519
+ formId,
520
+ form,
521
+ fieldArray,
522
+ subformArray,
523
+ triggerArray,
524
+ fields,
525
+ sourceType,
526
+ sourceTable,
527
+ tables,
528
+ emptyField,
529
+ emptyTrigger,
530
+ isSaving,
531
+ importFieldsFromTable
532
+ }) {
533
+ return /* @__PURE__ */ jsxs("div", { style: styles.page, children: [
534
+ /* @__PURE__ */ jsxs("div", { style: styles.header, children: [
535
+ /* @__PURE__ */ jsxs("div", { children: [
536
+ /* @__PURE__ */ jsx("h1", { style: styles.title, children: formId ? "Edit Dynamic Form" : "New Dynamic Form" }),
537
+ /* @__PURE__ */ jsx("p", { style: styles.description, children: "Configure fields, source mapping, layout, and triggers." })
538
+ ] }),
539
+ /* @__PURE__ */ jsxs("div", { style: styles.actions, children: [
540
+ /* @__PURE__ */ jsx("a", { href: options.backHref, style: styles.button, children: "Cancel" }),
541
+ /* @__PURE__ */ jsx("button", { type: "submit", disabled: isSaving, style: styles.primaryButton, children: isSaving ? "Saving..." : "Save" })
542
+ ] })
543
+ ] }),
544
+ /* @__PURE__ */ jsxs("section", { style: styles.section, children: [
545
+ /* @__PURE__ */ jsx("div", { style: styles.sectionHeader, children: /* @__PURE__ */ jsx("h2", { style: styles.sectionTitle, children: "Details" }) }),
546
+ /* @__PURE__ */ jsxs("div", { style: { ...styles.grid, ...styles.twoColumn }, children: [
547
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
548
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Title" }),
549
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register("title", { required: true }) })
550
+ ] }),
551
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
552
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Description" }),
553
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register("description") })
554
+ ] }),
555
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
556
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Datasource" }),
557
+ /* @__PURE__ */ jsxs("select", { style: styles.input, ...form.register("sourceType"), children: [
558
+ /* @__PURE__ */ jsx("option", { value: "none", children: "None" }),
559
+ /* @__PURE__ */ jsx("option", { value: "dbTable", children: "DB table" }),
560
+ /* @__PURE__ */ jsx("option", { value: "apiEndpoint", children: "API endpoint" })
561
+ ] })
562
+ ] }),
563
+ sourceType === "dbTable" ? /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
564
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Table" }),
565
+ /* @__PURE__ */ jsxs("select", { style: styles.input, ...form.register("sourceTable"), children: [
566
+ /* @__PURE__ */ jsx("option", { value: BUILDER_EDITOR_NONE_VALUE, children: "Select table" }),
567
+ tables.map((table) => /* @__PURE__ */ jsx("option", { value: table.name, children: table.label ?? table.name }, table.name))
568
+ ] })
569
+ ] }) : null,
570
+ sourceType === "apiEndpoint" ? /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
571
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "API base path" }),
572
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register("apiBasePath") })
573
+ ] }) : null,
574
+ sourceType === "dbTable" ? /* @__PURE__ */ jsx("div", { style: { ...styles.field, justifyContent: "end" }, children: /* @__PURE__ */ jsx(
575
+ "button",
576
+ {
577
+ type: "button",
578
+ style: styles.button,
579
+ disabled: sourceTable === BUILDER_EDITOR_NONE_VALUE,
580
+ onClick: importFieldsFromTable,
581
+ children: "Import fields"
582
+ }
583
+ ) }) : null
584
+ ] })
585
+ ] }),
586
+ /* @__PURE__ */ jsxs("section", { style: styles.section, children: [
587
+ /* @__PURE__ */ jsxs("div", { style: styles.sectionHeader, children: [
588
+ /* @__PURE__ */ jsxs("div", { children: [
589
+ /* @__PURE__ */ jsx("h2", { style: styles.sectionTitle, children: "Fields" }),
590
+ /* @__PURE__ */ jsx("p", { style: styles.description, children: "Runtime form inputs." })
591
+ ] }),
592
+ /* @__PURE__ */ jsx(
593
+ "button",
594
+ {
595
+ type: "button",
596
+ style: styles.button,
597
+ onClick: () => {
598
+ const key = `field${fieldArray.fields.length + 1}`;
599
+ fieldArray.append({
600
+ ...emptyField,
601
+ key,
602
+ label: `Field ${fieldArray.fields.length + 1}`,
603
+ type: options.fieldTypeOptions[0]?.value
604
+ });
605
+ },
606
+ children: "Add field"
607
+ }
608
+ )
609
+ ] }),
610
+ fieldArray.fields.map((field, index) => /* @__PURE__ */ jsxs("div", { style: styles.row, children: [
611
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
612
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Key" }),
613
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`fields.${index}.key`) })
614
+ ] }),
615
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
616
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Label" }),
617
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`fields.${index}.label`) })
618
+ ] }),
619
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
620
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Type" }),
621
+ /* @__PURE__ */ jsx(
622
+ "select",
623
+ {
624
+ style: styles.input,
625
+ ...form.register(`fields.${index}.type`),
626
+ children: optionItems(options.fieldTypeOptions)
627
+ }
628
+ )
629
+ ] }),
630
+ /* @__PURE__ */ jsxs("label", { style: styles.checkboxField, children: [
631
+ /* @__PURE__ */ jsx("input", { type: "checkbox", ...form.register(`fields.${index}.required`) }),
632
+ "Required"
633
+ ] }),
634
+ /* @__PURE__ */ jsxs("label", { style: styles.checkboxField, children: [
635
+ /* @__PURE__ */ jsx("input", { type: "checkbox", ...form.register(`fields.${index}.hidden`) }),
636
+ "Hidden"
637
+ ] }),
638
+ /* @__PURE__ */ jsxs("label", { style: styles.checkboxField, children: [
639
+ /* @__PURE__ */ jsx("input", { type: "checkbox", ...form.register(`fields.${index}.disabled`) }),
640
+ "Disabled"
641
+ ] }),
642
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
643
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Placeholder" }),
644
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`fields.${index}.placeholder`) })
645
+ ] }),
646
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
647
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Static options" }),
648
+ /* @__PURE__ */ jsx("textarea", { style: styles.textarea, ...form.register(`fields.${index}.optionsText`) })
649
+ ] }),
650
+ /* @__PURE__ */ jsx("div", { style: styles.actions, children: /* @__PURE__ */ jsx(
651
+ "button",
652
+ {
653
+ type: "button",
654
+ style: styles.button,
655
+ onClick: () => fieldArray.remove(index),
656
+ disabled: fieldArray.fields.length <= 1,
657
+ children: "Remove"
658
+ }
659
+ ) })
660
+ ] }, field.id))
661
+ ] }),
662
+ /* @__PURE__ */ jsxs("section", { style: styles.section, children: [
663
+ /* @__PURE__ */ jsxs("div", { style: styles.sectionHeader, children: [
664
+ /* @__PURE__ */ jsxs("div", { children: [
665
+ /* @__PURE__ */ jsx("h2", { style: styles.sectionTitle, children: "Layout" }),
666
+ /* @__PURE__ */ jsx("p", { style: styles.description, children: "Default layout can be regenerated from current fields." })
667
+ ] }),
668
+ /* @__PURE__ */ jsx(
669
+ "button",
670
+ {
671
+ type: "button",
672
+ style: styles.button,
673
+ onClick: () => form.setValue("layoutGroups", builderFormEditorDefaultLayoutGroups(fields), {
674
+ shouldDirty: true
675
+ }),
676
+ children: "Reset layout"
677
+ }
678
+ )
679
+ ] }),
680
+ form.watch("layoutGroups").map((group, groupIndex) => /* @__PURE__ */ jsxs("div", { style: styles.row, children: [
681
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
682
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Group title" }),
683
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`layoutGroups.${groupIndex}.title`) })
684
+ ] }),
685
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
686
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Columns" }),
687
+ /* @__PURE__ */ jsxs(
688
+ "select",
689
+ {
690
+ style: styles.input,
691
+ value: group.columns,
692
+ onChange: (event) => form.setValue(
693
+ `layoutGroups.${groupIndex}.columns`,
694
+ Number(event.target.value),
695
+ { shouldDirty: true }
696
+ ),
697
+ children: [
698
+ /* @__PURE__ */ jsx("option", { value: 6, children: "6" }),
699
+ /* @__PURE__ */ jsx("option", { value: 8, children: "8" }),
700
+ /* @__PURE__ */ jsx("option", { value: 12, children: "12" })
701
+ ]
702
+ }
703
+ )
704
+ ] }),
705
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
706
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Fields" }),
707
+ /* @__PURE__ */ jsx(
708
+ "input",
709
+ {
710
+ style: styles.input,
711
+ value: group.fields.map((item) => item.key).join(", "),
712
+ readOnly: true
713
+ }
714
+ )
715
+ ] })
716
+ ] }, group.id))
717
+ ] }),
718
+ /* @__PURE__ */ jsxs("section", { style: styles.section, children: [
719
+ /* @__PURE__ */ jsxs("div", { style: styles.sectionHeader, children: [
720
+ /* @__PURE__ */ jsxs("div", { children: [
721
+ /* @__PURE__ */ jsx("h2", { style: styles.sectionTitle, children: "Subforms" }),
722
+ /* @__PURE__ */ jsx("p", { style: styles.description, children: "Optional nested runtime view bindings." })
723
+ ] }),
724
+ /* @__PURE__ */ jsx(
725
+ "button",
726
+ {
727
+ type: "button",
728
+ style: styles.button,
729
+ onClick: () => subformArray.append({
730
+ key: `subform${subformArray.fields.length + 1}`,
731
+ label: `Subform ${subformArray.fields.length + 1}`,
732
+ viewId: BUILDER_EDITOR_NONE_VALUE,
733
+ parentField: "id",
734
+ childField: "parentId",
735
+ buttonLabel: "",
736
+ icon: ""
737
+ }),
738
+ children: "Add subform"
739
+ }
740
+ )
741
+ ] }),
742
+ subformArray.fields.map((subform, index) => /* @__PURE__ */ jsxs("div", { style: styles.row, children: [
743
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
744
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Key" }),
745
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`subforms.${index}.key`) })
746
+ ] }),
747
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
748
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Label" }),
749
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`subforms.${index}.label`) })
750
+ ] }),
751
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
752
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "View ID" }),
753
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`subforms.${index}.viewId`) })
754
+ ] }),
755
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
756
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Parent field" }),
757
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`subforms.${index}.parentField`) })
758
+ ] }),
759
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
760
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Child field" }),
761
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`subforms.${index}.childField`) })
762
+ ] }),
763
+ /* @__PURE__ */ jsx("button", { type: "button", style: styles.button, onClick: () => subformArray.remove(index), children: "Remove" })
764
+ ] }, subform.id))
765
+ ] }),
766
+ /* @__PURE__ */ jsxs("section", { style: styles.section, children: [
767
+ /* @__PURE__ */ jsxs("div", { style: styles.sectionHeader, children: [
768
+ /* @__PURE__ */ jsxs("div", { children: [
769
+ /* @__PURE__ */ jsx("h2", { style: styles.sectionTitle, children: "Form Triggers" }),
770
+ /* @__PURE__ */ jsx("p", { style: styles.description, children: "Optional API callbacks for lifecycle events." })
771
+ ] }),
772
+ /* @__PURE__ */ jsx("button", { type: "button", style: styles.button, onClick: () => triggerArray.append(emptyTrigger), children: "Add trigger" })
773
+ ] }),
774
+ triggerArray.fields.map((trigger, index) => /* @__PURE__ */ jsxs("div", { style: styles.row, children: [
775
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
776
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Event" }),
777
+ /* @__PURE__ */ jsx(
778
+ "select",
779
+ {
780
+ style: styles.input,
781
+ ...form.register(
782
+ `triggers.${index}.event`
783
+ ),
784
+ children: optionItems(options.formTriggerEventOptions)
785
+ }
786
+ )
787
+ ] }),
788
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
789
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Route" }),
790
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`triggers.${index}.route`) })
791
+ ] }),
792
+ /* @__PURE__ */ jsxs("label", { style: styles.checkboxField, children: [
793
+ /* @__PURE__ */ jsx("input", { type: "checkbox", ...form.register(`triggers.${index}.enabled`) }),
794
+ "Enabled"
795
+ ] }),
796
+ /* @__PURE__ */ jsxs("label", { style: styles.checkboxField, children: [
797
+ /* @__PURE__ */ jsx("input", { type: "checkbox", ...form.register(`triggers.${index}.blocking`) }),
798
+ "Blocking"
799
+ ] }),
800
+ /* @__PURE__ */ jsx("button", { type: "button", style: styles.button, onClick: () => triggerArray.remove(index), children: "Remove" })
801
+ ] }, trigger.id))
802
+ ] })
803
+ ] });
804
+ };
805
+ }
806
+ function createBuilderDefaultListEditorBody(options) {
807
+ return function BuilderDefaultListEditorBody({
808
+ listId,
809
+ form,
810
+ columnArray,
811
+ triggerArray,
812
+ sourceType,
813
+ sourceTable,
814
+ tables,
815
+ emptyColumn,
816
+ emptyTrigger,
817
+ isSaving,
818
+ importColumnsFromTable
819
+ }) {
820
+ return /* @__PURE__ */ jsxs("div", { style: styles.page, children: [
821
+ /* @__PURE__ */ jsxs("div", { style: styles.header, children: [
822
+ /* @__PURE__ */ jsxs("div", { children: [
823
+ /* @__PURE__ */ jsx("h1", { style: styles.title, children: listId ? "Edit Dynamic List" : "New Dynamic List" }),
824
+ /* @__PURE__ */ jsx("p", { style: styles.description, children: "Configure list columns, source mapping, and triggers." })
825
+ ] }),
826
+ /* @__PURE__ */ jsxs("div", { style: styles.actions, children: [
827
+ /* @__PURE__ */ jsx("a", { href: options.backHref, style: styles.button, children: "Cancel" }),
828
+ /* @__PURE__ */ jsx("button", { type: "submit", disabled: isSaving, style: styles.primaryButton, children: isSaving ? "Saving..." : "Save" })
829
+ ] })
830
+ ] }),
831
+ /* @__PURE__ */ jsxs("section", { style: styles.section, children: [
832
+ /* @__PURE__ */ jsx("div", { style: styles.sectionHeader, children: /* @__PURE__ */ jsx("h2", { style: styles.sectionTitle, children: "Details" }) }),
833
+ /* @__PURE__ */ jsxs("div", { style: { ...styles.grid, ...styles.twoColumn }, children: [
834
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
835
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Title" }),
836
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register("title", { required: true }) })
837
+ ] }),
838
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
839
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Description" }),
840
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register("description") })
841
+ ] }),
842
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
843
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Datasource" }),
844
+ /* @__PURE__ */ jsxs("select", { style: styles.input, ...form.register("sourceType"), children: [
845
+ /* @__PURE__ */ jsx("option", { value: "none", children: "None" }),
846
+ /* @__PURE__ */ jsx("option", { value: "dbTable", children: "DB table" }),
847
+ /* @__PURE__ */ jsx("option", { value: "apiEndpoint", children: "API endpoint" })
848
+ ] })
849
+ ] }),
850
+ sourceType === "dbTable" ? /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
851
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Table" }),
852
+ /* @__PURE__ */ jsxs("select", { style: styles.input, ...form.register("sourceTable"), children: [
853
+ /* @__PURE__ */ jsx("option", { value: BUILDER_EDITOR_NONE_VALUE, children: "Select table" }),
854
+ tables.map((table) => /* @__PURE__ */ jsx("option", { value: table.name, children: table.label ?? table.name }, table.name))
855
+ ] })
856
+ ] }) : null,
857
+ sourceType === "apiEndpoint" ? /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
858
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "API base path" }),
859
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register("apiBasePath") })
860
+ ] }) : null,
861
+ sourceType === "dbTable" ? /* @__PURE__ */ jsx("div", { style: { ...styles.field, justifyContent: "end" }, children: /* @__PURE__ */ jsx(
862
+ "button",
863
+ {
864
+ type: "button",
865
+ style: styles.button,
866
+ disabled: sourceTable === BUILDER_EDITOR_NONE_VALUE,
867
+ onClick: importColumnsFromTable,
868
+ children: "Import columns"
869
+ }
870
+ ) }) : null
871
+ ] })
872
+ ] }),
873
+ /* @__PURE__ */ jsxs("section", { style: styles.section, children: [
874
+ /* @__PURE__ */ jsxs("div", { style: styles.sectionHeader, children: [
875
+ /* @__PURE__ */ jsxs("div", { children: [
876
+ /* @__PURE__ */ jsx("h2", { style: styles.sectionTitle, children: "Columns" }),
877
+ /* @__PURE__ */ jsx("p", { style: styles.description, children: "Runtime list columns." })
878
+ ] }),
879
+ /* @__PURE__ */ jsx(
880
+ "button",
881
+ {
882
+ type: "button",
883
+ style: styles.button,
884
+ onClick: () => columnArray.append({
885
+ ...emptyColumn,
886
+ key: `field${columnArray.fields.length + 1}`,
887
+ label: `Column ${columnArray.fields.length + 1}`
888
+ }),
889
+ children: "Add column"
890
+ }
891
+ )
892
+ ] }),
893
+ columnArray.fields.map((column, index) => /* @__PURE__ */ jsxs("div", { style: styles.row, children: [
894
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
895
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Key" }),
896
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`columns.${index}.key`) })
897
+ ] }),
898
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
899
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Label" }),
900
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`columns.${index}.label`) })
901
+ ] }),
902
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
903
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Align" }),
904
+ /* @__PURE__ */ jsxs("select", { style: styles.input, ...form.register(`columns.${index}.align`), children: [
905
+ /* @__PURE__ */ jsx("option", { value: "left", children: "Left" }),
906
+ /* @__PURE__ */ jsx("option", { value: "right", children: "Right" })
907
+ ] })
908
+ ] }),
909
+ /* @__PURE__ */ jsxs("label", { style: styles.checkboxField, children: [
910
+ /* @__PURE__ */ jsx("input", { type: "checkbox", ...form.register(`columns.${index}.sortable`) }),
911
+ "Sortable"
912
+ ] }),
913
+ /* @__PURE__ */ jsx(
914
+ "button",
915
+ {
916
+ type: "button",
917
+ style: styles.button,
918
+ onClick: () => columnArray.remove(index),
919
+ disabled: columnArray.fields.length <= 1,
920
+ children: "Remove"
921
+ }
922
+ )
923
+ ] }, column.id))
924
+ ] }),
925
+ /* @__PURE__ */ jsxs("section", { style: styles.section, children: [
926
+ /* @__PURE__ */ jsxs("div", { style: styles.sectionHeader, children: [
927
+ /* @__PURE__ */ jsxs("div", { children: [
928
+ /* @__PURE__ */ jsx("h2", { style: styles.sectionTitle, children: "List Triggers" }),
929
+ /* @__PURE__ */ jsx("p", { style: styles.description, children: "Optional API callbacks for list lifecycle events." })
930
+ ] }),
931
+ /* @__PURE__ */ jsx("button", { type: "button", style: styles.button, onClick: () => triggerArray.append(emptyTrigger), children: "Add trigger" })
932
+ ] }),
933
+ triggerArray.fields.map((trigger, index) => /* @__PURE__ */ jsxs("div", { style: styles.row, children: [
934
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
935
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Event" }),
936
+ /* @__PURE__ */ jsx(
937
+ "select",
938
+ {
939
+ style: styles.input,
940
+ ...form.register(
941
+ `triggers.${index}.event`
942
+ ),
943
+ children: optionItems(options.listTriggerEventOptions)
944
+ }
945
+ )
946
+ ] }),
947
+ /* @__PURE__ */ jsxs("label", { style: styles.field, children: [
948
+ /* @__PURE__ */ jsx("span", { style: styles.label, children: "Route" }),
949
+ /* @__PURE__ */ jsx("input", { style: styles.input, ...form.register(`triggers.${index}.route`) })
950
+ ] }),
951
+ /* @__PURE__ */ jsxs("label", { style: styles.checkboxField, children: [
952
+ /* @__PURE__ */ jsx("input", { type: "checkbox", ...form.register(`triggers.${index}.enabled`) }),
953
+ "Enabled"
954
+ ] }),
955
+ /* @__PURE__ */ jsxs("label", { style: styles.checkboxField, children: [
956
+ /* @__PURE__ */ jsx("input", { type: "checkbox", ...form.register(`triggers.${index}.blocking`) }),
957
+ "Blocking"
958
+ ] }),
959
+ /* @__PURE__ */ jsx("button", { type: "button", style: styles.button, onClick: () => triggerArray.remove(index), children: "Remove" })
960
+ ] }, trigger.id))
961
+ ] })
962
+ ] });
963
+ };
964
+ }
965
+
966
+ // src/admin/BuilderDefaultEntityManager.tsx
967
+ import { useMemo, useState } from "react";
968
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
969
+ var styles2 = {
970
+ page: {
971
+ display: "grid",
972
+ gap: "1rem"
973
+ },
974
+ header: {
975
+ display: "flex",
976
+ justifyContent: "space-between",
977
+ gap: "1rem",
978
+ alignItems: "flex-start",
979
+ flexWrap: "wrap"
980
+ },
981
+ title: {
982
+ margin: 0,
983
+ fontSize: "1.5rem",
984
+ fontWeight: 650
985
+ },
986
+ description: {
987
+ margin: "0.25rem 0 0",
988
+ color: "var(--muted-foreground, #64748b)"
989
+ },
990
+ button: {
991
+ border: "1px solid var(--border, #d4d4d8)",
992
+ borderRadius: "0.375rem",
993
+ background: "var(--background, #fff)",
994
+ color: "inherit",
995
+ cursor: "pointer",
996
+ padding: "0.5rem 0.75rem",
997
+ textDecoration: "none",
998
+ fontSize: "0.875rem"
999
+ },
1000
+ primaryButton: {
1001
+ border: "1px solid var(--primary, #18181b)",
1002
+ borderRadius: "0.375rem",
1003
+ background: "var(--primary, #18181b)",
1004
+ color: "var(--primary-foreground, #fff)",
1005
+ cursor: "pointer",
1006
+ padding: "0.5rem 0.75rem",
1007
+ textDecoration: "none",
1008
+ fontSize: "0.875rem"
1009
+ },
1010
+ card: {
1011
+ border: "1px solid var(--border, #d4d4d8)",
1012
+ borderRadius: "0.5rem",
1013
+ overflow: "hidden",
1014
+ background: "var(--background, #fff)"
1015
+ },
1016
+ toolbar: {
1017
+ padding: "0.75rem",
1018
+ borderBottom: "1px solid var(--border, #d4d4d8)"
1019
+ },
1020
+ input: {
1021
+ width: "100%",
1022
+ maxWidth: "24rem",
1023
+ border: "1px solid var(--border, #d4d4d8)",
1024
+ borderRadius: "0.375rem",
1025
+ padding: "0.5rem 0.75rem",
1026
+ background: "var(--background, #fff)",
1027
+ color: "inherit"
1028
+ },
1029
+ tableWrap: {
1030
+ overflowX: "auto"
1031
+ },
1032
+ table: {
1033
+ width: "100%",
1034
+ borderCollapse: "collapse",
1035
+ fontSize: "0.875rem"
1036
+ },
1037
+ th: {
1038
+ padding: "0.75rem",
1039
+ borderBottom: "1px solid var(--border, #d4d4d8)",
1040
+ textAlign: "left",
1041
+ color: "var(--muted-foreground, #64748b)",
1042
+ fontWeight: 600,
1043
+ whiteSpace: "nowrap"
1044
+ },
1045
+ td: {
1046
+ padding: "0.75rem",
1047
+ borderBottom: "1px solid var(--border, #d4d4d8)",
1048
+ verticalAlign: "top"
1049
+ },
1050
+ actions: {
1051
+ display: "flex",
1052
+ justifyContent: "flex-end",
1053
+ gap: "0.5rem",
1054
+ flexWrap: "wrap"
1055
+ },
1056
+ empty: {
1057
+ padding: "2rem",
1058
+ textAlign: "center",
1059
+ color: "var(--muted-foreground, #64748b)"
1060
+ }
1061
+ };
1062
+ function createBuilderDefaultEntityManagerComponent(deps) {
1063
+ return function BuilderDefaultEntityManager() {
1064
+ const [state, setState] = useState({
1065
+ page: 1,
1066
+ pageSize: 20,
1067
+ search: ""
1068
+ });
1069
+ const query = deps.hooks.useEntities(useMemo(() => deps.buildQuery(state), [state]));
1070
+ const remove = deps.hooks.useDelete();
1071
+ const replicate = deps.hooks.useReplicate();
1072
+ const items = query.data?.items ?? [];
1073
+ const onReplicate = async (entity) => {
1074
+ const title = window.prompt(
1075
+ deps.labels.replicatePrompt(entity),
1076
+ deps.labels.replicateDefaultTitle(entity)
1077
+ );
1078
+ if (!title) return;
1079
+ await replicate.mutateAsync({ id: entity.id, input: { title } });
1080
+ };
1081
+ const onDelete = async (entity) => {
1082
+ if (!window.confirm(deps.labels.deleteConfirm(entity))) return;
1083
+ await remove.mutateAsync(entity.id);
1084
+ };
1085
+ return /* @__PURE__ */ jsxs2("section", { style: styles2.page, children: [
1086
+ /* @__PURE__ */ jsxs2("div", { style: styles2.header, children: [
1087
+ /* @__PURE__ */ jsxs2("div", { children: [
1088
+ /* @__PURE__ */ jsx2("h1", { style: styles2.title, children: deps.labels.title }),
1089
+ /* @__PURE__ */ jsx2("p", { style: styles2.description, children: deps.labels.description })
1090
+ ] }),
1091
+ deps.createHref ? /* @__PURE__ */ jsx2("a", { href: deps.createHref, style: styles2.primaryButton, children: deps.labels.addLabel }) : null
1092
+ ] }),
1093
+ /* @__PURE__ */ jsxs2("div", { style: styles2.card, children: [
1094
+ /* @__PURE__ */ jsx2("div", { style: styles2.toolbar, children: /* @__PURE__ */ jsx2(
1095
+ "input",
1096
+ {
1097
+ style: styles2.input,
1098
+ placeholder: deps.labels.searchPlaceholder,
1099
+ value: state.search,
1100
+ onChange: (event) => setState((current) => ({ ...current, page: 1, search: event.target.value }))
1101
+ }
1102
+ ) }),
1103
+ /* @__PURE__ */ jsx2("div", { style: styles2.tableWrap, children: /* @__PURE__ */ jsxs2("table", { style: styles2.table, children: [
1104
+ /* @__PURE__ */ jsx2("thead", { children: /* @__PURE__ */ jsxs2("tr", { children: [
1105
+ /* @__PURE__ */ jsx2("th", { style: styles2.th, children: deps.labels.title }),
1106
+ /* @__PURE__ */ jsx2("th", { style: styles2.th, children: "Detail" }),
1107
+ /* @__PURE__ */ jsx2("th", { style: styles2.th, children: "Description" }),
1108
+ /* @__PURE__ */ jsx2("th", { style: { ...styles2.th, textAlign: "right" }, children: "Actions" })
1109
+ ] }) }),
1110
+ /* @__PURE__ */ jsx2("tbody", { children: items.map((entity) => /* @__PURE__ */ jsxs2("tr", { children: [
1111
+ /* @__PURE__ */ jsx2("td", { style: styles2.td, children: /* @__PURE__ */ jsx2("a", { href: deps.editHref(entity), children: entity.title }) }),
1112
+ /* @__PURE__ */ jsx2("td", { style: styles2.td, children: deps.getDetail?.(entity) ?? "-" }),
1113
+ /* @__PURE__ */ jsx2("td", { style: styles2.td, children: entity.description || "-" }),
1114
+ /* @__PURE__ */ jsx2("td", { style: styles2.td, children: /* @__PURE__ */ jsxs2("div", { style: styles2.actions, children: [
1115
+ deps.runtimeHref ? /* @__PURE__ */ jsx2("a", { href: deps.runtimeHref(entity), style: styles2.button, children: deps.labels.runtimeLabel }) : null,
1116
+ /* @__PURE__ */ jsx2("a", { href: deps.editHref(entity), style: styles2.button, children: deps.labels.editLabel }),
1117
+ /* @__PURE__ */ jsx2(
1118
+ "button",
1119
+ {
1120
+ type: "button",
1121
+ style: styles2.button,
1122
+ onClick: () => void onReplicate(entity),
1123
+ children: deps.labels.replicateLabel
1124
+ }
1125
+ ),
1126
+ /* @__PURE__ */ jsx2(
1127
+ "button",
1128
+ {
1129
+ type: "button",
1130
+ style: styles2.button,
1131
+ disabled: remove.isPending,
1132
+ onClick: () => void onDelete(entity),
1133
+ children: deps.labels.deleteLabel
1134
+ }
1135
+ )
1136
+ ] }) })
1137
+ ] }, entity.id)) })
1138
+ ] }) }),
1139
+ query.isPending ? /* @__PURE__ */ jsx2("div", { style: styles2.empty, children: "Loading..." }) : null,
1140
+ query.isError ? /* @__PURE__ */ jsx2("div", { style: styles2.empty, children: "Failed to load." }) : null,
1141
+ !query.isPending && !query.isError && items.length === 0 ? /* @__PURE__ */ jsxs2("div", { style: styles2.empty, children: [
1142
+ /* @__PURE__ */ jsx2("strong", { children: deps.labels.emptyTitle }),
1143
+ /* @__PURE__ */ jsx2("div", { children: deps.labels.emptyDescription })
1144
+ ] }) : null
1145
+ ] })
1146
+ ] });
1147
+ };
1148
+ }
1149
+
1150
+ // src/admin/BuilderDefaultViewEditorPage.tsx
1151
+ import { useEffect } from "react";
1152
+ import { useForm } from "react-hook-form";
1153
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1154
+ var styles3 = {
1155
+ page: {
1156
+ display: "grid",
1157
+ gap: "1rem"
1158
+ },
1159
+ header: {
1160
+ display: "flex",
1161
+ justifyContent: "space-between",
1162
+ gap: "1rem",
1163
+ alignItems: "flex-start",
1164
+ flexWrap: "wrap"
1165
+ },
1166
+ title: {
1167
+ margin: 0,
1168
+ fontSize: "1.5rem",
1169
+ fontWeight: 650
1170
+ },
1171
+ description: {
1172
+ margin: "0.25rem 0 0",
1173
+ color: "var(--muted-foreground, #64748b)"
1174
+ },
1175
+ actions: {
1176
+ display: "flex",
1177
+ gap: "0.5rem",
1178
+ flexWrap: "wrap"
1179
+ },
1180
+ section: {
1181
+ border: "1px solid var(--border, #d4d4d8)",
1182
+ borderRadius: "0.5rem",
1183
+ background: "var(--background, #fff)",
1184
+ overflow: "hidden"
1185
+ },
1186
+ grid: {
1187
+ display: "grid",
1188
+ gridTemplateColumns: "repeat(auto-fit, minmax(14rem, 1fr))",
1189
+ gap: "0.75rem",
1190
+ padding: "0.75rem"
1191
+ },
1192
+ field: {
1193
+ display: "grid",
1194
+ gap: "0.25rem",
1195
+ fontSize: "0.875rem"
1196
+ },
1197
+ label: {
1198
+ color: "var(--muted-foreground, #64748b)",
1199
+ fontSize: "0.75rem",
1200
+ fontWeight: 600
1201
+ },
1202
+ input: {
1203
+ minWidth: 0,
1204
+ border: "1px solid var(--border, #d4d4d8)",
1205
+ borderRadius: "0.375rem",
1206
+ padding: "0.5rem 0.625rem",
1207
+ background: "var(--background, #fff)",
1208
+ color: "inherit"
1209
+ },
1210
+ textarea: {
1211
+ minWidth: 0,
1212
+ minHeight: "8rem",
1213
+ border: "1px solid var(--border, #d4d4d8)",
1214
+ borderRadius: "0.375rem",
1215
+ padding: "0.5rem 0.625rem",
1216
+ background: "var(--background, #fff)",
1217
+ color: "inherit",
1218
+ resize: "vertical"
1219
+ },
1220
+ button: {
1221
+ border: "1px solid var(--border, #d4d4d8)",
1222
+ borderRadius: "0.375rem",
1223
+ background: "var(--background, #fff)",
1224
+ color: "inherit",
1225
+ cursor: "pointer",
1226
+ padding: "0.5rem 0.75rem",
1227
+ textDecoration: "none",
1228
+ fontSize: "0.875rem"
1229
+ },
1230
+ primaryButton: {
1231
+ border: "1px solid var(--primary, #18181b)",
1232
+ borderRadius: "0.375rem",
1233
+ background: "var(--primary, #18181b)",
1234
+ color: "var(--primary-foreground, #fff)",
1235
+ cursor: "pointer",
1236
+ padding: "0.5rem 0.75rem",
1237
+ textDecoration: "none",
1238
+ fontSize: "0.875rem"
1239
+ },
1240
+ state: {
1241
+ color: "var(--muted-foreground, #64748b)",
1242
+ fontSize: "0.875rem"
1243
+ }
1244
+ };
1245
+ var defaultEmptyValues = {
1246
+ title: "",
1247
+ description: "",
1248
+ formId: BUILDER_EDITOR_NONE_VALUE,
1249
+ listId: BUILDER_EDITOR_NONE_VALUE,
1250
+ permissionRead: "",
1251
+ permissionWrite: "",
1252
+ triggersText: "[]",
1253
+ version: 0
1254
+ };
1255
+ function createBuilderDefaultViewEditorPage(options) {
1256
+ const defaultPermission = options.defaultPermission ?? "";
1257
+ const showPermissions = options.permissionOptions.length > 0;
1258
+ return function BuilderDefaultViewEditorPage({ viewId }) {
1259
+ const view = options.hooks.useBuilderView(viewId);
1260
+ const forms = options.hooks.useBuilderFormOptions();
1261
+ const lists = options.hooks.useBuilderListOptions();
1262
+ const save = options.hooks.useSaveBuilderView();
1263
+ const form = useForm({
1264
+ defaultValues: {
1265
+ ...defaultEmptyValues,
1266
+ permissionRead: defaultPermission,
1267
+ permissionWrite: defaultPermission
1268
+ }
1269
+ });
1270
+ useEffect(() => {
1271
+ if (viewId && !view.data) return;
1272
+ form.reset(
1273
+ builderViewEditorValuesFromEntity(
1274
+ view.data ?? null,
1275
+ {
1276
+ ...defaultEmptyValues,
1277
+ permissionRead: defaultPermission,
1278
+ permissionWrite: defaultPermission
1279
+ },
1280
+ defaultPermission
1281
+ )
1282
+ );
1283
+ }, [form, view.data, viewId]);
1284
+ const onSubmit = form.handleSubmit(async (values) => {
1285
+ try {
1286
+ const input = builderViewEditorInputFromValues(values, options.parseTriggers);
1287
+ const saved = await save.mutateAsync({
1288
+ id: viewId,
1289
+ input
1290
+ });
1291
+ options.notifySuccess?.(viewId ? "View saved" : "View created");
1292
+ if (!viewId) {
1293
+ window.location.assign(options.editHref(saved.id));
1294
+ }
1295
+ } catch (error) {
1296
+ if (options.notifyError) options.notifyError(error);
1297
+ else throw error;
1298
+ }
1299
+ });
1300
+ if (viewId && view.isPending) {
1301
+ return /* @__PURE__ */ jsx3("div", { style: styles3.state, children: "Loading..." });
1302
+ }
1303
+ if (viewId && view.isError) {
1304
+ return /* @__PURE__ */ jsx3("div", { style: styles3.state, children: "Failed to load view." });
1305
+ }
1306
+ return /* @__PURE__ */ jsxs3("form", { onSubmit, noValidate: true, style: styles3.page, children: [
1307
+ /* @__PURE__ */ jsxs3("div", { style: styles3.header, children: [
1308
+ /* @__PURE__ */ jsxs3("div", { children: [
1309
+ /* @__PURE__ */ jsx3("h1", { style: styles3.title, children: viewId ? "Edit Dynamic View" : "New Dynamic View" }),
1310
+ /* @__PURE__ */ jsx3("p", { style: styles3.description, children: "Connect a form and list into a runtime screen." })
1311
+ ] }),
1312
+ /* @__PURE__ */ jsxs3("div", { style: styles3.actions, children: [
1313
+ /* @__PURE__ */ jsx3("a", { href: options.backHref, style: styles3.button, children: "Cancel" }),
1314
+ /* @__PURE__ */ jsx3("button", { type: "submit", disabled: save.isPending, style: styles3.primaryButton, children: save.isPending ? "Saving..." : "Save" })
1315
+ ] })
1316
+ ] }),
1317
+ /* @__PURE__ */ jsx3("section", { style: styles3.section, children: /* @__PURE__ */ jsxs3("div", { style: styles3.grid, children: [
1318
+ /* @__PURE__ */ jsxs3("label", { style: styles3.field, children: [
1319
+ /* @__PURE__ */ jsx3("span", { style: styles3.label, children: "Title" }),
1320
+ /* @__PURE__ */ jsx3("input", { style: styles3.input, ...form.register("title", { required: true }) })
1321
+ ] }),
1322
+ /* @__PURE__ */ jsxs3("label", { style: styles3.field, children: [
1323
+ /* @__PURE__ */ jsx3("span", { style: styles3.label, children: "Description" }),
1324
+ /* @__PURE__ */ jsx3("input", { style: styles3.input, ...form.register("description") })
1325
+ ] }),
1326
+ /* @__PURE__ */ jsxs3("label", { style: styles3.field, children: [
1327
+ /* @__PURE__ */ jsx3("span", { style: styles3.label, children: "Form" }),
1328
+ /* @__PURE__ */ jsxs3("select", { style: styles3.input, ...form.register("formId"), children: [
1329
+ /* @__PURE__ */ jsx3("option", { value: BUILDER_EDITOR_NONE_VALUE, children: "No form" }),
1330
+ (forms.data ?? []).map((item) => /* @__PURE__ */ jsx3("option", { value: item.id, children: item.title }, item.id))
1331
+ ] })
1332
+ ] }),
1333
+ /* @__PURE__ */ jsxs3("label", { style: styles3.field, children: [
1334
+ /* @__PURE__ */ jsx3("span", { style: styles3.label, children: "List" }),
1335
+ /* @__PURE__ */ jsxs3("select", { style: styles3.input, ...form.register("listId"), children: [
1336
+ /* @__PURE__ */ jsx3("option", { value: BUILDER_EDITOR_NONE_VALUE, children: "No list" }),
1337
+ (lists.data ?? []).map((item) => /* @__PURE__ */ jsx3("option", { value: item.id, children: item.title }, item.id))
1338
+ ] })
1339
+ ] }),
1340
+ showPermissions ? /* @__PURE__ */ jsxs3(Fragment, { children: [
1341
+ /* @__PURE__ */ jsxs3("label", { style: styles3.field, children: [
1342
+ /* @__PURE__ */ jsx3("span", { style: styles3.label, children: "Read permission" }),
1343
+ /* @__PURE__ */ jsxs3("select", { style: styles3.input, ...form.register("permissionRead"), children: [
1344
+ /* @__PURE__ */ jsx3("option", { value: "", children: "Authenticated users" }),
1345
+ options.permissionOptions.map((permission) => /* @__PURE__ */ jsx3("option", { value: permission.value, children: permission.label }, permission.value))
1346
+ ] })
1347
+ ] }),
1348
+ /* @__PURE__ */ jsxs3("label", { style: styles3.field, children: [
1349
+ /* @__PURE__ */ jsx3("span", { style: styles3.label, children: "Write permission" }),
1350
+ /* @__PURE__ */ jsxs3("select", { style: styles3.input, ...form.register("permissionWrite"), children: [
1351
+ /* @__PURE__ */ jsx3("option", { value: "", children: "Authenticated users" }),
1352
+ options.permissionOptions.map((permission) => /* @__PURE__ */ jsx3("option", { value: permission.value, children: permission.label }, permission.value))
1353
+ ] })
1354
+ ] })
1355
+ ] }) : null,
1356
+ /* @__PURE__ */ jsxs3("label", { style: { ...styles3.field, gridColumn: "1 / -1" }, children: [
1357
+ /* @__PURE__ */ jsx3("span", { style: styles3.label, children: "Triggers JSON" }),
1358
+ /* @__PURE__ */ jsx3("textarea", { style: styles3.textarea, ...form.register("triggersText") })
1359
+ ] })
1360
+ ] }) })
1361
+ ] });
1362
+ };
1363
+ }
1364
+
1365
+ // src/admin/BuilderEditorPage.tsx
1366
+ import { useEffect as useEffect2 } from "react";
1367
+ import {
1368
+ FormProvider,
1369
+ useFieldArray,
1370
+ useForm as useForm2,
1371
+ useWatch
1372
+ } from "react-hook-form";
1373
+ import { jsx as jsx4 } from "react/jsx-runtime";
1374
+ function createBuilderFormEditorPageComponent(deps) {
1375
+ return function BuilderFormEditorPage({ formId }) {
1376
+ const Body = deps.Body;
1377
+ const entityQuery = deps.hooks.useEntity(formId);
1378
+ const tables = deps.hooks.useTables();
1379
+ const save = deps.hooks.useSave();
1380
+ const form = useForm2({
1381
+ resolver: deps.resolver,
1382
+ defaultValues: deps.emptyValues
1383
+ });
1384
+ const fieldArray = useFieldArray({ control: form.control, name: "fields" });
1385
+ const subformArray = useFieldArray({ control: form.control, name: "subforms" });
1386
+ const triggerArray = useFieldArray({ control: form.control, name: "triggers" });
1387
+ const fields = useWatch({ control: form.control, name: "fields" }) ?? [];
1388
+ const subforms = useWatch({ control: form.control, name: "subforms" }) ?? [];
1389
+ const sourceType = useWatch({ control: form.control, name: "sourceType" });
1390
+ const sourceTable = useWatch({ control: form.control, name: "sourceTable" });
1391
+ useEffect2(() => {
1392
+ if (formId && !entityQuery.data) return;
1393
+ form.reset(builderFormEditorValuesFromEntity(entityQuery.data ?? null, deps.emptyValues));
1394
+ }, [entityQuery.data, form, formId]);
1395
+ const importFieldsFromTable = () => {
1396
+ const table = tables.data?.find((item) => item.name === sourceTable);
1397
+ if (!table) return;
1398
+ const nextFields = builderFormEditorFieldsFromTable(table);
1399
+ fieldArray.replace(nextFields);
1400
+ form.setValue("layoutGroups", builderFormEditorDefaultLayoutGroups(nextFields), {
1401
+ shouldDirty: true
1402
+ });
1403
+ };
1404
+ const onSubmit = form.handleSubmit(async (values) => {
1405
+ try {
1406
+ const saved = await save.mutateAsync({ id: formId, input: deps.buildInput(values) });
1407
+ deps.notifications.success(formId ? deps.labels.saved : deps.labels.created);
1408
+ if (!formId) deps.navigateAfterCreate(saved);
1409
+ } catch (error) {
1410
+ deps.notifications.error(error);
1411
+ }
1412
+ });
1413
+ if (formId && entityQuery.isPending) {
1414
+ return /* @__PURE__ */ jsx4("div", { className: "text-muted-foreground text-sm", children: deps.states.loading });
1415
+ }
1416
+ if (formId && entityQuery.isError) {
1417
+ return /* @__PURE__ */ jsx4("div", { className: "text-destructive text-sm", children: deps.states.error });
1418
+ }
1419
+ return /* @__PURE__ */ jsx4(FormProvider, { ...form, children: /* @__PURE__ */ jsx4("form", { onSubmit, noValidate: true, className: "space-y-6", children: /* @__PURE__ */ jsx4(
1420
+ Body,
1421
+ {
1422
+ formId,
1423
+ form,
1424
+ fieldArray,
1425
+ subformArray,
1426
+ triggerArray,
1427
+ fields,
1428
+ subforms,
1429
+ sourceType,
1430
+ sourceTable,
1431
+ tables: tables.data ?? [],
1432
+ emptyField: deps.emptyField,
1433
+ emptyTrigger: deps.emptyTrigger,
1434
+ isSaving: save.isPending,
1435
+ importFieldsFromTable
1436
+ }
1437
+ ) }) });
1438
+ };
1439
+ }
1440
+ function createBuilderListEditorPageComponent(deps) {
1441
+ return function BuilderListEditorPage({ listId }) {
1442
+ const Body = deps.Body;
1443
+ const entityQuery = deps.hooks.useEntity(listId);
1444
+ const tables = deps.hooks.useTables();
1445
+ const save = deps.hooks.useSave();
1446
+ const form = useForm2({
1447
+ resolver: deps.resolver,
1448
+ defaultValues: deps.emptyValues
1449
+ });
1450
+ const columnArray = useFieldArray({ control: form.control, name: "columns" });
1451
+ const triggerArray = useFieldArray({ control: form.control, name: "triggers" });
1452
+ const sourceType = useWatch({ control: form.control, name: "sourceType" });
1453
+ const sourceTable = useWatch({ control: form.control, name: "sourceTable" });
1454
+ useEffect2(() => {
1455
+ if (listId && !entityQuery.data) return;
1456
+ form.reset(builderListEditorValuesFromEntity(entityQuery.data ?? null, deps.emptyValues));
1457
+ }, [entityQuery.data, form, listId]);
1458
+ const importColumnsFromTable = () => {
1459
+ const table = tables.data?.find((item) => item.name === sourceTable);
1460
+ if (!table) return;
1461
+ columnArray.replace(builderListEditorColumnsFromTable(table));
1462
+ };
1463
+ const onSubmit = form.handleSubmit(async (values) => {
1464
+ try {
1465
+ const saved = await save.mutateAsync({ id: listId, input: deps.buildInput(values) });
1466
+ deps.notifications.success(listId ? deps.labels.saved : deps.labels.created);
1467
+ if (!listId) deps.navigateAfterCreate(saved);
1468
+ } catch (error) {
1469
+ deps.notifications.error(error);
1470
+ }
1471
+ });
1472
+ if (listId && entityQuery.isPending) {
1473
+ return /* @__PURE__ */ jsx4("div", { className: "text-muted-foreground text-sm", children: deps.states.loading });
1474
+ }
1475
+ if (listId && entityQuery.isError) {
1476
+ return /* @__PURE__ */ jsx4("div", { className: "text-destructive text-sm", children: deps.states.error });
1477
+ }
1478
+ return /* @__PURE__ */ jsx4(FormProvider, { ...form, children: /* @__PURE__ */ jsx4("form", { onSubmit, noValidate: true, className: "space-y-6", children: /* @__PURE__ */ jsx4(
1479
+ Body,
1480
+ {
1481
+ listId,
1482
+ form,
1483
+ columnArray,
1484
+ triggerArray,
1485
+ sourceType,
1486
+ sourceTable,
1487
+ tables: tables.data ?? [],
1488
+ emptyColumn: deps.emptyColumn,
1489
+ emptyTrigger: deps.emptyTrigger,
1490
+ isSaving: save.isPending,
1491
+ importColumnsFromTable
1492
+ }
1493
+ ) }) });
1494
+ };
1495
+ }
1496
+
1497
+ // src/admin/BuilderAdminPages.tsx
1498
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1499
+ var defaultFieldTypeOptions = [
1500
+ { value: "text", label: "Text" },
1501
+ { value: "textarea", label: "Textarea" },
1502
+ { value: "number", label: "Number" },
1503
+ { value: "money", label: "Money" },
1504
+ { value: "select", label: "Select" },
1505
+ { value: "checkbox", label: "Checkbox" },
1506
+ { value: "date", label: "Date" }
1507
+ ];
1508
+ var defaultFormTriggerEventOptions = [
1509
+ { value: "beforeCreate", label: "Before create" },
1510
+ { value: "afterCreate", label: "After create" },
1511
+ { value: "beforeUpdate", label: "Before update" },
1512
+ { value: "afterUpdate", label: "After update" }
1513
+ ];
1514
+ var defaultListTriggerEventOptions = [
1515
+ { value: "beforeRead", label: "Before read" },
1516
+ { value: "afterRead", label: "After read" },
1517
+ { value: "beforeDelete", label: "Before delete" },
1518
+ { value: "afterDelete", label: "After delete" }
1519
+ ];
1520
+ var defaultFieldTriggerEventOptions = [
1521
+ { value: "onChange", label: "On change" },
1522
+ { value: "onBlur", label: "On blur" }
1523
+ ];
1524
+ var defaultPermissionOptions = [];
1525
+ function parseJsonArray(value) {
1526
+ const parsed = JSON.parse(value);
1527
+ return Array.isArray(parsed) ? parsed : [];
1528
+ }
1529
+ function joinPath(base, path) {
1530
+ return `${base.replace(/\/$/, "")}/${path.replace(/^\//, "")}`;
1531
+ }
1532
+ function defaultQuery(state) {
1533
+ return {
1534
+ page: state.page,
1535
+ pageSize: state.pageSize,
1536
+ search: state.search || void 0
1537
+ };
1538
+ }
1539
+ function detail(entity) {
1540
+ return entity.key ?? entity.id;
1541
+ }
1542
+ var homeStyles = {
1543
+ page: {
1544
+ display: "grid",
1545
+ gap: "1rem"
1546
+ },
1547
+ headerTitle: {
1548
+ margin: 0,
1549
+ fontSize: "1.5rem",
1550
+ fontWeight: 650
1551
+ },
1552
+ description: {
1553
+ margin: "0.25rem 0 0",
1554
+ color: "var(--muted-foreground, #64748b)"
1555
+ },
1556
+ grid: {
1557
+ display: "grid",
1558
+ gridTemplateColumns: "repeat(auto-fit, minmax(13rem, 1fr))",
1559
+ gap: "0.75rem"
1560
+ },
1561
+ card: {
1562
+ border: "1px solid var(--border, #d4d4d8)",
1563
+ borderRadius: "0.5rem",
1564
+ padding: "1rem",
1565
+ background: "var(--background, #fff)",
1566
+ color: "inherit",
1567
+ textDecoration: "none"
1568
+ },
1569
+ cardTitle: {
1570
+ margin: 0,
1571
+ fontSize: "1rem",
1572
+ fontWeight: 650
1573
+ }
1574
+ };
1575
+ function createBuilderAdminPages({
1576
+ hooks,
1577
+ routes = {},
1578
+ fieldTypeOptions = defaultFieldTypeOptions,
1579
+ formTriggerEventOptions = defaultFormTriggerEventOptions,
1580
+ listTriggerEventOptions = defaultListTriggerEventOptions,
1581
+ fieldTriggerEventOptions = defaultFieldTriggerEventOptions,
1582
+ permissionOptions = defaultPermissionOptions,
1583
+ defaultPermission = "",
1584
+ notifySuccess,
1585
+ notifyError,
1586
+ parseViewTriggers = parseJsonArray
1587
+ }) {
1588
+ const adminBasePath = routes.adminBasePath ?? "/admin/dev/builder";
1589
+ const runtimeBasePath = routes.runtimeBasePath ?? "/admin/winu";
1590
+ const formsPath = routes.formsPath ?? joinPath(adminBasePath, "forms");
1591
+ const newFormPath = routes.newFormPath ?? joinPath(formsPath, "new");
1592
+ const editFormPath = routes.editFormPath ?? ((id) => `${joinPath(formsPath, "edit")}?id=${encodeURIComponent(id)}`);
1593
+ const runtimeFormPath = routes.runtimeFormPath ?? ((id) => `${joinPath(runtimeBasePath, "form")}?id=${encodeURIComponent(id)}`);
1594
+ const listsPath = routes.listsPath ?? joinPath(adminBasePath, "lists");
1595
+ const newListPath = routes.newListPath ?? joinPath(listsPath, "new");
1596
+ const editListPath = routes.editListPath ?? ((id) => `${joinPath(listsPath, "edit")}?id=${encodeURIComponent(id)}`);
1597
+ const runtimeListPath = routes.runtimeListPath ?? ((id) => `${joinPath(runtimeBasePath, "list")}?id=${encodeURIComponent(id)}`);
1598
+ const viewsPath = routes.viewsPath ?? joinPath(adminBasePath, "views");
1599
+ const newViewPath = routes.newViewPath ?? joinPath(viewsPath, "new");
1600
+ const editViewPath = routes.editViewPath ?? ((id) => `${joinPath(viewsPath, "edit")}?id=${encodeURIComponent(id)}`);
1601
+ const runtimeViewPath = routes.runtimeViewPath ?? ((id) => `${joinPath(runtimeBasePath, "view")}?id=${encodeURIComponent(id)}`);
1602
+ const formFieldType = fieldTypeOptions[0]?.value ?? "text";
1603
+ const formTriggerEvent = formTriggerEventOptions[0]?.value ?? "beforeCreate";
1604
+ const listTriggerEvent = listTriggerEventOptions[0]?.value ?? "beforeRead";
1605
+ const emptyField = {
1606
+ key: "name",
1607
+ label: "Name",
1608
+ type: formFieldType,
1609
+ required: true,
1610
+ hidden: false,
1611
+ disabled: false,
1612
+ placeholder: "",
1613
+ optionsText: "",
1614
+ optionsMode: "static",
1615
+ optionsTable: BUILDER_EDITOR_NONE_VALUE,
1616
+ optionsValueField: BUILDER_EDITOR_NONE_VALUE,
1617
+ optionsLabelField: BUILDER_EDITOR_NONE_VALUE,
1618
+ optionsSearchFieldsText: "",
1619
+ optionsDependsOn: "",
1620
+ optionsFilterField: "",
1621
+ triggers: []
1622
+ };
1623
+ const emptyFormValues = {
1624
+ title: "",
1625
+ description: "",
1626
+ sourceType: "none",
1627
+ sourceTable: BUILDER_EDITOR_NONE_VALUE,
1628
+ apiBasePath: "",
1629
+ fields: [emptyField],
1630
+ layoutGroups: builderFormEditorDefaultLayoutGroups([emptyField]),
1631
+ subforms: [],
1632
+ triggers: [],
1633
+ version: 0
1634
+ };
1635
+ const emptyFormTrigger = {
1636
+ event: formTriggerEvent,
1637
+ route: "/api/v1/",
1638
+ enabled: false,
1639
+ blocking: true
1640
+ };
1641
+ const emptyColumn = {
1642
+ key: "name",
1643
+ label: "Name",
1644
+ sortable: true,
1645
+ align: "left"
1646
+ };
1647
+ const emptyListValues = {
1648
+ title: "",
1649
+ description: "",
1650
+ sourceType: "none",
1651
+ sourceTable: BUILDER_EDITOR_NONE_VALUE,
1652
+ apiBasePath: "",
1653
+ columns: [emptyColumn],
1654
+ triggers: [],
1655
+ version: 0
1656
+ };
1657
+ const emptyListTrigger = {
1658
+ event: listTriggerEvent,
1659
+ route: "/api/v1/",
1660
+ enabled: false,
1661
+ blocking: true
1662
+ };
1663
+ const FormEditorPage = createBuilderFormEditorPageComponent({
1664
+ emptyValues: emptyFormValues,
1665
+ emptyField,
1666
+ emptyTrigger: emptyFormTrigger,
1667
+ labels: {
1668
+ created: "Form created",
1669
+ saved: "Form saved"
1670
+ },
1671
+ states: {
1672
+ loading: "Loading...",
1673
+ error: "Failed to load form."
1674
+ },
1675
+ hooks: {
1676
+ useEntity: hooks.useBuilderForm,
1677
+ useTables: hooks.useBuilderDbTables,
1678
+ useSave: hooks.useSaveBuilderForm
1679
+ },
1680
+ buildInput: (values) => builderFormEditorInputFromValues(values),
1681
+ navigateAfterCreate: (entity) => window.location.assign(editFormPath(entity.id)),
1682
+ notifications: {
1683
+ success: (message) => notifySuccess?.(message),
1684
+ error: (error) => {
1685
+ if (notifyError) notifyError(error);
1686
+ else throw error;
1687
+ }
1688
+ },
1689
+ Body: createBuilderDefaultFormEditorBody({
1690
+ backHref: formsPath,
1691
+ fieldTypeOptions,
1692
+ formTriggerEventOptions,
1693
+ fieldTriggerEventOptions
1694
+ })
1695
+ });
1696
+ const ListEditorPage = createBuilderListEditorPageComponent({
1697
+ emptyValues: emptyListValues,
1698
+ emptyColumn,
1699
+ emptyTrigger: emptyListTrigger,
1700
+ labels: {
1701
+ created: "List created",
1702
+ saved: "List saved"
1703
+ },
1704
+ states: {
1705
+ loading: "Loading...",
1706
+ error: "Failed to load list."
1707
+ },
1708
+ hooks: {
1709
+ useEntity: hooks.useBuilderList,
1710
+ useTables: hooks.useBuilderDbTables,
1711
+ useSave: hooks.useSaveBuilderList
1712
+ },
1713
+ buildInput: (values) => builderListEditorInputFromValues(values),
1714
+ navigateAfterCreate: (entity) => window.location.assign(editListPath(entity.id)),
1715
+ notifications: {
1716
+ success: (message) => notifySuccess?.(message),
1717
+ error: (error) => {
1718
+ if (notifyError) notifyError(error);
1719
+ else throw error;
1720
+ }
1721
+ },
1722
+ Body: createBuilderDefaultListEditorBody({
1723
+ backHref: listsPath,
1724
+ listTriggerEventOptions
1725
+ })
1726
+ });
1727
+ const ViewEditorPage = createBuilderDefaultViewEditorPage({
1728
+ hooks,
1729
+ backHref: viewsPath,
1730
+ editHref: editViewPath,
1731
+ permissionOptions,
1732
+ defaultPermission,
1733
+ parseTriggers: parseViewTriggers,
1734
+ notifySuccess,
1735
+ notifyError
1736
+ });
1737
+ const AdminHomePage = function BuilderAdminHomePage() {
1738
+ const items = [
1739
+ {
1740
+ title: "Dynamic Forms",
1741
+ description: "Reusable form schemas.",
1742
+ href: formsPath
1743
+ },
1744
+ {
1745
+ title: "Dynamic Lists",
1746
+ description: "Reusable list and grid schemas.",
1747
+ href: listsPath
1748
+ },
1749
+ {
1750
+ title: "Dynamic Views",
1751
+ description: "Runtime screens composed from form and list schemas.",
1752
+ href: viewsPath
1753
+ }
1754
+ ];
1755
+ return /* @__PURE__ */ jsxs4("section", { style: homeStyles.page, children: [
1756
+ /* @__PURE__ */ jsxs4("div", { children: [
1757
+ /* @__PURE__ */ jsx5("h1", { style: homeStyles.headerTitle, children: "Builder" }),
1758
+ /* @__PURE__ */ jsx5("p", { style: homeStyles.description, children: "Manage dynamic forms, lists, and views." })
1759
+ ] }),
1760
+ /* @__PURE__ */ jsx5("div", { style: homeStyles.grid, children: items.map((item) => /* @__PURE__ */ jsxs4("a", { href: item.href, style: homeStyles.card, children: [
1761
+ /* @__PURE__ */ jsx5("h2", { style: homeStyles.cardTitle, children: item.title }),
1762
+ /* @__PURE__ */ jsx5("p", { style: homeStyles.description, children: item.description })
1763
+ ] }, item.href)) })
1764
+ ] });
1765
+ };
1766
+ const AdminFormsPage = createBuilderDefaultEntityManagerComponent({
1767
+ labels: {
1768
+ title: "Dynamic Forms",
1769
+ description: "Reusable form schemas.",
1770
+ searchPlaceholder: "Search forms...",
1771
+ emptyTitle: "No forms",
1772
+ emptyDescription: "Create your first dynamic form.",
1773
+ addLabel: "Add form",
1774
+ editLabel: "Edit",
1775
+ replicateLabel: "Replicate",
1776
+ deleteLabel: "Delete",
1777
+ runtimeLabel: "Open",
1778
+ deleteConfirm: (entity) => `Delete ${entity.title}?`,
1779
+ replicatePrompt: () => "New form title",
1780
+ replicateDefaultTitle: (entity) => `${entity.title} copy`
1781
+ },
1782
+ createHref: newFormPath,
1783
+ editHref: (entity) => editFormPath(entity.id),
1784
+ runtimeHref: (entity) => runtimeFormPath(entity.id),
1785
+ getDetail: detail,
1786
+ buildQuery: (state) => defaultQuery(state),
1787
+ hooks: {
1788
+ useEntities: (query) => hooks.useBuilderForms(query),
1789
+ useDelete: hooks.useDeleteBuilderForm,
1790
+ useReplicate: hooks.useReplicateBuilderForm
1791
+ }
1792
+ });
1793
+ const AdminListsPage = createBuilderDefaultEntityManagerComponent({
1794
+ labels: {
1795
+ title: "Dynamic Lists",
1796
+ description: "Reusable list and grid schemas.",
1797
+ searchPlaceholder: "Search lists...",
1798
+ emptyTitle: "No lists",
1799
+ emptyDescription: "Create your first dynamic list.",
1800
+ addLabel: "Add list",
1801
+ editLabel: "Edit",
1802
+ replicateLabel: "Replicate",
1803
+ deleteLabel: "Delete",
1804
+ runtimeLabel: "Open",
1805
+ deleteConfirm: (entity) => `Delete ${entity.title}?`,
1806
+ replicatePrompt: () => "New list title",
1807
+ replicateDefaultTitle: (entity) => `${entity.title} copy`
1808
+ },
1809
+ createHref: newListPath,
1810
+ editHref: (entity) => editListPath(entity.id),
1811
+ runtimeHref: (entity) => runtimeListPath(entity.id),
1812
+ getDetail: detail,
1813
+ buildQuery: (state) => defaultQuery(state),
1814
+ hooks: {
1815
+ useEntities: (query) => hooks.useBuilderLists(query),
1816
+ useDelete: hooks.useDeleteBuilderList,
1817
+ useReplicate: hooks.useReplicateBuilderList
1818
+ }
1819
+ });
1820
+ const AdminViewsPage = createBuilderDefaultEntityManagerComponent({
1821
+ labels: {
1822
+ title: "Dynamic Views",
1823
+ description: "Runtime screens composed from form and list schemas.",
1824
+ searchPlaceholder: "Search views...",
1825
+ emptyTitle: "No views",
1826
+ emptyDescription: "Create a dynamic view after preparing a form and list.",
1827
+ addLabel: "Add view",
1828
+ editLabel: "Edit",
1829
+ replicateLabel: "Replicate",
1830
+ deleteLabel: "Delete",
1831
+ runtimeLabel: "Open",
1832
+ deleteConfirm: (entity) => `Delete ${entity.title}?`,
1833
+ replicatePrompt: () => "New view title",
1834
+ replicateDefaultTitle: (entity) => `${entity.title} copy`
1835
+ },
1836
+ createHref: newViewPath,
1837
+ editHref: (entity) => editViewPath(entity.id),
1838
+ runtimeHref: (entity) => runtimeViewPath(entity.id),
1839
+ getDetail: detail,
1840
+ buildQuery: (state) => defaultQuery(state),
1841
+ hooks: {
1842
+ useEntities: (query) => hooks.useBuilderViews(query),
1843
+ useDelete: hooks.useDeleteBuilderView,
1844
+ useReplicate: hooks.useReplicateBuilderView
1845
+ }
1846
+ });
1847
+ return {
1848
+ AdminHomePage,
1849
+ AdminFormsPage,
1850
+ AdminNewFormPage: function BuilderAdminNewFormPage() {
1851
+ return /* @__PURE__ */ jsx5(FormEditorPage, {});
1852
+ },
1853
+ AdminEditFormPage: function BuilderAdminEditFormPage({ formId }) {
1854
+ return /* @__PURE__ */ jsx5(FormEditorPage, { formId });
1855
+ },
1856
+ AdminListsPage,
1857
+ AdminNewListPage: function BuilderAdminNewListPage() {
1858
+ return /* @__PURE__ */ jsx5(ListEditorPage, {});
1859
+ },
1860
+ AdminEditListPage: function BuilderAdminEditListPage({ listId }) {
1861
+ return /* @__PURE__ */ jsx5(ListEditorPage, { listId });
1862
+ },
1863
+ AdminViewsPage,
1864
+ AdminNewViewPage: function BuilderAdminNewViewPage() {
1865
+ return /* @__PURE__ */ jsx5(ViewEditorPage, {});
1866
+ },
1867
+ AdminEditViewPage: function BuilderAdminEditViewPage({ viewId }) {
1868
+ return /* @__PURE__ */ jsx5(ViewEditorPage, { viewId });
1869
+ },
1870
+ routes: {
1871
+ adminBasePath,
1872
+ formsPath,
1873
+ newFormPath,
1874
+ editFormPath,
1875
+ listsPath,
1876
+ newListPath,
1877
+ editListPath,
1878
+ viewsPath,
1879
+ newViewPath,
1880
+ editViewPath
1881
+ }
1882
+ };
1883
+ }
1884
+
90
1885
  // src/hooks/createBuilderHooks.ts
91
1886
  import { keepPreviousData, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
92
1887
  function createBuilderKeys() {
@@ -409,27 +2204,64 @@ function withQuery(path, query) {
409
2204
  }
410
2205
  function createBuilderIntegration(config) {
411
2206
  const adminBasePath = normalizeBasePath(config.adminBasePath ?? "/admin/dev/builder");
2207
+ const runtimeBasePath = normalizeBasePath(config.runtimeBasePath ?? "/admin/winu");
412
2208
  const api = createBuilderApiClient(config.transport);
413
2209
  const hooks = createBuilderHooks(api);
414
2210
  const formsPath = `${adminBasePath}/forms`;
415
2211
  const listsPath = `${adminBasePath}/lists`;
416
- return {
417
- api,
2212
+ const routes = {
2213
+ adminBasePath,
2214
+ runtimeBasePath,
2215
+ adminHomePath: adminBasePath,
2216
+ formsPath,
2217
+ newFormPath: `${formsPath}/new`,
2218
+ editFormPath: (id) => withQuery(`${formsPath}/edit`, { id }),
2219
+ listsPath,
2220
+ newListPath: `${listsPath}/new`,
2221
+ editListPath: (id) => withQuery(`${listsPath}/edit`, { id }),
2222
+ viewsPath: `${adminBasePath}/views`
2223
+ };
2224
+ const adminPages = config.adminPages ? createBuilderAdminPages({
2225
+ ...config.adminPages,
418
2226
  hooks,
419
2227
  routes: {
420
2228
  adminBasePath,
421
- adminHomePath: adminBasePath,
422
- formsPath,
423
- newFormPath: `${formsPath}/new`,
424
- editFormPath: (id) => withQuery(`${formsPath}/edit`, { id }),
425
- listsPath,
426
- newListPath: `${listsPath}/new`,
427
- editListPath: (id) => withQuery(`${listsPath}/edit`, { id }),
428
- viewsPath: `${adminBasePath}/views`
2229
+ runtimeBasePath
429
2230
  }
2231
+ }) : void 0;
2232
+ return {
2233
+ api,
2234
+ hooks,
2235
+ routes,
2236
+ adminPages
430
2237
  };
431
2238
  }
2239
+ function createBuilderPortalIntegration(config) {
2240
+ return createBuilderIntegration(config);
2241
+ }
2242
+ function validateBuilderFrontendSetup(config) {
2243
+ const checks = [
2244
+ {
2245
+ name: "adminBasePath",
2246
+ ok: Boolean(config.adminBasePath && normalizeBasePath(config.adminBasePath).startsWith("/")),
2247
+ message: "adminBasePath must be an absolute route path."
2248
+ },
2249
+ {
2250
+ name: "runtimeBasePath",
2251
+ ok: Boolean(config.runtimeBasePath && normalizeBasePath(config.runtimeBasePath).startsWith("/")),
2252
+ message: "runtimeBasePath must be an absolute route path."
2253
+ },
2254
+ {
2255
+ name: "transport",
2256
+ ok: Boolean(config.transport),
2257
+ message: "API transport is required."
2258
+ }
2259
+ ].map((check) => check.ok ? { name: check.name, ok: true } : check);
2260
+ return { ok: checks.every((check) => check.ok), checks };
2261
+ }
432
2262
  export {
433
- createBuilderIntegration
2263
+ createBuilderIntegration,
2264
+ createBuilderPortalIntegration,
2265
+ validateBuilderFrontendSetup
434
2266
  };
435
2267
  //# sourceMappingURL=index.js.map