@btst/plugin-cms 0.0.0-stage → 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 (204) hide show
  1. package/LICENSE.md +17 -0
  2. package/dist/api/getters.cjs +328 -0
  3. package/dist/api/getters.mjs +314 -0
  4. package/dist/api/index.cjs +29 -0
  5. package/dist/api/index.d.cts +209 -0
  6. package/dist/api/index.d.mts +209 -0
  7. package/dist/api/index.d.ts +209 -0
  8. package/dist/api/index.mjs +7 -0
  9. package/dist/api/lifecycle-migrations.cjs +13 -0
  10. package/dist/api/lifecycle-migrations.mjs +11 -0
  11. package/dist/api/mutations.cjs +59 -0
  12. package/dist/api/mutations.mjs +57 -0
  13. package/dist/api/operations.cjs +1769 -0
  14. package/dist/api/operations.mjs +1762 -0
  15. package/dist/api/plugin.cjs +337 -0
  16. package/dist/api/plugin.mjs +331 -0
  17. package/dist/api/query-key-defs.cjs +44 -0
  18. package/dist/api/query-key-defs.mjs +39 -0
  19. package/dist/api/relations.cjs +76 -0
  20. package/dist/api/relations.mjs +70 -0
  21. package/dist/client/components/forms/content-form.cjs +286 -0
  22. package/dist/client/components/forms/content-form.mjs +284 -0
  23. package/dist/client/components/forms/file-upload.cjs +160 -0
  24. package/dist/client/components/forms/file-upload.mjs +158 -0
  25. package/dist/client/components/forms/relation-field.cjs +237 -0
  26. package/dist/client/components/forms/relation-field.mjs +235 -0
  27. package/dist/client/components/index.cjs +14 -0
  28. package/dist/client/components/index.d.cts +20 -0
  29. package/dist/client/components/index.d.mts +20 -0
  30. package/dist/client/components/index.d.ts +20 -0
  31. package/dist/client/components/index.mjs +10 -0
  32. package/dist/client/components/inverse-relations-panel.cjs +272 -0
  33. package/dist/client/components/inverse-relations-panel.mjs +270 -0
  34. package/dist/client/components/loading/dashboard-skeleton.cjs +18 -0
  35. package/dist/client/components/loading/dashboard-skeleton.mjs +16 -0
  36. package/dist/client/components/loading/editor-skeleton.cjs +32 -0
  37. package/dist/client/components/loading/editor-skeleton.mjs +30 -0
  38. package/dist/client/components/loading/list-skeleton.cjs +32 -0
  39. package/dist/client/components/loading/list-skeleton.mjs +30 -0
  40. package/dist/client/components/pages/404-page.cjs +26 -0
  41. package/dist/client/components/pages/404-page.mjs +24 -0
  42. package/dist/client/components/pages/content-editor-page.cjs +84 -0
  43. package/dist/client/components/pages/content-editor-page.internal.cjs +170 -0
  44. package/dist/client/components/pages/content-editor-page.internal.mjs +168 -0
  45. package/dist/client/components/pages/content-editor-page.mjs +82 -0
  46. package/dist/client/components/pages/content-list-page.cjs +49 -0
  47. package/dist/client/components/pages/content-list-page.internal.cjs +334 -0
  48. package/dist/client/components/pages/content-list-page.internal.mjs +332 -0
  49. package/dist/client/components/pages/content-list-page.mjs +47 -0
  50. package/dist/client/components/pages/dashboard-page.cjs +42 -0
  51. package/dist/client/components/pages/dashboard-page.internal.cjs +102 -0
  52. package/dist/client/components/pages/dashboard-page.internal.mjs +100 -0
  53. package/dist/client/components/pages/dashboard-page.mjs +40 -0
  54. package/dist/client/components/shared/default-error.cjs +17 -0
  55. package/dist/client/components/shared/default-error.mjs +15 -0
  56. package/dist/client/components/shared/empty-state.cjs +16 -0
  57. package/dist/client/components/shared/empty-state.mjs +14 -0
  58. package/dist/client/components/shared/page-wrapper.cjs +29 -0
  59. package/dist/client/components/shared/page-wrapper.mjs +27 -0
  60. package/dist/client/constants.cjs +5 -0
  61. package/dist/client/constants.mjs +3 -0
  62. package/dist/client/hooks/cms-hooks.cjs +300 -0
  63. package/dist/client/hooks/cms-hooks.mjs +280 -0
  64. package/dist/client/hooks/cms-resource.cjs +13 -0
  65. package/dist/client/hooks/cms-resource.mjs +11 -0
  66. package/dist/client/hooks/index.cjs +25 -0
  67. package/dist/client/hooks/index.d.cts +693 -0
  68. package/dist/client/hooks/index.d.mts +693 -0
  69. package/dist/client/hooks/index.d.ts +693 -0
  70. package/dist/client/hooks/index.mjs +1 -0
  71. package/dist/client/index.cjs +15 -0
  72. package/dist/client/index.d.cts +654 -0
  73. package/dist/client/index.d.mts +654 -0
  74. package/dist/client/index.d.ts +654 -0
  75. package/dist/client/index.mjs +3 -0
  76. package/dist/client/overrides.cjs +26 -0
  77. package/dist/client/overrides.mjs +24 -0
  78. package/dist/client/plugin.cjs +316 -0
  79. package/dist/client/plugin.mjs +314 -0
  80. package/dist/client.css +3 -0
  81. package/dist/db.cjs +118 -0
  82. package/dist/db.mjs +116 -0
  83. package/dist/permissions.cjs +46 -0
  84. package/dist/permissions.d.cts +42 -0
  85. package/dist/permissions.d.mts +42 -0
  86. package/dist/permissions.d.ts +42 -0
  87. package/dist/permissions.mjs +44 -0
  88. package/dist/query-keys.cjs +229 -0
  89. package/dist/query-keys.d.cts +8 -0
  90. package/dist/query-keys.d.mts +8 -0
  91. package/dist/query-keys.d.ts +8 -0
  92. package/dist/query-keys.mjs +225 -0
  93. package/dist/schemas.cjs +81 -0
  94. package/dist/schemas.mjs +72 -0
  95. package/dist/shared/plugin-cms.BBshPRkb.d.cts +6 -0
  96. package/dist/shared/plugin-cms.BBshPRkb.d.mts +6 -0
  97. package/dist/shared/plugin-cms.BBshPRkb.d.ts +6 -0
  98. package/dist/shared/plugin-cms.DQLAoogy.d.ts +1749 -0
  99. package/dist/shared/plugin-cms.W4Le8D91.d.mts +1749 -0
  100. package/dist/shared/plugin-cms.c-GeWvTf.d.cts +1749 -0
  101. package/dist/style.css +6 -0
  102. package/dist/types.cjs +2 -0
  103. package/dist/types.d.cts +406 -0
  104. package/dist/types.d.mts +406 -0
  105. package/dist/types.d.ts +406 -0
  106. package/dist/types.mjs +1 -0
  107. package/dist/ui/src/components/accordion.cjs +81 -0
  108. package/dist/ui/src/components/accordion.mjs +62 -0
  109. package/dist/ui/src/components/alert-dialog.cjs +157 -0
  110. package/dist/ui/src/components/alert-dialog.mjs +132 -0
  111. package/dist/ui/src/components/auto-form/common/label.cjs +18 -0
  112. package/dist/ui/src/components/auto-form/common/label.mjs +16 -0
  113. package/dist/ui/src/components/auto-form/common/tooltip.cjs +9 -0
  114. package/dist/ui/src/components/auto-form/common/tooltip.mjs +7 -0
  115. package/dist/ui/src/components/auto-form/config.cjs +40 -0
  116. package/dist/ui/src/components/auto-form/config.mjs +37 -0
  117. package/dist/ui/src/components/auto-form/dependencies.cjs +48 -0
  118. package/dist/ui/src/components/auto-form/dependencies.mjs +46 -0
  119. package/dist/ui/src/components/auto-form/fields/array.cjs +266 -0
  120. package/dist/ui/src/components/auto-form/fields/array.mjs +264 -0
  121. package/dist/ui/src/components/auto-form/fields/checkbox.cjs +38 -0
  122. package/dist/ui/src/components/auto-form/fields/checkbox.mjs +36 -0
  123. package/dist/ui/src/components/auto-form/fields/date.cjs +67 -0
  124. package/dist/ui/src/components/auto-form/fields/date.mjs +65 -0
  125. package/dist/ui/src/components/auto-form/fields/enum.cjs +73 -0
  126. package/dist/ui/src/components/auto-form/fields/enum.mjs +71 -0
  127. package/dist/ui/src/components/auto-form/fields/input.cjs +32 -0
  128. package/dist/ui/src/components/auto-form/fields/input.mjs +30 -0
  129. package/dist/ui/src/components/auto-form/fields/number.cjs +31 -0
  130. package/dist/ui/src/components/auto-form/fields/number.mjs +29 -0
  131. package/dist/ui/src/components/auto-form/fields/object.cjs +233 -0
  132. package/dist/ui/src/components/auto-form/fields/object.mjs +217 -0
  133. package/dist/ui/src/components/auto-form/fields/radio-group.cjs +67 -0
  134. package/dist/ui/src/components/auto-form/fields/radio-group.mjs +65 -0
  135. package/dist/ui/src/components/auto-form/fields/switch.cjs +38 -0
  136. package/dist/ui/src/components/auto-form/fields/switch.mjs +36 -0
  137. package/dist/ui/src/components/auto-form/fields/textarea.cjs +31 -0
  138. package/dist/ui/src/components/auto-form/fields/textarea.mjs +29 -0
  139. package/dist/ui/src/components/auto-form/helpers.cjs +270 -0
  140. package/dist/ui/src/components/auto-form/helpers.mjs +259 -0
  141. package/dist/ui/src/components/auto-form/index.cjs +92 -0
  142. package/dist/ui/src/components/auto-form/index.mjs +86 -0
  143. package/dist/ui/src/components/auto-form/stepped-auto-form.cjs +374 -0
  144. package/dist/ui/src/components/auto-form/stepped-auto-form.mjs +368 -0
  145. package/dist/ui/src/components/auto-form/types.cjs +11 -0
  146. package/dist/ui/src/components/auto-form/types.mjs +9 -0
  147. package/dist/ui/src/components/badge.cjs +42 -0
  148. package/dist/ui/src/components/badge.mjs +39 -0
  149. package/dist/ui/src/components/button.cjs +56 -0
  150. package/dist/ui/src/components/button.mjs +53 -0
  151. package/dist/ui/src/components/calendar.cjs +207 -0
  152. package/dist/ui/src/components/calendar.mjs +190 -0
  153. package/dist/ui/src/components/card.cjs +56 -0
  154. package/dist/ui/src/components/card.mjs +51 -0
  155. package/dist/ui/src/components/checkbox.cjs +48 -0
  156. package/dist/ui/src/components/checkbox.mjs +32 -0
  157. package/dist/ui/src/components/command.cjs +78 -0
  158. package/dist/ui/src/components/command.mjs +73 -0
  159. package/dist/ui/src/components/date-picker.cjs +41 -0
  160. package/dist/ui/src/components/date-picker.mjs +39 -0
  161. package/dist/ui/src/components/dialog.cjs +119 -0
  162. package/dist/ui/src/components/dialog.mjs +97 -0
  163. package/dist/ui/src/components/form.cjs +120 -0
  164. package/dist/ui/src/components/form.mjs +98 -0
  165. package/dist/ui/src/components/input.cjs +23 -0
  166. package/dist/ui/src/components/input.mjs +21 -0
  167. package/dist/ui/src/components/label.cjs +39 -0
  168. package/dist/ui/src/components/label.mjs +23 -0
  169. package/dist/ui/src/components/multi-select.cjs +555 -0
  170. package/dist/ui/src/components/multi-select.mjs +540 -0
  171. package/dist/ui/src/components/page-layout.cjs +25 -0
  172. package/dist/ui/src/components/page-layout.mjs +23 -0
  173. package/dist/ui/src/components/page-wrapper.cjs +20 -0
  174. package/dist/ui/src/components/page-wrapper.mjs +18 -0
  175. package/dist/ui/src/components/popover.cjs +60 -0
  176. package/dist/ui/src/components/popover.mjs +42 -0
  177. package/dist/ui/src/components/radio-group.cjs +62 -0
  178. package/dist/ui/src/components/radio-group.mjs +45 -0
  179. package/dist/ui/src/components/select.cjs +161 -0
  180. package/dist/ui/src/components/select.mjs +139 -0
  181. package/dist/ui/src/components/separator.cjs +43 -0
  182. package/dist/ui/src/components/separator.mjs +27 -0
  183. package/dist/ui/src/components/skeleton.cjs +17 -0
  184. package/dist/ui/src/components/skeleton.mjs +15 -0
  185. package/dist/ui/src/components/stack-attribution.cjs +24 -0
  186. package/dist/ui/src/components/stack-attribution.mjs +22 -0
  187. package/dist/ui/src/components/switch.cjs +48 -0
  188. package/dist/ui/src/components/switch.mjs +32 -0
  189. package/dist/ui/src/components/table.cjs +89 -0
  190. package/dist/ui/src/components/table.mjs +82 -0
  191. package/dist/ui/src/components/textarea.cjs +20 -0
  192. package/dist/ui/src/components/textarea.mjs +18 -0
  193. package/dist/ui/src/hooks/use-debounce.cjs +16 -0
  194. package/dist/ui/src/hooks/use-debounce.mjs +14 -0
  195. package/dist/ui/src/hooks/use-route-lifecycle.cjs +31 -0
  196. package/dist/ui/src/hooks/use-route-lifecycle.mjs +29 -0
  197. package/dist/ui/src/lib/schema-converter.cjs +133 -0
  198. package/dist/ui/src/lib/schema-converter.mjs +127 -0
  199. package/dist/ui/src/lib/utils.cjs +10 -0
  200. package/dist/ui/src/lib/utils.mjs +8 -0
  201. package/dist/utils.cjs +19 -0
  202. package/dist/utils.mjs +12 -0
  203. package/package.json +170 -3
  204. package/README.md +0 -3
@@ -0,0 +1,237 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ const jsxRuntime = require('react/jsx-runtime');
5
+ const React = require('react');
6
+ const context = require('@btst/stack/context');
7
+ const constants = require('../../constants.cjs');
8
+ const multiSelect = require('../../../ui/src/components/multi-select.cjs');
9
+ const button = require('../../../ui/src/components/button.cjs');
10
+ const lucideReact = require('lucide-react');
11
+ const dialog = require('../../../ui/src/components/dialog.cjs');
12
+ const input = require('../../../ui/src/components/input.cjs');
13
+ const label = require('../../../ui/src/components/label.cjs');
14
+ const textarea = require('../../../ui/src/components/textarea.cjs');
15
+ const cmsHooks = require('../../hooks/cms-hooks.cjs');
16
+
17
+ function RelationField({
18
+ field,
19
+ fieldConfigItem,
20
+ label: label$1,
21
+ isRequired,
22
+ relation
23
+ }) {
24
+ const t = context.useTranslate();
25
+ const [isCreateDialogOpen, setIsCreateDialogOpen] = React.useState(false);
26
+ const [newItemName, setNewItemName] = React.useState("");
27
+ const [newItemDescription, setNewItemDescription] = React.useState("");
28
+ const [createError, setCreateError] = React.useState(null);
29
+ const { localization } = context.usePluginOverrides(constants.CMS_PLUGIN_ID);
30
+ const isSingleSelect = relation.type === "belongsTo";
31
+ const normalizedValue = React.useMemo(() => {
32
+ if (!field.value) return [];
33
+ if (isSingleSelect) {
34
+ const singleValue = field.value;
35
+ if (singleValue && singleValue.id) {
36
+ return [{ id: singleValue.id }];
37
+ }
38
+ return [];
39
+ }
40
+ return field.value || [];
41
+ }, [field.value, isSingleSelect]);
42
+ const { displayField } = relation;
43
+ const getOptionLabel = React.useCallback(
44
+ (item) => String(
45
+ item.parsedData?.[displayField] || item.slug
46
+ ),
47
+ [displayField]
48
+ );
49
+ const select = cmsHooks.useContentOptions({
50
+ targetType: relation.targetType,
51
+ value: normalizedValue.map((v) => v.id),
52
+ getOptionLabel
53
+ });
54
+ const options = select.options.map((option) => ({
55
+ value: option.value,
56
+ label: option.label
57
+ }));
58
+ const selectedOptions = select.selectedOptions.map((option) => ({
59
+ value: option.value,
60
+ label: option.label
61
+ }));
62
+ const createMutation = cmsHooks.useCreateContent(relation.targetType);
63
+ const handleChange = React.useCallback(
64
+ (newOptions) => {
65
+ if (isSingleSelect) {
66
+ if (newOptions.length > 0) {
67
+ field.onChange({ id: newOptions[0].value });
68
+ } else {
69
+ field.onChange(void 0);
70
+ }
71
+ } else {
72
+ const newValue = newOptions.map((opt) => ({ id: opt.value }));
73
+ field.onChange(newValue);
74
+ }
75
+ },
76
+ [field, isSingleSelect]
77
+ );
78
+ const handleCreateItem = async () => {
79
+ if (!newItemName.trim()) return;
80
+ setCreateError(null);
81
+ try {
82
+ const result = await createMutation.mutateAsync({
83
+ slug: newItemName.toLowerCase().replace(/\s+/g, "-"),
84
+ data: {
85
+ [relation.displayField]: newItemName,
86
+ description: newItemDescription || void 0
87
+ }
88
+ });
89
+ if (isSingleSelect) {
90
+ field.onChange({ id: result.id });
91
+ } else {
92
+ const newValue = [...normalizedValue, { id: result.id }];
93
+ field.onChange(newValue);
94
+ }
95
+ setNewItemName("");
96
+ setNewItemDescription("");
97
+ setIsCreateDialogOpen(false);
98
+ } catch (error) {
99
+ const message = error instanceof Error ? error.message : localization?.CMS_RELATION_CREATE_ERROR ?? t(
100
+ "cms.relations.createError",
101
+ "Failed to create item. Please try again."
102
+ );
103
+ setCreateError(message);
104
+ }
105
+ };
106
+ const handleRemove = React.useCallback(
107
+ (idToRemove) => {
108
+ if (isSingleSelect) {
109
+ field.onChange(void 0);
110
+ } else {
111
+ const newValue = normalizedValue.filter((v) => v.id !== idToRemove);
112
+ field.onChange(newValue);
113
+ }
114
+ },
115
+ [normalizedValue, field, isSingleSelect]
116
+ );
117
+ const displayFieldLabel = relation.displayField.charAt(0).toUpperCase() + relation.displayField.slice(1);
118
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
119
+ /* @__PURE__ */ jsxRuntime.jsxs(label.Label, { children: [
120
+ label$1,
121
+ isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-destructive ml-1", children: "*" })
122
+ ] }),
123
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
124
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
125
+ multiSelect,
126
+ {
127
+ value: selectedOptions,
128
+ onChange: handleChange,
129
+ options,
130
+ placeholder: select.isLoading ? localization?.CMS_RELATION_LOADING ?? t("cms.relations.loading", "Loading...") : (isSingleSelect ? localization?.CMS_RELATION_SELECT_PLACEHOLDER ?? t(
131
+ "cms.relations.selectPlaceholder",
132
+ "Select {targetType}..."
133
+ ) : localization?.CMS_RELATION_SELECT_PLACEHOLDER_MULTI ?? t(
134
+ "cms.relations.selectPlaceholderMulti",
135
+ "Select {targetType}(s)..."
136
+ )).replace("{targetType}", relation.targetType),
137
+ disabled: select.isLoading,
138
+ hidePlaceholderWhenSelected: true,
139
+ commandProps: { shouldFilter: false },
140
+ inputProps: { onValueChange: select.setSearch },
141
+ emptyIndicator: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-center text-sm text-muted-foreground py-4", children: (localization?.CMS_RELATION_EMPTY ?? t("cms.relations.empty", "No {targetType} items found")).replace("{targetType}", relation.targetType) }),
142
+ maxSelected: isSingleSelect ? 1 : void 0,
143
+ className: "min-h-10"
144
+ }
145
+ ) }),
146
+ relation.creatable && /* @__PURE__ */ jsxRuntime.jsxs(
147
+ dialog.Dialog,
148
+ {
149
+ open: isCreateDialogOpen,
150
+ onOpenChange: setIsCreateDialogOpen,
151
+ children: [
152
+ /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(button.Button, { type: "button", variant: "outline", size: "icon", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-4 w-4" }) }) }),
153
+ /* @__PURE__ */ jsxRuntime.jsxs(dialog.DialogContent, { children: [
154
+ /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(dialog.DialogTitle, { children: (localization?.CMS_RELATION_CREATE_TITLE ?? t("cms.relations.createTitle", "Create New {targetType}")).replace("{targetType}", relation.targetType) }) }),
155
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4 py-4", children: [
156
+ createError && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: createError }),
157
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
158
+ /* @__PURE__ */ jsxRuntime.jsx(label.Label, { htmlFor: "newItemName", children: displayFieldLabel }),
159
+ /* @__PURE__ */ jsxRuntime.jsx(
160
+ input.Input,
161
+ {
162
+ id: "newItemName",
163
+ value: newItemName,
164
+ onChange: (e) => setNewItemName(e.target.value),
165
+ placeholder: (localization?.CMS_RELATION_NAME_PLACEHOLDER ?? t("cms.relations.namePlaceholder", "Enter {field}...")).replace("{field}", relation.displayField)
166
+ }
167
+ )
168
+ ] }),
169
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
170
+ /* @__PURE__ */ jsxRuntime.jsx(label.Label, { htmlFor: "newItemDescription", children: localization?.CMS_RELATION_DESCRIPTION_LABEL ?? t(
171
+ "cms.relations.descriptionLabel",
172
+ "Description (optional)"
173
+ ) }),
174
+ /* @__PURE__ */ jsxRuntime.jsx(
175
+ textarea.Textarea,
176
+ {
177
+ id: "newItemDescription",
178
+ value: newItemDescription,
179
+ onChange: (e) => setNewItemDescription(e.target.value),
180
+ placeholder: localization?.CMS_RELATION_DESCRIPTION_PLACEHOLDER ?? t(
181
+ "cms.relations.descriptionPlaceholder",
182
+ "Enter description..."
183
+ ),
184
+ rows: 3
185
+ }
186
+ )
187
+ ] }),
188
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
189
+ /* @__PURE__ */ jsxRuntime.jsx(
190
+ button.Button,
191
+ {
192
+ type: "button",
193
+ variant: "outline",
194
+ onClick: () => setIsCreateDialogOpen(false),
195
+ children: localization?.CMS_BUTTON_CANCEL ?? t("cms.common.cancel", "Cancel")
196
+ }
197
+ ),
198
+ /* @__PURE__ */ jsxRuntime.jsx(
199
+ button.Button,
200
+ {
201
+ type: "button",
202
+ onClick: handleCreateItem,
203
+ disabled: !newItemName.trim() || createMutation.isPending,
204
+ children: createMutation.isPending ? localization?.CMS_RELATION_CREATING ?? t("cms.relations.creating", "Creating...") : localization?.CMS_RELATION_CREATE_BUTTON ?? t("cms.relations.createButton", "Create")
205
+ }
206
+ )
207
+ ] })
208
+ ] })
209
+ ] })
210
+ ]
211
+ }
212
+ )
213
+ ] }),
214
+ selectedOptions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1 mt-2", children: selectedOptions.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
215
+ "div",
216
+ {
217
+ className: "inline-flex items-center gap-1 px-2 py-1 text-xs rounded-md bg-secondary text-secondary-foreground",
218
+ children: [
219
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: opt.label }),
220
+ /* @__PURE__ */ jsxRuntime.jsx(
221
+ "button",
222
+ {
223
+ type: "button",
224
+ onClick: () => handleRemove(opt.value),
225
+ className: "hover:text-destructive",
226
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3 w-3" })
227
+ }
228
+ )
229
+ ]
230
+ },
231
+ opt.value
232
+ )) }),
233
+ fieldConfigItem?.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: fieldConfigItem.description })
234
+ ] });
235
+ }
236
+
237
+ exports.RelationField = RelationField;
@@ -0,0 +1,235 @@
1
+ 'use client';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { useState, useMemo, useCallback } from 'react';
4
+ import { useTranslate, usePluginOverrides } from '@btst/stack/context';
5
+ import { CMS_PLUGIN_ID } from '../../constants.mjs';
6
+ import MultipleSelector from '../../../ui/src/components/multi-select.mjs';
7
+ import { Button } from '../../../ui/src/components/button.mjs';
8
+ import { Plus, X } from 'lucide-react';
9
+ import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle } from '../../../ui/src/components/dialog.mjs';
10
+ import { Input } from '../../../ui/src/components/input.mjs';
11
+ import { Label } from '../../../ui/src/components/label.mjs';
12
+ import { Textarea } from '../../../ui/src/components/textarea.mjs';
13
+ import { useContentOptions, useCreateContent } from '../../hooks/cms-hooks.mjs';
14
+
15
+ function RelationField({
16
+ field,
17
+ fieldConfigItem,
18
+ label,
19
+ isRequired,
20
+ relation
21
+ }) {
22
+ const t = useTranslate();
23
+ const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);
24
+ const [newItemName, setNewItemName] = useState("");
25
+ const [newItemDescription, setNewItemDescription] = useState("");
26
+ const [createError, setCreateError] = useState(null);
27
+ const { localization } = usePluginOverrides(CMS_PLUGIN_ID);
28
+ const isSingleSelect = relation.type === "belongsTo";
29
+ const normalizedValue = useMemo(() => {
30
+ if (!field.value) return [];
31
+ if (isSingleSelect) {
32
+ const singleValue = field.value;
33
+ if (singleValue && singleValue.id) {
34
+ return [{ id: singleValue.id }];
35
+ }
36
+ return [];
37
+ }
38
+ return field.value || [];
39
+ }, [field.value, isSingleSelect]);
40
+ const { displayField } = relation;
41
+ const getOptionLabel = useCallback(
42
+ (item) => String(
43
+ item.parsedData?.[displayField] || item.slug
44
+ ),
45
+ [displayField]
46
+ );
47
+ const select = useContentOptions({
48
+ targetType: relation.targetType,
49
+ value: normalizedValue.map((v) => v.id),
50
+ getOptionLabel
51
+ });
52
+ const options = select.options.map((option) => ({
53
+ value: option.value,
54
+ label: option.label
55
+ }));
56
+ const selectedOptions = select.selectedOptions.map((option) => ({
57
+ value: option.value,
58
+ label: option.label
59
+ }));
60
+ const createMutation = useCreateContent(relation.targetType);
61
+ const handleChange = useCallback(
62
+ (newOptions) => {
63
+ if (isSingleSelect) {
64
+ if (newOptions.length > 0) {
65
+ field.onChange({ id: newOptions[0].value });
66
+ } else {
67
+ field.onChange(void 0);
68
+ }
69
+ } else {
70
+ const newValue = newOptions.map((opt) => ({ id: opt.value }));
71
+ field.onChange(newValue);
72
+ }
73
+ },
74
+ [field, isSingleSelect]
75
+ );
76
+ const handleCreateItem = async () => {
77
+ if (!newItemName.trim()) return;
78
+ setCreateError(null);
79
+ try {
80
+ const result = await createMutation.mutateAsync({
81
+ slug: newItemName.toLowerCase().replace(/\s+/g, "-"),
82
+ data: {
83
+ [relation.displayField]: newItemName,
84
+ description: newItemDescription || void 0
85
+ }
86
+ });
87
+ if (isSingleSelect) {
88
+ field.onChange({ id: result.id });
89
+ } else {
90
+ const newValue = [...normalizedValue, { id: result.id }];
91
+ field.onChange(newValue);
92
+ }
93
+ setNewItemName("");
94
+ setNewItemDescription("");
95
+ setIsCreateDialogOpen(false);
96
+ } catch (error) {
97
+ const message = error instanceof Error ? error.message : localization?.CMS_RELATION_CREATE_ERROR ?? t(
98
+ "cms.relations.createError",
99
+ "Failed to create item. Please try again."
100
+ );
101
+ setCreateError(message);
102
+ }
103
+ };
104
+ const handleRemove = useCallback(
105
+ (idToRemove) => {
106
+ if (isSingleSelect) {
107
+ field.onChange(void 0);
108
+ } else {
109
+ const newValue = normalizedValue.filter((v) => v.id !== idToRemove);
110
+ field.onChange(newValue);
111
+ }
112
+ },
113
+ [normalizedValue, field, isSingleSelect]
114
+ );
115
+ const displayFieldLabel = relation.displayField.charAt(0).toUpperCase() + relation.displayField.slice(1);
116
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
117
+ /* @__PURE__ */ jsxs(Label, { children: [
118
+ label,
119
+ isRequired && /* @__PURE__ */ jsx("span", { className: "text-destructive ml-1", children: "*" })
120
+ ] }),
121
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
122
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(
123
+ MultipleSelector,
124
+ {
125
+ value: selectedOptions,
126
+ onChange: handleChange,
127
+ options,
128
+ placeholder: select.isLoading ? localization?.CMS_RELATION_LOADING ?? t("cms.relations.loading", "Loading...") : (isSingleSelect ? localization?.CMS_RELATION_SELECT_PLACEHOLDER ?? t(
129
+ "cms.relations.selectPlaceholder",
130
+ "Select {targetType}..."
131
+ ) : localization?.CMS_RELATION_SELECT_PLACEHOLDER_MULTI ?? t(
132
+ "cms.relations.selectPlaceholderMulti",
133
+ "Select {targetType}(s)..."
134
+ )).replace("{targetType}", relation.targetType),
135
+ disabled: select.isLoading,
136
+ hidePlaceholderWhenSelected: true,
137
+ commandProps: { shouldFilter: false },
138
+ inputProps: { onValueChange: select.setSearch },
139
+ emptyIndicator: /* @__PURE__ */ jsx("p", { className: "text-center text-sm text-muted-foreground py-4", children: (localization?.CMS_RELATION_EMPTY ?? t("cms.relations.empty", "No {targetType} items found")).replace("{targetType}", relation.targetType) }),
140
+ maxSelected: isSingleSelect ? 1 : void 0,
141
+ className: "min-h-10"
142
+ }
143
+ ) }),
144
+ relation.creatable && /* @__PURE__ */ jsxs(
145
+ Dialog,
146
+ {
147
+ open: isCreateDialogOpen,
148
+ onOpenChange: setIsCreateDialogOpen,
149
+ children: [
150
+ /* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "icon", children: /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }) }) }),
151
+ /* @__PURE__ */ jsxs(DialogContent, { children: [
152
+ /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: (localization?.CMS_RELATION_CREATE_TITLE ?? t("cms.relations.createTitle", "Create New {targetType}")).replace("{targetType}", relation.targetType) }) }),
153
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-4", children: [
154
+ createError && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: createError }),
155
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
156
+ /* @__PURE__ */ jsx(Label, { htmlFor: "newItemName", children: displayFieldLabel }),
157
+ /* @__PURE__ */ jsx(
158
+ Input,
159
+ {
160
+ id: "newItemName",
161
+ value: newItemName,
162
+ onChange: (e) => setNewItemName(e.target.value),
163
+ placeholder: (localization?.CMS_RELATION_NAME_PLACEHOLDER ?? t("cms.relations.namePlaceholder", "Enter {field}...")).replace("{field}", relation.displayField)
164
+ }
165
+ )
166
+ ] }),
167
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
168
+ /* @__PURE__ */ jsx(Label, { htmlFor: "newItemDescription", children: localization?.CMS_RELATION_DESCRIPTION_LABEL ?? t(
169
+ "cms.relations.descriptionLabel",
170
+ "Description (optional)"
171
+ ) }),
172
+ /* @__PURE__ */ jsx(
173
+ Textarea,
174
+ {
175
+ id: "newItemDescription",
176
+ value: newItemDescription,
177
+ onChange: (e) => setNewItemDescription(e.target.value),
178
+ placeholder: localization?.CMS_RELATION_DESCRIPTION_PLACEHOLDER ?? t(
179
+ "cms.relations.descriptionPlaceholder",
180
+ "Enter description..."
181
+ ),
182
+ rows: 3
183
+ }
184
+ )
185
+ ] }),
186
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
187
+ /* @__PURE__ */ jsx(
188
+ Button,
189
+ {
190
+ type: "button",
191
+ variant: "outline",
192
+ onClick: () => setIsCreateDialogOpen(false),
193
+ children: localization?.CMS_BUTTON_CANCEL ?? t("cms.common.cancel", "Cancel")
194
+ }
195
+ ),
196
+ /* @__PURE__ */ jsx(
197
+ Button,
198
+ {
199
+ type: "button",
200
+ onClick: handleCreateItem,
201
+ disabled: !newItemName.trim() || createMutation.isPending,
202
+ children: createMutation.isPending ? localization?.CMS_RELATION_CREATING ?? t("cms.relations.creating", "Creating...") : localization?.CMS_RELATION_CREATE_BUTTON ?? t("cms.relations.createButton", "Create")
203
+ }
204
+ )
205
+ ] })
206
+ ] })
207
+ ] })
208
+ ]
209
+ }
210
+ )
211
+ ] }),
212
+ selectedOptions.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1 mt-2", children: selectedOptions.map((opt) => /* @__PURE__ */ jsxs(
213
+ "div",
214
+ {
215
+ className: "inline-flex items-center gap-1 px-2 py-1 text-xs rounded-md bg-secondary text-secondary-foreground",
216
+ children: [
217
+ /* @__PURE__ */ jsx("span", { children: opt.label }),
218
+ /* @__PURE__ */ jsx(
219
+ "button",
220
+ {
221
+ type: "button",
222
+ onClick: () => handleRemove(opt.value),
223
+ className: "hover:text-destructive",
224
+ children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3" })
225
+ }
226
+ )
227
+ ]
228
+ },
229
+ opt.value
230
+ )) }),
231
+ fieldConfigItem?.description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: fieldConfigItem.description })
232
+ ] });
233
+ }
234
+
235
+ export { RelationField };
@@ -0,0 +1,14 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ const dashboardPage = require('./pages/dashboard-page.cjs');
5
+ const contentListPage = require('./pages/content-list-page.cjs');
6
+ const contentEditorPage = require('./pages/content-editor-page.cjs');
7
+
8
+ const DashboardPage = dashboardPage.DashboardPageComponent;
9
+ const ContentListPage = contentListPage.ContentListPageComponent;
10
+ const ContentEditorPage = contentEditorPage.ContentEditorPageComponent;
11
+
12
+ exports.ContentEditorPage = ContentEditorPage;
13
+ exports.ContentListPage = ContentListPage;
14
+ exports.DashboardPage = DashboardPage;
@@ -0,0 +1,20 @@
1
+ 'use client';
2
+ import { D as DashboardPageComponent } from '../../shared/plugin-cms.BBshPRkb.cjs';
3
+ import * as react from 'react';
4
+
5
+ interface ContentListPageComponentProps {
6
+ typeSlug: string;
7
+ }
8
+ declare function ContentListPageComponent({ typeSlug, }: ContentListPageComponentProps): react.JSX.Element;
9
+
10
+ interface ContentEditorPageComponentProps {
11
+ typeSlug: string;
12
+ id?: string;
13
+ }
14
+ declare function ContentEditorPageComponent({ typeSlug, id, }: ContentEditorPageComponentProps): react.JSX.Element;
15
+
16
+ declare const DashboardPage: typeof DashboardPageComponent;
17
+ declare const ContentListPage: typeof ContentListPageComponent;
18
+ declare const ContentEditorPage: typeof ContentEditorPageComponent;
19
+
20
+ export { ContentEditorPage, ContentListPage, DashboardPage };
@@ -0,0 +1,20 @@
1
+ 'use client';
2
+ import { D as DashboardPageComponent } from '../../shared/plugin-cms.BBshPRkb.mjs';
3
+ import * as react from 'react';
4
+
5
+ interface ContentListPageComponentProps {
6
+ typeSlug: string;
7
+ }
8
+ declare function ContentListPageComponent({ typeSlug, }: ContentListPageComponentProps): react.JSX.Element;
9
+
10
+ interface ContentEditorPageComponentProps {
11
+ typeSlug: string;
12
+ id?: string;
13
+ }
14
+ declare function ContentEditorPageComponent({ typeSlug, id, }: ContentEditorPageComponentProps): react.JSX.Element;
15
+
16
+ declare const DashboardPage: typeof DashboardPageComponent;
17
+ declare const ContentListPage: typeof ContentListPageComponent;
18
+ declare const ContentEditorPage: typeof ContentEditorPageComponent;
19
+
20
+ export { ContentEditorPage, ContentListPage, DashboardPage };
@@ -0,0 +1,20 @@
1
+ 'use client';
2
+ import { D as DashboardPageComponent } from '../../shared/plugin-cms.BBshPRkb.js';
3
+ import * as react from 'react';
4
+
5
+ interface ContentListPageComponentProps {
6
+ typeSlug: string;
7
+ }
8
+ declare function ContentListPageComponent({ typeSlug, }: ContentListPageComponentProps): react.JSX.Element;
9
+
10
+ interface ContentEditorPageComponentProps {
11
+ typeSlug: string;
12
+ id?: string;
13
+ }
14
+ declare function ContentEditorPageComponent({ typeSlug, id, }: ContentEditorPageComponentProps): react.JSX.Element;
15
+
16
+ declare const DashboardPage: typeof DashboardPageComponent;
17
+ declare const ContentListPage: typeof ContentListPageComponent;
18
+ declare const ContentEditorPage: typeof ContentEditorPageComponent;
19
+
20
+ export { ContentEditorPage, ContentListPage, DashboardPage };
@@ -0,0 +1,10 @@
1
+ 'use client';
2
+ import { DashboardPageComponent } from './pages/dashboard-page.mjs';
3
+ import { ContentListPageComponent } from './pages/content-list-page.mjs';
4
+ import { ContentEditorPageComponent } from './pages/content-editor-page.mjs';
5
+
6
+ const DashboardPage = DashboardPageComponent;
7
+ const ContentListPage = ContentListPageComponent;
8
+ const ContentEditorPage = ContentEditorPageComponent;
9
+
10
+ export { ContentEditorPage, ContentListPage, DashboardPage };