includio-cms 0.36.8 → 0.37.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 (259) hide show
  1. package/API.md +179 -3
  2. package/CHANGELOG.md +64 -0
  3. package/DOCS.md +28 -3
  4. package/ROADMAP.md +18 -0
  5. package/dist/admin/client/booking/booking-dashboard.svelte +40 -0
  6. package/dist/admin/client/booking/booking-dashboard.svelte.d.ts +3 -0
  7. package/dist/admin/client/booking/customer-panel.svelte +102 -0
  8. package/dist/admin/client/booking/customer-panel.svelte.d.ts +11 -0
  9. package/dist/admin/client/booking/customer-view.d.ts +52 -0
  10. package/dist/admin/client/booking/customer-view.js +134 -0
  11. package/dist/admin/client/booking/participant-data-panel.svelte +330 -0
  12. package/dist/admin/client/booking/participant-data-panel.svelte.d.ts +22 -0
  13. package/dist/admin/client/booking/reservation-detail-page.svelte +637 -0
  14. package/dist/admin/client/booking/reservation-detail-page.svelte.d.ts +3 -0
  15. package/dist/admin/client/booking/reservations-page.svelte +221 -0
  16. package/dist/admin/client/booking/reservations-page.svelte.d.ts +3 -0
  17. package/dist/admin/client/collection/collection-entries.svelte +21 -2
  18. package/dist/admin/client/collection/entry-link.svelte +6 -4
  19. package/dist/admin/client/collection/entry-link.svelte.d.ts +3 -1
  20. package/dist/admin/client/collection/row-actions.svelte +12 -4
  21. package/dist/admin/client/collection/row-actions.svelte.d.ts +1 -0
  22. package/dist/admin/client/collection/status-badge.svelte +39 -3
  23. package/dist/admin/client/collection/status-badge.svelte.d.ts +5 -3
  24. package/dist/admin/client/entry/entry.svelte +26 -9
  25. package/dist/admin/client/entry/hybrid/hybrid-preview.svelte +73 -10
  26. package/dist/admin/client/entry/hybrid/hybrid-preview.svelte.d.ts +6 -5
  27. package/dist/admin/client/entry/preview-views-context.svelte.d.ts +22 -0
  28. package/dist/admin/client/entry/preview-views-context.svelte.js +58 -0
  29. package/dist/admin/client/entry/preview-views.d.ts +27 -0
  30. package/dist/admin/client/entry/preview-views.js +33 -0
  31. package/dist/admin/client/index.d.ts +3 -0
  32. package/dist/admin/client/index.js +3 -0
  33. package/dist/admin/client/modules/dashboard-registry.d.ts +7 -0
  34. package/dist/admin/client/modules/dashboard-registry.js +11 -0
  35. package/dist/admin/client/modules/module-dashboard-page.svelte +26 -0
  36. package/dist/admin/client/modules/module-dashboard-page.svelte.d.ts +6 -0
  37. package/dist/admin/client/modules/stat-card.svelte +22 -0
  38. package/dist/admin/client/modules/stat-card.svelte.d.ts +10 -0
  39. package/dist/admin/client/shop/shop-dashboard.svelte +29 -0
  40. package/dist/admin/client/shop/shop-dashboard.svelte.d.ts +3 -0
  41. package/dist/admin/components/fields/relation-field.svelte +11 -2
  42. package/dist/admin/components/icons/icon-map.js +8 -0
  43. package/dist/admin/components/layout/app-sidebar.svelte +45 -14
  44. package/dist/admin/components/layout/context-switcher.svelte +90 -0
  45. package/dist/admin/components/layout/context-switcher.svelte.d.ts +3 -0
  46. package/dist/admin/components/layout/lang.d.ts +4 -0
  47. package/dist/admin/components/layout/lang.js +8 -0
  48. package/dist/admin/components/layout/layout-renderer.svelte +2 -1
  49. package/dist/admin/components/layout/layout-tabs.svelte +14 -0
  50. package/dist/admin/components/layout/layout-tabs.svelte.d.ts +2 -0
  51. package/dist/admin/components/layout/nav-collections.svelte +16 -9
  52. package/dist/admin/components/layout/nav-collections.svelte.d.ts +5 -1
  53. package/dist/admin/components/layout/nav-global.svelte +45 -0
  54. package/dist/admin/components/layout/nav-global.svelte.d.ts +3 -0
  55. package/dist/admin/components/layout/nav-main.svelte +8 -32
  56. package/dist/admin/components/layout/nav-main.svelte.d.ts +6 -1
  57. package/dist/admin/components/layout/nav-module.svelte +57 -0
  58. package/dist/admin/components/layout/nav-module.svelte.d.ts +7 -0
  59. package/dist/admin/components/layout/nav-section.svelte +1 -1
  60. package/dist/admin/components/layout/nav-section.svelte.d.ts +1 -1
  61. package/dist/admin/components/layout/nav-singletons.svelte +14 -7
  62. package/dist/admin/components/layout/nav-singletons.svelte.d.ts +5 -1
  63. package/dist/admin/remote/booking.remote.d.ts +244 -0
  64. package/dist/admin/remote/booking.remote.js +283 -0
  65. package/dist/admin/remote/entry.remote.d.ts +3 -0
  66. package/dist/admin/remote/entry.remote.js +14 -3
  67. package/dist/admin/remote/index.d.ts +2 -0
  68. package/dist/admin/remote/index.js +2 -0
  69. package/dist/admin/remote/modules.remote.d.ts +10 -0
  70. package/dist/admin/remote/modules.remote.js +14 -0
  71. package/dist/admin/state/active-context-resolve.d.ts +37 -0
  72. package/dist/admin/state/active-context-resolve.js +66 -0
  73. package/dist/admin/state/active-context.svelte.d.ts +8 -0
  74. package/dist/admin/state/active-context.svelte.js +27 -0
  75. package/dist/booking/adjustments.d.ts +33 -0
  76. package/dist/booking/adjustments.js +26 -0
  77. package/dist/booking/capacity.d.ts +12 -0
  78. package/dist/booking/capacity.js +10 -0
  79. package/dist/booking/client/index.d.ts +39 -0
  80. package/dist/booking/client/index.js +44 -0
  81. package/dist/booking/client/use-booking.svelte.d.ts +25 -0
  82. package/dist/booking/client/use-booking.svelte.js +56 -0
  83. package/dist/booking/config.d.ts +156 -0
  84. package/dist/booking/config.js +36 -0
  85. package/dist/booking/draft.d.ts +77 -0
  86. package/dist/booking/draft.js +67 -0
  87. package/dist/booking/expiry-logic.d.ts +30 -0
  88. package/dist/booking/expiry-logic.js +33 -0
  89. package/dist/booking/index.d.ts +18 -0
  90. package/dist/booking/index.js +52 -0
  91. package/dist/booking/ledger.d.ts +46 -0
  92. package/dist/booking/ledger.js +60 -0
  93. package/dist/booking/number.d.ts +2 -0
  94. package/dist/booking/number.js +11 -0
  95. package/dist/booking/participant-fields/access.d.ts +10 -0
  96. package/dist/booking/participant-fields/access.js +14 -0
  97. package/dist/booking/participant-fields/age.d.ts +12 -0
  98. package/dist/booking/participant-fields/age.js +28 -0
  99. package/dist/booking/participant-fields/completeness.d.ts +8 -0
  100. package/dist/booking/participant-fields/completeness.js +13 -0
  101. package/dist/booking/participant-fields/group.d.ts +6 -0
  102. package/dist/booking/participant-fields/group.js +15 -0
  103. package/dist/booking/participant-fields/index.d.ts +6 -0
  104. package/dist/booking/participant-fields/index.js +6 -0
  105. package/dist/booking/participant-fields/types.d.ts +50 -0
  106. package/dist/booking/participant-fields/types.js +5 -0
  107. package/dist/booking/participant-fields/validate.d.ts +7 -0
  108. package/dist/booking/participant-fields/validate.js +44 -0
  109. package/dist/booking/participant-pricing.d.ts +93 -0
  110. package/dist/booking/participant-pricing.js +91 -0
  111. package/dist/booking/participants-view.d.ts +32 -0
  112. package/dist/booking/participants-view.js +22 -0
  113. package/dist/booking/payment-context.d.ts +19 -0
  114. package/dist/booking/payment-context.js +29 -0
  115. package/dist/booking/pricing-inputs.d.ts +28 -0
  116. package/dist/booking/pricing-inputs.js +35 -0
  117. package/dist/booking/pricing.d.ts +86 -0
  118. package/dist/booking/pricing.js +125 -0
  119. package/dist/booking/recompute.d.ts +22 -0
  120. package/dist/booking/recompute.js +31 -0
  121. package/dist/booking/reminders-schedule.d.ts +11 -0
  122. package/dist/booking/reminders-schedule.js +28 -0
  123. package/dist/booking/server/adjustments.d.ts +27 -0
  124. package/dist/booking/server/adjustments.js +136 -0
  125. package/dist/booking/server/bookings.d.ts +45 -0
  126. package/dist/booking/server/bookings.js +288 -0
  127. package/dist/booking/server/catalog.d.ts +9 -0
  128. package/dist/booking/server/catalog.js +20 -0
  129. package/dist/booking/server/db.d.ts +7 -0
  130. package/dist/booking/server/db.js +18 -0
  131. package/dist/booking/server/email.d.ts +23 -0
  132. package/dist/booking/server/email.js +59 -0
  133. package/dist/booking/server/emailTemplateRegistry.d.ts +29 -0
  134. package/dist/booking/server/emailTemplateRegistry.js +125 -0
  135. package/dist/booking/server/http/create-handler.d.ts +5 -0
  136. package/dist/booking/server/http/create-handler.js +68 -0
  137. package/dist/booking/server/http/index.d.ts +4 -0
  138. package/dist/booking/server/http/index.js +4 -0
  139. package/dist/booking/server/http/maintenance-handler.d.ts +9 -0
  140. package/dist/booking/server/http/maintenance-handler.js +36 -0
  141. package/dist/booking/server/http/portal-handler.d.ts +13 -0
  142. package/dist/booking/server/http/portal-handler.js +99 -0
  143. package/dist/booking/server/http/webhook-handler.d.ts +9 -0
  144. package/dist/booking/server/http/webhook-handler.js +39 -0
  145. package/dist/booking/server/index.d.ts +12 -0
  146. package/dist/booking/server/index.js +14 -0
  147. package/dist/booking/server/maintenance.d.ts +28 -0
  148. package/dist/booking/server/maintenance.js +134 -0
  149. package/dist/booking/server/participants.d.ts +26 -0
  150. package/dist/booking/server/participants.js +92 -0
  151. package/dist/booking/server/payments.d.ts +34 -0
  152. package/dist/booking/server/payments.js +207 -0
  153. package/dist/booking/server/portal-session.d.ts +16 -0
  154. package/dist/booking/server/portal-session.js +42 -0
  155. package/dist/booking/server/portal.d.ts +207 -0
  156. package/dist/booking/server/portal.js +234 -0
  157. package/dist/booking/server/refunds.d.ts +7 -0
  158. package/dist/booking/server/refunds.js +58 -0
  159. package/dist/booking/server/reminders.d.ts +9 -0
  160. package/dist/booking/server/reminders.js +24 -0
  161. package/dist/booking/server/validation.d.ts +17 -0
  162. package/dist/booking/server/validation.js +55 -0
  163. package/dist/booking/state-machine.d.ts +41 -0
  164. package/dist/booking/state-machine.js +73 -0
  165. package/dist/booking/templates/_partials/footer.en.html +4 -0
  166. package/dist/booking/templates/_partials/footer.pl.html +4 -0
  167. package/dist/booking/templates/_partials/header.en.html +4 -0
  168. package/dist/booking/templates/_partials/header.pl.html +4 -0
  169. package/dist/booking/templates/balance-due.en.html +10 -0
  170. package/dist/booking/templates/balance-due.pl.html +10 -0
  171. package/dist/booking/templates/balance-overdue-cancelled.en.html +5 -0
  172. package/dist/booking/templates/balance-overdue-cancelled.pl.html +5 -0
  173. package/dist/booking/templates/booking-received.en.html +9 -0
  174. package/dist/booking/templates/booking-received.pl.html +9 -0
  175. package/dist/booking/templates/deposit-paid.en.html +6 -0
  176. package/dist/booking/templates/deposit-paid.pl.html +6 -0
  177. package/dist/booking/templates/payment-link.en.html +5 -0
  178. package/dist/booking/templates/payment-link.pl.html +5 -0
  179. package/dist/booking/templates/payment-received.en.html +10 -0
  180. package/dist/booking/templates/payment-received.pl.html +10 -0
  181. package/dist/booking/totals.d.ts +38 -0
  182. package/dist/booking/totals.js +61 -0
  183. package/dist/booking/webhook-logic.d.ts +28 -0
  184. package/dist/booking/webhook-logic.js +50 -0
  185. package/dist/cli/create-user.d.ts +7 -1
  186. package/dist/cli/create-user.js +23 -15
  187. package/dist/cli/index.js +55 -7
  188. package/dist/cli/scaffold/admin.d.ts +6 -0
  189. package/dist/cli/scaffold/admin.js +5 -3
  190. package/dist/cli/scaffold/booking.d.ts +12 -0
  191. package/dist/cli/scaffold/booking.js +103 -0
  192. package/dist/core/cms.d.ts +24 -9
  193. package/dist/core/cms.js +72 -34
  194. package/dist/core/config/validatePreviews.d.ts +12 -0
  195. package/dist/core/config/validatePreviews.js +90 -0
  196. package/dist/core/fields/dotPath.d.ts +4 -0
  197. package/dist/core/fields/dotPath.js +18 -0
  198. package/dist/core/fields/slugPath.d.ts +9 -1
  199. package/dist/core/fields/slugPath.js +16 -2
  200. package/dist/core/fields/slugify.d.ts +3 -0
  201. package/dist/core/fields/slugify.js +4 -0
  202. package/dist/core/server/entries/operations/duplicate.d.ts +10 -0
  203. package/dist/core/server/entries/operations/duplicate.js +73 -0
  204. package/dist/core/server/entries/operations/slugNormalize.d.ts +8 -0
  205. package/dist/core/server/entries/operations/slugNormalize.js +20 -0
  206. package/dist/core/server/entries/operations/slugUniqueness.d.ts +17 -0
  207. package/dist/core/server/entries/operations/slugUniqueness.js +39 -0
  208. package/dist/core/server/fields/resolveRelationFields.d.ts +11 -2
  209. package/dist/core/server/fields/resolveRelationFields.js +12 -3
  210. package/dist/core/server/generator/generator.js +7 -2
  211. package/dist/db-postgres/schema/booking/bookingAdjustments.d.ts +186 -0
  212. package/dist/db-postgres/schema/booking/bookingAdjustments.js +22 -0
  213. package/dist/db-postgres/schema/booking/bookingItemPersons.d.ts +117 -0
  214. package/dist/db-postgres/schema/booking/bookingItemPersons.js +22 -0
  215. package/dist/db-postgres/schema/booking/bookingItems.d.ts +181 -0
  216. package/dist/db-postgres/schema/booking/bookingItems.js +22 -0
  217. package/dist/db-postgres/schema/booking/bookingPayments.d.ts +204 -0
  218. package/dist/db-postgres/schema/booking/bookingPayments.js +25 -0
  219. package/dist/db-postgres/schema/booking/bookingPersons.d.ts +99 -0
  220. package/dist/db-postgres/schema/booking/bookingPersons.js +17 -0
  221. package/dist/db-postgres/schema/booking/bookingReminders.d.ts +131 -0
  222. package/dist/db-postgres/schema/booking/bookingReminders.js +18 -0
  223. package/dist/db-postgres/schema/booking/bookings.d.ts +307 -0
  224. package/dist/db-postgres/schema/booking/bookings.js +40 -0
  225. package/dist/db-postgres/schema/booking/index.d.ts +7 -0
  226. package/dist/db-postgres/schema/booking/index.js +7 -0
  227. package/dist/db-postgres/schema/index.d.ts +1 -0
  228. package/dist/db-postgres/schema/index.js +1 -0
  229. package/dist/db-postgres/schema-booking.d.ts +1 -0
  230. package/dist/db-postgres/schema-booking.js +1 -0
  231. package/dist/server/security/rate-limit-handler.d.ts +19 -0
  232. package/dist/server/security/rate-limit-handler.js +34 -0
  233. package/dist/shop/adapters/payu/client.js +3 -1
  234. package/dist/shop/adapters/payu/index.js +37 -17
  235. package/dist/shop/adapters/payu/payload.d.ts +6 -0
  236. package/dist/shop/adapters/payu/payload.js +1 -1
  237. package/dist/shop/adapters/stripe/index.js +36 -21
  238. package/dist/shop/index.d.ts +11 -2
  239. package/dist/shop/index.js +43 -1
  240. package/dist/shop/rate-limit.d.ts +1 -9
  241. package/dist/shop/rate-limit.js +3 -28
  242. package/dist/shop/types.d.ts +19 -0
  243. package/dist/sveltekit/config.d.ts +4 -0
  244. package/dist/sveltekit/config.js +6 -0
  245. package/dist/types/cms.d.ts +15 -1
  246. package/dist/types/cms.schema.d.ts +96 -2
  247. package/dist/types/cms.schema.js +84 -1
  248. package/dist/types/config.d.ts +33 -0
  249. package/dist/types/layout.d.ts +6 -0
  250. package/dist/types/modules.d.ts +133 -0
  251. package/dist/types/modules.js +27 -0
  252. package/dist/updates/0.36.9/index.d.ts +2 -0
  253. package/dist/updates/0.36.9/index.js +12 -0
  254. package/dist/updates/0.37.0/index.d.ts +2 -0
  255. package/dist/updates/0.37.0/index.js +52 -0
  256. package/dist/updates/index.js +5 -1
  257. package/package.json +19 -1
  258. package/dist/admin/components/layout/nav-shop.svelte +0 -33
  259. package/dist/admin/components/layout/nav-shop.svelte.d.ts +0 -3
@@ -0,0 +1,90 @@
1
+ <script lang="ts">
2
+ import * as DropdownMenu from '../../../components/ui/dropdown-menu/index.js';
3
+ import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
4
+ import { getLocalizedLabel } from '../../utils/collectionLabel.js';
5
+ import { getIcon } from '../icons/icon-map.js';
6
+ import { getRemotes } from '../../../sveltekit/index.js';
7
+ import {
8
+ getActiveContext,
9
+ setActiveContext,
10
+ CONTENT_CONTEXT
11
+ } from '../../state/active-context.svelte.js';
12
+ import { sidebarLang } from './lang.js';
13
+ import { goto } from '$app/navigation';
14
+ import ChevronDownIcon from '@tabler/icons-svelte/icons/chevron-down';
15
+ import FileTextIcon from '@tabler/icons-svelte/icons/file-text';
16
+ import CheckIcon from '@tabler/icons-svelte/icons/check';
17
+ import type { Localized } from '../../../types/languages.js';
18
+
19
+ const interfaceLanguage = useInterfaceLanguage();
20
+ const remotes = getRemotes();
21
+
22
+ const modulesQuery = $derived(remotes.getModules());
23
+ const modules = $derived(modulesQuery.current ?? []);
24
+ const moduleIds = $derived(modules.map((m) => m.id));
25
+ const active = $derived(getActiveContext(moduleIds));
26
+ const lang = $derived(sidebarLang[interfaceLanguage.current].contexts);
27
+
28
+ function labelFor(id: string): string {
29
+ if (id === CONTENT_CONTEXT) return lang.content;
30
+ const m = modules.find((x) => x.id === id);
31
+ if (!m) return id;
32
+ return (
33
+ getLocalizedLabel(
34
+ (m.context.contextLabel ?? m.label) as Localized,
35
+ interfaceLanguage.current
36
+ ) || m.id
37
+ );
38
+ }
39
+
40
+ function iconFor(id: string) {
41
+ if (id === CONTENT_CONTEXT) return FileTextIcon;
42
+ const m = modules.find((x) => x.id === id);
43
+ return (m && getIcon(m.icon)) || FileTextIcon;
44
+ }
45
+
46
+ function choose(id: string) {
47
+ setActiveContext(id);
48
+ if (id === CONTENT_CONTEXT) {
49
+ goto('/admin');
50
+ return;
51
+ }
52
+ const m = modules.find((x) => x.id === id);
53
+ goto(m?.context.landingUrl ?? '/admin');
54
+ }
55
+ </script>
56
+
57
+ {#if modulesQuery.ready && modules.length >= 1}
58
+ {@const ActiveIcon = iconFor(active)}
59
+ <DropdownMenu.Root>
60
+ <DropdownMenu.Trigger
61
+ aria-label={lang.switch}
62
+ class="border-input hover:bg-lavender-lighter hover:border-lavender focus-visible:ring-ring ml-auto inline-flex cursor-pointer items-center gap-1.5 rounded-md border bg-transparent px-2 py-1 text-[13px] font-medium outline-none focus-visible:ring-2 focus-visible:ring-offset-1 group-data-[collapsible=icon]:ml-0 group-data-[collapsible=icon]:px-1.5"
63
+ >
64
+ <ActiveIcon class="text-primary size-4 shrink-0" />
65
+ <span class="text-primary group-data-[collapsible=icon]:hidden">{labelFor(active)}</span>
66
+ <ChevronDownIcon class="size-3.5 shrink-0 opacity-60 group-data-[collapsible=icon]:hidden" />
67
+ </DropdownMenu.Trigger>
68
+ <DropdownMenu.Content align="start" class="w-52">
69
+ {@const ContentIcon = iconFor(CONTENT_CONTEXT)}
70
+ <DropdownMenu.Item onSelect={() => choose(CONTENT_CONTEXT)}>
71
+ <ContentIcon class="mr-2 size-4" />
72
+ <span class="flex-1">{labelFor(CONTENT_CONTEXT)}</span>
73
+ {#if active === CONTENT_CONTEXT}
74
+ <CheckIcon class="text-primary size-4" />
75
+ {/if}
76
+ </DropdownMenu.Item>
77
+ <DropdownMenu.Separator />
78
+ {#each modules as m (m.id)}
79
+ {@const MIcon = iconFor(m.id)}
80
+ <DropdownMenu.Item onSelect={() => choose(m.id)}>
81
+ <MIcon class="mr-2 size-4" />
82
+ <span class="flex-1">{labelFor(m.id)}</span>
83
+ {#if active === m.id}
84
+ <CheckIcon class="text-primary size-4" />
85
+ {/if}
86
+ </DropdownMenu.Item>
87
+ {/each}
88
+ </DropdownMenu.Content>
89
+ </DropdownMenu.Root>
90
+ {/if}
@@ -0,0 +1,3 @@
1
+ declare const ContextSwitcher: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type ContextSwitcher = ReturnType<typeof ContextSwitcher>;
3
+ export default ContextSwitcher;
@@ -42,4 +42,8 @@ export declare const sidebarLang: Record<string, {
42
42
  breadcrumb: string;
43
43
  skipToMain: string;
44
44
  };
45
+ contexts: {
46
+ switch: string;
47
+ content: string;
48
+ };
45
49
  }>;
@@ -42,6 +42,10 @@ export const sidebarLang = {
42
42
  primary: 'Główna nawigacja',
43
43
  breadcrumb: 'Ścieżka okruszków',
44
44
  skipToMain: 'Przejdź do głównej treści'
45
+ },
46
+ contexts: {
47
+ switch: 'Przełącz widok',
48
+ content: 'Treści'
45
49
  }
46
50
  },
47
51
  en: {
@@ -87,6 +91,10 @@ export const sidebarLang = {
87
91
  primary: 'Main navigation',
88
92
  breadcrumb: 'Breadcrumb',
89
93
  skipToMain: 'Skip to main content'
94
+ },
95
+ contexts: {
96
+ switch: 'Switch view',
97
+ content: 'Content'
90
98
  }
91
99
  }
92
100
  };
@@ -109,7 +109,8 @@
109
109
  const hasError =
110
110
  !!errorPaths &&
111
111
  collectLeafPathsForNodes([child], fields).some((p) => errorPaths!.has(p));
112
- return { value, label, child, hasError };
112
+ const preview = child.type === 'tab' ? child.preview : undefined;
113
+ return { value, label, child, hasError, preview };
113
114
  });
114
115
  }
115
116
 
@@ -11,6 +11,8 @@
11
11
  child: LayoutNode;
12
12
  /** True when a leaf field inside this tab is currently invalid. */
13
13
  hasError: boolean;
14
+ /** Preview view this tab binds to (`TabNode.preview`), if any. */
15
+ preview?: string;
14
16
  };
15
17
  </script>
16
18
 
@@ -22,6 +24,7 @@
22
24
  import { browser } from '$app/environment';
23
25
  import { page } from '$app/state';
24
26
  import { replaceState } from '$app/navigation';
27
+ import { getPreviewViewsContext } from '../../client/entry/preview-views-context.svelte.js';
25
28
 
26
29
  type Props = {
27
30
  tabs: TabInfo[];
@@ -44,6 +47,17 @@
44
47
 
45
48
  let active = $state(initialValue());
46
49
 
50
+ const previewViews = getPreviewViewsContext();
51
+
52
+ // Tell the preview which view the active tab wants. Only the URL-synced
53
+ // (outermost) group drives it, so two tab groups never fight over the preview.
54
+ // Runs on mount too, so a `?tab=` deep-link lands on the right view.
55
+ $effect(() => {
56
+ if (!syncUrl || !previewViews) return;
57
+ const tab = tabs.find((t) => t.value === active);
58
+ if (tab) previewViews.onTabActivate(tab.value, tab.preview);
59
+ });
60
+
47
61
  // Write the active tab back to the URL (outermost group only). Guard against
48
62
  // the no-op case so we never trigger an infinite replaceState loop.
49
63
  $effect(() => {
@@ -9,6 +9,8 @@ export type TabInfo = {
9
9
  child: LayoutNode;
10
10
  /** True when a leaf field inside this tab is currently invalid. */
11
11
  hasError: boolean;
12
+ /** Preview view this tab binds to (`TabNode.preview`), if any. */
13
+ preview?: string;
12
14
  };
13
15
  import type { Snippet } from 'svelte';
14
16
  type Props = {
@@ -11,16 +11,19 @@
11
11
  import { getIcon } from '../icons/icon-map.js';
12
12
  import NavSection, { type NavSectionItem } from './nav-section.svelte';
13
13
 
14
+ let { filterSlugs, ctx }: { filterSlugs?: string[]; ctx?: string } = $props();
15
+
14
16
  const interfaceLanguage = useInterfaceLanguage();
15
17
  const remotes = getRemotes();
16
18
  const breadcrumbs = getBreadcrumbs();
17
19
 
18
20
  function isActive(url: string) {
21
+ const base = url.split('?')[0];
19
22
  const pathname = page.url.pathname;
20
- if (pathname === url || pathname.startsWith(url + '/')) return true;
23
+ if (pathname === base || pathname.startsWith(base + '/')) return true;
21
24
  // Also highlight a collection while editing one of its entries — the entry
22
25
  // page sets a breadcrumb linking back to the collection list.
23
- return breadcrumbs.state.some((b) => b.href === url);
26
+ return breadcrumbs.state.some((b) => b.href === base);
24
27
  }
25
28
 
26
29
  const collectionsQuery = $derived(remotes.getCollections());
@@ -41,16 +44,20 @@
41
44
  });
42
45
 
43
46
  const items: NavSectionItem[] = $derived(
44
- collectionsQuery.current?.map((item) => ({
45
- title: getLocalizedLabel(item.labels?.plural, interfaceLanguage.current) ?? item.slug,
46
- url: `/admin/collections/${item.slug}`,
47
- icon: getIcon(item.sidebarIcon) ?? FolderIcon,
48
- count: countsMap.get(item.slug)
49
- })) ?? []
47
+ (collectionsQuery.current ?? [])
48
+ .filter((item) => !filterSlugs || filterSlugs.includes(item.slug))
49
+ .map((item) => ({
50
+ title: getLocalizedLabel(item.labels?.plural, interfaceLanguage.current) ?? item.slug,
51
+ url: `/admin/collections/${item.slug}${ctx ? `?ctx=${ctx}` : ''}`,
52
+ icon: getIcon(item.sidebarIcon) ?? FolderIcon,
53
+ count: countsMap.get(item.slug)
54
+ }))
50
55
  );
51
56
  </script>
52
57
 
53
- {#if !collectionsQuery.ready}
58
+ {#if filterSlugs && filterSlugs.length === 0}
59
+ <!-- no surfaced collections in this context -->
60
+ {:else if !collectionsQuery.ready}
54
61
  <Sidebar.Group class="border-sidebar-border mt-1 border-t px-0 pt-2 pb-1">
55
62
  <Sidebar.GroupLabel
56
63
  >{sidebarLang[interfaceLanguage.current].collections.title}</Sidebar.GroupLabel
@@ -1,3 +1,7 @@
1
- declare const NavCollections: import("svelte").Component<Record<string, never>, {}, "">;
1
+ type $$ComponentProps = {
2
+ filterSlugs?: string[];
3
+ ctx?: string;
4
+ };
5
+ declare const NavCollections: import("svelte").Component<$$ComponentProps, {}, "">;
2
6
  type NavCollections = ReturnType<typeof NavCollections>;
3
7
  export default NavCollections;
@@ -0,0 +1,45 @@
1
+ <script lang="ts">
2
+ import { sidebarLang } from './lang.js';
3
+ import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
4
+ import CameraIcon from '@tabler/icons-svelte/icons/camera';
5
+ import UsersIcon from '@tabler/icons-svelte/icons/users';
6
+ import SettingsIcon from '@tabler/icons-svelte/icons/settings';
7
+ import { page } from '$app/state';
8
+ import { authClient } from '../../auth-client.js';
9
+ import NavSection, { type NavSectionItem } from './nav-section.svelte';
10
+
11
+ const interfaceLanguage = useInterfaceLanguage();
12
+ let session = authClient.useSession();
13
+
14
+ const isAdmin = $derived($session.data?.user?.role === 'admin');
15
+
16
+ const items: NavSectionItem[] = $derived([
17
+ {
18
+ title: sidebarLang[interfaceLanguage.current].main.media,
19
+ url: '/admin/media',
20
+ icon: CameraIcon
21
+ },
22
+ ...(isAdmin
23
+ ? [
24
+ {
25
+ title: sidebarLang[interfaceLanguage.current].main.users,
26
+ url: '/admin/users',
27
+ icon: UsersIcon
28
+ },
29
+ {
30
+ title: sidebarLang[interfaceLanguage.current].main.maintenance,
31
+ url: '/admin/maintenance',
32
+ icon: SettingsIcon
33
+ }
34
+ ]
35
+ : [])
36
+ ]);
37
+
38
+ function isActive(url: string) {
39
+ return page.url.pathname.startsWith(url);
40
+ }
41
+ </script>
42
+
43
+ <div class="mt-auto">
44
+ <NavSection {items} {isActive} withTopBorder />
45
+ </div>
@@ -0,0 +1,3 @@
1
+ declare const NavGlobal: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type NavGlobal = ReturnType<typeof NavGlobal>;
3
+ export default NavGlobal;
@@ -1,49 +1,25 @@
1
1
  <script lang="ts">
2
2
  import { sidebarLang } from './lang.js';
3
3
  import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
4
- import CameraIcon from '@tabler/icons-svelte/icons/camera';
5
4
  import LayoutDashboardIcon from '@tabler/icons-svelte/icons/layout-dashboard';
6
- import UsersIcon from '@tabler/icons-svelte/icons/users';
7
- import SettingsIcon from '@tabler/icons-svelte/icons/settings';
8
5
  import { page } from '$app/state';
9
- import { authClient } from '../../auth-client.js';
6
+ import type { Icon } from '@tabler/icons-svelte';
10
7
  import NavSection, { type NavSectionItem } from './nav-section.svelte';
11
8
 
12
- const interfaceLanguage = useInterfaceLanguage();
13
- let session = authClient.useSession();
9
+ let { url = '/admin', icon = LayoutDashboardIcon }: { url?: string; icon?: Icon } = $props();
14
10
 
15
- const isAdmin = $derived($session.data?.user?.role === 'admin');
11
+ const interfaceLanguage = useInterfaceLanguage();
16
12
 
17
13
  const items: NavSectionItem[] = $derived([
18
14
  {
19
15
  title: sidebarLang[interfaceLanguage.current].main.dashboard,
20
- url: '/admin',
21
- icon: LayoutDashboardIcon
22
- },
23
- {
24
- title: sidebarLang[interfaceLanguage.current].main.media,
25
- url: '/admin/media',
26
- icon: CameraIcon
27
- },
28
- ...(isAdmin
29
- ? [
30
- {
31
- title: sidebarLang[interfaceLanguage.current].main.users,
32
- url: '/admin/users',
33
- icon: UsersIcon
34
- },
35
- {
36
- title: sidebarLang[interfaceLanguage.current].main.maintenance,
37
- url: '/admin/maintenance',
38
- icon: SettingsIcon
39
- }
40
- ]
41
- : [])
16
+ url,
17
+ icon
18
+ }
42
19
  ]);
43
20
 
44
- function isActive(url: string) {
45
- if (url === '/admin') return page.url.pathname === '/admin';
46
- return page.url.pathname.startsWith(url);
21
+ function isActive(u: string) {
22
+ return page.url.pathname === u.split('?')[0];
47
23
  }
48
24
  </script>
49
25
 
@@ -1,3 +1,8 @@
1
- declare const NavMain: import("svelte").Component<Record<string, never>, {}, "">;
1
+ import type { Icon } from '@tabler/icons-svelte';
2
+ type $$ComponentProps = {
3
+ url?: string;
4
+ icon?: Icon;
5
+ };
6
+ declare const NavMain: import("svelte").Component<$$ComponentProps, {}, "">;
2
7
  type NavMain = ReturnType<typeof NavMain>;
3
8
  export default NavMain;
@@ -0,0 +1,57 @@
1
+ <script lang="ts">
2
+ import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
3
+ import { getLocalizedLabel } from '../../utils/collectionLabel.js';
4
+ import { getIcon } from '../icons/icon-map.js';
5
+ import { getRemotes } from '../../../sveltekit/index.js';
6
+ import { page } from '$app/state';
7
+ import BoxIcon from '@tabler/icons-svelte/icons/box';
8
+ import NavSection, { type NavSectionItem } from './nav-section.svelte';
9
+ import type { CMSModuleDescriptor } from '../../../types/modules.js';
10
+ import type { Localized } from '../../../types/languages.js';
11
+
12
+ let { module }: { module: CMSModuleDescriptor } = $props();
13
+
14
+ const interfaceLanguage = useInterfaceLanguage();
15
+ const remotes = getRemotes();
16
+
17
+ function isActive(url: string) {
18
+ const pathname = page.url.pathname;
19
+ const base = url.split('?')[0];
20
+ return pathname === base || pathname.startsWith(base + '/');
21
+ }
22
+
23
+ // Optional reactive badges for sections declaring a `countSource` remote
24
+ // query (mirrors nav-collections' entry-count pattern). Dormant otherwise.
25
+ let countsMap: Map<string, number> = $state(new Map());
26
+ $effect(() => {
27
+ const map = new Map<string, number>();
28
+ for (const section of module.context.sections) {
29
+ if (!section.countSource) continue;
30
+ const fn = (remotes as Record<string, unknown>)[section.countSource];
31
+ if (typeof fn !== 'function') continue;
32
+ const q = (fn as () => { ready: boolean; current: unknown })();
33
+ if (q.ready && typeof q.current === 'number') map.set(section.key, q.current);
34
+ }
35
+ countsMap = map;
36
+ });
37
+
38
+ const label = $derived(
39
+ getLocalizedLabel(module.label as Localized, interfaceLanguage.current) || module.id
40
+ );
41
+ const items: NavSectionItem[] = $derived(
42
+ module.context.sections.map((s) => ({
43
+ title: getLocalizedLabel(s.title as Localized, interfaceLanguage.current) || s.url,
44
+ url: s.url,
45
+ icon: getIcon(s.icon) ?? BoxIcon,
46
+ count: countsMap.get(s.key)
47
+ }))
48
+ );
49
+ </script>
50
+
51
+ <NavSection
52
+ {items}
53
+ {label}
54
+ labelIcon={getIcon(module.icon) ?? BoxIcon}
55
+ {isActive}
56
+ withTopBorder
57
+ />
@@ -0,0 +1,7 @@
1
+ import type { CMSModuleDescriptor } from '../../../types/modules.js';
2
+ type $$ComponentProps = {
3
+ module: CMSModuleDescriptor;
4
+ };
5
+ declare const NavModule: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type NavModule = ReturnType<typeof NavModule>;
7
+ export default NavModule;
@@ -1,7 +1,7 @@
1
1
  <script lang="ts" module>
2
2
  /**
3
3
  * @internal
4
- * Internal helper — DRY for sidebar nav-* sections (NavMain, NavCollections, NavSingletons, NavForms, NavShop).
4
+ * Internal helper — DRY for sidebar nav-* sections (NavMain, NavCollections, NavSingletons, NavForms, NavModule).
5
5
  * Not exported publicly. Sesja 4 (UI Normalization → 0.26.0).
6
6
  */
7
7
  import type { Icon } from '@tabler/icons-svelte';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @internal
3
- * Internal helper — DRY for sidebar nav-* sections (NavMain, NavCollections, NavSingletons, NavForms, NavShop).
3
+ * Internal helper — DRY for sidebar nav-* sections (NavMain, NavCollections, NavSingletons, NavForms, NavModule).
4
4
  * Not exported publicly. Sesja 4 (UI Normalization → 0.26.0).
5
5
  */
6
6
  import type { Icon } from '@tabler/icons-svelte';
@@ -10,26 +10,33 @@
10
10
  import { getIcon } from '../icons/icon-map.js';
11
11
  import NavSection, { type NavSectionItem } from './nav-section.svelte';
12
12
 
13
+ let { filterSlugs, ctx }: { filterSlugs?: string[]; ctx?: string } = $props();
14
+
13
15
  const interfaceLanguage = useInterfaceLanguage();
14
16
  const remotes = getRemotes();
15
17
 
16
18
  function isActive(url: string) {
19
+ const base = url.split('?')[0];
17
20
  const pathname = page.url.pathname;
18
- return pathname === url || pathname.startsWith(url + '/');
21
+ return pathname === base || pathname.startsWith(base + '/');
19
22
  }
20
23
 
21
24
  const query = $derived(remotes.getSingles());
22
25
 
23
26
  const items: NavSectionItem[] = $derived(
24
- query.current?.map((item) => ({
25
- title: getLocalizedLabel(item.label, interfaceLanguage.current) ?? item.slug,
26
- url: `/admin/entries/${item.slug}`,
27
- icon: getIcon(item.sidebarIcon) ?? SettingsIcon
28
- })) ?? []
27
+ (query.current ?? [])
28
+ .filter((item) => !filterSlugs || filterSlugs.includes(item.slug))
29
+ .map((item) => ({
30
+ title: getLocalizedLabel(item.label, interfaceLanguage.current) ?? item.slug,
31
+ url: `/admin/entries/${item.slug}${ctx ? `?ctx=${ctx}` : ''}`,
32
+ icon: getIcon(item.sidebarIcon) ?? SettingsIcon
33
+ }))
29
34
  );
30
35
  </script>
31
36
 
32
- {#if !query.ready}
37
+ {#if filterSlugs && filterSlugs.length === 0}
38
+ <!-- no surfaced singletons in this context -->
39
+ {:else if !query.ready}
33
40
  <Sidebar.Group class="border-sidebar-border mt-1 border-t px-0 pt-2 pb-1">
34
41
  <Sidebar.GroupLabel
35
42
  >{sidebarLang[interfaceLanguage.current].singletons.title}</Sidebar.GroupLabel
@@ -1,3 +1,7 @@
1
- declare const NavSingletons: import("svelte").Component<Record<string, never>, {}, "">;
1
+ type $$ComponentProps = {
2
+ filterSlugs?: string[];
3
+ ctx?: string;
4
+ };
5
+ declare const NavSingletons: import("svelte").Component<$$ComponentProps, {}, "">;
2
6
  type NavSingletons = ReturnType<typeof NavSingletons>;
3
7
  export default NavSingletons;