@xleddyl/nuxt-cms 0.9.4 → 0.10.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/README.md +1 -1
- package/dist/module.d.mts +20 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +1037 -148
- package/dist/runtime/app/blocks/CmsBlocks.d.vue.ts +1 -1
- package/dist/runtime/app/blocks/CmsBlocks.vue +57 -9
- package/dist/runtime/app/blocks/CmsBlocks.vue.d.ts +1 -1
- package/dist/runtime/app/blocks/CmsField.d.ts +24 -0
- package/dist/runtime/app/blocks/CmsField.js +16 -0
- package/dist/runtime/app/blocks/node.d.ts +37 -0
- package/dist/runtime/app/blocks/node.js +17 -0
- package/dist/runtime/app/blocks/resolve.d.ts +18 -0
- package/dist/runtime/app/blocks/resolve.js +34 -0
- package/dist/runtime/app/components/cms/BlockPicker.d.vue.ts +22 -0
- package/dist/runtime/app/components/cms/BlockPicker.vue +87 -0
- package/dist/runtime/app/components/cms/BlockPicker.vue.d.ts +22 -0
- package/dist/runtime/app/components/cms/BlockPickerPopover.d.vue.ts +38 -0
- package/dist/runtime/app/components/cms/BlockPickerPopover.vue +44 -0
- package/dist/runtime/app/components/cms/BlockPickerPopover.vue.d.ts +38 -0
- package/dist/runtime/app/components/cms/BlockTree.d.vue.ts +29 -0
- package/dist/runtime/app/components/cms/BlockTree.vue +322 -0
- package/dist/runtime/app/components/cms/BlockTree.vue.d.ts +29 -0
- package/dist/runtime/app/components/cms/BlocksField.vue +569 -218
- package/dist/runtime/app/components/cms/BrandMark.vue +7 -13
- package/dist/runtime/app/components/cms/ConfirmGroups.vue +15 -13
- package/dist/runtime/app/components/cms/{BlockPalette.vue.d.ts → EditorHistory.d.vue.ts} +6 -5
- package/dist/runtime/app/components/cms/EditorHistory.vue +34 -0
- package/dist/runtime/app/components/cms/{BlockPalette.d.vue.ts → EditorHistory.vue.d.ts} +6 -5
- package/dist/runtime/app/components/cms/EntryDrawer.d.vue.ts +11 -5
- package/dist/runtime/app/components/cms/EntryDrawer.vue +183 -152
- package/dist/runtime/app/components/cms/EntryDrawer.vue.d.ts +11 -5
- package/dist/runtime/app/components/cms/EntryForm.d.vue.ts +16 -4
- package/dist/runtime/app/components/cms/EntryForm.vue +106 -146
- package/dist/runtime/app/components/cms/EntryForm.vue.d.ts +16 -4
- package/dist/runtime/app/components/cms/FieldInput.vue +10 -2
- package/dist/runtime/app/components/cms/FormSections.d.vue.ts +22 -0
- package/dist/runtime/app/components/cms/FormSections.vue +71 -0
- package/dist/runtime/app/components/cms/FormSections.vue.d.ts +22 -0
- package/dist/runtime/app/components/cms/ImportModal.vue +46 -58
- package/dist/runtime/app/components/cms/LinkField.d.vue.ts +16 -0
- package/dist/runtime/app/components/cms/LinkField.vue +250 -0
- package/dist/runtime/app/components/cms/LinkField.vue.d.ts +16 -0
- package/dist/runtime/app/components/cms/ListFilters.vue +10 -12
- package/dist/runtime/app/components/cms/LocaleSwitch.d.vue.ts +2 -1
- package/dist/runtime/app/components/cms/LocaleSwitch.vue +29 -5
- package/dist/runtime/app/components/cms/LocaleSwitch.vue.d.ts +2 -1
- package/dist/runtime/app/components/cms/MediaDetail.d.vue.ts +2 -0
- package/dist/runtime/app/components/cms/MediaDetail.vue +112 -83
- package/dist/runtime/app/components/cms/MediaDetail.vue.d.ts +2 -0
- package/dist/runtime/app/components/cms/MediaField.vue +35 -25
- package/dist/runtime/app/components/cms/MediaFolderPicker.vue +26 -71
- package/dist/runtime/app/components/cms/MediaGallery.d.vue.ts +1 -0
- package/dist/runtime/app/components/cms/MediaGallery.vue +442 -419
- package/dist/runtime/app/components/cms/MediaGallery.vue.d.ts +1 -0
- package/dist/runtime/app/components/cms/MediaThumb.vue +9 -25
- package/dist/runtime/app/components/cms/MediaUpload.vue +1 -1
- package/dist/runtime/app/components/cms/{BlockOutline.d.vue.ts → NestedBlockList.d.vue.ts} +5 -5
- package/dist/runtime/app/components/cms/NestedBlockList.vue +87 -0
- package/dist/runtime/app/components/cms/{BlockOutline.vue.d.ts → NestedBlockList.vue.d.ts} +5 -5
- package/dist/runtime/app/components/cms/PoweredBy.vue +12 -5
- package/dist/runtime/app/components/cms/PresetModal.d.vue.ts +18 -0
- package/dist/runtime/app/components/cms/PresetModal.vue +70 -0
- package/dist/runtime/app/components/cms/PresetModal.vue.d.ts +18 -0
- package/dist/runtime/app/components/cms/RelationField.vue +2 -8
- package/dist/runtime/app/components/cms/SettingsAccount.vue +60 -71
- package/dist/runtime/app/components/cms/SettingsColumns.vue +47 -69
- package/dist/runtime/app/components/cms/SettingsEmptyAdd.d.vue.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsEmptyAdd.vue +21 -0
- package/dist/runtime/app/components/cms/SettingsEmptyAdd.vue.d.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsEntries.d.vue.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsEntries.vue +50 -0
- package/dist/runtime/app/components/cms/SettingsEntries.vue.d.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsEntry.d.vue.ts +1 -10
- package/dist/runtime/app/components/cms/SettingsEntry.vue +11 -95
- package/dist/runtime/app/components/cms/SettingsEntry.vue.d.ts +1 -10
- package/dist/runtime/app/components/cms/SettingsEntryActions.d.vue.ts +7 -0
- package/dist/runtime/app/components/cms/SettingsEntryActions.vue +42 -0
- package/dist/runtime/app/components/cms/SettingsEntryActions.vue.d.ts +7 -0
- package/dist/runtime/app/components/cms/SettingsEntryTabs.d.vue.ts +7 -0
- package/dist/runtime/app/components/cms/SettingsEntryTabs.vue +21 -0
- package/dist/runtime/app/components/cms/SettingsEntryTabs.vue.d.ts +7 -0
- package/dist/runtime/app/components/cms/SettingsFormAction.d.vue.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsFormAction.vue +17 -0
- package/dist/runtime/app/components/cms/SettingsFormAction.vue.d.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsLayout.vue +18 -14
- package/dist/runtime/app/components/cms/SettingsPresetForm.d.vue.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsPresetForm.vue +60 -0
- package/dist/runtime/app/components/cms/SettingsPresetForm.vue.d.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsPresets.d.vue.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsPresets.vue +65 -0
- package/dist/runtime/app/components/cms/SettingsPresets.vue.d.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsRecordActions.d.vue.ts +32 -0
- package/dist/runtime/app/components/cms/SettingsRecordActions.vue +45 -0
- package/dist/runtime/app/components/cms/SettingsRecordActions.vue.d.ts +32 -0
- package/dist/runtime/app/components/cms/SettingsRedirectActions.d.vue.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsRedirectActions.vue +59 -0
- package/dist/runtime/app/components/cms/SettingsRedirectActions.vue.d.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsRedirectForm.d.vue.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsRedirectForm.vue +77 -0
- package/dist/runtime/app/components/cms/SettingsRedirectForm.vue.d.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsRedirects.d.vue.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsRedirects.vue +117 -0
- package/dist/runtime/app/components/cms/SettingsRedirects.vue.d.ts +13 -0
- package/dist/runtime/app/components/cms/SettingsSecretCard.d.vue.ts +17 -0
- package/dist/runtime/app/components/cms/SettingsSecretCard.vue +43 -0
- package/dist/runtime/app/components/cms/SettingsSecretCard.vue.d.ts +17 -0
- package/dist/runtime/app/components/cms/SettingsSite.d.vue.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsSite.vue +140 -0
- package/dist/runtime/app/components/cms/SettingsSite.vue.d.ts +3 -0
- package/dist/runtime/app/components/cms/SettingsUserCreate.d.vue.ts +8 -0
- package/dist/runtime/app/components/cms/SettingsUserCreate.vue +82 -0
- package/dist/runtime/app/components/cms/SettingsUserCreate.vue.d.ts +8 -0
- package/dist/runtime/app/components/cms/SettingsUserDetail.d.vue.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsUserDetail.vue +179 -0
- package/dist/runtime/app/components/cms/SettingsUserDetail.vue.d.ts +11 -0
- package/dist/runtime/app/components/cms/SettingsUsers.d.vue.ts +9 -6
- package/dist/runtime/app/components/cms/SettingsUsers.vue +88 -234
- package/dist/runtime/app/components/cms/SettingsUsers.vue.d.ts +9 -6
- package/dist/runtime/app/components/cms/SharePreview.d.vue.ts +18 -0
- package/dist/runtime/app/components/cms/SharePreview.vue +219 -0
- package/dist/runtime/app/components/cms/SharePreview.vue.d.ts +18 -0
- package/dist/runtime/app/components/cms/SlugField.vue +5 -3
- package/dist/runtime/app/components/cms/StatusBadge.vue +9 -8
- package/dist/runtime/app/components/cms/TranslationBadges.d.vue.ts +7 -0
- package/dist/runtime/app/components/cms/TranslationBadges.vue +45 -0
- package/dist/runtime/app/components/cms/TranslationBadges.vue.d.ts +7 -0
- package/dist/runtime/app/components/cms/TranslationMenu.d.vue.ts +17 -0
- package/dist/runtime/app/components/cms/TranslationMenu.vue +28 -0
- package/dist/runtime/app/components/cms/TranslationMenu.vue.d.ts +17 -0
- package/dist/runtime/app/components/cms/VisualEditor.d.vue.ts +22 -4
- package/dist/runtime/app/components/cms/VisualEditor.vue +1152 -335
- package/dist/runtime/app/components/cms/VisualEditor.vue.d.ts +22 -4
- package/dist/runtime/app/components/cms/WelcomeModal.vue +32 -50
- package/dist/runtime/app/composables/cms-clipboard.d.ts +8 -0
- package/dist/runtime/app/composables/cms-clipboard.js +38 -0
- package/dist/runtime/app/composables/cms-confirm.d.ts +6 -8
- package/dist/runtime/app/composables/cms-confirm.js +5 -7
- package/dist/runtime/app/composables/cms-entry-disabled.d.ts +5 -1
- package/dist/runtime/app/composables/cms-entry-disabled.js +7 -0
- package/dist/runtime/app/composables/cms-entry-form.d.ts +15 -0
- package/dist/runtime/app/composables/cms-entry-form.js +40 -0
- package/dist/runtime/app/composables/cms-entry-settings.d.ts +30 -0
- package/dist/runtime/app/composables/cms-entry-settings.js +122 -0
- package/dist/runtime/app/composables/cms-entry.d.ts +5 -1
- package/dist/runtime/app/composables/cms-entry.js +36 -11
- package/dist/runtime/app/composables/cms-history.d.ts +10 -0
- package/dist/runtime/app/composables/cms-history.js +24 -0
- package/dist/runtime/app/composables/cms-link-targets.d.ts +10 -0
- package/dist/runtime/app/composables/cms-link-targets.js +40 -0
- package/dist/runtime/app/composables/cms-link-usage.d.ts +1 -0
- package/dist/runtime/app/composables/cms-link-usage.js +45 -0
- package/dist/runtime/app/composables/cms-media-library.d.ts +19 -9
- package/dist/runtime/app/composables/cms-media-library.js +31 -13
- package/dist/runtime/app/composables/cms-media-uploader.js +6 -20
- package/dist/runtime/app/composables/cms-permissions.d.ts +18 -0
- package/dist/runtime/app/composables/cms-permissions.js +54 -0
- package/dist/runtime/app/composables/cms-presets.d.ts +11 -0
- package/dist/runtime/app/composables/cms-presets.js +40 -0
- package/dist/runtime/app/composables/cms-preview-mode.d.ts +4 -0
- package/dist/runtime/app/composables/cms-preview-mode.js +8 -0
- package/dist/runtime/app/composables/cms-query.d.ts +3 -0
- package/dist/runtime/app/composables/cms-query.js +45 -20
- package/dist/runtime/app/composables/cms-record-list.d.ts +31 -0
- package/dist/runtime/app/composables/cms-record-list.js +75 -0
- package/dist/runtime/app/composables/cms-route.d.ts +6 -0
- package/dist/runtime/app/composables/cms-route.js +36 -0
- package/dist/runtime/app/composables/cms-runtime.d.ts +1 -0
- package/dist/runtime/app/composables/cms-save-handoff.d.ts +4 -0
- package/dist/runtime/app/composables/cms-save-handoff.js +19 -0
- package/dist/runtime/app/composables/cms-seo.d.ts +12 -0
- package/dist/runtime/app/composables/cms-seo.js +69 -0
- package/dist/runtime/app/composables/cms-settings-form.d.ts +13 -0
- package/dist/runtime/app/composables/cms-settings-form.js +29 -0
- package/dist/runtime/app/composables/cms-settings-sections.d.ts +26 -0
- package/dist/runtime/app/composables/cms-settings-sections.js +181 -0
- package/dist/runtime/app/composables/cms-settings-stores.d.ts +52 -0
- package/dist/runtime/app/composables/cms-settings-stores.js +156 -0
- package/dist/runtime/app/composables/cms-settings.d.ts +3 -3
- package/dist/runtime/app/composables/cms-settings.js +14 -8
- package/dist/runtime/app/composables/cms-site-disabled.d.ts +7 -0
- package/dist/runtime/app/composables/cms-site-disabled.js +8 -0
- package/dist/runtime/app/composables/cms-site.d.ts +7 -0
- package/dist/runtime/app/composables/cms-site.js +22 -0
- package/dist/runtime/app/composables/cms-toast.d.ts +2 -8
- package/dist/runtime/app/composables/cms-toast.js +3 -9
- package/dist/runtime/app/composables/cms-translation-actions.d.ts +15 -0
- package/dist/runtime/app/composables/cms-translation-actions.js +108 -0
- package/dist/runtime/app/css-url.d.ts +4 -0
- package/dist/runtime/app/image/CmsImage.d.vue.ts +89 -0
- package/dist/runtime/app/image/CmsImage.vue +91 -0
- package/dist/runtime/app/image/CmsImage.vue.d.ts +89 -0
- package/dist/runtime/app/layouts/cms-admin.d.vue.ts +2 -2
- package/dist/runtime/app/layouts/cms-admin.vue +98 -76
- package/dist/runtime/app/layouts/cms-admin.vue.d.ts +2 -2
- package/dist/runtime/app/layouts/cms-editor.vue +7 -2
- package/dist/runtime/app/links/CmsLink.d.vue.ts +18 -0
- package/dist/runtime/app/links/CmsLink.vue +18 -0
- package/dist/runtime/app/links/CmsLink.vue.d.ts +18 -0
- package/dist/runtime/app/middleware/cms-auth.js +13 -1
- package/dist/runtime/app/pages/admin-collection.vue +473 -297
- package/dist/runtime/app/pages/admin-content.vue +190 -53
- package/dist/runtime/app/pages/admin-entry.vue +79 -75
- package/dist/runtime/app/pages/admin-index.vue +18 -5
- package/dist/runtime/app/pages/admin-login.vue +1 -1
- package/dist/runtime/app/pages/admin-logout.vue +1 -1
- package/dist/runtime/app/pages/admin-media.vue +3 -3
- package/dist/runtime/app/pages/admin-preview.vue +112 -24
- package/dist/runtime/app/plugins/cms-field.d.ts +2 -0
- package/dist/runtime/app/plugins/cms-field.js +8 -0
- package/dist/runtime/app/plugins/cms-preview-banner.client.d.ts +2 -0
- package/dist/runtime/app/plugins/cms-preview-banner.client.js +19 -0
- package/dist/runtime/app/plugins/cms-preview.d.ts +2 -0
- package/dist/runtime/app/plugins/cms-preview.js +27 -0
- package/dist/runtime/app/preview/CmsPreviewBanner.d.vue.ts +7 -0
- package/dist/runtime/app/preview/CmsPreviewBanner.vue +63 -0
- package/dist/runtime/app/preview/CmsPreviewBanner.vue.d.ts +7 -0
- package/dist/runtime/app/preview/PreviewOverlay.d.vue.ts +17 -3
- package/dist/runtime/app/preview/PreviewOverlay.vue +629 -166
- package/dist/runtime/app/preview/PreviewOverlay.vue.d.ts +17 -3
- package/dist/runtime/app/preview/field-marker.d.ts +12 -0
- package/dist/runtime/app/preview/field-marker.js +20 -0
- package/dist/runtime/app/preview/inline-text.d.ts +15 -0
- package/dist/runtime/app/preview/inline-text.js +158 -0
- package/dist/runtime/app/utils/block-editor.d.ts +22 -0
- package/dist/runtime/app/utils/block-editor.js +12 -0
- package/dist/runtime/app/utils/keyboard.d.ts +6 -0
- package/dist/runtime/app/utils/keyboard.js +52 -0
- package/dist/runtime/app/utils/redirects.d.ts +6 -0
- package/dist/runtime/app/utils/redirects.js +7 -0
- package/dist/runtime/app/utils/ui.d.ts +8 -0
- package/dist/runtime/app/utils/ui.js +15 -1
- package/dist/runtime/app/utils/users.d.ts +15 -0
- package/dist/runtime/app/utils/users.js +14 -0
- package/dist/runtime/assets/cms.css +1 -1
- package/dist/runtime/generate.js +16 -2
- package/dist/runtime/server/api/collection-export.get.js +3 -2
- package/dist/runtime/server/api/collection-import.post.js +39 -11
- package/dist/runtime/server/api/collection.get.js +27 -9
- package/dist/runtime/server/api/collection.post.js +28 -11
- package/dist/runtime/server/api/collection.put.js +35 -13
- package/dist/runtime/server/api/item.delete.js +19 -7
- package/dist/runtime/server/api/item.get.js +3 -2
- package/dist/runtime/server/api/item.put.js +52 -18
- package/dist/runtime/server/api/link-usage.post.d.ts +5 -0
- package/dist/runtime/server/api/link-usage.post.js +21 -0
- package/dist/runtime/server/api/media-delete.post.js +4 -2
- package/dist/runtime/server/api/media-download.get.js +2 -2
- package/dist/runtime/server/api/media-folder.delete.js +8 -2
- package/dist/runtime/server/api/media-folder.patch.js +10 -2
- package/dist/runtime/server/api/media-folder.post.js +4 -2
- package/dist/runtime/server/api/media-move.post.js +7 -4
- package/dist/runtime/server/api/media-presign.post.js +2 -2
- package/dist/runtime/server/api/media-upload.put.js +2 -2
- package/dist/runtime/server/api/media-usage.post.d.ts +2 -4
- package/dist/runtime/server/api/media-usage.post.js +13 -9
- package/dist/runtime/server/api/media.delete.js +4 -2
- package/dist/runtime/server/api/media.get.js +22 -7
- package/dist/runtime/server/api/media.post.js +4 -2
- package/dist/runtime/server/api/media.put.js +33 -16
- package/dist/runtime/server/api/preset.delete.d.ts +4 -0
- package/dist/runtime/server/api/preset.delete.js +9 -0
- package/dist/runtime/server/api/preset.put.d.ts +3 -0
- package/dist/runtime/server/api/preset.put.js +23 -0
- package/dist/runtime/server/api/presets-field.get.d.ts +3 -0
- package/dist/runtime/server/api/presets-field.get.js +13 -0
- package/dist/runtime/server/api/presets-field.post.d.ts +3 -0
- package/dist/runtime/server/api/presets-field.post.js +36 -0
- package/dist/runtime/server/api/presets.get.d.ts +3 -0
- package/dist/runtime/server/api/presets.get.js +7 -0
- package/dist/runtime/server/api/preview-link.delete.d.ts +4 -0
- package/dist/runtime/server/api/preview-link.delete.js +15 -0
- package/dist/runtime/server/api/preview-links.get.d.ts +3 -0
- package/dist/runtime/server/api/preview-links.get.js +10 -0
- package/dist/runtime/server/api/preview-links.post.d.ts +3 -0
- package/dist/runtime/server/api/preview-links.post.js +17 -0
- package/dist/runtime/server/api/preview.get.d.ts +3 -0
- package/dist/runtime/server/api/preview.get.js +7 -0
- package/dist/runtime/server/api/redirect.delete.d.ts +4 -0
- package/dist/runtime/server/api/redirect.delete.js +10 -0
- package/dist/runtime/server/api/redirect.put.d.ts +3 -0
- package/dist/runtime/server/api/redirect.put.js +8 -0
- package/dist/runtime/server/api/redirects-import.post.d.ts +3 -0
- package/dist/runtime/server/api/redirects-import.post.js +13 -0
- package/dist/runtime/server/api/redirects.get.d.ts +3 -0
- package/dist/runtime/server/api/redirects.get.js +7 -0
- package/dist/runtime/server/api/redirects.post.d.ts +3 -0
- package/dist/runtime/server/api/redirects.post.js +7 -0
- package/dist/runtime/server/api/search.get.js +8 -3
- package/dist/runtime/server/api/settings.delete.js +2 -2
- package/dist/runtime/server/api/settings.get.js +4 -2
- package/dist/runtime/server/api/settings.put.js +2 -2
- package/dist/runtime/server/api/site.get.d.ts +3 -0
- package/dist/runtime/server/api/site.get.js +7 -0
- package/dist/runtime/server/api/site.put.d.ts +3 -0
- package/dist/runtime/server/api/site.put.js +16 -0
- package/dist/runtime/server/api/tags.get.js +3 -2
- package/dist/runtime/server/api/user.delete.js +8 -2
- package/dist/runtime/server/api/user.put.js +14 -4
- package/dist/runtime/server/api/users.get.js +2 -2
- package/dist/runtime/server/api/users.post.js +8 -4
- package/dist/runtime/server/middleware/hidden-site.d.ts +2 -0
- package/dist/runtime/server/middleware/hidden-site.js +8 -0
- package/dist/runtime/server/middleware/preview-links.d.ts +2 -0
- package/dist/runtime/server/middleware/preview-links.js +6 -0
- package/dist/runtime/server/middleware/redirects.d.ts +2 -0
- package/dist/runtime/server/middleware/redirects.js +18 -0
- package/dist/runtime/server/plugins/session-validate.js +4 -1
- package/dist/runtime/server/plugins/webhooks.d.ts +3 -0
- package/dist/runtime/server/plugins/webhooks.js +27 -0
- package/dist/runtime/server/routes/graphql.js +5 -3
- package/dist/runtime/server/routes/robots.d.ts +2 -0
- package/dist/runtime/server/routes/robots.js +16 -0
- package/dist/runtime/server/routes/sitemap.d.ts +2 -0
- package/dist/runtime/server/routes/sitemap.js +67 -0
- package/dist/runtime/server/utils/cached-value.d.ts +5 -0
- package/dist/runtime/server/utils/cached-value.js +24 -0
- package/dist/runtime/server/utils/content-routes.d.ts +5 -0
- package/dist/runtime/server/utils/content-routes.js +29 -0
- package/dist/runtime/server/utils/field-rules.d.ts +19 -0
- package/dist/runtime/server/utils/field-rules.js +72 -0
- package/dist/runtime/server/utils/graphql.d.ts +3 -1
- package/dist/runtime/server/utils/graphql.js +143 -28
- package/dist/runtime/server/utils/hooks.d.ts +14 -0
- package/dist/runtime/server/utils/hooks.js +71 -0
- package/dist/runtime/server/utils/import.d.ts +7 -2
- package/dist/runtime/server/utils/import.js +108 -14
- package/dist/runtime/server/utils/link-usage.d.ts +4 -0
- package/dist/runtime/server/utils/link-usage.js +28 -0
- package/dist/runtime/server/utils/media-references.d.ts +6 -1
- package/dist/runtime/server/utils/media-references.js +49 -20
- package/dist/runtime/server/utils/media-usage.d.ts +2 -0
- package/dist/runtime/server/utils/media-usage.js +5 -3
- package/dist/runtime/server/utils/page-rows.d.ts +2 -0
- package/dist/runtime/server/utils/page-rows.js +42 -29
- package/dist/runtime/server/utils/permissions.d.ts +14 -0
- package/dist/runtime/server/utils/permissions.js +43 -0
- package/dist/runtime/server/utils/presets.d.ts +18 -0
- package/dist/runtime/server/utils/presets.js +68 -0
- package/dist/runtime/server/utils/preview-links.d.ts +207 -0
- package/dist/runtime/server/utils/preview-links.js +161 -0
- package/dist/runtime/server/utils/redirects.d.ts +31 -0
- package/dist/runtime/server/utils/redirects.js +240 -0
- package/dist/runtime/server/utils/registry.js +4 -1
- package/dist/runtime/server/utils/relations.d.ts +8 -0
- package/dist/runtime/server/utils/relations.js +25 -6
- package/dist/runtime/server/utils/require-admin.d.ts +0 -1
- package/dist/runtime/server/utils/require-admin.js +4 -8
- package/dist/runtime/server/utils/search.d.ts +1 -0
- package/dist/runtime/server/utils/search.js +44 -19
- package/dist/runtime/server/utils/site.d.ts +23 -0
- package/dist/runtime/server/utils/site.js +79 -0
- package/dist/runtime/server/utils/sitemap.d.ts +12 -0
- package/dist/runtime/server/utils/sitemap.js +109 -0
- package/dist/runtime/server/utils/user-management.d.ts +4 -0
- package/dist/runtime/server/utils/user-management.js +20 -0
- package/dist/runtime/server/utils/users.d.ts +1 -1
- package/dist/runtime/server/utils/users.js +1 -1
- package/dist/runtime/server/utils/visibility.d.ts +1 -0
- package/dist/runtime/server/utils/visibility.js +6 -1
- package/dist/runtime/server/utils/webhooks.d.ts +24 -0
- package/dist/runtime/server/utils/webhooks.js +90 -0
- package/dist/runtime/shared/block-paths.d.ts +66 -0
- package/dist/runtime/shared/block-paths.js +310 -0
- package/dist/runtime/shared/block-picker.d.ts +18 -0
- package/dist/runtime/shared/block-picker.js +31 -0
- package/dist/runtime/shared/block-signature.d.ts +4 -0
- package/dist/runtime/shared/block-signature.js +46 -0
- package/dist/runtime/shared/clipboard.d.ts +31 -0
- package/dist/runtime/shared/clipboard.js +206 -0
- package/dist/runtime/shared/conditions.d.ts +36 -0
- package/dist/runtime/shared/conditions.js +100 -0
- package/dist/runtime/shared/content-routes.d.ts +42 -0
- package/dist/runtime/shared/content-routes.js +237 -0
- package/dist/runtime/shared/duplicate.js +2 -1
- package/dist/runtime/shared/editor-layout.d.ts +18 -0
- package/dist/runtime/shared/editor-layout.js +9 -0
- package/dist/runtime/shared/field-options.d.ts +21 -0
- package/dist/runtime/shared/field-options.js +90 -0
- package/dist/runtime/shared/graphql-sdl.d.ts +1 -0
- package/dist/runtime/shared/graphql-sdl.js +50 -26
- package/dist/runtime/shared/history.d.ts +27 -0
- package/dist/runtime/shared/history.js +123 -0
- package/dist/runtime/shared/hooks.d.ts +45 -0
- package/dist/runtime/shared/hooks.js +28 -0
- package/dist/runtime/shared/index.d.ts +90 -21
- package/dist/runtime/shared/index.js +121 -37
- package/dist/runtime/shared/inline-text.d.ts +38 -0
- package/dist/runtime/shared/inline-text.js +85 -0
- package/dist/runtime/shared/layout.d.ts +2 -1
- package/dist/runtime/shared/layout.js +3 -0
- package/dist/runtime/shared/links.d.ts +48 -0
- package/dist/runtime/shared/links.js +155 -0
- package/dist/runtime/shared/list-filters.d.ts +2 -0
- package/dist/runtime/shared/list-filters.js +2 -0
- package/dist/runtime/shared/media-focal.d.ts +30 -0
- package/dist/runtime/shared/media-focal.js +52 -0
- package/dist/runtime/shared/migrations-dir.d.ts +14 -0
- package/dist/runtime/shared/migrations-dir.js +48 -2
- package/dist/runtime/shared/presets.d.ts +67 -0
- package/dist/runtime/shared/presets.js +127 -0
- package/dist/runtime/shared/preview-links.d.ts +64 -0
- package/dist/runtime/shared/preview-links.js +54 -0
- package/dist/runtime/shared/preview.d.ts +124 -21
- package/dist/runtime/shared/preview.js +419 -81
- package/dist/runtime/shared/redirects.d.ts +75 -0
- package/dist/runtime/shared/redirects.js +230 -0
- package/dist/runtime/shared/roles.d.ts +57 -0
- package/dist/runtime/shared/roles.js +293 -0
- package/dist/runtime/shared/save-state.d.ts +8 -0
- package/dist/runtime/shared/save-state.js +15 -0
- package/dist/runtime/shared/search.d.ts +16 -1
- package/dist/runtime/shared/search.js +41 -7
- package/dist/runtime/shared/seo.d.ts +167 -0
- package/dist/runtime/shared/seo.js +335 -0
- package/dist/runtime/shared/transfer.js +2 -1
- package/dist/runtime/shared/translations.d.ts +29 -0
- package/dist/runtime/shared/translations.js +123 -0
- package/dist/runtime/shared/users.d.ts +1 -1
- package/dist/runtime/shared/validation.d.ts +23 -1
- package/dist/runtime/shared/validation.js +214 -64
- package/dist/runtime/webhooks.d.ts +33 -0
- package/dist/runtime/webhooks.js +57 -0
- package/dist/types.d.mts +5 -1
- package/package.json +6 -2
- package/dist/runtime/app/components/cms/BlockOutline.vue +0 -88
- package/dist/runtime/app/components/cms/BlockPalette.vue +0 -31
- package/dist/runtime/app/components/cms/SettingsModal.d.vue.ts +0 -12
- package/dist/runtime/app/components/cms/SettingsModal.vue +0 -126
- package/dist/runtime/app/components/cms/SettingsModal.vue.d.ts +0 -12
- package/dist/runtime/app/composables/cms-field-focus.d.ts +0 -4
- package/dist/runtime/app/composables/cms-field-focus.js +0 -20
|
@@ -6,8 +6,13 @@
|
|
|
6
6
|
:field-key="CONTENT_BODY_FIELD"
|
|
7
7
|
:fields="config.fields"
|
|
8
8
|
:title="headerTitle"
|
|
9
|
+
:trail="[{ label: config.label, onClick: goBack }]"
|
|
9
10
|
:focus="focus"
|
|
11
|
+
:locked="locked"
|
|
12
|
+
:readonly="!!lock"
|
|
13
|
+
:tab-query="CMS_TAB_QUERY"
|
|
10
14
|
content
|
|
15
|
+
@save="saveShortcut"
|
|
11
16
|
>
|
|
12
17
|
<template #start>
|
|
13
18
|
<KButton
|
|
@@ -16,28 +21,67 @@
|
|
|
16
21
|
variant="ghost"
|
|
17
22
|
tone="neutral"
|
|
18
23
|
:aria-label="`Back to ${config.label}`"
|
|
19
|
-
:title="`Back to ${config.label}`"
|
|
20
24
|
@click="goBack"
|
|
21
25
|
/>
|
|
22
26
|
</template>
|
|
23
27
|
|
|
24
28
|
<template #status>
|
|
25
29
|
<CmsStatusBadge :published="published" :scheduled="scheduled" :at="scheduledAt" />
|
|
26
|
-
<
|
|
30
|
+
<KBadge v-if="dirty" label="Unsaved changes" tone="warning" dot />
|
|
31
|
+
<KTooltip v-if="lock" :text="lock">
|
|
32
|
+
<KBadge size="sm" variant="outline" label="Read only" />
|
|
33
|
+
</KTooltip>
|
|
27
34
|
</template>
|
|
28
35
|
|
|
29
|
-
<template #
|
|
30
|
-
<
|
|
36
|
+
<template v-if="config.route" #content-start="{ locale }">
|
|
37
|
+
<KStack gap="xs">
|
|
38
|
+
<span class="k-label">Page address</span>
|
|
39
|
+
<KStack direction="row" align="center" gap="sm">
|
|
40
|
+
<span
|
|
41
|
+
class="cms-editor-address-path k-mono"
|
|
42
|
+
:class="{ 'is-empty': !addressOf(formState, locale) }"
|
|
43
|
+
>
|
|
44
|
+
{{ addressOf(formState, locale) ?? "No address yet" }}
|
|
45
|
+
</span>
|
|
46
|
+
<KButton
|
|
47
|
+
v-if="livePath(locale)"
|
|
48
|
+
:href="livePath(locale)"
|
|
49
|
+
target="_blank"
|
|
50
|
+
label="Open"
|
|
51
|
+
:icon="cmsIcon('arrow-top-right-on-square')"
|
|
52
|
+
size="sm"
|
|
53
|
+
variant="ghost"
|
|
54
|
+
tone="neutral"
|
|
55
|
+
/>
|
|
56
|
+
</KStack>
|
|
57
|
+
<KFieldMessage
|
|
58
|
+
v-if="addressHint(locale)"
|
|
59
|
+
tone="neutral"
|
|
60
|
+
:icon="false"
|
|
61
|
+
:message="addressHint(locale)"
|
|
62
|
+
/>
|
|
63
|
+
</KStack>
|
|
64
|
+
</template>
|
|
65
|
+
|
|
66
|
+
<template #actions="{ compact }">
|
|
67
|
+
<KPopover
|
|
68
|
+
v-if="!lock && canPublish"
|
|
69
|
+
v-model:open="scheduleOpen"
|
|
70
|
+
placement="bottom-end"
|
|
71
|
+
label="Schedule"
|
|
72
|
+
width="18rem"
|
|
73
|
+
>
|
|
31
74
|
<KButton
|
|
32
75
|
:icon="cmsIcon('clock')"
|
|
33
76
|
size="sm"
|
|
34
77
|
variant="ghost"
|
|
35
78
|
tone="neutral"
|
|
36
|
-
:label="
|
|
79
|
+
:label="compact ? void 0 : scheduleLabel"
|
|
80
|
+
:aria-label="scheduleLabel"
|
|
37
81
|
:disabled="saving"
|
|
38
82
|
/>
|
|
39
83
|
<template #content>
|
|
40
|
-
<KStack gap="md"
|
|
84
|
+
<KStack gap="md">
|
|
41
85
|
<KFormField
|
|
42
86
|
label="Publish at"
|
|
43
87
|
description="The item stays hidden from the site until this moment."
|
|
@@ -64,6 +108,7 @@
|
|
|
64
108
|
</template>
|
|
65
109
|
</KPopover>
|
|
66
110
|
<KButton
|
|
111
|
+
v-if="!lock"
|
|
67
112
|
label="Save"
|
|
68
113
|
size="sm"
|
|
69
114
|
variant="soft"
|
|
@@ -72,6 +117,7 @@
|
|
|
72
117
|
@click="save()"
|
|
73
118
|
/>
|
|
74
119
|
<KButton
|
|
120
|
+
v-if="!lock && canPublish"
|
|
75
121
|
:label="published ? 'Unpublish' : 'Publish'"
|
|
76
122
|
size="sm"
|
|
77
123
|
:variant="published ? 'soft' : 'solid'"
|
|
@@ -79,23 +125,24 @@
|
|
|
79
125
|
:loading="saving"
|
|
80
126
|
@click="save(published ? 'draft' : 'published')"
|
|
81
127
|
/>
|
|
82
|
-
<KDropdownMenu
|
|
83
|
-
v-if="!isNew"
|
|
84
|
-
:items="[
|
|
85
|
-
{ label: 'Duplicate', onSelect: duplicate },
|
|
86
|
-
{ label: 'Delete', onSelect: remove }
|
|
87
|
-
]"
|
|
88
|
-
placement="bottom-end"
|
|
89
|
-
>
|
|
128
|
+
<KDropdownMenu v-if="moreItems.length" :items="moreItems" placement="bottom-end">
|
|
90
129
|
<KButton
|
|
91
130
|
:icon="cmsIcon('ellipsis-horizontal')"
|
|
92
131
|
size="sm"
|
|
93
132
|
variant="ghost"
|
|
94
133
|
tone="neutral"
|
|
95
134
|
aria-label="More actions"
|
|
96
|
-
title="More actions"
|
|
97
135
|
/>
|
|
98
136
|
</KDropdownMenu>
|
|
137
|
+
<CmsSharePreview
|
|
138
|
+
v-if="canShare"
|
|
139
|
+
v-model:open="shareOpen"
|
|
140
|
+
:entry-name="name"
|
|
141
|
+
:item-id="id"
|
|
142
|
+
:dirty="dirty"
|
|
143
|
+
:locale="editor?.locale"
|
|
144
|
+
:save="() => save()"
|
|
145
|
+
/>
|
|
99
146
|
</template>
|
|
100
147
|
</CmsVisualEditor>
|
|
101
148
|
</template>
|
|
@@ -105,6 +152,8 @@ import {
|
|
|
105
152
|
CONTENT_BODY_FIELD,
|
|
106
153
|
CONTENT_PUBLISHED_AT_FIELD,
|
|
107
154
|
CONTENT_TITLE_FIELD,
|
|
155
|
+
contentItemPath,
|
|
156
|
+
contentRoutePattern,
|
|
108
157
|
isScheduledContent
|
|
109
158
|
} from "#nuxt-cms";
|
|
110
159
|
import {
|
|
@@ -112,8 +161,6 @@ import {
|
|
|
112
161
|
createError,
|
|
113
162
|
definePageMeta,
|
|
114
163
|
navigateTo,
|
|
115
|
-
onBeforeRouteLeave,
|
|
116
|
-
onBeforeUnmount,
|
|
117
164
|
onMounted,
|
|
118
165
|
ref,
|
|
119
166
|
useFetch,
|
|
@@ -122,28 +169,37 @@ import {
|
|
|
122
169
|
watch
|
|
123
170
|
} from "#imports";
|
|
124
171
|
import {
|
|
172
|
+
Badge as KBadge,
|
|
125
173
|
Button as KButton,
|
|
126
174
|
DropdownMenu as KDropdownMenu,
|
|
175
|
+
FieldMessage as KFieldMessage,
|
|
127
176
|
FormField as KFormField,
|
|
128
177
|
Popover as KPopover,
|
|
129
|
-
Stack as KStack
|
|
178
|
+
Stack as KStack,
|
|
179
|
+
Tooltip as KTooltip,
|
|
180
|
+
toast,
|
|
181
|
+
useLeaveGuard
|
|
130
182
|
} from "@xleddyl/443-components-vue";
|
|
131
183
|
import cmsConfig from "#cms-config";
|
|
132
184
|
import { duplicateEntryValues } from "../../shared/duplicate";
|
|
133
|
-
import {
|
|
134
|
-
import {
|
|
185
|
+
import { entryDefaults, lockedFieldKeys } from "../../shared/field-options";
|
|
186
|
+
import { cloneState, mergeSavedState } from "../../shared/save-state";
|
|
187
|
+
import { CMS_TAB_QUERY, cmsPageKey, readSearchFocus } from "../../shared/search";
|
|
188
|
+
import { useCmsPermissions } from "../composables/cms-permissions";
|
|
189
|
+
import { useCmsRouteFlag } from "../composables/cms-route";
|
|
190
|
+
import { useCmsLinkedDelete } from "../composables/cms-link-usage";
|
|
135
191
|
import { useCmsRuntime } from "../composables/cms-runtime";
|
|
136
|
-
import {
|
|
192
|
+
import { useCmsSaveHandoff } from "../composables/cms-save-handoff";
|
|
193
|
+
import { useCmsTranslationActions } from "../composables/cms-translation-actions";
|
|
137
194
|
import { cmsApi } from "../utils/api";
|
|
138
|
-
import { cmsIcon, errorMessage } from "../utils/ui";
|
|
195
|
+
import { cmsIcon, errorIssues, errorMessage } from "../utils/ui";
|
|
139
196
|
definePageMeta({
|
|
140
197
|
layout: "cms-editor",
|
|
141
198
|
middleware: "cms-auth",
|
|
142
|
-
key: (route2) => route2
|
|
199
|
+
key: (route2) => cmsPageKey(route2, [CMS_TAB_QUERY])
|
|
143
200
|
});
|
|
144
201
|
const route = useRoute();
|
|
145
|
-
const
|
|
146
|
-
const confirmAction = useCmsConfirm();
|
|
202
|
+
const confirmLinkedDelete = useCmsLinkedDelete();
|
|
147
203
|
const focus = readSearchFocus(route.query);
|
|
148
204
|
const { i18n } = useCmsRuntime();
|
|
149
205
|
const name = String(route.meta.cmsEntry ?? route.path.split("/")[2] ?? "");
|
|
@@ -159,9 +215,10 @@ const editor = ref(null);
|
|
|
159
215
|
function pickFields(row) {
|
|
160
216
|
return Object.fromEntries(formKeys.map((key) => [key, row[key] ?? null]));
|
|
161
217
|
}
|
|
162
|
-
const blankState =
|
|
163
|
-
formKeys.map((key) => [key, key === "status" ? "draft" : null])
|
|
164
|
-
)
|
|
218
|
+
const blankState = {
|
|
219
|
+
...Object.fromEntries(formKeys.map((key) => [key, key === "status" ? "draft" : null])),
|
|
220
|
+
...entryDefaults(config.fields, i18n, { local: true })
|
|
221
|
+
};
|
|
165
222
|
const formState = ref({ ...blankState });
|
|
166
223
|
const duplicateOf = isNew && typeof route.query.duplicate === "string" && route.query.duplicate ? route.query.duplicate : null;
|
|
167
224
|
const sourceId = isNew ? duplicateOf : id;
|
|
@@ -178,7 +235,57 @@ if (sourceId) {
|
|
|
178
235
|
formState.value = duplicateOf ? duplicateEntryValues(config, data.value) : pickFields(data.value);
|
|
179
236
|
}
|
|
180
237
|
const snapshot = ref(JSON.stringify(duplicateOf ? blankState : formState.value));
|
|
181
|
-
const
|
|
238
|
+
const handoff = useCmsSaveHandoff(name);
|
|
239
|
+
const handedOver = id ? handoff.take(id) : null;
|
|
240
|
+
if (handedOver) formState.value = handedOver;
|
|
241
|
+
const { can, entryLock } = useCmsPermissions();
|
|
242
|
+
const canPublish = computed(() => can(`${name}.publish`));
|
|
243
|
+
const lock = computed(
|
|
244
|
+
() => entryLock(name, config, {
|
|
245
|
+
isNew,
|
|
246
|
+
status: JSON.parse(snapshot.value).status
|
|
247
|
+
})
|
|
248
|
+
);
|
|
249
|
+
const { items: translationItems } = useCmsTranslationActions({
|
|
250
|
+
fields: config.fields,
|
|
251
|
+
state: formState,
|
|
252
|
+
locale: () => editor.value?.locale,
|
|
253
|
+
disabled: () => !!lock.value,
|
|
254
|
+
apply: (values) => editor.value?.applyChange(values)
|
|
255
|
+
});
|
|
256
|
+
const canShare = computed(() => !isNew && !!config.route && can(`${name}.update`));
|
|
257
|
+
const shareOpen = useCmsRouteFlag("share");
|
|
258
|
+
const itemActions = computed(
|
|
259
|
+
() => isNew ? [] : [
|
|
260
|
+
...canShare.value ? [
|
|
261
|
+
{
|
|
262
|
+
label: "Share preview",
|
|
263
|
+
icon: cmsIcon("link"),
|
|
264
|
+
onSelect: () => shareOpen.value = true
|
|
265
|
+
}
|
|
266
|
+
] : [],
|
|
267
|
+
...can(`${name}.create`) ? [
|
|
268
|
+
{
|
|
269
|
+
label: "Duplicate",
|
|
270
|
+
icon: cmsIcon("document-duplicate"),
|
|
271
|
+
onSelect: duplicate
|
|
272
|
+
}
|
|
273
|
+
] : []
|
|
274
|
+
]
|
|
275
|
+
);
|
|
276
|
+
const canDeleteItem = computed(
|
|
277
|
+
() => can(`${name}.delete`) && (JSON.parse(snapshot.value).status !== "published" || canPublish.value)
|
|
278
|
+
);
|
|
279
|
+
const deleteActions = computed(
|
|
280
|
+
() => !isNew && canDeleteItem.value ? [{ label: "Delete", icon: cmsIcon("trash"), tone: "danger", onSelect: remove }] : []
|
|
281
|
+
);
|
|
282
|
+
const moreItems = computed(
|
|
283
|
+
() => [itemActions.value, translationItems.value, deleteActions.value].filter((group) => group.length)
|
|
284
|
+
);
|
|
285
|
+
const locked = computed(
|
|
286
|
+
() => isNew ? [] : lockedFieldKeys(config.fields, JSON.parse(snapshot.value))
|
|
287
|
+
);
|
|
288
|
+
const dirty = computed(() => !lock.value && JSON.stringify(formState.value) !== snapshot.value);
|
|
182
289
|
const titleText = computed(() => {
|
|
183
290
|
const value = formState.value[CONTENT_TITLE_FIELD];
|
|
184
291
|
if (typeof value === "string") return value;
|
|
@@ -193,11 +300,39 @@ useHead({ title: computed(() => `${headerTitle.value} \xB7 ${config.label}`) });
|
|
|
193
300
|
const published = computed(() => formState.value.status === "published");
|
|
194
301
|
const mounted = ref(false);
|
|
195
302
|
const scheduled = computed(() => mounted.value && isScheduledContent(formState.value));
|
|
303
|
+
const scheduleLabel = computed(() => scheduled.value ? "Reschedule" : "Schedule");
|
|
196
304
|
const scheduledAt = computed(() => {
|
|
197
305
|
const value = formState.value[CONTENT_PUBLISHED_AT_FIELD];
|
|
198
306
|
if (!mounted.value || typeof value !== "string" || !value) return "";
|
|
199
307
|
return new Date(value).toLocaleString();
|
|
200
308
|
});
|
|
309
|
+
const savedState = computed(
|
|
310
|
+
() => isNew ? {} : JSON.parse(snapshot.value)
|
|
311
|
+
);
|
|
312
|
+
const live = computed(
|
|
313
|
+
() => mounted.value && savedState.value.status === "published" && !isScheduledContent(savedState.value)
|
|
314
|
+
);
|
|
315
|
+
function addressOf(values, locale) {
|
|
316
|
+
return contentItemPath(config, { ...values, id }, locale);
|
|
317
|
+
}
|
|
318
|
+
function livePath(locale) {
|
|
319
|
+
return live.value ? addressOf(savedState.value, locale) : null;
|
|
320
|
+
}
|
|
321
|
+
function missingAddressHint(locale) {
|
|
322
|
+
const pattern = contentRoutePattern(config.route, locale) ?? "";
|
|
323
|
+
if (isNew && pattern.includes("[id]")) return "Save the item to get its address.";
|
|
324
|
+
if (/\[(?:year|month|day)\]/.test(pattern) && !formState.value[CONTENT_PUBLISHED_AT_FIELD])
|
|
325
|
+
return "The address needs a publish date.";
|
|
326
|
+
return "The address needs a slug.";
|
|
327
|
+
}
|
|
328
|
+
function addressHint(locale) {
|
|
329
|
+
const current = addressOf(formState.value, locale);
|
|
330
|
+
if (!current) return missingAddressHint(locale);
|
|
331
|
+
if (!live.value)
|
|
332
|
+
return scheduled.value ? "Live from the scheduled date." : "Live once published.";
|
|
333
|
+
if (current !== livePath(locale)) return "Save to move the page to this address.";
|
|
334
|
+
return "";
|
|
335
|
+
}
|
|
201
336
|
const publishedAtField = computed(() => ({
|
|
202
337
|
...config.fields[CONTENT_PUBLISHED_AT_FIELD],
|
|
203
338
|
required: true
|
|
@@ -211,25 +346,14 @@ watch(scheduleOpen, (open) => {
|
|
|
211
346
|
const future = typeof current === "string" && new Date(current).getTime() > Date.now();
|
|
212
347
|
scheduleDraft.value = future ? current : new Date(Math.ceil(Date.now() / 36e5) * 36e5 + 36e5).toISOString();
|
|
213
348
|
});
|
|
214
|
-
|
|
215
|
-
event.preventDefault();
|
|
216
|
-
}
|
|
217
|
-
watch(dirty, (value) => {
|
|
218
|
-
if (value) window.addEventListener("beforeunload", onBeforeUnload);
|
|
219
|
-
else window.removeEventListener("beforeunload", onBeforeUnload);
|
|
220
|
-
});
|
|
349
|
+
const leave = useLeaveGuard(dirty);
|
|
221
350
|
onMounted(() => {
|
|
222
351
|
mounted.value = true;
|
|
223
|
-
if (dirty.value) window.addEventListener("beforeunload", onBeforeUnload);
|
|
224
|
-
});
|
|
225
|
-
onBeforeUnmount(() => window.removeEventListener("beforeunload", onBeforeUnload));
|
|
226
|
-
onBeforeRouteLeave(async () => {
|
|
227
|
-
if (dirty.value && !await confirmAction("You have unsaved changes. Leave anyway?"))
|
|
228
|
-
return false;
|
|
229
352
|
});
|
|
230
353
|
function goBack() {
|
|
231
354
|
navigateTo(`/cms/${name}`);
|
|
232
355
|
}
|
|
356
|
+
const LATER_CHANGES = "Changes made while saving are not saved yet.";
|
|
233
357
|
const SUCCESS = {
|
|
234
358
|
save: "Saved",
|
|
235
359
|
published: "Published",
|
|
@@ -245,19 +369,28 @@ async function save(status, changes = {}, notice) {
|
|
|
245
369
|
status: before.status,
|
|
246
370
|
...pickRevert(before, changes)
|
|
247
371
|
};
|
|
248
|
-
toast.
|
|
372
|
+
toast.error("Check the highlighted fields");
|
|
249
373
|
return false;
|
|
250
374
|
}
|
|
251
375
|
saving.value = true;
|
|
376
|
+
const submitted = cloneState(formState.value);
|
|
252
377
|
try {
|
|
253
|
-
const row = await (isNew ? cmsApi(endpoint, { method: "POST", body:
|
|
378
|
+
const row = await (isNew ? cmsApi(endpoint, { method: "POST", body: submitted }) : cmsApi(`${endpoint}/${id}`, {
|
|
254
379
|
method: "PUT",
|
|
255
|
-
body:
|
|
380
|
+
body: submitted
|
|
256
381
|
}));
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
382
|
+
const saved = pickFields(row);
|
|
383
|
+
const merged = mergeSavedState(submitted, formState.value, saved);
|
|
384
|
+
formState.value = merged.state;
|
|
385
|
+
snapshot.value = JSON.stringify(saved);
|
|
386
|
+
toast.success(SUCCESS[notice ?? status ?? "save"] ?? "Saved", {
|
|
387
|
+
description: merged.clean ? void 0 : LATER_CHANGES
|
|
388
|
+
});
|
|
389
|
+
if (isNew && row.id) {
|
|
390
|
+
if (!merged.clean) handoff.stash(String(row.id), merged.state);
|
|
391
|
+
leave.allowNext();
|
|
392
|
+
await navigateTo(`/cms/${name}/${row.id}`, { replace: true });
|
|
393
|
+
}
|
|
261
394
|
return true;
|
|
262
395
|
} catch (error) {
|
|
263
396
|
formState.value = {
|
|
@@ -265,12 +398,16 @@ async function save(status, changes = {}, notice) {
|
|
|
265
398
|
status: before.status,
|
|
266
399
|
...pickRevert(before, changes)
|
|
267
400
|
};
|
|
268
|
-
|
|
401
|
+
editor.value?.setIssues(errorIssues(error));
|
|
402
|
+
toast.error("Save failed", { description: errorMessage(error) });
|
|
269
403
|
return false;
|
|
270
404
|
} finally {
|
|
271
405
|
saving.value = false;
|
|
272
406
|
}
|
|
273
407
|
}
|
|
408
|
+
function saveShortcut() {
|
|
409
|
+
if (!saving.value) void save();
|
|
410
|
+
}
|
|
274
411
|
function pickRevert(before, changes) {
|
|
275
412
|
return Object.fromEntries(Object.keys(changes).map((key) => [key, before[key] ?? null]));
|
|
276
413
|
}
|
|
@@ -287,15 +424,15 @@ function duplicate() {
|
|
|
287
424
|
void navigateTo({ path: `/cms/${name}/new`, query: { duplicate: id } });
|
|
288
425
|
}
|
|
289
426
|
async function remove() {
|
|
290
|
-
if (!await
|
|
427
|
+
if (!await confirmLinkedDelete(name, id ? [id] : [], "Delete this item?")) return;
|
|
291
428
|
saving.value = true;
|
|
292
429
|
try {
|
|
293
430
|
await cmsApi(`${endpoint}/${id}`, { method: "DELETE" });
|
|
294
431
|
snapshot.value = JSON.stringify(formState.value);
|
|
295
|
-
toast.
|
|
432
|
+
toast.success("Deleted");
|
|
296
433
|
goBack();
|
|
297
434
|
} catch (error) {
|
|
298
|
-
toast.
|
|
435
|
+
toast.error("Delete failed", { description: errorMessage(error) });
|
|
299
436
|
} finally {
|
|
300
437
|
saving.value = false;
|
|
301
438
|
}
|
|
@@ -1,36 +1,39 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="
|
|
3
|
-
<KPageHeader
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
<div class="k-page">
|
|
3
|
+
<KPageHeader
|
|
4
|
+
:trail="breadcrumb"
|
|
5
|
+
trail-label="Page"
|
|
6
|
+
:title="headerTitle"
|
|
7
|
+
:icon="cmsIcon(config.icon ?? 'window')"
|
|
8
|
+
:description="pageRoute?.path"
|
|
9
|
+
>
|
|
7
10
|
<template #actions>
|
|
8
|
-
<
|
|
9
|
-
<KButton type="submit" :form="FORM_ID" label="Save" :loading="saving" />
|
|
11
|
+
<CmsTranslationMenu v-model="formState" :fields="fields" :disabled="!!lock" size="sm" />
|
|
10
12
|
<KButton
|
|
11
|
-
v-if="
|
|
13
|
+
v-if="!lock"
|
|
12
14
|
type="submit"
|
|
13
15
|
:form="FORM_ID"
|
|
14
|
-
|
|
16
|
+
label="Save"
|
|
17
|
+
size="sm"
|
|
15
18
|
:loading="saving"
|
|
16
|
-
@click="togglePublished"
|
|
17
19
|
/>
|
|
18
20
|
</template>
|
|
19
21
|
</KPageHeader>
|
|
20
22
|
|
|
21
|
-
<KCard
|
|
23
|
+
<KCard padding="lg" fill>
|
|
22
24
|
<CmsEntryForm
|
|
25
|
+
ref="formRef"
|
|
23
26
|
v-model="formState"
|
|
24
27
|
:fields="fields"
|
|
25
28
|
:tabs="config.tabs"
|
|
26
29
|
:entry-name="name"
|
|
27
|
-
:drafts="drafts"
|
|
28
30
|
:form-id="FORM_ID"
|
|
29
|
-
:
|
|
31
|
+
:tab-query="CMS_TAB_QUERY"
|
|
30
32
|
:focus="focus"
|
|
31
33
|
:path="pageRoute?.path"
|
|
34
|
+
:lock="lock"
|
|
35
|
+
:locked="locked"
|
|
32
36
|
@submit="save"
|
|
33
|
-
@error="revertStatus"
|
|
34
37
|
/>
|
|
35
38
|
</KCard>
|
|
36
39
|
</div>
|
|
@@ -43,33 +46,34 @@ import {
|
|
|
43
46
|
createError,
|
|
44
47
|
definePageMeta,
|
|
45
48
|
navigateTo,
|
|
46
|
-
onBeforeRouteLeave,
|
|
47
|
-
onBeforeUnmount,
|
|
48
49
|
ref,
|
|
49
50
|
useFetch,
|
|
50
|
-
useRoute
|
|
51
|
-
watch
|
|
51
|
+
useRoute
|
|
52
52
|
} from "#imports";
|
|
53
53
|
import {
|
|
54
54
|
Button as KButton,
|
|
55
55
|
Card as KCard,
|
|
56
|
-
PageHeader as KPageHeader
|
|
56
|
+
PageHeader as KPageHeader,
|
|
57
|
+
toast,
|
|
58
|
+
useLeaveGuard
|
|
57
59
|
} from "@xleddyl/443-components-vue";
|
|
58
60
|
import cmsConfig from "#cms-config";
|
|
59
|
-
import {
|
|
60
|
-
import {
|
|
61
|
-
import {
|
|
61
|
+
import { entryDefaults, lockedFieldKeys } from "../../shared/field-options";
|
|
62
|
+
import { cloneState, mergeSavedState } from "../../shared/save-state";
|
|
63
|
+
import { CMS_TAB_QUERY, cmsEntryLink, cmsPageKey, readSearchFocus } from "../../shared/search";
|
|
64
|
+
import { useCmsPermissions } from "../composables/cms-permissions";
|
|
65
|
+
import { CMS_NEW_RECORD } from "../composables/cms-route";
|
|
66
|
+
import { useCmsRuntime } from "../composables/cms-runtime";
|
|
62
67
|
import { cmsApi } from "../utils/api";
|
|
63
|
-
import { errorMessage } from "../utils/ui";
|
|
68
|
+
import { cmsIcon, errorIssues, errorMessage } from "../utils/ui";
|
|
64
69
|
const FORM_ID = "cms-entry-form";
|
|
65
70
|
definePageMeta({
|
|
66
71
|
layout: "cms-admin",
|
|
67
72
|
middleware: "cms-auth",
|
|
68
73
|
validate: (route2) => Object.hasOwn(cmsConfig, route2.params.collection),
|
|
69
|
-
key: (route2) => route2
|
|
74
|
+
key: (route2) => cmsPageKey(route2, [CMS_TAB_QUERY])
|
|
70
75
|
});
|
|
71
76
|
const route = useRoute();
|
|
72
|
-
const toast = useCmsToast();
|
|
73
77
|
const focus = readSearchFocus(route.query);
|
|
74
78
|
const name = route.params.collection;
|
|
75
79
|
const config = cmsConfig[name];
|
|
@@ -77,9 +81,23 @@ if (!config || config.kind !== "collection" && config.kind !== "page") {
|
|
|
77
81
|
throw createError({ statusCode: 404, statusMessage: "Unknown collection", fatal: true });
|
|
78
82
|
}
|
|
79
83
|
const id = route.params.id;
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
84
|
+
if (config.kind === "collection") {
|
|
85
|
+
const text = (key) => {
|
|
86
|
+
const value = route.query[key];
|
|
87
|
+
return typeof value === "string" ? value : void 0;
|
|
88
|
+
};
|
|
89
|
+
await navigateTo(
|
|
90
|
+
cmsEntryLink(name, id ?? CMS_NEW_RECORD, {
|
|
91
|
+
field: text("field"),
|
|
92
|
+
locale: text("locale"),
|
|
93
|
+
block: text("block"),
|
|
94
|
+
tab: text(CMS_TAB_QUERY)
|
|
95
|
+
}),
|
|
96
|
+
{ replace: true }
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
const pageRoute = config.kind === "page" && id ? pageRouteOf(config, id) : void 0;
|
|
100
|
+
if (config.kind === "page" && !pageRoute) {
|
|
83
101
|
throw createError({ statusCode: 404, statusMessage: "Unknown page", fatal: true });
|
|
84
102
|
}
|
|
85
103
|
const breadcrumb = (() => {
|
|
@@ -97,21 +115,22 @@ const breadcrumb = (() => {
|
|
|
97
115
|
}
|
|
98
116
|
return links;
|
|
99
117
|
})();
|
|
100
|
-
const isNew = !isPage && id === void 0;
|
|
101
|
-
const drafts = !isPage && !!config.drafts;
|
|
102
118
|
const fields = pageRoute ? pageFields(config, pageRoute.path) : config.fields;
|
|
103
|
-
const headerTitle = pageRoute
|
|
104
|
-
const
|
|
105
|
-
const formKeys = drafts ? [...fieldKeys, "status"] : fieldKeys;
|
|
119
|
+
const headerTitle = pageRoute?.label ?? config.label;
|
|
120
|
+
const formKeys = Object.keys(fields);
|
|
106
121
|
const endpoint = `/api/cms/admin/${name}`;
|
|
122
|
+
const { i18n } = useCmsRuntime();
|
|
107
123
|
function emptyState() {
|
|
108
|
-
return
|
|
124
|
+
return {
|
|
125
|
+
...Object.fromEntries(formKeys.map((k) => [k, null])),
|
|
126
|
+
...entryDefaults(fields, i18n, { local: true })
|
|
127
|
+
};
|
|
109
128
|
}
|
|
110
129
|
function pickFields(row) {
|
|
111
130
|
return Object.fromEntries(formKeys.map((k) => [k, row[k] ?? null]));
|
|
112
131
|
}
|
|
113
132
|
const formState = ref(emptyState());
|
|
114
|
-
if (
|
|
133
|
+
if (pageRoute) {
|
|
115
134
|
const { data, error } = await useFetch(`${endpoint}/${id}`);
|
|
116
135
|
if (error.value || !data.value) {
|
|
117
136
|
const statusCode = error.value?.statusCode ?? 404;
|
|
@@ -125,50 +144,35 @@ if (!isNew) {
|
|
|
125
144
|
}
|
|
126
145
|
const saving = ref(false);
|
|
127
146
|
const snapshot = ref(JSON.stringify(formState.value));
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
onBeforeUnmount(() => window.removeEventListener("beforeunload", onBeforeUnload));
|
|
137
|
-
const confirmAction = useCmsConfirm();
|
|
138
|
-
onBeforeRouteLeave(async () => {
|
|
139
|
-
if (dirty.value && !await confirmAction("You have unsaved changes. Leave anyway?"))
|
|
140
|
-
return false;
|
|
141
|
-
});
|
|
142
|
-
const published = computed({
|
|
143
|
-
get: () => formState.value.status === "published",
|
|
144
|
-
set: (value) => {
|
|
145
|
-
formState.value.status = value ? "published" : "draft";
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
function togglePublished() {
|
|
149
|
-
published.value = !published.value;
|
|
150
|
-
}
|
|
151
|
-
function revertStatus() {
|
|
152
|
-
if (!drafts) return;
|
|
153
|
-
formState.value.status = JSON.parse(snapshot.value).status ?? null;
|
|
154
|
-
}
|
|
155
|
-
function goBack() {
|
|
156
|
-
navigateTo(`/cms/${name}`);
|
|
157
|
-
}
|
|
147
|
+
const { entryLock } = useCmsPermissions();
|
|
148
|
+
const lock = computed(() => entryLock(name, config, { isNew: false }));
|
|
149
|
+
const locked = computed(
|
|
150
|
+
() => lockedFieldKeys(fields, JSON.parse(snapshot.value))
|
|
151
|
+
);
|
|
152
|
+
const formRef = ref(null);
|
|
153
|
+
const dirty = computed(() => !lock.value && JSON.stringify(formState.value) !== snapshot.value);
|
|
154
|
+
useLeaveGuard(dirty);
|
|
158
155
|
async function save() {
|
|
159
156
|
saving.value = true;
|
|
157
|
+
const submitted = cloneState(formState.value);
|
|
160
158
|
try {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
goBack();
|
|
165
|
-
} catch (error) {
|
|
166
|
-
revertStatus();
|
|
167
|
-
toast.add({
|
|
168
|
-
title: "Save failed",
|
|
169
|
-
description: errorMessage(error),
|
|
170
|
-
color: "error"
|
|
159
|
+
const row = await cmsApi(`${endpoint}/${id}`, {
|
|
160
|
+
method: "PUT",
|
|
161
|
+
body: submitted
|
|
171
162
|
});
|
|
163
|
+
const saved = pickFields(row ?? submitted);
|
|
164
|
+
const merged = mergeSavedState(submitted, formState.value, saved);
|
|
165
|
+
snapshot.value = JSON.stringify(saved);
|
|
166
|
+
formState.value = merged.state;
|
|
167
|
+
if (merged.clean) {
|
|
168
|
+
toast.success("Saved");
|
|
169
|
+
await navigateTo(`/cms/${name}`);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
toast.success("Saved", { description: "Changes made while saving are not saved yet." });
|
|
173
|
+
} catch (error) {
|
|
174
|
+
formRef.value?.setIssues(errorIssues(error));
|
|
175
|
+
toast.error("Save failed", { description: errorMessage(error) });
|
|
172
176
|
} finally {
|
|
173
177
|
saving.value = false;
|
|
174
178
|
}
|
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div class="k-page">
|
|
3
|
+
<KEmptyState
|
|
4
|
+
:icon="cmsIcon(access?.known === false ? 'exclamation-triangle' : 'lock-closed')"
|
|
5
|
+
:title="access?.known === false ? 'Your role no longer exists' : 'Nothing to show yet'"
|
|
6
|
+
:description="
|
|
7
|
+
access?.known === false ? `Your account has the role \'${access.role}\', which is not configured anymore, so it grants no access. Ask an administrator to assign you a new role.` : 'Your role does not give access to any section. Ask an administrator for more permissions.'
|
|
8
|
+
"
|
|
9
|
+
fill
|
|
10
|
+
/>
|
|
11
|
+
</div>
|
|
3
12
|
</template>
|
|
4
13
|
|
|
5
14
|
<script setup>
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
15
|
+
import { EmptyState as KEmptyState } from "@xleddyl/443-components-vue";
|
|
16
|
+
import { definePageMeta, navigateTo, useRoute } from "#imports";
|
|
17
|
+
import { useCmsPermissions } from "../composables/cms-permissions";
|
|
18
|
+
import { cmsIcon } from "../utils/ui";
|
|
8
19
|
definePageMeta({ layout: "cms-admin", middleware: "cms-auth" });
|
|
9
|
-
const
|
|
10
|
-
|
|
20
|
+
const route = useRoute();
|
|
21
|
+
const { access, firstPath } = useCmsPermissions();
|
|
22
|
+
const first = firstPath();
|
|
23
|
+
if (first) await navigateTo({ path: first, query: route.query }, { replace: true });
|
|
11
24
|
</script>
|