@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
|
@@ -1,248 +1,242 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div v-if="config.kind === 'single'" class="k-page">
|
|
3
3
|
<KPageHeader
|
|
4
4
|
:title="config.label"
|
|
5
5
|
:icon="cmsIcon(kindMeta.icon)"
|
|
6
6
|
:description="kindMeta.description"
|
|
7
7
|
>
|
|
8
|
-
<template v-if="
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
<template v-if="config.kind === 'single'" #actions>
|
|
12
|
-
<KButton type="submit" :form="FORM_ID" label="Save" :loading="saving" />
|
|
8
|
+
<template v-if="!singleLock" #actions>
|
|
9
|
+
<CmsTranslationMenu v-model="formState" :fields="config.fields" size="sm" />
|
|
10
|
+
<KButton type="submit" :form="FORM_ID" label="Save" size="sm" :loading="saving" />
|
|
13
11
|
</template>
|
|
14
12
|
</KPageHeader>
|
|
15
13
|
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
14
|
+
<KCard v-if="error" padding="lg" role="alert" fill>
|
|
15
|
+
<KEmptyState
|
|
16
|
+
variant="plain"
|
|
17
|
+
:icon="cmsIcon('exclamation-triangle')"
|
|
18
|
+
title="Could not load entry"
|
|
19
|
+
:description="errorMessage(error)"
|
|
20
|
+
:level="2"
|
|
21
|
+
fill
|
|
22
|
+
>
|
|
23
|
+
<template #actions>
|
|
24
|
+
<KButton
|
|
25
|
+
label="Retry"
|
|
26
|
+
:icon="cmsIcon('arrow-path')"
|
|
27
|
+
variant="soft"
|
|
28
|
+
tone="neutral"
|
|
29
|
+
size="sm"
|
|
30
|
+
:loading="status === 'pending'"
|
|
31
|
+
@click="reload"
|
|
32
|
+
/>
|
|
33
|
+
</template>
|
|
34
|
+
</KEmptyState>
|
|
35
|
+
</KCard>
|
|
31
36
|
|
|
32
|
-
<KCard v-else
|
|
37
|
+
<KCard v-else padding="lg" fill>
|
|
33
38
|
<CmsEntryForm
|
|
39
|
+
ref="singleForm"
|
|
34
40
|
v-model="formState"
|
|
35
41
|
:fields="config.fields"
|
|
36
42
|
:tabs="config.tabs"
|
|
37
43
|
:entry-name="name"
|
|
38
44
|
:form-id="FORM_ID"
|
|
39
|
-
:
|
|
45
|
+
:tab-query="CMS_TAB_QUERY"
|
|
40
46
|
:focus="focus"
|
|
47
|
+
:lock="singleLock"
|
|
48
|
+
:locked="singleLocked"
|
|
41
49
|
@submit="saveSingle"
|
|
42
50
|
/>
|
|
43
51
|
</KCard>
|
|
52
|
+
</div>
|
|
44
53
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
<KListPage
|
|
55
|
+
v-else
|
|
56
|
+
v-model:page="page"
|
|
57
|
+
v-model:page-size="pageSize"
|
|
58
|
+
v-model:search="search"
|
|
59
|
+
v-model:sort="sort"
|
|
60
|
+
v-model:selected="selected"
|
|
61
|
+
:title="config.label"
|
|
62
|
+
:icon="cmsIcon(kindMeta.icon)"
|
|
63
|
+
:description="kindMeta.description"
|
|
64
|
+
:rows="rows"
|
|
65
|
+
:columns="columns"
|
|
66
|
+
:row-key="rowKey"
|
|
67
|
+
:table-label="config.label"
|
|
68
|
+
:selectable="selectable"
|
|
69
|
+
:loading="pending"
|
|
70
|
+
:error="error ? errorMessage(error) : void 0"
|
|
71
|
+
error-title="Could not load entries"
|
|
72
|
+
:total="total"
|
|
73
|
+
:page-sizes="PAGE_SIZES"
|
|
74
|
+
:all-ids="selectable ? allIds : void 0"
|
|
75
|
+
fill
|
|
76
|
+
count
|
|
77
|
+
searchable
|
|
78
|
+
:search-placeholder="isPages ? 'Search pages' : 'Search entries'"
|
|
79
|
+
:search-label="isPages ? 'Search pages' : 'Search entries'"
|
|
80
|
+
toolbar-label="Search and filters"
|
|
81
|
+
:filtered="filterCount > 0"
|
|
82
|
+
:empty-icon="cmsIcon(isPages ? 'document-text' : 'sparkles')"
|
|
83
|
+
:empty-title="isPages ? 'No pages yet' : 'Nothing here yet'"
|
|
84
|
+
:empty-description="
|
|
85
|
+
isPages ? 'Pages come from the routes of the app.' : 'Entries you create will show up here.'
|
|
86
|
+
"
|
|
87
|
+
no-results-title="No matching entries"
|
|
88
|
+
no-results-description="Try another search or clear the filters."
|
|
89
|
+
clear-label="Clear filters"
|
|
90
|
+
:results-text="resultsLabel"
|
|
91
|
+
@retry="reload"
|
|
92
|
+
@clear="clearNarrowing"
|
|
93
|
+
@row-click="onSelect"
|
|
94
|
+
>
|
|
95
|
+
<template v-if="isCollectionKind(config)" #actions>
|
|
96
|
+
<KDropdownMenu v-if="transferItems.length" :items="transferItems" placement="bottom-end">
|
|
97
|
+
<KButton
|
|
98
|
+
:icon="cmsIcon('arrows-up-down')"
|
|
99
|
+
variant="ghost"
|
|
100
|
+
tone="neutral"
|
|
101
|
+
size="sm"
|
|
102
|
+
square
|
|
103
|
+
:loading="exporting"
|
|
104
|
+
aria-label="Import and export"
|
|
53
105
|
/>
|
|
54
|
-
</
|
|
55
|
-
|
|
56
|
-
<KCard v-if="pageTree.length" variant="outline" padding="none" class="cms-tree">
|
|
57
|
-
<NuxtLink
|
|
58
|
-
v-for="(node, index) in pageTree"
|
|
59
|
-
:key="String(node.row.id)"
|
|
60
|
-
:to="`/cms/${name}/${node.row.id}`"
|
|
61
|
-
class="cms-tree-row"
|
|
62
|
-
:style="{
|
|
63
|
-
paddingLeft: `${1 + node.depth * 1.5}rem`,
|
|
64
|
-
'--cms-row-delay': `${Math.min(index, 14) * 24}ms`
|
|
65
|
-
}"
|
|
66
|
-
>
|
|
67
|
-
<KIcon
|
|
68
|
-
v-if="node.depth"
|
|
69
|
-
:name="cmsIcon('arrow-turn-left-up')"
|
|
70
|
-
class="cms-tree-branch"
|
|
71
|
-
/>
|
|
72
|
-
<span class="cms-tree-doc">
|
|
73
|
-
<KIcon :name="cmsIcon('document-text')" />
|
|
74
|
-
</span>
|
|
75
|
-
<span class="cms-tree-text">
|
|
76
|
-
<span class="cms-tree-title">{{ node.row.label }}</span>
|
|
77
|
-
<span class="cms-tree-path">{{ node.row.path }}</span>
|
|
78
|
-
</span>
|
|
79
|
-
<span class="cms-tree-meta">{{ lastEdit(node.row.updatedAt) }}</span>
|
|
80
|
-
<KIcon name="chevron-right" class="cms-tree-chevron" />
|
|
81
|
-
</NuxtLink>
|
|
82
|
-
</KCard>
|
|
83
|
-
|
|
84
|
-
<KSpinner v-else-if="status === 'pending'" block />
|
|
85
|
-
|
|
86
|
-
<KEmptyState
|
|
87
|
-
v-else-if="searchTerm"
|
|
88
|
-
:icon="cmsIcon('magnifying-glass')"
|
|
89
|
-
title="No matching pages"
|
|
90
|
-
/>
|
|
91
|
-
|
|
92
|
-
<KEmptyState
|
|
93
|
-
v-else
|
|
94
|
-
:icon="cmsIcon('document-text')"
|
|
95
|
-
title="No pages yet"
|
|
96
|
-
description="Pages come from the routes of the app."
|
|
97
|
-
/>
|
|
98
|
-
|
|
99
|
-
<KPagination
|
|
100
|
-
v-model:page="page"
|
|
101
|
-
:total="total"
|
|
102
|
-
:page-size="PAGE_SIZE"
|
|
103
|
-
:show-summary="false"
|
|
104
|
-
/>
|
|
106
|
+
</KDropdownMenu>
|
|
107
|
+
<KCommandButton v-if="newCommand" :command="newCommand" size="sm" />
|
|
105
108
|
</template>
|
|
106
109
|
|
|
107
|
-
<template v-
|
|
108
|
-
<
|
|
109
|
-
|
|
110
|
-
v-if="total || narrowed"
|
|
111
|
-
v-model="search"
|
|
112
|
-
:icon="cmsIcon('magnifying-glass')"
|
|
113
|
-
placeholder="Search…"
|
|
114
|
-
aria-label="Search entries"
|
|
115
|
-
class="cms-toolbar-search"
|
|
116
|
-
/>
|
|
117
|
-
<div class="cms-toolbar-actions">
|
|
118
|
-
<KSelectionBar
|
|
119
|
-
:count="selected.length"
|
|
120
|
-
:total="rows.length"
|
|
121
|
-
:on-select-all="selectAll"
|
|
122
|
-
:on-clear="clearSelection"
|
|
123
|
-
>
|
|
124
|
-
<template #actions>
|
|
125
|
-
<template v-if="drafts">
|
|
126
|
-
<KButton
|
|
127
|
-
label="Draft"
|
|
128
|
-
:icon="cmsIcon('pencil-square')"
|
|
129
|
-
variant="soft"
|
|
130
|
-
:disabled="saving"
|
|
131
|
-
@click="bulkStatus('draft')"
|
|
132
|
-
/>
|
|
133
|
-
<KButton
|
|
134
|
-
label="Publish"
|
|
135
|
-
:icon="cmsIcon('check-circle')"
|
|
136
|
-
variant="soft"
|
|
137
|
-
:disabled="saving"
|
|
138
|
-
@click="bulkStatus('published')"
|
|
139
|
-
/>
|
|
140
|
-
</template>
|
|
141
|
-
<KButton
|
|
142
|
-
v-if="selected.length === 1"
|
|
143
|
-
label="Duplicate"
|
|
144
|
-
:icon="cmsIcon('document-duplicate')"
|
|
145
|
-
variant="soft"
|
|
146
|
-
:disabled="saving"
|
|
147
|
-
@click="duplicateSelected"
|
|
148
|
-
/>
|
|
149
|
-
<KButton
|
|
150
|
-
label="Delete"
|
|
151
|
-
:icon="cmsIcon('trash')"
|
|
152
|
-
tone="danger"
|
|
153
|
-
:loading="saving"
|
|
154
|
-
@click="bulkDelete"
|
|
155
|
-
/>
|
|
156
|
-
</template>
|
|
157
|
-
</KSelectionBar>
|
|
158
|
-
<template v-if="!selected.length">
|
|
159
|
-
<KDropdownMenu :items="transferItems" placement="bottom-end">
|
|
160
|
-
<KButton
|
|
161
|
-
:icon="cmsIcon('arrows-up-down')"
|
|
162
|
-
variant="soft"
|
|
163
|
-
tone="neutral"
|
|
164
|
-
:loading="exporting"
|
|
165
|
-
aria-label="Import and export"
|
|
166
|
-
title="Import and export"
|
|
167
|
-
/>
|
|
168
|
-
</KDropdownMenu>
|
|
169
|
-
<KButton label="New entry" icon="plus" @click="openCreate" />
|
|
170
|
-
</template>
|
|
171
|
-
</div>
|
|
172
|
-
</div>
|
|
110
|
+
<template v-if="newCommand" #empty-actions>
|
|
111
|
+
<KCommandButton :command="newCommand" size="sm" />
|
|
112
|
+
</template>
|
|
173
113
|
|
|
114
|
+
<template v-if="filterKeys.length" #toolbar>
|
|
174
115
|
<CmsListFilters
|
|
175
|
-
v-if="filterKeys.length && (total || narrowed)"
|
|
176
116
|
v-model="filters"
|
|
177
117
|
:config="config"
|
|
178
118
|
:keys="filterKeys"
|
|
179
119
|
:relations="relations"
|
|
180
120
|
/>
|
|
121
|
+
</template>
|
|
181
122
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
</
|
|
203
|
-
|
|
204
|
-
|
|
123
|
+
<template v-if="selectable" #bulk>
|
|
124
|
+
<template v-if="canBulkStatus">
|
|
125
|
+
<KButton
|
|
126
|
+
label="Draft"
|
|
127
|
+
:icon="cmsIcon('pencil-square')"
|
|
128
|
+
variant="soft"
|
|
129
|
+
tone="neutral"
|
|
130
|
+
size="sm"
|
|
131
|
+
:disabled="saving"
|
|
132
|
+
@click="bulkStatus('draft')"
|
|
133
|
+
/>
|
|
134
|
+
<KButton
|
|
135
|
+
label="Publish"
|
|
136
|
+
:icon="cmsIcon('check-circle')"
|
|
137
|
+
variant="soft"
|
|
138
|
+
tone="neutral"
|
|
139
|
+
size="sm"
|
|
140
|
+
:disabled="saving"
|
|
141
|
+
@click="bulkStatus('published')"
|
|
142
|
+
/>
|
|
143
|
+
</template>
|
|
144
|
+
<KButton
|
|
145
|
+
v-if="canCreate"
|
|
146
|
+
label="Duplicate"
|
|
147
|
+
:icon="cmsIcon('document-duplicate')"
|
|
148
|
+
variant="soft"
|
|
149
|
+
tone="neutral"
|
|
150
|
+
size="sm"
|
|
151
|
+
:disabled="saving || selected.length !== 1"
|
|
152
|
+
disabled-reason="Select one entry to duplicate"
|
|
153
|
+
@click="duplicateSelected"
|
|
154
|
+
/>
|
|
155
|
+
<KButton
|
|
156
|
+
v-if="canDelete"
|
|
157
|
+
label="Delete"
|
|
158
|
+
icon="trash"
|
|
159
|
+
variant="soft"
|
|
160
|
+
tone="danger"
|
|
161
|
+
size="sm"
|
|
162
|
+
:loading="saving"
|
|
163
|
+
@click="bulkDelete"
|
|
164
|
+
/>
|
|
165
|
+
</template>
|
|
205
166
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
167
|
+
<template v-if="isPages" #table>
|
|
168
|
+
<KSkeletonList
|
|
169
|
+
v-if="pending && !pageTree.length"
|
|
170
|
+
:rows="6"
|
|
171
|
+
:lines="2"
|
|
172
|
+
class="cms-list-loading"
|
|
173
|
+
/>
|
|
174
|
+
<KTree
|
|
175
|
+
v-else-if="pageTree.length"
|
|
176
|
+
v-model:expanded="pageExpanded"
|
|
177
|
+
:nodes="pageTree"
|
|
178
|
+
:bordered="false"
|
|
179
|
+
:selectable="false"
|
|
180
|
+
label="Pages"
|
|
211
181
|
>
|
|
212
|
-
<template
|
|
213
|
-
<
|
|
182
|
+
<template #meta="{ node }">
|
|
183
|
+
<KStack direction="row" gap="sm" align="center">
|
|
184
|
+
<CmsTranslationBadges
|
|
185
|
+
v-if="pageTranslations"
|
|
186
|
+
:progress="pageProgress(pageRows.get(String(node.id)))"
|
|
187
|
+
/>
|
|
188
|
+
<span>{{ node.meta }}</span>
|
|
189
|
+
</KStack>
|
|
214
190
|
</template>
|
|
215
|
-
</
|
|
216
|
-
|
|
191
|
+
</KTree>
|
|
217
192
|
<KEmptyState
|
|
218
193
|
v-else
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
194
|
+
variant="plain"
|
|
195
|
+
size="sm"
|
|
196
|
+
icon="magnifying-glass"
|
|
197
|
+
title="No matching pages"
|
|
198
|
+
class="cms-list-loading"
|
|
223
199
|
>
|
|
224
200
|
<template #actions>
|
|
225
|
-
<KButton
|
|
201
|
+
<KButton
|
|
202
|
+
label="Clear search"
|
|
203
|
+
variant="soft"
|
|
204
|
+
tone="neutral"
|
|
205
|
+
size="sm"
|
|
206
|
+
@click="clearNarrowing"
|
|
207
|
+
/>
|
|
226
208
|
</template>
|
|
227
209
|
</KEmptyState>
|
|
210
|
+
</template>
|
|
228
211
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
212
|
+
<template v-for="key in mediaKeys" #[`cell-${key}`]="{ row }" :key="key">
|
|
213
|
+
<CmsMediaThumb :value="mediaThumbValue(key, row[key])" />
|
|
214
|
+
</template>
|
|
215
|
+
<template v-if="translationsShown" #[`cell-${TRANSLATIONS_COLUMN}`]="{ row }">
|
|
216
|
+
<CmsTranslationBadges :progress="rowProgress(row)" />
|
|
217
|
+
</template>
|
|
218
|
+
<template v-if="drafts" #cell-status="{ row }">
|
|
219
|
+
<CmsStatusBadge
|
|
220
|
+
:published="row.status === 'published'"
|
|
221
|
+
:scheduled="mounted && isContent && isScheduledContent(row)"
|
|
222
|
+
:at="formatDateTime(row.publishedAt)"
|
|
234
223
|
/>
|
|
224
|
+
</template>
|
|
235
225
|
|
|
226
|
+
<template v-if="isCollectionKind(config)">
|
|
236
227
|
<CmsEntryDrawer
|
|
237
228
|
v-if="!isContent"
|
|
238
229
|
v-model:open="drawerOpen"
|
|
239
230
|
:collection="name"
|
|
240
231
|
:config="config"
|
|
241
|
-
:entry-id="
|
|
242
|
-
:
|
|
243
|
-
|
|
232
|
+
:entry-id="entryRecord.isNew.value ? null : entryRecord.id.value"
|
|
233
|
+
:duplicate-id="duplicateId"
|
|
234
|
+
:focus="drawerFocus"
|
|
235
|
+
@saved="() => refresh()"
|
|
236
|
+
@created="onCreated"
|
|
244
237
|
@deleted="onDrawerDeleted"
|
|
245
|
-
@duplicate="
|
|
238
|
+
@duplicate="(id) => openRecord(CMS_NEW_RECORD, { duplicate: id })"
|
|
239
|
+
@missing="entryRecord.close()"
|
|
246
240
|
/>
|
|
247
241
|
|
|
248
242
|
<CmsImportModal
|
|
@@ -252,7 +246,7 @@
|
|
|
252
246
|
@imported="refresh"
|
|
253
247
|
/>
|
|
254
248
|
</template>
|
|
255
|
-
</
|
|
249
|
+
</KListPage>
|
|
256
250
|
</template>
|
|
257
251
|
|
|
258
252
|
<script setup>
|
|
@@ -261,8 +255,11 @@ import {
|
|
|
261
255
|
isScheduledContent,
|
|
262
256
|
isTranslatableField,
|
|
263
257
|
isTranslatableMediaField,
|
|
258
|
+
linkDisplayText,
|
|
259
|
+
pageFields,
|
|
264
260
|
pageParentPath,
|
|
265
261
|
pickTranslatedMedia,
|
|
262
|
+
relationTarget,
|
|
266
263
|
selectDisplayValue
|
|
267
264
|
} from "#nuxt-cms";
|
|
268
265
|
import {
|
|
@@ -271,6 +268,8 @@ import {
|
|
|
271
268
|
definePageMeta,
|
|
272
269
|
navigateTo,
|
|
273
270
|
onMounted,
|
|
271
|
+
useRouter,
|
|
272
|
+
useState,
|
|
274
273
|
ref,
|
|
275
274
|
useFetch,
|
|
276
275
|
useRoute,
|
|
@@ -279,41 +278,62 @@ import {
|
|
|
279
278
|
import {
|
|
280
279
|
Button as KButton,
|
|
281
280
|
Card as KCard,
|
|
281
|
+
CommandButton as KCommandButton,
|
|
282
282
|
DropdownMenu as KDropdownMenu,
|
|
283
283
|
EmptyState as KEmptyState,
|
|
284
|
-
|
|
285
|
-
Input as KInput,
|
|
284
|
+
ListPage as KListPage,
|
|
286
285
|
PageHeader as KPageHeader,
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
286
|
+
SkeletonList as KSkeletonList,
|
|
287
|
+
Stack as KStack,
|
|
288
|
+
Tree as KTree,
|
|
289
|
+
routeHashValue,
|
|
290
|
+
toast,
|
|
291
|
+
useLeaveGuard,
|
|
292
|
+
usePageCommands,
|
|
293
|
+
useRouteRecord
|
|
291
294
|
} from "@xleddyl/443-components-vue";
|
|
292
295
|
import cmsConfig from "#cms-config";
|
|
293
|
-
import {
|
|
296
|
+
import { entryDefaults, lockedFieldKeys } from "../../shared/field-options";
|
|
294
297
|
import {
|
|
298
|
+
LIST_FILTER_PREFIX,
|
|
299
|
+
LIST_SEARCH_PARAM as CMS_LIST_SEARCH_PARAM,
|
|
295
300
|
entryListFilterKeys,
|
|
296
301
|
listFilterQuery,
|
|
297
302
|
parseListFilters,
|
|
298
303
|
withoutListFilterQuery
|
|
299
304
|
} from "../../shared/list-filters";
|
|
300
|
-
import {
|
|
305
|
+
import {
|
|
306
|
+
CMS_ENTRY_FOCUS_KEYS,
|
|
307
|
+
CMS_ENTRY_ROUTE_KEY,
|
|
308
|
+
CMS_ENTRY_TAB_ROUTE_KEY,
|
|
309
|
+
CMS_TAB_QUERY,
|
|
310
|
+
cmsPageKey,
|
|
311
|
+
readSearchFocus
|
|
312
|
+
} from "../../shared/search";
|
|
313
|
+
import {
|
|
314
|
+
TRANSLATIONS_COLUMN,
|
|
315
|
+
entryHasTranslations,
|
|
316
|
+
translationProgress
|
|
317
|
+
} from "../../shared/translations";
|
|
301
318
|
import { canTransferCsv, exportFileName } from "../../shared/transfer";
|
|
302
|
-
import {
|
|
319
|
+
import { useCmsPermissions } from "../composables/cms-permissions";
|
|
320
|
+
import { CMS_NEW_RECORD, useCmsPageQuery, useCmsRouteFlag } from "../composables/cms-route";
|
|
321
|
+
import { useCmsLinkedDelete } from "../composables/cms-link-usage";
|
|
303
322
|
import { useCmsRuntime } from "../composables/cms-runtime";
|
|
304
323
|
import { entryListColumns, useCmsSettingsState } from "../composables/cms-settings";
|
|
305
|
-
import { useCmsToast } from "../composables/cms-toast";
|
|
306
324
|
import { cmsApi } from "../utils/api";
|
|
307
|
-
import { cmsIcon, errorMessage } from "../utils/ui";
|
|
325
|
+
import { cmsIcon, errorIssues, errorMessage } from "../utils/ui";
|
|
308
326
|
definePageMeta({
|
|
309
327
|
layout: "cms-admin",
|
|
310
328
|
middleware: "cms-auth",
|
|
311
329
|
validate: (route2) => Object.hasOwn(cmsConfig, route2.params.collection),
|
|
312
|
-
key: (route2) =>
|
|
330
|
+
key: (route2) => cmsPageKey({ path: route2.path, query: withoutListFilterQuery(route2.query) }, [
|
|
331
|
+
CMS_LIST_SEARCH_PARAM,
|
|
332
|
+
CMS_TAB_QUERY
|
|
333
|
+
])
|
|
313
334
|
});
|
|
314
335
|
const FORM_ID = "cms-single-form";
|
|
315
336
|
const route = useRoute();
|
|
316
|
-
const toast = useCmsToast();
|
|
317
337
|
const focus = readSearchFocus(route.query);
|
|
318
338
|
const name = route.params.collection;
|
|
319
339
|
const config = cmsConfig[name];
|
|
@@ -330,21 +350,51 @@ const kindMeta = { ...KIND_META[config.kind], icon: config.icon ?? KIND_META[con
|
|
|
330
350
|
const fieldKeys = Object.keys(config.fields);
|
|
331
351
|
const mediaKeys = fieldKeys.filter((key) => config.fields[key].type === "media");
|
|
332
352
|
const isContent = config.kind === "content";
|
|
353
|
+
const isPages = config.kind === "page";
|
|
333
354
|
const drafts = isCollectionKind(config) && !!config.drafts;
|
|
334
355
|
const formKeys = drafts ? [...fieldKeys, "status"] : fieldKeys;
|
|
356
|
+
const { can, entryLock } = useCmsPermissions();
|
|
357
|
+
const canCreate = computed(() => can(`${name}.create`));
|
|
358
|
+
const canDelete = computed(() => can(`${name}.delete`));
|
|
359
|
+
const canBulkStatus = computed(() => drafts && can(`${name}.update`) && can(`${name}.publish`));
|
|
360
|
+
const selectable = computed(
|
|
361
|
+
() => isCollectionKind(config) && (canCreate.value || canDelete.value || canBulkStatus.value)
|
|
362
|
+
);
|
|
363
|
+
const singleLock = computed(
|
|
364
|
+
() => config.kind === "single" ? entryLock(name, config, { isNew: false }) : null
|
|
365
|
+
);
|
|
335
366
|
const endpoint = `/api/cms/admin/${name}`;
|
|
336
|
-
const
|
|
367
|
+
const PAGE_SIZES = [25, 50, 100];
|
|
337
368
|
const page = ref(1);
|
|
338
|
-
const
|
|
339
|
-
const
|
|
369
|
+
const pageSize = ref(25);
|
|
370
|
+
const limit = computed(() => typeof pageSize.value === "number" ? pageSize.value : 100);
|
|
371
|
+
const routeSearch = useCmsPageQuery(CMS_LIST_SEARCH_PARAM);
|
|
372
|
+
const search = ref(routeSearch.value);
|
|
373
|
+
const searchTerm = ref(search.value.trim());
|
|
340
374
|
let searchTimer;
|
|
341
375
|
watch(search, (value) => {
|
|
342
376
|
clearTimeout(searchTimer);
|
|
343
377
|
searchTimer = setTimeout(() => {
|
|
378
|
+
if (searchTerm.value === value.trim()) return;
|
|
344
379
|
searchTerm.value = value.trim();
|
|
380
|
+
routeSearch.value = searchTerm.value;
|
|
345
381
|
page.value = 1;
|
|
346
382
|
}, 300);
|
|
347
383
|
});
|
|
384
|
+
const router = useRouter();
|
|
385
|
+
const listPath = route.path;
|
|
386
|
+
function onListPage() {
|
|
387
|
+
return router.currentRoute.value.path === listPath;
|
|
388
|
+
}
|
|
389
|
+
watch(routeSearch, (value) => {
|
|
390
|
+
if (value === searchTerm.value || !onListPage()) return;
|
|
391
|
+
search.value = value;
|
|
392
|
+
searchTerm.value = value;
|
|
393
|
+
page.value = 1;
|
|
394
|
+
});
|
|
395
|
+
watch(pageSize, () => {
|
|
396
|
+
page.value = 1;
|
|
397
|
+
});
|
|
348
398
|
const sort = ref(null);
|
|
349
399
|
watch(sort, () => {
|
|
350
400
|
page.value = 1;
|
|
@@ -353,26 +403,52 @@ const filters = computed({
|
|
|
353
403
|
get: () => isCollectionKind(config) ? parseListFilters(config, route.query).filters : {},
|
|
354
404
|
set: (value) => {
|
|
355
405
|
void navigateTo(
|
|
356
|
-
{
|
|
406
|
+
{
|
|
407
|
+
query: { ...withoutListFilterQuery(route.query), ...listFilterQuery(value) },
|
|
408
|
+
hash: route.hash
|
|
409
|
+
},
|
|
357
410
|
{ replace: true }
|
|
358
411
|
);
|
|
359
412
|
}
|
|
360
413
|
});
|
|
361
|
-
const
|
|
414
|
+
const filterQueryKey = computed(() => JSON.stringify(listFilterQuery(filters.value)));
|
|
415
|
+
const filterQuery = computed(() => JSON.parse(filterQueryKey.value));
|
|
362
416
|
const filterCount = computed(() => Object.keys(filterQuery.value).length);
|
|
363
417
|
const narrowed = computed(() => !!searchTerm.value || filterCount.value > 0);
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
()
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
418
|
+
const rememberedQuery = useState(`cms-list-query-${name}`, () => ({}));
|
|
419
|
+
watch([filterQuery, searchTerm], () => {
|
|
420
|
+
if (!onListPage()) return;
|
|
421
|
+
rememberedQuery.value = {
|
|
422
|
+
...filterQuery.value,
|
|
423
|
+
...searchTerm.value ? { [CMS_LIST_SEARCH_PARAM]: searchTerm.value } : {}
|
|
424
|
+
};
|
|
425
|
+
});
|
|
426
|
+
onMounted(() => {
|
|
427
|
+
const remembered = rememberedQuery.value;
|
|
428
|
+
const hasListQuery = Object.keys(route.query).some(
|
|
429
|
+
(key) => key === CMS_LIST_SEARCH_PARAM || key.startsWith(LIST_FILTER_PREFIX)
|
|
430
|
+
);
|
|
431
|
+
if (hasListQuery || !Object.keys(remembered).length || !isCollectionKind(config)) return;
|
|
432
|
+
void navigateTo(
|
|
433
|
+
{ query: { ...route.query, ...remembered }, hash: route.hash },
|
|
434
|
+
{ replace: true }
|
|
435
|
+
);
|
|
436
|
+
});
|
|
437
|
+
function clearNarrowing() {
|
|
438
|
+
search.value = "";
|
|
439
|
+
searchTerm.value = "";
|
|
440
|
+
routeSearch.value = "";
|
|
441
|
+
filters.value = {};
|
|
442
|
+
}
|
|
443
|
+
watch(filterQueryKey, () => {
|
|
444
|
+
page.value = 1;
|
|
445
|
+
});
|
|
370
446
|
const sortQuery = computed(
|
|
371
447
|
() => sort.value ? { sort: sort.value.key, order: sort.value.direction } : {}
|
|
372
448
|
);
|
|
373
449
|
const listQuery = computed(() => ({
|
|
374
|
-
limit:
|
|
375
|
-
offset: (page.value - 1) *
|
|
450
|
+
limit: limit.value,
|
|
451
|
+
offset: (page.value - 1) * limit.value,
|
|
376
452
|
...searchTerm.value ? { search: searchTerm.value } : {},
|
|
377
453
|
...sortQuery.value,
|
|
378
454
|
...filterQuery.value
|
|
@@ -399,32 +475,76 @@ function lastEdit(value) {
|
|
|
399
475
|
minute: "2-digit"
|
|
400
476
|
})}`;
|
|
401
477
|
}
|
|
478
|
+
const pageRows = computed(() => new Map(rows.value.map((row) => [String(row.id), row])));
|
|
402
479
|
const pageTree = computed(() => {
|
|
403
|
-
const byPath = new
|
|
404
|
-
const
|
|
405
|
-
let depth = 0;
|
|
480
|
+
const byPath = new Map(rows.value.map((row) => [String(row.path), row]));
|
|
481
|
+
const parentOf = (path) => {
|
|
406
482
|
let parent = pageParentPath(path);
|
|
407
483
|
while (parent) {
|
|
408
|
-
|
|
484
|
+
const row = byPath.get(parent);
|
|
485
|
+
if (row) return String(row.id);
|
|
409
486
|
parent = pageParentPath(parent);
|
|
410
487
|
}
|
|
411
|
-
return
|
|
488
|
+
return null;
|
|
412
489
|
};
|
|
413
|
-
|
|
490
|
+
const children = /* @__PURE__ */ new Map();
|
|
491
|
+
for (const row of rows.value) {
|
|
492
|
+
const parent = parentOf(String(row.path));
|
|
493
|
+
children.set(parent, [...children.get(parent) ?? [], row]);
|
|
494
|
+
}
|
|
495
|
+
const build = (parent) => (children.get(parent) ?? []).map((row) => {
|
|
496
|
+
const nested = build(String(row.id));
|
|
497
|
+
return {
|
|
498
|
+
id: String(row.id),
|
|
499
|
+
label: String(row.label),
|
|
500
|
+
description: String(row.path),
|
|
501
|
+
meta: lastEdit(row.updatedAt),
|
|
502
|
+
icon: cmsIcon("document-text"),
|
|
503
|
+
to: `/cms/${name}/${row.id}`,
|
|
504
|
+
children: nested.length ? nested : void 0
|
|
505
|
+
};
|
|
506
|
+
});
|
|
507
|
+
return build(null);
|
|
508
|
+
});
|
|
509
|
+
const collapsedPages = ref([]);
|
|
510
|
+
const pageExpanded = computed({
|
|
511
|
+
get: () => [...pageTreeParents.value].filter((id) => !collapsedPages.value.includes(id)),
|
|
512
|
+
set: (ids) => {
|
|
513
|
+
collapsedPages.value = [...pageTreeParents.value].filter((id) => !ids.includes(id));
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
const pageTreeParents = computed(() => {
|
|
517
|
+
const ids = /* @__PURE__ */ new Set();
|
|
518
|
+
const walk = (nodes) => {
|
|
519
|
+
for (const node of nodes) {
|
|
520
|
+
if (!node.children) continue;
|
|
521
|
+
ids.add(node.id);
|
|
522
|
+
walk(node.children);
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
walk(pageTree.value);
|
|
526
|
+
return ids;
|
|
414
527
|
});
|
|
415
528
|
const total = computed(() => isList(data.value) ? data.value.total : 0);
|
|
416
529
|
const relations = computed(() => isList(data.value) ? data.value.relations ?? {} : {});
|
|
530
|
+
const pending = computed(() => status.value === "pending");
|
|
417
531
|
const reload = async () => {
|
|
418
532
|
await refresh();
|
|
419
|
-
if (config.kind === "single" && !error.value)
|
|
420
|
-
formState.value = data.value && !isList(data.value) ? pickFields(data.value) : emptyState();
|
|
421
|
-
}
|
|
533
|
+
if (config.kind === "single" && !error.value) resetSingle();
|
|
422
534
|
};
|
|
423
535
|
const saving = ref(false);
|
|
424
536
|
const formState = ref({});
|
|
537
|
+
const { i18n: contentI18n } = useCmsRuntime();
|
|
425
538
|
function emptyState() {
|
|
426
|
-
return
|
|
539
|
+
return {
|
|
540
|
+
...Object.fromEntries(formKeys.map((k) => [k, null])),
|
|
541
|
+
...entryDefaults(config.fields, contentI18n, { local: true })
|
|
542
|
+
};
|
|
427
543
|
}
|
|
544
|
+
const singleForm = ref(null);
|
|
545
|
+
const singleLocked = computed(
|
|
546
|
+
() => config.kind === "single" && data.value && !isList(data.value) ? lockedFieldKeys(config.fields, data.value) : []
|
|
547
|
+
);
|
|
428
548
|
function pickFields(row) {
|
|
429
549
|
return Object.fromEntries(formKeys.map((k) => [k, row[k] ?? null]));
|
|
430
550
|
}
|
|
@@ -436,24 +556,28 @@ async function submit(action) {
|
|
|
436
556
|
await refresh();
|
|
437
557
|
return true;
|
|
438
558
|
} catch (error2) {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
description: errorMessage(error2),
|
|
442
|
-
color: "error"
|
|
443
|
-
});
|
|
559
|
+
singleForm.value?.setIssues(errorIssues(error2));
|
|
560
|
+
toast.error("Save failed", { description: errorMessage(error2) });
|
|
444
561
|
return false;
|
|
445
562
|
} finally {
|
|
446
563
|
saving.value = false;
|
|
447
564
|
}
|
|
448
565
|
}
|
|
449
|
-
|
|
566
|
+
const singleSnapshot = ref("");
|
|
567
|
+
function resetSingle() {
|
|
450
568
|
formState.value = data.value && !isList(data.value) ? pickFields(data.value) : emptyState();
|
|
569
|
+
singleSnapshot.value = JSON.stringify(formState.value);
|
|
451
570
|
}
|
|
571
|
+
if (config.kind === "single") resetSingle();
|
|
572
|
+
useLeaveGuard(
|
|
573
|
+
() => config.kind === "single" && !singleLock.value && JSON.stringify(formState.value) !== singleSnapshot.value
|
|
574
|
+
);
|
|
452
575
|
async function saveSingle() {
|
|
576
|
+
const submitted = JSON.stringify(formState.value);
|
|
453
577
|
const ok = await submit(() => cmsApi(endpoint, { method: "PUT", body: formState.value }));
|
|
454
|
-
if (ok)
|
|
578
|
+
if (ok) singleSnapshot.value = submitted;
|
|
579
|
+
if (ok) toast.success("Saved");
|
|
455
580
|
}
|
|
456
|
-
const { i18n: contentI18n } = useCmsRuntime();
|
|
457
581
|
function stripHtml(html) {
|
|
458
582
|
return html.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
|
|
459
583
|
}
|
|
@@ -482,7 +606,7 @@ function formatDateTime(value) {
|
|
|
482
606
|
function relationLabel(field, key, id) {
|
|
483
607
|
const title = relations.value[key]?.[String(id)];
|
|
484
608
|
if (title == null || title === "") return `#${id}`;
|
|
485
|
-
const target = cmsConfig[field
|
|
609
|
+
const target = cmsConfig[relationTarget(field)];
|
|
486
610
|
const titleField = target?.titleField ? target.fields[target.titleField] : void 0;
|
|
487
611
|
const label = titleField && isTranslatableField(titleField) ? localized(title) : titleField?.type === "select" ? selectDisplayValue(titleField, title) : String(title);
|
|
488
612
|
return label || `#${id}`;
|
|
@@ -500,6 +624,10 @@ function displayValue(field, key, value) {
|
|
|
500
624
|
return truncate(stripHtml(String(value)));
|
|
501
625
|
case "json":
|
|
502
626
|
return truncate(JSON.stringify(value));
|
|
627
|
+
case "link":
|
|
628
|
+
return truncate(
|
|
629
|
+
linkDisplayText(value, contentI18n.defaultLocale, contentI18n.defaultLocale)
|
|
630
|
+
);
|
|
503
631
|
case "datetime":
|
|
504
632
|
return formatDateTime(value);
|
|
505
633
|
case "select":
|
|
@@ -518,13 +646,22 @@ function displayValue(field, key, value) {
|
|
|
518
646
|
}
|
|
519
647
|
}
|
|
520
648
|
function isSortable(field) {
|
|
521
|
-
if (field.type === "blocks") return false;
|
|
649
|
+
if (field.type === "blocks" || field.type === "link") return false;
|
|
522
650
|
return !(field.type === "relation" && field.cardinality === "many-to-many");
|
|
523
651
|
}
|
|
524
652
|
const settings = useCmsSettingsState();
|
|
653
|
+
const listColumns = computed(() => entryListColumns(name, settings.value, contentI18n));
|
|
654
|
+
const translationsShown = computed(() => listColumns.value.includes(TRANSLATIONS_COLUMN));
|
|
655
|
+
const pageTranslations = config.kind === "page" && contentI18n.locales.length > 1 && entryHasTranslations(config);
|
|
656
|
+
function rowProgress(row) {
|
|
657
|
+
return translationProgress(config.fields, row, contentI18n);
|
|
658
|
+
}
|
|
659
|
+
function pageProgress(row) {
|
|
660
|
+
return translationProgress(pageFields(config, String(row.path)), row, contentI18n);
|
|
661
|
+
}
|
|
525
662
|
const columns = computed(
|
|
526
|
-
() =>
|
|
527
|
-
(key) => key === "status" && !config.fields.status ? { key: "status", label: "Status", sortable: true } : {
|
|
663
|
+
() => listColumns.value.map(
|
|
664
|
+
(key) => key === TRANSLATIONS_COLUMN ? { key, label: "Translations" } : key === "status" && !config.fields.status ? { key: "status", label: "Status", sortable: true } : {
|
|
528
665
|
key,
|
|
529
666
|
label: config.fields[key].label,
|
|
530
667
|
value: (row) => displayValue(config.fields[key], key, row[key]),
|
|
@@ -537,55 +674,68 @@ function rowKey(row) {
|
|
|
537
674
|
}
|
|
538
675
|
const filterKeys = computed(() => {
|
|
539
676
|
if (!isCollectionKind(config)) return [];
|
|
540
|
-
const shown = entryListFilterKeys(config,
|
|
677
|
+
const shown = entryListFilterKeys(config, listColumns.value);
|
|
541
678
|
const active = Object.keys(filters.value).filter((key) => !shown.includes(key));
|
|
542
679
|
return [...shown, ...active];
|
|
543
680
|
});
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
|
|
681
|
+
const entryRecord = useRouteRecord(CMS_ENTRY_ROUTE_KEY, {
|
|
682
|
+
canCreate: () => canCreate.value,
|
|
683
|
+
linkedKeys: [CMS_ENTRY_TAB_ROUTE_KEY, "duplicate", ...CMS_ENTRY_FOCUS_KEYS]
|
|
684
|
+
});
|
|
685
|
+
const { openRecord } = entryRecord;
|
|
686
|
+
const drawerOpen = computed({
|
|
687
|
+
get: () => !isContent && !!entryRecord.param.value,
|
|
688
|
+
set: (value) => {
|
|
689
|
+
if (!value) void entryRecord.close();
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
const duplicateId = computed(
|
|
693
|
+
() => entryRecord.isNew.value ? routeHashValue(route.hash, "duplicate") ?? null : null
|
|
694
|
+
);
|
|
695
|
+
const drawerFocus = computed(
|
|
696
|
+
() => readSearchFocus(
|
|
697
|
+
Object.fromEntries(CMS_ENTRY_FOCUS_KEYS.map((key) => [key, routeHashValue(route.hash, key)]))
|
|
698
|
+
)
|
|
699
|
+
);
|
|
547
700
|
function onSelect(row) {
|
|
548
701
|
if (isContent) {
|
|
549
702
|
void navigateTo(`/cms/${name}/${row.id}`);
|
|
550
703
|
return;
|
|
551
704
|
}
|
|
552
|
-
|
|
553
|
-
drawerEntryId.value = String(row.id);
|
|
554
|
-
drawerOpen.value = true;
|
|
705
|
+
void openRecord(String(row.id));
|
|
555
706
|
}
|
|
556
707
|
function openCreate() {
|
|
557
708
|
if (isContent) {
|
|
558
709
|
void navigateTo(`/cms/${name}/new`);
|
|
559
710
|
return;
|
|
560
711
|
}
|
|
561
|
-
|
|
562
|
-
drawerEntryId.value = null;
|
|
563
|
-
drawerOpen.value = true;
|
|
712
|
+
void openRecord(CMS_NEW_RECORD);
|
|
564
713
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
714
|
+
const newCommand = computed(
|
|
715
|
+
() => isCollectionKind(config) && canCreate.value ? {
|
|
716
|
+
id: "new-entry",
|
|
717
|
+
label: "New entry",
|
|
718
|
+
icon: "plus",
|
|
719
|
+
keywords: [config.label],
|
|
720
|
+
onSelect: openCreate
|
|
721
|
+
} : null
|
|
722
|
+
);
|
|
723
|
+
usePageCommands(() => newCommand.value ? [newCommand.value] : []);
|
|
724
|
+
function onCreated(id) {
|
|
725
|
+
void refresh();
|
|
726
|
+
void entryRecord.replaceId(id);
|
|
569
727
|
}
|
|
570
|
-
|
|
728
|
+
function duplicateSelected() {
|
|
571
729
|
const id = selected.value.map(String)[0];
|
|
572
|
-
if (!id
|
|
730
|
+
if (!id) return;
|
|
573
731
|
if (isContent) {
|
|
574
732
|
void navigateTo({ path: `/cms/${name}/new`, query: { duplicate: id } });
|
|
575
733
|
return;
|
|
576
734
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
const entry = await cmsApi(`${endpoint}/${id}`);
|
|
580
|
-
selected.value = [];
|
|
581
|
-
openDuplicate(duplicateEntryValues(config, entry));
|
|
582
|
-
} catch (error2) {
|
|
583
|
-
toast.add({ title: "Could not load entry", description: errorMessage(error2), color: "error" });
|
|
584
|
-
} finally {
|
|
585
|
-
saving.value = false;
|
|
586
|
-
}
|
|
735
|
+
selected.value = [];
|
|
736
|
+
void openRecord(CMS_NEW_RECORD, { duplicate: id });
|
|
587
737
|
}
|
|
588
|
-
const importOpen =
|
|
738
|
+
const importOpen = useCmsRouteFlag("import");
|
|
589
739
|
const exporting = ref(false);
|
|
590
740
|
const csvAvailable = canTransferCsv(config);
|
|
591
741
|
async function exportEntries(format, narrow) {
|
|
@@ -606,7 +756,7 @@ async function exportEntries(format, narrow) {
|
|
|
606
756
|
link.click();
|
|
607
757
|
URL.revokeObjectURL(url);
|
|
608
758
|
} catch (error2) {
|
|
609
|
-
toast.
|
|
759
|
+
toast.error("Export failed", { description: errorMessage(error2) });
|
|
610
760
|
} finally {
|
|
611
761
|
exporting.value = false;
|
|
612
762
|
}
|
|
@@ -618,39 +768,58 @@ const transferItems = computed(() => {
|
|
|
618
768
|
onSelect: () => void exportEntries(format, narrow)
|
|
619
769
|
}));
|
|
620
770
|
return [
|
|
621
|
-
...narrowed.value ? exports(true) : [],
|
|
622
|
-
...
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
771
|
+
...can(`${name}.export`) ? [...narrowed.value ? exports(true) : [], ...exports(false)] : [],
|
|
772
|
+
...can(`${name}.import`) && canCreate.value ? [
|
|
773
|
+
{
|
|
774
|
+
label: "Import from file\u2026",
|
|
775
|
+
onSelect: () => {
|
|
776
|
+
importOpen.value = true;
|
|
777
|
+
}
|
|
627
778
|
}
|
|
628
|
-
|
|
779
|
+
] : []
|
|
629
780
|
];
|
|
630
781
|
});
|
|
631
782
|
async function onDrawerDeleted() {
|
|
783
|
+
const id = entryRecord.id.value;
|
|
784
|
+
if (id) selected.value = selected.value.filter((key) => String(key) !== id);
|
|
632
785
|
if (rows.value.length === 1 && page.value > 1) page.value -= 1;
|
|
633
786
|
else await refresh();
|
|
634
787
|
}
|
|
635
|
-
const
|
|
788
|
+
const confirmLinkedDelete = useCmsLinkedDelete();
|
|
636
789
|
const selected = ref([]);
|
|
637
|
-
watch(
|
|
638
|
-
const ids = new Set(current.map((row) => String(row.id)));
|
|
639
|
-
selected.value = selected.value.filter((id) => ids.has(String(id)));
|
|
640
|
-
});
|
|
641
|
-
function selectAll() {
|
|
642
|
-
selected.value = rows.value.map(rowKey);
|
|
643
|
-
}
|
|
644
|
-
function clearSelection() {
|
|
790
|
+
watch([searchTerm, filterQueryKey], () => {
|
|
645
791
|
selected.value = [];
|
|
792
|
+
});
|
|
793
|
+
async function allIds() {
|
|
794
|
+
const { ids } = await cmsApi(endpoint, {
|
|
795
|
+
query: {
|
|
796
|
+
ids: true,
|
|
797
|
+
...searchTerm.value ? { search: searchTerm.value } : {},
|
|
798
|
+
...sortQuery.value,
|
|
799
|
+
...filterQuery.value
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
return ids;
|
|
646
803
|
}
|
|
647
804
|
function entriesLabel(count) {
|
|
648
805
|
return `${count} ${count === 1 ? "entry" : "entries"}`;
|
|
649
806
|
}
|
|
807
|
+
function resultsLabel(count) {
|
|
808
|
+
return isPages ? `${count} ${count === 1 ? "page" : "pages"}` : entriesLabel(count);
|
|
809
|
+
}
|
|
810
|
+
const BULK_CONCURRENCY = 6;
|
|
811
|
+
async function settleInBatches(ids, action) {
|
|
812
|
+
const results = [];
|
|
813
|
+
for (let start = 0; start < ids.length; start += BULK_CONCURRENCY)
|
|
814
|
+
results.push(
|
|
815
|
+
...await Promise.allSettled(ids.slice(start, start + BULK_CONCURRENCY).map(action))
|
|
816
|
+
);
|
|
817
|
+
return results;
|
|
818
|
+
}
|
|
650
819
|
async function runBulk(ids, action) {
|
|
651
820
|
saving.value = true;
|
|
652
821
|
try {
|
|
653
|
-
const results = await
|
|
822
|
+
const results = await settleInBatches(ids, action);
|
|
654
823
|
const failed = results.flatMap(
|
|
655
824
|
(result, index) => result.status === "rejected" ? [{ id: ids[index], reason: result.reason }] : []
|
|
656
825
|
);
|
|
@@ -663,23 +832,30 @@ async function runBulk(ids, action) {
|
|
|
663
832
|
}
|
|
664
833
|
function reportBulk(done, total2, failed) {
|
|
665
834
|
const succeeded = total2 - failed.length;
|
|
666
|
-
if (succeeded) toast.
|
|
835
|
+
if (succeeded) toast.success(`${done}: ${entriesLabel(succeeded)}`);
|
|
667
836
|
if (failed.length) {
|
|
668
|
-
toast.
|
|
669
|
-
|
|
670
|
-
description: errorMessage(failed[0].reason),
|
|
671
|
-
color: "error"
|
|
837
|
+
toast.error(`${entriesLabel(failed.length)} not changed`, {
|
|
838
|
+
description: errorMessage(failed[0].reason)
|
|
672
839
|
});
|
|
673
840
|
}
|
|
674
841
|
}
|
|
675
842
|
async function bulkDelete() {
|
|
676
843
|
const ids = selected.value.map(String);
|
|
677
844
|
if (!ids.length || saving.value) return;
|
|
678
|
-
|
|
679
|
-
|
|
845
|
+
const titles = Object.fromEntries(
|
|
846
|
+
ids.map((id) => {
|
|
847
|
+
const row = rows.value.find((candidate) => String(candidate.id) === id);
|
|
848
|
+
const field = config?.titleField ? config.fields[config.titleField] : void 0;
|
|
849
|
+
const title = row && field && config?.titleField ? displayValue(field, config.titleField, row[config.titleField]) : "";
|
|
850
|
+
return [id, title || `#${id}`];
|
|
851
|
+
})
|
|
852
|
+
);
|
|
853
|
+
if (!await confirmLinkedDelete(name, ids, `Delete ${entriesLabel(ids.length)}?`, titles))
|
|
854
|
+
return;
|
|
680
855
|
const failed = await runBulk(ids, (id) => cmsApi(`${endpoint}/${id}`, { method: "DELETE" }));
|
|
681
856
|
reportBulk("Deleted", ids.length, failed);
|
|
682
|
-
if (
|
|
857
|
+
if (page.value > 1 && !rows.value.length)
|
|
858
|
+
page.value = Math.max(1, Math.ceil(total.value / limit.value));
|
|
683
859
|
}
|
|
684
860
|
async function bulkStatus(status2) {
|
|
685
861
|
const ids = selected.value.map(String);
|