includio-cms 0.25.0 → 0.26.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 +57 -4
- package/CHANGELOG.md +53 -0
- package/DOCS.md +1 -1
- package/README.md +2 -0
- package/ROADMAP.md +6 -0
- 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/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 +279 -130
- package/dist/admin/client/collection/data-table.svelte.d.ts +11 -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/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 +113 -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 +23 -21
- package/dist/admin/components/fields/file-field.svelte +344 -30
- package/dist/admin/components/fields/media-field.svelte +16 -2
- 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 +9 -4
- 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 +172 -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/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 +37 -32
- package/dist/admin/remote/shop.remote.js +9 -2
- 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/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/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 +3 -3
- 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/fields/fieldSchemaToTs.d.ts +7 -0
- package/dist/core/fields/fieldSchemaToTs.js +234 -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.js +10 -17
- package/dist/core/server/generator/formFields.js +2 -1
- package/dist/core/server/generator/generator.js +4 -4
- package/dist/core/server/generator/utils.d.ts +1 -0
- package/dist/core/server/generator/utils.js +4 -0
- package/dist/paraglide/messages/_index.d.ts +3 -36
- package/dist/paraglide/messages/_index.js +3 -71
- package/dist/paraglide/messages/hello_world.d.ts +5 -0
- package/dist/paraglide/messages/hello_world.js +33 -0
- package/dist/paraglide/messages/login_hello.d.ts +16 -0
- package/dist/paraglide/messages/login_hello.js +34 -0
- package/dist/paraglide/messages/login_please_login.d.ts +16 -0
- package/dist/paraglide/messages/login_please_login.js +34 -0
- package/dist/shop/server/orders.d.ts +1 -0
- package/dist/shop/server/orders.js +14 -0
- package/dist/shop/server/shop-data.d.ts +2 -0
- package/dist/shop/server/shop-data.js +20 -5
- package/dist/sveltekit/server/handle.js +17 -0
- package/dist/types/cms.schema.js +4 -2
- package/dist/types/fields.d.ts +35 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/layout.d.ts +35 -2
- package/dist/updates/0.26.0/index.d.ts +2 -0
- package/dist/updates/0.26.0/index.js +51 -0
- package/dist/updates/index.js +3 -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
- package/dist/paraglide/messages/en.d.ts +0 -5
- package/dist/paraglide/messages/en.js +0 -14
- package/dist/paraglide/messages/pl.d.ts +0 -5
- package/dist/paraglide/messages/pl.js +0 -14
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { goto } from '$app/navigation';
|
|
2
|
+
import { goto, beforeNavigate } from '$app/navigation';
|
|
3
3
|
import { isCollection } from '../../utils/is-collection.js';
|
|
4
4
|
import { toast } from 'svelte-sonner';
|
|
5
5
|
import { getRemotes } from '../../context/remotes.js';
|
|
6
6
|
import { getBreadcrumbs } from '../../state/breadcrumbs.svelte.js';
|
|
7
7
|
import { getContentLanguage } from '../../state/content-language.svelte.js';
|
|
8
8
|
import EntryHeader from './entry-header.svelte';
|
|
9
|
+
import ConfirmationDialog from '../../components/dialogs/confirmation-dialog.svelte';
|
|
10
|
+
import FormErrorSummary, {
|
|
11
|
+
type FormErrorEntry
|
|
12
|
+
} from '../../components/forms/form-error-summary.svelte';
|
|
9
13
|
import { getLocalizedLabel } from '../../utils/collectionLabel.js';
|
|
14
|
+
import { scrollIntoViewWithin } from '../../utils/scrollWithin.js';
|
|
15
|
+
import { activateContainingTabs } from '../../utils/tabActivation.js';
|
|
16
|
+
import { activateContainingAccordions } from '../../utils/accordionActivation.js';
|
|
17
|
+
import { findFieldPathElement } from '../../utils/fieldPathElement.js';
|
|
18
|
+
import { flattenFormErrors } from '../../utils/flattenFormErrors.js';
|
|
10
19
|
import { useInterfaceLanguage } from '../../state/interface-language.svelte.js';
|
|
20
|
+
import { sidebarLang } from '../../components/layout/lang.js';
|
|
21
|
+
import { errorMessages } from '../../i18n/errors.js';
|
|
11
22
|
import type { InterfaceLanguage } from '../../../types/languages.js';
|
|
12
|
-
import AlertCircle from '@tabler/icons-svelte/icons/alert-circle';
|
|
13
23
|
import ArchiveIcon from '@tabler/icons-svelte/icons/archive';
|
|
14
|
-
import XIcon from '@tabler/icons-svelte/icons/x';
|
|
15
24
|
import Button from '../../../components/ui/button/button.svelte';
|
|
16
25
|
import { cn } from '../../../utils.js';
|
|
17
26
|
import { ElementSize, useDebounce } from 'runed';
|
|
@@ -21,60 +30,16 @@
|
|
|
21
30
|
import type { DbEntryVersion, RawEntry } from '../../../types/entries.js';
|
|
22
31
|
import EntryForm from './entry-form.svelte';
|
|
23
32
|
import type { UpdateEntryVersionCommandType } from '../../../core/server/entries/operations/update.js';
|
|
24
|
-
import {
|
|
25
|
-
import type { Field } from '../../../types/fields.js';
|
|
33
|
+
import { getCollectionEntryDisplayLabel } from '../../utils/entryLabel.js';
|
|
26
34
|
import { getFieldsFromConfig, hasLayout } from '../../../core/fields/layoutUtils.js';
|
|
27
|
-
import type { ValidationErrors } from 'sveltekit-superforms';
|
|
28
35
|
import { createHybridContext } from './hybrid/hybrid-context.svelte.js';
|
|
29
|
-
import { onMount, setContext } from 'svelte';
|
|
36
|
+
import { onMount, setContext, tick } from 'svelte';
|
|
30
37
|
import { get } from 'svelte/store';
|
|
31
38
|
const contentLanguage = getContentLanguage();
|
|
32
39
|
const remotes = getRemotes();
|
|
33
40
|
const interfaceLanguage = useInterfaceLanguage();
|
|
34
41
|
const hybridContext = createHybridContext();
|
|
35
42
|
|
|
36
|
-
function flattenErrors(
|
|
37
|
-
errors: ValidationErrors<Record<string, unknown>>,
|
|
38
|
-
fields: Field[],
|
|
39
|
-
prefix = ''
|
|
40
|
-
): string[] {
|
|
41
|
-
const result: string[] = [];
|
|
42
|
-
|
|
43
|
-
for (const [key, value] of Object.entries(errors)) {
|
|
44
|
-
if (key === '_errors') {
|
|
45
|
-
if (Array.isArray(value)) {
|
|
46
|
-
result.push(...value);
|
|
47
|
-
}
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
const path = prefix ? `${prefix}.${key}` : key;
|
|
51
|
-
|
|
52
|
-
// Find field label
|
|
53
|
-
const field = fields.find((f) => f.slug === key);
|
|
54
|
-
const label = field
|
|
55
|
-
? getLocalizedLabel(field.label, interfaceLanguage.current) || field.slug
|
|
56
|
-
: key;
|
|
57
|
-
|
|
58
|
-
if (Array.isArray(value)) {
|
|
59
|
-
// Direct error messages
|
|
60
|
-
result.push(`${label}: ${value.join(', ')}`);
|
|
61
|
-
} else if (typeof value === 'object' && value !== null) {
|
|
62
|
-
// Nested errors - recurse
|
|
63
|
-
const nestedFields = field && 'fields' in field ? (field.fields as Field[]) : [];
|
|
64
|
-
const nested = flattenErrors(
|
|
65
|
-
value as ValidationErrors<Record<string, unknown>>,
|
|
66
|
-
nestedFields,
|
|
67
|
-
path
|
|
68
|
-
);
|
|
69
|
-
if (nested.length > 0) {
|
|
70
|
-
result.push(...nested.map((e) => `${label} > ${e}`));
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return result;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
43
|
const lang: Record<
|
|
79
44
|
InterfaceLanguage,
|
|
80
45
|
{
|
|
@@ -86,6 +51,7 @@
|
|
|
86
51
|
publishToast: string;
|
|
87
52
|
scheduledToast: string;
|
|
88
53
|
unpublishToast: string;
|
|
54
|
+
cancelScheduledToast: string;
|
|
89
55
|
saveFailed: string;
|
|
90
56
|
cannotPublish: string;
|
|
91
57
|
validationHint: string;
|
|
@@ -93,6 +59,7 @@
|
|
|
93
59
|
switchToDraft: string;
|
|
94
60
|
editingDraft: string;
|
|
95
61
|
switchToPublished: string;
|
|
62
|
+
unsavedConfirm: string;
|
|
96
63
|
}
|
|
97
64
|
> = {
|
|
98
65
|
en: {
|
|
@@ -104,13 +71,15 @@
|
|
|
104
71
|
publishToast: 'Entry published successfully',
|
|
105
72
|
scheduledToast: 'Entry scheduled successfully',
|
|
106
73
|
unpublishToast: 'Publication withdrawn',
|
|
107
|
-
|
|
74
|
+
cancelScheduledToast: 'Scheduled update cancelled',
|
|
75
|
+
saveFailed: errorMessages.server.saveFailed.en,
|
|
108
76
|
cannotPublish: 'Cannot publish',
|
|
109
77
|
validationHint: 'Fix the highlighted fields below to publish',
|
|
110
78
|
newerDraft: 'A newer unpublished draft exists',
|
|
111
79
|
switchToDraft: 'Switch to draft',
|
|
112
80
|
editingDraft: 'You are editing an unpublished draft',
|
|
113
|
-
switchToPublished: 'View published'
|
|
81
|
+
switchToPublished: 'View published',
|
|
82
|
+
unsavedConfirm: 'You have unsaved changes. Leave without saving?'
|
|
114
83
|
},
|
|
115
84
|
pl: {
|
|
116
85
|
entryArchived: 'Wpis został zarchiwizowany pomyślnie',
|
|
@@ -121,13 +90,15 @@
|
|
|
121
90
|
publishToast: 'Wpis został pomyślnie opublikowany',
|
|
122
91
|
scheduledToast: 'Wpis został zaplanowany pomyślnie',
|
|
123
92
|
unpublishToast: 'Wycofano publikację',
|
|
124
|
-
|
|
93
|
+
cancelScheduledToast: 'Anulowano zaplanowaną aktualizację',
|
|
94
|
+
saveFailed: errorMessages.server.saveFailed.pl,
|
|
125
95
|
cannotPublish: 'Nie można opublikować',
|
|
126
96
|
validationHint: 'Popraw wyróżnione pola, żeby opublikować',
|
|
127
97
|
newerDraft: 'Istnieje nowszy nieopublikowany szkic',
|
|
128
98
|
switchToDraft: 'Przejdź do szkicu',
|
|
129
99
|
editingDraft: 'Edytujesz nieopublikowany szkic',
|
|
130
|
-
switchToPublished: 'Zobacz opublikowaną'
|
|
100
|
+
switchToPublished: 'Zobacz opublikowaną',
|
|
101
|
+
unsavedConfirm: 'Masz niezapisane zmiany. Opuścić stronę bez zapisywania?'
|
|
131
102
|
}
|
|
132
103
|
};
|
|
133
104
|
|
|
@@ -148,7 +119,7 @@
|
|
|
148
119
|
const collectionSchema = generateZodSchemaFromFields(
|
|
149
120
|
getFieldsFromConfig(collection),
|
|
150
121
|
contentLanguage.all,
|
|
151
|
-
{ localized: false }
|
|
122
|
+
{ localized: false, messageLanguage: interfaceLanguage.current }
|
|
152
123
|
);
|
|
153
124
|
const form = superForm(defaults(editingEntry.data, zod4(collectionSchema)), {
|
|
154
125
|
validators: zod4Client(collectionSchema),
|
|
@@ -157,35 +128,68 @@
|
|
|
157
128
|
resetForm: false
|
|
158
129
|
});
|
|
159
130
|
|
|
160
|
-
let validationErrors = $state<
|
|
131
|
+
let validationErrors = $state<FormErrorEntry[]>([]);
|
|
161
132
|
|
|
162
|
-
//
|
|
133
|
+
// Save state — manual save only (autosave removed in S8; re-introduce with
|
|
134
|
+
// proper draft-only guard tracked in ROADMAP backlog).
|
|
163
135
|
type SaveStatus = 'idle' | 'saving' | 'saved' | 'unsaved' | 'error';
|
|
164
136
|
let saveStatus = $state<SaveStatus>('idle');
|
|
165
|
-
let
|
|
166
|
-
|
|
167
|
-
//
|
|
137
|
+
let lastSavedAt = $state<Date | null>(null);
|
|
138
|
+
// Baseline for the "unsaved changes" check. `null` until the form has settled
|
|
139
|
+
// after mount / after a save — superForm + field components normalise values
|
|
140
|
+
// (e.g. dates, empty selects) asynchronously, and snapshotting before that
|
|
141
|
+
// would cause a spurious "unsaved" flag. While `settling`, form changes are
|
|
142
|
+
// treated as normalisation and ignored.
|
|
143
|
+
let lastSavedData = $state<string | null>(null);
|
|
144
|
+
let settling = true;
|
|
145
|
+
let settleQuietTimer: ReturnType<typeof setTimeout> | undefined;
|
|
146
|
+
let settleMaxTimer: ReturnType<typeof setTimeout> | undefined;
|
|
168
147
|
let savedDraftVersionId = $state<string | null>(null);
|
|
169
148
|
|
|
170
|
-
|
|
149
|
+
// Field components (TipTap, SEO auto-fill, blocks hydration, …) normalise
|
|
150
|
+
// their values into `$formData` asynchronously over several ticks after
|
|
151
|
+
// mount/save. We can't tell those apart from real edits by value, so instead
|
|
152
|
+
// we wait for the form to go *quiet* (no changes for `SETTLE_QUIET_MS`) and
|
|
153
|
+
// only then snapshot the baseline. A hard cap guards against a field that
|
|
154
|
+
// never stops emitting.
|
|
155
|
+
const SETTLE_QUIET_MS = 300;
|
|
156
|
+
const SETTLE_MAX_MS = 3000;
|
|
157
|
+
|
|
158
|
+
function finishSettling() {
|
|
159
|
+
clearTimeout(settleQuietTimer);
|
|
160
|
+
clearTimeout(settleMaxTimer);
|
|
161
|
+
lastSavedData = JSON.stringify(get(form.form));
|
|
162
|
+
settling = false;
|
|
163
|
+
}
|
|
171
164
|
|
|
172
|
-
function
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
165
|
+
function beginSettling() {
|
|
166
|
+
settling = true;
|
|
167
|
+
clearTimeout(settleQuietTimer);
|
|
168
|
+
clearTimeout(settleMaxTimer);
|
|
169
|
+
settleQuietTimer = setTimeout(finishSettling, SETTLE_QUIET_MS);
|
|
170
|
+
settleMaxTimer = setTimeout(finishSettling, SETTLE_MAX_MS);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function pokeSettling() {
|
|
174
|
+
// a change arrived while still settling → treat as normalisation, push
|
|
175
|
+
// the quiet window out (but keep the hard cap running).
|
|
176
|
+
clearTimeout(settleQuietTimer);
|
|
177
|
+
settleQuietTimer = setTimeout(finishSettling, SETTLE_QUIET_MS);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// After a save the form is "clean" again — re-settle so post-save
|
|
181
|
+
// normalisation (validated/transformed data echoed back) isn't flagged.
|
|
182
|
+
function markSaved() {
|
|
183
|
+
lastSavedAt = new Date();
|
|
184
|
+
saveStatus = 'saved';
|
|
185
|
+
beginSettling();
|
|
180
186
|
}
|
|
181
187
|
|
|
182
|
-
async function
|
|
188
|
+
async function saveDraft() {
|
|
183
189
|
if (isArchived) return;
|
|
184
190
|
const currentFormData = get(form.form);
|
|
185
191
|
const currentData = JSON.stringify(currentFormData);
|
|
186
|
-
if (currentData === lastSavedData)
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
192
|
+
if (currentData === lastSavedData && saveStatus !== 'error') return;
|
|
189
193
|
|
|
190
194
|
saveStatus = 'saving';
|
|
191
195
|
try {
|
|
@@ -196,29 +200,37 @@
|
|
|
196
200
|
data: currentFormData,
|
|
197
201
|
type: 'draft'
|
|
198
202
|
});
|
|
199
|
-
|
|
200
|
-
saveStatus = 'saved';
|
|
201
|
-
// If we're editing the published version and saved a draft, track it for the banner
|
|
203
|
+
markSaved();
|
|
202
204
|
const publishedVersion = entry.publishedVersions[currentLang];
|
|
203
205
|
if (publishedVersion && editingEntry.id === publishedVersion.id && result?.id) {
|
|
204
206
|
savedDraftVersionId = result.id;
|
|
205
207
|
}
|
|
206
|
-
// Reset to idle after 3s
|
|
207
|
-
setTimeout(() => {
|
|
208
|
-
if (saveStatus === 'saved') saveStatus = 'idle';
|
|
209
|
-
}, 3000);
|
|
210
208
|
} catch {
|
|
211
209
|
saveStatus = 'error';
|
|
212
210
|
}
|
|
213
211
|
}
|
|
214
212
|
|
|
215
|
-
//
|
|
213
|
+
// Warn before leaving with unsaved changes (in-app navigations).
|
|
214
|
+
beforeNavigate((nav) => {
|
|
215
|
+
if (saveStatus !== 'unsaved' || isArchived) return;
|
|
216
|
+
if (nav.type === 'leave') return; // browser close/reload — handled by beforeunload
|
|
217
|
+
if (!confirm(lang[interfaceLanguage.current].unsavedConfirm)) {
|
|
218
|
+
nav.cancel();
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Mark unsaved when form content diverges from the last-saved snapshot.
|
|
216
223
|
onMount(() => {
|
|
224
|
+
beginSettling();
|
|
225
|
+
|
|
217
226
|
const unsub = form.form.subscribe((data) => {
|
|
218
|
-
|
|
219
|
-
|
|
227
|
+
if (settling) {
|
|
228
|
+
pokeSettling();
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (lastSavedData === null || saveStatus === 'saving') return;
|
|
232
|
+
if (JSON.stringify(data) !== lastSavedData) {
|
|
220
233
|
saveStatus = 'unsaved';
|
|
221
|
-
scheduleAutosave();
|
|
222
234
|
}
|
|
223
235
|
});
|
|
224
236
|
|
|
@@ -226,19 +238,25 @@
|
|
|
226
238
|
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
|
|
227
239
|
e.preventDefault();
|
|
228
240
|
if (isArchived) return;
|
|
229
|
-
|
|
230
|
-
clearTimeout(autosaveTimer);
|
|
231
|
-
autosaveTimer = null;
|
|
232
|
-
}
|
|
233
|
-
performAutosave();
|
|
241
|
+
saveDraft();
|
|
234
242
|
}
|
|
235
243
|
}
|
|
236
244
|
window.addEventListener('keydown', handleKeydown);
|
|
237
245
|
|
|
246
|
+
function handleBeforeUnload(e: BeforeUnloadEvent) {
|
|
247
|
+
if (saveStatus === 'unsaved' && !isArchived) {
|
|
248
|
+
e.preventDefault();
|
|
249
|
+
e.returnValue = '';
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
window.addEventListener('beforeunload', handleBeforeUnload);
|
|
253
|
+
|
|
238
254
|
return () => {
|
|
255
|
+
clearTimeout(settleQuietTimer);
|
|
256
|
+
clearTimeout(settleMaxTimer);
|
|
239
257
|
unsub();
|
|
240
258
|
window.removeEventListener('keydown', handleKeydown);
|
|
241
|
-
|
|
259
|
+
window.removeEventListener('beforeunload', handleBeforeUnload);
|
|
242
260
|
};
|
|
243
261
|
});
|
|
244
262
|
|
|
@@ -255,14 +273,8 @@
|
|
|
255
273
|
}
|
|
256
274
|
|
|
257
275
|
async function onSave(type: UpdateEntryVersionCommandType, scheduledAt?: Date) {
|
|
258
|
-
// Cancel pending autosave
|
|
259
|
-
if (autosaveTimer) {
|
|
260
|
-
clearTimeout(autosaveTimer);
|
|
261
|
-
autosaveTimer = null;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
276
|
// Unpublish — skip validation, call remote directly
|
|
265
|
-
if (type === 'cancel-published') {
|
|
277
|
+
if (type === 'cancel-published' || type === 'cancel-scheduled') {
|
|
266
278
|
saveStatus = 'saving';
|
|
267
279
|
try {
|
|
268
280
|
await remotes.updateEntryVersionCommand({
|
|
@@ -271,12 +283,12 @@
|
|
|
271
283
|
data: get(form.form),
|
|
272
284
|
type
|
|
273
285
|
});
|
|
274
|
-
|
|
275
|
-
toast.success(
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
286
|
+
markSaved();
|
|
287
|
+
toast.success(
|
|
288
|
+
type === 'cancel-scheduled'
|
|
289
|
+
? lang[interfaceLanguage.current].cancelScheduledToast
|
|
290
|
+
: lang[interfaceLanguage.current].unpublishToast
|
|
291
|
+
);
|
|
280
292
|
} catch (e) {
|
|
281
293
|
console.error('Unpublish error:', e);
|
|
282
294
|
saveStatus = 'error';
|
|
@@ -302,8 +314,7 @@
|
|
|
302
314
|
scheduledAt
|
|
303
315
|
});
|
|
304
316
|
|
|
305
|
-
|
|
306
|
-
saveStatus = 'saved';
|
|
317
|
+
markSaved();
|
|
307
318
|
|
|
308
319
|
const toastMsg =
|
|
309
320
|
type === 'published-now'
|
|
@@ -311,25 +322,57 @@
|
|
|
311
322
|
: lang[interfaceLanguage.current].scheduledToast;
|
|
312
323
|
toast.success(toastMsg);
|
|
313
324
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
325
|
+
// Publishing/scheduling from a pinned ?version (e.g. a newer draft)
|
|
326
|
+
// leaves the editor on that version, so the "editing an unpublished
|
|
327
|
+
// draft" banner stays. Drop the param so the editor resolves back to
|
|
328
|
+
// the canonical (published) version for the language.
|
|
329
|
+
const url = new URL(window.location.href);
|
|
330
|
+
if (url.searchParams.has('version')) {
|
|
331
|
+
url.searchParams.delete('version');
|
|
332
|
+
goto(url.pathname + url.search);
|
|
333
|
+
}
|
|
317
334
|
} catch (e) {
|
|
318
335
|
console.error('Publish error:', e);
|
|
319
336
|
saveStatus = 'error';
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
337
|
+
const body = (e as { body?: { message?: string; code?: string; path?: string } })
|
|
338
|
+
?.body;
|
|
339
|
+
const message =
|
|
340
|
+
body?.message ?? (e instanceof Error ? e.message : undefined);
|
|
341
|
+
|
|
342
|
+
// Server-side validation that pinpoints a field (e.g. SLUG_DUPLICATE)
|
|
343
|
+
// — surface it both inline (form.errors → Form.FieldErrors) and in
|
|
344
|
+
// the error summary banner.
|
|
345
|
+
if (body?.path && body.message) {
|
|
346
|
+
const path = body.path;
|
|
347
|
+
const msg = body.message;
|
|
348
|
+
form.errors.update(($errors) => {
|
|
349
|
+
const parts = path.split('.');
|
|
350
|
+
const root = $errors as unknown as Record<string, unknown>;
|
|
351
|
+
let cursor = root;
|
|
352
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
353
|
+
const key = parts[i];
|
|
354
|
+
if (typeof cursor[key] !== 'object' || cursor[key] === null) {
|
|
355
|
+
cursor[key] = {};
|
|
356
|
+
}
|
|
357
|
+
cursor = cursor[key] as Record<string, unknown>;
|
|
358
|
+
}
|
|
359
|
+
cursor[parts[parts.length - 1]] = [msg];
|
|
360
|
+
return $errors;
|
|
361
|
+
});
|
|
362
|
+
validationErrors = [{ path, message: msg }];
|
|
363
|
+
}
|
|
327
364
|
|
|
328
|
-
|
|
329
|
-
const firstErrorKey = Object.keys(validatedForm.errors).find((k) => k !== '_errors');
|
|
330
|
-
if (firstErrorKey) {
|
|
331
|
-
scrollToIssue(firstErrorKey);
|
|
365
|
+
toast.error(message ?? lang[interfaceLanguage.current].saveFailed);
|
|
332
366
|
}
|
|
367
|
+
} else {
|
|
368
|
+
// The error summary takes focus and scrolls itself into view; the user
|
|
369
|
+
// reviews the full list and clicks an entry to jump to a field.
|
|
370
|
+
validationErrors = flattenFormErrors(
|
|
371
|
+
validatedForm.errors as Record<string, unknown>,
|
|
372
|
+
getFieldsFromConfig(collection),
|
|
373
|
+
validatedForm.data,
|
|
374
|
+
interfaceLanguage.current
|
|
375
|
+
);
|
|
333
376
|
}
|
|
334
377
|
}
|
|
335
378
|
|
|
@@ -338,6 +381,9 @@
|
|
|
338
381
|
$effect(() => {
|
|
339
382
|
breadcrumbs.state = isCollection(collection)
|
|
340
383
|
? [
|
|
384
|
+
{
|
|
385
|
+
label: sidebarLang[interfaceLanguage.current].collections.title
|
|
386
|
+
},
|
|
341
387
|
{
|
|
342
388
|
label:
|
|
343
389
|
getLocalizedLabel(collection.labels?.plural, interfaceLanguage.current) ||
|
|
@@ -345,7 +391,13 @@
|
|
|
345
391
|
href: `/admin/collections/${collection.slug}`
|
|
346
392
|
},
|
|
347
393
|
{
|
|
348
|
-
|
|
394
|
+
// breadcrumb title follows the UI language (with cross-language fallback);
|
|
395
|
+
// untitled entries show a placeholder instead of the raw id
|
|
396
|
+
label: getCollectionEntryDisplayLabel(
|
|
397
|
+
entry,
|
|
398
|
+
collection,
|
|
399
|
+
interfaceLanguage.current
|
|
400
|
+
)
|
|
349
401
|
}
|
|
350
402
|
]
|
|
351
403
|
: [
|
|
@@ -520,14 +572,25 @@
|
|
|
520
572
|
|
|
521
573
|
const draftVersionId = $derived(currentDraftVersion?.id ?? savedDraftVersionId);
|
|
522
574
|
|
|
523
|
-
const scrollToIssue = (
|
|
524
|
-
|
|
575
|
+
const scrollToIssue = async (fieldPath: string) => {
|
|
576
|
+
// Resolve the deepest rendered ancestor of the (possibly nested) path —
|
|
577
|
+
// a leaf like `highlights.0.title` lives inside block/layout accordions
|
|
578
|
+
// that must be expanded before it can be measured / scrolled to.
|
|
579
|
+
const fieldEl = findFieldPathElement(fieldPath);
|
|
525
580
|
if (fieldEl) {
|
|
526
|
-
|
|
581
|
+
// Reveal the tab / accordion the field lives on (no-op if not nested
|
|
582
|
+
// inside one) before measuring geometry / scrolling. Tabs first so
|
|
583
|
+
// nested accordion-in-tab works in one pass; both are idempotent.
|
|
584
|
+
activateContainingTabs(fieldEl);
|
|
585
|
+
const openedAccordion = activateContainingAccordions(fieldEl);
|
|
586
|
+
await tick();
|
|
587
|
+
// A just-opened accordion expands via a ~200ms grid-template-rows
|
|
588
|
+
// transition; wait it out so the field's final position is settled
|
|
589
|
+
// before scrolling (otherwise the scroll lands mid-animation).
|
|
590
|
+
if (openedAccordion) await new Promise((resolve) => setTimeout(resolve, 240));
|
|
591
|
+
scrollIntoViewWithin(fieldEl);
|
|
527
592
|
const proseMirror = fieldEl.querySelector('.ProseMirror') as HTMLElement | null;
|
|
528
|
-
|
|
529
|
-
proseMirror.focus();
|
|
530
|
-
}
|
|
593
|
+
proseMirror?.focus({ preventScroll: true });
|
|
531
594
|
}
|
|
532
595
|
};
|
|
533
596
|
|
|
@@ -544,30 +607,74 @@
|
|
|
544
607
|
const t = $derived(lang[interfaceLanguage.current]);
|
|
545
608
|
const isHybrid = $derived(hybridContext.mode === 'hybrid' && !!collection.previewUrl);
|
|
546
609
|
|
|
610
|
+
const errorPaths = $derived(new Set(validationErrors.map((e) => e.path)));
|
|
611
|
+
|
|
612
|
+
let copyFromLangOpen = $state(false);
|
|
613
|
+
let pendingCopyLang = $state<string | null>(null);
|
|
614
|
+
|
|
547
615
|
function onCopyFromLang(sourceLang: string) {
|
|
548
616
|
const sourceVersion =
|
|
549
617
|
entry.draftVersions[sourceLang] ?? entry.publishedVersions[sourceLang];
|
|
550
618
|
if (!sourceVersion?.data) return;
|
|
619
|
+
pendingCopyLang = sourceLang;
|
|
620
|
+
copyFromLangOpen = true;
|
|
621
|
+
}
|
|
551
622
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
623
|
+
function performCopyFromLang() {
|
|
624
|
+
if (!pendingCopyLang) return;
|
|
625
|
+
const sourceVersion =
|
|
626
|
+
entry.draftVersions[pendingCopyLang] ?? entry.publishedVersions[pendingCopyLang];
|
|
627
|
+
if (sourceVersion?.data) {
|
|
628
|
+
form.form.set(sourceVersion.data);
|
|
629
|
+
}
|
|
630
|
+
pendingCopyLang = null;
|
|
560
631
|
}
|
|
632
|
+
|
|
633
|
+
const copyDialogText = $derived.by(() => {
|
|
634
|
+
const target = contentLanguage.current.toUpperCase();
|
|
635
|
+
const source = (pendingCopyLang ?? '').toUpperCase();
|
|
636
|
+
if (interfaceLanguage.current === 'pl') {
|
|
637
|
+
return {
|
|
638
|
+
title: `Skopiować treść z ${source}?`,
|
|
639
|
+
description: `Aktualne dane w wersji ${target} zostaną zastąpione kopią z wersji ${source}. Możesz cofnąć tę akcję, dopóki nie zapiszesz zmian.`,
|
|
640
|
+
confirmLabel: 'Skopiuj'
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
return {
|
|
644
|
+
title: `Copy content from ${source}?`,
|
|
645
|
+
description: `Current data in the ${target} version will be replaced with a copy from ${source}. You can revert as long as you don't save.`,
|
|
646
|
+
confirmLabel: 'Copy'
|
|
647
|
+
};
|
|
648
|
+
});
|
|
561
649
|
</script>
|
|
562
650
|
|
|
651
|
+
<svelte:window
|
|
652
|
+
onbeforeunload={(e) => {
|
|
653
|
+
if (saveStatus === 'unsaved') {
|
|
654
|
+
e.preventDefault();
|
|
655
|
+
return '';
|
|
656
|
+
}
|
|
657
|
+
}}
|
|
658
|
+
/>
|
|
659
|
+
|
|
660
|
+
<ConfirmationDialog
|
|
661
|
+
bind:open={copyFromLangOpen}
|
|
662
|
+
title={copyDialogText.title}
|
|
663
|
+
description={copyDialogText.description}
|
|
664
|
+
confirmLabel={copyDialogText.confirmLabel}
|
|
665
|
+
variant="default"
|
|
666
|
+
onConfirm={performCopyFromLang}
|
|
667
|
+
/>
|
|
668
|
+
|
|
563
669
|
<div class={isHybrid ? 'flex h-full flex-col overflow-hidden' : ''}>
|
|
564
670
|
<EntryHeader
|
|
565
671
|
{entry}
|
|
566
672
|
version={editingEntry}
|
|
567
673
|
{onSave}
|
|
568
|
-
onSaveDraft={
|
|
674
|
+
onSaveDraft={saveDraft}
|
|
569
675
|
{onArchive}
|
|
570
676
|
{saveStatus}
|
|
677
|
+
{lastSavedAt}
|
|
571
678
|
{isArchived}
|
|
572
679
|
fields={getFieldsFromConfig(collection)}
|
|
573
680
|
getFormData={() => get(form.form)}
|
|
@@ -576,29 +683,13 @@
|
|
|
576
683
|
/>
|
|
577
684
|
|
|
578
685
|
{#if validationErrors.length > 0}
|
|
579
|
-
<div
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
<p class="font-semibold">{t.cannotPublish}</p>
|
|
587
|
-
<p class="text-xs opacity-80">{t.validationHint}</p>
|
|
588
|
-
<ul class="mt-1 text-xs">
|
|
589
|
-
{#each validationErrors.slice(0, 5) as error}
|
|
590
|
-
<li>— {error}</li>
|
|
591
|
-
{/each}
|
|
592
|
-
</ul>
|
|
593
|
-
</div>
|
|
594
|
-
<button
|
|
595
|
-
type="button"
|
|
596
|
-
onclick={() => (validationErrors = [])}
|
|
597
|
-
class="shrink-0 rounded p-0.5 opacity-60 hover:opacity-100"
|
|
598
|
-
aria-label="Zamknij"
|
|
599
|
-
>
|
|
600
|
-
<XIcon class="size-4" />
|
|
601
|
-
</button>
|
|
686
|
+
<div class="border-b px-6 py-3">
|
|
687
|
+
<FormErrorSummary
|
|
688
|
+
errors={validationErrors}
|
|
689
|
+
title={t.cannotPublish}
|
|
690
|
+
onFocusField={(path) => scrollToIssue(path)}
|
|
691
|
+
onDismiss={() => (validationErrors = [])}
|
|
692
|
+
/>
|
|
602
693
|
</div>
|
|
603
694
|
{/if}
|
|
604
695
|
|
|
@@ -673,12 +764,25 @@
|
|
|
673
764
|
{/await}
|
|
674
765
|
{/snippet}
|
|
675
766
|
{#snippet formPanel()}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
767
|
+
{#if hasLayout(collection)}
|
|
768
|
+
<EntryForm
|
|
769
|
+
{form}
|
|
770
|
+
{entry}
|
|
771
|
+
{errorPaths}
|
|
772
|
+
focusedPath={hybridContext.focusedPath}
|
|
773
|
+
onPathSelect={(path) => (hybridContext.focusedPath = path)}
|
|
774
|
+
/>
|
|
775
|
+
{:else}
|
|
776
|
+
<div class="p-3 lg:p-4">
|
|
777
|
+
<EntryForm
|
|
778
|
+
{form}
|
|
779
|
+
{entry}
|
|
780
|
+
{errorPaths}
|
|
781
|
+
focusedPath={hybridContext.focusedPath}
|
|
782
|
+
onPathSelect={(path) => (hybridContext.focusedPath = path)}
|
|
783
|
+
/>
|
|
784
|
+
</div>
|
|
785
|
+
{/if}
|
|
682
786
|
{/snippet}
|
|
683
787
|
</HybridLayout>
|
|
684
788
|
{:catch}
|
|
@@ -686,13 +790,13 @@
|
|
|
686
790
|
{/await}
|
|
687
791
|
{:else if hasLayout(collection)}
|
|
688
792
|
<div class="overflow-y-auto" style="scroll-padding-top: 48px;">
|
|
689
|
-
<EntryForm {form} {entry} />
|
|
793
|
+
<EntryForm {form} {entry} {errorPaths} />
|
|
690
794
|
</div>
|
|
691
795
|
{:else}
|
|
692
796
|
<div class="flex items-stretch justify-center" style="scroll-padding-top: 48px;">
|
|
693
797
|
<div class="max-w-2xl grow p-4 lg:p-6">
|
|
694
798
|
<div class="bg-card rounded-2xl border p-4 shadow-sm lg:p-6">
|
|
695
|
-
<EntryForm {form} {entry} />
|
|
799
|
+
<EntryForm {form} {entry} {errorPaths} />
|
|
696
800
|
</div>
|
|
697
801
|
</div>
|
|
698
802
|
</div>
|