@xiwebdesign/xicms-core 4.17.1
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/dist/XiCMSProvider.d.ts +58 -0
- package/dist/XiCMSProvider.js +165 -0
- package/dist/XiCMSProvider.js.map +1 -0
- package/dist/admin/AdminApp.d.ts +4 -0
- package/dist/admin/AdminApp.js +62 -0
- package/dist/admin/AdminApp.js.map +1 -0
- package/dist/admin/AdminHelp.d.ts +2 -0
- package/dist/admin/AdminHelp.js +65 -0
- package/dist/admin/AdminHelp.js.map +1 -0
- package/dist/admin/NewsEditor.d.ts +2 -0
- package/dist/admin/NewsEditor.js +171 -0
- package/dist/admin/NewsEditor.js.map +1 -0
- package/dist/admin/SuperadminPanel.d.ts +4 -0
- package/dist/admin/SuperadminPanel.js +101 -0
- package/dist/admin/SuperadminPanel.js.map +1 -0
- package/dist/admin/blockEditors/StyleEditor.d.ts +10 -0
- package/dist/admin/blockEditors/StyleEditor.js +78 -0
- package/dist/admin/blockEditors/StyleEditor.js.map +1 -0
- package/dist/admin/blockEditors/index.d.ts +16 -0
- package/dist/admin/blockEditors/index.js +1602 -0
- package/dist/admin/blockEditors/index.js.map +1 -0
- package/dist/admin/editors/RichTextEditor.d.ts +10 -0
- package/dist/admin/editors/RichTextEditor.js +86 -0
- package/dist/admin/editors/RichTextEditor.js.map +1 -0
- package/dist/admin/forms/FormsPanel.d.ts +3 -0
- package/dist/admin/forms/FormsPanel.js +203 -0
- package/dist/admin/forms/FormsPanel.js.map +1 -0
- package/dist/admin/index.d.ts +21 -0
- package/dist/admin/index.js +38 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/lib/compressImage.d.ts +12 -0
- package/dist/admin/lib/compressImage.js +53 -0
- package/dist/admin/lib/compressImage.js.map +1 -0
- package/dist/admin/lib/mediaUsage.d.ts +9 -0
- package/dist/admin/lib/mediaUsage.js +25 -0
- package/dist/admin/lib/mediaUsage.js.map +1 -0
- package/dist/admin/lib/pagesClient.d.ts +38 -0
- package/dist/admin/lib/pagesClient.js +143 -0
- package/dist/admin/lib/pagesClient.js.map +1 -0
- package/dist/admin/lib/slugify.d.ts +2 -0
- package/dist/admin/lib/slugify.js +7 -0
- package/dist/admin/lib/slugify.js.map +1 -0
- package/dist/admin/lib/upload.d.ts +13 -0
- package/dist/admin/lib/upload.js +40 -0
- package/dist/admin/lib/upload.js.map +1 -0
- package/dist/admin/lib/zodFields.d.ts +27 -0
- package/dist/admin/lib/zodFields.js +48 -0
- package/dist/admin/lib/zodFields.js.map +1 -0
- package/dist/admin/media/MediaManager.d.ts +3 -0
- package/dist/admin/media/MediaManager.js +132 -0
- package/dist/admin/media/MediaManager.js.map +1 -0
- package/dist/admin/media/MediaPicker.d.ts +24 -0
- package/dist/admin/media/MediaPicker.js +93 -0
- package/dist/admin/media/MediaPicker.js.map +1 -0
- package/dist/admin/pages/PageEditor.d.ts +5 -0
- package/dist/admin/pages/PageEditor.js +239 -0
- package/dist/admin/pages/PageEditor.js.map +1 -0
- package/dist/admin/pages/PagesPanel.d.ts +3 -0
- package/dist/admin/pages/PagesPanel.js +92 -0
- package/dist/admin/pages/PagesPanel.js.map +1 -0
- package/dist/admin/panelRegistry.d.ts +20 -0
- package/dist/admin/panelRegistry.js +23 -0
- package/dist/admin/panelRegistry.js.map +1 -0
- package/dist/admin/patterns/PatternInserter.d.ts +10 -0
- package/dist/admin/patterns/PatternInserter.js +58 -0
- package/dist/admin/patterns/PatternInserter.js.map +1 -0
- package/dist/admin/settings/SettingsPanel.d.ts +3 -0
- package/dist/admin/settings/SettingsPanel.js +304 -0
- package/dist/admin/settings/SettingsPanel.js.map +1 -0
- package/dist/admin/settings/moduleSettingsForm.d.ts +12 -0
- package/dist/admin/settings/moduleSettingsForm.js +45 -0
- package/dist/admin/settings/moduleSettingsForm.js.map +1 -0
- package/dist/admin/stats/StatsPanel.d.ts +4 -0
- package/dist/admin/stats/StatsPanel.js +80 -0
- package/dist/admin/stats/StatsPanel.js.map +1 -0
- package/dist/admin/ui/ArrayEditor.d.ts +16 -0
- package/dist/admin/ui/ArrayEditor.js +38 -0
- package/dist/admin/ui/ArrayEditor.js.map +1 -0
- package/dist/admin/ui/ConfirmDialog.d.ts +22 -0
- package/dist/admin/ui/ConfirmDialog.js +47 -0
- package/dist/admin/ui/ConfirmDialog.js.map +1 -0
- package/dist/admin/ui/cn.d.ts +3 -0
- package/dist/admin/ui/cn.js +9 -0
- package/dist/admin/ui/cn.js.map +1 -0
- package/dist/admin/ui/dialog.d.ts +9 -0
- package/dist/admin/ui/dialog.js +47 -0
- package/dist/admin/ui/dialog.js.map +1 -0
- package/dist/admin/ui/index.d.ts +7 -0
- package/dist/admin/ui/index.js +29 -0
- package/dist/admin/ui/index.js.map +1 -0
- package/dist/admin/ui/primitives.d.ts +37 -0
- package/dist/admin/ui/primitives.js +137 -0
- package/dist/admin/ui/primitives.js.map +1 -0
- package/dist/admin/ui/select.d.ts +11 -0
- package/dist/admin/ui/select.js +27 -0
- package/dist/admin/ui/select.js.map +1 -0
- package/dist/admin/ui/switch.d.ts +6 -0
- package/dist/admin/ui/switch.js +22 -0
- package/dist/admin/ui/switch.js.map +1 -0
- package/dist/admin/ui/tabs.d.ts +5 -0
- package/dist/admin/ui/tabs.js +36 -0
- package/dist/admin/ui/tabs.js.map +1 -0
- package/dist/admin/ui/toast.d.ts +19 -0
- package/dist/admin/ui/toast.js +24 -0
- package/dist/admin/ui/toast.js.map +1 -0
- package/dist/admin/visual/VisualEditor.d.ts +10 -0
- package/dist/admin/visual/VisualEditor.js +597 -0
- package/dist/admin/visual/VisualEditor.js.map +1 -0
- package/dist/admin/visual/reorder.d.ts +6 -0
- package/dist/admin/visual/reorder.js +12 -0
- package/dist/admin/visual/reorder.js.map +1 -0
- package/dist/admin.css +1 -0
- package/dist/auth/AuthProvider.d.ts +30 -0
- package/dist/auth/AuthProvider.js +110 -0
- package/dist/auth/AuthProvider.js.map +1 -0
- package/dist/auth/LoginForm.d.ts +4 -0
- package/dist/auth/LoginForm.js +122 -0
- package/dist/auth/LoginForm.js.map +1 -0
- package/dist/auth/roles.d.ts +15 -0
- package/dist/auth/roles.js +21 -0
- package/dist/auth/roles.js.map +1 -0
- package/dist/blocks/BlockErrorBoundary.d.ts +32 -0
- package/dist/blocks/BlockErrorBoundary.js +39 -0
- package/dist/blocks/BlockErrorBoundary.js.map +1 -0
- package/dist/blocks/BlockRenderer.d.ts +15 -0
- package/dist/blocks/BlockRenderer.js +24 -0
- package/dist/blocks/BlockRenderer.js.map +1 -0
- package/dist/blocks/blockStyle.d.ts +14 -0
- package/dist/blocks/blockStyle.js +14 -0
- package/dist/blocks/blockStyle.js.map +1 -0
- package/dist/blocks/builtins/Accordion.d.ts +2 -0
- package/dist/blocks/builtins/Accordion.js +45 -0
- package/dist/blocks/builtins/Accordion.js.map +1 -0
- package/dist/blocks/builtins/Agenda.d.ts +23 -0
- package/dist/blocks/builtins/Agenda.js +102 -0
- package/dist/blocks/builtins/Agenda.js.map +1 -0
- package/dist/blocks/builtins/Banner.d.ts +2 -0
- package/dist/blocks/builtins/Banner.js +45 -0
- package/dist/blocks/builtins/Banner.js.map +1 -0
- package/dist/blocks/builtins/BeforeAfter.d.ts +2 -0
- package/dist/blocks/builtins/BeforeAfter.js +44 -0
- package/dist/blocks/builtins/BeforeAfter.js.map +1 -0
- package/dist/blocks/builtins/Booking.d.ts +2 -0
- package/dist/blocks/builtins/Booking.js +27 -0
- package/dist/blocks/builtins/Booking.js.map +1 -0
- package/dist/blocks/builtins/Breadcrumbs.d.ts +10 -0
- package/dist/blocks/builtins/Breadcrumbs.js +73 -0
- package/dist/blocks/builtins/Breadcrumbs.js.map +1 -0
- package/dist/blocks/builtins/Cards.d.ts +2 -0
- package/dist/blocks/builtins/Cards.js +94 -0
- package/dist/blocks/builtins/Cards.js.map +1 -0
- package/dist/blocks/builtins/Columns.d.ts +2 -0
- package/dist/blocks/builtins/Columns.js +22 -0
- package/dist/blocks/builtins/Columns.js.map +1 -0
- package/dist/blocks/builtins/Contact.d.ts +7 -0
- package/dist/blocks/builtins/Contact.js +121 -0
- package/dist/blocks/builtins/Contact.js.map +1 -0
- package/dist/blocks/builtins/Countdown.d.ts +18 -0
- package/dist/blocks/builtins/Countdown.js +65 -0
- package/dist/blocks/builtins/Countdown.js.map +1 -0
- package/dist/blocks/builtins/Cta.d.ts +2 -0
- package/dist/blocks/builtins/Cta.js +52 -0
- package/dist/blocks/builtins/Cta.js.map +1 -0
- package/dist/blocks/builtins/Downloads.d.ts +2 -0
- package/dist/blocks/builtins/Downloads.js +32 -0
- package/dist/blocks/builtins/Downloads.js.map +1 -0
- package/dist/blocks/builtins/Elements.d.ts +4 -0
- package/dist/blocks/builtins/Elements.js +64 -0
- package/dist/blocks/builtins/Elements.js.map +1 -0
- package/dist/blocks/builtins/Embed.d.ts +2 -0
- package/dist/blocks/builtins/Embed.js +30 -0
- package/dist/blocks/builtins/Embed.js.map +1 -0
- package/dist/blocks/builtins/Faq.d.ts +6 -0
- package/dist/blocks/builtins/Faq.js +110 -0
- package/dist/blocks/builtins/Faq.js.map +1 -0
- package/dist/blocks/builtins/FormBlock.d.ts +2 -0
- package/dist/blocks/builtins/FormBlock.js +221 -0
- package/dist/blocks/builtins/FormBlock.js.map +1 -0
- package/dist/blocks/builtins/Gallery.d.ts +23 -0
- package/dist/blocks/builtins/Gallery.js +177 -0
- package/dist/blocks/builtins/Gallery.js.map +1 -0
- package/dist/blocks/builtins/Hero.d.ts +2 -0
- package/dist/blocks/builtins/Hero.js +47 -0
- package/dist/blocks/builtins/Hero.js.map +1 -0
- package/dist/blocks/builtins/ImageBlock.d.ts +2 -0
- package/dist/blocks/builtins/ImageBlock.js +24 -0
- package/dist/blocks/builtins/ImageBlock.js.map +1 -0
- package/dist/blocks/builtins/Jobs.d.ts +8 -0
- package/dist/blocks/builtins/Jobs.js +65 -0
- package/dist/blocks/builtins/Jobs.js.map +1 -0
- package/dist/blocks/builtins/Layout.d.ts +3 -0
- package/dist/blocks/builtins/Layout.js +67 -0
- package/dist/blocks/builtins/Layout.js.map +1 -0
- package/dist/blocks/builtins/MapBlock.d.ts +2 -0
- package/dist/blocks/builtins/MapBlock.js +31 -0
- package/dist/blocks/builtins/MapBlock.js.map +1 -0
- package/dist/blocks/builtins/NewsListBlock.d.ts +2 -0
- package/dist/blocks/builtins/NewsListBlock.js +24 -0
- package/dist/blocks/builtins/NewsListBlock.js.map +1 -0
- package/dist/blocks/builtins/Newsletter.d.ts +2 -0
- package/dist/blocks/builtins/Newsletter.js +84 -0
- package/dist/blocks/builtins/Newsletter.js.map +1 -0
- package/dist/blocks/builtins/OpenNowBadge.d.ts +9 -0
- package/dist/blocks/builtins/OpenNowBadge.js +20 -0
- package/dist/blocks/builtins/OpenNowBadge.js.map +1 -0
- package/dist/blocks/builtins/OpeningHours.d.ts +2 -0
- package/dist/blocks/builtins/OpeningHours.js +47 -0
- package/dist/blocks/builtins/OpeningHours.js.map +1 -0
- package/dist/blocks/builtins/PdfBlock.d.ts +2 -0
- package/dist/blocks/builtins/PdfBlock.js +97 -0
- package/dist/blocks/builtins/PdfBlock.js.map +1 -0
- package/dist/blocks/builtins/Portfolio.d.ts +2 -0
- package/dist/blocks/builtins/Portfolio.js +94 -0
- package/dist/blocks/builtins/Portfolio.js.map +1 -0
- package/dist/blocks/builtins/Pricing.d.ts +6 -0
- package/dist/blocks/builtins/Pricing.js +77 -0
- package/dist/blocks/builtins/Pricing.js.map +1 -0
- package/dist/blocks/builtins/RichTextBlock.d.ts +2 -0
- package/dist/blocks/builtins/RichTextBlock.js +18 -0
- package/dist/blocks/builtins/RichTextBlock.js.map +1 -0
- package/dist/blocks/builtins/Search.d.ts +2 -0
- package/dist/blocks/builtins/Search.js +99 -0
- package/dist/blocks/builtins/Search.js.map +1 -0
- package/dist/blocks/builtins/Sections.d.ts +5 -0
- package/dist/blocks/builtins/Sections.js +183 -0
- package/dist/blocks/builtins/Sections.js.map +1 -0
- package/dist/blocks/builtins/Slider.d.ts +2 -0
- package/dist/blocks/builtins/Slider.js +131 -0
- package/dist/blocks/builtins/Slider.js.map +1 -0
- package/dist/blocks/builtins/Spacer.d.ts +2 -0
- package/dist/blocks/builtins/Spacer.js +19 -0
- package/dist/blocks/builtins/Spacer.js.map +1 -0
- package/dist/blocks/builtins/Team.d.ts +2 -0
- package/dist/blocks/builtins/Team.js +70 -0
- package/dist/blocks/builtins/Team.js.map +1 -0
- package/dist/blocks/builtins/Testimonials.d.ts +8 -0
- package/dist/blocks/builtins/Testimonials.js +82 -0
- package/dist/blocks/builtins/Testimonials.js.map +1 -0
- package/dist/blocks/builtins/Video.d.ts +9 -0
- package/dist/blocks/builtins/Video.js +77 -0
- package/dist/blocks/builtins/Video.js.map +1 -0
- package/dist/blocks/builtins/WhatsappChat.d.ts +8 -0
- package/dist/blocks/builtins/WhatsappChat.js +51 -0
- package/dist/blocks/builtins/WhatsappChat.js.map +1 -0
- package/dist/blocks/builtins/Widgets.d.ts +13 -0
- package/dist/blocks/builtins/Widgets.js +308 -0
- package/dist/blocks/builtins/Widgets.js.map +1 -0
- package/dist/blocks/builtins/consentEmbed.d.ts +17 -0
- package/dist/blocks/builtins/consentEmbed.js +31 -0
- package/dist/blocks/builtins/consentEmbed.js.map +1 -0
- package/dist/blocks/builtins/index.d.ts +46 -0
- package/dist/blocks/builtins/index.js +121 -0
- package/dist/blocks/builtins/index.js.map +1 -0
- package/dist/blocks/builtins/lib/bookingUrl.d.ts +8 -0
- package/dist/blocks/builtins/lib/bookingUrl.js +50 -0
- package/dist/blocks/builtins/lib/bookingUrl.js.map +1 -0
- package/dist/blocks/builtins/lib/fileIcon.d.ts +2 -0
- package/dist/blocks/builtins/lib/fileIcon.js +45 -0
- package/dist/blocks/builtins/lib/fileIcon.js.map +1 -0
- package/dist/blocks/builtins/lib/ics.d.ts +17 -0
- package/dist/blocks/builtins/lib/ics.js +50 -0
- package/dist/blocks/builtins/lib/ics.js.map +1 -0
- package/dist/blocks/builtins/lib/openingHours.d.ts +20 -0
- package/dist/blocks/builtins/lib/openingHours.js +79 -0
- package/dist/blocks/builtins/lib/openingHours.js.map +1 -0
- package/dist/blocks/builtins/lib/searchRank.d.ts +22 -0
- package/dist/blocks/builtins/lib/searchRank.js +45 -0
- package/dist/blocks/builtins/lib/searchRank.js.map +1 -0
- package/dist/blocks/builtins/lib/submitForm.d.ts +6 -0
- package/dist/blocks/builtins/lib/submitForm.js +14 -0
- package/dist/blocks/builtins/lib/submitForm.js.map +1 -0
- package/dist/blocks/context.d.ts +8 -0
- package/dist/blocks/context.js +20 -0
- package/dist/blocks/context.js.map +1 -0
- package/dist/blocks/index.d.ts +9 -0
- package/dist/blocks/index.js +21 -0
- package/dist/blocks/index.js.map +1 -0
- package/dist/blocks/registry.d.ts +75 -0
- package/dist/blocks/registry.js +71 -0
- package/dist/blocks/registry.js.map +1 -0
- package/dist/blocks/ui/index.d.ts +35 -0
- package/dist/blocks/ui/index.js +48 -0
- package/dist/blocks/ui/index.js.map +1 -0
- package/dist/components/EditBar.d.ts +2 -0
- package/dist/components/EditBar.js +20 -0
- package/dist/components/EditBar.js.map +1 -0
- package/dist/components/EditableImage.d.ts +18 -0
- package/dist/components/EditableImage.js +62 -0
- package/dist/components/EditableImage.js.map +1 -0
- package/dist/components/EditableRichText.d.ts +14 -0
- package/dist/components/EditableRichText.js +26 -0
- package/dist/components/EditableRichText.js.map +1 -0
- package/dist/components/EditableText.d.ts +21 -0
- package/dist/components/EditableText.js +25 -0
- package/dist/components/EditableText.js.map +1 -0
- package/dist/components/News.d.ts +38 -0
- package/dist/components/News.js +128 -0
- package/dist/components/News.js.map +1 -0
- package/dist/components/PageView.d.ts +8 -0
- package/dist/components/PageView.js +28 -0
- package/dist/components/PageView.js.map +1 -0
- package/dist/components/ResponsiveImage.d.ts +16 -0
- package/dist/components/ResponsiveImage.js +39 -0
- package/dist/components/ResponsiveImage.js.map +1 -0
- package/dist/components/site/SiteFooter.d.ts +12 -0
- package/dist/components/site/SiteFooter.js +34 -0
- package/dist/components/site/SiteFooter.js.map +1 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +17 -0
- package/dist/config.js.map +1 -0
- package/dist/content/ContentProvider.d.ts +23 -0
- package/dist/content/ContentProvider.js +86 -0
- package/dist/content/ContentProvider.js.map +1 -0
- package/dist/content/types.d.ts +41 -0
- package/dist/content/types.js +1 -0
- package/dist/content/types.js.map +1 -0
- package/dist/demo/DemoStudio.d.ts +7 -0
- package/dist/demo/DemoStudio.js +54 -0
- package/dist/demo/DemoStudio.js.map +1 -0
- package/dist/demo/demoStore.d.ts +26 -0
- package/dist/demo/demoStore.js +115 -0
- package/dist/demo/demoStore.js.map +1 -0
- package/dist/editor/EditorProvider.d.ts +12 -0
- package/dist/editor/EditorProvider.js +22 -0
- package/dist/editor/EditorProvider.js.map +1 -0
- package/dist/firebase/admin.d.ts +7 -0
- package/dist/firebase/admin.js +31 -0
- package/dist/firebase/admin.js.map +1 -0
- package/dist/firebase/appcheck.d.ts +6 -0
- package/dist/firebase/appcheck.js +23 -0
- package/dist/firebase/appcheck.js.map +1 -0
- package/dist/firebase/client.d.ts +9 -0
- package/dist/firebase/client.js +44 -0
- package/dist/firebase/client.js.map +1 -0
- package/dist/firebase/region.d.ts +5 -0
- package/dist/firebase/region.js +7 -0
- package/dist/firebase/region.js.map +1 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.js +172 -0
- package/dist/index.js.map +1 -0
- package/dist/layers.css +13 -0
- package/dist/lock/DemoLock.d.ts +5 -0
- package/dist/lock/DemoLock.js +35 -0
- package/dist/lock/DemoLock.js.map +1 -0
- package/dist/media/ImageContext.d.ts +41 -0
- package/dist/media/ImageContext.js +74 -0
- package/dist/media/ImageContext.js.map +1 -0
- package/dist/modules/builtins.d.ts +46 -0
- package/dist/modules/builtins.js +462 -0
- package/dist/modules/builtins.js.map +1 -0
- package/dist/modules/consent/ConsentBanner.d.ts +42 -0
- package/dist/modules/consent/ConsentBanner.js +119 -0
- package/dist/modules/consent/ConsentBanner.js.map +1 -0
- package/dist/modules/consent/useConsent.d.ts +28 -0
- package/dist/modules/consent/useConsent.js +52 -0
- package/dist/modules/consent/useConsent.js.map +1 -0
- package/dist/modules/context.d.ts +20 -0
- package/dist/modules/context.js +31 -0
- package/dist/modules/context.js.map +1 -0
- package/dist/modules/contract.d.ts +9 -0
- package/dist/modules/contract.js +38 -0
- package/dist/modules/contract.js.map +1 -0
- package/dist/modules/defineModule.d.ts +11 -0
- package/dist/modules/defineModule.js +7 -0
- package/dist/modules/defineModule.js.map +1 -0
- package/dist/modules/index.d.ts +13 -0
- package/dist/modules/index.js +44 -0
- package/dist/modules/index.js.map +1 -0
- package/dist/modules/overrides.d.ts +17 -0
- package/dist/modules/overrides.js +25 -0
- package/dist/modules/overrides.js.map +1 -0
- package/dist/modules/registry.d.ts +40 -0
- package/dist/modules/registry.js +79 -0
- package/dist/modules/registry.js.map +1 -0
- package/dist/modules/types.d.ts +66 -0
- package/dist/modules/types.js +1 -0
- package/dist/modules/types.js.map +1 -0
- package/dist/patterns/builtins.d.ts +31 -0
- package/dist/patterns/builtins.js +415 -0
- package/dist/patterns/builtins.js.map +1 -0
- package/dist/patterns/index.d.ts +6 -0
- package/dist/patterns/index.js +9 -0
- package/dist/patterns/index.js.map +1 -0
- package/dist/patterns/patterns.d.ts +28 -0
- package/dist/patterns/patterns.js +53 -0
- package/dist/patterns/patterns.js.map +1 -0
- package/dist/render/PageRenderer.d.ts +13 -0
- package/dist/render/PageRenderer.js +14 -0
- package/dist/render/PageRenderer.js.map +1 -0
- package/dist/render/PublishedPage.d.ts +13 -0
- package/dist/render/PublishedPage.js +16 -0
- package/dist/render/PublishedPage.js.map +1 -0
- package/dist/schema/blocks.d.ts +15052 -0
- package/dist/schema/blocks.js +794 -0
- package/dist/schema/blocks.js.map +1 -0
- package/dist/schema/forms.d.ts +111 -0
- package/dist/schema/forms.js +47 -0
- package/dist/schema/forms.js.map +1 -0
- package/dist/schema/index.d.ts +11 -0
- package/dist/schema/index.js +16 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/pages.d.ts +674 -0
- package/dist/schema/pages.js +37 -0
- package/dist/schema/pages.js.map +1 -0
- package/dist/schema/seo.d.ts +29 -0
- package/dist/schema/seo.js +17 -0
- package/dist/schema/seo.js.map +1 -0
- package/dist/schema/settings.d.ts +1126 -0
- package/dist/schema/settings.js +128 -0
- package/dist/schema/settings.js.map +1 -0
- package/dist/schema/siteConfig.d.ts +722 -0
- package/dist/schema/siteConfig.js +39 -0
- package/dist/schema/siteConfig.js.map +1 -0
- package/dist/server/env.d.ts +10 -0
- package/dist/server/env.js +36 -0
- package/dist/server/env.js.map +1 -0
- package/dist/server/index.d.ts +10 -0
- package/dist/server/index.js +51 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/pages.d.ts +15 -0
- package/dist/server/pages.js +99 -0
- package/dist/server/pages.js.map +1 -0
- package/dist/server/sanitize.d.ts +2 -0
- package/dist/server/sanitize.js +55 -0
- package/dist/server/sanitize.js.map +1 -0
- package/dist/server/seo.d.ts +116 -0
- package/dist/server/seo.js +179 -0
- package/dist/server/seo.js.map +1 -0
- package/dist/settings/useSiteSettings.d.ts +25 -0
- package/dist/settings/useSiteSettings.js +68 -0
- package/dist/settings/useSiteSettings.js.map +1 -0
- package/dist/styles.css +2495 -0
- package/dist/theme/presets.d.ts +12 -0
- package/dist/theme/presets.js +147 -0
- package/dist/theme/presets.js.map +1 -0
- package/package.json +112 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/visual/VisualEditor.tsx"],"sourcesContent":["\"use client\";\n\n// De admin-stijlen (Tailwind, scoped onder .xicms-admin) horen bij dit lazy-geladen chunk, zodat\n// publieke bezoekers ze nooit downloaden — alleen wanneer een editor de pagina-editor opent.\nimport \"@xiwebdesign/xicms-core/admin.css\";\nimport { useCallback, useEffect, useMemo, useRef, useState, type ComponentType } from \"react\";\nimport { useSiteConfig } from \"../../config\";\nimport { useRegistry } from \"../../blocks/context\";\nimport { BlockErrorBoundary } from \"../../blocks/BlockErrorBoundary\";\nimport { InlineImageEditProvider } from \"../../media/ImageContext\";\nimport { MediaPickerDialog } from \"../media/MediaPicker\";\nimport { EmptyState } from \"@astryxdesign/core/EmptyState\";\nimport { buildBlockAddOptions } from \"../../blocks/registry\";\nimport { getDraftByPath, saveDraft, publishPage, subscribeDraft, subscribeVersions, restoreVersion, type DraftPage } from \"../lib/pagesClient\";\nimport { isDemoActive } from \"../../demo/demoStore\";\nimport { builtinBlockEditors, GenericEditor, type BlockEditorProps } from \"../blockEditors\";\nimport { StyleEditor } from \"../blockEditors/StyleEditor\";\nimport { reorderBlocks } from \"./reorder\";\nimport { PatternInserter } from \"../patterns/PatternInserter\";\nimport type { BlockStyle } from \"../../schema/blocks\";\nimport { Button, Field, Input, Textarea } from \"../ui/primitives\";\nimport { Select } from \"../ui/select\";\nimport { ToastProvider, useToast } from \"../ui/toast\";\nimport { ConfirmHost, useConfirm } from \"../ui/ConfirmDialog\";\n\ntype Block = Record<string, unknown> & { _key: string; _type: string };\ntype Status = \"loading\" | \"none\" | \"idle\" | \"saving\" | \"saved\" | \"error\";\n\n/**\n * Welke tekstvelden per bloktype **inline** (direct in het canvas) bewerkbaar zijn, gemapt op hun\n * stabiele DOM-klasse — zo hoeft de blok-Render niet aangepast te worden. `html: true` synct innerHTML\n * (rich text), anders platte tekst. Elk blok kan ook zelf velden markeren met `data-xicms-edit=\"veld\"`.\n */\nconst INLINE_FIELDS: Record<string, { selector: string; field: string; html?: boolean }[]> = {\n hero: [\n { selector: \".xicms-hero__title\", field: \"title\" },\n { selector: \".xicms-hero__sub\", field: \"subtitle\" },\n ],\n};\n\n/** Selectors die in ELK blok inline bewerkbaar zijn: rich-text-inhoud (`.xicms-richtext`, gebruikt door\n * richText én feature) en velden die een blok zelf markeert met `data-xicms-edit=\"veld\"`. */\nconst GLOBAL_INLINE = [\n { selector: \".xicms-richtext\", field: \"html\", html: true as const },\n { selector: \"[data-xicms-edit]\", field: \"\", html: false as const, fromData: true as const },\n];\n\n/* Lees/schrijf een waarde op een gepunt pad (bv. \"items.0.title\") — zodat ook lijst-items inline\n bewerkt kunnen worden. setPath kloont onderweg (immutable) zodat React de wijziging detecteert. */\nfunction getPath(obj: unknown, path: string): unknown {\n return path.split(\".\").reduce<unknown>((o, k) => (o == null ? undefined : (o as Record<string, unknown>)[k]), obj);\n}\nfunction setPath<T>(obj: T, path: string, value: unknown): T {\n const keys = path.split(\".\");\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const clone = (v: unknown): any => (Array.isArray(v) ? [...v] : v && typeof v === \"object\" ? { ...(v as object) } : v);\n const root = clone(obj);\n let cur = root;\n for (let i = 0; i < keys.length - 1; i++) {\n cur[keys[i]] = clone(cur[keys[i]] ?? {});\n cur = cur[keys[i]];\n }\n cur[keys[keys.length - 1]] = value;\n return root as T;\n}\n\nfunction newKey() {\n return (globalThis.crypto?.randomUUID?.() ?? `b${Date.now()}${Math.random()}`).slice(0, 12);\n}\n\n/** JSON.stringify met gesorteerde sleutels — voor betrouwbare inhoudsvergelijking (conflictdetectie):\n * Firestore kan velden in een andere volgorde teruggeven dan wij ze lokaal opbouwden. */\nfunction stableStringify(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n if (value && typeof value === \"object\") {\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : 1));\n return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(\",\")}}`;\n }\n return JSON.stringify(value) ?? \"null\";\n}\n\n/**\n * Visuele/inline pagina-editor: opent als overlay over de live pagina, laadt de werkversie van de\n * huidige route, en laat de editor blokken op een live canvas selecteren, herordenen, dupliceren,\n * verwijderen en toevoegen — met autosave en publiceren. De zware code laadt pas bij openen\n * (lazy, via EditorProvider), dus bezoekers downloaden hem nooit.\n */\nexport default function VisualEditor({ onClose }: { onClose: () => void }) {\n return (\n <div className=\"xicms-admin xicms-ve\">\n <ToastProvider>\n <ConfirmHost>\n <VisualEditorInner onClose={onClose} />\n </ConfirmHost>\n </ToastProvider>\n </div>\n );\n}\n\nfunction VisualEditorInner({ onClose }: { onClose: () => void }) {\n const { siteId, name } = useSiteConfig();\n const registry = useRegistry();\n const { toast } = useToast();\n const confirm = useConfirm();\n\n const path = useMemo(() => (typeof window !== \"undefined\" ? window.location.pathname || \"/\" : \"/\"), []);\n const [draft, setDraft] = useState<DraftPage | null>(null);\n const [status, setStatus] = useState<Status>(\"loading\");\n const [selectedKey, setSelectedKey] = useState<string | null>(null);\n const [publishing, setPublishing] = useState(false);\n const [imgPicker, setImgPicker] = useState<{ key: string; field: string } | null>(null);\n const firstSave = useRef(true);\n const canvasRef = useRef<HTMLDivElement>(null);\n const [conflict, setConflict] = useState(false);\n\n // Drag-and-drop herordenen: dragIndex = het gesleepte blok, dropIndex = invoegpunt (0..length).\n // draggingRef pauzeert de inline-bind MutationObserver tijdens het slepen (re-bind-ruis).\n const [dragIndex, setDragIndex] = useState<number | null>(null);\n const [dropIndex, setDropIndex] = useState<number | null>(null);\n const draggingRef = useRef(false);\n const endDrag = useCallback(() => {\n draggingRef.current = false;\n setDragIndex(null);\n setDropIndex(null);\n }, []);\n\n // Altijd de laatste draft + \"zijn er niet-opgeslagen wijzigingen\" bij de hand houden, zodat we\n // bij sluiten/wegnavigeren nog kunnen flushen (de debounce zou de laatste 700ms anders weggooien).\n const draftRef = useRef<DraftPage | null>(null);\n const dirtyRef = useRef(false);\n // Inhoud (stable-stringified) van de laatst succesvol opgeslagen payload — voor conflictdetectie.\n const lastSavedRef = useRef<string | null>(null);\n useEffect(() => {\n draftRef.current = draft;\n }, [draft]);\n\n // ── Undo/redo ──────────────────────────────────────────────────────────────\n // History over draft-snapshots. Tekstedits (per toetsaanslag) worden ge-coalesced: binnen één\n // typ-burst (<800ms) staat het pre-burst-snapshot al op de stack, dus niet opnieuw pushen —\n // Ctrl+Z springt dan per burst terug i.p.v. per letter. `histVersion` forceert re-render van\n // de knop-states (de stacks leven in een ref).\n const historyRef = useRef<{ past: DraftPage[]; future: DraftPage[]; lastPush: number }>({ past: [], future: [], lastPush: 0 });\n const [, setHistVersion] = useState(0);\n\n const pushHistory = useCallback((prev: DraftPage, coalesce: boolean) => {\n const h = historyRef.current;\n const now = Date.now();\n if (!(coalesce && now - h.lastPush < 800 && h.past.length > 0)) {\n h.past.push(prev);\n if (h.past.length > 100) h.past.shift();\n }\n h.lastPush = now;\n h.future = [];\n setHistVersion((v) => v + 1);\n }, []);\n\n /** Enige route waarlangs de draft muteert: bewaart eerst een undo-snapshot. */\n const mutateDraft = useCallback(\n (updater: (d: DraftPage) => DraftPage, opts?: { coalesce?: boolean }) => {\n const d = draftRef.current;\n if (!d) return;\n const next = updater(d);\n if (next === d) return;\n pushHistory(d, opts?.coalesce ?? false);\n draftRef.current = next;\n setDraft(next);\n },\n [pushHistory],\n );\n\n const undo = useCallback(() => {\n const h = historyRef.current;\n const d = draftRef.current;\n const prev = h.past.pop();\n if (!prev || !d) return;\n h.future.push(d);\n h.lastPush = 0;\n draftRef.current = prev;\n setDraft(prev);\n setHistVersion((v) => v + 1);\n }, []);\n\n const redo = useCallback(() => {\n const h = historyRef.current;\n const d = draftRef.current;\n const next = h.future.pop();\n if (!next || !d) return;\n h.past.push(d);\n h.lastPush = 0;\n draftRef.current = next;\n setDraft(next);\n setHistVersion((v) => v + 1);\n }, []);\n\n const draftPayload = (d: DraftPage) => ({\n blocks: d.blocks,\n title: d.title,\n slug: d.slug,\n path: d.path,\n seo: d.seo,\n });\n\n // Schrijf eventuele pending wijziging direct weg (gebruikt bij Sluiten, tab-wissel en retry).\n const flushSave = useCallback(async () => {\n const d = draftRef.current;\n if (!d || !dirtyRef.current) return;\n const payload = draftPayload(d);\n await saveDraft(siteId, d.id, payload);\n lastSavedRef.current = stableStringify(payload);\n // Alleen \"schoon\" markeren als er intussen niets nieuws is getypt (anders zou een trage\n // write nieuwere wijzigingen als opgeslagen bestempelen).\n if (draftRef.current === d) {\n dirtyRef.current = false;\n setStatus(\"saved\");\n }\n }, [siteId]);\n\n const handleClose = useCallback(() => {\n void flushSave().finally(() => onClose());\n }, [flushSave, onClose]);\n\n useEffect(() => {\n let active = true;\n getDraftByPath(siteId, path)\n .then((d) => {\n if (!active) return;\n if (d) {\n setDraft(d);\n lastSavedRef.current = stableStringify({ blocks: d.blocks, title: d.title, slug: d.slug, path: d.path, seo: d.seo });\n setStatus(\"idle\");\n } else {\n setStatus(\"none\");\n }\n })\n .catch(() => active && setStatus(\"none\"));\n return () => {\n active = false;\n };\n }, [siteId, path]);\n\n // ── Conflictdetectie ───────────────────────────────────────────────────────\n // Luister naar de draft in Firestore. Een server-snapshot die afwijkt van zowel onze laatst\n // opgeslagen payload als onze huidige lokale staat, betekent: iemand anders (tab/collega)\n // heeft deze pagina gewijzigd. Waarschuw i.p.v. elkaars werk stil te overschrijven.\n const draftId = draft?.id;\n useEffect(() => {\n if (!draftId) return;\n return subscribeDraft(siteId, draftId, (remote, meta) => {\n if (!meta.fromServer || !remote) return;\n const remotePayload = stableStringify({ blocks: remote.blocks, title: remote.title, slug: remote.slug, path: remote.path, seo: remote.seo });\n const local = draftRef.current;\n const localPayload = local ? stableStringify(draftPayload(local)) : null;\n if (remotePayload !== lastSavedRef.current && remotePayload !== localPayload) setConflict(true);\n });\n }, [siteId, draftId]);\n\n const blocks = (draft?.blocks ?? []) as Block[];\n\n // Schrijf een inline (in-canvas) tekstwijziging terug naar het juiste blokveld.\n const commitInline = useCallback(\n (key: string, field: string, value: string) => {\n mutateDraft(\n (d) => {\n const bl = d.blocks as Block[];\n const idx = bl.findIndex((x) => x._key === key);\n if (idx < 0 || getPath(bl[idx], field) === value) return d;\n const next = [...bl];\n next[idx] = setPath(next[idx], field, value);\n return { ...d, blocks: next };\n },\n { coalesce: true },\n );\n },\n [mutateDraft],\n );\n\n // Maak de tekstvelden van het GESELECTEERDE blok ter plekke bewerkbaar (contentEditable) en synct\n // op blur terug — zo bewerk je tekst inline op het canvas i.p.v. via de zijbalk. We binden via een\n // MutationObserver én her-binden zodra React de DOM-node vervangt (re-render), met een dataset-flag\n // zodat we nooit dubbele listeners zetten.\n useEffect(() => {\n if (!selectedKey) return;\n const host = canvasRef.current?.querySelector<HTMLElement>(`[data-ve-content=\"${selectedKey}\"]`);\n if (!host) return;\n const type = blocks.find((b) => b._key === selectedKey)?._type ?? \"\";\n const specs = [...(INLINE_FIELDS[type] ?? []), ...GLOBAL_INLINE];\n\n const bound = new Set<HTMLElement>();\n const bind = (el: HTMLElement, field: string, html: boolean) => {\n if (el.dataset.veInline === \"1\") return;\n el.dataset.veInline = \"1\";\n el.setAttribute(\"contenteditable\", \"true\");\n el.setAttribute(\"spellcheck\", \"true\");\n el.classList.add(\"xicms-ve-inline\");\n el.addEventListener(\"blur\", () => commitInline(selectedKey, field, html ? el.innerHTML : (el.textContent ?? \"\")));\n el.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Escape\") el.blur();\n else if (!html && e.key === \"Enter\") { e.preventDefault(); el.blur(); }\n });\n bound.add(el);\n };\n const apply = () => {\n for (const m of specs) {\n host.querySelectorAll<HTMLElement>(m.selector).forEach((el) =>\n bind(el, \"fromData\" in m && m.fromData ? (el.dataset.xicmsEdit as string) : m.field, !!m.html),\n );\n }\n };\n\n apply();\n // Her-bind na elke re-render die de tekstnodes vervangt (niet tijdens slepen: dan muteert de\n // DOM continu door de drop-indicatoren en zou her-binden alleen maar ruis geven).\n const obs = new MutationObserver(() => {\n if (!draggingRef.current) apply();\n });\n obs.observe(host, { childList: true, subtree: true });\n return () => {\n obs.disconnect();\n bound.forEach((el) => {\n el.removeAttribute(\"contenteditable\");\n el.classList.remove(\"xicms-ve-inline\");\n delete el.dataset.veInline;\n });\n };\n }, [selectedKey, blocks, commitInline]);\n\n // Autosave naar de draft (publiceren blijft een aparte, bewuste stap).\n // Debounce 700ms ná de laatste wijziging, met een MAX-WAIT van 3s: de debounce herstart bij elke\n // toetsaanslag, dus zonder max-wait zou iemand die blijft doortypen nooit tussentijds opslaan.\n const dirtySinceRef = useRef<number | null>(null);\n useEffect(() => {\n if (!draft) return;\n if (firstSave.current) {\n firstSave.current = false;\n return;\n }\n dirtyRef.current = true;\n dirtySinceRef.current ??= Date.now();\n setStatus(\"saving\");\n const waited = Date.now() - dirtySinceRef.current;\n const delay = Math.max(0, Math.min(700, 3000 - waited));\n const t = setTimeout(() => {\n dirtySinceRef.current = null;\n const payload = draftPayload(draft);\n void saveDraft(siteId, draft.id, payload)\n .then(() => {\n lastSavedRef.current = stableStringify(payload);\n // Alleen \"schoon\"/\"saved\" als dit nog de nieuwste versie is (zie flushSave).\n if (draftRef.current === draft) {\n dirtyRef.current = false;\n setStatus(\"saved\");\n }\n })\n .catch(() => setStatus(\"error\"));\n }, delay);\n return () => clearTimeout(t);\n }, [draft, siteId]);\n\n // Waarschuw bij het verlaten van de pagina met niet-opgeslagen wijzigingen; flush bij tab-wissel/\n // verbergen (visibilitychange) en bij unmount. Escape sluit de editor (na flush).\n useEffect(() => {\n const beforeUnload = (e: BeforeUnloadEvent) => {\n if (dirtyRef.current) {\n e.preventDefault();\n e.returnValue = \"\";\n }\n };\n const onVisibility = () => {\n // Tab-wissel/minimaliseren: beste moment om pending werk veilig te stellen.\n if (document.visibilityState === \"hidden\") void flushSave().catch(() => {});\n };\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n handleClose();\n return;\n }\n // Undo/redo op canvas-niveau. In invoervelden/contentEditable laten we de native\n // tekst-undo van de browser zijn werk doen (anders vecht Ctrl+Z met het veld zelf).\n const t = e.target as HTMLElement | null;\n const inField = !!t && (t.isContentEditable || t.tagName === \"INPUT\" || t.tagName === \"TEXTAREA\" || t.tagName === \"SELECT\");\n if ((e.metaKey || e.ctrlKey) && !inField && (e.key.toLowerCase() === \"z\" || e.key.toLowerCase() === \"y\")) {\n e.preventDefault();\n if (e.key.toLowerCase() === \"y\" || e.shiftKey) redo();\n else undo();\n }\n };\n window.addEventListener(\"beforeunload\", beforeUnload);\n document.addEventListener(\"visibilitychange\", onVisibility);\n window.addEventListener(\"keydown\", onKeyDown);\n return () => {\n window.removeEventListener(\"beforeunload\", beforeUnload);\n document.removeEventListener(\"visibilitychange\", onVisibility);\n window.removeEventListener(\"keydown\", onKeyDown);\n const d = draftRef.current;\n if (d && dirtyRef.current) {\n // Laatste redmiddel bij unmount; log een mislukking i.p.v. stil verliezen.\n void saveDraft(siteId, d.id, draftPayload(d)).catch((err) =>\n console.warn(\"[xicms] autosave bij sluiten mislukt:\", err),\n );\n }\n };\n }, [siteId, handleClose, flushSave, undo, redo]);\n\n function setBlocks(next: Block[], opts?: { coalesce?: boolean }) {\n mutateDraft((d) => ({ ...d, blocks: next }), opts);\n }\n function updateByKey(key: string, next: Record<string, unknown>) {\n // Zijpaneel-edits komen per toetsaanslag binnen → coalesce voor zinnige undo-stappen.\n setBlocks(blocks.map((b) => (b._key === key ? ({ ...(next as Block), _key: b._key, _type: b._type }) : b)), { coalesce: true });\n }\n function move(i: number, dir: -1 | 1) {\n const j = i + dir;\n if (j < 0 || j >= blocks.length) return;\n const copy = [...blocks];\n [copy[i], copy[j]] = [copy[j], copy[i]];\n setBlocks(copy);\n }\n function duplicate(i: number) {\n const copy = [...blocks];\n copy.splice(i + 1, 0, { ...blocks[i], _key: newKey() });\n setBlocks(copy);\n }\n async function remove(i: number) {\n const ok = await confirm({ title: \"Blok verwijderen?\", confirmLabel: \"Verwijderen\", tone: \"danger\" });\n if (!ok) return;\n const removed = blocks[i];\n setBlocks(blocks.filter((_, idx) => idx !== i));\n if (selectedKey === removed._key) setSelectedKey(null);\n }\n function addBlock(type: string) {\n const def = registry.get(type);\n const base = def?.defaults?.() ?? {};\n const block = { _key: newKey(), _type: type, ...base } as Block;\n insertAfterSelected([block]);\n setSelectedKey(block._key);\n }\n function insertAfterSelected(newBlocks: Array<Record<string, unknown>>) {\n const sel = blocks.findIndex((b) => b._key === selectedKey);\n const copy = [...blocks];\n copy.splice(sel >= 0 ? sel + 1 : blocks.length, 0, ...(newBlocks as Block[]));\n setBlocks(copy);\n }\n\n async function onPublish() {\n if (!draft) return;\n const ok = await confirm({ title: \"Pagina publiceren?\", description: `De live site wordt bijgewerkt (${draft.path}).`, confirmLabel: \"Publiceren\" });\n if (!ok) return;\n setPublishing(true);\n try {\n await saveDraft(siteId, draft.id, { blocks: draft.blocks, title: draft.title, slug: draft.slug, path: draft.path, seo: draft.seo });\n await publishPage(siteId, draft.id);\n toast({ title: \"Gepubliceerd\", description: \"Herlaad de pagina om het live resultaat te zien.\", tone: \"success\" });\n } catch (e) {\n toast({ title: \"Publiceren mislukt\", description: (e as Error).message, tone: \"error\" });\n } finally {\n setPublishing(false);\n }\n }\n\n const addOptions = useMemo(() => buildBlockAddOptions(registry.values()), [registry]);\n\n const selectedIndex = blocks.findIndex((b) => b._key === selectedKey);\n const selected = selectedIndex >= 0 ? blocks[selectedIndex] : null;\n const canUndo = historyRef.current.past.length > 0;\n const canRedo = historyRef.current.future.length > 0;\n\n /** Laad de nieuwste (remote) versie in de editor — bij een gedetecteerd conflict. */\n const reloadRemote = useCallback(async () => {\n const remote = await getDraftByPath(siteId, path).catch(() => null);\n if (remote) {\n mutateDraft(() => remote);\n lastSavedRef.current = stableStringify(draftPayload(remote));\n dirtyRef.current = false;\n setStatus(\"idle\");\n }\n setConflict(false);\n }, [siteId, path, mutateDraft]);\n\n return (\n <div className=\"xicms-ve__shell\" role=\"dialog\" aria-modal=\"true\" aria-label={`Pagina bewerken: ${path}`}>\n <header className=\"xicms-ve__bar\">\n <strong className=\"xicms-ve__title\">Bewerken · {name}</strong>\n <span className=\"xicms-ve__path\">{path}</span>\n <button type=\"button\" className=\"xicms-ve__histbtn\" disabled={!canUndo} onClick={undo} title=\"Ongedaan maken (Ctrl+Z)\" aria-label=\"Ongedaan maken (Ctrl+Z)\">↶</button>\n <button type=\"button\" className=\"xicms-ve__histbtn\" disabled={!canRedo} onClick={redo} title=\"Opnieuw (Ctrl+Shift+Z)\" aria-label=\"Opnieuw (Ctrl+Shift+Z)\">↷</button>\n <span className=\"xicms-ve__status\" aria-live=\"polite\">\n {status === \"saving\" ? \"Opslaan…\" : status === \"saved\" ? \"✓ Opgeslagen\" : status === \"error\" ? \"⚠ Niet opgeslagen\" : \"\"}\n </span>\n {status === \"error\" && (\n <Button size=\"sm\" variant=\"secondary\" onClick={() => void flushSave().catch(() => setStatus(\"error\"))}>\n Opnieuw opslaan\n </Button>\n )}\n <div className=\"xicms-ve__bar-actions\">\n <Button size=\"sm\" disabled={publishing || !draft} onClick={onPublish}>\n {publishing ? \"Bezig…\" : \"Publiceren\"}\n </Button>\n <Button size=\"sm\" variant=\"secondary\" onClick={handleClose}>Sluiten</Button>\n </div>\n </header>\n\n {conflict && (\n <div className=\"xicms-ve__conflict\" role=\"alert\">\n <span>⚠ Deze pagina is intussen ergens anders gewijzigd (andere tab of collega). Doorwerken kan die wijzigingen overschrijven.</span>\n <Button size=\"sm\" onClick={() => void reloadRemote()}>Nieuwste versie laden</Button>\n <Button size=\"sm\" variant=\"secondary\" onClick={() => setConflict(false)}>Mijn versie behouden</Button>\n </div>\n )}\n\n <div className=\"xicms-ve__body\">\n <main className=\"xicms-ve__canvas\">\n {status === \"loading\" && <p className=\"xicms-ve__hint\">Laden…</p>}\n {status === \"none\" && (\n <EmptyState\n icon=\"📄\"\n title=\"Geen werkversie gevonden\"\n description={`Voor deze pagina (${path}) bestaat nog geen werkversie. Maak de pagina eerst aan in /beheer → Pagina's.`}\n />\n )}\n {draft && (\n <div className=\"container\" ref={canvasRef}>\n {blocks.length === 0 && (\n <EmptyState icon=\"🧱\" title=\"Nog geen blokken\" description=\"Voeg hieronder je eerste blok toe.\" />\n )}\n {blocks.map((b, i) => {\n const def = registry.get(b._type);\n const Render = def?.Render;\n // Coerce via het schema zodat defaults zijn toegepast (zoals de live pagina via\n // validateBlocks doet). Ruwe drafts missen anders defaults (bv. hero.buttons) en\n // zouden de Render laten throwen. Lukt het parsen niet, val terug op het ruwe blok\n // (de error-boundary hieronder vangt een eventuele fout dan netjes op).\n const safe = (() => {\n const parsed = def?.schema?.safeParse(b);\n return parsed?.success ? (parsed.data as Block) : b;\n })();\n const dropClass = dropIndex === i ? \" is-drop-before\" : dropIndex === i + 1 ? \" is-drop-after\" : \"\";\n return (\n <div\n key={b._key}\n className={`xicms-ve-block${b._key === selectedKey ? \" is-selected\" : \"\"}${dragIndex === i ? \" is-dragging\" : \"\"}${dropClass}`}\n onDragOver={(e) => {\n if (dragIndex === null) return;\n e.preventDefault();\n e.dataTransfer.dropEffect = \"move\";\n const rect = e.currentTarget.getBoundingClientRect();\n setDropIndex(e.clientY < rect.top + rect.height / 2 ? i : i + 1);\n }}\n onDrop={(e) => {\n if (dragIndex === null || dropIndex === null) return;\n e.preventDefault();\n setBlocks(reorderBlocks(blocks, dragIndex, dropIndex));\n endDrag();\n }}\n >\n <div className=\"xicms-ve-block__toolbar\">\n {/* Sleep-greep: apart element (niet het hele blok) zodat slepen nooit botst met\n het inline contentEditable-bewerken; ↑/↓ blijven het toetsenbord-alternatief. */}\n <button\n type=\"button\"\n className=\"xicms-ve-block__drag\"\n draggable\n onDragStart={(e) => {\n draggingRef.current = true;\n setDragIndex(i);\n e.dataTransfer.effectAllowed = \"move\";\n e.dataTransfer.setData(\"text/plain\", String(i));\n const blockEl = e.currentTarget.closest(\".xicms-ve-block\");\n if (blockEl) e.dataTransfer.setDragImage(blockEl, 24, 24);\n }}\n onDragEnd={endDrag}\n aria-label={`Versleep ${def?.label ?? b._type}`}\n title=\"Slepen om te verplaatsen\"\n >\n ⋮⋮\n </button>\n <span className=\"xicms-ve-block__label\">{def?.label ?? b._type}</span>\n <button type=\"button\" onClick={() => setSelectedKey(b._key)} aria-label={`Bewerk ${def?.label ?? b._type}`} title=\"Bewerken\">✏️</button>\n <button type=\"button\" disabled={i === 0} onClick={() => move(i, -1)} aria-label=\"Omhoog\">↑</button>\n <button type=\"button\" disabled={i === blocks.length - 1} onClick={() => move(i, 1)} aria-label=\"Omlaag\">↓</button>\n <button type=\"button\" onClick={() => duplicate(i)} aria-label=\"Dupliceren\">⎘</button>\n <button type=\"button\" onClick={() => void remove(i)} aria-label=\"Verwijderen\">🗑</button>\n </div>\n {/* Altijd renderen (geen conditie) → geen remount van de inhoud bij selecteren,\n anders verliest het inline-bewerken zijn contentEditable. Bij selectie zet de\n CSS deze overlay op pointer-events:none zodat je de tekst kunt bewerken. */}\n <button type=\"button\" className=\"xicms-ve-block__select\" aria-label={`Selecteer ${def?.label ?? b._type}`} onClick={() => setSelectedKey(b._key)} />\n <div className=\"xicms-ve-block__content\" data-ve-content={b._key}>\n {/* Net als BlockRenderer: elk blok in een per-blok error-boundary zodat één\n falend (maatwerk-)blok de hele editor niet blank maakt. mode=\"preview\". */}\n {Render ? (\n <BlockErrorBoundary mode=\"preview\" type={b._type} blockKey={b._key}>\n {/* Alleen het geselecteerde blok krijgt de inline beeld-vervangen-overlay. */}\n <InlineImageEditProvider onReplace={b._key === selectedKey ? (field) => setImgPicker({ key: b._key, field }) : null}>\n <Render block={safe} siteId={siteId} mode=\"preview\" />\n </InlineImageEditProvider>\n </BlockErrorBoundary>\n ) : (\n <p className=\"xicms-ve__hint\">Onbekend blok: {b._type}</p>\n )}\n </div>\n </div>\n );\n })}\n <div className=\"xicms-ve__inserter\">\n <span>Toevoegen{selected ? \" (na selectie)\" : \"\"}:</span>\n <div className=\"xicms-ve__inserter-select\">\n <Select value=\"\" placeholder=\"Kies een blok…\" onValueChange={(v) => v && addBlock(v)} options={addOptions} />\n </div>\n <PatternInserter onInsert={insertAfterSelected} />\n </div>\n </div>\n )}\n </main>\n\n <aside className=\"xicms-ve__panel\">\n {selected ? (\n <SelectedEditor key={selected._key} block={selected} siteId={siteId} registry={registry} onChange={(next) => updateByKey(selected._key, next)} />\n ) : draft ? (\n <div className=\"grid gap-4\">\n <p className=\"xicms-ve__hint\">Klik op een blok om het te bewerken.</p>\n <PageSettings draft={draft} onChange={(part) => mutateDraft((d) => ({ ...d, ...part }), { coalesce: true })} />\n {!isDemoActive() && (\n <VersionsPanel\n siteId={siteId}\n pageId={draft.id}\n onRestore={(restored) => {\n mutateDraft((d) => ({ ...d, ...restored }));\n toast({ title: \"Versie hersteld naar werkversie\", description: \"Publiceer om hem live te zetten.\", tone: \"success\" });\n }}\n />\n )}\n </div>\n ) : (\n <p className=\"xicms-ve__hint\">Klik op een blok om het te bewerken.</p>\n )}\n </aside>\n </div>\n\n {/* Inline afbeelding vervangen: opent de mediabibliotheek; alleen de URL van het veld wordt gezet. */}\n <MediaPickerDialog\n siteId={siteId}\n open={!!imgPicker}\n onOpenChange={(v) => { if (!v) setImgPicker(null); }}\n onSelect={(m) => { if (imgPicker) commitInline(imgPicker.key, imgPicker.field, m.url); setImgPicker(null); }}\n />\n </div>\n );\n}\n\n/**\n * Pagina-instellingen (titel + SEO) direct in de visuele editor — de klant hoeft niet meer naar\n * /beheer voor een titel- of meta-omschrijving-wijziging. Slug/pad blijven bewust in /beheer\n * (die raken URL-structuur en zijn niet iets om per ongeluk inline aan te passen).\n */\nfunction PageSettings({\n draft,\n onChange,\n}: {\n draft: DraftPage;\n onChange: (part: Partial<DraftPage>) => void;\n}) {\n const seo = draft.seo ?? {};\n return (\n <details className=\"xicms-ve__section\" open>\n <summary className=\"text-sm font-semibold\">Pagina-instellingen</summary>\n <div className=\"mt-2 grid gap-3\">\n <Field label=\"Paginatitel\">\n <Input value={draft.title ?? \"\"} onChange={(e) => onChange({ title: e.target.value })} />\n </Field>\n <Field label=\"SEO-titel\" hint=\"Leeg = paginatitel\">\n <Input value={seo.title ?? \"\"} onChange={(e) => onChange({ seo: { ...seo, title: e.target.value } })} />\n </Field>\n <Field label=\"SEO-omschrijving\" hint=\"Wordt getoond in zoekresultaten\">\n <Textarea rows={3} value={seo.description ?? \"\"} onChange={(e) => onChange({ seo: { ...seo, description: e.target.value } })} />\n </Field>\n </div>\n </details>\n );\n}\n\n/** Versiegeschiedenis in de visuele editor: herstellen zet een snapshot terug naar de WERKVERSIE. */\nfunction VersionsPanel({\n siteId,\n pageId,\n onRestore,\n}: {\n siteId: string;\n pageId: string;\n onRestore: (restored: Partial<DraftPage>) => void;\n}) {\n const [versions, setVersions] = useState<{ id: string; createdAt?: unknown; kind?: string; label?: string }[]>([]);\n const { toast } = useToast();\n const confirm = useConfirm();\n useEffect(() => subscribeVersions(siteId, pageId, setVersions), [siteId, pageId]);\n if (versions.length === 0) return null;\n\n const restore = async (versionId: string) => {\n const ok = await confirm({\n title: \"Versie herstellen?\",\n description: \"De snapshot vervangt je huidige werkversie (ongedaan te maken met Ctrl+Z). De live site verandert pas bij Publiceren.\",\n confirmLabel: \"Herstellen\",\n });\n if (!ok) return;\n try {\n const restored = await restoreVersion(siteId, pageId, versionId);\n onRestore(restored);\n } catch (e) {\n toast({ title: \"Herstellen mislukt\", description: (e as Error).message, tone: \"error\" });\n }\n };\n\n return (\n <details className=\"xicms-ve__section\">\n <summary className=\"text-sm font-semibold\">Versiegeschiedenis</summary>\n <ul className=\"mt-2 grid gap-1 text-sm\">\n {versions.slice(0, 10).map((v) => (\n <li key={v.id} className=\"flex items-center justify-between gap-2\">\n <span>\n {v.label ?? (v.kind === \"publish\" ? \"Gepubliceerd\" : v.kind ?? \"Snapshot\")}\n {formatVersionDate(v.createdAt) && <span className=\"text-slate-400\"> · {formatVersionDate(v.createdAt)}</span>}\n </span>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => void restore(v.id)}>\n Herstellen\n </Button>\n </li>\n ))}\n </ul>\n </details>\n );\n}\n\nfunction formatVersionDate(createdAt: unknown): string {\n const date =\n createdAt && typeof (createdAt as { toDate?: () => Date }).toDate === \"function\"\n ? (createdAt as { toDate: () => Date }).toDate()\n : typeof createdAt === \"string\"\n ? new Date(createdAt)\n : null;\n return date && !Number.isNaN(date.getTime()) ? date.toLocaleString(\"nl-NL\", { dateStyle: \"short\", timeStyle: \"short\" }) : \"\";\n}\n\nfunction SelectedEditor({\n block,\n siteId,\n registry,\n onChange,\n}: {\n block: Block;\n siteId: string;\n registry: ReturnType<typeof useRegistry>;\n onChange: (next: Record<string, unknown>) => void;\n}) {\n const def = registry.get(block._type);\n const Editor: ComponentType<BlockEditorProps> =\n (def?.Editor as ComponentType<BlockEditorProps> | undefined) ?? builtinBlockEditors[block._type] ?? GenericEditor;\n return (\n <div className=\"grid gap-3\">\n <h3 className=\"text-sm font-semibold\">{def?.label ?? block._type}</h3>\n {/* Maatwerk-Editors kunnen falen → vang per-blok op zodat het paneel niet leeg wordt. */}\n <BlockErrorBoundary mode=\"preview\" type={block._type} blockKey={block._key}>\n <Editor block={block} siteId={siteId} onChange={onChange} />\n </BlockErrorBoundary>\n <StyleEditor\n value={block._style as BlockStyle | undefined}\n onChange={(style) => onChange({ ...block, _style: style })}\n />\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Herordent een bloklijst voor drag-and-drop: haal het element op `from` weg en voeg het weer in
|
|
3
|
+
* vóór (oorspronkelijke) index `to` (0..length; length = achteraan). Puur — de visuele editor
|
|
4
|
+
* stuurt het resultaat door setBlocks/mutateDraft zodat undo/redo automatisch meedoet.
|
|
5
|
+
*/
|
|
6
|
+
export declare function reorderBlocks<T>(list: T[], from: number, to: number): T[];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
function reorderBlocks(list, from, to) {
|
|
2
|
+
if (from < 0 || from >= list.length || to < 0 || to > list.length) return list;
|
|
3
|
+
if (to === from || to === from + 1) return list;
|
|
4
|
+
const copy = [...list];
|
|
5
|
+
const [item] = copy.splice(from, 1);
|
|
6
|
+
copy.splice(to > from ? to - 1 : to, 0, item);
|
|
7
|
+
return copy;
|
|
8
|
+
}
|
|
9
|
+
export {
|
|
10
|
+
reorderBlocks
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=reorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/visual/reorder.ts"],"sourcesContent":["/**\n * Herordent een bloklijst voor drag-and-drop: haal het element op `from` weg en voeg het weer in\n * vóór (oorspronkelijke) index `to` (0..length; length = achteraan). Puur — de visuele editor\n * stuurt het resultaat door setBlocks/mutateDraft zodat undo/redo automatisch meedoet.\n */\nexport function reorderBlocks<T>(list: T[], from: number, to: number): T[] {\n if (from < 0 || from >= list.length || to < 0 || to > list.length) return list;\n // Invoegen direct vóór of ná zichzelf is geen verplaatsing.\n if (to === from || to === from + 1) return list;\n const copy = [...list];\n const [item] = copy.splice(from, 1);\n copy.splice(to > from ? to - 1 : to, 0, item);\n return copy;\n}\n"],"mappings":"AAKO,SAAS,cAAiB,MAAW,MAAc,IAAiB;AACzE,MAAI,OAAO,KAAK,QAAQ,KAAK,UAAU,KAAK,KAAK,KAAK,KAAK,OAAQ,QAAO;AAE1E,MAAI,OAAO,QAAQ,OAAO,OAAO,EAAG,QAAO;AAC3C,QAAM,OAAO,CAAC,GAAG,IAAI;AACrB,QAAM,CAAC,IAAI,IAAI,KAAK,OAAO,MAAM,CAAC;AAClC,OAAK,OAAO,KAAK,OAAO,KAAK,IAAI,IAAI,GAAG,IAAI;AAC5C,SAAO;AACT;","names":[]}
|
package/dist/admin.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.xicms-admin .static{position:static}.xicms-admin .fixed{position:fixed}.xicms-admin .relative{position:relative}.xicms-admin .sticky{position:sticky}.xicms-admin .inset-0{inset:0}.xicms-admin .left-1\/2{left:50%}.xicms-admin .top-1\/2{top:50%}.xicms-admin .z-\[10010\]{z-index:10010}.xicms-admin .z-\[10011\]{z-index:10011}.xicms-admin .mb-1{margin-bottom:.25rem}.xicms-admin .mb-2{margin-bottom:.5rem}.xicms-admin .mb-3{margin-bottom:.75rem}.xicms-admin .ml-1{margin-left:.25rem}.xicms-admin .mr-1{margin-right:.25rem}.xicms-admin .mt-1{margin-top:.25rem}.xicms-admin .mt-2{margin-top:.5rem}.xicms-admin .mt-3{margin-top:.75rem}.xicms-admin .mt-4{margin-top:1rem}.xicms-admin .mt-5{margin-top:1.25rem}.xicms-admin .\!block{display:block!important}.xicms-admin .block{display:block}.xicms-admin .inline{display:inline}.xicms-admin .flex{display:flex}.xicms-admin .inline-flex{display:inline-flex}.xicms-admin .grid{display:grid}.xicms-admin .hidden{display:none}.xicms-admin .aspect-square{aspect-ratio:1/1}.xicms-admin .h-10{height:2.5rem}.xicms-admin .h-28{height:7rem}.xicms-admin .h-32{height:8rem}.xicms-admin .h-8{height:2rem}.xicms-admin .h-9{height:2.25rem}.xicms-admin .h-fit{height:-moz-fit-content;height:fit-content}.xicms-admin .max-h-\[55vh\]{max-height:55vh}.xicms-admin .max-h-\[60vh\]{max-height:60vh}.xicms-admin .min-h-\[8rem\]{min-height:8rem}.xicms-admin .w-56{width:14rem}.xicms-admin .w-\[92vw\]{width:92vw}.xicms-admin .w-full{width:100%}.xicms-admin .min-w-8{min-width:2rem}.xicms-admin .max-w-2xl{max-width:42rem}.xicms-admin .max-w-lg{max-width:32rem}.xicms-admin .max-w-xl{max-width:36rem}.xicms-admin .flex-1{flex:1 1 0%}.xicms-admin .shrink-0{flex-shrink:0}.xicms-admin .-translate-x-1\/2{--tw-translate-x:-50%}.xicms-admin .-translate-x-1\/2,.xicms-admin .-translate-y-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.xicms-admin .-translate-y-1\/2{--tw-translate-y:-50%}.xicms-admin .cursor-pointer{cursor:pointer}.xicms-admin .resize{resize:both}.xicms-admin .list-disc{list-style-type:disc}.xicms-admin .grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xicms-admin .grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xicms-admin .grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xicms-admin .grid-cols-\[1fr_1fr_auto\]{grid-template-columns:1fr 1fr auto}.xicms-admin .grid-cols-\[3rem_1fr_1fr\]{grid-template-columns:3rem 1fr 1fr}.xicms-admin .flex-col{flex-direction:column}.xicms-admin .flex-wrap{flex-wrap:wrap}.xicms-admin .items-start{align-items:flex-start}.xicms-admin .items-end{align-items:flex-end}.xicms-admin .items-center{align-items:center}.xicms-admin .justify-end{justify-content:flex-end}.xicms-admin .justify-center{justify-content:center}.xicms-admin .justify-between{justify-content:space-between}.xicms-admin .gap-1{gap:.25rem}.xicms-admin .gap-1\.5{gap:.375rem}.xicms-admin .gap-2{gap:.5rem}.xicms-admin .gap-3{gap:.75rem}.xicms-admin .gap-4{gap:1rem}.xicms-admin .gap-6{gap:1.5rem}.xicms-admin .self-end{align-self:flex-end}.xicms-admin .overflow-auto{overflow:auto}.xicms-admin .overflow-hidden{overflow:hidden}.xicms-admin .overflow-y-auto{overflow-y:auto}.xicms-admin .truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.xicms-admin .rounded{border-radius:.25rem}.xicms-admin .rounded-full{border-radius:9999px}.xicms-admin .rounded-lg{border-radius:.5rem}.xicms-admin .rounded-md{border-radius:.375rem}.xicms-admin .rounded-xl{border-radius:.75rem}.xicms-admin .rounded-b-lg{border-bottom-right-radius:.5rem;border-bottom-left-radius:.5rem}.xicms-admin .rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.xicms-admin .rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.xicms-admin .border{border-width:1px}.xicms-admin .border-b{border-bottom-width:1px}.xicms-admin .border-t{border-top-width:1px}.xicms-admin .border-t-0{border-top-width:0}.xicms-admin .border-amber-300{--tw-border-opacity:1;border-color:rgb(252 211 77/var(--tw-border-opacity,1))}.xicms-admin .border-red-200{--tw-border-opacity:1;border-color:rgb(254 202 202/var(--tw-border-opacity,1))}.xicms-admin .border-slate-100{--tw-border-opacity:1;border-color:rgb(241 245 249/var(--tw-border-opacity,1))}.xicms-admin .border-slate-200{--tw-border-opacity:1;border-color:rgb(226 232 240/var(--tw-border-opacity,1))}.xicms-admin .border-slate-300{--tw-border-opacity:1;border-color:rgb(203 213 225/var(--tw-border-opacity,1))}.xicms-admin .bg-amber-50{--tw-bg-opacity:1;background-color:rgb(255 251 235/var(--tw-bg-opacity,1))}.xicms-admin .bg-black\/50{background-color:rgba(0,0,0,.5)}.xicms-admin .bg-brand-50{background-color:color-mix(in srgb,var(--xicms-primary,#4f46e5) 12%,#fff)}.xicms-admin .bg-brand-500{background-color:var(--xicms-primary,#4f46e5)}.xicms-admin .bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.xicms-admin .bg-slate-200{--tw-bg-opacity:1;background-color:rgb(226 232 240/var(--tw-bg-opacity,1))}.xicms-admin .bg-slate-50{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity,1))}.xicms-admin .bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.xicms-admin .object-cover{-o-object-fit:cover;object-fit:cover}.xicms-admin .p-1{padding:.25rem}.xicms-admin .p-2{padding:.5rem}.xicms-admin .p-3{padding:.75rem}.xicms-admin .p-4{padding:1rem}.xicms-admin .p-5{padding:1.25rem}.xicms-admin .p-6{padding:1.5rem}.xicms-admin .px-2{padding-left:.5rem;padding-right:.5rem}.xicms-admin .px-3{padding-left:.75rem;padding-right:.75rem}.xicms-admin .px-4{padding-left:1rem;padding-right:1rem}.xicms-admin .py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.xicms-admin .py-1{padding-top:.25rem;padding-bottom:.25rem}.xicms-admin .py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.xicms-admin .py-2{padding-top:.5rem;padding-bottom:.5rem}.xicms-admin .py-8{padding-top:2rem;padding-bottom:2rem}.xicms-admin .pl-5{padding-left:1.25rem}.xicms-admin .pt-5{padding-top:1.25rem}.xicms-admin .text-left{text-align:left}.xicms-admin .text-center{text-align:center}.xicms-admin .font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.xicms-admin .text-3xl{font-size:1.875rem;line-height:2.25rem}.xicms-admin .text-base{font-size:1rem;line-height:1.5rem}.xicms-admin .text-lg{font-size:1.125rem;line-height:1.75rem}.xicms-admin .text-sm{font-size:.875rem;line-height:1.25rem}.xicms-admin .text-xl{font-size:1.25rem;line-height:1.75rem}.xicms-admin .text-xs{font-size:.75rem;line-height:1rem}.xicms-admin .font-bold{font-weight:700}.xicms-admin .font-medium{font-weight:500}.xicms-admin .font-semibold{font-weight:600}.xicms-admin .uppercase{text-transform:uppercase}.xicms-admin .italic{font-style:italic}.xicms-admin .tracking-wide{letter-spacing:.025em}.xicms-admin .text-amber-800{--tw-text-opacity:1;color:rgb(146 64 14/var(--tw-text-opacity,1))}.xicms-admin .text-brand-700{color:color-mix(in srgb,var(--xicms-primary,#4f46e5) 82%,#000)}.xicms-admin .text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity,1))}.xicms-admin .text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity,1))}.xicms-admin .text-slate-400{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity,1))}.xicms-admin .text-slate-500{--tw-text-opacity:1;color:rgb(100 116 139/var(--tw-text-opacity,1))}.xicms-admin .text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity,1))}.xicms-admin .text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity,1))}.xicms-admin .opacity-70{opacity:.7}.xicms-admin .shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.xicms-admin .outline-none{outline:2px solid transparent;outline-offset:2px}.xicms-admin .outline{outline-style:solid}.xicms-admin .blur{--tw-blur:blur(8px)}.xicms-admin .blur,.xicms-admin .grayscale{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.xicms-admin .grayscale{--tw-grayscale:grayscale(100%)}.xicms-admin .filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.xicms-admin .transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.xicms-admin .transition-\[filter\]{transition-property:filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.xicms-admin,.xicms-admin *,.xicms-admin :after,.xicms-admin :before{box-sizing:border-box}.xicms-admin{font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;color:#0f172a;line-height:1.5;-webkit-font-smoothing:antialiased;color-scheme:light;background:#fff;--color-background-body:#fff;--color-background-surface:#fff;--color-background-card:#fff;--color-background-muted:#f1f5f9;--color-text-primary:#0f172a;--color-text-secondary:#475569;--color-border:#e2e8f0;--color-accent:#4f46e5;--color-on-accent:#fff;--color-error:#dc2626}.xicms-admin figure,.xicms-admin h1,.xicms-admin h2,.xicms-admin h3,.xicms-admin p{margin:0}.xicms-admin button{font:inherit;cursor:pointer}.xicms-admin input,.xicms-admin select,.xicms-admin textarea{font:inherit;color:inherit}.xicms-admin .ProseMirror{min-height:8rem;outline:none}.xicms-admin .ProseMirror p{margin:0 0 .6rem}.xicms-admin .ProseMirror:focus{outline:none}@media (prefers-reduced-motion:reduce){.xicms-admin *,.xicms-admin :after,.xicms-admin :before,.xicms-ve *,.xicms-ve :after,.xicms-ve :before{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}}.xicms-admin .last\:border-0:last-child{border-width:0}.xicms-admin .hover\:border-brand-500:hover{border-color:var(--xicms-primary,#4f46e5)}.xicms-admin .hover\:bg-slate-100:hover{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity,1))}.xicms-admin .hover\:bg-slate-50:hover{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity,1))}.xicms-admin .hover\:text-slate-900:hover{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity,1))}.xicms-admin .hover\:underline:hover{text-decoration-line:underline}.xicms-admin .hover\:brightness-95:hover{--tw-brightness:brightness(.95);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.xicms-admin .focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.xicms-admin .focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.xicms-admin .focus\:ring-indigo-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(99 102 241/var(--tw-ring-opacity,1))}.xicms-admin .focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.xicms-admin .focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.xicms-admin .focus-visible\:ring-brand-500:focus-visible{--tw-ring-color:var(--xicms-primary,#4f46e5)}.xicms-admin .disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.xicms-admin .disabled\:opacity-50:disabled{opacity:.5}.xicms-admin :is(.group:hover .group-hover\:opacity-80){opacity:.8}.xicms-admin .data-\[state\=active\]\:border-b-2[data-state=active]{border-bottom-width:2px}.xicms-admin .data-\[state\=active\]\:border-brand-600[data-state=active]{border-color:var(--xicms-primary,#4f46e5)}.xicms-admin .data-\[state\=active\]\:text-brand-700[data-state=active]{color:color-mix(in srgb,var(--xicms-primary,#4f46e5) 82%,#000)}@media (min-width:640px){.xicms-admin .sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xicms-admin .sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xicms-admin .sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (min-width:768px){.xicms-admin .md\:col-span-5{grid-column:span 5/span 5}.xicms-admin .md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xicms-admin .md\:grid-cols-\[1fr_1fr_140px_auto_auto\]{grid-template-columns:1fr 1fr 140px auto auto}.xicms-admin .md\:grid-cols-\[260px_1fr\]{grid-template-columns:260px 1fr}}@media (min-width:1024px){.xicms-admin .lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xicms-admin .lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { type User } from "firebase/auth";
|
|
3
|
+
import { type Role, type XiClaims } from "./roles";
|
|
4
|
+
interface AuthState {
|
|
5
|
+
/** Of Firebase überhaupt geconfigureerd is. */
|
|
6
|
+
configured: boolean;
|
|
7
|
+
/** Bezig met laden van de auth-status. */
|
|
8
|
+
loading: boolean;
|
|
9
|
+
user: User | null;
|
|
10
|
+
claims: XiClaims | null;
|
|
11
|
+
/** Effectieve rol op de huidige site (uit `siteId`). */
|
|
12
|
+
role: Role | null;
|
|
13
|
+
signIn: (email: string, password: string) => Promise<void>;
|
|
14
|
+
/** Inloggen met Google (popup). */
|
|
15
|
+
signInWithGoogle: () => Promise<void>;
|
|
16
|
+
signOut: () => Promise<void>;
|
|
17
|
+
/** Forceer het opnieuw inlezen van claims (na een rolwijziging). */
|
|
18
|
+
refresh: () => Promise<void>;
|
|
19
|
+
/** Demo-bewerkmodus actief (config.demoEditing) — pseudo-login, niets wordt opgeslagen. */
|
|
20
|
+
demo: boolean;
|
|
21
|
+
/** Pseudo-login voor de demo: geeft lokaal een siteadmin-rol zonder Firebase. */
|
|
22
|
+
demoSignIn: () => void;
|
|
23
|
+
}
|
|
24
|
+
export declare function AuthProvider({ siteId, demo, children, }: {
|
|
25
|
+
siteId: string;
|
|
26
|
+
demo?: boolean;
|
|
27
|
+
children: ReactNode;
|
|
28
|
+
}): import("react").JSX.Element;
|
|
29
|
+
export declare function useAuth(): AuthState;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import {
|
|
4
|
+
createContext,
|
|
5
|
+
useContext,
|
|
6
|
+
useEffect,
|
|
7
|
+
useMemo,
|
|
8
|
+
useState
|
|
9
|
+
} from "react";
|
|
10
|
+
import {
|
|
11
|
+
GoogleAuthProvider,
|
|
12
|
+
onAuthStateChanged,
|
|
13
|
+
signInWithEmailAndPassword,
|
|
14
|
+
signInWithPopup,
|
|
15
|
+
signOut as fbSignOut
|
|
16
|
+
} from "firebase/auth";
|
|
17
|
+
import { getFirebaseAuth, isFirebaseConfigured } from "../firebase/client";
|
|
18
|
+
import { roleForSite } from "./roles";
|
|
19
|
+
import { DEMO_EVENT, isDemoLoggedIn, setDemoLoggedIn } from "../demo/demoStore";
|
|
20
|
+
const AuthContext = createContext(null);
|
|
21
|
+
function AuthProvider({
|
|
22
|
+
siteId,
|
|
23
|
+
demo = false,
|
|
24
|
+
children
|
|
25
|
+
}) {
|
|
26
|
+
const configured = isFirebaseConfigured();
|
|
27
|
+
const [loading, setLoading] = useState(configured);
|
|
28
|
+
const [user, setUser] = useState(null);
|
|
29
|
+
const [claims, setClaims] = useState(null);
|
|
30
|
+
const [demoLoggedIn, setDemoLoggedInState] = useState(false);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (!demo) return;
|
|
33
|
+
const sync = () => setDemoLoggedInState(isDemoLoggedIn());
|
|
34
|
+
sync();
|
|
35
|
+
window.addEventListener(DEMO_EVENT, sync);
|
|
36
|
+
return () => window.removeEventListener(DEMO_EVENT, sync);
|
|
37
|
+
}, [demo]);
|
|
38
|
+
async function loadClaims(u) {
|
|
39
|
+
if (!u) {
|
|
40
|
+
setClaims(null);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const token = await u.getIdTokenResult(true);
|
|
44
|
+
setClaims({
|
|
45
|
+
superadmin: token.claims.superadmin === true,
|
|
46
|
+
roles: token.claims.roles ?? {}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
const auth = getFirebaseAuth();
|
|
51
|
+
if (!auth) {
|
|
52
|
+
setLoading(false);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const unsub = onAuthStateChanged(auth, async (u) => {
|
|
56
|
+
setUser(u);
|
|
57
|
+
await loadClaims(u);
|
|
58
|
+
setLoading(false);
|
|
59
|
+
});
|
|
60
|
+
return () => unsub();
|
|
61
|
+
}, []);
|
|
62
|
+
const value = useMemo(() => {
|
|
63
|
+
const demoActiveLogin = demo && demoLoggedIn;
|
|
64
|
+
const effectiveUser = demoActiveLogin ? { uid: "demo", email: "demo@voorbeeld.nl", displayName: "Demo-beheerder" } : user;
|
|
65
|
+
const effectiveClaims = demoActiveLogin ? { roles: { [siteId]: "siteadmin" } } : claims;
|
|
66
|
+
return {
|
|
67
|
+
configured,
|
|
68
|
+
loading,
|
|
69
|
+
user: effectiveUser,
|
|
70
|
+
claims: effectiveClaims,
|
|
71
|
+
role: roleForSite(effectiveClaims, siteId),
|
|
72
|
+
async signIn(email, password) {
|
|
73
|
+
const auth = getFirebaseAuth();
|
|
74
|
+
if (!auth) throw new Error("Firebase is niet geconfigureerd.");
|
|
75
|
+
await signInWithEmailAndPassword(auth, email, password);
|
|
76
|
+
},
|
|
77
|
+
async signInWithGoogle() {
|
|
78
|
+
const auth = getFirebaseAuth();
|
|
79
|
+
if (!auth) throw new Error("Firebase is niet geconfigureerd.");
|
|
80
|
+
await signInWithPopup(auth, new GoogleAuthProvider());
|
|
81
|
+
},
|
|
82
|
+
async signOut() {
|
|
83
|
+
if (demo) {
|
|
84
|
+
setDemoLoggedIn(false);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const auth = getFirebaseAuth();
|
|
88
|
+
if (auth) await fbSignOut(auth);
|
|
89
|
+
},
|
|
90
|
+
async refresh() {
|
|
91
|
+
await loadClaims(getFirebaseAuth()?.currentUser ?? user);
|
|
92
|
+
},
|
|
93
|
+
demo,
|
|
94
|
+
demoSignIn() {
|
|
95
|
+
setDemoLoggedIn(true);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}, [configured, loading, user, claims, siteId, demo, demoLoggedIn]);
|
|
99
|
+
return /* @__PURE__ */ jsx(AuthContext.Provider, { value, children });
|
|
100
|
+
}
|
|
101
|
+
function useAuth() {
|
|
102
|
+
const ctx = useContext(AuthContext);
|
|
103
|
+
if (!ctx) throw new Error("useAuth moet binnen <XiCMSProvider> worden gebruikt.");
|
|
104
|
+
return ctx;
|
|
105
|
+
}
|
|
106
|
+
export {
|
|
107
|
+
AuthProvider,
|
|
108
|
+
useAuth
|
|
109
|
+
};
|
|
110
|
+
//# sourceMappingURL=AuthProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/auth/AuthProvider.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactNode,\n} from \"react\";\nimport {\n GoogleAuthProvider,\n onAuthStateChanged,\n signInWithEmailAndPassword,\n signInWithPopup,\n signOut as fbSignOut,\n type User,\n} from \"firebase/auth\";\nimport { getFirebaseAuth, isFirebaseConfigured } from \"../firebase/client\";\nimport { roleForSite, type Role, type XiClaims } from \"./roles\";\nimport { DEMO_EVENT, isDemoLoggedIn, setDemoLoggedIn } from \"../demo/demoStore\";\n\ninterface AuthState {\n /** Of Firebase überhaupt geconfigureerd is. */\n configured: boolean;\n /** Bezig met laden van de auth-status. */\n loading: boolean;\n user: User | null;\n claims: XiClaims | null;\n /** Effectieve rol op de huidige site (uit `siteId`). */\n role: Role | null;\n signIn: (email: string, password: string) => Promise<void>;\n /** Inloggen met Google (popup). */\n signInWithGoogle: () => Promise<void>;\n signOut: () => Promise<void>;\n /** Forceer het opnieuw inlezen van claims (na een rolwijziging). */\n refresh: () => Promise<void>;\n /** Demo-bewerkmodus actief (config.demoEditing) — pseudo-login, niets wordt opgeslagen. */\n demo: boolean;\n /** Pseudo-login voor de demo: geeft lokaal een siteadmin-rol zonder Firebase. */\n demoSignIn: () => void;\n}\n\nconst AuthContext = createContext<AuthState | null>(null);\n\nexport function AuthProvider({\n siteId,\n demo = false,\n children,\n}: {\n siteId: string;\n demo?: boolean;\n children: ReactNode;\n}) {\n const configured = isFirebaseConfigured();\n const [loading, setLoading] = useState(configured);\n const [user, setUser] = useState<User | null>(null);\n const [claims, setClaims] = useState<XiClaims | null>(null);\n const [demoLoggedIn, setDemoLoggedInState] = useState(false);\n\n // Demo-login uit de (sessie)opslag inlezen + meeluisteren met wijzigingen (login/logout/reset).\n useEffect(() => {\n if (!demo) return;\n const sync = () => setDemoLoggedInState(isDemoLoggedIn());\n sync();\n window.addEventListener(DEMO_EVENT, sync);\n return () => window.removeEventListener(DEMO_EVENT, sync);\n }, [demo]);\n\n async function loadClaims(u: User | null) {\n if (!u) {\n setClaims(null);\n return;\n }\n const token = await u.getIdTokenResult(true);\n setClaims({\n superadmin: token.claims.superadmin === true,\n roles: (token.claims.roles as XiClaims[\"roles\"]) ?? {},\n });\n }\n\n useEffect(() => {\n const auth = getFirebaseAuth();\n if (!auth) {\n setLoading(false);\n return;\n }\n const unsub = onAuthStateChanged(auth, async (u) => {\n setUser(u);\n await loadClaims(u);\n setLoading(false);\n });\n return () => unsub();\n }, []);\n\n const value = useMemo<AuthState>(() => {\n // In de demo met actieve pseudo-login: doe alsof een siteadmin is ingelogd (lokaal, geen Firebase).\n const demoActiveLogin = demo && demoLoggedIn;\n const effectiveUser = demoActiveLogin\n ? ({ uid: \"demo\", email: \"demo@voorbeeld.nl\", displayName: \"Demo-beheerder\" } as unknown as User)\n : user;\n const effectiveClaims: XiClaims | null = demoActiveLogin ? { roles: { [siteId]: \"siteadmin\" } } : claims;\n\n return {\n configured,\n loading,\n user: effectiveUser,\n claims: effectiveClaims,\n role: roleForSite(effectiveClaims, siteId),\n async signIn(email, password) {\n const auth = getFirebaseAuth();\n if (!auth) throw new Error(\"Firebase is niet geconfigureerd.\");\n await signInWithEmailAndPassword(auth, email, password);\n },\n async signInWithGoogle() {\n const auth = getFirebaseAuth();\n if (!auth) throw new Error(\"Firebase is niet geconfigureerd.\");\n await signInWithPopup(auth, new GoogleAuthProvider());\n },\n async signOut() {\n if (demo) {\n setDemoLoggedIn(false);\n return;\n }\n const auth = getFirebaseAuth();\n if (auth) await fbSignOut(auth);\n },\n async refresh() {\n // Altijd de actuele Firebase-user (niet de gememoïseerde) — na een snelle\n // uitlog/inlog-wissel zou de closure anders claims van de vorige user verversen.\n await loadClaims(getFirebaseAuth()?.currentUser ?? user);\n },\n demo,\n demoSignIn() {\n setDemoLoggedIn(true);\n },\n };\n }, [configured, loading, user, claims, siteId, demo, demoLoggedIn]);\n\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n\nexport function useAuth(): AuthState {\n const ctx = useContext(AuthContext);\n if (!ctx) throw new Error(\"useAuth moet binnen <XiCMSProvider> worden gebruikt.\");\n return ctx;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { useAuth } from "./AuthProvider";
|
|
5
|
+
function authErrorMessage(e, fallback) {
|
|
6
|
+
const code = e?.code ?? "";
|
|
7
|
+
switch (code) {
|
|
8
|
+
case "auth/unauthorized-domain":
|
|
9
|
+
return "Dit domein mag nog niet inloggen. Voeg het toe in Firebase \u2192 Authentication \u2192 Settings \u2192 Authorized domains (bv. je Vercel-domein).";
|
|
10
|
+
case "auth/invalid-api-key":
|
|
11
|
+
case "auth/api-key-not-valid":
|
|
12
|
+
case "auth/api-key-not-valid.-please-pass-a-valid-api-key.":
|
|
13
|
+
return "De Firebase API-sleutel klopt niet. Controleer NEXT_PUBLIC_FIREBASE_API_KEY in de (Vercel-)omgevingsvariabelen.";
|
|
14
|
+
case "auth/configuration-not-found":
|
|
15
|
+
return "Firebase Authentication is niet (juist) ingericht voor dit project. Controleer de NEXT_PUBLIC_FIREBASE_*-env en of Authentication aan staat.";
|
|
16
|
+
case "auth/operation-not-allowed":
|
|
17
|
+
return "Deze inlogmethode staat uit. Zet 'm aan in Firebase \u2192 Authentication \u2192 Sign-in method (E-mail/wachtwoord of Google).";
|
|
18
|
+
case "auth/network-request-failed":
|
|
19
|
+
return "Geen verbinding met Firebase. Controleer je netwerk en de NEXT_PUBLIC_FIREBASE_*-config.";
|
|
20
|
+
case "auth/invalid-credential":
|
|
21
|
+
case "auth/wrong-password":
|
|
22
|
+
case "auth/user-not-found":
|
|
23
|
+
case "auth/invalid-email":
|
|
24
|
+
return "Inloggen mislukt. Controleer je e-mailadres en wachtwoord.";
|
|
25
|
+
case "auth/popup-blocked":
|
|
26
|
+
case "auth/popup-closed-by-user":
|
|
27
|
+
case "auth/cancelled-popup-request":
|
|
28
|
+
return "De Google-login-popup werd geblokkeerd of gesloten. Sta popups toe en probeer opnieuw.";
|
|
29
|
+
default:
|
|
30
|
+
return code ? `${fallback} (${code})` : fallback;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function LoginForm({ onSuccess }) {
|
|
34
|
+
const { signIn, signInWithGoogle, configured, user } = useAuth();
|
|
35
|
+
const [email, setEmail] = useState("");
|
|
36
|
+
const [password, setPassword] = useState("");
|
|
37
|
+
const [error, setError] = useState(null);
|
|
38
|
+
const [busy, setBusy] = useState(false);
|
|
39
|
+
async function handleGoogle() {
|
|
40
|
+
setError(null);
|
|
41
|
+
setBusy(true);
|
|
42
|
+
try {
|
|
43
|
+
await signInWithGoogle();
|
|
44
|
+
onSuccess?.();
|
|
45
|
+
} catch (e) {
|
|
46
|
+
setError(authErrorMessage(e, "Inloggen met Google mislukt of geannuleerd."));
|
|
47
|
+
} finally {
|
|
48
|
+
setBusy(false);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (!configured) {
|
|
52
|
+
return /* @__PURE__ */ jsx("div", { className: "xicms-card", style: { padding: "1.5rem" }, children: /* @__PURE__ */ jsxs("p", { children: [
|
|
53
|
+
"Firebase is nog niet geconfigureerd. Vul de ",
|
|
54
|
+
/* @__PURE__ */ jsx("code", { children: "NEXT_PUBLIC_FIREBASE_*" }),
|
|
55
|
+
"omgevingsvariabelen in om inloggen aan te zetten."
|
|
56
|
+
] }) });
|
|
57
|
+
}
|
|
58
|
+
if (user) {
|
|
59
|
+
return /* @__PURE__ */ jsx("div", { className: "xicms-card", style: { padding: "1.5rem" }, children: /* @__PURE__ */ jsxs("p", { children: [
|
|
60
|
+
"Je bent ingelogd als ",
|
|
61
|
+
/* @__PURE__ */ jsx("strong", { children: user.email }),
|
|
62
|
+
"."
|
|
63
|
+
] }) });
|
|
64
|
+
}
|
|
65
|
+
async function handleSubmit(e) {
|
|
66
|
+
e.preventDefault();
|
|
67
|
+
setError(null);
|
|
68
|
+
setBusy(true);
|
|
69
|
+
try {
|
|
70
|
+
await signIn(email, password);
|
|
71
|
+
onSuccess?.();
|
|
72
|
+
} catch (e2) {
|
|
73
|
+
setError(authErrorMessage(e2, "Inloggen mislukt. Controleer je e-mailadres en wachtwoord."));
|
|
74
|
+
} finally {
|
|
75
|
+
setBusy(false);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "xicms-card", style: { padding: "1.5rem", display: "grid", gap: "0.75rem", maxWidth: 360 }, children: [
|
|
79
|
+
/* @__PURE__ */ jsx("h2", { style: { margin: 0 }, children: "Inloggen" }),
|
|
80
|
+
/* @__PURE__ */ jsxs("label", { style: { display: "grid", gap: "0.25rem" }, children: [
|
|
81
|
+
/* @__PURE__ */ jsx("span", { children: "E-mailadres" }),
|
|
82
|
+
/* @__PURE__ */ jsx(
|
|
83
|
+
"input",
|
|
84
|
+
{
|
|
85
|
+
type: "email",
|
|
86
|
+
required: true,
|
|
87
|
+
value: email,
|
|
88
|
+
onChange: (e) => setEmail(e.target.value),
|
|
89
|
+
autoComplete: "email",
|
|
90
|
+
className: "xicms-input"
|
|
91
|
+
}
|
|
92
|
+
)
|
|
93
|
+
] }),
|
|
94
|
+
/* @__PURE__ */ jsxs("label", { style: { display: "grid", gap: "0.25rem" }, children: [
|
|
95
|
+
/* @__PURE__ */ jsx("span", { children: "Wachtwoord" }),
|
|
96
|
+
/* @__PURE__ */ jsx(
|
|
97
|
+
"input",
|
|
98
|
+
{
|
|
99
|
+
type: "password",
|
|
100
|
+
required: true,
|
|
101
|
+
value: password,
|
|
102
|
+
onChange: (e) => setPassword(e.target.value),
|
|
103
|
+
autoComplete: "current-password",
|
|
104
|
+
className: "xicms-input"
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
] }),
|
|
108
|
+
error && /* @__PURE__ */ jsx("p", { style: { color: "#dc2626", margin: 0 }, children: error }),
|
|
109
|
+
/* @__PURE__ */ jsx("button", { type: "submit", disabled: busy, className: "xicms-btn", children: busy ? "Bezig\u2026" : "Inloggen" }),
|
|
110
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", opacity: 0.6, fontSize: 13 }, children: [
|
|
111
|
+
/* @__PURE__ */ jsx("span", { style: { flex: 1, height: 1, background: "currentColor" } }),
|
|
112
|
+
" of",
|
|
113
|
+
" ",
|
|
114
|
+
/* @__PURE__ */ jsx("span", { style: { flex: 1, height: 1, background: "currentColor" } })
|
|
115
|
+
] }),
|
|
116
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: handleGoogle, disabled: busy, className: "xicms-btn xicms-btn--ghost", children: "Inloggen met Google" })
|
|
117
|
+
] });
|
|
118
|
+
}
|
|
119
|
+
export {
|
|
120
|
+
LoginForm
|
|
121
|
+
};
|
|
122
|
+
//# sourceMappingURL=LoginForm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/auth/LoginForm.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, type FormEvent } from \"react\";\nimport { useAuth } from \"./AuthProvider\";\n\n/**\n * Vertaalt een Firebase-auth-fout naar een bruikbare melding. Maakt de meest voorkomende\n * deploy-/configuratieproblemen zelf-diagnosticerend i.p.v. een generieke \"inloggen mislukt\".\n */\nfunction authErrorMessage(e: unknown, fallback: string): string {\n const code = (e as { code?: string })?.code ?? \"\";\n switch (code) {\n case \"auth/unauthorized-domain\":\n return \"Dit domein mag nog niet inloggen. Voeg het toe in Firebase → Authentication → Settings → Authorized domains (bv. je Vercel-domein).\";\n case \"auth/invalid-api-key\":\n case \"auth/api-key-not-valid\":\n case \"auth/api-key-not-valid.-please-pass-a-valid-api-key.\":\n return \"De Firebase API-sleutel klopt niet. Controleer NEXT_PUBLIC_FIREBASE_API_KEY in de (Vercel-)omgevingsvariabelen.\";\n case \"auth/configuration-not-found\":\n return \"Firebase Authentication is niet (juist) ingericht voor dit project. Controleer de NEXT_PUBLIC_FIREBASE_*-env en of Authentication aan staat.\";\n case \"auth/operation-not-allowed\":\n return \"Deze inlogmethode staat uit. Zet 'm aan in Firebase → Authentication → Sign-in method (E-mail/wachtwoord of Google).\";\n case \"auth/network-request-failed\":\n return \"Geen verbinding met Firebase. Controleer je netwerk en de NEXT_PUBLIC_FIREBASE_*-config.\";\n case \"auth/invalid-credential\":\n case \"auth/wrong-password\":\n case \"auth/user-not-found\":\n case \"auth/invalid-email\":\n return \"Inloggen mislukt. Controleer je e-mailadres en wachtwoord.\";\n case \"auth/popup-blocked\":\n case \"auth/popup-closed-by-user\":\n case \"auth/cancelled-popup-request\":\n return \"De Google-login-popup werd geblokkeerd of gesloten. Sta popups toe en probeer opnieuw.\";\n default:\n return code ? `${fallback} (${code})` : fallback;\n }\n}\n\n/** Eenvoudig inlogformulier voor beheerders/editors (e-mail/wachtwoord of Google). */\nexport function LoginForm({ onSuccess }: { onSuccess?: () => void }) {\n const { signIn, signInWithGoogle, configured, user } = useAuth();\n const [email, setEmail] = useState(\"\");\n const [password, setPassword] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n const [busy, setBusy] = useState(false);\n\n async function handleGoogle() {\n setError(null);\n setBusy(true);\n try {\n await signInWithGoogle();\n onSuccess?.();\n } catch (e) {\n setError(authErrorMessage(e, \"Inloggen met Google mislukt of geannuleerd.\"));\n } finally {\n setBusy(false);\n }\n }\n\n if (!configured) {\n return (\n <div className=\"xicms-card\" style={{ padding: \"1.5rem\" }}>\n <p>\n Firebase is nog niet geconfigureerd. Vul de <code>NEXT_PUBLIC_FIREBASE_*</code>\n omgevingsvariabelen in om inloggen aan te zetten.\n </p>\n </div>\n );\n }\n\n if (user) {\n return (\n <div className=\"xicms-card\" style={{ padding: \"1.5rem\" }}>\n <p>Je bent ingelogd als <strong>{user.email}</strong>.</p>\n </div>\n );\n }\n\n async function handleSubmit(e: FormEvent) {\n e.preventDefault();\n setError(null);\n setBusy(true);\n try {\n await signIn(email, password);\n onSuccess?.();\n } catch (e) {\n setError(authErrorMessage(e, \"Inloggen mislukt. Controleer je e-mailadres en wachtwoord.\"));\n } finally {\n setBusy(false);\n }\n }\n\n return (\n <form onSubmit={handleSubmit} className=\"xicms-card\" style={{ padding: \"1.5rem\", display: \"grid\", gap: \"0.75rem\", maxWidth: 360 }}>\n <h2 style={{ margin: 0 }}>Inloggen</h2>\n <label style={{ display: \"grid\", gap: \"0.25rem\" }}>\n <span>E-mailadres</span>\n <input\n type=\"email\"\n required\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n autoComplete=\"email\"\n className=\"xicms-input\"\n />\n </label>\n <label style={{ display: \"grid\", gap: \"0.25rem\" }}>\n <span>Wachtwoord</span>\n <input\n type=\"password\"\n required\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n autoComplete=\"current-password\"\n className=\"xicms-input\"\n />\n </label>\n {error && <p style={{ color: \"#dc2626\", margin: 0 }}>{error}</p>}\n <button type=\"submit\" disabled={busy} className=\"xicms-btn\">\n {busy ? \"Bezig…\" : \"Inloggen\"}\n </button>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: \"0.5rem\", opacity: 0.6, fontSize: 13 }}>\n <span style={{ flex: 1, height: 1, background: \"currentColor\" }} /> of{\" \"}\n <span style={{ flex: 1, height: 1, background: \"currentColor\" }} />\n </div>\n <button type=\"button\" onClick={handleGoogle} disabled={busy} className=\"xicms-btn xicms-btn--ghost\">\n Inloggen met Google\n </button>\n </form>\n );\n}\n"],"mappings":"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","names":["e"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Rollen binnen XiCMS. */
|
|
2
|
+
export type Role = "superadmin" | "siteadmin" | "editor";
|
|
3
|
+
/** Vorm van de custom claims op het Firebase ID-token. */
|
|
4
|
+
export interface XiClaims {
|
|
5
|
+
superadmin?: boolean;
|
|
6
|
+
roles?: Record<string, "siteadmin" | "editor">;
|
|
7
|
+
}
|
|
8
|
+
/** De effectieve rol van een gebruiker op één site. */
|
|
9
|
+
export declare function roleForSite(claims: XiClaims | null | undefined, siteId: string): Role | null;
|
|
10
|
+
/** Mag deze rol content bewerken? */
|
|
11
|
+
export declare function canEdit(role: Role | null): boolean;
|
|
12
|
+
/** Mag deze rol de site beheren (instellingen, accounts voor zover toegestaan)? */
|
|
13
|
+
export declare function canManageSite(role: Role | null): boolean;
|
|
14
|
+
/** Alleen de superadmin mag sites en accounts aanmaken. */
|
|
15
|
+
export declare function isSuperadmin(claims: XiClaims | null | undefined): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
function roleForSite(claims, siteId) {
|
|
2
|
+
if (!claims) return null;
|
|
3
|
+
if (claims.superadmin) return "superadmin";
|
|
4
|
+
return claims.roles?.[siteId] ?? null;
|
|
5
|
+
}
|
|
6
|
+
function canEdit(role) {
|
|
7
|
+
return role === "superadmin" || role === "siteadmin" || role === "editor";
|
|
8
|
+
}
|
|
9
|
+
function canManageSite(role) {
|
|
10
|
+
return role === "superadmin" || role === "siteadmin";
|
|
11
|
+
}
|
|
12
|
+
function isSuperadmin(claims) {
|
|
13
|
+
return Boolean(claims?.superadmin);
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
canEdit,
|
|
17
|
+
canManageSite,
|
|
18
|
+
isSuperadmin,
|
|
19
|
+
roleForSite
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=roles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/auth/roles.ts"],"sourcesContent":["/** Rollen binnen XiCMS. */\nexport type Role = \"superadmin\" | \"siteadmin\" | \"editor\";\n\n/** Vorm van de custom claims op het Firebase ID-token. */\nexport interface XiClaims {\n superadmin?: boolean;\n roles?: Record<string, \"siteadmin\" | \"editor\">;\n}\n\n/** De effectieve rol van een gebruiker op één site. */\nexport function roleForSite(claims: XiClaims | null | undefined, siteId: string): Role | null {\n if (!claims) return null;\n if (claims.superadmin) return \"superadmin\";\n return claims.roles?.[siteId] ?? null;\n}\n\n/** Mag deze rol content bewerken? */\nexport function canEdit(role: Role | null): boolean {\n return role === \"superadmin\" || role === \"siteadmin\" || role === \"editor\";\n}\n\n/** Mag deze rol de site beheren (instellingen, accounts voor zover toegestaan)? */\nexport function canManageSite(role: Role | null): boolean {\n return role === \"superadmin\" || role === \"siteadmin\";\n}\n\n/** Alleen de superadmin mag sites en accounts aanmaken. */\nexport function isSuperadmin(claims: XiClaims | null | undefined): boolean {\n return Boolean(claims?.superadmin);\n}\n"],"mappings":"AAUO,SAAS,YAAY,QAAqC,QAA6B;AAC5F,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,OAAO,WAAY,QAAO;AAC9B,SAAO,OAAO,QAAQ,MAAM,KAAK;AACnC;AAGO,SAAS,QAAQ,MAA4B;AAClD,SAAO,SAAS,gBAAgB,SAAS,eAAe,SAAS;AACnE;AAGO,SAAS,cAAc,MAA4B;AACxD,SAAO,SAAS,gBAAgB,SAAS;AAC3C;AAGO,SAAS,aAAa,QAA8C;AACzE,SAAO,QAAQ,QAAQ,UAAU;AACnC;","names":[]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Component, type ErrorInfo, type ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Vangt een fout in één block-Render op zodat één kapot block nooit de hele pagina sloopt
|
|
4
|
+
* (foolproof: een editor kan geen content maken die de live site breekt).
|
|
5
|
+
*
|
|
6
|
+
* - `mode="view"` (publiek): faalt stil — rendert niets, layout blijft intact.
|
|
7
|
+
* - `mode="preview"` (editor): toont een zichtbare foutkaart zodat de bewerker de breuk ziet.
|
|
8
|
+
*
|
|
9
|
+
* Let op de RSC-grens: een error-boundary is per definitie client (`componentDidCatch`). Daarom is
|
|
10
|
+
* dit een client-leaf die per block om de Render heen wordt gezet; `BlockRenderer` zelf blijft
|
|
11
|
+
* server-importeerbaar. Een fout die al tijdens server-rendering wordt gegooid valt buiten een
|
|
12
|
+
* client-boundary — maar `validateBlocks` haalt schema-ongeldige content er server-side al uit, en
|
|
13
|
+
* de meeste runtime-render-fouten (kapotte URL, client-hook) gebeuren client-side en worden hier
|
|
14
|
+
* dus wél gevangen.
|
|
15
|
+
*/
|
|
16
|
+
interface Props {
|
|
17
|
+
mode: "view" | "preview";
|
|
18
|
+
type: string;
|
|
19
|
+
blockKey: string;
|
|
20
|
+
children: ReactNode;
|
|
21
|
+
}
|
|
22
|
+
interface State {
|
|
23
|
+
error: Error | null;
|
|
24
|
+
}
|
|
25
|
+
export declare class BlockErrorBoundary extends Component<Props, State> {
|
|
26
|
+
state: State;
|
|
27
|
+
static getDerivedStateFromError(error: Error): State;
|
|
28
|
+
componentDidCatch(error: Error, info: ErrorInfo): void;
|
|
29
|
+
componentDidUpdate(prev: Props): void;
|
|
30
|
+
render(): ReactNode;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Component } from "react";
|
|
4
|
+
class BlockErrorBoundary extends Component {
|
|
5
|
+
state = { error: null };
|
|
6
|
+
static getDerivedStateFromError(error) {
|
|
7
|
+
return { error };
|
|
8
|
+
}
|
|
9
|
+
componentDidCatch(error, info) {
|
|
10
|
+
if (typeof console !== "undefined") {
|
|
11
|
+
console.error(`[xicms] block "${this.props.type}" gaf een renderfout:`, error, info);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
componentDidUpdate(prev) {
|
|
15
|
+
if (this.state.error && (prev.blockKey !== this.props.blockKey || prev.type !== this.props.type)) {
|
|
16
|
+
this.setState({ error: null });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
render() {
|
|
20
|
+
if (this.state.error) {
|
|
21
|
+
if (this.props.mode === "preview") {
|
|
22
|
+
return /* @__PURE__ */ jsxs("div", { className: "xicms-block-error", role: "alert", children: [
|
|
23
|
+
/* @__PURE__ */ jsxs("strong", { children: [
|
|
24
|
+
"Blok \u201C",
|
|
25
|
+
this.props.type,
|
|
26
|
+
"\u201D kon niet worden weergegeven."
|
|
27
|
+
] }),
|
|
28
|
+
/* @__PURE__ */ jsx("span", { children: this.state.error.message })
|
|
29
|
+
] });
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
return this.props.children;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
BlockErrorBoundary
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=BlockErrorBoundary.js.map
|