@magnet-cms/plugin-playground 2.0.0

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.
@@ -0,0 +1,2163 @@
1
+ var MagnetPluginPlayground = (function(exports, react, reactRouterDom, jsxRuntime, admin, components$1, lucideReact, lib, utils) {
2
+ "use strict";
3
+ const manifest = {
4
+ pluginName: "playground",
5
+ routes: [
6
+ {
7
+ path: "playground",
8
+ componentId: "PlaygroundIndex",
9
+ children: [
10
+ { path: "", componentId: "PlaygroundIndex" },
11
+ { path: "new", componentId: "PlaygroundEditor" },
12
+ { path: ":schemaName", componentId: "PlaygroundEditor" }
13
+ ]
14
+ }
15
+ ],
16
+ sidebar: [
17
+ {
18
+ id: "playground",
19
+ title: "Playground",
20
+ url: "/playground",
21
+ icon: "Boxes",
22
+ order: 20
23
+ }
24
+ ]
25
+ };
26
+ const components = {
27
+ PlaygroundIndex: () => Promise.resolve().then(() => index$1),
28
+ PlaygroundEditor: () => Promise.resolve().then(() => index)
29
+ };
30
+ function registerPlugin() {
31
+ if (!window.__MAGNET_PLUGINS__) {
32
+ window.__MAGNET_PLUGINS__ = [];
33
+ }
34
+ const alreadyRegistered = window.__MAGNET_PLUGINS__.some(
35
+ (p) => p.manifest.pluginName === manifest.pluginName
36
+ );
37
+ if (!alreadyRegistered) {
38
+ window.__MAGNET_PLUGINS__.push({ manifest, components });
39
+ console.log(`[Magnet] Plugin registered: ${manifest.pluginName}`);
40
+ }
41
+ }
42
+ registerPlugin();
43
+ const playgroundPlugin = () => ({ manifest, components });
44
+ const Playground = () => {
45
+ const navigate = reactRouterDom.useNavigate();
46
+ react.useEffect(() => {
47
+ navigate("/playground/new", { replace: true });
48
+ }, [navigate]);
49
+ return null;
50
+ };
51
+ const index$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
52
+ __proto__: null,
53
+ default: Playground
54
+ }, Symbol.toStringTag, { value: "Module" }));
55
+ const FIELD_TYPES = [
56
+ {
57
+ id: "text",
58
+ label: "Text",
59
+ description: "Short or long text content",
60
+ icon: lucideReact.Type,
61
+ tsType: "string",
62
+ defaultValidations: [{ type: "IsString" }, { type: "IsNotEmpty" }],
63
+ defaultUI: { type: "text" }
64
+ },
65
+ {
66
+ id: "number",
67
+ label: "Number",
68
+ description: "Integer or decimal numbers",
69
+ icon: lucideReact.Hash,
70
+ tsType: "number",
71
+ defaultValidations: [{ type: "IsNumber" }],
72
+ defaultUI: { type: "number" }
73
+ },
74
+ {
75
+ id: "date",
76
+ label: "Date",
77
+ description: "Date and time values",
78
+ icon: lucideReact.Calendar,
79
+ tsType: "Date",
80
+ defaultValidations: [{ type: "IsDate" }],
81
+ defaultUI: { type: "date" },
82
+ requiresTransformer: true
83
+ },
84
+ {
85
+ id: "boolean",
86
+ label: "Boolean",
87
+ description: "True or false toggle",
88
+ icon: lucideReact.ToggleLeft,
89
+ tsType: "boolean",
90
+ defaultValidations: [{ type: "IsBoolean" }],
91
+ defaultUI: { type: "switch" }
92
+ },
93
+ {
94
+ id: "select",
95
+ label: "Select",
96
+ description: "Dropdown with predefined options",
97
+ icon: lucideReact.List,
98
+ tsType: "string",
99
+ defaultValidations: [{ type: "IsString" }],
100
+ defaultUI: { type: "select", options: [] },
101
+ hasOptions: true
102
+ },
103
+ {
104
+ id: "relation",
105
+ label: "Relation",
106
+ description: "Reference to another schema",
107
+ icon: lucideReact.Link2,
108
+ tsType: "string",
109
+ defaultValidations: [{ type: "IsString" }],
110
+ defaultUI: { type: "relationship" },
111
+ hasRelationConfig: true
112
+ }
113
+ ];
114
+ function getFieldTypeDefinition(type) {
115
+ return FIELD_TYPES.find((ft) => ft.id === type);
116
+ }
117
+ const UI_SUBTYPES = {
118
+ text: [
119
+ { id: "text", label: "Text Input", description: "Single line text" },
120
+ { id: "textarea", label: "Text Area", description: "Multi-line text" },
121
+ { id: "email", label: "Email", description: "Email with validation" },
122
+ { id: "phone", label: "Phone", description: "Phone number input" },
123
+ { id: "richText", label: "Rich Text", description: "Rich text editor" }
124
+ ],
125
+ number: [
126
+ {
127
+ id: "number",
128
+ label: "Number Input",
129
+ description: "Standard number field"
130
+ },
131
+ {
132
+ id: "quantity",
133
+ label: "Quantity",
134
+ description: "Number with +/- buttons"
135
+ }
136
+ ],
137
+ date: [
138
+ {
139
+ id: "date",
140
+ label: "Date Picker",
141
+ description: "Calendar date selection"
142
+ }
143
+ ],
144
+ boolean: [
145
+ { id: "switch", label: "Switch", description: "Toggle switch" },
146
+ { id: "checkbox", label: "Checkbox", description: "Checkbox input" }
147
+ ],
148
+ select: [
149
+ { id: "select", label: "Dropdown", description: "Single select dropdown" },
150
+ {
151
+ id: "multiSelect",
152
+ label: "Multi Select",
153
+ description: "Select multiple options"
154
+ },
155
+ { id: "combobox", label: "Combobox", description: "Searchable dropdown" },
156
+ { id: "radio", label: "Radio Group", description: "Radio button options" }
157
+ ],
158
+ relation: [
159
+ {
160
+ id: "relationship",
161
+ label: "Relationship",
162
+ description: "Link to another schema"
163
+ }
164
+ ]
165
+ };
166
+ const VALIDATION_RULES_BY_TYPE = {
167
+ text: [
168
+ "IsString",
169
+ "IsNotEmpty",
170
+ "Length",
171
+ "MinLength",
172
+ "MaxLength",
173
+ "IsEmail",
174
+ "IsUrl",
175
+ "IsUUID",
176
+ "Matches"
177
+ ],
178
+ number: ["IsNumber", "IsInt", "IsPositive", "IsNegative", "Min", "Max"],
179
+ date: ["IsDate", "IsNotEmpty", "MinDate", "MaxDate"],
180
+ boolean: ["IsBoolean"],
181
+ select: ["IsString", "IsNotEmpty", "IsIn"],
182
+ relation: ["IsString", "IsNotEmpty", "IsMongoId"]
183
+ };
184
+ const VALIDATION_RULE_DEFINITIONS = [
185
+ {
186
+ type: "IsString",
187
+ label: "Is String",
188
+ description: "Must be a string",
189
+ constraintCount: 0
190
+ },
191
+ {
192
+ type: "IsNumber",
193
+ label: "Is Number",
194
+ description: "Must be a number",
195
+ constraintCount: 0
196
+ },
197
+ {
198
+ type: "IsInt",
199
+ label: "Is Integer",
200
+ description: "Must be an integer",
201
+ constraintCount: 0
202
+ },
203
+ {
204
+ type: "IsBoolean",
205
+ label: "Is Boolean",
206
+ description: "Must be true or false",
207
+ constraintCount: 0
208
+ },
209
+ {
210
+ type: "IsDate",
211
+ label: "Is Date",
212
+ description: "Must be a valid date",
213
+ constraintCount: 0
214
+ },
215
+ {
216
+ type: "IsNotEmpty",
217
+ label: "Not Empty",
218
+ description: "Cannot be empty",
219
+ constraintCount: 0
220
+ },
221
+ {
222
+ type: "IsEmail",
223
+ label: "Is Email",
224
+ description: "Must be a valid email",
225
+ constraintCount: 0
226
+ },
227
+ {
228
+ type: "IsUrl",
229
+ label: "Is URL",
230
+ description: "Must be a valid URL",
231
+ constraintCount: 0
232
+ },
233
+ {
234
+ type: "IsUUID",
235
+ label: "Is UUID",
236
+ description: "Must be a valid UUID",
237
+ constraintCount: 0
238
+ },
239
+ {
240
+ type: "IsMongoId",
241
+ label: "Is MongoDB ID",
242
+ description: "Must be a valid MongoDB ObjectId",
243
+ constraintCount: 0
244
+ },
245
+ {
246
+ type: "IsPositive",
247
+ label: "Is Positive",
248
+ description: "Must be a positive number",
249
+ constraintCount: 0
250
+ },
251
+ {
252
+ type: "IsNegative",
253
+ label: "Is Negative",
254
+ description: "Must be a negative number",
255
+ constraintCount: 0
256
+ },
257
+ {
258
+ type: "Length",
259
+ label: "Length",
260
+ description: "String length between min and max",
261
+ constraintCount: 2,
262
+ constraintLabels: ["Min", "Max"]
263
+ },
264
+ {
265
+ type: "MinLength",
266
+ label: "Min Length",
267
+ description: "Minimum string length",
268
+ constraintCount: 1,
269
+ constraintLabels: ["Min"]
270
+ },
271
+ {
272
+ type: "MaxLength",
273
+ label: "Max Length",
274
+ description: "Maximum string length",
275
+ constraintCount: 1,
276
+ constraintLabels: ["Max"]
277
+ },
278
+ {
279
+ type: "Min",
280
+ label: "Minimum",
281
+ description: "Minimum number value",
282
+ constraintCount: 1,
283
+ constraintLabels: ["Min"]
284
+ },
285
+ {
286
+ type: "Max",
287
+ label: "Maximum",
288
+ description: "Maximum number value",
289
+ constraintCount: 1,
290
+ constraintLabels: ["Max"]
291
+ },
292
+ {
293
+ type: "Matches",
294
+ label: "Matches Pattern",
295
+ description: "Must match regex pattern",
296
+ constraintCount: 1,
297
+ constraintLabels: ["Pattern"]
298
+ },
299
+ {
300
+ type: "IsIn",
301
+ label: "Is In List",
302
+ description: "Must be one of the allowed values",
303
+ constraintCount: 1,
304
+ constraintLabels: ["Values (comma-separated)"]
305
+ }
306
+ ];
307
+ function getValidationRuleDefinition(type) {
308
+ return VALIDATION_RULE_DEFINITIONS.find((r) => r.type === type);
309
+ }
310
+ const RELATION_TYPES = [
311
+ {
312
+ value: "oneToOne",
313
+ label: "One to One",
314
+ description: "Each record relates to exactly one other"
315
+ },
316
+ {
317
+ value: "oneToMany",
318
+ label: "One to Many",
319
+ description: "One record relates to many others"
320
+ },
321
+ {
322
+ value: "manyToOne",
323
+ label: "Many to One",
324
+ description: "Many records relate to one"
325
+ },
326
+ {
327
+ value: "manyToMany",
328
+ label: "Many to Many",
329
+ description: "Many records relate to many others"
330
+ }
331
+ ];
332
+ const FIELD_TYPE_COLORS = {
333
+ text: "bg-blue-50 text-blue-700 border-blue-200",
334
+ number: "bg-amber-50 text-amber-700 border-amber-200",
335
+ date: "bg-green-50 text-green-700 border-green-200",
336
+ boolean: "bg-purple-50 text-purple-700 border-purple-200",
337
+ select: "bg-cyan-50 text-cyan-700 border-cyan-200",
338
+ relation: "bg-indigo-50 text-indigo-700 border-indigo-200"
339
+ };
340
+ const DEFAULT_SCHEMA_CONFIG = {
341
+ name: "",
342
+ versioning: true,
343
+ i18n: true
344
+ };
345
+ const DEFAULT_BUILDER_STATE = {
346
+ schema: DEFAULT_SCHEMA_CONFIG,
347
+ fields: [],
348
+ selectedFieldId: null,
349
+ viewMode: "builder",
350
+ isDirty: false,
351
+ lastSaved: null,
352
+ isNew: true
353
+ };
354
+ const FIELD_TYPE_TO_TS_TYPE = {
355
+ text: "string",
356
+ number: "number",
357
+ date: "Date",
358
+ boolean: "boolean",
359
+ select: "string",
360
+ relation: "string"
361
+ // ObjectId reference
362
+ };
363
+ function generateSchemaCode(state) {
364
+ if (!state.schema.name) {
365
+ return "// Enter a schema name to generate code";
366
+ }
367
+ const imports = generateImports(state.fields);
368
+ const classDecorator = generateSchemaDecorator(state.schema);
369
+ const properties = state.fields.map((field) => generateFieldCode(field)).join("\n\n");
370
+ return `${imports}
371
+
372
+ ${classDecorator}
373
+ export class ${state.schema.name} {
374
+ ${properties}
375
+ }
376
+ `;
377
+ }
378
+ function generateImports(fields) {
379
+ const magnetImports = /* @__PURE__ */ new Set(["Schema", "Prop", "UI"]);
380
+ const validatorImports = /* @__PURE__ */ new Set();
381
+ let needsTypeTransformer = false;
382
+ for (const field of fields) {
383
+ if (field.validations.length > 0) {
384
+ magnetImports.add("Validators");
385
+ for (const v of field.validations) {
386
+ validatorImports.add(v.type);
387
+ }
388
+ }
389
+ if (field.type === "date") {
390
+ needsTypeTransformer = true;
391
+ }
392
+ }
393
+ const lines = [];
394
+ lines.push(
395
+ `import { ${Array.from(magnetImports).sort().join(", ")} } from '@magnet-cms/common'`
396
+ );
397
+ if (needsTypeTransformer) {
398
+ lines.push(`import { Type } from 'class-transformer'`);
399
+ }
400
+ if (validatorImports.size > 0) {
401
+ lines.push(
402
+ `import {
403
+ ${Array.from(validatorImports).sort().join(",\n ")},
404
+ } from 'class-validator'`
405
+ );
406
+ }
407
+ return lines.join("\n");
408
+ }
409
+ function generateSchemaDecorator(schema) {
410
+ const options = [];
411
+ if (schema.versioning !== void 0) {
412
+ options.push(`versioning: ${schema.versioning}`);
413
+ }
414
+ if (schema.i18n !== void 0) {
415
+ options.push(`i18n: ${schema.i18n}`);
416
+ }
417
+ if (options.length === 0) {
418
+ return "@Schema()";
419
+ }
420
+ return `@Schema({ ${options.join(", ")} })`;
421
+ }
422
+ function generateFieldCode(field) {
423
+ const decorators = [];
424
+ const indent = " ";
425
+ if (field.type === "date") {
426
+ decorators.push(`${indent}@Type(() => Date)`);
427
+ }
428
+ decorators.push(`${indent}@Prop(${generatePropOptions(field)})`);
429
+ if (field.validations.length > 0) {
430
+ const validators = field.validations.map((v) => formatValidator(v)).join(", ");
431
+ decorators.push(`${indent}@Validators(${validators})`);
432
+ }
433
+ decorators.push(`${indent}@UI(${generateUIOptions(field)})`);
434
+ const declaration = `${indent}${field.name}: ${field.tsType}`;
435
+ return [...decorators, declaration].join("\n");
436
+ }
437
+ function generatePropOptions(field) {
438
+ const options = [];
439
+ if (field.prop.required) {
440
+ options.push("required: true");
441
+ }
442
+ if (field.prop.unique) {
443
+ options.push("unique: true");
444
+ }
445
+ if (field.prop.intl) {
446
+ options.push("intl: true");
447
+ }
448
+ if (field.prop.hidden) {
449
+ options.push("hidden: true");
450
+ }
451
+ if (field.prop.readonly) {
452
+ options.push("readonly: true");
453
+ }
454
+ if (field.prop.default !== void 0) {
455
+ options.push(`default: ${JSON.stringify(field.prop.default)}`);
456
+ }
457
+ if (options.length === 0) {
458
+ return "";
459
+ }
460
+ return `{ ${options.join(", ")} }`;
461
+ }
462
+ function generateUIOptions(field) {
463
+ const options = [];
464
+ if (field.ui.tab) {
465
+ options.push(`tab: '${field.ui.tab}'`);
466
+ }
467
+ if (field.ui.side) {
468
+ options.push("side: true");
469
+ }
470
+ if (field.ui.type) {
471
+ options.push(`type: '${field.ui.type}'`);
472
+ }
473
+ if (field.ui.label && field.ui.label !== field.displayName) {
474
+ options.push(`label: '${escapeString(field.ui.label)}'`);
475
+ }
476
+ if (field.ui.description) {
477
+ options.push(`description: '${escapeString(field.ui.description)}'`);
478
+ }
479
+ if (field.ui.placeholder) {
480
+ options.push(`placeholder: '${escapeString(field.ui.placeholder)}'`);
481
+ }
482
+ if (field.ui.row) {
483
+ options.push("row: true");
484
+ }
485
+ if (field.ui.options && field.ui.options.length > 0) {
486
+ const optionsStr = field.ui.options.map(
487
+ (o) => `{ key: '${escapeString(o.key)}', value: '${escapeString(o.value)}' }`
488
+ ).join(", ");
489
+ options.push(`options: [${optionsStr}]`);
490
+ }
491
+ if (options.length === 0) {
492
+ return "{}";
493
+ }
494
+ return `{ ${options.join(", ")} }`;
495
+ }
496
+ function formatValidator(rule) {
497
+ if (!rule.constraints || rule.constraints.length === 0) {
498
+ return `${rule.type}()`;
499
+ }
500
+ const args = rule.constraints.map((c) => {
501
+ if (typeof c === "string") {
502
+ if (rule.type === "Matches") {
503
+ return c.startsWith("/") ? c : `/${c}/`;
504
+ }
505
+ return `'${escapeString(String(c))}'`;
506
+ }
507
+ return String(c);
508
+ }).join(", ");
509
+ return `${rule.type}(${args})`;
510
+ }
511
+ function escapeString(str) {
512
+ return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
513
+ }
514
+ function generateSchemaJSON(state) {
515
+ return {
516
+ name: state.schema.name,
517
+ options: {
518
+ versioning: state.schema.versioning,
519
+ i18n: state.schema.i18n
520
+ },
521
+ properties: state.fields.map((field) => ({
522
+ name: field.name,
523
+ displayName: field.displayName,
524
+ type: field.type,
525
+ tsType: field.tsType,
526
+ required: field.prop.required,
527
+ unique: field.prop.unique,
528
+ intl: field.prop.intl,
529
+ ui: field.ui,
530
+ validations: field.validations,
531
+ ...field.relationConfig && { relationConfig: field.relationConfig }
532
+ }))
533
+ };
534
+ }
535
+ function generateId() {
536
+ return `field_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
537
+ }
538
+ function toApiId(displayName) {
539
+ return displayName.toLowerCase().replace(/[^a-z0-9\s]/g, "").split(/\s+/).filter(Boolean).map(
540
+ (word, index2) => index2 === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1)
541
+ ).join("");
542
+ }
543
+ function schemaBuilderReducer(state, action) {
544
+ switch (action.type) {
545
+ case "SET_SCHEMA":
546
+ return {
547
+ ...state,
548
+ schema: { ...state.schema, ...action.payload },
549
+ isDirty: true
550
+ };
551
+ case "ADD_FIELD":
552
+ return {
553
+ ...state,
554
+ fields: [...state.fields, action.payload],
555
+ selectedFieldId: action.payload.id,
556
+ isDirty: true
557
+ };
558
+ case "UPDATE_FIELD":
559
+ return {
560
+ ...state,
561
+ fields: state.fields.map(
562
+ (field) => field.id === action.payload.id ? { ...field, ...action.payload.changes } : field
563
+ ),
564
+ isDirty: true
565
+ };
566
+ case "DELETE_FIELD":
567
+ return {
568
+ ...state,
569
+ fields: state.fields.filter((field) => field.id !== action.payload),
570
+ selectedFieldId: state.selectedFieldId === action.payload ? null : state.selectedFieldId,
571
+ isDirty: true
572
+ };
573
+ case "REORDER_FIELDS":
574
+ return {
575
+ ...state,
576
+ fields: action.payload,
577
+ isDirty: true
578
+ };
579
+ case "SELECT_FIELD":
580
+ return {
581
+ ...state,
582
+ selectedFieldId: action.payload
583
+ };
584
+ case "SET_VIEW_MODE":
585
+ return {
586
+ ...state,
587
+ viewMode: action.payload
588
+ };
589
+ case "MARK_DIRTY":
590
+ return {
591
+ ...state,
592
+ isDirty: true
593
+ };
594
+ case "MARK_SAVED":
595
+ return {
596
+ ...state,
597
+ isDirty: false,
598
+ lastSaved: /* @__PURE__ */ new Date()
599
+ };
600
+ case "RESET":
601
+ return action.payload;
602
+ case "LOAD_SCHEMA":
603
+ return {
604
+ ...state,
605
+ schema: action.payload.schema,
606
+ fields: action.payload.fields,
607
+ isDirty: false,
608
+ isNew: false
609
+ };
610
+ default:
611
+ return state;
612
+ }
613
+ }
614
+ const SchemaBuilderContext = react.createContext(null);
615
+ function useSchemaBuilder() {
616
+ const context = react.useContext(SchemaBuilderContext);
617
+ if (!context) {
618
+ throw new Error(
619
+ "useSchemaBuilder must be used within a SchemaBuilderProvider"
620
+ );
621
+ }
622
+ return context;
623
+ }
624
+ function useSchemaBuilderState(initialState = DEFAULT_BUILDER_STATE) {
625
+ const [state, dispatch] = react.useReducer(schemaBuilderReducer, initialState);
626
+ const addField = react.useCallback((partial) => {
627
+ const fieldType = partial.type || "text";
628
+ const definition = getFieldTypeDefinition(fieldType);
629
+ const displayName = partial.displayName || "New Field";
630
+ const name = partial.name || toApiId(displayName);
631
+ const field = {
632
+ id: generateId(),
633
+ name,
634
+ displayName,
635
+ type: fieldType,
636
+ tsType: (definition == null ? void 0 : definition.tsType) || FIELD_TYPE_TO_TS_TYPE[fieldType] || "string",
637
+ prop: {
638
+ required: false,
639
+ unique: false,
640
+ ...partial.prop
641
+ },
642
+ ui: {
643
+ tab: "General",
644
+ ...definition == null ? void 0 : definition.defaultUI,
645
+ ...partial.ui
646
+ },
647
+ validations: partial.validations || (definition == null ? void 0 : definition.defaultValidations) || [],
648
+ relationConfig: partial.relationConfig
649
+ };
650
+ dispatch({ type: "ADD_FIELD", payload: field });
651
+ }, []);
652
+ const updateField = react.useCallback(
653
+ (id, changes) => {
654
+ dispatch({ type: "UPDATE_FIELD", payload: { id, changes } });
655
+ },
656
+ []
657
+ );
658
+ const deleteField = react.useCallback((id) => {
659
+ dispatch({ type: "DELETE_FIELD", payload: id });
660
+ }, []);
661
+ const selectField = react.useCallback((id) => {
662
+ dispatch({ type: "SELECT_FIELD", payload: id });
663
+ }, []);
664
+ const reorderFields = react.useCallback((fields) => {
665
+ dispatch({ type: "REORDER_FIELDS", payload: fields });
666
+ }, []);
667
+ const setViewMode = react.useCallback((mode) => {
668
+ dispatch({ type: "SET_VIEW_MODE", payload: mode });
669
+ }, []);
670
+ const updateSchema = react.useCallback((changes) => {
671
+ dispatch({ type: "SET_SCHEMA", payload: changes });
672
+ }, []);
673
+ const resetState = react.useCallback((newState) => {
674
+ dispatch({ type: "RESET", payload: newState || DEFAULT_BUILDER_STATE });
675
+ }, []);
676
+ const selectedField = react.useMemo(
677
+ () => state.fields.find((f) => f.id === state.selectedFieldId) ?? null,
678
+ [state.fields, state.selectedFieldId]
679
+ );
680
+ const generatedCode = react.useMemo(() => generateSchemaCode(state), [state]);
681
+ const generatedJSON = react.useMemo(() => generateSchemaJSON(state), [state]);
682
+ return {
683
+ state,
684
+ dispatch,
685
+ addField,
686
+ updateField,
687
+ deleteField,
688
+ selectField,
689
+ reorderFields,
690
+ setViewMode,
691
+ updateSchema,
692
+ resetState,
693
+ selectedField,
694
+ generatedCode,
695
+ generatedJSON
696
+ };
697
+ }
698
+ function createFieldFromType(type, displayName) {
699
+ const definition = getFieldTypeDefinition(type);
700
+ if (!definition) {
701
+ return { type, displayName };
702
+ }
703
+ return {
704
+ type,
705
+ displayName,
706
+ name: toApiId(displayName),
707
+ tsType: definition.tsType,
708
+ validations: [...definition.defaultValidations],
709
+ ui: { ...definition.defaultUI },
710
+ prop: { required: false, unique: false },
711
+ ...definition.hasRelationConfig && {
712
+ relationConfig: {
713
+ targetSchema: "",
714
+ relationType: "manyToOne"
715
+ }
716
+ }
717
+ };
718
+ }
719
+ function AddFieldDialog({ open, onOpenChange }) {
720
+ const { addField } = useSchemaBuilder();
721
+ const [selectedType, setSelectedType] = react.useState("text");
722
+ const [displayName, setDisplayName] = react.useState("");
723
+ const [error, setError] = react.useState("");
724
+ const handleAdd = () => {
725
+ if (!displayName.trim()) {
726
+ setError("Display name is required");
727
+ return;
728
+ }
729
+ const fieldData = createFieldFromType(selectedType, displayName.trim());
730
+ addField(fieldData);
731
+ handleClose();
732
+ };
733
+ const handleClose = () => {
734
+ setSelectedType("text");
735
+ setDisplayName("");
736
+ setError("");
737
+ onOpenChange(false);
738
+ };
739
+ const handleQuickAdd = (type) => {
740
+ const fieldData = createFieldFromType(type.id, type.label);
741
+ addField(fieldData);
742
+ handleClose();
743
+ };
744
+ return /* @__PURE__ */ jsxRuntime.jsx(components$1.Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(components$1.DialogContent, { className: "sm:max-w-lg", children: [
745
+ /* @__PURE__ */ jsxRuntime.jsxs(components$1.DialogHeader, { children: [
746
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.DialogTitle, { children: "Add New Field" }),
747
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.DialogDescription, { children: "Choose a field type and enter a display name for your new field." })
748
+ ] }),
749
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6 py-4", children: [
750
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
751
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { className: "text-xs text-muted-foreground uppercase tracking-wider mb-3 block", children: "Quick Add" }),
752
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-3 gap-2", children: FIELD_TYPES.map((type) => {
753
+ const Icon = type.icon;
754
+ return /* @__PURE__ */ jsxRuntime.jsxs(
755
+ "button",
756
+ {
757
+ type: "button",
758
+ onClick: () => handleQuickAdd(type),
759
+ className: lib.cn(
760
+ "p-3 rounded-lg border text-left hover:shadow-sm transition-all",
761
+ "hover:border-muted-foreground/50"
762
+ ),
763
+ children: [
764
+ /* @__PURE__ */ jsxRuntime.jsx(
765
+ "div",
766
+ {
767
+ className: lib.cn(
768
+ "w-8 h-8 rounded-lg flex items-center justify-center mb-2",
769
+ FIELD_TYPE_COLORS[type.id]
770
+ ),
771
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-4 w-4" })
772
+ }
773
+ ),
774
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium", children: type.label }),
775
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[10px] text-muted-foreground line-clamp-1", children: type.description })
776
+ ]
777
+ },
778
+ type.id
779
+ );
780
+ }) })
781
+ ] }),
782
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
783
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-full border-t" }) }),
784
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex justify-center text-xs uppercase", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "bg-background px-2 text-muted-foreground", children: "Or customize" }) })
785
+ ] }),
786
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
787
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
788
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { htmlFor: "fieldType", children: "Field Type" }),
789
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-3 gap-2", children: FIELD_TYPES.map((type) => {
790
+ const Icon = type.icon;
791
+ return /* @__PURE__ */ jsxRuntime.jsxs(
792
+ "button",
793
+ {
794
+ type: "button",
795
+ onClick: () => setSelectedType(type.id),
796
+ className: lib.cn(
797
+ "p-2 rounded-lg border flex items-center gap-2 transition-all",
798
+ selectedType === type.id ? "border-foreground bg-muted" : "hover:border-muted-foreground/50"
799
+ ),
800
+ children: [
801
+ /* @__PURE__ */ jsxRuntime.jsx(
802
+ "div",
803
+ {
804
+ className: lib.cn(
805
+ "w-6 h-6 rounded flex items-center justify-center",
806
+ FIELD_TYPE_COLORS[type.id]
807
+ ),
808
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-3 w-3" })
809
+ }
810
+ ),
811
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", children: type.label })
812
+ ]
813
+ },
814
+ type.id
815
+ );
816
+ }) })
817
+ ] }),
818
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
819
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { htmlFor: "displayName", children: "Display Name" }),
820
+ /* @__PURE__ */ jsxRuntime.jsx(
821
+ components$1.Input,
822
+ {
823
+ id: "displayName",
824
+ placeholder: "e.g., First Name, Email Address, Birth Date...",
825
+ value: displayName,
826
+ onChange: (e) => {
827
+ setDisplayName(e.target.value);
828
+ setError("");
829
+ },
830
+ onKeyDown: (e) => {
831
+ if (e.key === "Enter" && displayName.trim()) {
832
+ handleAdd();
833
+ }
834
+ }
835
+ }
836
+ ),
837
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", children: error }),
838
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "The API ID will be automatically generated from the display name" })
839
+ ] })
840
+ ] })
841
+ ] }),
842
+ /* @__PURE__ */ jsxRuntime.jsxs(components$1.DialogFooter, { children: [
843
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Button, { variant: "outline", onClick: handleClose, children: "Cancel" }),
844
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Button, { onClick: handleAdd, disabled: !displayName.trim(), children: "Add Field" })
845
+ ] })
846
+ ] }) });
847
+ }
848
+ function CodePreview({ mode }) {
849
+ const { generatedCode, generatedJSON } = useSchemaBuilder();
850
+ const [copied, setCopied] = react.useState(false);
851
+ const content = mode === "code" ? generatedCode : JSON.stringify(generatedJSON, null, 2);
852
+ const handleCopy = async () => {
853
+ await navigator.clipboard.writeText(content);
854
+ setCopied(true);
855
+ setTimeout(() => setCopied(false), 2e3);
856
+ };
857
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex flex-col border rounded-xl overflow-hidden bg-muted/30", children: [
858
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-muted/50 border-b px-4 py-2 flex items-center justify-between", children: [
859
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: mode === "code" ? "Generated TypeScript" : "JSON Schema" }),
860
+ /* @__PURE__ */ jsxRuntime.jsx(
861
+ components$1.Button,
862
+ {
863
+ variant: "ghost",
864
+ size: "sm",
865
+ className: "h-7 text-xs",
866
+ onClick: handleCopy,
867
+ children: copied ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
868
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-3 w-3 mr-1.5 text-green-500" }),
869
+ "Copied"
870
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
871
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { className: "h-3 w-3 mr-1.5" }),
872
+ "Copy"
873
+ ] })
874
+ }
875
+ )
876
+ ] }),
877
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-auto p-4", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "text-xs font-mono text-muted-foreground whitespace-pre-wrap", children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: content }) }) })
878
+ ] });
879
+ }
880
+ function FieldCard({
881
+ field,
882
+ isSelected,
883
+ isDragging,
884
+ onClick,
885
+ onEdit,
886
+ onDelete,
887
+ showActions = true,
888
+ showDragHandle = true
889
+ }) {
890
+ const definition = getFieldTypeDefinition(field.type);
891
+ const Icon = definition == null ? void 0 : definition.icon;
892
+ const handleKeyDown = (e) => {
893
+ if (e.key === "Enter" || e.key === " ") {
894
+ e.preventDefault();
895
+ onClick == null ? void 0 : onClick();
896
+ }
897
+ };
898
+ return (
899
+ // biome-ignore lint/a11y/useSemanticElements: Using div for drag-and-drop compatibility
900
+ /* @__PURE__ */ jsxRuntime.jsxs(
901
+ "div",
902
+ {
903
+ role: "button",
904
+ tabIndex: 0,
905
+ className: lib.cn(
906
+ "group p-4 flex items-center justify-between transition-colors cursor-pointer border-l-2",
907
+ isSelected ? "bg-muted/80 border-l-foreground" : "hover:bg-muted/50 border-l-transparent hover:border-l-muted-foreground/50",
908
+ isDragging && "opacity-50"
909
+ ),
910
+ onClick,
911
+ onKeyDown: handleKeyDown,
912
+ children: [
913
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
914
+ showDragHandle ? /* @__PURE__ */ jsxRuntime.jsx(components$1.SortableItemHandle, { className: "text-muted-foreground/50 hover:text-muted-foreground touch-none", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GripVertical, { className: "h-4 w-4" }) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-muted-foreground/50", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GripVertical, { className: "h-4 w-4" }) }),
915
+ /* @__PURE__ */ jsxRuntime.jsx(
916
+ "div",
917
+ {
918
+ className: lib.cn(
919
+ "w-8 h-8 rounded-lg flex items-center justify-center border",
920
+ FIELD_TYPE_COLORS[field.type] || "bg-muted text-muted-foreground border-muted"
921
+ ),
922
+ children: Icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-4 w-4" })
923
+ }
924
+ ),
925
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
926
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
927
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold", children: field.displayName }),
928
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-mono text-muted-foreground bg-muted px-1.5 py-0.5 rounded border", children: field.name })
929
+ ] }),
930
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
931
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground capitalize", children: (definition == null ? void 0 : definition.label) || field.type }),
932
+ field.prop.required && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
933
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-0.5 h-0.5 rounded-full bg-muted-foreground/50" }),
934
+ /* @__PURE__ */ jsxRuntime.jsx(
935
+ components$1.Badge,
936
+ {
937
+ variant: "outline",
938
+ className: "text-[10px] px-1.5 py-0 h-4 bg-emerald-50 text-emerald-700 border-emerald-200",
939
+ children: "Required"
940
+ }
941
+ )
942
+ ] }),
943
+ field.prop.unique && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
944
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-0.5 h-0.5 rounded-full bg-muted-foreground/50" }),
945
+ /* @__PURE__ */ jsxRuntime.jsx(
946
+ components$1.Badge,
947
+ {
948
+ variant: "outline",
949
+ className: "text-[10px] px-1.5 py-0 h-4 bg-amber-50 text-amber-700 border-amber-200",
950
+ children: "Unique"
951
+ }
952
+ )
953
+ ] }),
954
+ field.relationConfig && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
955
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-0.5 h-0.5 rounded-full bg-muted-foreground/50" }),
956
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
957
+ field.relationConfig.relationType,
958
+ " (",
959
+ field.relationConfig.targetSchema || "...",
960
+ ")"
961
+ ] })
962
+ ] })
963
+ ] })
964
+ ] })
965
+ ] }),
966
+ showActions && /* @__PURE__ */ jsxRuntime.jsxs(
967
+ "div",
968
+ {
969
+ className: lib.cn(
970
+ "flex items-center gap-1 transition-opacity",
971
+ isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100"
972
+ ),
973
+ children: [
974
+ /* @__PURE__ */ jsxRuntime.jsx(
975
+ components$1.Button,
976
+ {
977
+ variant: "ghost",
978
+ size: "icon",
979
+ className: "h-8 w-8",
980
+ onClick: (e) => {
981
+ e.stopPropagation();
982
+ onEdit == null ? void 0 : onEdit();
983
+ },
984
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-3.5 w-3.5" })
985
+ }
986
+ ),
987
+ /* @__PURE__ */ jsxRuntime.jsx(
988
+ components$1.Button,
989
+ {
990
+ variant: "ghost",
991
+ size: "icon",
992
+ className: "h-8 w-8 text-muted-foreground hover:text-destructive hover:bg-destructive/10",
993
+ onClick: (e) => {
994
+ e.stopPropagation();
995
+ onDelete == null ? void 0 : onDelete();
996
+ },
997
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3.5 w-3.5" })
998
+ }
999
+ )
1000
+ ]
1001
+ }
1002
+ )
1003
+ ]
1004
+ }
1005
+ )
1006
+ );
1007
+ }
1008
+ function FieldList({ onAddField, readOnly = false }) {
1009
+ const { state, selectField, deleteField, reorderFields } = useSchemaBuilder();
1010
+ const handleReorder = (reorderedFields) => {
1011
+ reorderFields(reorderedFields);
1012
+ };
1013
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border rounded-xl overflow-hidden bg-background shadow-sm", children: [
1014
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-muted/50 border-b px-4 py-2 flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "text-xs font-semibold text-muted-foreground uppercase tracking-wider", children: [
1015
+ "Fields (",
1016
+ state.fields.length,
1017
+ ")"
1018
+ ] }) }),
1019
+ state.fields.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-8 text-center text-muted-foreground", children: [
1020
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "mx-auto h-10 w-10 mb-3 opacity-50" }),
1021
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium mb-1", children: "No fields yet" }),
1022
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs", children: readOnly ? "This schema has no fields defined" : "Add your first field to start building your schema" })
1023
+ ] }) : readOnly ? (
1024
+ // Read-only mode: simple list without drag/drop
1025
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y", children: state.fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(
1026
+ FieldCard,
1027
+ {
1028
+ field,
1029
+ isSelected: false,
1030
+ showActions: false,
1031
+ showDragHandle: false
1032
+ },
1033
+ field.id
1034
+ )) })
1035
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
1036
+ components$1.Sortable,
1037
+ {
1038
+ value: state.fields,
1039
+ onValueChange: handleReorder,
1040
+ getItemValue: (field) => field.id,
1041
+ children: [
1042
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.SortableContent, { className: "divide-y", children: state.fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(components$1.SortableItem, { value: field.id, children: /* @__PURE__ */ jsxRuntime.jsx(
1043
+ FieldCard,
1044
+ {
1045
+ field,
1046
+ isSelected: field.id === state.selectedFieldId,
1047
+ onClick: () => selectField(field.id),
1048
+ onEdit: () => selectField(field.id),
1049
+ onDelete: () => {
1050
+ if (window.confirm(
1051
+ `Are you sure you want to delete "${field.displayName}"?`
1052
+ )) {
1053
+ deleteField(field.id);
1054
+ }
1055
+ }
1056
+ }
1057
+ ) }, field.id)) }),
1058
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.SortableOverlay, { children: ({ value }) => {
1059
+ const field = state.fields.find((f) => f.id === value);
1060
+ return field ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-background border rounded-lg shadow-lg", children: /* @__PURE__ */ jsxRuntime.jsx(
1061
+ FieldCard,
1062
+ {
1063
+ field,
1064
+ isSelected: true,
1065
+ showActions: false,
1066
+ showDragHandle: false
1067
+ }
1068
+ ) }) : null;
1069
+ } })
1070
+ ]
1071
+ }
1072
+ ),
1073
+ !readOnly && onAddField && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 bg-muted/30 border-t", children: /* @__PURE__ */ jsxRuntime.jsxs(
1074
+ "button",
1075
+ {
1076
+ type: "button",
1077
+ onClick: onAddField,
1078
+ className: "w-full py-2.5 rounded-lg border-2 border-dashed border-muted-foreground/25 hover:border-muted-foreground/50 hover:bg-muted/50 transition-all text-xs font-semibold text-muted-foreground hover:text-foreground flex items-center justify-center gap-2 group",
1079
+ children: [
1080
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-muted rounded p-0.5 group-hover:bg-muted-foreground/20 transition-colors", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-3 w-3" }) }),
1081
+ "Add new field"
1082
+ ]
1083
+ }
1084
+ ) })
1085
+ ] });
1086
+ }
1087
+ const RELATION_TYPE_OPTIONS = [
1088
+ {
1089
+ value: "oneToOne",
1090
+ label: "One to One",
1091
+ description: "Each record relates to exactly one other record",
1092
+ diagram: { leftMultiple: false, rightMultiple: false }
1093
+ },
1094
+ {
1095
+ value: "oneToMany",
1096
+ label: "One to Many",
1097
+ description: "One record can relate to many other records",
1098
+ diagram: { leftMultiple: false, rightMultiple: true }
1099
+ },
1100
+ {
1101
+ value: "manyToOne",
1102
+ label: "Many to One",
1103
+ description: "Many records can relate to one other record",
1104
+ diagram: { leftMultiple: true, rightMultiple: false }
1105
+ },
1106
+ {
1107
+ value: "manyToMany",
1108
+ label: "Many to Many",
1109
+ description: "Many records can relate to many other records",
1110
+ diagram: { leftMultiple: true, rightMultiple: true }
1111
+ }
1112
+ ];
1113
+ function RelationDiagram({
1114
+ leftLabel,
1115
+ rightLabel,
1116
+ leftMultiple,
1117
+ rightMultiple,
1118
+ small = false
1119
+ }) {
1120
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-2", children: [
1121
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
1122
+ leftMultiple && /* @__PURE__ */ jsxRuntime.jsx(
1123
+ "div",
1124
+ {
1125
+ className: lib.cn(
1126
+ "bg-background border rounded font-medium shadow-sm opacity-50 -mr-1",
1127
+ small ? "px-1.5 py-0.5 text-[10px]" : "px-2 py-1 text-xs"
1128
+ ),
1129
+ children: leftLabel
1130
+ }
1131
+ ),
1132
+ /* @__PURE__ */ jsxRuntime.jsx(
1133
+ "div",
1134
+ {
1135
+ className: lib.cn(
1136
+ "bg-background border rounded font-medium shadow-sm relative z-10",
1137
+ small ? "px-1.5 py-0.5 text-[10px]" : "px-2 py-1 text-xs"
1138
+ ),
1139
+ children: leftLabel
1140
+ }
1141
+ )
1142
+ ] }),
1143
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
1144
+ /* @__PURE__ */ jsxRuntime.jsx(
1145
+ "div",
1146
+ {
1147
+ className: lib.cn(
1148
+ "bg-muted-foreground/60 rounded-full",
1149
+ small ? "w-1.5 h-1.5" : "w-2 h-2"
1150
+ )
1151
+ }
1152
+ ),
1153
+ /* @__PURE__ */ jsxRuntime.jsx(
1154
+ "div",
1155
+ {
1156
+ className: lib.cn("h-px bg-muted-foreground/60", small ? "w-6" : "w-10")
1157
+ }
1158
+ ),
1159
+ /* @__PURE__ */ jsxRuntime.jsx(
1160
+ "div",
1161
+ {
1162
+ className: lib.cn(
1163
+ "border-r-2 border-t-2 border-b-2 border-muted-foreground/60 rotate-45",
1164
+ small ? "w-1.5 h-1.5 -ml-1" : "w-2 h-2 -ml-1.5"
1165
+ )
1166
+ }
1167
+ )
1168
+ ] }),
1169
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
1170
+ /* @__PURE__ */ jsxRuntime.jsx(
1171
+ "div",
1172
+ {
1173
+ className: lib.cn(
1174
+ "bg-background border rounded font-medium shadow-sm relative z-10",
1175
+ small ? "px-1.5 py-0.5 text-[10px]" : "px-2 py-1 text-xs"
1176
+ ),
1177
+ children: rightLabel
1178
+ }
1179
+ ),
1180
+ rightMultiple && /* @__PURE__ */ jsxRuntime.jsx(
1181
+ "div",
1182
+ {
1183
+ className: lib.cn(
1184
+ "bg-background border rounded font-medium shadow-sm opacity-50 -ml-1",
1185
+ small ? "px-1.5 py-0.5 text-[10px]" : "px-2 py-1 text-xs"
1186
+ ),
1187
+ children: rightLabel
1188
+ }
1189
+ )
1190
+ ] })
1191
+ ] });
1192
+ }
1193
+ function RelationConfigModal({
1194
+ open,
1195
+ onOpenChange,
1196
+ currentSchema,
1197
+ relationConfig,
1198
+ onSave
1199
+ }) {
1200
+ const { schemas } = admin.useAdmin();
1201
+ const [config, setConfig] = react.useState({
1202
+ targetSchema: (relationConfig == null ? void 0 : relationConfig.targetSchema) || "",
1203
+ relationType: (relationConfig == null ? void 0 : relationConfig.relationType) || "manyToOne",
1204
+ inverseSide: relationConfig == null ? void 0 : relationConfig.inverseSide
1205
+ });
1206
+ const handleSave = () => {
1207
+ onSave(config);
1208
+ onOpenChange(false);
1209
+ };
1210
+ const selectedTypeOption = RELATION_TYPE_OPTIONS.find(
1211
+ (t) => t.value === config.relationType
1212
+ );
1213
+ return /* @__PURE__ */ jsxRuntime.jsx(components$1.Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(components$1.DialogContent, { className: "sm:max-w-lg", children: [
1214
+ /* @__PURE__ */ jsxRuntime.jsxs(components$1.DialogHeader, { children: [
1215
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.DialogTitle, { children: "Configure Relation" }),
1216
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.DialogDescription, { children: "Define how this field relates to another schema" })
1217
+ ] }),
1218
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6 py-4", children: [
1219
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
1220
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { className: "text-sm font-medium", children: "Relation Type" }),
1221
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-3", children: RELATION_TYPE_OPTIONS.map((option) => /* @__PURE__ */ jsxRuntime.jsxs(
1222
+ "button",
1223
+ {
1224
+ type: "button",
1225
+ onClick: () => setConfig((c) => ({ ...c, relationType: option.value })),
1226
+ className: lib.cn(
1227
+ "p-3 rounded-lg border text-left transition-all",
1228
+ config.relationType === option.value ? "border-primary bg-primary/5 ring-1 ring-primary" : "border-border hover:border-muted-foreground/50 hover:bg-muted/50"
1229
+ ),
1230
+ children: [
1231
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsx(
1232
+ RelationDiagram,
1233
+ {
1234
+ leftLabel: currentSchema || "A",
1235
+ rightLabel: config.targetSchema || "B",
1236
+ leftMultiple: option.diagram.leftMultiple,
1237
+ rightMultiple: option.diagram.rightMultiple,
1238
+ small: true
1239
+ }
1240
+ ) }),
1241
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium", children: option.label }),
1242
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: option.description })
1243
+ ]
1244
+ },
1245
+ option.value
1246
+ )) })
1247
+ ] }),
1248
+ selectedTypeOption && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 bg-muted/30 rounded-lg border", children: [
1249
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mb-3 text-center", children: "Preview" }),
1250
+ /* @__PURE__ */ jsxRuntime.jsx(
1251
+ RelationDiagram,
1252
+ {
1253
+ leftLabel: currentSchema || "Current",
1254
+ rightLabel: config.targetSchema || "Target",
1255
+ leftMultiple: selectedTypeOption.diagram.leftMultiple,
1256
+ rightMultiple: selectedTypeOption.diagram.rightMultiple
1257
+ }
1258
+ )
1259
+ ] }),
1260
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1261
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { className: "text-sm font-medium", children: "Related Collection" }),
1262
+ /* @__PURE__ */ jsxRuntime.jsxs(
1263
+ components$1.Select,
1264
+ {
1265
+ value: config.targetSchema,
1266
+ onValueChange: (value) => setConfig((c) => ({ ...c, targetSchema: value })),
1267
+ children: [
1268
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectTrigger, { children: /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectValue, { placeholder: "Select a schema..." }) }),
1269
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectContent, { children: schemas == null ? void 0 : schemas.map((schema) => /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectItem, { value: schema, children: schema }, schema)) })
1270
+ ]
1271
+ }
1272
+ ),
1273
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "The schema this field will reference" })
1274
+ ] }),
1275
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1276
+ /* @__PURE__ */ jsxRuntime.jsxs(components$1.Label, { className: "text-sm font-medium", children: [
1277
+ "Inverse Field Name",
1278
+ " ",
1279
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground font-normal", children: "(optional)" })
1280
+ ] }),
1281
+ /* @__PURE__ */ jsxRuntime.jsx(
1282
+ components$1.Input,
1283
+ {
1284
+ value: config.inverseSide || "",
1285
+ onChange: (e) => setConfig((c) => ({ ...c, inverseSide: e.target.value })),
1286
+ placeholder: `e.g., ${(currentSchema == null ? void 0 : currentSchema.toLowerCase()) || "items"}`
1287
+ }
1288
+ ),
1289
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Field name on the related schema for bidirectional access" })
1290
+ ] })
1291
+ ] }),
1292
+ /* @__PURE__ */ jsxRuntime.jsxs(components$1.DialogFooter, { children: [
1293
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Button, { variant: "outline", onClick: () => onOpenChange(false), children: "Cancel" }),
1294
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Button, { onClick: handleSave, disabled: !config.targetSchema, children: "Save Configuration" })
1295
+ ] })
1296
+ ] }) });
1297
+ }
1298
+ function FieldSettingsPanel() {
1299
+ var _a, _b, _c, _d, _e, _f, _g;
1300
+ const { selectedField, updateField, selectField, state } = useSchemaBuilder();
1301
+ const { schemas } = admin.useAdmin();
1302
+ const [newOptionKey, setNewOptionKey] = react.useState("");
1303
+ const [newOptionValue, setNewOptionValue] = react.useState("");
1304
+ const [relationModalOpen, setRelationModalOpen] = react.useState(false);
1305
+ if (!selectedField) {
1306
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full flex items-center justify-center text-muted-foreground p-8 text-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1307
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium mb-1", children: "No field selected" }),
1308
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs", children: "Select a field to edit its settings" })
1309
+ ] }) });
1310
+ }
1311
+ const fieldTypeDef = FIELD_TYPES.find((t) => t.id === selectedField.type);
1312
+ const availableValidations = VALIDATION_RULES_BY_TYPE[selectedField.type] || [];
1313
+ const uiSubtypes = UI_SUBTYPES[selectedField.type] || [];
1314
+ const handleAddOption = () => {
1315
+ if (!newOptionKey.trim() || !newOptionValue.trim()) return;
1316
+ const currentOptions = selectedField.ui.options || [];
1317
+ updateField(selectedField.id, {
1318
+ ui: {
1319
+ ...selectedField.ui,
1320
+ options: [
1321
+ ...currentOptions,
1322
+ { key: newOptionKey.trim(), value: newOptionValue.trim() }
1323
+ ]
1324
+ }
1325
+ });
1326
+ setNewOptionKey("");
1327
+ setNewOptionValue("");
1328
+ };
1329
+ const handleRemoveOption = (index2) => {
1330
+ const currentOptions = selectedField.ui.options || [];
1331
+ updateField(selectedField.id, {
1332
+ ui: {
1333
+ ...selectedField.ui,
1334
+ options: currentOptions.filter((_, i) => i !== index2)
1335
+ }
1336
+ });
1337
+ };
1338
+ const handleAddValidation = (type) => {
1339
+ const currentValidations = selectedField.validations || [];
1340
+ if (currentValidations.some((v) => v.type === type)) return;
1341
+ const ruleDef = getValidationRuleDefinition(type);
1342
+ const newRule = {
1343
+ type,
1344
+ constraints: (ruleDef == null ? void 0 : ruleDef.constraintCount) ? [] : void 0
1345
+ };
1346
+ updateField(selectedField.id, {
1347
+ validations: [...currentValidations, newRule]
1348
+ });
1349
+ };
1350
+ const handleRemoveValidation = (type) => {
1351
+ updateField(selectedField.id, {
1352
+ validations: selectedField.validations.filter((v) => v.type !== type)
1353
+ });
1354
+ };
1355
+ const handleUpdateValidationConstraints = (type, constraints) => {
1356
+ updateField(selectedField.id, {
1357
+ validations: selectedField.validations.map(
1358
+ (v) => v.type === type ? { ...v, constraints } : v
1359
+ )
1360
+ });
1361
+ };
1362
+ const getRelationTypeLabel = (type) => {
1363
+ var _a2;
1364
+ return ((_a2 = RELATION_TYPES.find((t) => t.value === type)) == null ? void 0 : _a2.label) || "Not Set";
1365
+ };
1366
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex flex-col", children: [
1367
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 border-b flex items-center justify-between", children: [
1368
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xs font-semibold uppercase tracking-wide", children: "Field Settings" }),
1369
+ /* @__PURE__ */ jsxRuntime.jsx(
1370
+ components$1.Button,
1371
+ {
1372
+ variant: "ghost",
1373
+ size: "icon",
1374
+ className: "h-8 w-8",
1375
+ onClick: () => selectField(null),
1376
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4" })
1377
+ }
1378
+ )
1379
+ ] }),
1380
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsxs(
1381
+ components$1.Accordion,
1382
+ {
1383
+ type: "multiple",
1384
+ defaultValue: ["basic-info", "constraints"],
1385
+ className: "w-full",
1386
+ children: [
1387
+ /* @__PURE__ */ jsxRuntime.jsxs(components$1.AccordionItem, { value: "basic-info", className: "px-5", children: [
1388
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.AccordionTrigger, { className: "text-xs font-semibold uppercase tracking-wide", children: "Basic Info" }),
1389
+ /* @__PURE__ */ jsxRuntime.jsxs(components$1.AccordionContent, { className: "space-y-4", children: [
1390
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
1391
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { className: "text-xs", children: "Display Name" }),
1392
+ /* @__PURE__ */ jsxRuntime.jsx(
1393
+ components$1.Input,
1394
+ {
1395
+ value: selectedField.displayName,
1396
+ onChange: (e) => updateField(selectedField.id, {
1397
+ displayName: e.target.value
1398
+ }),
1399
+ className: "font-medium"
1400
+ }
1401
+ )
1402
+ ] }),
1403
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
1404
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { className: "text-xs", children: "API ID" }),
1405
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
1406
+ /* @__PURE__ */ jsxRuntime.jsx(
1407
+ components$1.Input,
1408
+ {
1409
+ value: selectedField.name,
1410
+ onChange: (e) => updateField(selectedField.id, { name: e.target.value }),
1411
+ className: "font-mono text-muted-foreground pr-8"
1412
+ }
1413
+ ),
1414
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Lock, { className: "absolute right-2.5 top-2.5 h-3.5 w-3.5 text-muted-foreground/50" })
1415
+ ] })
1416
+ ] }),
1417
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
1418
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { className: "text-xs", children: "Field Type" }),
1419
+ /* @__PURE__ */ jsxRuntime.jsxs(
1420
+ components$1.Select,
1421
+ {
1422
+ value: selectedField.type,
1423
+ onValueChange: (value) => updateField(selectedField.id, { type: value }),
1424
+ children: [
1425
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectTrigger, { children: /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectValue, {}) }),
1426
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectContent, { children: FIELD_TYPES.map((type) => /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectItem, { value: type.id, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1427
+ /* @__PURE__ */ jsxRuntime.jsx(type.icon, { className: "h-4 w-4" }),
1428
+ type.label
1429
+ ] }) }, type.id)) })
1430
+ ]
1431
+ }
1432
+ )
1433
+ ] }),
1434
+ uiSubtypes.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
1435
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { className: "text-xs", children: "UI Type" }),
1436
+ /* @__PURE__ */ jsxRuntime.jsxs(
1437
+ components$1.Select,
1438
+ {
1439
+ value: selectedField.ui.type || ((_a = uiSubtypes[0]) == null ? void 0 : _a.id),
1440
+ onValueChange: (value) => updateField(selectedField.id, {
1441
+ ui: { ...selectedField.ui, type: value }
1442
+ }),
1443
+ children: [
1444
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectTrigger, { children: /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectValue, {}) }),
1445
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectContent, { children: uiSubtypes.map((subtype) => /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectItem, { value: subtype.id, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col", children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: subtype.label }) }) }, subtype.id)) })
1446
+ ]
1447
+ }
1448
+ ),
1449
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: ((_b = uiSubtypes.find((s) => s.id === selectedField.ui.type)) == null ? void 0 : _b.description) || ((_c = uiSubtypes[0]) == null ? void 0 : _c.description) })
1450
+ ] })
1451
+ ] })
1452
+ ] }),
1453
+ /* @__PURE__ */ jsxRuntime.jsxs(components$1.AccordionItem, { value: "ui-settings", className: "px-5", children: [
1454
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.AccordionTrigger, { className: "text-xs font-semibold uppercase tracking-wide", children: "UI Settings" }),
1455
+ /* @__PURE__ */ jsxRuntime.jsxs(components$1.AccordionContent, { className: "space-y-4", children: [
1456
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
1457
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { className: "text-xs", children: "Tab" }),
1458
+ /* @__PURE__ */ jsxRuntime.jsx(
1459
+ components$1.Input,
1460
+ {
1461
+ value: selectedField.ui.tab || "",
1462
+ onChange: (e) => updateField(selectedField.id, {
1463
+ ui: { ...selectedField.ui, tab: e.target.value }
1464
+ }),
1465
+ placeholder: "General"
1466
+ }
1467
+ )
1468
+ ] }),
1469
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
1470
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { className: "text-xs", children: "Description" }),
1471
+ /* @__PURE__ */ jsxRuntime.jsx(
1472
+ components$1.Textarea,
1473
+ {
1474
+ value: selectedField.ui.description || "",
1475
+ onChange: (e) => updateField(selectedField.id, {
1476
+ ui: { ...selectedField.ui, description: e.target.value }
1477
+ }),
1478
+ placeholder: "Help text for this field",
1479
+ rows: 2
1480
+ }
1481
+ )
1482
+ ] }),
1483
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
1484
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { className: "text-xs", children: "Show in Side Panel" }),
1485
+ /* @__PURE__ */ jsxRuntime.jsx(
1486
+ components$1.Switch,
1487
+ {
1488
+ checked: selectedField.ui.side || false,
1489
+ onCheckedChange: (checked) => updateField(selectedField.id, {
1490
+ ui: { ...selectedField.ui, side: checked }
1491
+ })
1492
+ }
1493
+ )
1494
+ ] }),
1495
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
1496
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { className: "text-xs", children: "Half Width (Row)" }),
1497
+ /* @__PURE__ */ jsxRuntime.jsx(
1498
+ components$1.Switch,
1499
+ {
1500
+ checked: selectedField.ui.row || false,
1501
+ onCheckedChange: (checked) => updateField(selectedField.id, {
1502
+ ui: { ...selectedField.ui, row: checked }
1503
+ })
1504
+ }
1505
+ )
1506
+ ] })
1507
+ ] })
1508
+ ] }),
1509
+ /* @__PURE__ */ jsxRuntime.jsxs(components$1.AccordionItem, { value: "constraints", className: "px-5", children: [
1510
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.AccordionTrigger, { className: "text-xs font-semibold uppercase tracking-wide", children: "Constraints" }),
1511
+ /* @__PURE__ */ jsxRuntime.jsxs(components$1.AccordionContent, { className: "space-y-4", children: [
1512
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
1513
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { className: "text-xs", children: "Required" }),
1514
+ /* @__PURE__ */ jsxRuntime.jsx(
1515
+ components$1.Switch,
1516
+ {
1517
+ checked: selectedField.prop.required || false,
1518
+ onCheckedChange: (checked) => updateField(selectedField.id, {
1519
+ prop: { ...selectedField.prop, required: checked }
1520
+ })
1521
+ }
1522
+ )
1523
+ ] }),
1524
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
1525
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { className: "text-xs", children: "Unique" }),
1526
+ /* @__PURE__ */ jsxRuntime.jsx(
1527
+ components$1.Switch,
1528
+ {
1529
+ checked: selectedField.prop.unique || false,
1530
+ onCheckedChange: (checked) => updateField(selectedField.id, {
1531
+ prop: { ...selectedField.prop, unique: checked }
1532
+ })
1533
+ }
1534
+ )
1535
+ ] }),
1536
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
1537
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { className: "text-xs", children: "Hidden" }),
1538
+ /* @__PURE__ */ jsxRuntime.jsx(
1539
+ components$1.Switch,
1540
+ {
1541
+ checked: selectedField.prop.hidden || false,
1542
+ onCheckedChange: (checked) => updateField(selectedField.id, {
1543
+ prop: { ...selectedField.prop, hidden: checked }
1544
+ })
1545
+ }
1546
+ )
1547
+ ] }),
1548
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
1549
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { className: "text-xs", children: "Read Only" }),
1550
+ /* @__PURE__ */ jsxRuntime.jsx(
1551
+ components$1.Switch,
1552
+ {
1553
+ checked: selectedField.prop.readonly || false,
1554
+ onCheckedChange: (checked) => updateField(selectedField.id, {
1555
+ prop: { ...selectedField.prop, readonly: checked }
1556
+ })
1557
+ }
1558
+ )
1559
+ ] }),
1560
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
1561
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { className: "text-xs", children: "Enable i18n" }),
1562
+ /* @__PURE__ */ jsxRuntime.jsx(
1563
+ components$1.Switch,
1564
+ {
1565
+ checked: selectedField.prop.intl || false,
1566
+ onCheckedChange: (checked) => updateField(selectedField.id, {
1567
+ prop: { ...selectedField.prop, intl: checked }
1568
+ })
1569
+ }
1570
+ )
1571
+ ] })
1572
+ ] })
1573
+ ] }),
1574
+ (fieldTypeDef == null ? void 0 : fieldTypeDef.hasOptions) && /* @__PURE__ */ jsxRuntime.jsxs(components$1.AccordionItem, { value: "select-options", className: "px-5", children: [
1575
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.AccordionTrigger, { className: "text-xs font-semibold uppercase tracking-wide", children: "Select Options" }),
1576
+ /* @__PURE__ */ jsxRuntime.jsxs(components$1.AccordionContent, { className: "space-y-4", children: [
1577
+ (selectedField.ui.options || []).map((option, index2) => /* @__PURE__ */ jsxRuntime.jsxs(
1578
+ "div",
1579
+ {
1580
+ className: "flex items-center gap-2",
1581
+ children: [
1582
+ /* @__PURE__ */ jsxRuntime.jsx(
1583
+ components$1.Input,
1584
+ {
1585
+ value: option.key,
1586
+ onChange: (e) => {
1587
+ const newOptions = [...selectedField.ui.options || []];
1588
+ newOptions[index2] = { ...option, key: e.target.value };
1589
+ updateField(selectedField.id, {
1590
+ ui: { ...selectedField.ui, options: newOptions }
1591
+ });
1592
+ },
1593
+ placeholder: "Key",
1594
+ className: "flex-1"
1595
+ }
1596
+ ),
1597
+ /* @__PURE__ */ jsxRuntime.jsx(
1598
+ components$1.Input,
1599
+ {
1600
+ value: option.value,
1601
+ onChange: (e) => {
1602
+ const newOptions = [...selectedField.ui.options || []];
1603
+ newOptions[index2] = { ...option, value: e.target.value };
1604
+ updateField(selectedField.id, {
1605
+ ui: { ...selectedField.ui, options: newOptions }
1606
+ });
1607
+ },
1608
+ placeholder: "Label",
1609
+ className: "flex-1"
1610
+ }
1611
+ ),
1612
+ /* @__PURE__ */ jsxRuntime.jsx(
1613
+ components$1.Button,
1614
+ {
1615
+ variant: "ghost",
1616
+ size: "icon",
1617
+ className: "h-8 w-8 text-muted-foreground hover:text-destructive",
1618
+ onClick: () => handleRemoveOption(index2),
1619
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3.5 w-3.5" })
1620
+ }
1621
+ )
1622
+ ]
1623
+ },
1624
+ option.key || `option-${index2}`
1625
+ )),
1626
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1627
+ /* @__PURE__ */ jsxRuntime.jsx(
1628
+ components$1.Input,
1629
+ {
1630
+ value: newOptionKey,
1631
+ onChange: (e) => setNewOptionKey(e.target.value),
1632
+ placeholder: "Key",
1633
+ className: "flex-1"
1634
+ }
1635
+ ),
1636
+ /* @__PURE__ */ jsxRuntime.jsx(
1637
+ components$1.Input,
1638
+ {
1639
+ value: newOptionValue,
1640
+ onChange: (e) => setNewOptionValue(e.target.value),
1641
+ placeholder: "Label",
1642
+ className: "flex-1"
1643
+ }
1644
+ ),
1645
+ /* @__PURE__ */ jsxRuntime.jsx(
1646
+ components$1.Button,
1647
+ {
1648
+ variant: "outline",
1649
+ size: "icon",
1650
+ className: "h-8 w-8",
1651
+ onClick: handleAddOption,
1652
+ disabled: !newOptionKey.trim() || !newOptionValue.trim(),
1653
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-3.5 w-3.5" })
1654
+ }
1655
+ )
1656
+ ] })
1657
+ ] })
1658
+ ] }),
1659
+ (fieldTypeDef == null ? void 0 : fieldTypeDef.hasRelationConfig) && /* @__PURE__ */ jsxRuntime.jsxs(components$1.AccordionItem, { value: "relation-config", className: "px-5", children: [
1660
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.AccordionTrigger, { className: "text-xs font-semibold uppercase tracking-wide", children: "Relation Configuration" }),
1661
+ /* @__PURE__ */ jsxRuntime.jsxs(components$1.AccordionContent, { className: "space-y-4", children: [
1662
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 bg-muted/50 rounded-lg border space-y-3", children: [
1663
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
1664
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Relation Type" }),
1665
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: getRelationTypeLabel(
1666
+ (_d = selectedField.relationConfig) == null ? void 0 : _d.relationType
1667
+ ) })
1668
+ ] }),
1669
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center py-3 gap-3", children: [
1670
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 py-1 bg-background border rounded text-xs font-medium shadow-sm", children: state.schema.name || "Current" }),
1671
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5", children: [
1672
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 bg-muted-foreground/60 rounded-full" }),
1673
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 h-px bg-muted-foreground/60" }),
1674
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-3 h-3 text-muted-foreground/60" })
1675
+ ] }),
1676
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 py-1 bg-background border rounded text-xs font-medium shadow-sm", children: ((_e = selectedField.relationConfig) == null ? void 0 : _e.targetSchema) || "Select..." })
1677
+ ] }),
1678
+ /* @__PURE__ */ jsxRuntime.jsx(
1679
+ components$1.Button,
1680
+ {
1681
+ variant: "secondary",
1682
+ size: "sm",
1683
+ className: "w-full",
1684
+ onClick: () => setRelationModalOpen(true),
1685
+ children: "Change Relation"
1686
+ }
1687
+ )
1688
+ ] }),
1689
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
1690
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { className: "text-xs", children: "Related Collection" }),
1691
+ /* @__PURE__ */ jsxRuntime.jsxs(
1692
+ components$1.Select,
1693
+ {
1694
+ value: ((_f = selectedField.relationConfig) == null ? void 0 : _f.targetSchema) || "",
1695
+ onValueChange: (value) => {
1696
+ var _a2;
1697
+ return updateField(selectedField.id, {
1698
+ relationConfig: {
1699
+ ...selectedField.relationConfig,
1700
+ targetSchema: value,
1701
+ relationType: ((_a2 = selectedField.relationConfig) == null ? void 0 : _a2.relationType) || "manyToOne"
1702
+ }
1703
+ });
1704
+ },
1705
+ children: [
1706
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectTrigger, { children: /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectValue, { placeholder: "Select schema..." }) }),
1707
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectContent, { children: schemas == null ? void 0 : schemas.map((schema) => /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectItem, { value: schema, children: schema }, schema)) })
1708
+ ]
1709
+ }
1710
+ )
1711
+ ] }),
1712
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
1713
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Label, { className: "text-xs", children: "Relation Type" }),
1714
+ /* @__PURE__ */ jsxRuntime.jsxs(
1715
+ components$1.Select,
1716
+ {
1717
+ value: ((_g = selectedField.relationConfig) == null ? void 0 : _g.relationType) || "manyToOne",
1718
+ onValueChange: (value) => {
1719
+ var _a2;
1720
+ return updateField(selectedField.id, {
1721
+ relationConfig: {
1722
+ ...selectedField.relationConfig,
1723
+ targetSchema: ((_a2 = selectedField.relationConfig) == null ? void 0 : _a2.targetSchema) || "",
1724
+ relationType: value
1725
+ }
1726
+ });
1727
+ },
1728
+ children: [
1729
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectTrigger, { children: /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectValue, {}) }),
1730
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectContent, { children: RELATION_TYPES.map((type) => /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectItem, { value: type.value, children: type.label }, type.value)) })
1731
+ ]
1732
+ }
1733
+ )
1734
+ ] })
1735
+ ] })
1736
+ ] }),
1737
+ /* @__PURE__ */ jsxRuntime.jsxs(components$1.AccordionItem, { value: "validations", className: "px-5", children: [
1738
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.AccordionTrigger, { className: "text-xs font-semibold uppercase tracking-wide", children: "Validations" }),
1739
+ /* @__PURE__ */ jsxRuntime.jsxs(components$1.AccordionContent, { className: "space-y-4", children: [
1740
+ selectedField.validations.map((validation) => {
1741
+ const ruleDef = getValidationRuleDefinition(validation.type);
1742
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1743
+ "div",
1744
+ {
1745
+ className: "p-3 bg-muted/50 rounded-lg border space-y-2",
1746
+ children: [
1747
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
1748
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: (ruleDef == null ? void 0 : ruleDef.label) || validation.type }),
1749
+ /* @__PURE__ */ jsxRuntime.jsx(
1750
+ components$1.Button,
1751
+ {
1752
+ variant: "ghost",
1753
+ size: "icon",
1754
+ className: "h-6 w-6 text-muted-foreground hover:text-destructive",
1755
+ onClick: () => handleRemoveValidation(validation.type),
1756
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3 w-3" })
1757
+ }
1758
+ )
1759
+ ] }),
1760
+ (ruleDef == null ? void 0 : ruleDef.constraintCount) && ruleDef.constraintCount > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-2", children: Array.from({ length: ruleDef.constraintCount }).map(
1761
+ (_, i) => {
1762
+ var _a2, _b2;
1763
+ return /* @__PURE__ */ jsxRuntime.jsx(
1764
+ components$1.Input,
1765
+ {
1766
+ type: validation.type.includes("Length") || validation.type.includes("Min") || validation.type.includes("Max") ? "number" : "text",
1767
+ placeholder: ((_a2 = ruleDef.constraintLabels) == null ? void 0 : _a2[i]) || `Arg ${i + 1}`,
1768
+ value: ((_b2 = validation.constraints) == null ? void 0 : _b2[i]) ?? "",
1769
+ onChange: (e) => {
1770
+ const newConstraints = [
1771
+ ...validation.constraints || []
1772
+ ];
1773
+ newConstraints[i] = validation.type.includes("Length") || validation.type.includes("Min") || validation.type.includes("Max") ? Number(e.target.value) : e.target.value;
1774
+ handleUpdateValidationConstraints(
1775
+ validation.type,
1776
+ newConstraints
1777
+ );
1778
+ },
1779
+ className: "flex-1"
1780
+ },
1781
+ `${validation.type}-constraint-${i}`
1782
+ );
1783
+ }
1784
+ ) })
1785
+ ]
1786
+ },
1787
+ validation.type
1788
+ );
1789
+ }),
1790
+ /* @__PURE__ */ jsxRuntime.jsxs(components$1.Select, { onValueChange: handleAddValidation, children: [
1791
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectTrigger, { children: /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectValue, { placeholder: "Add validation..." }) }),
1792
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectContent, { children: availableValidations.filter(
1793
+ (v) => !selectedField.validations.some((sv) => sv.type === v)
1794
+ ).map((validation) => {
1795
+ const ruleDef = getValidationRuleDefinition(validation);
1796
+ return /* @__PURE__ */ jsxRuntime.jsx(components$1.SelectItem, { value: validation, children: (ruleDef == null ? void 0 : ruleDef.label) || validation }, validation);
1797
+ }) })
1798
+ ] })
1799
+ ] })
1800
+ ] })
1801
+ ]
1802
+ }
1803
+ ) }),
1804
+ (fieldTypeDef == null ? void 0 : fieldTypeDef.hasRelationConfig) && /* @__PURE__ */ jsxRuntime.jsx(
1805
+ RelationConfigModal,
1806
+ {
1807
+ open: relationModalOpen,
1808
+ onOpenChange: setRelationModalOpen,
1809
+ currentSchema: state.schema.name,
1810
+ relationConfig: selectedField.relationConfig,
1811
+ onSave: (config) => {
1812
+ updateField(selectedField.id, {
1813
+ relationConfig: config
1814
+ });
1815
+ }
1816
+ }
1817
+ )
1818
+ ] });
1819
+ }
1820
+ function SchemaList() {
1821
+ const navigate = reactRouterDom.useNavigate();
1822
+ const { schemaName } = reactRouterDom.useParams();
1823
+ const { schemas, isLoading, error } = admin.useAdmin();
1824
+ if (isLoading) {
1825
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center items-center h-32", children: /* @__PURE__ */ jsxRuntime.jsx(components$1.Spinner, { className: "h-5 w-5" }) });
1826
+ }
1827
+ if (error) {
1828
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 text-xs text-red-600", children: "Error loading schemas" });
1829
+ }
1830
+ const isNewSchema = schemaName === "new";
1831
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col h-full", children: [
1832
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 border-b", children: /* @__PURE__ */ jsxRuntime.jsxs(
1833
+ components$1.Button,
1834
+ {
1835
+ size: "sm",
1836
+ className: "w-full",
1837
+ variant: isNewSchema ? "default" : "outline",
1838
+ onClick: () => navigate("/playground/new"),
1839
+ children: [
1840
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-3.5 w-3.5 mr-1.5" }),
1841
+ "New Schema"
1842
+ ]
1843
+ }
1844
+ ) }),
1845
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto", children: !schemas || schemas.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 text-center text-muted-foreground", children: [
1846
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Database, { className: "mx-auto h-8 w-8 mb-2 opacity-50" }),
1847
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs", children: "No schemas yet" })
1848
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-1", children: schemas.map((schema) => {
1849
+ const name = utils.names(schema);
1850
+ const isSelected = schemaName === name.key;
1851
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1852
+ "button",
1853
+ {
1854
+ type: "button",
1855
+ onClick: () => navigate(`/playground/${name.key}`),
1856
+ className: `w-full text-left px-3 py-2 text-sm transition-colors ${isSelected ? "bg-accent text-accent-foreground" : "hover:bg-muted/50 text-muted-foreground hover:text-foreground"}`,
1857
+ children: [
1858
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium truncate", children: name.title }),
1859
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs opacity-70 font-mono truncate", children: name.key })
1860
+ ]
1861
+ },
1862
+ schema
1863
+ );
1864
+ }) }) })
1865
+ ] });
1866
+ }
1867
+ function SchemaOptionsContent({ onClose }) {
1868
+ const { state, updateSchema } = useSchemaBuilder();
1869
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
1870
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
1871
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
1872
+ /* @__PURE__ */ jsxRuntime.jsx(
1873
+ "label",
1874
+ {
1875
+ htmlFor: "versioning",
1876
+ className: "text-sm font-medium cursor-pointer",
1877
+ children: "Enable Versioning"
1878
+ }
1879
+ ),
1880
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Track content changes with version history" })
1881
+ ] }),
1882
+ /* @__PURE__ */ jsxRuntime.jsx(
1883
+ components$1.Switch,
1884
+ {
1885
+ id: "versioning",
1886
+ checked: state.schema.versioning,
1887
+ onCheckedChange: (checked) => updateSchema({ versioning: checked })
1888
+ }
1889
+ )
1890
+ ] }),
1891
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
1892
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
1893
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "i18n", className: "text-sm font-medium cursor-pointer", children: "Enable i18n" }),
1894
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "Support multiple languages for content" })
1895
+ ] }),
1896
+ /* @__PURE__ */ jsxRuntime.jsx(
1897
+ components$1.Switch,
1898
+ {
1899
+ id: "i18n",
1900
+ checked: state.schema.i18n,
1901
+ onCheckedChange: (checked) => updateSchema({ i18n: checked })
1902
+ }
1903
+ )
1904
+ ] }),
1905
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end pt-2", children: /* @__PURE__ */ jsxRuntime.jsx(components$1.Button, { variant: "outline", onClick: onClose, children: "Close" }) })
1906
+ ] });
1907
+ }
1908
+ function useSchemaOptionsDialog() {
1909
+ const { showDialog, closeDialog } = admin.useDialog();
1910
+ return {
1911
+ open: () => {
1912
+ showDialog({
1913
+ title: "Schema Options",
1914
+ description: "Configure schema-level settings for versioning and internationalization.",
1915
+ size: "md",
1916
+ content: /* @__PURE__ */ jsxRuntime.jsx(SchemaOptionsContent, { onClose: closeDialog })
1917
+ });
1918
+ },
1919
+ close: closeDialog
1920
+ };
1921
+ }
1922
+ function SchemaOptionsDialog({
1923
+ open,
1924
+ onOpenChange
1925
+ }) {
1926
+ const schemaOptions = useSchemaOptionsDialog();
1927
+ react.useEffect(() => {
1928
+ if (open) {
1929
+ schemaOptions.open();
1930
+ }
1931
+ }, [open, schemaOptions]);
1932
+ react.useEffect(() => {
1933
+ return () => {
1934
+ if (open) {
1935
+ onOpenChange(false);
1936
+ }
1937
+ };
1938
+ }, [open, onOpenChange]);
1939
+ return null;
1940
+ }
1941
+ const ReadOnlyContext = react.createContext(false);
1942
+ function schemaMetadataToBuilderState(name, metadata) {
1943
+ var _a, _b, _c;
1944
+ return {
1945
+ schema: {
1946
+ name: name.charAt(0).toUpperCase() + name.slice(1),
1947
+ apiId: name,
1948
+ versioning: ((_a = metadata.options) == null ? void 0 : _a.versioning) ?? true,
1949
+ i18n: ((_b = metadata.options) == null ? void 0 : _b.i18n) ?? true
1950
+ },
1951
+ fields: ((_c = metadata.properties) == null ? void 0 : _c.map((prop, index2) => {
1952
+ var _a2;
1953
+ return {
1954
+ id: `field_${index2}_${prop.name}`,
1955
+ name: prop.name,
1956
+ displayName: ((_a2 = prop.ui) == null ? void 0 : _a2.label) || prop.name,
1957
+ type: inferFieldType(prop),
1958
+ tsType: prop.type || "string",
1959
+ prop: {
1960
+ required: prop.required || false,
1961
+ unique: prop.unique || false,
1962
+ intl: prop.intl || false,
1963
+ hidden: prop.hidden || false,
1964
+ readonly: prop.readonly || false
1965
+ },
1966
+ ui: prop.ui || {},
1967
+ validations: (prop.validations || []).map((v) => ({
1968
+ type: v.name || v.type,
1969
+ constraints: v.constraints
1970
+ }))
1971
+ };
1972
+ })) || [],
1973
+ selectedFieldId: null,
1974
+ viewMode: "builder",
1975
+ isDirty: false,
1976
+ lastSaved: null,
1977
+ isNew: false
1978
+ };
1979
+ }
1980
+ function inferFieldType(prop) {
1981
+ var _a;
1982
+ const uiType = (_a = prop.ui) == null ? void 0 : _a.type;
1983
+ if (uiType === "switch" || uiType === "checkbox") return "boolean";
1984
+ if (uiType === "date") return "date";
1985
+ if (uiType === "number") return "number";
1986
+ if (uiType === "select" || uiType === "radio") return "select";
1987
+ if (uiType === "relationship") return "relation";
1988
+ const tsType = typeof prop.type === "string" ? prop.type.toLowerCase() : null;
1989
+ if (tsType === "number") return "number";
1990
+ if (tsType === "boolean") return "boolean";
1991
+ if (tsType === "date") return "date";
1992
+ return "text";
1993
+ }
1994
+ function SchemaPlaygroundEditor() {
1995
+ const { schemaName } = reactRouterDom.useParams();
1996
+ const isNewSchema = !schemaName || schemaName === "new";
1997
+ const {
1998
+ data: schemaMetadata,
1999
+ isLoading,
2000
+ error
2001
+ } = admin.useSchema(schemaName && schemaName !== "new" ? schemaName : void 0);
2002
+ if (!isNewSchema && isLoading) {
2003
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center items-center h-64", children: /* @__PURE__ */ jsxRuntime.jsx(components$1.Spinner, {}) });
2004
+ }
2005
+ if (!isNewSchema && error) {
2006
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 border-l-4 border-red-500 bg-red-50 text-red-700 rounded m-8", children: [
2007
+ /* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "font-medium flex items-center gap-2", children: [
2008
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-4 w-4" }),
2009
+ "Error loading schema"
2010
+ ] }),
2011
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm mt-1", children: error.message })
2012
+ ] });
2013
+ }
2014
+ const initialState = !isNewSchema && schemaMetadata && schemaName && schemaName !== "new" ? schemaMetadataToBuilderState(schemaName, schemaMetadata) : { ...DEFAULT_BUILDER_STATE, isNew: true };
2015
+ const isReadOnly = false;
2016
+ return /* @__PURE__ */ jsxRuntime.jsx(
2017
+ SchemaEditorContentWithState,
2018
+ {
2019
+ initialState,
2020
+ isReadOnly
2021
+ },
2022
+ schemaName || "new"
2023
+ );
2024
+ }
2025
+ function SchemaEditorContentWithState({
2026
+ initialState,
2027
+ isReadOnly
2028
+ }) {
2029
+ const context = useSchemaBuilderState(initialState);
2030
+ return /* @__PURE__ */ jsxRuntime.jsx(ReadOnlyContext.Provider, { value: isReadOnly, children: /* @__PURE__ */ jsxRuntime.jsx(SchemaBuilderContext.Provider, { value: context, children: /* @__PURE__ */ jsxRuntime.jsx(SchemaEditorInner, {}) }) });
2031
+ }
2032
+ function SchemaEditorInner() {
2033
+ var _a, _b;
2034
+ const { state, setViewMode, updateSchema, generatedCode } = useSchemaBuilder();
2035
+ const isReadOnly = react.useContext(ReadOnlyContext);
2036
+ const [addFieldOpen, setAddFieldOpen] = react.useState(false);
2037
+ const [optionsOpen, setOptionsOpen] = react.useState(false);
2038
+ const handleDeploy = async () => {
2039
+ alert(
2040
+ `Deploy functionality will be implemented in the backend phase.
2041
+
2042
+ Generated code:
2043
+
2044
+ ${generatedCode}`
2045
+ );
2046
+ };
2047
+ const statusBadges = [];
2048
+ if (isReadOnly) {
2049
+ statusBadges.push({
2050
+ type: "info",
2051
+ label: "Read-only"
2052
+ });
2053
+ } else if (state.isDirty) {
2054
+ statusBadges.push({
2055
+ type: "warning",
2056
+ label: "Unsaved changes",
2057
+ dot: true
2058
+ });
2059
+ }
2060
+ if (!isReadOnly && state.lastSaved) {
2061
+ statusBadges.push({ type: "success", label: "Saved", dot: true });
2062
+ }
2063
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col h-full", children: [
2064
+ /* @__PURE__ */ jsxRuntime.jsx(
2065
+ admin.PageHeader,
2066
+ {
2067
+ status: statusBadges.length > 0 ? statusBadges : void 0,
2068
+ icon: isReadOnly ? lucideReact.Lock : lucideReact.Boxes,
2069
+ title: isReadOnly ? state.schema.name || "Schema" : {
2070
+ value: state.schema.name,
2071
+ onChange: (name) => updateSchema({ name }),
2072
+ placeholder: "SchemaName",
2073
+ editable: true
2074
+ },
2075
+ description: `api::${((_a = state.schema.name) == null ? void 0 : _a.toLowerCase()) || "schema"}.${((_b = state.schema.name) == null ? void 0 : _b.toLowerCase()) || "schema"}`,
2076
+ tabs: {
2077
+ items: [
2078
+ { id: "builder", label: "Builder", icon: lucideReact.PencilRuler },
2079
+ { id: "json", label: "JSON", icon: lucideReact.FileJson },
2080
+ { id: "code", label: "Code", icon: lucideReact.Code2 }
2081
+ ],
2082
+ value: state.viewMode,
2083
+ onChange: setViewMode
2084
+ },
2085
+ actions: isReadOnly ? void 0 : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2086
+ /* @__PURE__ */ jsxRuntime.jsx(
2087
+ components$1.Button,
2088
+ {
2089
+ variant: "outline",
2090
+ size: "icon",
2091
+ className: "h-8 w-8",
2092
+ onClick: () => setOptionsOpen(true),
2093
+ title: "Schema Options",
2094
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings2, { className: "h-4 w-4" })
2095
+ }
2096
+ ),
2097
+ /* @__PURE__ */ jsxRuntime.jsx(components$1.Button, { variant: "outline", size: "sm", disabled: true, children: "Preview API" }),
2098
+ /* @__PURE__ */ jsxRuntime.jsxs(
2099
+ components$1.Button,
2100
+ {
2101
+ size: "sm",
2102
+ onClick: handleDeploy,
2103
+ disabled: !state.schema.name,
2104
+ children: [
2105
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Rocket, { className: "h-4 w-4 mr-2" }),
2106
+ "Deploy Changes"
2107
+ ]
2108
+ }
2109
+ )
2110
+ ] })
2111
+ }
2112
+ ),
2113
+ isReadOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-2 border-b border-border bg-amber-50 dark:bg-amber-950/30 flex items-center gap-2", children: [
2114
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Lock, { className: "h-3.5 w-3.5 text-amber-600 dark:text-amber-400" }),
2115
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-amber-700 dark:text-amber-400", children: "This schema is defined in code and cannot be modified here. View the generated code in the Code tab to copy and customize." })
2116
+ ] }),
2117
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-hidden grid grid-cols-12 h-full", children: [
2118
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-2 border-r bg-muted/30 overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx(SchemaList, {}) }),
2119
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-7 overflow-hidden flex flex-col", children: state.viewMode === "builder" ? /* @__PURE__ */ jsxRuntime.jsx(admin.PageContent, { className: "p-6 overflow-y-auto flex-1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
2120
+ !isReadOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 bg-blue-50 border border-blue-100 rounded-lg flex items-start gap-3", children: [
2121
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Info, { className: "h-4 w-4 text-blue-500 mt-0.5 shrink-0" }),
2122
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-blue-900", children: [
2123
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Schema Guidelines:" }),
2124
+ " ",
2125
+ "Keep field names in camelCase for better compatibility."
2126
+ ] })
2127
+ ] }),
2128
+ /* @__PURE__ */ jsxRuntime.jsx(
2129
+ FieldList,
2130
+ {
2131
+ onAddField: isReadOnly ? void 0 : () => setAddFieldOpen(true),
2132
+ readOnly: isReadOnly
2133
+ }
2134
+ )
2135
+ ] }) }) : (
2136
+ /* Code/JSON View */
2137
+ /* @__PURE__ */ jsxRuntime.jsx(admin.PageContent, { className: "p-6 overflow-y-auto flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(CodePreview, { mode: state.viewMode }) })
2138
+ ) }),
2139
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-3 border-l bg-background overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx(FieldSettingsPanel, {}) })
2140
+ ] }),
2141
+ !isReadOnly && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2142
+ /* @__PURE__ */ jsxRuntime.jsx(AddFieldDialog, { open: addFieldOpen, onOpenChange: setAddFieldOpen }),
2143
+ /* @__PURE__ */ jsxRuntime.jsx(
2144
+ SchemaOptionsDialog,
2145
+ {
2146
+ open: optionsOpen,
2147
+ onOpenChange: setOptionsOpen
2148
+ }
2149
+ )
2150
+ ] })
2151
+ ] });
2152
+ }
2153
+ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2154
+ __proto__: null,
2155
+ SchemaPlaygroundEditor,
2156
+ default: SchemaPlaygroundEditor
2157
+ }, Symbol.toStringTag, { value: "Module" }));
2158
+ exports.default = playgroundPlugin;
2159
+ exports.playgroundPlugin = playgroundPlugin;
2160
+ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
2161
+ return exports;
2162
+ })({}, React, ReactRouterDOM, ReactJsxRuntime, MagnetAdmin, MagnetUI, LucideReact, MagnetUILib, MagnetUtils);
2163
+ //# sourceMappingURL=bundle.iife.js.map