@xiwebdesign/xicms-core 7.31.0 → 8.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/XiCMSProvider.js +23 -7
- package/dist/XiCMSProvider.js.map +1 -1
- package/dist/admin/AdminApp.js +175 -93
- package/dist/admin/AdminApp.js.map +1 -1
- package/dist/admin/AdminHelp.d.ts +1 -1
- package/dist/admin/AdminHelp.js +44 -36
- package/dist/admin/AdminHelp.js.map +1 -1
- package/dist/admin/CoreVersion.d.ts +6 -3
- package/dist/admin/CoreVersion.js +60 -24
- package/dist/admin/CoreVersion.js.map +1 -1
- package/dist/admin/NewsEditor.d.ts +7 -0
- package/dist/admin/NewsEditor.js +117 -75
- package/dist/admin/NewsEditor.js.map +1 -1
- package/dist/admin/SuperadminPanel.js +1 -1
- package/dist/admin/SuperadminPanel.js.map +1 -1
- package/dist/admin/ZoekPalet.d.ts +15 -0
- package/dist/admin/ZoekPalet.js +123 -0
- package/dist/admin/ZoekPalet.js.map +1 -0
- package/dist/admin/blockEditors/GenericEditor.js +36 -18
- package/dist/admin/blockEditors/GenericEditor.js.map +1 -1
- package/dist/admin/blockEditors/StyleEditor.js +37 -3
- package/dist/admin/blockEditors/StyleEditor.js.map +1 -1
- package/dist/admin/blockEditors/SymbolRefEditor.js +44 -2
- package/dist/admin/blockEditors/SymbolRefEditor.js.map +1 -1
- package/dist/admin/blockEditors/index.js +154 -73
- package/dist/admin/blockEditors/index.js.map +1 -1
- package/dist/admin/customBlocks/CustomBlocksPanel.js +18 -7
- package/dist/admin/customBlocks/CustomBlocksPanel.js.map +1 -1
- package/dist/admin/dashboard/DashboardPanel.js +26 -16
- package/dist/admin/dashboard/DashboardPanel.js.map +1 -1
- package/dist/admin/editors/RichTextEditor.d.ts +4 -3
- package/dist/admin/editors/RichTextEditor.js +19 -60
- package/dist/admin/editors/RichTextEditor.js.map +1 -1
- package/dist/admin/editors/fontSizeExtension.js +7 -1
- package/dist/admin/editors/fontSizeExtension.js.map +1 -1
- package/dist/admin/editors/slashMenu.d.ts +18 -0
- package/dist/admin/editors/slashMenu.js +34 -0
- package/dist/admin/editors/slashMenu.js.map +1 -0
- package/dist/admin/editors/useRijkeTekst.d.ts +48 -0
- package/dist/admin/editors/useRijkeTekst.js +310 -0
- package/dist/admin/editors/useRijkeTekst.js.map +1 -0
- package/dist/admin/forms/FormsPanel.d.ts +11 -2
- package/dist/admin/forms/FormsPanel.js +218 -65
- package/dist/admin/forms/FormsPanel.js.map +1 -1
- package/dist/admin/lib/blokInfo.d.ts +17 -0
- package/dist/admin/lib/blokInfo.js +80 -0
- package/dist/admin/lib/blokInfo.js.map +1 -0
- package/dist/admin/lib/foutNaarMens.d.ts +1 -0
- package/dist/admin/lib/foutNaarMens.js +22 -0
- package/dist/admin/lib/foutNaarMens.js.map +1 -0
- package/dist/admin/lib/gewisteVelden.d.ts +13 -0
- package/dist/admin/lib/gewisteVelden.js +31 -0
- package/dist/admin/lib/gewisteVelden.js.map +1 -0
- package/dist/admin/lib/heroHulp.d.ts +11 -0
- package/dist/admin/lib/heroHulp.js +18 -0
- package/dist/admin/lib/heroHulp.js.map +1 -0
- package/dist/admin/lib/kleur.js +2 -3
- package/dist/admin/lib/kleur.js.map +1 -1
- package/dist/admin/lib/leesbaarheid.d.ts +34 -0
- package/dist/admin/lib/leesbaarheid.js +82 -0
- package/dist/admin/lib/leesbaarheid.js.map +1 -0
- package/dist/admin/lib/lengteAdvies.d.ts +1 -0
- package/dist/admin/lib/lengteAdvies.js +16 -0
- package/dist/admin/lib/lengteAdvies.js.map +1 -0
- package/dist/admin/lib/mediaLijst.d.ts +26 -0
- package/dist/admin/lib/mediaLijst.js +42 -0
- package/dist/admin/lib/mediaLijst.js.map +1 -0
- package/dist/admin/lib/mediaUsage.d.ts +12 -1
- package/dist/admin/lib/mediaUsage.js +43 -33
- package/dist/admin/lib/mediaUsage.js.map +1 -1
- package/dist/admin/lib/menuHulp.d.ts +16 -0
- package/dist/admin/lib/menuHulp.js +41 -0
- package/dist/admin/lib/menuHulp.js.map +1 -0
- package/dist/admin/lib/onopgeslagen.d.ts +10 -0
- package/dist/admin/lib/onopgeslagen.js +35 -0
- package/dist/admin/lib/onopgeslagen.js.map +1 -0
- package/dist/admin/lib/pagesClient.d.ts +55 -2
- package/dist/admin/lib/pagesClient.js +126 -28
- package/dist/admin/lib/pagesClient.js.map +1 -1
- package/dist/admin/lib/paginaGebruik.d.ts +56 -0
- package/dist/admin/lib/paginaGebruik.js +109 -0
- package/dist/admin/lib/paginaGebruik.js.map +1 -0
- package/dist/admin/lib/publicatieCheck.d.ts +25 -0
- package/dist/admin/lib/publicatieCheck.js +114 -0
- package/dist/admin/lib/publicatieCheck.js.map +1 -0
- package/dist/admin/lib/scrollNaarDetail.d.ts +6 -0
- package/dist/admin/lib/scrollNaarDetail.js +20 -0
- package/dist/admin/lib/scrollNaarDetail.js.map +1 -0
- package/dist/admin/lib/symbolsClient.d.ts +10 -0
- package/dist/admin/lib/symbolsClient.js +14 -1
- package/dist/admin/lib/symbolsClient.js.map +1 -1
- package/dist/admin/lib/tekstVeld.d.ts +8 -0
- package/dist/admin/lib/tekstVeld.js +10 -0
- package/dist/admin/lib/tekstVeld.js.map +1 -0
- package/dist/admin/lib/useIsSuperadmin.d.ts +2 -0
- package/dist/admin/lib/useIsSuperadmin.js +13 -0
- package/dist/admin/lib/useIsSuperadmin.js.map +1 -0
- package/dist/admin/lib/veldTaal.d.ts +14 -0
- package/dist/admin/lib/veldTaal.js +248 -0
- package/dist/admin/lib/veldTaal.js.map +1 -0
- package/dist/admin/lib/versieVergelijk.d.ts +17 -0
- package/dist/admin/lib/versieVergelijk.js +24 -0
- package/dist/admin/lib/versieVergelijk.js.map +1 -0
- package/dist/admin/lib/zodFields.js +22 -4
- package/dist/admin/lib/zodFields.js.map +1 -1
- package/dist/admin/lib/zodFout.d.ts +9 -0
- package/dist/admin/lib/zodFout.js +63 -0
- package/dist/admin/lib/zodFout.js.map +1 -0
- package/dist/admin/media/MediaManager.js +162 -32
- package/dist/admin/media/MediaManager.js.map +1 -1
- package/dist/admin/media/MediaPicker.d.ts +3 -0
- package/dist/admin/media/MediaPicker.js +84 -31
- package/dist/admin/media/MediaPicker.js.map +1 -1
- package/dist/admin/menu/MenuPanel.js +144 -83
- package/dist/admin/menu/MenuPanel.js.map +1 -1
- package/dist/admin/navContext.d.ts +3 -0
- package/dist/admin/navContext.js +13 -1
- package/dist/admin/navContext.js.map +1 -1
- package/dist/admin/pages/PageEditor.js +146 -30
- package/dist/admin/pages/PageEditor.js.map +1 -1
- package/dist/admin/pages/PagesPanel.js +215 -61
- package/dist/admin/pages/PagesPanel.js.map +1 -1
- package/dist/admin/pages/StarterKiezer.d.ts +12 -0
- package/dist/admin/pages/StarterKiezer.js +113 -0
- package/dist/admin/pages/StarterKiezer.js.map +1 -0
- package/dist/admin/panelRegistry.d.ts +1 -1
- package/dist/admin/panelRegistry.js +2 -1
- package/dist/admin/panelRegistry.js.map +1 -1
- package/dist/admin/patterns/PatternInserter.js +1 -1
- package/dist/admin/patterns/PatternInserter.js.map +1 -1
- package/dist/admin/settings/BedrijfInstellingen.d.ts +20 -0
- package/dist/admin/settings/BedrijfInstellingen.js +159 -0
- package/dist/admin/settings/BedrijfInstellingen.js.map +1 -0
- package/dist/admin/settings/MailTemplates.js +5 -4
- package/dist/admin/settings/MailTemplates.js.map +1 -1
- package/dist/admin/settings/SettingsPanel.js +120 -78
- package/dist/admin/settings/SettingsPanel.js.map +1 -1
- package/dist/admin/settings/SnapshotsCard.js +4 -4
- package/dist/admin/settings/SnapshotsCard.js.map +1 -1
- package/dist/admin/settings/VorigeTerugzetten.d.ts +15 -0
- package/dist/admin/settings/VorigeTerugzetten.js +89 -0
- package/dist/admin/settings/VorigeTerugzetten.js.map +1 -0
- package/dist/admin/shop/CategoriesTab.js +6 -4
- package/dist/admin/shop/CategoriesTab.js.map +1 -1
- package/dist/admin/shop/DiscountsTab.js +4 -2
- package/dist/admin/shop/DiscountsTab.js.map +1 -1
- package/dist/admin/shop/OrdersTab.js +57 -19
- package/dist/admin/shop/OrdersTab.js.map +1 -1
- package/dist/admin/shop/ProductPaneel.js +2 -1
- package/dist/admin/shop/ProductPaneel.js.map +1 -1
- package/dist/admin/shop/ProductsTab.js +40 -19
- package/dist/admin/shop/ProductsTab.js.map +1 -1
- package/dist/admin/shop/ShopPanel.js +13 -1
- package/dist/admin/shop/ShopPanel.js.map +1 -1
- package/dist/admin/shop/ShopSettingsTab.js +56 -17
- package/dist/admin/shop/ShopSettingsTab.js.map +1 -1
- package/dist/admin/shop/productClient.js +7 -3
- package/dist/admin/shop/productClient.js.map +1 -1
- package/dist/admin/stats/NietGevonden.d.ts +10 -0
- package/dist/admin/stats/NietGevonden.js +78 -0
- package/dist/admin/stats/NietGevonden.js.map +1 -0
- package/dist/admin/stats/StatsPanel.js +6 -1
- package/dist/admin/stats/StatsPanel.js.map +1 -1
- package/dist/admin/stats/lib.d.ts +2 -0
- package/dist/admin/stats/lib.js.map +1 -1
- package/dist/admin/ui/ConfirmDialog.js +10 -1
- package/dist/admin/ui/ConfirmDialog.js.map +1 -1
- package/dist/admin/ui/Icon.d.ts +34 -0
- package/dist/admin/ui/Icon.js +44 -0
- package/dist/admin/ui/Icon.js.map +1 -0
- package/dist/admin/ui/ImageField.d.ts +15 -0
- package/dist/admin/ui/ImageField.js +67 -0
- package/dist/admin/ui/ImageField.js.map +1 -0
- package/dist/admin/ui/KleurVeld.d.ts +25 -0
- package/dist/admin/ui/KleurVeld.js +76 -0
- package/dist/admin/ui/KleurVeld.js.map +1 -0
- package/dist/admin/ui/LaadStaat.d.ts +21 -0
- package/dist/admin/ui/LaadStaat.js +43 -0
- package/dist/admin/ui/LaadStaat.js.map +1 -0
- package/dist/admin/ui/LeesbaarheidPaneel.d.ts +10 -0
- package/dist/admin/ui/LeesbaarheidPaneel.js +68 -0
- package/dist/admin/ui/LeesbaarheidPaneel.js.map +1 -0
- package/dist/admin/ui/LinkDialog.d.ts +22 -0
- package/dist/admin/ui/LinkDialog.js +131 -0
- package/dist/admin/ui/LinkDialog.js.map +1 -0
- package/dist/admin/ui/LinkVeld.d.ts +14 -0
- package/dist/admin/ui/LinkVeld.js +49 -0
- package/dist/admin/ui/LinkVeld.js.map +1 -0
- package/dist/admin/ui/PreviewLinkResultaat.d.ts +14 -0
- package/dist/admin/ui/PreviewLinkResultaat.js +52 -0
- package/dist/admin/ui/PreviewLinkResultaat.js.map +1 -0
- package/dist/admin/ui/charts.js +5 -5
- package/dist/admin/ui/charts.js.map +1 -1
- package/dist/admin/ui/primitives.d.ts +16 -6
- package/dist/admin/ui/primitives.js +98 -26
- package/dist/admin/ui/primitives.js.map +1 -1
- package/dist/admin/ui/select.js +5 -2
- package/dist/admin/ui/select.js.map +1 -1
- package/dist/admin/ui/switch.d.ts +3 -1
- package/dist/admin/ui/switch.js +6 -3
- package/dist/admin/ui/switch.js.map +1 -1
- package/dist/admin/ui/toast.d.ts +2 -1
- package/dist/admin/ui/toast.js +17 -11
- package/dist/admin/ui/toast.js.map +1 -1
- package/dist/admin/visual/BlokKiezer.d.ts +34 -0
- package/dist/admin/visual/BlokKiezer.js +225 -0
- package/dist/admin/visual/BlokKiezer.js.map +1 -0
- package/dist/admin/visual/InlineGereedschap.d.ts +52 -0
- package/dist/admin/visual/InlineGereedschap.js +356 -0
- package/dist/admin/visual/InlineGereedschap.js.map +1 -0
- package/dist/admin/visual/InlineRijkeTekst.d.ts +23 -0
- package/dist/admin/visual/InlineRijkeTekst.js +152 -0
- package/dist/admin/visual/InlineRijkeTekst.js.map +1 -0
- package/dist/admin/visual/VisualEditor.js +273 -125
- package/dist/admin/visual/VisualEditor.js.map +1 -1
- package/dist/admin.css +1 -1
- package/dist/auth/AuthProvider.js +1 -0
- package/dist/auth/AuthProvider.js.map +1 -1
- package/dist/bedrijf/bedrijf.d.ts +44 -0
- package/dist/bedrijf/bedrijf.js +125 -0
- package/dist/bedrijf/bedrijf.js.map +1 -0
- package/dist/blocks/BlockRenderer.js +11 -1
- package/dist/blocks/BlockRenderer.js.map +1 -1
- package/dist/blocks/blockStyle.d.ts +7 -0
- package/dist/blocks/blockStyle.js +16 -3
- package/dist/blocks/blockStyle.js.map +1 -1
- package/dist/blocks/builtinSchemas.js +6 -0
- package/dist/blocks/builtinSchemas.js.map +1 -1
- package/dist/blocks/builtins/AgendaRender.d.ts +22 -0
- package/dist/blocks/builtins/AgendaRender.js +69 -9
- package/dist/blocks/builtins/AgendaRender.js.map +1 -1
- package/dist/blocks/builtins/Availability.d.ts +1 -0
- package/dist/blocks/builtins/Availability.js +23 -0
- package/dist/blocks/builtins/Availability.js.map +1 -0
- package/dist/blocks/builtins/AvailabilityRender.d.ts +12 -0
- package/dist/blocks/builtins/AvailabilityRender.js +105 -0
- package/dist/blocks/builtins/AvailabilityRender.js.map +1 -0
- package/dist/blocks/builtins/BeforeAfterRender.js +4 -3
- package/dist/blocks/builtins/BeforeAfterRender.js.map +1 -1
- package/dist/blocks/builtins/Booking.js +1 -1
- package/dist/blocks/builtins/Booking.js.map +1 -1
- package/dist/blocks/builtins/BreadcrumbsRender.d.ts +4 -3
- package/dist/blocks/builtins/BreadcrumbsRender.js +14 -8
- package/dist/blocks/builtins/BreadcrumbsRender.js.map +1 -1
- package/dist/blocks/builtins/ColumnsRender.d.ts +1 -1
- package/dist/blocks/builtins/ColumnsRender.js +3 -1
- package/dist/blocks/builtins/ColumnsRender.js.map +1 -1
- package/dist/blocks/builtins/Contact.js +1 -1
- package/dist/blocks/builtins/Contact.js.map +1 -1
- package/dist/blocks/builtins/ContactRender.d.ts +1 -1
- package/dist/blocks/builtins/ContactRender.js +37 -8
- package/dist/blocks/builtins/ContactRender.js.map +1 -1
- package/dist/blocks/builtins/Cta.js +1 -1
- package/dist/blocks/builtins/Cta.js.map +1 -1
- package/dist/blocks/builtins/CtaRender.d.ts +1 -1
- package/dist/blocks/builtins/CtaRender.js +5 -4
- package/dist/blocks/builtins/CtaRender.js.map +1 -1
- package/dist/blocks/builtins/Embed.js +1 -1
- package/dist/blocks/builtins/Embed.js.map +1 -1
- package/dist/blocks/builtins/FormBlock.js +1 -0
- package/dist/blocks/builtins/FormBlock.js.map +1 -1
- package/dist/blocks/builtins/FormBlockRender.d.ts +6 -0
- package/dist/blocks/builtins/FormBlockRender.js +88 -16
- package/dist/blocks/builtins/FormBlockRender.js.map +1 -1
- package/dist/blocks/builtins/GalleryRender.js +3 -2
- package/dist/blocks/builtins/GalleryRender.js.map +1 -1
- package/dist/blocks/builtins/Hero.js +1 -1
- package/dist/blocks/builtins/Hero.js.map +1 -1
- package/dist/blocks/builtins/HeroRender.js +7 -3
- package/dist/blocks/builtins/HeroRender.js.map +1 -1
- package/dist/blocks/builtins/OpenNowBadge.d.ts +4 -2
- package/dist/blocks/builtins/OpenNowBadge.js +6 -6
- package/dist/blocks/builtins/OpenNowBadge.js.map +1 -1
- package/dist/blocks/builtins/OpeningHoursRender.d.ts +8 -2
- package/dist/blocks/builtins/OpeningHoursRender.js +27 -6
- package/dist/blocks/builtins/OpeningHoursRender.js.map +1 -1
- package/dist/blocks/builtins/PriceList.d.ts +1 -0
- package/dist/blocks/builtins/PriceList.js +25 -0
- package/dist/blocks/builtins/PriceList.js.map +1 -0
- package/dist/blocks/builtins/PriceListRender.d.ts +17 -0
- package/dist/blocks/builtins/PriceListRender.js +87 -0
- package/dist/blocks/builtins/PriceListRender.js.map +1 -0
- package/dist/blocks/builtins/RichTextBlockRender.js +6 -1
- package/dist/blocks/builtins/RichTextBlockRender.js.map +1 -1
- package/dist/blocks/builtins/SectionHeading.d.ts +1 -0
- package/dist/blocks/builtins/SectionHeading.js +14 -0
- package/dist/blocks/builtins/SectionHeading.js.map +1 -0
- package/dist/blocks/builtins/SectionHeadingRender.d.ts +7 -0
- package/dist/blocks/builtins/SectionHeadingRender.js +14 -0
- package/dist/blocks/builtins/SectionHeadingRender.js.map +1 -0
- package/dist/blocks/builtins/SliderRender.js +3 -2
- package/dist/blocks/builtins/SliderRender.js.map +1 -1
- package/dist/blocks/builtins/SymbolRef.js +1 -1
- package/dist/blocks/builtins/SymbolRef.js.map +1 -1
- package/dist/blocks/builtins/TeamRender.js +6 -1
- package/dist/blocks/builtins/TeamRender.js.map +1 -1
- package/dist/blocks/builtins/Testimonials.js +3 -1
- package/dist/blocks/builtins/Testimonials.js.map +1 -1
- package/dist/blocks/builtins/TestimonialsRender.js +4 -0
- package/dist/blocks/builtins/TestimonialsRender.js.map +1 -1
- package/dist/blocks/builtins/WhatsappChatRender.d.ts +1 -1
- package/dist/blocks/builtins/WhatsappChatRender.js +3 -1
- package/dist/blocks/builtins/WhatsappChatRender.js.map +1 -1
- package/dist/blocks/builtins/Widgets.js +2 -2
- package/dist/blocks/builtins/Widgets.js.map +1 -1
- package/dist/blocks/builtins/WidgetsRender.js +3 -1
- package/dist/blocks/builtins/WidgetsRender.js.map +1 -1
- package/dist/blocks/builtins/consentEmbed.js +26 -9
- package/dist/blocks/builtins/consentEmbed.js.map +1 -1
- package/dist/blocks/builtins/index.d.ts +3 -0
- package/dist/blocks/builtins/index.js +6 -0
- package/dist/blocks/builtins/index.js.map +1 -1
- package/dist/blocks/builtins/lib/autoComplete.d.ts +7 -0
- package/dist/blocks/builtins/lib/autoComplete.js +20 -0
- package/dist/blocks/builtins/lib/autoComplete.js.map +1 -0
- package/dist/blocks/builtins/lib/bookingUrl.d.ts +2 -2
- package/dist/blocks/builtins/lib/bookingUrl.js +1 -1
- package/dist/blocks/builtins/lib/bookingUrl.js.map +1 -1
- package/dist/blocks/builtins/lib/ical.d.ts +24 -0
- package/dist/blocks/builtins/lib/ical.js +82 -0
- package/dist/blocks/builtins/lib/ical.js.map +1 -0
- package/dist/blocks/builtins/lib/vandaag.d.ts +8 -0
- package/dist/blocks/builtins/lib/vandaag.js +22 -0
- package/dist/blocks/builtins/lib/vandaag.js.map +1 -0
- package/dist/blocks/registry.d.ts +6 -0
- package/dist/blocks/registry.js.map +1 -1
- package/dist/blocks/ui/index.js +1 -0
- package/dist/blocks/ui/index.js.map +1 -1
- package/dist/components/EditBar.js +2 -4
- package/dist/components/EditBar.js.map +1 -1
- package/dist/components/News.js +26 -18
- package/dist/components/News.js.map +1 -1
- package/dist/components/PageView.d.ts +7 -1
- package/dist/components/PageView.js +17 -0
- package/dist/components/PageView.js.map +1 -1
- package/dist/components/ResponsiveImage.js +18 -20
- package/dist/components/ResponsiveImage.js.map +1 -1
- package/dist/components/onderOffset.d.ts +13 -0
- package/dist/components/onderOffset.js +74 -0
- package/dist/components/onderOffset.js.map +1 -0
- package/dist/components/site/Bedrijfsnummers.d.ts +9 -0
- package/dist/components/site/Bedrijfsnummers.js +12 -0
- package/dist/components/site/Bedrijfsnummers.js.map +1 -0
- package/dist/components/site/SiteFooter.d.ts +6 -1
- package/dist/components/site/SiteFooter.js +62 -9
- package/dist/components/site/SiteFooter.js.map +1 -1
- package/dist/components/site/SiteHeader.d.ts +15 -0
- package/dist/components/site/SiteHeader.js +70 -0
- package/dist/components/site/SiteHeader.js.map +1 -0
- package/dist/components/site/SiteNav.d.ts +2 -0
- package/dist/components/site/SiteNav.js +29 -5
- package/dist/components/site/SiteNav.js.map +1 -1
- package/dist/demo/DemoStudio.js +2 -2
- package/dist/demo/DemoStudio.js.map +1 -1
- package/dist/demo/demoStore.d.ts +13 -0
- package/dist/demo/demoStore.js +36 -0
- package/dist/demo/demoStore.js.map +1 -1
- package/dist/editor/EditorProvider.js +15 -4
- package/dist/editor/EditorProvider.js.map +1 -1
- package/dist/firebase/functionsClient.js +5 -1
- package/dist/firebase/functionsClient.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +20 -1
- package/dist/index.js.map +1 -1
- package/dist/media/ImageContext.d.ts +17 -0
- package/dist/media/ImageContext.js +65 -20
- package/dist/media/ImageContext.js.map +1 -1
- package/dist/media/altInHtml.d.ts +9 -0
- package/dist/media/altInHtml.js +17 -0
- package/dist/media/altInHtml.js.map +1 -0
- package/dist/media/srcset.d.ts +12 -0
- package/dist/media/srcset.js +13 -0
- package/dist/media/srcset.js.map +1 -0
- package/dist/modules/builtins.d.ts +1 -0
- package/dist/modules/builtins.js +22 -9
- package/dist/modules/builtins.js.map +1 -1
- package/dist/modules/consent/ConsentBanner.d.ts +5 -0
- package/dist/modules/consent/ConsentBanner.js +8 -7
- package/dist/modules/consent/ConsentBanner.js.map +1 -1
- package/dist/modules/consent/standaard.d.ts +12 -0
- package/dist/modules/consent/standaard.js +10 -0
- package/dist/modules/consent/standaard.js.map +1 -0
- package/dist/modules/consent/useConsent.d.ts +4 -0
- package/dist/modules/consent/useConsent.js +30 -1
- package/dist/modules/consent/useConsent.js.map +1 -1
- package/dist/modules/shop/blocks/Cart.js +5 -3
- package/dist/modules/shop/blocks/Cart.js.map +1 -1
- package/dist/modules/shop/blocks/Checkout.js +56 -35
- package/dist/modules/shop/blocks/Checkout.js.map +1 -1
- package/dist/modules/shop/blocks/Donation.js +4 -4
- package/dist/modules/shop/blocks/Donation.js.map +1 -1
- package/dist/modules/shop/blocks/ProductDetail.js +19 -3
- package/dist/modules/shop/blocks/ProductDetail.js.map +1 -1
- package/dist/modules/shop/blocks/ProductGrid.d.ts +3 -1
- package/dist/modules/shop/blocks/ProductGrid.js +8 -4
- package/dist/modules/shop/blocks/ProductGrid.js.map +1 -1
- package/dist/modules/shop/blocks/ShopCategories.js +4 -3
- package/dist/modules/shop/blocks/ShopCategories.js.map +1 -1
- package/dist/modules/shop/lib.d.ts +4 -0
- package/dist/modules/shop/lib.js +14 -0
- package/dist/modules/shop/lib.js.map +1 -1
- package/dist/modules/shop/settings.d.ts +3 -0
- package/dist/modules/shop/settings.js +14 -0
- package/dist/modules/shop/settings.js.map +1 -1
- package/dist/modules/shop/strings.d.ts +4 -0
- package/dist/modules/shop/strings.js +8 -0
- package/dist/modules/shop/strings.js.map +1 -1
- package/dist/next/envCheck.d.ts +42 -0
- package/dist/next/envCheck.js +106 -0
- package/dist/next/envCheck.js.map +1 -0
- package/dist/next/index.cjs +185 -2
- package/dist/next/index.d.ts +2 -0
- package/dist/next/index.js +8 -1
- package/dist/next/index.js.map +1 -1
- package/dist/next/securityHeaders.d.ts +36 -0
- package/dist/next/securityHeaders.js +74 -0
- package/dist/next/securityHeaders.js.map +1 -0
- package/dist/patterns/builtins.d.ts +7 -0
- package/dist/patterns/builtins.js +138 -3
- package/dist/patterns/builtins.js.map +1 -1
- package/dist/releaseNotes.js +148 -0
- package/dist/releaseNotes.js.map +1 -1
- package/dist/render/PublishedPage.d.ts +2 -1
- package/dist/render/PublishedPage.js +10 -4
- package/dist/render/PublishedPage.js.map +1 -1
- package/dist/schema/blocks.d.ts +473 -0
- package/dist/schema/blocks.js +105 -5
- package/dist/schema/blocks.js.map +1 -1
- package/dist/schema/business.d.ts +118 -0
- package/dist/schema/business.js +75 -0
- package/dist/schema/business.js.map +1 -0
- package/dist/schema/fields.d.ts +2 -1
- package/dist/schema/fields.js.map +1 -1
- package/dist/schema/forms.d.ts +18 -0
- package/dist/schema/forms.js +18 -3
- package/dist/schema/forms.js.map +1 -1
- package/dist/schema/mail.d.ts +10 -0
- package/dist/schema/mail.js +7 -0
- package/dist/schema/mail.js.map +1 -1
- package/dist/schema/settings.d.ts +103 -0
- package/dist/schema/settings.js +24 -3
- package/dist/schema/settings.js.map +1 -1
- package/dist/schema/shop.d.ts +2 -0
- package/dist/schema/shop.js +4 -0
- package/dist/schema/shop.js.map +1 -1
- package/dist/schema/shopGeld.d.ts +1 -0
- package/dist/schema/shopGeld.js +1 -0
- package/dist/schema/shopGeld.js.map +1 -1
- package/dist/schema/siteConfig.d.ts +69 -1
- package/dist/schema/siteConfig.js +7 -2
- package/dist/schema/siteConfig.js.map +1 -1
- package/dist/server/blockData.d.ts +1 -1
- package/dist/server/blockData.js +74 -18
- package/dist/server/blockData.js.map +1 -1
- package/dist/server/cspRapport.d.ts +20 -0
- package/dist/server/cspRapport.js +56 -0
- package/dist/server/cspRapport.js.map +1 -0
- package/dist/server/fonts.d.ts +3 -0
- package/dist/server/fonts.js +39 -0
- package/dist/server/fonts.js.map +1 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.js +8 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/sanitize.js +2 -0
- package/dist/server/sanitize.js.map +1 -1
- package/dist/settings/useSiteSettings.js +4 -1
- package/dist/settings/useSiteSettings.js.map +1 -1
- package/dist/starters/starters.d.ts +69 -0
- package/dist/starters/starters.js +171 -0
- package/dist/starters/starters.js.map +1 -0
- package/dist/styles.css +1181 -212
- package/dist/theme/color.d.ts +10 -0
- package/dist/theme/color.js +17 -1
- package/dist/theme/color.js.map +1 -1
- package/dist/theme/fontFallback.d.ts +23 -0
- package/dist/theme/fontFallback.js +58 -0
- package/dist/theme/fontFallback.js.map +1 -0
- package/dist/theme/fonts.d.ts +11 -0
- package/dist/theme/fonts.js +16 -0
- package/dist/theme/fonts.js.map +1 -0
- package/dist/version.js +1 -1
- package/package.json +5 -4
package/dist/admin/NewsEditor.js
CHANGED
|
@@ -1,42 +1,85 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useRef, useState } from "react";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
4
|
import { getFirebaseApp } from "../firebase/client.js";
|
|
5
5
|
import { callable } from "../firebase/functionsClient.js";
|
|
6
6
|
import { useSiteConfig } from "../config.js";
|
|
7
7
|
import { useNews } from "../components/News.js";
|
|
8
|
-
import { uploadImage } from "./lib/upload.js";
|
|
9
8
|
import { slugify } from "./lib/slugify.js";
|
|
10
9
|
import { useConfirm } from "./ui/ConfirmDialog.js";
|
|
11
10
|
import { useToast } from "./ui/toast.js";
|
|
12
|
-
import { Button, Card, Field, Input, Badge } from "./ui/primitives.js";
|
|
11
|
+
import { Button, Card, Field, Input, Textarea, Badge, LegeStaat } from "./ui/primitives.js";
|
|
12
|
+
import { Switch } from "./ui/switch.js";
|
|
13
|
+
import { ImageField } from "./ui/ImageField.js";
|
|
14
|
+
import { LaadStaat } from "./ui/LaadStaat.js";
|
|
15
|
+
import { LeesbaarheidPaneel } from "./ui/LeesbaarheidPaneel.js";
|
|
16
|
+
import { useOnopgeslagen } from "./lib/onopgeslagen.js";
|
|
17
|
+
import { neemOpenVerzoek } from "./navContext.js";
|
|
13
18
|
import { RichTextEditor } from "./editors/RichTextEditor.js";
|
|
14
19
|
const empty = { title: "", excerpt: "", body: "", bodyHtml: "", coverUrl: "", date: "", published: false };
|
|
20
|
+
const vandaagIso = () => (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
21
|
+
function nlDatum(iso) {
|
|
22
|
+
if (!iso) return "";
|
|
23
|
+
const d = /* @__PURE__ */ new Date(`${iso.slice(0, 10)}T12:00:00`);
|
|
24
|
+
return Number.isNaN(d.getTime()) ? iso : d.toLocaleDateString("nl-NL", { day: "numeric", month: "long", year: "numeric" });
|
|
25
|
+
}
|
|
26
|
+
function nieuwsToestand(a, vandaag = vandaagIso()) {
|
|
27
|
+
if (!a.published) return "concept";
|
|
28
|
+
return a.date && a.date.slice(0, 10) > vandaag ? "ingepland" : "zichtbaar";
|
|
29
|
+
}
|
|
15
30
|
function NewsEditor() {
|
|
16
31
|
const { siteId } = useSiteConfig();
|
|
17
|
-
const { articles } = useNews({ onlyPublished: false });
|
|
32
|
+
const { articles, loading } = useNews({ onlyPublished: false });
|
|
18
33
|
const confirm = useConfirm();
|
|
19
34
|
const { toast } = useToast();
|
|
20
35
|
const [form, setForm] = useState(empty);
|
|
21
36
|
const [editingId, setEditingId] = useState(null);
|
|
22
37
|
const [busy, setBusy] = useState(false);
|
|
23
38
|
const [formulierOpen, setFormulierOpen] = useState(false);
|
|
24
|
-
const [
|
|
25
|
-
const
|
|
39
|
+
const [basis, setBasis] = useState(empty);
|
|
40
|
+
const gewijzigd = formulierOpen && JSON.stringify(form) !== JSON.stringify(basis);
|
|
41
|
+
useOnopgeslagen(gewijzigd);
|
|
42
|
+
const formRef = useRef(null);
|
|
43
|
+
const [teOpenen, setTeOpenen] = useState(() => neemOpenVerzoek("news"));
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!teOpenen) return;
|
|
46
|
+
const a = articles.find((x) => x.id === teOpenen);
|
|
47
|
+
if (!a) return;
|
|
48
|
+
setTeOpenen(null);
|
|
49
|
+
void loadIntoForm(a);
|
|
50
|
+
}, [teOpenen, articles]);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (formulierOpen) formRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
53
|
+
}, [formulierOpen, editingId]);
|
|
54
|
+
async function magWeg() {
|
|
55
|
+
if (!gewijzigd) return true;
|
|
56
|
+
return confirm({
|
|
57
|
+
title: "Wijzigingen weggooien?",
|
|
58
|
+
description: "Je hebt dit artikel aangepast maar nog niet opgeslagen.",
|
|
59
|
+
confirmLabel: "Weggooien",
|
|
60
|
+
tone: "danger"
|
|
61
|
+
});
|
|
62
|
+
}
|
|
26
63
|
function reset() {
|
|
27
64
|
setForm(empty);
|
|
65
|
+
setBasis(empty);
|
|
28
66
|
setEditingId(null);
|
|
29
67
|
setFormulierOpen(false);
|
|
30
68
|
}
|
|
31
|
-
function
|
|
32
|
-
|
|
69
|
+
async function sluiten() {
|
|
70
|
+
if (await magWeg()) reset();
|
|
71
|
+
}
|
|
72
|
+
async function nieuwArtikel() {
|
|
73
|
+
if (!await magWeg()) return;
|
|
74
|
+
const nieuw = { ...empty, date: vandaagIso() };
|
|
75
|
+
setForm(nieuw);
|
|
76
|
+
setBasis(nieuw);
|
|
33
77
|
setEditingId(null);
|
|
34
78
|
setFormulierOpen(true);
|
|
35
79
|
}
|
|
36
|
-
function loadIntoForm(a) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
setForm({
|
|
80
|
+
async function loadIntoForm(a) {
|
|
81
|
+
if (!await magWeg()) return;
|
|
82
|
+
const geladen = {
|
|
40
83
|
title: a.title,
|
|
41
84
|
excerpt: a.excerpt ?? "",
|
|
42
85
|
body: a.body,
|
|
@@ -44,7 +87,11 @@ function NewsEditor() {
|
|
|
44
87
|
coverUrl: a.coverUrl ?? "",
|
|
45
88
|
date: a.date,
|
|
46
89
|
published: a.published
|
|
47
|
-
}
|
|
90
|
+
};
|
|
91
|
+
setEditingId(a.id);
|
|
92
|
+
setFormulierOpen(true);
|
|
93
|
+
setForm(geladen);
|
|
94
|
+
setBasis(geladen);
|
|
48
95
|
}
|
|
49
96
|
async function save() {
|
|
50
97
|
if (!form.title.trim()) {
|
|
@@ -96,88 +143,83 @@ function NewsEditor() {
|
|
|
96
143
|
if (editingId === id) reset();
|
|
97
144
|
toast({ title: "Artikel verwijderd", tone: "success" });
|
|
98
145
|
}
|
|
99
|
-
async function onCover(file) {
|
|
100
|
-
setCoverBusy(true);
|
|
101
|
-
try {
|
|
102
|
-
const { url } = await uploadImage(siteId, file);
|
|
103
|
-
setForm((prev) => ({ ...prev, coverUrl: url }));
|
|
104
|
-
toast({ title: "Foto toegevoegd", description: "Automatisch verkleind voor het web.", tone: "success" });
|
|
105
|
-
} catch (e) {
|
|
106
|
-
toast({ title: "Upload mislukt", description: e.message, tone: "error" });
|
|
107
|
-
} finally {
|
|
108
|
-
setCoverBusy(false);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
146
|
return /* @__PURE__ */ jsxs("div", { className: "grid gap-4", children: [
|
|
112
|
-
formulierOpen && /* @__PURE__ */ jsxs(Card, { children: [
|
|
113
|
-
/* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center justify-between", children: [
|
|
147
|
+
formulierOpen && /* @__PURE__ */ jsx("div", { ref: formRef, className: "scroll-mt-24", children: /* @__PURE__ */ jsxs(Card, { children: [
|
|
148
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center justify-between gap-3", children: [
|
|
114
149
|
/* @__PURE__ */ jsx("h3", { className: "text-base font-semibold", children: editingId ? "Artikel bewerken" : "Nieuw artikel" }),
|
|
115
|
-
/* @__PURE__ */
|
|
150
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
151
|
+
gewijzigd && /* @__PURE__ */ jsx("span", { className: "text-sm text-amber-700", role: "status", children: "\u2022 Niet opgeslagen" }),
|
|
152
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", onClick: () => void sluiten(), children: "Sluiten" })
|
|
153
|
+
] })
|
|
116
154
|
] }),
|
|
117
|
-
/* @__PURE__ */ jsxs("div", { className: "grid gap-
|
|
118
|
-
/* @__PURE__ */
|
|
119
|
-
|
|
120
|
-
/* @__PURE__ */ jsx(Field, { label: "Coverfoto", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
121
|
-
/* @__PURE__ */ jsx(Input, { value: form.coverUrl, onChange: (e) => setForm({ ...form, coverUrl: e.target.value }), placeholder: "URL of upload \u2192" }),
|
|
122
|
-
/* @__PURE__ */ jsx(Button, { type: "button", variant: "secondary", size: "sm", disabled: coverBusy, onClick: () => coverInputRef.current?.click(), children: coverBusy ? "Uploaden\u2026" : "Upload" }),
|
|
155
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-6 lg:grid-cols-[minmax(0,1fr)_300px]", children: [
|
|
156
|
+
/* @__PURE__ */ jsxs("div", { className: "grid content-start gap-3", children: [
|
|
157
|
+
/* @__PURE__ */ jsx("label", { className: "sr-only", htmlFor: "xicms-nieuws-titel", children: "Titel" }),
|
|
123
158
|
/* @__PURE__ */ jsx(
|
|
124
|
-
"
|
|
159
|
+
"textarea",
|
|
125
160
|
{
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (f) void onCover(f);
|
|
133
|
-
e.target.value = "";
|
|
134
|
-
}
|
|
161
|
+
id: "xicms-nieuws-titel",
|
|
162
|
+
rows: 1,
|
|
163
|
+
value: form.title,
|
|
164
|
+
onChange: (e) => setForm({ ...form, title: e.target.value }),
|
|
165
|
+
placeholder: "Titel van het artikel",
|
|
166
|
+
className: "xicms-admin-native w-full resize-none border-0 bg-transparent p-0 text-2xl font-semibold leading-tight outline-none [field-sizing:content] placeholder:text-slate-400"
|
|
135
167
|
}
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
168
|
+
),
|
|
169
|
+
/* @__PURE__ */ jsx(
|
|
170
|
+
RichTextEditor,
|
|
171
|
+
{
|
|
172
|
+
siteId,
|
|
173
|
+
value: form.bodyHtml || (form.body ? `<p>${form.body.split(/\n{2,}/).join("</p><p>")}</p>` : ""),
|
|
174
|
+
onChange: (html) => setForm((f) => ({ ...f, bodyHtml: html }))
|
|
175
|
+
}
|
|
176
|
+
),
|
|
177
|
+
/* @__PURE__ */ jsx(LeesbaarheidPaneel, { html: form.bodyHtml || form.body })
|
|
146
178
|
] }),
|
|
147
|
-
/* @__PURE__ */
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
179
|
+
/* @__PURE__ */ jsxs("aside", { className: "grid content-start gap-4 rounded-xl border border-slate-200 p-4", style: { background: "var(--color-background-muted)" }, "aria-label": "Artikelgegevens", children: [
|
|
180
|
+
/* @__PURE__ */ jsx(
|
|
181
|
+
Field,
|
|
182
|
+
{
|
|
183
|
+
label: "Op de site",
|
|
184
|
+
hint: !form.published ? "Uit = concept: alleen hier zichtbaar." : nieuwsToestand(form) === "ingepland" ? `Ingepland: verschijnt op ${nlDatum(form.date)}.` : "Aan = zichtbaar zodra je opslaat.",
|
|
185
|
+
children: /* @__PURE__ */ jsx(Switch, { checked: form.published, onCheckedChange: (v) => setForm({ ...form, published: v }), label: "Zichtbaar op de site" })
|
|
186
|
+
}
|
|
187
|
+
),
|
|
188
|
+
/* @__PURE__ */ jsx(Field, { label: "Datum", hint: "Een datum in de toekomst = het artikel verschijnt pas op die dag.", children: /* @__PURE__ */ jsx(Input, { type: "date", value: form.date, onChange: (e) => setForm({ ...form, date: e.target.value }) }) }),
|
|
189
|
+
/* @__PURE__ */ jsx(Field, { label: "Coverfoto", children: /* @__PURE__ */ jsx(ImageField, { siteId, value: form.coverUrl || void 0, onChange: (url) => setForm((f) => ({ ...f, coverUrl: url ?? "" })) }) }),
|
|
190
|
+
/* @__PURE__ */ jsx(Field, { label: "Korte samenvatting", hint: `Verschijnt in de nieuwslijst en in Google (${form.excerpt.length}/155 tekens aanbevolen).`, children: /* @__PURE__ */ jsx(Textarea, { rows: 3, value: form.excerpt, onChange: (e) => setForm({ ...form, excerpt: e.target.value }) }) }),
|
|
191
|
+
/* @__PURE__ */ jsx(Button, { loading: busy, disabled: busy, onClick: () => void save(), className: "w-full", children: form.published ? "Opslaan \u2014 direct live" : "Opslaan als concept" })
|
|
192
|
+
] })
|
|
156
193
|
] })
|
|
157
|
-
] }),
|
|
194
|
+
] }) }),
|
|
158
195
|
/* @__PURE__ */ jsxs(Card, { children: [
|
|
159
196
|
/* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center justify-between", children: [
|
|
160
197
|
/* @__PURE__ */ jsx("h3", { className: "text-base font-semibold", children: "Artikelen" }),
|
|
161
|
-
/* @__PURE__ */ jsx(Button, { size: "sm", onClick: nieuwArtikel, children: "+ Nieuw artikel" })
|
|
198
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", onClick: () => void nieuwArtikel(), children: "+ Nieuw artikel" })
|
|
162
199
|
] }),
|
|
163
|
-
articles.length === 0 ? /* @__PURE__ */ jsx(
|
|
164
|
-
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-sm", children: [
|
|
200
|
+
/* @__PURE__ */ jsx(LaadStaat, { laden: loading && articles.length === 0, fout: null, wat: "artikelen", children: articles.length === 0 ? /* @__PURE__ */ jsx(LegeStaat, { titel: "Nog geen nieuws", tekst: "Schrijf je eerste bericht: een nieuwtje, een actie of een terugblik. Het verschijnt vanzelf op je site." }) : /* @__PURE__ */ jsx("div", { className: "grid gap-2", children: articles.map((a) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 rounded-lg border border-slate-200 p-3", children: [
|
|
201
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-wrap items-center gap-2 text-sm", children: [
|
|
165
202
|
/* @__PURE__ */ jsx("span", { className: "font-medium", children: a.title }),
|
|
166
|
-
/* @__PURE__ */ jsxs("span", { className: "text-slate-
|
|
203
|
+
/* @__PURE__ */ jsxs("span", { className: "text-slate-500", children: [
|
|
167
204
|
"\xB7 ",
|
|
168
|
-
a.date
|
|
205
|
+
nlDatum(a.date)
|
|
169
206
|
] }),
|
|
170
|
-
|
|
207
|
+
(() => {
|
|
208
|
+
const t = nieuwsToestand(a);
|
|
209
|
+
return /* @__PURE__ */ jsx(Badge, { tone: t === "zichtbaar" ? "green" : t === "ingepland" ? "slate" : "amber", children: t });
|
|
210
|
+
})()
|
|
171
211
|
] }),
|
|
172
|
-
/* @__PURE__ */ jsxs("span", { className: "flex gap-2", children: [
|
|
173
|
-
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => loadIntoForm(a), children: "
|
|
174
|
-
/* @__PURE__ */ jsx(Button, { variant: "
|
|
212
|
+
/* @__PURE__ */ jsxs("span", { className: "flex shrink-0 gap-2", children: [
|
|
213
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => void loadIntoForm(a), children: "Bewerken" }),
|
|
214
|
+
/* @__PURE__ */ jsx(Button, { variant: "dangerGhost", size: "sm", onClick: () => void remove(a.id, a.title), children: "Verwijderen" })
|
|
175
215
|
] })
|
|
176
|
-
] }, a.id)) })
|
|
216
|
+
] }, a.id)) }) })
|
|
177
217
|
] })
|
|
178
218
|
] });
|
|
179
219
|
}
|
|
180
220
|
export {
|
|
181
|
-
NewsEditor
|
|
221
|
+
NewsEditor,
|
|
222
|
+
nieuwsToestand,
|
|
223
|
+
nlDatum
|
|
182
224
|
};
|
|
183
225
|
//# sourceMappingURL=NewsEditor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/admin/NewsEditor.tsx"],"sourcesContent":["\"use client\";\n\nimport { useRef, useState } from \"react\";\nimport { getFirebaseApp } from \"../firebase/client\";\nimport { callable } from \"../firebase/functionsClient\";\nimport { useSiteConfig } from \"../config\";\nimport { useNews } from \"../components/News\";\nimport { uploadImage } from \"./lib/upload\";\nimport { slugify } from \"./lib/slugify\";\nimport { useConfirm } from \"./ui/ConfirmDialog\";\nimport { useToast } from \"./ui/toast\";\nimport { Button, Card, Field, Input, Textarea, Badge } from \"./ui/primitives\";\nimport { RichTextEditor } from \"./editors/RichTextEditor\";\nimport type { NewsArticle } from \"../content/types\";\n\nconst empty = { title: \"\", excerpt: \"\", body: \"\", bodyHtml: \"\", coverUrl: \"\", date: \"\", published: false };\n\n/**\n * Beheer van nieuwsartikelen — zelfde look & feel als de rest van /beheer.\n *\n * Schrijft via callables, niet rechtstreeks naar Firestore. Nieuws was het laatste publieke\n * contenttype dat de client zelf schreef; daardoor was er geen plek waar de cache-invalidatie kon\n * plaatsvinden en verscheen een gepubliceerd artikel niet op een live-reads-site (SITE-AUDIT §11).\n * De callable saneert, auditeert en verklaart de nieuws-tag ongeldig — net als bij symbolen.\n */\nexport function NewsEditor() {\n const { siteId } = useSiteConfig();\n const { articles } = useNews({ onlyPublished: false });\n const confirm = useConfirm();\n const { toast } = useToast();\n const [form, setForm] = useState<typeof empty>(empty);\n const [editingId, setEditingId] = useState<string | null>(null);\n const [busy, setBusy] = useState(false);\n /**\n * Staat het formulier open?\n *\n * Stond altijd open, en dat zette de lijst met bestaande artikelen onder een schermvullend\n * \"Nieuw artikel\"-blok. Wie alleen een typefout wilde herstellen moest eerst langs een leeg\n * formulier scrollen dat hij niet had gevraagd. Nu is de LIJST het uitgangspunt — dat is wat je\n * meestal komt doen — en opent het formulier pas als je er zelf om vraagt: via \"Nieuw artikel\",\n * of door op Bewerk te klikken bij een bestaand artikel.\n */\n const [formulierOpen, setFormulierOpen] = useState(false);\n const [coverBusy, setCoverBusy] = useState(false);\n const coverInputRef = useRef<HTMLInputElement>(null);\n\n function reset() {\n setForm(empty);\n setEditingId(null);\n setFormulierOpen(false);\n }\n\n /** Leeg formulier openen voor een nieuw artikel. */\n function nieuwArtikel() {\n setForm(empty);\n setEditingId(null);\n setFormulierOpen(true);\n }\n\n function loadIntoForm(a: NewsArticle) {\n setEditingId(a.id);\n setFormulierOpen(true);\n setForm({\n title: a.title,\n excerpt: a.excerpt ?? \"\",\n body: a.body,\n bodyHtml: a.bodyHtml ?? \"\",\n coverUrl: a.coverUrl ?? \"\",\n date: a.date,\n published: a.published,\n });\n }\n\n async function save() {\n if (!form.title.trim()) {\n toast({ title: \"Titel ontbreekt\", description: \"Geef het artikel een titel.\", tone: \"error\" });\n return;\n }\n setBusy(true);\n try {\n const app = getFirebaseApp();\n if (!app) throw new Error(\"Firebase is niet geconfigureerd.\");\n await callable(app, \"saveNewsArticle\")({\n siteId,\n ...(editingId ? { articleId: editingId } : {}),\n title: form.title.trim(),\n slug: slugify(form.title),\n excerpt: form.excerpt.trim(),\n body: form.body,\n // Rich text: de editor haalt het al door DOMPurify, de callable saneert server-side, en de\n // weergave sanitized nogmaals bij het renderen — drie lagen, elk met een eigen reden.\n bodyHtml: form.bodyHtml,\n coverUrl: form.coverUrl.trim(),\n date: form.date || new Date().toISOString().slice(0, 10),\n published: form.published,\n });\n toast({ title: editingId ? \"Artikel bijgewerkt\" : \"Artikel opgeslagen\", tone: \"success\" });\n reset();\n } catch (e) {\n toast({ title: \"Opslaan mislukt\", description: (e as Error).message, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n async function remove(id: string, title: string) {\n const app = getFirebaseApp();\n if (!app) return;\n const ok = await confirm({\n title: \"Artikel verwijderen?\",\n description: `“${title}” wordt definitief verwijderd.`,\n confirmLabel: \"Verwijderen\",\n tone: \"danger\",\n });\n if (!ok) return;\n try {\n await callable(app, \"deleteNewsArticle\")({ siteId, articleId: id });\n } catch (e) {\n toast({ title: \"Verwijderen mislukt\", description: (e as Error).message, tone: \"error\" });\n return;\n }\n if (editingId === id) reset();\n toast({ title: \"Artikel verwijderd\", tone: \"success\" });\n }\n\n async function onCover(file: File) {\n setCoverBusy(true);\n try {\n const { url } = await uploadImage(siteId, file);\n setForm((prev) => ({ ...prev, coverUrl: url }));\n toast({ title: \"Foto toegevoegd\", description: \"Automatisch verkleind voor het web.\", tone: \"success\" });\n } catch (e) {\n toast({ title: \"Upload mislukt\", description: (e as Error).message, tone: \"error\" });\n } finally {\n setCoverBusy(false);\n }\n }\n\n return (\n <div className=\"grid gap-4\">\n {formulierOpen && (\n <Card>\n <div className=\"mb-3 flex items-center justify-between\">\n <h3 className=\"text-base font-semibold\">{editingId ? \"Artikel bewerken\" : \"Nieuw artikel\"}</h3>\n <Button variant=\"secondary\" size=\"sm\" onClick={reset}>Sluiten</Button>\n </div>\n <div className=\"grid gap-3\">\n <Field label=\"Titel\">\n <Input value={form.title} onChange={(e) => setForm({ ...form, title: e.target.value })} placeholder=\"Kop van het artikel\" />\n </Field>\n <Field label=\"Korte samenvatting\" hint=\"Verschijnt in de nieuwslijst.\">\n <Input value={form.excerpt} onChange={(e) => setForm({ ...form, excerpt: e.target.value })} />\n </Field>\n <Field label=\"Coverfoto\">\n <div className=\"flex items-center gap-2\">\n <Input value={form.coverUrl} onChange={(e) => setForm({ ...form, coverUrl: e.target.value })} placeholder=\"URL of upload →\" />\n <Button type=\"button\" variant=\"secondary\" size=\"sm\" disabled={coverBusy} onClick={() => coverInputRef.current?.click()}>\n {coverBusy ? \"Uploaden…\" : \"Upload\"}\n </Button>\n <input\n ref={coverInputRef}\n type=\"file\"\n accept=\"image/*\"\n hidden\n onChange={(e) => {\n const f = e.target.files?.[0];\n if (f) void onCover(f);\n e.target.value = \"\";\n }}\n />\n </div>\n </Field>\n {form.coverUrl && (\n // eslint-disable-next-line @next/next/no-img-element\n <img src={form.coverUrl} alt=\"\" className=\"h-32 w-full rounded-lg object-cover\" />\n )}\n <div className=\"grid gap-3 md:grid-cols-2\">\n <Field label=\"Datum\">\n <Input type=\"date\" value={form.date} onChange={(e) => setForm({ ...form, date: e.target.value })} />\n </Field>\n <Field label=\"Zichtbaarheid\" hint=\"Aangevinkt = direct zichtbaar op de site; uit = concept.\">\n <label className=\"flex h-9 items-center gap-2 text-sm\">\n <input type=\"checkbox\" checked={form.published} onChange={(e) => setForm({ ...form, published: e.target.checked })} />\n Gepubliceerd\n </label>\n </Field>\n </div>\n <Field label=\"Tekst\" hint=\"Met opmaak (koppen, lijsten, links, afbeeldingen). Bestaande artikelen zonder opmaak blijven gewoon werken.\">\n <RichTextEditor\n siteId={siteId}\n value={form.bodyHtml || (form.body ? `<p>${form.body.split(/\\n{2,}/).join(\"</p><p>\")}</p>` : \"\")}\n onChange={(html) => setForm((f) => ({ ...f, bodyHtml: html }))}\n />\n </Field>\n <div>\n <Button disabled={busy} onClick={() => void save()}>\n {busy ? \"Opslaan…\" : editingId ? \"Wijzigingen opslaan\" : \"Artikel opslaan\"}\n </Button>\n </div>\n </div>\n </Card>\n )}\n\n <Card>\n <div className=\"mb-3 flex items-center justify-between\">\n <h3 className=\"text-base font-semibold\">Artikelen</h3>\n {/* De enige plek waar een leeg formulier vandaan komt. Dat is het verschil met eerder:\n je krijgt het als je erom vraagt, niet als openingsbeeld. */}\n <Button size=\"sm\" onClick={nieuwArtikel}>+ Nieuw artikel</Button>\n </div>\n {articles.length === 0 ? (\n <p className=\"text-sm text-slate-500\">Nog geen artikelen. Klik op “+ Nieuw artikel” om te beginnen.</p>\n ) : (\n <div className=\"grid gap-2\">\n {articles.map((a) => (\n <div key={a.id} className=\"flex items-center justify-between gap-3 rounded-lg border border-slate-200 p-3\">\n <span className=\"flex items-center gap-2 text-sm\">\n <span className=\"font-medium\">{a.title}</span>\n <span className=\"text-slate-400\">· {a.date}</span>\n {a.published ? <Badge tone=\"green\">zichtbaar</Badge> : <Badge tone=\"amber\">concept</Badge>}\n </span>\n <span className=\"flex gap-2\">\n <Button variant=\"ghost\" size=\"sm\" onClick={() => loadIntoForm(a)}>Bewerk</Button>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => void remove(a.id, a.title)}>Verwijder</Button>\n </span>\n </div>\n ))}\n </div>\n )}\n </Card>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/admin/NewsEditor.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { getFirebaseApp } from \"../firebase/client\";\nimport { callable } from \"../firebase/functionsClient\";\nimport { useSiteConfig } from \"../config\";\nimport { useNews } from \"../components/News\";\nimport { slugify } from \"./lib/slugify\";\nimport { useConfirm } from \"./ui/ConfirmDialog\";\nimport { useToast } from \"./ui/toast\";\nimport { Button, Card, Field, Input, Textarea, Badge, LegeStaat } from \"./ui/primitives\";\nimport { Switch } from \"./ui/switch\";\nimport { ImageField } from \"./ui/ImageField\";\nimport { LaadStaat } from \"./ui/LaadStaat\";\nimport { LeesbaarheidPaneel } from \"./ui/LeesbaarheidPaneel\";\nimport { useOnopgeslagen } from \"./lib/onopgeslagen\";\nimport { neemOpenVerzoek } from \"./navContext\";\nimport { RichTextEditor } from \"./editors/RichTextEditor\";\nimport type { NewsArticle } from \"../content/types\";\n\nconst empty = { title: \"\", excerpt: \"\", body: \"\", bodyHtml: \"\", coverUrl: \"\", date: \"\", published: false };\n\nconst vandaagIso = () => new Date().toISOString().slice(0, 10);\n\n/** \"2026-10-18\" → \"18 oktober 2026\" (8.2: de lijst toonde de ruwe ISO-datum). */\nexport function nlDatum(iso: string | undefined): string {\n if (!iso) return \"\";\n const d = new Date(`${iso.slice(0, 10)}T12:00:00`);\n return Number.isNaN(d.getTime()) ? iso : d.toLocaleDateString(\"nl-NL\", { day: \"numeric\", month: \"long\", year: \"numeric\" });\n}\n\n/** Zichtbaar, ingepland (gepubliceerd maar met een datum in de toekomst) of concept. */\nexport function nieuwsToestand(a: { published: boolean; date?: string }, vandaag = vandaagIso()): \"zichtbaar\" | \"ingepland\" | \"concept\" {\n if (!a.published) return \"concept\";\n return a.date && a.date.slice(0, 10) > vandaag ? \"ingepland\" : \"zichtbaar\";\n}\n\n/**\n * Beheer van nieuwsartikelen — zelfde look & feel als de rest van /beheer.\n *\n * Schrijft via callables, niet rechtstreeks naar Firestore. Nieuws was het laatste publieke\n * contenttype dat de client zelf schreef; daardoor was er geen plek waar de cache-invalidatie kon\n * plaatsvinden en verscheen een gepubliceerd artikel niet op een live-reads-site (SITE-AUDIT §11).\n * De callable saneert, auditeert en verklaart de nieuws-tag ongeldig — net als bij symbolen.\n */\nexport function NewsEditor() {\n const { siteId } = useSiteConfig();\n const { articles, loading } = useNews({ onlyPublished: false });\n const confirm = useConfirm();\n const { toast } = useToast();\n const [form, setForm] = useState<typeof empty>(empty);\n const [editingId, setEditingId] = useState<string | null>(null);\n const [busy, setBusy] = useState(false);\n /**\n * Staat het formulier open?\n *\n * Stond altijd open, en dat zette de lijst met bestaande artikelen onder een schermvullend\n * \"Nieuw artikel\"-blok. Wie alleen een typefout wilde herstellen moest eerst langs een leeg\n * formulier scrollen dat hij niet had gevraagd. Nu is de LIJST het uitgangspunt — dat is wat je\n * meestal komt doen — en opent het formulier pas als je er zelf om vraagt: via \"Nieuw artikel\",\n * of door op Bewerk te klikken bij een bestaand artikel.\n */\n const [formulierOpen, setFormulierOpen] = useState(false);\n /** Het formulier zoals het geopend werd: wijkt `form` daarvan af, dan is er onopgeslagen werk. */\n const [basis, setBasis] = useState<typeof empty>(empty);\n const gewijzigd = formulierOpen && JSON.stringify(form) !== JSON.stringify(basis);\n useOnopgeslagen(gewijzigd);\n const formRef = useRef<HTMLDivElement>(null);\n // 8.4: geopend vanuit het zoekpalet — dat artikel meteen in het formulier zetten.\n const [teOpenen, setTeOpenen] = useState<string | null>(() => neemOpenVerzoek(\"news\"));\n useEffect(() => {\n if (!teOpenen) return;\n const a = articles.find((x) => x.id === teOpenen);\n if (!a) return;\n setTeOpenen(null);\n void loadIntoForm(a);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [teOpenen, articles]);\n\n // Het formulier opent bóven de lijst; wie onderaan op \"Bewerk\" klikte zag niets gebeuren (8.2).\n useEffect(() => {\n if (formulierOpen) formRef.current?.scrollIntoView({ behavior: \"smooth\", block: \"start\" });\n }, [formulierOpen, editingId]);\n\n /** Vraagt bevestiging als er onopgeslagen werk weg zou gaan. */\n async function magWeg(): Promise<boolean> {\n if (!gewijzigd) return true;\n return confirm({\n title: \"Wijzigingen weggooien?\",\n description: \"Je hebt dit artikel aangepast maar nog niet opgeslagen.\",\n confirmLabel: \"Weggooien\",\n tone: \"danger\",\n });\n }\n\n function reset() {\n setForm(empty);\n setBasis(empty);\n setEditingId(null);\n setFormulierOpen(false);\n }\n\n async function sluiten() {\n if (await magWeg()) reset();\n }\n\n /** Leeg formulier openen voor een nieuw artikel. */\n async function nieuwArtikel() {\n if (!(await magWeg())) return;\n const nieuw = { ...empty, date: vandaagIso() };\n setForm(nieuw);\n setBasis(nieuw);\n setEditingId(null);\n setFormulierOpen(true);\n }\n\n async function loadIntoForm(a: NewsArticle) {\n if (!(await magWeg())) return;\n const geladen = {\n title: a.title,\n excerpt: a.excerpt ?? \"\",\n body: a.body,\n bodyHtml: a.bodyHtml ?? \"\",\n coverUrl: a.coverUrl ?? \"\",\n date: a.date,\n published: a.published,\n };\n setEditingId(a.id);\n setFormulierOpen(true);\n setForm(geladen);\n setBasis(geladen);\n }\n\n async function save() {\n if (!form.title.trim()) {\n toast({ title: \"Titel ontbreekt\", description: \"Geef het artikel een titel.\", tone: \"error\" });\n return;\n }\n setBusy(true);\n try {\n const app = getFirebaseApp();\n if (!app) throw new Error(\"Firebase is niet geconfigureerd.\");\n await callable(app, \"saveNewsArticle\")({\n siteId,\n ...(editingId ? { articleId: editingId } : {}),\n title: form.title.trim(),\n slug: slugify(form.title),\n excerpt: form.excerpt.trim(),\n body: form.body,\n // Rich text: de editor haalt het al door DOMPurify, de callable saneert server-side, en de\n // weergave sanitized nogmaals bij het renderen — drie lagen, elk met een eigen reden.\n bodyHtml: form.bodyHtml,\n coverUrl: form.coverUrl.trim(),\n date: form.date || new Date().toISOString().slice(0, 10),\n published: form.published,\n });\n toast({ title: editingId ? \"Artikel bijgewerkt\" : \"Artikel opgeslagen\", tone: \"success\" });\n reset();\n } catch (e) {\n toast({ title: \"Opslaan mislukt\", description: (e as Error).message, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n async function remove(id: string, title: string) {\n const app = getFirebaseApp();\n if (!app) return;\n const ok = await confirm({\n title: \"Artikel verwijderen?\",\n description: `“${title}” wordt definitief verwijderd.`,\n confirmLabel: \"Verwijderen\",\n tone: \"danger\",\n });\n if (!ok) return;\n try {\n await callable(app, \"deleteNewsArticle\")({ siteId, articleId: id });\n } catch (e) {\n toast({ title: \"Verwijderen mislukt\", description: (e as Error).message, tone: \"error\" });\n return;\n }\n if (editingId === id) reset();\n toast({ title: \"Artikel verwijderd\", tone: \"success\" });\n }\n\n return (\n <div className=\"grid gap-4\">\n {formulierOpen && (\n <div ref={formRef} className=\"scroll-mt-24\">\n <Card>\n <div className=\"mb-3 flex items-center justify-between gap-3\">\n <h3 className=\"text-base font-semibold\">{editingId ? \"Artikel bewerken\" : \"Nieuw artikel\"}</h3>\n <div className=\"flex items-center gap-3\">\n {gewijzigd && <span className=\"text-sm text-amber-700\" role=\"status\">• Niet opgeslagen</span>}\n <Button variant=\"secondary\" size=\"sm\" onClick={() => void sluiten()}>Sluiten</Button>\n </div>\n </div>\n {/* 8.5: schrijven links, de rest rechts — geïnspireerd op de editor van AstraCMS. Op een\n telefoon gewoon onder elkaar. */}\n <div className=\"grid gap-6 lg:grid-cols-[minmax(0,1fr)_300px]\">\n <div className=\"grid content-start gap-3\">\n <label className=\"sr-only\" htmlFor=\"xicms-nieuws-titel\">Titel</label>\n <textarea\n id=\"xicms-nieuws-titel\"\n rows={1}\n value={form.title}\n onChange={(e) => setForm({ ...form, title: e.target.value })}\n placeholder=\"Titel van het artikel\"\n className=\"xicms-admin-native w-full resize-none border-0 bg-transparent p-0 text-2xl font-semibold leading-tight outline-none [field-sizing:content] placeholder:text-slate-400\"\n />\n <RichTextEditor\n siteId={siteId}\n value={form.bodyHtml || (form.body ? `<p>${form.body.split(/\\n{2,}/).join(\"</p><p>\")}</p>` : \"\")}\n onChange={(html) => setForm((f) => ({ ...f, bodyHtml: html }))}\n />\n <LeesbaarheidPaneel html={form.bodyHtml || form.body} />\n </div>\n <aside className=\"grid content-start gap-4 rounded-xl border border-slate-200 p-4\" style={{ background: \"var(--color-background-muted)\" }} aria-label=\"Artikelgegevens\">\n <Field\n label=\"Op de site\"\n hint={\n !form.published\n ? \"Uit = concept: alleen hier zichtbaar.\"\n : nieuwsToestand(form) === \"ingepland\"\n ? `Ingepland: verschijnt op ${nlDatum(form.date)}.`\n : \"Aan = zichtbaar zodra je opslaat.\"\n }\n >\n <Switch checked={form.published} onCheckedChange={(v) => setForm({ ...form, published: v })} label=\"Zichtbaar op de site\" />\n </Field>\n <Field label=\"Datum\" hint=\"Een datum in de toekomst = het artikel verschijnt pas op die dag.\">\n <Input type=\"date\" value={form.date} onChange={(e) => setForm({ ...form, date: e.target.value })} />\n </Field>\n <Field label=\"Coverfoto\">\n <ImageField siteId={siteId} value={form.coverUrl || undefined} onChange={(url) => setForm((f) => ({ ...f, coverUrl: url ?? \"\" }))} />\n </Field>\n <Field label=\"Korte samenvatting\" hint={`Verschijnt in de nieuwslijst en in Google (${form.excerpt.length}/155 tekens aanbevolen).`}>\n <Textarea rows={3} value={form.excerpt} onChange={(e) => setForm({ ...form, excerpt: e.target.value })} />\n </Field>\n <Button loading={busy} disabled={busy} onClick={() => void save()} className=\"w-full\">\n {form.published ? \"Opslaan — direct live\" : \"Opslaan als concept\"}\n </Button>\n </aside>\n </div>\n </Card>\n </div>\n )}\n\n <Card>\n <div className=\"mb-3 flex items-center justify-between\">\n <h3 className=\"text-base font-semibold\">Artikelen</h3>\n {/* De enige plek waar een leeg formulier vandaan komt. Dat is het verschil met eerder:\n je krijgt het als je erom vraagt, niet als openingsbeeld. */}\n <Button size=\"sm\" onClick={() => void nieuwArtikel()}>+ Nieuw artikel</Button>\n </div>\n <LaadStaat laden={loading && articles.length === 0} fout={null} wat=\"artikelen\">\n {articles.length === 0 ? (\n <LegeStaat titel=\"Nog geen nieuws\" tekst=\"Schrijf je eerste bericht: een nieuwtje, een actie of een terugblik. Het verschijnt vanzelf op je site.\" />\n ) : (\n <div className=\"grid gap-2\">\n {articles.map((a) => (\n <div key={a.id} className=\"flex items-center justify-between gap-3 rounded-lg border border-slate-200 p-3\">\n <span className=\"flex min-w-0 flex-wrap items-center gap-2 text-sm\">\n <span className=\"font-medium\">{a.title}</span>\n <span className=\"text-slate-500\">· {nlDatum(a.date)}</span>\n {(() => {\n const t = nieuwsToestand(a);\n return <Badge tone={t === \"zichtbaar\" ? \"green\" : t === \"ingepland\" ? \"slate\" : \"amber\"}>{t}</Badge>;\n })()}\n </span>\n <span className=\"flex shrink-0 gap-2\">\n <Button variant=\"ghost\" size=\"sm\" onClick={() => void loadIntoForm(a)}>Bewerken</Button>\n <Button variant=\"dangerGhost\" size=\"sm\" onClick={() => void remove(a.id, a.title)}>Verwijderen</Button>\n </span>\n </div>\n ))}\n </div>\n )}\n </LaadStaat>\n </Card>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -101,7 +101,7 @@ function SuperadminPanel({ siteId }) {
|
|
|
101
101
|
" \xB7 ",
|
|
102
102
|
a.target,
|
|
103
103
|
" ",
|
|
104
|
-
/* @__PURE__ */ jsxs("span", { className: "text-slate-
|
|
104
|
+
/* @__PURE__ */ jsxs("span", { className: "text-slate-500", children: [
|
|
105
105
|
"(",
|
|
106
106
|
a.actorUid?.slice(0, 6),
|
|
107
107
|
")"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/admin/SuperadminPanel.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { collection, getDocs, limit, orderBy, query, where } from \"firebase/firestore\";\nimport { callable } from \"../firebase/functionsClient\";\nimport { getDb, getFirebaseApp } from \"../firebase/client\";\nimport { Button, Card, Field, Input } from \"./ui/primitives\";\nimport { Select } from \"./ui/select\";\nimport { useToast } from \"./ui/toast\";\n\n/** Callable van dit paneel: zoekt de app op en geeft de standaard `{ message }`-respons terug. */\nfunction superadminCall<T>(name: string) {\n const app = getFirebaseApp();\n if (!app) throw new Error(\"Firebase niet geconfigureerd.\");\n return callable<T, { message?: string }>(app, name);\n}\n\n/** Superadmin: sites/accounts aanmaken, wachtwoord-reset versturen en audit-log inzien. */\nexport function SuperadminPanel({ siteId }: { siteId: string }) {\n const { toast } = useToast();\n const [site, setSite] = useState({ siteId: \"\", name: \"\", domain: \"\" });\n const [account, setAccount] = useState({ siteId: \"\", email: \"\", password: \"\", role: \"siteadmin\" });\n const [resetEmail, setResetEmail] = useState(\"\");\n const [busy, setBusy] = useState(false);\n const [audit, setAudit] = useState<{ id: string; action?: string; target?: string; actorUid?: string }[]>([]);\n\n useEffect(() => {\n const db = getDb();\n if (!db) return;\n const q = query(\n collection(db, \"audit_log\"),\n where(\"siteId\", \"in\", [siteId, \"*\"]),\n orderBy(\"at\", \"desc\"),\n limit(20),\n );\n getDocs(q)\n .then((snap) => setAudit(snap.docs.map((d) => ({ id: d.id, ...(d.data() as Record<string, string>) }))))\n .catch(() => setAudit([]));\n }, [siteId]);\n\n async function run(fn: () => Promise<{ data: { message?: string } }>, ok: string) {\n setBusy(true);\n try {\n const res = await fn();\n toast({ title: res.data.message ?? ok, tone: \"success\" });\n } catch (e) {\n toast({ title: \"Mislukt\", description: (e as Error).message, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n return (\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <Card>\n <h3 className=\"mb-3 text-base font-semibold\">Nieuwe site</h3>\n <div className=\"grid gap-3\">\n <Field label=\"siteId\"><Input value={site.siteId} onChange={(e) => setSite({ ...site, siteId: e.target.value })} placeholder=\"bv. bakkerij-jansen\" /></Field>\n <Field label=\"Naam\"><Input value={site.name} onChange={(e) => setSite({ ...site, name: e.target.value })} /></Field>\n <Field label=\"Domein\"><Input value={site.domain} onChange={(e) => setSite({ ...site, domain: e.target.value })} /></Field>\n <Button disabled={busy || !site.siteId} onClick={() => run(() => superadminCall<typeof site>(\"createSite\")(site), \"Site aangemaakt\")}>\n Site aanmaken\n </Button>\n </div>\n </Card>\n\n <Card>\n <h3 className=\"mb-1 text-base font-semibold\">Beheeraccount</h3>\n <p className=\"mb-3 text-xs text-slate-500\">\n Alleen jij maakt accounts aan; siteadmins kunnen dat niet. De gebruiker krijgt een\n uitnodiging met inloglink gemaild (geen wachtwoord nodig — de login is wachtwoordloos).\n </p>\n <div className=\"grid gap-3\">\n <Field label=\"siteId\"><Input value={account.siteId} onChange={(e) => setAccount({ ...account, siteId: e.target.value })} /></Field>\n <Field label=\"E-mail\"><Input type=\"email\" value={account.email} onChange={(e) => setAccount({ ...account, email: e.target.value })} /></Field>\n <Field label=\"Wachtwoord (optioneel)\" hint=\"Leeg = uitnodiging met inloglink. Alleen invullen als iemand per se met een wachtwoord wil inloggen.\">\n <Input value={account.password} onChange={(e) => setAccount({ ...account, password: e.target.value })} autoComplete=\"off\" />\n </Field>\n <Field label=\"Rol\">\n <Select\n value={account.role}\n onValueChange={(v) => setAccount({ ...account, role: v })}\n options={[\n { value: \"siteadmin\", label: \"siteadmin (volledig)\" },\n { value: \"editor\", label: \"editor (alleen content)\" },\n ]}\n />\n </Field>\n <Button\n disabled={busy || !account.siteId || !account.email}\n onClick={() =>\n run(\n () => superadminCall<Omit<typeof account, \"password\"> & { password?: string }>(\"createSiteAccount\")({\n ...account,\n // Leeg wachtwoord NIET meesturen: de callable maakt dan een wachtwoordloos account en\n // mailt een uitnodiging.\n password: account.password.trim() || undefined,\n }),\n \"Account aangemaakt\",\n )\n }\n >\n {account.password.trim() ? \"Account aanmaken\" : \"Uitnodigen\"}\n </Button>\n </div>\n </Card>\n\n <Card>\n <h3 className=\"mb-3 text-base font-semibold\">Wachtwoord-reset versturen</h3>\n <div className=\"grid gap-3\">\n <Field label=\"E-mail\"><Input type=\"email\" value={resetEmail} onChange={(e) => setResetEmail(e.target.value)} /></Field>\n <Button variant=\"secondary\" disabled={busy || !resetEmail} onClick={() => run(() => superadminCall<{ email: string }>(\"requestPasswordReset\")({ email: resetEmail }), \"Reset-link verstuurd\")}>\n Reset-link versturen\n </Button>\n </div>\n </Card>\n\n <Card>\n <h3 className=\"mb-3 text-base font-semibold\">Audit-log (laatste 20)</h3>\n {audit.length === 0 ? (\n <p className=\"text-sm text-slate-500\">Geen recente acties (of geen rechten/index).</p>\n ) : (\n <ul className=\"text-sm text-slate-600\">\n {audit.map((a) => (\n <li key={a.id} className=\"border-b border-slate-100 py-1 last:border-0\">\n <span className=\"font-medium\">{a.action}</span> · {a.target} <span className=\"text-slate-400\">({a.actorUid?.slice(0, 6)})</span>\n </li>\n ))}\n </ul>\n )}\n </Card>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/admin/SuperadminPanel.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { collection, getDocs, limit, orderBy, query, where } from \"firebase/firestore\";\nimport { callable } from \"../firebase/functionsClient\";\nimport { getDb, getFirebaseApp } from \"../firebase/client\";\nimport { Button, Card, Field, Input } from \"./ui/primitives\";\nimport { Select } from \"./ui/select\";\nimport { useToast } from \"./ui/toast\";\n\n/** Callable van dit paneel: zoekt de app op en geeft de standaard `{ message }`-respons terug. */\nfunction superadminCall<T>(name: string) {\n const app = getFirebaseApp();\n if (!app) throw new Error(\"Firebase niet geconfigureerd.\");\n return callable<T, { message?: string }>(app, name);\n}\n\n/** Superadmin: sites/accounts aanmaken, wachtwoord-reset versturen en audit-log inzien. */\nexport function SuperadminPanel({ siteId }: { siteId: string }) {\n const { toast } = useToast();\n const [site, setSite] = useState({ siteId: \"\", name: \"\", domain: \"\" });\n const [account, setAccount] = useState({ siteId: \"\", email: \"\", password: \"\", role: \"siteadmin\" });\n const [resetEmail, setResetEmail] = useState(\"\");\n const [busy, setBusy] = useState(false);\n const [audit, setAudit] = useState<{ id: string; action?: string; target?: string; actorUid?: string }[]>([]);\n\n useEffect(() => {\n const db = getDb();\n if (!db) return;\n const q = query(\n collection(db, \"audit_log\"),\n where(\"siteId\", \"in\", [siteId, \"*\"]),\n orderBy(\"at\", \"desc\"),\n limit(20),\n );\n getDocs(q)\n .then((snap) => setAudit(snap.docs.map((d) => ({ id: d.id, ...(d.data() as Record<string, string>) }))))\n .catch(() => setAudit([]));\n }, [siteId]);\n\n async function run(fn: () => Promise<{ data: { message?: string } }>, ok: string) {\n setBusy(true);\n try {\n const res = await fn();\n toast({ title: res.data.message ?? ok, tone: \"success\" });\n } catch (e) {\n toast({ title: \"Mislukt\", description: (e as Error).message, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n return (\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <Card>\n <h3 className=\"mb-3 text-base font-semibold\">Nieuwe site</h3>\n <div className=\"grid gap-3\">\n <Field label=\"siteId\"><Input value={site.siteId} onChange={(e) => setSite({ ...site, siteId: e.target.value })} placeholder=\"bv. bakkerij-jansen\" /></Field>\n <Field label=\"Naam\"><Input value={site.name} onChange={(e) => setSite({ ...site, name: e.target.value })} /></Field>\n <Field label=\"Domein\"><Input value={site.domain} onChange={(e) => setSite({ ...site, domain: e.target.value })} /></Field>\n <Button disabled={busy || !site.siteId} onClick={() => run(() => superadminCall<typeof site>(\"createSite\")(site), \"Site aangemaakt\")}>\n Site aanmaken\n </Button>\n </div>\n </Card>\n\n <Card>\n <h3 className=\"mb-1 text-base font-semibold\">Beheeraccount</h3>\n <p className=\"mb-3 text-xs text-slate-500\">\n Alleen jij maakt accounts aan; siteadmins kunnen dat niet. De gebruiker krijgt een\n uitnodiging met inloglink gemaild (geen wachtwoord nodig — de login is wachtwoordloos).\n </p>\n <div className=\"grid gap-3\">\n <Field label=\"siteId\"><Input value={account.siteId} onChange={(e) => setAccount({ ...account, siteId: e.target.value })} /></Field>\n <Field label=\"E-mail\"><Input type=\"email\" value={account.email} onChange={(e) => setAccount({ ...account, email: e.target.value })} /></Field>\n <Field label=\"Wachtwoord (optioneel)\" hint=\"Leeg = uitnodiging met inloglink. Alleen invullen als iemand per se met een wachtwoord wil inloggen.\">\n <Input value={account.password} onChange={(e) => setAccount({ ...account, password: e.target.value })} autoComplete=\"off\" />\n </Field>\n <Field label=\"Rol\">\n <Select\n value={account.role}\n onValueChange={(v) => setAccount({ ...account, role: v })}\n options={[\n { value: \"siteadmin\", label: \"siteadmin (volledig)\" },\n { value: \"editor\", label: \"editor (alleen content)\" },\n ]}\n />\n </Field>\n <Button\n disabled={busy || !account.siteId || !account.email}\n onClick={() =>\n run(\n () => superadminCall<Omit<typeof account, \"password\"> & { password?: string }>(\"createSiteAccount\")({\n ...account,\n // Leeg wachtwoord NIET meesturen: de callable maakt dan een wachtwoordloos account en\n // mailt een uitnodiging.\n password: account.password.trim() || undefined,\n }),\n \"Account aangemaakt\",\n )\n }\n >\n {account.password.trim() ? \"Account aanmaken\" : \"Uitnodigen\"}\n </Button>\n </div>\n </Card>\n\n <Card>\n <h3 className=\"mb-3 text-base font-semibold\">Wachtwoord-reset versturen</h3>\n <div className=\"grid gap-3\">\n <Field label=\"E-mail\"><Input type=\"email\" value={resetEmail} onChange={(e) => setResetEmail(e.target.value)} /></Field>\n <Button variant=\"secondary\" disabled={busy || !resetEmail} onClick={() => run(() => superadminCall<{ email: string }>(\"requestPasswordReset\")({ email: resetEmail }), \"Reset-link verstuurd\")}>\n Reset-link versturen\n </Button>\n </div>\n </Card>\n\n <Card>\n <h3 className=\"mb-3 text-base font-semibold\">Audit-log (laatste 20)</h3>\n {audit.length === 0 ? (\n <p className=\"text-sm text-slate-500\">Geen recente acties (of geen rechten/index).</p>\n ) : (\n <ul className=\"text-sm text-slate-600\">\n {audit.map((a) => (\n <li key={a.id} className=\"border-b border-slate-100 py-1 last:border-0\">\n <span className=\"font-medium\">{a.action}</span> · {a.target} <span className=\"text-slate-500\">({a.actorUid?.slice(0, 6)})</span>\n </li>\n ))}\n </ul>\n )}\n </Card>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zoeken in het beheer (8.4): Ctrl/Cmd+K (of de knop in de zijbalk) en typ — een pagina, een
|
|
3
|
+
* nieuwsartikel, een formulier, een product of een onderdeel van het beheer. Enter opent het.
|
|
4
|
+
* De lijsten worden pas bij het openen gelezen (één keer per opening), niet bij elk bezoek aan /beheer.
|
|
5
|
+
*/
|
|
6
|
+
export declare function ZoekPalet({ siteId, panelen, gaNaar, open, onOpenChange, }: {
|
|
7
|
+
siteId: string;
|
|
8
|
+
panelen: {
|
|
9
|
+
tab: string;
|
|
10
|
+
label: string;
|
|
11
|
+
}[];
|
|
12
|
+
gaNaar: (tab: string) => void;
|
|
13
|
+
open: boolean;
|
|
14
|
+
onOpenChange: (v: boolean) => void;
|
|
15
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { collection, getDocs } from "firebase/firestore";
|
|
5
|
+
import { getDb } from "../firebase/client.js";
|
|
6
|
+
import { isDemoActive } from "../demo/demoStore.js";
|
|
7
|
+
import { Dialog, DialogContent } from "./ui/dialog.js";
|
|
8
|
+
import { vraagOpen } from "./navContext.js";
|
|
9
|
+
function ZoekPalet({
|
|
10
|
+
siteId,
|
|
11
|
+
panelen,
|
|
12
|
+
gaNaar,
|
|
13
|
+
open,
|
|
14
|
+
onOpenChange
|
|
15
|
+
}) {
|
|
16
|
+
const [q, setQ] = useState("");
|
|
17
|
+
const [items, setItems] = useState([]);
|
|
18
|
+
const [actief, setActief] = useState(0);
|
|
19
|
+
const lijstRef = useRef(null);
|
|
20
|
+
const invoerRef = useRef(null);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (!open) return;
|
|
23
|
+
const t = window.setTimeout(() => invoerRef.current?.focus(), 30);
|
|
24
|
+
return () => window.clearTimeout(t);
|
|
25
|
+
}, [open]);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (!open) return;
|
|
28
|
+
setQ("");
|
|
29
|
+
setActief(0);
|
|
30
|
+
const db = getDb();
|
|
31
|
+
if (!db || isDemoActive()) return;
|
|
32
|
+
const tabs = new Set(panelen.map((p) => p.tab));
|
|
33
|
+
const lees = async (col, maak, tab) => tabs.has(tab) ? (await getDocs(collection(db, "sites", siteId, col)).catch(() => null))?.docs.map(maak).filter((x) => x !== null) ?? [] : [];
|
|
34
|
+
void Promise.all([
|
|
35
|
+
lees("drafts", (d) => d.get("trashed") ? null : { soort: "Pagina", titel: String(d.get("title") ?? d.id), sub: String(d.get("path") ?? ""), tab: "pages", id: d.id }, "pages"),
|
|
36
|
+
lees("news", (d) => ({ soort: "Nieuws", titel: String(d.get("title") ?? d.id), tab: "news", id: d.id }), "news"),
|
|
37
|
+
lees("forms", (d) => ({ soort: "Formulier", titel: String(d.get("name") ?? d.id), tab: "forms", id: d.id }), "forms"),
|
|
38
|
+
lees("products", (d) => ({ soort: "Product", titel: String(d.get("name") ?? d.id), tab: "shop" }), "shop")
|
|
39
|
+
]).then((delen) => setItems(delen.flat()));
|
|
40
|
+
}, [open, siteId, panelen]);
|
|
41
|
+
const treffers = useMemo(() => {
|
|
42
|
+
const term = q.trim().toLowerCase();
|
|
43
|
+
const alle = [...panelen.map((p) => ({ soort: "Paneel", titel: p.label, tab: p.tab })), ...items];
|
|
44
|
+
if (!term) return alle.slice(0, 12);
|
|
45
|
+
return alle.map((t) => {
|
|
46
|
+
const tekst = `${t.titel} ${t.sub ?? ""}`.toLowerCase();
|
|
47
|
+
const i = tekst.indexOf(term);
|
|
48
|
+
return { t, score: i < 0 ? -1 : i === 0 ? 2 : 1 };
|
|
49
|
+
}).filter((x) => x.score >= 0).sort((a, b) => b.score - a.score).slice(0, 20).map((x) => x.t);
|
|
50
|
+
}, [q, items, panelen]);
|
|
51
|
+
useEffect(() => setActief(0), [q]);
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
lijstRef.current?.querySelector(`[data-index="${actief}"]`)?.scrollIntoView({ block: "nearest" });
|
|
54
|
+
}, [actief]);
|
|
55
|
+
function kies(t) {
|
|
56
|
+
if (!t) return;
|
|
57
|
+
if (t.id) vraagOpen(t.tab, t.id);
|
|
58
|
+
onOpenChange(false);
|
|
59
|
+
gaNaar(t.tab);
|
|
60
|
+
}
|
|
61
|
+
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { title: "Zoeken in het beheer", width: "min(92vw, 36rem)", children: [
|
|
62
|
+
/* @__PURE__ */ jsx(
|
|
63
|
+
"input",
|
|
64
|
+
{
|
|
65
|
+
ref: invoerRef,
|
|
66
|
+
role: "combobox",
|
|
67
|
+
"aria-expanded": "true",
|
|
68
|
+
"aria-controls": "xicms-zoekpalet-lijst",
|
|
69
|
+
"aria-activedescendant": treffers[actief] ? `xicms-zoek-${actief}` : void 0,
|
|
70
|
+
"aria-label": "Zoek een pagina, artikel, formulier, product of onderdeel",
|
|
71
|
+
placeholder: "Typ om te zoeken\u2026",
|
|
72
|
+
value: q,
|
|
73
|
+
onChange: (e) => setQ(e.target.value),
|
|
74
|
+
onKeyDown: (e) => {
|
|
75
|
+
if (e.key === "ArrowDown") {
|
|
76
|
+
e.preventDefault();
|
|
77
|
+
setActief((i) => Math.min(treffers.length - 1, i + 1));
|
|
78
|
+
} else if (e.key === "ArrowUp") {
|
|
79
|
+
e.preventDefault();
|
|
80
|
+
setActief((i) => Math.max(0, i - 1));
|
|
81
|
+
} else if (e.key === "Enter") {
|
|
82
|
+
e.preventDefault();
|
|
83
|
+
kies(treffers[actief]);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
className: "xicms-admin-native mb-3 h-11 w-full rounded-lg border px-3 text-base",
|
|
87
|
+
style: { borderColor: "var(--color-border)" }
|
|
88
|
+
}
|
|
89
|
+
),
|
|
90
|
+
/* @__PURE__ */ jsxs("ul", { id: "xicms-zoekpalet-lijst", role: "listbox", ref: lijstRef, className: "grid max-h-[55vh] gap-0.5 overflow-y-auto", children: [
|
|
91
|
+
treffers.length === 0 && /* @__PURE__ */ jsxs("li", { className: "px-3 py-6 text-center text-sm text-slate-500", children: [
|
|
92
|
+
"Niets gevonden voor \u201C",
|
|
93
|
+
q,
|
|
94
|
+
"\u201D."
|
|
95
|
+
] }),
|
|
96
|
+
treffers.map((t, i) => /* @__PURE__ */ jsxs(
|
|
97
|
+
"li",
|
|
98
|
+
{
|
|
99
|
+
id: `xicms-zoek-${i}`,
|
|
100
|
+
"data-index": i,
|
|
101
|
+
role: "option",
|
|
102
|
+
"aria-selected": i === actief,
|
|
103
|
+
onMouseEnter: () => setActief(i),
|
|
104
|
+
onClick: () => kies(t),
|
|
105
|
+
className: `flex cursor-pointer items-center justify-between gap-3 rounded-lg px-3 py-2 text-sm ${i === actief ? "bg-slate-100" : ""}`,
|
|
106
|
+
children: [
|
|
107
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
|
|
108
|
+
/* @__PURE__ */ jsx("span", { className: "block truncate text-slate-800", children: t.titel }),
|
|
109
|
+
t.sub && /* @__PURE__ */ jsx("span", { className: "block truncate text-xs text-slate-500", children: t.sub })
|
|
110
|
+
] }),
|
|
111
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-slate-500", children: t.soort })
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
`${t.soort}-${t.tab}-${t.id ?? t.titel}`
|
|
115
|
+
))
|
|
116
|
+
] }),
|
|
117
|
+
/* @__PURE__ */ jsx("p", { className: "mt-3 text-xs text-slate-500", children: "\u2191 \u2193 om te kiezen \xB7 Enter om te openen \xB7 Esc om te sluiten" })
|
|
118
|
+
] }) });
|
|
119
|
+
}
|
|
120
|
+
export {
|
|
121
|
+
ZoekPalet
|
|
122
|
+
};
|
|
123
|
+
//# sourceMappingURL=ZoekPalet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/admin/ZoekPalet.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { collection, getDocs } from \"firebase/firestore\";\nimport { getDb } from \"../firebase/client\";\nimport { isDemoActive } from \"../demo/demoStore\";\nimport { Dialog, DialogContent } from \"./ui/dialog\";\nimport { vraagOpen } from \"./navContext\";\n\ninterface Treffer {\n soort: \"Paneel\" | \"Pagina\" | \"Nieuws\" | \"Formulier\" | \"Product\";\n titel: string;\n sub?: string;\n tab: string;\n id?: string;\n}\n\n/**\n * Zoeken in het beheer (8.4): Ctrl/Cmd+K (of de knop in de zijbalk) en typ — een pagina, een\n * nieuwsartikel, een formulier, een product of een onderdeel van het beheer. Enter opent het.\n * De lijsten worden pas bij het openen gelezen (één keer per opening), niet bij elk bezoek aan /beheer.\n */\nexport function ZoekPalet({\n siteId,\n panelen,\n gaNaar,\n open,\n onOpenChange,\n}: {\n siteId: string;\n panelen: { tab: string; label: string }[];\n gaNaar: (tab: string) => void;\n open: boolean;\n onOpenChange: (v: boolean) => void;\n}) {\n const [q, setQ] = useState(\"\");\n const [items, setItems] = useState<Treffer[]>([]);\n const [actief, setActief] = useState(0);\n const lijstRef = useRef<HTMLUListElement>(null);\n const invoerRef = useRef<HTMLInputElement>(null);\n\n // De dialoog zet zelf de focus op zijn eerste element; daarna pas het zoekveld pakken, anders\n // landt wat je typt nergens.\n useEffect(() => {\n if (!open) return;\n const t = window.setTimeout(() => invoerRef.current?.focus(), 30);\n return () => window.clearTimeout(t);\n }, [open]);\n\n useEffect(() => {\n if (!open) return;\n setQ(\"\");\n setActief(0);\n const db = getDb();\n if (!db || isDemoActive()) return;\n const tabs = new Set(panelen.map((p) => p.tab));\n const lees = async (col: string, maak: (d: { id: string; get: (k: string) => unknown }) => Treffer | null, tab: string) =>\n tabs.has(tab) ? (await getDocs(collection(db, \"sites\", siteId, col)).catch(() => null))?.docs.map(maak).filter((x): x is Treffer => x !== null) ?? [] : [];\n void Promise.all([\n lees(\"drafts\", (d) => (d.get(\"trashed\") ? null : { soort: \"Pagina\", titel: String(d.get(\"title\") ?? d.id), sub: String(d.get(\"path\") ?? \"\"), tab: \"pages\", id: d.id }), \"pages\"),\n lees(\"news\", (d) => ({ soort: \"Nieuws\", titel: String(d.get(\"title\") ?? d.id), tab: \"news\", id: d.id }), \"news\"),\n lees(\"forms\", (d) => ({ soort: \"Formulier\", titel: String(d.get(\"name\") ?? d.id), tab: \"forms\", id: d.id }), \"forms\"),\n lees(\"products\", (d) => ({ soort: \"Product\", titel: String(d.get(\"name\") ?? d.id), tab: \"shop\" }), \"shop\"),\n ]).then((delen) => setItems(delen.flat()));\n }, [open, siteId, panelen]);\n\n const treffers = useMemo(() => {\n const term = q.trim().toLowerCase();\n const alle: Treffer[] = [...panelen.map((p) => ({ soort: \"Paneel\" as const, titel: p.label, tab: p.tab })), ...items];\n if (!term) return alle.slice(0, 12);\n return alle\n .map((t) => {\n const tekst = `${t.titel} ${t.sub ?? \"\"}`.toLowerCase();\n const i = tekst.indexOf(term);\n return { t, score: i < 0 ? -1 : i === 0 ? 2 : 1 };\n })\n .filter((x) => x.score >= 0)\n .sort((a, b) => b.score - a.score)\n .slice(0, 20)\n .map((x) => x.t);\n }, [q, items, panelen]);\n\n useEffect(() => setActief(0), [q]);\n useEffect(() => {\n lijstRef.current?.querySelector<HTMLElement>(`[data-index=\"${actief}\"]`)?.scrollIntoView({ block: \"nearest\" });\n }, [actief]);\n\n function kies(t: Treffer | undefined) {\n if (!t) return;\n if (t.id) vraagOpen(t.tab, t.id);\n onOpenChange(false);\n gaNaar(t.tab);\n }\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent title=\"Zoeken in het beheer\" width=\"min(92vw, 36rem)\">\n <input\n ref={invoerRef}\n role=\"combobox\"\n aria-expanded=\"true\"\n aria-controls=\"xicms-zoekpalet-lijst\"\n aria-activedescendant={treffers[actief] ? `xicms-zoek-${actief}` : undefined}\n aria-label=\"Zoek een pagina, artikel, formulier, product of onderdeel\"\n placeholder=\"Typ om te zoeken…\"\n value={q}\n onChange={(e) => setQ(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n setActief((i) => Math.min(treffers.length - 1, i + 1));\n } else if (e.key === \"ArrowUp\") {\n e.preventDefault();\n setActief((i) => Math.max(0, i - 1));\n } else if (e.key === \"Enter\") {\n e.preventDefault();\n kies(treffers[actief]);\n }\n }}\n className=\"xicms-admin-native mb-3 h-11 w-full rounded-lg border px-3 text-base\"\n style={{ borderColor: \"var(--color-border)\" }}\n />\n <ul id=\"xicms-zoekpalet-lijst\" role=\"listbox\" ref={lijstRef} className=\"grid max-h-[55vh] gap-0.5 overflow-y-auto\">\n {treffers.length === 0 && <li className=\"px-3 py-6 text-center text-sm text-slate-500\">Niets gevonden voor “{q}”.</li>}\n {treffers.map((t, i) => (\n <li\n key={`${t.soort}-${t.tab}-${t.id ?? t.titel}`}\n id={`xicms-zoek-${i}`}\n data-index={i}\n role=\"option\"\n aria-selected={i === actief}\n onMouseEnter={() => setActief(i)}\n onClick={() => kies(t)}\n className={`flex cursor-pointer items-center justify-between gap-3 rounded-lg px-3 py-2 text-sm ${i === actief ? \"bg-slate-100\" : \"\"}`}\n >\n <span className=\"min-w-0\">\n <span className=\"block truncate text-slate-800\">{t.titel}</span>\n {t.sub && <span className=\"block truncate text-xs text-slate-500\">{t.sub}</span>}\n </span>\n <span className=\"shrink-0 text-xs text-slate-500\">{t.soort}</span>\n </li>\n ))}\n </ul>\n <p className=\"mt-3 text-xs text-slate-500\">↑ ↓ om te kiezen · Enter om te openen · Esc om te sluiten</p>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"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","names":[]}
|