includio-cms 0.36.9 → 0.37.1

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 (247) hide show
  1. package/API.md +178 -3
  2. package/CHANGELOG.md +77 -0
  3. package/DOCS.md +28 -3
  4. package/ROADMAP.md +32 -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 +1 -1
  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/status-badge.svelte +39 -3
  21. package/dist/admin/client/collection/status-badge.svelte.d.ts +5 -3
  22. package/dist/admin/client/entry/entry.svelte +26 -9
  23. package/dist/admin/client/entry/hybrid/hybrid-preview.svelte +73 -10
  24. package/dist/admin/client/entry/hybrid/hybrid-preview.svelte.d.ts +6 -5
  25. package/dist/admin/client/entry/preview-views-context.svelte.d.ts +22 -0
  26. package/dist/admin/client/entry/preview-views-context.svelte.js +58 -0
  27. package/dist/admin/client/entry/preview-views.d.ts +27 -0
  28. package/dist/admin/client/entry/preview-views.js +33 -0
  29. package/dist/admin/client/index.d.ts +3 -0
  30. package/dist/admin/client/index.js +3 -0
  31. package/dist/admin/client/modules/dashboard-registry.d.ts +7 -0
  32. package/dist/admin/client/modules/dashboard-registry.js +11 -0
  33. package/dist/admin/client/modules/module-dashboard-page.svelte +26 -0
  34. package/dist/admin/client/modules/module-dashboard-page.svelte.d.ts +6 -0
  35. package/dist/admin/client/modules/stat-card.svelte +22 -0
  36. package/dist/admin/client/modules/stat-card.svelte.d.ts +10 -0
  37. package/dist/admin/client/shop/shop-dashboard.svelte +29 -0
  38. package/dist/admin/client/shop/shop-dashboard.svelte.d.ts +3 -0
  39. package/dist/admin/components/fields/relation-field.svelte +11 -2
  40. package/dist/admin/components/icons/icon-map.js +8 -0
  41. package/dist/admin/components/layout/app-sidebar.svelte +45 -14
  42. package/dist/admin/components/layout/context-switcher.svelte +90 -0
  43. package/dist/admin/components/layout/context-switcher.svelte.d.ts +3 -0
  44. package/dist/admin/components/layout/lang.d.ts +4 -0
  45. package/dist/admin/components/layout/lang.js +8 -0
  46. package/dist/admin/components/layout/layout-renderer.svelte +2 -1
  47. package/dist/admin/components/layout/layout-tabs.svelte +14 -0
  48. package/dist/admin/components/layout/layout-tabs.svelte.d.ts +2 -0
  49. package/dist/admin/components/layout/nav-collections.svelte +16 -9
  50. package/dist/admin/components/layout/nav-collections.svelte.d.ts +5 -1
  51. package/dist/admin/components/layout/nav-global.svelte +45 -0
  52. package/dist/admin/components/layout/nav-global.svelte.d.ts +3 -0
  53. package/dist/admin/components/layout/nav-main.svelte +8 -32
  54. package/dist/admin/components/layout/nav-main.svelte.d.ts +6 -1
  55. package/dist/admin/components/layout/nav-module.svelte +57 -0
  56. package/dist/admin/components/layout/nav-module.svelte.d.ts +7 -0
  57. package/dist/admin/components/layout/nav-section.svelte +1 -1
  58. package/dist/admin/components/layout/nav-section.svelte.d.ts +1 -1
  59. package/dist/admin/components/layout/nav-singletons.svelte +14 -7
  60. package/dist/admin/components/layout/nav-singletons.svelte.d.ts +5 -1
  61. package/dist/admin/remote/booking.remote.d.ts +247 -0
  62. package/dist/admin/remote/booking.remote.js +286 -0
  63. package/dist/admin/remote/index.d.ts +2 -0
  64. package/dist/admin/remote/index.js +2 -0
  65. package/dist/admin/remote/modules.remote.d.ts +10 -0
  66. package/dist/admin/remote/modules.remote.js +14 -0
  67. package/dist/admin/state/active-context-resolve.d.ts +37 -0
  68. package/dist/admin/state/active-context-resolve.js +66 -0
  69. package/dist/admin/state/active-context.svelte.d.ts +8 -0
  70. package/dist/admin/state/active-context.svelte.js +27 -0
  71. package/dist/booking/adjustments.d.ts +33 -0
  72. package/dist/booking/adjustments.js +26 -0
  73. package/dist/booking/capacity.d.ts +12 -0
  74. package/dist/booking/capacity.js +10 -0
  75. package/dist/booking/client/index.d.ts +42 -0
  76. package/dist/booking/client/index.js +48 -0
  77. package/dist/booking/client/use-booking.svelte.d.ts +24 -0
  78. package/dist/booking/client/use-booking.svelte.js +56 -0
  79. package/dist/booking/config.d.ts +176 -0
  80. package/dist/booking/config.js +37 -0
  81. package/dist/booking/draft.d.ts +96 -0
  82. package/dist/booking/draft.js +67 -0
  83. package/dist/booking/expiry-logic.d.ts +30 -0
  84. package/dist/booking/expiry-logic.js +33 -0
  85. package/dist/booking/index.d.ts +18 -0
  86. package/dist/booking/index.js +52 -0
  87. package/dist/booking/ledger.d.ts +46 -0
  88. package/dist/booking/ledger.js +60 -0
  89. package/dist/booking/number.d.ts +2 -0
  90. package/dist/booking/number.js +11 -0
  91. package/dist/booking/participant-fields/access.d.ts +10 -0
  92. package/dist/booking/participant-fields/access.js +14 -0
  93. package/dist/booking/participant-fields/age.d.ts +12 -0
  94. package/dist/booking/participant-fields/age.js +28 -0
  95. package/dist/booking/participant-fields/completeness.d.ts +8 -0
  96. package/dist/booking/participant-fields/completeness.js +13 -0
  97. package/dist/booking/participant-fields/group.d.ts +6 -0
  98. package/dist/booking/participant-fields/group.js +15 -0
  99. package/dist/booking/participant-fields/index.d.ts +6 -0
  100. package/dist/booking/participant-fields/index.js +6 -0
  101. package/dist/booking/participant-fields/types.d.ts +50 -0
  102. package/dist/booking/participant-fields/types.js +5 -0
  103. package/dist/booking/participant-fields/validate.d.ts +7 -0
  104. package/dist/booking/participant-fields/validate.js +44 -0
  105. package/dist/booking/participant-pricing.d.ts +93 -0
  106. package/dist/booking/participant-pricing.js +91 -0
  107. package/dist/booking/participants-view.d.ts +32 -0
  108. package/dist/booking/participants-view.js +22 -0
  109. package/dist/booking/payment-context.d.ts +19 -0
  110. package/dist/booking/payment-context.js +29 -0
  111. package/dist/booking/pricing-inputs.d.ts +28 -0
  112. package/dist/booking/pricing-inputs.js +35 -0
  113. package/dist/booking/pricing.d.ts +86 -0
  114. package/dist/booking/pricing.js +125 -0
  115. package/dist/booking/recompute.d.ts +22 -0
  116. package/dist/booking/recompute.js +31 -0
  117. package/dist/booking/reminders-schedule.d.ts +11 -0
  118. package/dist/booking/reminders-schedule.js +28 -0
  119. package/dist/booking/server/adjustments.d.ts +27 -0
  120. package/dist/booking/server/adjustments.js +139 -0
  121. package/dist/booking/server/bookings.d.ts +51 -0
  122. package/dist/booking/server/bookings.js +336 -0
  123. package/dist/booking/server/catalog.d.ts +9 -0
  124. package/dist/booking/server/catalog.js +20 -0
  125. package/dist/booking/server/db.d.ts +7 -0
  126. package/dist/booking/server/db.js +18 -0
  127. package/dist/booking/server/email.d.ts +23 -0
  128. package/dist/booking/server/email.js +59 -0
  129. package/dist/booking/server/emailTemplateRegistry.d.ts +29 -0
  130. package/dist/booking/server/emailTemplateRegistry.js +125 -0
  131. package/dist/booking/server/http/create-handler.d.ts +5 -0
  132. package/dist/booking/server/http/create-handler.js +70 -0
  133. package/dist/booking/server/http/index.d.ts +4 -0
  134. package/dist/booking/server/http/index.js +4 -0
  135. package/dist/booking/server/http/maintenance-handler.d.ts +9 -0
  136. package/dist/booking/server/http/maintenance-handler.js +36 -0
  137. package/dist/booking/server/http/portal-handler.d.ts +13 -0
  138. package/dist/booking/server/http/portal-handler.js +103 -0
  139. package/dist/booking/server/http/webhook-handler.d.ts +9 -0
  140. package/dist/booking/server/http/webhook-handler.js +39 -0
  141. package/dist/booking/server/index.d.ts +12 -0
  142. package/dist/booking/server/index.js +14 -0
  143. package/dist/booking/server/maintenance.d.ts +28 -0
  144. package/dist/booking/server/maintenance.js +134 -0
  145. package/dist/booking/server/participants.d.ts +26 -0
  146. package/dist/booking/server/participants.js +92 -0
  147. package/dist/booking/server/payments.d.ts +34 -0
  148. package/dist/booking/server/payments.js +207 -0
  149. package/dist/booking/server/portal-session.d.ts +16 -0
  150. package/dist/booking/server/portal-session.js +42 -0
  151. package/dist/booking/server/portal.d.ts +206 -0
  152. package/dist/booking/server/portal.js +236 -0
  153. package/dist/booking/server/price-source.d.ts +73 -0
  154. package/dist/booking/server/price-source.js +94 -0
  155. package/dist/booking/server/refunds.d.ts +7 -0
  156. package/dist/booking/server/refunds.js +58 -0
  157. package/dist/booking/server/reminders.d.ts +9 -0
  158. package/dist/booking/server/reminders.js +24 -0
  159. package/dist/booking/server/validation.d.ts +17 -0
  160. package/dist/booking/server/validation.js +55 -0
  161. package/dist/booking/state-machine.d.ts +41 -0
  162. package/dist/booking/state-machine.js +73 -0
  163. package/dist/booking/templates/_partials/footer.en.html +4 -0
  164. package/dist/booking/templates/_partials/footer.pl.html +4 -0
  165. package/dist/booking/templates/_partials/header.en.html +4 -0
  166. package/dist/booking/templates/_partials/header.pl.html +4 -0
  167. package/dist/booking/templates/balance-due.en.html +10 -0
  168. package/dist/booking/templates/balance-due.pl.html +10 -0
  169. package/dist/booking/templates/balance-overdue-cancelled.en.html +5 -0
  170. package/dist/booking/templates/balance-overdue-cancelled.pl.html +5 -0
  171. package/dist/booking/templates/booking-received.en.html +9 -0
  172. package/dist/booking/templates/booking-received.pl.html +9 -0
  173. package/dist/booking/templates/deposit-paid.en.html +6 -0
  174. package/dist/booking/templates/deposit-paid.pl.html +6 -0
  175. package/dist/booking/templates/payment-link.en.html +5 -0
  176. package/dist/booking/templates/payment-link.pl.html +5 -0
  177. package/dist/booking/templates/payment-received.en.html +10 -0
  178. package/dist/booking/templates/payment-received.pl.html +10 -0
  179. package/dist/booking/totals.d.ts +38 -0
  180. package/dist/booking/totals.js +61 -0
  181. package/dist/booking/webhook-logic.d.ts +28 -0
  182. package/dist/booking/webhook-logic.js +50 -0
  183. package/dist/cli/create-user.d.ts +7 -1
  184. package/dist/cli/create-user.js +23 -15
  185. package/dist/cli/index.js +55 -7
  186. package/dist/cli/scaffold/admin.d.ts +6 -0
  187. package/dist/cli/scaffold/admin.js +5 -3
  188. package/dist/cli/scaffold/booking.d.ts +12 -0
  189. package/dist/cli/scaffold/booking.js +103 -0
  190. package/dist/core/cms.d.ts +24 -9
  191. package/dist/core/cms.js +72 -34
  192. package/dist/core/config/validatePreviews.d.ts +12 -0
  193. package/dist/core/config/validatePreviews.js +90 -0
  194. package/dist/core/fields/slugPath.d.ts +9 -1
  195. package/dist/core/fields/slugPath.js +16 -2
  196. package/dist/core/server/fields/resolveRelationFields.d.ts +11 -2
  197. package/dist/core/server/fields/resolveRelationFields.js +12 -3
  198. package/dist/core/server/generator/generator.js +7 -2
  199. package/dist/db-postgres/schema/booking/bookingAdjustments.d.ts +186 -0
  200. package/dist/db-postgres/schema/booking/bookingAdjustments.js +22 -0
  201. package/dist/db-postgres/schema/booking/bookingItemPersons.d.ts +117 -0
  202. package/dist/db-postgres/schema/booking/bookingItemPersons.js +22 -0
  203. package/dist/db-postgres/schema/booking/bookingItems.d.ts +181 -0
  204. package/dist/db-postgres/schema/booking/bookingItems.js +22 -0
  205. package/dist/db-postgres/schema/booking/bookingPayments.d.ts +204 -0
  206. package/dist/db-postgres/schema/booking/bookingPayments.js +25 -0
  207. package/dist/db-postgres/schema/booking/bookingPersons.d.ts +99 -0
  208. package/dist/db-postgres/schema/booking/bookingPersons.js +17 -0
  209. package/dist/db-postgres/schema/booking/bookingReminders.d.ts +131 -0
  210. package/dist/db-postgres/schema/booking/bookingReminders.js +18 -0
  211. package/dist/db-postgres/schema/booking/bookings.d.ts +324 -0
  212. package/dist/db-postgres/schema/booking/bookings.js +52 -0
  213. package/dist/db-postgres/schema/booking/index.d.ts +7 -0
  214. package/dist/db-postgres/schema/booking/index.js +7 -0
  215. package/dist/db-postgres/schema/index.d.ts +1 -0
  216. package/dist/db-postgres/schema/index.js +1 -0
  217. package/dist/db-postgres/schema-booking.d.ts +1 -0
  218. package/dist/db-postgres/schema-booking.js +1 -0
  219. package/dist/server/security/rate-limit-handler.d.ts +19 -0
  220. package/dist/server/security/rate-limit-handler.js +34 -0
  221. package/dist/shop/adapters/payu/client.js +3 -1
  222. package/dist/shop/adapters/payu/index.js +37 -17
  223. package/dist/shop/adapters/payu/payload.d.ts +6 -0
  224. package/dist/shop/adapters/payu/payload.js +1 -1
  225. package/dist/shop/adapters/stripe/index.js +36 -21
  226. package/dist/shop/index.d.ts +11 -2
  227. package/dist/shop/index.js +43 -1
  228. package/dist/shop/rate-limit.d.ts +1 -9
  229. package/dist/shop/rate-limit.js +3 -28
  230. package/dist/shop/types.d.ts +19 -0
  231. package/dist/sveltekit/config.d.ts +4 -0
  232. package/dist/sveltekit/config.js +6 -0
  233. package/dist/types/cms.d.ts +15 -1
  234. package/dist/types/cms.schema.d.ts +96 -2
  235. package/dist/types/cms.schema.js +84 -1
  236. package/dist/types/config.d.ts +33 -0
  237. package/dist/types/layout.d.ts +6 -0
  238. package/dist/types/modules.d.ts +133 -0
  239. package/dist/types/modules.js +27 -0
  240. package/dist/updates/0.37.0/index.d.ts +2 -0
  241. package/dist/updates/0.37.0/index.js +52 -0
  242. package/dist/updates/0.37.1/index.d.ts +2 -0
  243. package/dist/updates/0.37.1/index.js +24 -0
  244. package/dist/updates/index.js +5 -1
  245. package/package.json +19 -1
  246. package/dist/admin/components/layout/nav-shop.svelte +0 -33
  247. package/dist/admin/components/layout/nav-shop.svelte.d.ts +0 -3
package/API.md CHANGED
@@ -1,8 +1,8 @@
1
- # includio-cms — Public API v0.36.9
1
+ # includio-cms — Public API v0.37.1
2
2
 
3
3
  > Auto-generated by `scripts/generate-api-md.ts`. Do not edit by hand.
4
4
 
5
- Entry points: **19** · Stable: **502** · Experimental: **4**
5
+ Entry points: **23** · Stable: **665** · Experimental: **4**
6
6
 
7
7
  Tags:
8
8
  - `@public` — frozen for v1.0; semver-protected.
@@ -148,6 +148,8 @@ Tags:
148
148
  - `const AdminLayout: LegacyComponentType`
149
149
  - `interface AdminNavItem` — One additional sidebar entry rendered below built-in nav sections
150
150
  - `const Badge: LegacyComponentType`
151
+ - `const BookingReservationDetailPage: LegacyComponentType`
152
+ - `const BookingReservationsPage: LegacyComponentType`
151
153
  - `interface Breadcrumb`
152
154
  - `class Breadcrumbs`
153
155
  - `buildCsv(opts: { headers: string[]; rows: (string | number | null ...): string` — Build a CSV string from `headers` and `rows`. Values are escaped per
@@ -177,6 +179,7 @@ Tags:
177
179
  - `const MaintenancePage: LegacyComponentType`
178
180
  - `const MediaPage: LegacyComponentType`
179
181
  - `const MediaSelector: LegacyComponentType`
182
+ - `const ModuleDashboardPage: LegacyComponentType`
180
183
  - `const PageHeader: LegacyComponentType`
181
184
  - `interface PaginationState`
182
185
  - `const ResetPasswordPage: LegacyComponentType`
@@ -205,11 +208,16 @@ Tags:
205
208
 
206
209
  ### `includio-cms/admin/remote`
207
210
 
211
+ - `const addAdjustment: <inferred>`
212
+ - `const addPaymentLink: <inferred>`
208
213
  - `const archiveEntryCommand: <inferred>`
209
214
  - `const bulkDeleteMediaFiles: <inferred>`
210
215
  - `const bulkSetMediaFileTags: <inferred>`
216
+ - `const cancelBooking: <inferred>`
211
217
  - `const cancelShipmentForOrderCmd: <inferred>`
218
+ - `const changeBookingStatus: <inferred>`
212
219
  - `const countMediaFiles: <inferred>`
220
+ - `const createBookingStaff: <inferred>`
213
221
  - `const createCouponCmd: <inferred>`
214
222
  - `const createEntry: <inferred>`
215
223
  - `const createMediaTag: <inferred>`
@@ -225,6 +233,7 @@ Tags:
225
233
  - `const deleteShippingMethodCmd: <inferred>`
226
234
  - `const deleteShopDataForEntry: <inferred>`
227
235
  - `const duplicateEntryCommand: <inferred>`
236
+ - `const editBookingStaff: <inferred>`
228
237
  - `const exportFormSubmissions: <inferred>`
229
238
  - `const exportOrdersCsv: <inferred>`
230
239
  - `const findMediaReferences: <inferred>`
@@ -232,6 +241,9 @@ Tags:
232
241
  - `const generateBalanceLinkForOrder: <inferred>`
233
242
  - `const getAdminExtraNavItems: <inferred>`
234
243
  - `const getAltOverview: <inferred>`
244
+ - `const getBooking: <inferred>`
245
+ - `const getBookingEnabled: <inferred>`
246
+ - `const getBookingStats: <inferred>`
235
247
  - `const getCollection: <inferred>`
236
248
  - `const getCollections: <inferred>`
237
249
  - `const getCouponAdmin: <inferred>`
@@ -251,6 +263,7 @@ Tags:
251
263
  - `const getMediaFiles: <inferred>`
252
264
  - `const getMediaTags: <inferred>`
253
265
  - `const getMediaTagsWithCounts: <inferred>`
266
+ - `const getModules: <inferred>`
254
267
  - `const getOrderForAdmin: <inferred>`
255
268
  - `const getOrderInvoiceAdmin: <inferred>`
256
269
  - `const getOrderRefundsAdmin: <inferred>`
@@ -267,12 +280,16 @@ Tags:
267
280
  - `const getSubmissionsOverview: <inferred>`
268
281
  - `const isAIAvailable: <inferred>`
269
282
  - `const issueInvoiceCmd: <inferred>`
283
+ - `const listBookingCatalog: <inferred>`
284
+ - `const listBookings: <inferred>`
270
285
  - `const listCouponsAdmin: <inferred>`
271
286
  - `const listOrdersAdmin: <inferred>`
272
287
  - `const listShippingMethodsAdmin: <inferred>`
273
288
  - `const listShopableCollections: <inferred>`
274
289
  - `const listShopProductEntries: <inferred>`
275
290
  - `const populatePreviewData: <inferred>`
291
+ - `const recordCashPayment: <inferred>`
292
+ - `const refundBookingPayment: <inferred>`
276
293
  - `const refundOrderCmd: <inferred>`
277
294
  - `const regenerateVideoPoster: <inferred>`
278
295
  - `const renameMediaFile: <inferred>`
@@ -282,6 +299,7 @@ Tags:
282
299
  - `const restoreOrderCmd: <inferred>`
283
300
  - `const searchEntries: <inferred>`
284
301
  - `const searchLinkableEntries: <inferred>`
302
+ - `const sendBookingEmailCmd: <inferred>`
285
303
  - `const setFocalPoint: <inferred>`
286
304
  - `const setMediaFileAlt: <inferred>`
287
305
  - `const setMediaFileTags: <inferred>`
@@ -293,8 +311,10 @@ Tags:
293
311
  - `const updateMediaAccessibility: <inferred>`
294
312
  - `const updateMediaTag: <inferred>`
295
313
  - `const updateOrderStatusCmd: <inferred>`
314
+ - `const updateParticipantData: <inferred>`
296
315
  - `const updateShippingMethodCmd: <inferred>`
297
316
  - `const upsertShopDataForEntry: <inferred>`
317
+ - `const voidAdjustment: <inferred>`
298
318
 
299
319
  ### `includio-cms/sveltekit`
300
320
 
@@ -351,6 +371,14 @@ Tags:
351
371
 
352
372
  - `const account: <inferred>`
353
373
  - `const accountRelations: <inferred>`
374
+ - `const bookingAdjustmentsTable: <inferred>`
375
+ - `const bookingItemPersonsTable: <inferred>`
376
+ - `const bookingItemsTable: <inferred>`
377
+ - `const bookingPaymentsTable: <inferred>`
378
+ - `const bookingPersonsTable: <inferred>`
379
+ - `const bookingRemindersTable: <inferred>`
380
+ - `const bookingsTable: <inferred>`
381
+ - `interface BookingTotals` — Computed money snapshot kept on the booking for fast list/detail reads.
354
382
  - `const consentLogsTable: <inferred>`
355
383
  - `type DatabaseAdapterWithDrizzle = DatabaseAdapter & { _drizzle: ReturnType<typeof drizzle<typeof schema>>; }`
356
384
  - `const entriesTable: <inferred>`
@@ -433,6 +461,17 @@ Tags:
433
461
  - `const shopStockReservationsTable: <inferred>`
434
462
  - `const shopWebhookEventsTable: <inferred>`
435
463
 
464
+ ### `includio-cms/db-postgres/schema-booking`
465
+
466
+ - `const bookingAdjustmentsTable: <inferred>`
467
+ - `const bookingItemPersonsTable: <inferred>`
468
+ - `const bookingItemsTable: <inferred>`
469
+ - `const bookingPaymentsTable: <inferred>`
470
+ - `const bookingPersonsTable: <inferred>`
471
+ - `const bookingRemindersTable: <inferred>`
472
+ - `const bookingsTable: <inferred>`
473
+ - `interface BookingTotals` — Computed money snapshot kept on the booking for fast list/detail reads.
474
+
436
475
  ### `includio-cms/auth-schema`
437
476
 
438
477
  - `const account: <inferred>`
@@ -451,7 +490,7 @@ Tags:
451
490
  - `interface ConsentConfig`
452
491
  - `interface CouponRef`
453
492
  - `type Currency = 'PLN'`
454
- - `defineShop(config: ShopConfig): ResolvedShopConfig`
493
+ - `defineShop(config: ShopConfig): CMSModule` — Defines the shop module. Returns a {@link CMSModule} for
455
494
  - `type DepositAmount = { type: 'percent'; value: number } | { type: 'amount'; value: number }` — Deposit amount specifier. `percent` charges `floor(base * value / 100)` of
456
495
  - `interface EmailContext` — Context passed to a {@link SubjectPlaceholderResolver}. Currently carries
457
496
  - `fakturowniaAdapter(opts: FakturowniaAdapterOptions): InvoicingAdapter` — Invoicing adapter backed by Fakturownia (fakturownia.pl). Issues a paid VAT
@@ -552,6 +591,142 @@ Tags:
552
591
  - `createShipmentLabelHandler(): { GET: RequestHandler }` — Admin-only PDF proxy that streams the carrier label without exposing the
553
592
  - `createShippingMethodsHandler(): { GET: RequestHandler }`
554
593
 
594
+ ### `includio-cms/booking`
595
+
596
+ - `type AdjustmentKind = 'manual_surcharge' | 'manual_discount'` — Manual booking adjustments — staff-entered surcharges (e.g. e-bike +200) and
597
+ - `interface AdjustmentSums`
598
+ - `ageAt(dob: string, anchor: Date): number | null` — Pełne lata między `dob` a `anchor`. `null` gdy data niepoprawna.
599
+ - `interface AgeBand` — A global age band: participants aged [minAge, maxAge] pay `pct`% of adult price.
600
+ - `interface AgeFlagConfig` — Generyczna flaga wieku: oznacza uczestnika, którego wiek (liczony z pola roli
601
+ - `interface AppliedRule`
602
+ - `assertAdjustmentMagnitude(magnitude: number): void` — Reject a zero/negative magnitude at the write boundary.
603
+ - `assertGrossNotBelowPaid(newGross: number, payments: PaymentEntry[]): void` — Asserts an edit's new gross does not drop below the already-paid total.
604
+ - `assertTransition(from: BookingStatus, to: BookingStatus): void` — Asserts a legal transition, throwing {@link BookingStateError} otherwise.
605
+ - `balance(gross: number, payments: PaymentEntry[]): number` — Outstanding balance: `gross − paid` (never below 0).
606
+ - `bandPctFor(age: number | null, bands: AgeBand[]): number` — Resolve the age-band percent for an age; 100 when null/unmatched (full price).
607
+ - `const BOOKING_STATUSES: readonly BookingStatus[]`
608
+ - `interface BookingAdjustment`
609
+ - `interface BookingConfig` — User-facing booking configuration. Passed to `defineBooking` and registered
610
+ - `class BookingStateError` — Thrown when an illegal status transition is attempted.
611
+ - `type BookingStatus = | 'draft' | 'reserved' | 'partially_paid' | 'paid' | 'completed' | 'cancelled'` — Booking state machine — pure, deterministic. Two concerns:
612
+ - `interface BookingValidationContext` — Context handed to the client-supplied {@link BookingValidator} seam.
613
+ - `type BookingValidator = (ctx: BookingValidationContext) => void | Promise<void>` — Client-specific validation seam (e.g. weekly-model term conflicts). Throws to
614
+ - `buildPricingInputs(persons: PersonRow[], items: ItemRow[], assignments: ItemPersonRow[], identityFields: FieldDef[]): PricingInputs`
615
+ - `canFill(field: FieldDef, audience: 'customer' | 'staff'): boolean` — Whether a given audience may fill/edit a field. Default `whoFills` is `'both'`.
616
+ - `canReduceGrossTo(newGross: number, payments: PaymentEntry[]): boolean` — Whether the gross may be reduced to `newGross` without going below paid.
617
+ - `canTransition(from: BookingStatus, to: BookingStatus): boolean` — Whether `from → to` is a legal transition (self-transitions are allowed).
618
+ - `completeness(data: Record<string, unknown>, fields: FieldDef[]): Completeness`
619
+ - `interface Completeness`
620
+ - `type Condition = | { kind: 'minTripCount'; value: number } // distinct trip refs >= value | { kind: 'minParticipan...` — Booking discount rules engine — a pure, deterministic, module-agnostic
621
+ - `daysUntil(departureDate: Date, now: Date): number` — Whole days from `now` until `departureDate` (negative if departure is past).
622
+ - `defineBooking(config: BookingConfig): CMSModule` — Defines the booking module — a long-lived, mutable reservation system with a
623
+ - `deriveAgeFlag(data: Record<string, unknown>, fields: FieldDef[], config: AgeFlagConfig | null, anchorDate: Date | null): { flagged: boolean; age: number | null }` — Generyczna flaga wieku — bez magicznego klucza. Bierze pole z rolą `birthDate`,
624
+ - `deriveStatus(input: DeriveStatusInput): BookingStatus` — Derives the payment-progress status from the ledger.
625
+ - `interface DiscountRule` — A serializable discount rule.
626
+ - `interface EarlyBird` — Early-bird: `percent`% off a full-price participant's gross when booked early.
627
+ - `type Effect = | { kind: 'percentOff'; value: number } // % off gross (0–100) | { kind: 'amountOff'; value: numb...` — An effect applied when a rule wins its group.
628
+ - `const FIELD_TYPES: <inferred>`
629
+ - `interface FieldDef` — Serializowalna definicja pojedynczego pola uczestnika.
630
+ - `interface FieldError`
631
+ - `interface FieldGroup`
632
+ - `type FieldRole = 'name' | 'birthDate'` — Generyczna „rola" pola — opcjonalna wskazówka semantyczna, którą UI/logika
633
+ - `type FieldType = (typeof FIELD_TYPES)[number]`
634
+ - `groupBySection(fields: FieldDef[]): FieldGroup[]`
635
+ - `isFieldType(v: unknown): v is FieldType`
636
+ - `isFullyPaid(gross: number, payments: PaymentEntry[]): boolean` — Whether the booking is fully settled (`gross > 0` and balance reached 0).
637
+ - `interface ItemPersonRow` — Assignment (junction) row.
638
+ - `interface ItemRow` — Trip-line row (subset needed for pricing). `unitPrice` = adult price (minor units).
639
+ - `interface LadderTier` — One ladder tier: from `minTrips` trips, the cheapest trip gets `percent`% off.
640
+ - `ladderTierFor(tripCount: number, ladder: LadderTier[]): LadderTier | null` — Highest ladder tier whose `minTrips` ≤ tripCount; null when none qualifies.
641
+ - `class LedgerError` — Thrown when an edit would push the gross below what has already been paid.
642
+ - `ledgerSummary(gross: number, payments: PaymentEntry[]): LedgerSummary` — Full ledger snapshot for a given gross.
643
+ - `interface LedgerSummary`
644
+ - `const NO_ADJUSTMENTS: AdjustmentSums`
645
+ - `interface ParticipantBreakdown`
646
+ - `interface ParticipantLine`
647
+ - `interface ParticipantPriceResult`
648
+ - `interface ParticipantPricingConfig` — Serializable per-participant pricing config (sourced from client globalSettings).
649
+ - `interface PaymentEntry`
650
+ - `type PaymentKind = 'deposit' | 'installment' | 'cash' | 'online'` — Booking payment ledger — pure helpers over a list of payment entries. The
651
+ - `type PaymentStatus = 'pending' | 'paid' | 'failed' | 'refunded'`
652
+ - `interface Perk`
653
+ - `interface PersonRow` — Person row (subset needed for pricing).
654
+ - `price(lines: PricingLine[], now: Date, config: PricingConfig): PriceResult`
655
+ - `priceByParticipant(participants: PricingParticipant[], trips: PricedTrip[], anchor: Date | null, now: Date, config: ParticipantPricingCo...): ParticipantPriceResult`
656
+ - `interface PricedTrip` — A trip available in the booking, with its full (adult) price (minor units).
657
+ - `interface PriceResult`
658
+ - `interface PricingConfig`
659
+ - `interface PricingInputs`
660
+ - `interface PricingLine` — A single priced line that retains trip identity (for cheapest/distinct-count).
661
+ - `interface PricingParticipant` — A participant and the trips they take.
662
+ - `interface ReminderRule` — One scheduled reminder rule, relative to the earliest departure date.
663
+ - `interface ResolvedBookingConfig` — Fully-defaulted booking config, carried on `CMSModule.config`.
664
+ - `signedAmount(kind: AdjustmentKind, magnitude: number): number` — Convert a UI-entered positive magnitude + kind into a signed stored amount.
665
+ - `sumAdjustments(rows: Pick<BookingAdjustment, 'amount' | 'voidedAt'>[]): AdjustmentSums` — Fold active (non-voided) adjustments into totals contributions. By sign.
666
+ - `totalPaid(payments: PaymentEntry[]): number` — Sum of entries currently in `paid` state.
667
+ - `totalPending(payments: PaymentEntry[]): number` — Sum of entries still pending (links generated, not yet settled).
668
+ - `totalRefunded(payments: PaymentEntry[]): number` — Sum of entries that were refunded.
669
+ - `validateAgainstSchema(data: Record<string, unknown>, fields: FieldDef[]): FieldError[]`
670
+ - `type WhoFills = 'customer' | 'staff' | 'both'` — Kto wypełnia pole. Default 'both'.
671
+
672
+ ### `includio-cms/booking/client`
673
+
674
+ - `class BookingClientError` — Error thrown on a non-2xx response; `code` carries the server's error code.
675
+ - `interface BookingClientOptions` — Browser SDK for the booking storefront + self-service portal. Thin fetch
676
+ - `createBookingClient(opts: BookingClientOptions = {}): <inferred>` — Create a booking API client bound to a base URL + fetch.
677
+ - `useBooking(token: string, opts: BookingClientOptions = {}): <inferred>` — Reactive self-service portal state for a booking access token. Loads the view
678
+
679
+ ### `includio-cms/booking/server`
680
+
681
+ - `interface AddAdjustmentInput`
682
+ - `addBookingAdjustment(bookingId: string, input: AddAdjustmentInput, opts: { actor: string; now?: Date }): Promise<BookingAdjustment>` — Add a manual adjustment (surcharge/discount) and recompute the booking totals
683
+ - `applyWebhookPayment(event: PaymentEvent): Promise<{ applied: boolean; bookingId: string | null; kind: PaymentKind | nul...` — Apply a provider webhook event to the ledger, idempotently. Locks the payment
684
+ - `interface BookingEditChanges` — Edit payload: replace people + items (+assignments) and/or customer details.
685
+ - `type BookingEmailKind = | 'booking-received' | 'deposit-paid' | 'payment-received' | 'payment-link' | 'balance-due' | 'ba...` — The transactional emails the booking module can send.
686
+ - `interface BookingStatus`
687
+ - `bookingStatusByToken(token: string): Promise<BookingStatus | null>`
688
+ - `interface BookingStatusItem`
689
+ - `type BookingTx = Parameters<Parameters<ReturnType<typeof getBookingDb>['transaction']>[0]>[0]` — Exact drizzle transaction handle, derived from the booking db's `transaction`.
690
+ - `class BookingValidationError` — Thrown when a booking fails capacity (core) or the client `validateBooking` seam.
691
+ - `createBooking(input: CreateBookingInput, opts: { now?: Date; actor?: PriceActor } = {}): Promise<{ booking: typeof bookingsTable.$inferSelect; accessToken: string }>` — Create a booking (public self-serve or staff). Validates catalog capacity +
692
+ - `createBookingHandler(): { POST: RequestHandler }` — Public self-serve booking creation. Mount at `POST /api/booking`.
693
+ - `createDepositPayment(bookingId: string): Promise<{ redirectUrl: string | null; status: 'redirect' | 'manual' | 'error'...` — Create the deposit payment for a freshly-created booking: ask the first
694
+ - `createInstallmentPayment(bookingId: string, amount: number): Promise<{ paymentLink: string | null }>` — Generate an installment/balance payment link for an arbitrary amount (staff or
695
+ - `createMaintenanceHandler(): { POST: RequestHandler }` — Secured external trigger for booking maintenance — the swap-in for the
696
+ - `createPortalHandler(): { GET: RequestHandler; POST: RequestHandler }` — Self-service portal. Mount at `GET|POST /api/booking/portal/[token]`.
697
+ - `createWebhookHandler(): { POST: RequestHandler }` — Payment-provider webhook. Mount at `POST /api/booking/webhook/[provider]`.
698
+ - `editBooking(bookingId: string, changes: BookingEditChanges, opts: { actor: 'staff' | 'portal'; now?: Date }): Promise<typeof bookingsTable.$inferSelect>` — Edit a booking (staff or portal). Recomputes totals per-participant + derived
699
+ - `getBookingDb(): <inferred>` — Returns the Drizzle handle, asserting the postgres adapter is in use.
700
+ - `getBookingMaintenanceStatus(): { running: boolean; lastRun: Date | null }`
701
+ - `listBookingAdjustments(bookingId: string): Promise<BookingAdjustment[]>` — List all adjustments (active + voided) for a booking, newest first.
702
+ - `pickEmailTemplate(kind: BookingEmailKind): string` — Template file name for a kind (1:1 — kept as a function for future indirection).
703
+ - `portalAddTrips(token: string, additions: Array<{ tripRef: string; assignments: string[] }>): <inferred>` — Self-service dobieranie wycieczki: doczytaj obecny stan, scal z `additions`
704
+ - `portalEdit(token: string, changes: BookingEditChanges): <inferred>` — Portal self-service edit (actor:'portal' guards). Throws → handler maps to 409/400.
705
+ - `portalPay(token: string, amount: number): <inferred>` — Portal: create an installment/balance payment link.
706
+ - `portalUpdateParticipant(token: string, assignmentId: string, data: Record<string, unknown>): <inferred>` — Portal self-service: customer fills their own participant data. Verifies the
707
+ - `portalView(token: string): <inferred>` — Portal view: booking + items + participants + ledger (no staff data).
708
+ - `priceFromExistingRows(tx: BookingTx, bookingId: string, config: ResolvedBookingConfig): Promise<ParticipantPriceResult>` — Price a booking from its CURRENTLY persisted people/items/assignments (no
709
+ - `processDueExpirations(now: Date = new Date()): Promise<{ abandoned: number; forfeited: number }>` — Auto-cancel expired bookings: abandoned drafts (deposit hold lapsed) and
710
+ - `processDueReminders(now: Date = new Date()): Promise<number>` — Send all due reminders (unsent, `scheduledFor <= now`) via the email adapter,
711
+ - `readTripCapacity(tripRef: string): Promise<number | null>` — Read a trip's seat capacity from the catalog collection.
712
+ - `recordCash(bookingId: string, amount: number): Promise<void>` — Record a manual cash payment (staff). Settles immediately and recomputes status.
713
+ - `refundPayment(paymentId: string, amount?: number): Promise<void>` — Refund a paid ledger entry via the adapter (staff-only manual action), then
714
+ - `renderBookingEmail(kind: BookingEmailKind, lang: string, context: Record<string, unknown>): Promise<string>` — Render a booking email's HTML body for the given language + context.
715
+ - `requireBookingConfig(): ResolvedBookingConfig` — Returns the resolved booking config, throwing when the module is not registered.
716
+ - `rescheduleReminders(tx: BookingTx, bookingId: string, dates: Array<Date | null>): Promise<void>` — Recompute the reminder schedule for a booking after a departure-date change:
717
+ - `resolveByToken(token: string): Promise<BookingRow | null>` — Resolve a booking by its unguessable portal access token (active only).
718
+ - `runBookingMaintenance(now: Date = new Date()): Promise<void>` — Run one maintenance pass (reminders + expirations), guarded against overlap.
719
+ - `runCapacityCheck(items: Array<{ tripRef: string; participantsCount: number }>, excludeBookingId?: string): Promise<void>` — Generic seat-capacity check (core). Sums requested participants per trip and
720
+ - `runValidateBooking(ctx: BookingValidationContext): Promise<void>` — Invoke the client-supplied validation seam, wrapping failures uniformly.
721
+ - `sendBookingEmail(bookingId: string, kind: BookingEmailKind, extra: Record<string, unknown> = {}): Promise<void>` — Send a transactional booking email to the booker. No-op when no email adapter
722
+ - `serverInit(): void` — Conventional server-init entry run by `initCMS` via `serverHooks.initModule`
723
+ - `startBookingMaintenance(): void` — Start the in-process loop (config-gated). Idempotent. Called from serverHooks.init.
724
+ - `stopBookingMaintenance(): void`
725
+ - `subjectFor(kind: BookingEmailKind, lang: string): string` — Localised subject for a kind, falling back to English.
726
+ - `toPortalView(booking: BookingRow, items: ItemRow[], participants: FlatParticipant[], payments: Array<PaymentEntry & { paidAt?: Dat...): <inferred>` — Pure: shape a portal-safe DTO. Explicitly whitelists fields — `staffNotes`,
727
+ - `verifyPhone(token: string, phone: string): Promise<BookingRow | null>` — Drugi czynnik: zwraca booking gdy podany numer pasuje do `customer.phone`.
728
+ - `voidBookingAdjustment(adjustmentId: string, opts: { actor: string; now?: Date }): Promise<void>` — Soft-void an adjustment (kept for audit) and recompute the booking totals +
729
+
555
730
  ### `includio-cms/files-local`
556
731
 
557
732
  - `const fullDir: <inferred>`
package/CHANGELOG.md CHANGED
@@ -3,6 +3,83 @@
3
3
  All notable changes to includio-cms are documented here.
4
4
  Generated from `src/lib/updates/` — do not edit manually.
5
5
 
6
+ ## 0.37.1 — 2026-07-14
7
+
8
+ Dwie łatki cenowe w bookingu. (1) Cena rezerwacji przestaje pochodzić z żądania HTTP — do 0.37.0 publiczne endpointy przyjmowały `unitPrice` prosto z ciała żądania i nikt nie konfrontował go z katalogiem, więc wyprawę dało się zarezerwować za grosz. (2) Data wyceny zostaje zamrożona na rezerwacji — dotąd każde jej dotknięcie (edycja, ale też zaksięgowanie korekty) przeliczało reguły zależne od czasu wg DZISIEJSZEJ daty, przez co early-bird sprzed miesięcy cicho znikał i klient robił się winien pieniądze.
9
+
10
+ ### Added
11
+ - `defineBooking({ resolvePrice })` — serwerowy szew zwracający cenę bazową (dorosłego) pozycji katalogu w groszach. Rdzeń nie zna kształtu katalogu, więc cenę podaje projekt: `resolvePrice: async (tripRef) => Math.round(Number((await resolveEntry({ id: tripRef, status: "published" }))?.price ?? 0) * 100)`.
12
+ - Model zaufania oparty na aktorze: `createBooking(input, { actor })` przyjmuje `public | portal | staff`. Domyślny aktor to `public` (niezaufany) — pominięcie parametru nie otwiera dziury. Zalogowana obsługa (`staff`) nadal może podać cenę ręcznie: korekta i rabat negocjowany telefonicznie zostają możliwe.
13
+ - Typ `PricedCreateInput` wymusza kolejność: rezerwacji nie da się zbudować z pozycji, której nie wyceniła bramka — pilnuje tego kompilator, nie dyscyplina.
14
+
15
+ ### Fixed
16
+ - **Bezpieczeństwo (data wyceny).** Nowa kolumna `bookings.priced_at` zamraża moment wyceny. `editBooking` i `priceFromExistingRows` (ścieżka korekt — `adjustments.ts:65` i `:155`) liczą teraz early-bird na tę datę, nie na `new Date()`. Wcześniej wystarczyło, że obsługa zaksięguje dopłatę ustaloną telefonicznie tydzień przed wylotem, a rabat za wczesną rezerwację przepadał. `priceFromExistingRows` straciło parametr `now` — kompilator nie pozwoli już podać tam zegara.
17
+ - Rezerwacje sprzed 0.37.1 mają `priced_at = NULL` i wyceniają się z `created_at` — dokładnie tym, czym było wtedy `now`. Zero migracji danych, zero zmiany zachowania.
18
+ - **Bezpieczeństwo (ceny).** Trzy publiczne drogi wpuszczały cenę z zewnątrz: tworzenie rezerwacji (`createBookingHandler`), dokładanie wycieczki przez portal (`add-trips`) i portalowa edycja pozycji (`portalEdit` → `editBooking`). Bramka stoi teraz w rdzeniu (`createBooking`/`editBooking`), nie w handlerach, więc każdy przyszły publiczny endpoint jest chroniony domyślnie.
19
+ - Cena pozycji już zapisanej w rezerwacji jest brana z jej `unitPriceSnapshot`, a nie przeliczana z katalogu — dotknięcie rezerwacji przez portal nie podniesie klientowi ceny, gdy cennik w międzyczasie poszedł w górę.
20
+ - Odczyt katalogu w `editBooking` odbywa się przed otwarciem transakcji: szew klienta robi własne I/O, a wołany pod blokadą `FOR UPDATE` trzymałby wiersz rezerwacji i sięgał po drugie połączenie z tej samej puli.
21
+ - Ceny ujemne i niecałkowite (ułamek grosza) są odrzucane — również dla obsługi.
22
+
23
+ ### Breaking
24
+ - Nowa kolumna `bookings.priced_at` — wymagana migracja (`drizzle-kit push`). Nullable, bez backfillu.
25
+ - **`defineBooking({ resolvePrice })` jest teraz wymagany, gdy booking wystawia publiczne endpointy** (`createBookingHandler`, portal). Bez niego rdzeń odmawia wyceny (fail-closed) zamiast po cichu zaufać cenie z żądania. Instalacje używające wyłącznie panelu admina działają bez zmian.
26
+ - `BookingItemInput.unitPrice` jest opcjonalne i **honorowane wyłącznie dla `actor: "staff"`**. Dla klienta końcowego jest ignorowane — cenę ustala katalog.
27
+ - Publiczne API klienta: `createBookingClient().addTrips(token, additions)` oraz `useBooking().addTrips(additions)` nie przyjmują już `unitPrice` w pozycjach (`{ tripRef, assignments }`). Storefronty przekazujące cenę muszą ją usunąć z ładunku — była i tak ignorowana po stronie serwera.
28
+
29
+ ## 0.37.0 — 2026-06-17
30
+
31
+ Moduły-nakładki + context switcher: shop i booking jako równorzędne, opcjonalne moduły rozszerzające rdzeń CMS (model „WooCommerce na WordPressie"). Zunifikowany rejestr `config.modules`, przełącznik kontekstu w adminie (Treści / Sklep / Rezerwacje) dla czytelniejszego UI, per-moduł dashboard. Shop przepięty na nowy kontrakt `CMSModule` (dowód generyczności #1), booking dodany jako drugi moduł biblioteki z pełnym rdzeniem domenowym (wycena/state machine/księga płatności) — dowód #2. Booking write-flow + portal self-service domknięte: tworzenie (public+staff), zadatek + webhook idempotentny, pełna edycja self-service (recompute/pojemność/„nie poniżej wpłaconego”), maile + przypomnienia, cron bez-infra (in-process loop + secured endpoint), strefa obsługi w adminie, scaffold `--booking`. Plan: `docs/superpowers/plans/2026-06-18-booking-write-flow-portal.md`, spec: `docs/superpowers/specs/2026-06-18-booking-write-flow-portal-design.md`.
32
+
33
+ ### Added
34
+ - `CMSModule` (`includio-cms` — `$lib/types/modules.ts`, `@public`) — kontrakt modułu-nakładki w 3 warstwach: serializowalny deskryptor (id/label/icon-string/context z nav sections + surfaced collections), server-only `serverHooks` (init), oraz client-side dashboard rejestrowany po `id`. Nav = dane (ikony jako nazwy z icon-map), nie komponenty.
35
+ - `config.modules: CMSModule[]` — zunifikowany rejestr modułów w `defineConfig`. CMS dostaje `getModule(id)` / `getModuleConfig<T>(id)` / `hasModule(id)` + `modules` Map. `initCMS` odpala `serverHooks.init` każdego modułu (idempotentnie, fire-and-forget).
36
+ - `moduleSchema` w `cms.schema.ts` — walidacja Zod modułów (unikalny `id`, wymagane icon/sections, `surfacedCollections`/`surfacedSingles` muszą referować zadeklarowane kolekcje/single).
37
+ - Context switcher (`context-switcher.svelte` w `site-header`) — przełącznik widoku admina: Treści (rdzeń CMS) / Sklep / Rezerwacje. Renderuje się tylko gdy ≥1 moduł; zero zmian wizualnych dla projektów bez modułów. Aktywny kontekst rozstrzygany przez `resolveActiveContext` (`?ctx` query > PersistedState sticky > ścieżka), ścieżki modułowe i content-core mają pierwszeństwo (poprawny deep-linking).
38
+ - Generyczny sidebar wg kontekstu (`nav-module.svelte`) — kontekst modułu pokazuje jego sekcje + „wciągnięte" kolekcje/single rdzenia (prop `filterSlugs` + `?ctx` w linkach na `nav-collections`/`nav-singletons`). Opcjonalne reaktywne badge przez `countSource`.
39
+ - Per-kontekst dashboard (`ModuleDashboardPage` + `dashboard-registry` po `id`, `@public` przez `includio-cms/admin/client`) — landing `/admin/shop` i `/admin/booking`. `shop-dashboard` (liczba zamówień + skróty), `booking-dashboard` (aktywne rezerwacje / zaległe salda / do potwierdzenia).
40
+ - `defineShop()` zwraca teraz `CMSModule` (id `shop`, ikona `building-store`, sekcje Produkty/Zamówienia/Wysyłka/Kupony). Pełna warstwa server/http/remote shopa nietknięta (363 testy zielone).
41
+ - `defineBooking(config): CMSModule` (`includio-cms/booking`, `@public`) — moduł rezerwacji. Config: `currency`, `catalogCollection` (surfaced), `depositAmount`, `pricing` (rabaty), `payment`, `adminEmail`.
42
+ - Booking rdzeń domenowy (pure, w pełni testowalny): `price()` — silnik wyceny (rabat ilościowy + early-bird, kompozycja largest-wins/sumowanie); `deriveStatus()` + `assertTransition()` — state machine `draft→reserved→partially_paid→paid→completed/cancelled` (status liczony z salda); `ledger` — saldo = gross − Σpaid, guard „nie poniżej wpłaconego". 37 testów.
43
+ - 5 tabel Drizzle bookingu (`bookings`/`booking_items`/`booking_participants`/`booking_payments`/`booking_reminders`) + eksport `includio-cms/db-postgres/schema-booking`. Kwoty w groszach, soft-delete, jsonb totals/customer/participants.
44
+ - `includio-cms/booking` + `includio-cms/db-postgres/schema-booking` — nowe entry points w `package.json` exports.
45
+ - Admin: lista Rezerwacje (`/admin/booking/reservations`) + remote `listBookings`/`getBookingEnabled`.
46
+ - Booking write-flow: `createBooking()` (public self-serve + staff, walidacja pojemności + seam `validateBooking`, snapshot ceny, harmonogram przypomnień), `createDepositPayment()` (link zadatku przez wstrzyknięty adapter), public handler `POST /api/booking`. Status zawsze liczony z księgi (`deriveStatus`).
47
+ - Webhook płatności (`POST /api/booking/webhook/[provider]`) — `applyWebhookPayment()` idempotentny przez row-lock (`SELECT … FOR UPDATE` po `providerRef`), przeliczenie salda + statusu, czyszczenie holdu zadatku, mail `deposit-paid`/`payment-received`. Czysta logika `applyPaymentEvent` testowana jednostkowo.
48
+ - Edycja rezerwacji (`editBooking()`, wspólna dla obsługi i portalu): recompute ceny, guardy wg actora, `portal` blokuje zejście poniżej wpłaconego (`LedgerError` → HTTP 409 „skontaktuj się z obsługą”), reschedule przypomnień przy zmianie wylotu. Refundy = ręczna akcja obsługi przez adapter (`refundPayment`, przeliczenie salda).
49
+ - Portal self-service (`GET|POST /api/booking/portal/[token]`, `?action=edit|pay`) + SDK `createBookingClient` i reaktywny `useBooking` (`includio-cms/booking/client`). `toPortalView` whitelistuje pola (brak `staffNotes`). Strony storefront/portalu buduje repo klienta na SDK.
50
+ - Maile bookingu: lokalny Handlebars registry (override projektem → package default → fallback lang) + 6 szablonów PL/EN (`booking-received`/`deposit-paid`/`payment-received`/`payment-link`/`balance-due`/`balance-overdue-cancelled`).
51
+ - Cron bez-infra: czyste `processDueReminders`/`processDueExpirations` (draft-expiry + balance-due forfeiture z przepadkiem zadatku) napędzane in-process loopem (`serverHooks.init`, interwał z configu, domyślnie 15 min) ORAZ secured endpointem `POST /api/booking/maintenance` (Bearer token) jako trigger wymienny.
52
+ - Strefa obsługi w adminie: detal rezerwacji `/admin/booking/reservations/[id]` (księga, pozycje, uczestnicy, akcje) + remote mutacje (`createBookingStaff`/`editBookingStaff`/`addPaymentLink`/`recordCashPayment`/`refundBookingPayment`/`changeBookingStatus`/`cancelBooking`/`sendBookingEmailCmd`).
53
+ - `includio-cms/booking/client` (SDK + `useBooking`) i `includio-cms/booking/server` (serwisy + HTTP handlery, node-only — kod serwerowy poza bundlem klienta) — nowe entry points.
54
+ - `defineBooking` config rozszerzony: `depositTtlMinutes` (TTL porzuconego draftu, def. 60), `balanceDueDaysBefore` (termin dopłaty), `capacityField` (pojemność z wpisu katalogu), `reminders[]`, `validateBooking(ctx)` (seam client-specific), `maintenance` (autoRun/intervalMinutes), `maintenanceToken`. Kolumny `bookings`: `balanceDueAt`/`holdExpiresAt`/`cancelReason`.
55
+ - Scaffold: `includio scaffold admin` emituje też trasy bookingu (api/booking/*, strony admina, portal `rezerwacja/[token]`) gdy wykryje `defineBooking(` — flagi `--booking`/`--no-booking`. Generyczny helper rate-limit podniesiony do core (`$lib/server/security/rate-limit-handler`, współdzielony shop+booking).
56
+ - Redesign kontekstów (admin context workspaces): context switcher przeniesiony do nagłówka sidebara obok loga (inline pill, w trybie zwiniętym sama ikona kontekstu), nawigacja scoped per kontekst — każdy kontekst pokazuje wyłącznie swoje sekcje + własny „Pulpit" (Treści→`/admin`, moduł→`landingUrl` z ikoną modułu). Globalne narzędzia (Biblioteka mediów + admin: Użytkownicy/Konserwacja) przyklejone do dołu sidebara w każdym kontekście. Trasy neutralne (`/admin/media|users|maintenance|account`) zachowują aktywny kontekst zamiast wracać do Treści. Tożsamość kontekstu przez ikonę, Plum jednolity (`data-context` na korzeniu sidebara jako forward-compat hook pod ewentualne akcenty). Spec: `docs/superpowers/specs/2026-06-18-admin-context-workspaces-design.md`.
57
+ - Silnik reguł rabatowych bookingu (`price()` przepisany, `includio-cms/booking`): rabaty jako serializowalne reguły `when`→`then` z grupami wykluczającymi (w grupie wygrywa największy rabat, między grupami sumują się). Zastępuje `quantityDiscounts`/`earlyBird`/`combine`. Nowe efekty: `percentOffCheapestLine` (100 = najtańsza pozycja gratis) + `grantPerk` (prezent — notka dla obsługi). `PriceResult.perks` + per-linia `appliedDiscounts` (targetRef). Typy: `DiscountRule`/`Condition`/`Effect`/`Perk`/`PricingLine`. Silnik czysty, bez sprzężenia z domeną (gotowy pod współdzielenie z shop).
58
+ - Generyczny seam wyceny per linia: `PricingLine.subtotal?` — gdy podany, jest grossem linii (zamiast `unitPrice×quantity`), używany też przez „najtańsza pozycja gratis" (cheapest wg subtotalu). Snapshot na `booking_items.subtotalSnapshot` (nullable) zapewnia wierny recompute przy edycji. Wstecznie kompatybilne (brak subtotalu = zachowanie bez zmian). Pozwala klientowi naliczać dowolną nie-płaską cenę linii (np. cena wg wieku uczestników, stawki grupowe) bez logiki klienta w rdzeniu — wartości/obliczenia po stronie projektu. Spec: `docs/superpowers/specs/2026-06-18-booking-participants-fields-child-pricing-design.md`.
59
+ - Booking + PayU (i każdy adapter shopu) bez modułu shop: `PaymentCreateContext` zyskuje `continueUrl` (pełny absolutny URL powrotu) + `buyer` ({firstName,lastName,phone}). Adapter PayU `createPayment` preferuje ctx i pomija `requireShopConfig`/`getOrderById` gdy `continueUrl` podane (ścieżka shop bez zmian, wstecznie zgodna). `defineBooking` zyskuje `portalUrlTemplate` (placeholder `{token}` → `accessToken`); `createDepositPayment`/`createInstallmentPayment` budują ctx przez czysty `buildPaymentContext` i przekazują do adaptera. Pozwala wpiąć realny PayU w booking bez shopu. Spec: `docs/superpowers/specs/2026-06-19-booking-payu.md` (repo klienta).
60
+ - Treść maili transakcyjnych bookingu dopracowana (PL/EN, wszystkie 6 rodzajów) we wspólnym layoucie — nowe partials `_partials/{header,footer}` (rejestrowane przed każdym renderem, kolizyjnie-bezpieczne wobec identycznych nazw w module shop), styling AriaCMS + przycisk do panelu rezerwacji (`portalUrl` z `accessToken`+`portalUrlTemplate`). `balance-due` podaje termin dopłaty z datą i przybliżoną liczbą dni oraz ostrzega o przepadku zadatku; nowe helpery Handlebars `date`/`daysUntil`. Link do płatności wysyłany mailem do bookera po akcji obsługi `addPaymentLink`. Nowe pole `defineBooking({ brand })` — nazwa firmy w stopce maila (override per-projekt nadal przez `src/emails/booking/`). Spec/plan: `docs/superpowers/specs/2026-06-19-booking-email-content-design.md`.
61
+ - Widoki podglądu (`previews[]` w kolekcji/singlu, `@public`) — jedno entry może mieć kilka celów podglądu (pełny wpis, zajawka/karta w liście). Przełącznik „Widok" w pasku podglądu (ukryty przy jednym widoku), a `preview: '<nazwa>'` na tabie przełącza widok automatycznie przy wejściu na ten tab; tab bez powiązania nie rusza podglądu, a ręcznie wybrany widok jest zapamiętywany per tab. Podgląd ładuje `{url}?preview={id}&view={name}` — projekt może mieć route per widok albo jeden route rozgałęziający się na `view`. Stary `previewUrl` działa bez zmian (normalizowany do jednego widoku). `defineConfig` waliduje spójność: unikalne nazwy, max jeden `default`, `previewUrl`/`previews` wykluczają się, każde `tab.preview` musi wskazywać istniejący widok (literówka wywala build). Spec: `docs/superpowers/specs/2026-07-13-preview-views-design.md`.
62
+ - Admin bookingu — lista rezerwacji na wspólnym `DataTable`: wyszukiwanie po numerze oraz po danych klienta, filtr statusu, paginacja serwerowa (`listBookings({status, search, limit, offset})` → `{items, total}`; było: `SELECT *` bez limitu, czyli cała baza rezerwacji do przeglądarki). Statystyki pulpitu liczy nowy `getBookingStats()` — agregat SQL, bo sumowanie stronicowanej listy pokazywałoby po cichu liczby z pierwszej strony.
63
+ - Admin bookingu — czytelne statusy (`StatusBadge variant="booking"`): „Czeka na zaliczkę", „Zarezerwowane", „Częściowo opłacone", … zamiast surowych wartości technicznych (`draft`, `partially_paid`) na liście i w detalu.
64
+ - Admin bookingu — pozycje rezerwacji pokazują NAZWĘ wycieczki, nie jej uuid. `getBooking` rozwiązuje wpis katalogu zawsze, gdy katalog jest skonfigurowany (wcześniej tylko gdy `participantFields` było funkcją) i wystawia `tripTitle`; picker pozycji i detal używają jednego helpera `catalogTitle`, więc nazywają wycieczkę tak samo.
65
+ - `defineBooking({ customerFields })` (`@experimental`) — etykiety worka `customer` w panelu admina (`FieldDef[]`: `key`, `label`, opcjonalna `role`). Rdzeń nie zna nazw pól projektu, więc bez tego panel pokazuje uczłowieczone klucze („Referral source"). Klucze zagnieżdżone adresuje się kropką (`people.birthDate`). Wyłącznie prezentacja — nie waliduje i nie zmienia danych.
66
+ - Admin bookingu — data urodzenia uczestnika pokazuje skończone lata w nawiasie („1990-05-05 (36 l.)"). Rozpoznanie po ROLI pola (`role: 'birthDate'`), nie po nazwie — z samej wartości nie odróżnisz daty urodzenia od daty przylotu.
67
+ - `defineBooking({ surfacedCollections })` (`@experimental`) — dodatkowe kolekcje wciągane do kontekstu „Rezerwacje" w adminie obok katalogu (np. kolekcja definiująca typy wypraw i pola uczestnika). Bez tego mieszkają w kontekście „Treści" i wyglądają jak doszyta kolekcja CMS-a, choć obsługują rezerwacje.
68
+ - Admin bookingu — nowa sekcja „Klient" w detalu rezerwacji. Wcześniej dane osoby rezerwującej (imię, e-mail, telefon, okno pobytu, odpowiedzi z formularza) były zapisywane, ale **nigdzie nie pokazywane** — obsługa nie mogła oddzwonić do klienta z panelu. Renderer jest generyczny: rozpoznaje typ po WARTOŚCI (e-mail → `mailto:`, telefon → `tel:`, data ISO → format lokalny), nie po nazwie pola, więc działa dla dowolnego kształtu `customer` bez zmian w rdzeniu; tablice obiektów (np. manifest uczestników) renderują się tabelką. `getBooking` wystawia `arrivalDateField` z konfiguracji.
69
+
70
+ ### Fixed
71
+ - Pole `relation` — dwa wpisy o tej samej nazwie przestają się zlewać w jeden. `Command.Item` dostawał `value={label}`, a `value` jest w bits-ui tożsamością pozycji (filtrowanie, nawigacja klawiaturą, podświetlenie), więc identyczne nazwy dawały `aria-selected=true` na obu wierszach naraz. Teraz tożsamość to `id` wpisu, a wyszukiwanie po nazwie zapewnia `keywords` (domyślny scorer skleja `value` + `keywords`). Zaznaczenie (`isSelected`) zawsze szło po `id` — psuło się wyłącznie podświetlenie.
72
+ - Ścieżka wpisu (`_url`, lista kolekcji) zawsze bezwzględna, z dokładnie jednym wiodącym slashem. `applyPathTemplate` zwracało szablon 1:1, więc `pathTemplate: 'blog/{slug}'` dawało `_url: 'blog/x'` — URL **względny** (z podstrony prowadził w złe miejsce), mimo że docs obiecują `/blog/x`. Projekty pisały więc `pathTemplate: '/blog/{slug}'`, a wtedy lista kolekcji w adminie pokazywała `//blog/x`, bo `entry-link` doklejał własny slash. Teraz szablon i slug są składane bez podwojenia (`//` → `/`), obie formy szablonu działają, a URL-e bezwzględne (`https://…`) zostają nietknięte. `EntryLink` przyjmuje `path` (gotowa ścieżka) zamiast `slug`.
73
+
74
+ ### Breaking
75
+ - `config.shop` USUNIĘTY. Shop rejestruje się teraz przez `config.modules`: `defineConfig({ modules: [defineShop({...})] })` zamiast `defineConfig({ shop: defineShop({...}) })`. `defineShop()` zwraca `CMSModule` (nie `ResolvedShopConfig`). Wewnętrznie `getCMS().shopConfig` działa nadal (shim → `getModuleConfig('shop')`), więc warstwa server/http/remote shopa pozostaje kompatybilna.
76
+ - Tabela `bookings` ma nowe kolumny (`balance_due_at`, `hold_expires_at`, `cancel_reason`) oraz `booking_items` kolumnę `subtotal_snapshot` (nullable) wymagane przez write-flow. Po aktualizacji uruchom `pnpm db:push` (lub migrację) w projekcie używającym bookingu.
77
+ - `PricingConfig` bookingu przepisany na `{ rules: DiscountRule[] }` — `quantityDiscounts`/`earlyBird`/`combine` USUNIĘTE. `price(lines, now, config)` ma nową sygnaturę (linie z `ref`/`departureDate` zamiast płaskich `{unitPrice,quantity}` + osobnej daty). `BookingTotals` zyskuje `perks`/`appliedRules` (jsonb `totals` — bez migracji DB). Zaktualizuj `defineBooking({ pricing: { rules: [...] } })`.
78
+
79
+ ### Notes
80
+
81
+ Pre-1.0: zmiana dozwolona bez okresu deprecacji (jednoosobowy konsument). Migracja: (1) w `cms.config.ts` zamień `shop: defineShop({...})` na `modules: [defineShop({...})]`; (2) jeśli używasz bookingu — `pnpm db:push` (nowe kolumny) i przescaffolduj trasy (`includio scaffold admin` wykryje `defineBooking(`). Provider płatności (Stripe/PayU) konfigurowalny przez `defineBooking({ payment: [...] })` — core adapter-agnostyczny.
82
+
6
83
  ## 0.36.9 — 2026-07-02
7
84
 
8
85
  Duplikacja wpisu z tabeli kolekcji + serwerowa normalizacja pól slug przy zapisie. Menu wiersza zyskuje „Duplikuj" — tworzy nieopublikowany szkic z tą samą treścią (wszystkie języki), z prefiksem tytułu „Kopia — " i unikalnym slugiem („-kopia"), i przenosi do edytora duplikatu. Media i relacje są współdzielone przez referencje (bez głębokiego klonu). Niezależnie od duplikacji: każde pole typu slug (SlugField oraz slug w polu SEO) jest przy zapisie normalizowane przez slugify — „Singapur"/„Hello World" zapisują się jako „singapur"/„hello-world", transparentnie dla użytkownika. Spec: `docs/superpowers/specs/2026-07-02-duplicate-entry-and-slug-normalization-design.md`, plan: `docs/superpowers/plans/2026-07-02-duplicate-entry-and-slug-normalization.md`.
package/DOCS.md CHANGED
@@ -1,4 +1,4 @@
1
- # Includio CMS Documentation (v0.36.9)
1
+ # Includio CMS Documentation (v0.37.1)
2
2
 
3
3
  > This file is auto-generated from the docs site. For the latest version, update the package.
4
4
 
@@ -587,7 +587,8 @@ const posts = defineCollection({
587
587
  | `labels` | `{ singular?: Localized; plural?: Localized }` | No | Display labels in admin |
588
588
  | `entryAdminTitle` | `string` | No | Field slug used as entry title in admin list |
589
589
  | `sidebarIcon` | `IconName` | No | Icon for the admin sidebar |
590
- | `previewUrl` | `string` | No | URL pattern for previewing entries |
590
+ | `previewUrl` | `string` | No | URL pattern for previewing entries (shorthand for one view) |
591
+ | `previews` | `PreviewView[]` | No | Named [preview views](#preview-views) — mutually exclusive with `previewUrl` |
591
592
  | `orderable` | `boolean` | No | Enable manual drag-and-drop ordering |
592
593
  | `listColumns` | `string[]` | No | Field slugs to display as columns in admin list |
593
594
  | `layout` | `Layout` | No | Admin editor [layout](/docs/getting-started/layout) |
@@ -596,6 +597,29 @@ const posts = defineCollection({
596
597
 
597
598
  > **entryAdminTitle:** Set `entryAdminTitle` to a text field slug (like `'title'`) so entries show meaningful names in the admin list instead of IDs.
598
599
 
600
+ ## Preview views
601
+
602
+ One entry is often rendered in more than one way on the front end — a full page, and a teaser card in a listing. Declare each way as a view:
603
+
604
+ ```typescript
605
+ previews: [
606
+ { name: 'full', label: { pl: 'Pełny wpis' }, url: '/preview/blog-post' },
607
+ { name: 'teaser', label: { pl: 'Zajawka' }, url: '/preview/blog-post/teaser' }
608
+ ]
609
+ ```
610
+
611
+ The admin loads `{url}?preview={entryId}&view={name}`, so you may expose one route per view, or a single route that branches on the `view` query param. A **Widok / View** switcher appears in the preview toolbar; with a single view it stays hidden. `previewUrl: '/preview/blog-post'` is the shorthand for exactly one view, and keeps working as before.
612
+
613
+ Bind a view to a tab and the preview follows the editor:
614
+
615
+ ```typescript
616
+ { type: 'tab', label: { pl: 'Zajawka' }, preview: 'teaser', fields: ['thumbnail', 'featured'] }
617
+ ```
618
+
619
+ A tab **without** `preview` leaves the current view alone, and a view the editor picks by hand is remembered for that tab. Views do not change what is fetched — the entry always arrives whole; they only change how it is rendered in the preview.
620
+
621
+ > **Typos fail the build:** `defineConfig` checks that view names are unique, that at most one is `default`, that `previewUrl` and `previews` are not both set, and that every `tab.preview` points at a declared view.
622
+
599
623
  ## Usage in Config
600
624
 
601
625
  ```typescript
@@ -674,7 +698,8 @@ const homepage = defineSingle({
674
698
  | `fields` | `Field[]` | Yes | Array of field definitions |
675
699
  | `label` | `Localized` | No | Display label in admin |
676
700
  | `sidebarIcon` | `IconName` | No | Icon for admin sidebar |
677
- | `previewUrl` | `string` | No | URL for previewing |
701
+ | `previewUrl` | `string` | No | URL for previewing (shorthand for one view) |
702
+ | `previews` | `PreviewView[]` | No | Named [preview views](/docs/collections#preview-views) — mutually exclusive with `previewUrl` |
678
703
  | `layout` | `Layout` | No | Admin editor [layout](/docs/getting-started/layout) |
679
704
  | `slugField` | `string` | No | Dot-path to slug field (default: `'seo.slug'`) |
680
705
  | `pathTemplate` | `string` | No | URL path template, e.g. `'about'` |
package/ROADMAP.md CHANGED
@@ -19,6 +19,9 @@
19
19
  - [x] `[chore]` `[P1]` Faza 10 — Documentation Pass (0.23.0): DOCS.md uzupełnione (number/boolean/date edge cases, REST cURL + error codes, Entries error handling + transactions, Admin UI a11y, Adapter Contracts), nowe sekcje (Stability Promise, Security Model), Migration Guide v0.x → v1.0 master cheatsheet (0.16-0.22), ROADMAP cleanup (pre-v1.0 → ROADMAP-ARCHIVE.md) <!-- files: src/routes/docs/{stability-promise,security}/+page.svx, src/routes/docs/migration/+page.svx, src/routes/docs/_config/nav.ts, scripts/compile-docs.ts, ROADMAP-ARCHIVE.md, src/lib/updates/0.23.0/ -->
20
20
  - [x] `[chore]` `[P1]` Faza 11 — Performance Pass (0.24.0): N+1 elimination (`getImageStyle` batch IN query, `getRawEntries` batch entry versions), Sharp srcset `Promise.allSettled` (one timeout doesn't kill batch), background maintenance in-process lock verified + tested, bench infra (`pnpm bench` + representative benchmarks), lazy adapter imports verified <!-- files: src/lib/core/server/media/styles/operations/, src/lib/core/server/fields/resolveImageFields.ts, src/lib/core/server/entries/operations/get.ts, src/lib/db-postgres/index.ts, src/lib/core/server/media/operations/backgroundMaintenance.spec.ts, src/lib/updates/0.24.0/ -->
21
21
  - [x] `[feature]` `[P0]` Faza 11.5 — Shop polish (0.25.0): Stripe adapter (Checkout Session, sig verify, idempotency), refund infra (full + partial cross-adapter), webhook idempotency table (`shop_webhook_events`), coupons (% / fixed, expiresAt, maxUses, minOrder), low-stock email alert, order export CSV, default email templates (PL/EN), SDK JSDoc + `@public`, pełna dokumentacja shopa <!-- files: src/lib/shop/adapters/stripe/, src/lib/shop/server/{refund,coupons}.ts, src/lib/db-postgres/schema/shop/{refunds,webhookEvents,coupons,couponRedemptions}.ts, src/lib/admin/client/shop/, src/lib/updates/0.25.0/ -->
22
+ - [x] `[feature]` `[breaking]` `[P1]` Moduły-nakładki + context switcher (0.37.0) — zunifikowany `config.modules` (`CMSModule`: deskryptor + serverHooks + client dashboard), przełącznik kontekstu admina (Treści/Sklep/Rezerwacje), per-moduł dashboard; shop przepięty na kontrakt modułu (`config.shop` usunięty, shim `shopConfig`); booking jako 2. moduł biblioteki — rdzeń domenowy (wycena/state-machine/księga, 37 testów) + 5 tabel Drizzle + lista Rezerwacje. Booking write-flow + portal self-service domknięte: create (public+staff), zadatek + webhook idempotentny (row-lock), pełna edycja self-service (recompute/pojemność/„nie poniżej wpłaconego” → 409), maile + przypomnienia, cron bez-infra (in-process loop + secured endpoint), strefa obsługi, SDK `./booking/client` + node-only `./booking/server`, scaffold `--booking`. <!-- files: src/lib/types/modules.ts, src/lib/core/cms.ts, src/lib/admin/state/active-context*, src/lib/admin/components/layout/{context-switcher,nav-module}.svelte, src/lib/booking/, src/lib/db-postgres/schema/booking/, src/lib/admin/remote/booking.remote.ts, src/lib/admin/client/booking/, src/lib/cli/scaffold/booking.ts, docs/superpowers/specs/2026-06-18-booking-write-flow-portal-design.md, docs/superpowers/plans/2026-06-18-booking-write-flow-portal.md -->
23
+ - [x] `[feature]` `[breaking]` `[P1]` Silnik reguł rabatowych bookingu (0.37.0) — `price()` przepisany na data-driven reguły `when`→`then` z grupami wykluczającymi; zastępuje qty/early-bird; `percentOffCheapestLine` + `grantPerk` (prezent); drabinka bundle pod-zwrotnikami jako dane w configu klienta. Spec: `docs/superpowers/specs/2026-06-18-booking-discount-rules-engine-design.md`, plan: `docs/superpowers/plans/2026-06-18-booking-discount-rules-engine.md`. <!-- files: src/lib/booking/pricing.ts, src/lib/booking/{draft,recompute}.ts, src/lib/booking/server/bookings.ts, src/lib/db-postgres/schema/booking/bookings.ts, src/lib/booking/templates/booking-received.*.html, src/lib/admin/client/booking/reservation-detail-page.svelte -->
24
+ - [x] `[feature]` `[P1]` Widoki podglądu (0.37.0) — `previews[]` w kolekcji/singlu (nazwa + label + url, `previewUrl` = skrót na jeden widok), przełącznik „Widok" w pasku podglądu, `tab.preview` przełącza widok przy wejściu na tab (ręczny wybór zapamiętywany per tab), URL podglądu `{url}?preview={id}&view={name}`, walidacja spójności w `defineConfig`. Spec: `docs/superpowers/specs/2026-07-13-preview-views-design.md`, plan: `docs/superpowers/plans/2026-07-13-preview-views.md`. <!-- files: src/lib/types/{config,layout,cms.schema}.ts, src/lib/core/config/validatePreviews.ts, src/lib/admin/client/entry/{preview-views.ts,preview-views-context.svelte.ts,entry.svelte,hybrid/hybrid-preview.svelte}, src/lib/admin/components/layout/{layout-tabs,layout-renderer}.svelte -->
22
25
  - [ ] `[fix]` `[P1]` Select field — `defaultValue` propagacja do zod schema (full repro: `ideas/post-v1/select-field-defaultvalue-bug.md`); fix planowany w Fazie 12 (RC)
23
26
  - [x] `[chore]` `[P2]` Demo orchestrator — `pnpm demo` startuje czystą instancję na tmpfs DB :5436, seeduje 12 blog postów / 6 projektów / 5 contact submissions / 8 gradient PNG-ów, włącza `demo-site` frontend pod `/demo-site`, każdy start = czysty stan <!-- files: scripts/demo.ts, scripts/seed-demo.ts, src/routes/demo-site/, docker-compose.yml, .env.demo -->
24
27
  - [x] `[feature]` `[P1]` Shop platformowy 0.27.0 — schema-driven variants + booking deposit (envet pilot). Plan: 7 faz. <!-- files: docs/superpowers/specs/2026-05-28-shop-platformowy-design.md, /Users/patryk/.claude/plans/implementacja-ariacms-0-27-partitioned-kahn.md -->
@@ -32,6 +35,33 @@
32
35
  - [x] `[feature]` `[P1]` Slug first-class + SEO v1.0 freeze (0.26.0) — `resolveSeo` public (`includio-cms/core`), `seoFieldDescriptor` SSOT (Zod+TS drift-guard), admin URL un-hardcode (`slugPath.ts` pure resolver, no `seo`-field requirement), `SlugField`/`SeoField`/`SeoFieldData` `@public` v1.0-frozen <!-- files: src/lib/core/fields/slugPath.ts, src/lib/core/fields/seoFieldDescriptor.ts, src/lib/core/fields/resolveSeo.ts, src/lib/types/fields.ts, src/lib/admin/client/collection/collection-entries.svelte, src/lib/updates/0.26.0/ -->
33
36
  - [x] `[feature]` `[P2]` Soft-delete zamówień admin-only (0.34.0) — ukrycie zamówienia z listy bez utraty danych (`deleted_at`/`deleted_by`), kosz + przywracanie, guard `decideOrderDeletion` (safe-statuses + faktura), auto-restore przy płatności, zwolnienie rezerwacji stocku, `requireRole('admin')` <!-- files: src/lib/shop/server/orders.ts, src/lib/db-postgres/schema/shop/order.ts, src/lib/admin/remote/shop.remote.ts, src/lib/admin/client/shop/{shop-order-detail-page,shop-orders-list-page,restore-order-cell}.svelte, src/lib/updates/0.34.0/ -->
34
37
 
38
+ ## 0.37.1 — Bramka cenowa (bezpieczeństwo)
39
+
40
+ - [x] `[fix]` `[breaking]` `[P0]` **Data wyceny zamrożona na rezerwacji** (`bookings.priced_at`). Dotąd cena bazowa była zamrożona, ale data — nie: `editBooking` i `priceFromExistingRows` (ścieżka **korekt**) przeliczały reguły zależne od czasu wg `new Date()`. Obsługa księgowała dopłatę ustaloną telefonicznie tydzień przed wylotem → `daysUntil(anchor, now)` spadało poniżej progu → **early-bird sprzed miesięcy cicho znikał, a klient robił się winien pieniądze**. Bomba tykała sama, bez żadnej zmiany cennika. Rezerwacje sprzed 0.37.1: `priced_at = NULL` → fallback na `created_at` (zero migracji danych). Pełny snapshot **wartości** polityki (`pricing_snapshot`) zostaje w 0.38.0 §4.4 — wymaga świadomej zmiany cennika przez klientkę, więc da się ją uprzedzić.
41
+ - [x] `[fix]` `[breaking]` `[P0]` **Cena rezerwacji przestaje pochodzić z żądania HTTP.** Do 0.37.0 trzy publiczne drogi wpuszczały `unitPrice` z ciała żądania i nikt nie konfrontował go z katalogiem (`create-handler.ts:37`, portalowe `add-trips`, portalowe `edit` → `editBooking`) — wyprawę dało się zarezerwować za grosz. Bramka stoi teraz w rdzeniu (`createBooking`/`editBooking`), oparta na aktorze: `public`/`portal` nie dyktują ceny (nowa pozycja → szew `resolvePrice` z katalogu, istniejąca → jej `unitPriceSnapshot`), `staff` nadal może korygować ręcznie. Domyślny aktor to `public`, więc przyszły publiczny endpoint jest bezpieczny bez pamiętania o niczym. Fail-closed: brak `resolvePrice` = odmowa wyceny.
42
+
43
+ ## 0.38.0 — Moduły: kontrakt vs polityka
44
+
45
+ > **WSTRZYMANE (2026-07-14).** Łuk jest dojrzały, ale klientka nie zobaczy z niego nic, a produktyzacja
46
+ > (`module_settings`, `AdminRouter`, kupony) zwraca się dopiero przy **drugim kliencie z bookingiem**.
47
+ > Wracamy, gdy taki się pojawi — wtedy abstrakcje wyjdą z dwóch realnych przypadków, nie z jednego.
48
+ > Wyjątek już zrobiony: kawałek 3a (jądro reguł) leży zrebase'owany na `feat/pricing-rules-kernel`.
49
+ > Poprawka do wprowadzenia przy powrocie: pozycja o typach wypraw twierdzi, że „moduł ma wnosić je sam" —
50
+ > **to jest błędne**. `tripType` to pojęcie domenowe klienta (klient nr 3 może wynajmować kajaki).
51
+ > Moduł może wnieść MECHANIZM (rejestr typów pól + generator pola `blocks` + adapter + headless front),
52
+ > nie POJĘCIE.
53
+
54
+ Spec: `docs/superpowers/specs/2026-07-13-moduly-kontrakt-vs-polityka-design.md`. Kolejność i uzasadnienia tam.
55
+
56
+ - [x] `[feature]` `[P0]` Kawałek 3a — jądro reguł w rdzeniu (`core/rules`) + cennik jako kategorie uczestnika; golden testy parytetu ze starym silnikiem. Branch `feat/pricing-rules-kernel`.
57
+ - [ ] `[feature]` `[P0]` Kawałek 1 — fundament: `module_settings` (polityka w bazie) + system migracji (`aria migrate` + guard) + `AdminRouter` (routes admina jako dane, koniec z plikami w repo klienta) + `PaymentAdapter` wyprowadzony z shopu do rdzenia.
58
+ - [ ] `[feature]` `[P0]` Kawałek 2 — polityka bookingu: strona „Ustawienia rezerwacji", builder reguł (generowany z rejestru, pole `blocks`), symulator ceny, **snapshot polityki na rezerwacji** (dziś dotknięcie starej rezerwacji przelicza early-bird wg dzisiejszej daty i potrafi podnieść cenę klientowi), kasacja `pricing.ts`.
59
+ - [ ] `[feature]` `[P0]` **Typy wypraw / kategorie uczestnika wracają do modułu.** Dziś kolekcja `tripType` (bloki-dane) + adapter `mapTripTypeFields` (dane→`FieldDef`) mieszkają w repo KLIENTA — każdy kolejny klient przepisuje ~100 linii, a kolekcja wyświetla się w kontekście „Treści", bo moduł nie umie wnieść własnej. Wzorzec jest poprawny (Payload form-builder: stała kolekcja + pola jako bloki; zero DDL, odpowiedzi w `booking_persons.data`) — zły jest tylko adres. Moduł ma wnosić to pojęcie sam (dane w ustawieniach modułu), mapować na `FieldDef` w rdzeniu i pokazywać w kontekście „Rezerwacje". Efekt: klient N nie pisze ani linijki. Obejście na 0.37.0: `defineBooking({ surfacedCollections })` (`@experimental`).
60
+ - [ ] `[feature]` `[P1]` **`customer` dostaje prawdziwy schemat**, nie tylko etykiety. Dziś `defineBooking({ customerFields })` (`@experimental`) opisuje worek `customer` WYŁĄCZNIE do prezentacji, a walidacja tego samego worka żyje w repo klienta (`assertManifest`, `validateSurveyAnswers`) — dwie deklaracje jednej rzeczy, gotowy dryf. Docelowo jedna deklaracja napędza walidację, render w adminie i formularz publiczny (jak `participantFields`).
61
+ - [ ] `[feature]` `[P0]` Kawałek 4 — kody rabatowe: wspólna tabela `coupons` + `coupon_redemptions` w rdzeniu; **port kuponu shopu na jądro reguł** (twardy warunek: jądro z jednym konsumentem to generalizacja bez dowodu).
62
+ - [ ] `[feature]` `[P1]` Kawałek 5 — braki domenowe: `weekdays[]` → dodatki (extras) → overrides per data `{date, capacity?, price?, status?}`.
63
+ - [ ] `[chore]` `[P1]` Konsolidacja pól: `booking/FieldDef` → koperta nad core `Field`. Usuwa import `VariantAttribute` z shopu w `core/server/generator/fields.ts` (dziś **rdzeń importuje z modułu** — odwrócona zależność).
64
+
35
65
  ## v1.x — Post-v1.0 deferred
36
66
 
37
67
  - [ ] `[feature]` `[P1]` SEO module — SERP preview, char limits, global settings, frontend components, sitemap (full plan: `ideas/post-v1/seo-aeo-geo-module.md`)
@@ -56,6 +86,8 @@
56
86
 
57
87
  ## Backlog
58
88
 
89
+ - [ ] `[fix]` `[P1]` Wyciek puli połączeń DB w dev (HMR) — pojedynczy proces `vite dev` konsumenta trzymał **100 połączeń** do Postgresa (cały `max_connections`), po czym baza odbijała wszystko: `FATAL: sorry, too many clients already` (zaobserwowane 2026-07-13 na pod-zwrotnikami). Hipoteza: każde przeładowanie modułu przez HMR tworzy nowy klient `postgres.js` (pula domyślnie 10), a stary nie jest zamykany — 10 przeładowań wyczerpuje limit. Sprawdzić zamykanie klienta w `import.meta.hot.dispose` / singleton na `globalThis` w adapterze. Dotyka każdego, kto dłużej pracuje w dev. <!-- files: src/lib/db-postgres/index.ts -->
90
+ - [ ] `[chore]` `[P1]` CMS doctor — `validateLayout()` jest napisany i przetestowany, ale nie jest wołany z żadnego miejsca produkcyjnego: literówka w nazwie pola w tabie przechodzi po cichu (pole ląduje w orphanach). Podpiąć go do `defineConfig` obok `validatePreviews`, dołożyć `pnpm cms check` w CLI i zestaw reguł zdrowia configu (martwe pola, `pathTemplate` bez `previewUrl`/`previews`, itp.). <!-- files: src/lib/core/fields/layoutUtils.ts, src/lib/sveltekit/config.ts, src/lib/core/config/, src/lib/cli/index.ts -->
59
91
  - [ ] `[feature]` `[P2]` Date/datetime field — przebudowa na shadcn-svelte (bits-ui Calendar/DatePicker) zamiast natywnego inputu. Zgłoszone w QA Etap 5a (4/5); funkcjonalnie OK, odłożone jako osobny redesign pola daty. <!-- files: src/lib/admin/components/fields/date-field.svelte, datetime-field.svelte -->
60
92
  - [ ] `[feature]` `[P2]` `<Video>` — eksponować ref wewnętrznego `<video>` (np. bindable `element` prop lub forward `bind:this`). Obecnie konsument potrzebujący programowej kontroli (play/pause, scrub, mute toggle) musi owijać w `<div bind:this>` + `querySelector('video')` — boilerplate i kruche. Zgłoszone przy customowym hero-wideo (autoplay + przycisk pauzy). <!-- files: src/lib/sveltekit/components/video.svelte -->
61
93
  - [ ] `[feature]` `[P1]` Re-introduce entry autosave with strict draft-only guard (opt-in via `cms.config.ts`, never touch published versions, debounced + visual countdown). Removed in 0.26.0 / S8 because old impl could touch published data ambiguously.
@@ -0,0 +1,40 @@
1
+ <script lang="ts">
2
+ import { getRemotes } from '../../../sveltekit/index.js';
3
+ import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
4
+ import StatCard from '../modules/stat-card.svelte';
5
+ import { Button } from '../../../components/ui/button/index.js';
6
+ import { formatCentsPrice } from '../../utils/formatters.js';
7
+
8
+ const remotes = getRemotes();
9
+ const interfaceLanguage = useInterfaceLanguage();
10
+
11
+ // Agregaty liczone w SQL — lista rezerwacji jest stronicowana, więc sumowanie jej wierszy
12
+ // dawałoby po cichu statystyki wyłącznie z pierwszej strony.
13
+ const statsQuery = $derived(remotes.getBookingStats());
14
+ const stats = $derived(statsQuery.current);
15
+
16
+ const active = $derived(stats?.active ?? 0);
17
+ const outstanding = $derived(stats?.outstanding ?? 0);
18
+ const toConfirm = $derived(stats?.toConfirm ?? 0);
19
+ const currency = $derived(stats?.currency ?? 'PLN');
20
+
21
+ const t = $derived(
22
+ interfaceLanguage.current === 'pl'
23
+ ? { active: 'Aktywne rezerwacje', outstanding: 'Zaległe salda', toConfirm: 'Do potwierdzenia', manage: 'Rezerwacje' }
24
+ : { active: 'Active bookings', outstanding: 'Outstanding balances', toConfirm: 'To confirm', manage: 'Reservations' }
25
+ );
26
+ </script>
27
+
28
+ <div class="flex flex-col gap-4 px-4 pb-6 sm:px-6">
29
+ <div class="grid grid-cols-2 gap-3 sm:grid-cols-3">
30
+ <StatCard label={t.active} value={statsQuery.ready ? active : '—'} />
31
+ <StatCard
32
+ label={t.outstanding}
33
+ value={statsQuery.ready ? formatCentsPrice(outstanding, currency) : '—'}
34
+ />
35
+ <StatCard label={t.toConfirm} value={statsQuery.ready ? toConfirm : '—'} />
36
+ </div>
37
+ <div class="flex flex-wrap gap-2">
38
+ <Button href="/admin/booking/reservations" variant="outline" size="sm">{t.manage}</Button>
39
+ </div>
40
+ </div>
@@ -0,0 +1,3 @@
1
+ declare const BookingDashboard: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type BookingDashboard = ReturnType<typeof BookingDashboard>;
3
+ export default BookingDashboard;