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
@@ -1,25 +1,155 @@
1
1
  <script lang="ts">
2
2
  import { getRemotes } from '../../../sveltekit/index.js';
3
3
  import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
4
+ import { getBreadcrumbs } from '../../state/breadcrumbs.svelte.js';
5
+ import { sidebarLang } from '../../components/layout/lang.js';
4
6
  import { getLocalizedLabel } from '../../utils/collectionLabel.js';
5
7
  import { Button } from '../../../components/ui/button/index.js';
6
8
  import * as DropdownMenu from '../../../components/ui/dropdown-menu/index.js';
7
9
  import PlusIcon from '@tabler/icons-svelte/icons/plus';
8
10
  import ChevronDownIcon from '@tabler/icons-svelte/icons/chevron-down';
11
+ import { formatPlnPrice } from '../../utils/formatters.js';
12
+ import PageHeader from '../../components/layout/page-header.svelte';
13
+ import DataTable from '../collection/data-table.svelte';
14
+ import TableToolbar from '../collection/table-toolbar.svelte';
15
+ import TablePagination from '../collection/table-pagination.svelte';
16
+ import StateDisplay from '../collection/state-display.svelte';
17
+ import StatusBadge from '../collection/status-badge.svelte';
18
+ import SortableHeader from '../collection/sortable-header.svelte';
19
+ import EntryLink from '../collection/entry-link.svelte';
20
+ import { renderComponent } from '../../../components/ui/data-table/render-helpers.js';
21
+ import type { ColumnDef, PaginationState, SortingState } from '@tanstack/table-core';
22
+ import type { InterfaceLanguage } from '../../../types/languages.js';
23
+
24
+ export type ShopProductRow = {
25
+ entryId: string;
26
+ collectionSlug: string;
27
+ basePrice: number;
28
+ vatRate: number;
29
+ isActive: boolean;
30
+ variantCount: number;
31
+ totalStock: number | null;
32
+ publishedData: Record<string, unknown> | null;
33
+ draftData: Record<string, unknown> | null;
34
+ publishedByLang?: Record<string, Record<string, unknown>>;
35
+ draftByLang?: Record<string, Record<string, unknown>>;
36
+ published: boolean;
37
+ };
38
+
39
+ type Props = {
40
+ data?: ShopProductRow[];
41
+ state?: 'loading' | 'error' | 'ok';
42
+ };
43
+
44
+ let { data: injectedData, state: injectedState }: Props = $props();
45
+ const useInjectedData = $derived(injectedData !== undefined);
9
46
 
10
47
  const remotes = getRemotes();
11
48
  const interfaceLanguage = useInterfaceLanguage();
49
+ const breadcrumbs = getBreadcrumbs();
50
+
51
+ $effect(() => {
52
+ const s = sidebarLang[interfaceLanguage.current].shop;
53
+ breadcrumbs.state = [{ label: s.title }, { label: s.products }];
54
+ });
12
55
 
13
- const entriesQuery = $derived(remotes.listShopProductEntries());
56
+ const entriesQuery = $derived(useInjectedData ? null : remotes.listShopProductEntries());
14
57
  const collectionsQuery = $derived(remotes.listShopableCollections());
15
58
 
16
- function formatPrice(pln: number) {
17
- return new Intl.NumberFormat('pl-PL', {
18
- style: 'currency',
19
- currency: 'PLN',
20
- minimumFractionDigits: 2
21
- }).format(pln);
22
- }
59
+ const allRows = $derived<ShopProductRow[]>(
60
+ useInjectedData
61
+ ? (injectedData ?? [])
62
+ : ((entriesQuery?.current as ShopProductRow[] | undefined) ?? [])
63
+ );
64
+ const isLoading = $derived(
65
+ useInjectedData ? injectedState === 'loading' : !(entriesQuery?.ready ?? false)
66
+ );
67
+ const isError = $derived(
68
+ useInjectedData ? injectedState === 'error' : Boolean(entriesQuery?.error)
69
+ );
70
+
71
+ let searchQuery = $state('');
72
+ let publishedFilter = $state<string | null>(null);
73
+ let activeFilter = $state<string | null>(null);
74
+ let sorting = $state<SortingState>([]);
75
+ let pagination = $state<PaginationState>({ pageIndex: 0, pageSize: 20 });
76
+
77
+ const lang: Record<
78
+ InterfaceLanguage,
79
+ {
80
+ title: string;
81
+ search: string;
82
+ searchPlaceholder: string;
83
+ filterPublished: string;
84
+ filterActive: string;
85
+ statusPublished: string;
86
+ statusDraft: string;
87
+ activeYes: string;
88
+ activeNo: string;
89
+ addProduct: string;
90
+ emptyTitle: string;
91
+ emptyDescription: string;
92
+ columnName: string;
93
+ columnCollection: string;
94
+ columnBasePrice: string;
95
+ columnVat: string;
96
+ columnVariants: string;
97
+ columnStock: string;
98
+ columnStatus: string;
99
+ unlimited: string;
100
+ noResults: string;
101
+ }
102
+ > = {
103
+ en: {
104
+ title: 'Products',
105
+ search: 'Search',
106
+ searchPlaceholder: 'Search by name…',
107
+ filterPublished: 'Status',
108
+ filterActive: 'Active',
109
+ statusPublished: 'Published',
110
+ statusDraft: 'Draft',
111
+ activeYes: 'Active',
112
+ activeNo: 'Inactive',
113
+ addProduct: 'Add product',
114
+ emptyTitle: 'No products yet',
115
+ emptyDescription:
116
+ "Add a field { type: 'shop' } to a collection, then create an entry and fill out the Shop section.",
117
+ columnName: 'Name',
118
+ columnCollection: 'Collection',
119
+ columnBasePrice: 'Base price',
120
+ columnVat: 'VAT',
121
+ columnVariants: 'Variants',
122
+ columnStock: 'Stock',
123
+ columnStatus: 'Status',
124
+ unlimited: 'unlimited',
125
+ noResults: 'No results.'
126
+ },
127
+ pl: {
128
+ title: 'Produkty',
129
+ search: 'Szukaj',
130
+ searchPlaceholder: 'Szukaj po nazwie…',
131
+ filterPublished: 'Status',
132
+ filterActive: 'Aktywność',
133
+ statusPublished: 'Opublikowany',
134
+ statusDraft: 'Szkic',
135
+ activeYes: 'Aktywny',
136
+ activeNo: 'Nieaktywny',
137
+ addProduct: 'Dodaj produkt',
138
+ emptyTitle: 'Brak produktów',
139
+ emptyDescription:
140
+ "Dodaj pole { type: 'shop' } do kolekcji, a następnie utwórz wpis i wypełnij sekcję „Sklep”.",
141
+ columnName: 'Nazwa',
142
+ columnCollection: 'Kolekcja',
143
+ columnBasePrice: 'Cena bazowa',
144
+ columnVat: 'VAT',
145
+ columnVariants: 'Warianty',
146
+ columnStock: 'Magazyn',
147
+ columnStatus: 'Status',
148
+ unlimited: 'nieograniczony',
149
+ noResults: 'Brak wyników.'
150
+ }
151
+ };
152
+ const t = $derived(lang[interfaceLanguage.current]);
23
153
 
24
154
  function resolveTitle(data: Record<string, unknown> | null, fallback: string): string {
25
155
  if (!data) return fallback;
@@ -30,6 +160,80 @@
30
160
  return fallback;
31
161
  }
32
162
 
163
+ function pickByLang(
164
+ byLang: Record<string, Record<string, unknown>> | undefined,
165
+ preferred: string
166
+ ): Record<string, unknown> | null {
167
+ if (!byLang) return null;
168
+ if (byLang[preferred]) return byLang[preferred];
169
+ const first = Object.values(byLang)[0];
170
+ return first ?? null;
171
+ }
172
+
173
+ type EnrichedRow = ShopProductRow & { _title: string };
174
+
175
+ const enrichedRows = $derived<EnrichedRow[]>(
176
+ allRows.map((row) => {
177
+ const published = pickByLang(row.publishedByLang, interfaceLanguage.current) ?? row.publishedData;
178
+ const draft = pickByLang(row.draftByLang, interfaceLanguage.current) ?? row.draftData;
179
+ return {
180
+ ...row,
181
+ _title: resolveTitle(published ?? draft, row.collectionSlug)
182
+ };
183
+ })
184
+ );
185
+
186
+ const filteredRows = $derived.by(() => {
187
+ const q = searchQuery.trim().toLowerCase();
188
+ let list = enrichedRows;
189
+ if (q) list = list.filter((row) => row._title.toLowerCase().includes(q));
190
+ if (publishedFilter !== null) {
191
+ const wantPublished = publishedFilter === 'published';
192
+ list = list.filter((row) => row.published === wantPublished);
193
+ }
194
+ if (activeFilter !== null) {
195
+ const wantActive = activeFilter === 'active';
196
+ list = list.filter((row) => row.isActive === wantActive);
197
+ }
198
+ if (sorting.length > 0) {
199
+ const [{ id, desc }] = sorting;
200
+ list = [...list].sort((a, b) => {
201
+ const av = (a as unknown as Record<string, unknown>)[id];
202
+ const bv = (b as unknown as Record<string, unknown>)[id];
203
+ if (av == null && bv == null) return 0;
204
+ if (av == null) return desc ? 1 : -1;
205
+ if (bv == null) return desc ? -1 : 1;
206
+ if (av < bv) return desc ? 1 : -1;
207
+ if (av > bv) return desc ? -1 : 1;
208
+ return 0;
209
+ });
210
+ }
211
+ return list;
212
+ });
213
+
214
+ const totalItems = $derived(filteredRows.length);
215
+ const pageCount = $derived(Math.max(1, Math.ceil(totalItems / pagination.pageSize)));
216
+
217
+ const dataFilters = $derived([
218
+ {
219
+ slug: 'published',
220
+ label: t.filterPublished,
221
+ options: [
222
+ { value: 'published', label: t.statusPublished },
223
+ { value: 'draft', label: t.statusDraft }
224
+ ]
225
+ },
226
+ {
227
+ slug: 'isActive',
228
+ label: t.filterActive,
229
+ options: [
230
+ { value: 'active', label: t.activeYes },
231
+ { value: 'inactive', label: t.activeNo }
232
+ ]
233
+ }
234
+ ]);
235
+ const activeDataFilters = $derived({ published: publishedFilter, isActive: activeFilter });
236
+
33
237
  async function createInCollection(slug: string) {
34
238
  try {
35
239
  const newEntry = await remotes.createEntry({ type: 'collection', slug });
@@ -38,121 +242,154 @@
38
242
  alert(err instanceof Error ? err.message : 'Nie udało się utworzyć wpisu');
39
243
  }
40
244
  }
245
+
246
+ const columns = $derived.by<ColumnDef<EnrichedRow>[]>(() => [
247
+ {
248
+ accessorKey: '_title',
249
+ header: ({ column }) =>
250
+ renderComponent(SortableHeader<EnrichedRow>, { column, label: t.columnName, sorting }),
251
+ cell: (info) =>
252
+ renderComponent(EntryLink, {
253
+ name: info.row.original._title,
254
+ url: `/admin/entries/${info.row.original.entryId}`
255
+ })
256
+ },
257
+ {
258
+ accessorKey: 'collectionSlug',
259
+ header: t.columnCollection,
260
+ cell: (info) => info.row.original.collectionSlug
261
+ },
262
+ {
263
+ accessorKey: 'basePrice',
264
+ header: ({ column }) =>
265
+ renderComponent(SortableHeader<EnrichedRow>, {
266
+ column,
267
+ label: t.columnBasePrice,
268
+ sorting
269
+ }),
270
+ cell: (info) => formatPlnPrice(info.row.original.basePrice)
271
+ },
272
+ {
273
+ accessorKey: 'vatRate',
274
+ header: t.columnVat,
275
+ cell: (info) => `${info.row.original.vatRate}%`
276
+ },
277
+ {
278
+ accessorKey: 'variantCount',
279
+ header: t.columnVariants,
280
+ cell: (info) => info.row.original.variantCount
281
+ },
282
+ {
283
+ accessorKey: 'totalStock',
284
+ header: t.columnStock,
285
+ cell: (info) => info.row.original.totalStock ?? t.unlimited
286
+ },
287
+ {
288
+ id: 'status',
289
+ header: t.columnStatus,
290
+ cell: (info) =>
291
+ renderComponent(StatusBadge, {
292
+ variant: 'boolean',
293
+ active: info.row.original.published,
294
+ activeLabel: t.statusPublished,
295
+ inactiveLabel: t.statusDraft
296
+ })
297
+ },
298
+ {
299
+ id: 'isActive',
300
+ header: t.filterActive,
301
+ cell: (info) =>
302
+ renderComponent(StatusBadge, {
303
+ variant: 'boolean',
304
+ active: info.row.original.isActive,
305
+ activeLabel: t.activeYes,
306
+ inactiveLabel: t.activeNo
307
+ })
308
+ }
309
+ ]);
310
+
311
+ const collections = $derived(collectionsQuery?.current ?? []);
41
312
  </script>
42
313
 
43
- <div class="flex items-center justify-between gap-4 p-6">
44
- <div>
45
- <h1 class="text-2xl font-extrabold tracking-tight">Produkty</h1>
46
- <p class="text-muted-foreground text-sm">
47
- {#if entriesQuery.ready}
48
- {entriesQuery.current?.length ?? 0}
49
- {(entriesQuery.current?.length ?? 0) === 1 ? 'produkt' : 'produktów'}
50
- {:else}
51
- Ładowanie…
314
+ <div class="p-5 pb-24 md:p-7">
315
+ <PageHeader title={t.title} count={!isLoading && !isError ? allRows.length : undefined}>
316
+ {#snippet primaryActions()}
317
+ {#if collections.length === 1}
318
+ {@const only = collections[0]}
319
+ <Button onclick={() => createInCollection(only.slug)}>
320
+ <PlusIcon class="size-4" />
321
+ {t.addProduct}
322
+ </Button>
323
+ {:else if collections.length > 1}
324
+ <DropdownMenu.Root>
325
+ <DropdownMenu.Trigger>
326
+ {#snippet child({ props })}
327
+ <Button {...props}>
328
+ <PlusIcon class="size-4" />
329
+ {t.addProduct}
330
+ <ChevronDownIcon class="size-4" />
331
+ </Button>
332
+ {/snippet}
333
+ </DropdownMenu.Trigger>
334
+ <DropdownMenu.Content>
335
+ {#each collections as c (c.slug)}
336
+ <DropdownMenu.Item onclick={() => createInCollection(c.slug)}>
337
+ {getLocalizedLabel(c.labels?.singular, interfaceLanguage.current) ?? c.slug}
338
+ </DropdownMenu.Item>
339
+ {/each}
340
+ </DropdownMenu.Content>
341
+ </DropdownMenu.Root>
52
342
  {/if}
53
- </p>
54
- </div>
55
- {#if collectionsQuery.ready && collectionsQuery.current && collectionsQuery.current.length > 0}
56
- {#if collectionsQuery.current.length === 1}
57
- {@const only = collectionsQuery.current[0]}
58
- <Button onclick={() => createInCollection(only.slug)}>
59
- <PlusIcon class="mr-1 size-4" />
60
- Dodaj produkt
61
- </Button>
62
- {:else}
63
- <DropdownMenu.Root>
64
- <DropdownMenu.Trigger>
65
- {#snippet child({ props })}
66
- <Button {...props}>
67
- <PlusIcon class="mr-1 size-4" />
68
- Dodaj produkt
69
- <ChevronDownIcon class="ml-1 size-4" />
70
- </Button>
71
- {/snippet}
72
- </DropdownMenu.Trigger>
73
- <DropdownMenu.Content>
74
- {#each collectionsQuery.current as c (c.slug)}
75
- <DropdownMenu.Item onclick={() => createInCollection(c.slug)}>
76
- {getLocalizedLabel(c.labels?.singular, interfaceLanguage.current) ?? c.slug}
77
- </DropdownMenu.Item>
78
- {/each}
79
- </DropdownMenu.Content>
80
- </DropdownMenu.Root>
81
- {/if}
82
- {/if}
83
- </div>
343
+ {/snippet}
344
+ </PageHeader>
84
345
 
85
- <div class="px-6 pb-12">
86
- {#if !entriesQuery.ready}
87
- <div class="text-muted-foreground">Ładowanie…</div>
88
- {:else if (entriesQuery.current?.length ?? 0) === 0}
89
- <div class="bg-lavender-lighter/40 border-border rounded-xl border p-8 text-center text-sm">
90
- <p class="mb-2 font-semibold">Brak produktów</p>
91
- <p class="text-muted-foreground">
92
- Dodaj pole <code class="text-primary">&lbrace; type: 'shop' &rbrace;</code> do kolekcji, a następnie
93
- utwórz wpis i wypełnij sekcję „Sklep".
94
- </p>
95
- </div>
346
+ {#if isError}
347
+ <StateDisplay kind="error" />
348
+ {:else if isLoading}
349
+ <StateDisplay kind="loading" />
350
+ {:else if allRows.length === 0}
351
+ <StateDisplay kind="empty" title={t.emptyTitle} description={t.emptyDescription} />
96
352
  {:else}
97
- <div class="border-border overflow-hidden rounded-xl border">
98
- <table class="w-full text-sm">
99
- <thead class="bg-muted/50 text-left text-xs tracking-wide uppercase">
100
- <tr>
101
- <th class="px-4 py-3 font-semibold">Nazwa</th>
102
- <th class="px-4 py-3 font-semibold">Kolekcja</th>
103
- <th class="px-4 py-3 font-semibold">Cena bazowa</th>
104
- <th class="px-4 py-3 font-semibold">VAT</th>
105
- <th class="px-4 py-3 font-semibold">Warianty</th>
106
- <th class="px-4 py-3 font-semibold">Magazyn</th>
107
- <th class="px-4 py-3 font-semibold">Status</th>
108
- </tr>
109
- </thead>
110
- <tbody>
111
- {#each entriesQuery.current ?? [] as row (row.entryId)}
112
- {@const title = resolveTitle(row.publishedData ?? row.draftData, row.collectionSlug)}
113
- <tr class="border-border hover:bg-muted/30 border-t">
114
- <td class="px-4 py-3">
115
- <a href={`/admin/entries/${row.entryId}`} class="text-primary hover:underline">
116
- {title}
117
- </a>
118
- </td>
119
- <td class="text-muted-foreground px-4 py-3 font-mono text-xs">{row.collectionSlug}</td
120
- >
121
- <td class="px-4 py-3">{formatPrice(row.basePrice)}</td>
122
- <td class="px-4 py-3">{row.vatRate}%</td>
123
- <td class="px-4 py-3">{row.variantCount}</td>
124
- <td class="px-4 py-3">
125
- {#if row.totalStock == null}
126
- <span class="text-muted-foreground text-xs">nieograniczony</span>
127
- {:else}
128
- {row.totalStock}
129
- {/if}
130
- </td>
131
- <td class="px-4 py-3">
132
- <div class="flex gap-1.5">
133
- {#if row.published}
134
- <span
135
- class="inline-flex rounded-full bg-green-100 px-2 py-0.5 text-xs text-green-800"
136
- >Opublikowany</span
137
- >
138
- {:else}
139
- <span
140
- class="inline-flex rounded-full bg-yellow-100 px-2 py-0.5 text-xs text-yellow-800"
141
- >Szkic</span
142
- >
143
- {/if}
144
- {#if !row.isActive}
145
- <span
146
- class="inline-flex rounded-full bg-gray-100 px-2 py-0.5 text-xs text-gray-800"
147
- >Nieaktywny</span
148
- >
149
- {/if}
150
- </div>
151
- </td>
152
- </tr>
153
- {/each}
154
- </tbody>
155
- </table>
353
+ <TableToolbar
354
+ {searchQuery}
355
+ searchPlaceholder={t.searchPlaceholder}
356
+ searchLabel={t.search}
357
+ onSearchChange={(q) => {
358
+ searchQuery = q;
359
+ pagination = { ...pagination, pageIndex: 0 };
360
+ }}
361
+ hideStatusFilter
362
+ hideViewToggle
363
+ {dataFilters}
364
+ {activeDataFilters}
365
+ onDataFilterChange={(slug, value) => {
366
+ if (slug === 'published') publishedFilter = value;
367
+ else if (slug === 'isActive') activeFilter = value;
368
+ pagination = { ...pagination, pageIndex: 0 };
369
+ }}
370
+ />
371
+
372
+ <div class="overflow-hidden rounded-xl border bg-card shadow-sm">
373
+ <DataTable
374
+ data={filteredRows}
375
+ {columns}
376
+ enableSorting
377
+ enablePagination
378
+ {sorting}
379
+ onSortingChange={(s) => (sorting = s)}
380
+ {pagination}
381
+ onPaginationChange={(p) => (pagination = p)}
382
+ emptyTitle={t.noResults}
383
+ />
384
+
385
+ <TablePagination
386
+ pageIndex={pagination.pageIndex}
387
+ pageSize={pagination.pageSize}
388
+ {pageCount}
389
+ {totalItems}
390
+ onPageChange={(p) => (pagination = { ...pagination, pageIndex: p })}
391
+ onPageSizeChange={(s) => (pagination = { pageIndex: 0, pageSize: s })}
392
+ />
156
393
  </div>
157
394
  {/if}
158
395
  </div>
@@ -1,3 +1,21 @@
1
- declare const ShopProductsListPage: import("svelte").Component<Record<string, never>, {}, "">;
1
+ export type ShopProductRow = {
2
+ entryId: string;
3
+ collectionSlug: string;
4
+ basePrice: number;
5
+ vatRate: number;
6
+ isActive: boolean;
7
+ variantCount: number;
8
+ totalStock: number | null;
9
+ publishedData: Record<string, unknown> | null;
10
+ draftData: Record<string, unknown> | null;
11
+ publishedByLang?: Record<string, Record<string, unknown>>;
12
+ draftByLang?: Record<string, Record<string, unknown>>;
13
+ published: boolean;
14
+ };
15
+ type Props = {
16
+ data?: ShopProductRow[];
17
+ state?: 'loading' | 'error' | 'ok';
18
+ };
19
+ declare const ShopProductsListPage: import("svelte").Component<Props, {}, "">;
2
20
  type ShopProductsListPage = ReturnType<typeof ShopProductsListPage>;
3
21
  export default ShopProductsListPage;
@@ -8,6 +8,7 @@
8
8
  import { page } from '$app/state';
9
9
  import { usersLang } from './lang.js';
10
10
  import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
11
+ import { generatePassword } from '../../shared/password-generate.js';
11
12
 
12
13
  const interfaceLanguage = useInterfaceLanguage();
13
14
  const lang = $derived(usersLang[interfaceLanguage.current]);
@@ -61,6 +62,15 @@
61
62
  return null;
62
63
  }
63
64
 
65
+ function handleGeneratePassword() {
66
+ const pw = generatePassword();
67
+ password = pw;
68
+ confirmPassword = pw;
69
+ showPassword = true;
70
+ showConfirm = true;
71
+ error = '';
72
+ }
73
+
64
74
  async function handleSubmit(e: Event) {
65
75
  e.preventDefault();
66
76
  const validationError = validate();
@@ -198,9 +208,20 @@
198
208
  {strengthLabelText}: {strengthLabels[strength.level]}
199
209
  </p>
200
210
  {/if}
201
- <p id="invite-pw-hint" class="text-text-light mt-1 text-xs">
202
- {lang.passwordHint}
203
- </p>
211
+ <div class="mt-1 flex items-center justify-between gap-2">
212
+ <p id="invite-pw-hint" class="text-text-light text-xs">
213
+ {lang.passwordHint}
214
+ </p>
215
+ <Button
216
+ variant="link"
217
+ size="sm"
218
+ type="button"
219
+ class="h-auto shrink-0 p-0 text-xs"
220
+ onclick={handleGeneratePassword}
221
+ >
222
+ {lang.generatePassword}
223
+ </Button>
224
+ </div>
204
225
  </div>
205
226
  <div class="space-y-2">
206
227
  <Label for="invite-confirm">{lang.invite.confirmPassword}</Label>
@@ -11,6 +11,7 @@
11
11
  import { toast } from 'svelte-sonner';
12
12
  import { usersLang } from './lang.js';
13
13
  import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
14
+ import { generatePassword } from '../../shared/password-generate.js';
14
15
  import type { UserRole } from '../../../types/roles.js';
15
16
 
16
17
  type Props = {
@@ -32,13 +33,6 @@
32
33
  let error = $state('');
33
34
  let showPassword = $state(false);
34
35
 
35
- function generatePassword(length = 20): string {
36
- const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%&*';
37
- const array = new Uint8Array(length);
38
- crypto.getRandomValues(array);
39
- return Array.from(array, (b) => chars[b % chars.length]).join('');
40
- }
41
-
42
36
  function reset() {
43
37
  name = '';
44
38
  email = '';
@@ -111,6 +105,7 @@
111
105
  type="button"
112
106
  class="absolute right-2 top-1/2 -translate-y-1/2"
113
107
  style="color: var(--text-light);"
108
+ aria-label={showPassword ? lang.hidePassword : lang.showPassword}
114
109
  onclick={() => (showPassword = !showPassword)}
115
110
  >
116
111
  {#if showPassword}
@@ -126,7 +121,7 @@
126
121
  size="sm"
127
122
  class="shrink-0 gap-1.5"
128
123
  onclick={() => {
129
- password = generatePassword();
124
+ password = generatePassword(20);
130
125
  showPassword = true;
131
126
  }}
132
127
  >
@@ -15,6 +15,8 @@ export declare const usersLang: Record<InterfaceLanguage, {
15
15
  cancel: string;
16
16
  password: string;
17
17
  passwordHint: string;
18
+ showPassword: string;
19
+ hidePassword: string;
18
20
  passwordMinLength: string;
19
21
  passwordRequirements: string;
20
22
  passwordMismatch: string;
@@ -15,6 +15,8 @@ export const usersLang = {
15
15
  cancel: 'Cancel',
16
16
  password: 'Password',
17
17
  passwordHint: 'Min. 8 characters, uppercase, number and special character',
18
+ showPassword: 'Show password',
19
+ hidePassword: 'Hide password',
18
20
  passwordMinLength: 'Password must be at least 8 characters',
19
21
  passwordRequirements: 'Password must contain an uppercase letter, a number and a special character',
20
22
  passwordMismatch: 'Passwords do not match',
@@ -101,6 +103,8 @@ export const usersLang = {
101
103
  cancel: 'Anuluj',
102
104
  password: 'Hasło',
103
105
  passwordHint: 'Min. 8 znaków, wielka litera, cyfra i znak specjalny',
106
+ showPassword: 'Pokaż hasło',
107
+ hidePassword: 'Ukryj hasło',
104
108
  passwordMinLength: 'Hasło musi mieć min. 8 znaków',
105
109
  passwordRequirements: 'Hasło musi zawierać wielką literę, cyfrę i znak specjalny',
106
110
  passwordMismatch: 'Hasła nie są zgodne',
@@ -7,7 +7,7 @@
7
7
  import { toast } from 'svelte-sonner';
8
8
  import { usersLang } from './lang.js';
9
9
  import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
10
- import { toLocaleCode } from '../../utils/formatDate.js';
10
+ import { formatShortDateOnly } from '../../utils/formatters.js';
11
11
  import { getRoleLabel } from '../../utils/roleLabel.js';
12
12
 
13
13
  type Props = {
@@ -82,13 +82,6 @@
82
82
  }
83
83
  }
84
84
 
85
- function formatDate(date: string): string {
86
- return new Date(date).toLocaleString(toLocaleCode(interfaceLanguage.current), {
87
- year: 'numeric',
88
- month: 'short',
89
- day: 'numeric'
90
- });
91
- }
92
85
  </script>
93
86
 
94
87
  <section class="mt-10 users-fade-up" aria-labelledby="invitations-heading">
@@ -144,7 +137,7 @@
144
137
  class:users-expiring-soon={isExpiringSoon(inv.expiresAt)}
145
138
  style={isExpiringSoon(inv.expiresAt) ? '' : 'color: var(--muted-foreground);'}
146
139
  >
147
- {formatDate(inv.expiresAt)}
140
+ {formatShortDateOnly(inv.expiresAt, interfaceLanguage.current)}
148
141
  {#if isExpiringSoon(inv.expiresAt)}
149
142
  — {lang.invite.expiringSoon}
150
143
  {/if}