@kyro-cms/admin 0.1.2

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.
Files changed (102) hide show
  1. package/.astro/content.d.ts +154 -0
  2. package/.astro/settings.json +5 -0
  3. package/.astro/types.d.ts +2 -0
  4. package/astro.config.mjs +28 -0
  5. package/bun.lock +1374 -0
  6. package/dist/client/_astro/AdminLayout.DkDpng53.css +1 -0
  7. package/dist/client/_astro/AutoForm.3eJCmCJp.js +1 -0
  8. package/dist/client/_astro/client.DyczpTbx.js +9 -0
  9. package/dist/client/_astro/index.B02hbnpo.js +1 -0
  10. package/dist/client/fonts/Serotiva-Black.woff2 +0 -0
  11. package/dist/client/fonts/Serotiva-Bold.woff2 +0 -0
  12. package/dist/client/fonts/Serotiva-Medium.woff2 +0 -0
  13. package/dist/client/fonts/Serotiva-Regular.woff2 +0 -0
  14. package/dist/client/fonts/Serotiva-SemiBold.woff2 +0 -0
  15. package/dist/server/chunks/AdminLayout_D-_JeUqC.mjs +26 -0
  16. package/dist/server/chunks/_id__BzI_o0qT.mjs +50 -0
  17. package/dist/server/chunks/_id__Cd-jOuY3.mjs +238 -0
  18. package/dist/server/chunks/_id__DvbD--iR.mjs +992 -0
  19. package/dist/server/chunks/_id__vpVaEo16.mjs +128 -0
  20. package/dist/server/chunks/_virtual_astro_server-island-manifest_CQQ1F5PF.mjs +7 -0
  21. package/dist/server/chunks/_virtual_astro_session-driver_Bk3Q189E.mjs +4 -0
  22. package/dist/server/chunks/astro-component_Dbx3T2Nh.mjs +37 -0
  23. package/dist/server/chunks/audit-logs_DrnUMRvY.mjs +74 -0
  24. package/dist/server/chunks/config_CPXslElD.mjs +4221 -0
  25. package/dist/server/chunks/dataStore_Dl7cA2Qp.mjs +89 -0
  26. package/dist/server/chunks/index_CVqOkerS.mjs +2960 -0
  27. package/dist/server/chunks/index_CX8SQ4BF.mjs +55 -0
  28. package/dist/server/chunks/index_CYofDU51.mjs +58 -0
  29. package/dist/server/chunks/index_DdNRhuaM.mjs +55 -0
  30. package/dist/server/chunks/index_DupPvtIF.mjs +42 -0
  31. package/dist/server/chunks/index_YTS_M-B9.mjs +263 -0
  32. package/dist/server/chunks/index_YeCzuVps.mjs +53 -0
  33. package/dist/server/chunks/login_DLyqMRO8.mjs +93 -0
  34. package/dist/server/chunks/logout_CSbt5wea.mjs +50 -0
  35. package/dist/server/chunks/me_C04jlYhH.mjs +41 -0
  36. package/dist/server/chunks/new_BbQ9b55M.mjs +92 -0
  37. package/dist/server/chunks/node_9bvTewss.mjs +1014 -0
  38. package/dist/server/chunks/noop-entrypoint_BOlrdqWF.mjs +3 -0
  39. package/dist/server/chunks/sequence_9cl7AJy-.mjs +2503 -0
  40. package/dist/server/chunks/server_peBx9VXG.mjs +8117 -0
  41. package/dist/server/chunks/sharp_pmJ7nHES.mjs +142 -0
  42. package/dist/server/chunks/users_Dzddy_YR.mjs +137 -0
  43. package/dist/server/entry.mjs +5 -0
  44. package/dist/server/virtual_astro_middleware.mjs +48 -0
  45. package/package.json +33 -0
  46. package/public/fonts/Serotiva-Black.woff2 +0 -0
  47. package/public/fonts/Serotiva-Bold.woff2 +0 -0
  48. package/public/fonts/Serotiva-Medium.woff2 +0 -0
  49. package/public/fonts/Serotiva-Regular.woff2 +0 -0
  50. package/public/fonts/Serotiva-SemiBold.woff2 +0 -0
  51. package/src/collections/auth/index.ts +155 -0
  52. package/src/components/ActionBar.tsx +215 -0
  53. package/src/components/Admin.tsx +214 -0
  54. package/src/components/AutoForm.tsx +1123 -0
  55. package/src/components/BulkActionsBar.tsx +80 -0
  56. package/src/components/CreateView.tsx +99 -0
  57. package/src/components/DetailView.tsx +329 -0
  58. package/src/components/Icons.tsx +23 -0
  59. package/src/components/ListView.tsx +192 -0
  60. package/src/components/StatusBadge.tsx +76 -0
  61. package/src/components/ThemeProvider.tsx +155 -0
  62. package/src/components/VersionHistoryPanel.tsx +205 -0
  63. package/src/components/fields/CheckboxField.tsx +37 -0
  64. package/src/components/fields/DateField.tsx +42 -0
  65. package/src/components/fields/NumberField.tsx +44 -0
  66. package/src/components/fields/RelationshipField.tsx +87 -0
  67. package/src/components/fields/SelectField.tsx +56 -0
  68. package/src/components/fields/TextField.tsx +49 -0
  69. package/src/components/index.ts +30 -0
  70. package/src/components/layout/Breadcrumbs.tsx +36 -0
  71. package/src/components/layout/Header.tsx +37 -0
  72. package/src/components/layout/Layout.tsx +25 -0
  73. package/src/components/layout/Sidebar.tsx +462 -0
  74. package/src/components/ui/Badge.tsx +14 -0
  75. package/src/components/ui/Button.tsx +41 -0
  76. package/src/components/ui/Dropdown.tsx +82 -0
  77. package/src/components/ui/Modal.tsx +135 -0
  78. package/src/components/ui/SlidePanel.tsx +73 -0
  79. package/src/components/ui/Spinner.tsx +24 -0
  80. package/src/components/ui/Toast.tsx +78 -0
  81. package/src/layouts/AdminLayout.astro +197 -0
  82. package/src/lib/config.ts +68 -0
  83. package/src/lib/dataStore.ts +111 -0
  84. package/src/middleware.ts +48 -0
  85. package/src/pages/[collection]/[id].astro +176 -0
  86. package/src/pages/[collection]/index.astro +180 -0
  87. package/src/pages/api/[collection]/[id].ts +258 -0
  88. package/src/pages/api/[collection]/index.ts +289 -0
  89. package/src/pages/api/auth/[id].ts +142 -0
  90. package/src/pages/api/auth/audit-logs.ts +80 -0
  91. package/src/pages/api/auth/login.ts +101 -0
  92. package/src/pages/api/auth/logout.ts +48 -0
  93. package/src/pages/api/auth/me.ts +36 -0
  94. package/src/pages/api/auth/users.ts +150 -0
  95. package/src/pages/audit/index.astro +110 -0
  96. package/src/pages/index.astro +225 -0
  97. package/src/pages/roles/index.astro +114 -0
  98. package/src/pages/users/[id].astro +174 -0
  99. package/src/pages/users/index.astro +142 -0
  100. package/src/pages/users/new.astro +91 -0
  101. package/src/styles/main.css +1449 -0
  102. package/tsconfig.json +12 -0
@@ -0,0 +1,992 @@
1
+ import { c as createComponent } from './astro-component_Dbx3T2Nh.mjs';
2
+ import 'piccolore';
3
+ import { Q as renderTemplate, bb as defineScriptVars, a3 as addAttribute, B as maybeRenderHead } from './sequence_9cl7AJy-.mjs';
4
+ import { r as renderComponent } from './server_peBx9VXG.mjs';
5
+ import { $ as $$AdminLayout } from './AdminLayout_D-_JeUqC.mjs';
6
+ import { c as collections } from './config_CPXslElD.mjs';
7
+ import { jsx, jsxs } from 'react/jsx-runtime';
8
+ import { useRef, useState, useEffect } from 'react';
9
+
10
+ function AutoForm({
11
+ config,
12
+ data = {},
13
+ errors = {},
14
+ onChange,
15
+ disabled
16
+ }) {
17
+ const handleFieldChange = (fieldName, value) => {
18
+ onChange?.({
19
+ ...data,
20
+ [fieldName]: value
21
+ });
22
+ };
23
+ const renderField = (field, parentData) => {
24
+ if (field.admin?.hidden) return null;
25
+ const value = parentData !== void 0 ? parentData[field.name] : data[field.name];
26
+ const error = errors[field.name];
27
+ if (field.type === "row" && "fields" in field) {
28
+ return /* @__PURE__ */ jsx(
29
+ "div",
30
+ {
31
+ className: "kyro-form-row",
32
+ children: field.fields.map((f) => renderField(f, parentData))
33
+ },
34
+ field.name || `row-${Math.random()}`
35
+ );
36
+ }
37
+ if (field.type === "collapsible" && "fields" in field) {
38
+ return /* @__PURE__ */ jsxs(
39
+ "details",
40
+ {
41
+ className: "kyro-form-collapsible",
42
+ children: [
43
+ /* @__PURE__ */ jsxs("summary", { className: "kyro-form-collapsible-header", children: [
44
+ /* @__PURE__ */ jsx(
45
+ "svg",
46
+ {
47
+ width: "16",
48
+ height: "16",
49
+ viewBox: "0 0 24 24",
50
+ fill: "none",
51
+ stroke: "currentColor",
52
+ strokeWidth: "2",
53
+ children: /* @__PURE__ */ jsx("path", { d: "M9 18l6-6-6-6" })
54
+ }
55
+ ),
56
+ field.label
57
+ ] }),
58
+ /* @__PURE__ */ jsx("div", { className: "kyro-form-collapsible-content", children: field.fields.map(
59
+ (f) => renderField(f, parentData)
60
+ ) })
61
+ ]
62
+ },
63
+ field.name || `collapsible-${Math.random()}`
64
+ );
65
+ }
66
+ if (field.type === "tabs" && "tabs" in field) {
67
+ return /* @__PURE__ */ jsx(
68
+ "div",
69
+ {
70
+ className: "kyro-form-tabs",
71
+ children: field.tabs.map((tab, index) => /* @__PURE__ */ jsxs("div", { className: "kyro-form-tab", children: [
72
+ /* @__PURE__ */ jsx("h3", { className: "kyro-form-tab-title", children: tab.label }),
73
+ /* @__PURE__ */ jsx("div", { className: "kyro-form-tab-content", children: tab.fields.map((f) => renderField(f, parentData)) })
74
+ ] }, index))
75
+ },
76
+ field.name || `tabs-${Math.random()}`
77
+ );
78
+ }
79
+ switch (field.type) {
80
+ case "text":
81
+ case "email":
82
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-field", children: [
83
+ /* @__PURE__ */ jsxs("label", { className: "kyro-form-label", children: [
84
+ field.label || field.name,
85
+ field.required && /* @__PURE__ */ jsx("span", { className: "kyro-form-label-required", children: "*" })
86
+ ] }),
87
+ /* @__PURE__ */ jsx(
88
+ "input",
89
+ {
90
+ type: field.type === "email" ? "email" : field.variant === "url" ? "url" : "text",
91
+ className: `kyro-form-input ${error ? "kyro-form-input-error" : ""}`,
92
+ value: value || "",
93
+ onChange: (e) => handleFieldChange(field.name, e.target.value),
94
+ disabled,
95
+ placeholder: `Enter ${field.label || field.name}`,
96
+ minLength: field.minLength,
97
+ maxLength: field.maxLength
98
+ }
99
+ ),
100
+ field.admin?.description && !error && /* @__PURE__ */ jsx("p", { className: "kyro-form-help", children: field.admin.description }),
101
+ error && /* @__PURE__ */ jsx("p", { className: "kyro-form-error", children: error })
102
+ ] }, field.name);
103
+ case "password":
104
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-field", children: [
105
+ /* @__PURE__ */ jsxs("label", { className: "kyro-form-label", children: [
106
+ field.label || field.name,
107
+ field.required && /* @__PURE__ */ jsx("span", { className: "kyro-form-label-required", children: "*" })
108
+ ] }),
109
+ /* @__PURE__ */ jsx(
110
+ "input",
111
+ {
112
+ type: "password",
113
+ className: `kyro-form-input ${error ? "kyro-form-input-error" : ""}`,
114
+ value: value || "",
115
+ onChange: (e) => handleFieldChange(field.name, e.target.value),
116
+ disabled,
117
+ placeholder: `Enter ${field.label || field.name}`
118
+ }
119
+ ),
120
+ error && /* @__PURE__ */ jsx("p", { className: "kyro-form-error", children: error })
121
+ ] }, field.name);
122
+ case "textarea":
123
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-field", children: [
124
+ /* @__PURE__ */ jsxs("label", { className: "kyro-form-label", children: [
125
+ field.label || field.name,
126
+ field.required && /* @__PURE__ */ jsx("span", { className: "kyro-form-label-required", children: "*" })
127
+ ] }),
128
+ /* @__PURE__ */ jsx(
129
+ "textarea",
130
+ {
131
+ className: `kyro-form-input kyro-form-textarea ${error ? "kyro-form-input-error" : ""}`,
132
+ value: value || "",
133
+ onChange: (e) => handleFieldChange(field.name, e.target.value),
134
+ disabled,
135
+ rows: field.rows || 4,
136
+ placeholder: `Enter ${field.label || field.name}`
137
+ }
138
+ ),
139
+ field.admin?.description && !error && /* @__PURE__ */ jsx("p", { className: "kyro-form-help", children: field.admin.description }),
140
+ error && /* @__PURE__ */ jsx("p", { className: "kyro-form-error", children: error })
141
+ ] }, field.name);
142
+ case "number":
143
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-field", children: [
144
+ /* @__PURE__ */ jsxs("label", { className: "kyro-form-label", children: [
145
+ field.label || field.name,
146
+ field.required && /* @__PURE__ */ jsx("span", { className: "kyro-form-label-required", children: "*" })
147
+ ] }),
148
+ /* @__PURE__ */ jsx(
149
+ "input",
150
+ {
151
+ type: "number",
152
+ className: `kyro-form-input kyro-form-number ${error ? "kyro-form-input-error" : ""}`,
153
+ value: value ?? "",
154
+ onChange: (e) => handleFieldChange(field.name, parseFloat(e.target.value) || 0),
155
+ disabled,
156
+ placeholder: "0",
157
+ min: field.min,
158
+ max: field.max,
159
+ step: field.step || "any"
160
+ }
161
+ ),
162
+ field.admin?.description && !error && /* @__PURE__ */ jsx("p", { className: "kyro-form-help", children: field.admin.description }),
163
+ error && /* @__PURE__ */ jsx("p", { className: "kyro-form-error", children: error })
164
+ ] }, field.name);
165
+ case "checkbox":
166
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-field", children: [
167
+ /* @__PURE__ */ jsxs("label", { className: "kyro-form-checkbox", children: [
168
+ /* @__PURE__ */ jsx(
169
+ "input",
170
+ {
171
+ type: "checkbox",
172
+ checked: value || false,
173
+ onChange: (e) => handleFieldChange(field.name, e.target.checked),
174
+ disabled
175
+ }
176
+ ),
177
+ /* @__PURE__ */ jsx("span", { className: "kyro-form-checkbox-label", children: field.label || field.name })
178
+ ] }),
179
+ field.admin?.description && /* @__PURE__ */ jsx("p", { className: "kyro-form-help", children: field.admin.description })
180
+ ] }, field.name);
181
+ case "date":
182
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-field", children: [
183
+ /* @__PURE__ */ jsxs("label", { className: "kyro-form-label", children: [
184
+ field.label || field.name,
185
+ field.required && /* @__PURE__ */ jsx("span", { className: "kyro-form-label-required", children: "*" })
186
+ ] }),
187
+ /* @__PURE__ */ jsx(
188
+ "input",
189
+ {
190
+ type: "date",
191
+ className: `kyro-form-input ${error ? "kyro-form-input-error" : ""}`,
192
+ value: value ? new Date(value).toISOString().split("T")[0] : "",
193
+ onChange: (e) => handleFieldChange(
194
+ field.name,
195
+ e.target.value ? new Date(e.target.value) : null
196
+ ),
197
+ disabled
198
+ }
199
+ ),
200
+ error && /* @__PURE__ */ jsx("p", { className: "kyro-form-error", children: error })
201
+ ] }, field.name);
202
+ case "select":
203
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-field", children: [
204
+ /* @__PURE__ */ jsxs("label", { className: "kyro-form-label", children: [
205
+ field.label || field.name,
206
+ field.required && /* @__PURE__ */ jsx("span", { className: "kyro-form-label-required", children: "*" })
207
+ ] }),
208
+ /* @__PURE__ */ jsxs(
209
+ "select",
210
+ {
211
+ className: `kyro-form-input kyro-form-select ${error ? "kyro-form-input-error" : ""}`,
212
+ value: value || "",
213
+ onChange: (e) => handleFieldChange(field.name, e.target.value),
214
+ disabled,
215
+ children: [
216
+ /* @__PURE__ */ jsxs("option", { value: "", children: [
217
+ "Select ",
218
+ field.label || field.name
219
+ ] }),
220
+ field.options?.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value || opt, children: opt.label || opt }, opt.value || opt))
221
+ ]
222
+ }
223
+ ),
224
+ field.admin?.description && !error && /* @__PURE__ */ jsx("p", { className: "kyro-form-help", children: field.admin.description }),
225
+ error && /* @__PURE__ */ jsx("p", { className: "kyro-form-error", children: error })
226
+ ] }, field.name);
227
+ case "radio":
228
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-field", children: [
229
+ /* @__PURE__ */ jsxs("label", { className: "kyro-form-label", children: [
230
+ field.label || field.name,
231
+ field.required && /* @__PURE__ */ jsx("span", { className: "kyro-form-label-required", children: "*" })
232
+ ] }),
233
+ /* @__PURE__ */ jsx("div", { className: "kyro-form-radio-group", children: field.options?.map((opt) => /* @__PURE__ */ jsxs("label", { className: "kyro-form-radio", children: [
234
+ /* @__PURE__ */ jsx(
235
+ "input",
236
+ {
237
+ type: "radio",
238
+ name: field.name,
239
+ value: opt.value || opt,
240
+ checked: value === (opt.value || opt),
241
+ onChange: (e) => handleFieldChange(field.name, e.target.value),
242
+ disabled
243
+ }
244
+ ),
245
+ /* @__PURE__ */ jsx("span", { children: opt.label || opt })
246
+ ] }, opt.value || opt)) }),
247
+ error && /* @__PURE__ */ jsx("p", { className: "kyro-form-error", children: error })
248
+ ] }, field.name);
249
+ case "color":
250
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-field", children: [
251
+ /* @__PURE__ */ jsxs("label", { className: "kyro-form-label", children: [
252
+ field.label || field.name,
253
+ field.required && /* @__PURE__ */ jsx("span", { className: "kyro-form-label-required", children: "*" })
254
+ ] }),
255
+ /* @__PURE__ */ jsxs("div", { className: "kyro-form-color-wrapper", children: [
256
+ /* @__PURE__ */ jsx(
257
+ "input",
258
+ {
259
+ type: "color",
260
+ className: `kyro-form-color ${error ? "kyro-form-input-error" : ""}`,
261
+ value: value || "#000000",
262
+ onChange: (e) => handleFieldChange(field.name, e.target.value),
263
+ disabled
264
+ }
265
+ ),
266
+ /* @__PURE__ */ jsx("span", { className: "kyro-form-color-value", children: value || "#000000" })
267
+ ] }),
268
+ error && /* @__PURE__ */ jsx("p", { className: "kyro-form-error", children: error })
269
+ ] }, field.name);
270
+ case "json":
271
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-field", children: [
272
+ /* @__PURE__ */ jsxs("label", { className: "kyro-form-label", children: [
273
+ field.label || field.name,
274
+ field.required && /* @__PURE__ */ jsx("span", { className: "kyro-form-label-required", children: "*" })
275
+ ] }),
276
+ /* @__PURE__ */ jsx(
277
+ "textarea",
278
+ {
279
+ className: `kyro-form-input kyro-form-textarea kyro-form-code ${error ? "kyro-form-input-error" : ""}`,
280
+ value: typeof value === "string" ? value : JSON.stringify(value || {}, null, 2),
281
+ onChange: (e) => {
282
+ try {
283
+ handleFieldChange(field.name, JSON.parse(e.target.value));
284
+ } catch {
285
+ handleFieldChange(field.name, e.target.value);
286
+ }
287
+ },
288
+ disabled,
289
+ rows: 6,
290
+ placeholder: '{"key": "value"}'
291
+ }
292
+ ),
293
+ field.admin?.description && !error && /* @__PURE__ */ jsx("p", { className: "kyro-form-help", children: field.admin.description }),
294
+ error && /* @__PURE__ */ jsx("p", { className: "kyro-form-error", children: error })
295
+ ] }, field.name);
296
+ case "markdown":
297
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-field", children: [
298
+ /* @__PURE__ */ jsxs("label", { className: "kyro-form-label", children: [
299
+ field.label || field.name,
300
+ field.required && /* @__PURE__ */ jsx("span", { className: "kyro-form-label-required", children: "*" })
301
+ ] }),
302
+ /* @__PURE__ */ jsx(
303
+ "textarea",
304
+ {
305
+ className: `kyro-form-input kyro-form-textarea ${error ? "kyro-form-input-error" : ""}`,
306
+ value: value || "",
307
+ onChange: (e) => handleFieldChange(field.name, e.target.value),
308
+ disabled,
309
+ rows: 8,
310
+ placeholder: "Enter markdown content..."
311
+ }
312
+ ),
313
+ field.admin?.description && !error && /* @__PURE__ */ jsx("p", { className: "kyro-form-help", children: field.admin.description }),
314
+ error && /* @__PURE__ */ jsx("p", { className: "kyro-form-error", children: error })
315
+ ] }, field.name);
316
+ case "code":
317
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-field", children: [
318
+ /* @__PURE__ */ jsxs("label", { className: "kyro-form-label", children: [
319
+ field.label || field.name,
320
+ field.required && /* @__PURE__ */ jsx("span", { className: "kyro-form-label-required", children: "*" })
321
+ ] }),
322
+ /* @__PURE__ */ jsx(
323
+ "textarea",
324
+ {
325
+ className: `kyro-form-input kyro-form-textarea kyro-form-code ${error ? "kyro-form-input-error" : ""}`,
326
+ value: value || "",
327
+ onChange: (e) => handleFieldChange(field.name, e.target.value),
328
+ disabled,
329
+ rows: 8,
330
+ placeholder: "Enter code..."
331
+ }
332
+ ),
333
+ field.admin?.description && !error && /* @__PURE__ */ jsx("p", { className: "kyro-form-help", children: field.admin.description }),
334
+ error && /* @__PURE__ */ jsx("p", { className: "kyro-form-error", children: error })
335
+ ] }, field.name);
336
+ case "group":
337
+ if ("fields" in field) {
338
+ const groupData = value || {};
339
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-group", children: [
340
+ /* @__PURE__ */ jsx("h3", { className: "kyro-form-group-title", children: field.label || field.name }),
341
+ /* @__PURE__ */ jsx("div", { className: "kyro-form-group-fields", children: field.fields.map(
342
+ (f) => renderField(f, groupData)
343
+ ) })
344
+ ] }, field.name);
345
+ }
346
+ return null;
347
+ case "array":
348
+ if ("fields" in field) {
349
+ const items = Array.isArray(value) ? value : [];
350
+ const labels = field.labels || {
351
+ singular: "Item",
352
+ plural: "Items"
353
+ };
354
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-field", children: [
355
+ /* @__PURE__ */ jsx("label", { className: "kyro-form-label", children: field.label || field.name }),
356
+ /* @__PURE__ */ jsxs("div", { className: "kyro-form-array", children: [
357
+ items.length === 0 ? /* @__PURE__ */ jsxs("p", { className: "kyro-form-array-empty", children: [
358
+ "No ",
359
+ labels.plural.toLowerCase(),
360
+ " yet"
361
+ ] }) : items.map((item, index) => /* @__PURE__ */ jsxs("div", { className: "kyro-form-array-item", children: [
362
+ /* @__PURE__ */ jsxs("div", { className: "kyro-form-array-item-header", children: [
363
+ /* @__PURE__ */ jsx("span", { className: "kyro-form-array-item-number", children: index + 1 }),
364
+ /* @__PURE__ */ jsx(
365
+ "button",
366
+ {
367
+ type: "button",
368
+ className: "kyro-form-array-item-remove",
369
+ onClick: () => {
370
+ const newItems = items.filter(
371
+ (_, i) => i !== index
372
+ );
373
+ handleFieldChange(field.name, newItems);
374
+ },
375
+ disabled,
376
+ children: /* @__PURE__ */ jsx(
377
+ "svg",
378
+ {
379
+ width: "14",
380
+ height: "14",
381
+ viewBox: "0 0 24 24",
382
+ fill: "none",
383
+ stroke: "currentColor",
384
+ strokeWidth: "2",
385
+ children: /* @__PURE__ */ jsx("path", { d: "M18 6L6 18M6 6l12 12" })
386
+ }
387
+ )
388
+ }
389
+ )
390
+ ] }),
391
+ /* @__PURE__ */ jsx("div", { className: "kyro-form-array-item-fields", children: field.fields.map((f) => {
392
+ const fieldKey = f.name;
393
+ const subFieldValue = item[fieldKey];
394
+ const handleSubFieldChange = (newValue) => {
395
+ const newItem = { ...item, [fieldKey]: newValue };
396
+ const newItems = [...items];
397
+ newItems[index] = newItem;
398
+ handleFieldChange(field.name, newItems);
399
+ };
400
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-field", children: [
401
+ /* @__PURE__ */ jsx("label", { className: "kyro-form-label", children: f.label || f.name }),
402
+ renderSubField(
403
+ f,
404
+ subFieldValue,
405
+ handleSubFieldChange,
406
+ disabled
407
+ )
408
+ ] }, fieldKey);
409
+ }) })
410
+ ] }, index)),
411
+ /* @__PURE__ */ jsxs(
412
+ "button",
413
+ {
414
+ type: "button",
415
+ className: "kyro-btn kyro-btn-secondary kyro-btn-sm",
416
+ onClick: () => {
417
+ const newItem = {};
418
+ field.fields.forEach((f) => {
419
+ if (f.defaultValue !== void 0) {
420
+ newItem[f.name] = f.defaultValue;
421
+ }
422
+ });
423
+ handleFieldChange(field.name, [...items, newItem]);
424
+ },
425
+ disabled,
426
+ children: [
427
+ /* @__PURE__ */ jsx(
428
+ "svg",
429
+ {
430
+ width: "14",
431
+ height: "14",
432
+ viewBox: "0 0 24 24",
433
+ fill: "none",
434
+ stroke: "currentColor",
435
+ strokeWidth: "2",
436
+ children: /* @__PURE__ */ jsx("path", { d: "M12 5v14M5 12h14" })
437
+ }
438
+ ),
439
+ "Add ",
440
+ labels.singular
441
+ ]
442
+ }
443
+ )
444
+ ] })
445
+ ] }, field.name);
446
+ }
447
+ return null;
448
+ case "blocks":
449
+ if ("blocks" in field) {
450
+ const blocks = Array.isArray(value) ? value : [];
451
+ const labels = field.labels || {
452
+ plural: "Blocks"
453
+ };
454
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-field", children: [
455
+ /* @__PURE__ */ jsx("label", { className: "kyro-form-label", children: field.label || field.name }),
456
+ /* @__PURE__ */ jsxs("div", { className: "kyro-form-blocks", children: [
457
+ blocks.length === 0 ? /* @__PURE__ */ jsxs("p", { className: "kyro-form-blocks-empty", children: [
458
+ "No ",
459
+ labels.plural.toLowerCase(),
460
+ " yet"
461
+ ] }) : blocks.map((block, index) => /* @__PURE__ */ jsxs("div", { className: "kyro-form-block-item", children: [
462
+ /* @__PURE__ */ jsxs("div", { className: "kyro-form-block-item-header", children: [
463
+ /* @__PURE__ */ jsx("span", { className: "kyro-form-block-item-type", children: field.blocks.find(
464
+ (b) => b.slug === block.blockType
465
+ )?.label || block.blockType }),
466
+ /* @__PURE__ */ jsxs("div", { className: "kyro-form-block-item-actions", children: [
467
+ /* @__PURE__ */ jsx(
468
+ "button",
469
+ {
470
+ type: "button",
471
+ className: "kyro-form-block-item-move",
472
+ onClick: () => {
473
+ if (index > 0) {
474
+ const newBlocks = [...blocks];
475
+ [newBlocks[index - 1], newBlocks[index]] = [
476
+ newBlocks[index],
477
+ newBlocks[index - 1]
478
+ ];
479
+ handleFieldChange(field.name, newBlocks);
480
+ }
481
+ },
482
+ disabled: disabled || index === 0,
483
+ children: /* @__PURE__ */ jsx(
484
+ "svg",
485
+ {
486
+ width: "14",
487
+ height: "14",
488
+ viewBox: "0 0 24 24",
489
+ fill: "none",
490
+ stroke: "currentColor",
491
+ strokeWidth: "2",
492
+ children: /* @__PURE__ */ jsx("path", { d: "M18 15l-6-6-6 6" })
493
+ }
494
+ )
495
+ }
496
+ ),
497
+ /* @__PURE__ */ jsx(
498
+ "button",
499
+ {
500
+ type: "button",
501
+ className: "kyro-form-block-item-remove",
502
+ onClick: () => {
503
+ const newBlocks = blocks.filter(
504
+ (_, i) => i !== index
505
+ );
506
+ handleFieldChange(field.name, newBlocks);
507
+ },
508
+ disabled,
509
+ children: /* @__PURE__ */ jsx(
510
+ "svg",
511
+ {
512
+ width: "14",
513
+ height: "14",
514
+ viewBox: "0 0 24 24",
515
+ fill: "none",
516
+ stroke: "currentColor",
517
+ strokeWidth: "2",
518
+ children: /* @__PURE__ */ jsx("path", { d: "M18 6L6 18M6 6l12 12" })
519
+ }
520
+ )
521
+ }
522
+ )
523
+ ] })
524
+ ] }),
525
+ /* @__PURE__ */ jsx("div", { className: "kyro-form-block-item-fields", children: field.blocks.find((b) => b.slug === block.blockType)?.fields?.map((f) => {
526
+ if (!f.name) return null;
527
+ const fieldKey = f.name;
528
+ const blockData = block;
529
+ const handleBlockFieldChange = (newValue) => {
530
+ const newBlock = {
531
+ ...blockData,
532
+ [fieldKey]: newValue
533
+ };
534
+ const newBlocks = [...blocks];
535
+ newBlocks[index] = newBlock;
536
+ handleFieldChange(field.name, newBlocks);
537
+ };
538
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-field", children: [
539
+ /* @__PURE__ */ jsx("label", { className: "kyro-form-label", children: f.label || f.name }),
540
+ renderSubField(
541
+ f,
542
+ block[fieldKey],
543
+ handleBlockFieldChange,
544
+ disabled
545
+ )
546
+ ] }, fieldKey);
547
+ }) })
548
+ ] }, index)),
549
+ /* @__PURE__ */ jsxs("div", { className: "kyro-form-blocks-add", children: [
550
+ /* @__PURE__ */ jsx("span", { className: "kyro-form-blocks-add-label", children: "Add block:" }),
551
+ /* @__PURE__ */ jsx("div", { className: "kyro-form-blocks-add-buttons", children: field.blocks.map((block) => /* @__PURE__ */ jsx(
552
+ "button",
553
+ {
554
+ type: "button",
555
+ className: "kyro-btn kyro-btn-secondary kyro-btn-sm",
556
+ onClick: () => {
557
+ const newBlock = { blockType: block.slug };
558
+ handleFieldChange(field.name, [...blocks, newBlock]);
559
+ },
560
+ disabled,
561
+ children: block.label
562
+ },
563
+ block.slug
564
+ )) })
565
+ ] })
566
+ ] })
567
+ ] }, field.name);
568
+ }
569
+ return null;
570
+ case "relationship":
571
+ return /* @__PURE__ */ jsx(
572
+ RelationshipField,
573
+ {
574
+ field,
575
+ value,
576
+ onChange: (newValue) => handleFieldChange(field.name, newValue),
577
+ disabled,
578
+ error
579
+ },
580
+ field.name
581
+ );
582
+ case "upload":
583
+ return /* @__PURE__ */ jsx(
584
+ UploadField,
585
+ {
586
+ field,
587
+ value,
588
+ onChange: (newValue) => handleFieldChange(field.name, newValue),
589
+ disabled,
590
+ error
591
+ },
592
+ field.name
593
+ );
594
+ case "richtext":
595
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-field", children: [
596
+ /* @__PURE__ */ jsxs("label", { className: "kyro-form-label", children: [
597
+ field.label || field.name,
598
+ field.required && /* @__PURE__ */ jsx("span", { className: "kyro-form-label-required", children: "*" })
599
+ ] }),
600
+ /* @__PURE__ */ jsx(
601
+ "textarea",
602
+ {
603
+ className: `kyro-form-input kyro-form-textarea kyro-form-richtext ${error ? "kyro-form-input-error" : ""}`,
604
+ value: value || "",
605
+ onChange: (e) => handleFieldChange(field.name, e.target.value),
606
+ disabled,
607
+ rows: 8,
608
+ placeholder: "Enter rich text content..."
609
+ }
610
+ ),
611
+ field.admin?.description && !error && /* @__PURE__ */ jsx("p", { className: "kyro-form-help", children: field.admin.description }),
612
+ error && /* @__PURE__ */ jsx("p", { className: "kyro-form-error", children: error })
613
+ ] }, field.name);
614
+ default: {
615
+ const anyField = field;
616
+ return /* @__PURE__ */ jsx("div", { className: "kyro-form-field", children: /* @__PURE__ */ jsxs("span", { className: "kyro-form-unsupported", children: [
617
+ "Unsupported field type: ",
618
+ anyField.type
619
+ ] }) }, anyField.name);
620
+ }
621
+ }
622
+ };
623
+ return /* @__PURE__ */ jsx("div", { className: "kyro-form", children: config.fields.map((field) => renderField(field)) });
624
+ }
625
+ function renderSubField(field, value, onChange, disabled) {
626
+ switch (field.type) {
627
+ case "text":
628
+ case "email":
629
+ case "password":
630
+ return /* @__PURE__ */ jsx(
631
+ "input",
632
+ {
633
+ type: field.type === "email" ? "email" : "text",
634
+ className: "kyro-form-input",
635
+ value: value || "",
636
+ onChange: (e) => onChange(e.target.value),
637
+ disabled
638
+ }
639
+ );
640
+ case "number":
641
+ return /* @__PURE__ */ jsx(
642
+ "input",
643
+ {
644
+ type: "number",
645
+ className: "kyro-form-input kyro-form-number",
646
+ value: value ?? "",
647
+ onChange: (e) => onChange(parseFloat(e.target.value) || 0),
648
+ disabled
649
+ }
650
+ );
651
+ case "checkbox":
652
+ return /* @__PURE__ */ jsx(
653
+ "input",
654
+ {
655
+ type: "checkbox",
656
+ checked: value || false,
657
+ onChange: (e) => onChange(e.target.checked),
658
+ disabled
659
+ }
660
+ );
661
+ case "textarea":
662
+ return /* @__PURE__ */ jsx(
663
+ "textarea",
664
+ {
665
+ className: "kyro-form-input kyro-form-textarea",
666
+ value: value || "",
667
+ onChange: (e) => onChange(e.target.value),
668
+ disabled,
669
+ rows: 3
670
+ }
671
+ );
672
+ case "select":
673
+ return /* @__PURE__ */ jsxs(
674
+ "select",
675
+ {
676
+ className: "kyro-form-input kyro-form-select",
677
+ value: value || "",
678
+ onChange: (e) => onChange(e.target.value),
679
+ disabled,
680
+ children: [
681
+ /* @__PURE__ */ jsx("option", { value: "", children: "Select..." }),
682
+ field.options?.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value || opt, children: opt.label || opt }, opt.value || opt))
683
+ ]
684
+ }
685
+ );
686
+ default:
687
+ return /* @__PURE__ */ jsx(
688
+ "input",
689
+ {
690
+ type: "text",
691
+ className: "kyro-form-input",
692
+ value: value || "",
693
+ onChange: (e) => onChange(e.target.value),
694
+ disabled
695
+ }
696
+ );
697
+ }
698
+ }
699
+ function UploadField({
700
+ field,
701
+ value,
702
+ onChange,
703
+ disabled,
704
+ error
705
+ }) {
706
+ const inputRef = useRef(null);
707
+ const [preview, setPreview] = useState(null);
708
+ const [isDragging, setIsDragging] = useState(false);
709
+ const isImage = (file) => file.type.startsWith("image/");
710
+ const handleFile = (file) => {
711
+ if (isImage(file)) {
712
+ const reader = new FileReader();
713
+ reader.onloadend = () => {
714
+ setPreview(reader.result);
715
+ };
716
+ reader.readAsDataURL(file);
717
+ }
718
+ onChange({
719
+ filename: file.name,
720
+ size: file.size,
721
+ type: file.type,
722
+ url: URL.createObjectURL(file)
723
+ });
724
+ };
725
+ const handleDrop = (e) => {
726
+ e.preventDefault();
727
+ setIsDragging(false);
728
+ const file = e.dataTransfer.files[0];
729
+ if (file) handleFile(file);
730
+ };
731
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-field", children: [
732
+ /* @__PURE__ */ jsxs("label", { className: "kyro-form-label", children: [
733
+ field.label || field.name,
734
+ field.required && /* @__PURE__ */ jsx("span", { className: "kyro-form-label-required", children: "*" })
735
+ ] }),
736
+ /* @__PURE__ */ jsxs(
737
+ "div",
738
+ {
739
+ className: `kyro-form-upload ${isDragging ? "kyro-form-upload-dragging" : ""} ${error ? "kyro-form-upload-error" : ""}`,
740
+ onDragOver: (e) => {
741
+ e.preventDefault();
742
+ setIsDragging(true);
743
+ },
744
+ onDragLeave: () => setIsDragging(false),
745
+ onDrop: handleDrop,
746
+ onClick: () => inputRef.current?.click(),
747
+ children: [
748
+ /* @__PURE__ */ jsx(
749
+ "input",
750
+ {
751
+ ref: inputRef,
752
+ type: "file",
753
+ className: "kyro-form-upload-input",
754
+ onChange: (e) => {
755
+ const file = e.target.files?.[0];
756
+ if (file) handleFile(file);
757
+ },
758
+ disabled,
759
+ accept: field.mimeTypes?.join(",")
760
+ }
761
+ ),
762
+ preview || value?.url ? /* @__PURE__ */ jsxs("div", { className: "kyro-form-upload-preview", children: [
763
+ /* @__PURE__ */ jsx(
764
+ "img",
765
+ {
766
+ src: preview || value.url,
767
+ alt: "Preview",
768
+ className: "kyro-form-upload-image"
769
+ }
770
+ ),
771
+ /* @__PURE__ */ jsxs("div", { className: "kyro-form-upload-info", children: [
772
+ /* @__PURE__ */ jsx("span", { className: "kyro-form-upload-filename", children: value?.filename || "Uploaded file" }),
773
+ /* @__PURE__ */ jsx(
774
+ "button",
775
+ {
776
+ type: "button",
777
+ className: "kyro-form-upload-change",
778
+ onClick: (e) => {
779
+ e.stopPropagation();
780
+ inputRef.current?.click();
781
+ },
782
+ children: "Change"
783
+ }
784
+ )
785
+ ] })
786
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "kyro-form-upload-placeholder", children: [
787
+ /* @__PURE__ */ jsxs(
788
+ "svg",
789
+ {
790
+ width: "32",
791
+ height: "32",
792
+ viewBox: "0 0 24 24",
793
+ fill: "none",
794
+ stroke: "currentColor",
795
+ strokeWidth: "1.5",
796
+ children: [
797
+ /* @__PURE__ */ jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
798
+ /* @__PURE__ */ jsx("polyline", { points: "17,8 12,3 7,8" }),
799
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "3", x2: "12", y2: "15" })
800
+ ]
801
+ }
802
+ ),
803
+ /* @__PURE__ */ jsx("span", { children: "Drop image here or click to upload" }),
804
+ /* @__PURE__ */ jsx("span", { className: "kyro-form-upload-hint", children: "PNG, JPG, GIF up to 10MB" })
805
+ ] })
806
+ ]
807
+ }
808
+ ),
809
+ field.admin?.description && !error && /* @__PURE__ */ jsx("p", { className: "kyro-form-help", children: field.admin.description }),
810
+ error && /* @__PURE__ */ jsx("p", { className: "kyro-form-error", children: error })
811
+ ] });
812
+ }
813
+ function RelationshipField({
814
+ field,
815
+ value,
816
+ onChange,
817
+ disabled,
818
+ error
819
+ }) {
820
+ const [isOpen, setIsOpen] = useState(false);
821
+ const [search, setSearch] = useState("");
822
+ const [options, setOptions] = useState([]);
823
+ const [loading, setLoading] = useState(false);
824
+ const isMultiple = field.hasMany;
825
+ const targetCollection = Array.isArray(field.relationTo) ? field.relationTo[0] : field.relationTo;
826
+ useEffect(() => {
827
+ if (isOpen) {
828
+ setLoading(true);
829
+ fetch(`/api/${targetCollection}?limit=50`).then((res) => res.json()).then((data) => {
830
+ setOptions(data.docs || []);
831
+ setLoading(false);
832
+ }).catch((err) => {
833
+ console.error("Failed to fetch relations:", err);
834
+ setLoading(false);
835
+ });
836
+ }
837
+ }, [isOpen, targetCollection]);
838
+ const filteredOptions = options.filter((opt) => {
839
+ const term = search.toLowerCase();
840
+ const searchableFields = ["title", "name", "filename", "id", "slug"];
841
+ return searchableFields.some(
842
+ (key) => opt[key] && String(opt[key]).toLowerCase().includes(term)
843
+ );
844
+ });
845
+ const getLabel = (opt) => {
846
+ if (!opt) return "";
847
+ return opt.title || opt.name || opt.filename || opt.slug || opt.id;
848
+ };
849
+ const isSelected = (optId) => {
850
+ if (!value) return false;
851
+ if (isMultiple) {
852
+ return value.some((v) => (v.id || v) === optId);
853
+ }
854
+ return (value.id || value) === optId;
855
+ };
856
+ const toggleSelection = (opt) => {
857
+ if (isMultiple) {
858
+ const current = Array.isArray(value) ? value : [];
859
+ if (isSelected(opt.id)) {
860
+ onChange(current.filter((item) => (item.id || item) !== opt.id));
861
+ } else {
862
+ onChange([...current, opt.id]);
863
+ }
864
+ } else {
865
+ if (isSelected(opt.id)) {
866
+ onChange(null);
867
+ } else {
868
+ onChange(opt.id);
869
+ setIsOpen(false);
870
+ }
871
+ }
872
+ };
873
+ return /* @__PURE__ */ jsxs("div", { className: "kyro-form-field", children: [
874
+ /* @__PURE__ */ jsxs("label", { className: "kyro-form-label", children: [
875
+ field.label || field.name,
876
+ field.required && /* @__PURE__ */ jsx("span", { className: "kyro-form-label-required", children: "*" })
877
+ ] }),
878
+ /* @__PURE__ */ jsxs(
879
+ "div",
880
+ {
881
+ className: "kyro-form-relationship",
882
+ onClick: () => !disabled && setIsOpen(true),
883
+ style: disabled ? { opacity: 0.5, cursor: "not-allowed" } : {},
884
+ children: [
885
+ /* @__PURE__ */ jsxs("div", { className: "kyro-form-relationship-header", children: [
886
+ /* @__PURE__ */ jsx("span", { className: "kyro-form-relationship-type", children: targetCollection }),
887
+ isMultiple && /* @__PURE__ */ jsx("span", { className: "kyro-form-relationship-badge", children: "Multiple" })
888
+ ] }),
889
+ /* @__PURE__ */ jsx("div", { className: "kyro-form-relationship-value", children: value ? isMultiple && Array.isArray(value) ? value.length > 0 ? `${value.length} items selected` : "None selected" : `Selected: ${getLabel(value) || value.id || value}` : /* @__PURE__ */ jsx("span", { className: "kyro-form-relationship-empty", children: "Click to search and select..." }) })
890
+ ]
891
+ }
892
+ ),
893
+ field.admin?.description && !error && /* @__PURE__ */ jsx("p", { className: "kyro-form-help", children: field.admin.description }),
894
+ error && /* @__PURE__ */ jsx("p", { className: "kyro-form-error", children: error }),
895
+ isOpen && /* @__PURE__ */ jsx("div", { className: "kyro-modal-overlay", onClick: () => setIsOpen(false), children: /* @__PURE__ */ jsxs(
896
+ "div",
897
+ {
898
+ className: "kyro-relation-modal",
899
+ onClick: (e) => e.stopPropagation(),
900
+ children: [
901
+ /* @__PURE__ */ jsxs("div", { className: "kyro-relation-modal-header", children: [
902
+ /* @__PURE__ */ jsxs("h3", { children: [
903
+ "Select ",
904
+ field.label || field.name
905
+ ] }),
906
+ /* @__PURE__ */ jsx(
907
+ "input",
908
+ {
909
+ type: "text",
910
+ autoFocus: true,
911
+ placeholder: `Search in ${targetCollection}...`,
912
+ className: "kyro-relation-modal-search",
913
+ value: search,
914
+ onChange: (e) => setSearch(e.target.value)
915
+ }
916
+ )
917
+ ] }),
918
+ /* @__PURE__ */ jsx("div", { className: "kyro-relation-modal-list", children: loading ? /* @__PURE__ */ jsx("div", { className: "kyro-relation-modal-empty", children: "Loading..." }) : filteredOptions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "kyro-relation-modal-empty", children: "No results found." }) : filteredOptions.map((opt) => /* @__PURE__ */ jsxs(
919
+ "button",
920
+ {
921
+ type: "button",
922
+ className: `kyro-relation-modal-item ${isSelected(opt.id) ? "selected" : ""}`,
923
+ onClick: () => toggleSelection(opt),
924
+ children: [
925
+ /* @__PURE__ */ jsx("span", { children: getLabel(opt) }),
926
+ /* @__PURE__ */ jsxs("span", { className: "kyro-relation-modal-item-id", children: [
927
+ "(",
928
+ opt.id.slice(0, 8),
929
+ "...)"
930
+ ] })
931
+ ]
932
+ },
933
+ opt.id
934
+ )) }),
935
+ /* @__PURE__ */ jsx("div", { className: "kyro-relation-modal-footer", children: /* @__PURE__ */ jsx(
936
+ "button",
937
+ {
938
+ type: "button",
939
+ className: "kyro-btn kyro-btn-secondary kyro-btn-sm",
940
+ onClick: () => setIsOpen(false),
941
+ children: "Done"
942
+ }
943
+ ) })
944
+ ]
945
+ }
946
+ ) })
947
+ ] });
948
+ }
949
+
950
+ var __freeze = Object.freeze;
951
+ var __defProp = Object.defineProperty;
952
+ var __template = (cooked, raw) => __freeze(__defProp(cooked, "raw", { value: __freeze(raw || cooked.slice()) }));
953
+ var _a;
954
+ const $$id = createComponent(async ($$result, $$props, $$slots) => {
955
+ const Astro2 = $$result.createAstro($$props, $$slots);
956
+ Astro2.self = $$id;
957
+ const { collection, id } = Astro2.params;
958
+ if (!collection || !collections[collection]) {
959
+ return Astro2.redirect("/");
960
+ }
961
+ const config = collections[collection];
962
+ let doc = null;
963
+ if (id && id !== "new") {
964
+ try {
965
+ const response = await fetch(`${Astro2.url.origin}/api/${collection}/${id}`);
966
+ if (response.ok) {
967
+ const result = await response.json();
968
+ doc = result.data || null;
969
+ }
970
+ } catch (error) {
971
+ console.error("Failed to fetch document:", error);
972
+ }
973
+ }
974
+ const isNew = !doc;
975
+ const title = isNew ? `Create ${config.singularLabel || config.label || collection}` : `Edit ${config.singularLabel || config.label || collection}`;
976
+ const description = config.admin?.description || `Manage ${(config.label || collection || "").toLowerCase()} documents`;
977
+ return renderTemplate`${renderComponent($$result, "AdminLayout", $$AdminLayout, { "title": title }, { "default": async ($$result2) => renderTemplate(_a || (_a = __template([" ", '<div class="flex-1 overflow-y-auto p-8 space-y-6"> <!-- Header --> <div class="surface-tile p-8 flex items-center justify-between"> <div class="flex items-center gap-4"> <a', ' class="inline-flex items-center justify-center w-10 h-10 rounded-xl border border-gray-200 text-[#64748b] hover:text-[#0b1222] hover:bg-gray-50 transition-colors"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M15 19l-7-7 7-7"></path> </svg> </a> <div> <h1 class="text-2xl font-black tracking-tighter text-[#0b1222]">', '</h1> <p class="text-sm text-[#64748b] mt-0.5">', '</p> </div> </div> <div class="flex items-center gap-3"> <a', ' class="px-5 py-2.5 border border-gray-200 rounded-xl text-[#0b1222] font-bold text-sm hover:bg-gray-50 transition-colors">\nCancel\n</a> <button type="submit" form="doc-form" id="btn-save" class="px-6 py-2.5 bg-[#0b1222] text-white rounded-xl font-bold text-sm hover:bg-[#1a2332] transition-colors active:scale-95"> ', ` </button> </div> </div> <!-- Form Card --> <div class="surface-tile p-8"> <!-- Toast container --> <div id="toast-container" class="hidden fixed bottom-6 right-6 z-50"> <div class="flex items-center gap-3 px-5 py-4 bg-[#0b1222] text-white rounded-xl shadow-2xl"> <span id="toast-message"></span> <button onclick="document.getElementById('toast-container').classList.add('hidden')" class="text-white/50 hover:text-white ml-2"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg> </button> </div> </div> <form id="doc-form"> `, " </form> </div> <!-- Document Metadata (edit mode only) --> ", " </div> <script>(function(){", "\n function showToast(message, isError = false) {\n const container = document.getElementById('toast-container');\n const msg = document.getElementById('toast-message');\n if (container && msg) {\n msg.textContent = message;\n container.classList.remove('hidden');\n if (!isError) {\n setTimeout(() => container.classList.add('hidden'), 3000);\n }\n }\n }\n\n document.getElementById('doc-form')?.addEventListener('submit', async (e) => {\n e.preventDefault();\n \n const btn = document.getElementById('btn-save');\n const originalText = btn?.textContent || '';\n if (btn) {\n btn.textContent = 'Saving…';\n btn.setAttribute('disabled', 'true');\n }\n\n const formData = new FormData(e.target);\n const data = {};\n formData.forEach((value, key) => {\n data[key] = value;\n });\n\n const url = isNew ? `/api/${collection}` : `/api/${collection}/${id}`;\n const method = isNew ? 'POST' : 'PATCH';\n\n try {\n const response = await fetch(url, {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n });\n\n if (response.ok) {\n showToast(isNew ? 'Document created successfully' : 'Changes saved');\n setTimeout(() => {\n window.location.href = `/${collection}`;\n }, 800);\n } else {\n const error = await response.json();\n showToast(error.error || 'An error occurred', true);\n }\n } catch (error) {\n showToast('Failed to save document', true);\n } finally {\n if (btn) {\n btn.textContent = originalText;\n btn.removeAttribute('disabled');\n }\n }\n });\n })();<\/script> "], [" ", '<div class="flex-1 overflow-y-auto p-8 space-y-6"> <!-- Header --> <div class="surface-tile p-8 flex items-center justify-between"> <div class="flex items-center gap-4"> <a', ' class="inline-flex items-center justify-center w-10 h-10 rounded-xl border border-gray-200 text-[#64748b] hover:text-[#0b1222] hover:bg-gray-50 transition-colors"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M15 19l-7-7 7-7"></path> </svg> </a> <div> <h1 class="text-2xl font-black tracking-tighter text-[#0b1222]">', '</h1> <p class="text-sm text-[#64748b] mt-0.5">', '</p> </div> </div> <div class="flex items-center gap-3"> <a', ' class="px-5 py-2.5 border border-gray-200 rounded-xl text-[#0b1222] font-bold text-sm hover:bg-gray-50 transition-colors">\nCancel\n</a> <button type="submit" form="doc-form" id="btn-save" class="px-6 py-2.5 bg-[#0b1222] text-white rounded-xl font-bold text-sm hover:bg-[#1a2332] transition-colors active:scale-95"> ', ` </button> </div> </div> <!-- Form Card --> <div class="surface-tile p-8"> <!-- Toast container --> <div id="toast-container" class="hidden fixed bottom-6 right-6 z-50"> <div class="flex items-center gap-3 px-5 py-4 bg-[#0b1222] text-white rounded-xl shadow-2xl"> <span id="toast-message"></span> <button onclick="document.getElementById('toast-container').classList.add('hidden')" class="text-white/50 hover:text-white ml-2"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg> </button> </div> </div> <form id="doc-form"> `, " </form> </div> <!-- Document Metadata (edit mode only) --> ", " </div> <script>(function(){", "\n function showToast(message, isError = false) {\n const container = document.getElementById('toast-container');\n const msg = document.getElementById('toast-message');\n if (container && msg) {\n msg.textContent = message;\n container.classList.remove('hidden');\n if (!isError) {\n setTimeout(() => container.classList.add('hidden'), 3000);\n }\n }\n }\n\n document.getElementById('doc-form')?.addEventListener('submit', async (e) => {\n e.preventDefault();\n \n const btn = document.getElementById('btn-save');\n const originalText = btn?.textContent || '';\n if (btn) {\n btn.textContent = 'Saving…';\n btn.setAttribute('disabled', 'true');\n }\n\n const formData = new FormData(e.target);\n const data = {};\n formData.forEach((value, key) => {\n data[key] = value;\n });\n\n const url = isNew ? \\`/api/\\${collection}\\` : \\`/api/\\${collection}/\\${id}\\`;\n const method = isNew ? 'POST' : 'PATCH';\n\n try {\n const response = await fetch(url, {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n });\n\n if (response.ok) {\n showToast(isNew ? 'Document created successfully' : 'Changes saved');\n setTimeout(() => {\n window.location.href = \\`/\\${collection}\\`;\n }, 800);\n } else {\n const error = await response.json();\n showToast(error.error || 'An error occurred', true);\n }\n } catch (error) {\n showToast('Failed to save document', true);\n } finally {\n if (btn) {\n btn.textContent = originalText;\n btn.removeAttribute('disabled');\n }\n }\n });\n })();<\/script> "])), maybeRenderHead(), addAttribute(`/${collection}`, "href"), title, description, addAttribute(`/${collection}`, "href"), isNew ? "Create" : "Save Changes", renderComponent($$result2, "AutoForm", AutoForm, { "client:load": true, "config": config, "data": doc || {}, "collectionSlug": collection, "client:component-hydration": "load", "client:component-path": "@/components/AutoForm", "client:component-export": "AutoForm" }), !isNew && doc && renderTemplate`<div class="surface-tile p-8"> <h3 class="text-xs font-bold text-[#64748b] uppercase tracking-[0.2em] mb-4">Document Info</h3> <div class="grid grid-cols-2 md:grid-cols-4 gap-6"> <div> <p class="text-[10px] font-bold text-[#9ca3af] uppercase tracking-widest mb-1">ID</p> <p class="text-sm text-[#0b1222] font-mono">${doc.id?.slice(0, 12)}…</p> </div> ${doc.createdAt && renderTemplate`<div> <p class="text-[10px] font-bold text-[#9ca3af] uppercase tracking-widest mb-1">Created</p> <p class="text-sm text-[#0b1222]">${new Date(doc.createdAt).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}</p> </div>`} ${doc.updatedAt && renderTemplate`<div> <p class="text-[10px] font-bold text-[#9ca3af] uppercase tracking-widest mb-1">Updated</p> <p class="text-sm text-[#0b1222]">${new Date(doc.updatedAt).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}</p> </div>`} </div> </div>`, defineScriptVars({ collection, id, isNew })) })}`;
978
+ }, "/Users/macbook/Dev/Web/Astro/kyro-cms/admin/src/pages/[collection]/[id].astro", void 0);
979
+
980
+ const $$file = "/Users/macbook/Dev/Web/Astro/kyro-cms/admin/src/pages/[collection]/[id].astro";
981
+ const $$url = "/[collection]/[id]";
982
+
983
+ const _page = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
984
+ __proto__: null,
985
+ default: $$id,
986
+ file: $$file,
987
+ url: $$url
988
+ }, Symbol.toStringTag, { value: 'Module' }));
989
+
990
+ const page = () => _page;
991
+
992
+ export { page };