@xiwebdesign/xicms-core 7.30.0 → 8.15.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 (463) hide show
  1. package/dist/XiCMSProvider.js +22 -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 -56
  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 +247 -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 +144 -0
  133. package/dist/admin/settings/BedrijfInstellingen.js.map +1 -0
  134. package/dist/admin/settings/MailSettings.js +2 -2
  135. package/dist/admin/settings/MailSettings.js.map +1 -1
  136. package/dist/admin/settings/MailTemplates.js +5 -4
  137. package/dist/admin/settings/MailTemplates.js.map +1 -1
  138. package/dist/admin/settings/SettingsPanel.js +120 -78
  139. package/dist/admin/settings/SettingsPanel.js.map +1 -1
  140. package/dist/admin/settings/SnapshotsCard.js +4 -4
  141. package/dist/admin/settings/SnapshotsCard.js.map +1 -1
  142. package/dist/admin/settings/VorigeTerugzetten.d.ts +15 -0
  143. package/dist/admin/settings/VorigeTerugzetten.js +89 -0
  144. package/dist/admin/settings/VorigeTerugzetten.js.map +1 -0
  145. package/dist/admin/shop/CategoriesTab.js +6 -4
  146. package/dist/admin/shop/CategoriesTab.js.map +1 -1
  147. package/dist/admin/shop/DiscountsTab.js +4 -2
  148. package/dist/admin/shop/DiscountsTab.js.map +1 -1
  149. package/dist/admin/shop/OrdersTab.js +57 -19
  150. package/dist/admin/shop/OrdersTab.js.map +1 -1
  151. package/dist/admin/shop/ProductPaneel.js +2 -1
  152. package/dist/admin/shop/ProductPaneel.js.map +1 -1
  153. package/dist/admin/shop/ProductsTab.js +40 -19
  154. package/dist/admin/shop/ProductsTab.js.map +1 -1
  155. package/dist/admin/shop/ShopPanel.js +13 -1
  156. package/dist/admin/shop/ShopPanel.js.map +1 -1
  157. package/dist/admin/shop/ShopSettingsTab.js +27 -1
  158. package/dist/admin/shop/ShopSettingsTab.js.map +1 -1
  159. package/dist/admin/shop/productClient.js +7 -3
  160. package/dist/admin/shop/productClient.js.map +1 -1
  161. package/dist/admin/stats/NietGevonden.d.ts +10 -0
  162. package/dist/admin/stats/NietGevonden.js +78 -0
  163. package/dist/admin/stats/NietGevonden.js.map +1 -0
  164. package/dist/admin/stats/StatsPanel.js +6 -1
  165. package/dist/admin/stats/StatsPanel.js.map +1 -1
  166. package/dist/admin/stats/lib.d.ts +2 -0
  167. package/dist/admin/stats/lib.js.map +1 -1
  168. package/dist/admin/ui/ConfirmDialog.js +10 -1
  169. package/dist/admin/ui/ConfirmDialog.js.map +1 -1
  170. package/dist/admin/ui/Icon.d.ts +34 -0
  171. package/dist/admin/ui/Icon.js +44 -0
  172. package/dist/admin/ui/Icon.js.map +1 -0
  173. package/dist/admin/ui/ImageField.d.ts +15 -0
  174. package/dist/admin/ui/ImageField.js +67 -0
  175. package/dist/admin/ui/ImageField.js.map +1 -0
  176. package/dist/admin/ui/KleurVeld.d.ts +25 -0
  177. package/dist/admin/ui/KleurVeld.js +76 -0
  178. package/dist/admin/ui/KleurVeld.js.map +1 -0
  179. package/dist/admin/ui/LaadStaat.d.ts +21 -0
  180. package/dist/admin/ui/LaadStaat.js +43 -0
  181. package/dist/admin/ui/LaadStaat.js.map +1 -0
  182. package/dist/admin/ui/LeesbaarheidPaneel.d.ts +10 -0
  183. package/dist/admin/ui/LeesbaarheidPaneel.js +68 -0
  184. package/dist/admin/ui/LeesbaarheidPaneel.js.map +1 -0
  185. package/dist/admin/ui/LinkDialog.d.ts +22 -0
  186. package/dist/admin/ui/LinkDialog.js +131 -0
  187. package/dist/admin/ui/LinkDialog.js.map +1 -0
  188. package/dist/admin/ui/LinkVeld.d.ts +14 -0
  189. package/dist/admin/ui/LinkVeld.js +49 -0
  190. package/dist/admin/ui/LinkVeld.js.map +1 -0
  191. package/dist/admin/ui/PreviewLinkResultaat.d.ts +14 -0
  192. package/dist/admin/ui/PreviewLinkResultaat.js +52 -0
  193. package/dist/admin/ui/PreviewLinkResultaat.js.map +1 -0
  194. package/dist/admin/ui/charts.js +5 -5
  195. package/dist/admin/ui/charts.js.map +1 -1
  196. package/dist/admin/ui/primitives.d.ts +16 -6
  197. package/dist/admin/ui/primitives.js +98 -26
  198. package/dist/admin/ui/primitives.js.map +1 -1
  199. package/dist/admin/ui/select.js +5 -2
  200. package/dist/admin/ui/select.js.map +1 -1
  201. package/dist/admin/ui/switch.d.ts +3 -1
  202. package/dist/admin/ui/switch.js +6 -3
  203. package/dist/admin/ui/switch.js.map +1 -1
  204. package/dist/admin/ui/toast.d.ts +2 -1
  205. package/dist/admin/ui/toast.js +17 -11
  206. package/dist/admin/ui/toast.js.map +1 -1
  207. package/dist/admin/visual/BlokKiezer.d.ts +34 -0
  208. package/dist/admin/visual/BlokKiezer.js +225 -0
  209. package/dist/admin/visual/BlokKiezer.js.map +1 -0
  210. package/dist/admin/visual/InlineGereedschap.d.ts +52 -0
  211. package/dist/admin/visual/InlineGereedschap.js +356 -0
  212. package/dist/admin/visual/InlineGereedschap.js.map +1 -0
  213. package/dist/admin/visual/InlineRijkeTekst.d.ts +23 -0
  214. package/dist/admin/visual/InlineRijkeTekst.js +152 -0
  215. package/dist/admin/visual/InlineRijkeTekst.js.map +1 -0
  216. package/dist/admin/visual/VisualEditor.js +273 -125
  217. package/dist/admin/visual/VisualEditor.js.map +1 -1
  218. package/dist/admin.css +1 -1
  219. package/dist/bedrijf/bedrijf.d.ts +36 -0
  220. package/dist/bedrijf/bedrijf.js +110 -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 +29 -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 +1 -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/index.d.ts +3 -0
  309. package/dist/blocks/builtins/index.js +6 -0
  310. package/dist/blocks/builtins/index.js.map +1 -1
  311. package/dist/blocks/builtins/lib/autoComplete.d.ts +7 -0
  312. package/dist/blocks/builtins/lib/autoComplete.js +20 -0
  313. package/dist/blocks/builtins/lib/autoComplete.js.map +1 -0
  314. package/dist/blocks/builtins/lib/bookingUrl.d.ts +2 -2
  315. package/dist/blocks/builtins/lib/bookingUrl.js +1 -1
  316. package/dist/blocks/builtins/lib/bookingUrl.js.map +1 -1
  317. package/dist/blocks/builtins/lib/ical.d.ts +24 -0
  318. package/dist/blocks/builtins/lib/ical.js +82 -0
  319. package/dist/blocks/builtins/lib/ical.js.map +1 -0
  320. package/dist/blocks/builtins/lib/vandaag.d.ts +8 -0
  321. package/dist/blocks/builtins/lib/vandaag.js +22 -0
  322. package/dist/blocks/builtins/lib/vandaag.js.map +1 -0
  323. package/dist/blocks/registry.d.ts +6 -0
  324. package/dist/blocks/registry.js.map +1 -1
  325. package/dist/blocks/ui/index.js +1 -0
  326. package/dist/blocks/ui/index.js.map +1 -1
  327. package/dist/components/EditBar.js +2 -4
  328. package/dist/components/EditBar.js.map +1 -1
  329. package/dist/components/News.js +26 -18
  330. package/dist/components/News.js.map +1 -1
  331. package/dist/components/PageView.d.ts +7 -1
  332. package/dist/components/PageView.js +17 -0
  333. package/dist/components/PageView.js.map +1 -1
  334. package/dist/components/ResponsiveImage.js +18 -20
  335. package/dist/components/ResponsiveImage.js.map +1 -1
  336. package/dist/components/site/SiteFooter.d.ts +6 -1
  337. package/dist/components/site/SiteFooter.js +61 -9
  338. package/dist/components/site/SiteFooter.js.map +1 -1
  339. package/dist/components/site/SiteHeader.d.ts +15 -0
  340. package/dist/components/site/SiteHeader.js +70 -0
  341. package/dist/components/site/SiteHeader.js.map +1 -0
  342. package/dist/components/site/SiteNav.d.ts +2 -0
  343. package/dist/components/site/SiteNav.js +29 -5
  344. package/dist/components/site/SiteNav.js.map +1 -1
  345. package/dist/demo/DemoStudio.js +2 -2
  346. package/dist/demo/DemoStudio.js.map +1 -1
  347. package/dist/demo/demoStore.d.ts +13 -0
  348. package/dist/demo/demoStore.js +36 -0
  349. package/dist/demo/demoStore.js.map +1 -1
  350. package/dist/editor/EditorProvider.js +15 -4
  351. package/dist/editor/EditorProvider.js.map +1 -1
  352. package/dist/index.d.ts +4 -1
  353. package/dist/index.js +16 -1
  354. package/dist/index.js.map +1 -1
  355. package/dist/media/ImageContext.d.ts +17 -0
  356. package/dist/media/ImageContext.js +65 -20
  357. package/dist/media/ImageContext.js.map +1 -1
  358. package/dist/media/altInHtml.d.ts +9 -0
  359. package/dist/media/altInHtml.js +17 -0
  360. package/dist/media/altInHtml.js.map +1 -0
  361. package/dist/media/srcset.d.ts +12 -0
  362. package/dist/media/srcset.js +13 -0
  363. package/dist/media/srcset.js.map +1 -0
  364. package/dist/modules/builtins.d.ts +1 -0
  365. package/dist/modules/builtins.js +16 -3
  366. package/dist/modules/builtins.js.map +1 -1
  367. package/dist/modules/shop/blocks/Cart.js +5 -3
  368. package/dist/modules/shop/blocks/Cart.js.map +1 -1
  369. package/dist/modules/shop/blocks/Checkout.js +56 -35
  370. package/dist/modules/shop/blocks/Checkout.js.map +1 -1
  371. package/dist/modules/shop/blocks/Donation.js +4 -4
  372. package/dist/modules/shop/blocks/Donation.js.map +1 -1
  373. package/dist/modules/shop/blocks/ProductDetail.js +19 -3
  374. package/dist/modules/shop/blocks/ProductDetail.js.map +1 -1
  375. package/dist/modules/shop/blocks/ProductGrid.d.ts +3 -1
  376. package/dist/modules/shop/blocks/ProductGrid.js +8 -4
  377. package/dist/modules/shop/blocks/ProductGrid.js.map +1 -1
  378. package/dist/modules/shop/blocks/ShopCategories.js +4 -3
  379. package/dist/modules/shop/blocks/ShopCategories.js.map +1 -1
  380. package/dist/modules/shop/lib.d.ts +4 -0
  381. package/dist/modules/shop/lib.js +14 -0
  382. package/dist/modules/shop/lib.js.map +1 -1
  383. package/dist/modules/shop/settings.d.ts +3 -0
  384. package/dist/modules/shop/settings.js +14 -0
  385. package/dist/modules/shop/settings.js.map +1 -1
  386. package/dist/modules/shop/strings.d.ts +4 -0
  387. package/dist/modules/shop/strings.js +8 -0
  388. package/dist/modules/shop/strings.js.map +1 -1
  389. package/dist/next/envCheck.d.ts +42 -0
  390. package/dist/next/envCheck.js +106 -0
  391. package/dist/next/envCheck.js.map +1 -0
  392. package/dist/next/index.cjs +185 -2
  393. package/dist/next/index.d.ts +2 -0
  394. package/dist/next/index.js +8 -1
  395. package/dist/next/index.js.map +1 -1
  396. package/dist/next/securityHeaders.d.ts +36 -0
  397. package/dist/next/securityHeaders.js +74 -0
  398. package/dist/next/securityHeaders.js.map +1 -0
  399. package/dist/patterns/builtins.d.ts +7 -0
  400. package/dist/patterns/builtins.js +138 -3
  401. package/dist/patterns/builtins.js.map +1 -1
  402. package/dist/releaseNotes.js +140 -0
  403. package/dist/releaseNotes.js.map +1 -1
  404. package/dist/render/PublishedPage.d.ts +2 -1
  405. package/dist/render/PublishedPage.js +10 -4
  406. package/dist/render/PublishedPage.js.map +1 -1
  407. package/dist/schema/blocks.d.ts +470 -0
  408. package/dist/schema/blocks.js +102 -4
  409. package/dist/schema/blocks.js.map +1 -1
  410. package/dist/schema/business.d.ts +117 -0
  411. package/dist/schema/business.js +73 -0
  412. package/dist/schema/business.js.map +1 -0
  413. package/dist/schema/fields.d.ts +2 -1
  414. package/dist/schema/fields.js.map +1 -1
  415. package/dist/schema/forms.d.ts +18 -0
  416. package/dist/schema/forms.js +18 -3
  417. package/dist/schema/forms.js.map +1 -1
  418. package/dist/schema/mail.d.ts +10 -0
  419. package/dist/schema/mail.js +7 -0
  420. package/dist/schema/mail.js.map +1 -1
  421. package/dist/schema/settings.d.ts +101 -0
  422. package/dist/schema/settings.js +24 -3
  423. package/dist/schema/settings.js.map +1 -1
  424. package/dist/schema/shop.d.ts +2 -0
  425. package/dist/schema/shop.js +4 -0
  426. package/dist/schema/shop.js.map +1 -1
  427. package/dist/schema/shopGeld.d.ts +1 -0
  428. package/dist/schema/shopGeld.js +1 -0
  429. package/dist/schema/shopGeld.js.map +1 -1
  430. package/dist/schema/siteConfig.d.ts +68 -1
  431. package/dist/schema/siteConfig.js +7 -2
  432. package/dist/schema/siteConfig.js.map +1 -1
  433. package/dist/server/blockData.d.ts +1 -1
  434. package/dist/server/blockData.js +74 -18
  435. package/dist/server/blockData.js.map +1 -1
  436. package/dist/server/cspRapport.d.ts +20 -0
  437. package/dist/server/cspRapport.js +56 -0
  438. package/dist/server/cspRapport.js.map +1 -0
  439. package/dist/server/fonts.d.ts +3 -0
  440. package/dist/server/fonts.js +39 -0
  441. package/dist/server/fonts.js.map +1 -0
  442. package/dist/server/index.d.ts +3 -0
  443. package/dist/server/index.js +8 -0
  444. package/dist/server/index.js.map +1 -1
  445. package/dist/server/sanitize.js +15 -2
  446. package/dist/server/sanitize.js.map +1 -1
  447. package/dist/settings/useSiteSettings.js +4 -1
  448. package/dist/settings/useSiteSettings.js.map +1 -1
  449. package/dist/starters/starters.d.ts +69 -0
  450. package/dist/starters/starters.js +171 -0
  451. package/dist/starters/starters.js.map +1 -0
  452. package/dist/styles.css +1149 -208
  453. package/dist/theme/color.d.ts +10 -0
  454. package/dist/theme/color.js +17 -1
  455. package/dist/theme/color.js.map +1 -1
  456. package/dist/theme/fontFallback.d.ts +23 -0
  457. package/dist/theme/fontFallback.js +58 -0
  458. package/dist/theme/fontFallback.js.map +1 -0
  459. package/dist/theme/fonts.d.ts +11 -0
  460. package/dist/theme/fonts.js +16 -0
  461. package/dist/theme/fonts.js.map +1 -0
  462. package/dist/version.js +1 -1
  463. package/package.json +6 -4
@@ -76,18 +76,18 @@ function SnapshotsCard({ siteId }) {
76
76
  className: "max-w-xs"
77
77
  }
78
78
  ),
79
- /* @__PURE__ */ jsx(Button, { variant: "secondary", disabled: busy !== null, onClick: () => void create(), children: busy === "create" ? "Bezig\u2026" : "Herstelpunt maken" })
79
+ /* @__PURE__ */ jsx(Button, { loading: busy === "create", variant: "secondary", disabled: busy !== null, onClick: () => void create(), children: "Herstelpunt maken" })
80
80
  ] }),
81
- snapshots.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-slate-400", children: "Nog geen herstelpunten." }) : /* @__PURE__ */ jsx("ul", { className: "grid gap-1 text-sm", children: snapshots.map((s) => /* @__PURE__ */ jsxs("li", { className: "flex items-center justify-between gap-2 rounded border border-slate-200 px-2 py-1.5", children: [
81
+ snapshots.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-slate-500", children: "Nog geen herstelpunten." }) : /* @__PURE__ */ jsx("ul", { className: "grid gap-1 text-sm", children: snapshots.map((s) => /* @__PURE__ */ jsxs("li", { className: "flex items-center justify-between gap-2 rounded border border-slate-200 px-2 py-1.5", children: [
82
82
  /* @__PURE__ */ jsxs("span", { children: [
83
83
  s.kind === "auto" ? "\u{1F6DF} " : "\u{1F4CC} ",
84
84
  s.label ?? "Herstelpunt",
85
- /* @__PURE__ */ jsxs("span", { className: "text-slate-400", children: [
85
+ /* @__PURE__ */ jsxs("span", { className: "text-slate-500", children: [
86
86
  " \xB7 ",
87
87
  formatDate(s.createdAt)
88
88
  ] })
89
89
  ] }),
90
- /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", disabled: busy !== null, onClick: () => void restore(s), children: busy === s.id ? "Bezig\u2026" : "Terugzetten" })
90
+ /* @__PURE__ */ jsx(Button, { loading: busy === s.id, variant: "ghost", size: "sm", disabled: busy !== null, onClick: () => void restore(s), children: "Terugzetten" })
91
91
  ] }, s.id)) })
92
92
  ] });
93
93
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/admin/settings/SnapshotsCard.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * \"Herstelpunten\" (5.0, primo-concept site_snapshots): met één klik de volledige content-stand\n * van de site vastleggen in Firestore, en later terugzetten. Herstellen maakt automatisch eerst\n * een nieuw herstelpunt van de huidige stand (server-side), dus het is altijd omkeerbaar.\n * Aanvulling op de download-back-up: geen bestand nodig, en de automatische vangnetten\n * (vóór restore/push-site) verschijnen hier ook.\n */\nimport { useEffect, useState } from \"react\";\nimport { collection, limit, onSnapshot, orderBy, query } from \"firebase/firestore\";\nimport { callable } from \"../../firebase/functionsClient\";\nimport { getDb, getFirebaseApp } from \"../../firebase/client\";\nimport { Button, Card, Input } from \"../ui/primitives\";\nimport { useToast } from \"../ui/toast\";\nimport { useConfirm } from \"../ui/ConfirmDialog\";\n\ninterface SnapshotMeta {\n id: string;\n label?: string;\n kind?: string;\n createdAt?: { toDate?: () => Date };\n}\n\nfunction formatDate(createdAt: SnapshotMeta[\"createdAt\"]): string {\n const date = createdAt && typeof createdAt.toDate === \"function\" ? createdAt.toDate() : null;\n return date ? date.toLocaleString(\"nl-NL\", { dateStyle: \"short\", timeStyle: \"short\" }) : \"\";\n}\n\nexport function SnapshotsCard({ siteId }: { siteId: string }) {\n const { toast } = useToast();\n const confirm = useConfirm();\n const [snapshots, setSnapshots] = useState<SnapshotMeta[]>([]);\n const [label, setLabel] = useState(\"\");\n const [busy, setBusy] = useState<string | null>(null);\n\n useEffect(() => {\n const db = getDb();\n if (!db) return;\n const q = query(collection(db, \"sites\", siteId, \"snapshots\"), orderBy(\"createdAt\", \"desc\"), limit(20));\n return onSnapshot(\n q,\n (snap) => setSnapshots(snap.docs.map((d) => ({ id: d.id, ...(d.data() as Omit<SnapshotMeta, \"id\">) }))),\n () => setSnapshots([]),\n );\n }, [siteId]);\n\n async function create() {\n const app = getFirebaseApp();\n if (!app) return;\n setBusy(\"create\");\n try {\n const fn = callable(app, \"createSnapshot\");\n await fn({ siteId, label: label.trim() || undefined });\n setLabel(\"\");\n toast({ title: \"Herstelpunt aangemaakt\", tone: \"success\" });\n } catch (e) {\n toast({ title: \"Herstelpunt mislukt\", description: (e as Error).message, tone: \"error\" });\n } finally {\n setBusy(null);\n }\n }\n\n async function restore(s: SnapshotMeta) {\n const ok = await confirm({\n title: \"Herstelpunt terugzetten?\",\n description: `De site wordt teruggezet naar \"${s.label ?? s.id}\" (${formatDate(s.createdAt)}). Er wordt eerst automatisch een nieuw herstelpunt van de huidige stand gemaakt.`,\n confirmLabel: \"Terugzetten\",\n tone: \"danger\",\n });\n if (!ok) return;\n const app = getFirebaseApp();\n if (!app) return;\n setBusy(s.id);\n try {\n const fn = callable(app, \"restoreSnapshot\");\n await fn({ siteId, snapshotId: s.id });\n toast({ title: \"Herstelpunt teruggezet\", description: \"Herlaad de pagina om alles bij te werken.\", tone: \"success\" });\n } catch (e) {\n toast({ title: \"Herstellen mislukt\", description: (e as Error).message, tone: \"error\" });\n } finally {\n setBusy(null);\n }\n }\n\n return (\n <Card>\n <h3 className=\"mb-1 text-base font-semibold\">Herstelpunten</h3>\n <p className=\"mb-3 text-xs text-slate-500\">\n Leg de huidige stand van de hele site vast (pagina's, instellingen, symbolen, bouwstenen) en\n zet hem later met één klik terug — zonder bestanden te downloaden. Vóór elk herstel wordt\n automatisch een nieuw herstelpunt gemaakt, dus terugzetten is altijd omkeerbaar.\n </p>\n <div className=\"mb-3 flex flex-wrap items-center gap-2\">\n <Input\n value={label}\n placeholder=\"Naam (bv. vóór grote wijziging)\"\n onChange={(e) => setLabel(e.target.value)}\n className=\"max-w-xs\"\n />\n <Button variant=\"secondary\" disabled={busy !== null} onClick={() => void create()}>\n {busy === \"create\" ? \"Bezig…\" : \"Herstelpunt maken\"}\n </Button>\n </div>\n {snapshots.length === 0 ? (\n <p className=\"text-xs text-slate-400\">Nog geen herstelpunten.</p>\n ) : (\n <ul className=\"grid gap-1 text-sm\">\n {snapshots.map((s) => (\n <li key={s.id} className=\"flex items-center justify-between gap-2 rounded border border-slate-200 px-2 py-1.5\">\n <span>\n {s.kind === \"auto\" ? \"🛟 \" : \"📌 \"}\n {s.label ?? \"Herstelpunt\"}\n <span className=\"text-slate-400\"> · {formatDate(s.createdAt)}</span>\n </span>\n <Button variant=\"ghost\" size=\"sm\" disabled={busy !== null} onClick={() => void restore(s)}>\n {busy === s.id ? \"Bezig…\" : \"Terugzetten\"}\n </Button>\n </li>\n ))}\n </ul>\n )}\n </Card>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/admin/settings/SnapshotsCard.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * \"Herstelpunten\" (5.0, primo-concept site_snapshots): met één klik de volledige content-stand\n * van de site vastleggen in Firestore, en later terugzetten. Herstellen maakt automatisch eerst\n * een nieuw herstelpunt van de huidige stand (server-side), dus het is altijd omkeerbaar.\n * Aanvulling op de download-back-up: geen bestand nodig, en de automatische vangnetten\n * (vóór restore/push-site) verschijnen hier ook.\n */\nimport { useEffect, useState } from \"react\";\nimport { collection, limit, onSnapshot, orderBy, query } from \"firebase/firestore\";\nimport { callable } from \"../../firebase/functionsClient\";\nimport { getDb, getFirebaseApp } from \"../../firebase/client\";\nimport { Button, Card, Input } from \"../ui/primitives\";\nimport { useToast } from \"../ui/toast\";\nimport { useConfirm } from \"../ui/ConfirmDialog\";\n\ninterface SnapshotMeta {\n id: string;\n label?: string;\n kind?: string;\n createdAt?: { toDate?: () => Date };\n}\n\nfunction formatDate(createdAt: SnapshotMeta[\"createdAt\"]): string {\n const date = createdAt && typeof createdAt.toDate === \"function\" ? createdAt.toDate() : null;\n return date ? date.toLocaleString(\"nl-NL\", { dateStyle: \"short\", timeStyle: \"short\" }) : \"\";\n}\n\nexport function SnapshotsCard({ siteId }: { siteId: string }) {\n const { toast } = useToast();\n const confirm = useConfirm();\n const [snapshots, setSnapshots] = useState<SnapshotMeta[]>([]);\n const [label, setLabel] = useState(\"\");\n const [busy, setBusy] = useState<string | null>(null);\n\n useEffect(() => {\n const db = getDb();\n if (!db) return;\n const q = query(collection(db, \"sites\", siteId, \"snapshots\"), orderBy(\"createdAt\", \"desc\"), limit(20));\n return onSnapshot(\n q,\n (snap) => setSnapshots(snap.docs.map((d) => ({ id: d.id, ...(d.data() as Omit<SnapshotMeta, \"id\">) }))),\n () => setSnapshots([]),\n );\n }, [siteId]);\n\n async function create() {\n const app = getFirebaseApp();\n if (!app) return;\n setBusy(\"create\");\n try {\n const fn = callable(app, \"createSnapshot\");\n await fn({ siteId, label: label.trim() || undefined });\n setLabel(\"\");\n toast({ title: \"Herstelpunt aangemaakt\", tone: \"success\" });\n } catch (e) {\n toast({ title: \"Herstelpunt mislukt\", description: (e as Error).message, tone: \"error\" });\n } finally {\n setBusy(null);\n }\n }\n\n async function restore(s: SnapshotMeta) {\n const ok = await confirm({\n title: \"Herstelpunt terugzetten?\",\n description: `De site wordt teruggezet naar \"${s.label ?? s.id}\" (${formatDate(s.createdAt)}). Er wordt eerst automatisch een nieuw herstelpunt van de huidige stand gemaakt.`,\n confirmLabel: \"Terugzetten\",\n tone: \"danger\",\n });\n if (!ok) return;\n const app = getFirebaseApp();\n if (!app) return;\n setBusy(s.id);\n try {\n const fn = callable(app, \"restoreSnapshot\");\n await fn({ siteId, snapshotId: s.id });\n toast({ title: \"Herstelpunt teruggezet\", description: \"Herlaad de pagina om alles bij te werken.\", tone: \"success\" });\n } catch (e) {\n toast({ title: \"Herstellen mislukt\", description: (e as Error).message, tone: \"error\" });\n } finally {\n setBusy(null);\n }\n }\n\n return (\n <Card>\n <h3 className=\"mb-1 text-base font-semibold\">Herstelpunten</h3>\n <p className=\"mb-3 text-xs text-slate-500\">\n Leg de huidige stand van de hele site vast (pagina's, instellingen, symbolen, bouwstenen) en\n zet hem later met één klik terug — zonder bestanden te downloaden. Vóór elk herstel wordt\n automatisch een nieuw herstelpunt gemaakt, dus terugzetten is altijd omkeerbaar.\n </p>\n <div className=\"mb-3 flex flex-wrap items-center gap-2\">\n <Input\n value={label}\n placeholder=\"Naam (bv. vóór grote wijziging)\"\n onChange={(e) => setLabel(e.target.value)}\n className=\"max-w-xs\"\n />\n <Button loading={busy === \"create\"} variant=\"secondary\" disabled={busy !== null} onClick={() => void create()}>\n {\"Herstelpunt maken\"}\n </Button>\n </div>\n {snapshots.length === 0 ? (\n <p className=\"text-xs text-slate-500\">Nog geen herstelpunten.</p>\n ) : (\n <ul className=\"grid gap-1 text-sm\">\n {snapshots.map((s) => (\n <li key={s.id} className=\"flex items-center justify-between gap-2 rounded border border-slate-200 px-2 py-1.5\">\n <span>\n {s.kind === \"auto\" ? \"🛟 \" : \"📌 \"}\n {s.label ?? \"Herstelpunt\"}\n <span className=\"text-slate-500\"> · {formatDate(s.createdAt)}</span>\n </span>\n <Button loading={busy === s.id} variant=\"ghost\" size=\"sm\" disabled={busy !== null} onClick={() => void restore(s)}>\n {\"Terugzetten\"}\n </Button>\n </li>\n ))}\n </ul>\n )}\n </Card>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,15 @@
1
+ type Sleutel = "theme" | "nav" | "footer" | "header" | "business";
2
+ /**
3
+ * "Vorige terugzetten" (8.10) voor de onderdelen die in één klap de hele site raken.
4
+ *
5
+ * De server bewaart bij elke opslag de stand van vóór de wijziging in `settings/vorige` (zie
6
+ * `saveSiteSettings`). Terugzetten is gewoon weer een opslag — dus wie terugzet, kan daarna ook
7
+ * weer terug naar wat hij net had. Eén stap, geen geschiedenis: dit is de "oeps"-knop, het
8
+ * herstelpunt van de hele site blijft voor de grote ongelukken.
9
+ */
10
+ export declare function VorigeTerugzetten({ siteId, sleutel, dirty }: {
11
+ siteId: string;
12
+ sleutel: Sleutel;
13
+ dirty?: boolean;
14
+ }): import("react").JSX.Element | null;
15
+ export {};
@@ -0,0 +1,89 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from "react";
4
+ import { doc, getDoc, onSnapshot } from "firebase/firestore";
5
+ import { getDb, getFirebaseApp } from "../../firebase/client.js";
6
+ import { callable } from "../../firebase/functionsClient.js";
7
+ import { SiteSettingsSchema } from "../../schema/settings.js";
8
+ import { Button } from "../ui/primitives.js";
9
+ import { useToast } from "../ui/toast.js";
10
+ import { useConfirm } from "../ui/ConfirmDialog.js";
11
+ import { isDemoActive } from "../../demo/demoStore.js";
12
+ const NAMEN = {
13
+ theme: { wat: "huisstijl", het: "de vorige huisstijl" },
14
+ nav: { wat: "menu", het: "het vorige menu" },
15
+ footer: { wat: "footer", het: "de vorige footer" },
16
+ header: { wat: "kop", het: "de vorige kop" },
17
+ business: { wat: "bedrijfsgegevens", het: "de vorige bedrijfsgegevens" }
18
+ };
19
+ function datum(v) {
20
+ const d = v && typeof v.toDate === "function" ? v.toDate() : null;
21
+ return d ? d.toLocaleString("nl-NL", { day: "numeric", month: "long", hour: "2-digit", minute: "2-digit" }) : null;
22
+ }
23
+ function VorigeTerugzetten({ siteId, sleutel, dirty }) {
24
+ const { toast } = useToast();
25
+ const confirm = useConfirm();
26
+ const [vorige, setVorige] = useState(null);
27
+ const [bezig, setBezig] = useState(false);
28
+ useEffect(() => {
29
+ const db = getDb();
30
+ if (!db || isDemoActive()) return;
31
+ return onSnapshot(
32
+ doc(db, "sites", siteId, "settings", "vorige"),
33
+ (snap) => setVorige((snap.data() ?? {})[sleutel] ?? null),
34
+ () => setVorige(null)
35
+ );
36
+ }, [siteId, sleutel]);
37
+ if (!vorige) return null;
38
+ const naam = NAMEN[sleutel];
39
+ const wanneer = datum(vorige.at);
40
+ async function zetTerug() {
41
+ const ok = await confirm({
42
+ title: `${naam.het[0].toUpperCase()}${naam.het.slice(1)} terugzetten?`,
43
+ description: `Je zet ${naam.het}${wanneer ? ` (van v\xF3\xF3r ${wanneer})` : ""} terug; dat staat meteen live. Daarna kun je met dezelfde knop weer terug naar wat er nu staat.`,
44
+ confirmLabel: "Terugzetten"
45
+ });
46
+ if (!ok) return;
47
+ const db = getDb();
48
+ const app = getFirebaseApp();
49
+ if (!db || !app || !vorige) return;
50
+ setBezig(true);
51
+ try {
52
+ const huidig = (await getDoc(doc(db, "sites", siteId, "settings", "site"))).data() ?? {};
53
+ const kandidaat = { ...huidig };
54
+ if (vorige.waarde == null) delete kandidaat[sleutel];
55
+ else kandidaat[sleutel] = vorige.waarde;
56
+ const parsed = SiteSettingsSchema.safeParse(kandidaat);
57
+ if (!parsed.success) throw new Error(`De vorige ${naam.wat} past niet meer in de huidige instellingen.`);
58
+ const settings = vorige.waarde == null ? { ...parsed.data, [sleutel]: null } : parsed.data;
59
+ await callable(app, "saveSiteSettings")({ siteId, settings });
60
+ toast({ title: `Vorige ${naam.wat} teruggezet`, description: "Binnen enkele seconden live.", tone: "success" });
61
+ } catch (e) {
62
+ toast({ title: "Terugzetten mislukt", description: e.message, tone: "error" });
63
+ } finally {
64
+ setBezig(false);
65
+ }
66
+ }
67
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 text-xs text-slate-500", children: [
68
+ /* @__PURE__ */ jsxs("span", { children: [
69
+ "Niet tevreden met de laatste wijziging",
70
+ wanneer ? ` (${wanneer})` : "",
71
+ "?"
72
+ ] }),
73
+ /* @__PURE__ */ jsx(
74
+ Button,
75
+ {
76
+ size: "sm",
77
+ variant: "ghost",
78
+ disabled: bezig || dirty,
79
+ title: dirty ? "Sla eerst op of verwerp je wijzigingen" : void 0,
80
+ onClick: () => void zetTerug(),
81
+ children: bezig ? "Terugzetten\u2026" : `Vorige ${naam.wat} terugzetten`
82
+ }
83
+ )
84
+ ] });
85
+ }
86
+ export {
87
+ VorigeTerugzetten
88
+ };
89
+ //# sourceMappingURL=VorigeTerugzetten.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/admin/settings/VorigeTerugzetten.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { doc, getDoc, onSnapshot } from \"firebase/firestore\";\nimport { getDb, getFirebaseApp } from \"../../firebase/client\";\nimport { callable } from \"../../firebase/functionsClient\";\nimport { SiteSettingsSchema, type SiteSettings } from \"../../schema/settings\";\nimport { Button } from \"../ui/primitives\";\nimport { useToast } from \"../ui/toast\";\nimport { useConfirm } from \"../ui/ConfirmDialog\";\nimport { isDemoActive } from \"../../demo/demoStore\";\n\ntype Sleutel = \"theme\" | \"nav\" | \"footer\" | \"header\" | \"business\";\n\nconst NAMEN: Record<Sleutel, { wat: string; het: string }> = {\n theme: { wat: \"huisstijl\", het: \"de vorige huisstijl\" },\n nav: { wat: \"menu\", het: \"het vorige menu\" },\n footer: { wat: \"footer\", het: \"de vorige footer\" },\n header: { wat: \"kop\", het: \"de vorige kop\" },\n business: { wat: \"bedrijfsgegevens\", het: \"de vorige bedrijfsgegevens\" },\n};\n\nfunction datum(v: unknown): string | null {\n const d = v && typeof (v as { toDate?: () => Date }).toDate === \"function\" ? (v as { toDate: () => Date }).toDate() : null;\n return d ? d.toLocaleString(\"nl-NL\", { day: \"numeric\", month: \"long\", hour: \"2-digit\", minute: \"2-digit\" }) : null;\n}\n\n/**\n * \"Vorige terugzetten\" (8.10) voor de onderdelen die in één klap de hele site raken.\n *\n * De server bewaart bij elke opslag de stand van vóór de wijziging in `settings/vorige` (zie\n * `saveSiteSettings`). Terugzetten is gewoon weer een opslag — dus wie terugzet, kan daarna ook\n * weer terug naar wat hij net had. Eén stap, geen geschiedenis: dit is de \"oeps\"-knop, het\n * herstelpunt van de hele site blijft voor de grote ongelukken.\n */\nexport function VorigeTerugzetten({ siteId, sleutel, dirty }: { siteId: string; sleutel: Sleutel; dirty?: boolean }) {\n const { toast } = useToast();\n const confirm = useConfirm();\n const [vorige, setVorige] = useState<{ waarde: unknown; at?: unknown } | null>(null);\n const [bezig, setBezig] = useState(false);\n\n useEffect(() => {\n const db = getDb();\n if (!db || isDemoActive()) return;\n return onSnapshot(\n doc(db, \"sites\", siteId, \"settings\", \"vorige\"),\n (snap) => setVorige(((snap.data() ?? {}) as Record<string, { waarde: unknown; at?: unknown }>)[sleutel] ?? null),\n () => setVorige(null),\n );\n }, [siteId, sleutel]);\n\n if (!vorige) return null;\n const naam = NAMEN[sleutel];\n const wanneer = datum(vorige.at);\n\n async function zetTerug() {\n const ok = await confirm({\n title: `${naam.het[0].toUpperCase()}${naam.het.slice(1)} terugzetten?`,\n description: `Je zet ${naam.het}${wanneer ? ` (van vóór ${wanneer})` : \"\"} terug; dat staat meteen live. Daarna kun je met dezelfde knop weer terug naar wat er nu staat.`,\n confirmLabel: \"Terugzetten\",\n });\n if (!ok) return;\n const db = getDb();\n const app = getFirebaseApp();\n if (!db || !app || !vorige) return;\n setBezig(true);\n try {\n const huidig = ((await getDoc(doc(db, \"sites\", siteId, \"settings\", \"site\"))).data() ?? {}) as SiteSettings;\n const kandidaat: Record<string, unknown> = { ...huidig };\n if (vorige.waarde == null) delete kandidaat[sleutel];\n else kandidaat[sleutel] = vorige.waarde;\n const parsed = SiteSettingsSchema.safeParse(kandidaat);\n if (!parsed.success) throw new Error(`De vorige ${naam.wat} past niet meer in de huidige instellingen.`);\n // `null` = er was nog niets eigens: de server wist het veld dan (terug naar de standaard).\n const settings = vorige.waarde == null ? { ...parsed.data, [sleutel]: null } : parsed.data;\n await callable(app, \"saveSiteSettings\")({ siteId, settings });\n toast({ title: `Vorige ${naam.wat} teruggezet`, description: \"Binnen enkele seconden live.\", tone: \"success\" });\n } catch (e) {\n toast({ title: \"Terugzetten mislukt\", description: (e as Error).message, tone: \"error\" });\n } finally {\n setBezig(false);\n }\n }\n\n return (\n <div className=\"flex flex-wrap items-center gap-2 text-xs text-slate-500\">\n <span>\n Niet tevreden met de laatste wijziging{wanneer ? ` (${wanneer})` : \"\"}?\n </span>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n disabled={bezig || dirty}\n title={dirty ? \"Sla eerst op of verwerp je wijzigingen\" : undefined}\n onClick={() => void zetTerug()}\n >\n {bezig ? \"Terugzetten…\" : `Vorige ${naam.wat} terugzetten`}\n </Button>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -11,6 +11,7 @@ import { useConfirm } from "../ui/ConfirmDialog.js";
11
11
  import { shopCallable } from "./lib.js";
12
12
  import { MediaPickerButton } from "../media/MediaPicker.js";
13
13
  import { slugify } from "../lib/slugify.js";
14
+ import { zodFoutTekst } from "../lib/zodFout.js";
14
15
  function CategoriesTab({ siteId }) {
15
16
  const { toast } = useToast();
16
17
  const confirm = useConfirm();
@@ -23,7 +24,8 @@ function CategoriesTab({ siteId }) {
23
24
  collection(db, "sites", siteId, "shopCategories"),
24
25
  (snap) => setCats(
25
26
  snap.docs.map((d) => ({ id: d.id, ...d.data() })).sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
26
- )
27
+ ),
28
+ (e) => console.warn("[xicms] live-lijst niet geladen:", e.message)
27
29
  );
28
30
  }, [siteId]);
29
31
  async function createCategory() {
@@ -59,7 +61,7 @@ function CategoriesTab({ siteId }) {
59
61
  className: `w-full truncate rounded-lg px-3 py-2 text-left text-sm ${c.id === selected ? "bg-brand-50 text-brand-700" : "hover:bg-slate-100"}`,
60
62
  children: [
61
63
  c.name,
62
- c.visible === false && /* @__PURE__ */ jsx("span", { className: "ml-1 text-xs text-slate-400", children: "(verborgen)" })
64
+ c.visible === false && /* @__PURE__ */ jsx("span", { className: "ml-1 text-xs text-slate-500", children: "(verborgen)" })
63
65
  ]
64
66
  }
65
67
  ) }, c.id)),
@@ -92,7 +94,7 @@ function CategoryDetail({
92
94
  if (!db) return;
93
95
  const parsed = ShopCategorySchema.safeParse(def);
94
96
  if (!parsed.success) {
95
- onToast({ title: "Ongeldige categorie", description: parsed.error.issues[0]?.message, tone: "error" });
97
+ onToast({ title: "Ongeldige categorie", description: zodFoutTekst(parsed.error.issues[0]), tone: "error" });
96
98
  return;
97
99
  }
98
100
  try {
@@ -131,7 +133,7 @@ function CategoryDetail({
131
133
  onChange: (e) => setDef((p) => ({ ...p, name: e.target.value, slug: slugify(e.target.value) || p.slug }))
132
134
  }
133
135
  ) }),
134
- /* @__PURE__ */ jsx(Field, { label: "Slug", hint: "Wordt gebruikt in de winkel-URL (?cat=\u2026)", children: /* @__PURE__ */ jsx(Input, { value: def.slug, onChange: (e) => setDef((p) => ({ ...p, slug: slugify(e.target.value) })) }) }),
136
+ /* @__PURE__ */ jsx(Field, { label: "Adresnaam", hint: "Het stukje in het webadres van deze categorie (alleen kleine letters en streepjes).", children: /* @__PURE__ */ jsx(Input, { value: def.slug, onChange: (e) => setDef((p) => ({ ...p, slug: slugify(e.target.value) })) }) }),
135
137
  /* @__PURE__ */ jsx(Field, { label: "Omschrijving (optioneel)", children: /* @__PURE__ */ jsx(Textarea, { rows: 2, value: def.description ?? "", onChange: (e) => setDef((p) => ({ ...p, description: e.target.value })) }) }),
136
138
  /* @__PURE__ */ jsx(Field, { label: "Afbeelding", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
137
139
  def.imageUrl && /* @__PURE__ */ jsx("img", { src: def.imageUrl, alt: "", className: "h-14 w-14 rounded-lg object-cover" }),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/admin/shop/CategoriesTab.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { addDoc, collection, deleteDoc, doc, onSnapshot, setDoc } from \"firebase/firestore\";\nimport { getDb } from \"../../firebase/client\";\nimport { ShopCategorySchema, type ShopCategory } from \"../../schema/shop\";\nimport { Button, Card, Field, Input, Textarea } from \"../ui/primitives\";\nimport { Switch } from \"../ui/switch\";\nimport { useToast } from \"../ui/toast\";\nimport { useConfirm } from \"../ui/ConfirmDialog\";\nimport { shopCallable } from \"./lib\";\nimport { MediaPickerButton } from \"../media/MediaPicker\";\nimport { slugify } from \"../lib/slugify\";\n\ntype StoredCategory = ShopCategory & { id: string };\n\nexport function CategoriesTab({ siteId }: { siteId: string }) {\n const { toast } = useToast();\n const confirm = useConfirm();\n const [cats, setCats] = useState<StoredCategory[]>([]);\n const [selected, setSelected] = useState<string | null>(null);\n\n useEffect(() => {\n const db = getDb();\n if (!db) return;\n return onSnapshot(collection(db, \"sites\", siteId, \"shopCategories\"), (snap) =>\n setCats(\n snap.docs\n .map((d) => ({ id: d.id, ...(d.data() as ShopCategory) }))\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0)),\n ),\n );\n }, [siteId]);\n\n async function createCategory() {\n const db = getDb();\n if (!db) return;\n const nieuw = {\n name: \"Nieuwe categorie\",\n slug: `categorie-${cats.length + 1}`,\n order: cats.length,\n visible: true,\n } satisfies ShopCategory;\n try {\n const res = await shopCallable<{ siteId: string; category: unknown }, { id: string }>(\"saveShopCategory\")({ siteId, category: nieuw });\n setSelected(res.id);\n } catch (err) {\n if ((err as { code?: string } | null)?.code !== \"functions/not-found\") throw err;\n const ref = await addDoc(collection(db, \"sites\", siteId, \"shopCategories\"), nieuw);\n setSelected(ref.id);\n }\n }\n\n const current = cats.find((c) => c.id === selected) ?? null;\n\n return (\n <div className=\"grid gap-4 md:grid-cols-[260px_1fr]\">\n <Card className=\"h-fit\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h3 className=\"text-sm font-semibold\">Categorieën</h3>\n <Button size=\"sm\" onClick={createCategory}>+ Nieuw</Button>\n </div>\n <ul className=\"grid gap-1\">\n {cats.map((c) => (\n <li key={c.id}>\n <button\n onClick={() => setSelected(c.id)}\n className={`w-full truncate rounded-lg px-3 py-2 text-left text-sm ${\n c.id === selected ? \"bg-brand-50 text-brand-700\" : \"hover:bg-slate-100\"\n }`}\n >\n {c.name}\n {c.visible === false && <span className=\"ml-1 text-xs text-slate-400\">(verborgen)</span>}\n </button>\n </li>\n ))}\n {cats.length === 0 && <li className=\"px-3 py-2 text-sm text-slate-500\">Nog geen categorieën.</li>}\n </ul>\n </Card>\n {current ? (\n <CategoryDetail\n key={current.id}\n siteId={siteId}\n category={current}\n onDeleted={() => setSelected(null)}\n onToast={toast}\n confirm={confirm}\n />\n ) : (\n <Card>\n <p className=\"text-sm text-slate-500\">Selecteer of maak een categorie.</p>\n </Card>\n )}\n </div>\n );\n}\n\nfunction CategoryDetail({\n siteId,\n category,\n onDeleted,\n onToast,\n confirm,\n}: {\n siteId: string;\n category: StoredCategory;\n onDeleted: () => void;\n onToast: (t: { title: string; description?: string; tone?: \"default\" | \"success\" | \"error\" }) => void;\n confirm: (opts: { title: string; description?: string }) => Promise<boolean>;\n}) {\n const [def, setDef] = useState<ShopCategory>(category);\n\n async function save() {\n const db = getDb();\n if (!db) return;\n const parsed = ShopCategorySchema.safeParse(def);\n if (!parsed.success) {\n onToast({ title: \"Ongeldige categorie\", description: parsed.error.issues[0]?.message, tone: \"error\" });\n return;\n }\n /**\n * Via de callable (7.18): die maakt de server-cache van de categorieënlijst ongeldig. Een\n * rechtstreekse write stond meteen in Firestore maar pas na het ISR-vangnet (tot een uur) op\n * de winkel — zelfde valkuil als bij producten. Terugval op de directe write houdt een site\n * met een oudere functions-deploy werkend (zie productClient.ts).\n */\n try {\n await shopCallable<{ siteId: string; categoryId: string; category: unknown }, { id: string }>(\"saveShopCategory\")({\n siteId,\n categoryId: category.id,\n category: parsed.data,\n });\n } catch (err) {\n if ((err as { code?: string } | null)?.code !== \"functions/not-found\") throw err;\n await setDoc(doc(db, \"sites\", siteId, \"shopCategories\", category.id), parsed.data);\n }\n onToast({ title: \"Categorie opgeslagen\", tone: \"success\" });\n }\n\n async function remove() {\n const db = getDb();\n if (!db) return;\n const ok = await confirm({\n title: `\"${category.name}\" verwijderen?`,\n description: \"Producten in deze categorie blijven bestaan (alleen de koppeling vervalt).\",\n });\n if (!ok) return;\n try {\n await shopCallable<{ siteId: string; categoryId: string }, unknown>(\"deleteShopCategory\")({ siteId, categoryId: category.id });\n } catch (err) {\n if ((err as { code?: string } | null)?.code !== \"functions/not-found\") throw err;\n await deleteDoc(doc(db, \"sites\", siteId, \"shopCategories\", category.id));\n }\n onDeleted();\n }\n\n return (\n <Card>\n <div className=\"grid gap-4\">\n <Field label=\"Naam\">\n <Input\n value={def.name}\n onChange={(e) => setDef((p) => ({ ...p, name: e.target.value, slug: slugify(e.target.value) || p.slug }))}\n />\n </Field>\n <Field label=\"Slug\" hint=\"Wordt gebruikt in de winkel-URL (?cat=…)\">\n <Input value={def.slug} onChange={(e) => setDef((p) => ({ ...p, slug: slugify(e.target.value) }))} />\n </Field>\n <Field label=\"Omschrijving (optioneel)\">\n <Textarea rows={2} value={def.description ?? \"\"} onChange={(e) => setDef((p) => ({ ...p, description: e.target.value }))} />\n </Field>\n <Field label=\"Afbeelding\">\n <div className=\"flex items-center gap-3\">\n {def.imageUrl && <img src={def.imageUrl} alt=\"\" className=\"h-14 w-14 rounded-lg object-cover\" />}\n <MediaPickerButton siteId={siteId} onSelect={(m) => setDef((p) => ({ ...p, imageUrl: m.url }))} />\n {def.imageUrl && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setDef((p) => ({ ...p, imageUrl: undefined }))}>\n Verwijder\n </Button>\n )}\n </div>\n </Field>\n <div className=\"flex items-center gap-6\">\n <Field label=\"Volgorde\">\n <Input\n type=\"number\"\n className=\"w-24\"\n value={def.order ?? 0}\n onChange={(e) => setDef((p) => ({ ...p, order: Math.max(0, Number(e.target.value) || 0) }))}\n />\n </Field>\n <Switch checked={def.visible !== false} onCheckedChange={(v) => setDef((p) => ({ ...p, visible: v }))} label=\"Zichtbaar in de winkel\" />\n </div>\n <div className=\"flex items-center justify-between\">\n <Button onClick={save}>Opslaan</Button>\n <Button variant=\"ghost\" onClick={remove}>Verwijderen</Button>\n </div>\n </div>\n </Card>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/admin/shop/CategoriesTab.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { addDoc, collection, deleteDoc, doc, onSnapshot, setDoc } from \"firebase/firestore\";\nimport { getDb } from \"../../firebase/client\";\nimport { ShopCategorySchema, type ShopCategory } from \"../../schema/shop\";\nimport { Button, Card, Field, Input, Textarea } from \"../ui/primitives\";\nimport { Switch } from \"../ui/switch\";\nimport { useToast } from \"../ui/toast\";\nimport { useConfirm } from \"../ui/ConfirmDialog\";\nimport { shopCallable } from \"./lib\";\nimport { MediaPickerButton } from \"../media/MediaPicker\";\nimport { slugify } from \"../lib/slugify\";\nimport { zodFoutTekst } from \"../lib/zodFout\";\n\ntype StoredCategory = ShopCategory & { id: string };\n\nexport function CategoriesTab({ siteId }: { siteId: string }) {\n const { toast } = useToast();\n const confirm = useConfirm();\n const [cats, setCats] = useState<StoredCategory[]>([]);\n const [selected, setSelected] = useState<string | null>(null);\n\n useEffect(() => {\n const db = getDb();\n if (!db) return;\n return onSnapshot(collection(db, \"sites\", siteId, \"shopCategories\"), (snap) =>\n setCats(\n snap.docs\n .map((d) => ({ id: d.id, ...(d.data() as ShopCategory) }))\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0)),\n ),\n (e) => console.warn(\"[xicms] live-lijst niet geladen:\", e.message),\n );\n }, [siteId]);\n\n async function createCategory() {\n const db = getDb();\n if (!db) return;\n const nieuw = {\n name: \"Nieuwe categorie\",\n slug: `categorie-${cats.length + 1}`,\n order: cats.length,\n visible: true,\n } satisfies ShopCategory;\n try {\n const res = await shopCallable<{ siteId: string; category: unknown }, { id: string }>(\"saveShopCategory\")({ siteId, category: nieuw });\n setSelected(res.id);\n } catch (err) {\n if ((err as { code?: string } | null)?.code !== \"functions/not-found\") throw err;\n const ref = await addDoc(collection(db, \"sites\", siteId, \"shopCategories\"), nieuw);\n setSelected(ref.id);\n }\n }\n\n const current = cats.find((c) => c.id === selected) ?? null;\n\n return (\n <div className=\"grid gap-4 md:grid-cols-[260px_1fr]\">\n <Card className=\"h-fit\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h3 className=\"text-sm font-semibold\">Categorieën</h3>\n <Button size=\"sm\" onClick={createCategory}>+ Nieuw</Button>\n </div>\n <ul className=\"grid gap-1\">\n {cats.map((c) => (\n <li key={c.id}>\n <button\n onClick={() => setSelected(c.id)}\n className={`w-full truncate rounded-lg px-3 py-2 text-left text-sm ${\n c.id === selected ? \"bg-brand-50 text-brand-700\" : \"hover:bg-slate-100\"\n }`}\n >\n {c.name}\n {c.visible === false && <span className=\"ml-1 text-xs text-slate-500\">(verborgen)</span>}\n </button>\n </li>\n ))}\n {cats.length === 0 && <li className=\"px-3 py-2 text-sm text-slate-500\">Nog geen categorieën.</li>}\n </ul>\n </Card>\n {current ? (\n <CategoryDetail\n key={current.id}\n siteId={siteId}\n category={current}\n onDeleted={() => setSelected(null)}\n onToast={toast}\n confirm={confirm}\n />\n ) : (\n <Card>\n <p className=\"text-sm text-slate-500\">Selecteer of maak een categorie.</p>\n </Card>\n )}\n </div>\n );\n}\n\nfunction CategoryDetail({\n siteId,\n category,\n onDeleted,\n onToast,\n confirm,\n}: {\n siteId: string;\n category: StoredCategory;\n onDeleted: () => void;\n onToast: (t: { title: string; description?: string; tone?: \"default\" | \"success\" | \"error\" }) => void;\n confirm: (opts: { title: string; description?: string }) => Promise<boolean>;\n}) {\n const [def, setDef] = useState<ShopCategory>(category);\n\n async function save() {\n const db = getDb();\n if (!db) return;\n const parsed = ShopCategorySchema.safeParse(def);\n if (!parsed.success) {\n onToast({ title: \"Ongeldige categorie\", description: zodFoutTekst(parsed.error.issues[0]), tone: \"error\" });\n return;\n }\n /**\n * Via de callable (7.18): die maakt de server-cache van de categorieënlijst ongeldig. Een\n * rechtstreekse write stond meteen in Firestore maar pas na het ISR-vangnet (tot een uur) op\n * de winkel — zelfde valkuil als bij producten. Terugval op de directe write houdt een site\n * met een oudere functions-deploy werkend (zie productClient.ts).\n */\n try {\n await shopCallable<{ siteId: string; categoryId: string; category: unknown }, { id: string }>(\"saveShopCategory\")({\n siteId,\n categoryId: category.id,\n category: parsed.data,\n });\n } catch (err) {\n if ((err as { code?: string } | null)?.code !== \"functions/not-found\") throw err;\n await setDoc(doc(db, \"sites\", siteId, \"shopCategories\", category.id), parsed.data);\n }\n onToast({ title: \"Categorie opgeslagen\", tone: \"success\" });\n }\n\n async function remove() {\n const db = getDb();\n if (!db) return;\n const ok = await confirm({\n title: `\"${category.name}\" verwijderen?`,\n description: \"Producten in deze categorie blijven bestaan (alleen de koppeling vervalt).\",\n });\n if (!ok) return;\n try {\n await shopCallable<{ siteId: string; categoryId: string }, unknown>(\"deleteShopCategory\")({ siteId, categoryId: category.id });\n } catch (err) {\n if ((err as { code?: string } | null)?.code !== \"functions/not-found\") throw err;\n await deleteDoc(doc(db, \"sites\", siteId, \"shopCategories\", category.id));\n }\n onDeleted();\n }\n\n return (\n <Card>\n <div className=\"grid gap-4\">\n <Field label=\"Naam\">\n <Input\n value={def.name}\n onChange={(e) => setDef((p) => ({ ...p, name: e.target.value, slug: slugify(e.target.value) || p.slug }))}\n />\n </Field>\n <Field label=\"Adresnaam\" hint=\"Het stukje in het webadres van deze categorie (alleen kleine letters en streepjes).\">\n <Input value={def.slug} onChange={(e) => setDef((p) => ({ ...p, slug: slugify(e.target.value) }))} />\n </Field>\n <Field label=\"Omschrijving (optioneel)\">\n <Textarea rows={2} value={def.description ?? \"\"} onChange={(e) => setDef((p) => ({ ...p, description: e.target.value }))} />\n </Field>\n <Field label=\"Afbeelding\">\n <div className=\"flex items-center gap-3\">\n {def.imageUrl && <img src={def.imageUrl} alt=\"\" className=\"h-14 w-14 rounded-lg object-cover\" />}\n <MediaPickerButton siteId={siteId} onSelect={(m) => setDef((p) => ({ ...p, imageUrl: m.url }))} />\n {def.imageUrl && (\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setDef((p) => ({ ...p, imageUrl: undefined }))}>\n Verwijder\n </Button>\n )}\n </div>\n </Field>\n <div className=\"flex items-center gap-6\">\n <Field label=\"Volgorde\">\n <Input\n type=\"number\"\n className=\"w-24\"\n value={def.order ?? 0}\n onChange={(e) => setDef((p) => ({ ...p, order: Math.max(0, Number(e.target.value) || 0) }))}\n />\n </Field>\n <Switch checked={def.visible !== false} onCheckedChange={(v) => setDef((p) => ({ ...p, visible: v }))} label=\"Zichtbaar in de winkel\" />\n </div>\n <div className=\"flex items-center justify-between\">\n <Button onClick={save}>Opslaan</Button>\n <Button variant=\"ghost\" onClick={remove}>Verwijderen</Button>\n </div>\n </div>\n </Card>\n );\n}\n"],"mappings":"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","names":[]}
@@ -10,6 +10,7 @@ import { Switch } from "../ui/switch.js";
10
10
  import { useToast } from "../ui/toast.js";
11
11
  import { useConfirm } from "../ui/ConfirmDialog.js";
12
12
  import { centsToEuroInput, eurosToCents } from "./lib.js";
13
+ import { zodFoutTekst } from "../lib/zodFout.js";
13
14
  function DiscountsTab({ siteId }) {
14
15
  const { toast } = useToast();
15
16
  const confirm = useConfirm();
@@ -20,7 +21,8 @@ function DiscountsTab({ siteId }) {
20
21
  if (!db) return;
21
22
  return onSnapshot(
22
23
  collection(db, "sites", siteId, "discountCodes"),
23
- (snap) => setCodes(snap.docs.map((d) => ({ id: d.id, ...d.data() })).sort((a, b) => a.code.localeCompare(b.code)))
24
+ (snap) => setCodes(snap.docs.map((d) => ({ id: d.id, ...d.data() })).sort((a, b) => a.code.localeCompare(b.code))),
25
+ (e) => console.warn("[xicms] live-lijst niet geladen:", e.message)
24
26
  );
25
27
  }, [siteId]);
26
28
  const current = codes.find((c) => c.id === selected) ?? null;
@@ -84,7 +86,7 @@ function DiscountDetail({
84
86
  minOrderCents: minInput.trim() === "" ? 0 : eurosToCents(minInput) ?? 0
85
87
  });
86
88
  if (!parsed.success) {
87
- onToast({ title: "Ongeldige code", description: parsed.error.issues[0]?.message, tone: "error" });
89
+ onToast({ title: "Ongeldige code", description: zodFoutTekst(parsed.error.issues[0]), tone: "error" });
88
90
  return;
89
91
  }
90
92
  await setDoc(doc(db, "sites", siteId, "discountCodes", parsed.data.code), parsed.data);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/admin/shop/DiscountsTab.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { collection, deleteDoc, doc, onSnapshot, setDoc } from \"firebase/firestore\";\nimport { getDb } from \"../../firebase/client\";\nimport { DiscountCodeSchema, formatCents, type DiscountCode } from \"../../schema/shop\";\nimport { Badge, Button, Card, Field, Input } from \"../ui/primitives\";\nimport { Select } from \"../ui/select\";\nimport { Switch } from \"../ui/switch\";\nimport { useToast } from \"../ui/toast\";\nimport { useConfirm } from \"../ui/ConfirmDialog\";\nimport { centsToEuroInput, eurosToCents } from \"./lib\";\n\ntype Stored = DiscountCode & { id: string };\n\n/** Kortingscodes — doc-id = code; de webhook telt het gebruik op. */\nexport function DiscountsTab({ siteId }: { siteId: string }) {\n const { toast } = useToast();\n const confirm = useConfirm();\n const [codes, setCodes] = useState<Stored[]>([]);\n const [selected, setSelected] = useState<string | null>(null);\n\n useEffect(() => {\n const db = getDb();\n if (!db) return;\n return onSnapshot(collection(db, \"sites\", siteId, \"discountCodes\"), (snap) =>\n setCodes(snap.docs.map((d) => ({ id: d.id, ...(d.data() as DiscountCode) })).sort((a, b) => a.code.localeCompare(b.code))),\n );\n }, [siteId]);\n\n const current = codes.find((c) => c.id === selected) ?? null;\n\n return (\n <div className=\"grid gap-4 md:grid-cols-[280px_1fr]\">\n <Card className=\"h-fit\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h3 className=\"text-sm font-semibold\">Kortingscodes</h3>\n <Button size=\"sm\" onClick={() => setSelected(\"__new__\")}>+ Nieuw</Button>\n </div>\n <ul className=\"grid gap-1\">\n {codes.map((c) => (\n <li key={c.id}>\n <button\n onClick={() => setSelected(c.id)}\n className={`flex w-full items-center justify-between gap-2 rounded-lg px-3 py-2 text-left text-sm ${\n c.id === selected ? \"bg-brand-50 text-brand-700\" : \"hover:bg-slate-100\"\n }`}\n >\n <span className=\"font-mono font-semibold\">{c.code}</span>\n <span className=\"flex items-center gap-1\">\n <span className=\"text-xs text-slate-500\">\n {c.type === \"percent\" ? `${c.value}%` : formatCents(c.value)}\n </span>\n <Badge tone={c.active !== false ? \"green\" : \"slate\"}>{c.active !== false ? \"actief\" : \"uit\"}</Badge>\n </span>\n </button>\n </li>\n ))}\n {codes.length === 0 && <li className=\"px-3 py-2 text-sm text-slate-500\">Nog geen kortingscodes.</li>}\n </ul>\n </Card>\n {current || selected === \"__new__\" ? (\n <DiscountDetail\n key={selected}\n siteId={siteId}\n existing={current}\n onDone={() => setSelected(null)}\n onToast={toast}\n confirm={confirm}\n />\n ) : (\n <Card><p className=\"text-sm text-slate-500\">Selecteer of maak een kortingscode.</p></Card>\n )}\n </div>\n );\n}\n\nfunction DiscountDetail({\n siteId,\n existing,\n onDone,\n onToast,\n confirm,\n}: {\n siteId: string;\n existing: Stored | null;\n onDone: () => void;\n onToast: (t: { title: string; description?: string; tone?: \"default\" | \"success\" | \"error\" }) => void;\n confirm: (opts: { title: string; description?: string; tone?: \"default\" | \"danger\" }) => Promise<boolean>;\n}) {\n const [def, setDef] = useState<DiscountCode>(\n existing ?? { code: \"\", type: \"percent\", value: 10, minOrderCents: 0, freeShipping: false, active: true, maxUses: 0, usedCount: 0, oncePerCustomer: false },\n );\n const [fixedInput, setFixedInput] = useState(existing?.type === \"fixed\" ? centsToEuroInput(existing.value) : \"\");\n const [minInput, setMinInput] = useState(existing?.minOrderCents ? centsToEuroInput(existing.minOrderCents) : \"\");\n\n async function save() {\n const db = getDb();\n if (!db) return;\n const value = def.type === \"fixed\" ? (eurosToCents(fixedInput) ?? 0) : def.value;\n const parsed = DiscountCodeSchema.safeParse({\n ...def,\n code: def.code.toUpperCase(),\n value,\n minOrderCents: minInput.trim() === \"\" ? 0 : (eurosToCents(minInput) ?? 0),\n });\n if (!parsed.success) {\n onToast({ title: \"Ongeldige code\", description: parsed.error.issues[0]?.message, tone: \"error\" });\n return;\n }\n await setDoc(doc(db, \"sites\", siteId, \"discountCodes\", parsed.data.code), parsed.data);\n onToast({ title: \"Kortingscode opgeslagen\", tone: \"success\" });\n if (!existing) onDone();\n }\n\n async function remove() {\n const db = getDb();\n if (!db || !existing) return;\n const ok = await confirm({ title: `Code ${existing.code} verwijderen?`, tone: \"danger\" });\n if (!ok) return;\n await deleteDoc(doc(db, \"sites\", siteId, \"discountCodes\", existing.id));\n onDone();\n }\n\n return (\n <Card>\n <div className=\"grid gap-4\">\n <div className=\"grid gap-4 md:grid-cols-3\">\n <Field label=\"Code\" hint=\"Hoofdletters, cijfers, streepjes\">\n <Input\n value={def.code}\n disabled={Boolean(existing)}\n onChange={(e) => setDef((p) => ({ ...p, code: e.target.value.toUpperCase().replace(/[^A-Z0-9-]/g, \"\") }))}\n placeholder=\"WELKOM10\"\n />\n </Field>\n <Field label=\"Soort\">\n <Select\n value={def.type}\n onValueChange={(v) => setDef((p) => ({ ...p, type: v as \"percent\" | \"fixed\" }))}\n options={[\n { value: \"percent\", label: \"Percentage\" },\n { value: \"fixed\", label: \"Vast bedrag\" },\n ]}\n />\n </Field>\n {def.type === \"percent\" ? (\n <Field label=\"Percentage\">\n <Input\n type=\"number\"\n min={1}\n max={100}\n value={def.value}\n onChange={(e) => setDef((p) => ({ ...p, value: Math.max(1, Math.min(100, Number(e.target.value) || 0)) }))}\n className=\"w-24\"\n />\n </Field>\n ) : (\n <Field label=\"Bedrag (€)\">\n <Input value={fixedInput} onChange={(e) => setFixedInput(e.target.value)} placeholder=\"5,00\" className=\"w-24\" />\n </Field>\n )}\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-3\">\n <Field label=\"Minimaal orderbedrag (€)\" hint=\"Leeg = geen minimum\">\n <Input value={minInput} onChange={(e) => setMinInput(e.target.value)} placeholder=\"25,00\" className=\"w-28\" />\n </Field>\n <Field label=\"Geldig van\" hint=\"Leeg = direct\">\n <Input type=\"date\" value={def.startsAt ?? \"\"} onChange={(e) => setDef((p) => ({ ...p, startsAt: e.target.value || undefined }))} />\n </Field>\n <Field label=\"Geldig t/m\" hint=\"Leeg = onbeperkt\">\n <Input type=\"date\" value={def.endsAt ?? \"\"} onChange={(e) => setDef((p) => ({ ...p, endsAt: e.target.value || undefined }))} />\n </Field>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-6\">\n <Field label=\"Max. aantal keer te gebruiken\" hint=\"0 = onbeperkt\">\n <Input\n type=\"number\"\n min={0}\n value={def.maxUses ?? 0}\n onChange={(e) => setDef((p) => ({ ...p, maxUses: Math.max(0, Number(e.target.value) || 0) }))}\n className=\"w-24\"\n />\n </Field>\n <Switch checked={def.freeShipping === true} onCheckedChange={(v) => setDef((p) => ({ ...p, freeShipping: v }))} label=\"Gratis verzending\" />\n <Switch\n checked={def.oncePerCustomer === true}\n onCheckedChange={(v) => setDef((p) => ({ ...p, oncePerCustomer: v }))}\n label=\"Eén keer per klant (op e-mailadres)\"\n />\n <Switch checked={def.active !== false} onCheckedChange={(v) => setDef((p) => ({ ...p, active: v }))} label=\"Actief\" />\n {existing && (\n <span className=\"text-sm text-slate-500\">{existing.usedCount ?? 0}× gebruikt</span>\n )}\n </div>\n\n <div className=\"flex items-center justify-between border-t border-slate-100 pt-4\">\n <Button onClick={save} disabled={!def.code}>Opslaan</Button>\n {existing && <Button variant=\"danger\" onClick={remove}>Verwijderen</Button>}\n </div>\n </div>\n </Card>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/admin/shop/DiscountsTab.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { collection, deleteDoc, doc, onSnapshot, setDoc } from \"firebase/firestore\";\nimport { getDb } from \"../../firebase/client\";\nimport { DiscountCodeSchema, formatCents, type DiscountCode } from \"../../schema/shop\";\nimport { Badge, Button, Card, Field, Input } from \"../ui/primitives\";\nimport { Select } from \"../ui/select\";\nimport { Switch } from \"../ui/switch\";\nimport { useToast } from \"../ui/toast\";\nimport { useConfirm } from \"../ui/ConfirmDialog\";\nimport { centsToEuroInput, eurosToCents } from \"./lib\";\nimport { zodFoutTekst } from \"../lib/zodFout\";\n\ntype Stored = DiscountCode & { id: string };\n\n/** Kortingscodes — doc-id = code; de webhook telt het gebruik op. */\nexport function DiscountsTab({ siteId }: { siteId: string }) {\n const { toast } = useToast();\n const confirm = useConfirm();\n const [codes, setCodes] = useState<Stored[]>([]);\n const [selected, setSelected] = useState<string | null>(null);\n\n useEffect(() => {\n const db = getDb();\n if (!db) return;\n return onSnapshot(collection(db, \"sites\", siteId, \"discountCodes\"), (snap) =>\n setCodes(snap.docs.map((d) => ({ id: d.id, ...(d.data() as DiscountCode) })).sort((a, b) => a.code.localeCompare(b.code))),\n (e) => console.warn(\"[xicms] live-lijst niet geladen:\", e.message),\n );\n }, [siteId]);\n\n const current = codes.find((c) => c.id === selected) ?? null;\n\n return (\n <div className=\"grid gap-4 md:grid-cols-[280px_1fr]\">\n <Card className=\"h-fit\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h3 className=\"text-sm font-semibold\">Kortingscodes</h3>\n <Button size=\"sm\" onClick={() => setSelected(\"__new__\")}>+ Nieuw</Button>\n </div>\n <ul className=\"grid gap-1\">\n {codes.map((c) => (\n <li key={c.id}>\n <button\n onClick={() => setSelected(c.id)}\n className={`flex w-full items-center justify-between gap-2 rounded-lg px-3 py-2 text-left text-sm ${\n c.id === selected ? \"bg-brand-50 text-brand-700\" : \"hover:bg-slate-100\"\n }`}\n >\n <span className=\"font-mono font-semibold\">{c.code}</span>\n <span className=\"flex items-center gap-1\">\n <span className=\"text-xs text-slate-500\">\n {c.type === \"percent\" ? `${c.value}%` : formatCents(c.value)}\n </span>\n <Badge tone={c.active !== false ? \"green\" : \"slate\"}>{c.active !== false ? \"actief\" : \"uit\"}</Badge>\n </span>\n </button>\n </li>\n ))}\n {codes.length === 0 && <li className=\"px-3 py-2 text-sm text-slate-500\">Nog geen kortingscodes.</li>}\n </ul>\n </Card>\n {current || selected === \"__new__\" ? (\n <DiscountDetail\n key={selected}\n siteId={siteId}\n existing={current}\n onDone={() => setSelected(null)}\n onToast={toast}\n confirm={confirm}\n />\n ) : (\n <Card><p className=\"text-sm text-slate-500\">Selecteer of maak een kortingscode.</p></Card>\n )}\n </div>\n );\n}\n\nfunction DiscountDetail({\n siteId,\n existing,\n onDone,\n onToast,\n confirm,\n}: {\n siteId: string;\n existing: Stored | null;\n onDone: () => void;\n onToast: (t: { title: string; description?: string; tone?: \"default\" | \"success\" | \"error\" }) => void;\n confirm: (opts: { title: string; description?: string; tone?: \"default\" | \"danger\" }) => Promise<boolean>;\n}) {\n const [def, setDef] = useState<DiscountCode>(\n existing ?? { code: \"\", type: \"percent\", value: 10, minOrderCents: 0, freeShipping: false, active: true, maxUses: 0, usedCount: 0, oncePerCustomer: false },\n );\n const [fixedInput, setFixedInput] = useState(existing?.type === \"fixed\" ? centsToEuroInput(existing.value) : \"\");\n const [minInput, setMinInput] = useState(existing?.minOrderCents ? centsToEuroInput(existing.minOrderCents) : \"\");\n\n async function save() {\n const db = getDb();\n if (!db) return;\n const value = def.type === \"fixed\" ? (eurosToCents(fixedInput) ?? 0) : def.value;\n const parsed = DiscountCodeSchema.safeParse({\n ...def,\n code: def.code.toUpperCase(),\n value,\n minOrderCents: minInput.trim() === \"\" ? 0 : (eurosToCents(minInput) ?? 0),\n });\n if (!parsed.success) {\n onToast({ title: \"Ongeldige code\", description: zodFoutTekst(parsed.error.issues[0]), tone: \"error\" });\n return;\n }\n await setDoc(doc(db, \"sites\", siteId, \"discountCodes\", parsed.data.code), parsed.data);\n onToast({ title: \"Kortingscode opgeslagen\", tone: \"success\" });\n if (!existing) onDone();\n }\n\n async function remove() {\n const db = getDb();\n if (!db || !existing) return;\n const ok = await confirm({ title: `Code ${existing.code} verwijderen?`, tone: \"danger\" });\n if (!ok) return;\n await deleteDoc(doc(db, \"sites\", siteId, \"discountCodes\", existing.id));\n onDone();\n }\n\n return (\n <Card>\n <div className=\"grid gap-4\">\n <div className=\"grid gap-4 md:grid-cols-3\">\n <Field label=\"Code\" hint=\"Hoofdletters, cijfers, streepjes\">\n <Input\n value={def.code}\n disabled={Boolean(existing)}\n onChange={(e) => setDef((p) => ({ ...p, code: e.target.value.toUpperCase().replace(/[^A-Z0-9-]/g, \"\") }))}\n placeholder=\"WELKOM10\"\n />\n </Field>\n <Field label=\"Soort\">\n <Select\n value={def.type}\n onValueChange={(v) => setDef((p) => ({ ...p, type: v as \"percent\" | \"fixed\" }))}\n options={[\n { value: \"percent\", label: \"Percentage\" },\n { value: \"fixed\", label: \"Vast bedrag\" },\n ]}\n />\n </Field>\n {def.type === \"percent\" ? (\n <Field label=\"Percentage\">\n <Input\n type=\"number\"\n min={1}\n max={100}\n value={def.value}\n onChange={(e) => setDef((p) => ({ ...p, value: Math.max(1, Math.min(100, Number(e.target.value) || 0)) }))}\n className=\"w-24\"\n />\n </Field>\n ) : (\n <Field label=\"Bedrag (€)\">\n <Input value={fixedInput} onChange={(e) => setFixedInput(e.target.value)} placeholder=\"5,00\" className=\"w-24\" />\n </Field>\n )}\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-3\">\n <Field label=\"Minimaal orderbedrag (€)\" hint=\"Leeg = geen minimum\">\n <Input value={minInput} onChange={(e) => setMinInput(e.target.value)} placeholder=\"25,00\" className=\"w-28\" />\n </Field>\n <Field label=\"Geldig van\" hint=\"Leeg = direct\">\n <Input type=\"date\" value={def.startsAt ?? \"\"} onChange={(e) => setDef((p) => ({ ...p, startsAt: e.target.value || undefined }))} />\n </Field>\n <Field label=\"Geldig t/m\" hint=\"Leeg = onbeperkt\">\n <Input type=\"date\" value={def.endsAt ?? \"\"} onChange={(e) => setDef((p) => ({ ...p, endsAt: e.target.value || undefined }))} />\n </Field>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-6\">\n <Field label=\"Max. aantal keer te gebruiken\" hint=\"0 = onbeperkt\">\n <Input\n type=\"number\"\n min={0}\n value={def.maxUses ?? 0}\n onChange={(e) => setDef((p) => ({ ...p, maxUses: Math.max(0, Number(e.target.value) || 0) }))}\n className=\"w-24\"\n />\n </Field>\n <Switch checked={def.freeShipping === true} onCheckedChange={(v) => setDef((p) => ({ ...p, freeShipping: v }))} label=\"Gratis verzending\" />\n <Switch\n checked={def.oncePerCustomer === true}\n onCheckedChange={(v) => setDef((p) => ({ ...p, oncePerCustomer: v }))}\n label=\"Eén keer per klant (op e-mailadres)\"\n />\n <Switch checked={def.active !== false} onCheckedChange={(v) => setDef((p) => ({ ...p, active: v }))} label=\"Actief\" />\n {existing && (\n <span className=\"text-sm text-slate-500\">{existing.usedCount ?? 0}× gebruikt</span>\n )}\n </div>\n\n <div className=\"flex items-center justify-between border-t border-slate-100 pt-4\">\n <Button onClick={save} disabled={!def.code}>Opslaan</Button>\n {existing && <Button variant=\"danger\" onClick={remove}>Verwijderen</Button>}\n </div>\n </div>\n </Card>\n );\n}\n"],"mappings":"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","names":[]}