@riducms/admin 0.2.2 → 0.3.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 (352) hide show
  1. package/THIRD_PARTY_NOTICES.md +60 -37
  2. package/package.json +27 -15
  3. package/src/app/admin-core-view-route.svelte +38 -53
  4. package/src/app/admin-extension-route.svelte +19 -0
  5. package/src/app/admin-layout.scss +175 -0
  6. package/src/app/admin-layout.svelte +144 -410
  7. package/src/app/admin-navigation-progress.svelte +11 -77
  8. package/src/app/admin-page-title.svelte +17 -85
  9. package/src/app/admin-page-title.ts +136 -0
  10. package/src/app/admin-route-error.svelte +24 -0
  11. package/src/app/admin-router-provider.svelte +34 -0
  12. package/src/app/admin-router-root.svelte +41 -0
  13. package/src/app/admin-routes.svelte +56 -19
  14. package/src/app/loading-boundary.svelte +3 -1
  15. package/src/app-meta.ts +13 -0
  16. package/src/app.css +138 -496
  17. package/src/app.svelte +60 -26
  18. package/src/components/brand/ridu-logo.svelte +19 -5
  19. package/src/components/data/list-table.scss +63 -0
  20. package/src/components/icons/LICENSE.md +24 -0
  21. package/src/components/icons/chevron.svelte +8 -0
  22. package/src/components/icons/plus.svelte +12 -0
  23. package/src/components/icons/search.svelte +12 -0
  24. package/src/components/icons/x.svelte +8 -0
  25. package/src/components/json-tree/json-tree-node.svelte +61 -67
  26. package/src/components/json-tree/json-viewer.scss +153 -0
  27. package/src/components/json-tree/json-viewer.svelte +57 -96
  28. package/src/components/ui/banner/banner.scss +28 -0
  29. package/src/components/ui/banner/banner.svelte +9 -8
  30. package/src/components/ui/banner/index.ts +1 -5
  31. package/src/components/ui/code-editor/code-editor.scss +20 -0
  32. package/src/components/ui/code-editor/code-editor.svelte +71 -0
  33. package/src/components/ui/code-editor/code-editor.ts +151 -0
  34. package/src/components/ui/code-editor/code-languages.ts +29 -0
  35. package/src/components/ui/collapsible-panel/collapsible-panel.scss +38 -0
  36. package/src/components/ui/collapsible-panel/collapsible-panel.svelte +13 -0
  37. package/src/components/ui/collapsible-panel/index.ts +1 -0
  38. package/src/components/ui/combobox/combobox.scss +163 -0
  39. package/src/components/ui/combobox/index.ts +22 -0
  40. package/src/components/ui/date-value-control/date-value-calendar.svelte +128 -16
  41. package/src/components/ui/date-value-control/date-value-control-loader.scss +26 -0
  42. package/src/components/ui/date-value-control/date-value-control-loader.svelte +35 -13
  43. package/src/components/ui/date-value-control/date-value-control.scss +223 -0
  44. package/src/components/ui/date-value-control/date-value-control.svelte +62 -44
  45. package/src/components/ui/drawer/drawer-depth.ts +8 -0
  46. package/src/components/ui/dropdown-menu/dropdown-menu-content.svelte +2 -5
  47. package/src/components/ui/dropdown-menu/dropdown-menu-item.svelte +2 -5
  48. package/src/components/ui/dropdown-menu/dropdown-menu-separator.svelte +2 -2
  49. package/src/components/ui/dropdown-menu/dropdown-menu.scss +62 -0
  50. package/src/components/ui/kbd/kbd.scss +17 -0
  51. package/src/components/ui/kbd/kbd.svelte +3 -10
  52. package/src/components/ui/navigation-drawer/navigation-drawer.scss +40 -0
  53. package/src/components/ui/navigation-drawer/navigation-drawer.svelte +38 -0
  54. package/src/components/ui/popover/popover-content.svelte +3 -5
  55. package/src/components/ui/popover/popover.scss +18 -0
  56. package/src/components/ui/radio-group/index.ts +0 -1
  57. package/src/components/ui/radio-group/radio-card-item.svelte +7 -12
  58. package/src/components/ui/radio-group/radio-group.svelte +2 -2
  59. package/src/components/ui/radio-group/radio.scss +45 -0
  60. package/src/components/ui/select/select-content.svelte +5 -6
  61. package/src/components/ui/select/select-item.svelte +8 -8
  62. package/src/components/ui/select/select-scroll-down-button.svelte +3 -5
  63. package/src/components/ui/select/select-scroll-up-button.svelte +3 -5
  64. package/src/components/ui/select/select-trigger.svelte +6 -7
  65. package/src/components/ui/select/select.scss +126 -0
  66. package/src/components/ui/sheet/sheet-content.svelte +6 -34
  67. package/src/components/ui/sheet/sheet-description.svelte +2 -2
  68. package/src/components/ui/sheet/sheet-overlay.svelte +2 -21
  69. package/src/components/ui/sheet/sheet-title.svelte +2 -2
  70. package/src/components/ui/sheet/sheet.scss +221 -0
  71. package/src/components/ui/skeleton/skeleton.scss +20 -0
  72. package/src/components/ui/skeleton/skeleton.svelte +4 -16
  73. package/src/components/ui/sonner/sonner.scss +121 -0
  74. package/src/components/ui/sonner/sonner.svelte +11 -10
  75. package/src/components/ui/status-indicator/status-indicator.scss +48 -0
  76. package/src/components/ui/status-indicator/status-indicator.svelte +14 -14
  77. package/src/components/ui/tabs/tabs-content.svelte +2 -2
  78. package/src/components/ui/tabs/tabs-list.svelte +7 -7
  79. package/src/components/ui/tabs/tabs-trigger.svelte +2 -8
  80. package/src/components/ui/tabs/tabs.scss +86 -0
  81. package/src/components/ui/tabs/tabs.svelte +2 -2
  82. package/src/components/ui/timezone-picker/timezone-picker.svelte +152 -0
  83. package/src/core/api/admin-client.ts +2 -2
  84. package/src/core/bootstrap/admin-bootstrap.ts +396 -0
  85. package/src/core/bootstrap/admin-prepared-state.ts +213 -0
  86. package/src/core/bootstrap/admin-route-modules.ts +66 -0
  87. package/src/core/clipboard/copy-feedback.svelte.ts +43 -0
  88. package/src/core/forms/embedded-fields.ts +4 -8
  89. package/src/core/forms/field-editor-authoring.ts +8 -0
  90. package/src/core/forms/field-issue-focus.ts +4 -4
  91. package/src/core/forms/field-occurrence.ts +4 -7
  92. package/src/core/forms/form-controller.svelte.ts +14 -21
  93. package/src/core/forms/form-draft-recovery.ts +17 -4
  94. package/src/core/forms/form-issue-correlation.ts +13 -13
  95. package/src/core/forms/form-path.ts +17 -0
  96. package/src/core/forms/form-schema.ts +12 -43
  97. package/src/core/forms/form-validation.ts +7 -46
  98. package/src/core/forms/plugin-field-authoring.ts +6 -2
  99. package/src/core/forms/plugin-field-write.ts +2 -6
  100. package/src/core/i18n/admin-i18n.svelte.ts +18 -0
  101. package/src/core/i18n/format-admin-date-time.ts +19 -0
  102. package/src/core/i18n/humanize-identifier.ts +5 -0
  103. package/src/core/i18n/time-zone-label.ts +20 -0
  104. package/src/core/loaders/admin-view-host.svelte +72 -0
  105. package/src/core/loaders/admin-view-loader.svelte.ts +90 -0
  106. package/src/core/notifications/validation-toast.svelte +2 -2
  107. package/src/core/plugins/field-registry.ts +7 -8
  108. package/src/core/routing/admin-paths.ts +53 -2
  109. package/src/core/routing/admin-scroll.svelte.ts +74 -0
  110. package/src/core/routing/admin-view-selection.ts +78 -0
  111. package/src/core/runtime/admin-runtime.svelte.ts +121 -124
  112. package/src/core/schema/field-default.ts +33 -0
  113. package/src/core/schema/field-localization.ts +15 -0
  114. package/src/core/schema/read-document-path.ts +11 -0
  115. package/src/features/account/account-menu.scss +37 -0
  116. package/src/features/account/account-menu.svelte +115 -0
  117. package/src/features/{auth → account}/account-navigation.svelte +16 -19
  118. package/src/features/{auth → account}/account-route.svelte +206 -49
  119. package/src/features/account/account-security-controller.svelte.ts +157 -0
  120. package/src/features/{auth → account}/account-security-route.svelte +140 -42
  121. package/src/features/api-reference/api-reference-code.svelte +64 -0
  122. package/src/features/api-reference/api-reference-content.svelte +258 -0
  123. package/src/features/api-reference/api-reference-drawer.scss +74 -0
  124. package/src/features/api-reference/api-reference-examples.ts +381 -0
  125. package/src/features/api-reference/api-reference-schema.ts +332 -0
  126. package/src/features/api-reference/api-reference.scss +190 -0
  127. package/src/features/api-reference/api-reference.svelte +74 -0
  128. package/src/features/auth/auth-brand.svelte +5 -3
  129. package/src/features/auth/auth-field.svelte +92 -0
  130. package/src/features/auth/auth-frame.svelte +5 -8
  131. package/src/features/auth/auth.scss +122 -0
  132. package/src/features/auth/create-first-user-controller.svelte.ts +20 -51
  133. package/src/features/auth/create-first-user-route.svelte +83 -122
  134. package/src/features/auth/forgot-password-route.svelte +31 -23
  135. package/src/features/auth/login-route.svelte +68 -116
  136. package/src/features/auth/logout-button.scss +34 -0
  137. package/src/features/auth/logout-button.svelte +52 -0
  138. package/src/features/auth/request-verification-route.svelte +27 -22
  139. package/src/features/auth/reset-password-route.svelte +108 -41
  140. package/src/features/auth/verify-email-route.svelte +49 -15
  141. package/src/features/bulk-edit/bulk-edit-fields.ts +23 -0
  142. package/src/features/bulk-edit/bulk-edit.scss +152 -0
  143. package/src/features/bulk-edit/field-picker.svelte +176 -0
  144. package/src/features/collections/bulk/bulk-actions.svelte +148 -0
  145. package/src/features/collections/bulk/bulk-confirmation.svelte +153 -0
  146. package/src/features/collections/bulk/bulk-editor-loader.svelte +95 -0
  147. package/src/features/collections/bulk/bulk-editor.svelte +186 -0
  148. package/src/features/collections/collection-list-cell-values.ts +144 -0
  149. package/src/features/collections/collection-list-controller.svelte.ts +268 -379
  150. package/src/features/collections/collection-list-preferences-controller.svelte.ts +54 -40
  151. package/src/features/collections/collection-list-query.ts +223 -0
  152. package/src/features/collections/collection-list-results.svelte +205 -329
  153. package/src/features/collections/collection-list-route.svelte +143 -884
  154. package/src/features/collections/collection-list.scss +261 -0
  155. package/src/features/collections/collection-list.svelte.ts +362 -0
  156. package/src/features/collections/controls/column-picker.svelte +110 -0
  157. package/src/features/collections/controls/column-pill.svelte +62 -0
  158. package/src/features/collections/controls/filter-builder.svelte +193 -0
  159. package/src/features/collections/controls/filter-row.svelte +142 -0
  160. package/src/features/collections/controls/list-controls.scss +196 -0
  161. package/src/features/collections/controls/list-options.svelte +203 -0
  162. package/src/features/collections/controls/list-pagination.scss +78 -0
  163. package/src/features/collections/controls/list-pagination.svelte +115 -0
  164. package/src/features/collections/controls/list-toolbar.svelte +132 -0
  165. package/src/features/collections/controls/sort-heading.scss +52 -0
  166. package/src/features/collections/controls/sort-heading.svelte +78 -0
  167. package/src/features/collections/list-columns.ts +43 -0
  168. package/src/features/collections/list-workspace.ts +155 -54
  169. package/src/features/dashboard/dashboard-route.svelte +82 -86
  170. package/src/features/dashboard/dashboard.scss +131 -0
  171. package/src/features/development/development-tools.svelte +20 -36
  172. package/src/features/documents/document-action-bar.svelte +438 -0
  173. package/src/features/documents/document-api-view-loader.svelte +17 -6
  174. package/src/features/documents/document-api-view.scss +120 -0
  175. package/src/features/documents/document-api-view.svelte +169 -454
  176. package/src/features/documents/document-controller.svelte.ts +526 -613
  177. package/src/features/documents/document-credentials.scss +37 -0
  178. package/src/features/documents/document-credentials.svelte +72 -0
  179. package/src/features/documents/{document-route-dirty.ts → document-credentials.ts} +25 -0
  180. package/src/features/documents/document-field-sections.scss +39 -0
  181. package/src/features/documents/document-field-sections.svelte +4 -13
  182. package/src/features/documents/document-loading.scss +24 -0
  183. package/src/features/documents/document-lock-controller.svelte.ts +187 -0
  184. package/src/features/documents/document-operation-dialogs.svelte +110 -0
  185. package/src/features/documents/document-route-loader.svelte +17 -5
  186. package/src/features/documents/document-route.svelte +305 -811
  187. package/src/features/documents/document-schedule.scss +127 -0
  188. package/src/features/documents/document-schedule.svelte +323 -0
  189. package/src/features/documents/document-title.ts +25 -0
  190. package/src/features/documents/document.scss +274 -0
  191. package/src/features/documents/live-preview-panel.scss +117 -0
  192. package/src/features/documents/live-preview-panel.svelte +167 -168
  193. package/src/features/documents/live-preview.ts +2 -10
  194. package/src/features/documents/upload-document-preview-loader.svelte +26 -23
  195. package/src/features/localization/content-locale-switcher.scss +43 -0
  196. package/src/features/localization/content-locale-switcher.svelte +24 -17
  197. package/src/features/navigation/admin-breadcrumbs.scss +56 -0
  198. package/src/features/navigation/admin-breadcrumbs.svelte +102 -37
  199. package/src/features/navigation/admin-command-menu.svelte +38 -15
  200. package/src/features/navigation/admin-navigation.scss +133 -0
  201. package/src/features/navigation/admin-navigation.svelte +169 -0
  202. package/src/features/navigation/breadcrumb-context.svelte.ts +15 -0
  203. package/src/features/navigation/navigation-preference.svelte.ts +70 -0
  204. package/src/features/reference-browser/reference-browser-editor.svelte +180 -0
  205. package/src/features/reference-browser/reference-browser-list.scss +113 -0
  206. package/src/features/reference-browser/reference-browser-list.svelte +370 -0
  207. package/src/features/reference-browser/reference-browser-loader.svelte +26 -8
  208. package/src/features/reference-browser/reference-browser-workflow.svelte.ts +335 -236
  209. package/src/features/reference-browser/reference-browser.scss +217 -0
  210. package/src/features/reference-browser/reference-browser.svelte +154 -797
  211. package/src/features/reference-browser/reference-collection-picker.svelte +116 -0
  212. package/src/features/reference-browser/reference-list-query.ts +35 -0
  213. package/src/features/uploads/bulk-upload-controller.svelte.ts +604 -0
  214. package/src/features/uploads/bulk-upload-edit-all.svelte +92 -0
  215. package/src/features/uploads/bulk-upload-editor.svelte +191 -0
  216. package/src/features/uploads/bulk-upload-file-picker.svelte +70 -0
  217. package/src/features/uploads/bulk-upload-remote.svelte +100 -0
  218. package/src/features/uploads/bulk-upload-route-loader.svelte +54 -0
  219. package/src/features/uploads/bulk-upload-route.svelte +172 -592
  220. package/src/features/uploads/bulk-upload-sidebar.svelte +125 -0
  221. package/src/features/uploads/bulk-upload.scss +526 -0
  222. package/src/features/uploads/bulk-upload.ts +105 -14
  223. package/src/features/uploads/crop-surface.ts +150 -0
  224. package/src/features/uploads/image-editor.svelte +231 -0
  225. package/src/features/uploads/rendition-preview.svelte +64 -0
  226. package/src/features/uploads/upload-control.svelte +340 -0
  227. package/src/features/uploads/upload-document-contracts.ts +30 -0
  228. package/src/features/uploads/upload-draft.svelte.ts +294 -0
  229. package/src/features/uploads/upload-metadata-fields.scss +32 -0
  230. package/src/features/uploads/upload-metadata-fields.svelte +8 -7
  231. package/src/features/uploads/upload-thumbnail.svelte +47 -0
  232. package/src/features/uploads/upload.scss +361 -0
  233. package/src/features/versions/version-comparison.svelte +361 -0
  234. package/src/features/versions/version-diff-fields.svelte +136 -0
  235. package/src/features/versions/version-diff.ts +296 -41
  236. package/src/features/versions/version-history-controller.svelte.ts +311 -0
  237. package/src/features/versions/version-history-route-loader.svelte +32 -0
  238. package/src/features/versions/version-history-route.svelte +157 -522
  239. package/src/features/versions/version-history.scss +444 -0
  240. package/src/features/versions/version-history.ts +45 -0
  241. package/src/features/versions/version-table.svelte +91 -0
  242. package/src/fields/embedded-schema-draft-editor.scss +48 -0
  243. package/src/fields/embedded-schema-draft-editor.svelte +24 -16
  244. package/src/fields/field-collapsible.svelte +14 -8
  245. package/src/fields/field-layout.scss +171 -0
  246. package/src/fields/field-layout.svelte +3 -5
  247. package/src/fields/field-renderer.svelte +17 -29
  248. package/src/fields/field-shell.svelte +1 -1
  249. package/src/fields/field-tab-layout.svelte +25 -19
  250. package/src/fields/join/join-field-controller.svelte.ts +513 -0
  251. package/src/fields/join/join-field-values.ts +111 -0
  252. package/src/fields/join/join-field.scss +189 -0
  253. package/src/fields/join/join-field.svelte +164 -332
  254. package/src/fields/live-validation-feedback.svelte +4 -3
  255. package/src/fields/local-field-editor.svelte +2 -2
  256. package/src/fields/nested/block-name-input.svelte +11 -12
  257. package/src/fields/nested/block-picker.scss +128 -0
  258. package/src/fields/nested/block-picker.svelte +81 -0
  259. package/src/fields/nested/block-placeholder.svg +2 -0
  260. package/src/fields/nested/nested-field.scss +331 -0
  261. package/src/fields/nested/nested-field.svelte +265 -245
  262. package/src/fields/nested/sortable-row.svelte +9 -7
  263. package/src/fields/plugin-field.svelte +4 -6
  264. package/src/fields/point/point-field.svelte +51 -38
  265. package/src/fields/primitive-list/primitive-list-field.svelte +63 -19
  266. package/src/fields/primitive-list/primitive-list.scss +109 -0
  267. package/src/fields/radio/radio-field.svelte +2 -1
  268. package/src/fields/relationship/relationship-field-controller.svelte.ts +81 -41
  269. package/src/fields/relationship/relationship-field.svelte +144 -175
  270. package/src/fields/relationship/relationship-lookup-controller.svelte.ts +58 -30
  271. package/src/fields/relationship/relationship-query.ts +2 -10
  272. package/src/fields/relationship/relationship-quick-picker-controller.svelte.ts +36 -62
  273. package/src/fields/relationship/relationship-quick-picker.svelte +178 -227
  274. package/src/fields/relationship/relationship-selection.svelte +120 -0
  275. package/src/fields/relationship/relationship-value.ts +46 -7
  276. package/src/fields/relationship/relationship.scss +182 -0
  277. package/src/fields/scalar/code-field.svelte +101 -0
  278. package/src/fields/scalar/date-control-value.ts +13 -0
  279. package/src/fields/scalar/scalar-field.svelte +25 -67
  280. package/src/fields/select/select-field.svelte +132 -94
  281. package/src/fields/text/slug-field.scss +28 -0
  282. package/src/fields/text/slug-field.svelte +19 -15
  283. package/src/fields/text/text-field.svelte +1 -2
  284. package/src/fields/ui/ui-field.svelte +7 -4
  285. package/src/fields/virtual/virtual-field.svelte +2 -5
  286. package/src/index.ts +1 -2
  287. package/src/routing.ts +2 -0
  288. package/src/app/deferred-redirect.svelte +0 -23
  289. package/src/components/code-block/code-block.svelte +0 -107
  290. package/src/components/ui/bulk-action-bar/bulk-action-bar.svelte +0 -50
  291. package/src/components/ui/bulk-action-bar/index.ts +0 -1
  292. package/src/components/ui/button/index.ts +0 -8
  293. package/src/components/ui/calendar/calendar-caption.svelte +0 -72
  294. package/src/components/ui/calendar/calendar-cell.svelte +0 -20
  295. package/src/components/ui/calendar/calendar-day.svelte +0 -27
  296. package/src/components/ui/calendar/calendar-grid-row.svelte +0 -13
  297. package/src/components/ui/calendar/calendar-grid.svelte +0 -17
  298. package/src/components/ui/calendar/calendar-head-cell.svelte +0 -20
  299. package/src/components/ui/calendar/calendar-header.svelte +0 -20
  300. package/src/components/ui/calendar/calendar-heading.svelte +0 -17
  301. package/src/components/ui/calendar/calendar-month-select.svelte +0 -45
  302. package/src/components/ui/calendar/calendar-month.svelte +0 -16
  303. package/src/components/ui/calendar/calendar-months.svelte +0 -20
  304. package/src/components/ui/calendar/calendar-nav.svelte +0 -20
  305. package/src/components/ui/calendar/calendar-next-button.svelte +0 -30
  306. package/src/components/ui/calendar/calendar-prev-button.svelte +0 -30
  307. package/src/components/ui/calendar/calendar-year-select.svelte +0 -44
  308. package/src/components/ui/calendar/calendar.svelte +0 -118
  309. package/src/components/ui/calendar/index.ts +0 -1
  310. package/src/components/ui/checkbox/checkbox.svelte +0 -35
  311. package/src/components/ui/checkbox/index.ts +0 -1
  312. package/src/components/ui/dialog/dialog-close.svelte +0 -11
  313. package/src/components/ui/dialog/dialog-content.svelte +0 -52
  314. package/src/components/ui/dialog/dialog-description.svelte +0 -20
  315. package/src/components/ui/dialog/dialog-footer.svelte +0 -20
  316. package/src/components/ui/dialog/dialog-header.svelte +0 -20
  317. package/src/components/ui/dialog/dialog-overlay.svelte +0 -20
  318. package/src/components/ui/dialog/dialog-title.svelte +0 -17
  319. package/src/components/ui/dialog/dialog-trigger.svelte +0 -11
  320. package/src/components/ui/dialog/dialog.svelte +0 -7
  321. package/src/components/ui/dialog/index.ts +0 -8
  322. package/src/components/ui/empty-state/empty-state.svelte +0 -34
  323. package/src/components/ui/empty-state/index.ts +0 -1
  324. package/src/components/ui/input/index.ts +0 -1
  325. package/src/components/ui/pagination/index.ts +0 -1
  326. package/src/components/ui/pagination/pagination.svelte +0 -88
  327. package/src/components/ui/radio-group/radio-group-item.svelte +0 -17
  328. package/src/components/ui/slider/index.ts +0 -3
  329. package/src/components/ui/slider/slider-content.svelte +0 -59
  330. package/src/components/ui/slider/slider.svelte +0 -35
  331. package/src/components/ui/table/index.ts +0 -6
  332. package/src/components/ui/table/table-body.svelte +0 -20
  333. package/src/components/ui/table/table-cell.svelte +0 -23
  334. package/src/components/ui/table/table-head.svelte +0 -23
  335. package/src/components/ui/table/table-header.svelte +0 -20
  336. package/src/components/ui/table/table-row.svelte +0 -23
  337. package/src/components/ui/table/table.svelte +0 -22
  338. package/src/components/ui/textarea/index.ts +0 -1
  339. package/src/components/ui/tooltip/index.ts +0 -4
  340. package/src/components/ui/tooltip/tooltip-content.svelte +0 -50
  341. package/src/components/ui/tooltip/tooltip-provider.svelte +0 -7
  342. package/src/components/ui/tooltip/tooltip-trigger.svelte +0 -7
  343. package/src/components/ui/tooltip/tooltip.svelte +0 -7
  344. package/src/features/auth/account-menu.svelte +0 -159
  345. package/src/features/auth/account-security-controller.svelte.ts +0 -119
  346. package/src/features/collections/collection-list-bulk-actions.svelte +0 -143
  347. package/src/features/collections/collection-list-bulk-editor.svelte +0 -157
  348. package/src/features/collections/collection-list-filter-popover.svelte +0 -238
  349. package/src/features/collections/collection-list-workspace-toolbar.svelte +0 -409
  350. package/src/features/collections/collection-table-skeleton.svelte +0 -44
  351. package/src/features/documents/document-heading.ts +0 -20
  352. package/src/features/documents/upload-document-preview.svelte +0 -525
@@ -1,581 +1,216 @@
1
- <script lang="ts" module>
2
- function withContentLocale(path: string, locale: string | undefined) {
3
- if (locale === undefined) return path;
4
- return `${path}?${new URLSearchParams({ locale }).toString()}`;
5
- }
6
- </script>
7
-
8
1
  <script lang="ts">
9
2
  import { Link, useLocation, useNavigate, useParams } from "@hvniel/svelte-router";
10
- import type { OperationCapabilities, SchemaCollection } from "@riducms/protocol";
11
3
  import { untrack } from "svelte";
12
- import ArrowLeftIcon from "~icons/lucide/arrow-left";
13
- import CalendarClockIcon from "~icons/lucide/calendar-clock";
14
- import RotateCcwIcon from "~icons/lucide/rotate-ccw";
15
- import XIcon from "~icons/lucide/x";
16
-
4
+ import { Button } from "@riducms/ui";
17
5
  import { Banner } from "@admin/components/ui/banner";
18
- import { Button, buttonVariants } from "@admin/components/ui/button";
19
- import { Checkbox } from "@admin/components/ui/checkbox";
20
- import { DateValueControl } from "@admin/components/ui/date-value-control";
21
- import { Pagination } from "@admin/components/ui/pagination";
22
- import { Select, SelectContent, SelectItem, SelectTrigger } from "@admin/components/ui/select";
23
6
  import { Skeleton } from "@admin/components/ui/skeleton";
24
7
  import { getNotificationCenter } from "@admin/core/notifications/notification-center.svelte";
25
8
  import {
26
9
  documentIDFromAdminPath,
27
10
  documentPath,
11
+ documentAPIPath,
28
12
  documentVersionPath,
29
13
  documentVersionsPath,
30
14
  globalPath,
15
+ globalAPIPath,
31
16
  globalVersionPath,
32
17
  globalVersionsPath,
18
+ parseAdminVersionRevision,
19
+ withContentLocale,
33
20
  } from "@admin/core/routing/admin-paths";
34
21
  import { getAdminRuntime } from "@admin/core/runtime/admin-runtime.svelte";
35
- import type {
36
- AdminDocument,
37
- AdminScheduledPublish,
38
- AdminVersion,
39
- } from "@admin/core/api/admin-client";
40
- import { formatVersionValue, versionDiffRows } from "@admin/features/versions/version-diff";
22
+ import { getAdminBootstrapCoordinator } from "@admin/core/bootstrap/admin-bootstrap";
23
+ import { getBreadcrumbs } from "@admin/features/navigation/breadcrumb-context.svelte";
24
+ import { registerAdminScrollPage } from "@admin/core/routing/admin-scroll.svelte";
25
+ import { documentLabel } from "@admin/features/documents/document-title";
26
+ import { VersionHistoryController } from "@admin/features/versions/version-history-controller.svelte";
27
+ import { versionDate } from "@admin/features/versions/version-history";
28
+ import VersionTable from "@admin/features/versions/version-table.svelte";
29
+ import VersionComparison from "@admin/features/versions/version-comparison.svelte";
30
+ import "@admin/features/documents/document.scss";
31
+ import "@admin/features/versions/version-history.scss";
41
32
 
42
33
  const runtime = getAdminRuntime();
43
- const notifications = getNotificationCenter();
34
+ const i18n = runtime.i18n;
35
+ const bootstrap = getAdminBootstrapCoordinator();
36
+ const breadcrumbs = getBreadcrumbs();
37
+ const controller = new VersionHistoryController(runtime, getNotificationCenter());
38
+ registerAdminScrollPage({ ready: () => !controller.loading });
44
39
  const navigate = useNavigate();
45
- const location = $derived(useLocation());
46
- const params = $derived(useParams<"collection" | "document" | "global" | "revision">());
47
- const globalResource = $derived(params.global !== undefined);
48
- const slug = $derived(params.global ?? params.collection ?? "");
49
- let collectionDocumentID = $state(
50
- untrack(() => documentIDFromAdminPath(location.pathname) ?? "")
51
- );
52
- $effect(() => {
53
- if (globalResource) return;
54
- const decoded = documentIDFromAdminPath(location.pathname);
55
- if (decoded !== undefined) collectionDocumentID = decoded;
40
+ const location = useLocation();
41
+ const params = useParams<"collection" | "document" | "global" | "revision">();
42
+ const globalResource = $derived(params.current.global !== undefined);
43
+ const slug = $derived(params.current.global ?? params.current.collection ?? "");
44
+ let retainedID = $state(documentIDFromAdminPath(location.current.pathname) ?? "");
45
+
46
+ $effect.pre(() => {
47
+ const decoded = documentIDFromAdminPath(location.current.pathname);
48
+ // Keep the outgoing owner while its parent match is clearing.
49
+ if (decoded !== undefined) retainedID = decoded;
56
50
  });
57
- const documentID = $derived(globalResource ? slug : collectionDocumentID);
58
- const requestedRevision = $derived(parseRevision(params.revision));
51
+
52
+ const documentID = $derived(globalResource ? slug : retainedID);
53
+ const search = $derived(new URLSearchParams(location.current.search));
54
+ const locale = $derived(runtime.resolveContentLocale(search.get("locale")));
55
+ const requestedRevision = $derived(parseAdminVersionRevision(params.current.revision));
59
56
  const collection = $derived(
60
57
  (globalResource ? runtime.manifest?.globals : runtime.manifest?.collections)?.find(
61
- (candidate) => candidate.slug === slug
58
+ (item) => item.slug === slug
62
59
  )
63
60
  );
64
- const localization = $derived(runtime.manifest?.application.localization);
65
- const requestedLocale = $derived(new URLSearchParams(location.search).get("locale"));
66
- const contentLocale = $derived(
67
- localization?.locales.some((locale) => locale.code === requestedLocale)
68
- ? (requestedLocale ?? localization?.defaultLocale)
69
- : (runtime.contentLocale ?? localization?.defaultLocale)
61
+ const owner = $derived(
62
+ JSON.stringify([globalResource, slug, documentID, requestedRevision, locale])
70
63
  );
71
64
  const historyPath = $derived(
72
65
  withContentLocale(
73
66
  globalResource ? globalVersionsPath(slug) : documentVersionsPath(slug, documentID),
74
- contentLocale
67
+ locale
75
68
  )
76
69
  );
77
70
  const editPath = $derived(
71
+ withContentLocale(globalResource ? globalPath(slug) : documentPath(slug, documentID), locale)
72
+ );
73
+ const apiPath = $derived(
78
74
  withContentLocale(
79
- globalResource ? globalPath(slug) : documentPath(slug, documentID),
80
- contentLocale
75
+ globalResource ? globalAPIPath(slug) : documentAPIPath(slug, documentID),
76
+ locale
81
77
  )
82
78
  );
83
-
84
- let versions = $state.raw<AdminVersion[]>([]);
85
- let currentDocument = $state.raw<AdminDocument>();
86
- let operations = $state.raw<OperationCapabilities>();
87
- let schedules = $state.raw<AdminScheduledPublish[]>([]);
88
- let loading = $state(true);
89
- let error = $state<string>();
90
- let page = $state(1);
91
- let compareRevision = $state("");
92
- let modifiedOnly = $state(true);
93
- let restoring = $state(false);
94
- let scheduling = $state(false);
95
- let scheduleAt = $state(defaultScheduleTime());
96
-
97
- const selectedVersion = $derived(
98
- requestedRevision === undefined
99
- ? undefined
100
- : versions.find((version) => version.Revision === requestedRevision)
101
- );
102
- const comparisonVersion = $derived(
103
- versions.find((version) => version.Revision === Number(compareRevision))
104
- );
105
- const comparisonLabel = $derived(
106
- comparisonVersion === undefined
107
- ? runtime.i18n.t("versions:noComparison")
108
- : runtime.i18n.t("versions:revisionWithStatus", {
109
- revision: runtime.i18n.formatNumber(comparisonVersion.Revision),
110
- status: comparisonVersion.Status,
111
- })
112
- );
113
- const rows = $derived(
114
- collection === undefined || selectedVersion === undefined
115
- ? []
116
- : versionDiffRows(collection, selectedVersion, comparisonVersion, runtime.i18n)
79
+ const heading = $derived(
80
+ controller.document
81
+ ? documentLabel(collection, controller.document)
82
+ : collection
83
+ ? i18n.text(collection.labels.singular, collection.labels.singularTranslations)
84
+ : slug
117
85
  );
118
- const visibleRows = $derived(modifiedOnly ? rows.filter((row) => row.changed) : rows);
119
- const pageSize = 10;
120
- const totalPages = $derived(Math.max(1, Math.ceil(versions.length / pageSize)));
121
- const pageVersions = $derived(versions.slice((page - 1) * pageSize, page * pageSize));
122
86
 
123
- $effect(() => {
124
- const key = `${runtime.manifestRevision}:${slug}:${documentID}:${requestedRevision ?? "list"}:${contentLocale ?? "default"}`;
125
- if (key.startsWith("0:") || slug === "" || documentID === "") return;
126
- const request = new AbortController();
127
- void load(request.signal);
128
- return () => request.abort();
87
+ $effect.pre(() => {
88
+ const target = {
89
+ slug,
90
+ id: documentID,
91
+ global: globalResource,
92
+ revision: requestedRevision,
93
+ locale,
94
+ };
95
+ const revision = runtime.manifestRevision;
96
+ if (!revision || !slug || !documentID) return;
97
+ // Query-only changes alter presentation, not the owner of loaded history or pending restores.
98
+ untrack(() => {
99
+ const prepared = bootstrap.routeData(location.current.pathname, location.current.search);
100
+ controller.sync(
101
+ target,
102
+ revision,
103
+ prepared?.kind === (globalResource ? "global-versions" : "collection-versions")
104
+ ? prepared.versions
105
+ : undefined
106
+ );
107
+ });
129
108
  });
109
+
130
110
  $effect(() => {
131
- if (contentLocale === undefined || requestedLocale === contentLocale) return;
132
- const next = new URLSearchParams(location.search);
133
- next.set("locale", contentLocale);
134
- navigate(`${location.pathname}?${next.toString()}${location.hash}`, { replace: true });
111
+ const unregister = runtime.registerContentLocaleBlocker(() => controller.restoring);
112
+ return () => {
113
+ unregister();
114
+ controller.dispose();
115
+ };
135
116
  });
136
- $effect(() => runtime.registerContentLocaleBlocker(() => restoring || scheduling));
137
-
138
- async function load(signal: AbortSignal) {
139
- loading = true;
140
- error = undefined;
141
- try {
142
- const [history, detail, document, access, queued] = await Promise.all([
143
- globalResource
144
- ? runtime.client.globalVersions(slug, { signal, locale: contentLocale })
145
- : runtime.client.versions(slug, documentID, { signal, locale: contentLocale }),
146
- requestedRevision === undefined
147
- ? Promise.resolve(undefined)
148
- : globalResource
149
- ? runtime.client.globalVersion(slug, requestedRevision, {
150
- signal,
151
- locale: contentLocale,
152
- })
153
- : runtime.client.version(slug, documentID, requestedRevision, {
154
- signal,
155
- locale: contentLocale,
156
- }),
157
- globalResource
158
- ? runtime.client.global(slug, { signal, locale: contentLocale })
159
- : runtime.client.find(slug, documentID, { signal, locale: contentLocale }),
160
- globalResource
161
- ? runtime.client.globalAccess(slug, { signal, locale: contentLocale })
162
- : runtime.client.collectionAccess(slug, {
163
- id: documentID,
164
- signal,
165
- locale: contentLocale,
166
- }),
167
- globalResource
168
- ? Promise.resolve([])
169
- : runtime.client.scheduledPublishes(slug, documentID, { signal }),
170
- ]);
171
- if (signal.aborted) return;
172
- versions = [...history].sort((left, right) => right.Revision - left.Revision);
173
- if (
174
- detail !== undefined &&
175
- !versions.some((version) => version.Revision === detail.Revision)
176
- ) {
177
- versions = [detail, ...versions].sort((left, right) => right.Revision - left.Revision);
178
- }
179
- currentDocument = document;
180
- operations = access.operations;
181
- schedules = queued;
182
- page = Math.min(page, Math.max(1, Math.ceil(versions.length / pageSize)));
183
- if (detail !== undefined) {
184
- const index = versions.findIndex((version) => version.Revision === detail.Revision);
185
- compareRevision = String(
186
- versions[index + 1]?.Revision ?? versions[index - 1]?.Revision ?? ""
187
- );
188
- }
189
- } catch (cause) {
190
- if (!signal.aborted) {
191
- error =
192
- cause instanceof Error ? cause.message : runtime.i18n.t("versions:historyLoadFailed");
193
- }
194
- } finally {
195
- if (!signal.aborted) loading = false;
196
- }
197
- }
198
-
199
- async function restoreSelected(draft = false) {
200
- const version = selectedVersion;
201
- const document = currentDocument;
202
- if (version === undefined || document === undefined) return;
203
- const destination = withContentLocale(
204
- globalResource ? globalPath(slug) : documentPath(slug, documentID),
205
- contentLocale
206
- );
207
- restoring = true;
208
- try {
209
- const revision = typeof document._revision === "number" ? document._revision : 0;
210
- if (globalResource) {
211
- await runtime.client.restoreGlobal(slug, version.Revision, {
212
- revision,
213
- draft,
214
- locale: contentLocale,
215
- });
216
- } else {
217
- await runtime.client.restore(slug, documentID, version.Revision, {
218
- revision,
219
- draft,
220
- locale: contentLocale,
221
- });
222
- }
223
- runtime.documentsChanged();
224
- notifications.success({
225
- title: runtime.i18n.t(
226
- draft ? "versions:revisionRestoredAsDraft" : "versions:revisionRestored",
227
- {
228
- revision: runtime.i18n.formatNumber(version.Revision),
229
- }
230
- ),
231
- message: runtime.i18n.t("versions:previousCurrentPreserved"),
232
- });
233
- navigate(destination);
234
- } catch (cause) {
235
- notifications.error({
236
- title: runtime.i18n.t("versions:revisionNotRestored"),
237
- message:
238
- cause instanceof Error ? cause.message : runtime.i18n.t("versions:revisionRestoreFailed"),
239
- });
240
- } finally {
241
- restoring = false;
242
- }
243
- }
244
117
 
245
- async function schedulePublish() {
246
- const runAt = new Date(scheduleAt);
247
- if (Number.isNaN(runAt.valueOf()) || runAt <= new Date()) {
248
- notifications.error({ title: runtime.i18n.t("versions:chooseFuturePublishTime") });
249
- return;
250
- }
251
- scheduling = true;
252
- try {
253
- const revision =
254
- typeof currentDocument?._revision === "number" ? currentDocument._revision : 0;
255
- await runtime.client.schedulePublish(slug, documentID, runAt, { revision });
256
- schedules = await runtime.client.scheduledPublishes(slug, documentID);
257
- scheduleAt = defaultScheduleTime();
258
- notifications.success({
259
- title: runtime.i18n.t("versions:publishScheduled"),
260
- message: formatDate(runAt.toISOString()),
261
- });
262
- } catch (cause) {
263
- notifications.error({
264
- title: runtime.i18n.t("versions:publishNotScheduled"),
265
- message:
266
- cause instanceof Error ? cause.message : runtime.i18n.t("versions:publishScheduleFailed"),
267
- });
268
- } finally {
269
- scheduling = false;
270
- }
271
- }
118
+ $effect(() => {
119
+ if (!breadcrumbs) return;
120
+ const entry = {
121
+ pathname: location.current.pathname,
122
+ label: heading,
123
+ version: controller.selectedVersion
124
+ ? versionDate(controller.selectedVersion.CreatedAt, i18n)
125
+ : undefined,
126
+ };
127
+ breadcrumbs.document = entry;
128
+ return () => {
129
+ if (breadcrumbs.document === entry) breadcrumbs.document = undefined;
130
+ };
131
+ });
272
132
 
273
- async function cancelSchedule(job: AdminScheduledPublish) {
274
- try {
275
- await runtime.client.cancelScheduledPublish(slug, documentID, job.id);
276
- schedules = schedules.filter((candidate) => candidate.id !== job.id);
277
- notifications.success({ title: runtime.i18n.t("versions:scheduledPublishCancelled") });
278
- } catch (cause) {
279
- notifications.error({
280
- title: runtime.i18n.t("versions:scheduleNotCancelled"),
281
- message:
282
- cause instanceof Error ? cause.message : runtime.i18n.t("versions:scheduleCancelFailed"),
283
- });
284
- }
133
+ function updateQuery(changes: Record<string, string>) {
134
+ const next = new URLSearchParams(location.current.search);
135
+ for (const [key, value] of Object.entries(changes)) next.set(key, value);
136
+ navigate(`${location.current.pathname}?${next}`, { preventScrollReset: true });
285
137
  }
286
138
 
287
- function versionPath(version: AdminVersion) {
139
+ function versionPath(version: { Revision: number }) {
288
140
  return withContentLocale(
289
141
  globalResource
290
142
  ? globalVersionPath(slug, version.Revision)
291
143
  : documentVersionPath(slug, documentID, version.Revision),
292
- contentLocale
144
+ locale
293
145
  );
294
146
  }
295
-
296
- function parseRevision(value: string | undefined) {
297
- const revision = Number(value);
298
- return Number.isInteger(revision) && revision > 0 ? revision : undefined;
299
- }
300
-
301
- function defaultScheduleTime() {
302
- const date = new Date(Date.now() + 60 * 60 * 1_000);
303
- date.setSeconds(0, 0);
304
- return date.toISOString();
305
- }
306
-
307
- function formatDate(value: string) {
308
- const date = new Date(value);
309
- if (Number.isNaN(date.valueOf())) return value;
310
- return runtime.i18n.formatDate(date, { dateStyle: "medium", timeStyle: "short" });
311
- }
312
147
  </script>
313
148
 
314
- <section class="mx-auto w-full max-w-6xl px-5 py-8 sm:px-8 lg:px-11">
315
- <header class="flex flex-wrap items-start gap-4 border-b border-control-border pb-6">
316
- <Link
317
- class={buttonVariants({ variant: "ghost", size: "icon-sm" })}
318
- to={editPath}
319
- aria-label={runtime.i18n.t("versions:backToDocument")}
320
- >
321
- <ArrowLeftIcon class="size-3.5 rtl:rotate-180" />
322
- </Link>
323
- <div class="min-w-0 flex-1 basis-full sm:basis-auto">
324
- <p class="font-mono text-[9.5px] tracking-[0.15em] text-foreground-faint uppercase">
325
- {collection === undefined
326
- ? slug
327
- : runtime.i18n.text(collection.labels.singular, collection.labels.singularTranslations)} · {documentID}
328
- </p>
329
- <h1 class="mt-2 text-[28px] leading-tight font-semibold text-foreground">
330
- {requestedRevision === undefined
331
- ? runtime.i18n.t("versions:history")
332
- : runtime.i18n.t("versions:revision", {
333
- revision: runtime.i18n.formatNumber(requestedRevision),
334
- })}
335
- </h1>
336
- <p class="mt-2 text-[13px] text-foreground-sub">
337
- {selectedVersion === undefined
338
- ? runtime.i18n.t("versions:historyDescription")
339
- : runtime.i18n.t("versions:saved", {
340
- date: formatDate(selectedVersion.CreatedAt),
341
- status: selectedVersion.Status,
342
- })}
343
- </p>
344
- </div>
345
- {#if selectedVersion !== undefined}
346
- <Link class={buttonVariants({ variant: "outline" })} to={historyPath}>
347
- {runtime.i18n.t("versions:allVersions")}
348
- </Link>
349
- {#if operations?.update === true}
350
- <Button disabled={restoring} onclick={() => restoreSelected(false)}>
351
- <RotateCcwIcon class="size-3.5" />
352
- {restoring ? runtime.i18n.t("versions:restoring") : runtime.i18n.t("versions:restore")}
353
- </Button>
354
- {#if collection?.versionSettings?.drafts === true}
355
- <Button variant="outline" disabled={restoring} onclick={() => restoreSelected(true)}>
356
- <RotateCcwIcon class="size-3.5" />
357
- {runtime.i18n.t("versions:restoreAsDraft")}
358
- </Button>
359
- {/if}
360
- {/if}
361
- {/if}
362
- </header>
363
-
364
- {#if error !== undefined}
365
- <Banner class="mt-6" tone="destructive">{error}</Banner>
366
- {/if}
367
-
368
- {#if loading}
369
- <div class="mt-7 grid gap-4"><Skeleton class="h-12" /><Skeleton class="h-72" /></div>
370
- {:else if collection === undefined}
371
- <Banner class="mt-6" tone="warning">{runtime.i18n.t("versions:resourceUnavailable")}</Banner>
372
- {:else if selectedVersion !== undefined}
373
- <div
374
- class="mt-7 flex flex-wrap items-end gap-4 rounded-[4px] border border-control-border bg-control p-4"
375
- >
376
- <label class="grid min-w-56 gap-1.5 text-[11.5px] text-foreground-sub">
377
- {runtime.i18n.t("versions:compareRevisionWith", {
378
- revision: runtime.i18n.formatNumber(selectedVersion.Revision),
379
- })}
380
- <Select
381
- type="single"
382
- value={compareRevision}
383
- onValueChange={(revision) => (compareRevision = revision)}
149
+ <section class="ridu-versions" aria-busy={controller.loading}>
150
+ <header class="ridu-document-header ridu-versions__header">
151
+ <div class="ridu-document-heading">
152
+ <h1 class="ridu-document-title">{heading}</h1>
153
+ <nav class="ridu-document-tabs" aria-label={i18n.t("documents:documentViews")}>
154
+ <Link to={editPath} class="ridu-document-tab">{i18n.t("documents:edit")}</Link>
155
+ <Link
156
+ to={historyPath}
157
+ class="ridu-document-tab ridu-document-tab--active"
158
+ aria-current="page"
384
159
  >
385
- <SelectTrigger
386
- size="compact"
387
- class="w-full"
388
- aria-label={runtime.i18n.t("versions:comparisonRevision")}
389
- >
390
- {comparisonLabel}
391
- </SelectTrigger>
392
- <SelectContent>
393
- <SelectItem value="" label={runtime.i18n.t("versions:noComparison")} />
394
- {#each versions.filter((version) => version.Revision !== selectedVersion?.Revision) as version (version.ID)}
395
- <SelectItem
396
- value={String(version.Revision)}
397
- label={runtime.i18n.t("versions:revisionWithStatus", {
398
- revision: runtime.i18n.formatNumber(version.Revision),
399
- status: version.Status,
400
- })}
401
- />
402
- {/each}
403
- </SelectContent>
404
- </Select>
405
- </label>
406
- <label class="mb-2 flex items-center gap-2 text-[12px] text-foreground-muted">
407
- <Checkbox bind:checked={modifiedOnly} />
408
- {runtime.i18n.t("versions:modifiedFieldsOnly")}
409
- </label>
160
+ {i18n.t("documents:versions")}
161
+ {#if !controller.loading && !controller.error}
162
+ <span class="ridu-versions__count">
163
+ {i18n.formatNumber(controller.versions.length)}
164
+ </span>
165
+ {/if}
166
+ </Link>
167
+ <Link to={apiPath} class="ridu-document-tab">{i18n.t("documents:api")}</Link>
168
+ </nav>
410
169
  </div>
170
+ </header>
411
171
 
412
- <div class="mt-5 overflow-hidden rounded-[4px] border border-control-border">
413
- <div
414
- class="grid grid-cols-[minmax(130px,0.6fr)_minmax(0,1fr)_minmax(0,1fr)] border-b border-control-border bg-control px-4 py-2.5 font-mono text-[9.5px] tracking-[0.08em] text-foreground-faint uppercase"
415
- >
416
- <span>{runtime.i18n.t("versions:field")}</span>
417
- <span>
418
- {runtime.i18n.t("versions:revision", {
419
- revision:
420
- comparisonVersion === undefined
421
- ? "—"
422
- : runtime.i18n.formatNumber(comparisonVersion.Revision),
423
- })}
424
- </span>
425
- <span>
426
- {runtime.i18n.t("versions:revision", {
427
- revision: runtime.i18n.formatNumber(selectedVersion.Revision),
428
- })}
429
- </span>
430
- </div>
431
- {#if visibleRows.length === 0}
432
- <p class="px-4 py-10 text-center text-[13px] text-foreground-faint">
433
- {runtime.i18n.t("versions:noModifiedFields")}
434
- </p>
435
- {:else}
436
- {#each visibleRows as row (row.path)}
437
- <div
438
- class={[
439
- "grid grid-cols-[minmax(130px,0.6fr)_minmax(0,1fr)_minmax(0,1fr)] border-b border-control-border px-4 py-3 last:border-b-0",
440
- row.changed && "bg-primary/[0.025]",
441
- ]}
442
- >
443
- <div class="pe-4">
444
- <p class="text-[12.5px] text-foreground-muted">{row.label}</p>
445
- <p class="font-mono mt-1 text-[9px] text-foreground-faint">{row.path}</p>
446
- </div>
447
- <pre
448
- class="font-mono overflow-x-auto pe-5 text-wrap text-[11px] leading-5 text-foreground-sub">{formatVersionValue(
449
- row.before,
450
- runtime.i18n
451
- )}</pre>
452
- <pre
453
- class="font-mono overflow-x-auto text-wrap text-[11px] leading-5 text-foreground-body">{formatVersionValue(
454
- row.after,
455
- runtime.i18n
456
- )}</pre>
457
- </div>
458
- {/each}
172
+ {#if controller.error}
173
+ <div class="ridu-versions__message">
174
+ <Banner tone="destructive">{controller.error}</Banner>
175
+ {#if controller.canRetry}
176
+ <Button variant="outline" onclick={controller.retry}>
177
+ {i18n.t("general:retry")}
178
+ </Button>
459
179
  {/if}
460
180
  </div>
461
- {:else}
462
- {#if !globalResource && operations?.publish === true}
463
- <section
464
- class="mt-7 rounded-[4px] border border-control-border bg-control p-5"
465
- aria-labelledby="scheduled-publishing"
466
- >
467
- <div class="flex items-start gap-3">
468
- <CalendarClockIcon class="mt-0.5 size-4 text-primary" />
469
- <div>
470
- <h2 id="scheduled-publishing" class="text-[14px] font-medium text-foreground">
471
- {runtime.i18n.t("versions:scheduledPublishing")}
472
- </h2>
473
- <p class="mt-1 text-[12px] text-foreground-sub">
474
- {runtime.i18n.t("versions:scheduledPublishingDescription")}
475
- </p>
476
- </div>
477
- </div>
478
- <div class="mt-4 flex flex-wrap items-end gap-3">
479
- <label class="grid min-w-64 gap-1.5 text-[11.5px] text-foreground-sub">
480
- {runtime.i18n.t("versions:publishDateTime")}
481
- <DateValueControl
482
- id="scheduled-publish-at"
483
- appearance="date-time"
484
- value={scheduleAt}
485
- label={runtime.i18n.t("versions:publishDateTime")}
486
- onValueChange={(next) => (scheduleAt = next)}
487
- />
488
- </label>
489
- <Button disabled={scheduling} onclick={schedulePublish}>
490
- {scheduling
491
- ? runtime.i18n.t("versions:scheduling")
492
- : runtime.i18n.t("versions:schedulePublish")}
493
- </Button>
494
- </div>
495
- {#if schedules.length > 0}
496
- <ul class="mt-4 divide-y divide-control-border border-t border-control-border">
497
- {#each schedules as job (job.id)}
498
- <li class="flex items-center gap-3 py-3 text-[12px]">
499
- <span class="min-w-0 flex-1 text-foreground-muted">
500
- {formatDate(job.runAt)}
501
- <span class="font-mono ms-2 text-[9.5px] text-foreground-faint">
502
- {runtime.i18n.t("versions:revision", {
503
- revision: runtime.i18n.formatNumber(job.expectedRevision),
504
- })}
505
- </span>
506
- </span>
507
- <Button
508
- size="icon-xs"
509
- variant="ghost"
510
- onclick={() => cancelSchedule(job)}
511
- aria-label={runtime.i18n.t("versions:cancelScheduledPublish")}
512
- >
513
- <XIcon class="size-3" />
514
- </Button>
515
- </li>
516
- {/each}
517
- </ul>
518
- {/if}
519
- </section>
520
- {/if}
521
-
522
- <section class="mt-7" aria-labelledby="revision-list">
523
- <div class="flex items-end justify-between gap-4">
524
- <div>
525
- <h2 id="revision-list" class="text-[14px] font-medium text-foreground">
526
- {runtime.i18n.t("versions:savedRevisions")}
527
- </h2>
528
- <p class="mt-1 text-[12px] text-foreground-sub">
529
- {runtime.i18n.t("versions:savedRevisionsDescription")}
530
- </p>
531
- </div>
532
- <span class="font-mono text-[10px] text-foreground-faint">
533
- {runtime.i18n.t("versions:total", {
534
- count: runtime.i18n.formatNumber(versions.length),
535
- })}
536
- </span>
537
- </div>
538
- <div class="mt-4 overflow-hidden rounded-[4px] border border-control-border">
539
- {#if versions.length === 0}
540
- <p class="px-5 py-12 text-center text-[13px] text-foreground-faint">
541
- {runtime.i18n.t("versions:noRevisions")}
542
- </p>
543
- {:else}
544
- {#each pageVersions as version (version.ID)}
545
- <Link
546
- class="grid grid-cols-[70px_minmax(0,1fr)_120px] items-center border-b border-control-border px-4 py-3 transition-colors last:border-b-0 hover:bg-control"
547
- to={versionPath(version)}
548
- >
549
- <span class="font-mono text-[11px] text-foreground-muted">
550
- {runtime.i18n.t("versions:shortRevision", {
551
- revision: runtime.i18n.formatNumber(version.Revision),
552
- })}
553
- </span>
554
- <span class="text-[12.5px] text-foreground-body">
555
- {formatDate(version.CreatedAt)}
556
- </span>
557
- <span
558
- class="justify-self-end rounded-[3px] border border-control-border bg-control px-2 py-0.5 text-[10px] capitalize text-foreground-sub"
559
- >
560
- {version.Status}
561
- </span>
562
- </Link>
563
- {/each}
564
- {/if}
565
- </div>
566
- {#if versions.length > 0}
567
- <Pagination
568
- start={(page - 1) * pageSize + 1}
569
- end={Math.min(page * pageSize, versions.length)}
570
- total={versions.length}
571
- {page}
572
- {totalPages}
573
- hasPrevious={page > 1}
574
- hasNext={page < totalPages}
575
- onPrevious={() => (page -= 1)}
576
- onNext={() => (page += 1)}
181
+ {:else if controller.loading}
182
+ <div class="ridu-versions__message" data-ridu-loading-surface="versions">
183
+ <Skeleton class="h-12" /><Skeleton class="h-72" />
184
+ </div>
185
+ {:else if !collection}
186
+ <div class="ridu-versions__message">
187
+ <Banner tone="warning">{i18n.t("versions:resourceUnavailable")}</Banner>
188
+ </div>
189
+ {:else if requestedRevision !== undefined}
190
+ {#if controller.selectedVersion}
191
+ {#key owner}
192
+ <VersionComparison
193
+ {controller}
194
+ {collection}
195
+ {search}
196
+ onQueryChange={updateQuery}
197
+ onRestored={() => navigate(editPath)}
577
198
  />
578
- {/if}
579
- </section>
199
+ {/key}
200
+ {:else}
201
+ <div class="ridu-versions__message">
202
+ <Banner tone="warning">{i18n.t("versions:versionUnavailable")}</Banner>
203
+ </div>
204
+ {/if}
205
+ {:else}
206
+ <div class="ridu-versions__list">
207
+ <VersionTable
208
+ versions={controller.versions}
209
+ document={controller.document}
210
+ {search}
211
+ onQueryChange={updateQuery}
212
+ {versionPath}
213
+ />
214
+ </div>
580
215
  {/if}
581
216
  </section>