@xiwebdesign/xicms-core 7.23.0 → 7.24.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/admin/AdminApp.js +2 -1
- package/dist/admin/AdminApp.js.map +1 -1
- package/dist/admin/CoreVersion.d.ts +6 -0
- package/dist/admin/CoreVersion.js +42 -1
- package/dist/admin/CoreVersion.js.map +1 -1
- package/dist/admin/SuperadminPanel.js +19 -3
- package/dist/admin/SuperadminPanel.js.map +1 -1
- package/dist/admin/dashboard/DashboardPanel.js +46 -9
- package/dist/admin/dashboard/DashboardPanel.js.map +1 -1
- package/dist/admin/forms/FormsPanel.d.ts +9 -0
- package/dist/admin/forms/FormsPanel.js +102 -16
- package/dist/admin/forms/FormsPanel.js.map +1 -1
- package/dist/admin/forms/gezien.d.ts +3 -0
- package/dist/admin/forms/gezien.js +22 -0
- package/dist/admin/forms/gezien.js.map +1 -0
- package/dist/admin/lib/kleur.d.ts +32 -0
- package/dist/admin/lib/kleur.js +60 -0
- package/dist/admin/lib/kleur.js.map +1 -0
- package/dist/admin/lib/pagesClient.d.ts +32 -0
- package/dist/admin/lib/pagesClient.js +60 -1
- package/dist/admin/lib/pagesClient.js.map +1 -1
- package/dist/admin/lib/seoHulp.d.ts +28 -0
- package/dist/admin/lib/seoHulp.js +33 -0
- package/dist/admin/lib/seoHulp.js.map +1 -0
- package/dist/admin/media/MediaManager.js +133 -51
- package/dist/admin/media/MediaManager.js.map +1 -1
- package/dist/admin/pages/PageEditor.d.ts +14 -0
- package/dist/admin/pages/PageEditor.js +192 -123
- package/dist/admin/pages/PageEditor.js.map +1 -1
- package/dist/admin/pages/PagesPanel.js +88 -40
- package/dist/admin/pages/PagesPanel.js.map +1 -1
- package/dist/admin/settings/SettingsPanel.js +100 -19
- package/dist/admin/settings/SettingsPanel.js.map +1 -1
- package/dist/admin/shop/ProductsTab.js +1 -1
- package/dist/admin/shop/ProductsTab.js.map +1 -1
- package/dist/admin/shop/ShopPanel.js +5 -2
- package/dist/admin/shop/ShopPanel.js.map +1 -1
- package/dist/admin/stats/StatsPanel.js +30 -2
- package/dist/admin/stats/StatsPanel.js.map +1 -1
- package/dist/admin/stats/lib.d.ts +14 -1
- package/dist/admin/stats/lib.js +46 -2
- package/dist/admin/stats/lib.js.map +1 -1
- package/dist/admin/visual/VisualEditor.js +80 -4
- package/dist/admin/visual/VisualEditor.js.map +1 -1
- package/dist/admin.css +1 -1
- package/dist/blocks/builtins/FormBlockRender.js +8 -2
- package/dist/blocks/builtins/FormBlockRender.js.map +1 -1
- package/dist/blocks/builtins/lib/submitForm.d.ts +3 -1
- package/dist/blocks/builtins/lib/submitForm.js +2 -2
- package/dist/blocks/builtins/lib/submitForm.js.map +1 -1
- package/dist/components/EditBar.d.ts +3 -1
- package/dist/components/EditBar.js +30 -4
- package/dist/components/EditBar.js.map +1 -1
- package/dist/components/News.js +8 -4
- package/dist/components/News.js.map +1 -1
- package/dist/components/PageView.d.ts +5 -0
- package/dist/components/PageView.js +24 -2
- package/dist/components/PageView.js.map +1 -1
- package/dist/components/ResponsiveImage.d.ts +1 -1
- package/dist/components/ResponsiveImage.js +3 -1
- package/dist/components/ResponsiveImage.js.map +1 -1
- package/dist/components/site/SiteNav.d.ts +29 -0
- package/dist/components/site/SiteNav.js +82 -0
- package/dist/components/site/SiteNav.js.map +1 -0
- package/dist/content/types.d.ts +8 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/media/ImageContext.d.ts +1 -6
- package/dist/media/ImageContext.js +6 -1
- package/dist/media/ImageContext.js.map +1 -1
- package/dist/releaseNotes.d.ts +15 -0
- package/dist/releaseNotes.js +28 -0
- package/dist/releaseNotes.js.map +1 -0
- package/dist/schema/settings.d.ts +105 -0
- package/dist/schema/settings.js +45 -5
- package/dist/schema/settings.js.map +1 -1
- package/dist/schema/siteConfig.d.ts +9 -0
- package/dist/server/blockData.js +6 -2
- package/dist/server/blockData.js.map +1 -1
- package/dist/server/draft.d.ts +11 -0
- package/dist/server/draft.js +20 -1
- package/dist/server/draft.js.map +1 -1
- package/dist/server/index.d.ts +3 -1
- package/dist/server/index.js +7 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/pages.js +4 -1
- package/dist/server/pages.js.map +1 -1
- package/dist/server/previewToken.d.ts +34 -0
- package/dist/server/previewToken.js +44 -0
- package/dist/server/previewToken.js.map +1 -0
- package/dist/server/redirects.d.ts +15 -0
- package/dist/server/redirects.js +15 -0
- package/dist/server/redirects.js.map +1 -0
- package/dist/styles.css +110 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -12,7 +12,11 @@ import {
|
|
|
12
12
|
deltaTovVorige,
|
|
13
13
|
huidigMaandId,
|
|
14
14
|
maandLabel,
|
|
15
|
-
topPaden
|
|
15
|
+
topPaden,
|
|
16
|
+
topVan,
|
|
17
|
+
verwijzerLabel,
|
|
18
|
+
apparaatLabel,
|
|
19
|
+
gebeurtenisLabel
|
|
16
20
|
} from "./lib.js";
|
|
17
21
|
function StatsPanel({ siteId }) {
|
|
18
22
|
const [months, setMonths] = useState([]);
|
|
@@ -52,6 +56,10 @@ function StatsPanel({ siteId }) {
|
|
|
52
56
|
const verstreken = isHuidige ? Math.max(1, vandaagDag) : nDagen;
|
|
53
57
|
const delta = deltaTovVorige(maand, vorige, isHuidige ? vandaagDag : nDagen);
|
|
54
58
|
const paden = useMemo(() => topPaden(maand, 8), [maand]);
|
|
59
|
+
const verwijzers = useMemo(() => topVan(maand?.refs, 8, verwijzerLabel), [maand]);
|
|
60
|
+
const apparaten = useMemo(() => topVan(maand?.devices, 3, apparaatLabel), [maand]);
|
|
61
|
+
const gebeurtenissen = useMemo(() => topVan(maand?.events, 6, gebeurtenisLabel), [maand]);
|
|
62
|
+
const conversie = maand?.events?.formulier || maand?.events?.bestelling ? ((maand?.events?.formulier ?? 0) + (maand?.events?.bestelling ?? 0)) / Math.max(1, totaalMaand) * 100 : null;
|
|
55
63
|
const trend = useMemo(
|
|
56
64
|
() => months.slice(-12).map((m) => ({ label: maandLabel(m.id), waarde: m.total ?? 0 })),
|
|
57
65
|
[months]
|
|
@@ -126,7 +134,27 @@ function StatsPanel({ siteId }) {
|
|
|
126
134
|
paden.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Nog geen data in deze maand." }) : /* @__PURE__ */ jsx(BalkjesLijst, { items: paden })
|
|
127
135
|
] })
|
|
128
136
|
] }),
|
|
129
|
-
/* @__PURE__ */
|
|
137
|
+
(verwijzers.length > 0 || apparaten.length > 0 || gebeurtenissen.length > 0) && /* @__PURE__ */ jsxs("div", { className: "grid gap-4 lg:grid-cols-3", children: [
|
|
138
|
+
/* @__PURE__ */ jsxs(Card, { children: [
|
|
139
|
+
/* @__PURE__ */ jsx("h3", { className: "mb-4 text-sm font-semibold text-slate-800", children: "Waar bezoekers vandaan komen" }),
|
|
140
|
+
verwijzers.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Nog geen data." }) : /* @__PURE__ */ jsx(BalkjesLijst, { items: verwijzers })
|
|
141
|
+
] }),
|
|
142
|
+
/* @__PURE__ */ jsxs(Card, { children: [
|
|
143
|
+
/* @__PURE__ */ jsx("h3", { className: "mb-4 text-sm font-semibold text-slate-800", children: "Apparaat" }),
|
|
144
|
+
apparaten.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Nog geen data." }) : /* @__PURE__ */ jsx(BalkjesLijst, { items: apparaten })
|
|
145
|
+
] }),
|
|
146
|
+
/* @__PURE__ */ jsxs(Card, { children: [
|
|
147
|
+
/* @__PURE__ */ jsx("h3", { className: "mb-4 text-sm font-semibold text-slate-800", children: "Wat bezoekers doen" }),
|
|
148
|
+
gebeurtenissen.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Nog geen formulieren of bestellingen deze maand." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
149
|
+
/* @__PURE__ */ jsx(BalkjesLijst, { items: gebeurtenissen }),
|
|
150
|
+
conversie !== null && /* @__PURE__ */ jsxs("p", { className: "mt-3 text-xs text-slate-500", children: [
|
|
151
|
+
Math.round(conversie * 10) / 10,
|
|
152
|
+
"% van de weergaven leidde tot een formulier of bestelling."
|
|
153
|
+
] })
|
|
154
|
+
] })
|
|
155
|
+
] })
|
|
156
|
+
] }),
|
|
157
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-slate-400", children: "Privacy-vriendelijk geteld (geen cookies, geen persoonsgegevens) \u2014 \xE9\xE9n telling per bezoeker-sessie per pagina. Van de verwijzer wordt alleen de website bewaard, nooit de pagina of zoekterm." })
|
|
130
158
|
] });
|
|
131
159
|
}
|
|
132
160
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/stats/StatsPanel.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useState } from \"react\";\nimport { collection, getDocs } from \"firebase/firestore\";\nimport { getDb } from \"../../firebase/client\";\nimport { Card } from \"../ui/primitives\";\nimport { Select } from \"../ui/select\";\nimport { AlsTabel, BalkjesLijst, DagStaven, MaandLijn, StatTegel } from \"../ui/charts\";\nimport {\n dagenInMaand,\n dagReeks,\n deltaTovVorige,\n huidigMaandId,\n maandLabel,\n topPaden,\n type MaandStat,\n} from \"./lib\";\n\n/**\n * Bezoekersstatistieken (7.17) — van drie kale tellers naar een leesbaar verhaal: tegels met\n * context (delta t.o.v. vorige maand, t/m dezelfde dag), een dag-grafiek met hover, de\n * maandtrend als lijn, en de toppagina's als balkjes. Zelfde privacy-vriendelijke bron\n * (`sites/{siteId}/stats`, eigen teller, geen cookies) — er is alleen beter naar gekeken.\n */\nexport function StatsPanel({ siteId }: { siteId: string }) {\n const [months, setMonths] = useState<MaandStat[]>([]);\n const [loading, setLoading] = useState(true);\n const [gekozen, setGekozen] = useState<string>(huidigMaandId());\n\n useEffect(() => {\n const db = getDb();\n if (!db) {\n setLoading(false);\n return;\n }\n let active = true;\n getDocs(collection(db, \"sites\", siteId, \"stats\"))\n .then((snap) => {\n if (!active) return;\n const uit = snap.docs\n .map((d) => ({ id: d.id, ...(d.data() as Omit<MaandStat, \"id\">) }))\n .filter((m) => /^\\d{4}-\\d{2}$/.test(m.id))\n .sort((a, b) => a.id.localeCompare(b.id));\n setMonths(uit);\n // Standaard de nieuwste maand mét data (meestal de huidige).\n if (uit.length && !uit.some((m) => m.id === huidigMaandId())) setGekozen(uit[uit.length - 1].id);\n setLoading(false);\n })\n .catch(() => active && setLoading(false));\n return () => {\n active = false;\n };\n }, [siteId]);\n\n const maand = months.find((m) => m.id === gekozen);\n const vorigeId = useMemo(() => {\n const [j, m] = gekozen.split(\"-\").map(Number);\n const d = new Date(Date.UTC(j, (m ?? 1) - 2, 1));\n return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, \"0\")}`;\n }, [gekozen]);\n const vorige = months.find((m) => m.id === vorigeId);\n\n const isHuidige = gekozen === huidigMaandId();\n const nDagen = dagenInMaand(gekozen);\n const vandaagDag = new Date().getUTCDate();\n const dagen = useMemo(() => dagReeks(maand, nDagen), [maand, nDagen]);\n const totaalMaand = maand?.total ?? 0;\n const totaalOoit = useMemo(() => months.reduce((s, m) => s + (m.total ?? 0), 0), [months]);\n // Gemiddelde over de dagen die er al ZIJN: een lopende maand door 31 delen praat het cijfer omlaag.\n const verstreken = isHuidige ? Math.max(1, vandaagDag) : nDagen;\n const delta = deltaTovVorige(maand, vorige, isHuidige ? vandaagDag : nDagen);\n const paden = useMemo(() => topPaden(maand, 8), [maand]);\n const trend = useMemo(\n () => months.slice(-12).map((m) => ({ label: maandLabel(m.id), waarde: m.total ?? 0 })),\n [months],\n );\n\n if (loading) return <p className=\"text-sm text-slate-500\">Laden…</p>;\n\n if (months.length === 0) {\n return (\n <Card>\n <p className=\"text-sm text-slate-600\">\n Nog geen bezoekersdata. Zodra de live site bezocht wordt, verschijnen hier de cijfers.\n </p>\n </Card>\n );\n }\n\n return (\n <div className=\"grid gap-4\">\n {/* Eén filterregel boven de inhoud: de maand waar alles hieronder over gaat. */}\n <div className=\"flex items-center justify-between gap-3\">\n <p className=\"text-sm text-slate-500\">\n Bezoek in <span className=\"font-medium text-slate-700\">{maandLabel(gekozen, true)}</span>\n {isHuidige ? \" (loopt nog)\" : \"\"}\n </p>\n <Select\n value={gekozen}\n onValueChange={setGekozen}\n placeholder=\"Maand\"\n className=\"w-44\"\n options={[...months].reverse().map((m) => ({ value: m.id, label: maandLabel(m.id, true) }))}\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n <StatTegel\n label=\"Weergaven deze maand\"\n waarde={totaalMaand}\n delta={delta}\n deltaLabel={`t.o.v. ${maandLabel(vorigeId)}${isHuidige ? \" (t/m vandaag)\" : \"\"}`}\n />\n {isHuidige ? (\n <StatTegel label=\"Vandaag\" waarde={dagen[vandaagDag - 1] ?? 0} />\n ) : (\n <StatTegel label=\"Drukste dag\" waarde={Math.max(0, ...dagen)} />\n )}\n <StatTegel label=\"Gemiddeld per dag\" waarde={Math.round(totaalMaand / verstreken)} />\n <StatTegel label=\"Totaal gemeten\" waarde={totaalOoit} />\n </div>\n\n <Card>\n <h3 className=\"mb-4 text-sm font-semibold text-slate-800\">Per dag</h3>\n {totaalMaand === 0 ? (\n <p className=\"text-sm text-slate-500\">Nog geen data in deze maand.</p>\n ) : (\n <>\n <DagStaven\n waarden={dagen}\n accentIndex={isHuidige ? vandaagDag - 1 : undefined}\n hoogte={170}\n titel={`Weergaven per dag in ${maandLabel(gekozen, true)}`}\n />\n <AlsTabel\n kop={[\"Dag\", \"Weergaven\"]}\n rijen={dagen.map((v, i) => [`${i + 1} ${maandLabel(gekozen)}`, v] as [string, number]).filter(([, v]) => v > 0)}\n />\n </>\n )}\n </Card>\n\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <Card>\n <h3 className=\"mb-4 text-sm font-semibold text-slate-800\">Maand op maand</h3>\n {trend.length < 2 ? (\n <p className=\"text-sm text-slate-500\">Nog te weinig maanden voor een trend — die groeit hier vanzelf.</p>\n ) : (\n <>\n <MaandLijn punten={trend} hoogte={170} titel=\"Weergaven per maand, laatste twaalf maanden\" />\n <AlsTabel kop={[\"Maand\", \"Weergaven\"]} rijen={trend.map((p) => [p.label, p.waarde] as [string, number])} />\n </>\n )}\n </Card>\n\n <Card>\n <h3 className=\"mb-4 text-sm font-semibold text-slate-800\">Best bezochte pagina's</h3>\n {paden.length === 0 ? (\n <p className=\"text-sm text-slate-500\">Nog geen data in deze maand.</p>\n ) : (\n <BalkjesLijst items={paden} />\n )}\n </Card>\n </div>\n\n <p className=\"text-xs text-slate-400\">\n Privacy-vriendelijk geteld (geen cookies, geen persoonsgegevens) — één telling per\n bezoeker-sessie per pagina.\n </p>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/stats/StatsPanel.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useState } from \"react\";\nimport { collection, getDocs } from \"firebase/firestore\";\nimport { getDb } from \"../../firebase/client\";\nimport { Card } from \"../ui/primitives\";\nimport { Select } from \"../ui/select\";\nimport { AlsTabel, BalkjesLijst, DagStaven, MaandLijn, StatTegel } from \"../ui/charts\";\nimport {\n dagenInMaand,\n dagReeks,\n deltaTovVorige,\n huidigMaandId,\n maandLabel,\n topPaden,\n topVan,\n verwijzerLabel,\n apparaatLabel,\n gebeurtenisLabel,\n type MaandStat,\n} from \"./lib\";\n\n/**\n * Bezoekersstatistieken (7.17) — van drie kale tellers naar een leesbaar verhaal: tegels met\n * context (delta t.o.v. vorige maand, t/m dezelfde dag), een dag-grafiek met hover, de\n * maandtrend als lijn, en de toppagina's als balkjes. Zelfde privacy-vriendelijke bron\n * (`sites/{siteId}/stats`, eigen teller, geen cookies) — er is alleen beter naar gekeken.\n */\nexport function StatsPanel({ siteId }: { siteId: string }) {\n const [months, setMonths] = useState<MaandStat[]>([]);\n const [loading, setLoading] = useState(true);\n const [gekozen, setGekozen] = useState<string>(huidigMaandId());\n\n useEffect(() => {\n const db = getDb();\n if (!db) {\n setLoading(false);\n return;\n }\n let active = true;\n getDocs(collection(db, \"sites\", siteId, \"stats\"))\n .then((snap) => {\n if (!active) return;\n const uit = snap.docs\n .map((d) => ({ id: d.id, ...(d.data() as Omit<MaandStat, \"id\">) }))\n .filter((m) => /^\\d{4}-\\d{2}$/.test(m.id))\n .sort((a, b) => a.id.localeCompare(b.id));\n setMonths(uit);\n // Standaard de nieuwste maand mét data (meestal de huidige).\n if (uit.length && !uit.some((m) => m.id === huidigMaandId())) setGekozen(uit[uit.length - 1].id);\n setLoading(false);\n })\n .catch(() => active && setLoading(false));\n return () => {\n active = false;\n };\n }, [siteId]);\n\n const maand = months.find((m) => m.id === gekozen);\n const vorigeId = useMemo(() => {\n const [j, m] = gekozen.split(\"-\").map(Number);\n const d = new Date(Date.UTC(j, (m ?? 1) - 2, 1));\n return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, \"0\")}`;\n }, [gekozen]);\n const vorige = months.find((m) => m.id === vorigeId);\n\n const isHuidige = gekozen === huidigMaandId();\n const nDagen = dagenInMaand(gekozen);\n const vandaagDag = new Date().getUTCDate();\n const dagen = useMemo(() => dagReeks(maand, nDagen), [maand, nDagen]);\n const totaalMaand = maand?.total ?? 0;\n const totaalOoit = useMemo(() => months.reduce((s, m) => s + (m.total ?? 0), 0), [months]);\n // Gemiddelde over de dagen die er al ZIJN: een lopende maand door 31 delen praat het cijfer omlaag.\n const verstreken = isHuidige ? Math.max(1, vandaagDag) : nDagen;\n const delta = deltaTovVorige(maand, vorige, isHuidige ? vandaagDag : nDagen);\n const paden = useMemo(() => topPaden(maand, 8), [maand]);\n const verwijzers = useMemo(() => topVan(maand?.refs, 8, verwijzerLabel), [maand]);\n const apparaten = useMemo(() => topVan(maand?.devices, 3, apparaatLabel), [maand]);\n const gebeurtenissen = useMemo(() => topVan(maand?.events, 6, gebeurtenisLabel), [maand]);\n const conversie = maand?.events?.formulier || maand?.events?.bestelling\n ? (((maand?.events?.formulier ?? 0) + (maand?.events?.bestelling ?? 0)) / Math.max(1, totaalMaand)) * 100\n : null;\n const trend = useMemo(\n () => months.slice(-12).map((m) => ({ label: maandLabel(m.id), waarde: m.total ?? 0 })),\n [months],\n );\n\n if (loading) return <p className=\"text-sm text-slate-500\">Laden…</p>;\n\n if (months.length === 0) {\n return (\n <Card>\n <p className=\"text-sm text-slate-600\">\n Nog geen bezoekersdata. Zodra de live site bezocht wordt, verschijnen hier de cijfers.\n </p>\n </Card>\n );\n }\n\n return (\n <div className=\"grid gap-4\">\n {/* Eén filterregel boven de inhoud: de maand waar alles hieronder over gaat. */}\n <div className=\"flex items-center justify-between gap-3\">\n <p className=\"text-sm text-slate-500\">\n Bezoek in <span className=\"font-medium text-slate-700\">{maandLabel(gekozen, true)}</span>\n {isHuidige ? \" (loopt nog)\" : \"\"}\n </p>\n <Select\n value={gekozen}\n onValueChange={setGekozen}\n placeholder=\"Maand\"\n className=\"w-44\"\n options={[...months].reverse().map((m) => ({ value: m.id, label: maandLabel(m.id, true) }))}\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n <StatTegel\n label=\"Weergaven deze maand\"\n waarde={totaalMaand}\n delta={delta}\n deltaLabel={`t.o.v. ${maandLabel(vorigeId)}${isHuidige ? \" (t/m vandaag)\" : \"\"}`}\n />\n {isHuidige ? (\n <StatTegel label=\"Vandaag\" waarde={dagen[vandaagDag - 1] ?? 0} />\n ) : (\n <StatTegel label=\"Drukste dag\" waarde={Math.max(0, ...dagen)} />\n )}\n <StatTegel label=\"Gemiddeld per dag\" waarde={Math.round(totaalMaand / verstreken)} />\n <StatTegel label=\"Totaal gemeten\" waarde={totaalOoit} />\n </div>\n\n <Card>\n <h3 className=\"mb-4 text-sm font-semibold text-slate-800\">Per dag</h3>\n {totaalMaand === 0 ? (\n <p className=\"text-sm text-slate-500\">Nog geen data in deze maand.</p>\n ) : (\n <>\n <DagStaven\n waarden={dagen}\n accentIndex={isHuidige ? vandaagDag - 1 : undefined}\n hoogte={170}\n titel={`Weergaven per dag in ${maandLabel(gekozen, true)}`}\n />\n <AlsTabel\n kop={[\"Dag\", \"Weergaven\"]}\n rijen={dagen.map((v, i) => [`${i + 1} ${maandLabel(gekozen)}`, v] as [string, number]).filter(([, v]) => v > 0)}\n />\n </>\n )}\n </Card>\n\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <Card>\n <h3 className=\"mb-4 text-sm font-semibold text-slate-800\">Maand op maand</h3>\n {trend.length < 2 ? (\n <p className=\"text-sm text-slate-500\">Nog te weinig maanden voor een trend — die groeit hier vanzelf.</p>\n ) : (\n <>\n <MaandLijn punten={trend} hoogte={170} titel=\"Weergaven per maand, laatste twaalf maanden\" />\n <AlsTabel kop={[\"Maand\", \"Weergaven\"]} rijen={trend.map((p) => [p.label, p.waarde] as [string, number])} />\n </>\n )}\n </Card>\n\n <Card>\n <h3 className=\"mb-4 text-sm font-semibold text-slate-800\">Best bezochte pagina's</h3>\n {paden.length === 0 ? (\n <p className=\"text-sm text-slate-500\">Nog geen data in deze maand.</p>\n ) : (\n <BalkjesLijst items={paden} />\n )}\n </Card>\n </div>\n\n {/* 7.24: waar komen bezoekers vandaan, waarop kijken ze, en wat dóén ze — de drie vragen\n die klanten stellen zodra ze een cijfer zien. Verzameld sinds 7.24; oudere maanden\n tonen deze kaarten niet. */}\n {(verwijzers.length > 0 || apparaten.length > 0 || gebeurtenissen.length > 0) && (\n <div className=\"grid gap-4 lg:grid-cols-3\">\n <Card>\n <h3 className=\"mb-4 text-sm font-semibold text-slate-800\">Waar bezoekers vandaan komen</h3>\n {verwijzers.length === 0 ? <p className=\"text-sm text-slate-500\">Nog geen data.</p> : <BalkjesLijst items={verwijzers} />}\n </Card>\n <Card>\n <h3 className=\"mb-4 text-sm font-semibold text-slate-800\">Apparaat</h3>\n {apparaten.length === 0 ? <p className=\"text-sm text-slate-500\">Nog geen data.</p> : <BalkjesLijst items={apparaten} />}\n </Card>\n <Card>\n <h3 className=\"mb-4 text-sm font-semibold text-slate-800\">Wat bezoekers doen</h3>\n {gebeurtenissen.length === 0 ? (\n <p className=\"text-sm text-slate-500\">Nog geen formulieren of bestellingen deze maand.</p>\n ) : (\n <>\n <BalkjesLijst items={gebeurtenissen} />\n {conversie !== null && (\n <p className=\"mt-3 text-xs text-slate-500\">\n {Math.round(conversie * 10) / 10}% van de weergaven leidde tot een formulier of bestelling.\n </p>\n )}\n </>\n )}\n </Card>\n </div>\n )}\n\n <p className=\"text-xs text-slate-400\">\n Privacy-vriendelijk geteld (geen cookies, geen persoonsgegevens) — één telling per\n bezoeker-sessie per pagina. Van de verwijzer wordt alleen de website bewaard, nooit de pagina of zoekterm.\n </p>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -7,9 +7,22 @@ export interface MaandStat {
|
|
|
7
7
|
total?: number;
|
|
8
8
|
days?: Record<string, number>;
|
|
9
9
|
paths?: Record<string, number>;
|
|
10
|
+
/** 7.24: verwijzers (host of gebundelde naam zoals "google"), apparaten en gebeurtenissen. */
|
|
11
|
+
refs?: Record<string, number>;
|
|
12
|
+
devices?: Record<string, number>;
|
|
13
|
+
events?: Record<string, number>;
|
|
10
14
|
}
|
|
11
|
-
/** trackView slaat paden veilig op: "home" voor "/"
|
|
15
|
+
/** trackView slaat paden veilig op: "home" voor "/", percent-encoded tekens. Terug naar leesbaar. */
|
|
12
16
|
export declare function padLabel(sleutel: string): string;
|
|
17
|
+
/** Verwijzer-sleutel → leesbaar ("google" → "Google", "nieuws%2enl" → "nieuws.nl"). */
|
|
18
|
+
export declare function verwijzerLabel(sleutel: string): string;
|
|
19
|
+
export declare function apparaatLabel(sleutel: string): string;
|
|
20
|
+
export declare function gebeurtenisLabel(sleutel: string): string;
|
|
21
|
+
/** Top-N van een teller-record, gelabeld en gesorteerd. */
|
|
22
|
+
export declare function topVan(record: Record<string, number> | undefined, n: number, label: (k: string) => string): {
|
|
23
|
+
label: string;
|
|
24
|
+
waarde: number;
|
|
25
|
+
}[];
|
|
13
26
|
/** "2026-09" → "sep 2026" (kort, voor as-labels en de maandkiezer). */
|
|
14
27
|
export declare function maandLabel(id: string, lang?: boolean): string;
|
|
15
28
|
/** Het maand-id van nu (UTC — trackView telt in UTC). */
|
package/dist/admin/stats/lib.js
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
function padLabel(sleutel) {
|
|
2
|
-
|
|
2
|
+
if (sleutel === "home") return "/";
|
|
3
|
+
let s = sleutel;
|
|
4
|
+
try {
|
|
5
|
+
s = decodeURIComponent(sleutel.replace(/%(?![0-9a-fA-F]{2})/g, "%25"));
|
|
6
|
+
} catch {
|
|
7
|
+
}
|
|
8
|
+
return `/${s.replace(/_/g, "/")}`;
|
|
9
|
+
}
|
|
10
|
+
function verwijzerLabel(sleutel) {
|
|
11
|
+
const vast = {
|
|
12
|
+
direct: "Direct / onbekend",
|
|
13
|
+
google: "Google",
|
|
14
|
+
bing: "Bing",
|
|
15
|
+
duckduckgo: "DuckDuckGo",
|
|
16
|
+
facebook: "Facebook",
|
|
17
|
+
instagram: "Instagram",
|
|
18
|
+
linkedin: "LinkedIn",
|
|
19
|
+
x: "X (Twitter)"
|
|
20
|
+
};
|
|
21
|
+
if (vast[sleutel]) return vast[sleutel];
|
|
22
|
+
try {
|
|
23
|
+
return decodeURIComponent(sleutel);
|
|
24
|
+
} catch {
|
|
25
|
+
return sleutel;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function apparaatLabel(sleutel) {
|
|
29
|
+
return { mobiel: "Mobiel", tablet: "Tablet", desktop: "Desktop" }[sleutel] ?? sleutel;
|
|
30
|
+
}
|
|
31
|
+
function gebeurtenisLabel(sleutel) {
|
|
32
|
+
return {
|
|
33
|
+
formulier: "Formulier verstuurd",
|
|
34
|
+
bestelling: "Bestelling betaald",
|
|
35
|
+
uitgaand: "Uitgaande link",
|
|
36
|
+
telefoon: "Telefoonnummer geklikt",
|
|
37
|
+
mail: "E-mailadres geklikt",
|
|
38
|
+
whatsapp: "WhatsApp geopend"
|
|
39
|
+
}[sleutel] ?? sleutel;
|
|
40
|
+
}
|
|
41
|
+
function topVan(record, n, label) {
|
|
42
|
+
return Object.entries(record ?? {}).sort((a, b) => b[1] - a[1]).slice(0, n).map(([k, w]) => ({ label: label(k), waarde: w }));
|
|
3
43
|
}
|
|
4
44
|
function maandLabel(id, lang = false) {
|
|
5
45
|
const [j, m] = id.split("-").map(Number);
|
|
@@ -29,12 +69,16 @@ function deltaTovVorige(huidig, vorige, totDag) {
|
|
|
29
69
|
return (nu - toen) / toen * 100;
|
|
30
70
|
}
|
|
31
71
|
export {
|
|
72
|
+
apparaatLabel,
|
|
32
73
|
dagReeks,
|
|
33
74
|
dagenInMaand,
|
|
34
75
|
deltaTovVorige,
|
|
76
|
+
gebeurtenisLabel,
|
|
35
77
|
huidigMaandId,
|
|
36
78
|
maandLabel,
|
|
37
79
|
padLabel,
|
|
38
|
-
topPaden
|
|
80
|
+
topPaden,
|
|
81
|
+
topVan,
|
|
82
|
+
verwijzerLabel
|
|
39
83
|
};
|
|
40
84
|
//# sourceMappingURL=lib.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/admin/stats/lib.ts"],"sourcesContent":["/**\n * Gedeelde stats-helpers — gebruikt door het Statistieken-paneel én de Start-widget, zodat\n * paden en maanden op beide plekken hetzelfde heten.\n */\n\nexport interface MaandStat {\n id: string; // \"YYYY-MM\"\n total?: number;\n days?: Record<string, number>;\n paths?: Record<string, number>;\n}\n\n/** trackView slaat paden veilig op: \"home\" voor \"/\"
|
|
1
|
+
{"version":3,"sources":["../../../src/admin/stats/lib.ts"],"sourcesContent":["/**\n * Gedeelde stats-helpers — gebruikt door het Statistieken-paneel én de Start-widget, zodat\n * paden en maanden op beide plekken hetzelfde heten.\n */\n\nexport interface MaandStat {\n id: string; // \"YYYY-MM\"\n total?: number;\n days?: Record<string, number>;\n paths?: Record<string, number>;\n /** 7.24: verwijzers (host of gebundelde naam zoals \"google\"), apparaten en gebeurtenissen. */\n refs?: Record<string, number>;\n devices?: Record<string, number>;\n events?: Record<string, number>;\n}\n\n/** trackView slaat paden veilig op: \"home\" voor \"/\", percent-encoded tekens. Terug naar leesbaar. */\nexport function padLabel(sleutel: string): string {\n if (sleutel === \"home\") return \"/\";\n let s = sleutel;\n try {\n s = decodeURIComponent(sleutel.replace(/%(?![0-9a-fA-F]{2})/g, \"%25\"));\n } catch {\n /* oude, niet-gecodeerde sleutel: laat staan */\n }\n return `/${s.replace(/_/g, \"/\")}`;\n}\n\n/** Verwijzer-sleutel → leesbaar (\"google\" → \"Google\", \"nieuws%2enl\" → \"nieuws.nl\"). */\nexport function verwijzerLabel(sleutel: string): string {\n const vast: Record<string, string> = {\n direct: \"Direct / onbekend\",\n google: \"Google\",\n bing: \"Bing\",\n duckduckgo: \"DuckDuckGo\",\n facebook: \"Facebook\",\n instagram: \"Instagram\",\n linkedin: \"LinkedIn\",\n x: \"X (Twitter)\",\n };\n if (vast[sleutel]) return vast[sleutel];\n try {\n return decodeURIComponent(sleutel);\n } catch {\n return sleutel;\n }\n}\n\nexport function apparaatLabel(sleutel: string): string {\n return ({ mobiel: \"Mobiel\", tablet: \"Tablet\", desktop: \"Desktop\" } as Record<string, string>)[sleutel] ?? sleutel;\n}\n\nexport function gebeurtenisLabel(sleutel: string): string {\n return (\n ({\n formulier: \"Formulier verstuurd\",\n bestelling: \"Bestelling betaald\",\n uitgaand: \"Uitgaande link\",\n telefoon: \"Telefoonnummer geklikt\",\n mail: \"E-mailadres geklikt\",\n whatsapp: \"WhatsApp geopend\",\n }) as Record<string, string>\n )[sleutel] ?? sleutel;\n}\n\n/** Top-N van een teller-record, gelabeld en gesorteerd. */\nexport function topVan(record: Record<string, number> | undefined, n: number, label: (k: string) => string): { label: string; waarde: number }[] {\n return Object.entries(record ?? {})\n .sort((a, b) => b[1] - a[1])\n .slice(0, n)\n .map(([k, w]) => ({ label: label(k), waarde: w }));\n}\n\n/** \"2026-09\" → \"sep 2026\" (kort, voor as-labels en de maandkiezer). */\nexport function maandLabel(id: string, lang = false): string {\n const [j, m] = id.split(\"-\").map(Number);\n const d = new Date(Date.UTC(j, (m ?? 1) - 1, 1));\n return d.toLocaleDateString(\"nl-NL\", { month: lang ? \"long\" : \"short\", year: \"numeric\", timeZone: \"UTC\" });\n}\n\n/** Het maand-id van nu (UTC — trackView telt in UTC). */\nexport function huidigMaandId(): string {\n const nu = new Date();\n return `${nu.getUTCFullYear()}-${String(nu.getUTCMonth() + 1).padStart(2, \"0\")}`;\n}\n\n/** Aantal dagen in een \"YYYY-MM\"-maand. */\nexport function dagenInMaand(id: string): number {\n const [j, m] = id.split(\"-\").map(Number);\n return new Date(Date.UTC(j, m ?? 1, 0)).getUTCDate();\n}\n\n/** days-record → array per dag (index 0 = dag 1), met nullen voor ontbrekende dagen. */\nexport function dagReeks(stat: MaandStat | undefined, nDagen: number): number[] {\n return Array.from({ length: nDagen }, (_, i) => stat?.days?.[String(i + 1).padStart(2, \"0\")] ?? 0);\n}\n\n/** Top-N paden van een maand, genormaliseerd en gesorteerd. */\nexport function topPaden(stat: MaandStat | undefined, n: number): { label: string; waarde: number }[] {\n return Object.entries(stat?.paths ?? {})\n .sort((a, b) => b[1] - a[1])\n .slice(0, n)\n .map(([p, w]) => ({ label: padLabel(p), waarde: w }));\n}\n\n/**\n * Eerlijke maand-op-maand-delta: vergelijk t/m dezelfde dag. Halverwege de maand het hele\n * vorige maandtotaal ernaast leggen leest als \"−50%\" terwijl het gewoon de 15e is.\n */\nexport function deltaTovVorige(huidig: MaandStat | undefined, vorige: MaandStat | undefined, totDag: number): number | null {\n if (!huidig || !vorige) return null;\n const som = (s: MaandStat) =>\n Object.entries(s.days ?? {}).reduce((acc, [d, n]) => (Number(d) <= totDag ? acc + n : acc), 0);\n const nu = som(huidig);\n const toen = som(vorige);\n if (toen === 0) return null;\n return ((nu - toen) / toen) * 100;\n}\n"],"mappings":"AAiBO,SAAS,SAAS,SAAyB;AAChD,MAAI,YAAY,OAAQ,QAAO;AAC/B,MAAI,IAAI;AACR,MAAI;AACF,QAAI,mBAAmB,QAAQ,QAAQ,wBAAwB,KAAK,CAAC;AAAA,EACvE,QAAQ;AAAA,EAER;AACA,SAAO,IAAI,EAAE,QAAQ,MAAM,GAAG,CAAC;AACjC;AAGO,SAAS,eAAe,SAAyB;AACtD,QAAM,OAA+B;AAAA,IACnC,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,WAAW;AAAA,IACX,UAAU;AAAA,IACV,GAAG;AAAA,EACL;AACA,MAAI,KAAK,OAAO,EAAG,QAAO,KAAK,OAAO;AACtC,MAAI;AACF,WAAO,mBAAmB,OAAO;AAAA,EACnC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,cAAc,SAAyB;AACrD,SAAQ,EAAE,QAAQ,UAAU,QAAQ,UAAU,SAAS,UAAU,EAA6B,OAAO,KAAK;AAC5G;AAEO,SAAS,iBAAiB,SAAyB;AACxD,SACG;AAAA,IACC,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,UAAU;AAAA,IACV,MAAM;AAAA,IACN,UAAU;AAAA,EACZ,EACA,OAAO,KAAK;AAChB;AAGO,SAAS,OAAO,QAA4C,GAAW,OAAmE;AAC/I,SAAO,OAAO,QAAQ,UAAU,CAAC,CAAC,EAC/B,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,EAC1B,MAAM,GAAG,CAAC,EACV,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,MAAM,CAAC,GAAG,QAAQ,EAAE,EAAE;AACrD;AAGO,SAAS,WAAW,IAAY,OAAO,OAAe;AAC3D,QAAM,CAAC,GAAG,CAAC,IAAI,GAAG,MAAM,GAAG,EAAE,IAAI,MAAM;AACvC,QAAM,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,GAAG,CAAC,CAAC;AAC/C,SAAO,EAAE,mBAAmB,SAAS,EAAE,OAAO,OAAO,SAAS,SAAS,MAAM,WAAW,UAAU,MAAM,CAAC;AAC3G;AAGO,SAAS,gBAAwB;AACtC,QAAM,KAAK,oBAAI,KAAK;AACpB,SAAO,GAAG,GAAG,eAAe,CAAC,IAAI,OAAO,GAAG,YAAY,IAAI,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC;AAChF;AAGO,SAAS,aAAa,IAAoB;AAC/C,QAAM,CAAC,GAAG,CAAC,IAAI,GAAG,MAAM,GAAG,EAAE,IAAI,MAAM;AACvC,SAAO,IAAI,KAAK,KAAK,IAAI,GAAG,KAAK,GAAG,CAAC,CAAC,EAAE,WAAW;AACrD;AAGO,SAAS,SAAS,MAA6B,QAA0B;AAC9E,SAAO,MAAM,KAAK,EAAE,QAAQ,OAAO,GAAG,CAAC,GAAG,MAAM,MAAM,OAAO,OAAO,IAAI,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC;AACnG;AAGO,SAAS,SAAS,MAA6B,GAAgD;AACpG,SAAO,OAAO,QAAQ,MAAM,SAAS,CAAC,CAAC,EACpC,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,EAC1B,MAAM,GAAG,CAAC,EACV,IAAI,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,SAAS,CAAC,GAAG,QAAQ,EAAE,EAAE;AACxD;AAMO,SAAS,eAAe,QAA+B,QAA+B,QAA+B;AAC1H,MAAI,CAAC,UAAU,CAAC,OAAQ,QAAO;AAC/B,QAAM,MAAM,CAAC,MACX,OAAO,QAAQ,EAAE,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,MAAO,OAAO,CAAC,KAAK,SAAS,MAAM,IAAI,KAAM,CAAC;AAC/F,QAAM,KAAK,IAAI,MAAM;AACrB,QAAM,OAAO,IAAI,MAAM;AACvB,MAAI,SAAS,EAAG,QAAO;AACvB,UAAS,KAAK,QAAQ,OAAQ;AAChC;","names":[]}
|
|
@@ -9,7 +9,9 @@ import { useSiteConfig } from "../../config.js";
|
|
|
9
9
|
import { useRegistry } from "../../blocks/context.js";
|
|
10
10
|
import { introspectToFields } from "../lib/zodFields.js";
|
|
11
11
|
import { BlockErrorBoundary } from "../../blocks/BlockErrorBoundary.js";
|
|
12
|
-
import { MediaPickerDialog } from "../media/MediaPicker.js";
|
|
12
|
+
import { MediaPickerDialog, MediaPickerButton } from "../media/MediaPicker.js";
|
|
13
|
+
import { Switch } from "../ui/switch.js";
|
|
14
|
+
import { titelMeter, omschrijvingMeter } from "../lib/seoHulp.js";
|
|
13
15
|
import { useThemeVars } from "../../XiCMSProvider.js";
|
|
14
16
|
import { buildBlockAddOptions } from "../../blocks/registry.js";
|
|
15
17
|
import DOMPurify from "dompurify";
|
|
@@ -787,6 +789,10 @@ function VisualEditorInner({ onClose }) {
|
|
|
787
789
|
if (e.key.toLowerCase() === "y" || e.shiftKey) redo();
|
|
788
790
|
else undo();
|
|
789
791
|
}
|
|
792
|
+
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "s") {
|
|
793
|
+
e.preventDefault();
|
|
794
|
+
void flushSave().catch(() => setStatus("error"));
|
|
795
|
+
}
|
|
790
796
|
};
|
|
791
797
|
window.addEventListener("beforeunload", beforeUnload);
|
|
792
798
|
document.addEventListener("visibilitychange", onVisibility);
|
|
@@ -1093,7 +1099,7 @@ function VisualEditorInner({ onClose }) {
|
|
|
1093
1099
|
/* @__PURE__ */ jsx(PageTranslationSettings, { draft, locale, onTranslate: setTranslation })
|
|
1094
1100
|
] }) : draft ? /* @__PURE__ */ jsxs("div", { className: "grid gap-4", children: [
|
|
1095
1101
|
/* @__PURE__ */ jsx("p", { className: "xicms-ve__hint", children: "Klik op een blok om het te bewerken." }),
|
|
1096
|
-
/* @__PURE__ */ jsx(PageSettings, { draft, onChange: (part) => mutateDraft((d) => ({ ...d, ...part }), { coalesce: true }) }),
|
|
1102
|
+
/* @__PURE__ */ jsx(PageSettings, { draft, siteId, onChange: (part) => mutateDraft((d) => ({ ...d, ...part }), { coalesce: true }) }),
|
|
1097
1103
|
!isDemoActive() && /* @__PURE__ */ jsx(
|
|
1098
1104
|
VersionsPanel,
|
|
1099
1105
|
{
|
|
@@ -1199,6 +1205,22 @@ function VisualEditorInner({ onClose }) {
|
|
|
1199
1205
|
children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "13", height: "13", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M12 5v14M19 12l-7 7-7-7" }) })
|
|
1200
1206
|
}
|
|
1201
1207
|
),
|
|
1208
|
+
/* @__PURE__ */ jsx(
|
|
1209
|
+
"button",
|
|
1210
|
+
{
|
|
1211
|
+
type: "button",
|
|
1212
|
+
className: "xicms-ve-blokknop",
|
|
1213
|
+
title: "Blok invoegen na dit blok",
|
|
1214
|
+
"aria-label": `Blok invoegen na ${b.label}`,
|
|
1215
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
1216
|
+
onClick: () => {
|
|
1217
|
+
setSelectedProduct(null);
|
|
1218
|
+
setSelectedKey(b.key);
|
|
1219
|
+
setPanelOpen(true);
|
|
1220
|
+
},
|
|
1221
|
+
children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "13", height: "13", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M12 5v14M5 12h14" }) })
|
|
1222
|
+
}
|
|
1223
|
+
),
|
|
1202
1224
|
/* @__PURE__ */ jsx(
|
|
1203
1225
|
"button",
|
|
1204
1226
|
{
|
|
@@ -1403,17 +1425,71 @@ function FmtBtn({ label, title, onRun }) {
|
|
|
1403
1425
|
}
|
|
1404
1426
|
);
|
|
1405
1427
|
}
|
|
1428
|
+
function MeterRegel({ m }) {
|
|
1429
|
+
const kleur = m.toon === "goed" ? "text-emerald-700" : m.toon === "lang" || m.toon === "leeg" ? "text-amber-700" : "text-slate-500";
|
|
1430
|
+
return /* @__PURE__ */ jsx("p", { className: `mt-1 text-xs ${kleur}`, children: m.tekst });
|
|
1431
|
+
}
|
|
1406
1432
|
function PageSettings({
|
|
1407
1433
|
draft,
|
|
1434
|
+
siteId,
|
|
1408
1435
|
onChange
|
|
1409
1436
|
}) {
|
|
1410
1437
|
const seo = draft.seo ?? {};
|
|
1438
|
+
const { toast } = useToast();
|
|
1439
|
+
const [previewUrl, setPreviewUrl] = useState(null);
|
|
1440
|
+
const [bezig, setBezig] = useState(false);
|
|
1441
|
+
async function maakPreviewLink() {
|
|
1442
|
+
const { getFirebaseApp } = await import("../../firebase/client.js");
|
|
1443
|
+
const { callable } = await import("../../firebase/functionsClient.js");
|
|
1444
|
+
const app = getFirebaseApp();
|
|
1445
|
+
if (!app) return;
|
|
1446
|
+
setBezig(true);
|
|
1447
|
+
try {
|
|
1448
|
+
const res = await callable(app, "createPreviewLink")({ siteId, path: draft.path });
|
|
1449
|
+
setPreviewUrl(res.data.url);
|
|
1450
|
+
try {
|
|
1451
|
+
await navigator.clipboard?.writeText(res.data.url);
|
|
1452
|
+
toast({ title: "Preview-link gekopieerd", description: "Zeven dagen geldig; wie hem opent ziet de nieuwste stand, ook wat nog niet live is.", tone: "success" });
|
|
1453
|
+
} catch {
|
|
1454
|
+
toast({ title: "Preview-link gemaakt", tone: "success" });
|
|
1455
|
+
}
|
|
1456
|
+
} catch (e) {
|
|
1457
|
+
toast({ title: "Preview-link mislukt", description: e.message, tone: "error" });
|
|
1458
|
+
} finally {
|
|
1459
|
+
setBezig(false);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1411
1462
|
return /* @__PURE__ */ jsxs("details", { className: "xicms-ve__section", open: true, children: [
|
|
1412
1463
|
/* @__PURE__ */ jsx("summary", { className: "text-sm font-semibold", children: "Pagina-instellingen" }),
|
|
1413
1464
|
/* @__PURE__ */ jsxs("div", { className: "mt-2 grid gap-3", children: [
|
|
1414
1465
|
/* @__PURE__ */ jsx(Field, { label: "Paginatitel", children: /* @__PURE__ */ jsx(Input, { value: draft.title ?? "", onChange: (e) => onChange({ title: e.target.value }) }) }),
|
|
1415
|
-
/* @__PURE__ */
|
|
1416
|
-
|
|
1466
|
+
/* @__PURE__ */ jsxs(Field, { label: "Titel in zoekresultaten", hint: "Leeg = paginatitel", children: [
|
|
1467
|
+
/* @__PURE__ */ jsx(Input, { value: seo.title ?? "", onChange: (e) => onChange({ seo: { ...seo, title: e.target.value } }) }),
|
|
1468
|
+
/* @__PURE__ */ jsx(MeterRegel, { m: titelMeter(seo.title, draft.title) })
|
|
1469
|
+
] }),
|
|
1470
|
+
/* @__PURE__ */ jsxs(Field, { label: "Omschrijving voor Google", hint: "E\xE9n of twee zinnen die uitnodigen om te klikken", children: [
|
|
1471
|
+
/* @__PURE__ */ jsx(Textarea, { rows: 3, value: seo.description ?? "", onChange: (e) => onChange({ seo: { ...seo, description: e.target.value } }) }),
|
|
1472
|
+
/* @__PURE__ */ jsx(MeterRegel, { m: omschrijvingMeter(seo.description) })
|
|
1473
|
+
] }),
|
|
1474
|
+
/* @__PURE__ */ jsx(Field, { label: "Afbeelding bij het delen", hint: "Voorbeeld op social media/WhatsApp. Leeg = standaardafbeelding van de site.", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1475
|
+
seo.ogImage && // eslint-disable-next-line @next/next/no-img-element
|
|
1476
|
+
/* @__PURE__ */ jsx("img", { src: seo.ogImage, alt: "", className: "h-12 w-20 rounded-md border border-slate-200 object-cover" }),
|
|
1477
|
+
/* @__PURE__ */ jsx(MediaPickerButton, { siteId, label: seo.ogImage ? "Andere kiezen" : "Kies / upload", onSelect: (m) => onChange({ seo: { ...seo, ogImage: m.url } }) }),
|
|
1478
|
+
seo.ogImage && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => onChange({ seo: { ...seo, ogImage: void 0 } }), children: "Weghalen" })
|
|
1479
|
+
] }) }),
|
|
1480
|
+
/* @__PURE__ */ jsx(
|
|
1481
|
+
Switch,
|
|
1482
|
+
{
|
|
1483
|
+
checked: Boolean(seo.noindex),
|
|
1484
|
+
onCheckedChange: (v) => onChange({ seo: { ...seo, noindex: v || void 0 } }),
|
|
1485
|
+
label: "Niet opnemen in Google"
|
|
1486
|
+
}
|
|
1487
|
+
),
|
|
1488
|
+
!isDemoActive() && /* @__PURE__ */ jsxs("div", { className: "grid gap-1 border-t border-slate-200 pt-3", children: [
|
|
1489
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", disabled: bezig, onClick: () => void maakPreviewLink(), children: bezig ? "Bezig\u2026" : "\u{1F517} Preview-link delen" }),
|
|
1490
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-slate-500", children: "Laat iemand zonder inlog meekijken naar de werkversie. Zeven dagen geldig, geen toegang tot het beheer." }),
|
|
1491
|
+
previewUrl && /* @__PURE__ */ jsx(Input, { readOnly: true, value: previewUrl, className: "text-xs", onFocus: (e) => e.currentTarget.select() })
|
|
1492
|
+
] })
|
|
1417
1493
|
] })
|
|
1418
1494
|
] });
|
|
1419
1495
|
}
|