includio-cms 0.24.1 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (349) hide show
  1. package/API.md +57 -4
  2. package/CHANGELOG.md +53 -0
  3. package/DOCS.md +1 -1
  4. package/README.md +2 -0
  5. package/ROADMAP.md +6 -0
  6. package/dist/admin/client/account/lang.d.ts +1 -0
  7. package/dist/admin/client/account/lang.js +4 -2
  8. package/dist/admin/client/account/profile-section.svelte +2 -2
  9. package/dist/admin/client/account/security-section.svelte +27 -4
  10. package/dist/admin/client/account/sessions-section.svelte +1 -1
  11. package/dist/admin/client/admin/admin-after-login-layout-content.svelte +1 -1
  12. package/dist/admin/client/admin/dashboard-page.svelte +34 -10
  13. package/dist/admin/client/collection/bulk-actions-bar.svelte +86 -44
  14. package/dist/admin/client/collection/bulk-actions-bar.svelte.d.ts +3 -1
  15. package/dist/admin/client/collection/collection-entries.svelte +52 -36
  16. package/dist/admin/client/collection/collection-entries.svelte.d.ts +3 -0
  17. package/dist/admin/client/collection/collection.svelte +28 -14
  18. package/dist/admin/client/collection/collection.svelte.d.ts +3 -0
  19. package/dist/admin/client/collection/data-table.svelte +279 -130
  20. package/dist/admin/client/collection/data-table.svelte.d.ts +11 -0
  21. package/dist/admin/client/collection/date-cell.svelte +4 -4
  22. package/dist/admin/client/collection/row-actions.svelte +2 -1
  23. package/dist/admin/client/collection/sortable-header.svelte +33 -9
  24. package/dist/admin/client/collection/state-display.svelte +102 -0
  25. package/dist/admin/client/collection/state-display.svelte.d.ts +12 -0
  26. package/dist/admin/client/collection/status-badge.svelte +99 -11
  27. package/dist/admin/client/collection/status-badge.svelte.d.ts +15 -1
  28. package/dist/admin/client/collection/table-pagination.svelte +21 -6
  29. package/dist/admin/client/collection/table-toolbar.svelte +105 -80
  30. package/dist/admin/client/collection/table-toolbar.svelte.d.ts +11 -8
  31. package/dist/admin/client/entry/entry-form.svelte +36 -11
  32. package/dist/admin/client/entry/entry-form.svelte.d.ts +1 -0
  33. package/dist/admin/client/entry/entry-header.svelte +22 -15
  34. package/dist/admin/client/entry/entry-header.svelte.d.ts +1 -0
  35. package/dist/admin/client/entry/entry.svelte +269 -165
  36. package/dist/admin/client/entry/header/a11y-header-badge.svelte +47 -0
  37. package/dist/admin/client/entry/header/a11y-header-badge.svelte.d.ts +8 -0
  38. package/dist/admin/client/entry/header/publish-panel.svelte +69 -13
  39. package/dist/admin/client/entry/header/save-indicator.svelte +57 -28
  40. package/dist/admin/client/entry/header/save-indicator.svelte.d.ts +1 -0
  41. package/dist/admin/client/entry/header/status-badge.svelte +60 -15
  42. package/dist/admin/client/entry/header/status-badge.svelte.d.ts +1 -2
  43. package/dist/admin/client/entry/header/version-history-sheet.svelte +1 -1
  44. package/dist/admin/client/entry/hybrid/hybrid-layout.svelte +74 -23
  45. package/dist/admin/client/entry/hybrid/hybrid-preview.svelte +1 -1
  46. package/dist/admin/client/entry/utils.d.ts +14 -0
  47. package/dist/admin/client/entry/utils.js +28 -0
  48. package/dist/admin/client/form/form-submission/form-submission.svelte +2 -2
  49. package/dist/admin/client/form/form-submissions.svelte +143 -194
  50. package/dist/admin/client/form/form-submissions.svelte.d.ts +2 -0
  51. package/dist/admin/client/login/lang.d.ts +3 -0
  52. package/dist/admin/client/login/lang.js +10 -4
  53. package/dist/admin/client/login/login-form.svelte +8 -1
  54. package/dist/admin/client/login/reset-password-page.svelte +24 -3
  55. package/dist/admin/client/login/schema.d.ts +14 -2
  56. package/dist/admin/client/login/schema.js +19 -8
  57. package/dist/admin/client/maintenance/maintenance-page.svelte +16 -17
  58. package/dist/admin/client/media/media-page.svelte +1 -1
  59. package/dist/admin/client/shop/coupon-edit-page.svelte +117 -13
  60. package/dist/admin/client/shop/coupon-form.svelte +282 -138
  61. package/dist/admin/client/shop/coupon-form.svelte.d.ts +1 -9
  62. package/dist/admin/client/shop/coupon-new-page.svelte +40 -10
  63. package/dist/admin/client/shop/coupon-new-page.svelte.d.ts +2 -17
  64. package/dist/admin/client/shop/coupon-schema.d.ts +28 -0
  65. package/dist/admin/client/shop/coupon-schema.js +53 -0
  66. package/dist/admin/client/shop/coupons-list-page.svelte +262 -118
  67. package/dist/admin/client/shop/coupons-list-page.svelte.d.ts +16 -1
  68. package/dist/admin/client/shop/shipping-method-edit-page.svelte +108 -59
  69. package/dist/admin/client/shop/shipping-method-form.svelte +36 -9
  70. package/dist/admin/client/shop/shipping-method-new-page.svelte +44 -13
  71. package/dist/admin/client/shop/shipping-methods-list-page.svelte +101 -59
  72. package/dist/admin/client/shop/shop-order-detail-page.svelte +113 -84
  73. package/dist/admin/client/shop/shop-orders-list-page.svelte +302 -152
  74. package/dist/admin/client/shop/shop-orders-list-page.svelte.d.ts +18 -1
  75. package/dist/admin/client/shop/shop-products-list-page.svelte +355 -118
  76. package/dist/admin/client/shop/shop-products-list-page.svelte.d.ts +19 -1
  77. package/dist/admin/client/users/accept-invite-page.svelte +24 -3
  78. package/dist/admin/client/users/create-user-dialog.svelte +3 -8
  79. package/dist/admin/client/users/lang.d.ts +2 -0
  80. package/dist/admin/client/users/lang.js +4 -0
  81. package/dist/admin/client/users/pending-invitations.svelte +2 -9
  82. package/dist/admin/client/users/user-name-cell.svelte +20 -0
  83. package/dist/admin/client/users/user-name-cell.svelte.d.ts +9 -0
  84. package/dist/admin/client/users/user-role-badge.svelte +16 -0
  85. package/dist/admin/client/users/user-role-badge.svelte.d.ts +7 -0
  86. package/dist/admin/client/users/user-row-actions.svelte +72 -0
  87. package/dist/admin/client/users/user-row-actions.svelte.d.ts +20 -0
  88. package/dist/admin/client/users/user-sessions-sheet.svelte +2 -11
  89. package/dist/admin/client/users/users-page.svelte +283 -497
  90. package/dist/admin/client/users/users-page.svelte.d.ts +12 -1
  91. package/dist/admin/components/dashboard/form-submissions-widget.svelte +59 -74
  92. package/dist/admin/components/dashboard/recent-activity.svelte +17 -5
  93. package/dist/admin/components/dashboard/recent-entries.svelte +19 -7
  94. package/dist/admin/components/dialogs/confirmation-dialog.svelte +105 -0
  95. package/dist/admin/components/dialogs/confirmation-dialog.svelte.d.ts +13 -0
  96. package/dist/admin/components/fields/block-picker-modal.svelte +6 -0
  97. package/dist/admin/components/fields/blocks-field.svelte +46 -1
  98. package/dist/admin/components/fields/boolean-field.svelte +1 -1
  99. package/dist/admin/components/fields/field-renderer.svelte +23 -21
  100. package/dist/admin/components/fields/file-field.svelte +344 -30
  101. package/dist/admin/components/fields/media-field.svelte +16 -2
  102. package/dist/admin/components/fields/radio-field.svelte +22 -0
  103. package/dist/admin/components/fields/relation-field.svelte +123 -97
  104. package/dist/admin/components/fields/relation-picker-dialog.svelte +2 -2
  105. package/dist/admin/components/fields/seo-field.svelte +60 -30
  106. package/dist/admin/components/fields/shop-field.svelte +9 -4
  107. package/dist/admin/components/fields/simple-array-field.svelte +321 -151
  108. package/dist/admin/components/fields/simple-array-field.svelte.d.ts +3 -0
  109. package/dist/admin/components/fields/slug-field.svelte +146 -21
  110. package/dist/admin/components/fields/text-field-wrapper.svelte +37 -20
  111. package/dist/admin/components/fields/text-field.svelte +7 -2
  112. package/dist/admin/components/fields/url-field-wrapper.svelte +10 -0
  113. package/dist/admin/components/fields/url-field.svelte +36 -23
  114. package/dist/admin/components/forms/form-error-summary.svelte +143 -0
  115. package/dist/admin/components/forms/form-error-summary.svelte.d.ts +27 -0
  116. package/dist/admin/components/layout/app-sidebar.svelte +7 -2
  117. package/dist/admin/components/layout/detail-page-shell.svelte +71 -0
  118. package/dist/admin/components/layout/detail-page-shell.svelte.d.ts +24 -0
  119. package/dist/admin/components/layout/lang.d.ts +5 -0
  120. package/dist/admin/components/layout/lang.js +10 -0
  121. package/dist/admin/components/layout/layout-renderer.svelte +71 -2
  122. package/dist/admin/components/layout/layout-renderer.svelte.d.ts +1 -0
  123. package/dist/admin/components/layout/layout-tabs.svelte +172 -0
  124. package/dist/admin/components/layout/layout-tabs.svelte.d.ts +24 -0
  125. package/dist/admin/components/layout/nav-breadcrumbs.svelte +25 -7
  126. package/dist/admin/components/layout/nav-collections.svelte +23 -36
  127. package/dist/admin/components/layout/nav-forms.svelte +19 -35
  128. package/dist/admin/components/layout/nav-main.svelte +3 -28
  129. package/dist/admin/components/layout/nav-search.svelte +70 -2
  130. package/dist/admin/components/layout/nav-section.svelte +77 -0
  131. package/dist/admin/components/layout/nav-section.svelte.d.ts +22 -0
  132. package/dist/admin/components/layout/nav-shop.svelte +3 -27
  133. package/dist/admin/components/layout/nav-singletons.svelte +16 -28
  134. package/dist/admin/components/layout/page-header.stories.svelte +93 -0
  135. package/dist/admin/components/layout/page-header.stories.svelte.d.ts +27 -0
  136. package/dist/admin/components/layout/page-header.svelte +68 -0
  137. package/dist/admin/components/layout/page-header.svelte.d.ts +17 -0
  138. package/dist/admin/components/layout/site-header.svelte +9 -0
  139. package/dist/admin/components/layout/site-header.svelte.d.ts +2 -17
  140. package/dist/admin/components/media/file/file-name-input.svelte +6 -2
  141. package/dist/admin/components/media/file/file-preview.svelte +130 -17
  142. package/dist/admin/components/media/file-upload.svelte +16 -7
  143. package/dist/admin/components/media/file-upload.svelte.d.ts +1 -0
  144. package/dist/admin/components/media/files-list.svelte +153 -53
  145. package/dist/admin/components/media/files-list.svelte.d.ts +1 -0
  146. package/dist/admin/components/media/media-library.svelte +577 -198
  147. package/dist/admin/components/media/media-library.svelte.d.ts +4 -0
  148. package/dist/admin/components/media/media-selector.svelte +4 -2
  149. package/dist/admin/components/media/media-selector.svelte.d.ts +1 -0
  150. package/dist/admin/components/media/tag-sidebar.svelte +4 -4
  151. package/dist/admin/components/tiptap/FigureNodeView.svelte +10 -0
  152. package/dist/admin/components/tiptap/bubble-menu.svelte +104 -0
  153. package/dist/admin/components/tiptap/bubble-menu.svelte.d.ts +19 -0
  154. package/dist/admin/components/tiptap/content-editor.svelte +28 -24
  155. package/dist/admin/components/tiptap/editor-toolbar.svelte +7 -7
  156. package/dist/admin/components/tiptap/extensions.js +5 -1
  157. package/dist/admin/components/tiptap/image-dialog.svelte +5 -1
  158. package/dist/admin/components/tiptap/link-dialog.svelte +2 -0
  159. package/dist/admin/components/tiptap/tiptap-editor.svelte +18 -20
  160. package/dist/admin/components/tiptap/video-dialog.svelte +1 -1
  161. package/dist/admin/i18n/errors.d.ts +140 -0
  162. package/dist/admin/i18n/errors.js +151 -0
  163. package/dist/admin/remote/entry.remote.d.ts +59 -4
  164. package/dist/admin/remote/entry.remote.js +239 -62
  165. package/dist/admin/remote/shop.remote.d.ts +37 -32
  166. package/dist/admin/remote/shop.remote.js +9 -2
  167. package/dist/admin/shared/password-generate.d.ts +6 -0
  168. package/dist/admin/shared/password-generate.js +40 -0
  169. package/dist/admin/shared/password-schema.d.ts +6 -0
  170. package/dist/admin/shared/password-schema.js +10 -3
  171. package/dist/admin/styles/admin.css +23 -6
  172. package/dist/admin/styles/tokens.md +244 -0
  173. package/dist/admin/utils/accordionActivation.d.ts +13 -0
  174. package/dist/admin/utils/accordionActivation.js +35 -0
  175. package/dist/admin/utils/entryLabel.d.ts +23 -0
  176. package/dist/admin/utils/entryLabel.js +51 -12
  177. package/dist/admin/utils/field-a11y.d.ts +29 -0
  178. package/dist/admin/utils/field-a11y.js +23 -0
  179. package/dist/admin/utils/fieldPathElement.d.ts +9 -0
  180. package/dist/admin/utils/fieldPathElement.js +18 -0
  181. package/dist/admin/utils/fileDisplay.d.ts +10 -0
  182. package/dist/admin/utils/fileDisplay.js +26 -0
  183. package/dist/admin/utils/flattenFormErrors.d.ts +19 -0
  184. package/dist/admin/utils/flattenFormErrors.js +102 -0
  185. package/dist/admin/utils/formatters.d.ts +12 -0
  186. package/dist/admin/utils/{formatDate.js → formatters.js} +23 -2
  187. package/dist/admin/utils/scrollWithin.d.ts +9 -0
  188. package/dist/admin/utils/scrollWithin.js +32 -0
  189. package/dist/admin/utils/tabActivation.d.ts +12 -0
  190. package/dist/admin/utils/tabActivation.js +24 -0
  191. package/dist/cms/runtime/schema.d.ts +1 -0
  192. package/dist/cms/runtime/schema.js +1 -0
  193. package/dist/cms/runtime/types.d.ts +80 -7
  194. package/dist/components/ui/accordion/accordion-content.svelte +17 -3
  195. package/dist/components/ui/accordion/accordion.stories.svelte +21 -1
  196. package/dist/components/ui/alert/alert.stories.svelte +14 -0
  197. package/dist/components/ui/alert-dialog/alert-dialog.stories.svelte +45 -0
  198. package/dist/components/ui/alert-dialog/alert-dialog.stories.svelte.d.ts +27 -0
  199. package/dist/components/ui/avatar/avatar.stories.svelte +27 -0
  200. package/dist/components/ui/badge/badge.stories.svelte +15 -0
  201. package/dist/components/ui/breadcrumb/breadcrumb.stories.svelte +47 -0
  202. package/dist/components/ui/breadcrumb/breadcrumb.svelte +1 -1
  203. package/dist/components/ui/button/button.stories.svelte +53 -6
  204. package/dist/components/ui/button/button.svelte +39 -5
  205. package/dist/components/ui/button/button.svelte.d.ts +4 -0
  206. package/dist/components/ui/button-group/button-group.stories.svelte +44 -0
  207. package/dist/components/ui/button-group/button-group.stories.svelte.d.ts +27 -0
  208. package/dist/components/ui/calendar/calendar.stories.svelte +36 -0
  209. package/dist/components/ui/calendar/calendar.stories.svelte.d.ts +27 -0
  210. package/dist/components/ui/card/card.stories.svelte +7 -0
  211. package/dist/components/ui/carousel/carousel.stories.svelte +43 -0
  212. package/dist/components/ui/carousel/carousel.stories.svelte.d.ts +27 -0
  213. package/dist/components/ui/checkbox/checkbox.stories.svelte +67 -0
  214. package/dist/components/ui/checkbox/checkbox.stories.svelte.d.ts +27 -0
  215. package/dist/components/ui/checkbox/checkbox.svelte +3 -3
  216. package/dist/components/ui/command/command.stories.svelte +18 -0
  217. package/dist/components/ui/data-table/data-table.stories.svelte +61 -0
  218. package/dist/components/ui/data-table/data-table.stories.svelte.d.ts +18 -0
  219. package/dist/components/ui/dialog/dialog-content.svelte +5 -0
  220. package/dist/components/ui/dialog/dialog-content.svelte.d.ts +2 -0
  221. package/dist/components/ui/dialog/dialog.stories.svelte +35 -0
  222. package/dist/components/ui/dropdown-menu/dropdown-menu.stories.svelte +74 -0
  223. package/dist/components/ui/dropdown-menu/dropdown-menu.stories.svelte.d.ts +27 -0
  224. package/dist/components/ui/field/field-context.svelte.d.ts +22 -0
  225. package/dist/components/ui/field/field-context.svelte.js +9 -0
  226. package/dist/components/ui/field/field-control.svelte +18 -0
  227. package/dist/components/ui/field/field-control.svelte.d.ts +8 -0
  228. package/dist/components/ui/field/field-description.svelte +12 -0
  229. package/dist/components/ui/field/field-error.svelte +14 -6
  230. package/dist/components/ui/field/field-label.svelte +10 -0
  231. package/dist/components/ui/field/field.stories.svelte +95 -9
  232. package/dist/components/ui/field/field.svelte +57 -0
  233. package/dist/components/ui/field/field.svelte.d.ts +2 -0
  234. package/dist/components/ui/field/index.d.ts +3 -1
  235. package/dist/components/ui/field/index.js +4 -2
  236. package/dist/components/ui/form/form-field-errors.svelte +1 -1
  237. package/dist/components/ui/form/form.stories.svelte +25 -0
  238. package/dist/components/ui/form/form.stories.svelte.d.ts +26 -0
  239. package/dist/components/ui/input/input.stories.svelte +26 -0
  240. package/dist/components/ui/input-group/input-group-input.svelte.d.ts +1 -1
  241. package/dist/components/ui/input-group/input-group.stories.svelte +43 -0
  242. package/dist/components/ui/input-group/input-group.stories.svelte.d.ts +27 -0
  243. package/dist/components/ui/item/item.stories.svelte +61 -0
  244. package/dist/components/ui/item/item.stories.svelte.d.ts +27 -0
  245. package/dist/components/ui/label/label.stories.svelte +7 -0
  246. package/dist/components/ui/live-region/index.d.ts +1 -0
  247. package/dist/components/ui/live-region/index.js +1 -0
  248. package/dist/components/ui/live-region/live-region-demo.svelte +32 -0
  249. package/dist/components/ui/live-region/live-region-demo.svelte.d.ts +7 -0
  250. package/dist/components/ui/live-region/live-region.stories.svelte +23 -0
  251. package/dist/components/ui/live-region/live-region.stories.svelte.d.ts +26 -0
  252. package/dist/components/ui/live-region/live-region.svelte +12 -0
  253. package/dist/components/ui/live-region/live-region.svelte.d.ts +8 -0
  254. package/dist/components/ui/popover/popover.stories.svelte +34 -0
  255. package/dist/components/ui/radio-group/radio-group.stories.svelte +58 -0
  256. package/dist/components/ui/radio-group/radio-group.stories.svelte.d.ts +27 -0
  257. package/dist/components/ui/resizable/resizable.stories.svelte +56 -0
  258. package/dist/components/ui/resizable/resizable.stories.svelte.d.ts +27 -0
  259. package/dist/components/ui/select/select.stories.svelte +49 -0
  260. package/dist/components/ui/separator/separator.stories.svelte +18 -0
  261. package/dist/components/ui/sheet/sheet.stories.svelte +34 -0
  262. package/dist/components/ui/sidebar/sidebar-input.svelte.d.ts +1 -1
  263. package/dist/components/ui/sidebar/sidebar-menu-button.svelte +1 -0
  264. package/dist/components/ui/sidebar/sidebar-trigger.svelte +1 -1
  265. package/dist/components/ui/sidebar/sidebar.stories.svelte +72 -0
  266. package/dist/components/ui/sidebar/sidebar.stories.svelte.d.ts +27 -0
  267. package/dist/components/ui/skeleton/skeleton.stories.svelte +39 -0
  268. package/dist/components/ui/skeleton/skeleton.stories.svelte.d.ts +27 -0
  269. package/dist/components/ui/skeleton/skeleton.svelte +6 -0
  270. package/dist/components/ui/sonner/index.d.ts +1 -1
  271. package/dist/components/ui/sonner/index.js +1 -1
  272. package/dist/components/ui/sonner/sonner.stories.svelte +7 -0
  273. package/dist/components/ui/sonner/sonner.svelte +17 -1
  274. package/dist/components/ui/sonner/sonner.svelte.d.ts +6 -0
  275. package/dist/components/ui/spinner/spinner.stories.svelte +30 -0
  276. package/dist/components/ui/spinner/spinner.stories.svelte.d.ts +27 -0
  277. package/dist/components/ui/switch/switch.stories.svelte +56 -0
  278. package/dist/components/ui/switch/switch.stories.svelte.d.ts +27 -0
  279. package/dist/components/ui/table/table-cell.svelte +1 -1
  280. package/dist/components/ui/table/table-head.svelte +1 -1
  281. package/dist/components/ui/table/table.stories.svelte +68 -0
  282. package/dist/components/ui/table/table.stories.svelte.d.ts +27 -0
  283. package/dist/components/ui/table/table.svelte +1 -1
  284. package/dist/components/ui/tabs/tabs.stories.svelte +48 -0
  285. package/dist/components/ui/tabs/tabs.stories.svelte.d.ts +27 -0
  286. package/dist/components/ui/textarea/textarea.stories.svelte +21 -0
  287. package/dist/components/ui/toggle/toggle.stories.svelte +23 -0
  288. package/dist/components/ui/toggle-group/toggle-group.stories.svelte +43 -0
  289. package/dist/components/ui/tooltip/tooltip.stories.svelte +46 -6
  290. package/dist/core/fields/fieldSchemaToTs.d.ts +7 -0
  291. package/dist/core/fields/fieldSchemaToTs.js +234 -90
  292. package/dist/core/fields/layoutUtils.d.ts +4 -1
  293. package/dist/core/fields/layoutUtils.js +41 -4
  294. package/dist/core/fields/resolveSeo.d.ts +70 -0
  295. package/dist/core/fields/resolveSeo.js +88 -0
  296. package/dist/core/fields/seoFieldDescriptor.d.ts +43 -0
  297. package/dist/core/fields/seoFieldDescriptor.js +74 -0
  298. package/dist/core/fields/slugPath.d.ts +13 -0
  299. package/dist/core/fields/slugPath.js +32 -0
  300. package/dist/core/fields/urlUtils.d.ts +8 -0
  301. package/dist/core/fields/urlUtils.js +27 -0
  302. package/dist/core/index.d.ts +1 -0
  303. package/dist/core/index.js +1 -0
  304. package/dist/core/server/entries/operations/create.js +13 -0
  305. package/dist/core/server/entries/operations/get.d.ts +7 -0
  306. package/dist/core/server/entries/operations/get.js +10 -6
  307. package/dist/core/server/entries/operations/slugUniqueness.d.ts +37 -0
  308. package/dist/core/server/entries/operations/slugUniqueness.js +116 -0
  309. package/dist/core/server/entries/operations/update.d.ts +6 -1
  310. package/dist/core/server/entries/operations/update.js +24 -1
  311. package/dist/core/server/fields/slugResolver.d.ts +3 -13
  312. package/dist/core/server/fields/slugResolver.js +8 -37
  313. package/dist/core/server/generator/fields.js +10 -17
  314. package/dist/core/server/generator/formFields.js +2 -1
  315. package/dist/core/server/generator/generator.js +4 -4
  316. package/dist/core/server/generator/utils.d.ts +1 -0
  317. package/dist/core/server/generator/utils.js +4 -0
  318. package/dist/paraglide/messages/_index.d.ts +3 -36
  319. package/dist/paraglide/messages/_index.js +3 -71
  320. package/dist/paraglide/messages/hello_world.d.ts +5 -0
  321. package/dist/paraglide/messages/hello_world.js +33 -0
  322. package/dist/paraglide/messages/login_hello.d.ts +16 -0
  323. package/dist/paraglide/messages/login_hello.js +34 -0
  324. package/dist/paraglide/messages/login_please_login.d.ts +16 -0
  325. package/dist/paraglide/messages/login_please_login.js +34 -0
  326. package/dist/shop/server/orders.d.ts +1 -0
  327. package/dist/shop/server/orders.js +14 -0
  328. package/dist/shop/server/shop-data.d.ts +2 -0
  329. package/dist/shop/server/shop-data.js +20 -5
  330. package/dist/sveltekit/server/handle.js +17 -0
  331. package/dist/types/cms.schema.js +4 -2
  332. package/dist/types/fields.d.ts +35 -0
  333. package/dist/types/index.d.ts +1 -1
  334. package/dist/types/layout.d.ts +35 -2
  335. package/dist/updates/0.26.0/index.d.ts +2 -0
  336. package/dist/updates/0.26.0/index.js +51 -0
  337. package/dist/updates/index.js +3 -1
  338. package/package.json +29 -7
  339. package/dist/admin/client/collection/empty-state.svelte +0 -28
  340. package/dist/admin/client/collection/empty-state.svelte.d.ts +0 -9
  341. package/dist/admin/client/form/submission-status-badge.svelte +0 -41
  342. package/dist/admin/client/form/submission-status-badge.svelte.d.ts +0 -7
  343. package/dist/admin/components/media/file-preview.svelte +0 -51
  344. package/dist/admin/components/media/file-preview.svelte.d.ts +0 -6
  345. package/dist/admin/utils/formatDate.d.ts +0 -5
  346. package/dist/paraglide/messages/en.d.ts +0 -5
  347. package/dist/paraglide/messages/en.js +0 -14
  348. package/dist/paraglide/messages/pl.d.ts +0 -5
  349. package/dist/paraglide/messages/pl.js +0 -14
@@ -0,0 +1,151 @@
1
+ /**
2
+ * AriaCMS — Error message catalog (COGA-friendly, PL/EN).
3
+ *
4
+ * Tone-of-voice (zob. `src/stories/docs/tone-of-voice.mdx`):
5
+ * - Mów do użytkownika, nie o nim
6
+ * - Konkretnie + akcja co zrobić, nie sucha diagnoza
7
+ * - Bez żargonu, kodów HTTP, "UWAGA!", "Validation failed"
8
+ * - Bez strachu — prowadź, nie strasz
9
+ *
10
+ * Wzorzec: "Brak/Problem X — zrób Y, żeby Z."
11
+ *
12
+ * `@public` od v0.26.0. Migracja do paraglide w S10.
13
+ */
14
+ export const errorMessages = {
15
+ validation: {
16
+ required: {
17
+ pl: "Wpisz to pole, żeby zapisać.",
18
+ en: "Fill in this field to save.",
19
+ },
20
+ invalidEmail: {
21
+ pl: "Sprawdź email — wygląda jakby brakowało @ i domeny.",
22
+ en: "Check the email — looks like @ or domain is missing.",
23
+ },
24
+ minLength: (n) => ({
25
+ pl: `Wpisz przynajmniej ${n} ${pluralPL(n, "znak", "znaki", "znaków")}.`,
26
+ en: `Use at least ${n} character${n === 1 ? "" : "s"}.`,
27
+ }),
28
+ passwordWeak: {
29
+ pl: "Hasło musi mieć wielką literę, cyfrę i znak specjalny — to chroni twoje konto.",
30
+ en: "Password needs an uppercase letter, a digit and a special character — for your account safety.",
31
+ },
32
+ passwordsDoNotMatch: {
33
+ pl: "Hasła się różnią — wpisz to samo w obu polach.",
34
+ en: "Passwords don't match — type the same one in both fields.",
35
+ },
36
+ invalidFormat: {
37
+ pl: "Sprawdź wpisaną wartość — format wygląda na inny niż oczekiwany.",
38
+ en: "Check the value — the format looks unexpected.",
39
+ },
40
+ },
41
+ network: {
42
+ offline: {
43
+ pl: "Brak połączenia z internetem — sprawdź sieć i spróbuj ponownie.",
44
+ en: "No internet connection — check your network and try again.",
45
+ },
46
+ timeout: {
47
+ pl: "Serwer odpowiada wolno — spróbuj jeszcze raz za chwilę.",
48
+ en: "The server is slow to respond — try again in a moment.",
49
+ },
50
+ generic: {
51
+ pl: "Nie udało się połączyć — spróbuj jeszcze raz.",
52
+ en: "Couldn't connect — try again.",
53
+ },
54
+ },
55
+ permission: {
56
+ denied: {
57
+ pl: "Nie masz dostępu do tego działania — poproś administratora o uprawnienia.",
58
+ en: "You don't have access to this action — ask an admin for permissions.",
59
+ },
60
+ sessionExpired: {
61
+ pl: "Sesja wygasła — zaloguj się ponownie, żeby kontynuować.",
62
+ en: "Your session expired — sign in again to continue.",
63
+ },
64
+ },
65
+ conflict: {
66
+ nameTaken: {
67
+ pl: "Ta nazwa jest już zajęta — wybierz inną.",
68
+ en: "That name is already taken — pick a different one.",
69
+ },
70
+ alreadyExists: {
71
+ pl: "Taki wpis już istnieje — sprawdź czy nie tworzysz duplikatu.",
72
+ en: "An item like this already exists — check that you're not duplicating it.",
73
+ },
74
+ staleData: {
75
+ pl: "Ktoś zaktualizował te dane — odśwież stronę i wprowadź zmiany ponownie.",
76
+ en: "Someone updated this data — refresh the page and try again.",
77
+ },
78
+ },
79
+ server: {
80
+ saveFailed: {
81
+ pl: "Nie udało się zapisać — spróbuj jeszcze raz.",
82
+ en: "Couldn't save — try again.",
83
+ },
84
+ loadFailed: {
85
+ pl: "Nie udało się wczytać danych — odśwież stronę.",
86
+ en: "Couldn't load the data — refresh the page.",
87
+ },
88
+ uploadFailed: {
89
+ pl: "Nie udało się przesłać pliku — spróbuj jeszcze raz.",
90
+ en: "Couldn't upload the file — try again.",
91
+ },
92
+ generic: {
93
+ pl: "Coś poszło nie tak — spróbuj jeszcze raz.",
94
+ en: "Something went wrong — try again.",
95
+ },
96
+ },
97
+ upload: {
98
+ fileTooLarge: (maxMB) => ({
99
+ pl: `Plik za duży — maksymalnie ${maxMB} MB. Spróbuj go skompresować.`,
100
+ en: `File too large — max ${maxMB} MB. Try compressing it.`,
101
+ }),
102
+ unsupportedType: {
103
+ pl: "Ten format nie jest obsługiwany — wybierz inny plik.",
104
+ en: "This format isn't supported — pick a different file.",
105
+ },
106
+ },
107
+ notFound: {
108
+ entry: {
109
+ pl: "Nie znaleziono wpisu — mógł zostać usunięty lub przeniesiony.",
110
+ en: "Entry not found — it may have been deleted or moved.",
111
+ },
112
+ page: {
113
+ pl: "Nie znaleziono strony — sprawdź adres lub wróć do panelu.",
114
+ en: "Page not found — check the address or return to the dashboard.",
115
+ },
116
+ },
117
+ };
118
+ export const successMessages = {
119
+ saved: {
120
+ pl: "Zapisano.",
121
+ en: "Saved.",
122
+ },
123
+ published: {
124
+ pl: "Opublikowano — zmiany są już widoczne na stronie.",
125
+ en: "Published — changes are now live.",
126
+ },
127
+ uploaded: {
128
+ pl: "Plik przesłany.",
129
+ en: "File uploaded.",
130
+ },
131
+ deleted: {
132
+ pl: "Usunięto.",
133
+ en: "Deleted.",
134
+ },
135
+ };
136
+ /**
137
+ * Pobierz zlokalizowany komunikat dla interfejsu.
138
+ * @example pickError(errorMessages.server.saveFailed, lang.current)
139
+ */
140
+ export function pickError(msg, lang) {
141
+ return msg[lang];
142
+ }
143
+ function pluralPL(n, singular, few, many) {
144
+ const mod10 = n % 10;
145
+ const mod100 = n % 100;
146
+ if (n === 1)
147
+ return singular;
148
+ if (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20))
149
+ return few;
150
+ return many;
151
+ }
@@ -1,3 +1,4 @@
1
+ import type { Localized } from '../../types/languages.js';
1
2
  import { type RawEntry } from '../../types/entries.js';
2
3
  export declare const getRawEntries: import("@sveltejs/kit").RemoteQueryFunction<{
3
4
  ids?: string[] | undefined;
@@ -25,12 +26,30 @@ export declare const getEntries: import("@sveltejs/kit").RemoteQueryFunction<{
25
26
  direction: "asc" | "desc";
26
27
  } | undefined;
27
28
  }, import("../../types/entries.js").Entry[]>;
29
+ /**
30
+ * Cross-collection search for the URL field's internal-link picker. Matches
31
+ * the term (case-insensitive, OR) against each collection's admin-title field
32
+ * (`entryAdminTitle`), its `slug`-type field, and `seo.slug` / `seo.title`.
33
+ * The previous `getEntries({ dataLike })` call was a no-op because it never
34
+ * passed a collection slug (`if (!input.slug) return []`).
35
+ */
36
+ export declare const searchLinkableEntries: import("@sveltejs/kit").RemoteQueryFunction<{
37
+ term: string;
38
+ language?: string | undefined;
39
+ }, {
40
+ _id: string;
41
+ _slug: string;
42
+ _url?: string;
43
+ title: string;
44
+ slug: string;
45
+ }[]>;
28
46
  export declare const getEntryLabels: import("@sveltejs/kit").RemoteQueryFunction<{
29
47
  slug: string;
30
48
  ids?: string[] | undefined;
31
49
  search?: string | undefined;
32
50
  limit?: number | undefined;
33
51
  status?: "draft" | "published" | "all" | undefined;
52
+ language?: string | undefined;
34
53
  }, {
35
54
  id: string;
36
55
  label: string;
@@ -58,7 +77,7 @@ export declare const updateEntryVersionCommand: import("@sveltejs/kit").RemoteCo
58
77
  entryId: string;
59
78
  lang: string;
60
79
  data: Record<string, unknown>;
61
- type: "draft" | "published-now" | "published-scheduled" | "cancel-published";
80
+ type: "draft" | "published-now" | "published-scheduled" | "cancel-published" | "cancel-scheduled";
62
81
  scheduledAt?: Date | undefined;
63
82
  }, Promise<import("../../types/entries.js").DbEntryVersion | undefined>>;
64
83
  export declare const updateEntryCommand: import("@sveltejs/kit").RemoteCommand<{
@@ -79,18 +98,54 @@ export declare const getEntryVersion: import("@sveltejs/kit").RemoteQueryFunctio
79
98
  id: string;
80
99
  language: string;
81
100
  }, import("../../types/entries.js").Entry | null>;
82
- export declare const getRecentEntries: import("@sveltejs/kit").RemoteQueryFunction<number | undefined, {
101
+ /**
102
+ * `containerLabel` carries the collection's plural label for collection entries
103
+ * and the single's name for singles; clients localise it. For collection entries
104
+ * `label` is the entry's own title (or `null` to fall back to the slug); for
105
+ * singles `label` is always `null` — the title shown is `containerLabel`.
106
+ */
107
+ export declare const getRecentEntries: import("@sveltejs/kit").RemoteQueryFunction<{
108
+ language: string;
109
+ limit?: number | undefined;
110
+ }, {
83
111
  entryId: string;
84
112
  slug: string;
85
- collectionLabel: Record<string, string>;
113
+ kind: "collection" | "single";
114
+ containerLabel: Localized;
86
115
  label: string | null;
116
+ labelLanguage: string | null;
87
117
  status: "draft" | "published" | "scheduled";
88
118
  updatedAt: Date;
89
119
  }[]>;
90
- export declare const getRecentActivity: import("@sveltejs/kit").RemoteQueryFunction<number | undefined, {
120
+ export declare const getRecentActivity: import("@sveltejs/kit").RemoteQueryFunction<{
121
+ language: string;
122
+ limit?: number | undefined;
123
+ }, {
91
124
  type: "entry_edit";
92
125
  entryId: string;
93
126
  slug: string;
127
+ kind: "collection" | "single";
94
128
  label: string | null;
129
+ labelLanguage: string | null;
130
+ containerLabel: Localized;
131
+ editorId: string | null;
132
+ editorName: string | null;
95
133
  updatedAt: Date;
96
134
  }[]>;
135
+ /**
136
+ * Global quick-search over entry titles plus single names. Titles are matched
137
+ * and returned in the requested `language`, falling back to any available
138
+ * translation (`titleLanguage` reports which one was used). Used by the sidebar
139
+ * command palette. A full multi-language FTS search is tracked separately.
140
+ */
141
+ export declare const searchEntries: import("@sveltejs/kit").RemoteQueryFunction<{
142
+ query: string;
143
+ language: string;
144
+ }, {
145
+ entryId: string;
146
+ title: string;
147
+ titleLanguage: string | null;
148
+ kind: "collection" | "single";
149
+ containerSlug: string;
150
+ containerLabel: Localized;
151
+ }[]>;
@@ -1,10 +1,16 @@
1
1
  import { command, query } from '$app/server';
2
- import { getAtPath } from '../utils/objectPath.js';
2
+ import { error } from '@sveltejs/kit';
3
+ import { CmsError } from '../../core/errors.js';
4
+ import { getRawCollectionEntryLabelWithLanguage } from '../utils/entryLabel.js';
5
+ import { db } from '../../server/db/index.js';
6
+ import { user } from '../../server/db/schema/auth-schema.js';
7
+ import { inArray } from 'drizzle-orm';
3
8
  import { createEntry as createEntryOperation, createEntrySchema, createEntryVersion } from '../../core/server/entries/operations/create.js';
4
9
  import { _getRawEntries as getRawEntriesOperation, _countRawEntries as countRawEntriesOperation, _getRawEntry as getRawEntryOperation, _getRawEntryOrThrow as getRawEntryOrThrow, _getDbEntry as getDbEntry, getEntryVersion as getEntryVersionOperation, getEntryLabels as getEntryLabelsOperation } from '../../core/server/entries/operations/get.js';
5
10
  import { resolveEntries, resolveEntry } from '../../core/server/entries/operations/resolveEntry.js';
6
11
  import { getCMS } from '../../core/cms.js';
7
- import { pruneOldDraftVersions, unpublishEntryLang, upsertDraftVersion, updateEntry, updateEntrySchema, updateEntryVersionCommandTypes } from '../../core/server/entries/operations/update.js';
12
+ import { getFieldsFromConfig } from '../../core/fields/layoutUtils.js';
13
+ import { pruneOldDraftVersions, unpublishEntryLang, cancelScheduledEntryLang, upsertDraftVersion, updateEntry, updateEntrySchema, updateEntryVersionCommandTypes } from '../../core/server/entries/operations/update.js';
8
14
  import z from 'zod';
9
15
  import { requireAuth } from './middleware/auth.js';
10
16
  import { stableSlotReorder } from './reorder.js';
@@ -55,12 +61,84 @@ export const getEntries = query(z.object({
55
61
  orderBy: input.orderBy
56
62
  });
57
63
  });
64
+ /**
65
+ * Cross-collection search for the URL field's internal-link picker. Matches
66
+ * the term (case-insensitive, OR) against each collection's admin-title field
67
+ * (`entryAdminTitle`), its `slug`-type field, and `seo.slug` / `seo.title`.
68
+ * The previous `getEntries({ dataLike })` call was a no-op because it never
69
+ * passed a collection slug (`if (!input.slug) return []`).
70
+ */
71
+ export const searchLinkableEntries = query(z.object({ term: z.string(), language: z.string().optional() }), async ({ term, language }) => {
72
+ requireAuth();
73
+ const q = term.trim();
74
+ if (q.length < 2)
75
+ return [];
76
+ const cms = getCMS();
77
+ // Internal links are language-agnostic (resolved by id at render), but
78
+ // resolveEntries is strict per locale — so an entry that only has a
79
+ // `pl` version is invisible when searching the default `en`. Scan all
80
+ // languages (preferred one first) and dedupe by entry id.
81
+ const langs = [
82
+ ...(language ? [language] : []),
83
+ ...cms.languages.filter((l) => l !== language)
84
+ ];
85
+ const results = [];
86
+ for (const config of Object.values(cms.collections)) {
87
+ if (config.type !== 'collection')
88
+ continue;
89
+ const fields = getFieldsFromConfig(config);
90
+ const titleField = config.entryAdminTitle;
91
+ const slugField = fields.find((f) => f.type === 'slug')?.slug;
92
+ const ilike = { seo: { slug: q, title: q } };
93
+ if (titleField)
94
+ ilike[titleField] = q;
95
+ if (slugField)
96
+ ilike[slugField] = q;
97
+ for (const locale of langs) {
98
+ let entries;
99
+ try {
100
+ entries = await resolveEntries({
101
+ collection: config.slug,
102
+ locale,
103
+ status: 'published',
104
+ filter: { dataILikeOr: ilike }
105
+ });
106
+ }
107
+ catch {
108
+ continue;
109
+ }
110
+ for (const e of entries) {
111
+ const seo = e.seo;
112
+ const rawTitle = titleField ? e[titleField] : undefined;
113
+ const title = (typeof rawTitle === 'string' && rawTitle) ||
114
+ (typeof seo?.title === 'string' && seo.title) ||
115
+ '';
116
+ const rawSlug = slugField ? e[slugField] : undefined;
117
+ const slug = (typeof rawSlug === 'string' && rawSlug) ||
118
+ (typeof seo?.slug === 'string' && seo.slug) ||
119
+ '';
120
+ results.push({
121
+ _id: e._id,
122
+ _slug: config.slug,
123
+ _url: typeof e._url === 'string' ? e._url : undefined,
124
+ title,
125
+ slug
126
+ });
127
+ }
128
+ }
129
+ }
130
+ const seen = new Set();
131
+ return results
132
+ .filter((r) => (seen.has(r._id) ? false : (seen.add(r._id), true)))
133
+ .slice(0, 8);
134
+ });
58
135
  export const getEntryLabels = query(z.object({
59
136
  slug: z.string(),
60
137
  ids: z.array(z.string().uuid()).optional(),
61
138
  search: z.string().optional(),
62
139
  limit: z.number().int().positive().optional(),
63
- status: z.enum(['published', 'draft', 'all']).optional()
140
+ status: z.enum(['published', 'draft', 'all']).optional(),
141
+ language: z.string().optional()
64
142
  }), async (input) => {
65
143
  return getEntryLabelsOperation(input);
66
144
  });
@@ -128,37 +206,51 @@ const updateEntryVersionCommandSchema = z.object({
128
206
  export const updateEntryVersionCommand = command(updateEntryVersionCommandSchema, async (input) => {
129
207
  const { user } = requireAuth();
130
208
  let result;
131
- switch (input.type) {
132
- case 'draft':
133
- result = await upsertDraftVersion(input.entryId, input.data, input.lang, { skipValidation: true });
134
- break;
135
- case 'published-now': {
136
- // Create a new published version for this language
137
- result = await createEntryVersion({
209
+ // Publish failures should reach the client with a structured body so the
210
+ // editor can pin the message to the offending field. SvelteKit hides any
211
+ // thrown error that isn't created via `error()`, so wrap publish writes.
212
+ const runPublish = async (publishedAt) => {
213
+ try {
214
+ return await createEntryVersion({
138
215
  entryId: input.entryId,
139
216
  lang: input.lang,
140
217
  data: input.data,
141
- publishedAt: new Date(),
218
+ publishedAt,
142
219
  publishedBy: user.id
143
220
  });
221
+ }
222
+ catch (e) {
223
+ if (e instanceof CmsError && e.code === 'SLUG_DUPLICATE') {
224
+ error(409, {
225
+ message: e.message,
226
+ code: e.code,
227
+ path: typeof e.context.path === 'string' ? e.context.path : undefined
228
+ });
229
+ }
230
+ throw e;
231
+ }
232
+ };
233
+ switch (input.type) {
234
+ case 'draft':
235
+ result = await upsertDraftVersion(input.entryId, input.data, input.lang, { skipValidation: true });
236
+ break;
237
+ case 'published-now': {
238
+ result = await runPublish(new Date());
144
239
  break;
145
240
  }
146
241
  case 'published-scheduled': {
147
242
  if (!input.scheduledAt) {
148
243
  throw new Error('scheduledAt is required for scheduled publishing');
149
244
  }
150
- result = await createEntryVersion({
151
- entryId: input.entryId,
152
- lang: input.lang,
153
- data: input.data,
154
- publishedAt: input.scheduledAt,
155
- publishedBy: user.id
156
- });
245
+ result = await runPublish(input.scheduledAt);
157
246
  break;
158
247
  }
159
248
  case 'cancel-published':
160
249
  await unpublishEntryLang(input.entryId, input.lang);
161
250
  break;
251
+ case 'cancel-scheduled':
252
+ await cancelScheduledEntryLang(input.entryId, input.lang);
253
+ break;
162
254
  }
163
255
  // Prune old draft versions only after publish/schedule (drafts upsert in-place)
164
256
  if (input.type !== 'draft') {
@@ -210,7 +302,13 @@ export const getEntryVersion = query(z.object({
210
302
  }), async (input) => {
211
303
  return getEntryVersionOperation(input);
212
304
  });
213
- export const getRecentEntries = query(z.number().default(6), async (limit) => {
305
+ /**
306
+ * `containerLabel` carries the collection's plural label for collection entries
307
+ * and the single's name for singles; clients localise it. For collection entries
308
+ * `label` is the entry's own title (or `null` to fall back to the slug); for
309
+ * singles `label` is always `null` — the title shown is `containerLabel`.
310
+ */
311
+ export const getRecentEntries = query(z.object({ limit: z.number().default(6), language: z.string() }), async ({ limit, language }) => {
214
312
  const entries = await getRawEntriesOperation({
215
313
  orderBy: { column: 'updatedAt', direction: 'desc' },
216
314
  limit
@@ -223,30 +321,22 @@ export const getRecentEntries = query(z.number().default(6), async (limit) => {
223
321
  catch {
224
322
  // orphaned entry
225
323
  }
226
- const latestVersion = entry.versions[0];
324
+ const kind = config?.type === 'single' ? 'single' : 'collection';
227
325
  let label = null;
228
- if (config && config.type === 'collection' && config.entryAdminTitle && latestVersion) {
229
- const titleData = getAtPath(latestVersion.data, config.entryAdminTitle);
230
- if (typeof titleData === 'string') {
231
- label = titleData || '';
326
+ let labelLanguage = null;
327
+ if (config && config.type === 'collection') {
328
+ const resolved = getRawCollectionEntryLabelWithLanguage(entry, config, language);
329
+ if (resolved.label !== entry.id) {
330
+ label = resolved.label;
331
+ labelLanguage = resolved.language;
232
332
  }
233
333
  }
234
- if (config && config.type === 'single' && config.label) {
235
- if (typeof config.label === 'object') {
236
- label = String(Object.values(config.label)[0] || '');
237
- }
238
- else {
239
- label = config.label;
240
- }
334
+ let containerLabel = { pl: entry.slug, en: entry.slug };
335
+ if (config?.type === 'collection' && config.labels) {
336
+ containerLabel = config.labels.plural ?? config.labels.singular ?? containerLabel;
241
337
  }
242
- let collectionLabel = { pl: entry.slug, en: entry.slug };
243
- if (config) {
244
- if (config.type === 'collection' && config.labels) {
245
- collectionLabel = config.labels.plural ?? config.labels.singular ?? collectionLabel;
246
- }
247
- else if (config.type === 'single' && config.label) {
248
- collectionLabel = config.label;
249
- }
338
+ else if (config?.type === 'single' && config.label) {
339
+ containerLabel = config.label;
250
340
  }
251
341
  const hasPublished = Object.values(entry.publishedVersions).some((v) => v != null);
252
342
  const hasScheduled = Object.values(entry.scheduledVersions).some((v) => v != null);
@@ -258,43 +348,130 @@ export const getRecentEntries = query(z.number().default(6), async (limit) => {
258
348
  return {
259
349
  entryId: entry.id,
260
350
  slug: entry.slug,
261
- collectionLabel,
351
+ kind,
352
+ containerLabel,
262
353
  label,
354
+ labelLanguage,
263
355
  status,
264
356
  updatedAt: entry.updatedAt
265
357
  };
266
358
  });
267
359
  });
268
- export const getRecentActivity = query(z.number().default(10), async (limit) => {
360
+ export const getRecentActivity = query(z.object({ limit: z.number().default(10), language: z.string() }), async ({ limit, language }) => {
269
361
  const entries = await getRawEntriesOperation({});
270
362
  const activity = [];
271
363
  for (const entry of entries) {
272
364
  const latestVersion = entry.versions[0];
273
- if (latestVersion) {
274
- const config = getCMS().getBySlug(entry.slug);
275
- let label = null;
276
- if (config && config.type === 'collection' && config.entryAdminTitle) {
277
- const titleData = getAtPath(latestVersion.data, config.entryAdminTitle);
278
- if (typeof titleData === 'string') {
279
- label = titleData || null;
280
- }
365
+ if (!latestVersion)
366
+ continue;
367
+ let config = null;
368
+ try {
369
+ config = getCMS().getBySlug(entry.slug);
370
+ }
371
+ catch {
372
+ // orphaned entry — still surface the edit, just without a friendly label
373
+ }
374
+ const kind = config?.type === 'single' ? 'single' : 'collection';
375
+ let label = null;
376
+ let labelLanguage = null;
377
+ let containerLabel = { pl: entry.slug, en: entry.slug };
378
+ if (config && config.type === 'collection') {
379
+ const resolved = getRawCollectionEntryLabelWithLanguage(entry, config, language);
380
+ if (resolved.label !== entry.id) {
381
+ label = resolved.label;
382
+ labelLanguage = resolved.language;
281
383
  }
282
- if (config && config.type === 'single' && config.label) {
283
- if (typeof config.label === 'object') {
284
- label = String(Object.values(config.label)[0] || '');
285
- }
286
- else {
287
- label = config.label;
288
- }
384
+ if (config.labels) {
385
+ containerLabel = config.labels.singular ?? config.labels.plural ?? containerLabel;
289
386
  }
290
- activity.push({
291
- type: 'entry_edit',
387
+ }
388
+ else if (config?.type === 'single' && config.label) {
389
+ containerLabel = config.label;
390
+ }
391
+ activity.push({
392
+ type: 'entry_edit',
393
+ entryId: entry.id,
394
+ slug: entry.slug,
395
+ kind,
396
+ label,
397
+ labelLanguage,
398
+ containerLabel,
399
+ editorId: latestVersion.createdBy ?? null,
400
+ editorName: null,
401
+ updatedAt: latestVersion.createdAt
402
+ });
403
+ }
404
+ const recent = activity
405
+ .sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())
406
+ .slice(0, limit);
407
+ // resolve editor names for the items we're returning
408
+ const editorIds = [...new Set(recent.map((a) => a.editorId).filter((id) => !!id))];
409
+ if (editorIds.length > 0) {
410
+ const rows = await db
411
+ .select({ id: user.id, name: user.name })
412
+ .from(user)
413
+ .where(inArray(user.id, editorIds));
414
+ const nameById = new Map(rows.map((r) => [r.id, r.name]));
415
+ for (const item of recent) {
416
+ if (item.editorId)
417
+ item.editorName = nameById.get(item.editorId) ?? null;
418
+ }
419
+ }
420
+ return recent;
421
+ });
422
+ /**
423
+ * Global quick-search over entry titles plus single names. Titles are matched
424
+ * and returned in the requested `language`, falling back to any available
425
+ * translation (`titleLanguage` reports which one was used). Used by the sidebar
426
+ * command palette. A full multi-language FTS search is tracked separately.
427
+ */
428
+ export const searchEntries = query(z.object({ query: z.string(), language: z.string() }), async ({ query: rawQuery, language }) => {
429
+ requireAuth();
430
+ const q = rawQuery.trim();
431
+ if (q.length < 2)
432
+ return [];
433
+ const cms = getCMS();
434
+ const needle = q.toLocaleLowerCase();
435
+ const results = [];
436
+ for (const config of Object.values(cms.collections)) {
437
+ const containerLabel = config.labels?.plural ?? config.labels?.singular ?? { pl: config.slug, en: config.slug };
438
+ const entries = await getRawEntriesOperation({ slug: config.slug });
439
+ for (const entry of entries) {
440
+ const resolved = getRawCollectionEntryLabelWithLanguage(entry, config, language);
441
+ if (resolved.label === entry.id)
442
+ continue; // no usable title
443
+ if (!resolved.label.toLocaleLowerCase().includes(needle))
444
+ continue;
445
+ results.push({
292
446
  entryId: entry.id,
293
- slug: entry.slug,
294
- label,
295
- updatedAt: latestVersion.createdAt
447
+ title: resolved.label,
448
+ titleLanguage: resolved.language,
449
+ kind: 'collection',
450
+ containerSlug: config.slug,
451
+ containerLabel
296
452
  });
297
453
  }
298
454
  }
299
- return activity.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()).slice(0, limit);
455
+ for (const config of Object.values(cms.singles)) {
456
+ const labelObj = config.label;
457
+ const names = labelObj && typeof labelObj === 'object' ? Object.values(labelObj) : labelObj ? [labelObj] : [];
458
+ const matches = config.slug.toLocaleLowerCase().includes(needle) ||
459
+ names.some((n) => typeof n === 'string' && n.toLocaleLowerCase().includes(needle));
460
+ if (!matches)
461
+ continue;
462
+ const localizedTitle = labelObj && typeof labelObj === 'object' ? labelObj[language] : undefined;
463
+ const title = localizedTitle ??
464
+ (labelObj && typeof labelObj === 'object'
465
+ ? (Object.values(labelObj)[0] ?? config.slug)
466
+ : labelObj || config.slug);
467
+ results.push({
468
+ entryId: config.slug,
469
+ title,
470
+ titleLanguage: localizedTitle ? language : null,
471
+ kind: 'single',
472
+ containerSlug: config.slug,
473
+ containerLabel: labelObj && typeof labelObj === 'object' ? labelObj : { pl: title, en: title }
474
+ });
475
+ }
476
+ return results.slice(0, 20);
300
477
  });