includio-cms 0.25.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.
- package/API.md +97 -4
- package/CHANGELOG.md +118 -0
- package/DOCS.md +1 -1
- package/README.md +2 -0
- package/ROADMAP.md +14 -0
- package/dist/admin/auth-client.d.ts +42 -42
- package/dist/admin/client/account/lang.d.ts +1 -0
- package/dist/admin/client/account/lang.js +4 -2
- package/dist/admin/client/account/profile-section.svelte +2 -2
- package/dist/admin/client/account/security-section.svelte +27 -4
- package/dist/admin/client/account/sessions-section.svelte +1 -1
- package/dist/admin/client/admin/admin-after-login-layout-content.svelte +1 -1
- package/dist/admin/client/admin/admin-layout.svelte +12 -2
- package/dist/admin/client/admin/admin-layout.svelte.d.ts +2 -1
- package/dist/admin/client/admin/dashboard-page.svelte +34 -10
- package/dist/admin/client/collection/bulk-actions-bar.svelte +86 -44
- package/dist/admin/client/collection/bulk-actions-bar.svelte.d.ts +3 -1
- package/dist/admin/client/collection/collection-entries.svelte +52 -36
- package/dist/admin/client/collection/collection-entries.svelte.d.ts +3 -0
- package/dist/admin/client/collection/collection.svelte +28 -14
- package/dist/admin/client/collection/collection.svelte.d.ts +3 -0
- package/dist/admin/client/collection/data-table.svelte +240 -130
- package/dist/admin/client/collection/data-table.svelte.d.ts +9 -0
- package/dist/admin/client/collection/date-cell.svelte +4 -4
- package/dist/admin/client/collection/row-actions.svelte +2 -1
- package/dist/admin/client/collection/sortable-header.svelte +33 -9
- package/dist/admin/client/collection/state-display.svelte +102 -0
- package/dist/admin/client/collection/state-display.svelte.d.ts +12 -0
- package/dist/admin/client/collection/status-badge.svelte +99 -11
- package/dist/admin/client/collection/status-badge.svelte.d.ts +15 -1
- package/dist/admin/client/collection/table-pagination.svelte +21 -6
- package/dist/admin/client/collection/table-toolbar.svelte +105 -80
- package/dist/admin/client/collection/table-toolbar.svelte.d.ts +11 -8
- package/dist/admin/client/entry/entry-form.svelte +36 -11
- package/dist/admin/client/entry/entry-form.svelte.d.ts +1 -0
- package/dist/admin/client/entry/entry-header.svelte +22 -15
- package/dist/admin/client/entry/entry-header.svelte.d.ts +1 -0
- package/dist/admin/client/entry/entry.svelte +269 -165
- package/dist/admin/client/entry/header/a11y-header-badge.svelte +47 -0
- package/dist/admin/client/entry/header/a11y-header-badge.svelte.d.ts +8 -0
- package/dist/admin/client/entry/header/publish-panel.svelte +69 -13
- package/dist/admin/client/entry/header/save-indicator.svelte +57 -28
- package/dist/admin/client/entry/header/save-indicator.svelte.d.ts +1 -0
- package/dist/admin/client/entry/header/status-badge.svelte +60 -15
- package/dist/admin/client/entry/header/status-badge.svelte.d.ts +1 -2
- package/dist/admin/client/entry/header/version-history-sheet.svelte +1 -1
- package/dist/admin/client/entry/hybrid/hybrid-layout.svelte +74 -23
- package/dist/admin/client/entry/hybrid/hybrid-preview.svelte +1 -1
- package/dist/admin/client/entry/utils.d.ts +14 -0
- package/dist/admin/client/entry/utils.js +28 -0
- package/dist/admin/client/form/form-submission/form-submission.svelte +2 -2
- package/dist/admin/client/form/form-submissions.svelte +143 -194
- package/dist/admin/client/form/form-submissions.svelte.d.ts +2 -0
- package/dist/admin/client/login/lang.d.ts +3 -0
- package/dist/admin/client/login/lang.js +10 -4
- package/dist/admin/client/login/login-form.svelte +8 -1
- package/dist/admin/client/login/reset-password-page.svelte +24 -3
- package/dist/admin/client/login/schema.d.ts +14 -2
- package/dist/admin/client/login/schema.js +19 -8
- package/dist/admin/client/maintenance/maintenance-page.svelte +16 -17
- package/dist/admin/client/media/media-page.svelte +1 -1
- package/dist/admin/client/shop/coupon-edit-page.svelte +117 -13
- package/dist/admin/client/shop/coupon-form.svelte +282 -138
- package/dist/admin/client/shop/coupon-form.svelte.d.ts +1 -9
- package/dist/admin/client/shop/coupon-new-page.svelte +40 -10
- package/dist/admin/client/shop/coupon-new-page.svelte.d.ts +2 -17
- package/dist/admin/client/shop/coupon-schema.d.ts +28 -0
- package/dist/admin/client/shop/coupon-schema.js +53 -0
- package/dist/admin/client/shop/coupons-list-page.svelte +262 -118
- package/dist/admin/client/shop/coupons-list-page.svelte.d.ts +16 -1
- package/dist/admin/client/shop/refund-dialog.svelte +37 -1
- package/dist/admin/client/shop/refund-dialog.svelte.d.ts +3 -0
- package/dist/admin/client/shop/shipping-method-edit-page.svelte +108 -59
- package/dist/admin/client/shop/shipping-method-form.svelte +36 -9
- package/dist/admin/client/shop/shipping-method-new-page.svelte +44 -13
- package/dist/admin/client/shop/shipping-methods-list-page.svelte +101 -59
- package/dist/admin/client/shop/shop-order-detail-page.svelte +220 -84
- package/dist/admin/client/shop/shop-orders-list-page.svelte +302 -152
- package/dist/admin/client/shop/shop-orders-list-page.svelte.d.ts +18 -1
- package/dist/admin/client/shop/shop-products-list-page.svelte +355 -118
- package/dist/admin/client/shop/shop-products-list-page.svelte.d.ts +19 -1
- package/dist/admin/client/users/accept-invite-page.svelte +24 -3
- package/dist/admin/client/users/create-user-dialog.svelte +3 -8
- package/dist/admin/client/users/lang.d.ts +2 -0
- package/dist/admin/client/users/lang.js +4 -0
- package/dist/admin/client/users/pending-invitations.svelte +2 -9
- package/dist/admin/client/users/user-name-cell.svelte +20 -0
- package/dist/admin/client/users/user-name-cell.svelte.d.ts +9 -0
- package/dist/admin/client/users/user-role-badge.svelte +16 -0
- package/dist/admin/client/users/user-role-badge.svelte.d.ts +7 -0
- package/dist/admin/client/users/user-row-actions.svelte +72 -0
- package/dist/admin/client/users/user-row-actions.svelte.d.ts +20 -0
- package/dist/admin/client/users/user-sessions-sheet.svelte +2 -11
- package/dist/admin/client/users/users-page.svelte +283 -497
- package/dist/admin/client/users/users-page.svelte.d.ts +12 -1
- package/dist/admin/components/dashboard/form-submissions-widget.svelte +59 -74
- package/dist/admin/components/dashboard/recent-activity.svelte +17 -5
- package/dist/admin/components/dashboard/recent-entries.svelte +19 -7
- package/dist/admin/components/dialogs/confirmation-dialog.svelte +105 -0
- package/dist/admin/components/dialogs/confirmation-dialog.svelte.d.ts +13 -0
- package/dist/admin/components/fields/block-picker-modal.svelte +6 -0
- package/dist/admin/components/fields/blocks-field.svelte +46 -1
- package/dist/admin/components/fields/boolean-field.svelte +1 -1
- package/dist/admin/components/fields/field-renderer.svelte +29 -22
- package/dist/admin/components/fields/file-field.svelte +344 -30
- package/dist/admin/components/fields/icon-field.svelte +86 -0
- package/dist/admin/components/fields/icon-field.svelte.d.ts +8 -0
- package/dist/admin/components/fields/icon-picker-dialog.svelte +174 -0
- package/dist/admin/components/fields/icon-picker-dialog.svelte.d.ts +11 -0
- package/dist/admin/components/fields/media-field.svelte +16 -2
- package/dist/admin/components/fields/object-field.svelte +27 -7
- package/dist/admin/components/fields/radio-field.svelte +22 -0
- package/dist/admin/components/fields/relation-field.svelte +123 -97
- package/dist/admin/components/fields/relation-picker-dialog.svelte +2 -2
- package/dist/admin/components/fields/seo-field.svelte +60 -30
- package/dist/admin/components/fields/shop-field.svelte +219 -24
- package/dist/admin/components/fields/simple-array-field.svelte +321 -151
- package/dist/admin/components/fields/simple-array-field.svelte.d.ts +3 -0
- package/dist/admin/components/fields/slug-field.svelte +146 -21
- package/dist/admin/components/fields/text-field-wrapper.svelte +37 -20
- package/dist/admin/components/fields/text-field.svelte +7 -2
- package/dist/admin/components/fields/url-field-wrapper.svelte +10 -0
- package/dist/admin/components/fields/url-field.svelte +36 -23
- package/dist/admin/components/forms/form-error-summary.svelte +143 -0
- package/dist/admin/components/forms/form-error-summary.svelte.d.ts +27 -0
- package/dist/admin/components/layout/app-sidebar.svelte +7 -2
- package/dist/admin/components/layout/detail-page-shell.svelte +71 -0
- package/dist/admin/components/layout/detail-page-shell.svelte.d.ts +24 -0
- package/dist/admin/components/layout/lang.d.ts +5 -0
- package/dist/admin/components/layout/lang.js +10 -0
- package/dist/admin/components/layout/layout-renderer.svelte +71 -2
- package/dist/admin/components/layout/layout-renderer.svelte.d.ts +1 -0
- package/dist/admin/components/layout/layout-tabs.svelte +173 -0
- package/dist/admin/components/layout/layout-tabs.svelte.d.ts +24 -0
- package/dist/admin/components/layout/nav-breadcrumbs.svelte +25 -7
- package/dist/admin/components/layout/nav-collections.svelte +23 -36
- package/dist/admin/components/layout/nav-forms.svelte +19 -35
- package/dist/admin/components/layout/nav-main.svelte +3 -28
- package/dist/admin/components/layout/nav-search.svelte +70 -2
- package/dist/admin/components/layout/nav-section.svelte +77 -0
- package/dist/admin/components/layout/nav-section.svelte.d.ts +22 -0
- package/dist/admin/components/layout/nav-shop.svelte +3 -27
- package/dist/admin/components/layout/nav-singletons.svelte +16 -28
- package/dist/admin/components/layout/page-header.stories.svelte +93 -0
- package/dist/admin/components/layout/page-header.stories.svelte.d.ts +27 -0
- package/dist/admin/components/layout/page-header.svelte +68 -0
- package/dist/admin/components/layout/page-header.svelte.d.ts +17 -0
- package/dist/admin/components/layout/site-header.svelte +9 -0
- package/dist/admin/components/layout/site-header.svelte.d.ts +2 -17
- package/dist/admin/components/media/file/file-name-input.svelte +6 -2
- package/dist/admin/components/media/file/file-preview.svelte +130 -17
- package/dist/admin/components/media/file-upload.svelte +16 -7
- package/dist/admin/components/media/file-upload.svelte.d.ts +1 -0
- package/dist/admin/components/media/files-list.svelte +153 -53
- package/dist/admin/components/media/files-list.svelte.d.ts +1 -0
- package/dist/admin/components/media/media-library.svelte +577 -198
- package/dist/admin/components/media/media-library.svelte.d.ts +4 -0
- package/dist/admin/components/media/media-selector.svelte +4 -2
- package/dist/admin/components/media/media-selector.svelte.d.ts +1 -0
- package/dist/admin/components/media/tag-sidebar.svelte +4 -4
- package/dist/admin/components/tiptap/FigureNodeView.svelte +10 -0
- package/dist/admin/components/tiptap/bubble-menu.svelte +104 -0
- package/dist/admin/components/tiptap/bubble-menu.svelte.d.ts +19 -0
- package/dist/admin/components/tiptap/content-editor.svelte +28 -24
- package/dist/admin/components/tiptap/editor-toolbar.svelte +7 -7
- package/dist/admin/components/tiptap/extensions.js +5 -1
- package/dist/admin/components/tiptap/image-dialog.svelte +5 -1
- package/dist/admin/components/tiptap/link-dialog.svelte +2 -0
- package/dist/admin/components/tiptap/tiptap-editor.svelte +18 -20
- package/dist/admin/components/tiptap/video-dialog.svelte +1 -1
- package/dist/admin/components/variant-form/VariantAttributeRenderer.svelte +109 -0
- package/dist/admin/components/variant-form/VariantAttributeRenderer.svelte.d.ts +9 -0
- package/dist/admin/helpers/build-icon-set-map.d.ts +8 -0
- package/dist/admin/helpers/build-icon-set-map.js +16 -0
- package/dist/admin/helpers/index.d.ts +2 -0
- package/dist/admin/helpers/index.js +2 -0
- package/dist/admin/i18n/errors.d.ts +140 -0
- package/dist/admin/i18n/errors.js +151 -0
- package/dist/admin/remote/entry.remote.d.ts +59 -4
- package/dist/admin/remote/entry.remote.js +239 -62
- package/dist/admin/remote/shop.remote.d.ts +87 -48
- package/dist/admin/remote/shop.remote.js +70 -8
- package/dist/admin/shared/password-generate.d.ts +6 -0
- package/dist/admin/shared/password-generate.js +40 -0
- package/dist/admin/shared/password-schema.d.ts +6 -0
- package/dist/admin/shared/password-schema.js +10 -3
- package/dist/admin/state/icon-sets.svelte.d.ts +9 -0
- package/dist/admin/state/icon-sets.svelte.js +20 -0
- package/dist/admin/styles/admin.css +23 -6
- package/dist/admin/styles/tokens.md +244 -0
- package/dist/admin/utils/accordionActivation.d.ts +13 -0
- package/dist/admin/utils/accordionActivation.js +35 -0
- package/dist/admin/utils/entryLabel.d.ts +23 -0
- package/dist/admin/utils/entryLabel.js +51 -12
- package/dist/admin/utils/field-a11y.d.ts +29 -0
- package/dist/admin/utils/field-a11y.js +23 -0
- package/dist/admin/utils/fieldPathElement.d.ts +9 -0
- package/dist/admin/utils/fieldPathElement.js +18 -0
- package/dist/admin/utils/fileDisplay.d.ts +10 -0
- package/dist/admin/utils/fileDisplay.js +26 -0
- package/dist/admin/utils/flattenFormErrors.d.ts +19 -0
- package/dist/admin/utils/flattenFormErrors.js +102 -0
- package/dist/admin/utils/formatters.d.ts +12 -0
- package/dist/admin/utils/{formatDate.js → formatters.js} +23 -2
- package/dist/admin/utils/scrollWithin.d.ts +9 -0
- package/dist/admin/utils/scrollWithin.js +32 -0
- package/dist/admin/utils/tabActivation.d.ts +12 -0
- package/dist/admin/utils/tabActivation.js +24 -0
- package/dist/cli/scaffold/admin.js +2 -2
- package/dist/cms/runtime/schema.d.ts +1 -0
- package/dist/cms/runtime/schema.js +1 -0
- package/dist/cms/runtime/types.d.ts +80 -7
- package/dist/components/ui/accordion/accordion-content.svelte +17 -3
- package/dist/components/ui/accordion/accordion.stories.svelte +21 -1
- package/dist/components/ui/alert/alert.stories.svelte +14 -0
- package/dist/components/ui/alert-dialog/alert-dialog.stories.svelte +45 -0
- package/dist/components/ui/alert-dialog/alert-dialog.stories.svelte.d.ts +27 -0
- package/dist/components/ui/avatar/avatar.stories.svelte +27 -0
- package/dist/components/ui/badge/badge.stories.svelte +15 -0
- package/dist/components/ui/breadcrumb/breadcrumb.stories.svelte +47 -0
- package/dist/components/ui/breadcrumb/breadcrumb.svelte +1 -1
- package/dist/components/ui/button/button.stories.svelte +53 -6
- package/dist/components/ui/button/button.svelte +39 -5
- package/dist/components/ui/button/button.svelte.d.ts +4 -0
- package/dist/components/ui/button-group/button-group.stories.svelte +44 -0
- package/dist/components/ui/button-group/button-group.stories.svelte.d.ts +27 -0
- package/dist/components/ui/calendar/calendar.stories.svelte +36 -0
- package/dist/components/ui/calendar/calendar.stories.svelte.d.ts +27 -0
- package/dist/components/ui/card/card.stories.svelte +7 -0
- package/dist/components/ui/carousel/carousel.stories.svelte +43 -0
- package/dist/components/ui/carousel/carousel.stories.svelte.d.ts +27 -0
- package/dist/components/ui/checkbox/checkbox.stories.svelte +67 -0
- package/dist/components/ui/checkbox/checkbox.stories.svelte.d.ts +27 -0
- package/dist/components/ui/checkbox/checkbox.svelte +1 -1
- package/dist/components/ui/command/command.stories.svelte +18 -0
- package/dist/components/ui/data-table/data-table.stories.svelte +61 -0
- package/dist/components/ui/data-table/data-table.stories.svelte.d.ts +18 -0
- package/dist/components/ui/dialog/dialog-content.svelte +5 -0
- package/dist/components/ui/dialog/dialog-content.svelte.d.ts +2 -0
- package/dist/components/ui/dialog/dialog.stories.svelte +35 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu.stories.svelte +74 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu.stories.svelte.d.ts +27 -0
- package/dist/components/ui/field/field-context.svelte.d.ts +22 -0
- package/dist/components/ui/field/field-context.svelte.js +9 -0
- package/dist/components/ui/field/field-control.svelte +18 -0
- package/dist/components/ui/field/field-control.svelte.d.ts +8 -0
- package/dist/components/ui/field/field-description.svelte +12 -0
- package/dist/components/ui/field/field-error.svelte +14 -6
- package/dist/components/ui/field/field-label.svelte +10 -0
- package/dist/components/ui/field/field.stories.svelte +95 -9
- package/dist/components/ui/field/field.svelte +57 -0
- package/dist/components/ui/field/field.svelte.d.ts +2 -0
- package/dist/components/ui/field/index.d.ts +3 -1
- package/dist/components/ui/field/index.js +4 -2
- package/dist/components/ui/form/form-field-errors.svelte +1 -1
- package/dist/components/ui/form/form.stories.svelte +25 -0
- package/dist/components/ui/form/form.stories.svelte.d.ts +26 -0
- package/dist/components/ui/input/input.stories.svelte +26 -0
- package/dist/components/ui/input-group/input-group-input.svelte.d.ts +1 -1
- package/dist/components/ui/input-group/input-group.stories.svelte +43 -0
- package/dist/components/ui/input-group/input-group.stories.svelte.d.ts +27 -0
- package/dist/components/ui/item/item.stories.svelte +61 -0
- package/dist/components/ui/item/item.stories.svelte.d.ts +27 -0
- package/dist/components/ui/label/label.stories.svelte +7 -0
- package/dist/components/ui/live-region/index.d.ts +1 -0
- package/dist/components/ui/live-region/index.js +1 -0
- package/dist/components/ui/live-region/live-region-demo.svelte +32 -0
- package/dist/components/ui/live-region/live-region-demo.svelte.d.ts +7 -0
- package/dist/components/ui/live-region/live-region.stories.svelte +23 -0
- package/dist/components/ui/live-region/live-region.stories.svelte.d.ts +26 -0
- package/dist/components/ui/live-region/live-region.svelte +12 -0
- package/dist/components/ui/live-region/live-region.svelte.d.ts +8 -0
- package/dist/components/ui/popover/popover.stories.svelte +34 -0
- package/dist/components/ui/radio-group/radio-group.stories.svelte +58 -0
- package/dist/components/ui/radio-group/radio-group.stories.svelte.d.ts +27 -0
- package/dist/components/ui/resizable/resizable.stories.svelte +56 -0
- package/dist/components/ui/resizable/resizable.stories.svelte.d.ts +27 -0
- package/dist/components/ui/select/select.stories.svelte +49 -0
- package/dist/components/ui/separator/separator.stories.svelte +18 -0
- package/dist/components/ui/sheet/sheet.stories.svelte +34 -0
- package/dist/components/ui/sidebar/sidebar-input.svelte.d.ts +1 -1
- package/dist/components/ui/sidebar/sidebar-menu-button.svelte +1 -0
- package/dist/components/ui/sidebar/sidebar-trigger.svelte +1 -1
- package/dist/components/ui/sidebar/sidebar.stories.svelte +72 -0
- package/dist/components/ui/sidebar/sidebar.stories.svelte.d.ts +27 -0
- package/dist/components/ui/skeleton/skeleton.stories.svelte +39 -0
- package/dist/components/ui/skeleton/skeleton.stories.svelte.d.ts +27 -0
- package/dist/components/ui/skeleton/skeleton.svelte +6 -0
- package/dist/components/ui/sonner/index.d.ts +1 -1
- package/dist/components/ui/sonner/index.js +1 -1
- package/dist/components/ui/sonner/sonner.stories.svelte +7 -0
- package/dist/components/ui/sonner/sonner.svelte +17 -1
- package/dist/components/ui/sonner/sonner.svelte.d.ts +6 -0
- package/dist/components/ui/spinner/spinner.stories.svelte +30 -0
- package/dist/components/ui/spinner/spinner.stories.svelte.d.ts +27 -0
- package/dist/components/ui/switch/switch.stories.svelte +56 -0
- package/dist/components/ui/switch/switch.stories.svelte.d.ts +27 -0
- package/dist/components/ui/table/table-cell.svelte +1 -1
- package/dist/components/ui/table/table-head.svelte +1 -1
- package/dist/components/ui/table/table.stories.svelte +68 -0
- package/dist/components/ui/table/table.stories.svelte.d.ts +27 -0
- package/dist/components/ui/table/table.svelte +1 -1
- package/dist/components/ui/tabs/tabs.stories.svelte +48 -0
- package/dist/components/ui/tabs/tabs.stories.svelte.d.ts +27 -0
- package/dist/components/ui/textarea/textarea.stories.svelte +21 -0
- package/dist/components/ui/toggle/toggle.stories.svelte +23 -0
- package/dist/components/ui/toggle-group/toggle-group.stories.svelte +43 -0
- package/dist/components/ui/tooltip/tooltip.stories.svelte +46 -6
- package/dist/core/cms.d.ts +11 -2
- package/dist/core/cms.js +29 -0
- package/dist/core/fields/fieldSchemaToTs.d.ts +7 -0
- package/dist/core/fields/fieldSchemaToTs.js +241 -90
- package/dist/core/fields/layoutUtils.d.ts +4 -1
- package/dist/core/fields/layoutUtils.js +41 -4
- package/dist/core/fields/resolveSeo.d.ts +70 -0
- package/dist/core/fields/resolveSeo.js +88 -0
- package/dist/core/fields/seoFieldDescriptor.d.ts +43 -0
- package/dist/core/fields/seoFieldDescriptor.js +74 -0
- package/dist/core/fields/slugPath.d.ts +13 -0
- package/dist/core/fields/slugPath.js +32 -0
- package/dist/core/fields/urlUtils.d.ts +8 -0
- package/dist/core/fields/urlUtils.js +27 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +1 -0
- package/dist/core/server/entries/operations/create.js +13 -0
- package/dist/core/server/entries/operations/get.d.ts +7 -0
- package/dist/core/server/entries/operations/get.js +10 -6
- package/dist/core/server/entries/operations/slugUniqueness.d.ts +37 -0
- package/dist/core/server/entries/operations/slugUniqueness.js +116 -0
- package/dist/core/server/entries/operations/update.d.ts +6 -1
- package/dist/core/server/entries/operations/update.js +24 -1
- package/dist/core/server/fields/slugResolver.d.ts +3 -13
- package/dist/core/server/fields/slugResolver.js +8 -37
- package/dist/core/server/generator/fields.d.ts +2 -0
- package/dist/core/server/generator/fields.js +44 -18
- package/dist/core/server/generator/formFields.js +2 -1
- package/dist/core/server/generator/generator.js +6 -5
- package/dist/core/server/generator/utils.d.ts +1 -0
- package/dist/core/server/generator/utils.js +4 -0
- package/dist/db-postgres/schema/shop/order.d.ts +37 -1
- package/dist/db-postgres/schema/shop/order.js +3 -1
- package/dist/db-postgres/schema/shop/payment.d.ts +20 -0
- package/dist/db-postgres/schema/shop/payment.js +4 -1
- package/dist/db-postgres/schema/shop/product.d.ts +20 -0
- package/dist/db-postgres/schema/shop/product.js +3 -1
- package/dist/db-postgres/schema/shop/productVariant.d.ts +12 -2
- package/dist/db-postgres/schema/shop/productVariant.js +22 -0
- package/dist/shop/cart/types.d.ts +1 -0
- package/dist/shop/client/index.d.ts +54 -0
- package/dist/shop/client/index.js +5 -1
- package/dist/shop/expiry.d.ts +35 -0
- package/dist/shop/expiry.js +68 -0
- package/dist/shop/http/balance-handler.d.ts +20 -0
- package/dist/shop/http/balance-handler.js +91 -0
- package/dist/shop/http/cart-handler.js +19 -0
- package/dist/shop/http/checkout-handler.js +19 -1
- package/dist/shop/http/index.d.ts +2 -0
- package/dist/shop/http/index.js +2 -0
- package/dist/shop/http/upcoming-handler.d.ts +16 -0
- package/dist/shop/http/upcoming-handler.js +65 -0
- package/dist/shop/http/webhook-handler.js +46 -9
- package/dist/shop/index.d.ts +4 -1
- package/dist/shop/index.js +7 -1
- package/dist/shop/server/balance-payment.d.ts +40 -0
- package/dist/shop/server/balance-payment.js +140 -0
- package/dist/shop/server/cart-hydrate.js +2 -0
- package/dist/shop/server/init.d.ts +14 -0
- package/dist/shop/server/init.js +35 -0
- package/dist/shop/server/orders.d.ts +35 -0
- package/dist/shop/server/orders.js +155 -2
- package/dist/shop/server/payment-policy.d.ts +35 -0
- package/dist/shop/server/payment-policy.js +55 -0
- package/dist/shop/server/payments.d.ts +29 -0
- package/dist/shop/server/payments.js +64 -0
- package/dist/shop/server/populate.d.ts +1 -1
- package/dist/shop/server/refund.d.ts +17 -12
- package/dist/shop/server/refund.js +96 -13
- package/dist/shop/server/shop-data.d.ts +6 -1
- package/dist/shop/server/shop-data.js +44 -7
- package/dist/shop/template.d.ts +13 -0
- package/dist/shop/template.js +98 -0
- package/dist/shop/types.d.ts +142 -1
- package/dist/shop/variant-attributes.d.ts +28 -0
- package/dist/shop/variant-attributes.js +69 -0
- package/dist/sveltekit/server/handle.js +17 -0
- package/dist/sveltekit/server/index.d.ts +1 -0
- package/dist/sveltekit/server/index.js +2 -0
- package/dist/types/cms.d.ts +4 -3
- package/dist/types/cms.schema.d.ts +1 -1
- package/dist/types/cms.schema.js +13 -2
- package/dist/types/fields.d.ts +56 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js +1 -1
- package/dist/types/layout.d.ts +35 -2
- package/dist/types/plugins.d.ts +40 -0
- package/dist/types/plugins.js +4 -1
- package/dist/updates/0.26.0/index.d.ts +2 -0
- package/dist/updates/0.26.0/index.js +51 -0
- package/dist/updates/0.26.1/index.d.ts +2 -0
- package/dist/updates/0.26.1/index.js +19 -0
- package/dist/updates/0.27.0/index.d.ts +2 -0
- package/dist/updates/0.27.0/index.js +50 -0
- package/dist/updates/index.js +7 -1
- package/package.json +29 -7
- package/dist/admin/client/collection/empty-state.svelte +0 -28
- package/dist/admin/client/collection/empty-state.svelte.d.ts +0 -9
- package/dist/admin/client/form/submission-status-badge.svelte +0 -41
- package/dist/admin/client/form/submission-status-badge.svelte.d.ts +0 -7
- package/dist/admin/components/media/file-preview.svelte +0 -51
- package/dist/admin/components/media/file-preview.svelte.d.ts +0 -6
- package/dist/admin/utils/formatDate.d.ts +0 -5
|
@@ -7,6 +7,7 @@ import { building } from '$app/environment';
|
|
|
7
7
|
import { csrfGuard } from '../../server/security/csrf.js';
|
|
8
8
|
import { rateLimitGuard } from '../../server/security/rate-limit.js';
|
|
9
9
|
import { buildCspHeader } from '../../server/security/csp.js';
|
|
10
|
+
import { baseLocale, extractLocaleFromRequest, isLocale } from '../../paraglide/runtime.js';
|
|
10
11
|
const adminGuard = async ({ event, resolve }) => {
|
|
11
12
|
const { user, session } = event.locals;
|
|
12
13
|
// Secure the admin routes
|
|
@@ -60,6 +61,21 @@ const handleAuth = async ({ event, resolve }) => {
|
|
|
60
61
|
}
|
|
61
62
|
return svelteKitHandler({ event, resolve, auth, building });
|
|
62
63
|
};
|
|
64
|
+
const paraglideLang = async ({ event, resolve }) => {
|
|
65
|
+
let locale = baseLocale;
|
|
66
|
+
try {
|
|
67
|
+
const detected = extractLocaleFromRequest(event.request);
|
|
68
|
+
if (detected && isLocale(detected)) {
|
|
69
|
+
locale = detected;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// fall back to baseLocale
|
|
74
|
+
}
|
|
75
|
+
return resolve(event, {
|
|
76
|
+
transformPageChunk: ({ html }) => html.replace('%paraglide.lang%', locale)
|
|
77
|
+
});
|
|
78
|
+
};
|
|
63
79
|
const detectBrowserContext = async ({ event, resolve }) => {
|
|
64
80
|
const ua = event.request.headers.get('user-agent') ?? '';
|
|
65
81
|
const isSafari = /Safari/i.test(ua) && !/(?:Chrome|Chromium|Edg)/i.test(ua);
|
|
@@ -90,6 +106,7 @@ export function includioCMS(cmsConfig) {
|
|
|
90
106
|
generateRuntime(cmsConfig); // Generate runtime code based on the CMS config
|
|
91
107
|
initCMS(cmsConfig);
|
|
92
108
|
const handles = [];
|
|
109
|
+
handles.push(paraglideLang);
|
|
93
110
|
handles.push(detectBrowserContext);
|
|
94
111
|
handles.push(csrfGuard);
|
|
95
112
|
if (cmsConfig.auth) {
|
|
@@ -7,3 +7,4 @@ export { createConsentLog } from '../../core/server/consentLogs/operations/creat
|
|
|
7
7
|
export { getPreviewEntry } from './preview.js';
|
|
8
8
|
export { createRestApiHandler } from '../../admin/api/rest/handler.js';
|
|
9
9
|
export { generateApiKey } from '../../admin/api/rest/middleware/generateApiKey.js';
|
|
10
|
+
export { createAdminApiHandler } from '../../admin/api/handler.js';
|
|
@@ -8,3 +8,5 @@ export { getPreviewEntry } from './preview.js';
|
|
|
8
8
|
// Folded from `./admin/api/rest/handler` (dropped as separate export in 0.20.0)
|
|
9
9
|
export { createRestApiHandler } from '../../admin/api/rest/handler.js';
|
|
10
10
|
export { generateApiKey } from '../../admin/api/rest/middleware/generateApiKey.js';
|
|
11
|
+
// Folded from `./admin/api/handler` (dropped as separate export in 0.26.1)
|
|
12
|
+
export { createAdminApiHandler } from '../../admin/api/handler.js';
|
package/dist/types/cms.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { FilesAdapter } from './adapters/files.js';
|
|
|
3
3
|
import type { CollectionConfig, CollectionConfigWithType } from './collections.js';
|
|
4
4
|
import type { Language } from './languages.js';
|
|
5
5
|
import type { SingleConfig, SingleConfigWithType } from './singles.js';
|
|
6
|
-
import type { CustomFieldDefinition,
|
|
6
|
+
import type { CustomFieldDefinition, IconSetPlugin, Plugin } from './plugins.js';
|
|
7
7
|
import type { FormConfig } from './forms.js';
|
|
8
8
|
import type { AIAdapter } from './adapters/ai.js';
|
|
9
9
|
import type { EmailAdapter } from './adapters/email.js';
|
|
@@ -77,7 +77,7 @@ export interface CMSConfig {
|
|
|
77
77
|
files: FilesAdapter;
|
|
78
78
|
email?: EmailAdapter;
|
|
79
79
|
auth?: AuthConfig;
|
|
80
|
-
plugins?:
|
|
80
|
+
plugins?: Plugin[];
|
|
81
81
|
ai?: AIAdapter;
|
|
82
82
|
media?: MediaConfig;
|
|
83
83
|
apiKeys?: ApiKeyConfig[];
|
|
@@ -95,8 +95,9 @@ export interface ICMS {
|
|
|
95
95
|
filesAdapter: FilesAdapter;
|
|
96
96
|
emailAdapter: EmailAdapter | null;
|
|
97
97
|
authConfig: AuthConfig | null;
|
|
98
|
-
plugins:
|
|
98
|
+
plugins: Plugin[];
|
|
99
99
|
customFields: Map<string, CustomFieldDefinition>;
|
|
100
|
+
iconSets: Map<string, IconSetPlugin>;
|
|
100
101
|
aiAdapter: AIAdapter | null;
|
|
101
102
|
mediaConfig: MediaConfig;
|
|
102
103
|
apiKeys: ApiKeyConfig[];
|
|
@@ -445,7 +445,7 @@ export declare const _internal: {
|
|
|
445
445
|
shop: z.ZodOptional<z.ZodUnknown>;
|
|
446
446
|
cmp: z.ZodOptional<z.ZodUnknown>;
|
|
447
447
|
}, z.core.$loose>;
|
|
448
|
-
FIELD_TYPES: readonly ["text", "content", "number", "boolean", "date", "datetime", "file", "media", "select", "radio", "checkboxes", "relation", "object", "array", "blocks", "slug", "seo", "shop", "url", "custom"];
|
|
448
|
+
FIELD_TYPES: readonly ["text", "content", "number", "boolean", "date", "datetime", "file", "media", "select", "radio", "checkboxes", "relation", "object", "array", "blocks", "slug", "seo", "shop", "url", "icon", "custom"];
|
|
449
449
|
FORM_FIELD_TYPES: readonly ["text", "email", "textarea", "checkbox", "select", "file"];
|
|
450
450
|
getLangCode: typeof getLangCode;
|
|
451
451
|
};
|
package/dist/types/cms.schema.js
CHANGED
|
@@ -6,9 +6,9 @@ const localizedSchema = z.union([z.string(), z.record(z.string(), z.string())]);
|
|
|
6
6
|
/* -------------------------------------------------------------------------- */
|
|
7
7
|
/* Slug */
|
|
8
8
|
/* -------------------------------------------------------------------------- */
|
|
9
|
-
const SLUG_RE = /^[a-z][a-
|
|
9
|
+
const SLUG_RE = /^[a-z][a-zA-Z0-9-]*$/;
|
|
10
10
|
const slugSchema = z.string().regex(SLUG_RE, {
|
|
11
|
-
message: 'must
|
|
11
|
+
message: 'must start with a lowercase letter; letters, digits, hyphens allowed'
|
|
12
12
|
});
|
|
13
13
|
/* -------------------------------------------------------------------------- */
|
|
14
14
|
/* Languages */
|
|
@@ -51,6 +51,7 @@ const FIELD_TYPES = [
|
|
|
51
51
|
'seo',
|
|
52
52
|
'shop',
|
|
53
53
|
'url',
|
|
54
|
+
'icon',
|
|
54
55
|
'custom'
|
|
55
56
|
];
|
|
56
57
|
const baseFieldShape = {
|
|
@@ -82,6 +83,8 @@ const fieldSchema = z.lazy(() => z.discriminatedUnion('type', [
|
|
|
82
83
|
minLength: z.number().int().nonnegative().optional(),
|
|
83
84
|
maxLength: z.number().int().positive().optional(),
|
|
84
85
|
pattern: z.string().optional(),
|
|
86
|
+
patternError: localizedSchema.optional(),
|
|
87
|
+
patternHint: localizedSchema.optional(),
|
|
85
88
|
multiline: z.boolean().optional()
|
|
86
89
|
})
|
|
87
90
|
.passthrough(),
|
|
@@ -267,6 +270,14 @@ const fieldSchema = z.lazy(() => z.discriminatedUnion('type', [
|
|
|
267
270
|
type: z.literal('custom'),
|
|
268
271
|
fieldType: z.string().min(1, { message: 'custom.fieldType is required' }),
|
|
269
272
|
config: z.record(z.string(), z.unknown()).optional()
|
|
273
|
+
})
|
|
274
|
+
.passthrough(),
|
|
275
|
+
// icon — set provided by IconSetPlugin (registered in plugins[])
|
|
276
|
+
z
|
|
277
|
+
.object({
|
|
278
|
+
...baseFieldShape,
|
|
279
|
+
type: z.literal('icon'),
|
|
280
|
+
set: z.string().optional()
|
|
270
281
|
})
|
|
271
282
|
.passthrough()
|
|
272
283
|
]));
|
package/dist/types/fields.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { FormatEnum } from 'sharp';
|
|
|
2
2
|
import type { ImageStyle, MediaFile, VideoStyle } from './media.js';
|
|
3
3
|
import type { Localized } from './languages.js';
|
|
4
4
|
import type { StructuredContentDoc } from './structured-content.js';
|
|
5
|
-
export type FieldType = 'text' | 'content' | 'number' | 'boolean' | 'date' | 'datetime' | 'file' | 'media' | 'select' | 'radio' | 'checkboxes' | 'relation' | 'object' | 'array' | 'blocks' | 'slug' | 'seo' | 'shop' | 'url' | 'custom';
|
|
5
|
+
export type FieldType = 'text' | 'content' | 'number' | 'boolean' | 'date' | 'datetime' | 'file' | 'media' | 'select' | 'radio' | 'checkboxes' | 'relation' | 'object' | 'array' | 'blocks' | 'slug' | 'seo' | 'shop' | 'url' | 'icon' | 'custom';
|
|
6
6
|
export interface FieldCondition {
|
|
7
7
|
field: string;
|
|
8
8
|
equals?: string | string[];
|
|
@@ -23,6 +23,19 @@ export interface TextField extends BaseField {
|
|
|
23
23
|
minLength?: number;
|
|
24
24
|
maxLength?: number;
|
|
25
25
|
pattern?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Friendly validation message shown when {@link pattern} fails. A raw regex
|
|
28
|
+
* means nothing to an editor, so provide a localized human message here
|
|
29
|
+
* (e.g. "Use lowercase letters and dashes only"). Falls back to a generic
|
|
30
|
+
* message when omitted.
|
|
31
|
+
*/
|
|
32
|
+
patternError?: Localized;
|
|
33
|
+
/**
|
|
34
|
+
* Localized hint describing the expected format, shown in the field
|
|
35
|
+
* description instead of the raw regex. Without it the raw {@link pattern}
|
|
36
|
+
* is never surfaced to the editor.
|
|
37
|
+
*/
|
|
38
|
+
patternHint?: Localized;
|
|
26
39
|
multiline?: boolean;
|
|
27
40
|
defaultValue?: string;
|
|
28
41
|
}
|
|
@@ -60,6 +73,12 @@ export interface FileField extends BaseField {
|
|
|
60
73
|
maxSizeMB?: number;
|
|
61
74
|
multiple?: boolean;
|
|
62
75
|
defaultValue?: string | string[];
|
|
76
|
+
/**
|
|
77
|
+
* Render mode for selected file(s).
|
|
78
|
+
* - 'preview' (default): aspect-square miniature + carousel for multiple (parity with media field).
|
|
79
|
+
* - 'list': compact list of rows (icon + name + size + X) — better for non-visual files (PDF, audio, other).
|
|
80
|
+
*/
|
|
81
|
+
display?: 'preview' | 'list';
|
|
63
82
|
}
|
|
64
83
|
export interface ImageFieldStyle {
|
|
65
84
|
name: string;
|
|
@@ -172,17 +191,33 @@ export interface BlocksField extends BaseField {
|
|
|
172
191
|
defaultValue?: ObjectFieldData[];
|
|
173
192
|
displayMode?: 'simple' | 'blocks';
|
|
174
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* @public
|
|
196
|
+
* Slug field — a normalized URL-safe string. Its presence does NOT imply a
|
|
197
|
+
* public URL; routability is decided by the collection's `slugField`/`pathTemplate`
|
|
198
|
+
* config, not by this field existing.
|
|
199
|
+
*/
|
|
175
200
|
export interface SlugField extends BaseField {
|
|
176
201
|
type: 'slug';
|
|
177
202
|
pattern?: string;
|
|
178
203
|
sourceField?: string;
|
|
179
204
|
defaultValue?: string;
|
|
180
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* @public
|
|
208
|
+
* v1.0-frozen. New properties may only be added as OPTIONAL (additive,
|
|
209
|
+
* non-breaking).
|
|
210
|
+
*/
|
|
181
211
|
export interface SeoField extends BaseField {
|
|
182
212
|
type: 'seo';
|
|
183
213
|
slugSource?: string;
|
|
184
214
|
titleSource?: string;
|
|
185
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* @public
|
|
218
|
+
* v1.0-frozen data contract: `entry.seo.*`. New fields may only be added as
|
|
219
|
+
* OPTIONAL (additive, non-breaking).
|
|
220
|
+
*/
|
|
186
221
|
export interface SeoFieldData {
|
|
187
222
|
slug: string;
|
|
188
223
|
canonicalUrl?: string;
|
|
@@ -199,6 +234,11 @@ export interface ShopFieldData {
|
|
|
199
234
|
basePrice: number;
|
|
200
235
|
vatRate: number;
|
|
201
236
|
isActive: boolean;
|
|
237
|
+
/**
|
|
238
|
+
* Per-product payment policy. Omitted = `{ type: 'full' }` (legacy behavior).
|
|
239
|
+
* @public
|
|
240
|
+
*/
|
|
241
|
+
paymentPolicy?: import('../shop/types.js').PaymentPolicy;
|
|
202
242
|
variants?: Array<{
|
|
203
243
|
id?: string;
|
|
204
244
|
sku?: string | null;
|
|
@@ -229,4 +269,18 @@ export interface CustomField extends BaseField {
|
|
|
229
269
|
/** Plugin-specific configuration, opaque to core */
|
|
230
270
|
config?: Record<string, unknown>;
|
|
231
271
|
}
|
|
232
|
-
|
|
272
|
+
/**
|
|
273
|
+
* @public
|
|
274
|
+
* Icon picker field. Stores a string key referencing an icon registered by an
|
|
275
|
+
* {@link import('./plugins.js').IconSetPlugin}. Admin UI renders a kafelek
|
|
276
|
+
* (collapsed) and opens a searchable grid dialog. The set of icons (Svelte
|
|
277
|
+
* components + labels) is provided by a plugin at admin bootstrap — NOT inline
|
|
278
|
+
* in `cms.config.ts` (which is server-only / Zod-validated).
|
|
279
|
+
*/
|
|
280
|
+
export interface IconField extends BaseField {
|
|
281
|
+
type: 'icon';
|
|
282
|
+
/** Plugin slug to source icons from when multiple icon-set plugins registered. Defaults to the first one. */
|
|
283
|
+
set?: string;
|
|
284
|
+
defaultValue?: string;
|
|
285
|
+
}
|
|
286
|
+
export type Field = TextField | ContentField | NumberField | BooleanField | DateField | DateTimeField | FileField | MediaField | SelectField | RadioField | CheckboxesField | RelationField | ObjectField | ArrayField | BlocksField | SlugField | SeoField | ShopField | UrlField | IconField | CustomField;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -10,8 +10,8 @@ export { type SingleConfig } from './singles.js';
|
|
|
10
10
|
export { type FormConfig, type FormSubmission } from './forms.js';
|
|
11
11
|
export { type FormField, type FormFieldType, type FormBaseField, type FormTextField, type FormEmailField, type FormTextareaField, type FormCheckboxField, type FormSelectField } from './formFields.js';
|
|
12
12
|
export { type CMSConfig, type ApiKeyConfig, type AuthConfig, type TypographyConfig } from './cms.js';
|
|
13
|
-
export { type PluginConfig, type CustomFieldDefinition } from './plugins.js';
|
|
13
|
+
export { type PluginConfig, type CustomFieldDefinition, type IconSetPlugin, type IconDefinition, type Plugin, isIconSetPlugin } from './plugins.js';
|
|
14
14
|
export { type Language, type Localized } from './languages.js';
|
|
15
|
-
export { type Layout, type LayoutNode, type LayoutPreset, type LayoutNodeType, type ColumnRatio, type SectionNode, type ColumnsNode, type CardNode, type AccordionNode, type StackNode } from './layout.js';
|
|
15
|
+
export { type Layout, type LayoutNode, type LayoutPreset, type LayoutNodeType, type ColumnRatio, type SectionNode, type ColumnsNode, type CardNode, type AccordionNode, type StackNode, type TabsNode, type TabNode } from './layout.js';
|
|
16
16
|
export { type CmsContext } from './cms-context.js';
|
|
17
17
|
export { type UserData, type Breadcrumb } from '../admin/types.js';
|
package/dist/types/index.js
CHANGED
|
@@ -10,7 +10,7 @@ export {} from './singles.js';
|
|
|
10
10
|
export {} from './forms.js';
|
|
11
11
|
export {} from './formFields.js';
|
|
12
12
|
export {} from './cms.js';
|
|
13
|
-
export {} from './plugins.js';
|
|
13
|
+
export { isIconSetPlugin } from './plugins.js';
|
|
14
14
|
export {} from './languages.js';
|
|
15
15
|
export {} from './layout.js';
|
|
16
16
|
export {} from './cms-context.js';
|
package/dist/types/layout.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export type LayoutPreset = 'sidebar-right' | 'two-column' | {
|
|
|
7
7
|
preset: 'two-column';
|
|
8
8
|
left?: string[];
|
|
9
9
|
};
|
|
10
|
-
export type LayoutNodeType = 'section' | 'columns' | 'card' | 'accordion' | 'stack';
|
|
10
|
+
export type LayoutNodeType = 'section' | 'columns' | 'card' | 'accordion' | 'stack' | 'tabs' | 'tab';
|
|
11
11
|
export type ColumnRatio = '1fr 1fr' | '2fr 1fr' | '1fr 2fr' | '3fr 1fr' | '1fr 3fr' | '1fr 1fr 1fr';
|
|
12
12
|
interface LayoutNodeBase {
|
|
13
13
|
type: LayoutNodeType;
|
|
@@ -51,7 +51,40 @@ export interface StackNode extends LayoutNodeBase {
|
|
|
51
51
|
fields?: string[];
|
|
52
52
|
children?: LayoutNode[];
|
|
53
53
|
}
|
|
54
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Tab strip. Renders an accessible tablist; each child `tab` becomes a panel.
|
|
56
|
+
* All panels stay mounted (inactive ones hidden) so validation, dirty-tracking
|
|
57
|
+
* and conditional visibility keep working across tabs.
|
|
58
|
+
*
|
|
59
|
+
* Constraints (enforced by {@link validateLayout}):
|
|
60
|
+
* - `children` must contain only `tab` nodes
|
|
61
|
+
* - `tabs` may not be nested inside another `tab`/`tabs`
|
|
62
|
+
* - allowed top-level or inside section/card/columns/stack
|
|
63
|
+
*
|
|
64
|
+
* @public
|
|
65
|
+
* @example
|
|
66
|
+
* { type: 'tabs', children: [
|
|
67
|
+
* { type: 'tab', label: { en: 'Content', pl: 'Treść' }, fields: ['title', 'body'] },
|
|
68
|
+
* { type: 'tab', label: { en: 'SEO' }, fields: ['seo'] }
|
|
69
|
+
* ] }
|
|
70
|
+
*/
|
|
71
|
+
export interface TabsNode extends LayoutNodeBase {
|
|
72
|
+
type: 'tabs';
|
|
73
|
+
children: TabNode[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* A single tab. Only valid as a direct child of {@link TabsNode}.
|
|
77
|
+
* `label` is required — it is the trigger text. Use `fields` for a flat tab
|
|
78
|
+
* or `children` to nest sections/cards/columns inside the tab.
|
|
79
|
+
* @public
|
|
80
|
+
*/
|
|
81
|
+
export interface TabNode extends LayoutNodeBase {
|
|
82
|
+
type: 'tab';
|
|
83
|
+
label: Localized;
|
|
84
|
+
fields?: string[];
|
|
85
|
+
children?: LayoutNode[];
|
|
86
|
+
}
|
|
87
|
+
export type LayoutNode = SectionNode | ColumnsNode | CardNode | AccordionNode | StackNode | TabsNode | TabNode;
|
|
55
88
|
export declare function isLayoutLeaf(node: LayoutNode): node is LayoutNode & {
|
|
56
89
|
fields: string[];
|
|
57
90
|
};
|
package/dist/types/plugins.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Component } from 'svelte';
|
|
|
2
2
|
import type { z } from 'zod';
|
|
3
3
|
import type { RawEntry } from './entries.js';
|
|
4
4
|
import type { CustomField } from './fields.js';
|
|
5
|
+
import type { Localized } from './languages.js';
|
|
5
6
|
import type { PopulateCtx } from '../core/server/entries/operations/resolveEntry.js';
|
|
6
7
|
/**
|
|
7
8
|
* Defines a custom field type contributed by a plugin.
|
|
@@ -40,3 +41,42 @@ export interface PluginConfig {
|
|
|
40
41
|
afterDelete?: (id: string) => Promise<void>;
|
|
41
42
|
};
|
|
42
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* @public
|
|
46
|
+
* Single icon entry contributed by an {@link IconSetPlugin}. Carries a Svelte
|
|
47
|
+
* component, a localized label and optional search keywords. Components are
|
|
48
|
+
* eagerly imported (tree-shakable) — for small sets (10–50 icons) lazy import
|
|
49
|
+
* adds overhead without benefit.
|
|
50
|
+
*/
|
|
51
|
+
export interface IconDefinition {
|
|
52
|
+
/** Svelte icon component (e.g. `Brain` from `@lucide/svelte`). */
|
|
53
|
+
component: Component;
|
|
54
|
+
/** Localized display label shown in collapsed state and in the picker grid. */
|
|
55
|
+
label: Localized;
|
|
56
|
+
/** Extra search keywords (non-localized). Search matches key + label + keywords. */
|
|
57
|
+
keywords?: string[];
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* @public
|
|
61
|
+
* Icon set plugin — registers a named set of icons consumable by the `icon`
|
|
62
|
+
* field type. Registered alongside other plugins in `cms.config.ts`'s
|
|
63
|
+
* `plugins:` array. Because Svelte components do NOT serialize to the client,
|
|
64
|
+
* the application's admin layout must wire the same plugin instance to
|
|
65
|
+
* `AdminLayout` via `buildIconSetMap(...)` — see admin/helpers.
|
|
66
|
+
*/
|
|
67
|
+
export interface IconSetPlugin {
|
|
68
|
+
/** Unique slug — referenced by `IconField.set` when multiple sets exist. */
|
|
69
|
+
slug: string;
|
|
70
|
+
type: 'icon-set';
|
|
71
|
+
icons: Record<string, IconDefinition>;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* @public
|
|
75
|
+
* Union of all plugin kinds accepted in `cms.config.ts → plugins:`. New
|
|
76
|
+
* plugin kinds must be added here. Type discriminated by:
|
|
77
|
+
* - {@link IconSetPlugin} → `type === 'icon-set'`
|
|
78
|
+
* - {@link PluginConfig} → everything else (no `type` field).
|
|
79
|
+
*/
|
|
80
|
+
export type Plugin = PluginConfig | IconSetPlugin;
|
|
81
|
+
/** Type guard for {@link IconSetPlugin}. */
|
|
82
|
+
export declare function isIconSetPlugin(p: Plugin): p is IconSetPlugin;
|
package/dist/types/plugins.js
CHANGED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export const update = {
|
|
2
|
+
version: '0.26.0',
|
|
3
|
+
date: '2026-05-26',
|
|
4
|
+
description: '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).',
|
|
5
|
+
features: [
|
|
6
|
+
'`getFieldA11yProps({ id, hasDescription, hasError, required })` (`src/lib/admin/utils/field-a11y.ts`) — buduje `aria-describedby` chain, `aria-invalid`, `aria-required` dla pól w `ui/form` (formsnap) — bridge bez breaking migracji ui/form ↔ ui/field koegzystencji z S3.',
|
|
7
|
+
'`<LiveRegion>` (`src/lib/components/ui/live-region/`) — prosty primitive (`sr-only` + `role="status"` + `aria-live`) zastąpił inline duplicates w relation-field i blocks-field; gotowy do reuse.',
|
|
8
|
+
'`field-renderer.svelte` propaguje `Form.Control` `props` (formsnap snippet `children({ props })`) do wszystkich field components (boolean, number, date, datetime, select, media, file, blocks, array, object, slug, seo, shop, url, relation). Dotąd tylko text/radio/checkboxes dostawały aria — reszta była niewired.',
|
|
9
|
+
'Blocks field — keyboard reorder/add/remove/duplicate ogłasza przez `<LiveRegion>` ("Przeniesiono X na pozycję Y", "Dodano blok X" itp.). Block picker modal — `aria-labelledby` z Dialog.Title + `aria-live` na search results count.',
|
|
10
|
+
'Slug field — live preview slugify\'d wartości pod inputem (gdy user typuje wielką literą), copy button z `UseClipboard` + toast "Skopiowano slug". Description default w demo blog-post.',
|
|
11
|
+
'Relation field — combobox a11y wzmocnione: `aria-busy` na Command.List podczas async search, picker dialog `Dialog.Title`/`Dialog.Description` (sr-only), reorder LiveRegion przeniesione na primitive.',
|
|
12
|
+
'Media + File fields — `Dialog.Title` + `Dialog.Description` (sr-only) na picker dialogs (eliminuje bits-ui ostrzeżenie o brakujących aria-labelledby).',
|
|
13
|
+
'Shop field — `aria-pressed` na price mode toggle (Netto/Brutto), `role="group"` + `aria-label` na toggle group, `aria-label="Usuń wariant N"` na trash buttons.',
|
|
14
|
+
'SEO field — `aria-live="polite"` na char counter (50/60, 120/160) — SR słyszy zmianę gdy user wpisuje.',
|
|
15
|
+
'Storybook fields — nowe `file.stories.svelte` (4 warianty) i `shop.stories.svelte` (4 warianty). Slug stories rozszerzone o WithPreview/ClipboardCopy. Text stories — Required wariant. Slug ma już `tags: [\'autodocs\']` (zdjęty `no-test`).',
|
|
16
|
+
'Slugi pól, kolekcji, singles i form akceptują camelCase (`/^[a-z][a-zA-Z0-9-]*$/`). Można pisać `slug: \'siteName\'` zamiast `\'site-name\'` i odczytywać `entry.data.siteName` bez bracket notation. Walidacja blokuje nadal PascalCase, prefix `_` i digit-leading.',
|
|
17
|
+
'Helper `quoteKey()` w `src/lib/core/server/generator/utils.ts` — opakowuje slugi nie-spełniające JS identifier shape w cudzysłowy w wygenerowanym TS.',
|
|
18
|
+
'Nowy węzeł layoutu `tabs`/`tab` (`src/lib/types/layout.ts`) — taby konfigurowane per kolekcja w `layout[]`, renderowane przez `LayoutRenderer` (bits-ui Tabs: `role=tab/tablist/tabpanel`, strzałki/Home/End, `aria-selected`). Wszystkie panele pozostają zamontowane (walidacja/dirty-tracking/`showWhen` działają na nieaktywnych tabach), error-summary i klik-w-preview (hybrid) automatycznie przełączają tab i fokusują pole (`activateContainingTabs`), trigger taba z błędem ma wskaźnik (kropka + tekst dla SR, nie sam kolor). Aktywny tab najwyższego poziomu utrwalany w `?tab` (deep-link, przeżywa reload). `tab` przyjmuje `fields` (płaski) lub `children` (sekcje/karty w środku); zakaz zagnieżdżonych tabów + walidacja struktury w `validateLayout`. Stary sztywny tab Content/SEO w `entry.svelte` usunięty — SEO to teraz zwykły `tab` w configu (demo blog-post).',
|
|
19
|
+
'`<FormErrorSummary>` (`src/lib/admin/components/forms/`) — banner z listą błędów, anchor links scroll do `[data-field-path]`, focus pierwszego błędnego pola, `role="alert"` + `aria-live="assertive"`. Adoptowany w entry, coupon-form, shipping-method-form, shop-order-detail.',
|
|
20
|
+
'`<ConfirmationDialog>` (`src/lib/admin/components/dialogs/`) — typed wrapper na bits-ui AlertDialog z destructive/default variant, default focus na Cancel (bezpieczniejszy), loading state. Zastąpił 5× `confirm()` (form-submissions, entry, shop-order-detail, coupons-list, dashboard) + dodany dla Archive entry.',
|
|
21
|
+
'`<DetailPageShell>` (`src/lib/admin/components/layout/`) — wspólny layout (PageHeader + body + opcjonalny sidebar) dla coupon-edit / shipping-method-edit. Skip-link target `#detail-main`.',
|
|
22
|
+
'Coupon edit form zmigrowany na sveltekit-superforms + formsnap (jak account/profile-section) — schema zod (`coupon-schema.ts`), SPA mode, error summary + per-field errors.',
|
|
23
|
+
'Entry SaveIndicator: nowe stany + COGA copy (`Niezapisane zmiany`, `Zapisuję…`, `Zapisano X temu` z `formatRelativeDate` recompute co 30s, `Błąd zapisu`), `aria-atomic` + role flip alert/status. `<svelte:window>` `beforeunload` guard gdy unsaved.',
|
|
24
|
+
'`<A11yHeaderBadge>` (`src/lib/admin/client/entry/header/a11y-header-badge.svelte`) — persistent warning chip w entry-header pokazujący liczbę a11y issues (images bez alt, heading hierarchy, generic link text). Reuse `validateA11y` z `a11y-validator.ts`. Strong-warning UX (B.1) — nie blokuje save, prowadzi autora. Kolumna a11y w collection-entries.svelte (S5) + persistent header badge (S10) + publish-panel sheet detail = pełna ścieżka widoczności.',
|
|
25
|
+
'`scripts/lighthouse-audit.ts` (`pnpm audit:lighthouse`) — ad-hoc CLI runner po 11 URL admin (login, dashboard, collections, users, media, forms, account, maintenance, shop ×3). Lighthouse 13 + chrome-launcher + auth cookie z `e2e/.auth/admin.json`. Output `.tmp/lighthouse/<view>.json` + `summary.md`, target a11y ≥95.',
|
|
26
|
+
'`e2e/admin/a11y-baseline.spec.ts` rozszerzone 4 → 14 widoków: 10 page-level (login, dashboard, collections-list, entry-edit, users-list, media-library, forms-detail, account, maintenance, user-create-dialog) + 4 shop best-effort (skip jeśli moduł nie configured). Per spec 0 critical violations enforced.',
|
|
27
|
+
'TipTap link-dialog `aria-expanded` na 2 collapsible sections (Dostępność + Indeksowanie) — uzupełnia ATAG B.3 author tool a11y. FigureNodeView/image-dialog/publish-panel: `:focus-visible` fallback z `var(--ring)` outline (focus indicator widoczny zawsze, nawet gdy `:focus` zmienia tylko border-color).',
|
|
28
|
+
'Publiczny `resolveSeo(entry, language?)` (`includio-cms/core`) — stabilny znormalizowany read-path nad zamrożonym kontraktem `entry.seo.*` (wartości localized spłaszczane po języku; `ogImage` przepuszczany jako string lub MediaFile) do budowy własnego `<head>` lub zasilenia komponentu `<Seo>`. `SlugField`/`SeoField`/`SeoFieldData` oznaczone `@public` + v1.0-frozen.',
|
|
29
|
+
'Wewnętrzny `seoFieldDescriptor` — pojedyncze źródło prawdy kształtu pola SEO dla generatorów Zod + TS (wyjście bit-identyczne, pilnowane snapshotami i testami drift-guard). Przygotowuje przyszły plugin-rehoming SEO bez breaking change. Istniejąca niespójność Zod(required)/TS(optional) dla `description`/`ogImage` udokumentowana i zachowana 1:1 (do rozstrzygnięcia post-1.0).',
|
|
30
|
+
'Mobile responsiveness full pass (S10c) — pełny mobile UX panelu admina: `DataTable` sticky first column + edge-bleed scroll wrapper (1 fix → wszystkie 8+ list pages, w tym shop), `Dialog.Content` `fullscreenMobile` prop (media/file picker + tiptap image/video dialogi), media library tags sidebar + FileDetails jako Sheet drawer na <md (`hidden md:flex` aside), entry hybrid preview Sheet drawer toggle z `IsMobile(1024)` (<lg pokazuje tylko form + floating "Podgląd" button), shop-order detail items table z `min-w-[640px]` + horizontal scroll, char counter w `text-field-wrapper` na mobile stackowany pod polem zamiast `absolute right`, EntryHeader padding + SaveIndicator text `max-sm:sr-only`, PageHeader text-scale `text-2xl sm:text-3xl lg:text-4xl`, breadcrumb truncacja middle segments `/…/` na <sm, table-toolbar search `w-full min-w-0` na mobile, tiptap toolbar separatory `hidden sm:block`, relation popover `w-[var(--bits-popover-anchor-width)] max-w-[calc(100vw-1rem)]`. Playwright `e2e/admin/mobile-responsive.spec.ts` rozszerzony o iPad portrait (768×1024) + shop-shipping view + `scrollWidth <= window.innerWidth` page-overflow assertion: 43/43 testów pass, 0 critical, 0-2 serious axe per (3 viewports × 12 widoków).',
|
|
31
|
+
'Dashboard mobile layout flip (S10c follow-up) — `dashboard-page.svelte`: main wrapper `flex gap-5` → `flex flex-col gap-5 lg:flex-row` + sub-grid `grid-cols-2` → `grid-cols-1 sm:grid-cols-2` + sidebar `max-w-80 flex-1 shrink-0` → `w-full shrink-0 lg:max-w-80 lg:flex-1`. Side-by-side 2-col layout aktywowany dopiero od `lg` (1024px); poniżej stack vertically, sub-grid kolumna pojedyncza do `sm` (640px). Eliminuje cramped 2-col scroll na 375-768px.'
|
|
32
|
+
],
|
|
33
|
+
fixes: [
|
|
34
|
+
'`runtime/types.ts` generuje poprawny TypeScript dla kebab-case slugów (np. `\'published-at\'?: string` zamiast gołego `published-at?: string`, który był syntaks błędem). Naprawione w `generateTsTypeFromFields`, `generateFlatTsTypeFromFields`, `generateInlineBlockTypeString`, object/blocks variants oraz form fields generator.',
|
|
35
|
+
'Shop order detail: `bg-red-50` / `bg-green-50` zamienione na tokenized `bg-destructive-bg` / `bg-success-bg` z `role="alert"`/`role="status"`. Status form ma teraz SaveIndicator + FormErrorSummary.',
|
|
36
|
+
'Shipping-method form: `<fieldset>/<legend>` na payment methods (poprawne grouping dla SR), `aria-live="polite"` na carrier config panel, FormErrorSummary zamiast `bg-red-50` div, `text-amber-700` zamienione na `text-warning` token.',
|
|
37
|
+
'Coupons list: delete dialog A11y (focus trap, ESC, return focus) zamiast browser `confirm()`.',
|
|
38
|
+
'Form submissions bulk delete: AlertDialog zamiast `confirm()`, plural PL forms ("3 zgłoszenia" vs "5 zgłoszeń").',
|
|
39
|
+
'Dashboard delete-orphaned: AlertDialog z liczbą wpisów w description zamiast `confirm()`.',
|
|
40
|
+
'`pnpm demo` seed (`scripts/seed-demo.ts`) — twarda bramka walidacji: każdy `entry_version` przed surowym INSERT-em przechodzi przez realny schemat CMS (`generateZodSchemaFromFields(..., { localized: false })` — ten sam oracle co admin/publish). Niezgodne dane przerywają seed z czytelnym `collection/lang/entry: path: message`. Dane demo doprowadzone do zgodności: blog-post `title` bez nierealnego `minLength: 50` (+ usunięte śmieciowe debugowe pole `test`/`dupa` z kolekcji), media jako UUID-string zamiast `{id,url,alt}` (cover/thumbnail/image/logo/favicon/photo), daty pełne ISO, dołożone wymagane obiekty `seo`, `url` jako obiekt, bloki z `_slug` zamiast `_type`, `localizedTags`/`links` w kształcie schematu.',
|
|
41
|
+
'Lista entries w kolekcji pokazuje "(bez tytułu)"/"(untitled)" zamiast surowego UUID gdy wpis nie ma tytułu — spójne z breadcrumbem na ekranie edycji. Wspólny helper `getCollectionEntryDisplayLabel` (`src/lib/admin/utils/entryLabel.ts`) używany przez list/grid i breadcrumb (fallback wykrywany po `language === null`, odporny na tytuł = id).',
|
|
42
|
+
'Slug first-class: podgląd URL wpisu w adminie (`collection-entries.svelte`) odhardkodowany z wymogu pola typu `seo` — kolekcja ze standalone polem slug + skonfigurowanym `slugField` pokazuje URL; bez `slugField`/`pathTemplate` świadomie brak `_url` (np. kolekcje atrybutów), bez błędu. Wydzielony czysty resolver `slugPath.ts` (bez `getCMS`) współdzielony przez admin client i server.'
|
|
43
|
+
],
|
|
44
|
+
breakingChanges: [
|
|
45
|
+
'**Entry autosave usunięty** — wcześniej 30s timeout + Ctrl/Cmd+S manual. Teraz tylko manual save (button "Zapisz szkic" zawsze widoczny + Ctrl/Cmd+S shortcut). Powód: stara impl mogła w niejasnych sytuacjach modyfikować published data; bezpieczniejsza re-introdukcja z draft-only guard zaplanowana w ROADMAP backlog. Migracja: użytkownicy muszą explicit zapisać przed nawigacją (jest `beforeunload` guard).',
|
|
46
|
+
'EntryHeader props: nowy `lastSavedAt?: Date | null` (default `null`). Niezbędny dla SaveIndicator "Zapisano X temu". Konsumenci niestandardowi (entry-header-story-demo, custom integracje) muszą propagować z entry.svelte.',
|
|
47
|
+
'EntryForm props: usunięty hardcoded podział Content/SEO (`tabFilter` nigdy nie trafił do release\'u — zastąpiony węzłem layoutu `tabs`). Grupowanie w taby = teraz config `layout[]`. Kolekcje bez `tabs` bez zmian; SEO w demo blog-post wyrażone jako `tab`. Dodany prop `errorPaths?: Set<string>` (EntryForm/LayoutRenderer) do wskaźnika błędu na tabie.',
|
|
48
|
+
'`coupon-form.svelte` przepisane od zera na sveltekit-superforms — props identyczne (`initial`, `submitLabel`, `onSubmit`, `onCancel`), ale wewnętrzny mechanizm walidacji zmieniony (zod schema zamiast hand-rolled). Call-sites bez zmian.'
|
|
49
|
+
],
|
|
50
|
+
notes: 'V1-DECISIONS.md sekcja "Sesja 8" zawiera pełny audit decyzji + grep gates. Pełna superForm migracja shipping-method-form odłożona do backlogu (skomplikowany state: net/gross toggle + dynamic carrier + multi-lang record). Storybook entry-page story też w backlogu (wymaga mock setRemotes + RawEntry fixtures — robione w S10 a11y sweep). VoiceOver smoke test (manual user-delegated) — checklist w `V1-UI-AUDIT.md` sekcja "VoiceOver smoke (manual)". Kompatybilność z istniejącymi danymi: pełna (zero schema changes; coupon zachowuje stary kontrakt API).'
|
|
51
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const update = {
|
|
2
|
+
version: '0.26.1',
|
|
3
|
+
date: '2026-05-28',
|
|
4
|
+
description: '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).',
|
|
5
|
+
features: [
|
|
6
|
+
'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.',
|
|
7
|
+
'`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.',
|
|
8
|
+
'`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.',
|
|
9
|
+
'`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).'
|
|
10
|
+
],
|
|
11
|
+
fixes: [
|
|
12
|
+
'`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).',
|
|
13
|
+
'`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).',
|
|
14
|
+
'`includio-cms/sveltekit/server` re-eksportuje teraz `createAdminApiHandler` (obok istniejącego `createRestApiHandler`).',
|
|
15
|
+
'`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.',
|
|
16
|
+
'`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.'
|
|
17
|
+
],
|
|
18
|
+
breakingChanges: []
|
|
19
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export const update = {
|
|
2
|
+
version: '0.27.0',
|
|
3
|
+
date: '2026-05-29',
|
|
4
|
+
description: '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`.',
|
|
5
|
+
features: [
|
|
6
|
+
'`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.',
|
|
7
|
+
'`VariantAttribute` + per-type interfejsy (`VariantAttributeText/Number/Datetime/Select/Boolean/Image/Entry/Slug`) wyeksportowane jako `@public` z `includio-cms/shop`.',
|
|
8
|
+
'`InvalidVariantAttributesError` (code `INVALID_VARIANT_ATTRIBUTES`) — rzucany przez `upsertShopData` gdy `variant.attributes` nie pasują do schemy. Eksport publiczny z `includio-cms/shop`.',
|
|
9
|
+
'`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).',
|
|
10
|
+
'`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_]*`).',
|
|
11
|
+
'`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.',
|
|
12
|
+
'`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).',
|
|
13
|
+
'`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>`.',
|
|
14
|
+
'`getShopConfig` remote (admin) — response zawiera teraz `variantAttributes` z `ResolvedShopConfig`, dzięki czemu `shop-field` może lokalnie renderować typed atrybuty bez dodatkowego round-tripa.',
|
|
15
|
+
'`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).',
|
|
16
|
+
'`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.',
|
|
17
|
+
'`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).',
|
|
18
|
+
'`VariantLabelConfig` eksportowany jako `@public` typ z `includio-cms/shop`.',
|
|
19
|
+
'`getShopConfig` remote response zawiera teraz `variantLabel: shop.variantLabel ?? null`.',
|
|
20
|
+
'`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.',
|
|
21
|
+
'`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.',
|
|
22
|
+
'`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.',
|
|
23
|
+
'`VariantExpiredError` (code `VARIANT_EXPIRED`, `variantId`) — eksport `@public`. Rzucany przez `createOrderFromCart` w `server/orders.ts` przed stock reservation (idempotent guard).',
|
|
24
|
+
'`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.',
|
|
25
|
+
'`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.',
|
|
26
|
+
'`client.products.listUpcoming(productId)` + `VariantPublic` (`includio-cms/shop/client`, `@public`) — nowy namespace SDK. Zwraca upcoming variants per produkt; zero-config passthrough kiedy `variantExpiry` nieustawiony.',
|
|
27
|
+
'`getShopConfig` remote (admin) — response zawiera teraz `variantExpiry: shop.variantExpiry ?? null`.',
|
|
28
|
+
'`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.',
|
|
29
|
+
'`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.',
|
|
30
|
+
'`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.',
|
|
31
|
+
'`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.',
|
|
32
|
+
'`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.',
|
|
33
|
+
'`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).',
|
|
34
|
+
'`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.',
|
|
35
|
+
'`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).',
|
|
36
|
+
'`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`.',
|
|
37
|
+
'`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`.',
|
|
38
|
+
'`client.orders.payBalance(orderNumber, token)` + `PayBalanceResult` (`includio-cms/shop/client`, `@public` / `@experimental`) — REST wrapper dla customer flow dopłaty.',
|
|
39
|
+
'`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.',
|
|
40
|
+
'`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".',
|
|
41
|
+
'`generateBalanceLinkForOrder` (admin remote) — auth-protected, requires `INCLUDIO_BALANCE_TOKEN_SECRET`, builds URL z configured `orderViewUrl` + `?balance=1&balanceToken=...`. Zwraca `{ success, url, balanceAmount }`.',
|
|
42
|
+
'`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".'
|
|
43
|
+
],
|
|
44
|
+
fixes: [
|
|
45
|
+
'`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.',
|
|
46
|
+
'`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).',
|
|
47
|
+
'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.'
|
|
48
|
+
],
|
|
49
|
+
breakingChanges: []
|
|
50
|
+
};
|
package/dist/updates/index.js
CHANGED
|
@@ -59,6 +59,9 @@ import { update as update0220 } from './0.22.0/index.js';
|
|
|
59
59
|
import { update as update0230 } from './0.23.0/index.js';
|
|
60
60
|
import { update as update0240 } from './0.24.0/index.js';
|
|
61
61
|
import { update as update0250 } from './0.25.0/index.js';
|
|
62
|
+
import { update as update0260 } from './0.26.0/index.js';
|
|
63
|
+
import { update as update0261 } from './0.26.1/index.js';
|
|
64
|
+
import { update as update0270 } from './0.27.0/index.js';
|
|
62
65
|
export const updates = [
|
|
63
66
|
update0065,
|
|
64
67
|
update0066,
|
|
@@ -120,7 +123,10 @@ export const updates = [
|
|
|
120
123
|
update0220,
|
|
121
124
|
update0230,
|
|
122
125
|
update0240,
|
|
123
|
-
update0250
|
|
126
|
+
update0250,
|
|
127
|
+
update0260,
|
|
128
|
+
update0261,
|
|
129
|
+
update0270
|
|
124
130
|
];
|
|
125
131
|
export const getUpdatesFrom = (fromVersion) => {
|
|
126
132
|
const fromParts = fromVersion.split('.').map(Number);
|