convex-cms 0.0.1 → 0.0.3

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 (267) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +99 -0
  3. package/admin-dist/nitro.json +15 -0
  4. package/admin-dist/public/assets/CmsEmptyState-CRswfTzk.js +5 -0
  5. package/admin-dist/public/assets/CmsPageHeader-CirpXndm.js +1 -0
  6. package/admin-dist/public/assets/CmsStatusBadge-CbEUpQu-.js +1 -0
  7. package/admin-dist/public/assets/CmsToolbar-BI2nZOXp.js +1 -0
  8. package/admin-dist/public/assets/ContentEntryEditor-CBeCyK_m.js +4 -0
  9. package/admin-dist/public/assets/ErrorState-BIVaWmom.js +1 -0
  10. package/admin-dist/public/assets/TaxonomyFilter-ChaY6Y_x.js +1 -0
  11. package/admin-dist/public/assets/_contentTypeId-DQ8k_Rvw.js +1 -0
  12. package/admin-dist/public/assets/_entryId-CKU_glsK.js +1 -0
  13. package/admin-dist/public/assets/alert-BXjTqrwQ.js +1 -0
  14. package/admin-dist/public/assets/badge-hvUOzpVZ.js +1 -0
  15. package/admin-dist/public/assets/circle-check-big-CF_pR17r.js +1 -0
  16. package/admin-dist/public/assets/command-DU82cJlt.js +1 -0
  17. package/admin-dist/public/assets/content-_LXl3pp7.js +1 -0
  18. package/admin-dist/public/assets/content-types-KjxaXGxY.js +2 -0
  19. package/admin-dist/public/assets/globals-CS6BZ0zp.css +1 -0
  20. package/admin-dist/public/assets/index-DNGIZHL-.js +1 -0
  21. package/admin-dist/public/assets/label-KNtpL71g.js +1 -0
  22. package/admin-dist/public/assets/link-2-Bw2aI4V4.js +1 -0
  23. package/admin-dist/public/assets/list-sYepHjt_.js +1 -0
  24. package/admin-dist/public/assets/main-CKj5yfEi.js +97 -0
  25. package/admin-dist/public/assets/media-Bkrkffm7.js +1 -0
  26. package/admin-dist/public/assets/new._contentTypeId-C3LstjNs.js +1 -0
  27. package/admin-dist/public/assets/plus-DUn8v_Xf.js +1 -0
  28. package/admin-dist/public/assets/rotate-ccw-DJEoHcRI.js +1 -0
  29. package/admin-dist/public/assets/scroll-area-DfIlT0in.js +1 -0
  30. package/admin-dist/public/assets/search-MuAUDJKR.js +1 -0
  31. package/admin-dist/public/assets/select-BD29IXCI.js +1 -0
  32. package/admin-dist/public/assets/settings-DmMyn_6A.js +1 -0
  33. package/admin-dist/public/assets/switch-h3Rrnl5i.js +1 -0
  34. package/admin-dist/public/assets/tabs-imc8h-Dp.js +1 -0
  35. package/admin-dist/public/assets/taxonomies-dAsrT65H.js +1 -0
  36. package/admin-dist/public/assets/textarea-BTy7nwzR.js +1 -0
  37. package/admin-dist/public/assets/trash-SAWKZZHv.js +1 -0
  38. package/admin-dist/public/assets/triangle-alert-E52Vfeuh.js +1 -0
  39. package/admin-dist/public/assets/useBreadcrumbLabel-BECBMCzM.js +1 -0
  40. package/admin-dist/public/assets/usePermissions-Basjs9BT.js +1 -0
  41. package/admin-dist/public/favicon.ico +0 -0
  42. package/admin-dist/server/_chunks/_libs/@date-fns/tz.mjs +217 -0
  43. package/admin-dist/server/_chunks/_libs/@floating-ui/core.mjs +719 -0
  44. package/admin-dist/server/_chunks/_libs/@floating-ui/dom.mjs +622 -0
  45. package/admin-dist/server/_chunks/_libs/@floating-ui/react-dom.mjs +292 -0
  46. package/admin-dist/server/_chunks/_libs/@floating-ui/utils.mjs +320 -0
  47. package/admin-dist/server/_chunks/_libs/@radix-ui/number.mjs +6 -0
  48. package/admin-dist/server/_chunks/_libs/@radix-ui/primitive.mjs +11 -0
  49. package/admin-dist/server/_chunks/_libs/@radix-ui/react-arrow.mjs +23 -0
  50. package/admin-dist/server/_chunks/_libs/@radix-ui/react-avatar.mjs +119 -0
  51. package/admin-dist/server/_chunks/_libs/@radix-ui/react-checkbox.mjs +270 -0
  52. package/admin-dist/server/_chunks/_libs/@radix-ui/react-collection.mjs +69 -0
  53. package/admin-dist/server/_chunks/_libs/@radix-ui/react-compose-refs.mjs +39 -0
  54. package/admin-dist/server/_chunks/_libs/@radix-ui/react-context.mjs +137 -0
  55. package/admin-dist/server/_chunks/_libs/@radix-ui/react-dialog.mjs +325 -0
  56. package/admin-dist/server/_chunks/_libs/@radix-ui/react-direction.mjs +9 -0
  57. package/admin-dist/server/_chunks/_libs/@radix-ui/react-dismissable-layer.mjs +210 -0
  58. package/admin-dist/server/_chunks/_libs/@radix-ui/react-dropdown-menu.mjs +253 -0
  59. package/admin-dist/server/_chunks/_libs/@radix-ui/react-focus-guards.mjs +29 -0
  60. package/admin-dist/server/_chunks/_libs/@radix-ui/react-focus-scope.mjs +206 -0
  61. package/admin-dist/server/_chunks/_libs/@radix-ui/react-id.mjs +14 -0
  62. package/admin-dist/server/_chunks/_libs/@radix-ui/react-label.mjs +23 -0
  63. package/admin-dist/server/_chunks/_libs/@radix-ui/react-menu.mjs +812 -0
  64. package/admin-dist/server/_chunks/_libs/@radix-ui/react-popover.mjs +300 -0
  65. package/admin-dist/server/_chunks/_libs/@radix-ui/react-popper.mjs +286 -0
  66. package/admin-dist/server/_chunks/_libs/@radix-ui/react-portal.mjs +16 -0
  67. package/admin-dist/server/_chunks/_libs/@radix-ui/react-presence.mjs +128 -0
  68. package/admin-dist/server/_chunks/_libs/@radix-ui/react-primitive.mjs +141 -0
  69. package/admin-dist/server/_chunks/_libs/@radix-ui/react-roving-focus.mjs +224 -0
  70. package/admin-dist/server/_chunks/_libs/@radix-ui/react-scroll-area.mjs +721 -0
  71. package/admin-dist/server/_chunks/_libs/@radix-ui/react-select.mjs +1163 -0
  72. package/admin-dist/server/_chunks/_libs/@radix-ui/react-separator.mjs +28 -0
  73. package/admin-dist/server/_chunks/_libs/@radix-ui/react-slot.mjs +601 -0
  74. package/admin-dist/server/_chunks/_libs/@radix-ui/react-switch.mjs +152 -0
  75. package/admin-dist/server/_chunks/_libs/@radix-ui/react-tabs.mjs +189 -0
  76. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-callback-ref.mjs +11 -0
  77. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-controllable-state.mjs +69 -0
  78. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-effect-event.mjs +1 -0
  79. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-escape-keydown.mjs +17 -0
  80. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-is-hydrated.mjs +15 -0
  81. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-layout-effect.mjs +6 -0
  82. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-previous.mjs +14 -0
  83. package/admin-dist/server/_chunks/_libs/@radix-ui/react-use-size.mjs +39 -0
  84. package/admin-dist/server/_chunks/_libs/@radix-ui/react-visually-hidden.mjs +33 -0
  85. package/admin-dist/server/_chunks/_libs/@tanstack/history.mjs +409 -0
  86. package/admin-dist/server/_chunks/_libs/@tanstack/react-router.mjs +1711 -0
  87. package/admin-dist/server/_chunks/_libs/@tanstack/react-store.mjs +56 -0
  88. package/admin-dist/server/_chunks/_libs/@tanstack/router-core.mjs +4829 -0
  89. package/admin-dist/server/_chunks/_libs/@tanstack/store.mjs +134 -0
  90. package/admin-dist/server/_chunks/_libs/react-dom.mjs +10781 -0
  91. package/admin-dist/server/_chunks/_libs/react.mjs +513 -0
  92. package/admin-dist/server/_libs/aria-hidden.mjs +122 -0
  93. package/admin-dist/server/_libs/class-variance-authority.mjs +44 -0
  94. package/admin-dist/server/_libs/clsx.mjs +16 -0
  95. package/admin-dist/server/_libs/cmdk.mjs +315 -0
  96. package/admin-dist/server/_libs/convex.mjs +4841 -0
  97. package/admin-dist/server/_libs/cookie-es.mjs +58 -0
  98. package/admin-dist/server/_libs/croner.mjs +1 -0
  99. package/admin-dist/server/_libs/crossws.mjs +1 -0
  100. package/admin-dist/server/_libs/date-fns.mjs +1716 -0
  101. package/admin-dist/server/_libs/detect-node-es.mjs +1 -0
  102. package/admin-dist/server/_libs/get-nonce.mjs +9 -0
  103. package/admin-dist/server/_libs/h3-v2.mjs +277 -0
  104. package/admin-dist/server/_libs/h3.mjs +401 -0
  105. package/admin-dist/server/_libs/hookable.mjs +1 -0
  106. package/admin-dist/server/_libs/isbot.mjs +20 -0
  107. package/admin-dist/server/_libs/lucide-react.mjs +850 -0
  108. package/admin-dist/server/_libs/ohash.mjs +1 -0
  109. package/admin-dist/server/_libs/react-day-picker.mjs +2201 -0
  110. package/admin-dist/server/_libs/react-remove-scroll-bar.mjs +82 -0
  111. package/admin-dist/server/_libs/react-remove-scroll.mjs +328 -0
  112. package/admin-dist/server/_libs/react-style-singleton.mjs +69 -0
  113. package/admin-dist/server/_libs/rou3.mjs +8 -0
  114. package/admin-dist/server/_libs/seroval-plugins.mjs +58 -0
  115. package/admin-dist/server/_libs/seroval.mjs +1765 -0
  116. package/admin-dist/server/_libs/srvx.mjs +719 -0
  117. package/admin-dist/server/_libs/tailwind-merge.mjs +3010 -0
  118. package/admin-dist/server/_libs/tiny-invariant.mjs +12 -0
  119. package/admin-dist/server/_libs/tiny-warning.mjs +5 -0
  120. package/admin-dist/server/_libs/tslib.mjs +39 -0
  121. package/admin-dist/server/_libs/ufo.mjs +54 -0
  122. package/admin-dist/server/_libs/unctx.mjs +1 -0
  123. package/admin-dist/server/_libs/unstorage.mjs +1 -0
  124. package/admin-dist/server/_libs/use-callback-ref.mjs +66 -0
  125. package/admin-dist/server/_libs/use-sidecar.mjs +106 -0
  126. package/admin-dist/server/_libs/use-sync-external-store.mjs +139 -0
  127. package/admin-dist/server/_libs/zod.mjs +4223 -0
  128. package/admin-dist/server/_ssr/CmsEmptyState-DU7-7-mV.mjs +290 -0
  129. package/admin-dist/server/_ssr/CmsPageHeader-CseW0AHm.mjs +24 -0
  130. package/admin-dist/server/_ssr/CmsStatusBadge-B_pi4KCp.mjs +127 -0
  131. package/admin-dist/server/_ssr/CmsToolbar-X75ex6ek.mjs +49 -0
  132. package/admin-dist/server/_ssr/ContentEntryEditor-CepusRsA.mjs +3720 -0
  133. package/admin-dist/server/_ssr/ErrorState-cI-bKLez.mjs +89 -0
  134. package/admin-dist/server/_ssr/TaxonomyFilter-Bwrq0-cz.mjs +188 -0
  135. package/admin-dist/server/_ssr/_contentTypeId-BqYKEcLr.mjs +379 -0
  136. package/admin-dist/server/_ssr/_entryId-CRfnqeDf.mjs +161 -0
  137. package/admin-dist/server/_ssr/_tanstack-start-manifest_v-BwDlABVk.mjs +4 -0
  138. package/admin-dist/server/_ssr/alert-CVt45UUP.mjs +92 -0
  139. package/admin-dist/server/_ssr/badge-6BsP37vG.mjs +125 -0
  140. package/admin-dist/server/_ssr/command-fy8epIKf.mjs +128 -0
  141. package/admin-dist/server/_ssr/config.server-D7JHDcDv.mjs +117 -0
  142. package/admin-dist/server/_ssr/content-B5RhL7uW.mjs +532 -0
  143. package/admin-dist/server/_ssr/content-types-BIOqCQYN.mjs +1166 -0
  144. package/admin-dist/server/_ssr/index-DHSHDPt1.mjs +193 -0
  145. package/admin-dist/server/_ssr/index.mjs +1275 -0
  146. package/admin-dist/server/_ssr/label-C8Dko1j7.mjs +22 -0
  147. package/admin-dist/server/_ssr/media-CSx3XttC.mjs +1832 -0
  148. package/admin-dist/server/_ssr/new._contentTypeId-DzanEZQM.mjs +144 -0
  149. package/admin-dist/server/_ssr/router-DDWcF-kt.mjs +1556 -0
  150. package/admin-dist/server/_ssr/scroll-area-bjPYwhXN.mjs +59 -0
  151. package/admin-dist/server/_ssr/select-BUhDDf4T.mjs +142 -0
  152. package/admin-dist/server/_ssr/settings-DAsxnw2q.mjs +348 -0
  153. package/admin-dist/server/_ssr/start-HYkvq4Ni.mjs +4 -0
  154. package/admin-dist/server/_ssr/switch-BgyRtQ1Z.mjs +31 -0
  155. package/admin-dist/server/_ssr/tabs-DzMdRB1A.mjs +628 -0
  156. package/admin-dist/server/_ssr/taxonomies-C8j8g5Q5.mjs +915 -0
  157. package/admin-dist/server/_ssr/textarea-9jNeYJSc.mjs +18 -0
  158. package/admin-dist/server/_ssr/trash-DYMxwhZB.mjs +291 -0
  159. package/admin-dist/server/_ssr/useBreadcrumbLabel-FNSAr2Ha.mjs +16 -0
  160. package/admin-dist/server/_ssr/usePermissions-BJGGahrJ.mjs +68 -0
  161. package/admin-dist/server/favicon.ico +0 -0
  162. package/admin-dist/server/index.mjs +627 -0
  163. package/dist/cli/index.js +0 -0
  164. package/dist/client/admin-config.d.ts +0 -1
  165. package/dist/client/admin-config.d.ts.map +1 -1
  166. package/dist/client/admin-config.js +0 -1
  167. package/dist/client/admin-config.js.map +1 -1
  168. package/dist/client/adminApi.d.ts.map +1 -1
  169. package/dist/client/agentTools.d.ts +1237 -135
  170. package/dist/client/agentTools.d.ts.map +1 -1
  171. package/dist/client/agentTools.js +33 -9
  172. package/dist/client/agentTools.js.map +1 -1
  173. package/dist/client/index.d.ts +1 -1
  174. package/dist/client/index.d.ts.map +1 -1
  175. package/dist/client/index.js.map +1 -1
  176. package/dist/component/_generated/component.d.ts +9 -0
  177. package/dist/component/_generated/component.d.ts.map +1 -1
  178. package/dist/component/mediaAssets.d.ts +35 -0
  179. package/dist/component/mediaAssets.d.ts.map +1 -1
  180. package/dist/component/mediaAssets.js +81 -0
  181. package/dist/component/mediaAssets.js.map +1 -1
  182. package/dist/test.d.ts.map +1 -1
  183. package/dist/test.js +2 -1
  184. package/dist/test.js.map +1 -1
  185. package/package.json +24 -9
  186. package/dist/component/auditLog.d.ts +0 -410
  187. package/dist/component/auditLog.d.ts.map +0 -1
  188. package/dist/component/auditLog.js +0 -607
  189. package/dist/component/auditLog.js.map +0 -1
  190. package/dist/component/types.d.ts +0 -4
  191. package/dist/component/types.d.ts.map +0 -1
  192. package/dist/component/types.js +0 -2
  193. package/dist/component/types.js.map +0 -1
  194. package/src/cli/commands/admin.ts +0 -104
  195. package/src/cli/index.ts +0 -21
  196. package/src/cli/utils/detectConvexUrl.ts +0 -54
  197. package/src/cli/utils/openBrowser.ts +0 -16
  198. package/src/client/admin-config.ts +0 -138
  199. package/src/client/adminApi.ts +0 -942
  200. package/src/client/agentTools.ts +0 -1311
  201. package/src/client/argTypes.ts +0 -316
  202. package/src/client/field-types.ts +0 -187
  203. package/src/client/index.ts +0 -1301
  204. package/src/client/queryBuilder.ts +0 -1100
  205. package/src/client/schema/codegen.ts +0 -500
  206. package/src/client/schema/defineContentType.ts +0 -501
  207. package/src/client/schema/index.ts +0 -169
  208. package/src/client/schema/schemaDrift.ts +0 -574
  209. package/src/client/schema/typedClient.ts +0 -688
  210. package/src/client/schema/types.ts +0 -666
  211. package/src/client/types.ts +0 -723
  212. package/src/client/workflows.ts +0 -141
  213. package/src/client/wrapper.ts +0 -4304
  214. package/src/component/_generated/api.ts +0 -140
  215. package/src/component/_generated/component.ts +0 -5029
  216. package/src/component/_generated/dataModel.ts +0 -60
  217. package/src/component/_generated/server.ts +0 -156
  218. package/src/component/authorization.ts +0 -647
  219. package/src/component/authorizationHooks.ts +0 -668
  220. package/src/component/bulkOperations.ts +0 -687
  221. package/src/component/contentEntries.ts +0 -1976
  222. package/src/component/contentEntryMutations.ts +0 -1223
  223. package/src/component/contentEntryValidation.ts +0 -707
  224. package/src/component/contentLock.ts +0 -550
  225. package/src/component/contentTypeMigration.ts +0 -1064
  226. package/src/component/contentTypeMutations.ts +0 -969
  227. package/src/component/contentTypes.ts +0 -346
  228. package/src/component/convex.config.ts +0 -44
  229. package/src/component/documentTypes.ts +0 -240
  230. package/src/component/eventEmitter.ts +0 -485
  231. package/src/component/exportImport.ts +0 -1169
  232. package/src/component/index.ts +0 -491
  233. package/src/component/lib/deepReferenceResolver.ts +0 -999
  234. package/src/component/lib/errors.ts +0 -816
  235. package/src/component/lib/index.ts +0 -145
  236. package/src/component/lib/mediaReferenceResolver.ts +0 -495
  237. package/src/component/lib/metadataExtractor.ts +0 -792
  238. package/src/component/lib/mutationAuth.ts +0 -199
  239. package/src/component/lib/queries.ts +0 -79
  240. package/src/component/lib/ragContentChunker.ts +0 -1371
  241. package/src/component/lib/referenceResolver.ts +0 -430
  242. package/src/component/lib/slugGenerator.ts +0 -262
  243. package/src/component/lib/slugUniqueness.ts +0 -333
  244. package/src/component/lib/softDelete.ts +0 -44
  245. package/src/component/localeFallbackChain.ts +0 -673
  246. package/src/component/localeFields.ts +0 -896
  247. package/src/component/mediaAssetMutations.ts +0 -725
  248. package/src/component/mediaAssets.ts +0 -932
  249. package/src/component/mediaFolderMutations.ts +0 -1046
  250. package/src/component/mediaUploadMutations.ts +0 -224
  251. package/src/component/mediaVariantMutations.ts +0 -900
  252. package/src/component/mediaVariants.ts +0 -793
  253. package/src/component/ragContentIndexer.ts +0 -1067
  254. package/src/component/rateLimitHooks.ts +0 -572
  255. package/src/component/roles.ts +0 -1360
  256. package/src/component/scheduledPublish.ts +0 -358
  257. package/src/component/schema.ts +0 -617
  258. package/src/component/taxonomies.ts +0 -949
  259. package/src/component/taxonomyMutations.ts +0 -1210
  260. package/src/component/trash.ts +0 -724
  261. package/src/component/userContext.ts +0 -898
  262. package/src/component/validation.ts +0 -1388
  263. package/src/component/validators.ts +0 -949
  264. package/src/component/versionMutations.ts +0 -392
  265. package/src/component/webhookTrigger.ts +0 -1922
  266. package/src/react/index.ts +0 -898
  267. package/src/test.ts +0 -1580
@@ -0,0 +1,3720 @@
1
+ import { r as reactExports, j as jsxRuntimeExports } from "../_chunks/_libs/react.mjs";
2
+ import { u as useSettingsConfig, a as api, c as cn, P as Popover, j as PopoverTrigger, k as PopoverContent, B as Button, q as buttonVariants } from "./router-DDWcF-kt.mjs";
3
+ import { L as Label } from "./label-C8Dko1j7.mjs";
4
+ import { c as CmsDialog, I as Input, b as CmsConfirmDialog, D as Dialog, d as DialogContent, e as DialogHeader, f as DialogTitle, C as CmsEmptyState, g as DialogFooter, a as Checkbox } from "./CmsEmptyState-DU7-7-mV.mjs";
5
+ import { S as Switch } from "./switch-BgyRtQ1Z.mjs";
6
+ import { T as Textarea } from "./textarea-9jNeYJSc.mjs";
7
+ import { R as Root } from "../_chunks/_libs/@radix-ui/react-separator.mjs";
8
+ import { S as Select, a as SelectTrigger, b as SelectValue, c as SelectContent, d as SelectItem } from "./select-BUhDDf4T.mjs";
9
+ import { T as Tabs, a as TabsList, b as TabsTrigger, c as TabsContent, U as UploadDropzone } from "./tabs-DzMdRB1A.mjs";
10
+ import { C as CmsButton, B as Badge } from "./badge-6BsP37vG.mjs";
11
+ import { C as Command, a as CommandInput, b as CommandList, d as CommandGroup, f as CommandItem, c as CommandEmpty } from "./command-fy8epIKf.mjs";
12
+ import { C as CmsStatusBadge } from "./CmsStatusBadge-B_pi4KCp.mjs";
13
+ import { S as ScrollArea } from "./scroll-area-bjPYwhXN.mjs";
14
+ import { d as useMutation, u as useQuery } from "../_libs/convex.mjs";
15
+ import { L as LoaderCircle, aj as CircleCheckBig, g as CircleAlert, am as RefreshCw, an as History, t as Clock, X, d as ChevronDown, V as Tag, Q as Plus, a8 as Link2, J as FileText, Z as Check, W as Pencil, I as Image, ah as FolderOpen, a3 as Upload, N as Search, u as Calendar$1, ao as GitCompare, ap as ArrowRight, O as TriangleAlert, f as ChevronRight, a9 as File, aa as Music, ab as Video, a1 as ChevronLeft, aq as Bold, ar as Italic, as as Heading2, s as Link, ae as List, at as Quote, c as Code, au as Minus } from "../_libs/lucide-react.mjs";
16
+ import { g as getDefaultClassNames, D as DayPicker } from "../_libs/react-day-picker.mjs";
17
+ function FieldWrapper({
18
+ field,
19
+ children,
20
+ error,
21
+ className = "",
22
+ id,
23
+ customLabel
24
+ }) {
25
+ const hasError = !!error;
26
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn("space-y-2", className), children: [
27
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
28
+ Label,
29
+ {
30
+ htmlFor: id,
31
+ className: cn(
32
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
33
+ hasError && "text-destructive"
34
+ ),
35
+ children: [
36
+ customLabel ?? field.label,
37
+ field.required && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-1 text-destructive", children: "*" })
38
+ ]
39
+ }
40
+ ),
41
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "relative", children }),
42
+ field.description && !hasError && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { id: `${id}-description`, className: "text-[13px] text-muted-foreground", children: field.description }),
43
+ hasError && /* @__PURE__ */ jsxRuntimeExports.jsx(
44
+ "p",
45
+ {
46
+ id: `${id}-error`,
47
+ className: "text-[13px] font-medium text-destructive",
48
+ role: "alert",
49
+ children: error.message
50
+ }
51
+ )
52
+ ] });
53
+ }
54
+ function TextField({
55
+ field,
56
+ value,
57
+ onChange,
58
+ error,
59
+ disabled = false,
60
+ readOnly = false,
61
+ className = "",
62
+ id: providedId,
63
+ placeholder,
64
+ inputType = "text"
65
+ }) {
66
+ const generatedId = reactExports.useId();
67
+ const id = providedId ?? `field-${field.name}-${generatedId}`;
68
+ const handleChange = (e) => {
69
+ onChange(e.target.value);
70
+ };
71
+ const { minLength, maxLength, pattern } = field.options ?? {};
72
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(FieldWrapper, { field, error, className, id, children: [
73
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
74
+ Input,
75
+ {
76
+ type: inputType,
77
+ id,
78
+ name: field.name,
79
+ value: value ?? "",
80
+ onChange: handleChange,
81
+ disabled,
82
+ readOnly,
83
+ required: field.required,
84
+ minLength,
85
+ maxLength,
86
+ pattern,
87
+ placeholder,
88
+ className: cn(error && "border-destructive focus-visible:ring-destructive"),
89
+ "aria-invalid": !!error,
90
+ "aria-describedby": error ? `${id}-error` : field.description ? `${id}-description` : void 0
91
+ }
92
+ ),
93
+ maxLength && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "mt-1 block text-right text-xs text-muted-foreground", children: [
94
+ (value ?? "").length,
95
+ " / ",
96
+ maxLength
97
+ ] })
98
+ ] });
99
+ }
100
+ function NumberField({
101
+ field,
102
+ value,
103
+ onChange,
104
+ error,
105
+ disabled = false,
106
+ readOnly = false,
107
+ className = "",
108
+ id: providedId,
109
+ placeholder
110
+ }) {
111
+ const generatedId = reactExports.useId();
112
+ const id = providedId ?? `field-${field.name}-${generatedId}`;
113
+ const handleChange = (e) => {
114
+ const inputValue = e.target.value;
115
+ if (inputValue === "") {
116
+ onChange(null);
117
+ return;
118
+ }
119
+ const numValue = parseFloat(inputValue);
120
+ if (!isNaN(numValue)) {
121
+ const { precision: precision2 } = field.options ?? {};
122
+ if (precision2 !== void 0 && precision2 >= 0) {
123
+ const factor = Math.pow(10, precision2);
124
+ onChange(Math.round(numValue * factor) / factor);
125
+ } else {
126
+ onChange(numValue);
127
+ }
128
+ }
129
+ };
130
+ const { min, max, step, precision } = field.options ?? {};
131
+ const computedStep = step ?? (precision !== void 0 ? Math.pow(10, -precision) : "any");
132
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(FieldWrapper, { field, error, className, id, children: [
133
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
134
+ Input,
135
+ {
136
+ type: "number",
137
+ id,
138
+ name: field.name,
139
+ value: value ?? "",
140
+ onChange: handleChange,
141
+ disabled,
142
+ readOnly,
143
+ required: field.required,
144
+ min,
145
+ max,
146
+ step: computedStep,
147
+ placeholder,
148
+ className: cn(error && "border-destructive focus-visible:ring-destructive"),
149
+ "aria-invalid": !!error,
150
+ "aria-describedby": error ? `${id}-error` : field.description ? `${id}-description` : void 0
151
+ }
152
+ ),
153
+ (min !== void 0 || max !== void 0) && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mt-1 block text-xs text-muted-foreground", children: min !== void 0 && max !== void 0 ? `Range: ${min} - ${max}` : min !== void 0 ? `Min: ${min}` : `Max: ${max}` })
154
+ ] });
155
+ }
156
+ function BooleanField({
157
+ field,
158
+ value,
159
+ onChange,
160
+ error,
161
+ disabled = false,
162
+ readOnly = false,
163
+ className = "",
164
+ id: providedId,
165
+ trueLabel = "Yes",
166
+ falseLabel = "No"
167
+ }) {
168
+ const generatedId = reactExports.useId();
169
+ const id = providedId ?? `field-${field.name}-${generatedId}`;
170
+ const handleChange = (checked) => {
171
+ if (!readOnly) {
172
+ onChange(checked);
173
+ }
174
+ };
175
+ const hasError = !!error;
176
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn("space-y-2", className), children: [
177
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
178
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
179
+ Label,
180
+ {
181
+ htmlFor: id,
182
+ className: cn(
183
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
184
+ hasError && "text-destructive"
185
+ ),
186
+ children: [
187
+ field.label,
188
+ field.required && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-1 text-destructive", children: "*" })
189
+ ]
190
+ }
191
+ ),
192
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
193
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
194
+ Switch,
195
+ {
196
+ id,
197
+ checked: value ?? false,
198
+ onCheckedChange: handleChange,
199
+ disabled: disabled || readOnly,
200
+ "aria-invalid": hasError,
201
+ "aria-describedby": error ? `${id}-error` : field.description ? `${id}-description` : void 0
202
+ }
203
+ ),
204
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-muted-foreground", children: value ? trueLabel : falseLabel })
205
+ ] })
206
+ ] }),
207
+ field.description && !hasError && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { id: `${id}-description`, className: "text-[13px] text-muted-foreground", children: field.description }),
208
+ hasError && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { id: `${id}-error`, className: "text-[13px] font-medium text-destructive", role: "alert", children: error.message })
209
+ ] });
210
+ }
211
+ function Calendar({
212
+ className,
213
+ classNames,
214
+ showOutsideDays = true,
215
+ captionLayout = "label",
216
+ buttonVariant = "ghost",
217
+ formatters,
218
+ components,
219
+ ...props
220
+ }) {
221
+ const defaultClassNames = getDefaultClassNames();
222
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
223
+ DayPicker,
224
+ {
225
+ showOutsideDays,
226
+ className: cn(
227
+ "bg-background group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
228
+ String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
229
+ String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
230
+ className
231
+ ),
232
+ captionLayout,
233
+ formatters: {
234
+ formatMonthDropdown: (date) => date.toLocaleString("default", { month: "short" }),
235
+ ...formatters
236
+ },
237
+ classNames: {
238
+ root: cn("w-fit", defaultClassNames.root),
239
+ months: cn(
240
+ "flex gap-4 flex-col md:flex-row relative",
241
+ defaultClassNames.months
242
+ ),
243
+ month: cn("flex flex-col w-full gap-4", defaultClassNames.month),
244
+ nav: cn(
245
+ "flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between",
246
+ defaultClassNames.nav
247
+ ),
248
+ button_previous: cn(
249
+ buttonVariants({ variant: buttonVariant }),
250
+ "size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
251
+ defaultClassNames.button_previous
252
+ ),
253
+ button_next: cn(
254
+ buttonVariants({ variant: buttonVariant }),
255
+ "size-(--cell-size) aria-disabled:opacity-50 p-0 select-none",
256
+ defaultClassNames.button_next
257
+ ),
258
+ month_caption: cn(
259
+ "flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)",
260
+ defaultClassNames.month_caption
261
+ ),
262
+ dropdowns: cn(
263
+ "w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5",
264
+ defaultClassNames.dropdowns
265
+ ),
266
+ dropdown_root: cn(
267
+ "relative has-focus:border-ring border border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md",
268
+ defaultClassNames.dropdown_root
269
+ ),
270
+ dropdown: cn(
271
+ "absolute bg-popover inset-0 opacity-0",
272
+ defaultClassNames.dropdown
273
+ ),
274
+ caption_label: cn(
275
+ "select-none font-medium",
276
+ captionLayout === "label" ? "text-sm" : "rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5",
277
+ defaultClassNames.caption_label
278
+ ),
279
+ table: "w-full border-collapse",
280
+ weekdays: cn("flex", defaultClassNames.weekdays),
281
+ weekday: cn(
282
+ "text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none",
283
+ defaultClassNames.weekday
284
+ ),
285
+ week: cn("flex w-full mt-2", defaultClassNames.week),
286
+ week_number_header: cn(
287
+ "select-none w-(--cell-size)",
288
+ defaultClassNames.week_number_header
289
+ ),
290
+ week_number: cn(
291
+ "text-[0.8rem] select-none text-muted-foreground",
292
+ defaultClassNames.week_number
293
+ ),
294
+ day: cn(
295
+ "relative w-full h-full p-0 text-center [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none",
296
+ props.showWeekNumber ? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-md" : "[&:first-child[data-selected=true]_button]:rounded-l-md",
297
+ defaultClassNames.day
298
+ ),
299
+ range_start: cn(
300
+ "rounded-l-md bg-accent",
301
+ defaultClassNames.range_start
302
+ ),
303
+ range_middle: cn("rounded-none", defaultClassNames.range_middle),
304
+ range_end: cn("rounded-r-md bg-accent", defaultClassNames.range_end),
305
+ today: cn(
306
+ "bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none",
307
+ defaultClassNames.today
308
+ ),
309
+ outside: cn(
310
+ "text-muted-foreground aria-selected:text-muted-foreground",
311
+ defaultClassNames.outside
312
+ ),
313
+ disabled: cn(
314
+ "text-muted-foreground opacity-50",
315
+ defaultClassNames.disabled
316
+ ),
317
+ hidden: cn("invisible", defaultClassNames.hidden),
318
+ ...classNames
319
+ },
320
+ components: {
321
+ Root: ({ className: className2, rootRef, ...props2 }) => {
322
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
323
+ "div",
324
+ {
325
+ "data-slot": "calendar",
326
+ ref: rootRef,
327
+ className: cn(className2),
328
+ ...props2
329
+ }
330
+ );
331
+ },
332
+ Chevron: ({ className: className2, orientation, ...props2 }) => {
333
+ if (orientation === "left") {
334
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronLeft, { className: cn("size-4", className2), ...props2 });
335
+ }
336
+ if (orientation === "right") {
337
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
338
+ ChevronRight,
339
+ {
340
+ className: cn("size-4", className2),
341
+ ...props2
342
+ }
343
+ );
344
+ }
345
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: cn("size-4", className2), ...props2 });
346
+ },
347
+ DayButton: CalendarDayButton,
348
+ WeekNumber: ({ children, ...props2 }) => {
349
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("td", { ...props2, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex size-(--cell-size) items-center justify-center text-center", children }) });
350
+ },
351
+ ...components
352
+ },
353
+ ...props
354
+ }
355
+ );
356
+ }
357
+ function CalendarDayButton({
358
+ className,
359
+ day,
360
+ modifiers,
361
+ ...props
362
+ }) {
363
+ const defaultClassNames = getDefaultClassNames();
364
+ const ref = reactExports.useRef(null);
365
+ reactExports.useEffect(() => {
366
+ if (modifiers.focused) ref.current?.focus();
367
+ }, [modifiers.focused]);
368
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
369
+ Button,
370
+ {
371
+ ref,
372
+ variant: "ghost",
373
+ size: "icon",
374
+ "data-day": day.date.toLocaleDateString(),
375
+ "data-selected-single": modifiers.selected && !modifiers.range_start && !modifiers.range_end && !modifiers.range_middle,
376
+ "data-range-start": modifiers.range_start,
377
+ "data-range-end": modifiers.range_end,
378
+ "data-range-middle": modifiers.range_middle,
379
+ className: cn(
380
+ "data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 dark:hover:text-accent-foreground flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-70",
381
+ defaultClassNames.day,
382
+ className
383
+ ),
384
+ ...props
385
+ }
386
+ );
387
+ }
388
+ function formatDateTimeLocal$1(date) {
389
+ const year = date.getFullYear();
390
+ const month = String(date.getMonth() + 1).padStart(2, "0");
391
+ const day = String(date.getDate()).padStart(2, "0");
392
+ const hours = String(date.getHours()).padStart(2, "0");
393
+ const minutes = String(date.getMinutes()).padStart(2, "0");
394
+ return `${year}-${month}-${day}T${hours}:${minutes}`;
395
+ }
396
+ function formatDateOnly$1(date) {
397
+ const year = date.getFullYear();
398
+ const month = String(date.getMonth() + 1).padStart(2, "0");
399
+ const day = String(date.getDate()).padStart(2, "0");
400
+ return `${year}-${month}-${day}`;
401
+ }
402
+ function DateField({
403
+ field,
404
+ value,
405
+ onChange,
406
+ error,
407
+ disabled = false,
408
+ readOnly = false,
409
+ className = "",
410
+ id: providedId,
411
+ includeTime = false
412
+ }) {
413
+ const generatedId = reactExports.useId();
414
+ const id = providedId ?? `field-${field.name}-${generatedId}`;
415
+ const [open, setOpen] = reactExports.useState(false);
416
+ const showTime = includeTime || field.type === "datetime";
417
+ const selectedDate = value ? new Date(value) : void 0;
418
+ const isValidDate = selectedDate && !isNaN(selectedDate.getTime());
419
+ const timeValue = value && showTime && value.includes("T") ? value.split("T")[1]?.substring(0, 5) || "" : "";
420
+ const handleDateSelect = (date) => {
421
+ if (!date) {
422
+ onChange(null);
423
+ setOpen(false);
424
+ return;
425
+ }
426
+ if (showTime && timeValue) {
427
+ const [hours, minutes] = timeValue.split(":").map(Number);
428
+ date.setHours(hours || 0, minutes || 0);
429
+ }
430
+ const formatted = showTime ? formatDateTimeLocal$1(date) : formatDateOnly$1(date);
431
+ onChange(formatted);
432
+ if (!showTime) {
433
+ setOpen(false);
434
+ }
435
+ };
436
+ const handleTimeChange = (e) => {
437
+ const time = e.target.value;
438
+ if (!time) return;
439
+ const [hours, minutes] = time.split(":").map(Number);
440
+ if (isValidDate && selectedDate) {
441
+ const newDate = new Date(selectedDate);
442
+ newDate.setHours(hours || 0, minutes || 0);
443
+ onChange(formatDateTimeLocal$1(newDate));
444
+ } else {
445
+ const today = /* @__PURE__ */ new Date();
446
+ today.setHours(hours || 0, minutes || 0, 0, 0);
447
+ onChange(formatDateTimeLocal$1(today));
448
+ }
449
+ };
450
+ const formatDisplayDate = (date) => {
451
+ return date.toLocaleDateString(void 0, {
452
+ year: "numeric",
453
+ month: "short",
454
+ day: "numeric"
455
+ });
456
+ };
457
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(FieldWrapper, { field, error, className, id, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
458
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Popover, { open, onOpenChange: setOpen, children: [
459
+ /* @__PURE__ */ jsxRuntimeExports.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
460
+ Button,
461
+ {
462
+ id,
463
+ type: "button",
464
+ variant: "outline",
465
+ disabled: disabled || readOnly,
466
+ className: cn(
467
+ "justify-between font-normal",
468
+ showTime ? "w-44" : "flex-1",
469
+ !value && "text-muted-foreground",
470
+ error && "border-destructive focus-visible:ring-destructive"
471
+ ),
472
+ "aria-invalid": !!error,
473
+ "aria-describedby": error ? `${id}-error` : field.description ? `${id}-description` : void 0,
474
+ children: [
475
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: isValidDate && selectedDate ? formatDisplayDate(selectedDate) : "Select date" }),
476
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Calendar$1, { className: "size-4 opacity-50" })
477
+ ]
478
+ }
479
+ ) }),
480
+ /* @__PURE__ */ jsxRuntimeExports.jsx(PopoverContent, { className: "w-auto p-0", align: "start", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
481
+ Calendar,
482
+ {
483
+ mode: "single",
484
+ selected: selectedDate,
485
+ onSelect: handleDateSelect,
486
+ captionLayout: "dropdown",
487
+ disabled: disabled || readOnly
488
+ }
489
+ ) })
490
+ ] }),
491
+ showTime && /* @__PURE__ */ jsxRuntimeExports.jsx(
492
+ Input,
493
+ {
494
+ type: "time",
495
+ value: timeValue,
496
+ onChange: handleTimeChange,
497
+ disabled: disabled || readOnly,
498
+ className: cn(
499
+ "w-28",
500
+ error && "border-destructive focus-visible:ring-destructive"
501
+ ),
502
+ "aria-label": "Time"
503
+ }
504
+ ),
505
+ value && !readOnly && !disabled && /* @__PURE__ */ jsxRuntimeExports.jsx(
506
+ Button,
507
+ {
508
+ type: "button",
509
+ variant: "ghost",
510
+ size: "icon",
511
+ className: "size-9 shrink-0",
512
+ onClick: () => onChange(null),
513
+ "aria-label": "Clear date",
514
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-4" })
515
+ }
516
+ )
517
+ ] }) });
518
+ }
519
+ function Separator({
520
+ className,
521
+ orientation = "horizontal",
522
+ decorative = true,
523
+ ...props
524
+ }) {
525
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
526
+ Root,
527
+ {
528
+ "data-slot": "separator",
529
+ decorative,
530
+ orientation,
531
+ className: cn(
532
+ "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
533
+ className
534
+ ),
535
+ ...props
536
+ }
537
+ );
538
+ }
539
+ const toolbarActions = [
540
+ { label: "Bold", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Bold, { className: "size-4" }), prefix: "**", suffix: "**" },
541
+ { label: "Italic", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Italic, { className: "size-4" }), prefix: "_", suffix: "_" },
542
+ { label: "Heading", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Heading2, { className: "size-4" }), prefix: "## ", suffix: "", block: true },
543
+ { label: "Link", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Link, { className: "size-4" }), prefix: "[", suffix: "](url)" },
544
+ { label: "List", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(List, { className: "size-4" }), prefix: "- ", suffix: "", block: true },
545
+ { label: "Quote", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Quote, { className: "size-4" }), prefix: "> ", suffix: "", block: true },
546
+ { label: "Code", icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Code, { className: "size-4" }), prefix: "`", suffix: "`" }
547
+ ];
548
+ function RichTextField({
549
+ field,
550
+ value,
551
+ onChange,
552
+ error,
553
+ disabled = false,
554
+ readOnly = false,
555
+ className = "",
556
+ id: providedId,
557
+ placeholder = "Write your content here... (Markdown supported)",
558
+ minHeight = 200
559
+ }) {
560
+ const generatedId = reactExports.useId();
561
+ const id = providedId ?? `field-${field.name}-${generatedId}`;
562
+ const [textareaRef, setTextareaRef] = reactExports.useState(null);
563
+ const handleChange = (e) => {
564
+ onChange(e.target.value);
565
+ };
566
+ const applyFormatting = reactExports.useCallback(
567
+ (action) => {
568
+ if (!textareaRef || disabled || readOnly) return;
569
+ const start = textareaRef.selectionStart;
570
+ const end = textareaRef.selectionEnd;
571
+ const selectedText = value.slice(start, end);
572
+ const currentValue = value;
573
+ let newValue;
574
+ let newCursorPos;
575
+ if (action.block) {
576
+ const lineStart = currentValue.lastIndexOf("\n", start - 1) + 1;
577
+ const beforeLine = currentValue.slice(0, lineStart);
578
+ const afterLine = currentValue.slice(lineStart);
579
+ if (afterLine.startsWith(action.prefix)) {
580
+ newValue = beforeLine + afterLine.slice(action.prefix.length);
581
+ newCursorPos = start - action.prefix.length;
582
+ } else {
583
+ newValue = beforeLine + action.prefix + afterLine;
584
+ newCursorPos = start + action.prefix.length;
585
+ }
586
+ } else {
587
+ if (selectedText) {
588
+ newValue = currentValue.slice(0, start) + action.prefix + selectedText + action.suffix + currentValue.slice(end);
589
+ newCursorPos = end + action.prefix.length + action.suffix.length;
590
+ } else {
591
+ const placeholderText = action.label.toLowerCase();
592
+ newValue = currentValue.slice(0, start) + action.prefix + placeholderText + action.suffix + currentValue.slice(end);
593
+ newCursorPos = start + action.prefix.length;
594
+ }
595
+ }
596
+ onChange(newValue);
597
+ requestAnimationFrame(() => {
598
+ if (textareaRef) {
599
+ textareaRef.focus();
600
+ textareaRef.setSelectionRange(newCursorPos, newCursorPos);
601
+ }
602
+ });
603
+ },
604
+ [textareaRef, value, onChange, disabled, readOnly]
605
+ );
606
+ const { maxLength } = field.options ?? {};
607
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(FieldWrapper, { field, error, className, id, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
608
+ "div",
609
+ {
610
+ className: cn(
611
+ "overflow-hidden rounded-md border border-input",
612
+ error && "border-destructive"
613
+ ),
614
+ children: [
615
+ !readOnly && !disabled && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
616
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
617
+ "div",
618
+ {
619
+ className: "flex flex-wrap gap-1 bg-muted/50 p-1.5",
620
+ role: "toolbar",
621
+ "aria-label": "Formatting options",
622
+ children: toolbarActions.map((action) => /* @__PURE__ */ jsxRuntimeExports.jsx(
623
+ Button,
624
+ {
625
+ type: "button",
626
+ variant: "ghost",
627
+ size: "icon",
628
+ className: "size-7",
629
+ onClick: () => applyFormatting(action),
630
+ title: action.label,
631
+ "aria-label": action.label,
632
+ children: action.icon
633
+ },
634
+ action.label
635
+ ))
636
+ }
637
+ ),
638
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Separator, {})
639
+ ] }),
640
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
641
+ Textarea,
642
+ {
643
+ ref: setTextareaRef,
644
+ id,
645
+ name: field.name,
646
+ value: value ?? "",
647
+ onChange: handleChange,
648
+ disabled,
649
+ readOnly,
650
+ required: field.required,
651
+ maxLength,
652
+ placeholder,
653
+ className: "resize-none rounded-none border-0 focus-visible:ring-0",
654
+ style: { minHeight: `${minHeight}px` },
655
+ "aria-invalid": !!error,
656
+ "aria-describedby": error ? `${id}-error` : field.description ? `${id}-description` : void 0
657
+ }
658
+ ),
659
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between border-t bg-muted/30 px-3 py-1.5 text-xs text-muted-foreground", children: [
660
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Markdown supported" }),
661
+ maxLength && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
662
+ (value ?? "").length,
663
+ " / ",
664
+ maxLength
665
+ ] })
666
+ ] })
667
+ ]
668
+ }
669
+ ) });
670
+ }
671
+ function SelectField({
672
+ field,
673
+ value,
674
+ onChange,
675
+ error,
676
+ disabled = false,
677
+ readOnly = false,
678
+ className = "",
679
+ id,
680
+ placeholder = "Select an option..."
681
+ }) {
682
+ const generatedId = reactExports.useId();
683
+ const fieldId = id ?? `field-${field.name}-${generatedId}`;
684
+ const options = field.options?.options ?? [];
685
+ const handleChange = (newValue) => {
686
+ onChange(newValue);
687
+ };
688
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(FieldWrapper, { field, error, className, id: fieldId, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
689
+ Select,
690
+ {
691
+ value: value ?? "",
692
+ onValueChange: handleChange,
693
+ disabled: disabled || readOnly,
694
+ required: field.required,
695
+ children: [
696
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
697
+ SelectTrigger,
698
+ {
699
+ id: fieldId,
700
+ className: cn(error && "border-destructive focus:ring-destructive"),
701
+ "aria-invalid": !!error,
702
+ "aria-describedby": error ? `${fieldId}-error` : void 0,
703
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder })
704
+ }
705
+ ),
706
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectContent, { children: options.map((option) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: option.value, children: option.label }, option.value)) })
707
+ ]
708
+ }
709
+ ) });
710
+ }
711
+ function MultiSelectField({
712
+ field,
713
+ value,
714
+ onChange,
715
+ error,
716
+ disabled = false,
717
+ readOnly = false,
718
+ className = "",
719
+ id
720
+ }) {
721
+ const generatedId = reactExports.useId();
722
+ const fieldId = id ?? `field-${field.name}-${generatedId}`;
723
+ const options = field.options?.options ?? [];
724
+ const selectedValues = value ?? [];
725
+ const handleChange = (optionValue, checked) => {
726
+ if (checked) {
727
+ onChange([...selectedValues, optionValue]);
728
+ } else {
729
+ onChange(selectedValues.filter((v) => v !== optionValue));
730
+ }
731
+ };
732
+ const handleSelectAll = () => {
733
+ const allValues = options.map((o) => o.value);
734
+ onChange(allValues);
735
+ };
736
+ const handleClearAll = () => {
737
+ onChange([]);
738
+ };
739
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(FieldWrapper, { field, error, className, id: fieldId, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
740
+ "div",
741
+ {
742
+ className: cn(
743
+ "rounded-md border border-input p-3",
744
+ error && "border-destructive"
745
+ ),
746
+ role: "group",
747
+ "aria-labelledby": `${fieldId}-label`,
748
+ children: [
749
+ options.length > 3 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-3 flex gap-2", children: [
750
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
751
+ Button,
752
+ {
753
+ type: "button",
754
+ variant: "outline",
755
+ size: "sm",
756
+ onClick: handleSelectAll,
757
+ disabled: disabled || readOnly,
758
+ children: "Select all"
759
+ }
760
+ ),
761
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
762
+ Button,
763
+ {
764
+ type: "button",
765
+ variant: "outline",
766
+ size: "sm",
767
+ onClick: handleClearAll,
768
+ disabled: disabled || readOnly || selectedValues.length === 0,
769
+ children: "Clear"
770
+ }
771
+ )
772
+ ] }),
773
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2", children: options.map((option) => {
774
+ const optionId = `${fieldId}-${option.value}`;
775
+ const isChecked = selectedValues.includes(option.value);
776
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
777
+ "div",
778
+ {
779
+ className: cn(
780
+ "flex items-center space-x-2 rounded-md p-2",
781
+ isChecked && "bg-accent"
782
+ ),
783
+ children: [
784
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
785
+ Checkbox,
786
+ {
787
+ id: optionId,
788
+ checked: isChecked,
789
+ onCheckedChange: (checked) => handleChange(option.value, checked === true),
790
+ disabled: disabled || readOnly
791
+ }
792
+ ),
793
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
794
+ Label,
795
+ {
796
+ htmlFor: optionId,
797
+ className: "cursor-pointer text-sm font-normal",
798
+ children: option.label
799
+ }
800
+ )
801
+ ]
802
+ },
803
+ option.value
804
+ );
805
+ }) }),
806
+ selectedValues.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 text-xs text-muted-foreground", children: [
807
+ selectedValues.length,
808
+ " of ",
809
+ options.length,
810
+ " selected"
811
+ ] })
812
+ ]
813
+ }
814
+ ) });
815
+ }
816
+ function JsonField({
817
+ field,
818
+ value,
819
+ onChange,
820
+ error,
821
+ disabled = false,
822
+ readOnly = false,
823
+ className = "",
824
+ id,
825
+ placeholder = "{\n \n}",
826
+ rows = 8
827
+ }) {
828
+ const generatedId = reactExports.useId();
829
+ const fieldId = id ?? `field-${field.name}-${generatedId}`;
830
+ const [textValue, setTextValue] = reactExports.useState(() => {
831
+ if (value === null || value === void 0) return "";
832
+ try {
833
+ return JSON.stringify(value, null, 2);
834
+ } catch {
835
+ return "";
836
+ }
837
+ });
838
+ const [syntaxError, setSyntaxError] = reactExports.useState(null);
839
+ reactExports.useEffect(() => {
840
+ if (value === null || value === void 0) {
841
+ setTextValue("");
842
+ return;
843
+ }
844
+ try {
845
+ const formatted = JSON.stringify(value, null, 2);
846
+ const currentParsed = textValue ? JSON.parse(textValue) : null;
847
+ if (JSON.stringify(currentParsed) !== JSON.stringify(value)) {
848
+ setTextValue(formatted);
849
+ }
850
+ } catch {
851
+ }
852
+ }, [value]);
853
+ const handleChange = reactExports.useCallback(
854
+ (e) => {
855
+ const newText = e.target.value;
856
+ setTextValue(newText);
857
+ if (newText.trim() === "") {
858
+ setSyntaxError(null);
859
+ onChange(null);
860
+ return;
861
+ }
862
+ try {
863
+ const parsed = JSON.parse(newText);
864
+ setSyntaxError(null);
865
+ onChange(parsed);
866
+ } catch (err) {
867
+ const message = err instanceof Error ? err.message : "Invalid JSON";
868
+ setSyntaxError(message);
869
+ }
870
+ },
871
+ [onChange]
872
+ );
873
+ const handleFormat = reactExports.useCallback(() => {
874
+ if (textValue.trim() === "") return;
875
+ try {
876
+ const parsed = JSON.parse(textValue);
877
+ const formatted = JSON.stringify(parsed, null, 2);
878
+ setTextValue(formatted);
879
+ setSyntaxError(null);
880
+ } catch {
881
+ }
882
+ }, [textValue]);
883
+ const handleMinify = reactExports.useCallback(() => {
884
+ if (textValue.trim() === "") return;
885
+ try {
886
+ const parsed = JSON.parse(textValue);
887
+ const minified = JSON.stringify(parsed);
888
+ setTextValue(minified);
889
+ setSyntaxError(null);
890
+ } catch {
891
+ }
892
+ }, [textValue]);
893
+ const displayError = syntaxError ? { message: `JSON syntax error: ${syntaxError}`, code: "SYNTAX_ERROR" } : error;
894
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(FieldWrapper, { field, error: displayError, className, id: fieldId, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
895
+ "div",
896
+ {
897
+ className: cn(
898
+ "overflow-hidden rounded-md border border-input",
899
+ displayError && "border-destructive"
900
+ ),
901
+ children: [
902
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 border-b bg-muted/50 px-2 py-1.5", children: [
903
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
904
+ Button,
905
+ {
906
+ type: "button",
907
+ variant: "ghost",
908
+ size: "sm",
909
+ className: "h-7 px-2 text-xs",
910
+ onClick: handleFormat,
911
+ disabled: disabled || readOnly || !!syntaxError,
912
+ children: "Format"
913
+ }
914
+ ),
915
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
916
+ Button,
917
+ {
918
+ type: "button",
919
+ variant: "ghost",
920
+ size: "sm",
921
+ className: "h-7 px-2 text-xs",
922
+ onClick: handleMinify,
923
+ disabled: disabled || readOnly || !!syntaxError,
924
+ children: "Minify"
925
+ }
926
+ )
927
+ ] }),
928
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
929
+ Textarea,
930
+ {
931
+ id: fieldId,
932
+ name: field.name,
933
+ value: textValue,
934
+ onChange: handleChange,
935
+ placeholder,
936
+ rows,
937
+ disabled,
938
+ readOnly,
939
+ required: field.required,
940
+ "aria-invalid": !!displayError,
941
+ "aria-describedby": displayError ? `${fieldId}-error` : void 0,
942
+ className: "resize-none rounded-none border-0 font-mono text-sm focus-visible:ring-0",
943
+ spellCheck: false
944
+ }
945
+ ),
946
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between border-t bg-muted/30 px-3 py-1.5 text-xs text-muted-foreground", children: [
947
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Enter valid JSON data" }),
948
+ syntaxError && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-destructive", children: [
949
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "size-3" }),
950
+ "Invalid"
951
+ ] }),
952
+ !syntaxError && textValue.trim() !== "" && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-emerald-600 dark:text-emerald-400", children: [
953
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3" }),
954
+ "Valid JSON"
955
+ ] })
956
+ ] })
957
+ ]
958
+ }
959
+ ) });
960
+ }
961
+ function getMediaTypeIcon(type, className = "size-6") {
962
+ const iconProps = { className };
963
+ switch (type) {
964
+ case "image":
965
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Image, { ...iconProps });
966
+ case "video":
967
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Video, { ...iconProps });
968
+ case "audio":
969
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { ...iconProps });
970
+ case "document":
971
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(FileText, { ...iconProps });
972
+ default:
973
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(File, { ...iconProps });
974
+ }
975
+ }
976
+ function getMediaTypeFromMimeType(mimeType) {
977
+ if (!mimeType) return "other";
978
+ if (mimeType.startsWith("image/")) return "image";
979
+ if (mimeType.startsWith("video/")) return "video";
980
+ if (mimeType.startsWith("audio/")) return "audio";
981
+ if (mimeType === "application/pdf" || mimeType.includes("document") || mimeType.includes("sheet") || mimeType.includes("presentation") || mimeType.startsWith("text/")) {
982
+ return "document";
983
+ }
984
+ return "other";
985
+ }
986
+ function formatFileSize(bytes) {
987
+ if (bytes === 0) return "0 B";
988
+ const k = 1024;
989
+ const sizes = ["B", "KB", "MB", "GB"];
990
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
991
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
992
+ }
993
+ function MediaField({
994
+ field,
995
+ value,
996
+ onChange,
997
+ error,
998
+ disabled = false,
999
+ readOnly = false,
1000
+ className = "",
1001
+ id,
1002
+ placeholder = "Select media..."
1003
+ }) {
1004
+ const fieldId = id || `field-${field.name}`;
1005
+ const [showPicker, setShowPicker] = reactExports.useState(false);
1006
+ const [activeTab, setActiveTab] = reactExports.useState("browse");
1007
+ const [searchQuery, setSearchQuery] = reactExports.useState("");
1008
+ const [typeFilter, setTypeFilter] = reactExports.useState("");
1009
+ const allowedMimeTypes = field.options?.allowedMimeTypes ?? [];
1010
+ const selectedAsset = useQuery(
1011
+ api.media.getAsset,
1012
+ value ? { id: value } : "skip"
1013
+ );
1014
+ const assetsResult = useQuery(
1015
+ api.media.listAssets,
1016
+ showPicker ? {
1017
+ type: typeFilter ? typeFilter : void 0,
1018
+ search: searchQuery || void 0,
1019
+ paginationOpts: { numItems: 50, cursor: null }
1020
+ } : "skip"
1021
+ );
1022
+ const handleSelect = reactExports.useCallback(
1023
+ (assetId) => {
1024
+ onChange(assetId);
1025
+ setShowPicker(false);
1026
+ },
1027
+ [onChange]
1028
+ );
1029
+ const handleClear = reactExports.useCallback(() => {
1030
+ onChange(null);
1031
+ }, [onChange]);
1032
+ const handleUploadComplete = reactExports.useCallback((_results) => {
1033
+ setActiveTab("browse");
1034
+ }, []);
1035
+ const filteredAssets = assetsResult?.page?.filter((asset) => {
1036
+ if (allowedMimeTypes.length === 0) return true;
1037
+ return allowedMimeTypes.some((pattern) => {
1038
+ if (pattern.endsWith("/*")) {
1039
+ const prefix = pattern.slice(0, -1);
1040
+ return asset.mimeType?.startsWith(prefix);
1041
+ }
1042
+ return asset.mimeType === pattern;
1043
+ });
1044
+ });
1045
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(FieldWrapper, { field, error, className, id: fieldId, children: [
1046
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2", children: value && selectedAsset ? (() => {
1047
+ const selectedMediaType = getMediaTypeFromMimeType(selectedAsset.mimeType);
1048
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 rounded-lg border border-border bg-card p-3", children: [
1049
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex size-12 shrink-0 items-center justify-center overflow-hidden rounded-md bg-muted", children: selectedMediaType === "image" && selectedAsset.url ? /* @__PURE__ */ jsxRuntimeExports.jsx(
1050
+ "img",
1051
+ {
1052
+ src: selectedAsset.url,
1053
+ alt: selectedAsset.title || selectedAsset.name,
1054
+ className: "size-full object-cover"
1055
+ }
1056
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground", children: getMediaTypeIcon(selectedMediaType) }) }),
1057
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
1058
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1059
+ "p",
1060
+ {
1061
+ className: "truncate text-sm font-medium text-foreground",
1062
+ title: selectedAsset.name,
1063
+ children: selectedAsset.name
1064
+ }
1065
+ ),
1066
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
1067
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "capitalize", children: selectedMediaType }),
1068
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "•" }),
1069
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: formatFileSize(selectedAsset.size ?? 0) })
1070
+ ] })
1071
+ ] }),
1072
+ !disabled && !readOnly && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
1073
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1074
+ CmsButton,
1075
+ {
1076
+ variant: "ghost",
1077
+ size: "icon-sm",
1078
+ onClick: () => setShowPicker(true),
1079
+ title: "Change media",
1080
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Pencil, { className: "size-4" })
1081
+ }
1082
+ ),
1083
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1084
+ CmsButton,
1085
+ {
1086
+ variant: "ghost",
1087
+ size: "icon-sm",
1088
+ onClick: handleClear,
1089
+ title: "Remove media",
1090
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-4" })
1091
+ }
1092
+ )
1093
+ ] })
1094
+ ] });
1095
+ })() : /* @__PURE__ */ jsxRuntimeExports.jsxs(
1096
+ "button",
1097
+ {
1098
+ type: "button",
1099
+ className: cn(
1100
+ "flex w-full flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed border-border bg-muted/30 p-6 text-center transition-colors",
1101
+ "hover:border-primary/50 hover:bg-muted/50",
1102
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1103
+ disabled && "cursor-not-allowed opacity-50"
1104
+ ),
1105
+ onClick: () => setShowPicker(true),
1106
+ disabled: disabled || readOnly,
1107
+ children: [
1108
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex size-10 items-center justify-center rounded-full bg-muted", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Image, { className: "size-5 text-muted-foreground" }) }),
1109
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-medium text-foreground", children: placeholder }),
1110
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-muted-foreground", children: "Click to browse or upload media" })
1111
+ ]
1112
+ }
1113
+ ) }),
1114
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Dialog, { open: showPicker, onOpenChange: setShowPicker, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogContent, { className: "max-w-2xl", children: [
1115
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DialogHeader, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(DialogTitle, { children: "Select Media" }) }),
1116
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, { value: activeTab, onValueChange: setActiveTab, children: [
1117
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(TabsList, { className: "w-full", children: [
1118
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(TabsTrigger, { value: "browse", className: "flex-1 gap-2", children: [
1119
+ /* @__PURE__ */ jsxRuntimeExports.jsx(FolderOpen, { className: "size-4" }),
1120
+ "Browse Library"
1121
+ ] }),
1122
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(TabsTrigger, { value: "upload", className: "flex-1 gap-2", children: [
1123
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Upload, { className: "size-4" }),
1124
+ "Upload New"
1125
+ ] })
1126
+ ] }),
1127
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(TabsContent, { value: "browse", className: "mt-4 space-y-4", children: [
1128
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
1129
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex-1", children: [
1130
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { className: "absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" }),
1131
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1132
+ Input,
1133
+ {
1134
+ type: "search",
1135
+ placeholder: "Search files...",
1136
+ value: searchQuery,
1137
+ onChange: (e) => setSearchQuery(e.target.value),
1138
+ className: "pl-9"
1139
+ }
1140
+ )
1141
+ ] }),
1142
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
1143
+ Select,
1144
+ {
1145
+ value: typeFilter || "all",
1146
+ onValueChange: (v) => setTypeFilter(v === "all" ? "" : v),
1147
+ children: [
1148
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "w-[140px]", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "All Types" }) }),
1149
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
1150
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "all", children: "All Types" }),
1151
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "image", children: "Images" }),
1152
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "video", children: "Videos" }),
1153
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "audio", children: "Audio" }),
1154
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "document", children: "Documents" }),
1155
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "other", children: "Other" })
1156
+ ] })
1157
+ ]
1158
+ }
1159
+ )
1160
+ ] }),
1161
+ assetsResult === void 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center py-12", children: [
1162
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "size-6 animate-spin rounded-full border-2 border-muted border-t-primary" }),
1163
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "Loading media..." })
1164
+ ] }) : filteredAssets && filteredAssets.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid max-h-[300px] grid-cols-4 gap-2 overflow-y-auto", children: filteredAssets.map((asset) => {
1165
+ const assetMediaType = getMediaTypeFromMimeType(asset.mimeType);
1166
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1167
+ "button",
1168
+ {
1169
+ type: "button",
1170
+ className: cn(
1171
+ "group relative flex flex-col overflow-hidden rounded-lg border bg-card transition-all",
1172
+ "hover:border-primary hover:shadow-sm",
1173
+ value === asset._id && "border-primary ring-2 ring-primary/20"
1174
+ ),
1175
+ onClick: () => handleSelect(asset._id),
1176
+ children: [
1177
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "aspect-square bg-muted", children: assetMediaType === "image" && asset.url ? /* @__PURE__ */ jsxRuntimeExports.jsx(
1178
+ "img",
1179
+ {
1180
+ src: asset.url,
1181
+ alt: asset.title || asset.name,
1182
+ className: "size-full object-cover"
1183
+ }
1184
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex size-full items-center justify-center text-muted-foreground", children: getMediaTypeIcon(assetMediaType) }) }),
1185
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-1.5", children: [
1186
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1187
+ "p",
1188
+ {
1189
+ className: "truncate text-xs font-medium",
1190
+ title: asset.name,
1191
+ children: asset.name
1192
+ }
1193
+ ),
1194
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-muted-foreground", children: formatFileSize(asset.size ?? 0) })
1195
+ ] }),
1196
+ value === asset._id && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute right-1 top-1 flex size-5 items-center justify-center rounded-full bg-primary text-primary-foreground", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3" }) })
1197
+ ]
1198
+ },
1199
+ asset._id
1200
+ );
1201
+ }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx(
1202
+ CmsEmptyState,
1203
+ {
1204
+ icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Image, { className: "size-6" }),
1205
+ title: "No media found",
1206
+ description: "Upload some media to get started",
1207
+ action: {
1208
+ label: "Upload Media",
1209
+ onClick: () => setActiveTab("upload")
1210
+ }
1211
+ }
1212
+ )
1213
+ ] }),
1214
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "upload", className: "mt-4", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1215
+ UploadDropzone,
1216
+ {
1217
+ generateUploadUrl: api.media.generateUploadUrl,
1218
+ createAsset: api.media.createAsset,
1219
+ onUploadComplete: handleUploadComplete,
1220
+ allowedMimeTypes,
1221
+ maxFileSize: field.options?.maxFileSize,
1222
+ maxConcurrentUploads: 3
1223
+ }
1224
+ ) })
1225
+ ] }),
1226
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DialogFooter, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(CmsButton, { variant: "secondary", onClick: () => setShowPicker(false), children: "Cancel" }) })
1227
+ ] }) })
1228
+ ] });
1229
+ }
1230
+ function getEntryDisplayTitle(entry) {
1231
+ const titleFields = ["title", "name", "heading", "label"];
1232
+ for (const field of titleFields) {
1233
+ const value = entry.data?.[field];
1234
+ if (typeof value === "string" && value.trim()) {
1235
+ return value;
1236
+ }
1237
+ }
1238
+ return entry.slug || entry._id;
1239
+ }
1240
+ function ReferenceField({
1241
+ field,
1242
+ value,
1243
+ onChange,
1244
+ error,
1245
+ disabled = false,
1246
+ readOnly = false,
1247
+ className = "",
1248
+ id,
1249
+ placeholder = "Select content..."
1250
+ }) {
1251
+ const fieldId = id || `field-${field.name}`;
1252
+ const [showPicker, setShowPicker] = reactExports.useState(false);
1253
+ const [searchQuery, setSearchQuery] = reactExports.useState("");
1254
+ const [contentTypeFilter, setContentTypeFilter] = reactExports.useState("");
1255
+ const allowedContentTypes = field.options?.allowedContentTypes ?? [];
1256
+ const allowMultiple = field.options?.multiple ?? false;
1257
+ const selectedIds = reactExports.useMemo(() => {
1258
+ if (!value) return [];
1259
+ return Array.isArray(value) ? value : [value];
1260
+ }, [value]);
1261
+ const contentTypes = useQuery(api.contentTypes.list, {
1262
+ isActive: true,
1263
+ includeEntryCounts: false
1264
+ });
1265
+ const filteredContentTypes = reactExports.useMemo(() => {
1266
+ if (!contentTypes?.page) return [];
1267
+ if (allowedContentTypes.length === 0) return contentTypes.page;
1268
+ return contentTypes.page.filter(
1269
+ (ct) => allowedContentTypes.includes(ct.name) || allowedContentTypes.includes(ct._id)
1270
+ );
1271
+ }, [contentTypes?.page, allowedContentTypes]);
1272
+ const selectedEntry = useQuery(
1273
+ api.entries.get,
1274
+ selectedIds.length === 1 ? { id: selectedIds[0] } : "skip"
1275
+ );
1276
+ const selectedEntries = useQuery(
1277
+ api.entries.list,
1278
+ selectedIds.length > 1 ? {
1279
+ paginationOpts: { numItems: 100, cursor: null }
1280
+ } : "skip"
1281
+ );
1282
+ const multipleSelectedEntries = reactExports.useMemo(() => {
1283
+ if (!selectedEntries?.page || selectedIds.length <= 1) return [];
1284
+ return selectedEntries.page.filter((entry) => selectedIds.includes(entry._id));
1285
+ }, [selectedEntries?.page, selectedIds]);
1286
+ const entriesResult = useQuery(
1287
+ api.entries.list,
1288
+ showPicker ? {
1289
+ contentTypeId: contentTypeFilter || void 0,
1290
+ search: searchQuery || void 0,
1291
+ paginationOpts: { numItems: 50, cursor: null }
1292
+ } : "skip"
1293
+ );
1294
+ const filteredEntries = reactExports.useMemo(() => {
1295
+ if (!entriesResult?.page) return [];
1296
+ if (allowedContentTypes.length === 0) return entriesResult.page;
1297
+ const allowedIds = filteredContentTypes.map((ct) => ct._id);
1298
+ return entriesResult.page.filter(
1299
+ (entry) => allowedIds.includes(entry.contentTypeId)
1300
+ );
1301
+ }, [entriesResult?.page, allowedContentTypes, filteredContentTypes]);
1302
+ const getContentTypeName = reactExports.useCallback(
1303
+ (contentTypeId) => {
1304
+ const ct = contentTypes?.page?.find((c) => c._id === contentTypeId);
1305
+ return ct?.displayName || ct?.name || "Unknown";
1306
+ },
1307
+ [contentTypes?.page]
1308
+ );
1309
+ const handleSelect = reactExports.useCallback(
1310
+ (entryId) => {
1311
+ if (allowMultiple) {
1312
+ if (selectedIds.includes(entryId)) {
1313
+ const newIds = selectedIds.filter((id2) => id2 !== entryId);
1314
+ onChange(newIds.length > 0 ? newIds : null);
1315
+ } else {
1316
+ onChange([...selectedIds, entryId]);
1317
+ }
1318
+ } else {
1319
+ onChange(entryId);
1320
+ setShowPicker(false);
1321
+ }
1322
+ },
1323
+ [allowMultiple, selectedIds, onChange]
1324
+ );
1325
+ const handleRemove = reactExports.useCallback(
1326
+ (entryId) => {
1327
+ if (allowMultiple) {
1328
+ const newIds = selectedIds.filter((id2) => id2 !== entryId);
1329
+ onChange(newIds.length > 0 ? newIds : null);
1330
+ } else {
1331
+ onChange(null);
1332
+ }
1333
+ },
1334
+ [allowMultiple, selectedIds, onChange]
1335
+ );
1336
+ const handleClear = reactExports.useCallback(() => {
1337
+ onChange(null);
1338
+ }, [onChange]);
1339
+ const renderSelectedEntry = (entry, showRemove = true) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
1340
+ "div",
1341
+ {
1342
+ className: "flex items-center gap-3 rounded-lg border border-border bg-card p-3",
1343
+ children: [
1344
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex size-9 shrink-0 items-center justify-center rounded-md bg-muted", children: /* @__PURE__ */ jsxRuntimeExports.jsx(FileText, { className: "size-4 text-muted-foreground" }) }),
1345
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
1346
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "truncate text-sm font-medium text-foreground", children: getEntryDisplayTitle(entry) }),
1347
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
1348
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-muted-foreground", children: getContentTypeName(entry.contentTypeId) }),
1349
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CmsStatusBadge, { status: entry.status })
1350
+ ] })
1351
+ ] }),
1352
+ showRemove && !disabled && !readOnly && /* @__PURE__ */ jsxRuntimeExports.jsx(
1353
+ CmsButton,
1354
+ {
1355
+ variant: "ghost",
1356
+ size: "icon-sm",
1357
+ onClick: (e) => {
1358
+ e.stopPropagation();
1359
+ handleRemove(entry._id);
1360
+ },
1361
+ title: "Remove reference",
1362
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-4" })
1363
+ }
1364
+ )
1365
+ ]
1366
+ },
1367
+ entry._id
1368
+ );
1369
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(FieldWrapper, { field, error, className, id: fieldId, children: [
1370
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2", children: selectedIds.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
1371
+ selectedIds.length === 1 && selectedEntry && renderSelectedEntry(selectedEntry),
1372
+ selectedIds.length > 1 && multipleSelectedEntries.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2", children: multipleSelectedEntries.map((entry) => renderSelectedEntry(entry)) }),
1373
+ !disabled && !readOnly && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
1374
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
1375
+ CmsButton,
1376
+ {
1377
+ variant: "outline",
1378
+ size: "sm",
1379
+ onClick: () => setShowPicker(true),
1380
+ children: [
1381
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-4" }),
1382
+ allowMultiple ? "Add more" : "Change"
1383
+ ]
1384
+ }
1385
+ ),
1386
+ selectedIds.length > 1 && /* @__PURE__ */ jsxRuntimeExports.jsx(CmsButton, { variant: "ghost", size: "sm", onClick: handleClear, children: "Clear all" })
1387
+ ] })
1388
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
1389
+ "button",
1390
+ {
1391
+ type: "button",
1392
+ className: cn(
1393
+ "flex w-full flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed border-border bg-muted/30 p-6 text-center transition-colors",
1394
+ "hover:border-primary/50 hover:bg-muted/50",
1395
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1396
+ disabled && "cursor-not-allowed opacity-50"
1397
+ ),
1398
+ onClick: () => setShowPicker(true),
1399
+ disabled: disabled || readOnly,
1400
+ children: [
1401
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex size-10 items-center justify-center rounded-full bg-muted", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Link2, { className: "size-5 text-muted-foreground" }) }),
1402
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-medium text-foreground", children: placeholder }),
1403
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-muted-foreground", children: [
1404
+ "Click to select ",
1405
+ allowMultiple ? "content entries" : "a content entry"
1406
+ ] })
1407
+ ]
1408
+ }
1409
+ ) }),
1410
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Dialog, { open: showPicker, onOpenChange: setShowPicker, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogContent, { className: "max-w-lg p-0", children: [
1411
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DialogHeader, { className: "px-4 pt-4", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DialogTitle, { children: "Select Content" }) }),
1412
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Command, { className: "border-none", children: [
1413
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 border-b px-3 pb-2", children: [
1414
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1415
+ CommandInput,
1416
+ {
1417
+ placeholder: "Search entries...",
1418
+ value: searchQuery,
1419
+ onValueChange: setSearchQuery,
1420
+ className: "border-none shadow-none focus:ring-0"
1421
+ }
1422
+ ),
1423
+ filteredContentTypes.length > 1 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1424
+ Select,
1425
+ {
1426
+ value: contentTypeFilter || "all",
1427
+ onValueChange: (v) => setContentTypeFilter(v === "all" ? "" : v),
1428
+ children: [
1429
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "h-8 w-[120px] shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "All Types" }) }),
1430
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
1431
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "all", children: "All Types" }),
1432
+ filteredContentTypes.map((ct) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: ct._id, children: ct.displayName || ct.name }, ct._id))
1433
+ ] })
1434
+ ]
1435
+ }
1436
+ )
1437
+ ] }),
1438
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CommandList, { className: "max-h-[300px]", children: entriesResult === void 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center py-8", children: [
1439
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "size-5 animate-spin rounded-full border-2 border-muted border-t-primary" }),
1440
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "Loading..." })
1441
+ ] }) : filteredEntries.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx(CommandGroup, { children: filteredEntries.map((entry) => {
1442
+ const isSelected = selectedIds.includes(entry._id);
1443
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1444
+ CommandItem,
1445
+ {
1446
+ value: entry._id,
1447
+ onSelect: () => handleSelect(entry._id),
1448
+ className: "cursor-pointer",
1449
+ children: [
1450
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex size-8 shrink-0 items-center justify-center rounded bg-muted", children: /* @__PURE__ */ jsxRuntimeExports.jsx(FileText, { className: "size-4 text-muted-foreground" }) }),
1451
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
1452
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "truncate text-sm font-medium", children: getEntryDisplayTitle(entry) }),
1453
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
1454
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-muted-foreground", children: getContentTypeName(entry.contentTypeId) }),
1455
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1456
+ Badge,
1457
+ {
1458
+ variant: "secondary",
1459
+ className: cn(
1460
+ "px-1.5 py-0 text-[10px]",
1461
+ entry.status === "published" && "status-published",
1462
+ entry.status === "draft" && "status-draft",
1463
+ entry.status === "scheduled" && "status-scheduled",
1464
+ entry.status === "archived" && "status-archived"
1465
+ ),
1466
+ children: entry.status
1467
+ }
1468
+ )
1469
+ ] })
1470
+ ] }),
1471
+ isSelected && /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-4 shrink-0 text-primary" })
1472
+ ]
1473
+ },
1474
+ entry._id
1475
+ );
1476
+ }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx(CommandEmpty, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1477
+ CmsEmptyState,
1478
+ {
1479
+ icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Link2, { className: "size-6" }),
1480
+ title: "No content found",
1481
+ description: searchQuery ? "Try adjusting your search" : "Create some content entries first",
1482
+ className: "py-6"
1483
+ }
1484
+ ) }) })
1485
+ ] }),
1486
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogFooter, { className: "border-t px-4 py-3", children: [
1487
+ allowMultiple && selectedIds.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "mr-auto text-sm text-muted-foreground", children: [
1488
+ selectedIds.length,
1489
+ " selected"
1490
+ ] }),
1491
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CmsButton, { variant: "secondary", onClick: () => setShowPicker(false), children: allowMultiple ? "Done" : "Cancel" })
1492
+ ] })
1493
+ ] }) })
1494
+ ] });
1495
+ }
1496
+ function asTaxonomyId(id) {
1497
+ return id;
1498
+ }
1499
+ function asTaxonomyTermIds(ids) {
1500
+ return ids;
1501
+ }
1502
+ function TagField({
1503
+ field,
1504
+ value,
1505
+ onChange,
1506
+ error,
1507
+ disabled = false,
1508
+ readOnly = false,
1509
+ className = "",
1510
+ id,
1511
+ placeholder = "Add tags..."
1512
+ }) {
1513
+ const generatedId = reactExports.useId();
1514
+ const fieldId = id ?? `field-${field.name}-${generatedId}`;
1515
+ const taxonomyId = field.options?.taxonomyId;
1516
+ const allowCreate = field.options?.allowCreate ?? false;
1517
+ const maxTags = field.options?.maxTags;
1518
+ const minTags = field.options?.minTags;
1519
+ const [inputValue, setInputValue] = reactExports.useState("");
1520
+ const [showSuggestions, setShowSuggestions] = reactExports.useState(false);
1521
+ const [activeSuggestionIndex, setActiveSuggestionIndex] = reactExports.useState(0);
1522
+ const [isCreating, setIsCreating] = reactExports.useState(false);
1523
+ const inputRef = reactExports.useRef(null);
1524
+ const suggestionsRef = reactExports.useRef(null);
1525
+ const containerRef = reactExports.useRef(null);
1526
+ const suggestionsResult = useQuery(
1527
+ api.taxonomies.suggestTerms,
1528
+ taxonomyId ? {
1529
+ taxonomyId: asTaxonomyId(taxonomyId),
1530
+ query: inputValue,
1531
+ limit: 10,
1532
+ excludeIds: asTaxonomyTermIds(value || [])
1533
+ } : "skip"
1534
+ );
1535
+ const suggestions = suggestionsResult ?? [];
1536
+ const selectedTermsResult = useQuery(
1537
+ api.taxonomies.listTerms,
1538
+ taxonomyId && value && value.length > 0 ? {
1539
+ taxonomyId: asTaxonomyId(taxonomyId),
1540
+ paginationOpts: { numItems: 100, cursor: null }
1541
+ } : "skip"
1542
+ );
1543
+ const selectedTermsMap = /* @__PURE__ */ new Map();
1544
+ if (selectedTermsResult?.page) {
1545
+ for (const term of selectedTermsResult.page) {
1546
+ if (value?.includes(term._id)) {
1547
+ selectedTermsMap.set(term._id, term);
1548
+ }
1549
+ }
1550
+ }
1551
+ const createTermMutation = useMutation(api.taxonomies.createTerm);
1552
+ reactExports.useEffect(() => {
1553
+ function handleClickOutside(event) {
1554
+ if (containerRef.current && !containerRef.current.contains(event.target)) {
1555
+ setShowSuggestions(false);
1556
+ }
1557
+ }
1558
+ document.addEventListener("mousedown", handleClickOutside);
1559
+ return () => document.removeEventListener("mousedown", handleClickOutside);
1560
+ }, []);
1561
+ const addTag = reactExports.useCallback(
1562
+ (termId) => {
1563
+ if (!value?.includes(termId)) {
1564
+ if (maxTags && (value?.length ?? 0) >= maxTags) return;
1565
+ onChange([...value || [], termId]);
1566
+ }
1567
+ setInputValue("");
1568
+ setShowSuggestions(false);
1569
+ setActiveSuggestionIndex(0);
1570
+ inputRef.current?.focus();
1571
+ },
1572
+ [value, onChange, maxTags]
1573
+ );
1574
+ const removeTag = reactExports.useCallback(
1575
+ (termId) => {
1576
+ onChange((value || []).filter((id2) => id2 !== termId));
1577
+ },
1578
+ [value, onChange]
1579
+ );
1580
+ const createTag = reactExports.useCallback(async () => {
1581
+ if (!inputValue.trim() || !taxonomyId || !allowCreate) return;
1582
+ setIsCreating(true);
1583
+ try {
1584
+ const termId = await createTermMutation({
1585
+ taxonomyId: asTaxonomyId(taxonomyId),
1586
+ name: inputValue.trim()
1587
+ });
1588
+ addTag(termId);
1589
+ } catch (err) {
1590
+ console.error("Failed to create tag:", err);
1591
+ } finally {
1592
+ setIsCreating(false);
1593
+ }
1594
+ }, [inputValue, taxonomyId, allowCreate, createTermMutation, addTag]);
1595
+ const handleInputChange = (e) => {
1596
+ setInputValue(e.target.value);
1597
+ setShowSuggestions(true);
1598
+ setActiveSuggestionIndex(0);
1599
+ };
1600
+ const handleInputFocus = () => {
1601
+ setShowSuggestions(true);
1602
+ };
1603
+ const handleKeyDown = (e) => {
1604
+ if (disabled || readOnly) return;
1605
+ switch (e.key) {
1606
+ case "ArrowDown":
1607
+ e.preventDefault();
1608
+ setActiveSuggestionIndex(
1609
+ (prev) => prev < suggestions.length - 1 ? prev + 1 : prev
1610
+ );
1611
+ break;
1612
+ case "ArrowUp":
1613
+ e.preventDefault();
1614
+ setActiveSuggestionIndex((prev) => prev > 0 ? prev - 1 : 0);
1615
+ break;
1616
+ case "Enter":
1617
+ e.preventDefault();
1618
+ if (suggestions.length > 0 && activeSuggestionIndex < suggestions.length) {
1619
+ addTag(suggestions[activeSuggestionIndex]._id);
1620
+ } else if (inputValue.trim() && allowCreate) {
1621
+ createTag();
1622
+ }
1623
+ break;
1624
+ case "Escape":
1625
+ setShowSuggestions(false);
1626
+ setActiveSuggestionIndex(0);
1627
+ break;
1628
+ case "Backspace":
1629
+ if (inputValue === "" && value && value.length > 0) {
1630
+ removeTag(value[value.length - 1]);
1631
+ }
1632
+ break;
1633
+ case "Tab":
1634
+ if (showSuggestions && suggestions.length > 0) {
1635
+ e.preventDefault();
1636
+ addTag(suggestions[activeSuggestionIndex]._id);
1637
+ }
1638
+ break;
1639
+ }
1640
+ };
1641
+ reactExports.useEffect(() => {
1642
+ if (suggestionsRef.current && showSuggestions) {
1643
+ const activeElement = suggestionsRef.current.querySelector(
1644
+ `[data-index="${activeSuggestionIndex}"]`
1645
+ );
1646
+ if (activeElement) {
1647
+ activeElement.scrollIntoView({ block: "nearest" });
1648
+ }
1649
+ }
1650
+ }, [activeSuggestionIndex, showSuggestions]);
1651
+ const canAddMore = !maxTags || (value?.length ?? 0) < maxTags;
1652
+ const filteredSuggestions = suggestions.filter((term) => !value?.includes(term._id));
1653
+ const showCreateOption = allowCreate && inputValue.trim() && !filteredSuggestions.some(
1654
+ (s) => s.name.toLowerCase() === inputValue.trim().toLowerCase()
1655
+ );
1656
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(FieldWrapper, { field, error, className, id: fieldId, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
1657
+ "div",
1658
+ {
1659
+ ref: containerRef,
1660
+ className: cn(
1661
+ "relative rounded-md border border-input bg-background",
1662
+ error && "border-destructive",
1663
+ disabled && "opacity-50"
1664
+ ),
1665
+ children: [
1666
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-1.5 p-2", children: [
1667
+ (value || []).map((termId) => {
1668
+ const term = selectedTermsMap.get(termId);
1669
+ const tagName = term?.name ?? "Loading...";
1670
+ const tagColor = term?.color;
1671
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1672
+ Badge,
1673
+ {
1674
+ variant: "secondary",
1675
+ className: "gap-1 pr-1",
1676
+ style: tagColor ? { backgroundColor: tagColor, color: "#fff" } : void 0,
1677
+ children: [
1678
+ tagName,
1679
+ !disabled && !readOnly && /* @__PURE__ */ jsxRuntimeExports.jsx(
1680
+ Button,
1681
+ {
1682
+ type: "button",
1683
+ variant: "ghost",
1684
+ size: "icon",
1685
+ className: "size-4 hover:bg-transparent",
1686
+ onClick: () => removeTag(termId),
1687
+ "aria-label": `Remove ${tagName}`,
1688
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-3" })
1689
+ }
1690
+ )
1691
+ ]
1692
+ },
1693
+ termId
1694
+ );
1695
+ }),
1696
+ canAddMore && !disabled && !readOnly && /* @__PURE__ */ jsxRuntimeExports.jsx(
1697
+ Input,
1698
+ {
1699
+ ref: inputRef,
1700
+ type: "text",
1701
+ id: fieldId,
1702
+ className: "h-7 min-w-[120px] flex-1 border-0 bg-transparent px-1 shadow-none focus-visible:ring-0",
1703
+ value: inputValue,
1704
+ onChange: handleInputChange,
1705
+ onFocus: handleInputFocus,
1706
+ onKeyDown: handleKeyDown,
1707
+ placeholder: value?.length === 0 ? placeholder : "",
1708
+ disabled: disabled || isCreating,
1709
+ "aria-autocomplete": "list",
1710
+ "aria-controls": `${fieldId}-suggestions`,
1711
+ "aria-expanded": showSuggestions
1712
+ }
1713
+ )
1714
+ ] }),
1715
+ showSuggestions && (filteredSuggestions.length > 0 || showCreateOption) && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1716
+ "div",
1717
+ {
1718
+ ref: suggestionsRef,
1719
+ id: `${fieldId}-suggestions`,
1720
+ className: "absolute left-0 right-0 top-full z-50 mt-1 max-h-60 overflow-auto rounded-md border bg-popover shadow-lg",
1721
+ role: "listbox",
1722
+ children: [
1723
+ filteredSuggestions.map((term, index) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
1724
+ "div",
1725
+ {
1726
+ "data-index": index,
1727
+ className: cn(
1728
+ "flex cursor-pointer items-center gap-2 px-3 py-2 text-sm",
1729
+ index === activeSuggestionIndex && "bg-accent"
1730
+ ),
1731
+ onClick: () => addTag(term._id),
1732
+ role: "option",
1733
+ "aria-selected": index === activeSuggestionIndex,
1734
+ children: [
1735
+ term.color && /* @__PURE__ */ jsxRuntimeExports.jsx(
1736
+ "span",
1737
+ {
1738
+ className: "size-2.5 rounded-full",
1739
+ style: { backgroundColor: term.color }
1740
+ }
1741
+ ),
1742
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1", children: term.name }),
1743
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-muted-foreground", children: [
1744
+ term.usageCount,
1745
+ " uses"
1746
+ ] })
1747
+ ]
1748
+ },
1749
+ term._id
1750
+ )),
1751
+ showCreateOption && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1752
+ "div",
1753
+ {
1754
+ "data-index": filteredSuggestions.length,
1755
+ className: cn(
1756
+ "flex cursor-pointer items-center gap-2 px-3 py-2 text-sm",
1757
+ filteredSuggestions.length === activeSuggestionIndex && "bg-accent"
1758
+ ),
1759
+ onClick: createTag,
1760
+ role: "option",
1761
+ "aria-selected": filteredSuggestions.length === activeSuggestionIndex,
1762
+ children: [
1763
+ isCreating ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-4" }),
1764
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
1765
+ 'Create "',
1766
+ inputValue.trim(),
1767
+ '"'
1768
+ ] })
1769
+ ]
1770
+ }
1771
+ )
1772
+ ]
1773
+ }
1774
+ ),
1775
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-t px-3 py-1.5 text-xs text-muted-foreground", children: [
1776
+ value?.length ?? 0,
1777
+ " tag",
1778
+ (value?.length ?? 0) !== 1 ? "s" : "",
1779
+ minTags && (value?.length ?? 0) < minTags && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-amber-600", children: [
1780
+ " (minimum ",
1781
+ minTags,
1782
+ ")"
1783
+ ] }),
1784
+ maxTags && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
1785
+ " / ",
1786
+ maxTags,
1787
+ " max"
1788
+ ] })
1789
+ ] })
1790
+ ]
1791
+ }
1792
+ ) });
1793
+ }
1794
+ function CategoryField({
1795
+ field,
1796
+ value,
1797
+ onChange,
1798
+ error,
1799
+ disabled = false,
1800
+ readOnly = false,
1801
+ className = "",
1802
+ id,
1803
+ placeholder = "Select category..."
1804
+ }) {
1805
+ const fieldId = id || `field-${field.name}`;
1806
+ const taxonomyId = field.options?.taxonomyId;
1807
+ const allowMultiple = field.options?.allowMultiple ?? false;
1808
+ const [isOpen, setIsOpen] = reactExports.useState(false);
1809
+ const [expandedCategories, setExpandedCategories] = reactExports.useState(/* @__PURE__ */ new Set());
1810
+ const [highlightedId, setHighlightedId] = reactExports.useState(null);
1811
+ const containerRef = reactExports.useRef(null);
1812
+ const hierarchyResult = useQuery(
1813
+ api.taxonomies.getTermsHierarchy,
1814
+ taxonomyId ? { taxonomyId: asTaxonomyId(taxonomyId) } : "skip"
1815
+ );
1816
+ const categoryTree = hierarchyResult ?? [];
1817
+ const flattenTree = reactExports.useCallback(
1818
+ (nodes, result = []) => {
1819
+ for (const node of nodes) {
1820
+ result.push(node);
1821
+ if (expandedCategories.has(node._id) && node.children.length > 0) {
1822
+ flattenTree(node.children, result);
1823
+ }
1824
+ }
1825
+ return result;
1826
+ },
1827
+ [expandedCategories]
1828
+ );
1829
+ const flatCategories = flattenTree(categoryTree);
1830
+ const getSelectedDisplayText = reactExports.useCallback(() => {
1831
+ if (!value) return null;
1832
+ const ids = Array.isArray(value) ? value : [value];
1833
+ if (ids.length === 0) return null;
1834
+ const names = [];
1835
+ for (const categoryId of ids) {
1836
+ const cat = flatCategories.find((c) => c._id === categoryId);
1837
+ if (cat) {
1838
+ names.push(cat.name);
1839
+ }
1840
+ }
1841
+ if (names.length === 0) return "Loading...";
1842
+ if (names.length === 1) return names[0];
1843
+ return `${names.length} categories selected`;
1844
+ }, [value, flatCategories]);
1845
+ const toggleCategory = reactExports.useCallback(
1846
+ (categoryId) => {
1847
+ if (disabled || readOnly) return;
1848
+ if (allowMultiple) {
1849
+ const currentIds = Array.isArray(value) ? value : value ? [value] : [];
1850
+ if (currentIds.includes(categoryId)) {
1851
+ onChange(currentIds.filter((catId) => catId !== categoryId));
1852
+ } else {
1853
+ onChange([...currentIds, categoryId]);
1854
+ }
1855
+ } else {
1856
+ onChange(categoryId);
1857
+ setIsOpen(false);
1858
+ }
1859
+ },
1860
+ [disabled, readOnly, allowMultiple, value, onChange]
1861
+ );
1862
+ const toggleExpanded = reactExports.useCallback((categoryId, e) => {
1863
+ e.stopPropagation();
1864
+ setExpandedCategories((prev) => {
1865
+ const next = new Set(prev);
1866
+ if (next.has(categoryId)) {
1867
+ next.delete(categoryId);
1868
+ } else {
1869
+ next.add(categoryId);
1870
+ }
1871
+ return next;
1872
+ });
1873
+ }, []);
1874
+ const isSelected = reactExports.useCallback(
1875
+ (categoryId) => {
1876
+ if (!value) return false;
1877
+ const ids = Array.isArray(value) ? value : [value];
1878
+ return ids.includes(categoryId);
1879
+ },
1880
+ [value]
1881
+ );
1882
+ const handleKeyDown = (e) => {
1883
+ if (disabled || readOnly) return;
1884
+ switch (e.key) {
1885
+ case "Enter":
1886
+ case " ": {
1887
+ e.preventDefault();
1888
+ if (!isOpen) {
1889
+ setIsOpen(true);
1890
+ if (flatCategories.length > 0) {
1891
+ setHighlightedId(flatCategories[0]._id);
1892
+ }
1893
+ } else if (highlightedId) {
1894
+ toggleCategory(highlightedId);
1895
+ }
1896
+ break;
1897
+ }
1898
+ case "Escape": {
1899
+ setIsOpen(false);
1900
+ setHighlightedId(null);
1901
+ break;
1902
+ }
1903
+ case "ArrowDown": {
1904
+ e.preventDefault();
1905
+ if (!isOpen) {
1906
+ setIsOpen(true);
1907
+ if (flatCategories.length > 0) {
1908
+ setHighlightedId(flatCategories[0]._id);
1909
+ }
1910
+ } else {
1911
+ const currentIndex = flatCategories.findIndex((c) => c._id === highlightedId);
1912
+ if (currentIndex < flatCategories.length - 1) {
1913
+ setHighlightedId(flatCategories[currentIndex + 1]._id);
1914
+ }
1915
+ }
1916
+ break;
1917
+ }
1918
+ case "ArrowUp": {
1919
+ e.preventDefault();
1920
+ if (isOpen) {
1921
+ const currentIndex = flatCategories.findIndex((c) => c._id === highlightedId);
1922
+ if (currentIndex > 0) {
1923
+ setHighlightedId(flatCategories[currentIndex - 1]._id);
1924
+ }
1925
+ }
1926
+ break;
1927
+ }
1928
+ case "ArrowRight": {
1929
+ e.preventDefault();
1930
+ if (highlightedId) {
1931
+ const cat = flatCategories.find((c) => c._id === highlightedId);
1932
+ if (cat && cat.children.length > 0 && !expandedCategories.has(highlightedId)) {
1933
+ setExpandedCategories((prev) => /* @__PURE__ */ new Set([...prev, highlightedId]));
1934
+ }
1935
+ }
1936
+ break;
1937
+ }
1938
+ case "ArrowLeft": {
1939
+ e.preventDefault();
1940
+ if (highlightedId) {
1941
+ const cat = flatCategories.find((c) => c._id === highlightedId);
1942
+ if (cat) {
1943
+ if (expandedCategories.has(highlightedId)) {
1944
+ setExpandedCategories((prev) => {
1945
+ const next = new Set(prev);
1946
+ next.delete(highlightedId);
1947
+ return next;
1948
+ });
1949
+ } else if (cat.parentId) {
1950
+ setHighlightedId(cat.parentId);
1951
+ }
1952
+ }
1953
+ }
1954
+ break;
1955
+ }
1956
+ }
1957
+ };
1958
+ reactExports.useEffect(() => {
1959
+ if (containerRef.current && highlightedId) {
1960
+ const highlighted = containerRef.current.querySelector(
1961
+ `[data-category-id="${highlightedId}"]`
1962
+ );
1963
+ if (highlighted) {
1964
+ highlighted.scrollIntoView({ block: "nearest" });
1965
+ }
1966
+ }
1967
+ }, [highlightedId]);
1968
+ const renderCategory = (category, depth = 0) => {
1969
+ const hasChildren = category.children && category.children.length > 0;
1970
+ const isExpanded = expandedCategories.has(category._id);
1971
+ const isHighlighted = highlightedId === category._id;
1972
+ const selected = isSelected(category._id);
1973
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1974
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
1975
+ "div",
1976
+ {
1977
+ "data-category-id": category._id,
1978
+ className: cn(
1979
+ "flex cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors",
1980
+ "hover:bg-accent",
1981
+ isHighlighted && "bg-accent",
1982
+ selected && "font-medium"
1983
+ ),
1984
+ style: { paddingLeft: `${depth * 16 + 8}px` },
1985
+ onClick: () => toggleCategory(category._id),
1986
+ onMouseEnter: () => setHighlightedId(category._id),
1987
+ role: "option",
1988
+ "aria-selected": selected,
1989
+ children: [
1990
+ hasChildren ? /* @__PURE__ */ jsxRuntimeExports.jsx(
1991
+ "button",
1992
+ {
1993
+ type: "button",
1994
+ className: "flex size-4 items-center justify-center rounded hover:bg-muted",
1995
+ onClick: (e) => toggleExpanded(category._id, e),
1996
+ "aria-label": isExpanded ? "Collapse" : "Expand",
1997
+ children: isExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3" })
1998
+ }
1999
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "size-4" }),
2000
+ allowMultiple && /* @__PURE__ */ jsxRuntimeExports.jsx(
2001
+ Checkbox,
2002
+ {
2003
+ checked: selected,
2004
+ className: "pointer-events-none size-4"
2005
+ }
2006
+ ),
2007
+ category.color && /* @__PURE__ */ jsxRuntimeExports.jsx(
2008
+ "span",
2009
+ {
2010
+ className: "size-2.5 shrink-0 rounded-full",
2011
+ style: { backgroundColor: category.color }
2012
+ }
2013
+ ),
2014
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 truncate", children: category.name }),
2015
+ !allowMultiple && selected && /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-4 text-primary" })
2016
+ ]
2017
+ }
2018
+ ),
2019
+ hasChildren && isExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: category.children.map((child) => renderCategory(child, depth + 1)) })
2020
+ ] }, category._id);
2021
+ };
2022
+ const handleClear = (e) => {
2023
+ e.stopPropagation();
2024
+ onChange(allowMultiple ? [] : null);
2025
+ };
2026
+ const displayText = getSelectedDisplayText();
2027
+ const hasSelection = displayText !== null;
2028
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(FieldWrapper, { field, error, className, id: fieldId, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ref: containerRef, children: [
2029
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [
2030
+ /* @__PURE__ */ jsxRuntimeExports.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
2031
+ "button",
2032
+ {
2033
+ type: "button",
2034
+ id: fieldId,
2035
+ className: cn(
2036
+ "flex w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background",
2037
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2038
+ "disabled:cursor-not-allowed disabled:opacity-50",
2039
+ error && "border-destructive",
2040
+ !hasSelection && "text-muted-foreground"
2041
+ ),
2042
+ onKeyDown: handleKeyDown,
2043
+ disabled,
2044
+ "aria-haspopup": "listbox",
2045
+ "aria-expanded": isOpen,
2046
+ "data-testid": "category-trigger",
2047
+ children: [
2048
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: displayText || placeholder }),
2049
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
2050
+ hasSelection && !disabled && !readOnly && /* @__PURE__ */ jsxRuntimeExports.jsx(
2051
+ "button",
2052
+ {
2053
+ type: "button",
2054
+ className: "rounded p-0.5 hover:bg-muted",
2055
+ onClick: handleClear,
2056
+ "aria-label": "Clear selection",
2057
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-3" })
2058
+ }
2059
+ ),
2060
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2061
+ ChevronDown,
2062
+ {
2063
+ className: cn(
2064
+ "size-4 text-muted-foreground transition-transform",
2065
+ isOpen && "rotate-180"
2066
+ )
2067
+ }
2068
+ )
2069
+ ] })
2070
+ ]
2071
+ }
2072
+ ) }),
2073
+ /* @__PURE__ */ jsxRuntimeExports.jsx(PopoverContent, { className: "w-[var(--radix-popover-trigger-width)] p-0", align: "start", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, { className: "max-h-[280px]", children: categoryTree.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center py-6 text-center", children: [
2074
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Tag, { className: "mb-2 size-6 text-muted-foreground" }),
2075
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground", children: "No categories available" })
2076
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-1", role: "listbox", "aria-multiselectable": allowMultiple, children: categoryTree.map((category) => renderCategory(category)) }) }) })
2077
+ ] }),
2078
+ allowMultiple && Array.isArray(value) && value.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 flex flex-wrap gap-1", children: value.map((catId) => {
2079
+ const cat = flatCategories.find((c) => c._id === catId);
2080
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
2081
+ Badge,
2082
+ {
2083
+ variant: "secondary",
2084
+ className: "gap-1 pr-1",
2085
+ children: [
2086
+ cat?.color && /* @__PURE__ */ jsxRuntimeExports.jsx(
2087
+ "span",
2088
+ {
2089
+ className: "size-2 rounded-full",
2090
+ style: { backgroundColor: cat.color }
2091
+ }
2092
+ ),
2093
+ cat?.name ?? "Loading...",
2094
+ !disabled && !readOnly && /* @__PURE__ */ jsxRuntimeExports.jsx(
2095
+ "button",
2096
+ {
2097
+ type: "button",
2098
+ className: "ml-0.5 rounded-full p-0.5 hover:bg-muted",
2099
+ onClick: () => toggleCategory(catId),
2100
+ "aria-label": `Remove ${cat?.name}`,
2101
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-3" })
2102
+ }
2103
+ )
2104
+ ]
2105
+ },
2106
+ catId
2107
+ );
2108
+ }) })
2109
+ ] }) });
2110
+ }
2111
+ function DefaultFieldRenderer({
2112
+ field,
2113
+ value,
2114
+ onChange,
2115
+ error,
2116
+ disabled,
2117
+ readOnly,
2118
+ className
2119
+ }) {
2120
+ const generatedId = reactExports.useId();
2121
+ const id = `field-${generatedId}`;
2122
+ const stringValue = value === void 0 || value === null ? "" : JSON.stringify(value, null, 2);
2123
+ const handleChange = (newValue) => {
2124
+ if (readOnly) return;
2125
+ if (newValue.trim() === "") {
2126
+ onChange(null);
2127
+ return;
2128
+ }
2129
+ try {
2130
+ const parsed = JSON.parse(newValue);
2131
+ onChange(parsed);
2132
+ } catch {
2133
+ onChange(newValue);
2134
+ }
2135
+ };
2136
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2137
+ FieldWrapper,
2138
+ {
2139
+ field,
2140
+ error,
2141
+ id,
2142
+ className,
2143
+ customLabel: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
2144
+ field.label,
2145
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-2 rounded bg-muted px-1.5 py-0.5 text-xs font-normal text-muted-foreground", children: field.type })
2146
+ ] }),
2147
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
2148
+ Textarea,
2149
+ {
2150
+ id,
2151
+ value: stringValue,
2152
+ onChange: (e) => handleChange(e.target.value),
2153
+ disabled,
2154
+ readOnly,
2155
+ className: "font-mono text-sm",
2156
+ rows: 6,
2157
+ placeholder: `Enter ${field.type} value as JSON...`,
2158
+ "aria-invalid": !!error,
2159
+ "aria-describedby": error ? `${id}-error` : field.description ? `${id}-desc` : void 0
2160
+ }
2161
+ )
2162
+ }
2163
+ );
2164
+ }
2165
+ const rendererRegistry = /* @__PURE__ */ new Map();
2166
+ function getFieldRenderer(fieldType) {
2167
+ return rendererRegistry.get(fieldType);
2168
+ }
2169
+ function FieldRenderer({
2170
+ field,
2171
+ value,
2172
+ onChange,
2173
+ error,
2174
+ disabled = false,
2175
+ readOnly = false,
2176
+ className = ""
2177
+ }) {
2178
+ const commonProps = {
2179
+ field,
2180
+ error,
2181
+ disabled,
2182
+ readOnly,
2183
+ className
2184
+ };
2185
+ switch (field.type) {
2186
+ case "text":
2187
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2188
+ TextField,
2189
+ {
2190
+ ...commonProps,
2191
+ value: value ?? "",
2192
+ onChange
2193
+ }
2194
+ );
2195
+ case "richText":
2196
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2197
+ RichTextField,
2198
+ {
2199
+ ...commonProps,
2200
+ value: value ?? "",
2201
+ onChange
2202
+ }
2203
+ );
2204
+ case "number":
2205
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2206
+ NumberField,
2207
+ {
2208
+ ...commonProps,
2209
+ value: value ?? null,
2210
+ onChange
2211
+ }
2212
+ );
2213
+ case "boolean":
2214
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2215
+ BooleanField,
2216
+ {
2217
+ ...commonProps,
2218
+ value: value ?? false,
2219
+ onChange
2220
+ }
2221
+ );
2222
+ case "date":
2223
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2224
+ DateField,
2225
+ {
2226
+ ...commonProps,
2227
+ value: value ?? null,
2228
+ onChange,
2229
+ includeTime: false
2230
+ }
2231
+ );
2232
+ case "datetime":
2233
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2234
+ DateField,
2235
+ {
2236
+ ...commonProps,
2237
+ value: value ?? null,
2238
+ onChange,
2239
+ includeTime: true
2240
+ }
2241
+ );
2242
+ case "select":
2243
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2244
+ SelectField,
2245
+ {
2246
+ ...commonProps,
2247
+ value: value ?? "",
2248
+ onChange
2249
+ }
2250
+ );
2251
+ case "multiSelect":
2252
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2253
+ MultiSelectField,
2254
+ {
2255
+ ...commonProps,
2256
+ value: value ?? [],
2257
+ onChange
2258
+ }
2259
+ );
2260
+ case "json":
2261
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(JsonField, { ...commonProps, value: value ?? null, onChange });
2262
+ case "media":
2263
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2264
+ MediaField,
2265
+ {
2266
+ ...commonProps,
2267
+ value: value ?? null,
2268
+ onChange
2269
+ }
2270
+ );
2271
+ case "reference":
2272
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2273
+ ReferenceField,
2274
+ {
2275
+ ...commonProps,
2276
+ value: value ?? null,
2277
+ onChange
2278
+ }
2279
+ );
2280
+ case "tags":
2281
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2282
+ TagField,
2283
+ {
2284
+ ...commonProps,
2285
+ value: value ?? [],
2286
+ onChange
2287
+ }
2288
+ );
2289
+ case "category":
2290
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2291
+ CategoryField,
2292
+ {
2293
+ ...commonProps,
2294
+ value: value ?? null,
2295
+ onChange
2296
+ }
2297
+ );
2298
+ default: {
2299
+ const CustomRenderer = getFieldRenderer(field.type);
2300
+ if (CustomRenderer) {
2301
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(CustomRenderer, { ...commonProps, value, onChange });
2302
+ }
2303
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(DefaultFieldRenderer, { ...commonProps, value, onChange });
2304
+ }
2305
+ }
2306
+ }
2307
+ function VersionCompare({
2308
+ entryId,
2309
+ fromVersion,
2310
+ toVersion,
2311
+ onClose,
2312
+ onRollback
2313
+ }) {
2314
+ const comparisonQuery = useQuery(api.versions.compare, {
2315
+ entryId,
2316
+ fromVersionNumber: fromVersion,
2317
+ toVersionNumber: toVersion
2318
+ });
2319
+ const isLoading = comparisonQuery === void 0;
2320
+ const comparison = comparisonQuery;
2321
+ const formatValue = (value) => {
2322
+ if (value === null || value === void 0) {
2323
+ return "(empty)";
2324
+ }
2325
+ if (typeof value === "object") {
2326
+ return JSON.stringify(value, null, 2);
2327
+ }
2328
+ return String(value);
2329
+ };
2330
+ const getChangeIcon = (changeType) => {
2331
+ switch (changeType) {
2332
+ case "added":
2333
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-3" });
2334
+ case "removed":
2335
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Minus, { className: "size-3" });
2336
+ case "modified":
2337
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "size-3" });
2338
+ default:
2339
+ return null;
2340
+ }
2341
+ };
2342
+ const getChangeStyles = (changeType) => {
2343
+ switch (changeType) {
2344
+ case "added":
2345
+ return "border-emerald-200 bg-emerald-50";
2346
+ case "removed":
2347
+ return "border-red-200 bg-red-50";
2348
+ case "modified":
2349
+ return "border-amber-200 bg-amber-50";
2350
+ default:
2351
+ return "border-border bg-card";
2352
+ }
2353
+ };
2354
+ const getChangeIconStyles = (changeType) => {
2355
+ switch (changeType) {
2356
+ case "added":
2357
+ return "bg-emerald-100 text-emerald-700";
2358
+ case "removed":
2359
+ return "bg-red-100 text-red-700";
2360
+ case "modified":
2361
+ return "bg-amber-100 text-amber-700";
2362
+ default:
2363
+ return "bg-muted text-muted-foreground";
2364
+ }
2365
+ };
2366
+ const formatDate = (timestamp) => {
2367
+ return new Date(timestamp).toLocaleString(void 0, {
2368
+ year: "numeric",
2369
+ month: "short",
2370
+ day: "numeric",
2371
+ hour: "2-digit",
2372
+ minute: "2-digit"
2373
+ });
2374
+ };
2375
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-full flex-col bg-background", children: [
2376
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between border-b px-4 py-3", children: [
2377
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
2378
+ /* @__PURE__ */ jsxRuntimeExports.jsx(GitCompare, { className: "size-4 text-muted-foreground" }),
2379
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "font-semibold", children: [
2380
+ "Comparing v",
2381
+ fromVersion,
2382
+ " → v",
2383
+ toVersion
2384
+ ] })
2385
+ ] }),
2386
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2387
+ "button",
2388
+ {
2389
+ type: "button",
2390
+ onClick: onClose,
2391
+ className: "rounded p-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
2392
+ "aria-label": "Close comparison",
2393
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-4" })
2394
+ }
2395
+ )
2396
+ ] }),
2397
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-4", children: isLoading ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center py-8", children: [
2398
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "size-6 animate-spin rounded-full border-2 border-muted border-t-primary" }),
2399
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "Loading comparison..." })
2400
+ ] }) : !comparison ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800", children: "Could not load version comparison" }) : !comparison.hasChanges ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "py-8 text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground", children: "No differences found between these versions" }) }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
2401
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-4 flex items-center justify-center gap-3 rounded-lg border bg-muted/30 p-3", children: [
2402
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center", children: [
2403
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Badge, { variant: "secondary", className: "font-mono", children: [
2404
+ "v",
2405
+ comparison.fromVersion.versionNumber
2406
+ ] }),
2407
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: formatDate(comparison.fromVersion.createdAt) })
2408
+ ] }),
2409
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowRight, { className: "size-4 text-muted-foreground" }),
2410
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center", children: [
2411
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Badge, { variant: "secondary", className: "font-mono", children: [
2412
+ "v",
2413
+ comparison.toVersion.versionNumber
2414
+ ] }),
2415
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: formatDate(comparison.toVersion.createdAt) })
2416
+ ] })
2417
+ ] }),
2418
+ comparison.changeSummary && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mb-4 text-sm text-muted-foreground", children: comparison.changeSummary }),
2419
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-3", children: comparison.fieldDiffs.map((change, index) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
2420
+ "div",
2421
+ {
2422
+ className: cn(
2423
+ "rounded-lg border p-3",
2424
+ getChangeStyles(change.changeType)
2425
+ ),
2426
+ children: [
2427
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
2428
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2429
+ "span",
2430
+ {
2431
+ className: cn(
2432
+ "flex size-5 items-center justify-center rounded",
2433
+ getChangeIconStyles(change.changeType)
2434
+ ),
2435
+ children: getChangeIcon(change.changeType)
2436
+ }
2437
+ ),
2438
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium text-foreground", children: change.field }),
2439
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: "text-xs capitalize", children: change.changeType })
2440
+ ] }),
2441
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 space-y-2", children: [
2442
+ change.changeType !== "added" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded border border-red-200 bg-white p-2", children: [
2443
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mb-1 text-xs font-medium text-red-700", children: "Before:" }),
2444
+ /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "overflow-x-auto whitespace-pre-wrap break-words font-mono text-xs text-red-900", children: formatValue(change.fromValue) })
2445
+ ] }),
2446
+ change.changeType !== "removed" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded border border-emerald-200 bg-white p-2", children: [
2447
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mb-1 text-xs font-medium text-emerald-700", children: "After:" }),
2448
+ /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "overflow-x-auto whitespace-pre-wrap break-words font-mono text-xs text-emerald-900", children: formatValue(change.toValue) })
2449
+ ] })
2450
+ ] })
2451
+ ]
2452
+ },
2453
+ index
2454
+ )) })
2455
+ ] }) }) }),
2456
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between border-t px-4 py-3", children: [
2457
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CmsButton, { variant: "outline", onClick: onClose, children: "Back to History" }),
2458
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(CmsButton, { variant: "ghost", onClick: () => onRollback(fromVersion), children: [
2459
+ "Rollback to v",
2460
+ fromVersion
2461
+ ] })
2462
+ ] })
2463
+ ] });
2464
+ }
2465
+ function VersionRollbackModal({
2466
+ targetVersion,
2467
+ currentVersion,
2468
+ isLoading,
2469
+ error,
2470
+ onConfirm,
2471
+ onCancel
2472
+ }) {
2473
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2474
+ CmsDialog,
2475
+ {
2476
+ open: true,
2477
+ onOpenChange: (open) => !open && !isLoading && onCancel(),
2478
+ title: "Confirm Rollback",
2479
+ size: "sm",
2480
+ footer: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
2481
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CmsButton, { variant: "outline", onClick: onCancel, disabled: isLoading, children: "Cancel" }),
2482
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(CmsButton, { variant: "warning", onClick: onConfirm, loading: isLoading, children: [
2483
+ "Rollback to v",
2484
+ targetVersion
2485
+ ] })
2486
+ ] }),
2487
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
2488
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-sm text-muted-foreground", children: [
2489
+ "You are about to rollback from",
2490
+ " ",
2491
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-semibold text-foreground", children: [
2492
+ "version ",
2493
+ currentVersion
2494
+ ] }),
2495
+ " ",
2496
+ "to",
2497
+ " ",
2498
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-semibold text-foreground", children: [
2499
+ "version ",
2500
+ targetVersion
2501
+ ] }),
2502
+ "."
2503
+ ] }),
2504
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded-lg border border-amber-200 bg-amber-50 p-3", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
2505
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "mt-0.5 size-4 shrink-0 text-amber-600" }),
2506
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
2507
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-medium text-amber-800", children: "This action will:" }),
2508
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("ul", { className: "space-y-1 text-sm text-amber-700", children: [
2509
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { children: [
2510
+ "• Create a new version with the content from version",
2511
+ " ",
2512
+ targetVersion
2513
+ ] }),
2514
+ /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: "• The current version will be preserved in history" }),
2515
+ /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: "• Any unsaved changes will be lost" })
2516
+ ] })
2517
+ ] })
2518
+ ] }) }),
2519
+ error && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-800", children: [
2520
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium", children: "Error:" }),
2521
+ " ",
2522
+ error
2523
+ ] })
2524
+ ] })
2525
+ }
2526
+ );
2527
+ }
2528
+ function VersionHistory({
2529
+ entryId,
2530
+ currentVersion,
2531
+ onRollbackComplete,
2532
+ onClose
2533
+ }) {
2534
+ const [selectedVersions, setSelectedVersions] = reactExports.useState(null);
2535
+ const [rollbackTarget, setRollbackTarget] = reactExports.useState(null);
2536
+ const [isRollingBack, setIsRollingBack] = reactExports.useState(false);
2537
+ const [rollbackError, setRollbackError] = reactExports.useState(null);
2538
+ const [rollbackSuccess, setRollbackSuccess] = reactExports.useState(false);
2539
+ const versionsQuery = useQuery(api.versions.getHistory, {
2540
+ entryId,
2541
+ paginationOpts: { numItems: 50, cursor: null }
2542
+ });
2543
+ const rollbackMutation = useMutation(api.versions.rollback);
2544
+ const versions = versionsQuery?.page ?? [];
2545
+ const isLoading = versionsQuery === void 0;
2546
+ const handleCompare = reactExports.useCallback(
2547
+ (fromVersion, toVersion) => {
2548
+ setSelectedVersions([fromVersion, toVersion]);
2549
+ },
2550
+ []
2551
+ );
2552
+ const handleRollback = reactExports.useCallback((versionNumber) => {
2553
+ setRollbackTarget(versionNumber);
2554
+ setRollbackError(null);
2555
+ }, []);
2556
+ const handleConfirmRollback = reactExports.useCallback(async () => {
2557
+ if (rollbackTarget === null) return;
2558
+ setIsRollingBack(true);
2559
+ setRollbackError(null);
2560
+ try {
2561
+ await rollbackMutation({
2562
+ entryId,
2563
+ versionNumber: rollbackTarget
2564
+ });
2565
+ setRollbackTarget(null);
2566
+ setRollbackSuccess(true);
2567
+ setTimeout(() => {
2568
+ setRollbackSuccess(false);
2569
+ onRollbackComplete?.();
2570
+ }, 1500);
2571
+ } catch (error) {
2572
+ const message = error instanceof Error ? error.message : "Failed to rollback";
2573
+ setRollbackError(message);
2574
+ } finally {
2575
+ setIsRollingBack(false);
2576
+ }
2577
+ }, [entryId, rollbackTarget, rollbackMutation, onRollbackComplete]);
2578
+ const formatDate = (timestamp) => {
2579
+ return new Date(timestamp).toLocaleString(void 0, {
2580
+ year: "numeric",
2581
+ month: "short",
2582
+ day: "numeric",
2583
+ hour: "2-digit",
2584
+ minute: "2-digit"
2585
+ });
2586
+ };
2587
+ if (selectedVersions) {
2588
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-full flex-col border-l bg-background", children: [
2589
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2590
+ VersionCompare,
2591
+ {
2592
+ entryId,
2593
+ fromVersion: selectedVersions[0],
2594
+ toVersion: selectedVersions[1],
2595
+ onClose: () => setSelectedVersions(null),
2596
+ onRollback: (version) => {
2597
+ setSelectedVersions(null);
2598
+ handleRollback(version);
2599
+ }
2600
+ }
2601
+ ),
2602
+ rollbackTarget !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(
2603
+ VersionRollbackModal,
2604
+ {
2605
+ targetVersion: rollbackTarget,
2606
+ currentVersion,
2607
+ isLoading: isRollingBack,
2608
+ error: rollbackError,
2609
+ onConfirm: handleConfirmRollback,
2610
+ onCancel: () => {
2611
+ setRollbackTarget(null);
2612
+ setRollbackError(null);
2613
+ }
2614
+ }
2615
+ )
2616
+ ] });
2617
+ }
2618
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex h-full flex-col border-l bg-background", children: [
2619
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between border-b px-4 py-3", children: [
2620
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
2621
+ /* @__PURE__ */ jsxRuntimeExports.jsx(History, { className: "size-4 text-muted-foreground" }),
2622
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-semibold", children: "Version History" })
2623
+ ] }),
2624
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2625
+ "button",
2626
+ {
2627
+ type: "button",
2628
+ onClick: onClose,
2629
+ className: "rounded p-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
2630
+ "aria-label": "Close version history",
2631
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-4" })
2632
+ }
2633
+ )
2634
+ ] }),
2635
+ rollbackSuccess && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 border-b bg-emerald-50 px-4 py-2 text-sm text-emerald-800", children: [
2636
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { className: "size-4" }),
2637
+ "Successfully rolled back to previous version"
2638
+ ] }),
2639
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-4", children: isLoading ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center py-8", children: [
2640
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "size-6 animate-spin rounded-full border-2 border-muted border-t-primary" }),
2641
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "Loading versions..." })
2642
+ ] }) : versions.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "py-8 text-center text-sm text-muted-foreground", children: "Save changes to start building version history" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-3", children: versions.map((version, index) => {
2643
+ const isCurrent = version.versionNumber === currentVersion;
2644
+ const prevVersion = versions[index + 1];
2645
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
2646
+ "div",
2647
+ {
2648
+ className: cn(
2649
+ "rounded-lg border p-3 transition-colors",
2650
+ isCurrent ? "border-primary/50 bg-primary/5" : "bg-card"
2651
+ ),
2652
+ children: [
2653
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
2654
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
2655
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Badge, { variant: "secondary", className: "font-mono", children: [
2656
+ "v",
2657
+ version.versionNumber
2658
+ ] }),
2659
+ isCurrent && /* @__PURE__ */ jsxRuntimeExports.jsx(
2660
+ Badge,
2661
+ {
2662
+ variant: "outline",
2663
+ className: "border-primary/50 text-primary",
2664
+ children: "Current"
2665
+ }
2666
+ ),
2667
+ version.wasPublished && /* @__PURE__ */ jsxRuntimeExports.jsx(
2668
+ Badge,
2669
+ {
2670
+ variant: "outline",
2671
+ className: "border-emerald-500/50 text-emerald-600",
2672
+ children: "Published"
2673
+ }
2674
+ )
2675
+ ] }),
2676
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2677
+ CmsStatusBadge,
2678
+ {
2679
+ status: version.status
2680
+ }
2681
+ )
2682
+ ] }),
2683
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 text-xs text-muted-foreground", children: [
2684
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: formatDate(version._creationTime) }),
2685
+ version.createdBy && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ml-2", children: [
2686
+ "by ",
2687
+ version.createdBy
2688
+ ] })
2689
+ ] }),
2690
+ version.changeDescription && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: version.changeDescription }),
2691
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-3 flex gap-2", children: [
2692
+ prevVersion && /* @__PURE__ */ jsxRuntimeExports.jsxs(
2693
+ CmsButton,
2694
+ {
2695
+ variant: "outline",
2696
+ size: "sm",
2697
+ onClick: () => handleCompare(
2698
+ prevVersion.versionNumber,
2699
+ version.versionNumber
2700
+ ),
2701
+ children: [
2702
+ "Compare with v",
2703
+ prevVersion.versionNumber
2704
+ ]
2705
+ }
2706
+ ),
2707
+ !isCurrent && /* @__PURE__ */ jsxRuntimeExports.jsx(
2708
+ CmsButton,
2709
+ {
2710
+ variant: "ghost",
2711
+ size: "sm",
2712
+ onClick: () => handleRollback(version.versionNumber),
2713
+ children: "Rollback"
2714
+ }
2715
+ )
2716
+ ] })
2717
+ ]
2718
+ },
2719
+ version._id
2720
+ );
2721
+ }) }) }) }),
2722
+ rollbackTarget !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(
2723
+ VersionRollbackModal,
2724
+ {
2725
+ targetVersion: rollbackTarget,
2726
+ currentVersion,
2727
+ isLoading: isRollingBack,
2728
+ error: rollbackError,
2729
+ onConfirm: handleConfirmRollback,
2730
+ onCancel: () => {
2731
+ setRollbackTarget(null);
2732
+ setRollbackError(null);
2733
+ }
2734
+ }
2735
+ )
2736
+ ] });
2737
+ }
2738
+ function parseServerError(error) {
2739
+ const result = {
2740
+ fieldErrors: {},
2741
+ generalError: null
2742
+ };
2743
+ if (!error) {
2744
+ return result;
2745
+ }
2746
+ const message = error instanceof Error ? error.message : String(error);
2747
+ try {
2748
+ const jsonMatch = message.match(/\{[\s\S]*\}/);
2749
+ if (jsonMatch) {
2750
+ const parsed = JSON.parse(jsonMatch[0]);
2751
+ if (Array.isArray(parsed.errors)) {
2752
+ for (const err of parsed.errors) {
2753
+ if (err.field && err.message) {
2754
+ result.fieldErrors[err.field] = {
2755
+ message: err.message,
2756
+ code: err.code ?? "SERVER_ERROR"
2757
+ };
2758
+ }
2759
+ }
2760
+ if (Object.keys(result.fieldErrors).length > 0) {
2761
+ return result;
2762
+ }
2763
+ }
2764
+ if (parsed.field && parsed.message) {
2765
+ result.fieldErrors[parsed.field] = {
2766
+ message: parsed.message,
2767
+ code: parsed.code ?? "SERVER_ERROR"
2768
+ };
2769
+ return result;
2770
+ }
2771
+ if (typeof parsed === "object" && !Array.isArray(parsed)) {
2772
+ let hasFieldErrors = false;
2773
+ for (const [key, value] of Object.entries(parsed)) {
2774
+ if (typeof value === "string" && key !== "message" && key !== "code") {
2775
+ result.fieldErrors[key] = {
2776
+ message: value,
2777
+ code: "SERVER_ERROR"
2778
+ };
2779
+ hasFieldErrors = true;
2780
+ }
2781
+ }
2782
+ if (hasFieldErrors) {
2783
+ return result;
2784
+ }
2785
+ }
2786
+ }
2787
+ } catch {
2788
+ }
2789
+ const validationPattern = /(?:Content validation failed|Validation error|Validation failed):\s*(.+)/i;
2790
+ const validationMatch = message.match(validationPattern);
2791
+ if (validationMatch) {
2792
+ const errorPart = validationMatch[1].trim();
2793
+ const fieldErrorParts = errorPart.split(/;\s*/);
2794
+ for (const part of fieldErrorParts) {
2795
+ const colonIndex = part.indexOf(":");
2796
+ if (colonIndex > 0 && colonIndex < part.length - 1) {
2797
+ const fieldName = part.substring(0, colonIndex).trim();
2798
+ const errorMsg = part.substring(colonIndex + 1).trim();
2799
+ if (fieldName && !fieldName.includes(" ") && fieldName.length <= 50 && errorMsg) {
2800
+ result.fieldErrors[fieldName] = {
2801
+ message: errorMsg,
2802
+ code: "SERVER_ERROR"
2803
+ };
2804
+ }
2805
+ }
2806
+ }
2807
+ if (Object.keys(result.fieldErrors).length > 0) {
2808
+ return result;
2809
+ }
2810
+ }
2811
+ result.generalError = message;
2812
+ return result;
2813
+ }
2814
+ function isRetryableError(error) {
2815
+ if (!error) return false;
2816
+ const message = (error instanceof Error ? error.message : String(error)).toLowerCase();
2817
+ const retryablePatterns = [
2818
+ "network",
2819
+ "timeout",
2820
+ "timed out",
2821
+ "connection",
2822
+ "fetch failed",
2823
+ "failed to fetch",
2824
+ "offline",
2825
+ "unavailable",
2826
+ "rate limit",
2827
+ "too many requests",
2828
+ "503",
2829
+ "504",
2830
+ "429"
2831
+ ];
2832
+ return retryablePatterns.some((pattern) => message.includes(pattern));
2833
+ }
2834
+ function formatDateTimeLocal(timestamp) {
2835
+ const date = new Date(timestamp);
2836
+ const year = date.getFullYear();
2837
+ const month = String(date.getMonth() + 1).padStart(2, "0");
2838
+ const day = String(date.getDate()).padStart(2, "0");
2839
+ const hours = String(date.getHours()).padStart(2, "0");
2840
+ const minutes = String(date.getMinutes()).padStart(2, "0");
2841
+ return `${year}-${month}-${day}T${hours}:${minutes}`;
2842
+ }
2843
+ function parseDateTimeLocal(value) {
2844
+ return new Date(value).getTime();
2845
+ }
2846
+ function formatDateOnly(timestamp) {
2847
+ const date = new Date(timestamp);
2848
+ const year = date.getFullYear();
2849
+ const month = String(date.getMonth() + 1).padStart(2, "0");
2850
+ const day = String(date.getDate()).padStart(2, "0");
2851
+ return `${year}-${month}-${day}`;
2852
+ }
2853
+ function transformDataForUI(data, fields) {
2854
+ const transformed = { ...data };
2855
+ for (const field of fields) {
2856
+ const value = data[field.name];
2857
+ if ((field.type === "date" || field.type === "datetime") && typeof value === "number") {
2858
+ transformed[field.name] = field.type === "datetime" ? formatDateTimeLocal(value) : formatDateOnly(value);
2859
+ }
2860
+ }
2861
+ return transformed;
2862
+ }
2863
+ function transformDataForBackend(data, fields) {
2864
+ const transformed = { ...data };
2865
+ for (const field of fields) {
2866
+ const value = data[field.name];
2867
+ if (field.type === "date" || field.type === "datetime") {
2868
+ if (typeof value === "string" && value) {
2869
+ transformed[field.name] = new Date(value).getTime();
2870
+ } else if (!value) {
2871
+ transformed[field.name] = null;
2872
+ }
2873
+ }
2874
+ }
2875
+ return transformed;
2876
+ }
2877
+ function ContentEntryEditor({
2878
+ contentType,
2879
+ entry,
2880
+ onSave,
2881
+ onCancel,
2882
+ onDelete,
2883
+ autosaveEnabled = true,
2884
+ autosaveInterval = 3e4,
2885
+ canDelete: canDeleteProp = false
2886
+ }) {
2887
+ const { settings } = useSettingsConfig();
2888
+ const getInitialData = reactExports.useCallback(() => {
2889
+ if (entry) {
2890
+ return transformDataForUI({ ...entry.data }, contentType.fields);
2891
+ }
2892
+ const defaults = {};
2893
+ for (const field of contentType.fields) {
2894
+ if (field.defaultValue !== void 0) {
2895
+ defaults[field.name] = field.defaultValue;
2896
+ } else {
2897
+ switch (field.type) {
2898
+ case "text":
2899
+ case "richText":
2900
+ defaults[field.name] = "";
2901
+ break;
2902
+ case "boolean":
2903
+ defaults[field.name] = false;
2904
+ break;
2905
+ case "number":
2906
+ case "date":
2907
+ case "datetime":
2908
+ case "reference":
2909
+ case "media":
2910
+ case "json":
2911
+ defaults[field.name] = null;
2912
+ break;
2913
+ case "select":
2914
+ defaults[field.name] = "";
2915
+ break;
2916
+ case "multiSelect":
2917
+ defaults[field.name] = [];
2918
+ break;
2919
+ }
2920
+ }
2921
+ }
2922
+ return defaults;
2923
+ }, [contentType.fields, entry]);
2924
+ const [formData, setFormData] = reactExports.useState(getInitialData);
2925
+ const [fieldErrors, setFieldErrors] = reactExports.useState({});
2926
+ const [isSubmitting, setIsSubmitting] = reactExports.useState(false);
2927
+ const [isDirty, setIsDirty] = reactExports.useState(false);
2928
+ const [lastSavedData, setLastSavedData] = reactExports.useState(entry ? { ...entry.data } : null);
2929
+ const [autosaveStatus, setAutosaveStatus] = reactExports.useState("idle");
2930
+ const [autosaveError, setAutosaveError] = reactExports.useState(null);
2931
+ const [autosaveRetryCount, setAutosaveRetryCount] = reactExports.useState(0);
2932
+ const [submitError, setSubmitError] = reactExports.useState(null);
2933
+ const [saveSuccess, setSaveSuccess] = reactExports.useState(false);
2934
+ const maxAutosaveRetries = 3;
2935
+ const autosaveTimerRef = reactExports.useRef(null);
2936
+ const formDataRef = reactExports.useRef(formData);
2937
+ formDataRef.current = formData;
2938
+ const createEntry = useMutation(api.entries.create);
2939
+ const updateEntry = useMutation(api.entries.update);
2940
+ const publishEntry = useMutation(api.entries.publish);
2941
+ const unpublishEntry = useMutation(api.entries.unpublish);
2942
+ const scheduleEntry = useMutation(api.entries.schedule);
2943
+ const cancelScheduleEntry = useMutation(api.entries.cancelSchedule);
2944
+ const deleteEntryMutation = useMutation(api.entries.remove);
2945
+ const duplicateEntryMutation = useMutation(api.entries.duplicate);
2946
+ const [showScheduleModal, setShowScheduleModal] = reactExports.useState(false);
2947
+ const [scheduleDateTime, setScheduleDateTime] = reactExports.useState(() => {
2948
+ const tomorrow = /* @__PURE__ */ new Date();
2949
+ tomorrow.setDate(tomorrow.getDate() + 1);
2950
+ tomorrow.setHours(9, 0, 0, 0);
2951
+ return formatDateTimeLocal(tomorrow.getTime());
2952
+ });
2953
+ const [isPublishing, setIsPublishing] = reactExports.useState(false);
2954
+ const [publishError, setPublishError] = reactExports.useState(null);
2955
+ reactExports.useEffect(() => {
2956
+ const newData = getInitialData();
2957
+ setFormData(newData);
2958
+ setFieldErrors({});
2959
+ setIsDirty(false);
2960
+ setLastSavedData(entry ? { ...entry.data } : null);
2961
+ setSubmitError(null);
2962
+ }, [entry?._id, getInitialData]);
2963
+ const handleFieldChange = reactExports.useCallback((fieldName, value) => {
2964
+ setFormData((prev) => {
2965
+ const updated = { ...prev, [fieldName]: value };
2966
+ return updated;
2967
+ });
2968
+ setIsDirty(true);
2969
+ setFieldErrors((prev) => {
2970
+ if (prev[fieldName]) {
2971
+ const { [fieldName]: _removed, ...rest } = prev;
2972
+ return rest;
2973
+ }
2974
+ return prev;
2975
+ });
2976
+ }, []);
2977
+ const validateForm = reactExports.useCallback(async () => {
2978
+ const errors = {};
2979
+ for (const field of contentType.fields) {
2980
+ const value = formData[field.name];
2981
+ if (field.required) {
2982
+ const isEmpty = value === null || value === void 0 || value === "" || Array.isArray(value) && value.length === 0;
2983
+ if (isEmpty) {
2984
+ errors[field.name] = {
2985
+ message: `${field.label} is required`,
2986
+ code: "REQUIRED"
2987
+ };
2988
+ continue;
2989
+ }
2990
+ }
2991
+ if (value !== null && value !== void 0 && value !== "") {
2992
+ switch (field.type) {
2993
+ case "text": {
2994
+ const strValue = String(value);
2995
+ const opts = field.options;
2996
+ if (opts?.minLength && strValue.length < opts.minLength) {
2997
+ errors[field.name] = {
2998
+ message: `Minimum ${opts.minLength} characters required`,
2999
+ code: "MIN_LENGTH"
3000
+ };
3001
+ } else if (opts?.maxLength && strValue.length > opts.maxLength) {
3002
+ errors[field.name] = {
3003
+ message: `Maximum ${opts.maxLength} characters allowed`,
3004
+ code: "MAX_LENGTH"
3005
+ };
3006
+ } else if (opts?.pattern) {
3007
+ const regex = new RegExp(opts.pattern);
3008
+ if (!regex.test(strValue)) {
3009
+ errors[field.name] = {
3010
+ message: "Value does not match the required format",
3011
+ code: "PATTERN_MISMATCH"
3012
+ };
3013
+ }
3014
+ }
3015
+ break;
3016
+ }
3017
+ case "number": {
3018
+ const numValue = Number(value);
3019
+ const opts = field.options;
3020
+ if (isNaN(numValue)) {
3021
+ errors[field.name] = {
3022
+ message: "Must be a valid number",
3023
+ code: "INVALID_TYPE"
3024
+ };
3025
+ } else {
3026
+ if (opts?.min !== void 0 && numValue < opts.min) {
3027
+ errors[field.name] = {
3028
+ message: `Minimum value is ${opts.min}`,
3029
+ code: "MIN_VALUE"
3030
+ };
3031
+ } else if (opts?.max !== void 0 && numValue > opts.max) {
3032
+ errors[field.name] = {
3033
+ message: `Maximum value is ${opts.max}`,
3034
+ code: "MAX_VALUE"
3035
+ };
3036
+ } else if (opts?.precision === 0 && !Number.isInteger(numValue)) {
3037
+ errors[field.name] = {
3038
+ message: "Must be a whole number",
3039
+ code: "NOT_INTEGER"
3040
+ };
3041
+ }
3042
+ }
3043
+ break;
3044
+ }
3045
+ case "select": {
3046
+ const opts = field.options;
3047
+ if (opts?.options && !opts.options.some((o) => o.value === value)) {
3048
+ errors[field.name] = {
3049
+ message: "Please select a valid option",
3050
+ code: "INVALID_OPTION"
3051
+ };
3052
+ }
3053
+ break;
3054
+ }
3055
+ case "multiSelect": {
3056
+ const opts = field.options;
3057
+ if (Array.isArray(value) && opts?.options) {
3058
+ const validValues = opts.options.map((o) => o.value);
3059
+ const invalid = value.filter(
3060
+ (v) => !validValues.includes(String(v))
3061
+ );
3062
+ if (invalid.length > 0) {
3063
+ errors[field.name] = {
3064
+ message: "Contains invalid options",
3065
+ code: "INVALID_OPTION"
3066
+ };
3067
+ }
3068
+ }
3069
+ break;
3070
+ }
3071
+ }
3072
+ }
3073
+ }
3074
+ setFieldErrors(errors);
3075
+ return Object.keys(errors).length === 0;
3076
+ }, [contentType.fields, formData]);
3077
+ const autosaveDraft = reactExports.useCallback(
3078
+ async (retryAttempt = 0) => {
3079
+ if (!isDirty || !entry) return;
3080
+ if (entry.status !== "draft") return;
3081
+ try {
3082
+ setAutosaveStatus("saving");
3083
+ setAutosaveError(null);
3084
+ await updateEntry({
3085
+ id: entry._id,
3086
+ data: transformDataForBackend(formDataRef.current, contentType.fields)
3087
+ });
3088
+ setLastSavedData({ ...formDataRef.current });
3089
+ setAutosaveStatus("saved");
3090
+ setIsDirty(false);
3091
+ setAutosaveRetryCount(0);
3092
+ setTimeout(() => {
3093
+ setAutosaveStatus("idle");
3094
+ }, 3e3);
3095
+ } catch (error) {
3096
+ console.error("Autosave failed:", error);
3097
+ const errorMessage = error instanceof Error ? error.message : "Failed to save";
3098
+ const canRetry = isRetryableError(error) && retryAttempt < maxAutosaveRetries;
3099
+ if (canRetry) {
3100
+ const retryDelay = Math.min(1e3 * Math.pow(2, retryAttempt), 1e4);
3101
+ setAutosaveStatus("error");
3102
+ setAutosaveError(
3103
+ `Save failed, retrying in ${Math.round(retryDelay / 1e3)}s...`
3104
+ );
3105
+ setAutosaveRetryCount(retryAttempt + 1);
3106
+ setTimeout(() => {
3107
+ autosaveDraft(retryAttempt + 1);
3108
+ }, retryDelay);
3109
+ } else {
3110
+ setAutosaveStatus("error");
3111
+ setAutosaveError(errorMessage);
3112
+ setAutosaveRetryCount(0);
3113
+ }
3114
+ }
3115
+ },
3116
+ [isDirty, entry, updateEntry, maxAutosaveRetries]
3117
+ );
3118
+ const handleAutosaveRetry = reactExports.useCallback(() => {
3119
+ setAutosaveRetryCount(0);
3120
+ autosaveDraft(0);
3121
+ }, [autosaveDraft]);
3122
+ reactExports.useEffect(() => {
3123
+ if (!autosaveEnabled || !entry || entry.status !== "draft") {
3124
+ return;
3125
+ }
3126
+ if (autosaveTimerRef.current) {
3127
+ clearTimeout(autosaveTimerRef.current);
3128
+ }
3129
+ if (isDirty) {
3130
+ autosaveTimerRef.current = setTimeout(() => {
3131
+ autosaveDraft();
3132
+ }, autosaveInterval);
3133
+ }
3134
+ return () => {
3135
+ if (autosaveTimerRef.current) {
3136
+ clearTimeout(autosaveTimerRef.current);
3137
+ }
3138
+ };
3139
+ }, [autosaveEnabled, autosaveInterval, isDirty, entry, autosaveDraft]);
3140
+ const [showConfirmModal, setShowConfirmModal] = reactExports.useState(false);
3141
+ const [confirmAction, setConfirmAction] = reactExports.useState(null);
3142
+ const [showDeleteModal, setShowDeleteModal] = reactExports.useState(false);
3143
+ const [isDeleting, setIsDeleting] = reactExports.useState(false);
3144
+ const [deleteError, setDeleteError] = reactExports.useState(null);
3145
+ const [isDuplicating, setIsDuplicating] = reactExports.useState(false);
3146
+ const [isArchiving, setIsArchiving] = reactExports.useState(false);
3147
+ const [showVersionHistory, setShowVersionHistory] = reactExports.useState(false);
3148
+ const handlePublishClick = reactExports.useCallback(() => {
3149
+ setConfirmAction("publish");
3150
+ setShowConfirmModal(true);
3151
+ }, []);
3152
+ const handleUnpublishClick = reactExports.useCallback(() => {
3153
+ setConfirmAction("unpublish");
3154
+ setShowConfirmModal(true);
3155
+ }, []);
3156
+ const handleConfirmAction = reactExports.useCallback(async () => {
3157
+ if (!entry || !confirmAction) return;
3158
+ setShowConfirmModal(false);
3159
+ setIsPublishing(true);
3160
+ setPublishError(null);
3161
+ try {
3162
+ if (confirmAction === "publish") {
3163
+ const publishedEntry = await publishEntry({
3164
+ id: entry._id,
3165
+ changeDescription: "Published from editor"
3166
+ });
3167
+ onSave?.(publishedEntry);
3168
+ } else {
3169
+ const draftEntry = await unpublishEntry({
3170
+ id: entry._id
3171
+ });
3172
+ onSave?.(draftEntry);
3173
+ }
3174
+ } catch (error) {
3175
+ const message = error instanceof Error ? error.message : `Failed to ${confirmAction}`;
3176
+ setPublishError(message);
3177
+ } finally {
3178
+ setIsPublishing(false);
3179
+ setConfirmAction(null);
3180
+ }
3181
+ }, [entry, confirmAction, publishEntry, unpublishEntry, onSave]);
3182
+ const handlePublish = reactExports.useCallback(async () => {
3183
+ if (!entry) return;
3184
+ setIsPublishing(true);
3185
+ setPublishError(null);
3186
+ try {
3187
+ const publishedEntry = await publishEntry({
3188
+ id: entry._id,
3189
+ changeDescription: "Published from editor"
3190
+ });
3191
+ onSave?.(publishedEntry);
3192
+ } catch (error) {
3193
+ const message = error instanceof Error ? error.message : "Failed to publish";
3194
+ setPublishError(message);
3195
+ } finally {
3196
+ setIsPublishing(false);
3197
+ }
3198
+ }, [entry, publishEntry, onSave]);
3199
+ reactExports.useCallback(async () => {
3200
+ if (!entry) return;
3201
+ setIsPublishing(true);
3202
+ setPublishError(null);
3203
+ try {
3204
+ const draftEntry = await unpublishEntry({
3205
+ id: entry._id
3206
+ });
3207
+ onSave?.(draftEntry);
3208
+ } catch (error) {
3209
+ const message = error instanceof Error ? error.message : "Failed to unpublish";
3210
+ setPublishError(message);
3211
+ } finally {
3212
+ setIsPublishing(false);
3213
+ }
3214
+ }, [entry, unpublishEntry, onSave]);
3215
+ const handleSchedule = reactExports.useCallback(async () => {
3216
+ if (!entry) return;
3217
+ const publishAt = parseDateTimeLocal(scheduleDateTime);
3218
+ const minimumTime = Date.now() + 60 * 1e3;
3219
+ if (publishAt < minimumTime) {
3220
+ setPublishError("Schedule time must be at least 1 minute in the future");
3221
+ return;
3222
+ }
3223
+ setIsPublishing(true);
3224
+ setPublishError(null);
3225
+ try {
3226
+ const scheduledEntry = await scheduleEntry({
3227
+ id: entry._id,
3228
+ publishAt
3229
+ });
3230
+ setShowScheduleModal(false);
3231
+ onSave?.(scheduledEntry);
3232
+ } catch (error) {
3233
+ const message = error instanceof Error ? error.message : "Failed to schedule";
3234
+ setPublishError(message);
3235
+ } finally {
3236
+ setIsPublishing(false);
3237
+ }
3238
+ }, [entry, scheduleDateTime, scheduleEntry, onSave]);
3239
+ const handleCancelSchedule = reactExports.useCallback(async () => {
3240
+ if (!entry) return;
3241
+ setIsPublishing(true);
3242
+ setPublishError(null);
3243
+ try {
3244
+ const draftEntry = await cancelScheduleEntry({
3245
+ id: entry._id
3246
+ });
3247
+ onSave?.(draftEntry);
3248
+ } catch (error) {
3249
+ const message = error instanceof Error ? error.message : "Failed to cancel schedule";
3250
+ setPublishError(message);
3251
+ } finally {
3252
+ setIsPublishing(false);
3253
+ }
3254
+ }, [entry, cancelScheduleEntry, onSave]);
3255
+ const handleDeleteClick = reactExports.useCallback(() => {
3256
+ setDeleteError(null);
3257
+ setShowDeleteModal(true);
3258
+ }, []);
3259
+ const handleDeleteConfirm = reactExports.useCallback(async () => {
3260
+ if (!entry) return;
3261
+ setIsDeleting(true);
3262
+ setDeleteError(null);
3263
+ try {
3264
+ await deleteEntryMutation({
3265
+ id: entry._id,
3266
+ hardDelete: false
3267
+ });
3268
+ setShowDeleteModal(false);
3269
+ onDelete?.();
3270
+ } catch (error) {
3271
+ const message = error instanceof Error ? error.message : "Failed to delete entry";
3272
+ setDeleteError(message);
3273
+ } finally {
3274
+ setIsDeleting(false);
3275
+ }
3276
+ }, [entry, deleteEntryMutation, onDelete]);
3277
+ const handleDuplicate = reactExports.useCallback(async () => {
3278
+ if (!entry) return;
3279
+ setIsDuplicating(true);
3280
+ setSubmitError(null);
3281
+ try {
3282
+ const duplicatedEntry = await duplicateEntryMutation({
3283
+ sourceEntryId: entry._id
3284
+ });
3285
+ onSave?.(duplicatedEntry);
3286
+ } catch (error) {
3287
+ const message = error instanceof Error ? error.message : "Failed to duplicate entry";
3288
+ setSubmitError(message);
3289
+ } finally {
3290
+ setIsDuplicating(false);
3291
+ }
3292
+ }, [entry, duplicateEntryMutation, onSave]);
3293
+ const handleArchive = reactExports.useCallback(async () => {
3294
+ if (!entry) return;
3295
+ setIsArchiving(true);
3296
+ setSubmitError(null);
3297
+ try {
3298
+ const archivedEntry = await updateEntry({
3299
+ id: entry._id,
3300
+ status: "archived"
3301
+ });
3302
+ onSave?.(archivedEntry);
3303
+ } catch (error) {
3304
+ const message = error instanceof Error ? error.message : "Failed to archive entry";
3305
+ setSubmitError(message);
3306
+ } finally {
3307
+ setIsArchiving(false);
3308
+ }
3309
+ }, [entry, updateEntry, onSave]);
3310
+ const handleSubmit = reactExports.useCallback(
3311
+ async (e) => {
3312
+ e.preventDefault();
3313
+ setSubmitError(null);
3314
+ const isValid = await validateForm();
3315
+ if (!isValid) {
3316
+ return;
3317
+ }
3318
+ setIsSubmitting(true);
3319
+ try {
3320
+ let savedEntry;
3321
+ const dataForBackend = transformDataForBackend(
3322
+ formData,
3323
+ contentType.fields
3324
+ );
3325
+ if (entry) {
3326
+ savedEntry = await updateEntry({
3327
+ id: entry._id,
3328
+ data: dataForBackend
3329
+ });
3330
+ } else {
3331
+ savedEntry = await createEntry({
3332
+ contentTypeId: contentType._id,
3333
+ data: dataForBackend
3334
+ });
3335
+ }
3336
+ setIsDirty(false);
3337
+ setLastSavedData({ ...formData });
3338
+ setSaveSuccess(true);
3339
+ setTimeout(() => setSaveSuccess(false), 3e3);
3340
+ onSave?.(savedEntry);
3341
+ } catch (error) {
3342
+ const { fieldErrors: serverFieldErrors, generalError } = parseServerError(error);
3343
+ const message = generalError ?? (error instanceof Error ? error.message : "Failed to save entry");
3344
+ setSubmitError(message);
3345
+ if (Object.keys(serverFieldErrors).length > 0) {
3346
+ setFieldErrors((prev) => ({ ...prev, ...serverFieldErrors }));
3347
+ }
3348
+ } finally {
3349
+ setIsSubmitting(false);
3350
+ }
3351
+ },
3352
+ [
3353
+ validateForm,
3354
+ entry,
3355
+ formData,
3356
+ contentType._id,
3357
+ createEntry,
3358
+ updateEntry,
3359
+ onSave
3360
+ ]
3361
+ );
3362
+ const handleCancel = reactExports.useCallback(() => {
3363
+ if (isDirty) {
3364
+ const confirmed = window.confirm(
3365
+ "You have unsaved changes. Are you sure you want to leave?"
3366
+ );
3367
+ if (!confirmed) return;
3368
+ }
3369
+ onCancel?.();
3370
+ }, [isDirty, onCancel]);
3371
+ const getAutosaveStatusText = () => {
3372
+ switch (autosaveStatus) {
3373
+ case "saving":
3374
+ return autosaveRetryCount > 0 ? `Retrying (${autosaveRetryCount}/${maxAutosaveRetries})...` : "Saving...";
3375
+ case "saved":
3376
+ return "Draft saved";
3377
+ case "error":
3378
+ return autosaveError ?? "Autosave failed";
3379
+ default:
3380
+ return null;
3381
+ }
3382
+ };
3383
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { className: "space-y-6", onSubmit: handleSubmit, children: [
3384
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4", children: [
3385
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
3386
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "text-xl font-semibold", children: [
3387
+ entry ? "Edit" : "Create",
3388
+ " ",
3389
+ contentType.displayName
3390
+ ] }),
3391
+ entry && /* @__PURE__ */ jsxRuntimeExports.jsx(CmsStatusBadge, { status: entry.status })
3392
+ ] }),
3393
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
3394
+ autosaveStatus !== "idle" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
3395
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
3396
+ "span",
3397
+ {
3398
+ className: cn(
3399
+ "flex items-center gap-1.5 text-sm",
3400
+ autosaveStatus === "saving" && "text-muted-foreground",
3401
+ autosaveStatus === "saved" && "text-emerald-600",
3402
+ autosaveStatus === "error" && "text-red-600"
3403
+ ),
3404
+ "data-testid": "autosave-status",
3405
+ children: [
3406
+ autosaveStatus === "saving" && /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3 animate-spin" }),
3407
+ autosaveStatus === "saved" && /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { className: "size-3" }),
3408
+ autosaveStatus === "error" && /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "size-3" }),
3409
+ getAutosaveStatusText()
3410
+ ]
3411
+ }
3412
+ ),
3413
+ autosaveStatus === "error" && autosaveRetryCount === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
3414
+ "button",
3415
+ {
3416
+ type: "button",
3417
+ onClick: handleAutosaveRetry,
3418
+ className: "text-sm text-primary hover:underline",
3419
+ "data-testid": "autosave-retry-button",
3420
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "size-3" })
3421
+ }
3422
+ )
3423
+ ] }),
3424
+ isDirty && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-amber-600", children: "Unsaved changes" })
3425
+ ] })
3426
+ ] }),
3427
+ saveSuccess && /* @__PURE__ */ jsxRuntimeExports.jsxs(
3428
+ "div",
3429
+ {
3430
+ className: "flex items-center gap-2 rounded-lg border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-800",
3431
+ role: "status",
3432
+ children: [
3433
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { className: "size-4" }),
3434
+ "Changes saved successfully"
3435
+ ]
3436
+ }
3437
+ ),
3438
+ (submitError || publishError) && /* @__PURE__ */ jsxRuntimeExports.jsxs(
3439
+ "div",
3440
+ {
3441
+ className: "rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800",
3442
+ role: "alert",
3443
+ children: [
3444
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium", children: "Error:" }),
3445
+ " ",
3446
+ submitError || publishError
3447
+ ]
3448
+ }
3449
+ ),
3450
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-4", children: contentType.fields.map((field) => /* @__PURE__ */ jsxRuntimeExports.jsx(
3451
+ FieldRenderer,
3452
+ {
3453
+ field,
3454
+ value: formData[field.name],
3455
+ onChange: (value) => handleFieldChange(field.name, value),
3456
+ error: fieldErrors[field.name],
3457
+ disabled: isSubmitting
3458
+ },
3459
+ field.name
3460
+ )) }),
3461
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4 border-t pt-4", children: [
3462
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
3463
+ entry && canDeleteProp && /* @__PURE__ */ jsxRuntimeExports.jsx(
3464
+ CmsButton,
3465
+ {
3466
+ type: "button",
3467
+ variant: "danger",
3468
+ onClick: handleDeleteClick,
3469
+ disabled: isSubmitting || isPublishing || isDeleting || isDuplicating,
3470
+ loading: isDeleting,
3471
+ "data-testid": "delete-button",
3472
+ children: "Delete"
3473
+ }
3474
+ ),
3475
+ entry && /* @__PURE__ */ jsxRuntimeExports.jsx(
3476
+ CmsButton,
3477
+ {
3478
+ type: "button",
3479
+ variant: "secondary",
3480
+ onClick: handleDuplicate,
3481
+ disabled: isSubmitting || isPublishing || isDeleting || isDuplicating || isArchiving,
3482
+ loading: isDuplicating,
3483
+ "data-testid": "duplicate-button",
3484
+ children: "Duplicate"
3485
+ }
3486
+ ),
3487
+ entry && (entry.status === "draft" || entry.status === "scheduled") && /* @__PURE__ */ jsxRuntimeExports.jsx(
3488
+ CmsButton,
3489
+ {
3490
+ type: "button",
3491
+ variant: "secondary",
3492
+ onClick: handleArchive,
3493
+ disabled: isSubmitting || isPublishing || isDeleting || isDuplicating || isArchiving,
3494
+ loading: isArchiving,
3495
+ "data-testid": "archive-button",
3496
+ children: "Archive"
3497
+ }
3498
+ ),
3499
+ entry && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4 text-sm text-muted-foreground", children: [
3500
+ settings?.features.versioning && /* @__PURE__ */ jsxRuntimeExports.jsxs(
3501
+ "button",
3502
+ {
3503
+ type: "button",
3504
+ onClick: () => setShowVersionHistory(true),
3505
+ className: "flex items-center gap-2 rounded-md border bg-muted/30 px-3 py-1.5 transition-colors hover:bg-muted/50",
3506
+ children: [
3507
+ /* @__PURE__ */ jsxRuntimeExports.jsx(History, { className: "size-4 text-muted-foreground" }),
3508
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-medium text-foreground", children: [
3509
+ "Version ",
3510
+ entry.version
3511
+ ] }),
3512
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-muted-foreground", children: "View history" })
3513
+ ]
3514
+ }
3515
+ ),
3516
+ entry.lastPublishedAt && /* @__PURE__ */ jsxRuntimeExports.jsxs(
3517
+ "span",
3518
+ {
3519
+ className: "text-xs",
3520
+ "data-testid": "last-published-time",
3521
+ children: [
3522
+ "Last published:",
3523
+ " ",
3524
+ new Date(entry.lastPublishedAt).toLocaleString()
3525
+ ]
3526
+ }
3527
+ ),
3528
+ settings?.features.scheduling && entry.status === "scheduled" && entry.scheduledPublishAt && /* @__PURE__ */ jsxRuntimeExports.jsxs(
3529
+ "span",
3530
+ {
3531
+ className: "flex items-center gap-1 text-xs text-blue-600",
3532
+ "data-testid": "scheduled-time",
3533
+ children: [
3534
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
3535
+ "Scheduled:",
3536
+ " ",
3537
+ new Date(entry.scheduledPublishAt).toLocaleString()
3538
+ ]
3539
+ }
3540
+ )
3541
+ ] })
3542
+ ] }),
3543
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
3544
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3545
+ CmsButton,
3546
+ {
3547
+ type: "button",
3548
+ variant: "outline",
3549
+ onClick: handleCancel,
3550
+ disabled: isSubmitting || isPublishing,
3551
+ children: "Cancel"
3552
+ }
3553
+ ),
3554
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3555
+ CmsButton,
3556
+ {
3557
+ type: "submit",
3558
+ variant: "primary",
3559
+ disabled: isSubmitting || isPublishing,
3560
+ loading: isSubmitting,
3561
+ children: entry ? "Save Changes" : "Create Entry"
3562
+ }
3563
+ ),
3564
+ entry && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3565
+ entry.status === "draft" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3566
+ settings?.features.scheduling && /* @__PURE__ */ jsxRuntimeExports.jsx(
3567
+ CmsButton,
3568
+ {
3569
+ type: "button",
3570
+ variant: "secondary",
3571
+ onClick: () => setShowScheduleModal(true),
3572
+ disabled: isSubmitting || isPublishing,
3573
+ children: "Schedule"
3574
+ }
3575
+ ),
3576
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3577
+ CmsButton,
3578
+ {
3579
+ type: "button",
3580
+ variant: "success",
3581
+ onClick: handlePublishClick,
3582
+ disabled: isSubmitting || isPublishing,
3583
+ loading: isPublishing,
3584
+ "data-testid": "publish-button",
3585
+ children: "Publish Now"
3586
+ }
3587
+ )
3588
+ ] }),
3589
+ settings?.features.scheduling && entry.status === "scheduled" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3590
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3591
+ CmsButton,
3592
+ {
3593
+ type: "button",
3594
+ variant: "secondary",
3595
+ onClick: handleCancelSchedule,
3596
+ disabled: isSubmitting || isPublishing,
3597
+ children: "Cancel Schedule"
3598
+ }
3599
+ ),
3600
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3601
+ CmsButton,
3602
+ {
3603
+ type: "button",
3604
+ variant: "success",
3605
+ onClick: handlePublish,
3606
+ disabled: isSubmitting || isPublishing,
3607
+ loading: isPublishing,
3608
+ children: "Publish Now"
3609
+ }
3610
+ )
3611
+ ] }),
3612
+ entry.status === "published" && /* @__PURE__ */ jsxRuntimeExports.jsx(
3613
+ CmsButton,
3614
+ {
3615
+ type: "button",
3616
+ variant: "warning",
3617
+ onClick: handleUnpublishClick,
3618
+ disabled: isSubmitting || isPublishing,
3619
+ loading: isPublishing,
3620
+ "data-testid": "unpublish-button",
3621
+ children: "Unpublish"
3622
+ }
3623
+ )
3624
+ ] })
3625
+ ] })
3626
+ ] }),
3627
+ settings?.features.scheduling && /* @__PURE__ */ jsxRuntimeExports.jsx(
3628
+ CmsDialog,
3629
+ {
3630
+ open: showScheduleModal,
3631
+ onOpenChange: setShowScheduleModal,
3632
+ title: "Schedule Publication",
3633
+ size: "sm",
3634
+ footer: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3635
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3636
+ CmsButton,
3637
+ {
3638
+ variant: "outline",
3639
+ onClick: () => setShowScheduleModal(false),
3640
+ children: "Cancel"
3641
+ }
3642
+ ),
3643
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3644
+ CmsButton,
3645
+ {
3646
+ variant: "primary",
3647
+ onClick: handleSchedule,
3648
+ loading: isPublishing,
3649
+ children: "Schedule"
3650
+ }
3651
+ )
3652
+ ] }),
3653
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
3654
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground", children: "Choose when this content should be automatically published:" }),
3655
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3656
+ Input,
3657
+ {
3658
+ type: "datetime-local",
3659
+ value: scheduleDateTime,
3660
+ onChange: (e) => setScheduleDateTime(e.target.value),
3661
+ min: formatDateTimeLocal(Date.now() + 60 * 1e3)
3662
+ }
3663
+ ),
3664
+ publishError && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-destructive", children: publishError })
3665
+ ] })
3666
+ }
3667
+ ),
3668
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3669
+ CmsConfirmDialog,
3670
+ {
3671
+ open: showConfirmModal && confirmAction !== null,
3672
+ onOpenChange: (open) => {
3673
+ if (!open) {
3674
+ setShowConfirmModal(false);
3675
+ setConfirmAction(null);
3676
+ }
3677
+ },
3678
+ title: confirmAction === "publish" ? "Confirm Publish" : "Confirm Unpublish",
3679
+ description: confirmAction === "publish" ? "Are you sure you want to publish this entry? It will become publicly visible." : "Are you sure you want to unpublish this entry? It will no longer be publicly visible.",
3680
+ confirmLabel: confirmAction === "publish" ? "Publish" : "Unpublish",
3681
+ variant: confirmAction === "publish" ? "primary" : "warning",
3682
+ onConfirm: handleConfirmAction,
3683
+ isLoading: isPublishing
3684
+ }
3685
+ ),
3686
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3687
+ CmsConfirmDialog,
3688
+ {
3689
+ open: showDeleteModal,
3690
+ onOpenChange: (open) => {
3691
+ if (!open && !isDeleting) {
3692
+ setShowDeleteModal(false);
3693
+ setDeleteError(null);
3694
+ }
3695
+ },
3696
+ title: "Delete Entry",
3697
+ description: "Are you sure you want to delete this entry? It will be moved to the trash and can be restored within the retention period.",
3698
+ confirmLabel: "Delete",
3699
+ variant: "danger",
3700
+ onConfirm: handleDeleteConfirm,
3701
+ isLoading: isDeleting,
3702
+ error: deleteError
3703
+ }
3704
+ ),
3705
+ settings?.features.versioning && showVersionHistory && entry && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed inset-y-0 right-0 z-50 flex w-96 flex-col shadow-xl", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3706
+ VersionHistory,
3707
+ {
3708
+ entryId: entry._id,
3709
+ currentVersion: entry.version,
3710
+ onRollbackComplete: () => {
3711
+ setShowVersionHistory(false);
3712
+ },
3713
+ onClose: () => setShowVersionHistory(false)
3714
+ }
3715
+ ) })
3716
+ ] });
3717
+ }
3718
+ export {
3719
+ ContentEntryEditor as C
3720
+ };