includio-cms 0.25.0 → 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,102 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import FileText from '@tabler/icons-svelte/icons/file-text';
4
+ import Loader2 from '@tabler/icons-svelte/icons/loader-2';
5
+ import AlertCircle from '@tabler/icons-svelte/icons/alert-circle';
6
+ import Plus from '@tabler/icons-svelte/icons/plus';
7
+ import Refresh from '@tabler/icons-svelte/icons/refresh';
8
+ import Button from '../../../components/ui/button/button.svelte';
9
+ import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
10
+ import { errorMessages, pickError } from '../../i18n/errors.js';
11
+ import type { InterfaceLanguage } from '../../../types/languages.js';
12
+
13
+ type Props = {
14
+ kind: 'empty' | 'loading' | 'error';
15
+ title?: string;
16
+ description?: string;
17
+ ctaLabel?: string;
18
+ onCta?: () => void;
19
+ icon?: Snippet;
20
+ };
21
+
22
+ let { kind, title, description, ctaLabel, onCta, icon }: Props = $props();
23
+
24
+ const interfaceLanguage = useInterfaceLanguage();
25
+
26
+ const defaults: Record<InterfaceLanguage, { loading: string; error: string; empty: string; retry: string }> = {
27
+ en: {
28
+ loading: 'Loading…',
29
+ error: pickError(errorMessages.network.generic, 'en'),
30
+ empty: 'Nothing here yet',
31
+ retry: 'Try again'
32
+ },
33
+ pl: {
34
+ loading: 'Ładowanie…',
35
+ error: pickError(errorMessages.network.generic, 'pl'),
36
+ empty: 'Nic tu jeszcze nie ma',
37
+ retry: 'Spróbuj ponownie'
38
+ }
39
+ };
40
+
41
+ const t = $derived(defaults[interfaceLanguage.current]);
42
+ const resolvedTitle = $derived(title ?? (kind === 'loading' ? t.loading : kind === 'error' ? t.error : t.empty));
43
+ const resolvedCtaLabel = $derived(ctaLabel ?? (kind === 'error' ? t.retry : undefined));
44
+ </script>
45
+
46
+ {#if kind === 'loading'}
47
+ <div
48
+ class="flex flex-col items-center justify-center py-16 px-4 text-center"
49
+ role="status"
50
+ aria-live="polite"
51
+ aria-busy="true"
52
+ >
53
+ {#if icon}{@render icon()}{:else}
54
+ <div class="flex items-center justify-center w-[72px] h-[72px] rounded-2xl bg-muted mb-5">
55
+ <Loader2 class="size-8 text-muted-foreground animate-spin" />
56
+ </div>
57
+ {/if}
58
+ <h3 class="text-lg font-bold text-foreground mb-2">{resolvedTitle}</h3>
59
+ {#if description}
60
+ <p class="text-sm text-muted-foreground max-w-[360px]">{description}</p>
61
+ {/if}
62
+ </div>
63
+ {:else if kind === 'error'}
64
+ <div
65
+ class="flex flex-col items-center justify-center py-16 px-4 text-center"
66
+ role="alert"
67
+ >
68
+ {#if icon}{@render icon()}{:else}
69
+ <div class="flex items-center justify-center w-[72px] h-[72px] rounded-2xl bg-destructive-bg mb-5">
70
+ <AlertCircle class="size-8 text-destructive" />
71
+ </div>
72
+ {/if}
73
+ <h3 class="text-lg font-bold text-foreground mb-2">{resolvedTitle}</h3>
74
+ {#if description}
75
+ <p class="text-sm text-muted-foreground max-w-[360px] mb-6">{description}</p>
76
+ {/if}
77
+ {#if resolvedCtaLabel && onCta}
78
+ <Button variant="default" onclick={onCta}>
79
+ <Refresh class="size-4 mr-1.5" />
80
+ {resolvedCtaLabel}
81
+ </Button>
82
+ {/if}
83
+ </div>
84
+ {:else}
85
+ <div class="flex flex-col items-center justify-center py-16 px-4 text-center">
86
+ {#if icon}{@render icon()}{:else}
87
+ <div class="flex items-center justify-center w-[72px] h-[72px] rounded-2xl bg-muted mb-5">
88
+ <FileText class="size-8 text-muted-foreground" />
89
+ </div>
90
+ {/if}
91
+ <h3 class="text-lg font-bold text-foreground mb-2">{resolvedTitle}</h3>
92
+ {#if description}
93
+ <p class="text-sm text-muted-foreground max-w-[360px] mb-6">{description}</p>
94
+ {/if}
95
+ {#if resolvedCtaLabel && onCta}
96
+ <Button variant="default" onclick={onCta} data-testid="create-entry-button">
97
+ <Plus class="size-4 mr-1.5" />
98
+ {resolvedCtaLabel}
99
+ </Button>
100
+ {/if}
101
+ </div>
102
+ {/if}
@@ -0,0 +1,12 @@
1
+ import type { Snippet } from 'svelte';
2
+ type Props = {
3
+ kind: 'empty' | 'loading' | 'error';
4
+ title?: string;
5
+ description?: string;
6
+ ctaLabel?: string;
7
+ onCta?: () => void;
8
+ icon?: Snippet;
9
+ };
10
+ declare const StateDisplay: import("svelte").Component<Props, {}, "">;
11
+ type StateDisplay = ReturnType<typeof StateDisplay>;
12
+ export default StateDisplay;
@@ -1,17 +1,46 @@
1
1
  <script lang="ts">
2
2
  import type { EntryStatus } from '../../../types/entries.js';
3
+ import type { OrderStatus } from '../../../shop/types.js';
3
4
  import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
4
5
  import type { InterfaceLanguage } from '../../../types/languages.js';
5
6
 
7
+ /**
8
+ * StatusBadge accepts three usage modes (variants):
9
+ * - entry (default): pass `status` as `EntryStatus`
10
+ * - order: pass `variant="order"` and `status` as `OrderStatus`
11
+ * - boolean: pass `variant="boolean"` and `active: boolean`
12
+ *
13
+ * Type stays permissive (FlatProps) so consumers can pass any combination;
14
+ * runtime narrows by `variant` and falls back to muted style on unknown.
15
+ */
6
16
  type Props = {
7
- status: EntryStatus;
17
+ variant?: 'entry' | 'order' | 'boolean';
18
+ status?: EntryStatus | OrderStatus;
19
+ active?: boolean;
20
+ activeLabel?: string;
21
+ inactiveLabel?: string;
8
22
  };
9
23
 
10
- let { status }: Props = $props();
24
+ let {
25
+ variant = 'entry',
26
+ status,
27
+ active,
28
+ activeLabel,
29
+ inactiveLabel
30
+ }: Props = $props();
11
31
 
12
32
  const interfaceLanguage = useInterfaceLanguage();
13
33
 
14
- const labels: Record<InterfaceLanguage, Record<EntryStatus, string>> = {
34
+ type StatusStyle = { dot: string; classes: string };
35
+
36
+ const entryStyles: Record<EntryStatus, StatusStyle> = {
37
+ published: { dot: '●', classes: 'bg-success-bg text-success' },
38
+ draft: { dot: '○', classes: 'bg-muted text-muted-foreground' },
39
+ scheduled: { dot: '◐', classes: 'bg-warning-bg text-warning' },
40
+ archived: { dot: '◎', classes: 'bg-muted text-muted-foreground' }
41
+ };
42
+
43
+ const entryLabels: Record<InterfaceLanguage, Record<EntryStatus, string>> = {
15
44
  en: {
16
45
  published: 'Published',
17
46
  draft: 'Draft',
@@ -26,17 +55,76 @@
26
55
  }
27
56
  };
28
57
 
29
- const statusConfig: Record<EntryStatus, { dot: string; classes: string }> = {
30
- published: { dot: '●', classes: 'bg-success-bg text-success' },
31
- draft: { dot: '', classes: 'bg-muted text-muted-foreground' },
32
- scheduled: { dot: '', classes: 'bg-warning-bg text-warning' },
33
- archived: { dot: '', classes: 'bg-muted text-muted-foreground' }
58
+ const orderStyles: Record<OrderStatus, StatusStyle> = {
59
+ new: { dot: '●', classes: 'bg-info-bg text-info' },
60
+ awaitingPayment: { dot: '', classes: 'bg-warning-bg text-warning' },
61
+ paid: { dot: '', classes: 'bg-success-bg text-success' },
62
+ preparing: { dot: '', classes: 'bg-warning-bg text-warning' },
63
+ sent: { dot: '●', classes: 'bg-info-bg text-info' },
64
+ done: { dot: '●', classes: 'bg-success-bg text-success' },
65
+ cancelled: { dot: '○', classes: 'bg-muted text-muted-foreground' },
66
+ paymentRejected: { dot: '●', classes: 'bg-destructive-bg text-destructive' },
67
+ refunded: { dot: '○', classes: 'bg-muted text-muted-foreground' }
34
68
  };
69
+
70
+ const orderLabels: Record<InterfaceLanguage, Record<OrderStatus, string>> = {
71
+ en: {
72
+ new: 'New',
73
+ awaitingPayment: 'Awaiting payment',
74
+ paid: 'Paid',
75
+ preparing: 'Preparing',
76
+ sent: 'Sent',
77
+ done: 'Done',
78
+ cancelled: 'Cancelled',
79
+ paymentRejected: 'Payment rejected',
80
+ refunded: 'Refunded'
81
+ },
82
+ pl: {
83
+ new: 'Nowe',
84
+ awaitingPayment: 'Oczekuje płatności',
85
+ paid: 'Opłacone',
86
+ preparing: 'W przygotowaniu',
87
+ sent: 'Wysłane',
88
+ done: 'Zrealizowane',
89
+ cancelled: 'Anulowane',
90
+ paymentRejected: 'Płatność odrzucona',
91
+ refunded: 'Zwrócone'
92
+ }
93
+ };
94
+
95
+ const booleanDefaults: Record<InterfaceLanguage, { active: string; inactive: string }> = {
96
+ en: { active: 'Active', inactive: 'Inactive' },
97
+ pl: { active: 'Aktywny', inactive: 'Nieaktywny' }
98
+ };
99
+
100
+ const fallbackStyle: StatusStyle = { dot: '○', classes: 'bg-muted text-muted-foreground' };
101
+
102
+ const computed = $derived.by(() => {
103
+ const lang = interfaceLanguage.current;
104
+ if (variant === 'order') {
105
+ const s = status as OrderStatus | undefined;
106
+ if (!s || !(s in orderStyles)) return { style: fallbackStyle, label: String(s ?? '') };
107
+ return { style: orderStyles[s], label: orderLabels[lang][s] };
108
+ }
109
+ if (variant === 'boolean') {
110
+ const isActive = active === true;
111
+ const fallback = booleanDefaults[lang];
112
+ return {
113
+ style: isActive
114
+ ? { dot: '●', classes: 'bg-success-bg text-success' }
115
+ : { dot: '○', classes: 'bg-muted text-muted-foreground' },
116
+ label: isActive ? (activeLabel ?? fallback.active) : (inactiveLabel ?? fallback.inactive)
117
+ };
118
+ }
119
+ const s = status as EntryStatus | undefined;
120
+ if (!s || !(s in entryStyles)) return { style: fallbackStyle, label: String(s ?? '') };
121
+ return { style: entryStyles[s], label: entryLabels[lang][s] };
122
+ });
35
123
  </script>
36
124
 
37
125
  <span
38
- class="inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-[11px] font-semibold leading-none {statusConfig[status].classes}"
126
+ class="inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-[11px] font-semibold leading-none {computed.style.classes}"
39
127
  >
40
- <span class="text-[9px]" aria-hidden="true">{statusConfig[status].dot}</span>
41
- {labels[interfaceLanguage.current][status]}
128
+ <span class="text-[9px]" aria-hidden="true">{computed.style.dot}</span>
129
+ {computed.label}
42
130
  </span>
@@ -1,6 +1,20 @@
1
1
  import type { EntryStatus } from '../../../types/entries.js';
2
+ import type { OrderStatus } from '../../../shop/types.js';
3
+ /**
4
+ * StatusBadge accepts three usage modes (variants):
5
+ * - entry (default): pass `status` as `EntryStatus`
6
+ * - order: pass `variant="order"` and `status` as `OrderStatus`
7
+ * - boolean: pass `variant="boolean"` and `active: boolean`
8
+ *
9
+ * Type stays permissive (FlatProps) so consumers can pass any combination;
10
+ * runtime narrows by `variant` and falls back to muted style on unknown.
11
+ */
2
12
  type Props = {
3
- status: EntryStatus;
13
+ variant?: 'entry' | 'order' | 'boolean';
14
+ status?: EntryStatus | OrderStatus;
15
+ active?: boolean;
16
+ activeLabel?: string;
17
+ inactiveLabel?: string;
4
18
  };
5
19
  declare const StatusBadge: import("svelte").Component<Props, {}, "">;
6
20
  type StatusBadge = ReturnType<typeof StatusBadge>;
@@ -28,6 +28,8 @@
28
28
  {
29
29
  showing: (start: number, end: number, total: number, label?: string) => string;
30
30
  perPage: string;
31
+ itemsPerPage: string;
32
+ pagination: string;
31
33
  firstPage: string;
32
34
  previousPage: string;
33
35
  nextPage: string;
@@ -38,6 +40,8 @@
38
40
  en: {
39
41
  showing: (s, e, t, l) => `Showing ${s}–${e} of ${t}${l ? ` ${l}` : ''}`,
40
42
  perPage: 'per page',
43
+ itemsPerPage: 'Items per page',
44
+ pagination: 'Pagination',
41
45
  firstPage: 'First page',
42
46
  previousPage: 'Previous page',
43
47
  nextPage: 'Next page',
@@ -47,6 +51,8 @@
47
51
  pl: {
48
52
  showing: (s, e, t, l) => `Wyświetlono ${s}–${e} z ${t}${l ? ` ${l}` : ''}`,
49
53
  perPage: 'na stronie',
54
+ itemsPerPage: 'Elementów na stronie',
55
+ pagination: 'Paginacja',
50
56
  firstPage: 'Pierwsza strona',
51
57
  previousPage: 'Poprzednia strona',
52
58
  nextPage: 'Następna strona',
@@ -64,6 +70,8 @@
64
70
  const startItem = $derived(totalItems === 0 ? 0 : pageIndex * pageSize + 1);
65
71
  const endItem = $derived(Math.min((pageIndex + 1) * pageSize, totalItems));
66
72
 
73
+ const showingMessage = $derived(t.showing(startItem, endItem, totalItems, itemLabel));
74
+
67
75
  /** Generate visible page numbers with ellipsis */
68
76
  const visiblePages = $derived.by(() => {
69
77
  if (pageCount <= 7) {
@@ -91,12 +99,15 @@
91
99
 
92
100
  {#if totalItems > 0}
93
101
  <div class="flex flex-wrap items-center justify-between gap-4 px-1 py-4">
94
- <div class="text-muted-foreground text-sm">
95
- {t.showing(startItem, endItem, totalItems, itemLabel)}
102
+ <div class="text-muted-foreground text-sm" aria-hidden="true">
103
+ {showingMessage}
104
+ </div>
105
+ <div role="status" aria-live="polite" aria-atomic="true" class="sr-only">
106
+ {showingMessage}
96
107
  </div>
97
108
 
98
109
  <div class="flex items-center gap-3">
99
- <nav class="flex items-center gap-1" aria-label="Pagination">
110
+ <nav class="flex items-center gap-1" aria-label={t.pagination}>
100
111
  <Button
101
112
  variant="outline"
102
113
  size="icon"
@@ -104,6 +115,7 @@
104
115
  disabled={!canGoPrevious}
105
116
  onclick={() => onPageChange(0)}
106
117
  title={t.firstPage}
118
+ aria-label={t.firstPage}
107
119
  >
108
120
  <ChevronsLeft class="size-4" />
109
121
  </Button>
@@ -114,13 +126,14 @@
114
126
  disabled={!canGoPrevious}
115
127
  onclick={() => onPageChange(pageIndex - 1)}
116
128
  title={t.previousPage}
129
+ aria-label={t.previousPage}
117
130
  >
118
131
  <ChevronLeft class="size-4" />
119
132
  </Button>
120
133
 
121
134
  {#each visiblePages as page}
122
135
  {#if page === 'ellipsis'}
123
- <span class="px-1 text-sm text-muted-foreground">…</span>
136
+ <span class="px-1 text-sm text-muted-foreground" aria-hidden="true">…</span>
124
137
  {:else}
125
138
  <Button
126
139
  variant={page === pageIndex ? 'default' : 'outline'}
@@ -142,6 +155,7 @@
142
155
  disabled={!canGoNext}
143
156
  onclick={() => onPageChange(pageIndex + 1)}
144
157
  title={t.nextPage}
158
+ aria-label={t.nextPage}
145
159
  >
146
160
  <ChevronRight class="size-4" />
147
161
  </Button>
@@ -152,6 +166,7 @@
152
166
  disabled={!canGoNext}
153
167
  onclick={() => onPageChange(pageCount - 1)}
154
168
  title={t.lastPage}
169
+ aria-label={t.lastPage}
155
170
  >
156
171
  <ChevronsRight class="size-4" />
157
172
  </Button>
@@ -163,7 +178,7 @@
163
178
  value={String(pageSize)}
164
179
  onValueChange={(value) => value && onPageSizeChange(Number(value))}
165
180
  >
166
- <Select.Trigger class="h-8 w-[70px] text-xs">
181
+ <Select.Trigger class="h-8 w-[70px] text-xs" aria-label={t.itemsPerPage}>
167
182
  {pageSize}
168
183
  </Select.Trigger>
169
184
  <Select.Content>
@@ -172,7 +187,7 @@
172
187
  {/each}
173
188
  </Select.Content>
174
189
  </Select.Root>
175
- <span class="text-xs text-muted-foreground">{t.perPage}</span>
190
+ <span class="text-xs text-muted-foreground" aria-hidden="true">{t.perPage}</span>
176
191
  </div>
177
192
  </div>
178
193
  </div>
@@ -1,8 +1,8 @@
1
1
  <script lang="ts">
2
+ import type { Snippet } from 'svelte';
2
3
  import Search from '@tabler/icons-svelte/icons/search';
3
4
  import List from '@tabler/icons-svelte/icons/list';
4
5
  import LayoutGrid from '@tabler/icons-svelte/icons/layout-grid';
5
- import Plus from '@tabler/icons-svelte/icons/plus';
6
6
  import Filter from '@tabler/icons-svelte/icons/filter';
7
7
  import Input from '../../../components/ui/input/input.svelte';
8
8
  import Button from '../../../components/ui/button/button.svelte';
@@ -18,33 +18,37 @@
18
18
  }
19
19
 
20
20
  type Props = {
21
- searchQuery: string;
22
- onSearchChange: (query: string) => void;
23
- statusFilter: StatusFilter;
24
- onStatusFilterChange: (filter: StatusFilter) => void;
25
- viewMode: ViewMode;
26
- onViewModeChange: (mode: ViewMode) => void;
27
- onCreateEntry: () => void;
28
- createLabel: string;
21
+ searchQuery?: string;
22
+ onSearchChange?: (query: string) => void;
29
23
  searchPlaceholder?: string;
24
+ searchLabel?: string;
25
+ statusFilter?: StatusFilter;
26
+ onStatusFilterChange?: (filter: StatusFilter) => void;
27
+ hideStatusFilter?: boolean;
28
+ viewMode?: ViewMode;
29
+ onViewModeChange?: (mode: ViewMode) => void;
30
+ hideViewToggle?: boolean;
30
31
  dataFilters?: DataFilterConfig[];
31
32
  activeDataFilters?: Record<string, string | null>;
32
33
  onDataFilterChange?: (slug: string, value: string | null) => void;
34
+ actions?: Snippet;
33
35
  };
34
36
 
35
37
  let {
36
- searchQuery,
38
+ searchQuery = '',
37
39
  onSearchChange,
38
- statusFilter,
40
+ searchPlaceholder,
41
+ searchLabel,
42
+ statusFilter = null,
39
43
  onStatusFilterChange,
40
- viewMode,
44
+ hideStatusFilter = false,
45
+ viewMode = 'list',
41
46
  onViewModeChange,
42
- onCreateEntry,
43
- createLabel,
44
- searchPlaceholder,
47
+ hideViewToggle = false,
45
48
  dataFilters = [],
46
49
  activeDataFilters = {},
47
- onDataFilterChange
50
+ onDataFilterChange,
51
+ actions
48
52
  }: Props = $props();
49
53
 
50
54
  const interfaceLanguage = useInterfaceLanguage();
@@ -53,6 +57,7 @@
53
57
  InterfaceLanguage,
54
58
  {
55
59
  search: string;
60
+ searchAria: string;
56
61
  listView: string;
57
62
  gridView: string;
58
63
  status: string;
@@ -65,6 +70,7 @@
65
70
  > = {
66
71
  en: {
67
72
  search: 'Search...',
73
+ searchAria: 'Search',
68
74
  listView: 'List view',
69
75
  gridView: 'Grid view',
70
76
  status: 'Status',
@@ -76,6 +82,7 @@
76
82
  },
77
83
  pl: {
78
84
  search: 'Szukaj...',
85
+ searchAria: 'Szukaj',
79
86
  listView: 'Widok listy',
80
87
  gridView: 'Widok kafelków',
81
88
  status: 'Status',
@@ -105,69 +112,80 @@
105
112
 
106
113
  let statusPopoverOpen = $state(false);
107
114
  let dataFilterPopovers = $state<Record<string, boolean>>({});
115
+
116
+ const searchId = `table-toolbar-search-${Math.random().toString(36).slice(2, 9)}`;
108
117
  </script>
109
118
 
110
- <div class="mb-6 flex flex-wrap items-center gap-2.5">
111
- <div class="relative min-w-[240px] flex-1">
119
+ <div class="mb-4 flex flex-wrap items-center gap-2 sm:mb-6 sm:gap-2.5">
120
+ <div class="relative w-full min-w-0 flex-1 sm:w-auto sm:min-w-[240px]">
121
+ <label for={searchId} class="sr-only">{searchLabel ?? t.searchAria}</label>
112
122
  <Search
113
123
  class="text-muted-foreground pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2"
114
124
  />
115
125
  <Input
126
+ id={searchId}
116
127
  type="text"
117
128
  placeholder={searchPlaceholder ?? t.search}
118
129
  class="border-border focus-visible:ring-primary/30 bg-white pl-9"
119
130
  value={searchQuery}
120
- oninput={(e) => onSearchChange(e.currentTarget.value)}
131
+ oninput={(e) => onSearchChange?.(e.currentTarget.value)}
121
132
  />
122
133
  </div>
123
134
 
124
- <Popover.Root bind:open={statusPopoverOpen}>
125
- <Popover.Trigger>
126
- {#snippet child({ props })}
127
- <Button
128
- {...props}
129
- variant="outline"
130
- size="sm"
131
- class="gap-1.5 {hasActiveFilter
132
- ? 'border-primary/30 bg-lavender-lighter text-primary'
133
- : ''}"
134
- data-testid="status-filter-trigger"
135
- >
136
- <Filter class="size-3.5" />
137
- {t.status}{hasActiveFilter ? `: ${activeFilterLabel}` : ''}
138
- </Button>
139
- {/snippet}
140
- </Popover.Trigger>
141
- <Popover.Content class="w-44 p-1" align="start">
142
- {#each statusOptions as option}
143
- <button
144
- class="hover:bg-accent flex w-full items-center rounded-md px-2.5 py-1.5 text-sm transition-colors {statusFilter ===
145
- option.value
146
- ? 'bg-accent text-accent-foreground font-medium'
147
- : 'text-foreground'}"
148
- onclick={() => {
149
- onStatusFilterChange(option.value);
150
- statusPopoverOpen = false;
151
- }}
152
- data-testid="status-filter-option-{option.value ?? 'all'}"
153
- >
154
- {option.label()}
155
- </button>
156
- {/each}
157
- </Popover.Content>
158
- </Popover.Root>
135
+ {#if !hideStatusFilter && onStatusFilterChange}
136
+ <Popover.Root bind:open={statusPopoverOpen}>
137
+ <Popover.Trigger>
138
+ {#snippet child({ props })}
139
+ <Button
140
+ {...props}
141
+ variant="outline"
142
+ size="sm"
143
+ aria-pressed={hasActiveFilter}
144
+ class="gap-1.5 {hasActiveFilter
145
+ ? 'border-primary/30 bg-lavender-lighter text-primary'
146
+ : ''}"
147
+ data-testid="status-filter-trigger"
148
+ >
149
+ <Filter class="size-3.5" />
150
+ {t.status}{hasActiveFilter ? `: ${activeFilterLabel}` : ''}
151
+ </Button>
152
+ {/snippet}
153
+ </Popover.Trigger>
154
+ <Popover.Content class="w-44 p-1" align="start">
155
+ {#each statusOptions as option}
156
+ <button
157
+ class="hover:bg-accent flex w-full items-center rounded-md px-2.5 py-1.5 text-sm transition-colors {statusFilter ===
158
+ option.value
159
+ ? 'bg-accent text-accent-foreground font-medium'
160
+ : 'text-foreground'}"
161
+ onclick={() => {
162
+ onStatusFilterChange(option.value);
163
+ statusPopoverOpen = false;
164
+ }}
165
+ data-testid="status-filter-option-{option.value ?? 'all'}"
166
+ >
167
+ {option.label()}
168
+ </button>
169
+ {/each}
170
+ </Popover.Content>
171
+ </Popover.Root>
172
+ {/if}
159
173
 
160
174
  {#each dataFilters as filter}
161
175
  {@const activeValue = activeDataFilters[filter.slug] ?? null}
162
176
  {@const hasActive = activeValue !== null}
163
177
  {@const activeLabel = filter.options.find((o) => o.value === activeValue)?.label ?? ''}
164
- <Popover.Root open={dataFilterPopovers[filter.slug] ?? false} onOpenChange={(v) => dataFilterPopovers[filter.slug] = v}>
178
+ <Popover.Root
179
+ open={dataFilterPopovers[filter.slug] ?? false}
180
+ onOpenChange={(v) => (dataFilterPopovers[filter.slug] = v)}
181
+ >
165
182
  <Popover.Trigger>
166
183
  {#snippet child({ props })}
167
184
  <Button
168
185
  {...props}
169
186
  variant="outline"
170
187
  size="sm"
188
+ aria-pressed={hasActive}
171
189
  class="gap-1.5 {hasActive
172
190
  ? 'border-primary/30 bg-lavender-lighter text-primary'
173
191
  : ''}"
@@ -207,31 +225,38 @@
207
225
  </Popover.Root>
208
226
  {/each}
209
227
 
210
- <div class="flex items-center gap-0.5 rounded-lg border p-0.5">
211
- <Button
212
- variant={viewMode === 'list' ? 'secondary' : 'ghost'}
213
- size="icon"
214
- class="h-7 w-7"
215
- onclick={() => onViewModeChange('list')}
216
- title={t.listView}
217
- >
218
- <List class="size-3.5" />
219
- </Button>
220
- <Button
221
- variant={viewMode === 'grid' ? 'secondary' : 'ghost'}
222
- size="icon"
223
- class="h-7 w-7"
224
- onclick={() => onViewModeChange('grid')}
225
- title={t.gridView}
226
- >
227
- <LayoutGrid class="size-3.5" />
228
- </Button>
229
- </div>
228
+ {#if !hideViewToggle && onViewModeChange}
229
+ <div class="flex items-center gap-0.5 rounded-lg border p-0.5" role="group" aria-label={t.listView + ' / ' + t.gridView}>
230
+ <Button
231
+ variant={viewMode === 'list' ? 'secondary' : 'ghost'}
232
+ size="icon"
233
+ class="h-7 w-7"
234
+ aria-pressed={viewMode === 'list'}
235
+ onclick={() => onViewModeChange('list')}
236
+ title={t.listView}
237
+ aria-label={t.listView}
238
+ >
239
+ <List class="size-3.5" />
240
+ </Button>
241
+ <Button
242
+ variant={viewMode === 'grid' ? 'secondary' : 'ghost'}
243
+ size="icon"
244
+ class="h-7 w-7"
245
+ aria-pressed={viewMode === 'grid'}
246
+ onclick={() => onViewModeChange('grid')}
247
+ title={t.gridView}
248
+ aria-label={t.gridView}
249
+ >
250
+ <LayoutGrid class="size-3.5" />
251
+ </Button>
252
+ </div>
253
+ {/if}
230
254
 
231
- <div class="flex-1"></div>
255
+ <div class="hidden flex-1 sm:block"></div>
232
256
 
233
- <Button variant="default" size="sm" onclick={onCreateEntry} data-testid="create-entry-button">
234
- <Plus class="mr-1 size-4" />
235
- {createLabel}
236
- </Button>
257
+ {#if actions}
258
+ <div class="flex flex-wrap items-center gap-2 max-sm:ml-auto">
259
+ {@render actions()}
260
+ </div>
261
+ {/if}
237
262
  </div>