@questpie/admin 0.0.1 → 1.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.
Files changed (250) hide show
  1. package/README.md +439 -424
  2. package/dist/auth-layout-M8K8_q5R.mjs +181 -0
  3. package/dist/auth-layout-M8K8_q5R.mjs.map +1 -0
  4. package/dist/bulk-upload-dialog-h7zXD78Y.mjs +274 -0
  5. package/dist/bulk-upload-dialog-h7zXD78Y.mjs.map +1 -0
  6. package/dist/{components/ui/card.mjs → card-BKHjBQfw.mjs} +8 -8
  7. package/dist/card-BKHjBQfw.mjs.map +1 -0
  8. package/dist/client/styles/index.css +434 -0
  9. package/dist/client-BCGpkAz6.mjs +22635 -0
  10. package/dist/client-BCGpkAz6.mjs.map +1 -0
  11. package/dist/client-CcWZbkBP.d.mts +13585 -0
  12. package/dist/client-CcWZbkBP.d.mts.map +1 -0
  13. package/dist/client.d.mts +3 -0
  14. package/dist/client.mjs +14 -0
  15. package/dist/content-locales-provider-BXvuIgfg.mjs +1650 -0
  16. package/dist/content-locales-provider-BXvuIgfg.mjs.map +1 -0
  17. package/dist/dashboard-page-B4PGEdc2.mjs +2500 -0
  18. package/dist/dashboard-page-B4PGEdc2.mjs.map +1 -0
  19. package/dist/dashboard-page-CVlyR40m.mjs +6 -0
  20. package/dist/dropzone-Do3awXKd.mjs +634 -0
  21. package/dist/dropzone-Do3awXKd.mjs.map +1 -0
  22. package/dist/{views/auth/forgot-password-form.mjs → forgot-password-page-Bcp-An4Y.mjs} +87 -14
  23. package/dist/forgot-password-page-Bcp-An4Y.mjs.map +1 -0
  24. package/dist/forgot-password-page-CIILVhfo.mjs +7 -0
  25. package/dist/index-B9Xwk4hi.d.mts +2753 -0
  26. package/dist/index-B9Xwk4hi.d.mts.map +1 -0
  27. package/dist/index.d.mts +3 -0
  28. package/dist/index.mjs +14 -0
  29. package/dist/login-page-8K7fo0qK.mjs +7 -0
  30. package/dist/login-page-CP4gA-dl.mjs +298 -0
  31. package/dist/login-page-CP4gA-dl.mjs.map +1 -0
  32. package/dist/preview-utils-BKQ9-TMa.mjs +65 -0
  33. package/dist/preview-utils-BKQ9-TMa.mjs.map +1 -0
  34. package/dist/{views/auth/reset-password-form.mjs → reset-password-page-BqfDmLxA.mjs} +111 -14
  35. package/dist/reset-password-page-BqfDmLxA.mjs.map +1 -0
  36. package/dist/reset-password-page-DLATv0xQ.mjs +7 -0
  37. package/dist/runtime-6VZM878K.mjs +69 -0
  38. package/dist/runtime-6VZM878K.mjs.map +1 -0
  39. package/dist/saved-views.types-BMsz5mCy.d.mts +42 -0
  40. package/dist/saved-views.types-BMsz5mCy.d.mts.map +1 -0
  41. package/dist/server.d.mts +250 -0
  42. package/dist/server.d.mts.map +1 -0
  43. package/dist/server.mjs +832 -0
  44. package/dist/server.mjs.map +1 -0
  45. package/dist/setup-page-CMZ5P_OE.mjs +6 -0
  46. package/dist/setup-page-YAP_fzqh.mjs +264 -0
  47. package/dist/setup-page-YAP_fzqh.mjs.map +1 -0
  48. package/dist/shared.d.mts +57 -0
  49. package/dist/shared.d.mts.map +1 -0
  50. package/dist/shared.mjs +3 -0
  51. package/dist/{hooks/use-auth.mjs → use-auth-BoLmWtmU.mjs} +42 -30
  52. package/dist/use-auth-BoLmWtmU.mjs.map +1 -0
  53. package/package.json +48 -197
  54. package/.turbo/turbo-build.log +0 -108
  55. package/CHANGELOG.md +0 -10
  56. package/STATUS.md +0 -917
  57. package/VALIDATION.md +0 -602
  58. package/components.json +0 -24
  59. package/dist/__tests__/setup.mjs +0 -38
  60. package/dist/__tests__/test-utils.mjs +0 -45
  61. package/dist/__tests__/vitest.d.mjs +0 -3
  62. package/dist/components/admin-app.mjs +0 -69
  63. package/dist/components/fields/array-field.mjs +0 -190
  64. package/dist/components/fields/checkbox-field.mjs +0 -34
  65. package/dist/components/fields/custom-field.mjs +0 -32
  66. package/dist/components/fields/date-field.mjs +0 -41
  67. package/dist/components/fields/datetime-field.mjs +0 -42
  68. package/dist/components/fields/email-field.mjs +0 -37
  69. package/dist/components/fields/embedded-collection.mjs +0 -253
  70. package/dist/components/fields/field-types.mjs +0 -1
  71. package/dist/components/fields/field-utils.mjs +0 -10
  72. package/dist/components/fields/field-wrapper.mjs +0 -34
  73. package/dist/components/fields/index.mjs +0 -23
  74. package/dist/components/fields/json-field.mjs +0 -243
  75. package/dist/components/fields/locale-badge.mjs +0 -16
  76. package/dist/components/fields/number-field.mjs +0 -39
  77. package/dist/components/fields/password-field.mjs +0 -37
  78. package/dist/components/fields/relation-field.mjs +0 -104
  79. package/dist/components/fields/relation-picker.mjs +0 -229
  80. package/dist/components/fields/relation-select.mjs +0 -188
  81. package/dist/components/fields/rich-text-editor/index.mjs +0 -897
  82. package/dist/components/fields/select-field.mjs +0 -41
  83. package/dist/components/fields/switch-field.mjs +0 -34
  84. package/dist/components/fields/text-field.mjs +0 -38
  85. package/dist/components/fields/textarea-field.mjs +0 -38
  86. package/dist/components/index.mjs +0 -59
  87. package/dist/components/primitives/checkbox-input.mjs +0 -127
  88. package/dist/components/primitives/date-input.mjs +0 -303
  89. package/dist/components/primitives/index.mjs +0 -12
  90. package/dist/components/primitives/number-input.mjs +0 -104
  91. package/dist/components/primitives/select-input.mjs +0 -177
  92. package/dist/components/primitives/tag-input.mjs +0 -135
  93. package/dist/components/primitives/text-input.mjs +0 -39
  94. package/dist/components/primitives/textarea-input.mjs +0 -37
  95. package/dist/components/primitives/toggle-input.mjs +0 -31
  96. package/dist/components/primitives/types.mjs +0 -12
  97. package/dist/components/ui/accordion.mjs +0 -55
  98. package/dist/components/ui/avatar.mjs +0 -54
  99. package/dist/components/ui/badge.mjs +0 -34
  100. package/dist/components/ui/button.mjs +0 -48
  101. package/dist/components/ui/checkbox.mjs +0 -21
  102. package/dist/components/ui/combobox.mjs +0 -163
  103. package/dist/components/ui/dialog.mjs +0 -95
  104. package/dist/components/ui/dropdown-menu.mjs +0 -138
  105. package/dist/components/ui/field.mjs +0 -113
  106. package/dist/components/ui/input-group.mjs +0 -82
  107. package/dist/components/ui/input.mjs +0 -17
  108. package/dist/components/ui/label.mjs +0 -15
  109. package/dist/components/ui/popover.mjs +0 -56
  110. package/dist/components/ui/scroll-area.mjs +0 -38
  111. package/dist/components/ui/select.mjs +0 -100
  112. package/dist/components/ui/separator.mjs +0 -16
  113. package/dist/components/ui/sheet.mjs +0 -90
  114. package/dist/components/ui/sidebar.mjs +0 -387
  115. package/dist/components/ui/skeleton.mjs +0 -14
  116. package/dist/components/ui/spinner.mjs +0 -16
  117. package/dist/components/ui/switch.mjs +0 -22
  118. package/dist/components/ui/table.mjs +0 -68
  119. package/dist/components/ui/tabs.mjs +0 -48
  120. package/dist/components/ui/textarea.mjs +0 -15
  121. package/dist/components/ui/tooltip.mjs +0 -44
  122. package/dist/config/component-registry.mjs +0 -38
  123. package/dist/config/index.mjs +0 -129
  124. package/dist/hooks/admin-provider.mjs +0 -70
  125. package/dist/hooks/index.mjs +0 -7
  126. package/dist/hooks/store.mjs +0 -178
  127. package/dist/hooks/use-collection-db.mjs +0 -146
  128. package/dist/hooks/use-collection.mjs +0 -112
  129. package/dist/hooks/use-global.mjs +0 -46
  130. package/dist/hooks/use-mobile.mjs +0 -20
  131. package/dist/lib/utils.mjs +0 -10
  132. package/dist/styles/index.css +0 -336
  133. package/dist/styles/index.mjs +0 -1
  134. package/dist/utils/index.mjs +0 -9
  135. package/dist/views/auth/auth-layout.mjs +0 -52
  136. package/dist/views/auth/index.mjs +0 -6
  137. package/dist/views/auth/login-form.mjs +0 -156
  138. package/dist/views/collection/auto-form-fields.mjs +0 -525
  139. package/dist/views/collection/collection-form.mjs +0 -91
  140. package/dist/views/collection/collection-list.mjs +0 -76
  141. package/dist/views/collection/form-field.mjs +0 -42
  142. package/dist/views/collection/index.mjs +0 -6
  143. package/dist/views/common/index.mjs +0 -4
  144. package/dist/views/common/locale-switcher.mjs +0 -39
  145. package/dist/views/common/version-history.mjs +0 -272
  146. package/dist/views/index.mjs +0 -9
  147. package/dist/views/layout/admin-layout.mjs +0 -40
  148. package/dist/views/layout/admin-router.mjs +0 -95
  149. package/dist/views/layout/admin-sidebar.mjs +0 -63
  150. package/dist/views/layout/index.mjs +0 -5
  151. package/src/__tests__/setup.ts +0 -44
  152. package/src/__tests__/test-utils.tsx +0 -49
  153. package/src/__tests__/vitest.d.ts +0 -9
  154. package/src/components/admin-app.tsx +0 -221
  155. package/src/components/fields/array-field.tsx +0 -237
  156. package/src/components/fields/checkbox-field.tsx +0 -47
  157. package/src/components/fields/custom-field.tsx +0 -50
  158. package/src/components/fields/date-field.tsx +0 -65
  159. package/src/components/fields/datetime-field.tsx +0 -67
  160. package/src/components/fields/email-field.tsx +0 -51
  161. package/src/components/fields/embedded-collection.tsx +0 -315
  162. package/src/components/fields/field-types.ts +0 -162
  163. package/src/components/fields/field-utils.ts +0 -6
  164. package/src/components/fields/field-wrapper.tsx +0 -52
  165. package/src/components/fields/index.ts +0 -66
  166. package/src/components/fields/json-field.tsx +0 -440
  167. package/src/components/fields/locale-badge.tsx +0 -15
  168. package/src/components/fields/number-field.tsx +0 -57
  169. package/src/components/fields/password-field.tsx +0 -51
  170. package/src/components/fields/relation-field.tsx +0 -243
  171. package/src/components/fields/relation-picker.tsx +0 -402
  172. package/src/components/fields/relation-select.tsx +0 -327
  173. package/src/components/fields/rich-text-editor/index.tsx +0 -1337
  174. package/src/components/fields/select-field.tsx +0 -61
  175. package/src/components/fields/switch-field.tsx +0 -47
  176. package/src/components/fields/text-field.tsx +0 -55
  177. package/src/components/fields/textarea-field.tsx +0 -55
  178. package/src/components/index.ts +0 -40
  179. package/src/components/primitives/checkbox-input.tsx +0 -193
  180. package/src/components/primitives/date-input.tsx +0 -401
  181. package/src/components/primitives/index.ts +0 -24
  182. package/src/components/primitives/number-input.tsx +0 -132
  183. package/src/components/primitives/select-input.tsx +0 -296
  184. package/src/components/primitives/tag-input.tsx +0 -200
  185. package/src/components/primitives/text-input.tsx +0 -49
  186. package/src/components/primitives/textarea-input.tsx +0 -46
  187. package/src/components/primitives/toggle-input.tsx +0 -36
  188. package/src/components/primitives/types.ts +0 -235
  189. package/src/components/ui/accordion.tsx +0 -72
  190. package/src/components/ui/avatar.tsx +0 -106
  191. package/src/components/ui/badge.tsx +0 -48
  192. package/src/components/ui/button.tsx +0 -53
  193. package/src/components/ui/card.tsx +0 -94
  194. package/src/components/ui/checkbox.tsx +0 -27
  195. package/src/components/ui/combobox.tsx +0 -290
  196. package/src/components/ui/dialog.tsx +0 -151
  197. package/src/components/ui/dropdown-menu.tsx +0 -254
  198. package/src/components/ui/field.tsx +0 -227
  199. package/src/components/ui/input-group.tsx +0 -149
  200. package/src/components/ui/input.tsx +0 -20
  201. package/src/components/ui/label.tsx +0 -18
  202. package/src/components/ui/popover.tsx +0 -88
  203. package/src/components/ui/scroll-area.tsx +0 -53
  204. package/src/components/ui/select.tsx +0 -192
  205. package/src/components/ui/separator.tsx +0 -23
  206. package/src/components/ui/sheet.tsx +0 -127
  207. package/src/components/ui/sidebar.tsx +0 -723
  208. package/src/components/ui/skeleton.tsx +0 -13
  209. package/src/components/ui/spinner.tsx +0 -10
  210. package/src/components/ui/switch.tsx +0 -32
  211. package/src/components/ui/table.tsx +0 -99
  212. package/src/components/ui/tabs.tsx +0 -82
  213. package/src/components/ui/textarea.tsx +0 -18
  214. package/src/components/ui/tooltip.tsx +0 -70
  215. package/src/config/component-registry.ts +0 -190
  216. package/src/config/index.ts +0 -1099
  217. package/src/hooks/README.md +0 -269
  218. package/src/hooks/admin-provider.tsx +0 -110
  219. package/src/hooks/index.ts +0 -41
  220. package/src/hooks/store.ts +0 -248
  221. package/src/hooks/use-auth.ts +0 -168
  222. package/src/hooks/use-collection-db.ts +0 -209
  223. package/src/hooks/use-collection.ts +0 -156
  224. package/src/hooks/use-global.ts +0 -69
  225. package/src/hooks/use-mobile.ts +0 -21
  226. package/src/lib/utils.ts +0 -6
  227. package/src/styles/index.css +0 -340
  228. package/src/utils/index.ts +0 -6
  229. package/src/views/auth/auth-layout.tsx +0 -77
  230. package/src/views/auth/forgot-password-form.tsx +0 -192
  231. package/src/views/auth/index.ts +0 -21
  232. package/src/views/auth/login-form.tsx +0 -229
  233. package/src/views/auth/reset-password-form.tsx +0 -232
  234. package/src/views/collection/auto-form-fields.tsx +0 -982
  235. package/src/views/collection/collection-form.tsx +0 -186
  236. package/src/views/collection/collection-list.tsx +0 -223
  237. package/src/views/collection/form-field.tsx +0 -52
  238. package/src/views/collection/index.ts +0 -15
  239. package/src/views/common/index.ts +0 -8
  240. package/src/views/common/locale-switcher.tsx +0 -45
  241. package/src/views/common/version-history.tsx +0 -406
  242. package/src/views/index.ts +0 -25
  243. package/src/views/layout/admin-layout.tsx +0 -117
  244. package/src/views/layout/admin-router.tsx +0 -206
  245. package/src/views/layout/admin-sidebar.tsx +0 -185
  246. package/src/views/layout/index.ts +0 -12
  247. package/tsconfig.json +0 -13
  248. package/tsconfig.tsbuildinfo +0 -1
  249. package/tsdown.config.ts +0 -13
  250. package/vitest.config.ts +0 -29
@@ -1,525 +0,0 @@
1
- import * as React$1 from "react";
2
- import { jsx, jsxs } from "react/jsx-runtime";
3
- import { useAdminContext } from "../../hooks/admin-provider";
4
- import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "../../components/ui/accordion";
5
- import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../components/ui/tabs";
6
- import { useFormContext } from "react-hook-form";
7
- import { RelationSelect } from "../../components/fields/relation-select";
8
- import { RelationPicker } from "../../components/fields/relation-picker";
9
- import { EmbeddedCollectionField } from "../../components/fields/embedded-collection";
10
- import { ArrayField } from "../../components/fields/array-field";
11
- import { RichTextEditor } from "../../components/fields/rich-text-editor";
12
- import { FormField } from "./form-field";
13
- import { cn } from "../../utils";
14
-
15
- //#region src/views/collection/auto-form-fields.tsx
16
- /**
17
- * AutoFormFields Component
18
- *
19
- * Automatically generates form fields from:
20
- * 1. CMS schema (Drizzle columns)
21
- * 2. Admin config (field overrides, layout, sections, tabs)
22
- *
23
- * Replaces manual field definitions!
24
- */
25
- /**
26
- * Get default fields for a collection
27
- * This would come from CMS schema introspection
28
- */
29
- function getDefaultFields(collection) {
30
- return {
31
- barbers: [
32
- "name",
33
- "email",
34
- "phone",
35
- "bio",
36
- "avatar",
37
- "isActive"
38
- ],
39
- services: [
40
- "name",
41
- "description",
42
- "duration",
43
- "price",
44
- "isActive"
45
- ],
46
- appointments: [
47
- "customerId",
48
- "barberId",
49
- "serviceId",
50
- "scheduledAt",
51
- "status",
52
- "notes"
53
- ],
54
- reviews: [
55
- "appointmentId",
56
- "rating",
57
- "comment"
58
- ]
59
- }[collection] || [];
60
- }
61
- function getFormValues(form, fieldPrefix) {
62
- if (!form?.watch) return {};
63
- return (fieldPrefix ? form.watch(fieldPrefix) : form.watch()) ?? {};
64
- }
65
- function getFullFieldName(fieldName, fieldPrefix) {
66
- return fieldPrefix ? `${fieldPrefix}.${fieldName}` : fieldName;
67
- }
68
- function resolveValue(value, formValues, defaultValue) {
69
- if (value === void 0) return defaultValue;
70
- if (typeof value === "function") return value(formValues);
71
- return value;
72
- }
73
- function resolveOptions(options, formValues) {
74
- if (!options) return void 0;
75
- if (typeof options === "function") return options(formValues);
76
- return options;
77
- }
78
- function resolveRelationTarget(relationConfig) {
79
- return relationConfig?.targetCollection;
80
- }
81
- function getFieldContext({ fieldName, fieldConfig, collection, form, fieldPrefix, locale }) {
82
- const formValues = getFormValues(form, fieldPrefix);
83
- const fullFieldName = getFullFieldName(fieldName, fieldPrefix);
84
- const fieldError = (form?.getFieldState ? form.getFieldState(fullFieldName) : void 0)?.error?.message;
85
- const fieldValue = formValues[fieldName];
86
- const label = fieldConfig?.label || fieldName;
87
- const description = fieldConfig?.description;
88
- const placeholder = fieldConfig?.placeholder;
89
- const isVisible = resolveValue(fieldConfig?.visible, formValues, true);
90
- const isReadOnly = resolveValue(fieldConfig?.readOnly, formValues, false);
91
- const isDisabled = resolveValue(fieldConfig?.disabled, formValues, false);
92
- const isRequired = resolveValue(fieldConfig?.required, formValues, false);
93
- const options = resolveOptions(fieldConfig?.options, formValues);
94
- const isLocalized = !!fieldConfig?.localized;
95
- const type = fieldConfig?.type;
96
- const updateValue = (nextValue) => {
97
- if (form?.setValue) form.setValue(fullFieldName, nextValue, {
98
- shouldDirty: true,
99
- shouldTouch: true
100
- });
101
- };
102
- return {
103
- fieldName,
104
- fullFieldName,
105
- collection,
106
- fieldConfig,
107
- fieldValue,
108
- label,
109
- description,
110
- placeholder,
111
- options,
112
- isVisible,
113
- isReadOnly,
114
- isDisabled,
115
- isRequired,
116
- isLocalized,
117
- locale,
118
- fieldError,
119
- updateValue,
120
- type
121
- };
122
- }
123
- function buildComponentProps(context) {
124
- return {
125
- key: context.fullFieldName,
126
- name: context.fullFieldName,
127
- value: context.fieldValue,
128
- onChange: context.updateValue,
129
- label: context.label,
130
- description: context.description,
131
- placeholder: context.placeholder,
132
- required: context.isRequired,
133
- disabled: context.isDisabled,
134
- readOnly: context.isReadOnly,
135
- error: context.fieldError,
136
- localized: context.isLocalized,
137
- locale: context.locale
138
- };
139
- }
140
- function buildRelationProps(context) {
141
- return {
142
- label: context.label,
143
- required: context.isRequired,
144
- disabled: context.isDisabled,
145
- readOnly: context.isReadOnly,
146
- placeholder: context.placeholder,
147
- error: context.fieldError,
148
- localized: context.isLocalized,
149
- locale: context.locale
150
- };
151
- }
152
- function buildFormFieldProps(context) {
153
- return {
154
- name: context.fullFieldName,
155
- label: context.label,
156
- description: context.description,
157
- placeholder: context.placeholder,
158
- required: context.isRequired,
159
- disabled: context.isDisabled || context.isReadOnly,
160
- localized: context.isLocalized,
161
- locale: context.locale
162
- };
163
- }
164
- function renderConfigError(message) {
165
- return /* @__PURE__ */ jsx("div", {
166
- className: "rounded-md border border-destructive/40 bg-destructive/5 p-3 text-sm text-destructive",
167
- children: message
168
- });
169
- }
170
- function renderFormField(formFieldProps, type, extra) {
171
- return /* @__PURE__ */ jsx(FormField, {
172
- ...formFieldProps,
173
- type,
174
- ...extra
175
- });
176
- }
177
- function renderCustomComponent({ context, registry, componentProps }) {
178
- const component = context.fieldConfig?.component;
179
- if (!component) return null;
180
- const CustomComponent = typeof component === "string" ? (registry?.fields)?.[component] || registry?.custom?.[component] : component;
181
- if (!CustomComponent) return null;
182
- return /* @__PURE__ */ jsx(CustomComponent, { ...componentProps });
183
- }
184
- function renderRelationField({ context, relationProps, formFieldProps }) {
185
- const relationConfig = context.fieldConfig?.relation;
186
- if (!(context.type === "relation" || !!relationConfig)) return null;
187
- if (!relationConfig) return renderConfigError(`Missing relation config for "${context.fieldName}". Define relation.targetCollection.`);
188
- const targetCollection = resolveRelationTarget(relationConfig);
189
- if (!targetCollection) return renderConfigError(`Missing relation.targetCollection for "${context.fieldName}".`);
190
- const baseRelationProps = {
191
- key: context.fullFieldName,
192
- name: context.fullFieldName,
193
- targetCollection,
194
- filter: relationConfig?.filter,
195
- renderFormFields: void 0,
196
- ...relationProps
197
- };
198
- if (relationConfig?.mode === "picker" || Array.isArray(context.fieldValue)) return /* @__PURE__ */ jsx(RelationPicker, {
199
- ...baseRelationProps,
200
- value: context.fieldValue || [],
201
- onChange: context.updateValue,
202
- orderable: relationConfig?.orderable
203
- });
204
- return /* @__PURE__ */ jsx(RelationSelect, {
205
- ...baseRelationProps,
206
- value: context.fieldValue,
207
- onChange: context.updateValue
208
- });
209
- }
210
- function renderEmbeddedField({ context, registry, allCollectionsConfig, componentProps }) {
211
- const embeddedConfig = context.fieldConfig?.embedded;
212
- if (!embeddedConfig) return null;
213
- const embeddedCollection = embeddedConfig.collection;
214
- const embeddedCollectionConfig = embeddedCollection ? allCollectionsConfig?.[embeddedCollection] : void 0;
215
- return /* @__PURE__ */ jsx(registry?.fields?.embedded || EmbeddedCollectionField, {
216
- ...componentProps,
217
- value: context.fieldValue || [],
218
- collection: embeddedCollection,
219
- mode: embeddedConfig?.mode,
220
- orderable: embeddedConfig?.orderable,
221
- rowLabel: embeddedConfig?.rowLabel,
222
- renderFields: (index) => embeddedCollection ? /* @__PURE__ */ jsx(AutoFormFields, {
223
- collection: embeddedCollection,
224
- config: embeddedCollectionConfig,
225
- registry,
226
- fieldPrefix: `${context.fullFieldName}.${index}`,
227
- allCollectionsConfig
228
- }) : null
229
- });
230
- }
231
- function renderArrayField({ context, registry, componentProps }) {
232
- const arrayConfig = context.fieldConfig?.array;
233
- if (!(context.type === "array" || !!arrayConfig)) return null;
234
- const ArrayComponent = registry?.fields?.array || ArrayField;
235
- const config = arrayConfig || {};
236
- return /* @__PURE__ */ jsx(ArrayComponent, {
237
- ...componentProps,
238
- value: context.fieldValue || [],
239
- placeholder: config.placeholder ?? context.placeholder,
240
- itemType: config.itemType,
241
- options: config.options || context.options,
242
- orderable: config.orderable,
243
- minItems: config.minItems,
244
- maxItems: config.maxItems
245
- });
246
- }
247
- function renderRichTextField({ context, registry, componentProps }) {
248
- if (context.type !== "richText") return null;
249
- return /* @__PURE__ */ jsx(registry?.fields?.richText || RichTextEditor, {
250
- ...componentProps,
251
- ...context.fieldConfig?.richText
252
- });
253
- }
254
- function renderPrimitiveField({ context, formFieldProps }) {
255
- if (!context.type) return renderConfigError(`Missing field type for "${context.fieldName}". Define it in admin config.`);
256
- if (context.type === "boolean") return renderFormField(formFieldProps, "switch");
257
- if (context.type === "select" && context.options) return renderFormField(formFieldProps, "select", { options: context.options });
258
- return renderFormField(formFieldProps, context.type);
259
- }
260
- /**
261
- * Render a single field with conditional logic
262
- */
263
- function FieldRenderer({ fieldName, fieldConfig, collection, registry, fieldPrefix, allCollectionsConfig }) {
264
- const form = useFormContext();
265
- const { locale } = useAdminContext();
266
- const context = getFieldContext({
267
- fieldName,
268
- fieldConfig,
269
- collection,
270
- form,
271
- fieldPrefix,
272
- locale
273
- });
274
- if (!context.isVisible) return null;
275
- const componentProps = buildComponentProps(context);
276
- const relationProps = buildRelationProps(context);
277
- const formFieldProps = buildFormFieldProps(context);
278
- const renderers = [
279
- () => renderCustomComponent({
280
- context,
281
- registry,
282
- componentProps
283
- }),
284
- () => renderRelationField({
285
- context,
286
- relationProps,
287
- formFieldProps
288
- }),
289
- () => renderEmbeddedField({
290
- context,
291
- registry,
292
- allCollectionsConfig,
293
- componentProps
294
- }),
295
- () => renderArrayField({
296
- context,
297
- registry,
298
- componentProps
299
- }),
300
- () => renderRichTextField({
301
- context,
302
- registry,
303
- componentProps
304
- }),
305
- () => renderPrimitiveField({
306
- context,
307
- formFieldProps
308
- })
309
- ];
310
- for (const render of renderers) {
311
- const node = render();
312
- if (node) return node;
313
- }
314
- return null;
315
- }
316
- const gridColumnClasses = {
317
- 1: "grid-cols-1",
318
- 2: "grid-cols-2",
319
- 3: "grid-cols-3",
320
- 4: "grid-cols-4",
321
- 5: "grid-cols-5",
322
- 6: "grid-cols-6",
323
- 7: "grid-cols-7",
324
- 8: "grid-cols-8",
325
- 9: "grid-cols-9",
326
- 10: "grid-cols-10",
327
- 11: "grid-cols-11",
328
- 12: "grid-cols-12"
329
- };
330
- function getGridColumnsClass(columns, prefix) {
331
- if (!columns) return "";
332
- const base = gridColumnClasses[columns];
333
- if (!base) return "";
334
- return prefix ? `${prefix}:${base}` : base;
335
- }
336
- function getGapStyle(gap) {
337
- if (gap === void 0) return void 0;
338
- return `${gap * .25}rem`;
339
- }
340
- function normalizeSectionFields(fields) {
341
- if (!fields?.length) return [];
342
- if (typeof fields[0] === "string") return fields.map((field) => ({ field }));
343
- return fields;
344
- }
345
- function resolveSpan(span, columns) {
346
- if (!span) return void 0;
347
- if (typeof span === "number") return Math.max(1, Math.min(columns, span));
348
- if (span === "full") return columns;
349
- const fraction = {
350
- "1/2": .5,
351
- "1/3": 1 / 3,
352
- "2/3": 2 / 3,
353
- "1/4": .25,
354
- "3/4": .75
355
- }[span];
356
- if (!fraction) return void 0;
357
- return Math.max(1, Math.round(columns * fraction));
358
- }
359
- /**
360
- * AutoFormFields Component
361
- */
362
- function AutoFormFields({ cms: _cms, collection, config, registry, renderField, fieldPrefix, allCollectionsConfig }) {
363
- const formValues = getFormValues(useFormContext(), fieldPrefix);
364
- const fieldOrder = config?.edit?.fields || getDefaultFields(collection);
365
- const excludedFields = new Set(config?.edit?.exclude || []);
366
- const sidebarFieldSet = new Set(config?.edit?.sidebar?.fields || []);
367
- const isFieldExcluded = React$1.useCallback((fieldName) => {
368
- if (excludedFields.has(fieldName)) return true;
369
- if ((config?.fields?.[fieldName])?.hidden) return true;
370
- return false;
371
- }, [config?.fields, excludedFields]);
372
- const renderFieldNode = React$1.useCallback((fieldName) => {
373
- if (isFieldExcluded(fieldName)) return null;
374
- const fieldConfig = config?.fields?.[fieldName];
375
- const fullFieldName = getFullFieldName(fieldName, fieldPrefix);
376
- if (renderField) return renderField(fullFieldName, fieldConfig);
377
- return /* @__PURE__ */ jsx(FieldRenderer, {
378
- fieldName,
379
- fieldConfig,
380
- collection,
381
- registry,
382
- fieldPrefix,
383
- allCollectionsConfig
384
- }, fullFieldName);
385
- }, [
386
- collection,
387
- config?.fields,
388
- fieldPrefix,
389
- isFieldExcluded,
390
- registry,
391
- renderField,
392
- allCollectionsConfig
393
- ]);
394
- const renderFieldLayout = React$1.useCallback((field, layout, columns) => {
395
- const fieldNode = renderFieldNode(field.field);
396
- if (!fieldNode) return null;
397
- const style = {};
398
- if (layout === "columns" || layout === "grid") {
399
- const span = resolveSpan(field.span, columns);
400
- if (span) style.gridColumn = `span ${span} / span ${span}`;
401
- if (field.rowSpan) style.gridRow = `span ${field.rowSpan} / span ${field.rowSpan}`;
402
- }
403
- if (field.width) style.width = field.width;
404
- return /* @__PURE__ */ jsx("div", {
405
- style,
406
- className: cn(layout === "inline" ? "min-w-[180px] flex-1" : "min-w-0"),
407
- children: fieldNode
408
- }, field.field);
409
- }, [renderFieldNode]);
410
- const renderFieldsContainer = React$1.useCallback((fields, layout, columns, grid) => {
411
- const normalizedFields = fields.filter((field) => !isFieldExcluded(field.field));
412
- if (!normalizedFields.length) return null;
413
- if (layout === "inline") return /* @__PURE__ */ jsx("div", {
414
- className: "flex flex-wrap gap-4",
415
- style: { gap: getGapStyle(grid?.gap) },
416
- children: normalizedFields.map((field) => renderFieldLayout(field, layout, columns))
417
- });
418
- if (layout === "columns" || layout === "grid") {
419
- const baseClass = getGridColumnsClass(columns);
420
- return /* @__PURE__ */ jsx("div", {
421
- className: cn("grid gap-4", baseClass, getGridColumnsClass(grid?.responsive?.sm, "sm"), getGridColumnsClass(grid?.responsive?.md, "md"), getGridColumnsClass(grid?.responsive?.lg, "lg")),
422
- style: {
423
- gap: getGapStyle(grid?.gap),
424
- ...baseClass ? {} : { gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` }
425
- },
426
- children: normalizedFields.map((field) => renderFieldLayout(field, layout, columns))
427
- });
428
- }
429
- return /* @__PURE__ */ jsx("div", {
430
- className: "space-y-4",
431
- children: normalizedFields.map((field) => renderFieldLayout(field, layout, columns))
432
- });
433
- }, [isFieldExcluded, renderFieldLayout]);
434
- const renderSection = React$1.useCallback((section, index, excludedSet) => {
435
- if (!resolveValue(section.visible, formValues, true)) return null;
436
- const sectionFields = normalizeSectionFields(section.fields).filter((field) => !excludedSet?.has(field.field));
437
- if (!sectionFields.length) return null;
438
- const layout = section.layout ?? "auto";
439
- const content = renderFieldsContainer(sectionFields, layout, layout === "columns" ? section.columns || section.grid?.columns || 2 : section.grid?.columns || section.columns || 1, section.grid);
440
- if (!content) return null;
441
- const header = section.title || section.description ? /* @__PURE__ */ jsxs("div", { children: [section.title && /* @__PURE__ */ jsx("h3", {
442
- className: "text-lg font-semibold",
443
- children: section.title
444
- }), section.description && /* @__PURE__ */ jsx("p", {
445
- className: "text-sm text-muted-foreground",
446
- children: section.description
447
- })] }) : null;
448
- if (section.collapsible) {
449
- const value = `section-${index}`;
450
- return /* @__PURE__ */ jsx(Accordion, {
451
- defaultValue: section.defaultOpen ? [value] : [],
452
- children: /* @__PURE__ */ jsxs(AccordionItem, {
453
- value,
454
- children: [/* @__PURE__ */ jsx(AccordionTrigger, { children: section.title || "Section" }), /* @__PURE__ */ jsxs(AccordionContent, {
455
- className: "space-y-3 pt-2",
456
- children: [section.description && /* @__PURE__ */ jsx("p", {
457
- className: "text-sm text-muted-foreground",
458
- children: section.description
459
- }), content]
460
- })]
461
- })
462
- }, value);
463
- }
464
- return /* @__PURE__ */ jsxs("div", {
465
- className: cn("space-y-4", section.className),
466
- children: [header, content]
467
- }, index);
468
- }, [formValues, renderFieldsContainer]);
469
- const renderSections = React$1.useCallback((sections, excludedSet) => {
470
- const renderedSections = sections.map((section, index) => renderSection(section, index, excludedSet)).filter(Boolean);
471
- if (!renderedSections.length) return null;
472
- return /* @__PURE__ */ jsx("div", {
473
- className: "space-y-6",
474
- children: renderedSections
475
- });
476
- }, [renderSection]);
477
- const renderTabs = React$1.useCallback((tabs, excludedSet) => {
478
- const visibleTabs = tabs.filter((tab) => resolveValue(tab.visible, formValues, true));
479
- if (!visibleTabs.length) return null;
480
- const defaultTab = visibleTabs[0]?.id;
481
- return /* @__PURE__ */ jsxs(Tabs, {
482
- defaultValue: defaultTab,
483
- children: [/* @__PURE__ */ jsx(TabsList, {
484
- variant: "line",
485
- children: visibleTabs.map((tab) => /* @__PURE__ */ jsx(TabsTrigger, {
486
- value: tab.id,
487
- children: tab.label
488
- }, tab.id))
489
- }), visibleTabs.map((tab) => /* @__PURE__ */ jsx(TabsContent, {
490
- value: tab.id,
491
- children: tab.sections ? renderSections(tab.sections, excludedSet) : renderFieldsContainer(normalizeSectionFields(tab.fields || []).filter((field) => !excludedSet?.has(field.field)), "auto", 1)
492
- }, tab.id))]
493
- });
494
- }, [
495
- formValues,
496
- renderFieldsContainer,
497
- renderSections
498
- ]);
499
- const visibleFields = fieldOrder.filter((fieldName) => !isFieldExcluded(fieldName));
500
- const defaultContent = config?.edit?.tabs ? renderTabs(config.edit.tabs, void 0) : config?.edit?.sections ? renderSections(config.edit.sections, void 0) : renderFieldsContainer(normalizeSectionFields(visibleFields), "auto", 1);
501
- if (config?.edit?.layout === "with-sidebar" && sidebarFieldSet.size > 0) {
502
- const sidebarContent = renderFieldsContainer(normalizeSectionFields(Array.from(sidebarFieldSet).filter((fieldName) => !isFieldExcluded(fieldName))), "auto", 1);
503
- const mainContent = config?.edit?.tabs ? renderTabs(config.edit.tabs, sidebarFieldSet) : config?.edit?.sections ? renderSections(config.edit.sections, sidebarFieldSet) : renderFieldsContainer(normalizeSectionFields(visibleFields.filter((fieldName) => !sidebarFieldSet.has(fieldName))), "auto", 1);
504
- const sidebarPosition = config?.edit?.sidebar?.position || "right";
505
- const sidebarWidth = config?.edit?.sidebar?.width || "280px";
506
- return /* @__PURE__ */ jsxs("div", {
507
- className: cn("flex flex-col gap-6 lg:flex-row", sidebarPosition === "left" ? "lg:flex-row-reverse" : ""),
508
- children: [/* @__PURE__ */ jsx("div", {
509
- className: "min-w-0 flex-1",
510
- children: mainContent
511
- }), sidebarContent && /* @__PURE__ */ jsx("aside", {
512
- className: "rounded-md border bg-card p-4",
513
- style: { width: sidebarWidth },
514
- children: /* @__PURE__ */ jsx("div", {
515
- className: "space-y-4",
516
- children: sidebarContent
517
- })
518
- })]
519
- });
520
- }
521
- return /* @__PURE__ */ jsx("div", { children: defaultContent });
522
- }
523
-
524
- //#endregion
525
- export { AutoFormFields };
@@ -1,91 +0,0 @@
1
- import React from "react";
2
- import { jsx, jsxs } from "react/jsx-runtime";
3
- import { Button } from "../../components/ui/button";
4
- import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "../../components/ui/card";
5
- import { useCollection } from "../../hooks/use-collection-db";
6
- import { FormProvider, useForm } from "react-hook-form";
7
-
8
- //#region src/views/collection/collection-form.tsx
9
- /**
10
- * CollectionForm - Form wrapper for CMS collections
11
- * Uses React Hook Form + TanStack DB Collection for optimistic updates
12
- *
13
- * @example
14
- * ```tsx
15
- * import { CollectionForm } from '@questpie/admin/views'
16
- * import type { cms } from './server/cms'
17
- *
18
- * function PostForm({ id }: { id?: string }) {
19
- * return (
20
- * <CollectionForm<typeof cms, 'posts'>
21
- * collection="posts"
22
- * id={id}
23
- * title={id ? 'Edit Post' : 'Create Post'}
24
- * onSuccess={() => router.push('/posts')}
25
- * >
26
- * <FormField name="title" label="Title" />
27
- * <FormField name="content" label="Content" type="textarea" />
28
- * </CollectionForm>
29
- * )
30
- * }
31
- * ```
32
- */
33
- function CollectionForm({ collection, id, defaultValues, children, onSuccess, onCancel, title, headerActions, submitText = "Save", cancelText = "Cancel" }) {
34
- const collectionData = useCollection(collection);
35
- const item = id ? collectionData.toArray.find((i) => collectionData.config.getKey(i) === id) : void 0;
36
- const form = useForm({ defaultValues: item ?? defaultValues ?? {} });
37
- React.useEffect(() => {
38
- form.reset(item ?? defaultValues ?? {});
39
- }, [
40
- form,
41
- item,
42
- defaultValues
43
- ]);
44
- const onSubmit = async (data) => {
45
- try {
46
- let result;
47
- if (id) {
48
- await collectionData.update(id, data);
49
- result = {
50
- ...item,
51
- ...data
52
- };
53
- } else result = await collectionData.insert(data);
54
- onSuccess?.(result);
55
- } catch (error) {
56
- console.error("Form submission error:", error);
57
- }
58
- };
59
- return /* @__PURE__ */ jsx(FormProvider, {
60
- ...form,
61
- children: /* @__PURE__ */ jsx("form", {
62
- onSubmit: form.handleSubmit(onSubmit),
63
- children: /* @__PURE__ */ jsxs(Card, { children: [
64
- (title || headerActions) && /* @__PURE__ */ jsxs(CardHeader, {
65
- className: "flex flex-wrap items-center justify-between gap-3",
66
- children: [title && /* @__PURE__ */ jsx(CardTitle, { children: title }), headerActions && /* @__PURE__ */ jsx("div", { children: headerActions })]
67
- }),
68
- /* @__PURE__ */ jsx(CardContent, {
69
- className: "space-y-4",
70
- children
71
- }),
72
- /* @__PURE__ */ jsxs(CardFooter, {
73
- className: "flex justify-end gap-2",
74
- children: [onCancel && /* @__PURE__ */ jsx(Button, {
75
- type: "button",
76
- variant: "outline",
77
- onClick: onCancel,
78
- children: cancelText
79
- }), /* @__PURE__ */ jsx(Button, {
80
- type: "submit",
81
- disabled: form.formState.isSubmitting,
82
- children: form.formState.isSubmitting ? "Saving..." : submitText
83
- })]
84
- })
85
- ] })
86
- })
87
- });
88
- }
89
-
90
- //#endregion
91
- export { CollectionForm };
@@ -1,76 +0,0 @@
1
- import React from "react";
2
- import { jsx, jsxs } from "react/jsx-runtime";
3
- import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../../components/ui/table";
4
- import { useCollection } from "../../hooks/use-collection-db";
5
- import { flexRender, getCoreRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
6
-
7
- //#region src/views/collection/collection-list.tsx
8
- /**
9
- * CollectionList - Table view for CMS collections
10
- * Uses TanStack DB Collection for offline-first, realtime data
11
- *
12
- * @example
13
- * ```tsx
14
- * import { CollectionList } from '@questpie/admin/views'
15
- * import type { cms } from './server/cms'
16
- *
17
- * function PostsList() {
18
- * return (
19
- * <CollectionList<typeof cms, 'posts'>
20
- * collection="posts"
21
- * columns={[
22
- * { accessorKey: 'title', header: 'Title' },
23
- * { accessorKey: 'createdAt', header: 'Created' },
24
- * ]}
25
- * realtime={true}
26
- * onRowClick={(post) => router.push(`/posts/${post.id}`)}
27
- * />
28
- * )
29
- * }
30
- * ```
31
- */
32
- function CollectionList({ collection, columns, baseFindOptions, realtime = false, rowActions, headerActions, emptyState, onRowClick }) {
33
- const collectionData = useCollection(collection, {
34
- baseFindOptions,
35
- realtime
36
- });
37
- const [sorting, setSorting] = React.useState([]);
38
- const items = collectionData.toArray;
39
- const table = useReactTable({
40
- data: items,
41
- columns,
42
- getCoreRowModel: getCoreRowModel(),
43
- getSortedRowModel: getSortedRowModel(),
44
- onSortingChange: setSorting,
45
- state: { sorting }
46
- });
47
- return /* @__PURE__ */ jsxs("div", {
48
- className: "space-y-4",
49
- children: [headerActions && /* @__PURE__ */ jsxs("div", {
50
- className: "flex items-center justify-between",
51
- children: [/* @__PURE__ */ jsxs("div", {
52
- className: "text-sm text-muted-foreground",
53
- children: [items.length, " items"]
54
- }), /* @__PURE__ */ jsx("div", { children: headerActions })]
55
- }), /* @__PURE__ */ jsx("div", {
56
- className: "rounded-md border",
57
- children: /* @__PURE__ */ jsxs(Table, { children: [/* @__PURE__ */ jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsxs(TableRow, { children: [headerGroup.headers.map((header) => /* @__PURE__ */ jsx(TableHead, { children: header.isPlaceholder ? null : /* @__PURE__ */ jsxs("div", {
58
- className: header.column.getCanSort() ? "cursor-pointer select-none flex items-center gap-2" : "",
59
- onClick: header.column.getToggleSortingHandler(),
60
- children: [flexRender(header.column.columnDef.header, header.getContext()), header.column.getIsSorted() && /* @__PURE__ */ jsx("span", { children: header.column.getIsSorted() === "asc" ? "↑" : "↓" })]
61
- }) }, header.id)), rowActions && /* @__PURE__ */ jsx(TableHead, { children: "Actions" })] }, headerGroup.id)) }), /* @__PURE__ */ jsx(TableBody, { children: table.getRowModel().rows?.length ? table.getRowModel().rows.map((row) => /* @__PURE__ */ jsxs(TableRow, {
62
- "data-state": row.getIsSelected() && "selected",
63
- onClick: () => onRowClick?.(row.original),
64
- className: onRowClick ? "cursor-pointer" : "",
65
- children: [row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(TableCell, { children: flexRender(cell.column.columnDef.cell, cell.getContext()) }, cell.id)), rowActions && /* @__PURE__ */ jsx(TableCell, { children: rowActions(row.original) })]
66
- }, row.id)) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, {
67
- colSpan: columns.length + (rowActions ? 1 : 0),
68
- className: "h-24 text-center",
69
- children: emptyState || "No results."
70
- }) }) })] })
71
- })]
72
- });
73
- }
74
-
75
- //#endregion
76
- export { CollectionList };