@xiwebdesign/xicms-core 8.15.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 +1 -0
- package/dist/XiCMSProvider.js.map +1 -1
- package/dist/admin/lib/veldTaal.js +1 -0
- package/dist/admin/lib/veldTaal.js.map +1 -1
- package/dist/admin/settings/BedrijfInstellingen.js +17 -2
- package/dist/admin/settings/BedrijfInstellingen.js.map +1 -1
- package/dist/admin/shop/ShopSettingsTab.js +29 -16
- package/dist/admin/shop/ShopSettingsTab.js.map +1 -1
- package/dist/auth/AuthProvider.js +1 -0
- package/dist/auth/AuthProvider.js.map +1 -1
- package/dist/bedrijf/bedrijf.d.ts +8 -0
- package/dist/bedrijf/bedrijf.js +16 -1
- package/dist/bedrijf/bedrijf.js.map +1 -1
- package/dist/blocks/builtins/ContactRender.js +8 -0
- package/dist/blocks/builtins/ContactRender.js.map +1 -1
- package/dist/blocks/builtins/WhatsappChatRender.js +2 -0
- package/dist/blocks/builtins/WhatsappChatRender.js.map +1 -1
- package/dist/blocks/builtins/consentEmbed.js +26 -9
- package/dist/blocks/builtins/consentEmbed.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.js +2 -1
- package/dist/components/site/SiteFooter.js.map +1 -1
- package/dist/firebase/functionsClient.js +5 -1
- package/dist/firebase/functionsClient.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/modules/builtins.js +6 -6
- 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/releaseNotes.js +8 -0
- package/dist/releaseNotes.js.map +1 -1
- package/dist/schema/blocks.d.ts +3 -0
- package/dist/schema/blocks.js +3 -1
- package/dist/schema/blocks.js.map +1 -1
- package/dist/schema/business.d.ts +1 -0
- package/dist/schema/business.js +2 -0
- package/dist/schema/business.js.map +1 -1
- package/dist/schema/settings.d.ts +2 -0
- package/dist/schema/siteConfig.d.ts +1 -0
- package/dist/styles.css +32 -4
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/modules/consent/ConsentBanner.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { consentDecided, useConsent } from \"./useConsent\";\n\nexport interface ConsentCategory {\n id: string;\n label: string;\n /** Korte uitleg wat deze categorie doet (getoond in het voorkeuren-paneel). */\n description?: string;\n required?: boolean;\n}\n\nexport interface ConsentSettings {\n title?: string;\n message?: string;\n privacyUrl?: string;\n categories?: ConsentCategory[];\n /** Beleidsversie: verhoog dit als het cookiebeleid wijzigt — bezoekers krijgen de banner dan opnieuw. */\n version?: string;\n /** Zwevend 🍪-knopje tonen om de keuze later te wijzigen (AVG). Standaard aan. */\n showReopen?: boolean;\n}\n\nconst DEFAULT_CATEGORIES: ConsentCategory[] = [\n { id: \"necessary\", label: \"Noodzakelijk\", required: true, description: \"Nodig om de site te laten werken. Altijd actief.\" },\n { id: \"analytics\", label: \"Statistieken\", description: \"Anonieme bezoekersstatistieken om de site te verbeteren.\" },\n { id: \"marketing\", label: \"Marketing\", description: \"Voor gepersonaliseerde content en advertenties.\" },\n];\n\n/**\n * AVG-toestemmingsbanner met twee-staps-UX: compact (accepteren / weigeren / voorkeuren), en een\n * uitklapbaar voorkeuren-paneel met toggle-switches + uitleg per categorie. Strak gestyled op de\n * thema-tokens (`--color-*`), volledig overschrijfbaar via de `xicms-consent*`-classNames. Verschijnt\n * pas als de bezoeker nog niet heeft gekozen; logica/opslag blijven in core.\n */\nexport function ConsentBanner({ settings }: { settings?: ConsentSettings }) {\n const { state, save } = useConsent();\n const categories = settings?.categories?.length ? settings.categories : DEFAULT_CATEGORIES;\n const [choices, setChoices] = useState<Record<string, boolean>>(() =>\n Object.fromEntries(categories.map((c) => [c.id, Boolean(c.required)])),\n );\n const [showPrefs, setShowPrefs] = useState(false);\n // Geforceerd (her)openen: via het 🍪-knopje of het `xicms:consent:open`-event (footerlink).\n const [reopened, setReopened] = useState(false);\n const bannerRef = useRef<HTMLDivElement | null>(null);\n // Bij expliciet heropenen focus de banner in (de bezoeker vroeg erom); bij eerste bezoek\n // stelen we géén focus — de banner is niet-modaal en mag het lezen niet onderbreken.\n useEffect(() => {\n if (reopened) bannerRef.current?.querySelector<HTMLElement>(\".xicms-consent__btn--primary\")?.focus();\n }, [reopened]);\n // Een oude keuze telt niet meer als de beleidsversie is gewijzigd → banner opnieuw tonen.\n const decided = consentDecided(state, settings?.version);\n\n const openBanner = () => {\n // Voorkeuren voor-invullen met de huidige keuze zodat wijzigen intuïtief is.\n setChoices((prev) => ({ ...prev, ...state.categories }));\n setShowPrefs(true);\n setReopened(true);\n };\n\n useEffect(() => {\n const onOpen = () => openBanner();\n window.addEventListener(\"xicms:consent:open\", onOpen);\n return () => window.removeEventListener(\"xicms:consent:open\", onOpen);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [state.categories]);\n\n if (decided && !reopened) {\n // AVG: de bezoeker moet zijn keuze altijd kunnen wijzigen — zwevend knopje (opt-out via settings).\n if (settings?.showReopen === false) return null;\n return (\n <button\n type=\"button\"\n className=\"xicms-consent__reopen\"\n aria-label=\"Cookie-instellingen wijzigen\"\n title=\"Cookie-instellingen\"\n onClick={openBanner}\n >\n 🍪\n </button>\n );\n }\n\n const commit = (categories: Record<string, boolean>) => {\n save(categories, settings?.version);\n setReopened(false);\n };\n const acceptAll = () => commit(Object.fromEntries(categories.map((c) => [c.id, true])));\n const rejectNonEssential = () =>\n commit(Object.fromEntries(categories.map((c) => [c.id, Boolean(c.required)])));\n const saveChoices = () => commit(choices);\n\n return (\n <div ref={bannerRef} className=\"xicms-consent\" role=\"dialog\" aria-modal=\"false\" aria-label=\"Cookievoorkeuren\">\n <div className=\"xicms-consent__inner\" data-prefs={showPrefs ? \"1\" : \"0\"}>\n <div className=\"xicms-consent__head\">\n <p className=\"xicms-consent__title\">{settings?.title ?? \"🍪 Cookievoorkeuren\"}</p>\n <p className=\"xicms-consent__message\">\n {settings?.message ??\n \"We gebruiken cookies om de site te laten werken en te verbeteren. Kies hieronder wat je toestaat.\"}\n {settings?.privacyUrl && (\n <>\n {\" \"}\n <a className=\"xicms-consent__link\" href={settings.privacyUrl}>\n Privacyverklaring\n </a>\n </>\n )}\n </p>\n </div>\n\n {showPrefs && (\n <fieldset className=\"xicms-consent__categories\">\n <legend className=\"xicms-sr-only\">Cookiecategorieën</legend>\n {categories.map((c) => {\n const on = choices[c.id] ?? Boolean(c.required);\n return (\n <label key={c.id} className=\"xicms-consent__category\" data-on={on ? \"1\" : \"0\"}>\n <span className=\"xicms-consent__cat-text\">\n <span className=\"xicms-consent__cat-label\">\n {c.label}\n {c.required && <span className=\"xicms-consent__required\"> · altijd aan</span>}\n </span>\n {c.description && <span className=\"xicms-consent__cat-desc\">{c.description}</span>}\n </span>\n <span className=\"xicms-consent__switch\">\n <input\n type=\"checkbox\"\n checked={on}\n disabled={c.required}\n onChange={(e) => setChoices((prev) => ({ ...prev, [c.id]: e.target.checked }))}\n />\n <span className=\"xicms-consent__track\" aria-hidden=\"true\" />\n </span>\n </label>\n );\n })}\n </fieldset>\n )}\n\n <div className=\"xicms-consent__actions\">\n {!showPrefs ? (\n <button type=\"button\" className=\"xicms-consent__btn xicms-consent__btn--ghost\" onClick={() => setShowPrefs(true)}>\n Voorkeuren\n </button>\n ) : (\n <button type=\"button\" className=\"xicms-consent__btn xicms-consent__btn--ghost\" onClick={saveChoices}>\n Mijn keuze opslaan\n </button>\n )}\n <button type=\"button\" className=\"xicms-consent__btn xicms-consent__btn--ghost\" onClick={rejectNonEssential}>\n Alleen noodzakelijk\n </button>\n <button type=\"button\" className=\"xicms-consent__btn xicms-consent__btn--primary\" onClick={acceptAll}>\n Alles accepteren\n </button>\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * Toont children alleen als toestemming voor `category` is gegeven. Gebruik dit om niet-essentiële\n * scripts/embeds (analytics, video, maps) pas na consent te laden.\n */\nexport function ConsentGate({ category, children }: { category: string; children: React.ReactNode }) {\n const { has } = useConsent();\n return has(category) ? <>{children}</> : null;\n}\n\n/**\n * Tekstlink/knop die de cookievoorkeuren heropent — voor in een footer (\"Cookie-instellingen\").\n * Werkt via het `xicms:consent:open`-event; de ConsentBanner luistert daarnaar.\n */\nexport function ConsentReopenLink({ children, className }: { children?: React.ReactNode; className?: string }) {\n return (\n <button\n type=\"button\"\n className={className ?? \"xicms-consent__reopen-link\"}\n onClick={() => window.dispatchEvent(new Event(\"xicms:consent:open\"))}\n >\n {children ?? \"Cookie-instellingen\"}\n </button>\n );\n}\n"],"mappings":"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","names":["categories"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/modules/consent/ConsentBanner.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { consentDecided, useConsent, useConsentNodig, useMeldConsentNodig } from \"./useConsent\";\nimport { useOnderOffset } from \"../../components/onderOffset\";\nimport { DEFAULT_CATEGORIES, DEFAULT_MESSAGE } from \"./standaard\";\n\nexport interface ConsentCategory {\n id: string;\n label: string;\n /** Korte uitleg wat deze categorie doet (getoond in het voorkeuren-paneel). */\n description?: string;\n required?: boolean;\n}\n\nexport interface ConsentSettings {\n title?: string;\n message?: string;\n privacyUrl?: string;\n categories?: ConsentCategory[];\n /** Beleidsversie: verhoog dit als het cookiebeleid wijzigt — bezoekers krijgen de banner dan opnieuw. */\n version?: string;\n /** Zwevend 🍪-knopje tonen om de keuze later te wijzigen (AVG). Standaard aan. */\n showReopen?: boolean;\n /**\n * 8.16: \"automatisch\" (standaard) = de banner verschijnt alleen op een pagina waar iets op\n * toestemming wacht (video, kaart, boekingswidget, ConsentGate). \"altijd\" = het oude gedrag.\n */\n toon?: \"automatisch\" | \"altijd\";\n}\n\n/**\n * AVG-toestemmingsbanner met twee-staps-UX: compact (accepteren / weigeren / voorkeuren), en een\n * uitklapbaar voorkeuren-paneel met toggle-switches + uitleg per categorie. Strak gestyled op de\n * thema-tokens (`--color-*`), volledig overschrijfbaar via de `xicms-consent*`-classNames. Verschijnt\n * pas als de bezoeker nog niet heeft gekozen; logica/opslag blijven in core.\n */\nexport function ConsentBanner({ settings }: { settings?: ConsentSettings }) {\n const { state, save } = useConsent();\n const categories = settings?.categories?.length ? settings.categories : DEFAULT_CATEGORIES;\n const [choices, setChoices] = useState<Record<string, boolean>>(() =>\n Object.fromEntries(categories.map((c) => [c.id, Boolean(c.required)])),\n );\n const [showPrefs, setShowPrefs] = useState(false);\n // Geforceerd (her)openen: via het 🍪-knopje of het `xicms:consent:open`-event (footerlink).\n const [reopened, setReopened] = useState(false);\n const bannerRef = useRef<HTMLDivElement | null>(null);\n // Bij expliciet heropenen focus de banner in (de bezoeker vroeg erom); bij eerste bezoek\n // stelen we géén focus — de banner is niet-modaal en mag het lezen niet onderbreken.\n useEffect(() => {\n if (reopened) bannerRef.current?.querySelector<HTMLElement>(\".xicms-consent__btn--primary\")?.focus();\n }, [reopened]);\n // Een oude keuze telt niet meer als de beleidsversie is gewijzigd → banner opnieuw tonen.\n const decided = consentDecided(state, settings?.version);\n const nodig = useConsentNodig();\n // De banner en het 🍪-knopje boven vaste balken onderaan (demobalk, belbalk, eigen CTA-balk).\n useOnderOffset();\n\n const openBanner = () => {\n // Voorkeuren voor-invullen met de huidige keuze zodat wijzigen intuïtief is.\n setChoices((prev) => ({ ...prev, ...state.categories }));\n setShowPrefs(true);\n setReopened(true);\n };\n\n useEffect(() => {\n const onOpen = () => openBanner();\n window.addEventListener(\"xicms:consent:open\", onOpen);\n return () => window.removeEventListener(\"xicms:consent:open\", onOpen);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [state.categories]);\n\n // 8.16: niets te vragen op deze pagina → geen banner. De footerlink \"Cookie-instellingen\" werkt wel.\n if (!decided && !reopened && settings?.toon !== \"altijd\" && !nodig) return null;\n\n if (decided && !reopened) {\n // AVG: de bezoeker moet zijn keuze altijd kunnen wijzigen — zwevend knopje (opt-out via settings).\n if (settings?.showReopen === false) return null;\n return (\n <button\n type=\"button\"\n className=\"xicms-consent__reopen\"\n aria-label=\"Cookie-instellingen wijzigen\"\n title=\"Cookie-instellingen\"\n onClick={openBanner}\n >\n 🍪\n </button>\n );\n }\n\n const commit = (categories: Record<string, boolean>) => {\n save(categories, settings?.version);\n setReopened(false);\n };\n const acceptAll = () => commit(Object.fromEntries(categories.map((c) => [c.id, true])));\n const rejectNonEssential = () =>\n commit(Object.fromEntries(categories.map((c) => [c.id, Boolean(c.required)])));\n const saveChoices = () => commit(choices);\n\n return (\n <div ref={bannerRef} className=\"xicms-consent\" role=\"dialog\" aria-modal=\"false\" aria-label=\"Cookievoorkeuren\">\n <div className=\"xicms-consent__inner\" data-prefs={showPrefs ? \"1\" : \"0\"}>\n <div className=\"xicms-consent__head\">\n <p className=\"xicms-consent__title\">{settings?.title ?? \"🍪 Cookievoorkeuren\"}</p>\n <p className=\"xicms-consent__message\">\n {settings?.message ??\n DEFAULT_MESSAGE}\n {settings?.privacyUrl && (\n <>\n {\" \"}\n <a className=\"xicms-consent__link\" href={settings.privacyUrl}>\n Privacyverklaring\n </a>\n </>\n )}\n </p>\n </div>\n\n {showPrefs && (\n <fieldset className=\"xicms-consent__categories\">\n <legend className=\"xicms-sr-only\">Cookiecategorieën</legend>\n {categories.map((c) => {\n const on = choices[c.id] ?? Boolean(c.required);\n return (\n <label key={c.id} className=\"xicms-consent__category\" data-on={on ? \"1\" : \"0\"}>\n <span className=\"xicms-consent__cat-text\">\n <span className=\"xicms-consent__cat-label\">\n {c.label}\n {c.required && <span className=\"xicms-consent__required\"> · altijd aan</span>}\n </span>\n {c.description && <span className=\"xicms-consent__cat-desc\">{c.description}</span>}\n </span>\n <span className=\"xicms-consent__switch\">\n <input\n type=\"checkbox\"\n checked={on}\n disabled={c.required}\n onChange={(e) => setChoices((prev) => ({ ...prev, [c.id]: e.target.checked }))}\n />\n <span className=\"xicms-consent__track\" aria-hidden=\"true\" />\n </span>\n </label>\n );\n })}\n </fieldset>\n )}\n\n <div className=\"xicms-consent__actions\">\n {!showPrefs ? (\n <button type=\"button\" className=\"xicms-consent__btn xicms-consent__btn--ghost\" onClick={() => setShowPrefs(true)}>\n Voorkeuren\n </button>\n ) : (\n <button type=\"button\" className=\"xicms-consent__btn xicms-consent__btn--ghost\" onClick={saveChoices}>\n Mijn keuze opslaan\n </button>\n )}\n <button type=\"button\" className=\"xicms-consent__btn xicms-consent__btn--ghost\" onClick={rejectNonEssential}>\n Alleen noodzakelijk\n </button>\n <button type=\"button\" className=\"xicms-consent__btn xicms-consent__btn--primary\" onClick={acceptAll}>\n Alles accepteren\n </button>\n </div>\n </div>\n </div>\n );\n}\n\n/**\n * Toont children alleen als toestemming voor `category` is gegeven. Gebruik dit om niet-essentiële\n * scripts/embeds (analytics, video, maps) pas na consent te laden.\n */\nexport function ConsentGate({ category, children }: { category: string; children: React.ReactNode }) {\n const { has } = useConsent();\n // 8.16: zolang dit wacht, mag de banner (in \"automatisch\") verschijnen.\n useMeldConsentNodig(category, !has(category));\n return has(category) ? <>{children}</> : null;\n}\n\n/**\n * Tekstlink/knop die de cookievoorkeuren heropent — voor in een footer (\"Cookie-instellingen\").\n * Werkt via het `xicms:consent:open`-event; de ConsentBanner luistert daarnaar.\n */\nexport function ConsentReopenLink({ children, className }: { children?: React.ReactNode; className?: string }) {\n return (\n <button\n type=\"button\"\n className={className ?? \"xicms-consent__reopen-link\"}\n onClick={() => window.dispatchEvent(new Event(\"xicms:consent:open\"))}\n >\n {children ?? \"Cookie-instellingen\"}\n </button>\n );\n}\n"],"mappings":"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","names":["categories"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 8.16: alleen wat er echt is. De bezoekersteller van XiCMS is cookieloos en hoeft geen toestemming
|
|
3
|
+
* (er stond een schakelaar "Statistieken" die nergens iets deed); "marketing" is de categorie
|
|
4
|
+
* waaronder video's, kaarten en boekingswidgets al jaren wachten — de id blijft, het label klopt nu.
|
|
5
|
+
*/
|
|
6
|
+
export declare const DEFAULT_CATEGORIES: {
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
required?: boolean;
|
|
11
|
+
}[];
|
|
12
|
+
export declare const DEFAULT_MESSAGE = "Deze site gebruikt zelf geen tracking-cookies. Inhoud van andere partijen, zoals video's en kaarten, laden we pas als je dat toestaat.";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const DEFAULT_CATEGORIES = [
|
|
2
|
+
{ id: "necessary", label: "Noodzakelijk", required: true, description: "Nodig om de site te laten werken, zoals je winkelwagen en deze keuze. Geen tracking." },
|
|
3
|
+
{ id: "marketing", label: "Inhoud van andere partijen", description: "Video's, kaarten en boekingsmodules (zoals YouTube en Google Maps). Die partijen kunnen cookies plaatsen." }
|
|
4
|
+
];
|
|
5
|
+
const DEFAULT_MESSAGE = "Deze site gebruikt zelf geen tracking-cookies. Inhoud van andere partijen, zoals video's en kaarten, laden we pas als je dat toestaat.";
|
|
6
|
+
export {
|
|
7
|
+
DEFAULT_CATEGORIES,
|
|
8
|
+
DEFAULT_MESSAGE
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=standaard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/modules/consent/standaard.ts"],"sourcesContent":["// Gedeeld door de banner (client) en het module-schema (server): daarom geen \"use client\" hier.\n\n/**\n * 8.16: alleen wat er echt is. De bezoekersteller van XiCMS is cookieloos en hoeft geen toestemming\n * (er stond een schakelaar \"Statistieken\" die nergens iets deed); \"marketing\" is de categorie\n * waaronder video's, kaarten en boekingswidgets al jaren wachten — de id blijft, het label klopt nu.\n */\nexport const DEFAULT_CATEGORIES: { id: string; label: string; description?: string; required?: boolean }[] = [\n { id: \"necessary\", label: \"Noodzakelijk\", required: true, description: \"Nodig om de site te laten werken, zoals je winkelwagen en deze keuze. Geen tracking.\" },\n { id: \"marketing\", label: \"Inhoud van andere partijen\", description: \"Video's, kaarten en boekingsmodules (zoals YouTube en Google Maps). Die partijen kunnen cookies plaatsen.\" },\n];\n\nexport const DEFAULT_MESSAGE =\n \"Deze site gebruikt zelf geen tracking-cookies. Inhoud van andere partijen, zoals video's en kaarten, laden we pas als je dat toestaat.\";\n"],"mappings":"AAOO,MAAM,qBAAgG;AAAA,EAC3G,EAAE,IAAI,aAAa,OAAO,gBAAgB,UAAU,MAAM,aAAa,uFAAuF;AAAA,EAC9J,EAAE,IAAI,aAAa,OAAO,8BAA8B,aAAa,4GAA4G;AACnL;AAEO,MAAM,kBACX;","names":[]}
|
|
@@ -26,3 +26,7 @@ export declare function useConsent(): {
|
|
|
26
26
|
has: (category: string) => boolean;
|
|
27
27
|
save: (categories: Record<string, boolean>, version?: string) => void;
|
|
28
28
|
};
|
|
29
|
+
/** Meld dat dit onderdeel op toestemming voor `category` wacht (zolang `actief`). */
|
|
30
|
+
export declare function useMeldConsentNodig(category: string, actief: boolean): void;
|
|
31
|
+
/** Wacht er op deze pagina iets op toestemming? */
|
|
32
|
+
export declare function useConsentNodig(): boolean;
|
|
@@ -56,9 +56,38 @@ function useConsent() {
|
|
|
56
56
|
);
|
|
57
57
|
return { state, decided, has, save };
|
|
58
58
|
}
|
|
59
|
+
const nodig = /* @__PURE__ */ new Map();
|
|
60
|
+
function meldNodig(category) {
|
|
61
|
+
nodig.set(category, (nodig.get(category) ?? 0) + 1);
|
|
62
|
+
window.dispatchEvent(new Event("xicms:consent:nodig"));
|
|
63
|
+
return () => {
|
|
64
|
+
const n = (nodig.get(category) ?? 1) - 1;
|
|
65
|
+
if (n <= 0) nodig.delete(category);
|
|
66
|
+
else nodig.set(category, n);
|
|
67
|
+
window.dispatchEvent(new Event("xicms:consent:nodig"));
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function useMeldConsentNodig(category, actief) {
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (!actief || typeof window === "undefined") return;
|
|
73
|
+
return meldNodig(category);
|
|
74
|
+
}, [category, actief]);
|
|
75
|
+
}
|
|
76
|
+
function useConsentNodig() {
|
|
77
|
+
const [ja, setJa] = useState(false);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
const lees = () => setJa(nodig.size > 0);
|
|
80
|
+
lees();
|
|
81
|
+
window.addEventListener("xicms:consent:nodig", lees);
|
|
82
|
+
return () => window.removeEventListener("xicms:consent:nodig", lees);
|
|
83
|
+
}, []);
|
|
84
|
+
return ja;
|
|
85
|
+
}
|
|
59
86
|
export {
|
|
60
87
|
consentDecided,
|
|
61
88
|
normalizeConsentState,
|
|
62
|
-
useConsent
|
|
89
|
+
useConsent,
|
|
90
|
+
useConsentNodig,
|
|
91
|
+
useMeldConsentNodig
|
|
63
92
|
};
|
|
64
93
|
//# sourceMappingURL=useConsent.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/modules/consent/useConsent.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useEffect, useState } from \"react\";\nimport { useModuleSettings } from \"../context\";\n\nconst STORAGE_KEY = \"xicms-consent\";\n\nexport interface ConsentState {\n /** Per categorie-id of de bezoeker toestemming gaf. */\n categories: Record<string, boolean>;\n /** Tijdstip (ms) waarop de keuze is gemaakt; null = nog niet gekozen. */\n decidedAt: number | null;\n /** Versie van het cookiebeleid waarvoor de keuze gold — wijzigt de site zijn beleid\n * (nieuwe `version` in de instellingen), dan telt een oude keuze niet meer. */\n version?: string;\n}\n\nconst EMPTY: ConsentState = { categories: {}, decidedAt: null };\n\n/** Normaliseer willekeurige opgeslagen JSON naar een geldige ConsentState (defensief tegen\n * corrupte/oude vormen — `has()` mag nooit crashen op een afwijkende localStorage-waarde). */\nexport function normalizeConsentState(value: unknown): ConsentState {\n if (!value || typeof value !== \"object\") return EMPTY;\n const v = value as Record<string, unknown>;\n const categories =\n v.categories && typeof v.categories === \"object\" ? (v.categories as Record<string, boolean>) : {};\n const decidedAt = typeof v.decidedAt === \"number\" ? v.decidedAt : null;\n const version = typeof v.version === \"string\" ? v.version : undefined;\n return version === undefined ? { categories, decidedAt } : { categories, decidedAt, version };\n}\n\n/**\n * Geldt de opgeslagen keuze nog? Alleen als er beslist is én de keuze bij de huidige\n * beleidsversie hoort (geen `requiredVersion` = elke keuze telt — het pre-versie-gedrag).\n */\nexport function consentDecided(state: ConsentState, requiredVersion?: string): boolean {\n if (state.decidedAt === null) return false;\n if (!requiredVersion) return true;\n return state.version === requiredVersion;\n}\n\n// Vangnet als localStorage weigert (privémodus/vol): de keuze geldt dan alleen voor deze\n// paginasessie, maar wél consistent voor álle componenten (read() geeft hetzelfde terug).\nlet memoryFallback: ConsentState | null = null;\n\nfunction read(): ConsentState {\n if (typeof window === \"undefined\") return EMPTY;\n try {\n const raw = window.localStorage.getItem(STORAGE_KEY);\n if (raw) return normalizeConsentState(JSON.parse(raw));\n } catch {\n /* corrupt/onbeschikbaar: behandel als nog-niet-gekozen */\n }\n return memoryFallback ?? EMPTY;\n}\n\n/**\n * Leest/zet cookie-consent (client-side, geen backend). Andere code gebruikt `has(\"analytics\")`\n * om niet-essentiële scripts pas te laden na toestemming. De `xicms:consent`-event laat losse\n * luisteraars reageren zonder re-render.\n */\nexport function useConsent() {\n const [state, setState] = useState<ConsentState>({ categories: {}, decidedAt: null });\n // Beleidsversie + categorie-configuratie van de site: een keuze van een oude versie telt niet\n // meer (AVG), en dat moet ook voor `has()` gelden — niet alleen voor het hertonen van de banner.\n const settings = useModuleSettings<{ version?: string; categories?: Array<{ id: string }> }>(\"consent\");\n\n useEffect(() => {\n setState(read());\n const onChange = () => setState(read());\n window.addEventListener(\"xicms:consent\", onChange);\n return () => window.removeEventListener(\"xicms:consent\", onChange);\n }, []);\n\n const save = useCallback((categories: Record<string, boolean>, version?: string) => {\n const next: ConsentState = { categories, decidedAt: Date.now(), ...(version ? { version } : {}) };\n try {\n window.localStorage.setItem(STORAGE_KEY, JSON.stringify(next));\n } catch {\n memoryFallback = next; // opslag geweigerd: keuze geldt alleen voor deze paginasessie\n }\n setState(next);\n window.dispatchEvent(new Event(\"xicms:consent\"));\n }, []);\n\n const decided = consentDecided(state, settings.version);\n const has = useCallback(\n (category: string) => {\n if (!decided) return false;\n if (state.categories[category] === true) return true;\n // Een categorie die niet in de banner voorkomt kan een bezoeker nooit aanvinken; na een\n // geldige keuze vallen we dan open i.p.v. de inhoud eeuwig achter een onbereikbare gate\n // te zetten (maatwerk-categorieën vs. de standaard-gates van video/kaart/embed).\n return settings.categories !== undefined && !settings.categories.some((c) => c.id === category);\n },\n [decided, state, settings.categories],\n );\n\n return { state, decided, has, save };\n}\n"],"mappings":";AAEA,SAAS,aAAa,WAAW,gBAAgB;AACjD,SAAS,yBAAyB;AAElC,MAAM,cAAc;AAYpB,MAAM,QAAsB,EAAE,YAAY,CAAC,GAAG,WAAW,KAAK;AAIvD,SAAS,sBAAsB,OAA8B;AAClE,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,IAAI;AACV,QAAM,aACJ,EAAE,cAAc,OAAO,EAAE,eAAe,WAAY,EAAE,aAAyC,CAAC;AAClG,QAAM,YAAY,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY;AAClE,QAAM,UAAU,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU;AAC5D,SAAO,YAAY,SAAY,EAAE,YAAY,UAAU,IAAI,EAAE,YAAY,WAAW,QAAQ;AAC9F;AAMO,SAAS,eAAe,OAAqB,iBAAmC;AACrF,MAAI,MAAM,cAAc,KAAM,QAAO;AACrC,MAAI,CAAC,gBAAiB,QAAO;AAC7B,SAAO,MAAM,YAAY;AAC3B;AAIA,IAAI,iBAAsC;AAE1C,SAAS,OAAqB;AAC5B,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,MAAI;AACF,UAAM,MAAM,OAAO,aAAa,QAAQ,WAAW;AACnD,QAAI,IAAK,QAAO,sBAAsB,KAAK,MAAM,GAAG,CAAC;AAAA,EACvD,QAAQ;AAAA,EAER;AACA,SAAO,kBAAkB;AAC3B;AAOO,SAAS,aAAa;AAC3B,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAuB,EAAE,YAAY,CAAC,GAAG,WAAW,KAAK,CAAC;AAGpF,QAAM,WAAW,kBAA4E,SAAS;AAEtG,YAAU,MAAM;AACd,aAAS,KAAK,CAAC;AACf,UAAM,WAAW,MAAM,SAAS,KAAK,CAAC;AACtC,WAAO,iBAAiB,iBAAiB,QAAQ;AACjD,WAAO,MAAM,OAAO,oBAAoB,iBAAiB,QAAQ;AAAA,EACnE,GAAG,CAAC,CAAC;AAEL,QAAM,OAAO,YAAY,CAAC,YAAqC,YAAqB;AAClF,UAAM,OAAqB,EAAE,YAAY,WAAW,KAAK,IAAI,GAAG,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,EAAG;AAChG,QAAI;AACF,aAAO,aAAa,QAAQ,aAAa,KAAK,UAAU,IAAI,CAAC;AAAA,IAC/D,QAAQ;AACN,uBAAiB;AAAA,IACnB;AACA,aAAS,IAAI;AACb,WAAO,cAAc,IAAI,MAAM,eAAe,CAAC;AAAA,EACjD,GAAG,CAAC,CAAC;AAEL,QAAM,UAAU,eAAe,OAAO,SAAS,OAAO;AACtD,QAAM,MAAM;AAAA,IACV,CAAC,aAAqB;AACpB,UAAI,CAAC,QAAS,QAAO;AACrB,UAAI,MAAM,WAAW,QAAQ,MAAM,KAAM,QAAO;AAIhD,aAAO,SAAS,eAAe,UAAa,CAAC,SAAS,WAAW,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ;AAAA,IAChG;AAAA,IACA,CAAC,SAAS,OAAO,SAAS,UAAU;AAAA,EACtC;AAEA,SAAO,EAAE,OAAO,SAAS,KAAK,KAAK;AACrC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/modules/consent/useConsent.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useEffect, useState } from \"react\";\nimport { useModuleSettings } from \"../context\";\n\nconst STORAGE_KEY = \"xicms-consent\";\n\nexport interface ConsentState {\n /** Per categorie-id of de bezoeker toestemming gaf. */\n categories: Record<string, boolean>;\n /** Tijdstip (ms) waarop de keuze is gemaakt; null = nog niet gekozen. */\n decidedAt: number | null;\n /** Versie van het cookiebeleid waarvoor de keuze gold — wijzigt de site zijn beleid\n * (nieuwe `version` in de instellingen), dan telt een oude keuze niet meer. */\n version?: string;\n}\n\nconst EMPTY: ConsentState = { categories: {}, decidedAt: null };\n\n/** Normaliseer willekeurige opgeslagen JSON naar een geldige ConsentState (defensief tegen\n * corrupte/oude vormen — `has()` mag nooit crashen op een afwijkende localStorage-waarde). */\nexport function normalizeConsentState(value: unknown): ConsentState {\n if (!value || typeof value !== \"object\") return EMPTY;\n const v = value as Record<string, unknown>;\n const categories =\n v.categories && typeof v.categories === \"object\" ? (v.categories as Record<string, boolean>) : {};\n const decidedAt = typeof v.decidedAt === \"number\" ? v.decidedAt : null;\n const version = typeof v.version === \"string\" ? v.version : undefined;\n return version === undefined ? { categories, decidedAt } : { categories, decidedAt, version };\n}\n\n/**\n * Geldt de opgeslagen keuze nog? Alleen als er beslist is én de keuze bij de huidige\n * beleidsversie hoort (geen `requiredVersion` = elke keuze telt — het pre-versie-gedrag).\n */\nexport function consentDecided(state: ConsentState, requiredVersion?: string): boolean {\n if (state.decidedAt === null) return false;\n if (!requiredVersion) return true;\n return state.version === requiredVersion;\n}\n\n// Vangnet als localStorage weigert (privémodus/vol): de keuze geldt dan alleen voor deze\n// paginasessie, maar wél consistent voor álle componenten (read() geeft hetzelfde terug).\nlet memoryFallback: ConsentState | null = null;\n\nfunction read(): ConsentState {\n if (typeof window === \"undefined\") return EMPTY;\n try {\n const raw = window.localStorage.getItem(STORAGE_KEY);\n if (raw) return normalizeConsentState(JSON.parse(raw));\n } catch {\n /* corrupt/onbeschikbaar: behandel als nog-niet-gekozen */\n }\n return memoryFallback ?? EMPTY;\n}\n\n/**\n * Leest/zet cookie-consent (client-side, geen backend). Andere code gebruikt `has(\"analytics\")`\n * om niet-essentiële scripts pas te laden na toestemming. De `xicms:consent`-event laat losse\n * luisteraars reageren zonder re-render.\n */\nexport function useConsent() {\n const [state, setState] = useState<ConsentState>({ categories: {}, decidedAt: null });\n // Beleidsversie + categorie-configuratie van de site: een keuze van een oude versie telt niet\n // meer (AVG), en dat moet ook voor `has()` gelden — niet alleen voor het hertonen van de banner.\n const settings = useModuleSettings<{ version?: string; categories?: Array<{ id: string }> }>(\"consent\");\n\n useEffect(() => {\n setState(read());\n const onChange = () => setState(read());\n window.addEventListener(\"xicms:consent\", onChange);\n return () => window.removeEventListener(\"xicms:consent\", onChange);\n }, []);\n\n const save = useCallback((categories: Record<string, boolean>, version?: string) => {\n const next: ConsentState = { categories, decidedAt: Date.now(), ...(version ? { version } : {}) };\n try {\n window.localStorage.setItem(STORAGE_KEY, JSON.stringify(next));\n } catch {\n memoryFallback = next; // opslag geweigerd: keuze geldt alleen voor deze paginasessie\n }\n setState(next);\n window.dispatchEvent(new Event(\"xicms:consent\"));\n }, []);\n\n const decided = consentDecided(state, settings.version);\n const has = useCallback(\n (category: string) => {\n if (!decided) return false;\n if (state.categories[category] === true) return true;\n // Een categorie die niet in de banner voorkomt kan een bezoeker nooit aanvinken; na een\n // geldige keuze vallen we dan open i.p.v. de inhoud eeuwig achter een onbereikbare gate\n // te zetten (maatwerk-categorieën vs. de standaard-gates van video/kaart/embed).\n return settings.categories !== undefined && !settings.categories.some((c) => c.id === category);\n },\n [decided, state, settings.categories],\n );\n\n return { state, decided, has, save };\n}\n\n/*\n * ── 8.16: welke toestemming is er OP DEZE PAGINA nodig? ───────────────────────────────────\n *\n * XiCMS zelf plaatst geen tracking-cookies: de bezoekersteller is cookieloos (één sessionStorage-\n * vlag per pad, geen persoonsgegevens) en de winkelwagen is functioneel. Daarvoor hoeft niemand\n * toestemming te geven (Telecommunicatiewet 11.7a: functioneel en analytisch zonder privacy-\n * gevolgen). Toestemming is pas nodig voor inhoud van ANDERE partijen: een YouTube-video, een\n * Google-kaart, een boekingswidget. Een banner op elke pagina van elke site was dus vooral ruis.\n *\n * Nu melden zulke onderdelen zich hier aan zolang ze op toestemming wachten (`useMeldConsentNodig`),\n * en toont de banner zich alleen als er iets is aangemeld (instelling `toon: \"automatisch\"`).\n */\nconst nodig = new Map<string, number>();\n\nfunction meldNodig(category: string): () => void {\n nodig.set(category, (nodig.get(category) ?? 0) + 1);\n window.dispatchEvent(new Event(\"xicms:consent:nodig\"));\n return () => {\n const n = (nodig.get(category) ?? 1) - 1;\n if (n <= 0) nodig.delete(category);\n else nodig.set(category, n);\n window.dispatchEvent(new Event(\"xicms:consent:nodig\"));\n };\n}\n\n/** Meld dat dit onderdeel op toestemming voor `category` wacht (zolang `actief`). */\nexport function useMeldConsentNodig(category: string, actief: boolean): void {\n useEffect(() => {\n if (!actief || typeof window === \"undefined\") return;\n return meldNodig(category);\n }, [category, actief]);\n}\n\n/** Wacht er op deze pagina iets op toestemming? */\nexport function useConsentNodig(): boolean {\n const [ja, setJa] = useState(false);\n useEffect(() => {\n const lees = () => setJa(nodig.size > 0);\n lees();\n window.addEventListener(\"xicms:consent:nodig\", lees);\n return () => window.removeEventListener(\"xicms:consent:nodig\", lees);\n }, []);\n return ja;\n}\n"],"mappings":"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","names":[]}
|
package/dist/releaseNotes.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
const RELEASE_NOTES = [
|
|
2
|
+
{
|
|
3
|
+
versie: "8.16",
|
|
4
|
+
punten: [
|
|
5
|
+
"KvK- en btw-nummer: vul ze in bij Instellingen \u2192 Bedrijf en ze staan onderaan je site, in het contactblok en in de gegevens voor Google. Een KvK-nummer op je website is verplicht; een btw-nummer ook als je aan consumenten verkoopt.",
|
|
6
|
+
"Webwinkel: lege bedrijfsgegevens voor de factuur vullen zich aan uit Instellingen \u2192 Bedrijf \u2014 je hoeft ze maar op \xE9\xE9n plek in te vullen.",
|
|
7
|
+
"Cookiemelding alleen waar het nodig is: je site plaatst zelf geen tracking-cookies, dus bezoekers zien de melding alleen op een pagina met een video, kaart of boekingsmodule van een andere partij \u2014 en kunnen die ook direct op die plek toestaan. De melding valt niet meer achter een balk onderaan het scherm."
|
|
8
|
+
]
|
|
9
|
+
},
|
|
2
10
|
{
|
|
3
11
|
versie: "8.15",
|
|
4
12
|
punten: [
|
package/dist/releaseNotes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/releaseNotes.ts"],"sourcesContent":["/**\n * \"Wat is nieuw\" (7.24) — korte, klantgerichte notities per release, getoond in /beheer.\n *\n * Features die je bouwt zonder dat de klant het hoort, bestaan voor die klant niet. Dit is de\n * enige plek waar dat verteld wordt; `CoreVersion` toont de notitie van de huidige minor één keer.\n * Alleen wat de KLANT merkt, in zijn woorden — geen interne refactors.\n */\nexport interface ReleaseNote {\n /** major.minor — patches tellen niet als \"nieuw\". */\n versie: string;\n punten: string[];\n}\n\nexport const RELEASE_NOTES: ReleaseNote[] = [\n {\n versie: \"8.15\",\n punten: [\n \"Nieuw blok Beschikbaarheid (module Verhuur): een kalender met vrije en bezette data uit de agenda-link van Airbnb, Booking.com of je boekingssysteem. Bezoekers kiezen aankomst en vertrek en vragen direct aan — het formulier staat dan al ingevuld.\",\n \"Agenda: activiteiten die elke week, om de week of elke maand terugkomen zet je er één keer in.\",\n ],\n },\n {\n versie: \"8.14\",\n punten: [\n \"Snel starten: een nieuwe site kies je als kapper, restaurant, dienstverlener, webwinkel, vakantiehuis, vereniging, praktijk of creatief — en krijgt meteen de pagina's, het menu en een passende huisstijl, als werkversie om in te vullen.\",\n \"Nieuwe paginasjablonen: tarieven, menukaart, afspraak maken, team, verblijf, werk en agenda.\",\n \"Formulieren: een veld voor een periode (aankomst – vertrek), verborgen velden, vooraf ingevuld via een link (/contact?huisje=…), en een kopie naar meerdere e-mailadressen.\",\n \"Agenda: datum en tijd kiezen met een kalender, prijs, foto, link en status (afgelast, verplaatst, online) — en vollediger in Google.\",\n \"Boeken voor verhuur via Lodgify, Booking Experts, Tommy of Bookingmotor. Webwinkels kunnen nette productadressen gebruiken (/product/naam).\",\n ],\n },\n {\n versie: \"8.13\",\n punten: [\n \"Nieuw: Instellingen → Bedrijf. Vul adres, telefoon en openingstijden één keer in — ze verschijnen in Google, in het contact- en openingstijdenblok en onderaan je site. Ook feestdagen en vakanties, met 'Nu geopend' dat daar rekening mee houdt.\",\n \"Nieuw blok Prijslijst / menukaart: behandelingen, gerechten of tarieven met prijs, duur en labels — als lijst, menukaart of in kolommen. Regels voeg je op de pagina zelf toe.\",\n \"Het openingsbeeld kan nu ook met de tekst náást de foto, en met korte vertrouwenspunten (✓ Gratis offerte). Nieuw blok Sectiekop voor een nette kop boven elke sectie.\",\n \"Het reviewblok krijgt een knop 'Laat een review achter' als je in Instellingen → Bedrijf je Google-reviewlink invult.\",\n \"Instellingen → Kop: logo, de knop rechtsboven en een belbalk op de telefoon (voor sites met de standaardkop).\",\n ],\n },\n {\n versie: \"8.12\",\n punten: [\n \"Nieuwsberichten staan op de site als een net raster van kaarten met even grote foto's.\",\n \"'Omlijnde' knoppen zijn nu echt omlijnd, en knoptekst op je merkkleur is altijd leesbaar — ook bij een lichte kleur.\",\n \"Vóór publiceren krijg je een seintje als er nog voorbeeldtekst op de pagina staat, zoals 'Dienst één'.\",\n \"Het beheer is rustiger en duidelijker: iconen in plaats van losse tekens, knoppen die laten zien dat ze bezig zijn, uitnodigende lege lijsten en foutmeldingen in gewone woorden.\",\n ],\n },\n {\n versie: \"8.11\",\n punten: [\n \"Het potlood bewerkt nu alles op de pagina zelf: tekst, maar ook kaarten, teamleden of reviews toevoegen, verplaatsen en weghalen, waar een knop heen gaat, en snelle keuzes zoals het aantal kolommen. Het tandwiel opent alle instellingen in de zijbalk.\",\n \"Onder elk blok verschijnt een + om iets toe te voegen. De blokkiezer heeft een zoekveld, groepen en per blok een korte uitleg, plus kant-en-klare secties.\",\n \"Alle blokinstellingen zijn in het Nederlands, met duidelijke keuzes (zoals 'Ingekaderd' of 'Raster') en knoppen voor kolommen en sterren.\",\n \"Een rand met de naam van het blok laat zien waar een blok begint en eindigt; een leeg blok is zichtbaar in plaats van verdwenen.\",\n \"Wat je nog niet publiceerde is subtiel gemarkeerd in plaats van rood. De cookiemelding staat tijdens het bewerken niet meer in de weg.\",\n ],\n },\n {\n versie: \"8.10\",\n punten: [\n \"Veiliger bewerken: de homepage kan niet meer per ongeluk offline of in de prullenbak, en bij het weggooien van een pagina of formulier zie je eerst waar het nog gebruikt wordt (menu, footer, andere pagina's).\",\n \"Huisstijl, menu en footer hebben een knop 'Vorige terugzetten' — en gedeelde blokken bewaren hun eerdere versies.\",\n \"Knoppen en links kies je overal uit de pagina's van je site, in plaats van een adres te typen.\",\n \"Een blok met een achtergrondkleur zet de tekst vanzelf in een leesbare kleur en krijgt wat lucht.\",\n \"De controle vóór publiceren ziet nu ook links in lopende tekst, knoppen zonder bestemming, links naar pagina's die nog niet live staan en meer dan één hoofdkop — ook bij 'Alles publiceren'.\",\n \"Foutmeldingen zeggen in gewone woorden welk veld er niet klopt.\",\n ],\n },\n {\n versie: \"8.6\",\n punten: [\n \"Teksten op de site zelf bewerk je nu met dezelfde editor als in het beheer: typ / voor een kop, lijst, tabel of foto, plak of sleep foto's erin, en kies of een link in een nieuw tabblad opent.\",\n \"Opsommingen en citaten staan op de site nu ook echt zo: met bolletjes, nummers en inspringing.\",\n \"Het beheer is rustiger: 'Nieuw in het beheer' is één regel op Start, je account staat bovenaan, en op een telefoon passen alle kaarten weer op het scherm.\",\n ],\n },\n {\n versie: \"8.5\",\n punten: [\n \"Sneller schrijven: typ / in een tekstvak voor een kop, lijst, tabel, foto of link. Selecteer tekst voor een klein menu met vet, cursief en link.\",\n \"Foto's kun je nu direct in een tekst plakken of erin slepen; ze worden vanzelf geüpload.\",\n \"Nieuwsartikelen schrijf je in een rustige opzet: titel en tekst links, datum, coverfoto en samenvatting ernaast.\",\n \"Leesbaarheid: onder nieuws en bij elke pagina zie je hoe makkelijk je tekst leest (taalniveau, leestijd en tips).\",\n \"Links kun je laten openen in een nieuw tabblad.\",\n \"Media: sorteer op datum, naam of grootte, zie grootte en datum per foto, selecteer er meerdere tegelijk om te verwijderen, en download een foto.\",\n \"Een preview-link kopieer, open of deel je met één knop, en je ziet tot wanneer hij werkt.\",\n ],\n },\n {\n versie: \"8.4\",\n punten: [\n \"Inplannen: zet een pagina op een gekozen moment vanzelf live of offline (Pagina's → pagina → Inplannen).\",\n \"Vóór publiceren krijg je een seintje bij foto's zonder beschrijving, links naar pagina's die niet bestaan en een ontbrekende omschrijving voor Google.\",\n \"Statistieken toont adressen die bezoekers niet vonden — met één klik stuur je ze door naar een bestaande pagina.\",\n \"Vóór je een oude versie terugzet, zie je wat er verandert.\",\n \"In Media filter je op foto's zonder beschrijving, en op de site vult een foto zonder beschrijving die vanzelf aan uit de bibliotheek.\",\n \"Zoeken in het beheer: druk Ctrl+K (of Cmd+K) of klik op Zoeken, en typ een pagina, artikel, formulier of onderdeel.\",\n ],\n },\n {\n versie: \"8.3\",\n punten: [\n \"Foto's laden op telefoons veel sneller: de site kiest nu de juiste maat in plaats van altijd de grootste.\",\n \"Lettertypen kunnen via je eigen site laden, zodat bezoekers geen verbinding met Google maken (privacy).\",\n \"Het kruimelpad toont de echte paginatitels.\",\n \"Raakt de verbinding weg tijdens het bewerken, dan zie je dat — en je werk wordt vanzelf bewaard zodra je weer online bent.\",\n \"De bezoekersstatistieken tellen geen bots en testtools meer mee.\",\n ],\n },\n {\n versie: \"8.2\",\n punten: [\n \"Een foto kiezen of wijzigen gaat overal hetzelfde: je ziet de foto zelf, met “Wijzigen” en “Weghalen”.\",\n \"Een link plaatsen doe je in een eigen venster: kies een pagina van je site of typ een adres.\",\n \"Heb je iets nog niet opgeslagen en klik je op een ander tabblad, dan vraagt het beheer eerst of dat de bedoeling is.\",\n \"Een nieuwe pagina zet je in één stap ook in het menu, en met “Nu bewerken op de site” ga je er direct heen.\",\n \"Een blok weghalen in de editor kan zonder bevestiging: “Ongedaan maken” zet het terug.\",\n \"Formulieren: keuzes vul je per regel in, en inzendingen tonen de vragen in plaats van technische namen.\",\n \"Nieuws: een artikel met een datum in de toekomst heet nu “ingepland”, en datums staan in het Nederlands.\",\n \"Op de startpagina opent een klik op een pagina, bestelling of formulier meteen dat item.\",\n \"Op telefoon en tablet zijn de knoppen in de editor groter, en uitloggen kan ook op een klein scherm.\",\n ],\n },\n {\n versie: \"8.1\",\n punten: [\n \"Een formulier verwijderen werkt weer, en een leeggemaakte bedankt-pagina of kopie-adres blijft nu ook echt leeg.\",\n \"Een logo, deelafbeelding of lettertype dat je weghaalt in Instellingen komt niet meer terug.\",\n \"Wie een formulier invult krijgt een duidelijke melding als er iets niet klopt (bijvoorbeeld een ongeldig e-mailadres).\",\n \"Werk je in Instellingen of Menu terwijl iemand anders iets opslaat, dan raak je je wijzigingen niet meer kwijt: je kiest zelf welke versie blijft.\",\n \"Lukt het laden van een lijst niet, dan zie je dat (met een knop om het opnieuw te proberen) in plaats van een lege lijst.\",\n \"Telefoonfoto's staan na het uploaden altijd rechtop.\",\n ],\n },\n {\n versie: \"8.0\",\n punten: [\n \"Beschrijvingen (alt-teksten) bij foto's in Media worden nu echt bewaard zodra je het veld verlaat.\",\n \"Het adres van een pagina wordt automatisch netjes gemaakt (kleine letters, streepjes).\",\n \"Met het toetsenbord zie je nu altijd waar je bent, en schermlezers noemen elk veld bij zijn naam.\",\n \"Formulieren op je site vullen naam, e-mail en adres automatisch in, en telefoonvelden tonen een cijfertoetsenbord.\",\n ],\n },\n {\n versie: \"7.33\",\n punten: [\n \"Webwinkel: klanten kunnen kiezen voor afhalen. Jij stelt onder Webwinkel → Instellingen de toelichting in (adres, tijden).\",\n \"In de bestelmails is elk product nu klikbaar: je komt direct op de juiste productpagina, met de bestelde variant al gekozen.\",\n \"In een bestelling in het beheer zijn de producten ook aanklikbaar.\",\n ],\n },\n {\n versie: \"7.24\",\n punten: [\n \"Het menu dat je onder Menu instelt staat nu écht op de site — met submenu's.\",\n \"Bij Pagina's zie je meteen welke pagina's wijzigingen hebben die nog niet live staan.\",\n \"Een pagina kun je offline halen (en later weer publiceren) of naar de prullenbak doen — 30 dagen te herstellen.\",\n \"Verander je het adres van een pagina, dan verwijst het oude adres automatisch door.\",\n \"Deel een preview-link van een werkversie met iemand zonder account.\",\n \"Kies een focuspunt op een foto zodat het juiste deel in beeld blijft op mobiel.\",\n \"Formulieren: inzendingen exporteren als CSV, verwijderen, en zien wat nieuw is.\",\n \"Statistieken tonen nu ook waar bezoekers vandaan komen en op welk apparaat.\",\n ],\n },\n {\n versie: \"7.23\",\n punten: [\"Per productoptie (bijvoorbeeld Print of Kleur) kun je aangeven dat elke waarde een eigen foto heeft.\"],\n },\n];\n\n/** De notitie die bij deze engine-versie hoort (op major.minor). */\nexport function releaseNoteVoor(versie: string): ReleaseNote | null {\n const mm = versie.split(\".\").slice(0, 2).join(\".\");\n return RELEASE_NOTES.find((n) => n.versie === mm) ?? null;\n}\n"],"mappings":"AAaO,MAAM,gBAA+B;AAAA,EAC1C;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ,CAAC,sGAAsG;AAAA,EACjH;AACF;AAGO,SAAS,gBAAgB,QAAoC;AAClE,QAAM,KAAK,OAAO,MAAM,GAAG,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AACjD,SAAO,cAAc,KAAK,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK;AACvD;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/releaseNotes.ts"],"sourcesContent":["/**\n * \"Wat is nieuw\" (7.24) — korte, klantgerichte notities per release, getoond in /beheer.\n *\n * Features die je bouwt zonder dat de klant het hoort, bestaan voor die klant niet. Dit is de\n * enige plek waar dat verteld wordt; `CoreVersion` toont de notitie van de huidige minor één keer.\n * Alleen wat de KLANT merkt, in zijn woorden — geen interne refactors.\n */\nexport interface ReleaseNote {\n /** major.minor — patches tellen niet als \"nieuw\". */\n versie: string;\n punten: string[];\n}\n\nexport const RELEASE_NOTES: ReleaseNote[] = [\n {\n versie: \"8.16\",\n punten: [\n \"KvK- en btw-nummer: vul ze in bij Instellingen → Bedrijf en ze staan onderaan je site, in het contactblok en in de gegevens voor Google. Een KvK-nummer op je website is verplicht; een btw-nummer ook als je aan consumenten verkoopt.\",\n \"Webwinkel: lege bedrijfsgegevens voor de factuur vullen zich aan uit Instellingen → Bedrijf — je hoeft ze maar op één plek in te vullen.\",\n \"Cookiemelding alleen waar het nodig is: je site plaatst zelf geen tracking-cookies, dus bezoekers zien de melding alleen op een pagina met een video, kaart of boekingsmodule van een andere partij — en kunnen die ook direct op die plek toestaan. De melding valt niet meer achter een balk onderaan het scherm.\",\n ],\n },\n {\n versie: \"8.15\",\n punten: [\n \"Nieuw blok Beschikbaarheid (module Verhuur): een kalender met vrije en bezette data uit de agenda-link van Airbnb, Booking.com of je boekingssysteem. Bezoekers kiezen aankomst en vertrek en vragen direct aan — het formulier staat dan al ingevuld.\",\n \"Agenda: activiteiten die elke week, om de week of elke maand terugkomen zet je er één keer in.\",\n ],\n },\n {\n versie: \"8.14\",\n punten: [\n \"Snel starten: een nieuwe site kies je als kapper, restaurant, dienstverlener, webwinkel, vakantiehuis, vereniging, praktijk of creatief — en krijgt meteen de pagina's, het menu en een passende huisstijl, als werkversie om in te vullen.\",\n \"Nieuwe paginasjablonen: tarieven, menukaart, afspraak maken, team, verblijf, werk en agenda.\",\n \"Formulieren: een veld voor een periode (aankomst – vertrek), verborgen velden, vooraf ingevuld via een link (/contact?huisje=…), en een kopie naar meerdere e-mailadressen.\",\n \"Agenda: datum en tijd kiezen met een kalender, prijs, foto, link en status (afgelast, verplaatst, online) — en vollediger in Google.\",\n \"Boeken voor verhuur via Lodgify, Booking Experts, Tommy of Bookingmotor. Webwinkels kunnen nette productadressen gebruiken (/product/naam).\",\n ],\n },\n {\n versie: \"8.13\",\n punten: [\n \"Nieuw: Instellingen → Bedrijf. Vul adres, telefoon en openingstijden één keer in — ze verschijnen in Google, in het contact- en openingstijdenblok en onderaan je site. Ook feestdagen en vakanties, met 'Nu geopend' dat daar rekening mee houdt.\",\n \"Nieuw blok Prijslijst / menukaart: behandelingen, gerechten of tarieven met prijs, duur en labels — als lijst, menukaart of in kolommen. Regels voeg je op de pagina zelf toe.\",\n \"Het openingsbeeld kan nu ook met de tekst náást de foto, en met korte vertrouwenspunten (✓ Gratis offerte). Nieuw blok Sectiekop voor een nette kop boven elke sectie.\",\n \"Het reviewblok krijgt een knop 'Laat een review achter' als je in Instellingen → Bedrijf je Google-reviewlink invult.\",\n \"Instellingen → Kop: logo, de knop rechtsboven en een belbalk op de telefoon (voor sites met de standaardkop).\",\n ],\n },\n {\n versie: \"8.12\",\n punten: [\n \"Nieuwsberichten staan op de site als een net raster van kaarten met even grote foto's.\",\n \"'Omlijnde' knoppen zijn nu echt omlijnd, en knoptekst op je merkkleur is altijd leesbaar — ook bij een lichte kleur.\",\n \"Vóór publiceren krijg je een seintje als er nog voorbeeldtekst op de pagina staat, zoals 'Dienst één'.\",\n \"Het beheer is rustiger en duidelijker: iconen in plaats van losse tekens, knoppen die laten zien dat ze bezig zijn, uitnodigende lege lijsten en foutmeldingen in gewone woorden.\",\n ],\n },\n {\n versie: \"8.11\",\n punten: [\n \"Het potlood bewerkt nu alles op de pagina zelf: tekst, maar ook kaarten, teamleden of reviews toevoegen, verplaatsen en weghalen, waar een knop heen gaat, en snelle keuzes zoals het aantal kolommen. Het tandwiel opent alle instellingen in de zijbalk.\",\n \"Onder elk blok verschijnt een + om iets toe te voegen. De blokkiezer heeft een zoekveld, groepen en per blok een korte uitleg, plus kant-en-klare secties.\",\n \"Alle blokinstellingen zijn in het Nederlands, met duidelijke keuzes (zoals 'Ingekaderd' of 'Raster') en knoppen voor kolommen en sterren.\",\n \"Een rand met de naam van het blok laat zien waar een blok begint en eindigt; een leeg blok is zichtbaar in plaats van verdwenen.\",\n \"Wat je nog niet publiceerde is subtiel gemarkeerd in plaats van rood. De cookiemelding staat tijdens het bewerken niet meer in de weg.\",\n ],\n },\n {\n versie: \"8.10\",\n punten: [\n \"Veiliger bewerken: de homepage kan niet meer per ongeluk offline of in de prullenbak, en bij het weggooien van een pagina of formulier zie je eerst waar het nog gebruikt wordt (menu, footer, andere pagina's).\",\n \"Huisstijl, menu en footer hebben een knop 'Vorige terugzetten' — en gedeelde blokken bewaren hun eerdere versies.\",\n \"Knoppen en links kies je overal uit de pagina's van je site, in plaats van een adres te typen.\",\n \"Een blok met een achtergrondkleur zet de tekst vanzelf in een leesbare kleur en krijgt wat lucht.\",\n \"De controle vóór publiceren ziet nu ook links in lopende tekst, knoppen zonder bestemming, links naar pagina's die nog niet live staan en meer dan één hoofdkop — ook bij 'Alles publiceren'.\",\n \"Foutmeldingen zeggen in gewone woorden welk veld er niet klopt.\",\n ],\n },\n {\n versie: \"8.6\",\n punten: [\n \"Teksten op de site zelf bewerk je nu met dezelfde editor als in het beheer: typ / voor een kop, lijst, tabel of foto, plak of sleep foto's erin, en kies of een link in een nieuw tabblad opent.\",\n \"Opsommingen en citaten staan op de site nu ook echt zo: met bolletjes, nummers en inspringing.\",\n \"Het beheer is rustiger: 'Nieuw in het beheer' is één regel op Start, je account staat bovenaan, en op een telefoon passen alle kaarten weer op het scherm.\",\n ],\n },\n {\n versie: \"8.5\",\n punten: [\n \"Sneller schrijven: typ / in een tekstvak voor een kop, lijst, tabel, foto of link. Selecteer tekst voor een klein menu met vet, cursief en link.\",\n \"Foto's kun je nu direct in een tekst plakken of erin slepen; ze worden vanzelf geüpload.\",\n \"Nieuwsartikelen schrijf je in een rustige opzet: titel en tekst links, datum, coverfoto en samenvatting ernaast.\",\n \"Leesbaarheid: onder nieuws en bij elke pagina zie je hoe makkelijk je tekst leest (taalniveau, leestijd en tips).\",\n \"Links kun je laten openen in een nieuw tabblad.\",\n \"Media: sorteer op datum, naam of grootte, zie grootte en datum per foto, selecteer er meerdere tegelijk om te verwijderen, en download een foto.\",\n \"Een preview-link kopieer, open of deel je met één knop, en je ziet tot wanneer hij werkt.\",\n ],\n },\n {\n versie: \"8.4\",\n punten: [\n \"Inplannen: zet een pagina op een gekozen moment vanzelf live of offline (Pagina's → pagina → Inplannen).\",\n \"Vóór publiceren krijg je een seintje bij foto's zonder beschrijving, links naar pagina's die niet bestaan en een ontbrekende omschrijving voor Google.\",\n \"Statistieken toont adressen die bezoekers niet vonden — met één klik stuur je ze door naar een bestaande pagina.\",\n \"Vóór je een oude versie terugzet, zie je wat er verandert.\",\n \"In Media filter je op foto's zonder beschrijving, en op de site vult een foto zonder beschrijving die vanzelf aan uit de bibliotheek.\",\n \"Zoeken in het beheer: druk Ctrl+K (of Cmd+K) of klik op Zoeken, en typ een pagina, artikel, formulier of onderdeel.\",\n ],\n },\n {\n versie: \"8.3\",\n punten: [\n \"Foto's laden op telefoons veel sneller: de site kiest nu de juiste maat in plaats van altijd de grootste.\",\n \"Lettertypen kunnen via je eigen site laden, zodat bezoekers geen verbinding met Google maken (privacy).\",\n \"Het kruimelpad toont de echte paginatitels.\",\n \"Raakt de verbinding weg tijdens het bewerken, dan zie je dat — en je werk wordt vanzelf bewaard zodra je weer online bent.\",\n \"De bezoekersstatistieken tellen geen bots en testtools meer mee.\",\n ],\n },\n {\n versie: \"8.2\",\n punten: [\n \"Een foto kiezen of wijzigen gaat overal hetzelfde: je ziet de foto zelf, met “Wijzigen” en “Weghalen”.\",\n \"Een link plaatsen doe je in een eigen venster: kies een pagina van je site of typ een adres.\",\n \"Heb je iets nog niet opgeslagen en klik je op een ander tabblad, dan vraagt het beheer eerst of dat de bedoeling is.\",\n \"Een nieuwe pagina zet je in één stap ook in het menu, en met “Nu bewerken op de site” ga je er direct heen.\",\n \"Een blok weghalen in de editor kan zonder bevestiging: “Ongedaan maken” zet het terug.\",\n \"Formulieren: keuzes vul je per regel in, en inzendingen tonen de vragen in plaats van technische namen.\",\n \"Nieuws: een artikel met een datum in de toekomst heet nu “ingepland”, en datums staan in het Nederlands.\",\n \"Op de startpagina opent een klik op een pagina, bestelling of formulier meteen dat item.\",\n \"Op telefoon en tablet zijn de knoppen in de editor groter, en uitloggen kan ook op een klein scherm.\",\n ],\n },\n {\n versie: \"8.1\",\n punten: [\n \"Een formulier verwijderen werkt weer, en een leeggemaakte bedankt-pagina of kopie-adres blijft nu ook echt leeg.\",\n \"Een logo, deelafbeelding of lettertype dat je weghaalt in Instellingen komt niet meer terug.\",\n \"Wie een formulier invult krijgt een duidelijke melding als er iets niet klopt (bijvoorbeeld een ongeldig e-mailadres).\",\n \"Werk je in Instellingen of Menu terwijl iemand anders iets opslaat, dan raak je je wijzigingen niet meer kwijt: je kiest zelf welke versie blijft.\",\n \"Lukt het laden van een lijst niet, dan zie je dat (met een knop om het opnieuw te proberen) in plaats van een lege lijst.\",\n \"Telefoonfoto's staan na het uploaden altijd rechtop.\",\n ],\n },\n {\n versie: \"8.0\",\n punten: [\n \"Beschrijvingen (alt-teksten) bij foto's in Media worden nu echt bewaard zodra je het veld verlaat.\",\n \"Het adres van een pagina wordt automatisch netjes gemaakt (kleine letters, streepjes).\",\n \"Met het toetsenbord zie je nu altijd waar je bent, en schermlezers noemen elk veld bij zijn naam.\",\n \"Formulieren op je site vullen naam, e-mail en adres automatisch in, en telefoonvelden tonen een cijfertoetsenbord.\",\n ],\n },\n {\n versie: \"7.33\",\n punten: [\n \"Webwinkel: klanten kunnen kiezen voor afhalen. Jij stelt onder Webwinkel → Instellingen de toelichting in (adres, tijden).\",\n \"In de bestelmails is elk product nu klikbaar: je komt direct op de juiste productpagina, met de bestelde variant al gekozen.\",\n \"In een bestelling in het beheer zijn de producten ook aanklikbaar.\",\n ],\n },\n {\n versie: \"7.24\",\n punten: [\n \"Het menu dat je onder Menu instelt staat nu écht op de site — met submenu's.\",\n \"Bij Pagina's zie je meteen welke pagina's wijzigingen hebben die nog niet live staan.\",\n \"Een pagina kun je offline halen (en later weer publiceren) of naar de prullenbak doen — 30 dagen te herstellen.\",\n \"Verander je het adres van een pagina, dan verwijst het oude adres automatisch door.\",\n \"Deel een preview-link van een werkversie met iemand zonder account.\",\n \"Kies een focuspunt op een foto zodat het juiste deel in beeld blijft op mobiel.\",\n \"Formulieren: inzendingen exporteren als CSV, verwijderen, en zien wat nieuw is.\",\n \"Statistieken tonen nu ook waar bezoekers vandaan komen en op welk apparaat.\",\n ],\n },\n {\n versie: \"7.23\",\n punten: [\"Per productoptie (bijvoorbeeld Print of Kleur) kun je aangeven dat elke waarde een eigen foto heeft.\"],\n },\n];\n\n/** De notitie die bij deze engine-versie hoort (op major.minor). */\nexport function releaseNoteVoor(versie: string): ReleaseNote | null {\n const mm = versie.split(\".\").slice(0, 2).join(\".\");\n return RELEASE_NOTES.find((n) => n.versie === mm) ?? null;\n}\n"],"mappings":"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","names":[]}
|
package/dist/schema/blocks.d.ts
CHANGED
|
@@ -1044,6 +1044,7 @@ export declare const ContactBlockSchema: z.ZodMiniObject<{
|
|
|
1044
1044
|
lng: z.ZodMiniNumber<number>;
|
|
1045
1045
|
}, z.core.$strip>>;
|
|
1046
1046
|
jsonLd: z.ZodMiniDefault<z.ZodMiniBoolean<boolean>>;
|
|
1047
|
+
bedrijfsnummers: z.ZodMiniDefault<z.ZodMiniBoolean<boolean>>;
|
|
1047
1048
|
}, z.core.$strip>;
|
|
1048
1049
|
export declare const MapBlockSchema: z.ZodMiniObject<{
|
|
1049
1050
|
_key: z.ZodMiniString<string>;
|
|
@@ -3424,6 +3425,7 @@ export declare const BuiltinBlockSchema: z.ZodMiniDiscriminatedUnion<[z.ZodMiniO
|
|
|
3424
3425
|
lng: z.ZodMiniNumber<number>;
|
|
3425
3426
|
}, z.core.$strip>>;
|
|
3426
3427
|
jsonLd: z.ZodMiniDefault<z.ZodMiniBoolean<boolean>>;
|
|
3428
|
+
bedrijfsnummers: z.ZodMiniDefault<z.ZodMiniBoolean<boolean>>;
|
|
3427
3429
|
}, z.core.$strip>, z.ZodMiniObject<{
|
|
3428
3430
|
_key: z.ZodMiniString<string>;
|
|
3429
3431
|
_style: z.ZodMiniOptional<z.ZodMiniObject<{
|
|
@@ -5786,6 +5788,7 @@ export declare const AnyBlockSchema: z.ZodMiniUnion<readonly [z.ZodMiniDiscrimin
|
|
|
5786
5788
|
lng: z.ZodMiniNumber<number>;
|
|
5787
5789
|
}, z.core.$strip>>;
|
|
5788
5790
|
jsonLd: z.ZodMiniDefault<z.ZodMiniBoolean<boolean>>;
|
|
5791
|
+
bedrijfsnummers: z.ZodMiniDefault<z.ZodMiniBoolean<boolean>>;
|
|
5789
5792
|
}, z.core.$strip>, z.ZodMiniObject<{
|
|
5790
5793
|
_key: z.ZodMiniString<string>;
|
|
5791
5794
|
_style: z.ZodMiniOptional<z.ZodMiniObject<{
|
package/dist/schema/blocks.js
CHANGED
|
@@ -382,7 +382,9 @@ const ContactBlockSchema = z.object({
|
|
|
382
382
|
/** Coördinaten voor de JSON-LD (Google toont het bedrijf dan op de juiste plek). */
|
|
383
383
|
geo: z.optional(z.object({ lat: z.number().check(z.minimum(-90)).check(z.maximum(90)), lng: z.number().check(z.minimum(-180)).check(z.maximum(180)) })),
|
|
384
384
|
/** Voeg ContactPoint/LocalBusiness-JSON-LD toe. */
|
|
385
|
-
jsonLd: z._default(z.boolean(), false)
|
|
385
|
+
jsonLd: z._default(z.boolean(), false),
|
|
386
|
+
/** 8.16: KvK- en btw-nummer uit het bedrijfsprofiel tonen. */
|
|
387
|
+
bedrijfsnummers: z._default(z.boolean(), true)
|
|
386
388
|
});
|
|
387
389
|
const MapBlockSchema = z.object({
|
|
388
390
|
...blockBase2,
|