@xiwebdesign/xicms-core 7.31.0 → 8.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (482) hide show
  1. package/dist/XiCMSProvider.js +23 -7
  2. package/dist/XiCMSProvider.js.map +1 -1
  3. package/dist/admin/AdminApp.js +175 -93
  4. package/dist/admin/AdminApp.js.map +1 -1
  5. package/dist/admin/AdminHelp.d.ts +1 -1
  6. package/dist/admin/AdminHelp.js +44 -36
  7. package/dist/admin/AdminHelp.js.map +1 -1
  8. package/dist/admin/CoreVersion.d.ts +6 -3
  9. package/dist/admin/CoreVersion.js +60 -24
  10. package/dist/admin/CoreVersion.js.map +1 -1
  11. package/dist/admin/NewsEditor.d.ts +7 -0
  12. package/dist/admin/NewsEditor.js +117 -75
  13. package/dist/admin/NewsEditor.js.map +1 -1
  14. package/dist/admin/SuperadminPanel.js +1 -1
  15. package/dist/admin/SuperadminPanel.js.map +1 -1
  16. package/dist/admin/ZoekPalet.d.ts +15 -0
  17. package/dist/admin/ZoekPalet.js +123 -0
  18. package/dist/admin/ZoekPalet.js.map +1 -0
  19. package/dist/admin/blockEditors/GenericEditor.js +36 -18
  20. package/dist/admin/blockEditors/GenericEditor.js.map +1 -1
  21. package/dist/admin/blockEditors/StyleEditor.js +37 -3
  22. package/dist/admin/blockEditors/StyleEditor.js.map +1 -1
  23. package/dist/admin/blockEditors/SymbolRefEditor.js +44 -2
  24. package/dist/admin/blockEditors/SymbolRefEditor.js.map +1 -1
  25. package/dist/admin/blockEditors/index.js +154 -73
  26. package/dist/admin/blockEditors/index.js.map +1 -1
  27. package/dist/admin/customBlocks/CustomBlocksPanel.js +18 -7
  28. package/dist/admin/customBlocks/CustomBlocksPanel.js.map +1 -1
  29. package/dist/admin/dashboard/DashboardPanel.js +26 -16
  30. package/dist/admin/dashboard/DashboardPanel.js.map +1 -1
  31. package/dist/admin/editors/RichTextEditor.d.ts +4 -3
  32. package/dist/admin/editors/RichTextEditor.js +19 -60
  33. package/dist/admin/editors/RichTextEditor.js.map +1 -1
  34. package/dist/admin/editors/fontSizeExtension.js +7 -1
  35. package/dist/admin/editors/fontSizeExtension.js.map +1 -1
  36. package/dist/admin/editors/slashMenu.d.ts +18 -0
  37. package/dist/admin/editors/slashMenu.js +34 -0
  38. package/dist/admin/editors/slashMenu.js.map +1 -0
  39. package/dist/admin/editors/useRijkeTekst.d.ts +48 -0
  40. package/dist/admin/editors/useRijkeTekst.js +310 -0
  41. package/dist/admin/editors/useRijkeTekst.js.map +1 -0
  42. package/dist/admin/forms/FormsPanel.d.ts +11 -2
  43. package/dist/admin/forms/FormsPanel.js +218 -65
  44. package/dist/admin/forms/FormsPanel.js.map +1 -1
  45. package/dist/admin/lib/blokInfo.d.ts +17 -0
  46. package/dist/admin/lib/blokInfo.js +80 -0
  47. package/dist/admin/lib/blokInfo.js.map +1 -0
  48. package/dist/admin/lib/foutNaarMens.d.ts +1 -0
  49. package/dist/admin/lib/foutNaarMens.js +22 -0
  50. package/dist/admin/lib/foutNaarMens.js.map +1 -0
  51. package/dist/admin/lib/gewisteVelden.d.ts +13 -0
  52. package/dist/admin/lib/gewisteVelden.js +31 -0
  53. package/dist/admin/lib/gewisteVelden.js.map +1 -0
  54. package/dist/admin/lib/heroHulp.d.ts +11 -0
  55. package/dist/admin/lib/heroHulp.js +18 -0
  56. package/dist/admin/lib/heroHulp.js.map +1 -0
  57. package/dist/admin/lib/kleur.js +2 -3
  58. package/dist/admin/lib/kleur.js.map +1 -1
  59. package/dist/admin/lib/leesbaarheid.d.ts +34 -0
  60. package/dist/admin/lib/leesbaarheid.js +82 -0
  61. package/dist/admin/lib/leesbaarheid.js.map +1 -0
  62. package/dist/admin/lib/lengteAdvies.d.ts +1 -0
  63. package/dist/admin/lib/lengteAdvies.js +16 -0
  64. package/dist/admin/lib/lengteAdvies.js.map +1 -0
  65. package/dist/admin/lib/mediaLijst.d.ts +26 -0
  66. package/dist/admin/lib/mediaLijst.js +42 -0
  67. package/dist/admin/lib/mediaLijst.js.map +1 -0
  68. package/dist/admin/lib/mediaUsage.d.ts +12 -1
  69. package/dist/admin/lib/mediaUsage.js +43 -33
  70. package/dist/admin/lib/mediaUsage.js.map +1 -1
  71. package/dist/admin/lib/menuHulp.d.ts +16 -0
  72. package/dist/admin/lib/menuHulp.js +41 -0
  73. package/dist/admin/lib/menuHulp.js.map +1 -0
  74. package/dist/admin/lib/onopgeslagen.d.ts +10 -0
  75. package/dist/admin/lib/onopgeslagen.js +35 -0
  76. package/dist/admin/lib/onopgeslagen.js.map +1 -0
  77. package/dist/admin/lib/pagesClient.d.ts +55 -2
  78. package/dist/admin/lib/pagesClient.js +126 -28
  79. package/dist/admin/lib/pagesClient.js.map +1 -1
  80. package/dist/admin/lib/paginaGebruik.d.ts +56 -0
  81. package/dist/admin/lib/paginaGebruik.js +109 -0
  82. package/dist/admin/lib/paginaGebruik.js.map +1 -0
  83. package/dist/admin/lib/publicatieCheck.d.ts +25 -0
  84. package/dist/admin/lib/publicatieCheck.js +114 -0
  85. package/dist/admin/lib/publicatieCheck.js.map +1 -0
  86. package/dist/admin/lib/scrollNaarDetail.d.ts +6 -0
  87. package/dist/admin/lib/scrollNaarDetail.js +20 -0
  88. package/dist/admin/lib/scrollNaarDetail.js.map +1 -0
  89. package/dist/admin/lib/symbolsClient.d.ts +10 -0
  90. package/dist/admin/lib/symbolsClient.js +14 -1
  91. package/dist/admin/lib/symbolsClient.js.map +1 -1
  92. package/dist/admin/lib/tekstVeld.d.ts +8 -0
  93. package/dist/admin/lib/tekstVeld.js +10 -0
  94. package/dist/admin/lib/tekstVeld.js.map +1 -0
  95. package/dist/admin/lib/useIsSuperadmin.d.ts +2 -0
  96. package/dist/admin/lib/useIsSuperadmin.js +13 -0
  97. package/dist/admin/lib/useIsSuperadmin.js.map +1 -0
  98. package/dist/admin/lib/veldTaal.d.ts +14 -0
  99. package/dist/admin/lib/veldTaal.js +248 -0
  100. package/dist/admin/lib/veldTaal.js.map +1 -0
  101. package/dist/admin/lib/versieVergelijk.d.ts +17 -0
  102. package/dist/admin/lib/versieVergelijk.js +24 -0
  103. package/dist/admin/lib/versieVergelijk.js.map +1 -0
  104. package/dist/admin/lib/zodFields.js +22 -4
  105. package/dist/admin/lib/zodFields.js.map +1 -1
  106. package/dist/admin/lib/zodFout.d.ts +9 -0
  107. package/dist/admin/lib/zodFout.js +63 -0
  108. package/dist/admin/lib/zodFout.js.map +1 -0
  109. package/dist/admin/media/MediaManager.js +162 -32
  110. package/dist/admin/media/MediaManager.js.map +1 -1
  111. package/dist/admin/media/MediaPicker.d.ts +3 -0
  112. package/dist/admin/media/MediaPicker.js +84 -31
  113. package/dist/admin/media/MediaPicker.js.map +1 -1
  114. package/dist/admin/menu/MenuPanel.js +144 -83
  115. package/dist/admin/menu/MenuPanel.js.map +1 -1
  116. package/dist/admin/navContext.d.ts +3 -0
  117. package/dist/admin/navContext.js +13 -1
  118. package/dist/admin/navContext.js.map +1 -1
  119. package/dist/admin/pages/PageEditor.js +146 -30
  120. package/dist/admin/pages/PageEditor.js.map +1 -1
  121. package/dist/admin/pages/PagesPanel.js +215 -61
  122. package/dist/admin/pages/PagesPanel.js.map +1 -1
  123. package/dist/admin/pages/StarterKiezer.d.ts +12 -0
  124. package/dist/admin/pages/StarterKiezer.js +113 -0
  125. package/dist/admin/pages/StarterKiezer.js.map +1 -0
  126. package/dist/admin/panelRegistry.d.ts +1 -1
  127. package/dist/admin/panelRegistry.js +2 -1
  128. package/dist/admin/panelRegistry.js.map +1 -1
  129. package/dist/admin/patterns/PatternInserter.js +1 -1
  130. package/dist/admin/patterns/PatternInserter.js.map +1 -1
  131. package/dist/admin/settings/BedrijfInstellingen.d.ts +20 -0
  132. package/dist/admin/settings/BedrijfInstellingen.js +159 -0
  133. package/dist/admin/settings/BedrijfInstellingen.js.map +1 -0
  134. package/dist/admin/settings/MailTemplates.js +5 -4
  135. package/dist/admin/settings/MailTemplates.js.map +1 -1
  136. package/dist/admin/settings/SettingsPanel.js +120 -78
  137. package/dist/admin/settings/SettingsPanel.js.map +1 -1
  138. package/dist/admin/settings/SnapshotsCard.js +4 -4
  139. package/dist/admin/settings/SnapshotsCard.js.map +1 -1
  140. package/dist/admin/settings/VorigeTerugzetten.d.ts +15 -0
  141. package/dist/admin/settings/VorigeTerugzetten.js +89 -0
  142. package/dist/admin/settings/VorigeTerugzetten.js.map +1 -0
  143. package/dist/admin/shop/CategoriesTab.js +6 -4
  144. package/dist/admin/shop/CategoriesTab.js.map +1 -1
  145. package/dist/admin/shop/DiscountsTab.js +4 -2
  146. package/dist/admin/shop/DiscountsTab.js.map +1 -1
  147. package/dist/admin/shop/OrdersTab.js +57 -19
  148. package/dist/admin/shop/OrdersTab.js.map +1 -1
  149. package/dist/admin/shop/ProductPaneel.js +2 -1
  150. package/dist/admin/shop/ProductPaneel.js.map +1 -1
  151. package/dist/admin/shop/ProductsTab.js +40 -19
  152. package/dist/admin/shop/ProductsTab.js.map +1 -1
  153. package/dist/admin/shop/ShopPanel.js +13 -1
  154. package/dist/admin/shop/ShopPanel.js.map +1 -1
  155. package/dist/admin/shop/ShopSettingsTab.js +56 -17
  156. package/dist/admin/shop/ShopSettingsTab.js.map +1 -1
  157. package/dist/admin/shop/productClient.js +7 -3
  158. package/dist/admin/shop/productClient.js.map +1 -1
  159. package/dist/admin/stats/NietGevonden.d.ts +10 -0
  160. package/dist/admin/stats/NietGevonden.js +78 -0
  161. package/dist/admin/stats/NietGevonden.js.map +1 -0
  162. package/dist/admin/stats/StatsPanel.js +6 -1
  163. package/dist/admin/stats/StatsPanel.js.map +1 -1
  164. package/dist/admin/stats/lib.d.ts +2 -0
  165. package/dist/admin/stats/lib.js.map +1 -1
  166. package/dist/admin/ui/ConfirmDialog.js +10 -1
  167. package/dist/admin/ui/ConfirmDialog.js.map +1 -1
  168. package/dist/admin/ui/Icon.d.ts +34 -0
  169. package/dist/admin/ui/Icon.js +44 -0
  170. package/dist/admin/ui/Icon.js.map +1 -0
  171. package/dist/admin/ui/ImageField.d.ts +15 -0
  172. package/dist/admin/ui/ImageField.js +67 -0
  173. package/dist/admin/ui/ImageField.js.map +1 -0
  174. package/dist/admin/ui/KleurVeld.d.ts +25 -0
  175. package/dist/admin/ui/KleurVeld.js +76 -0
  176. package/dist/admin/ui/KleurVeld.js.map +1 -0
  177. package/dist/admin/ui/LaadStaat.d.ts +21 -0
  178. package/dist/admin/ui/LaadStaat.js +43 -0
  179. package/dist/admin/ui/LaadStaat.js.map +1 -0
  180. package/dist/admin/ui/LeesbaarheidPaneel.d.ts +10 -0
  181. package/dist/admin/ui/LeesbaarheidPaneel.js +68 -0
  182. package/dist/admin/ui/LeesbaarheidPaneel.js.map +1 -0
  183. package/dist/admin/ui/LinkDialog.d.ts +22 -0
  184. package/dist/admin/ui/LinkDialog.js +131 -0
  185. package/dist/admin/ui/LinkDialog.js.map +1 -0
  186. package/dist/admin/ui/LinkVeld.d.ts +14 -0
  187. package/dist/admin/ui/LinkVeld.js +49 -0
  188. package/dist/admin/ui/LinkVeld.js.map +1 -0
  189. package/dist/admin/ui/PreviewLinkResultaat.d.ts +14 -0
  190. package/dist/admin/ui/PreviewLinkResultaat.js +52 -0
  191. package/dist/admin/ui/PreviewLinkResultaat.js.map +1 -0
  192. package/dist/admin/ui/charts.js +5 -5
  193. package/dist/admin/ui/charts.js.map +1 -1
  194. package/dist/admin/ui/primitives.d.ts +16 -6
  195. package/dist/admin/ui/primitives.js +98 -26
  196. package/dist/admin/ui/primitives.js.map +1 -1
  197. package/dist/admin/ui/select.js +5 -2
  198. package/dist/admin/ui/select.js.map +1 -1
  199. package/dist/admin/ui/switch.d.ts +3 -1
  200. package/dist/admin/ui/switch.js +6 -3
  201. package/dist/admin/ui/switch.js.map +1 -1
  202. package/dist/admin/ui/toast.d.ts +2 -1
  203. package/dist/admin/ui/toast.js +17 -11
  204. package/dist/admin/ui/toast.js.map +1 -1
  205. package/dist/admin/visual/BlokKiezer.d.ts +34 -0
  206. package/dist/admin/visual/BlokKiezer.js +225 -0
  207. package/dist/admin/visual/BlokKiezer.js.map +1 -0
  208. package/dist/admin/visual/InlineGereedschap.d.ts +52 -0
  209. package/dist/admin/visual/InlineGereedschap.js +356 -0
  210. package/dist/admin/visual/InlineGereedschap.js.map +1 -0
  211. package/dist/admin/visual/InlineRijkeTekst.d.ts +23 -0
  212. package/dist/admin/visual/InlineRijkeTekst.js +152 -0
  213. package/dist/admin/visual/InlineRijkeTekst.js.map +1 -0
  214. package/dist/admin/visual/VisualEditor.js +273 -125
  215. package/dist/admin/visual/VisualEditor.js.map +1 -1
  216. package/dist/admin.css +1 -1
  217. package/dist/auth/AuthProvider.js +1 -0
  218. package/dist/auth/AuthProvider.js.map +1 -1
  219. package/dist/bedrijf/bedrijf.d.ts +44 -0
  220. package/dist/bedrijf/bedrijf.js +125 -0
  221. package/dist/bedrijf/bedrijf.js.map +1 -0
  222. package/dist/blocks/BlockRenderer.js +11 -1
  223. package/dist/blocks/BlockRenderer.js.map +1 -1
  224. package/dist/blocks/blockStyle.d.ts +7 -0
  225. package/dist/blocks/blockStyle.js +16 -3
  226. package/dist/blocks/blockStyle.js.map +1 -1
  227. package/dist/blocks/builtinSchemas.js +6 -0
  228. package/dist/blocks/builtinSchemas.js.map +1 -1
  229. package/dist/blocks/builtins/AgendaRender.d.ts +22 -0
  230. package/dist/blocks/builtins/AgendaRender.js +69 -9
  231. package/dist/blocks/builtins/AgendaRender.js.map +1 -1
  232. package/dist/blocks/builtins/Availability.d.ts +1 -0
  233. package/dist/blocks/builtins/Availability.js +23 -0
  234. package/dist/blocks/builtins/Availability.js.map +1 -0
  235. package/dist/blocks/builtins/AvailabilityRender.d.ts +12 -0
  236. package/dist/blocks/builtins/AvailabilityRender.js +105 -0
  237. package/dist/blocks/builtins/AvailabilityRender.js.map +1 -0
  238. package/dist/blocks/builtins/BeforeAfterRender.js +4 -3
  239. package/dist/blocks/builtins/BeforeAfterRender.js.map +1 -1
  240. package/dist/blocks/builtins/Booking.js +1 -1
  241. package/dist/blocks/builtins/Booking.js.map +1 -1
  242. package/dist/blocks/builtins/BreadcrumbsRender.d.ts +4 -3
  243. package/dist/blocks/builtins/BreadcrumbsRender.js +14 -8
  244. package/dist/blocks/builtins/BreadcrumbsRender.js.map +1 -1
  245. package/dist/blocks/builtins/ColumnsRender.d.ts +1 -1
  246. package/dist/blocks/builtins/ColumnsRender.js +3 -1
  247. package/dist/blocks/builtins/ColumnsRender.js.map +1 -1
  248. package/dist/blocks/builtins/Contact.js +1 -1
  249. package/dist/blocks/builtins/Contact.js.map +1 -1
  250. package/dist/blocks/builtins/ContactRender.d.ts +1 -1
  251. package/dist/blocks/builtins/ContactRender.js +37 -8
  252. package/dist/blocks/builtins/ContactRender.js.map +1 -1
  253. package/dist/blocks/builtins/Cta.js +1 -1
  254. package/dist/blocks/builtins/Cta.js.map +1 -1
  255. package/dist/blocks/builtins/CtaRender.d.ts +1 -1
  256. package/dist/blocks/builtins/CtaRender.js +5 -4
  257. package/dist/blocks/builtins/CtaRender.js.map +1 -1
  258. package/dist/blocks/builtins/Embed.js +1 -1
  259. package/dist/blocks/builtins/Embed.js.map +1 -1
  260. package/dist/blocks/builtins/FormBlock.js +1 -0
  261. package/dist/blocks/builtins/FormBlock.js.map +1 -1
  262. package/dist/blocks/builtins/FormBlockRender.d.ts +6 -0
  263. package/dist/blocks/builtins/FormBlockRender.js +88 -16
  264. package/dist/blocks/builtins/FormBlockRender.js.map +1 -1
  265. package/dist/blocks/builtins/GalleryRender.js +3 -2
  266. package/dist/blocks/builtins/GalleryRender.js.map +1 -1
  267. package/dist/blocks/builtins/Hero.js +1 -1
  268. package/dist/blocks/builtins/Hero.js.map +1 -1
  269. package/dist/blocks/builtins/HeroRender.js +7 -3
  270. package/dist/blocks/builtins/HeroRender.js.map +1 -1
  271. package/dist/blocks/builtins/OpenNowBadge.d.ts +4 -2
  272. package/dist/blocks/builtins/OpenNowBadge.js +6 -6
  273. package/dist/blocks/builtins/OpenNowBadge.js.map +1 -1
  274. package/dist/blocks/builtins/OpeningHoursRender.d.ts +8 -2
  275. package/dist/blocks/builtins/OpeningHoursRender.js +27 -6
  276. package/dist/blocks/builtins/OpeningHoursRender.js.map +1 -1
  277. package/dist/blocks/builtins/PriceList.d.ts +1 -0
  278. package/dist/blocks/builtins/PriceList.js +25 -0
  279. package/dist/blocks/builtins/PriceList.js.map +1 -0
  280. package/dist/blocks/builtins/PriceListRender.d.ts +17 -0
  281. package/dist/blocks/builtins/PriceListRender.js +87 -0
  282. package/dist/blocks/builtins/PriceListRender.js.map +1 -0
  283. package/dist/blocks/builtins/RichTextBlockRender.js +6 -1
  284. package/dist/blocks/builtins/RichTextBlockRender.js.map +1 -1
  285. package/dist/blocks/builtins/SectionHeading.d.ts +1 -0
  286. package/dist/blocks/builtins/SectionHeading.js +14 -0
  287. package/dist/blocks/builtins/SectionHeading.js.map +1 -0
  288. package/dist/blocks/builtins/SectionHeadingRender.d.ts +7 -0
  289. package/dist/blocks/builtins/SectionHeadingRender.js +14 -0
  290. package/dist/blocks/builtins/SectionHeadingRender.js.map +1 -0
  291. package/dist/blocks/builtins/SliderRender.js +3 -2
  292. package/dist/blocks/builtins/SliderRender.js.map +1 -1
  293. package/dist/blocks/builtins/SymbolRef.js +1 -1
  294. package/dist/blocks/builtins/SymbolRef.js.map +1 -1
  295. package/dist/blocks/builtins/TeamRender.js +6 -1
  296. package/dist/blocks/builtins/TeamRender.js.map +1 -1
  297. package/dist/blocks/builtins/Testimonials.js +3 -1
  298. package/dist/blocks/builtins/Testimonials.js.map +1 -1
  299. package/dist/blocks/builtins/TestimonialsRender.js +4 -0
  300. package/dist/blocks/builtins/TestimonialsRender.js.map +1 -1
  301. package/dist/blocks/builtins/WhatsappChatRender.d.ts +1 -1
  302. package/dist/blocks/builtins/WhatsappChatRender.js +3 -1
  303. package/dist/blocks/builtins/WhatsappChatRender.js.map +1 -1
  304. package/dist/blocks/builtins/Widgets.js +2 -2
  305. package/dist/blocks/builtins/Widgets.js.map +1 -1
  306. package/dist/blocks/builtins/WidgetsRender.js +3 -1
  307. package/dist/blocks/builtins/WidgetsRender.js.map +1 -1
  308. package/dist/blocks/builtins/consentEmbed.js +26 -9
  309. package/dist/blocks/builtins/consentEmbed.js.map +1 -1
  310. package/dist/blocks/builtins/index.d.ts +3 -0
  311. package/dist/blocks/builtins/index.js +6 -0
  312. package/dist/blocks/builtins/index.js.map +1 -1
  313. package/dist/blocks/builtins/lib/autoComplete.d.ts +7 -0
  314. package/dist/blocks/builtins/lib/autoComplete.js +20 -0
  315. package/dist/blocks/builtins/lib/autoComplete.js.map +1 -0
  316. package/dist/blocks/builtins/lib/bookingUrl.d.ts +2 -2
  317. package/dist/blocks/builtins/lib/bookingUrl.js +1 -1
  318. package/dist/blocks/builtins/lib/bookingUrl.js.map +1 -1
  319. package/dist/blocks/builtins/lib/ical.d.ts +24 -0
  320. package/dist/blocks/builtins/lib/ical.js +82 -0
  321. package/dist/blocks/builtins/lib/ical.js.map +1 -0
  322. package/dist/blocks/builtins/lib/vandaag.d.ts +8 -0
  323. package/dist/blocks/builtins/lib/vandaag.js +22 -0
  324. package/dist/blocks/builtins/lib/vandaag.js.map +1 -0
  325. package/dist/blocks/registry.d.ts +6 -0
  326. package/dist/blocks/registry.js.map +1 -1
  327. package/dist/blocks/ui/index.js +1 -0
  328. package/dist/blocks/ui/index.js.map +1 -1
  329. package/dist/components/EditBar.js +2 -4
  330. package/dist/components/EditBar.js.map +1 -1
  331. package/dist/components/News.js +26 -18
  332. package/dist/components/News.js.map +1 -1
  333. package/dist/components/PageView.d.ts +7 -1
  334. package/dist/components/PageView.js +17 -0
  335. package/dist/components/PageView.js.map +1 -1
  336. package/dist/components/ResponsiveImage.js +18 -20
  337. package/dist/components/ResponsiveImage.js.map +1 -1
  338. package/dist/components/onderOffset.d.ts +13 -0
  339. package/dist/components/onderOffset.js +74 -0
  340. package/dist/components/onderOffset.js.map +1 -0
  341. package/dist/components/site/Bedrijfsnummers.d.ts +9 -0
  342. package/dist/components/site/Bedrijfsnummers.js +12 -0
  343. package/dist/components/site/Bedrijfsnummers.js.map +1 -0
  344. package/dist/components/site/SiteFooter.d.ts +6 -1
  345. package/dist/components/site/SiteFooter.js +62 -9
  346. package/dist/components/site/SiteFooter.js.map +1 -1
  347. package/dist/components/site/SiteHeader.d.ts +15 -0
  348. package/dist/components/site/SiteHeader.js +70 -0
  349. package/dist/components/site/SiteHeader.js.map +1 -0
  350. package/dist/components/site/SiteNav.d.ts +2 -0
  351. package/dist/components/site/SiteNav.js +29 -5
  352. package/dist/components/site/SiteNav.js.map +1 -1
  353. package/dist/demo/DemoStudio.js +2 -2
  354. package/dist/demo/DemoStudio.js.map +1 -1
  355. package/dist/demo/demoStore.d.ts +13 -0
  356. package/dist/demo/demoStore.js +36 -0
  357. package/dist/demo/demoStore.js.map +1 -1
  358. package/dist/editor/EditorProvider.js +15 -4
  359. package/dist/editor/EditorProvider.js.map +1 -1
  360. package/dist/firebase/functionsClient.js +5 -1
  361. package/dist/firebase/functionsClient.js.map +1 -1
  362. package/dist/index.d.ts +5 -1
  363. package/dist/index.js +20 -1
  364. package/dist/index.js.map +1 -1
  365. package/dist/media/ImageContext.d.ts +17 -0
  366. package/dist/media/ImageContext.js +65 -20
  367. package/dist/media/ImageContext.js.map +1 -1
  368. package/dist/media/altInHtml.d.ts +9 -0
  369. package/dist/media/altInHtml.js +17 -0
  370. package/dist/media/altInHtml.js.map +1 -0
  371. package/dist/media/srcset.d.ts +12 -0
  372. package/dist/media/srcset.js +13 -0
  373. package/dist/media/srcset.js.map +1 -0
  374. package/dist/modules/builtins.d.ts +1 -0
  375. package/dist/modules/builtins.js +22 -9
  376. package/dist/modules/builtins.js.map +1 -1
  377. package/dist/modules/consent/ConsentBanner.d.ts +5 -0
  378. package/dist/modules/consent/ConsentBanner.js +8 -7
  379. package/dist/modules/consent/ConsentBanner.js.map +1 -1
  380. package/dist/modules/consent/standaard.d.ts +12 -0
  381. package/dist/modules/consent/standaard.js +10 -0
  382. package/dist/modules/consent/standaard.js.map +1 -0
  383. package/dist/modules/consent/useConsent.d.ts +4 -0
  384. package/dist/modules/consent/useConsent.js +30 -1
  385. package/dist/modules/consent/useConsent.js.map +1 -1
  386. package/dist/modules/shop/blocks/Cart.js +5 -3
  387. package/dist/modules/shop/blocks/Cart.js.map +1 -1
  388. package/dist/modules/shop/blocks/Checkout.js +56 -35
  389. package/dist/modules/shop/blocks/Checkout.js.map +1 -1
  390. package/dist/modules/shop/blocks/Donation.js +4 -4
  391. package/dist/modules/shop/blocks/Donation.js.map +1 -1
  392. package/dist/modules/shop/blocks/ProductDetail.js +19 -3
  393. package/dist/modules/shop/blocks/ProductDetail.js.map +1 -1
  394. package/dist/modules/shop/blocks/ProductGrid.d.ts +3 -1
  395. package/dist/modules/shop/blocks/ProductGrid.js +8 -4
  396. package/dist/modules/shop/blocks/ProductGrid.js.map +1 -1
  397. package/dist/modules/shop/blocks/ShopCategories.js +4 -3
  398. package/dist/modules/shop/blocks/ShopCategories.js.map +1 -1
  399. package/dist/modules/shop/lib.d.ts +4 -0
  400. package/dist/modules/shop/lib.js +14 -0
  401. package/dist/modules/shop/lib.js.map +1 -1
  402. package/dist/modules/shop/settings.d.ts +3 -0
  403. package/dist/modules/shop/settings.js +14 -0
  404. package/dist/modules/shop/settings.js.map +1 -1
  405. package/dist/modules/shop/strings.d.ts +4 -0
  406. package/dist/modules/shop/strings.js +8 -0
  407. package/dist/modules/shop/strings.js.map +1 -1
  408. package/dist/next/envCheck.d.ts +42 -0
  409. package/dist/next/envCheck.js +106 -0
  410. package/dist/next/envCheck.js.map +1 -0
  411. package/dist/next/index.cjs +185 -2
  412. package/dist/next/index.d.ts +2 -0
  413. package/dist/next/index.js +8 -1
  414. package/dist/next/index.js.map +1 -1
  415. package/dist/next/securityHeaders.d.ts +36 -0
  416. package/dist/next/securityHeaders.js +74 -0
  417. package/dist/next/securityHeaders.js.map +1 -0
  418. package/dist/patterns/builtins.d.ts +7 -0
  419. package/dist/patterns/builtins.js +138 -3
  420. package/dist/patterns/builtins.js.map +1 -1
  421. package/dist/releaseNotes.js +148 -0
  422. package/dist/releaseNotes.js.map +1 -1
  423. package/dist/render/PublishedPage.d.ts +2 -1
  424. package/dist/render/PublishedPage.js +10 -4
  425. package/dist/render/PublishedPage.js.map +1 -1
  426. package/dist/schema/blocks.d.ts +473 -0
  427. package/dist/schema/blocks.js +105 -5
  428. package/dist/schema/blocks.js.map +1 -1
  429. package/dist/schema/business.d.ts +118 -0
  430. package/dist/schema/business.js +75 -0
  431. package/dist/schema/business.js.map +1 -0
  432. package/dist/schema/fields.d.ts +2 -1
  433. package/dist/schema/fields.js.map +1 -1
  434. package/dist/schema/forms.d.ts +18 -0
  435. package/dist/schema/forms.js +18 -3
  436. package/dist/schema/forms.js.map +1 -1
  437. package/dist/schema/mail.d.ts +10 -0
  438. package/dist/schema/mail.js +7 -0
  439. package/dist/schema/mail.js.map +1 -1
  440. package/dist/schema/settings.d.ts +103 -0
  441. package/dist/schema/settings.js +24 -3
  442. package/dist/schema/settings.js.map +1 -1
  443. package/dist/schema/shop.d.ts +2 -0
  444. package/dist/schema/shop.js +4 -0
  445. package/dist/schema/shop.js.map +1 -1
  446. package/dist/schema/shopGeld.d.ts +1 -0
  447. package/dist/schema/shopGeld.js +1 -0
  448. package/dist/schema/shopGeld.js.map +1 -1
  449. package/dist/schema/siteConfig.d.ts +69 -1
  450. package/dist/schema/siteConfig.js +7 -2
  451. package/dist/schema/siteConfig.js.map +1 -1
  452. package/dist/server/blockData.d.ts +1 -1
  453. package/dist/server/blockData.js +74 -18
  454. package/dist/server/blockData.js.map +1 -1
  455. package/dist/server/cspRapport.d.ts +20 -0
  456. package/dist/server/cspRapport.js +56 -0
  457. package/dist/server/cspRapport.js.map +1 -0
  458. package/dist/server/fonts.d.ts +3 -0
  459. package/dist/server/fonts.js +39 -0
  460. package/dist/server/fonts.js.map +1 -0
  461. package/dist/server/index.d.ts +3 -0
  462. package/dist/server/index.js +8 -0
  463. package/dist/server/index.js.map +1 -1
  464. package/dist/server/sanitize.js +2 -0
  465. package/dist/server/sanitize.js.map +1 -1
  466. package/dist/settings/useSiteSettings.js +4 -1
  467. package/dist/settings/useSiteSettings.js.map +1 -1
  468. package/dist/starters/starters.d.ts +69 -0
  469. package/dist/starters/starters.js +171 -0
  470. package/dist/starters/starters.js.map +1 -0
  471. package/dist/styles.css +1181 -212
  472. package/dist/theme/color.d.ts +10 -0
  473. package/dist/theme/color.js +17 -1
  474. package/dist/theme/color.js.map +1 -1
  475. package/dist/theme/fontFallback.d.ts +23 -0
  476. package/dist/theme/fontFallback.js +58 -0
  477. package/dist/theme/fontFallback.js.map +1 -0
  478. package/dist/theme/fonts.d.ts +11 -0
  479. package/dist/theme/fonts.js +16 -0
  480. package/dist/theme/fonts.js.map +1 -0
  481. package/dist/version.js +1 -1
  482. package/package.json +5 -4
@@ -6,9 +6,11 @@ import { callable } from "../../firebase/functionsClient.js";
6
6
  import { getDb, getFirebaseApp } from "../../firebase/client.js";
7
7
  import { CustomBlockSchema } from "../../schema/customBlocks.js";
8
8
  import { FieldsSchema } from "../../schema/fields.js";
9
- import { Button, Field, Input, Textarea } from "../ui/primitives.js";
9
+ import { Button, Field, Input, Textarea, LegeStaat } from "../ui/primitives.js";
10
10
  import { useToast } from "../ui/toast.js";
11
+ import { FoutMelding } from "../ui/LaadStaat.js";
11
12
  import { useConfirm } from "../ui/ConfirmDialog.js";
13
+ import { zodFoutTekst } from "../lib/zodFout.js";
12
14
  const EMPTY = {
13
15
  type: "custom-",
14
16
  label: "",
@@ -32,19 +34,23 @@ function CustomBlocksPanel({ siteId }) {
32
34
  const [editing, setEditing] = useState(null);
33
35
  const [fieldsText, setFieldsText] = useState("[]");
34
36
  const [busy, setBusy] = useState(false);
37
+ const [typeVast, setTypeVast] = useState(false);
38
+ const [laadFout, setLaadFout] = useState(null);
35
39
  useEffect(() => {
36
40
  const db = getDb();
37
41
  if (!db) return;
38
42
  return onSnapshot(collection(db, "sites", siteId, "customBlocks"), (snap) => {
43
+ setLaadFout(null);
39
44
  const out = [];
40
45
  snap.forEach((d) => {
41
46
  const res = CustomBlockSchema.safeParse({ ...d.data(), type: d.id });
42
47
  if (res.success) out.push(res.data);
43
48
  });
44
49
  setBlocks(out.sort((a, b) => a.label.localeCompare(b.label)));
45
- });
50
+ }, (e) => setLaadFout(e.message));
46
51
  }, [siteId]);
47
52
  function open(def) {
53
+ setTypeVast(blocks.some((b) => b.type === def.type));
48
54
  setEditing(def);
49
55
  setFieldsText(JSON.stringify(def.fields, null, 2));
50
56
  }
@@ -69,12 +75,16 @@ function CustomBlocksPanel({ siteId }) {
69
75
  }
70
76
  const parsedFields = FieldsSchema.safeParse(fields);
71
77
  if (!parsedFields.success) {
72
- toast({ title: "Velden ongeldig", description: parsedFields.error.issues[0]?.message, tone: "error" });
78
+ toast({ title: "Velden ongeldig", description: zodFoutTekst(parsedFields.error.issues[0]), tone: "error" });
73
79
  return;
74
80
  }
75
81
  const candidate = CustomBlockSchema.safeParse({ ...editing, fields: parsedFields.data });
76
82
  if (!candidate.success) {
77
- toast({ title: "Bouwsteen ongeldig", description: candidate.error.issues[0]?.message, tone: "error" });
83
+ toast({ title: "Bouwsteen ongeldig", description: zodFoutTekst(candidate.error.issues[0]), tone: "error" });
84
+ return;
85
+ }
86
+ if (!typeVast && blocks.some((b) => b.type === candidate.data.type)) {
87
+ toast({ title: "Dit bloktype bestaat al", description: "Kies een ander type, of bewerk de bestaande bouwsteen.", tone: "error" });
78
88
  return;
79
89
  }
80
90
  const app = getFirebaseApp();
@@ -107,7 +117,7 @@ function CustomBlocksPanel({ siteId }) {
107
117
  return /* @__PURE__ */ jsxs("div", { className: "grid max-w-3xl gap-3", children: [
108
118
  /* @__PURE__ */ jsx("h2", { className: "text-lg font-bold", children: editing.label || editing.type }),
109
119
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
110
- /* @__PURE__ */ jsx(Field, { label: "Bloktype", hint: 'begint met "custom-"; niet meer te wijzigen na aanmaken', children: /* @__PURE__ */ jsx(Input, { value: editing.type, onChange: (e) => setEditing({ ...editing, type: e.target.value }), disabled: blocks.some((b) => b.type === editing.type) }) }),
120
+ /* @__PURE__ */ jsx(Field, { label: "Bloktype", hint: 'begint met "custom-"; niet meer te wijzigen na aanmaken', children: /* @__PURE__ */ jsx(Input, { value: editing.type, onChange: (e) => setEditing({ ...editing, type: e.target.value }), disabled: typeVast }) }),
111
121
  /* @__PURE__ */ jsx(Field, { label: "Naam in het menu", children: /* @__PURE__ */ jsx(Input, { value: editing.label, onChange: (e) => setEditing({ ...editing, label: e.target.value }) }) })
112
122
  ] }),
113
123
  /* @__PURE__ */ jsx(Field, { label: "Icoon (emoji)", children: /* @__PURE__ */ jsx(Input, { value: editing.icon, maxLength: 16, onChange: (e) => setEditing({ ...editing, icon: e.target.value }), className: "w-24" }) }),
@@ -116,7 +126,7 @@ ${FIELDS_VOORBEELD}`, children: /* @__PURE__ */ jsx(Textarea, { rows: 10, value:
116
126
  /* @__PURE__ */ jsx(Field, { label: "Template", hint: "Mustache-subset: {{veld}}, {{{veldHtml}}}, {{#each items}}\u2026{{/each}}, {{#if veld}}\u2026{{else}}\u2026{{/if}}. Geen scripts \u2014 die worden bij opslaan gestript.", children: /* @__PURE__ */ jsx(Textarea, { rows: 10, value: editing.template, onChange: (e) => setEditing({ ...editing, template: e.target.value }), spellCheck: false, className: "font-mono text-xs" }) }),
117
127
  /* @__PURE__ */ jsx(Field, { label: "CSS", hint: 'Automatisch gescoped op dit blok ([data-xicms-cb="\u2026"]).', children: /* @__PURE__ */ jsx(Textarea, { rows: 8, value: editing.css, onChange: (e) => setEditing({ ...editing, css: e.target.value }), spellCheck: false, className: "font-mono text-xs" }) }),
118
128
  /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
119
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "primary", disabled: busy, onClick: () => void save(), children: busy ? "Opslaan\u2026" : "Opslaan" }),
129
+ /* @__PURE__ */ jsx(Button, { loading: busy, type: "button", variant: "primary", disabled: busy, onClick: () => void save(), children: "Opslaan" }),
120
130
  /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", onClick: () => setEditing(null), children: "Annuleren" })
121
131
  ] })
122
132
  ] });
@@ -126,8 +136,9 @@ ${FIELDS_VOORBEELD}`, children: /* @__PURE__ */ jsx(Textarea, { rows: 10, value:
126
136
  /* @__PURE__ */ jsx("h2", { className: "text-lg font-bold", children: "Bouwstenen" }),
127
137
  /* @__PURE__ */ jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => open(EMPTY), children: "+ Nieuwe bouwsteen" })
128
138
  ] }),
139
+ laadFout && /* @__PURE__ */ jsx(FoutMelding, { wat: "de bouwstenen", detail: laadFout }),
129
140
  /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Eigen bloktypes voor deze site \u2014 zonder release. Definieer velden, een veilige template en CSS; het blok verschijnt direct in het toevoeg-menu van de pagina-editor." }),
130
- blocks.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Nog geen bouwstenen." }),
141
+ blocks.length === 0 && /* @__PURE__ */ jsx(LegeStaat, { titel: "Nog geen bouwstenen", tekst: "Een bouwsteen is een eigen bloktype voor deze site, zonder nieuwe release." }),
131
142
  /* @__PURE__ */ jsx("ul", { className: "grid gap-2", children: blocks.map((b) => /* @__PURE__ */ jsxs("li", { className: "flex items-center justify-between rounded-lg border border-slate-200 p-3", children: [
132
143
  /* @__PURE__ */ jsxs("div", { children: [
133
144
  /* @__PURE__ */ jsx("span", { className: "mr-2", children: b.icon }),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/admin/customBlocks/CustomBlocksPanel.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * Beheer-paneel \"Bouwstenen\" (5.0): custom blocks — per site gedefinieerde bloktypes zonder\n * deploy (primo-concept, veilige template-variant). Voor de developer-/agent-rol: velden als\n * Field-IR (JSON, gevalideerd vóór opslaan), een veilige mustache-subset-template en scoped CSS.\n * Opslaan loopt via de saveCustomBlock-callable (sanitize + audit); het blok verschijnt direct\n * in het toevoeg-menu van de editor (live registry-sync in XiCMSProvider).\n */\nimport { useEffect, useState } from \"react\";\nimport { collection, onSnapshot } from \"firebase/firestore\";\nimport { callable } from \"../../firebase/functionsClient\";\nimport { getDb, getFirebaseApp } from \"../../firebase/client\";\nimport { CustomBlockSchema, type CustomBlock } from \"../../schema/customBlocks\";\nimport { FieldsSchema } from \"../../schema/fields\";\nimport { Button, Field, Input, Textarea } from \"../ui/primitives\";\nimport { useToast } from \"../ui/toast\";\nimport { useConfirm } from \"../ui/ConfirmDialog\";\n\nconst EMPTY: CustomBlock = {\n type: \"custom-\",\n label: \"\",\n icon: \"🧩\",\n fields: [],\n template: \"\",\n css: \"\",\n version: 1,\n};\n\nconst FIELDS_VOORBEELD = `[\n { \"key\": \"heading\", \"label\": \"Kop\", \"type\": \"text\" },\n { \"key\": \"items\", \"label\": \"Items\", \"type\": \"repeater\", \"fields\": [\n { \"key\": \"title\", \"label\": \"Titel\", \"type\": \"text\" },\n { \"key\": \"bodyHtml\", \"label\": \"Tekst\", \"type\": \"richtext\" }\n ] }\n]`;\n\nexport function CustomBlocksPanel({ siteId }: { siteId: string }) {\n const { toast } = useToast();\n const confirm = useConfirm();\n const [blocks, setBlocks] = useState<CustomBlock[]>([]);\n const [editing, setEditing] = useState<CustomBlock | null>(null);\n const [fieldsText, setFieldsText] = useState(\"[]\");\n const [busy, setBusy] = useState(false);\n\n useEffect(() => {\n const db = getDb();\n if (!db) return;\n return onSnapshot(collection(db, \"sites\", siteId, \"customBlocks\"), (snap) => {\n const out: CustomBlock[] = [];\n snap.forEach((d) => {\n const res = CustomBlockSchema.safeParse({ ...(d.data() as Record<string, unknown>), type: d.id });\n if (res.success) out.push(res.data);\n });\n setBlocks(out.sort((a, b) => a.label.localeCompare(b.label)));\n });\n }, [siteId]);\n\n function open(def: CustomBlock) {\n setEditing(def);\n setFieldsText(JSON.stringify(def.fields, null, 2));\n }\n\n async function save() {\n if (!editing) return;\n let fields: unknown;\n try {\n fields = JSON.parse(fieldsText);\n } catch (e) {\n // Regel/kolom uit de parserfout halen: \"position N\" zonder context was ondebugbaar bij\n // een handgetypte Field-IR — één verkeerde komma blokkeerde het hele bouwsteen-opslaan.\n const msg = (e as Error).message;\n const pos = /position (\\d+)/.exec(msg);\n let where = \"\";\n if (pos) {\n const n = Number(pos[1]);\n const before = fieldsText.slice(0, n);\n const line = before.split(\"\\n\").length;\n const col = n - before.lastIndexOf(\"\\n\");\n where = ` (regel ${line}, kolom ${col})`;\n }\n toast({ title: \"Velden zijn geen geldige JSON\", description: `${msg}${where}`, tone: \"error\" });\n return;\n }\n const parsedFields = FieldsSchema.safeParse(fields);\n if (!parsedFields.success) {\n toast({ title: \"Velden ongeldig\", description: parsedFields.error.issues[0]?.message, tone: \"error\" });\n return;\n }\n const candidate = CustomBlockSchema.safeParse({ ...editing, fields: parsedFields.data });\n if (!candidate.success) {\n toast({ title: \"Bouwsteen ongeldig\", description: candidate.error.issues[0]?.message, tone: \"error\" });\n return;\n }\n const app = getFirebaseApp();\n if (!app) return;\n setBusy(true);\n try {\n const fn = callable(app, \"saveCustomBlock\");\n const res = await fn({ siteId, ...candidate.data });\n toast({ title: \"Opgeslagen\", description: (res.data as { message?: string }).message, tone: \"success\" });\n setEditing(null);\n } catch (e) {\n toast({ title: \"Opslaan mislukt\", description: (e as Error).message, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n async function remove(type: string) {\n const ok = await confirm({ title: \"Bouwsteen verwijderen?\", description: \"Pagina's die dit blok gebruiken slaan het daarna over.\", confirmLabel: \"Verwijderen\", tone: \"danger\" });\n if (!ok) return;\n const app = getFirebaseApp();\n if (!app) return;\n try {\n await callable(app, \"deleteCustomBlock\")({ siteId, type });\n toast({ title: \"Verwijderd\", tone: \"success\" });\n } catch (e) {\n toast({ title: \"Verwijderen mislukt\", description: (e as Error).message, tone: \"error\" });\n }\n }\n\n if (editing) {\n return (\n <div className=\"grid max-w-3xl gap-3\">\n <h2 className=\"text-lg font-bold\">{editing.label || editing.type}</h2>\n <div className=\"grid grid-cols-2 gap-2\">\n <Field label=\"Bloktype\" hint='begint met \"custom-\"; niet meer te wijzigen na aanmaken'>\n <Input value={editing.type} onChange={(e) => setEditing({ ...editing, type: e.target.value })} disabled={blocks.some((b) => b.type === editing.type)} />\n </Field>\n <Field label=\"Naam in het menu\">\n <Input value={editing.label} onChange={(e) => setEditing({ ...editing, label: e.target.value })} />\n </Field>\n </div>\n <Field label=\"Icoon (emoji)\">\n <Input value={editing.icon} maxLength={16} onChange={(e) => setEditing({ ...editing, icon: e.target.value })} className=\"w-24\" />\n </Field>\n <Field label=\"Velden (Field-IR, JSON)\" hint={`Veldtypes: text, textarea, richtext (sleutel eindigt op Html), number, switch, select, date, image, url, link, group, repeater. Voorbeeld:\\n${FIELDS_VOORBEELD}`}>\n <Textarea rows={10} value={fieldsText} onChange={(e) => setFieldsText(e.target.value)} spellCheck={false} className=\"font-mono text-xs\" />\n </Field>\n <Field label=\"Template\" hint=\"Mustache-subset: {{veld}}, {{{veldHtml}}}, {{#each items}}…{{/each}}, {{#if veld}}…{{else}}…{{/if}}. Geen scripts — die worden bij opslaan gestript.\">\n <Textarea rows={10} value={editing.template} onChange={(e) => setEditing({ ...editing, template: e.target.value })} spellCheck={false} className=\"font-mono text-xs\" />\n </Field>\n <Field label=\"CSS\" hint='Automatisch gescoped op dit blok ([data-xicms-cb=\"…\"]).'>\n <Textarea rows={8} value={editing.css} onChange={(e) => setEditing({ ...editing, css: e.target.value })} spellCheck={false} className=\"font-mono text-xs\" />\n </Field>\n <div className=\"flex gap-2\">\n <Button type=\"button\" variant=\"primary\" disabled={busy} onClick={() => void save()}>{busy ? \"Opslaan…\" : \"Opslaan\"}</Button>\n <Button type=\"button\" variant=\"ghost\" onClick={() => setEditing(null)}>Annuleren</Button>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"grid gap-3\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-bold\">Bouwstenen</h2>\n <Button type=\"button\" variant=\"secondary\" size=\"sm\" onClick={() => open(EMPTY)}>+ Nieuwe bouwsteen</Button>\n </div>\n <p className=\"text-sm text-slate-500\">\n Eigen bloktypes voor deze site — zonder release. Definieer velden, een veilige template en CSS;\n het blok verschijnt direct in het toevoeg-menu van de pagina-editor.\n </p>\n {blocks.length === 0 && <p className=\"text-sm text-slate-500\">Nog geen bouwstenen.</p>}\n <ul className=\"grid gap-2\">\n {blocks.map((b) => (\n <li key={b.type} className=\"flex items-center justify-between rounded-lg border border-slate-200 p-3\">\n <div>\n <span className=\"mr-2\">{b.icon}</span>\n <strong>{b.label}</strong> <span className=\"text-xs text-slate-500\">({b.type} · v{b.version})</span>\n </div>\n <div className=\"flex gap-2\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => open(b)}>Bewerken</Button>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => void remove(b.type)}>Verwijderen</Button>\n </div>\n </li>\n ))}\n </ul>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/admin/customBlocks/CustomBlocksPanel.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * Beheer-paneel \"Bouwstenen\" (5.0): custom blocks — per site gedefinieerde bloktypes zonder\n * deploy (primo-concept, veilige template-variant). Voor de developer-/agent-rol: velden als\n * Field-IR (JSON, gevalideerd vóór opslaan), een veilige mustache-subset-template en scoped CSS.\n * Opslaan loopt via de saveCustomBlock-callable (sanitize + audit); het blok verschijnt direct\n * in het toevoeg-menu van de editor (live registry-sync in XiCMSProvider).\n */\nimport { useEffect, useState } from \"react\";\nimport { collection, onSnapshot } from \"firebase/firestore\";\nimport { callable } from \"../../firebase/functionsClient\";\nimport { getDb, getFirebaseApp } from \"../../firebase/client\";\nimport { CustomBlockSchema, type CustomBlock } from \"../../schema/customBlocks\";\nimport { FieldsSchema } from \"../../schema/fields\";\nimport { Button, Field, Input, Textarea, LegeStaat } from \"../ui/primitives\";\nimport { useToast } from \"../ui/toast\";\nimport { FoutMelding } from \"../ui/LaadStaat\";\nimport { useConfirm } from \"../ui/ConfirmDialog\";\nimport { zodFoutTekst } from \"../lib/zodFout\";\n\nconst EMPTY: CustomBlock = {\n type: \"custom-\",\n label: \"\",\n icon: \"🧩\",\n fields: [],\n template: \"\",\n css: \"\",\n version: 1,\n};\n\nconst FIELDS_VOORBEELD = `[\n { \"key\": \"heading\", \"label\": \"Kop\", \"type\": \"text\" },\n { \"key\": \"items\", \"label\": \"Items\", \"type\": \"repeater\", \"fields\": [\n { \"key\": \"title\", \"label\": \"Titel\", \"type\": \"text\" },\n { \"key\": \"bodyHtml\", \"label\": \"Tekst\", \"type\": \"richtext\" }\n ] }\n]`;\n\nexport function CustomBlocksPanel({ siteId }: { siteId: string }) {\n const { toast } = useToast();\n const confirm = useConfirm();\n const [blocks, setBlocks] = useState<CustomBlock[]>([]);\n const [editing, setEditing] = useState<CustomBlock | null>(null);\n const [fieldsText, setFieldsText] = useState(\"[]\");\n const [busy, setBusy] = useState(false);\n // Het type van een bestáánde bouwsteen ligt vast (blokken verwijzen ernaar). Bij een nieuwe niet:\n // vergelijken met de lijst zou het veld op slot zetten zodra je toevallig een bestaand type typt.\n const [typeVast, setTypeVast] = useState(false);\n const [laadFout, setLaadFout] = useState<string | null>(null);\n\n useEffect(() => {\n const db = getDb();\n if (!db) return;\n return onSnapshot(collection(db, \"sites\", siteId, \"customBlocks\"), (snap) => {\n setLaadFout(null);\n const out: CustomBlock[] = [];\n snap.forEach((d) => {\n const res = CustomBlockSchema.safeParse({ ...(d.data() as Record<string, unknown>), type: d.id });\n if (res.success) out.push(res.data);\n });\n setBlocks(out.sort((a, b) => a.label.localeCompare(b.label)));\n }, (e) => setLaadFout(e.message));\n }, [siteId]);\n\n function open(def: CustomBlock) {\n setTypeVast(blocks.some((b) => b.type === def.type));\n setEditing(def);\n setFieldsText(JSON.stringify(def.fields, null, 2));\n }\n\n async function save() {\n if (!editing) return;\n let fields: unknown;\n try {\n fields = JSON.parse(fieldsText);\n } catch (e) {\n // Regel/kolom uit de parserfout halen: \"position N\" zonder context was ondebugbaar bij\n // een handgetypte Field-IR — één verkeerde komma blokkeerde het hele bouwsteen-opslaan.\n const msg = (e as Error).message;\n const pos = /position (\\d+)/.exec(msg);\n let where = \"\";\n if (pos) {\n const n = Number(pos[1]);\n const before = fieldsText.slice(0, n);\n const line = before.split(\"\\n\").length;\n const col = n - before.lastIndexOf(\"\\n\");\n where = ` (regel ${line}, kolom ${col})`;\n }\n toast({ title: \"Velden zijn geen geldige JSON\", description: `${msg}${where}`, tone: \"error\" });\n return;\n }\n const parsedFields = FieldsSchema.safeParse(fields);\n if (!parsedFields.success) {\n toast({ title: \"Velden ongeldig\", description: zodFoutTekst(parsedFields.error.issues[0]), tone: \"error\" });\n return;\n }\n const candidate = CustomBlockSchema.safeParse({ ...editing, fields: parsedFields.data });\n if (!candidate.success) {\n toast({ title: \"Bouwsteen ongeldig\", description: zodFoutTekst(candidate.error.issues[0]), tone: \"error\" });\n return;\n }\n if (!typeVast && blocks.some((b) => b.type === candidate.data.type)) {\n toast({ title: \"Dit bloktype bestaat al\", description: \"Kies een ander type, of bewerk de bestaande bouwsteen.\", tone: \"error\" });\n return;\n }\n const app = getFirebaseApp();\n if (!app) return;\n setBusy(true);\n try {\n const fn = callable(app, \"saveCustomBlock\");\n const res = await fn({ siteId, ...candidate.data });\n toast({ title: \"Opgeslagen\", description: (res.data as { message?: string }).message, tone: \"success\" });\n setEditing(null);\n } catch (e) {\n toast({ title: \"Opslaan mislukt\", description: (e as Error).message, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n async function remove(type: string) {\n const ok = await confirm({ title: \"Bouwsteen verwijderen?\", description: \"Pagina's die dit blok gebruiken slaan het daarna over.\", confirmLabel: \"Verwijderen\", tone: \"danger\" });\n if (!ok) return;\n const app = getFirebaseApp();\n if (!app) return;\n try {\n await callable(app, \"deleteCustomBlock\")({ siteId, type });\n toast({ title: \"Verwijderd\", tone: \"success\" });\n } catch (e) {\n toast({ title: \"Verwijderen mislukt\", description: (e as Error).message, tone: \"error\" });\n }\n }\n\n if (editing) {\n return (\n <div className=\"grid max-w-3xl gap-3\">\n <h2 className=\"text-lg font-bold\">{editing.label || editing.type}</h2>\n <div className=\"grid grid-cols-2 gap-2\">\n <Field label=\"Bloktype\" hint='begint met \"custom-\"; niet meer te wijzigen na aanmaken'>\n <Input value={editing.type} onChange={(e) => setEditing({ ...editing, type: e.target.value })} disabled={typeVast} />\n </Field>\n <Field label=\"Naam in het menu\">\n <Input value={editing.label} onChange={(e) => setEditing({ ...editing, label: e.target.value })} />\n </Field>\n </div>\n <Field label=\"Icoon (emoji)\">\n <Input value={editing.icon} maxLength={16} onChange={(e) => setEditing({ ...editing, icon: e.target.value })} className=\"w-24\" />\n </Field>\n <Field label=\"Velden (Field-IR, JSON)\" hint={`Veldtypes: text, textarea, richtext (sleutel eindigt op Html), number, switch, select, date, image, url, link, group, repeater. Voorbeeld:\\n${FIELDS_VOORBEELD}`}>\n <Textarea rows={10} value={fieldsText} onChange={(e) => setFieldsText(e.target.value)} spellCheck={false} className=\"font-mono text-xs\" />\n </Field>\n <Field label=\"Template\" hint=\"Mustache-subset: {{veld}}, {{{veldHtml}}}, {{#each items}}…{{/each}}, {{#if veld}}…{{else}}…{{/if}}. Geen scripts — die worden bij opslaan gestript.\">\n <Textarea rows={10} value={editing.template} onChange={(e) => setEditing({ ...editing, template: e.target.value })} spellCheck={false} className=\"font-mono text-xs\" />\n </Field>\n <Field label=\"CSS\" hint='Automatisch gescoped op dit blok ([data-xicms-cb=\"…\"]).'>\n <Textarea rows={8} value={editing.css} onChange={(e) => setEditing({ ...editing, css: e.target.value })} spellCheck={false} className=\"font-mono text-xs\" />\n </Field>\n <div className=\"flex gap-2\">\n <Button loading={busy} type=\"button\" variant=\"primary\" disabled={busy} onClick={() => void save()}>{\"Opslaan\"}</Button>\n <Button type=\"button\" variant=\"ghost\" onClick={() => setEditing(null)}>Annuleren</Button>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"grid gap-3\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-bold\">Bouwstenen</h2>\n <Button type=\"button\" variant=\"secondary\" size=\"sm\" onClick={() => open(EMPTY)}>+ Nieuwe bouwsteen</Button>\n </div>\n {laadFout && <FoutMelding wat=\"de bouwstenen\" detail={laadFout} />}\n <p className=\"text-sm text-slate-500\">\n Eigen bloktypes voor deze site — zonder release. Definieer velden, een veilige template en CSS;\n het blok verschijnt direct in het toevoeg-menu van de pagina-editor.\n </p>\n {blocks.length === 0 && <LegeStaat titel=\"Nog geen bouwstenen\" tekst=\"Een bouwsteen is een eigen bloktype voor deze site, zonder nieuwe release.\" />}\n <ul className=\"grid gap-2\">\n {blocks.map((b) => (\n <li key={b.type} className=\"flex items-center justify-between rounded-lg border border-slate-200 p-3\">\n <div>\n <span className=\"mr-2\">{b.icon}</span>\n <strong>{b.label}</strong> <span className=\"text-xs text-slate-500\">({b.type} · v{b.version})</span>\n </div>\n <div className=\"flex gap-2\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => open(b)}>Bewerken</Button>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => void remove(b.type)}>Verwijderen</Button>\n </div>\n </li>\n ))}\n </ul>\n </div>\n );\n}\n"],"mappings":";AAyIQ,cACA,YADA;AAhIR,SAAS,WAAW,gBAAgB;AACpC,SAAS,YAAY,kBAAkB;AACvC,SAAS,gBAAgB;AACzB,SAAS,OAAO,sBAAsB;AACtC,SAAS,yBAA2C;AACpD,SAAS,oBAAoB;AAC7B,SAAS,QAAQ,OAAO,OAAO,UAAU,iBAAiB;AAC1D,SAAS,gBAAgB;AACzB,SAAS,mBAAmB;AAC5B,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB;AAE7B,MAAM,QAAqB;AAAA,EACzB,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AAAA,EACN,QAAQ,CAAC;AAAA,EACT,UAAU;AAAA,EACV,KAAK;AAAA,EACL,SAAS;AACX;AAEA,MAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQlB,SAAS,kBAAkB,EAAE,OAAO,GAAuB;AAChE,QAAM,EAAE,MAAM,IAAI,SAAS;AAC3B,QAAM,UAAU,WAAW;AAC3B,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAwB,CAAC,CAAC;AACtD,QAAM,CAAC,SAAS,UAAU,IAAI,SAA6B,IAAI;AAC/D,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,IAAI;AACjD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AAGtC,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,CAAC,UAAU,WAAW,IAAI,SAAwB,IAAI;AAE5D,YAAU,MAAM;AACd,UAAM,KAAK,MAAM;AACjB,QAAI,CAAC,GAAI;AACT,WAAO,WAAW,WAAW,IAAI,SAAS,QAAQ,cAAc,GAAG,CAAC,SAAS;AAC3E,kBAAY,IAAI;AAChB,YAAM,MAAqB,CAAC;AAC5B,WAAK,QAAQ,CAAC,MAAM;AAClB,cAAM,MAAM,kBAAkB,UAAU,EAAE,GAAI,EAAE,KAAK,GAA+B,MAAM,EAAE,GAAG,CAAC;AAChG,YAAI,IAAI,QAAS,KAAI,KAAK,IAAI,IAAI;AAAA,MACpC,CAAC;AACD,gBAAU,IAAI,KAAK,CAAC,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,KAAK,CAAC,CAAC;AAAA,IAC9D,GAAG,CAAC,MAAM,YAAY,EAAE,OAAO,CAAC;AAAA,EAClC,GAAG,CAAC,MAAM,CAAC;AAEX,WAAS,KAAK,KAAkB;AAC9B,gBAAY,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,IAAI,CAAC;AACnD,eAAW,GAAG;AACd,kBAAc,KAAK,UAAU,IAAI,QAAQ,MAAM,CAAC,CAAC;AAAA,EACnD;AAEA,iBAAe,OAAO;AACpB,QAAI,CAAC,QAAS;AACd,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,UAAU;AAAA,IAChC,SAAS,GAAG;AAGV,YAAM,MAAO,EAAY;AACzB,YAAM,MAAM,iBAAiB,KAAK,GAAG;AACrC,UAAI,QAAQ;AACZ,UAAI,KAAK;AACP,cAAM,IAAI,OAAO,IAAI,CAAC,CAAC;AACvB,cAAM,SAAS,WAAW,MAAM,GAAG,CAAC;AACpC,cAAM,OAAO,OAAO,MAAM,IAAI,EAAE;AAChC,cAAM,MAAM,IAAI,OAAO,YAAY,IAAI;AACvC,gBAAQ,WAAW,IAAI,WAAW,GAAG;AAAA,MACvC;AACA,YAAM,EAAE,OAAO,iCAAiC,aAAa,GAAG,GAAG,GAAG,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC9F;AAAA,IACF;AACA,UAAM,eAAe,aAAa,UAAU,MAAM;AAClD,QAAI,CAAC,aAAa,SAAS;AACzB,YAAM,EAAE,OAAO,mBAAmB,aAAa,aAAa,aAAa,MAAM,OAAO,CAAC,CAAC,GAAG,MAAM,QAAQ,CAAC;AAC1G;AAAA,IACF;AACA,UAAM,YAAY,kBAAkB,UAAU,EAAE,GAAG,SAAS,QAAQ,aAAa,KAAK,CAAC;AACvF,QAAI,CAAC,UAAU,SAAS;AACtB,YAAM,EAAE,OAAO,sBAAsB,aAAa,aAAa,UAAU,MAAM,OAAO,CAAC,CAAC,GAAG,MAAM,QAAQ,CAAC;AAC1G;AAAA,IACF;AACA,QAAI,CAAC,YAAY,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,UAAU,KAAK,IAAI,GAAG;AACnE,YAAM,EAAE,OAAO,2BAA2B,aAAa,0DAA0D,MAAM,QAAQ,CAAC;AAChI;AAAA,IACF;AACA,UAAM,MAAM,eAAe;AAC3B,QAAI,CAAC,IAAK;AACV,YAAQ,IAAI;AACZ,QAAI;AACF,YAAM,KAAK,SAAS,KAAK,iBAAiB;AAC1C,YAAM,MAAM,MAAM,GAAG,EAAE,QAAQ,GAAG,UAAU,KAAK,CAAC;AAClD,YAAM,EAAE,OAAO,cAAc,aAAc,IAAI,KAA8B,SAAS,MAAM,UAAU,CAAC;AACvG,iBAAW,IAAI;AAAA,IACjB,SAAS,GAAG;AACV,YAAM,EAAE,OAAO,mBAAmB,aAAc,EAAY,SAAS,MAAM,QAAQ,CAAC;AAAA,IACtF,UAAE;AACA,cAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAEA,iBAAe,OAAO,MAAc;AAClC,UAAM,KAAK,MAAM,QAAQ,EAAE,OAAO,0BAA0B,aAAa,0DAA0D,cAAc,eAAe,MAAM,SAAS,CAAC;AAChL,QAAI,CAAC,GAAI;AACT,UAAM,MAAM,eAAe;AAC3B,QAAI,CAAC,IAAK;AACV,QAAI;AACF,YAAM,SAAS,KAAK,mBAAmB,EAAE,EAAE,QAAQ,KAAK,CAAC;AACzD,YAAM,EAAE,OAAO,cAAc,MAAM,UAAU,CAAC;AAAA,IAChD,SAAS,GAAG;AACV,YAAM,EAAE,OAAO,uBAAuB,aAAc,EAAY,SAAS,MAAM,QAAQ,CAAC;AAAA,IAC1F;AAAA,EACF;AAEA,MAAI,SAAS;AACX,WACE,qBAAC,SAAI,WAAU,wBACb;AAAA,0BAAC,QAAG,WAAU,qBAAqB,kBAAQ,SAAS,QAAQ,MAAK;AAAA,MACjE,qBAAC,SAAI,WAAU,0BACb;AAAA,4BAAC,SAAM,OAAM,YAAW,MAAK,2DAC3B,8BAAC,SAAM,OAAO,QAAQ,MAAM,UAAU,CAAC,MAAM,WAAW,EAAE,GAAG,SAAS,MAAM,EAAE,OAAO,MAAM,CAAC,GAAG,UAAU,UAAU,GACrH;AAAA,QACA,oBAAC,SAAM,OAAM,oBACX,8BAAC,SAAM,OAAO,QAAQ,OAAO,UAAU,CAAC,MAAM,WAAW,EAAE,GAAG,SAAS,OAAO,EAAE,OAAO,MAAM,CAAC,GAAG,GACnG;AAAA,SACF;AAAA,MACA,oBAAC,SAAM,OAAM,iBACX,8BAAC,SAAM,OAAO,QAAQ,MAAM,WAAW,IAAI,UAAU,CAAC,MAAM,WAAW,EAAE,GAAG,SAAS,MAAM,EAAE,OAAO,MAAM,CAAC,GAAG,WAAU,QAAO,GACjI;AAAA,MACA,oBAAC,SAAM,OAAM,2BAA0B,MAAM;AAAA,EAA+I,gBAAgB,IAC1M,8BAAC,YAAS,MAAM,IAAI,OAAO,YAAY,UAAU,CAAC,MAAM,cAAc,EAAE,OAAO,KAAK,GAAG,YAAY,OAAO,WAAU,qBAAoB,GAC1I;AAAA,MACA,oBAAC,SAAM,OAAM,YAAW,MAAK,4KAC3B,8BAAC,YAAS,MAAM,IAAI,OAAO,QAAQ,UAAU,UAAU,CAAC,MAAM,WAAW,EAAE,GAAG,SAAS,UAAU,EAAE,OAAO,MAAM,CAAC,GAAG,YAAY,OAAO,WAAU,qBAAoB,GACvK;AAAA,MACA,oBAAC,SAAM,OAAM,OAAM,MAAK,gEACtB,8BAAC,YAAS,MAAM,GAAG,OAAO,QAAQ,KAAK,UAAU,CAAC,MAAM,WAAW,EAAE,GAAG,SAAS,KAAK,EAAE,OAAO,MAAM,CAAC,GAAG,YAAY,OAAO,WAAU,qBAAoB,GAC5J;AAAA,MACA,qBAAC,SAAI,WAAU,cACb;AAAA,4BAAC,UAAO,SAAS,MAAM,MAAK,UAAS,SAAQ,WAAU,UAAU,MAAM,SAAS,MAAM,KAAK,KAAK,GAAI,qBAAU;AAAA,QAC9G,oBAAC,UAAO,MAAK,UAAS,SAAQ,SAAQ,SAAS,MAAM,WAAW,IAAI,GAAG,uBAAS;AAAA,SAClF;AAAA,OACF;AAAA,EAEJ;AAEA,SACE,qBAAC,SAAI,WAAU,cACb;AAAA,yBAAC,SAAI,WAAU,qCACb;AAAA,0BAAC,QAAG,WAAU,qBAAoB,wBAAU;AAAA,MAC5C,oBAAC,UAAO,MAAK,UAAS,SAAQ,aAAY,MAAK,MAAK,SAAS,MAAM,KAAK,KAAK,GAAG,gCAAkB;AAAA,OACpG;AAAA,IACC,YAAY,oBAAC,eAAY,KAAI,iBAAgB,QAAQ,UAAU;AAAA,IAChE,oBAAC,OAAE,WAAU,0BAAyB,uLAGtC;AAAA,IACC,OAAO,WAAW,KAAK,oBAAC,aAAU,OAAM,uBAAsB,OAAM,8EAA6E;AAAA,IAClJ,oBAAC,QAAG,WAAU,cACX,iBAAO,IAAI,CAAC,MACX,qBAAC,QAAgB,WAAU,4EACzB;AAAA,2BAAC,SACC;AAAA,4BAAC,UAAK,WAAU,QAAQ,YAAE,MAAK;AAAA,QAC/B,oBAAC,YAAQ,YAAE,OAAM;AAAA,QAAS;AAAA,QAAC,qBAAC,UAAK,WAAU,0BAAyB;AAAA;AAAA,UAAE,EAAE;AAAA,UAAK;AAAA,UAAK,EAAE;AAAA,UAAQ;AAAA,WAAC;AAAA,SAC/F;AAAA,MACA,qBAAC,SAAI,WAAU,cACb;AAAA,4BAAC,UAAO,MAAK,UAAS,SAAQ,SAAQ,MAAK,MAAK,SAAS,MAAM,KAAK,CAAC,GAAG,sBAAQ;AAAA,QAChF,oBAAC,UAAO,MAAK,UAAS,SAAQ,SAAQ,MAAK,MAAK,SAAS,MAAM,KAAK,OAAO,EAAE,IAAI,GAAG,yBAAW;AAAA,SACjG;AAAA,SARO,EAAE,IASX,CACD,GACH;AAAA,KACF;AAEJ;","names":[]}
@@ -20,7 +20,8 @@ import { useModules } from "../../modules/context.js";
20
20
  import { formatCents } from "../../schema/shop.js";
21
21
  import { Badge, Button, Card, Input } from "../ui/primitives.js";
22
22
  import { useToast } from "../ui/toast.js";
23
- import { useAdminNav } from "../navContext.js";
23
+ import { useAdminNav, vraagOpen } from "../navContext.js";
24
+ import { ORDER_STATUS_LABELS } from "../shop/lib.js";
24
25
  import { BalkjesLijst, DagStaven } from "../ui/charts.js";
25
26
  import { dagenInMaand, dagReeks, deltaTovVorige, huidigMaandId, maandLabel, topPaden } from "../stats/lib.js";
26
27
  import { paginaToestand } from "../lib/pagesClient.js";
@@ -100,7 +101,7 @@ function DashboardPanel({ siteId }) {
100
101
  setOrders([]);
101
102
  }
102
103
  getDocs(collection(db, "sites", siteId, "forms")).then(async (snap) => {
103
- const forms = snap.docs.slice(0, 4);
104
+ const forms = snap.docs.slice(0, 40);
104
105
  const uit = await Promise.all(
105
106
  forms.map(async (f) => {
106
107
  const subs = collection(db, "sites", siteId, "forms", f.id, "submissions");
@@ -157,9 +158,9 @@ function DashboardPanel({ siteId }) {
157
158
  /* @__PURE__ */ jsx("em", { children: "Bekijk site" }),
158
159
  " en kies ",
159
160
  /* @__PURE__ */ jsx("em", { children: "Bewerken" }),
160
- " in de zwarte balk. Elk blok krijgt een",
161
- " ",
162
- "potlood (tekst) en een tandwiel (instellingen) \u2014 je typt gewoon in de pagina."
161
+ " in de balk onderaan. Het potlood bij een blok bewerkt het op de pagina zelf, het tandwiel opent alle instellingen, en met ",
162
+ /* @__PURE__ */ jsx("em", { children: "+" }),
163
+ " voeg je iets toe."
163
164
  ] }),
164
165
  /* @__PURE__ */ jsxs("p", { children: [
165
166
  /* @__PURE__ */ jsx("strong", { className: "text-slate-800", children: "2 \xB7 Publiceer als het goed is." }),
@@ -176,7 +177,7 @@ function DashboardPanel({ siteId }) {
176
177
  /* @__PURE__ */ jsx("strong", { className: "text-slate-800", children: "3 \xB7 De rest staat hier." }),
177
178
  " Nieuwe pagina's, het menu, foto's, formulieren",
178
179
  heeftShop ? ", de webwinkel" : "",
179
- " en statistieken vind je in de secties hiernaast. Onderaan de zijbalk staat ook een korte helptekst."
180
+ " en statistieken vind je in de secties hiernaast. Iets kwijt? Druk Ctrl+K (of Cmd+K) en typ wat je zoekt."
180
181
  ] })
181
182
  ] })
182
183
  ] }),
@@ -252,15 +253,18 @@ function DashboardPanel({ siteId }) {
252
253
  "button",
253
254
  {
254
255
  type: "button",
255
- onClick: () => gaNaar("pages"),
256
+ onClick: () => {
257
+ vraagOpen("pages", p.id);
258
+ gaNaar("pages");
259
+ },
256
260
  className: "flex w-full items-center justify-between gap-2 rounded-lg px-2 py-1.5 text-left text-sm hover:bg-slate-100",
257
261
  title: `${p.title ?? p.id} \u2014 ${p.path ?? ""}`,
258
262
  children: [
259
263
  /* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
260
264
  /* @__PURE__ */ jsx("span", { className: "block truncate text-slate-800", children: p.title ?? p.id }),
261
- /* @__PURE__ */ jsx("span", { className: "block truncate text-xs text-slate-400", children: p.path })
265
+ /* @__PURE__ */ jsx("span", { className: "block truncate text-xs text-slate-500", children: p.path })
262
266
  ] }),
263
- /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-slate-400", children: fmtDatum(p.updatedAt) })
267
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-slate-500", children: fmtDatum(p.updatedAt) })
264
268
  ]
265
269
  }
266
270
  ) }, p.id)) })
@@ -271,16 +275,19 @@ function DashboardPanel({ siteId }) {
271
275
  "button",
272
276
  {
273
277
  type: "button",
274
- onClick: () => gaNaar("shop"),
278
+ onClick: () => {
279
+ vraagOpen("shop", o.id);
280
+ gaNaar("shop");
281
+ },
275
282
  className: "flex w-full items-center justify-between gap-2 rounded-lg px-2 py-1.5 text-left text-sm hover:bg-slate-100",
276
283
  children: [
277
284
  /* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
278
285
  /* @__PURE__ */ jsx("span", { className: "block truncate text-slate-800", children: o.customer?.name ?? "Onbekend" }),
279
- /* @__PURE__ */ jsx("span", { className: "block text-xs text-slate-400", children: fmtDatum(o.createdAt) })
286
+ /* @__PURE__ */ jsx("span", { className: "block text-xs text-slate-500", children: fmtDatum(o.createdAt) })
280
287
  ] }),
281
288
  /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-2", children: [
282
289
  typeof o.amount?.totalCents === "number" && /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-slate-700", children: formatCents(o.amount.totalCents) }),
283
- /* @__PURE__ */ jsx(Badge, { tone: o.status === "paid" || o.status === "shipped" ? "green" : "amber", children: o.status ?? "?" })
290
+ /* @__PURE__ */ jsx(Badge, { tone: ORDER_STATUS_LABELS[o.status ?? ""]?.tone ?? "amber", children: ORDER_STATUS_LABELS[o.status ?? ""]?.label ?? o.status ?? "Onbekend" })
284
291
  ] })
285
292
  ]
286
293
  }
@@ -288,11 +295,14 @@ function DashboardPanel({ siteId }) {
288
295
  ] }),
289
296
  /* @__PURE__ */ jsxs(Card, { children: [
290
297
  /* @__PURE__ */ jsx(WidgetKop, { titel: "Formulieren", tab: "forms", tabLabel: "Formulieren" }),
291
- formulieren === null ? /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Laden\u2026" }) : formulieren.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Nog geen formulieren." }) : /* @__PURE__ */ jsx("ul", { className: "grid gap-1", children: formulieren.map((f) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
298
+ formulieren === null ? /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Laden\u2026" }) : formulieren.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Nog geen formulieren." }) : /* @__PURE__ */ jsx("ul", { className: "grid gap-1", children: [...formulieren].sort((a, b) => b.nieuw - a.nieuw).slice(0, 4).map((f) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
292
299
  "button",
293
300
  {
294
301
  type: "button",
295
- onClick: () => gaNaar("forms"),
302
+ onClick: () => {
303
+ vraagOpen("forms", f.id);
304
+ gaNaar("forms");
305
+ },
296
306
  className: "flex w-full items-center justify-between gap-2 rounded-lg px-2 py-1.5 text-left text-sm hover:bg-slate-100",
297
307
  children: [
298
308
  /* @__PURE__ */ jsx("span", { className: "truncate text-slate-800", title: f.name, children: f.name }),
@@ -360,7 +370,7 @@ function DashboardPanel({ siteId }) {
360
370
  ),
361
371
  /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
362
372
  /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", onClick: () => setLinksBewerken(null), children: "Annuleren" }),
363
- /* @__PURE__ */ jsx(Button, { size: "sm", disabled: linksOpslaan, onClick: () => void slaLinksOp(), children: linksOpslaan ? "Bezig\u2026" : "Opslaan" })
373
+ /* @__PURE__ */ jsx(Button, { loading: linksOpslaan, size: "sm", disabled: linksOpslaan, onClick: () => void slaLinksOp(), children: "Opslaan" })
364
374
  ] })
365
375
  ] })
366
376
  ] }) : links.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: magInstellen ? "Nog geen snelkoppelingen. Zet hier de plekken waar je vaak moet zijn \u2014 je webwinkel, een pagina, een extern systeem." : "Nog geen snelkoppelingen ingesteld." }) : /* @__PURE__ */ jsx("ul", { className: "flex flex-wrap gap-2", children: links.map((l, i) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
@@ -373,7 +383,7 @@ function DashboardPanel({ siteId }) {
373
383
  children: [
374
384
  l.label,
375
385
  " ",
376
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "text-slate-400", children: "\u2197" })
386
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "text-slate-500", children: "\u2197" })
377
387
  ]
378
388
  }
379
389
  ) }, i)) })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/admin/dashboard/DashboardPanel.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * Startpagina van /beheer (7.16).\n *\n * /beheer opende recht in het pagina-overzicht: een lijst technische titels als eerste indruk, en\n * nergens een plek die uitlegt hoe het systeem in elkaar zit. Deze startpagina doet drie dingen:\n * uitleggen hoe je de site bewerkt (op de site zelf, niet hier), in één oogopslag tonen wat er\n * gebeurt (bezoek, bestellingen, inzendingen, laatst bewerkt), en snelkoppelingen bieden die de\n * siteadmin zelf kan instellen.\n *\n * Bewust eenmalige reads (geen live listeners): dit is een overzicht bij binnenkomst, geen\n * monitor. Elke widget vangt zijn eigen fouten — een dichtgezette collectie (rules, module uit)\n * mag nooit de hele startpagina blokkeren.\n */\nimport { useEffect, useMemo, useState } from \"react\";\nimport {\n collection,\n doc,\n getCountFromServer,\n getDoc,\n getDocs,\n limit,\n orderBy,\n query,\n where,\n} from \"firebase/firestore\";\nimport type { Timestamp } from \"firebase/firestore\";\nimport { getDb, getFirebaseApp } from \"../../firebase/client\";\nimport { callable } from \"../../firebase/functionsClient\";\nimport { useAuth } from \"../../auth/AuthProvider\";\nimport { canManageSite } from \"../../auth/roles\";\nimport { useModules } from \"../../modules/context\";\nimport { formatCents } from \"../../schema/shop\";\nimport { Badge, Button, Card, Input } from \"../ui/primitives\";\nimport { useToast } from \"../ui/toast\";\nimport { useAdminNav } from \"../navContext\";\nimport { BalkjesLijst, DagStaven } from \"../ui/charts\";\nimport { dagenInMaand, dagReeks, deltaTovVorige, huidigMaandId, maandLabel, topPaden, type MaandStat as StatsMaand } from \"../stats/lib\";\nimport { paginaToestand } from \"../lib/pagesClient\";\nimport { formulierenGezienSinds } from \"../forms/gezien\";\n\ninterface RecentePagina {\n id: string;\n title?: string;\n path?: string;\n /** Timestamp uit de editor, ISO-string uit import-scripts — zie updatedMs(). */\n updatedAt?: unknown;\n}\ntype MaandStat = Omit<StatsMaand, \"id\">;\ninterface RecenteOrder {\n id: string;\n status?: string;\n createdAt?: Timestamp;\n customer?: { name?: string };\n amount?: { totalCents?: number };\n}\ninterface FormOverzicht {\n id: string;\n name: string;\n aantal: number;\n laatste?: Timestamp;\n /** 7.24: inzendingen sinds je het formulier voor het laatst opende. */\n nieuw: number;\n}\ninterface Snelkoppeling {\n label: string;\n href: string;\n}\n\n/**\n * `updatedAt` is in de praktijk een Firestore-Timestamp (editor-saves) ÓF een ISO-string\n * (import-/migratiescripts schrijven via REST) — live aangetroffen op christelijkkraamcadeau,\n * dóór elkaar in dezelfde collectie. Firestore sorteert die in aparte typegroepen, dus een\n * server-side orderBy geeft dan een fout beeld; we normaliseren en sorteren daarom client-side.\n */\nfunction updatedMs(v: unknown): number {\n if (v && typeof (v as Timestamp).toDate === \"function\") return (v as Timestamp).toDate().getTime();\n if (typeof v === \"string\") {\n const t = Date.parse(v);\n return Number.isNaN(t) ? 0 : t;\n }\n return 0;\n}\n\nfunction fmtDatum(v: unknown): string {\n const ms = updatedMs(v);\n if (!ms) return \"—\";\n return new Date(ms).toLocaleString(\"nl-NL\", { dateStyle: \"short\", timeStyle: \"short\" });\n}\n\n/** Kop van een widget met een \"ga ernaartoe\"-pijl die naar het bijbehorende paneel springt. */\nfunction WidgetKop({ titel, tab, tabLabel }: { titel: string; tab?: string; tabLabel?: string }) {\n const gaNaar = useAdminNav();\n return (\n <div className=\"mb-3 flex items-center justify-between gap-2\">\n <h2 className=\"text-sm font-semibold text-slate-800\">{titel}</h2>\n {tab && (\n <button\n type=\"button\"\n onClick={() => gaNaar(tab)}\n className=\"shrink-0 text-xs font-medium text-brand-700 hover:underline focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:outline-none\"\n >\n {tabLabel ?? \"Bekijk alles\"} →\n </button>\n )}\n </div>\n );\n}\n\nexport function DashboardPanel({ siteId }: { siteId: string }) {\n const { role } = useAuth();\n const { activeIds } = useModules();\n const { toast } = useToast();\n const gaNaar = useAdminNav();\n const heeftShop = activeIds.has(\"shop\");\n const magInstellen = canManageSite(role);\n\n const [paginas, setPaginas] = useState<RecentePagina[] | null>(null);\n /** 7.24: pagina's met wijzigingen die nog niet live staan — de \"wacht op jou\"-teller. */\n const [nietLive, setNietLive] = useState<RecentePagina[]>([]);\n const [teVerzenden, setTeVerzenden] = useState<number>(0);\n const [maand, setMaand] = useState<MaandStat | null | undefined>(undefined);\n const [vorigeMaand, setVorigeMaand] = useState<MaandStat | null>(null);\n const [orders, setOrders] = useState<RecenteOrder[] | null>(null);\n const [formulieren, setFormulieren] = useState<FormOverzicht[] | null>(null);\n const [links, setLinks] = useState<Snelkoppeling[]>([]);\n const [linksBewerken, setLinksBewerken] = useState<Snelkoppeling[] | null>(null);\n const [linksOpslaan, setLinksOpslaan] = useState(false);\n\n useEffect(() => {\n const db = getDb();\n if (!db) {\n setPaginas([]);\n setMaand(null);\n setOrders([]);\n setFormulieren([]);\n return;\n }\n\n // Laatst bewerkte pagina's. Bewust GEEN server-side orderBy op updatedAt: dat veld is een\n // mengsel van Timestamps en ISO-strings (zie updatedMs) en Firestore sorteert per typegroep.\n // Drafts-collecties zijn klein; alles ophalen en zelf sorteren is hier de betrouwbare weg.\n getDocs(collection(db, \"sites\", siteId, \"drafts\"))\n .then((snap) => {\n const alle = snap.docs\n .map((d) => ({ id: d.id, ...(d.data() as Omit<RecentePagina, \"id\"> & { published?: boolean; lastPublishedAt?: unknown; trashed?: boolean; blocks?: unknown[] }) }))\n .filter((p) => !p.trashed)\n .sort((a, b) => updatedMs(b.updatedAt) - updatedMs(a.updatedAt));\n setPaginas(alle.slice(0, 5));\n // Zelfde regel als de badge in Pagina's: gepubliceerd, en daarna opnieuw opgeslagen.\n setNietLive(alle.filter((p) => paginaToestand({ ...p, blocks: [] } as never) === \"gewijzigd\"));\n })\n .catch(() => setPaginas([]));\n\n // Bezoek deze maand + vorige maand (voor de eerlijke delta) — zelfde bron als Statistieken.\n const maandId = huidigMaandId();\n const [j, m] = maandId.split(\"-\").map(Number);\n const vd = new Date(Date.UTC(j, (m ?? 1) - 2, 1));\n const vorigeId = `${vd.getUTCFullYear()}-${String(vd.getUTCMonth() + 1).padStart(2, \"0\")}`;\n getDoc(doc(db, \"sites\", siteId, \"stats\", maandId))\n .then((snap) => setMaand(snap.exists() ? (snap.data() as MaandStat) : null))\n .catch(() => setMaand(null));\n getDoc(doc(db, \"sites\", siteId, \"stats\", vorigeId))\n .then((snap) => setVorigeMaand(snap.exists() ? (snap.data() as MaandStat) : null))\n .catch(() => setVorigeMaand(null));\n\n // Laatste bestellingen — alleen als de shop-module actief is.\n if (heeftShop) {\n getDocs(query(collection(db, \"sites\", siteId, \"orders\"), orderBy(\"createdAt\", \"desc\"), limit(5)))\n .then((snap) => setOrders(snap.docs.map((d) => ({ id: d.id, ...(d.data() as Omit<RecenteOrder, \"id\">) }))))\n .catch(() => setOrders([]));\n // Betaald maar nog niet verzonden: dat is werk dat op de winkelier wacht.\n getCountFromServer(query(collection(db, \"sites\", siteId, \"orders\"), where(\"status\", \"==\", \"paid\")))\n .then((c) => setTeVerzenden(c.data().count))\n .catch(() => setTeVerzenden(0));\n } else {\n setOrders([]);\n }\n\n // Formulieren: naam + aantal inzendingen + laatste inzending (aggregatie-count, geen\n // volledige submissions-download). Max vier formulieren — de widget is een signaal, geen lijst.\n getDocs(collection(db, \"sites\", siteId, \"forms\"))\n .then(async (snap) => {\n const forms = snap.docs.slice(0, 4);\n const uit = await Promise.all(\n forms.map(async (f) => {\n const subs = collection(db, \"sites\", siteId, \"forms\", f.id, \"submissions\");\n const sinds = formulierenGezienSinds(siteId, f.id);\n const [count, laatste, nieuw] = await Promise.all([\n getCountFromServer(subs).then((c) => c.data().count).catch(() => 0),\n getDocs(query(subs, orderBy(\"createdAt\", \"desc\"), limit(1)))\n .then((s) => (s.docs[0]?.data() as { createdAt?: Timestamp } | undefined)?.createdAt)\n .catch(() => undefined),\n // Nieuw sinds je laatste bezoek aan dit formulier (moment staat lokaal, zie forms/gezien.ts).\n sinds\n ? getCountFromServer(query(subs, where(\"createdAt\", \">\", sinds))).then((c) => c.data().count).catch(() => 0)\n : Promise.resolve(0),\n ]);\n return { id: f.id, name: (f.data() as { name?: string }).name ?? f.id, aantal: count, laatste, nieuw };\n }),\n );\n setFormulieren(uit);\n })\n .catch(() => setFormulieren([]));\n\n // Snelkoppelingen uit settings/site (editor mag lezen; instellen is siteadmin-werk).\n getDoc(doc(db, \"sites\", siteId, \"settings\", \"site\"))\n .then((snap) => {\n const d = snap.exists() ? (snap.data() as { dashboardLinks?: Snelkoppeling[] }) : {};\n setLinks(Array.isArray(d.dashboardLinks) ? d.dashboardLinks : []);\n })\n .catch(() => setLinks([]));\n }, [siteId, heeftShop]);\n\n const vandaagDag = new Date().getUTCDate();\n const dagen = useMemo(() => dagReeks(maand ? { id: \"\", ...maand } : undefined, dagenInMaand(huidigMaandId())), [maand]);\n const delta = useMemo(\n () =>\n maand && vorigeMaand\n ? deltaTovVorige({ id: \"\", ...maand }, { id: \"\", ...vorigeMaand }, vandaagDag)\n : null,\n [maand, vorigeMaand, vandaagDag],\n );\n const paden = useMemo(() => topPaden(maand ? { id: \"\", ...maand } : undefined, 3), [maand]);\n\n async function slaLinksOp() {\n if (!linksBewerken) return;\n const schoon = linksBewerken.filter((l) => l.label.trim() && l.href.trim());\n const app = getFirebaseApp();\n if (!app) return;\n setLinksOpslaan(true);\n try {\n // Zelfde route als de rest van de instellingen: de saveSiteSettings-callable (merge).\n const fn = callable(app, \"saveSiteSettings\");\n await fn({ siteId, settings: { dashboardLinks: schoon } });\n setLinks(schoon);\n setLinksBewerken(null);\n toast({ title: \"Snelkoppelingen opgeslagen\", tone: \"success\" });\n } catch (e) {\n toast({ title: \"Opslaan mislukt\", description: (e as Error).message, tone: \"error\" });\n } finally {\n setLinksOpslaan(false);\n }\n }\n\n return (\n <div className=\"grid gap-4\">\n {/* ── Zo werkt het ── */}\n <Card>\n <h2 className=\"text-sm font-semibold text-slate-800\">Zo werkt je site</h2>\n <div className=\"mt-2 grid gap-2 text-sm text-slate-600 md:grid-cols-3\">\n <p>\n <strong className=\"text-slate-800\">1 · Bewerk op de site zelf.</strong> Klik rechtsboven op{\" \"}\n <em>Bekijk site</em> en kies <em>Bewerken</em> in de zwarte balk. Elk blok krijgt een{\" \"}\n potlood (tekst) en een tandwiel (instellingen) — je typt gewoon in de pagina.\n </p>\n <p>\n <strong className=\"text-slate-800\">2 · Publiceer als het goed is.</strong> Alles wat je\n wijzigt is eerst een <em>werkversie</em>: bezoekers zien er niets van. Pas de knop{\" \"}\n <em>Publiceren</em> (rechtsonder) zet het live — en <em>Verwerpen</em> gooit het weg.\n </p>\n <p>\n <strong className=\"text-slate-800\">3 · De rest staat hier.</strong> Nieuwe pagina&apos;s, het\n menu, foto&apos;s, formulieren{heeftShop ? \", de webwinkel\" : \"\"} en statistieken vind je in\n de secties hiernaast. Onderaan de zijbalk staat ook een korte helptekst.\n </p>\n </div>\n </Card>\n\n {/* ── Wacht op jou (7.24) ──\n Eén tegel met wat er van de beheerder verwacht wordt: wijzigingen die nog niet live staan,\n nieuwe inzendingen, bestellingen die de deur uit moeten. Dát is waarvoor iemand inlogt;\n de overzichten hieronder zijn context. Verschijnt alleen als er iets ligt. */}\n {(() => {\n const nieuweInzendingen = (formulieren ?? []).reduce((som, f) => som + f.nieuw, 0);\n const items: { tekst: string; tab: string; tabLabel: string }[] = [];\n if (nietLive.length > 0) {\n items.push({\n tekst: nietLive.length === 1\n ? `1 pagina heeft wijzigingen die nog niet live staan (${nietLive[0].title ?? nietLive[0].path})`\n : `${nietLive.length} pagina's hebben wijzigingen die nog niet live staan`,\n tab: \"pages\",\n tabLabel: \"Naar Pagina's\",\n });\n }\n if (nieuweInzendingen > 0) {\n items.push({ tekst: nieuweInzendingen === 1 ? \"1 nieuwe inzending\" : `${nieuweInzendingen} nieuwe inzendingen`, tab: \"forms\", tabLabel: \"Naar Formulieren\" });\n }\n if (heeftShop && teVerzenden > 0) {\n items.push({ tekst: teVerzenden === 1 ? \"1 betaalde bestelling wacht op verzending\" : `${teVerzenden} betaalde bestellingen wachten op verzending`, tab: \"shop\", tabLabel: \"Naar Webwinkel\" });\n }\n if (items.length === 0) return null;\n return (\n <Card>\n <h2 className=\"mb-2 text-sm font-semibold text-slate-800\">Wacht op jou</h2>\n <ul className=\"grid gap-1.5\">\n {items.map((it) => (\n <li key={it.tab} className=\"flex flex-wrap items-center justify-between gap-2 rounded-lg bg-amber-50 px-3 py-2 text-sm text-amber-900\">\n <span>{it.tekst}</span>\n <button type=\"button\" onClick={() => gaNaar(it.tab)} className=\"text-xs font-medium text-brand-700 hover:underline\">\n {it.tabLabel} →\n </button>\n </li>\n ))}\n </ul>\n </Card>\n );\n })()}\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n {/* ── Statistieken ── */}\n <Card>\n <WidgetKop titel=\"Bezoek deze maand\" tab=\"stats\" tabLabel=\"Statistieken\" />\n {maand === undefined ? (\n <p className=\"text-sm text-slate-500\">Laden…</p>\n ) : maand === null ? (\n <p className=\"text-sm text-slate-500\">Nog geen bezoekersdata deze maand.</p>\n ) : (\n <div className=\"grid gap-4\">\n {/* Hero-cijfer met context: het totaal leidt, vandaag en de delta lopen mee. */}\n <div className=\"flex flex-wrap items-baseline gap-x-5 gap-y-1\">\n <p>\n <span className=\"text-3xl font-semibold text-slate-900\">{(maand.total ?? 0).toLocaleString(\"nl-NL\")}</span>{\" \"}\n <span className=\"text-xs text-slate-500\">weergaven in {maandLabel(huidigMaandId())}</span>\n </p>\n <p>\n <span className=\"text-lg font-semibold text-slate-700\">{(dagen[vandaagDag - 1] ?? 0).toLocaleString(\"nl-NL\")}</span>{\" \"}\n <span className=\"text-xs text-slate-500\">vandaag</span>\n </p>\n {delta != null && Number.isFinite(delta) && (\n <p className={`text-xs font-medium ${delta >= 0 ? \"text-emerald-700\" : \"text-red-700\"}`}>\n <span aria-hidden=\"true\">{delta >= 0 ? \"▲\" : \"▼\"}</span> {Math.abs(Math.round(delta))}%\n <span className=\"font-normal text-slate-500\"> t.o.v. vorige maand</span>\n </p>\n )}\n </div>\n <DagStaven\n waarden={dagen}\n accentIndex={vandaagDag - 1}\n hoogte={110}\n titel={`Weergaven per dag in ${maandLabel(huidigMaandId(), true)}`}\n />\n {paden.length > 0 && <BalkjesLijst items={paden} />}\n </div>\n )}\n </Card>\n\n {/* ── Laatst bewerkte pagina's ── */}\n <Card>\n <WidgetKop titel=\"Laatst bewerkt\" tab=\"pages\" tabLabel=\"Pagina's\" />\n {paginas === null ? (\n <p className=\"text-sm text-slate-500\">Laden…</p>\n ) : paginas.length === 0 ? (\n <p className=\"text-sm text-slate-500\">Nog geen bewerkte pagina&apos;s.</p>\n ) : (\n <ul className=\"grid gap-1\">\n {paginas.map((p) => (\n <li key={p.id}>\n <button\n type=\"button\"\n onClick={() => gaNaar(\"pages\")}\n className=\"flex w-full items-center justify-between gap-2 rounded-lg px-2 py-1.5 text-left text-sm hover:bg-slate-100\"\n title={`${p.title ?? p.id} — ${p.path ?? \"\"}`}\n >\n <span className=\"min-w-0\">\n <span className=\"block truncate text-slate-800\">{p.title ?? p.id}</span>\n <span className=\"block truncate text-xs text-slate-400\">{p.path}</span>\n </span>\n <span className=\"shrink-0 text-xs text-slate-400\">{fmtDatum(p.updatedAt)}</span>\n </button>\n </li>\n ))}\n </ul>\n )}\n </Card>\n\n {/* ── Bestellingen (alleen met shop) ── */}\n {heeftShop && (\n <Card>\n <WidgetKop titel=\"Laatste bestellingen\" tab=\"shop\" tabLabel=\"Webwinkel\" />\n {orders === null ? (\n <p className=\"text-sm text-slate-500\">Laden…</p>\n ) : orders.length === 0 ? (\n <p className=\"text-sm text-slate-500\">Nog geen bestellingen.</p>\n ) : (\n <ul className=\"grid gap-1\">\n {orders.map((o) => (\n <li key={o.id}>\n <button\n type=\"button\"\n onClick={() => gaNaar(\"shop\")}\n className=\"flex w-full items-center justify-between gap-2 rounded-lg px-2 py-1.5 text-left text-sm hover:bg-slate-100\"\n >\n <span className=\"min-w-0\">\n <span className=\"block truncate text-slate-800\">{o.customer?.name ?? \"Onbekend\"}</span>\n <span className=\"block text-xs text-slate-400\">{fmtDatum(o.createdAt)}</span>\n </span>\n <span className=\"flex shrink-0 items-center gap-2\">\n {typeof o.amount?.totalCents === \"number\" && (\n <span className=\"text-sm font-medium text-slate-700\">{formatCents(o.amount.totalCents)}</span>\n )}\n <Badge tone={o.status === \"paid\" || o.status === \"shipped\" ? \"green\" : \"amber\"}>\n {o.status ?? \"?\"}\n </Badge>\n </span>\n </button>\n </li>\n ))}\n </ul>\n )}\n </Card>\n )}\n\n {/* ── Formulieren ── */}\n <Card>\n <WidgetKop titel=\"Formulieren\" tab=\"forms\" tabLabel=\"Formulieren\" />\n {formulieren === null ? (\n <p className=\"text-sm text-slate-500\">Laden…</p>\n ) : formulieren.length === 0 ? (\n <p className=\"text-sm text-slate-500\">Nog geen formulieren.</p>\n ) : (\n <ul className=\"grid gap-1\">\n {formulieren.map((f) => (\n <li key={f.id}>\n <button\n type=\"button\"\n onClick={() => gaNaar(\"forms\")}\n className=\"flex w-full items-center justify-between gap-2 rounded-lg px-2 py-1.5 text-left text-sm hover:bg-slate-100\"\n >\n <span className=\"truncate text-slate-800\" title={f.name}>{f.name}</span>\n <span className=\"shrink-0 text-xs text-slate-500\">\n {f.aantal} {f.aantal === 1 ? \"inzending\" : \"inzendingen\"}\n {f.laatste ? ` · laatste ${fmtDatum(f.laatste)}` : \"\"}\n </span>\n </button>\n </li>\n ))}\n </ul>\n )}\n </Card>\n\n {/* ── Snelkoppelingen ── */}\n <Card>\n <div className=\"mb-3 flex items-center justify-between gap-2\">\n <h2 className=\"text-sm font-semibold text-slate-800\">Snelkoppelingen</h2>\n {magInstellen && linksBewerken === null && (\n <Button variant=\"secondary\" size=\"sm\" onClick={() => setLinksBewerken(links.length ? [...links] : [{ label: \"\", href: \"\" }])}>\n Instellen\n </Button>\n )}\n </div>\n {linksBewerken ? (\n <div className=\"grid gap-2\">\n <div className=\"grid grid-cols-[1fr_1.4fr_auto] gap-2 text-xs font-medium text-slate-500\">\n <span>Naam</span>\n <span>Adres</span>\n <span aria-hidden=\"true\" />\n </div>\n {linksBewerken.map((l, i) => (\n <div key={i} className=\"grid grid-cols-[1fr_1.4fr_auto] gap-2\">\n <Input value={l.label} placeholder=\"bv. Webwinkel\" aria-label=\"Naam van de snelkoppeling\"\n onChange={(e) => setLinksBewerken(linksBewerken.map((x, j) => (j === i ? { ...x, label: e.target.value } : x)))} />\n <Input value={l.href} placeholder=\"/webwinkel of https://…\" aria-label=\"Adres van de snelkoppeling\"\n onChange={(e) => setLinksBewerken(linksBewerken.map((x, j) => (j === i ? { ...x, href: e.target.value } : x)))} />\n <Button variant=\"ghost\" size=\"sm\" aria-label=\"Verwijderen\"\n onClick={() => setLinksBewerken(linksBewerken.filter((_, j) => j !== i))}>✕</Button>\n </div>\n ))}\n <div className=\"flex items-center justify-between gap-2\">\n <Button variant=\"secondary\" size=\"sm\" disabled={linksBewerken.length >= 12}\n onClick={() => setLinksBewerken([...linksBewerken, { label: \"\", href: \"\" }])}>\n + Snelkoppeling\n </Button>\n <div className=\"flex gap-2\">\n <Button variant=\"secondary\" size=\"sm\" onClick={() => setLinksBewerken(null)}>Annuleren</Button>\n <Button size=\"sm\" disabled={linksOpslaan} onClick={() => void slaLinksOp()}>\n {linksOpslaan ? \"Bezig…\" : \"Opslaan\"}\n </Button>\n </div>\n </div>\n </div>\n ) : links.length === 0 ? (\n <p className=\"text-sm text-slate-500\">\n {magInstellen\n ? \"Nog geen snelkoppelingen. Zet hier de plekken waar je vaak moet zijn — je webwinkel, een pagina, een extern systeem.\"\n : \"Nog geen snelkoppelingen ingesteld.\"}\n </p>\n ) : (\n <ul className=\"flex flex-wrap gap-2\">\n {links.map((l, i) => (\n <li key={i}>\n <a\n href={l.href}\n target={/^https?:\\/\\//i.test(l.href) ? \"_blank\" : undefined}\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-slate-200 px-3 py-1.5 text-sm text-slate-700 hover:bg-slate-100\"\n >\n {l.label} <span aria-hidden=\"true\" className=\"text-slate-400\">↗</span>\n </a>\n </li>\n ))}\n </ul>\n )}\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/admin/dashboard/DashboardPanel.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * Startpagina van /beheer (7.16).\n *\n * /beheer opende recht in het pagina-overzicht: een lijst technische titels als eerste indruk, en\n * nergens een plek die uitlegt hoe het systeem in elkaar zit. Deze startpagina doet drie dingen:\n * uitleggen hoe je de site bewerkt (op de site zelf, niet hier), in één oogopslag tonen wat er\n * gebeurt (bezoek, bestellingen, inzendingen, laatst bewerkt), en snelkoppelingen bieden die de\n * siteadmin zelf kan instellen.\n *\n * Bewust eenmalige reads (geen live listeners): dit is een overzicht bij binnenkomst, geen\n * monitor. Elke widget vangt zijn eigen fouten — een dichtgezette collectie (rules, module uit)\n * mag nooit de hele startpagina blokkeren.\n */\nimport { useEffect, useMemo, useState } from \"react\";\nimport {\n collection,\n doc,\n getCountFromServer,\n getDoc,\n getDocs,\n limit,\n orderBy,\n query,\n where,\n} from \"firebase/firestore\";\nimport type { Timestamp } from \"firebase/firestore\";\nimport { getDb, getFirebaseApp } from \"../../firebase/client\";\nimport { callable } from \"../../firebase/functionsClient\";\nimport { useAuth } from \"../../auth/AuthProvider\";\nimport { canManageSite } from \"../../auth/roles\";\nimport { useModules } from \"../../modules/context\";\nimport { formatCents } from \"../../schema/shop\";\nimport { Badge, Button, Card, Input } from \"../ui/primitives\";\nimport { useToast } from \"../ui/toast\";\nimport { useAdminNav, vraagOpen } from \"../navContext\";\nimport { ORDER_STATUS_LABELS } from \"../shop/lib\";\nimport { BalkjesLijst, DagStaven } from \"../ui/charts\";\nimport { dagenInMaand, dagReeks, deltaTovVorige, huidigMaandId, maandLabel, topPaden, type MaandStat as StatsMaand } from \"../stats/lib\";\nimport { paginaToestand } from \"../lib/pagesClient\";\nimport { formulierenGezienSinds } from \"../forms/gezien\";\n\ninterface RecentePagina {\n id: string;\n title?: string;\n path?: string;\n /** Timestamp uit de editor, ISO-string uit import-scripts — zie updatedMs(). */\n updatedAt?: unknown;\n}\ntype MaandStat = Omit<StatsMaand, \"id\">;\ninterface RecenteOrder {\n id: string;\n status?: string;\n createdAt?: Timestamp;\n customer?: { name?: string };\n amount?: { totalCents?: number };\n}\ninterface FormOverzicht {\n id: string;\n name: string;\n aantal: number;\n laatste?: Timestamp;\n /** 7.24: inzendingen sinds je het formulier voor het laatst opende. */\n nieuw: number;\n}\ninterface Snelkoppeling {\n label: string;\n href: string;\n}\n\n/**\n * `updatedAt` is in de praktijk een Firestore-Timestamp (editor-saves) ÓF een ISO-string\n * (import-/migratiescripts schrijven via REST) — live aangetroffen op christelijkkraamcadeau,\n * dóór elkaar in dezelfde collectie. Firestore sorteert die in aparte typegroepen, dus een\n * server-side orderBy geeft dan een fout beeld; we normaliseren en sorteren daarom client-side.\n */\nfunction updatedMs(v: unknown): number {\n if (v && typeof (v as Timestamp).toDate === \"function\") return (v as Timestamp).toDate().getTime();\n if (typeof v === \"string\") {\n const t = Date.parse(v);\n return Number.isNaN(t) ? 0 : t;\n }\n return 0;\n}\n\nfunction fmtDatum(v: unknown): string {\n const ms = updatedMs(v);\n if (!ms) return \"—\";\n return new Date(ms).toLocaleString(\"nl-NL\", { dateStyle: \"short\", timeStyle: \"short\" });\n}\n\n/** Kop van een widget met een \"ga ernaartoe\"-pijl die naar het bijbehorende paneel springt. */\nfunction WidgetKop({ titel, tab, tabLabel }: { titel: string; tab?: string; tabLabel?: string }) {\n const gaNaar = useAdminNav();\n return (\n <div className=\"mb-3 flex items-center justify-between gap-2\">\n <h2 className=\"text-sm font-semibold text-slate-800\">{titel}</h2>\n {tab && (\n <button\n type=\"button\"\n onClick={() => gaNaar(tab)}\n className=\"shrink-0 text-xs font-medium text-brand-700 hover:underline focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:outline-none\"\n >\n {tabLabel ?? \"Bekijk alles\"} →\n </button>\n )}\n </div>\n );\n}\n\nexport function DashboardPanel({ siteId }: { siteId: string }) {\n const { role } = useAuth();\n const { activeIds } = useModules();\n const { toast } = useToast();\n const gaNaar = useAdminNav();\n const heeftShop = activeIds.has(\"shop\");\n const magInstellen = canManageSite(role);\n\n const [paginas, setPaginas] = useState<RecentePagina[] | null>(null);\n /** 7.24: pagina's met wijzigingen die nog niet live staan — de \"wacht op jou\"-teller. */\n const [nietLive, setNietLive] = useState<RecentePagina[]>([]);\n const [teVerzenden, setTeVerzenden] = useState<number>(0);\n const [maand, setMaand] = useState<MaandStat | null | undefined>(undefined);\n const [vorigeMaand, setVorigeMaand] = useState<MaandStat | null>(null);\n const [orders, setOrders] = useState<RecenteOrder[] | null>(null);\n const [formulieren, setFormulieren] = useState<FormOverzicht[] | null>(null);\n const [links, setLinks] = useState<Snelkoppeling[]>([]);\n const [linksBewerken, setLinksBewerken] = useState<Snelkoppeling[] | null>(null);\n const [linksOpslaan, setLinksOpslaan] = useState(false);\n\n useEffect(() => {\n const db = getDb();\n if (!db) {\n setPaginas([]);\n setMaand(null);\n setOrders([]);\n setFormulieren([]);\n return;\n }\n\n // Laatst bewerkte pagina's. Bewust GEEN server-side orderBy op updatedAt: dat veld is een\n // mengsel van Timestamps en ISO-strings (zie updatedMs) en Firestore sorteert per typegroep.\n // Drafts-collecties zijn klein; alles ophalen en zelf sorteren is hier de betrouwbare weg.\n getDocs(collection(db, \"sites\", siteId, \"drafts\"))\n .then((snap) => {\n const alle = snap.docs\n .map((d) => ({ id: d.id, ...(d.data() as Omit<RecentePagina, \"id\"> & { published?: boolean; lastPublishedAt?: unknown; trashed?: boolean; blocks?: unknown[] }) }))\n .filter((p) => !p.trashed)\n .sort((a, b) => updatedMs(b.updatedAt) - updatedMs(a.updatedAt));\n setPaginas(alle.slice(0, 5));\n // Zelfde regel als de badge in Pagina's: gepubliceerd, en daarna opnieuw opgeslagen.\n setNietLive(alle.filter((p) => paginaToestand({ ...p, blocks: [] } as never) === \"gewijzigd\"));\n })\n .catch(() => setPaginas([]));\n\n // Bezoek deze maand + vorige maand (voor de eerlijke delta) — zelfde bron als Statistieken.\n const maandId = huidigMaandId();\n const [j, m] = maandId.split(\"-\").map(Number);\n const vd = new Date(Date.UTC(j, (m ?? 1) - 2, 1));\n const vorigeId = `${vd.getUTCFullYear()}-${String(vd.getUTCMonth() + 1).padStart(2, \"0\")}`;\n getDoc(doc(db, \"sites\", siteId, \"stats\", maandId))\n .then((snap) => setMaand(snap.exists() ? (snap.data() as MaandStat) : null))\n .catch(() => setMaand(null));\n getDoc(doc(db, \"sites\", siteId, \"stats\", vorigeId))\n .then((snap) => setVorigeMaand(snap.exists() ? (snap.data() as MaandStat) : null))\n .catch(() => setVorigeMaand(null));\n\n // Laatste bestellingen — alleen als de shop-module actief is.\n if (heeftShop) {\n getDocs(query(collection(db, \"sites\", siteId, \"orders\"), orderBy(\"createdAt\", \"desc\"), limit(5)))\n .then((snap) => setOrders(snap.docs.map((d) => ({ id: d.id, ...(d.data() as Omit<RecenteOrder, \"id\">) }))))\n .catch(() => setOrders([]));\n // Betaald maar nog niet verzonden: dat is werk dat op de winkelier wacht.\n getCountFromServer(query(collection(db, \"sites\", siteId, \"orders\"), where(\"status\", \"==\", \"paid\")))\n .then((c) => setTeVerzenden(c.data().count))\n .catch(() => setTeVerzenden(0));\n } else {\n setOrders([]);\n }\n\n // Formulieren: naam + aantal inzendingen + laatste inzending (aggregatie-count, geen\n // volledige submissions-download). Alle formulieren tellen mee voor \"Wacht op jou\" (8.3: eerst\n // alleen de eerste vier); de widget toont er vier, die met nieuwe inzendingen eerst.\n getDocs(collection(db, \"sites\", siteId, \"forms\"))\n .then(async (snap) => {\n const forms = snap.docs.slice(0, 40);\n const uit = await Promise.all(\n forms.map(async (f) => {\n const subs = collection(db, \"sites\", siteId, \"forms\", f.id, \"submissions\");\n const sinds = formulierenGezienSinds(siteId, f.id);\n const [count, laatste, nieuw] = await Promise.all([\n getCountFromServer(subs).then((c) => c.data().count).catch(() => 0),\n getDocs(query(subs, orderBy(\"createdAt\", \"desc\"), limit(1)))\n .then((s) => (s.docs[0]?.data() as { createdAt?: Timestamp } | undefined)?.createdAt)\n .catch(() => undefined),\n // Nieuw sinds je laatste bezoek aan dit formulier (moment staat lokaal, zie forms/gezien.ts).\n sinds\n ? getCountFromServer(query(subs, where(\"createdAt\", \">\", sinds))).then((c) => c.data().count).catch(() => 0)\n : Promise.resolve(0),\n ]);\n return { id: f.id, name: (f.data() as { name?: string }).name ?? f.id, aantal: count, laatste, nieuw };\n }),\n );\n setFormulieren(uit);\n })\n .catch(() => setFormulieren([]));\n\n // Snelkoppelingen uit settings/site (editor mag lezen; instellen is siteadmin-werk).\n getDoc(doc(db, \"sites\", siteId, \"settings\", \"site\"))\n .then((snap) => {\n const d = snap.exists() ? (snap.data() as { dashboardLinks?: Snelkoppeling[] }) : {};\n setLinks(Array.isArray(d.dashboardLinks) ? d.dashboardLinks : []);\n })\n .catch(() => setLinks([]));\n }, [siteId, heeftShop]);\n\n const vandaagDag = new Date().getUTCDate();\n const dagen = useMemo(() => dagReeks(maand ? { id: \"\", ...maand } : undefined, dagenInMaand(huidigMaandId())), [maand]);\n const delta = useMemo(\n () =>\n maand && vorigeMaand\n ? deltaTovVorige({ id: \"\", ...maand }, { id: \"\", ...vorigeMaand }, vandaagDag)\n : null,\n [maand, vorigeMaand, vandaagDag],\n );\n const paden = useMemo(() => topPaden(maand ? { id: \"\", ...maand } : undefined, 3), [maand]);\n\n async function slaLinksOp() {\n if (!linksBewerken) return;\n const schoon = linksBewerken.filter((l) => l.label.trim() && l.href.trim());\n const app = getFirebaseApp();\n if (!app) return;\n setLinksOpslaan(true);\n try {\n // Zelfde route als de rest van de instellingen: de saveSiteSettings-callable (merge).\n const fn = callable(app, \"saveSiteSettings\");\n await fn({ siteId, settings: { dashboardLinks: schoon } });\n setLinks(schoon);\n setLinksBewerken(null);\n toast({ title: \"Snelkoppelingen opgeslagen\", tone: \"success\" });\n } catch (e) {\n toast({ title: \"Opslaan mislukt\", description: (e as Error).message, tone: \"error\" });\n } finally {\n setLinksOpslaan(false);\n }\n }\n\n return (\n <div className=\"grid gap-4\">\n {/* ── Zo werkt het ── */}\n <Card>\n <h2 className=\"text-sm font-semibold text-slate-800\">Zo werkt je site</h2>\n <div className=\"mt-2 grid gap-2 text-sm text-slate-600 md:grid-cols-3\">\n <p>\n <strong className=\"text-slate-800\">1 · Bewerk op de site zelf.</strong> Klik rechtsboven op{\" \"}\n <em>Bekijk site</em> en kies <em>Bewerken</em> in de balk onderaan. Het potlood bij een blok\n bewerkt het op de pagina zelf, het tandwiel opent alle instellingen, en met <em>+</em> voeg je iets toe.\n </p>\n <p>\n <strong className=\"text-slate-800\">2 · Publiceer als het goed is.</strong> Alles wat je\n wijzigt is eerst een <em>werkversie</em>: bezoekers zien er niets van. Pas de knop{\" \"}\n <em>Publiceren</em> (rechtsonder) zet het live — en <em>Verwerpen</em> gooit het weg.\n </p>\n <p>\n <strong className=\"text-slate-800\">3 · De rest staat hier.</strong> Nieuwe pagina&apos;s, het\n menu, foto&apos;s, formulieren{heeftShop ? \", de webwinkel\" : \"\"} en statistieken vind je in\n de secties hiernaast. Iets kwijt? Druk Ctrl+K (of Cmd+K) en typ wat je zoekt.\n </p>\n </div>\n </Card>\n\n {/* ── Wacht op jou (7.24) ──\n Eén tegel met wat er van de beheerder verwacht wordt: wijzigingen die nog niet live staan,\n nieuwe inzendingen, bestellingen die de deur uit moeten. Dát is waarvoor iemand inlogt;\n de overzichten hieronder zijn context. Verschijnt alleen als er iets ligt. */}\n {(() => {\n const nieuweInzendingen = (formulieren ?? []).reduce((som, f) => som + f.nieuw, 0);\n const items: { tekst: string; tab: string; tabLabel: string }[] = [];\n if (nietLive.length > 0) {\n items.push({\n tekst: nietLive.length === 1\n ? `1 pagina heeft wijzigingen die nog niet live staan (${nietLive[0].title ?? nietLive[0].path})`\n : `${nietLive.length} pagina's hebben wijzigingen die nog niet live staan`,\n tab: \"pages\",\n tabLabel: \"Naar Pagina's\",\n });\n }\n if (nieuweInzendingen > 0) {\n items.push({ tekst: nieuweInzendingen === 1 ? \"1 nieuwe inzending\" : `${nieuweInzendingen} nieuwe inzendingen`, tab: \"forms\", tabLabel: \"Naar Formulieren\" });\n }\n if (heeftShop && teVerzenden > 0) {\n items.push({ tekst: teVerzenden === 1 ? \"1 betaalde bestelling wacht op verzending\" : `${teVerzenden} betaalde bestellingen wachten op verzending`, tab: \"shop\", tabLabel: \"Naar Webwinkel\" });\n }\n if (items.length === 0) return null;\n return (\n <Card>\n <h2 className=\"mb-2 text-sm font-semibold text-slate-800\">Wacht op jou</h2>\n <ul className=\"grid gap-1.5\">\n {items.map((it) => (\n <li key={it.tab} className=\"flex flex-wrap items-center justify-between gap-2 rounded-lg bg-amber-50 px-3 py-2 text-sm text-amber-900\">\n <span>{it.tekst}</span>\n <button type=\"button\" onClick={() => gaNaar(it.tab)} className=\"text-xs font-medium text-brand-700 hover:underline\">\n {it.tabLabel} →\n </button>\n </li>\n ))}\n </ul>\n </Card>\n );\n })()}\n\n <div className=\"grid gap-4 md:grid-cols-2\">\n {/* ── Statistieken ── */}\n <Card>\n <WidgetKop titel=\"Bezoek deze maand\" tab=\"stats\" tabLabel=\"Statistieken\" />\n {maand === undefined ? (\n <p className=\"text-sm text-slate-500\">Laden…</p>\n ) : maand === null ? (\n <p className=\"text-sm text-slate-500\">Nog geen bezoekersdata deze maand.</p>\n ) : (\n <div className=\"grid gap-4\">\n {/* Hero-cijfer met context: het totaal leidt, vandaag en de delta lopen mee. */}\n <div className=\"flex flex-wrap items-baseline gap-x-5 gap-y-1\">\n <p>\n <span className=\"text-3xl font-semibold text-slate-900\">{(maand.total ?? 0).toLocaleString(\"nl-NL\")}</span>{\" \"}\n <span className=\"text-xs text-slate-500\">weergaven in {maandLabel(huidigMaandId())}</span>\n </p>\n <p>\n <span className=\"text-lg font-semibold text-slate-700\">{(dagen[vandaagDag - 1] ?? 0).toLocaleString(\"nl-NL\")}</span>{\" \"}\n <span className=\"text-xs text-slate-500\">vandaag</span>\n </p>\n {delta != null && Number.isFinite(delta) && (\n <p className={`text-xs font-medium ${delta >= 0 ? \"text-emerald-700\" : \"text-red-700\"}`}>\n <span aria-hidden=\"true\">{delta >= 0 ? \"▲\" : \"▼\"}</span> {Math.abs(Math.round(delta))}%\n <span className=\"font-normal text-slate-500\"> t.o.v. vorige maand</span>\n </p>\n )}\n </div>\n <DagStaven\n waarden={dagen}\n accentIndex={vandaagDag - 1}\n hoogte={110}\n titel={`Weergaven per dag in ${maandLabel(huidigMaandId(), true)}`}\n />\n {paden.length > 0 && <BalkjesLijst items={paden} />}\n </div>\n )}\n </Card>\n\n {/* ── Laatst bewerkte pagina's ── */}\n <Card>\n <WidgetKop titel=\"Laatst bewerkt\" tab=\"pages\" tabLabel=\"Pagina's\" />\n {paginas === null ? (\n <p className=\"text-sm text-slate-500\">Laden…</p>\n ) : paginas.length === 0 ? (\n <p className=\"text-sm text-slate-500\">Nog geen bewerkte pagina&apos;s.</p>\n ) : (\n <ul className=\"grid gap-1\">\n {paginas.map((p) => (\n <li key={p.id}>\n <button\n type=\"button\"\n onClick={() => {\n vraagOpen(\"pages\", p.id);\n gaNaar(\"pages\");\n }}\n className=\"flex w-full items-center justify-between gap-2 rounded-lg px-2 py-1.5 text-left text-sm hover:bg-slate-100\"\n title={`${p.title ?? p.id} — ${p.path ?? \"\"}`}\n >\n <span className=\"min-w-0\">\n <span className=\"block truncate text-slate-800\">{p.title ?? p.id}</span>\n <span className=\"block truncate text-xs text-slate-500\">{p.path}</span>\n </span>\n <span className=\"shrink-0 text-xs text-slate-500\">{fmtDatum(p.updatedAt)}</span>\n </button>\n </li>\n ))}\n </ul>\n )}\n </Card>\n\n {/* ── Bestellingen (alleen met shop) ── */}\n {heeftShop && (\n <Card>\n <WidgetKop titel=\"Laatste bestellingen\" tab=\"shop\" tabLabel=\"Webwinkel\" />\n {orders === null ? (\n <p className=\"text-sm text-slate-500\">Laden…</p>\n ) : orders.length === 0 ? (\n <p className=\"text-sm text-slate-500\">Nog geen bestellingen.</p>\n ) : (\n <ul className=\"grid gap-1\">\n {orders.map((o) => (\n <li key={o.id}>\n <button\n type=\"button\"\n onClick={() => {\n vraagOpen(\"shop\", o.id);\n gaNaar(\"shop\");\n }}\n className=\"flex w-full items-center justify-between gap-2 rounded-lg px-2 py-1.5 text-left text-sm hover:bg-slate-100\"\n >\n <span className=\"min-w-0\">\n <span className=\"block truncate text-slate-800\">{o.customer?.name ?? \"Onbekend\"}</span>\n <span className=\"block text-xs text-slate-500\">{fmtDatum(o.createdAt)}</span>\n </span>\n <span className=\"flex shrink-0 items-center gap-2\">\n {typeof o.amount?.totalCents === \"number\" && (\n <span className=\"text-sm font-medium text-slate-700\">{formatCents(o.amount.totalCents)}</span>\n )}\n <Badge tone={ORDER_STATUS_LABELS[o.status ?? \"\"]?.tone ?? \"amber\"}>\n {ORDER_STATUS_LABELS[o.status ?? \"\"]?.label ?? o.status ?? \"Onbekend\"}\n </Badge>\n </span>\n </button>\n </li>\n ))}\n </ul>\n )}\n </Card>\n )}\n\n {/* ── Formulieren ── */}\n <Card>\n <WidgetKop titel=\"Formulieren\" tab=\"forms\" tabLabel=\"Formulieren\" />\n {formulieren === null ? (\n <p className=\"text-sm text-slate-500\">Laden…</p>\n ) : formulieren.length === 0 ? (\n <p className=\"text-sm text-slate-500\">Nog geen formulieren.</p>\n ) : (\n <ul className=\"grid gap-1\">\n {[...formulieren].sort((a, b) => b.nieuw - a.nieuw).slice(0, 4).map((f) => (\n <li key={f.id}>\n <button\n type=\"button\"\n onClick={() => {\n vraagOpen(\"forms\", f.id);\n gaNaar(\"forms\");\n }}\n className=\"flex w-full items-center justify-between gap-2 rounded-lg px-2 py-1.5 text-left text-sm hover:bg-slate-100\"\n >\n <span className=\"truncate text-slate-800\" title={f.name}>{f.name}</span>\n <span className=\"shrink-0 text-xs text-slate-500\">\n {f.aantal} {f.aantal === 1 ? \"inzending\" : \"inzendingen\"}\n {f.laatste ? ` · laatste ${fmtDatum(f.laatste)}` : \"\"}\n </span>\n </button>\n </li>\n ))}\n </ul>\n )}\n </Card>\n\n {/* ── Snelkoppelingen ── */}\n <Card>\n <div className=\"mb-3 flex items-center justify-between gap-2\">\n <h2 className=\"text-sm font-semibold text-slate-800\">Snelkoppelingen</h2>\n {magInstellen && linksBewerken === null && (\n <Button variant=\"secondary\" size=\"sm\" onClick={() => setLinksBewerken(links.length ? [...links] : [{ label: \"\", href: \"\" }])}>\n Instellen\n </Button>\n )}\n </div>\n {linksBewerken ? (\n <div className=\"grid gap-2\">\n <div className=\"grid grid-cols-[1fr_1.4fr_auto] gap-2 text-xs font-medium text-slate-500\">\n <span>Naam</span>\n <span>Adres</span>\n <span aria-hidden=\"true\" />\n </div>\n {linksBewerken.map((l, i) => (\n <div key={i} className=\"grid grid-cols-[1fr_1.4fr_auto] gap-2\">\n <Input value={l.label} placeholder=\"bv. Webwinkel\" aria-label=\"Naam van de snelkoppeling\"\n onChange={(e) => setLinksBewerken(linksBewerken.map((x, j) => (j === i ? { ...x, label: e.target.value } : x)))} />\n <Input value={l.href} placeholder=\"/webwinkel of https://…\" aria-label=\"Adres van de snelkoppeling\"\n onChange={(e) => setLinksBewerken(linksBewerken.map((x, j) => (j === i ? { ...x, href: e.target.value } : x)))} />\n <Button variant=\"ghost\" size=\"sm\" aria-label=\"Verwijderen\"\n onClick={() => setLinksBewerken(linksBewerken.filter((_, j) => j !== i))}>✕</Button>\n </div>\n ))}\n <div className=\"flex items-center justify-between gap-2\">\n <Button variant=\"secondary\" size=\"sm\" disabled={linksBewerken.length >= 12}\n onClick={() => setLinksBewerken([...linksBewerken, { label: \"\", href: \"\" }])}>\n + Snelkoppeling\n </Button>\n <div className=\"flex gap-2\">\n <Button variant=\"secondary\" size=\"sm\" onClick={() => setLinksBewerken(null)}>Annuleren</Button>\n <Button loading={linksOpslaan} size=\"sm\" disabled={linksOpslaan} onClick={() => void slaLinksOp()}>\n {\"Opslaan\"}\n </Button>\n </div>\n </div>\n </div>\n ) : links.length === 0 ? (\n <p className=\"text-sm text-slate-500\">\n {magInstellen\n ? \"Nog geen snelkoppelingen. Zet hier de plekken waar je vaak moet zijn — je webwinkel, een pagina, een extern systeem.\"\n : \"Nog geen snelkoppelingen ingesteld.\"}\n </p>\n ) : (\n <ul className=\"flex flex-wrap gap-2\">\n {links.map((l, i) => (\n <li key={i}>\n <a\n href={l.href}\n target={/^https?:\\/\\//i.test(l.href) ? \"_blank\" : undefined}\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-slate-200 px-3 py-1.5 text-sm text-slate-700 hover:bg-slate-100\"\n >\n {l.label} <span aria-hidden=\"true\" className=\"text-slate-500\">↗</span>\n </a>\n </li>\n ))}\n </ul>\n )}\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1,7 +1,8 @@
1
1
  /**
2
- * WYSIWYG-editor (TipTap) met opmaak én afbeeldingen. Output wordt client-side met DOMPurify
3
- * gesanitized voor preview/opslag; de server sanitized nogmaals bij publiceren (server/sanitize.ts)
4
- * — nooit op de client vertrouwen. Geef `siteId` mee om afbeeldingen te kunnen uploaden/invoegen.
2
+ * WYSIWYG-editor (TipTap) met opmaak én afbeeldingen — de jas voor het beheer, met vaste werkbalk.
3
+ * De kern (extensies, slash-menu, selectie-werkbalk, foto's plakken/slepen, dialogen) zit in
4
+ * `useRijkeTekst` en is dezelfde als bij bewerken op de site (`InlineRijkeTekst`).
5
+ * Geef `siteId` mee om afbeeldingen te kunnen uploaden/invoegen.
5
6
  */
6
7
  export declare function RichTextEditor({ value, onChange, siteId, }: {
7
8
  value: string;
@@ -1,61 +1,27 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { useEditor, EditorContent } from "@tiptap/react";
4
- import StarterKit from "@tiptap/starter-kit";
5
- import { TableKit } from "@tiptap/extension-table";
6
- import Link from "@tiptap/extension-link";
7
- import Image from "@tiptap/extension-image";
8
- import DOMPurify from "dompurify";
3
+ import { EditorContent } from "@tiptap/react";
9
4
  import { useCallback, useEffect } from "react";
10
- import { cn } from "../ui/cn.js";
11
5
  import { MediaPickerButton } from "../media/MediaPicker.js";
12
- import { FontSize, FONT_SIZES } from "./fontSizeExtension.js";
6
+ import { FONT_SIZES } from "./fontSizeExtension.js";
7
+ import { RteKnop as Btn, useRijkeTekst, zonderLegeStaart } from "./useRijkeTekst.js";
13
8
  function RichTextEditor({
14
9
  value,
15
10
  onChange,
16
11
  siteId
17
12
  }) {
18
- const editor = useEditor({
19
- immediatelyRender: false,
20
- extensions: [
21
- // Tiptap 3 bundelt Link en Underline IN StarterKit. Los toevoegen geeft dubbele
22
- // extensienamen (en stille gedragsfouten), dus: Underline komt uit StarterKit, en zijn Link
23
- // zetten we uit omdat we die hieronder zelf configureren (geen open-on-click, eigen rel).
24
- StarterKit.configure({ heading: { levels: [2, 3, 4] }, link: false }),
25
- FontSize,
26
- // Zonder deze extensie gooit TipTap een <table> bij het inladen weg (onbekende node) — een
27
- // tabel uit de seed of een import overleefde dan geen enkele bewerking van dat blok.
28
- TableKit.configure({ table: { resizable: false } }),
29
- Link.configure({ openOnClick: false, autolink: true, HTMLAttributes: { rel: "noopener noreferrer nofollow" } }),
30
- Image.configure({ inline: false, allowBase64: false, HTMLAttributes: { loading: "lazy", decoding: "async" } })
31
- ],
32
- content: value || "<p></p>",
33
- onUpdate: ({ editor: editor2 }) => onChange(DOMPurify.sanitize(editor2.getHTML())),
34
- editorProps: {
35
- attributes: { class: "min-h-[8rem] rounded-b-lg border border-t-0 border-slate-300 bg-white p-3 text-sm" }
36
- }
13
+ const { editor, overlays, openLink: setLink } = useRijkeTekst({
14
+ value,
15
+ onChange,
16
+ siteId,
17
+ editorClass: "xicms-richtext-editor min-h-[8rem] rounded-b-lg border border-t-0 border-slate-300 bg-white p-3 text-sm"
37
18
  });
38
19
  useEffect(() => {
39
20
  if (!editor || editor.isFocused) return;
40
- const current = editor.getHTML();
21
+ const current = zonderLegeStaart(editor.getHTML()) || "<p></p>";
41
22
  const next = value || "<p></p>";
42
23
  if (current !== next) editor.commands.setContent(next, { emitUpdate: false });
43
24
  }, [value, editor]);
44
- const setLink = useCallback(() => {
45
- if (!editor) return;
46
- const prev = editor.getAttributes("link").href;
47
- const url = window.prompt("Link-URL (leeg = verwijderen)", prev ?? "https://");
48
- if (url === null) return;
49
- if (url === "") editor.chain().focus().extendMarkRange("link").unsetLink().run();
50
- else editor.chain().focus().extendMarkRange("link").setLink({ href: url }).run();
51
- }, [editor]);
52
- const insertImageByUrl = useCallback(() => {
53
- if (!editor) return;
54
- const url = window.prompt("Afbeeldings-URL (https)");
55
- if (!url) return;
56
- const alt = window.prompt("Beschrijving (alt-tekst, voor toegankelijkheid)") ?? "";
57
- editor.chain().focus().setImage({ src: url, alt }).run();
58
- }, [editor]);
59
25
  const setFontSize = useCallback(
60
26
  (value2) => {
61
27
  if (!editor) return;
@@ -64,20 +30,6 @@ function RichTextEditor({
64
30
  [editor]
65
31
  );
66
32
  if (!editor) return null;
67
- const Btn = ({ on, active, label, title }) => /* @__PURE__ */ jsx(
68
- "button",
69
- {
70
- type: "button",
71
- title,
72
- onMouseDown: (e) => e.preventDefault(),
73
- onClick: on,
74
- className: cn(
75
- "h-8 min-w-8 rounded px-2 text-sm hover:bg-slate-100",
76
- active && "bg-slate-200 font-semibold"
77
- ),
78
- children: label
79
- }
80
- );
81
33
  return /* @__PURE__ */ jsxs("div", { children: [
82
34
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1 rounded-t-lg border border-slate-300 bg-slate-50 p-1", children: [
83
35
  /* @__PURE__ */ jsx(Btn, { on: () => editor.chain().focus().toggleBold().run(), active: editor.isActive("bold"), label: "B", title: "Vet" }),
@@ -109,10 +61,17 @@ function RichTextEditor({
109
61
  variant: "ghost",
110
62
  onSelect: (m) => editor.chain().focus().setImage({ src: m.url, alt: m.alt ?? "" }).run()
111
63
  }
112
- ),
113
- /* @__PURE__ */ jsx(Btn, { on: insertImageByUrl, label: "\u{1F5BC} URL", title: "Externe afbeelding via URL invoegen" })
64
+ )
65
+ ] }),
66
+ /* @__PURE__ */ jsx(EditorContent, { editor }),
67
+ /* @__PURE__ */ jsxs("p", { className: "mt-1 text-xs text-slate-500", children: [
68
+ "Tip: typ ",
69
+ /* @__PURE__ */ jsx("kbd", { className: "rounded border border-slate-300 px-1", children: "/" }),
70
+ " voor koppen, lijsten, tabellen",
71
+ siteId ? " en foto's \u2014 of sleep een foto erin" : "",
72
+ "."
114
73
  ] }),
115
- /* @__PURE__ */ jsx(EditorContent, { editor })
74
+ overlays
116
75
  ] });
117
76
  }
118
77
  export {