@riducms/admin 0.1.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 (226) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -0
  3. package/THIRD_PARTY_NOTICES.md +217 -0
  4. package/package.json +62 -0
  5. package/src/app/admin-core-view-route.svelte +89 -0
  6. package/src/app/admin-layout.svelte +457 -0
  7. package/src/app/admin-navigation-progress.svelte +204 -0
  8. package/src/app/admin-page-title.svelte +94 -0
  9. package/src/app/admin-provider-layer.svelte +26 -0
  10. package/src/app/admin-router-root.svelte +9 -0
  11. package/src/app/admin-routes.svelte +116 -0
  12. package/src/app/deferred-redirect.svelte +23 -0
  13. package/src/app/loading-boundary.svelte +70 -0
  14. package/src/app/not-found-route.svelte +29 -0
  15. package/src/app-meta.ts +2 -0
  16. package/src/app.css +467 -0
  17. package/src/app.svelte +88 -0
  18. package/src/assets/ridu-logo-arch.svg +3 -0
  19. package/src/assets/ridu-word-logo.svg +7 -0
  20. package/src/components/brand/ridu-logo.svelte +24 -0
  21. package/src/components/code-block/code-block.svelte +107 -0
  22. package/src/components/json-tree/json-tree-node.svelte +77 -0
  23. package/src/components/json-tree/json-viewer.svelte +113 -0
  24. package/src/components/ui/banner/banner.svelte +41 -0
  25. package/src/components/ui/banner/index.ts +5 -0
  26. package/src/components/ui/bulk-action-bar/bulk-action-bar.svelte +50 -0
  27. package/src/components/ui/bulk-action-bar/index.ts +1 -0
  28. package/src/components/ui/button/index.ts +8 -0
  29. package/src/components/ui/calendar/calendar-caption.svelte +72 -0
  30. package/src/components/ui/calendar/calendar-cell.svelte +20 -0
  31. package/src/components/ui/calendar/calendar-day.svelte +27 -0
  32. package/src/components/ui/calendar/calendar-grid-row.svelte +13 -0
  33. package/src/components/ui/calendar/calendar-grid.svelte +17 -0
  34. package/src/components/ui/calendar/calendar-head-cell.svelte +20 -0
  35. package/src/components/ui/calendar/calendar-header.svelte +20 -0
  36. package/src/components/ui/calendar/calendar-heading.svelte +17 -0
  37. package/src/components/ui/calendar/calendar-month-select.svelte +45 -0
  38. package/src/components/ui/calendar/calendar-month.svelte +16 -0
  39. package/src/components/ui/calendar/calendar-months.svelte +20 -0
  40. package/src/components/ui/calendar/calendar-nav.svelte +20 -0
  41. package/src/components/ui/calendar/calendar-next-button.svelte +30 -0
  42. package/src/components/ui/calendar/calendar-prev-button.svelte +30 -0
  43. package/src/components/ui/calendar/calendar-year-select.svelte +44 -0
  44. package/src/components/ui/calendar/calendar.svelte +118 -0
  45. package/src/components/ui/calendar/index.ts +1 -0
  46. package/src/components/ui/checkbox/checkbox.svelte +35 -0
  47. package/src/components/ui/checkbox/index.ts +1 -0
  48. package/src/components/ui/confirmation-dialog/confirmation-dialog.svelte +37 -0
  49. package/src/components/ui/confirmation-dialog/index.ts +1 -0
  50. package/src/components/ui/date-value-control/date-value-calendar.svelte +37 -0
  51. package/src/components/ui/date-value-control/date-value-control-loader.svelte +36 -0
  52. package/src/components/ui/date-value-control/date-value-control.svelte +177 -0
  53. package/src/components/ui/date-value-control/index.ts +3 -0
  54. package/src/components/ui/dialog/dialog-close.svelte +11 -0
  55. package/src/components/ui/dialog/dialog-content.svelte +52 -0
  56. package/src/components/ui/dialog/dialog-description.svelte +20 -0
  57. package/src/components/ui/dialog/dialog-footer.svelte +20 -0
  58. package/src/components/ui/dialog/dialog-header.svelte +20 -0
  59. package/src/components/ui/dialog/dialog-overlay.svelte +20 -0
  60. package/src/components/ui/dialog/dialog-title.svelte +17 -0
  61. package/src/components/ui/dialog/dialog-trigger.svelte +11 -0
  62. package/src/components/ui/dialog/dialog.svelte +7 -0
  63. package/src/components/ui/dialog/index.ts +8 -0
  64. package/src/components/ui/dropdown-menu/dropdown-menu-content.svelte +27 -0
  65. package/src/components/ui/dropdown-menu/dropdown-menu-item.svelte +20 -0
  66. package/src/components/ui/dropdown-menu/dropdown-menu-separator.svelte +17 -0
  67. package/src/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +16 -0
  68. package/src/components/ui/dropdown-menu/dropdown-menu.svelte +7 -0
  69. package/src/components/ui/dropdown-menu/index.ts +5 -0
  70. package/src/components/ui/empty-state/empty-state.svelte +34 -0
  71. package/src/components/ui/empty-state/index.ts +1 -0
  72. package/src/components/ui/input/index.ts +1 -0
  73. package/src/components/ui/kbd/index.ts +1 -0
  74. package/src/components/ui/kbd/kbd.svelte +24 -0
  75. package/src/components/ui/pagination/index.ts +1 -0
  76. package/src/components/ui/pagination/pagination.svelte +88 -0
  77. package/src/components/ui/popover/index.ts +3 -0
  78. package/src/components/ui/popover/popover-content.svelte +29 -0
  79. package/src/components/ui/popover/popover-trigger.svelte +11 -0
  80. package/src/components/ui/popover/popover.svelte +7 -0
  81. package/src/components/ui/radio-group/index.ts +3 -0
  82. package/src/components/ui/radio-group/radio-card-item.svelte +38 -0
  83. package/src/components/ui/radio-group/radio-group-item.svelte +17 -0
  84. package/src/components/ui/radio-group/radio-group.svelte +19 -0
  85. package/src/components/ui/select/index.ts +4 -0
  86. package/src/components/ui/select/select-content.svelte +38 -0
  87. package/src/components/ui/select/select-item.svelte +40 -0
  88. package/src/components/ui/select/select-scroll-down-button.svelte +23 -0
  89. package/src/components/ui/select/select-scroll-up-button.svelte +23 -0
  90. package/src/components/ui/select/select-trigger.svelte +29 -0
  91. package/src/components/ui/select/select.svelte +11 -0
  92. package/src/components/ui/sheet/index.ts +6 -0
  93. package/src/components/ui/sheet/sheet-close.svelte +7 -0
  94. package/src/components/ui/sheet/sheet-content.svelte +77 -0
  95. package/src/components/ui/sheet/sheet-description.svelte +17 -0
  96. package/src/components/ui/sheet/sheet-overlay.svelte +36 -0
  97. package/src/components/ui/sheet/sheet-title.svelte +17 -0
  98. package/src/components/ui/sheet/sheet-trigger.svelte +7 -0
  99. package/src/components/ui/sheet/sheet.svelte +7 -0
  100. package/src/components/ui/skeleton/index.ts +1 -0
  101. package/src/components/ui/skeleton/skeleton.svelte +31 -0
  102. package/src/components/ui/slider/index.ts +3 -0
  103. package/src/components/ui/slider/slider-content.svelte +59 -0
  104. package/src/components/ui/slider/slider.svelte +35 -0
  105. package/src/components/ui/sonner/index.ts +1 -0
  106. package/src/components/ui/sonner/sonner.svelte +55 -0
  107. package/src/components/ui/status-indicator/index.ts +5 -0
  108. package/src/components/ui/status-indicator/status-indicator.svelte +54 -0
  109. package/src/components/ui/table/index.ts +6 -0
  110. package/src/components/ui/table/table-body.svelte +20 -0
  111. package/src/components/ui/table/table-cell.svelte +23 -0
  112. package/src/components/ui/table/table-head.svelte +23 -0
  113. package/src/components/ui/table/table-header.svelte +20 -0
  114. package/src/components/ui/table/table-row.svelte +23 -0
  115. package/src/components/ui/table/table.svelte +22 -0
  116. package/src/components/ui/tabs/index.ts +8 -0
  117. package/src/components/ui/tabs/tabs-content.svelte +17 -0
  118. package/src/components/ui/tabs/tabs-list.svelte +40 -0
  119. package/src/components/ui/tabs/tabs-trigger.svelte +23 -0
  120. package/src/components/ui/tabs/tabs.svelte +19 -0
  121. package/src/components/ui/textarea/index.ts +1 -0
  122. package/src/components/ui/tooltip/index.ts +4 -0
  123. package/src/components/ui/tooltip/tooltip-content.svelte +50 -0
  124. package/src/components/ui/tooltip/tooltip-provider.svelte +7 -0
  125. package/src/components/ui/tooltip/tooltip-trigger.svelte +7 -0
  126. package/src/components/ui/tooltip/tooltip.svelte +7 -0
  127. package/src/core/api/admin-client.ts +45 -0
  128. package/src/core/forms/field-condition.ts +63 -0
  129. package/src/core/forms/form-controller.svelte.ts +369 -0
  130. package/src/core/forms/form-draft-recovery.ts +80 -0
  131. package/src/core/forms/form-schema.ts +609 -0
  132. package/src/core/forms/form-validation.ts +750 -0
  133. package/src/core/i18n/admin-i18n.svelte.ts +364 -0
  134. package/src/core/i18n/localized-schema.ts +111 -0
  135. package/src/core/notifications/notification-center.svelte.ts +69 -0
  136. package/src/core/notifications/validation-toast.svelte +18 -0
  137. package/src/core/plugins/field-registry.ts +60 -0
  138. package/src/core/plugins/row-label-registry.ts +62 -0
  139. package/src/core/preferences/preference-write-queue.ts +107 -0
  140. package/src/core/routing/admin-paths.ts +137 -0
  141. package/src/core/runtime/admin-runtime.svelte.ts +625 -0
  142. package/src/features/auth/account-menu.svelte +159 -0
  143. package/src/features/auth/account-navigation.svelte +44 -0
  144. package/src/features/auth/account-route.svelte +395 -0
  145. package/src/features/auth/account-security-controller.svelte.ts +119 -0
  146. package/src/features/auth/account-security-route.svelte +460 -0
  147. package/src/features/auth/auth-brand.svelte +17 -0
  148. package/src/features/auth/auth-frame.svelte +18 -0
  149. package/src/features/auth/create-first-user-controller.svelte.ts +220 -0
  150. package/src/features/auth/create-first-user-route.svelte +174 -0
  151. package/src/features/auth/forgot-password-route.svelte +78 -0
  152. package/src/features/auth/login-route.svelte +221 -0
  153. package/src/features/auth/request-verification-route.svelte +73 -0
  154. package/src/features/auth/reset-password-route.svelte +107 -0
  155. package/src/features/auth/verify-email-route.svelte +60 -0
  156. package/src/features/collections/collection-list-bulk-actions.svelte +143 -0
  157. package/src/features/collections/collection-list-bulk-editor.svelte +157 -0
  158. package/src/features/collections/collection-list-controller.svelte.ts +717 -0
  159. package/src/features/collections/collection-list-filter-popover.svelte +238 -0
  160. package/src/features/collections/collection-list-preferences-controller.svelte.ts +393 -0
  161. package/src/features/collections/collection-list-results.svelte +410 -0
  162. package/src/features/collections/collection-list-route.svelte +951 -0
  163. package/src/features/collections/collection-list-workspace-toolbar.svelte +409 -0
  164. package/src/features/collections/collection-table-skeleton.svelte +44 -0
  165. package/src/features/collections/list-workspace.ts +265 -0
  166. package/src/features/dashboard/dashboard-route.svelte +141 -0
  167. package/src/features/development/development-tools.svelte +217 -0
  168. package/src/features/documents/document-api-view-loader.svelte +31 -0
  169. package/src/features/documents/document-api-view.svelte +547 -0
  170. package/src/features/documents/document-controller.svelte.ts +1252 -0
  171. package/src/features/documents/document-field-partition.ts +18 -0
  172. package/src/features/documents/document-field-sections.svelte +46 -0
  173. package/src/features/documents/document-heading.ts +20 -0
  174. package/src/features/documents/document-route-dirty.ts +43 -0
  175. package/src/features/documents/document-route-loader.svelte +22 -0
  176. package/src/features/documents/document-route.svelte +1124 -0
  177. package/src/features/documents/live-preview-panel.svelte +390 -0
  178. package/src/features/documents/live-preview.ts +61 -0
  179. package/src/features/documents/upload-document-preview-loader.svelte +40 -0
  180. package/src/features/documents/upload-document-preview.svelte +525 -0
  181. package/src/features/localization/content-locale-switcher.svelte +93 -0
  182. package/src/features/navigation/admin-breadcrumbs.svelte +124 -0
  183. package/src/features/navigation/admin-command-menu.svelte +245 -0
  184. package/src/features/navigation/admin-resource-groups.ts +72 -0
  185. package/src/features/reference-browser/reference-browser-loader.svelte +15 -0
  186. package/src/features/reference-browser/reference-browser-workflow.svelte.ts +691 -0
  187. package/src/features/reference-browser/reference-browser.svelte +844 -0
  188. package/src/features/uploads/bulk-upload-route.svelte +624 -0
  189. package/src/features/uploads/bulk-upload.ts +39 -0
  190. package/src/features/uploads/upload-metadata-fields.svelte +112 -0
  191. package/src/features/versions/version-diff.ts +72 -0
  192. package/src/features/versions/version-history-route.svelte +581 -0
  193. package/src/fields/field-clipboard.ts +132 -0
  194. package/src/fields/field-layout.svelte +60 -0
  195. package/src/fields/field-layout.ts +57 -0
  196. package/src/fields/field-messages.svelte +29 -0
  197. package/src/fields/field-renderer.svelte +152 -0
  198. package/src/fields/field-shell.svelte +30 -0
  199. package/src/fields/field-tab-layout.svelte +99 -0
  200. package/src/fields/join/join-field.svelte +358 -0
  201. package/src/fields/localized-field-presentation.ts +17 -0
  202. package/src/fields/nested/nested-field.svelte +602 -0
  203. package/src/fields/nested/scoped-field.ts +78 -0
  204. package/src/fields/nested/sortable-row.svelte +32 -0
  205. package/src/fields/point/point-field.svelte +52 -0
  206. package/src/fields/radio/radio-field.svelte +34 -0
  207. package/src/fields/relationship/relationship-field-controller.svelte.ts +245 -0
  208. package/src/fields/relationship/relationship-field.svelte +258 -0
  209. package/src/fields/relationship/relationship-lookup-controller.svelte.ts +129 -0
  210. package/src/fields/relationship/relationship-query.ts +88 -0
  211. package/src/fields/relationship/relationship-quick-picker-controller.svelte.ts +198 -0
  212. package/src/fields/relationship/relationship-quick-picker.svelte +327 -0
  213. package/src/fields/relationship/relationship-value.ts +47 -0
  214. package/src/fields/scalar/date-control-value.ts +60 -0
  215. package/src/fields/scalar/date-value.ts +26 -0
  216. package/src/fields/scalar/scalar-field.svelte +137 -0
  217. package/src/fields/select/select-field.svelte +132 -0
  218. package/src/fields/select/select-value.ts +14 -0
  219. package/src/fields/text/slug-field.svelte +87 -0
  220. package/src/fields/text/slug.ts +38 -0
  221. package/src/fields/text/text-field.svelte +41 -0
  222. package/src/fields/ui/ui-field.svelte +12 -0
  223. package/src/fields/virtual/virtual-field.svelte +28 -0
  224. package/src/index.ts +49 -0
  225. package/src/main.ts +12 -0
  226. package/src/vite-env.d.ts +9 -0
@@ -0,0 +1,94 @@
1
+ <script lang="ts">
2
+ import { useLocation } from "@hvniel/svelte-router";
3
+
4
+ import { ADMIN_NAME } from "@admin/app-meta";
5
+ import { getAdminRuntime } from "@admin/core/runtime/admin-runtime.svelte";
6
+
7
+ const runtime = getAdminRuntime();
8
+ const location = $derived(useLocation());
9
+ const pageTitle = $derived.by(resolvePageTitle);
10
+
11
+ function resolvePageTitle() {
12
+ const segments = location.pathname.split("/").filter(Boolean);
13
+ if (segments.length === 0) return titled(runtime.i18n.t("dashboard:heading"));
14
+ if (segments[0] === "create-first-user") return titled(runtime.i18n.t("auth:createFirstUser"));
15
+ if (segments[0] === "login") return titled(runtime.i18n.t("auth:login"));
16
+ if (segments[0] === "forgot-password") return titled(runtime.i18n.t("auth:forgotPassword"));
17
+ if (segments[0] === "reset-password") return titled(runtime.i18n.t("auth:resetPassword"));
18
+ if (segments[0] === "request-verification")
19
+ return titled(runtime.i18n.t("auth:requestVerification"));
20
+ if (segments[0] === "verify-email") return titled(runtime.i18n.t("auth:verifyEmail"));
21
+ if (segments[0] === "account") {
22
+ return titled(
23
+ segments[1] === "security"
24
+ ? runtime.i18n.t("account:accountSecurity")
25
+ : runtime.i18n.t("account:account")
26
+ );
27
+ }
28
+ if (segments[0] === "collections" && segments[1] !== undefined) {
29
+ const collection = runtime.manifest?.collections.find(
30
+ (candidate) => candidate.slug === segments[1]
31
+ );
32
+ const singular =
33
+ collection === undefined
34
+ ? humanize(segments[1])
35
+ : runtime.i18n.text(collection.labels.singular, collection.labels.singularTranslations);
36
+ const plural =
37
+ collection === undefined
38
+ ? singular
39
+ : runtime.i18n.text(collection.labels.plural, collection.labels.pluralTranslations);
40
+ if (segments[2] === undefined) return titled(plural);
41
+ if (segments[2] === "create") {
42
+ return titled(
43
+ segments[3] === "api"
44
+ ? runtime.i18n.t("documents:apiFor", { label: singular })
45
+ : runtime.i18n.t("collections:createNew", { label: singular })
46
+ );
47
+ }
48
+ if (segments[2] === "trash")
49
+ return titled(
50
+ runtime.i18n.t("collections:trashFor", {
51
+ label: plural,
52
+ })
53
+ );
54
+ if (segments[3] === "api")
55
+ return titled(runtime.i18n.t("documents:apiFor", { label: singular }));
56
+ if (segments[3] === "versions")
57
+ return titled(runtime.i18n.t("documents:versionsFor", { label: singular }));
58
+ return titled(runtime.i18n.t("documents:editing", { label: singular }));
59
+ }
60
+ if (segments[0] === "globals" && segments[1] !== undefined) {
61
+ const global = runtime.manifest?.globals?.find((candidate) => candidate.slug === segments[1]);
62
+ const label =
63
+ global === undefined
64
+ ? humanize(segments[1])
65
+ : runtime.i18n.text(global.labels.singular, global.labels.singularTranslations);
66
+ if (segments[2] === "api") return titled(runtime.i18n.t("documents:apiFor", { label }));
67
+ if (segments[2] === "versions")
68
+ return titled(runtime.i18n.t("documents:versionsFor", { label }));
69
+ return titled(label);
70
+ }
71
+ const pluginPath = segments.join("/");
72
+ const pluginRoute = runtime.pluginRoutes.find((route) => route.path === pluginPath);
73
+ if (pluginRoute?.navigation?.labelKey !== undefined)
74
+ return titled(runtime.i18n.t(pluginRoute.navigation.labelKey));
75
+ if (pluginRoute?.navigation?.label !== undefined) return titled(pluginRoute.navigation.label);
76
+ return titled(humanize(segments.at(-1) ?? runtime.i18n.t("navigation:page")));
77
+ }
78
+
79
+ function titled(value: string) {
80
+ const application = runtime.manifest?.application;
81
+ const name =
82
+ application === undefined
83
+ ? ADMIN_NAME
84
+ : runtime.i18n.text(application.name, application.nameTranslations);
85
+ return `${value} - ${name}`;
86
+ }
87
+
88
+ function humanize(value: string) {
89
+ const decoded = decodeURIComponent(value).replaceAll("-", " ");
90
+ return decoded.charAt(0).toLocaleUpperCase(runtime.i18n.language) + decoded.slice(1);
91
+ }
92
+ </script>
93
+
94
+ <svelte:head><title>{pageTitle}</title></svelte:head>
@@ -0,0 +1,26 @@
1
+ <script lang="ts">
2
+ import type { AdminI18n, AdminProvider } from "@riducms/plugin";
3
+ import type { Snippet } from "svelte";
4
+
5
+ import AdminProviderLayer from "@admin/app/admin-provider-layer.svelte";
6
+
7
+ interface Props {
8
+ providers: readonly AdminProvider[];
9
+ children: Snippet;
10
+ i18n: AdminI18n;
11
+ index?: number;
12
+ }
13
+
14
+ let { providers, children, i18n, index = 0 }: Props = $props();
15
+ const provider = $derived(providers[index]);
16
+ </script>
17
+
18
+ {#snippet remainingView()}
19
+ <AdminProviderLayer {providers} {children} {i18n} index={index + 1} />
20
+ {/snippet}
21
+
22
+ {#if provider === undefined}
23
+ {@render children()}
24
+ {:else}
25
+ <provider.component defaultView={remainingView} {i18n} />
26
+ {/if}
@@ -0,0 +1,9 @@
1
+ <script lang="ts">
2
+ import AdminNavigationProgress from "@admin/app/admin-navigation-progress.svelte";
3
+ import AdminPageTitle from "@admin/app/admin-page-title.svelte";
4
+ import AdminRoutes from "@admin/app/admin-routes.svelte";
5
+ </script>
6
+
7
+ <AdminNavigationProgress />
8
+ <AdminPageTitle />
9
+ <AdminRoutes />
@@ -0,0 +1,116 @@
1
+ <script lang="ts">
2
+ import { Route, Routes, useLocation } from "@hvniel/svelte-router";
3
+
4
+ import {
5
+ adminCreateFirstUserPath,
6
+ adminLoginPath,
7
+ adminRedirectFromSearch,
8
+ adminRoutePatterns,
9
+ } from "@admin/core/routing/admin-paths";
10
+ import { getAdminRuntime } from "@admin/core/runtime/admin-runtime.svelte";
11
+ import CollectionListRoute from "@admin/features/collections/collection-list-route.svelte";
12
+ import BulkUploadRoute from "@admin/features/uploads/bulk-upload-route.svelte";
13
+ import LoginRoute from "@admin/features/auth/login-route.svelte";
14
+ import CreateFirstUserRoute from "@admin/features/auth/create-first-user-route.svelte";
15
+ import ForgotPasswordRoute from "@admin/features/auth/forgot-password-route.svelte";
16
+ import ResetPasswordRoute from "@admin/features/auth/reset-password-route.svelte";
17
+ import RequestVerificationRoute from "@admin/features/auth/request-verification-route.svelte";
18
+ import VerifyEmailRoute from "@admin/features/auth/verify-email-route.svelte";
19
+ import AccountSecurityRoute from "@admin/features/auth/account-security-route.svelte";
20
+ import AccountRoute from "@admin/features/auth/account-route.svelte";
21
+ import DashboardRoute from "@admin/features/dashboard/dashboard-route.svelte";
22
+ import DocumentRoute from "@admin/features/documents/document-route-loader.svelte";
23
+ import VersionHistoryRoute from "@admin/features/versions/version-history-route.svelte";
24
+
25
+ import AdminLayout from "@admin/app/admin-layout.svelte";
26
+ import AdminCoreViewRoute from "@admin/app/admin-core-view-route.svelte";
27
+ import DeferredRedirect from "@admin/app/deferred-redirect.svelte";
28
+ import NotFoundRoute from "@admin/app/not-found-route.svelte";
29
+
30
+ const runtime = getAdminRuntime();
31
+ const location = $derived(useLocation());
32
+ const requestedPath = $derived(`${location.pathname}${location.search}${location.hash}`);
33
+ const postLoginPath = $derived(adminRedirectFromSearch(location.search));
34
+ </script>
35
+
36
+ {#if runtime.authenticated}
37
+ <Routes>
38
+ <Route path="login">
39
+ {#snippet element()}
40
+ <DeferredRedirect to={postLoginPath} replace />
41
+ {/snippet}
42
+ </Route>
43
+ <Route path="create-first-user">
44
+ {#snippet element()}
45
+ <DeferredRedirect to={adminRoutePatterns.home} replace />
46
+ {/snippet}
47
+ </Route>
48
+ <Route Component={AdminLayout}>
49
+ <Route path="account" Component={AccountRoute} />
50
+ <Route path="account/security" Component={AccountSecurityRoute} />
51
+ <Route index Component={DashboardRoute} />
52
+ <Route path="collections/:collection">
53
+ {#snippet element()}
54
+ <AdminCoreViewRoute surface="collectionList" DefaultView={CollectionListRoute} />
55
+ {/snippet}
56
+ </Route>
57
+ <Route path="collections/:collection/trash" Component={CollectionListRoute} />
58
+ <Route path="collections/:collection/upload" Component={BulkUploadRoute} />
59
+ <Route path="collections/:collection/create/:view?">
60
+ {#snippet element()}
61
+ <AdminCoreViewRoute surface="collectionCreate" DefaultView={DocumentRoute} />
62
+ {/snippet}
63
+ </Route>
64
+ <Route
65
+ path="collections/:collection/:document/versions/:revision?"
66
+ Component={VersionHistoryRoute}
67
+ />
68
+ <Route path="collections/:collection/:document/:view?">
69
+ {#snippet element()}
70
+ <AdminCoreViewRoute surface="collectionEdit" DefaultView={DocumentRoute} />
71
+ {/snippet}
72
+ </Route>
73
+ <Route path="globals/:global/versions/:revision?" Component={VersionHistoryRoute} />
74
+ <Route path="globals/:global/:view?">
75
+ {#snippet element()}
76
+ <AdminCoreViewRoute surface="global" DefaultView={DocumentRoute} />
77
+ {/snippet}
78
+ </Route>
79
+ {#each runtime.pluginRoutes as pluginRoute (pluginRoute.path)}
80
+ <Route path={pluginRoute.path} Component={pluginRoute.component} />
81
+ {/each}
82
+ <Route path="*">
83
+ {#snippet element()}
84
+ <AdminCoreViewRoute surface="notFound" DefaultView={NotFoundRoute} />
85
+ {/snippet}
86
+ </Route>
87
+ </Route>
88
+ </Routes>
89
+ {:else if runtime.authBootstrapAvailable}
90
+ <Routes>
91
+ <Route path="create-first-user" Component={CreateFirstUserRoute} />
92
+ <Route path="*">
93
+ {#snippet element()}
94
+ <DeferredRedirect to={adminCreateFirstUserPath()} replace />
95
+ {/snippet}
96
+ </Route>
97
+ </Routes>
98
+ {:else}
99
+ <Routes>
100
+ <Route path="create-first-user">
101
+ {#snippet element()}
102
+ <DeferredRedirect to={adminRoutePatterns.login} replace />
103
+ {/snippet}
104
+ </Route>
105
+ <Route path="login" Component={LoginRoute} />
106
+ <Route path="forgot-password" Component={ForgotPasswordRoute} />
107
+ <Route path="reset-password" Component={ResetPasswordRoute} />
108
+ <Route path="request-verification" Component={RequestVerificationRoute} />
109
+ <Route path="verify-email" Component={VerifyEmailRoute} />
110
+ <Route path="*">
111
+ {#snippet element()}
112
+ <DeferredRedirect to={adminLoginPath(requestedPath)} replace />
113
+ {/snippet}
114
+ </Route>
115
+ </Routes>
116
+ {/if}
@@ -0,0 +1,23 @@
1
+ <script lang="ts">
2
+ import { useNavigate } from "@hvniel/svelte-router";
3
+
4
+ interface Props {
5
+ to: string;
6
+ replace?: boolean;
7
+ }
8
+
9
+ let { to, replace = false }: Props = $props();
10
+ const navigate = useNavigate();
11
+
12
+ $effect(() => {
13
+ let active = true;
14
+ // The router activates useNavigate in a parent effect. Deferring one
15
+ // microtask prevents a mounted redirect from racing that activation.
16
+ queueMicrotask(() => {
17
+ if (active) navigate(to, { replace });
18
+ });
19
+ return () => {
20
+ active = false;
21
+ };
22
+ });
23
+ </script>
@@ -0,0 +1,70 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+
4
+ import RiduLogo from "@admin/components/brand/ridu-logo.svelte";
5
+ import { Banner } from "@admin/components/ui/banner";
6
+ import { Button } from "@admin/components/ui/button";
7
+ import { getAdminRuntime } from "@admin/core/runtime/admin-runtime.svelte";
8
+
9
+ let { children }: { children: Snippet } = $props();
10
+ const runtime = getAdminRuntime();
11
+ const development = import.meta.hot !== undefined;
12
+ </script>
13
+
14
+ {#if runtime.loading}
15
+ <main
16
+ class="ridu-surface-grid grid min-h-screen place-items-center"
17
+ aria-label={runtime.i18n.t("general:loadingSchema")}
18
+ >
19
+ <div class="grid justify-items-center gap-4 text-[13px] text-foreground-muted">
20
+ <RiduLogo class="h-7 text-foreground" />
21
+ <span
22
+ class="size-4 animate-spin rounded-full border border-control-border-hover border-r-primary"
23
+ aria-hidden="true"
24
+ ></span>
25
+ {runtime.i18n.t("general:loadingSchema")}
26
+ </div>
27
+ </main>
28
+ {:else if runtime.error !== undefined}
29
+ <main class="ridu-surface-grid grid min-h-screen place-items-center p-6">
30
+ <section
31
+ class="grid max-w-[480px] gap-5 rounded-[4px] border border-control-border bg-background-surface p-7"
32
+ role="alert"
33
+ >
34
+ <p class="font-mono text-[10px] tracking-[0.14em] text-destructive uppercase">
35
+ {runtime.i18n.t("errors:adminBootstrapFailed")}
36
+ </p>
37
+ <h1 class="font-serif text-[31px] leading-tight tracking-[-0.015em]">
38
+ {runtime.i18n.t("errors:couldNotLoadApplication")}
39
+ </h1>
40
+ <p class="text-[14px] leading-6 text-foreground-muted">{runtime.error}</p>
41
+ <Button class="w-fit" onclick={() => runtime.bootstrap()}>
42
+ {runtime.i18n.t("general:retry")}
43
+ </Button>
44
+ </section>
45
+ </main>
46
+ {:else}
47
+ {@render children()}
48
+ {#if !development && runtime.refreshingManifest}
49
+ <div class="pointer-events-none fixed top-4 left-1/2 z-50 w-[min(92vw,420px)] -translate-x-1/2">
50
+ <Banner class="shadow-[var(--shadow-popover)]" role="status">
51
+ <span
52
+ class="size-3.5 shrink-0 animate-spin rounded-full border border-control-border-hover border-r-primary"
53
+ aria-hidden="true"
54
+ ></span>
55
+ {runtime.i18n.t("navigation:applyingSchemaUpdate")}
56
+ </Banner>
57
+ </div>
58
+ {:else if !development && runtime.manifestRefreshError !== undefined}
59
+ <div class="fixed top-4 left-1/2 z-50 w-[min(92vw,520px)] -translate-x-1/2">
60
+ <Banner class="shadow-[var(--shadow-popover)]" tone="destructive" role="alert">
61
+ <span class="size-2 shrink-0 rounded-full bg-destructive" aria-hidden="true"></span>
62
+ <span class="min-w-0">
63
+ {runtime.i18n.t("navigation:schemaUpdateFailed", {
64
+ message: runtime.manifestRefreshError,
65
+ })}
66
+ </span>
67
+ </Banner>
68
+ </div>
69
+ {/if}
70
+ {/if}
@@ -0,0 +1,29 @@
1
+ <script lang="ts">
2
+ import { Link } from "@hvniel/svelte-router";
3
+ import ArrowLeftIcon from "~icons/lucide/arrow-left";
4
+
5
+ import { getAdminRuntime } from "@admin/core/runtime/admin-runtime.svelte";
6
+
7
+ const runtime = getAdminRuntime();
8
+ </script>
9
+
10
+ <section class="grid min-h-[72vh] place-items-center px-5 py-12 text-center">
11
+ <div class="max-w-md">
12
+ <p class="font-mono text-[10px] tracking-[0.16em] text-primary uppercase">
13
+ {runtime.i18n.t("errors:notFoundCode")}
14
+ </p>
15
+ <h1 class="font-serif mt-3 text-[38px] leading-tight tracking-[-0.02em]">
16
+ {runtime.i18n.t("errors:notFoundHeading")}
17
+ </h1>
18
+ <p class="mt-3 text-[13.5px] leading-6 text-foreground-muted">
19
+ {runtime.i18n.t("errors:notFoundDescription")}
20
+ </p>
21
+ <Link
22
+ class="mt-6 inline-flex items-center gap-2 text-[13px] font-medium text-primary hover:text-primary-hover"
23
+ to="/"
24
+ >
25
+ <ArrowLeftIcon class="size-3.5 rtl:rotate-180" />
26
+ {runtime.i18n.t("errors:returnDashboard")}
27
+ </Link>
28
+ </div>
29
+ </section>
@@ -0,0 +1,2 @@
1
+ export const ADMIN_NAME = "Ridu";
2
+ export const ADMIN_TITLE = `${ADMIN_NAME} Admin`;