includio-cms 0.26.0 → 0.27.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 (112) hide show
  1. package/API.md +42 -2
  2. package/CHANGELOG.md +65 -0
  3. package/DOCS.md +1 -1
  4. package/ROADMAP.md +8 -0
  5. package/dist/admin/auth-client.d.ts +42 -42
  6. package/dist/admin/client/admin/admin-layout.svelte +12 -2
  7. package/dist/admin/client/admin/admin-layout.svelte.d.ts +2 -1
  8. package/dist/admin/client/collection/data-table.svelte +0 -39
  9. package/dist/admin/client/collection/data-table.svelte.d.ts +0 -2
  10. package/dist/admin/client/shop/coupon-schema.d.ts +1 -1
  11. package/dist/admin/client/shop/refund-dialog.svelte +37 -1
  12. package/dist/admin/client/shop/refund-dialog.svelte.d.ts +3 -0
  13. package/dist/admin/client/shop/shop-order-detail-page.svelte +107 -0
  14. package/dist/admin/components/fields/field-renderer.svelte +6 -1
  15. package/dist/admin/components/fields/icon-field.svelte +86 -0
  16. package/dist/admin/components/fields/icon-field.svelte.d.ts +8 -0
  17. package/dist/admin/components/fields/icon-picker-dialog.svelte +174 -0
  18. package/dist/admin/components/fields/icon-picker-dialog.svelte.d.ts +11 -0
  19. package/dist/admin/components/fields/object-field.svelte +27 -7
  20. package/dist/admin/components/fields/shop-field.svelte +210 -20
  21. package/dist/admin/components/layout/layout-tabs.svelte +1 -0
  22. package/dist/admin/components/variant-form/VariantAttributeRenderer.svelte +109 -0
  23. package/dist/admin/components/variant-form/VariantAttributeRenderer.svelte.d.ts +9 -0
  24. package/dist/admin/helpers/build-icon-set-map.d.ts +8 -0
  25. package/dist/admin/helpers/build-icon-set-map.js +16 -0
  26. package/dist/admin/helpers/index.d.ts +2 -0
  27. package/dist/admin/helpers/index.js +2 -0
  28. package/dist/admin/remote/shop.remote.d.ts +58 -24
  29. package/dist/admin/remote/shop.remote.js +61 -6
  30. package/dist/admin/state/icon-sets.svelte.d.ts +9 -0
  31. package/dist/admin/state/icon-sets.svelte.js +20 -0
  32. package/dist/cli/scaffold/admin.js +2 -2
  33. package/dist/components/ui/checkbox/checkbox.svelte +3 -3
  34. package/dist/core/cms.d.ts +11 -2
  35. package/dist/core/cms.js +29 -0
  36. package/dist/core/fields/fieldSchemaToTs.js +7 -0
  37. package/dist/core/server/generator/fields.d.ts +2 -0
  38. package/dist/core/server/generator/fields.js +34 -1
  39. package/dist/core/server/generator/generator.js +2 -1
  40. package/dist/db-postgres/schema/shop/order.d.ts +37 -1
  41. package/dist/db-postgres/schema/shop/order.js +3 -1
  42. package/dist/db-postgres/schema/shop/payment.d.ts +20 -0
  43. package/dist/db-postgres/schema/shop/payment.js +4 -1
  44. package/dist/db-postgres/schema/shop/product.d.ts +20 -0
  45. package/dist/db-postgres/schema/shop/product.js +3 -1
  46. package/dist/db-postgres/schema/shop/productVariant.d.ts +12 -2
  47. package/dist/db-postgres/schema/shop/productVariant.js +22 -0
  48. package/dist/paraglide/messages/_index.d.ts +36 -3
  49. package/dist/paraglide/messages/_index.js +71 -3
  50. package/dist/paraglide/messages/en.d.ts +5 -0
  51. package/dist/paraglide/messages/en.js +14 -0
  52. package/dist/paraglide/messages/pl.d.ts +5 -0
  53. package/dist/paraglide/messages/pl.js +14 -0
  54. package/dist/shop/cart/types.d.ts +1 -0
  55. package/dist/shop/client/index.d.ts +54 -0
  56. package/dist/shop/client/index.js +5 -1
  57. package/dist/shop/expiry.d.ts +35 -0
  58. package/dist/shop/expiry.js +68 -0
  59. package/dist/shop/http/balance-handler.d.ts +20 -0
  60. package/dist/shop/http/balance-handler.js +91 -0
  61. package/dist/shop/http/cart-handler.js +19 -0
  62. package/dist/shop/http/checkout-handler.js +19 -1
  63. package/dist/shop/http/index.d.ts +2 -0
  64. package/dist/shop/http/index.js +2 -0
  65. package/dist/shop/http/upcoming-handler.d.ts +16 -0
  66. package/dist/shop/http/upcoming-handler.js +65 -0
  67. package/dist/shop/http/webhook-handler.js +46 -9
  68. package/dist/shop/index.d.ts +4 -1
  69. package/dist/shop/index.js +7 -1
  70. package/dist/shop/server/balance-payment.d.ts +40 -0
  71. package/dist/shop/server/balance-payment.js +140 -0
  72. package/dist/shop/server/cart-hydrate.js +2 -0
  73. package/dist/shop/server/init.d.ts +14 -0
  74. package/dist/shop/server/init.js +35 -0
  75. package/dist/shop/server/orders.d.ts +34 -0
  76. package/dist/shop/server/orders.js +141 -2
  77. package/dist/shop/server/payment-policy.d.ts +35 -0
  78. package/dist/shop/server/payment-policy.js +55 -0
  79. package/dist/shop/server/payments.d.ts +29 -0
  80. package/dist/shop/server/payments.js +64 -0
  81. package/dist/shop/server/populate.d.ts +1 -1
  82. package/dist/shop/server/refund.d.ts +17 -12
  83. package/dist/shop/server/refund.js +96 -13
  84. package/dist/shop/server/shop-data.d.ts +4 -1
  85. package/dist/shop/server/shop-data.js +24 -2
  86. package/dist/shop/template.d.ts +13 -0
  87. package/dist/shop/template.js +98 -0
  88. package/dist/shop/types.d.ts +142 -1
  89. package/dist/shop/variant-attributes.d.ts +28 -0
  90. package/dist/shop/variant-attributes.js +69 -0
  91. package/dist/sveltekit/server/index.d.ts +1 -0
  92. package/dist/sveltekit/server/index.js +2 -0
  93. package/dist/types/cms.d.ts +4 -3
  94. package/dist/types/cms.schema.d.ts +1 -1
  95. package/dist/types/cms.schema.js +9 -0
  96. package/dist/types/fields.d.ts +21 -2
  97. package/dist/types/index.d.ts +1 -1
  98. package/dist/types/index.js +1 -1
  99. package/dist/types/plugins.d.ts +40 -0
  100. package/dist/types/plugins.js +4 -1
  101. package/dist/updates/0.26.1/index.d.ts +2 -0
  102. package/dist/updates/0.26.1/index.js +19 -0
  103. package/dist/updates/0.27.0/index.d.ts +2 -0
  104. package/dist/updates/0.27.0/index.js +50 -0
  105. package/dist/updates/index.js +5 -1
  106. package/package.json +1 -1
  107. package/dist/paraglide/messages/hello_world.d.ts +0 -5
  108. package/dist/paraglide/messages/hello_world.js +0 -33
  109. package/dist/paraglide/messages/login_hello.d.ts +0 -16
  110. package/dist/paraglide/messages/login_hello.js +0 -34
  111. package/dist/paraglide/messages/login_please_login.d.ts +0 -16
  112. package/dist/paraglide/messages/login_please_login.js +0 -34
package/API.md CHANGED
@@ -1,8 +1,8 @@
1
- # includio-cms — Public API v0.26.0
1
+ # includio-cms — Public API v0.27.0
2
2
 
3
3
  > Auto-generated by `scripts/generate-api-md.ts`. Do not edit by hand.
4
4
 
5
- Entry points: **18** · Stable: **413** · Experimental: **2**
5
+ Entry points: **18** · Stable: **448** · Experimental: **4**
6
6
 
7
7
  Tags:
8
8
  - `@public` — frozen for v1.0; semver-protected.
@@ -75,10 +75,14 @@ Tags:
75
75
  - `interface FormSubmission`
76
76
  - `interface FormTextareaField`
77
77
  - `interface FormTextField`
78
+ - `interface IconDefinition`
79
+ - `interface IconField`
78
80
  - `type IconName = keyof typeof iconMap`
81
+ - `interface IconSetPlugin`
79
82
  - `interface ImageFieldData`
80
83
  - `interface ImageFieldStyle`
81
84
  - `interface ImageStyle`
85
+ - `isIconSetPlugin(p: Plugin): p is IconSetPlugin` — Type guard for {@link IconSetPlugin}.
82
86
  - `type Language = 'en' | 'pl'`
83
87
  - `type Layout = LayoutPreset | LayoutNode[]`
84
88
  - `type LayoutNode = | SectionNode | ColumnsNode | CardNode | AccordionNode | StackNode | TabsNode | TabNode`
@@ -93,6 +97,7 @@ Tags:
93
97
  - `interface NumberField`
94
98
  - `interface ObjectField`
95
99
  - `type ObjectFieldData = { _id?: string; _slug?: string; } & Record<string, unknown>`
100
+ - `type Plugin = PluginConfig | IconSetPlugin`
96
101
  - `interface RadioField`
97
102
  - `interface RelationField`
98
103
  - `type RelationFieldData = string | string[]`
@@ -132,6 +137,7 @@ Tags:
132
137
  - `const AdminLayout: LegacyComponentType`
133
138
  - `const Badge: LegacyComponentType`
134
139
  - `buildCustomFieldsMap(...plugins: PluginConfig[]): Map<string, CustomFieldDefinition>` — Build a Map<fieldType, CustomFieldDefinition> from plugin configs.
140
+ - `buildIconSetMap(...plugins: IconSetPlugin[]): Map<string, IconSetPlugin>` — Build a Map<slug, IconSetPlugin> from icon-set plugin instances.
135
141
  - `const Button: LegacyComponentType`
136
142
  - `Card`
137
143
  - `const CollectionPage: LegacyComponentType`
@@ -143,6 +149,7 @@ Tags:
143
149
  - `const FormSubmissionPage: LegacyComponentType`
144
150
  - `getContentLanguage`
145
151
  - `getCustomFields(): Map<string, CustomFieldDefinition>`
152
+ - `getIconSets(): Map<string, IconSetPlugin>`
146
153
  - `getLocalizedLabel(label: Localized | undefined, lang: InterfaceLanguage): string`
147
154
  - `getRemotes(): typeof remotes`
148
155
  - `const Input: LegacyComponentType`
@@ -152,6 +159,7 @@ Tags:
152
159
  - `const MediaPage: LegacyComponentType`
153
160
  - `const MediaSelector: LegacyComponentType`
154
161
  - `const ResetPasswordPage: LegacyComponentType`
162
+ - `resolveIconSet(slug?: string): IconSetPlugin | null` — Resolve a single icon set: explicit `slug` (from {@link IconField.set}) takes
155
163
  - `const Separator: LegacyComponentType`
156
164
  - `const ShippingMethodEditPage: LegacyComponentType`
157
165
  - `const ShippingMethodNewPage: LegacyComponentType`
@@ -193,6 +201,7 @@ Tags:
193
201
  - `const exportOrdersCsv: <inferred>`
194
202
  - `const findMediaReferences: <inferred>`
195
203
  - `const generateAltText: <inferred>`
204
+ - `const generateBalanceLinkForOrder: <inferred>`
196
205
  - `const getAltOverview: <inferred>`
197
206
  - `const getCollection: <inferred>`
198
207
  - `const getCollections: <inferred>`
@@ -289,6 +298,7 @@ Tags:
289
298
  - `cmsLayoutLoad(event: RequestEvent): <inferred>` — Returns `cmsContext` from `event.locals` for use in a SvelteKit layout
290
299
  - `countEntries(opts: CountEntriesOptions): Promise<number>` — Count entries matching the same filters as `resolveEntries`, without
291
300
  - `type CountEntriesOptions = Omit< ResolveEntriesOptions, 'limit' | 'offset' | 'populate' | 'orderBy' | 'dataOrderBy' >`
301
+ - `createAdminApiHandler(options?: AdminApiOptions): <inferred>`
292
302
  - `const createConsentLog: <inferred>`
293
303
  - `const createFormSubmission: <inferred>`
294
304
  - `createRestApiHandler(): <inferred>` — REST API handler factory. Returns `{ GET, POST, PUT, DELETE }`
@@ -330,6 +340,8 @@ Tags:
330
340
  - `const shopOrderItemsTable: <inferred>`
331
341
  - `const shopOrdersTable: <inferred>`
332
342
  - `const shopOrderStatusHistoryTable: <inferred>`
343
+ - `type ShopPaymentKind = 'full' | 'deposit' | 'balance'`
344
+ - `const shopPaymentKindEnum: <inferred>`
333
345
  - `const shopPaymentsTable: <inferred>`
334
346
  - `type ShopPaymentStatus = 'pending' | 'paid' | 'failed' | 'refunded' | 'cancelled'`
335
347
  - `const shopProductsTable: <inferred>`
@@ -368,6 +380,8 @@ Tags:
368
380
  - `const shopOrderItemsTable: <inferred>`
369
381
  - `const shopOrdersTable: <inferred>`
370
382
  - `const shopOrderStatusHistoryTable: <inferred>`
383
+ - `type ShopPaymentKind = 'full' | 'deposit' | 'balance'`
384
+ - `const shopPaymentKindEnum: <inferred>`
371
385
  - `const shopPaymentsTable: <inferred>`
372
386
  - `type ShopPaymentStatus = 'pending' | 'paid' | 'failed' | 'refunded' | 'cancelled'`
373
387
  - `const shopProductsTable: <inferred>`
@@ -397,19 +411,26 @@ Tags:
397
411
  - `interface CouponRef`
398
412
  - `type Currency = 'PLN'`
399
413
  - `defineShop(config: ShopConfig): ResolvedShopConfig`
414
+ - `type DepositAmount = | { type: 'percent'; value: number } | { type: 'amount'; value: number }` — Deposit amount specifier. `percent` charges `floor(base * value / 100)` of
415
+ - `filterUpcoming(variants: T[], config: VariantExpiryConfig | null, now?: Date): T[]` — Return the subset of `variants` that have not yet expired. Order is
400
416
  - `type GeowidgetConfigPreset = 'parcelcollect' | 'parcelsend' | 'parcelcollect247' | string`
401
417
  - `type I18nText = { [lang: string]: string; }`
402
418
  - `inpostAdapter(opts: InpostAdapterOptions): CarrierAdapter`
403
419
  - `interface InpostAdapterOptions`
404
420
  - `type InpostEnvironment = 'production' | 'sandbox'` — InPost Points API — public, no auth required. Used to validate that a parcel
405
421
  - `interface InpostSenderAddress`
422
+ - `interpolateTemplate(template: string, vars: Record<string, unknown>, locale: string): string` — String interpolation engine used by `defineShop({ variantLabel.template })`
423
+ - `class InvalidVariantAttributesError`
424
+ - `isVariantExpired(variant: VariantLike, config: VariantExpiryConfig | null, now: Date = new Date(Date.now())): boolean` — Check whether a variant has expired under the given config. Fail-open by
406
425
  - `manualAdapter(opts: ManualPaymentAdapterOptions = {}): PaymentAdapter` — Manual payment adapter — order goes to awaitingPayment, admin marks as paid later
407
426
  - `interface OrderRef`
408
427
  - `type OrderStatus = | 'new' | 'awaitingPayment' | 'paid' | 'preparing' | 'sent' | 'done' | 'cancelled' | 'paymentReje...`
428
+ - `interface PartialPayment` — Persisted partial-payment summary on `order.partialPayment` when the order
409
429
  - `interface PaymentAdapter`
410
430
  - `interface PaymentCreateContext`
411
431
  - `interface PaymentCreateResult`
412
432
  - `interface PaymentEvent`
433
+ - `type PaymentPolicy = { type: 'full' } | { type: 'deposit'; depositAmount: DepositAmount }` — Per-product payment policy. `full` (default) charges the full order total
413
434
  - `interface PaymentRefundInput`
414
435
  - `interface PaymentRefundResult`
415
436
  - `payuAdapter(opts: PayuAdapterOptions): PaymentAdapter`
@@ -422,6 +443,18 @@ Tags:
422
443
  - `interface ShopFeatures`
423
444
  - `stripeAdapter(opts: StripeAdapterOptions): PaymentAdapter` — Stripe payment adapter (Checkout Session flow).
424
445
  - `interface StripeAdapterOptions`
446
+ - `type VariantAttribute = | VariantAttributeText | VariantAttributeNumber | VariantAttributeDatetime | VariantAttributeSele...` — Schema descriptor for one product-variant attribute (city, startsAt, …).
447
+ - `interface VariantAttributeBoolean`
448
+ - `interface VariantAttributeDatetime`
449
+ - `interface VariantAttributeEntry`
450
+ - `interface VariantAttributeImage`
451
+ - `interface VariantAttributeNumber`
452
+ - `interface VariantAttributeSelect`
453
+ - `interface VariantAttributeSlug`
454
+ - `interface VariantAttributeText`
455
+ - `class VariantExpiredError` — Thrown by `createOrderFromCart` / cart-add HTTP guard when a referenced
456
+ - `interface VariantExpiryConfig` — Opt-in storefront filter that hides variants whose datetime attribute
457
+ - `interface VariantLabelConfig` — Template used to auto-generate `variant.name` from `variantAttributes`.
425
458
 
426
459
  ### `includio-cms/shop/client`
427
460
 
@@ -440,11 +473,13 @@ Tags:
440
473
  - `type OrderState = OrderStateImpl`
441
474
  - `const OrderStatus: LegacyComponentType`
442
475
  - `interface OrderStatusLabels`
476
+ - `interface PayBalanceResult` — Result of `orders.payBalance` — initiates a payment session for the
443
477
  - `interface RefreshPaymentResult`
444
478
  - `interface RetryPaymentResult`
445
479
  - `interface ShippingMethodPublic`
446
480
  - `interface ShopClient` — Headless shop SDK surface returned by `createShopClient()`.
447
481
  - `interface ShopClientOptions` — Options for `createShopClient()`.
482
+ - `interface VariantPublic` — Public-facing variant row returned by `client.products.listUpcoming`.
448
483
 
449
484
  ### `includio-cms/shop/http`
450
485
 
@@ -484,3 +519,8 @@ Tags:
484
519
 
485
520
  - `interface CustomFieldDefinition` — Defines a custom field type contributed by a plugin.
486
521
  - `interface PluginConfig` — CMS plugin configuration — register custom field types and CRUD lifecycle hooks.
522
+
523
+ ### `includio-cms/shop/http`
524
+
525
+ - `createBalanceHandler(): { GET: RequestHandler; POST: RequestHandler }`
526
+ - `createUpcomingVariantsHandler(): { GET: RequestHandler }` — Build a SvelteKit `GET` handler for upcoming variants of a single product.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,71 @@
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.27.0 — 2026-05-29
7
+
8
+ Shop platformowy — Fazy 1 + 2 + 3 + 4 + 5: schema-driven `variantAttributes` (typed `defineShop` schema, Zod walidacja, GIN index helper, ts-gen typed `variant.attributes`) + admin renderer per attribute type + `variantLabel.template` interpolacja z admin auto-prefill nazwy wariantu + `variantExpiry` opt-in (storefront filter, cart/checkout guard, admin "Zakończony" badge) + `paymentPolicy` A-lite (deposit + balance + signed token + refund per kind + admin balance link + per-kind refund dialog). Faza 6 envet pilot (consumer-side end-to-end QA: szkolenia variants + deposit flow + balance link — nie wymaga zmian w core). Post-Faza-5 hot-fixy uwzględnione w tym wydaniu (single release, nie patch): datetime walidator z offsetem, auto-detect deposit kind przy admin manual mark-paid, admin paymentPolicy UI + preserve-on-undefined w upsert. Additive only — żadnych breaking zmian. Plan: `/Users/patryk/.claude/plans/implementacja-ariacms-0-27-partitioned-kahn.md`.
9
+
10
+ ### Added
11
+ - `defineShop({ variantAttributes })` (`includio-cms/shop`) — typed schema dla atrybutów wariantu: `text | number | datetime | select | boolean | image | entry | slug` z polami `{ type, label, required?, indexable?, options?, entryCollection? }`. Zostaje jedno źródło prawdy: napędza Zod walidację, ts-gen typings, GIN index utility oraz renderer admin form.
12
+ - `VariantAttribute` + per-type interfejsy (`VariantAttributeText/Number/Datetime/Select/Boolean/Image/Entry/Slug`) wyeksportowane jako `@public` z `includio-cms/shop`.
13
+ - `InvalidVariantAttributesError` (code `INVALID_VARIANT_ATTRIBUTES`) — rzucany przez `upsertShopData` gdy `variant.attributes` nie pasują do schemy. Eksport publiczny z `includio-cms/shop`.
14
+ - `runtime/types.ts` — pole `shop` w entry typings generuje teraz typed `variant.attributes` (`{ city: string; startsAt: string; online?: boolean; level: 'basic' | 'advanced' }`) zamiast `Record<string, string>` gdy `defineShop` ma `variantAttributes`. Brak `variantAttributes` = poprzednie `Record<string, string> | null` (backwards-compatible).
15
+ - `createVariantAttributeIndexes(attrs)` w `$lib/db-postgres/schema/shop/productVariant.ts` — zwraca `CREATE INDEX IF NOT EXISTS shop_variant_attr_<key>_gin_idx ON shop_product_variants USING gin ((attributes->'<key>'))` DDL per atrybut z `indexable: true`. Wymusza safe SQL identifier dla klucza (`[A-Za-z][A-Za-z0-9_]*`).
16
+ - `applyVariantAttributeIndexes(shop, db)` w `$lib/shop/server/init.ts` — idempotentny serialized applier indeksów GIN. `initCMS()` odpala go automatycznie fire-and-forget gdy shop jest skonfigurowany (zero ręcznych hooków u konsumenta). `cms.shopInitPromise` wystawiony jako `@internal` punkt synchronizacji dla testów.
17
+ - `VariantAttributeRenderer.svelte` (NEW, `$lib/admin/components/variant-form/`) — switch po `attr.type`, deleguje do field components z `$lib/admin/components/fields/` (text/number/datetime/select/boolean) lub renderuje prosty Input dla slug/image/entry (pełny picker UI dla image/entry defer post-1.0).
18
+ - `shop-field.svelte` (admin variant editor) — variant row pokazuje siatkę atrybutów (responsywna 1/2-col) ponad legacy polami (nazwa/SKU/delta/stock) gdy `shopConfig.variantAttributes` ma wpisy. Brak attrs = legacy behavior bez zmian. Hydratacja + save propaguje `variant.attributes` jako `Record<string, unknown>`.
19
+ - `getShopConfig` remote (admin) — response zawiera teraz `variantAttributes` z `ResolvedShopConfig`, dzięki czemu `shop-field` może lokalnie renderować typed atrybuty bez dodatkowego round-tripa.
20
+ - `upsertShopDataForEntry` (admin remote command) — input schema dla `variant.attributes` zluzowany do `z.record(z.string(), z.unknown())`; typed shape egzekwowany serwerowo przez `validateVariantAttributes` (jedno źródło prawdy).
21
+ - `defineShop({ variantLabel: { template } })` (`includio-cms/shop`) — opcjonalny szablon nazwy wariantu w składni `{key|filter:arg}`. Filtry: `date` (long/medium/short), `currency` (kod waluty, domyślnie PLN), `uppercase`. Nieznany klucz → pusty string + dev warn; nieznany filtr → passthrough; malformed template → surowy template + warn.
22
+ - `interpolateTemplate(template, vars, locale)` (`includio-cms/shop`, `@public`) — silnik interpolacji wykorzystywany wewnętrznie przez admin do pre-fill `variant.name`, dostępny też publicznie dla konsumentów (np. storefront-side derived labels).
23
+ - `VariantLabelConfig` eksportowany jako `@public` typ z `includio-cms/shop`.
24
+ - `getShopConfig` remote response zawiera teraz `variantLabel: shop.variantLabel ?? null`.
25
+ - `shop-field.svelte` (admin variant editor) — gdy `variantLabel.template` zdefiniowany, pole `Nazwa` auto-wypełnia się z interpolacji `variantAttributes` (locale `pl` w 0.27; multi-locale defer post-1.0). Per-variant dirty flag: po hydratacji z niepustym `name` lock = aktywny (nie nadpisujemy editor decision); ręczna edycja w polu `Nazwa` (`oninput`) trwale wyłącza pre-fill dla wariantu w bieżącej sesji.
26
+ - `defineShop({ variantExpiry: { source, offsetDays } })` (`includio-cms/shop`, `@public`) — opt-in filtr wariantów time-bound (events, courses, szkolenia). `source` = klucz atrybutu typu `datetime`; `offsetDays` przesuwa cut-off (`0` = expire dokładnie gdy datetime hits now, `1` = jeden dzień grace, `-1` = jeden dzień przed). Globalny config — brak per-product override.
27
+ - `isVariantExpired(variant, config, now?)` + `filterUpcoming(variants, config, now?)` (`includio-cms/shop`, `@public`) — fail-open helpers: `null` config / brak source attribute / non-string / malformed datetime → variant traktowany jako upcoming (z `console.warn` dla malformed w dev). `filterUpcoming` zachowuje order; sort source-asc realizowany na poziomie HTTP / admin osobno.
28
+ - `VariantExpiredError` (code `VARIANT_EXPIRED`, `variantId`) — eksport `@public`. Rzucany przez `createOrderFromCart` w `server/orders.ts` przed stock reservation (idempotent guard).
29
+ - `POST /api/shop/cart` (add) — preflight `VARIANT_EXPIRED` guard: kiedy `variantExpiry` jest skonfigurowany i variant ma expired source attribute, response 400 JSON `{ error: "VARIANT_EXPIRED", variantId }` bez aktualizacji cart cookie.
30
+ - `createUpcomingVariantsHandler` (`includio-cms/shop/http`, `@experimental`) — nowy GET endpoint `/api/shop/products/[id]/variants/upcoming`. Zwraca `{ items: VariantPublic[] }` przefiltrowanych przez `filterUpcoming` + posortowanych ascendingly po `attributes.<source>` (Date.parse) gdy `variantExpiry` ustawiony, w insertion order gdy brak.
31
+ - `client.products.listUpcoming(productId)` + `VariantPublic` (`includio-cms/shop/client`, `@public`) — nowy namespace SDK. Zwraca upcoming variants per produkt; zero-config passthrough kiedy `variantExpiry` nieustawiony.
32
+ - `getShopConfig` remote (admin) — response zawiera teraz `variantExpiry: shop.variantExpiry ?? null`.
33
+ - `shop-field.svelte` (admin variant editor) — gdy `variantExpiry` skonfigurowany: per-variant badge "Zakończony" (greyed-out, `bg-muted text-muted-foreground` z border), checkbox filter "Pokaż zakończone (N)" w toolbarze wariantów (default off, ukryty gdy zero expired), opacity-60 na expired row, sort wariantów po source ASC (closer terms first). Brak config = zero zmian w UI.
34
+ - `PaymentPolicy` + `DepositAmount` + `PartialPayment` (`includio-cms/shop`, `@public`) — per-product policy `{ type: "full" } | { type: "deposit", depositAmount: { type: "percent" | "amount", value } }` ustawiana w `ShopFieldData.paymentPolicy`. Cart deposit-aware: order zachowuje pełen `totalGross`, `partialPayment.balanceAmount` = co zostaje do dopłaty, `balanceOwed = true` po zapłaconym depozycie. Walidacja: percent w (0, 100], amount = positive integer (minor units). `validatePaymentPolicy` wymusza to przy upsert produktu.
35
+ - `resolvePaymentAmount(policy, lineGross)` + `computeDepositAmount(spec, base)` (`$lib/shop/server/payment-policy.ts`, server-internal — nie eksportowane z publicznego entry pointu) — silnik resolver per linia. `percent` flooruje `base * value / 100`; `amount` klampuje do `base`. Null / `full` policy → pełen `lineGross`, `kind = "full"`, balance 0. 15 spec testów + 10 integration.
36
+ - `MixedPaymentPolicyError` (code `MIXED_PAYMENT_POLICY`) — `createOrderFromCart` odrzuca cart z więcej niż jednym produktem gdy którykolwiek z nich ma deposit policy. Decyzja: deposit orders span ONE product (refund-per-kind + balance link unambiguous). Cart all-`full` mixed-product nadal działa.
37
+ - `shop_orders.partial_payment jsonb + balance_owed boolean` — nowe kolumny. `shop_payments.kind enum ("full" | "deposit" | "balance")` z default `"full"` — back-compat dla istniejących płatności. `shop_products.payment_policy jsonb` przechowuje per-product policy.
38
+ - `shop_payments` zaczyna być aktywnie wypełniane: `checkout-handler` po `adapter.createPayment` insert payment row z odpowiednim `kind` (full lub deposit) + `providerRef`. Webhook handler matchuje (provider, providerRef) → branchuje per kind: `full`/`deposit` przepuszcza `updateOrderStatus(paid, paymentKind)`, `balance` bypasses status flow i woła `markBalancePaid` (stock przy depozycie już permanentnie potwierdzony).
39
+ - `createOrderFromCart` CreateOrderResult: nowe pola `amountToPay` (deposit-aware kwota do pobrania na checkout) + `paymentKind` (`"full" | "deposit"`). `checkout-handler` przekazuje `amountToPay` do adaptera przez OrderRef.totalGross override — adaptery PayU/Stripe/manual działają bez zmian.
40
+ - `generateBalanceToken` + `verifyBalanceToken` (`includio-cms/shop/server`, `@internal`) — HMAC-SHA256 nad JSON payload `{orderId, type: "balance"}`, encoding `base64url(payload).base64url(sig)`. Deterministic per (orderId, secret) — brak rotation, brak exp, server-side invalidation przez `order.balanceOwed === true`. Secret = `INCLUDIO_BALANCE_TOKEN_SECRET` env (≥16 chars, hard-fail przy brak gdy deposit policy aktywne). 8 spec testów (roundtrip, tampered, wrong orderId/secret, malformed, deterministic).
41
+ - `createBalanceSession(orderNumber, token, ctx?)` (`includio-cms/shop/server`, `@experimental`) — customer flow: verify token + `balanceOwed`, reuse `order.paymentMethod` adapter, call `adapter.createPayment` z `amount = partialPayment.balanceAmount`, insert payment row `kind: "balance"`, mirror providerRef na order. `markBalancePaid` (idempotent) clears balanceOwed + bumps `paidAmount` do `totalGross`.
42
+ - `createBalanceHandler` (`includio-cms/shop/http`, `@experimental`) — GET `/api/shop/orders/[number]/balance?token=...` zwraca minimalny widok ({orderNumber, currency, totalGross, amountToPay, paidAmount, paymentMethod, language}); POST inicjuje payment session ({status, redirectUrl, requiresPaymentRedirect}). 403 przy złym tokenie lub wyczyszczonym `balanceOwed`.
43
+ - `client.orders.payBalance(orderNumber, token)` + `PayBalanceResult` (`includio-cms/shop/client`, `@public` / `@experimental`) — REST wrapper dla customer flow dopłaty.
44
+ - `refundOrder({ kind?, releaseStock?, ... })` (`includio-cms/shop/server`) — per-kind routing. `kind: "deposit" | "balance" | "full"` matchuje konkretny payment row; kwota cap = `row.amount - prior refunds`. Domyślnie `kind` = `"full"` z heurystyką: pojedynczy paid row → użyj go, multi-row → wybierz `full`, brak rows → legacy fallback do `order.paymentProviderRef`. Refund row link do `paymentId` dla per-row audit. `getCollectedAmount` decyduje o transition do `refunded` (sum paid rows lub `totalGross` dla legacy). `releaseStock: true` (opt-in) reinkrementuje variant.stock per order item. 6 integration spec.
45
+ - `refundOrderCmd` (admin remote) dostaje `kind` + `releaseStock` w schemacie input. `refund-dialog.svelte` — radio "Która płatność?" (deposit/balance) widoczne gdy `hasPartial`, balance radio disabled gdy `balanceOwed=true` (nic do refund), checkbox "Zwolnij stan magazynowy".
46
+ - `generateBalanceLinkForOrder` (admin remote) — auth-protected, requires `INCLUDIO_BALANCE_TOKEN_SECRET`, builds URL z configured `orderViewUrl` + `?balance=1&balanceToken=...`. Zwraca `{ success, url, balanceAmount }`.
47
+ - `shop-order-detail-page.svelte` — nowa sekcja "Płatność dzielona" widoczna gdy `order.partialPayment != null`: Plum badge "Czeka na dopłatę" przy `balanceOwed=true` / success badge "Opłacone w całości" po dopłacie, paid/owed/total breakdown z `formatCentsPrice`, button "Wyślij link do dopłaty" generuje token + kopiuje URL do clipboard, wyświetla URL w `<code>` blocku jako fallback dla "skopiuj ręcznie".
48
+
49
+ ### Fixed
50
+ - `variant-attributes.ts` — `datetime` walidator używa teraz `z.string().datetime({ offset: true })`: akceptuje pełen ISO 8601 (`Z` UTC z admin `DatetimeField.toISOString()` ORAZ offset `+02:00` z importów/seedów/zewnętrznych systemów). Wcześniej tryb default odrzucał offset, blokując offsetowe wartości atrybutów.
51
+ - `updateOrderStatus` (`server/orders.ts`) — admin manual mark-paid auto-detektuje `deposit` kind: gdy `order.partialPayment.kind === "deposit"` + `!balanceOwed` + `paidAt == null`, `effectiveKind = "deposit"`. Wcześniej `updateOrderStatusCmd` (w przeciwieństwie do webhooka) nie przekazywał `paymentKind`, więc admin oznaczając deposit order jako paid pomijał deposit logic (`paidAmount` nie był bumpowany, `balanceOwed` nie był ustawiany). Pozwala testować pełny deposit flow przez manual adapter bez sandbox PayU; webhook bez zmian (zawsze pass explicit kind).
52
+ - admin `paymentPolicy` — trzy bugi powodujące kasowanie deposit policy przy każdym save naprawione razem: (1) `upsertShopData` (`server/shop-data.ts`) — `undefined` zachowuje istniejącą policy, tylko `null` kasuje (było `?? null` nadpisujące przy każdym upsert innego pola); (2) `shopDataInputSchema` (admin remote) — dodany discriminated union `paymentPolicy` (full/deposit + percent/amount), wcześniej command odrzucał pole; (3) `shop-field.svelte` — sekcja "Zasady płatności" (radio full/deposit + warunkowe inputy percent/amount + live preview kwoty depozytu), wcześniej brak UI do ustawienia deposit.
53
+
54
+ ## 0.26.1 — 2026-05-28
55
+
56
+ Nowy typ pola `icon` + IconSetPlugin (zestaw ikon dostarczany przez aplikację, dialog z gridem + search). Scaffolder fix + DataTable polish: scaffold admin używa publicznych entry points; pierwsza kolumna tabeli bez sticky-first; checkbox visual wraca do 16 px z transparent hit-area 24×24 (WCAG 2.5.5).
57
+
58
+ ### Added
59
+ - Nowy typ pola `icon` (`IconField` @public, `src/lib/types/fields.ts`) — kompaktowy kafelek 96×96 w formularzu (ikona + nazwa, X do wyczyszczenia) otwiera dialog z gridem responsive 3–6 kolumn, wyszukiwarką (filtruje po `key`, lokalizowanym `label`, opcjonalnych `keywords`) i przyciskami Anuluj / OK. Klik na kafelku w dialogu zaznacza, OK zatwierdza; dwuklik = szybki zapis. Stan "missing" (wartość w DB nieobecna w bibliotece) pokazuje placeholder + przyjazne ostrzeżenie zamiast resetować wartość — zgodne z ToV "informuj, nie strasz". i18n PL/EN, `role="listbox"`/`role="option"`/`aria-selected`, LiveRegion z liczbą wyników.
60
+ - `IconSetPlugin` / `IconDefinition` (`@public`, `src/lib/types/plugins.ts`) — nowy rodzaj pluginu rejestrujący nazwany zestaw ikon: `{ slug, type: 'icon-set', icons: Record<key, { component, label: Localized, keywords?: string[] }> }`. Komponenty Svelte (eager import, tree-shakable — np. `Brain` z `@lucide/svelte`) ŻYJĄ w pluginie, bo `cms.config.ts` jest server-only i Zod-validated. `Plugin = PluginConfig | IconSetPlugin` + `isIconSetPlugin` type guard.
61
+ - `buildIconSetMap(...plugins)` (`includio-cms/admin`) + nowy prop `iconSets?: Map<string, IconSetPlugin>` na `AdminLayout` — konsumencki `+layout.svelte` admina przekazuje plugin instancje na klienta (funkcji komponentów nie da się serializować przez SvelteKit data). `getIconSets()` / `resolveIconSet(slug?)` w `includio-cms/admin` — runtime lookup używany przez `icon-field.svelte` przez Svelte context.
62
+ - `CMS.iconSets: Map<slug, IconSetPlugin>` agregowane przy starcie z `cms.config.plugins` przez `isIconSetPlugin` guard; duplikat slug → throw. Generator TS mapuje `icon` → `string` w wygenerowanym `types.ts`; Zod runtime → `z.string()` (analogicznie do `slug` field).
63
+
64
+ ### Fixed
65
+ - `scaffold admin` generuje `src/routes/admin/api/[...path]/+server.ts` z `import { createAdminApiHandler } from 'includio-cms/sveltekit/server'`. Wcześniej generowany import `includio-cms/admin/api/handler` rzucał Vite `Missing "./admin/api/handler" specifier in "includio-cms" package` przy starcie dev/build — `admin/api/handler` nie ma w `exports` (v1.0 frozen 18 ścieżek).
66
+ - `scaffold admin` generuje `src/routes/admin/api/rest/[...restPath]/+server.ts` z `import { createRestApiHandler } from 'includio-cms/sveltekit/server'` (analogicznie — był broken w 0.20.0+ ale scaffolder nie zaktualizowany).
67
+ - `includio-cms/sveltekit/server` re-eksportuje teraz `createAdminApiHandler` (obok istniejącego `createRestApiHandler`).
68
+ - `DataTable`: revert sticky-first column (z 0.26.0 / S10c) — pierwsza kolumna scrolluje razem z resztą, bez wyróżnienia kolorystycznego. Usunięty prop `stickyFirstColumn` i powiązany CSS.
69
+ - `Checkbox` primitive: visual wraca do 16×16 px (z 24×24 w 0.26.0 / S10b), klikalny obszar utrzymany na ≥24×24 przez transparent `::after` pseudo-element (`after:-inset-1`) — WCAG 2.5.5 target-size AA dalej spełniony, lepszy wygląd zgodny z proporcjami pól tekstowych.
70
+
6
71
  ## 0.26.0 — 2026-05-26
7
72
 
8
73
  UI Normalization → release readiness: slug regex camelCase + S8 detail/edit + S9 fields a11y polish + S10 a11y final audit + S10b/S10c mobile responsiveness full pass (sticky first column DataTable, Dialog fullscreenMobile prop, media library Sheet drawers, entry hybrid preview drawer toggle, dashboard mobile layout flip). 43/43 mobile Playwright pass (3 viewports × 12 widoków, 0 critical axe).
package/DOCS.md CHANGED
@@ -1,4 +1,4 @@
1
- # Includio CMS Documentation (v0.26.0)
1
+ # Includio CMS Documentation (v0.27.0)
2
2
 
3
3
  > This file is auto-generated from the docs site. For the latest version, update the package.
4
4
 
package/ROADMAP.md CHANGED
@@ -21,6 +21,14 @@
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
22
  - [ ] `[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
23
  - [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
+ - [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 -->
25
+ - [x] Faza 1 — Schema-driven `variantAttributes` (typed `defineShop` schema, Zod builder, GIN index util, ts-gen typed `variant.attributes`, `INVALID_VARIANT_ATTRIBUTES` guard w `upsertShopData`). <!-- files: src/lib/shop/types.ts, src/lib/shop/variant-attributes.ts, src/lib/shop/server/{shop-data,init}.ts, src/lib/db-postgres/schema/shop/productVariant.ts, src/lib/core/server/generator/fields.ts -->
26
+ - [x] Faza 2 — Admin dynamic variant attribute form renderer (`VariantAttributeRenderer.svelte`, integracja w `shop-field.svelte`, `getShopConfig` remote response zwraca `variantAttributes`, auto-apply GIN indexes na `initCMS()`). <!-- files: src/lib/admin/components/variant-form/VariantAttributeRenderer.svelte, src/lib/admin/components/fields/shop-field.svelte, src/lib/admin/remote/shop.remote.ts, src/lib/core/cms.ts, src/lib/shop/server/init.ts, src/stories/fields/shop-with-attributes.stories.svelte -->
27
+ - [x] Faza 3 — `variantLabel.template` interpolation + admin pre-fill (`interpolateTemplate` engine z filtrami `date`/`currency`/`uppercase`, `ShopConfig.variantLabel`, `defineShop` przepuszcza, `getShopConfig` remote zwraca, `shop-field.svelte` auto-prefills `variant.name` per locale `pl` z per-variant dirty flag — locked po hydratacji jeśli name istnieje + po `oninput` w name). <!-- files: src/lib/shop/template.ts, src/lib/shop/types.ts, src/lib/shop/index.ts, src/lib/admin/remote/shop.remote.ts, src/lib/admin/components/fields/shop-field.svelte, src/stories/fields/shop-with-attributes-story-demo.svelte, src/stories/fields/shop-with-attributes.stories.svelte -->
28
+ - [x] Faza 4 — `variantExpiry` opt-in (storefront filter + cart-add guard + checkout guard + admin "Zakończony" badge + filter toggle + source-asc sort + upcoming HTTP endpoint + `client.products.listUpcoming` SDK). <!-- files: src/lib/shop/expiry.ts, src/lib/shop/types.ts, src/lib/shop/index.ts, src/lib/shop/server/orders.ts, src/lib/shop/http/{cart-handler,upcoming-handler,index}.ts, src/lib/shop/client/index.ts, src/lib/admin/remote/shop.remote.ts, src/lib/admin/components/fields/shop-field.svelte -->
29
+ - [x] Faza 5 — `paymentPolicy` A-lite (deposit + balance + signed token + refund per kind, admin balance link + refund-per-kind dialog, shop_payments rows tagged by kind). <!-- files: src/lib/shop/types.ts, src/lib/shop/server/{payment-policy,balance-payment,payments,orders,refund,shop-data}.ts, src/lib/shop/http/{balance-handler,checkout-handler,webhook-handler,index}.ts, src/lib/shop/client/index.ts, src/lib/db-postgres/schema/shop/{order,payment,product}.ts, src/lib/admin/{remote/shop.remote.ts,client/shop/{shop-order-detail-page,refund-dialog}.svelte} -->
30
+ - [x] Faza 6 — envet integration (pilot consumer) — szkolenia variants + deposit flow + balance link end-to-end QA. <!-- files: envet repo src/lib/cms/cms.config.ts, src/routes/api/shop/, src/routes/(website)/{szkolenia,koszyk}/, src/lib/components/{CartIcon,AddToCartModal,CoursePage}.svelte, cli/seed.ts -->
31
+ - [x] Faza 7 — Release artifacts (changelog, ROADMAP cleanup, API.md, build) — 3 post-Faza-5 hotfixy w 0.27.0 (single release). <!-- files: src/lib/updates/0.27.0/index.ts, ROADMAP.md, CHANGELOG.md, API.md, MIGRATION-... -->
24
32
  - [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/ -->
25
33
 
26
34
  ## v1.x — Post-v1.0 deferred