@xiwebdesign/xicms-core 6.5.0 → 7.1.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 (381) hide show
  1. package/dist/SiteChrome.d.ts +12 -0
  2. package/dist/SiteChrome.js +24 -0
  3. package/dist/SiteChrome.js.map +1 -0
  4. package/dist/XiCMSProvider.d.ts +16 -8
  5. package/dist/XiCMSProvider.js +90 -61
  6. package/dist/XiCMSProvider.js.map +1 -1
  7. package/dist/admin/AdminApp.d.ts +1 -1
  8. package/dist/admin/AdminApp.js +81 -35
  9. package/dist/admin/AdminApp.js.map +1 -1
  10. package/dist/admin/AdminHelp.d.ts +8 -1
  11. package/dist/admin/AdminHelp.js +2 -2
  12. package/dist/admin/AdminHelp.js.map +1 -1
  13. package/dist/admin/CoreVersion.d.ts +13 -0
  14. package/dist/admin/CoreVersion.js +38 -0
  15. package/dist/admin/CoreVersion.js.map +1 -0
  16. package/dist/admin/NewsEditor.js +9 -9
  17. package/dist/admin/SuperadminPanel.js +5 -5
  18. package/dist/admin/blockEditors/GenericEditor.d.ts +2 -2
  19. package/dist/admin/blockEditors/GenericEditor.js +12 -11
  20. package/dist/admin/blockEditors/GenericEditor.js.map +1 -1
  21. package/dist/admin/blockEditors/StyleEditor.d.ts +1 -1
  22. package/dist/admin/blockEditors/StyleEditor.js +2 -2
  23. package/dist/admin/blockEditors/SymbolRefEditor.d.ts +1 -1
  24. package/dist/admin/blockEditors/SymbolRefEditor.js +5 -5
  25. package/dist/admin/blockEditors/index.d.ts +1 -1
  26. package/dist/admin/blockEditors/index.js +10 -10
  27. package/dist/admin/customBlocks/CustomBlocksPanel.js +7 -7
  28. package/dist/admin/editors/RichTextEditor.js +8 -7
  29. package/dist/admin/editors/RichTextEditor.js.map +1 -1
  30. package/dist/admin/editors/fontSizeExtension.js +1 -1
  31. package/dist/admin/editors/fontSizeExtension.js.map +1 -1
  32. package/dist/admin/forms/FormsPanel.js +9 -9
  33. package/dist/admin/index.d.ts +16 -16
  34. package/dist/admin/index.js +16 -16
  35. package/dist/admin/lib/mediaUsage.js +1 -1
  36. package/dist/admin/lib/pagesClient.d.ts +1 -1
  37. package/dist/admin/lib/pagesClient.js +11 -7
  38. package/dist/admin/lib/pagesClient.js.map +1 -1
  39. package/dist/admin/lib/sanitizeDraft.d.ts +9 -0
  40. package/dist/admin/lib/sanitizeDraft.js +28 -0
  41. package/dist/admin/lib/sanitizeDraft.js.map +1 -0
  42. package/dist/admin/lib/symbolsClient.d.ts +1 -1
  43. package/dist/admin/lib/symbolsClient.js +3 -3
  44. package/dist/admin/lib/upload.js +3 -3
  45. package/dist/admin/lib/useNestedDialog.d.ts +15 -0
  46. package/dist/admin/lib/useNestedDialog.js +23 -0
  47. package/dist/admin/lib/useNestedDialog.js.map +1 -0
  48. package/dist/admin/lib/zodFields.d.ts +4 -18
  49. package/dist/admin/lib/zodFields.js +54 -35
  50. package/dist/admin/lib/zodFields.js.map +1 -1
  51. package/dist/admin/media/MediaManager.js +7 -7
  52. package/dist/admin/media/MediaPicker.js +7 -7
  53. package/dist/admin/media/MediaPicker.js.map +1 -1
  54. package/dist/admin/pages/PageEditor.d.ts +1 -1
  55. package/dist/admin/pages/PageEditor.js +13 -13
  56. package/dist/admin/pages/PagesPanel.js +30 -30
  57. package/dist/admin/pages/PagesPanel.js.map +1 -1
  58. package/dist/admin/panelRegistry.js +7 -7
  59. package/dist/admin/patterns/PatternInserter.d.ts +1 -1
  60. package/dist/admin/patterns/PatternInserter.js +5 -5
  61. package/dist/admin/patterns/PatternInserter.js.map +1 -1
  62. package/dist/admin/settings/MailSettings.js +5 -5
  63. package/dist/admin/settings/MailTemplates.d.ts +1 -1
  64. package/dist/admin/settings/MailTemplates.js +5 -5
  65. package/dist/admin/settings/SettingsPanel.js +66 -20
  66. package/dist/admin/settings/SettingsPanel.js.map +1 -1
  67. package/dist/admin/settings/SnapshotsCard.js +5 -5
  68. package/dist/admin/settings/moduleSettingsForm.d.ts +1 -1
  69. package/dist/admin/settings/moduleSettingsForm.js +4 -4
  70. package/dist/admin/settings/moduleSettingsForm.js.map +1 -1
  71. package/dist/admin/shop/CategoriesTab.js +8 -8
  72. package/dist/admin/shop/ConnectionsTab.js +3 -3
  73. package/dist/admin/shop/DiscountsTab.js +8 -8
  74. package/dist/admin/shop/OrdersTab.js +10 -10
  75. package/dist/admin/shop/OrdersTab.js.map +1 -1
  76. package/dist/admin/shop/ProductsTab.js +10 -10
  77. package/dist/admin/shop/ShopPanel.js +9 -9
  78. package/dist/admin/shop/ShopSettingsTab.js +4 -4
  79. package/dist/admin/shop/index.d.ts +2 -2
  80. package/dist/admin/shop/index.js +2 -2
  81. package/dist/admin/shop/lib.js +2 -2
  82. package/dist/admin/stats/StatsPanel.js +2 -2
  83. package/dist/admin/ui/ArrayEditor.js +1 -1
  84. package/dist/admin/ui/PanelIcon.d.ts +3 -0
  85. package/dist/admin/ui/PanelIcon.js +34 -0
  86. package/dist/admin/ui/PanelIcon.js.map +1 -0
  87. package/dist/admin/ui/dialog.d.ts +21 -6
  88. package/dist/admin/ui/dialog.js +55 -34
  89. package/dist/admin/ui/dialog.js.map +1 -1
  90. package/dist/admin/ui/index.d.ts +7 -7
  91. package/dist/admin/ui/index.js +7 -7
  92. package/dist/admin/ui/primitives.js +1 -1
  93. package/dist/admin/ui/tabs.js +1 -1
  94. package/dist/admin/visual/TranslationPanel.d.ts +1 -1
  95. package/dist/admin/visual/TranslationPanel.js +3 -3
  96. package/dist/admin/visual/VisualEditor.js +47 -25
  97. package/dist/admin/visual/VisualEditor.js.map +1 -1
  98. package/dist/admin.css +2 -1
  99. package/dist/agentFiles/catalog.d.ts +2 -2
  100. package/dist/agentFiles/catalog.js +6 -5
  101. package/dist/agentFiles/catalog.js.map +1 -1
  102. package/dist/agentFiles/files.d.ts +5 -5
  103. package/dist/agentFiles/files.js +17 -17
  104. package/dist/agentFiles/files.js.map +1 -1
  105. package/dist/all.d.ts +4 -4
  106. package/dist/all.js +6 -6
  107. package/dist/auth/AuthProvider.d.ts +2 -2
  108. package/dist/auth/AuthProvider.js +51 -28
  109. package/dist/auth/AuthProvider.js.map +1 -1
  110. package/dist/auth/LoginForm.js +1 -1
  111. package/dist/auth/roles.js +4 -1
  112. package/dist/auth/roles.js.map +1 -1
  113. package/dist/blocks/BlockRenderer.d.ts +1 -1
  114. package/dist/blocks/BlockRenderer.js +3 -3
  115. package/dist/blocks/blockStyle.d.ts +1 -1
  116. package/dist/blocks/builtins/Accordion.d.ts +1 -1
  117. package/dist/blocks/builtins/Accordion.js +13 -6
  118. package/dist/blocks/builtins/Accordion.js.map +1 -1
  119. package/dist/blocks/builtins/Agenda.d.ts +1 -1
  120. package/dist/blocks/builtins/Agenda.js +5 -5
  121. package/dist/blocks/builtins/Banner.d.ts +1 -1
  122. package/dist/blocks/builtins/Banner.js +2 -2
  123. package/dist/blocks/builtins/BeforeAfter.d.ts +1 -1
  124. package/dist/blocks/builtins/BeforeAfter.js +4 -4
  125. package/dist/blocks/builtins/BeforeAfter.js.map +1 -1
  126. package/dist/blocks/builtins/Booking.d.ts +1 -1
  127. package/dist/blocks/builtins/Booking.js +5 -5
  128. package/dist/blocks/builtins/Breadcrumbs.d.ts +1 -1
  129. package/dist/blocks/builtins/Breadcrumbs.js +3 -3
  130. package/dist/blocks/builtins/Cards.d.ts +1 -1
  131. package/dist/blocks/builtins/Cards.js +5 -5
  132. package/dist/blocks/builtins/Cards.js.map +1 -1
  133. package/dist/blocks/builtins/Columns.d.ts +1 -1
  134. package/dist/blocks/builtins/Columns.js +4 -4
  135. package/dist/blocks/builtins/Contact.d.ts +3 -3
  136. package/dist/blocks/builtins/Contact.js +4 -4
  137. package/dist/blocks/builtins/Countdown.d.ts +1 -1
  138. package/dist/blocks/builtins/Countdown.js +4 -4
  139. package/dist/blocks/builtins/Countdown.js.map +1 -1
  140. package/dist/blocks/builtins/Cta.d.ts +1 -1
  141. package/dist/blocks/builtins/Cta.js +3 -3
  142. package/dist/blocks/builtins/Downloads.d.ts +1 -1
  143. package/dist/blocks/builtins/Downloads.js +4 -4
  144. package/dist/blocks/builtins/Elements.d.ts +3 -3
  145. package/dist/blocks/builtins/Elements.js +3 -3
  146. package/dist/blocks/builtins/Embed.d.ts +1 -1
  147. package/dist/blocks/builtins/Embed.js +3 -3
  148. package/dist/blocks/builtins/Faq.d.ts +1 -1
  149. package/dist/blocks/builtins/Faq.js +14 -6
  150. package/dist/blocks/builtins/Faq.js.map +1 -1
  151. package/dist/blocks/builtins/FormBlock.d.ts +1 -1
  152. package/dist/blocks/builtins/FormBlock.js +28 -18
  153. package/dist/blocks/builtins/FormBlock.js.map +1 -1
  154. package/dist/blocks/builtins/Gallery.d.ts +2 -2
  155. package/dist/blocks/builtins/Gallery.js +5 -5
  156. package/dist/blocks/builtins/Hero.d.ts +1 -1
  157. package/dist/blocks/builtins/Hero.js +4 -4
  158. package/dist/blocks/builtins/ImageBlock.d.ts +1 -1
  159. package/dist/blocks/builtins/ImageBlock.js +3 -3
  160. package/dist/blocks/builtins/Jobs.d.ts +2 -2
  161. package/dist/blocks/builtins/Jobs.js +5 -5
  162. package/dist/blocks/builtins/Layout.d.ts +2 -2
  163. package/dist/blocks/builtins/Layout.js +4 -4
  164. package/dist/blocks/builtins/MapBlock.d.ts +1 -1
  165. package/dist/blocks/builtins/MapBlock.js +3 -3
  166. package/dist/blocks/builtins/NewsListBlock.d.ts +1 -1
  167. package/dist/blocks/builtins/NewsListBlock.js +4 -4
  168. package/dist/blocks/builtins/Newsletter.d.ts +1 -1
  169. package/dist/blocks/builtins/Newsletter.js +7 -7
  170. package/dist/blocks/builtins/Newsletter.js.map +1 -1
  171. package/dist/blocks/builtins/OpenNowBadge.d.ts +1 -1
  172. package/dist/blocks/builtins/OpenNowBadge.js +8 -3
  173. package/dist/blocks/builtins/OpenNowBadge.js.map +1 -1
  174. package/dist/blocks/builtins/OpeningHours.d.ts +1 -1
  175. package/dist/blocks/builtins/OpeningHours.js +6 -6
  176. package/dist/blocks/builtins/PdfBlock.d.ts +1 -1
  177. package/dist/blocks/builtins/PdfBlock.js +3 -3
  178. package/dist/blocks/builtins/Portfolio.d.ts +1 -1
  179. package/dist/blocks/builtins/Portfolio.js +5 -5
  180. package/dist/blocks/builtins/Pricing.d.ts +2 -2
  181. package/dist/blocks/builtins/Pricing.js +5 -5
  182. package/dist/blocks/builtins/Pricing.js.map +1 -1
  183. package/dist/blocks/builtins/RichTextBlock.d.ts +1 -1
  184. package/dist/blocks/builtins/RichTextBlock.js +2 -2
  185. package/dist/blocks/builtins/RichTextBlock.js.map +1 -1
  186. package/dist/blocks/builtins/Search.d.ts +1 -1
  187. package/dist/blocks/builtins/Search.js +9 -7
  188. package/dist/blocks/builtins/Search.js.map +1 -1
  189. package/dist/blocks/builtins/Sections.d.ts +4 -4
  190. package/dist/blocks/builtins/Sections.js +4 -4
  191. package/dist/blocks/builtins/Slider.d.ts +1 -1
  192. package/dist/blocks/builtins/Slider.js +3 -3
  193. package/dist/blocks/builtins/Spacer.d.ts +1 -1
  194. package/dist/blocks/builtins/Spacer.js +2 -2
  195. package/dist/blocks/builtins/SymbolRef.d.ts +1 -1
  196. package/dist/blocks/builtins/SymbolRef.js +31 -17
  197. package/dist/blocks/builtins/SymbolRef.js.map +1 -1
  198. package/dist/blocks/builtins/Team.d.ts +1 -1
  199. package/dist/blocks/builtins/Team.js +4 -4
  200. package/dist/blocks/builtins/Testimonials.d.ts +2 -2
  201. package/dist/blocks/builtins/Testimonials.js +4 -4
  202. package/dist/blocks/builtins/Video.d.ts +2 -2
  203. package/dist/blocks/builtins/Video.js +4 -4
  204. package/dist/blocks/builtins/Video.js.map +1 -1
  205. package/dist/blocks/builtins/WhatsappChat.d.ts +1 -1
  206. package/dist/blocks/builtins/WhatsappChat.js +2 -2
  207. package/dist/blocks/builtins/Widgets.d.ts +12 -12
  208. package/dist/blocks/builtins/Widgets.js +19 -19
  209. package/dist/blocks/builtins/Widgets.js.map +1 -1
  210. package/dist/blocks/builtins/consentEmbed.js +2 -2
  211. package/dist/blocks/builtins/index.d.ts +40 -40
  212. package/dist/blocks/builtins/index.js +40 -40
  213. package/dist/blocks/builtins/lib/submitForm.js +5 -3
  214. package/dist/blocks/builtins/lib/submitForm.js.map +1 -1
  215. package/dist/blocks/context.d.ts +1 -1
  216. package/dist/blocks/customTemplate/definition.d.ts +3 -3
  217. package/dist/blocks/customTemplate/definition.js +6 -6
  218. package/dist/blocks/customTemplate/definition.js.map +1 -1
  219. package/dist/blocks/customTemplate/interpret.js +1 -1
  220. package/dist/blocks/index.d.ts +5 -5
  221. package/dist/blocks/index.js +5 -5
  222. package/dist/blocks/registry.d.ts +17 -7
  223. package/dist/blocks/registry.js +2 -1
  224. package/dist/blocks/registry.js.map +1 -1
  225. package/dist/components/EditBar.js +3 -3
  226. package/dist/components/FeedbackWidget.js +4 -4
  227. package/dist/components/News.d.ts +1 -1
  228. package/dist/components/News.js +50 -37
  229. package/dist/components/News.js.map +1 -1
  230. package/dist/components/PageView.js +11 -8
  231. package/dist/components/PageView.js.map +1 -1
  232. package/dist/components/ResponsiveImage.d.ts +1 -1
  233. package/dist/components/site/SiteFooter.d.ts +1 -1
  234. package/dist/components/site/SiteFooter.js +2 -2
  235. package/dist/config.d.ts +1 -1
  236. package/dist/demo/DemoStudio.js +3 -3
  237. package/dist/demo/demoStore.d.ts +3 -3
  238. package/dist/demo/index.d.ts +2 -2
  239. package/dist/demo/index.js +2 -2
  240. package/dist/editor/EditorProvider.js +1 -1
  241. package/dist/firebase/admin.d.ts +0 -2
  242. package/dist/firebase/admin.js +0 -6
  243. package/dist/firebase/admin.js.map +1 -1
  244. package/dist/firebase/appcheck.d.ts +1 -1
  245. package/dist/firebase/appcheck.js +5 -3
  246. package/dist/firebase/appcheck.js.map +1 -1
  247. package/dist/firebase/client.d.ts +2 -1
  248. package/dist/firebase/client.js +2 -14
  249. package/dist/firebase/client.js.map +1 -1
  250. package/dist/firebase/config.d.ts +31 -0
  251. package/dist/firebase/config.js +39 -0
  252. package/dist/firebase/config.js.map +1 -0
  253. package/dist/firebase/feedback.js +5 -3
  254. package/dist/firebase/feedback.js.map +1 -1
  255. package/dist/i18n/overlay.d.ts +2 -2
  256. package/dist/i18n/overlay.js +6 -4
  257. package/dist/i18n/overlay.js.map +1 -1
  258. package/dist/index.d.ts +46 -44
  259. package/dist/index.js +39 -33
  260. package/dist/index.js.map +1 -1
  261. package/dist/lock/DemoLock.js +1 -1
  262. package/dist/modules/builtins.d.ts +1 -1
  263. package/dist/modules/builtins.js +53 -55
  264. package/dist/modules/builtins.js.map +1 -1
  265. package/dist/modules/consent/ConsentBanner.js +1 -1
  266. package/dist/modules/consent/useConsent.js +1 -1
  267. package/dist/modules/context.d.ts +1 -1
  268. package/dist/modules/contract.d.ts +1 -1
  269. package/dist/modules/defineModule.d.ts +1 -1
  270. package/dist/modules/index.d.ts +9 -9
  271. package/dist/modules/index.js +8 -8
  272. package/dist/modules/overrides.d.ts +1 -1
  273. package/dist/modules/registry.d.ts +2 -7
  274. package/dist/modules/registry.js +2 -1
  275. package/dist/modules/registry.js.map +1 -1
  276. package/dist/modules/shop/blocks/Cart.d.ts +1 -1
  277. package/dist/modules/shop/blocks/Cart.js +6 -6
  278. package/dist/modules/shop/blocks/Checkout.d.ts +1 -1
  279. package/dist/modules/shop/blocks/Checkout.js +6 -6
  280. package/dist/modules/shop/blocks/Donation.d.ts +1 -1
  281. package/dist/modules/shop/blocks/Donation.js +5 -5
  282. package/dist/modules/shop/blocks/OrderStatus.d.ts +1 -1
  283. package/dist/modules/shop/blocks/OrderStatus.js +5 -5
  284. package/dist/modules/shop/blocks/ProductDetail.d.ts +1 -1
  285. package/dist/modules/shop/blocks/ProductDetail.js +8 -8
  286. package/dist/modules/shop/blocks/ProductGrid.d.ts +3 -3
  287. package/dist/modules/shop/blocks/ProductGrid.js +7 -7
  288. package/dist/modules/shop/blocks/ShopCategories.d.ts +1 -1
  289. package/dist/modules/shop/blocks/ShopCategories.js +6 -6
  290. package/dist/modules/shop/index.d.ts +5 -5
  291. package/dist/modules/shop/index.js +5 -5
  292. package/dist/modules/shop/lib.d.ts +2 -2
  293. package/dist/modules/shop/lib.js +18 -13
  294. package/dist/modules/shop/lib.js.map +1 -1
  295. package/dist/modules/shop/module.d.ts +1 -1
  296. package/dist/modules/shop/module.js +9 -9
  297. package/dist/modules/shop/settings.d.ts +18 -50
  298. package/dist/modules/shop/settings.js +16 -16
  299. package/dist/modules/shop/settings.js.map +1 -1
  300. package/dist/modules/shop/strings.js +1 -1
  301. package/dist/modules/shop/types.d.ts +1 -1
  302. package/dist/modules/types.d.ts +2 -2
  303. package/dist/patterns/builtins.d.ts +1 -1
  304. package/dist/patterns/builtins.js +1 -1
  305. package/dist/patterns/index.d.ts +2 -2
  306. package/dist/patterns/index.js +2 -2
  307. package/dist/render/PageRenderer.d.ts +2 -2
  308. package/dist/render/PageRenderer.js +1 -1
  309. package/dist/render/PublishedPage.d.ts +1 -1
  310. package/dist/render/PublishedPage.js +2 -2
  311. package/dist/schema/blocks.d.ts +6793 -15095
  312. package/dist/schema/blocks.js +335 -355
  313. package/dist/schema/blocks.js.map +1 -1
  314. package/dist/schema/customBlocks.d.ts +10 -29
  315. package/dist/schema/customBlocks.js +14 -13
  316. package/dist/schema/customBlocks.js.map +1 -1
  317. package/dist/schema/fields.d.ts +10 -16
  318. package/dist/schema/fields.js +37 -37
  319. package/dist/schema/fields.js.map +1 -1
  320. package/dist/schema/forms.d.ts +62 -111
  321. package/dist/schema/forms.js +17 -17
  322. package/dist/schema/forms.js.map +1 -1
  323. package/dist/schema/index.d.ts +7 -7
  324. package/dist/schema/index.js +7 -7
  325. package/dist/schema/mail.d.ts +39 -87
  326. package/dist/schema/mail.js +4 -4
  327. package/dist/schema/mail.js.map +1 -1
  328. package/dist/schema/pages.d.ts +152 -674
  329. package/dist/schema/pages.js +26 -15
  330. package/dist/schema/pages.js.map +1 -1
  331. package/dist/schema/seo.d.ts +9 -27
  332. package/dist/schema/seo.js +7 -7
  333. package/dist/schema/seo.js.map +1 -1
  334. package/dist/schema/settings.d.ts +233 -1074
  335. package/dist/schema/settings.js +71 -56
  336. package/dist/schema/settings.js.map +1 -1
  337. package/dist/schema/shop.d.ts +470 -1214
  338. package/dist/schema/shop.js +124 -124
  339. package/dist/schema/shop.js.map +1 -1
  340. package/dist/schema/siteConfig.d.ts +78 -597
  341. package/dist/schema/siteConfig.js +17 -17
  342. package/dist/schema/siteConfig.js.map +1 -1
  343. package/dist/schema/symbols.d.ts +34 -191
  344. package/dist/schema/symbols.js +8 -8
  345. package/dist/schema/symbols.js.map +1 -1
  346. package/dist/server/auth.d.ts +17 -0
  347. package/dist/server/auth.js +11 -0
  348. package/dist/server/auth.js.map +1 -0
  349. package/dist/server/bundle.d.ts +15 -6
  350. package/dist/server/bundle.js +5 -1
  351. package/dist/server/bundle.js.map +1 -1
  352. package/dist/server/draft.d.ts +16 -0
  353. package/dist/server/draft.js +12 -0
  354. package/dist/server/draft.js.map +1 -0
  355. package/dist/server/env.js +8 -8
  356. package/dist/server/env.js.map +1 -1
  357. package/dist/server/index.d.ts +11 -9
  358. package/dist/server/index.js +13 -10
  359. package/dist/server/index.js.map +1 -1
  360. package/dist/server/pages.d.ts +11 -4
  361. package/dist/server/pages.js +43 -15
  362. package/dist/server/pages.js.map +1 -1
  363. package/dist/server/revalidate.js +11 -2
  364. package/dist/server/revalidate.js.map +1 -1
  365. package/dist/server/seo.d.ts +3 -3
  366. package/dist/server/seo.js +1 -1
  367. package/dist/server/shop.d.ts +3 -3
  368. package/dist/server/shop.js +6 -3
  369. package/dist/server/shop.js.map +1 -1
  370. package/dist/server/version.d.ts +16 -0
  371. package/dist/server/version.js +17 -0
  372. package/dist/server/version.js.map +1 -0
  373. package/dist/settings/useSiteSettings.d.ts +2 -2
  374. package/dist/settings/useSiteSettings.js +48 -28
  375. package/dist/settings/useSiteSettings.js.map +1 -1
  376. package/dist/styles.css +30 -4
  377. package/dist/theme/presets.d.ts +1 -1
  378. package/dist/version.d.ts +8 -0
  379. package/dist/version.js +16 -0
  380. package/dist/version.js.map +1 -0
  381. package/package.json +35 -34
@@ -2,12 +2,12 @@
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useMemo, useRef, useState } from "react";
4
4
  import { collection, limit, onSnapshot, orderBy, query } from "firebase/firestore";
5
- import { getDb } from "../../firebase/client";
6
- import { DEMO_EVENT, isDemoActive, listDemoMediaAll } from "../../demo/demoStore";
7
- import { Button, Input } from "../ui/primitives";
8
- import { Dialog, DialogContent } from "../ui/dialog";
9
- import { useToast } from "../ui/toast";
10
- import { uploadImage } from "../lib/upload";
5
+ import { getDb } from "../../firebase/client.js";
6
+ import { DEMO_EVENT, isDemoActive, listDemoMediaAll } from "../../demo/demoStore.js";
7
+ import { Button, Input } from "../ui/primitives.js";
8
+ import { Dialog, DialogContent } from "../ui/dialog.js";
9
+ import { useToast } from "../ui/toast.js";
10
+ import { uploadImage } from "../lib/upload.js";
11
11
  const PICKER_PAGE_SIZE = 48;
12
12
  function MediaPickerDialog({
13
13
  siteId,
@@ -68,7 +68,7 @@ function MediaPickerDialog({
68
68
  setBusy(false);
69
69
  }
70
70
  }
71
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { title: "Afbeelding kiezen", className: "max-w-2xl", children: [
71
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { title: "Afbeelding kiezen", width: "min(92vw, 42rem)", children: [
72
72
  /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center gap-2", children: [
73
73
  /* @__PURE__ */ jsx(Input, { value: q, onChange: (e) => setQ(e.target.value), placeholder: "Zoek in bibliotheek\u2026", className: "h-9" }),
74
74
  /* @__PURE__ */ jsx(Button, { type: "button", disabled: busy, onClick: () => inputRef.current?.click(), children: busy ? "Uploaden\u2026" : "Upload nieuwe" }),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/admin/media/MediaPicker.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { collection, limit, onSnapshot, orderBy, query } from \"firebase/firestore\";\nimport { getDb } from \"../../firebase/client\";\nimport { DEMO_EVENT, isDemoActive, listDemoMediaAll } from \"../../demo/demoStore\";\nimport type { MediaItem } from \"../../content/types\";\nimport { Button, Input } from \"../ui/primitives\";\nimport { Dialog, DialogContent } from \"../ui/dialog\";\nimport { useToast } from \"../ui/toast\";\nimport { uploadImage } from \"../lib/upload\";\n\nexport interface PickedMedia {\n url: string;\n mediaId: string;\n alt?: string;\n}\n\n/** Paginagrootte: begrensd laden i.p.v. de hele bibliotheek bij elke kiezer-open. */\nconst PICKER_PAGE_SIZE = 48;\n\n/** Gecontroleerde mediabibliotheek-kiezer (open/onOpenChange extern) — zoek + klik om te kiezen, of\n * upload direct. Gebruikt door de knop hieronder én door de inline beeld-vervangen in de editor. */\nexport function MediaPickerDialog({\n siteId,\n open,\n onOpenChange,\n onSelect,\n}: {\n siteId: string;\n open: boolean;\n onOpenChange: (v: boolean) => void;\n onSelect: (m: PickedMedia) => void;\n}) {\n const { toast } = useToast();\n const [items, setItems] = useState<MediaItem[]>([]);\n const [q, setQ] = useState(\"\");\n const [busy, setBusy] = useState(false);\n const [pageCount, setPageCount] = useState(1);\n const [hasMore, setHasMore] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n if (!open) return;\n // Demo-modus: lokale bibliotheek (geseede foto's + eigen \"uploads\") i.p.v. Firestore.\n if (isDemoActive()) {\n const sync = () => setItems(listDemoMediaAll(siteId));\n sync();\n window.addEventListener(DEMO_EVENT, sync);\n return () => window.removeEventListener(DEMO_EVENT, sync);\n }\n const db = getDb();\n if (!db) return;\n // Begrensd laden (+\"meer laden\"): geen volledige collectie-read bij elke kiezer-open.\n const max = pageCount * PICKER_PAGE_SIZE;\n const qy = query(collection(db, \"sites\", siteId, \"media\"), orderBy(\"createdAt\", \"desc\"), limit(max + 1));\n return onSnapshot(\n qy,\n (snap) => {\n setHasMore(snap.docs.length > max);\n setItems(snap.docs.slice(0, max).map((d) => ({ id: d.id, ...(d.data() as Omit<MediaItem, \"id\">) })));\n },\n // Zonder onError worden lees-/rules-/App-Check-fouten stil geslikt → de kiezer toont dan\n // onterecht \"Nog geen afbeeldingen\". Nu een duidelijke melding.\n (err) => toast({ title: \"Kon mediabibliotheek niet laden\", description: err.message, tone: \"error\" }),\n );\n }, [open, siteId, toast, pageCount]);\n\n const filtered = useMemo(() => {\n const term = q.trim().toLowerCase();\n if (!term) return items;\n return items.filter((m) => `${m.name ?? \"\"} ${m.alt ?? \"\"}`.toLowerCase().includes(term));\n }, [items, q]);\n\n function pick(m: MediaItem) {\n onSelect({ url: m.url, mediaId: m.id, alt: m.alt });\n onOpenChange(false);\n }\n\n async function onUpload(files: FileList | null) {\n const f = files?.[0];\n if (!f) return;\n setBusy(true);\n try {\n const { url, mediaId } = await uploadImage(siteId, f);\n onSelect({ url, mediaId, alt: \"\" });\n onOpenChange(false);\n } catch (err) {\n toast({ title: \"Upload mislukt\", description: (err as Error).message, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent title=\"Afbeelding kiezen\" className=\"max-w-2xl\">\n <div className=\"mb-3 flex items-center gap-2\">\n <Input value={q} onChange={(e) => setQ(e.target.value)} placeholder=\"Zoek in bibliotheek…\" className=\"h-9\" />\n <Button type=\"button\" disabled={busy} onClick={() => inputRef.current?.click()}>\n {busy ? \"Uploaden…\" : \"Upload nieuwe\"}\n </Button>\n <input ref={inputRef} type=\"file\" accept=\"image/*\" hidden onChange={(e) => { void onUpload(e.target.files); e.target.value = \"\"; }} />\n </div>\n <div className=\"max-h-[55vh] overflow-y-auto\">\n {filtered.length === 0 ? (\n <p className=\"py-8 text-center text-sm text-slate-500\">\n {items.length === 0 ? \"Nog geen afbeeldingen — upload er een.\" : \"Geen resultaten.\"}\n </p>\n ) : (\n <div className=\"grid grid-cols-3 gap-2 sm:grid-cols-4\">\n {filtered.map((m) => (\n <button\n key={m.id}\n type=\"button\"\n onClick={() => pick(m)}\n title={m.alt || m.name}\n className=\"group relative overflow-hidden rounded-md border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500\"\n >\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img src={m.url} alt={m.alt ?? m.name} className=\"aspect-square w-full object-cover transition group-hover:opacity-80\" />\n </button>\n ))}\n </div>\n )}\n {hasMore && !q.trim() && (\n <p className=\"mt-2 text-center\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => setPageCount((c) => c + 1)}>\n Meer laden…\n </Button>\n </p>\n )}\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n\n/**\n * Knop die de mediabibliotheek-kiezer opent: zoek + klik om een bestaande afbeelding te kiezen, of\n * upload er direct een nieuwe in. Vervangt het plakken van losse URL's.\n */\nexport function MediaPickerButton({\n siteId,\n onSelect,\n label = \"Kies / upload\",\n size = \"sm\",\n variant = \"secondary\",\n}: {\n siteId: string;\n onSelect: (m: PickedMedia) => void;\n label?: string;\n size?: \"sm\" | \"md\";\n variant?: \"primary\" | \"secondary\" | \"ghost\";\n}) {\n const [open, setOpen] = useState(false);\n return (\n <>\n <Button type=\"button\" variant={variant} size={size} onClick={() => setOpen(true)}>{label}</Button>\n <MediaPickerDialog siteId={siteId} open={open} onOpenChange={setOpen} onSelect={onSelect} />\n </>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/admin/media/MediaPicker.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { collection, limit, onSnapshot, orderBy, query } from \"firebase/firestore\";\nimport { getDb } from \"../../firebase/client\";\nimport { DEMO_EVENT, isDemoActive, listDemoMediaAll } from \"../../demo/demoStore\";\nimport type { MediaItem } from \"../../content/types\";\nimport { Button, Input } from \"../ui/primitives\";\nimport { Dialog, DialogContent } from \"../ui/dialog\";\nimport { useToast } from \"../ui/toast\";\nimport { uploadImage } from \"../lib/upload\";\n\nexport interface PickedMedia {\n url: string;\n mediaId: string;\n alt?: string;\n}\n\n/** Paginagrootte: begrensd laden i.p.v. de hele bibliotheek bij elke kiezer-open. */\nconst PICKER_PAGE_SIZE = 48;\n\n/** Gecontroleerde mediabibliotheek-kiezer (open/onOpenChange extern) — zoek + klik om te kiezen, of\n * upload direct. Gebruikt door de knop hieronder én door de inline beeld-vervangen in de editor. */\nexport function MediaPickerDialog({\n siteId,\n open,\n onOpenChange,\n onSelect,\n}: {\n siteId: string;\n open: boolean;\n onOpenChange: (v: boolean) => void;\n onSelect: (m: PickedMedia) => void;\n}) {\n const { toast } = useToast();\n const [items, setItems] = useState<MediaItem[]>([]);\n const [q, setQ] = useState(\"\");\n const [busy, setBusy] = useState(false);\n const [pageCount, setPageCount] = useState(1);\n const [hasMore, setHasMore] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n if (!open) return;\n // Demo-modus: lokale bibliotheek (geseede foto's + eigen \"uploads\") i.p.v. Firestore.\n if (isDemoActive()) {\n const sync = () => setItems(listDemoMediaAll(siteId));\n sync();\n window.addEventListener(DEMO_EVENT, sync);\n return () => window.removeEventListener(DEMO_EVENT, sync);\n }\n const db = getDb();\n if (!db) return;\n // Begrensd laden (+\"meer laden\"): geen volledige collectie-read bij elke kiezer-open.\n const max = pageCount * PICKER_PAGE_SIZE;\n const qy = query(collection(db, \"sites\", siteId, \"media\"), orderBy(\"createdAt\", \"desc\"), limit(max + 1));\n return onSnapshot(\n qy,\n (snap) => {\n setHasMore(snap.docs.length > max);\n setItems(snap.docs.slice(0, max).map((d) => ({ id: d.id, ...(d.data() as Omit<MediaItem, \"id\">) })));\n },\n // Zonder onError worden lees-/rules-/App-Check-fouten stil geslikt → de kiezer toont dan\n // onterecht \"Nog geen afbeeldingen\". Nu een duidelijke melding.\n (err) => toast({ title: \"Kon mediabibliotheek niet laden\", description: err.message, tone: \"error\" }),\n );\n }, [open, siteId, toast, pageCount]);\n\n const filtered = useMemo(() => {\n const term = q.trim().toLowerCase();\n if (!term) return items;\n return items.filter((m) => `${m.name ?? \"\"} ${m.alt ?? \"\"}`.toLowerCase().includes(term));\n }, [items, q]);\n\n function pick(m: MediaItem) {\n onSelect({ url: m.url, mediaId: m.id, alt: m.alt });\n onOpenChange(false);\n }\n\n async function onUpload(files: FileList | null) {\n const f = files?.[0];\n if (!f) return;\n setBusy(true);\n try {\n const { url, mediaId } = await uploadImage(siteId, f);\n onSelect({ url, mediaId, alt: \"\" });\n onOpenChange(false);\n } catch (err) {\n toast({ title: \"Upload mislukt\", description: (err as Error).message, tone: \"error\" });\n } finally {\n setBusy(false);\n }\n }\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent title=\"Afbeelding kiezen\" width=\"min(92vw, 42rem)\">\n <div className=\"mb-3 flex items-center gap-2\">\n <Input value={q} onChange={(e) => setQ(e.target.value)} placeholder=\"Zoek in bibliotheek…\" className=\"h-9\" />\n <Button type=\"button\" disabled={busy} onClick={() => inputRef.current?.click()}>\n {busy ? \"Uploaden…\" : \"Upload nieuwe\"}\n </Button>\n <input ref={inputRef} type=\"file\" accept=\"image/*\" hidden onChange={(e) => { void onUpload(e.target.files); e.target.value = \"\"; }} />\n </div>\n <div className=\"max-h-[55vh] overflow-y-auto\">\n {filtered.length === 0 ? (\n <p className=\"py-8 text-center text-sm text-slate-500\">\n {items.length === 0 ? \"Nog geen afbeeldingen — upload er een.\" : \"Geen resultaten.\"}\n </p>\n ) : (\n <div className=\"grid grid-cols-3 gap-2 sm:grid-cols-4\">\n {filtered.map((m) => (\n <button\n key={m.id}\n type=\"button\"\n onClick={() => pick(m)}\n title={m.alt || m.name}\n className=\"group relative overflow-hidden rounded-md border border-slate-200 focus:outline-none focus:ring-2 focus:ring-indigo-500\"\n >\n {/* eslint-disable-next-line @next/next/no-img-element */}\n <img src={m.url} alt={m.alt ?? m.name} className=\"aspect-square w-full object-cover transition group-hover:opacity-80\" />\n </button>\n ))}\n </div>\n )}\n {hasMore && !q.trim() && (\n <p className=\"mt-2 text-center\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => setPageCount((c) => c + 1)}>\n Meer laden…\n </Button>\n </p>\n )}\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n\n/**\n * Knop die de mediabibliotheek-kiezer opent: zoek + klik om een bestaande afbeelding te kiezen, of\n * upload er direct een nieuwe in. Vervangt het plakken van losse URL's.\n */\nexport function MediaPickerButton({\n siteId,\n onSelect,\n label = \"Kies / upload\",\n size = \"sm\",\n variant = \"secondary\",\n}: {\n siteId: string;\n onSelect: (m: PickedMedia) => void;\n label?: string;\n size?: \"sm\" | \"md\";\n variant?: \"primary\" | \"secondary\" | \"ghost\";\n}) {\n const [open, setOpen] = useState(false);\n return (\n <>\n <Button type=\"button\" variant={variant} size={size} onClick={() => setOpen(true)}>{label}</Button>\n <MediaPickerDialog siteId={siteId} open={open} onOpenChange={setOpen} onSelect={onSelect} />\n </>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1,4 +1,4 @@
1
- import { type DraftPage } from "../lib/pagesClient";
1
+ import { type DraftPage } from "../lib/pagesClient.js";
2
2
  export declare function PageEditor({ siteId, page, allPages }: {
3
3
  siteId: string;
4
4
  page: DraftPage;
@@ -1,19 +1,19 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useMemo, useRef, useState } from "react";
4
- import { useRegistry } from "../../blocks/context";
5
- import { buildBlockAddOptions } from "../../blocks/registry";
6
- import { useModules } from "../../modules/context";
7
- import { moduleIdForBlockType } from "../../modules/registry";
8
- import { Button, Card, Field, Input, Textarea, Badge } from "../ui/primitives";
9
- import { Select } from "../ui/select";
10
- import { useToast } from "../ui/toast";
11
- import { useConfirm } from "../ui/ConfirmDialog";
12
- import { builtinBlockEditors, GenericEditor } from "../blockEditors";
13
- import { StyleEditor } from "../blockEditors/StyleEditor";
14
- import { PatternInserter } from "../patterns/PatternInserter";
15
- import { saveDraft, publishPage, subscribeVersions, restoreVersion } from "../lib/pagesClient";
16
- import { slugify } from "../lib/slugify";
4
+ import { useRegistry } from "../../blocks/context.js";
5
+ import { buildBlockAddOptions } from "../../blocks/registry.js";
6
+ import { useModules } from "../../modules/context.js";
7
+ import { moduleIdForBlockType } from "../../modules/registry.js";
8
+ import { Button, Card, Field, Input, Textarea, Badge } from "../ui/primitives.js";
9
+ import { Select } from "../ui/select.js";
10
+ import { useToast } from "../ui/toast.js";
11
+ import { useConfirm } from "../ui/ConfirmDialog.js";
12
+ import { builtinBlockEditors, GenericEditor } from "../blockEditors/index.js";
13
+ import { StyleEditor } from "../blockEditors/StyleEditor.js";
14
+ import { PatternInserter } from "../patterns/PatternInserter.js";
15
+ import { saveDraft, publishPage, subscribeVersions, restoreVersion } from "../lib/pagesClient.js";
16
+ import { slugify } from "../lib/slugify.js";
17
17
  function newKey() {
18
18
  return (globalThis.crypto?.randomUUID?.() ?? `b${Date.now()}${Math.random()}`).slice(0, 12);
19
19
  }
@@ -1,12 +1,12 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useState } from "react";
4
- import { Button, Card, Field, Input, Badge } from "../ui/primitives";
5
- import { Dialog, DialogTrigger, DialogContent, DialogClose } from "../ui/dialog";
6
- import { useToast } from "../ui/toast";
7
- import { useConfirm } from "../ui/ConfirmDialog";
8
- import { subscribePages, createPage, deletePage } from "../lib/pagesClient";
9
- import { PageEditor } from "./PageEditor";
4
+ import { Button, Card, Field, Input, Badge } from "../ui/primitives.js";
5
+ import { Dialog, DialogTrigger, DialogContent, DialogClose } from "../ui/dialog.js";
6
+ import { useToast } from "../ui/toast.js";
7
+ import { useConfirm } from "../ui/ConfirmDialog.js";
8
+ import { subscribePages, createPage, deletePage } from "../lib/pagesClient.js";
9
+ import { PageEditor } from "./PageEditor.js";
10
10
  function slugify(s) {
11
11
  return s.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
12
12
  }
@@ -37,8 +37,8 @@ function PagesPanel({ siteId }) {
37
37
  }
38
38
  return /* @__PURE__ */ jsxs("div", { className: "grid gap-4 md:grid-cols-[260px_1fr]", children: [
39
39
  /* @__PURE__ */ jsxs(Card, { className: "h-fit", children: [
40
- /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center justify-between", children: [
41
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold", children: "Pagina's" }),
40
+ /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center justify-between gap-2", children: [
41
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium tracking-wide text-slate-500 uppercase", children: "Overzicht" }),
42
42
  /* @__PURE__ */ jsxs(Dialog, { children: [
43
43
  /* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { size: "sm", children: "+ Nieuw" }) }),
44
44
  /* @__PURE__ */ jsx(DialogContent, { title: "Nieuwe pagina", children: /* @__PURE__ */ jsxs("div", { className: "grid gap-3", children: [
@@ -50,27 +50,25 @@ function PagesPanel({ siteId }) {
50
50
  ] }) })
51
51
  ] })
52
52
  ] }),
53
- /* @__PURE__ */ jsxs("ul", { className: "grid gap-1", children: [
54
- pages.map((p) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
55
- "button",
56
- {
57
- onClick: () => setSelected(p.id),
58
- className: `flex w-full items-center justify-between rounded-lg px-3 py-2 text-left text-sm ${p.id === selected ? "bg-brand-50 text-brand-700" : "hover:bg-slate-100"}`,
59
- children: [
60
- /* @__PURE__ */ jsx("span", { className: "truncate", children: p.title }),
61
- p.published ? /* @__PURE__ */ jsx(Badge, { tone: "green", children: "live" }) : /* @__PURE__ */ jsx(Badge, { tone: "amber", children: "concept" })
62
- ]
63
- }
64
- ) }, p.id)),
65
- pages.length === 0 && /* @__PURE__ */ jsx("li", { className: "px-3 py-2 text-sm text-slate-500", children: "Nog geen pagina's." })
66
- ] })
53
+ /* @__PURE__ */ jsx("ul", { className: "grid gap-1", children: pages.map((p) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
54
+ "button",
55
+ {
56
+ onClick: () => setSelected(p.id),
57
+ className: `flex w-full items-center justify-between rounded-lg px-3 py-2 text-left text-sm ${p.id === selected ? "bg-brand-50 text-brand-700" : "hover:bg-slate-100"}`,
58
+ children: [
59
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: p.title }),
60
+ p.published ? /* @__PURE__ */ jsx(Badge, { tone: "green", children: "live" }) : /* @__PURE__ */ jsx(Badge, { tone: "amber", children: "concept" })
61
+ ]
62
+ }
63
+ ) }, p.id)) })
67
64
  ] }),
68
65
  /* @__PURE__ */ jsx("div", { children: current ? /* @__PURE__ */ jsxs(Fragment, { children: [
69
- /* @__PURE__ */ jsx("div", { className: "mb-3 flex justify-end", children: /* @__PURE__ */ jsx(
70
- Button,
66
+ /* @__PURE__ */ jsx(PageEditor, { siteId, page: current, allPages: pages }),
67
+ /* @__PURE__ */ jsx("div", { className: "mt-4 flex justify-end border-t border-slate-200 pt-4", children: /* @__PURE__ */ jsx(
68
+ "button",
71
69
  {
72
- variant: "danger",
73
- size: "sm",
70
+ type: "button",
71
+ className: "rounded px-2 py-1 text-sm text-red-600 hover:underline focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:outline-none",
74
72
  onClick: async () => {
75
73
  const ok = await confirm({
76
74
  title: `Pagina "${current.title}" verwijderen?`,
@@ -83,11 +81,13 @@ function PagesPanel({ siteId }) {
83
81
  setSelected(null);
84
82
  toast({ title: "Pagina verwijderd" });
85
83
  },
86
- children: "Verwijderen"
84
+ children: "Pagina verwijderen"
87
85
  }
88
- ) }),
89
- /* @__PURE__ */ jsx(PageEditor, { siteId, page: current, allPages: pages })
90
- ] }) : /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Selecteer of maak een pagina om te bewerken." }) }) })
86
+ ) })
87
+ ] }) : /* @__PURE__ */ jsx(Card, { className: "grid place-items-center py-16 text-center", children: /* @__PURE__ */ jsxs("div", { className: "max-w-sm", children: [
88
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-slate-700", children: pages.length === 0 ? "Nog geen pagina's" : "Geen pagina geselecteerd" }),
89
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-slate-500", children: pages.length === 0 ? "Maak je eerste pagina met + Nieuw. Wat je maakt blijft een concept tot je publiceert." : "Kies links een pagina om de tekst en secties aan te passen." })
90
+ ] }) }) })
91
91
  ] });
92
92
  }
93
93
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/admin/pages/PagesPanel.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { Button, Card, Field, Input, Badge } from \"../ui/primitives\";\nimport { Dialog, DialogTrigger, DialogContent, DialogClose } from \"../ui/dialog\";\nimport { useToast } from \"../ui/toast\";\nimport { useConfirm } from \"../ui/ConfirmDialog\";\nimport { subscribePages, createPage, deletePage, type DraftPage } from \"../lib/pagesClient\";\nimport { PageEditor } from \"./PageEditor\";\n\nfunction slugify(s: string): string {\n return s\n .toLowerCase()\n .normalize(\"NFD\")\n .replace(/[̀-ͯ]/g, \"\")\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/(^-|-$)/g, \"\");\n}\n\nexport function PagesPanel({ siteId }: { siteId: string }) {\n const { toast } = useToast();\n const confirm = useConfirm();\n const [pages, setPages] = useState<DraftPage[]>([]);\n const [selected, setSelected] = useState<string | null>(null);\n const [newTitle, setNewTitle] = useState(\"\");\n\n useEffect(() => subscribePages(siteId, setPages), [siteId]);\n useEffect(() => {\n if (!selected && pages.length) setSelected(pages[0].id);\n }, [pages, selected]);\n\n const current = pages.find((p) => p.id === selected) ?? null;\n\n async function create() {\n const title = newTitle.trim();\n if (!title) return;\n const slug = slugify(title) || \"pagina\";\n const path = slug === \"home\" ? \"/\" : `/${slug}`;\n try {\n const id = await createPage(siteId, { title, slug, path });\n setNewTitle(\"\");\n setSelected(id);\n toast({ title: \"Pagina aangemaakt\", description: path });\n } catch (e) {\n // De dialoog sluit sowieso (DialogClose) — zonder deze toast dacht de klant dat de pagina\n // bestond terwijl de write (rules/offline) faalde.\n toast({ title: \"Pagina aanmaken mislukt\", description: (e as Error).message, tone: \"error\" });\n }\n }\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\">Pagina's</h3>\n <Dialog>\n <DialogTrigger asChild>\n <Button size=\"sm\">+ Nieuw</Button>\n </DialogTrigger>\n <DialogContent title=\"Nieuwe pagina\">\n <div className=\"grid gap-3\">\n <Field label=\"Titel\">\n <Input value={newTitle} onChange={(e) => setNewTitle(e.target.value)} placeholder=\"bv. Over ons\" />\n </Field>\n <div className=\"flex justify-end gap-2\">\n <DialogClose asChild>\n <Button variant=\"secondary\">Annuleren</Button>\n </DialogClose>\n <DialogClose asChild>\n <Button onClick={create}>Aanmaken</Button>\n </DialogClose>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n </div>\n <ul className=\"grid gap-1\">\n {pages.map((p) => (\n <li key={p.id}>\n <button\n onClick={() => setSelected(p.id)}\n className={`flex w-full items-center justify-between rounded-lg px-3 py-2 text-left text-sm ${\n p.id === selected ? \"bg-brand-50 text-brand-700\" : \"hover:bg-slate-100\"\n }`}\n >\n <span className=\"truncate\">{p.title}</span>\n {p.published ? <Badge tone=\"green\">live</Badge> : <Badge tone=\"amber\">concept</Badge>}\n </button>\n </li>\n ))}\n {pages.length === 0 && <li className=\"px-3 py-2 text-sm text-slate-500\">Nog geen pagina's.</li>}\n </ul>\n </Card>\n\n <div>\n {current ? (\n <>\n <div className=\"mb-3 flex justify-end\">\n <Button\n variant=\"danger\"\n size=\"sm\"\n onClick={async () => {\n const ok = await confirm({\n title: `Pagina \"${current.title}\" verwijderen?`,\n description:\n \"De pagina en de bijbehorende werkversie worden verwijderd. Als de pagina gepubliceerd is, verdwijnt hij ook van de live site. Dit kan niet ongedaan worden gemaakt.\",\n confirmLabel: \"Verwijderen\",\n tone: \"danger\",\n });\n if (!ok) return;\n await deletePage(siteId, current.id);\n setSelected(null);\n toast({ title: \"Pagina verwijderd\" });\n }}\n >\n Verwijderen\n </Button>\n </div>\n <PageEditor siteId={siteId} page={current} allPages={pages} />\n </>\n ) : (\n <Card>\n <p className=\"text-sm text-slate-500\">Selecteer of maak een pagina om te bewerken.</p>\n </Card>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/admin/pages/PagesPanel.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { Button, Card, Field, Input, Badge } from \"../ui/primitives\";\nimport { Dialog, DialogTrigger, DialogContent, DialogClose } from \"../ui/dialog\";\nimport { useToast } from \"../ui/toast\";\nimport { useConfirm } from \"../ui/ConfirmDialog\";\nimport { subscribePages, createPage, deletePage, type DraftPage } from \"../lib/pagesClient\";\nimport { PageEditor } from \"./PageEditor\";\n\nfunction slugify(s: string): string {\n return s\n .toLowerCase()\n .normalize(\"NFD\")\n .replace(/[̀-ͯ]/g, \"\")\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/(^-|-$)/g, \"\");\n}\n\nexport function PagesPanel({ siteId }: { siteId: string }) {\n const { toast } = useToast();\n const confirm = useConfirm();\n const [pages, setPages] = useState<DraftPage[]>([]);\n const [selected, setSelected] = useState<string | null>(null);\n const [newTitle, setNewTitle] = useState(\"\");\n\n useEffect(() => subscribePages(siteId, setPages), [siteId]);\n useEffect(() => {\n if (!selected && pages.length) setSelected(pages[0].id);\n }, [pages, selected]);\n\n const current = pages.find((p) => p.id === selected) ?? null;\n\n async function create() {\n const title = newTitle.trim();\n if (!title) return;\n const slug = slugify(title) || \"pagina\";\n const path = slug === \"home\" ? \"/\" : `/${slug}`;\n try {\n const id = await createPage(siteId, { title, slug, path });\n setNewTitle(\"\");\n setSelected(id);\n toast({ title: \"Pagina aangemaakt\", description: path });\n } catch (e) {\n // De dialoog sluit sowieso (DialogClose) — zonder deze toast dacht de klant dat de pagina\n // bestond terwijl de write (rules/offline) faalde.\n toast({ title: \"Pagina aanmaken mislukt\", description: (e as Error).message, tone: \"error\" });\n }\n }\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 gap-2\">\n <span className=\"text-xs font-medium tracking-wide text-slate-500 uppercase\">Overzicht</span>\n <Dialog>\n <DialogTrigger asChild>\n <Button size=\"sm\">+ Nieuw</Button>\n </DialogTrigger>\n <DialogContent title=\"Nieuwe pagina\">\n <div className=\"grid gap-3\">\n <Field label=\"Titel\">\n <Input value={newTitle} onChange={(e) => setNewTitle(e.target.value)} placeholder=\"bv. Over ons\" />\n </Field>\n <div className=\"flex justify-end gap-2\">\n <DialogClose asChild>\n <Button variant=\"secondary\">Annuleren</Button>\n </DialogClose>\n <DialogClose asChild>\n <Button onClick={create}>Aanmaken</Button>\n </DialogClose>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n </div>\n <ul className=\"grid gap-1\">\n {pages.map((p) => (\n <li key={p.id}>\n <button\n onClick={() => setSelected(p.id)}\n className={`flex w-full items-center justify-between rounded-lg px-3 py-2 text-left text-sm ${\n p.id === selected ? \"bg-brand-50 text-brand-700\" : \"hover:bg-slate-100\"\n }`}\n >\n <span className=\"truncate\">{p.title}</span>\n {p.published ? <Badge tone=\"green\">live</Badge> : <Badge tone=\"amber\">concept</Badge>}\n </button>\n </li>\n ))}\n {/* Geen lege-lijst-tekst hier: de lege staat rechts zegt hetzelfde én noemt de volgende\n stap. Twee keer \"Nog geen pagina's\" naast elkaar leest als een fout. */}\n </ul>\n </Card>\n\n <div>\n {current ? (\n <>\n <PageEditor siteId={siteId} page={current} allPages={pages} />\n {/* Verwijderen staat ONDER de editor en draagt geen rode vlakknop meer. Het was het\n opvallendste element op het scherm — helderrood, bovenaan, bóven \"Publiceren\" —\n waarmee de onomkeerbare actie meer aandacht kreeg dan de actie waarvoor je komt.\n Een tekstknop in de foutkleur is duidelijk genoeg; de bevestiging vangt de rest. */}\n <div className=\"mt-4 flex justify-end border-t border-slate-200 pt-4\">\n <button\n type=\"button\"\n className=\"rounded px-2 py-1 text-sm text-red-600 hover:underline focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:outline-none\"\n onClick={async () => {\n const ok = await confirm({\n title: `Pagina \"${current.title}\" verwijderen?`,\n description:\n \"De pagina en de bijbehorende werkversie worden verwijderd. Als de pagina gepubliceerd is, verdwijnt hij ook van de live site. Dit kan niet ongedaan worden gemaakt.\",\n confirmLabel: \"Verwijderen\",\n tone: \"danger\",\n });\n if (!ok) return;\n await deletePage(siteId, current.id);\n setSelected(null);\n toast({ title: \"Pagina verwijderd\" });\n }}\n >\n Pagina verwijderen\n </button>\n </div>\n </>\n ) : (\n <Card className=\"grid place-items-center py-16 text-center\">\n <div className=\"max-w-sm\">\n {/* Een lege lijst is een uitnodiging, geen mededeling: zeg wat de volgende stap is. */}\n <p className=\"text-sm font-medium text-slate-700\">\n {pages.length === 0 ? \"Nog geen pagina's\" : \"Geen pagina geselecteerd\"}\n </p>\n <p className=\"mt-1 text-sm text-slate-500\">\n {pages.length === 0\n ? \"Maak je eerste pagina met + Nieuw. Wat je maakt blijft een concept tot je publiceert.\"\n : \"Kies links een pagina om de tekst en secties aan te passen.\"}\n </p>\n </div>\n </Card>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1,12 +1,12 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
- import { PagesPanel } from "./pages/PagesPanel";
4
- import { MediaManager } from "./media/MediaManager";
5
- import { FormsPanel } from "./forms/FormsPanel";
6
- import { StatsPanel } from "./stats/StatsPanel";
7
- import { SettingsPanel } from "./settings/SettingsPanel";
8
- import { NewsEditor } from "./NewsEditor";
9
- import { CustomBlocksPanel } from "./customBlocks/CustomBlocksPanel";
3
+ import { PagesPanel } from "./pages/PagesPanel.js";
4
+ import { MediaManager } from "./media/MediaManager.js";
5
+ import { FormsPanel } from "./forms/FormsPanel.js";
6
+ import { StatsPanel } from "./stats/StatsPanel.js";
7
+ import { SettingsPanel } from "./settings/SettingsPanel.js";
8
+ import { NewsEditor } from "./NewsEditor.js";
9
+ import { CustomBlocksPanel } from "./customBlocks/CustomBlocksPanel.js";
10
10
  function NewsPanel(_) {
11
11
  return /* @__PURE__ */ jsx(NewsEditor, {});
12
12
  }
@@ -1,4 +1,4 @@
1
- import { type Pattern } from "../../patterns";
1
+ import { type Pattern } from "../../patterns/index.js";
2
2
  /**
3
3
  * "Sectie toevoegen" — kiest een kant-en-klaar patroon en voegt de blocks (met verse keys) in.
4
4
  * Hergebruikt in de visuele editor én het /beheer-paginapaneel.
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useMemo, useState } from "react";
4
- import { Dialog, DialogContent, DialogClose } from "../ui/dialog";
5
- import { Button } from "../ui/primitives";
6
- import { useRegistry } from "../../blocks/context";
7
- import { builtinPatterns, instantiatePattern, collectPatternTypes } from "../../patterns";
4
+ import { Dialog, DialogContent, DialogClose } from "../ui/dialog.js";
5
+ import { Button } from "../ui/primitives.js";
6
+ import { useRegistry } from "../../blocks/context.js";
7
+ import { builtinPatterns, instantiatePattern, collectPatternTypes } from "../../patterns/index.js";
8
8
  function PatternInserter({
9
9
  patterns = builtinPatterns,
10
10
  onInsert,
@@ -24,7 +24,7 @@ function PatternInserter({
24
24
  }, [patterns, registry]);
25
25
  return /* @__PURE__ */ jsxs(Fragment, { children: [
26
26
  /* @__PURE__ */ jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setOpen(true), children: label }),
27
- /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(DialogContent, { title: "Sectie toevoegen", className: "max-w-xl", children: [
27
+ /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(DialogContent, { title: "Sectie toevoegen", width: "min(92vw, 36rem)", children: [
28
28
  /* @__PURE__ */ jsx("p", { className: "mb-3 text-sm text-slate-500", children: "Kies een kant-en-klare sectie; je past de inhoud daarna aan." }),
29
29
  /* @__PURE__ */ jsx("div", { className: "grid max-h-[60vh] gap-4 overflow-auto", children: byCategory.map(([cat, items]) => /* @__PURE__ */ jsxs("div", { children: [
30
30
  /* @__PURE__ */ jsx("h4", { className: "mb-1 text-xs font-semibold uppercase tracking-wide text-slate-400", children: cat }),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/admin/patterns/PatternInserter.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo, useState } from \"react\";\nimport { Dialog, DialogContent, DialogClose } from \"../ui/dialog\";\nimport { Button } from \"../ui/primitives\";\nimport { useRegistry } from \"../../blocks/context\";\nimport { builtinPatterns, instantiatePattern, collectPatternTypes, type Pattern } from \"../../patterns\";\n\n/**\n * \"Sectie toevoegen\" — kiest een kant-en-klaar patroon en voegt de blocks (met verse keys) in.\n * Hergebruikt in de visuele editor én het /beheer-paginapaneel.\n */\nexport function PatternInserter({\n patterns = builtinPatterns,\n onInsert,\n label = \"+ Sectie toevoegen\",\n}: {\n patterns?: Pattern[];\n onInsert: (blocks: Array<Record<string, unknown>>) => void;\n label?: string;\n}) {\n const [open, setOpen] = useState(false);\n const registry = useRegistry();\n const byCategory = useMemo(() => {\n // Verberg patronen waarvan een benodigd blok niet in de registry zit (module uitgeschakeld):\n // invoegen zou anders \"Onbekend blok\"-gaten geven.\n const available = patterns.filter((p) => collectPatternTypes(p).every((t) => registry.get(t)));\n const map = new Map<string, Pattern[]>();\n for (const p of available) {\n const arr = map.get(p.category) ?? [];\n arr.push(p);\n map.set(p.category, arr);\n }\n return [...map.entries()];\n }, [patterns, registry]);\n\n return (\n <>\n <Button type=\"button\" variant=\"secondary\" size=\"sm\" onClick={() => setOpen(true)}>\n {label}\n </Button>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent title=\"Sectie toevoegen\" className=\"max-w-xl\">\n <p className=\"mb-3 text-sm text-slate-500\">Kies een kant-en-klare sectie; je past de inhoud daarna aan.</p>\n <div className=\"grid max-h-[60vh] gap-4 overflow-auto\">\n {byCategory.map(([cat, items]) => (\n <div key={cat}>\n <h4 className=\"mb-1 text-xs font-semibold uppercase tracking-wide text-slate-400\">{cat}</h4>\n <div className=\"grid gap-2\">\n {items.map((p) => (\n <button\n key={p.id}\n type=\"button\"\n className=\"rounded-lg border border-slate-200 p-3 text-left text-sm hover:border-brand-500 hover:bg-slate-50\"\n onClick={() => {\n onInsert(instantiatePattern(p));\n setOpen(false);\n }}\n >\n <span className=\"flex items-center justify-between gap-2\">\n <span className=\"font-medium\">{p.label}</span>\n {/* Mini-preview: de iconen van de blokken die dit patroon invoegt. */}\n <span className=\"shrink-0 text-base opacity-70\" aria-hidden>\n {p.blocks.slice(0, 6).map((b, idx) => (\n <span key={idx}>{registry.get(b._type)?.icon ?? \"🧩\"}</span>\n ))}\n </span>\n </span>\n {p.description && <span className=\"block text-xs text-slate-500\">{p.description}</span>}\n </button>\n ))}\n </div>\n </div>\n ))}\n </div>\n <div className=\"mt-4 flex justify-end\">\n <DialogClose asChild>\n <Button variant=\"secondary\" size=\"sm\">Sluiten</Button>\n </DialogClose>\n </div>\n </DialogContent>\n </Dialog>\n </>\n );\n}\n"],"mappings":";AAqCI,mBACE,KAqBgB,YAtBlB;AAnCJ,SAAS,SAAS,gBAAgB;AAClC,SAAS,QAAQ,eAAe,mBAAmB;AACnD,SAAS,cAAc;AACvB,SAAS,mBAAmB;AAC5B,SAAS,iBAAiB,oBAAoB,2BAAyC;AAMhF,SAAS,gBAAgB;AAAA,EAC9B,WAAW;AAAA,EACX;AAAA,EACA,QAAQ;AACV,GAIG;AACD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,WAAW,YAAY;AAC7B,QAAM,aAAa,QAAQ,MAAM;AAG/B,UAAM,YAAY,SAAS,OAAO,CAAC,MAAM,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,SAAS,IAAI,CAAC,CAAC,CAAC;AAC7F,UAAM,MAAM,oBAAI,IAAuB;AACvC,eAAW,KAAK,WAAW;AACzB,YAAM,MAAM,IAAI,IAAI,EAAE,QAAQ,KAAK,CAAC;AACpC,UAAI,KAAK,CAAC;AACV,UAAI,IAAI,EAAE,UAAU,GAAG;AAAA,IACzB;AACA,WAAO,CAAC,GAAG,IAAI,QAAQ,CAAC;AAAA,EAC1B,GAAG,CAAC,UAAU,QAAQ,CAAC;AAEvB,SACE,iCACE;AAAA,wBAAC,UAAO,MAAK,UAAS,SAAQ,aAAY,MAAK,MAAK,SAAS,MAAM,QAAQ,IAAI,GAC5E,iBACH;AAAA,IACA,oBAAC,UAAO,MAAY,cAAc,SAChC,+BAAC,iBAAc,OAAM,oBAAmB,WAAU,YAChD;AAAA,0BAAC,OAAE,WAAU,+BAA8B,0EAA4D;AAAA,MACvG,oBAAC,SAAI,WAAU,yCACZ,qBAAW,IAAI,CAAC,CAAC,KAAK,KAAK,MAC1B,qBAAC,SACC;AAAA,4BAAC,QAAG,WAAU,qEAAqE,eAAI;AAAA,QACvF,oBAAC,SAAI,WAAU,cACZ,gBAAM,IAAI,CAAC,MACV;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,SAAS,MAAM;AACb,uBAAS,mBAAmB,CAAC,CAAC;AAC9B,sBAAQ,KAAK;AAAA,YACf;AAAA,YAEA;AAAA,mCAAC,UAAK,WAAU,2CACd;AAAA,oCAAC,UAAK,WAAU,eAAe,YAAE,OAAM;AAAA,gBAEvC,oBAAC,UAAK,WAAU,iCAAgC,eAAW,MACxD,YAAE,OAAO,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,QAC5B,oBAAC,UAAgB,mBAAS,IAAI,EAAE,KAAK,GAAG,QAAQ,eAArC,GAA0C,CACtD,GACH;AAAA,iBACF;AAAA,cACC,EAAE,eAAe,oBAAC,UAAK,WAAU,gCAAgC,YAAE,aAAY;AAAA;AAAA;AAAA,UAjB3E,EAAE;AAAA,QAkBT,CACD,GACH;AAAA,WAzBQ,GA0BV,CACD,GACH;AAAA,MACA,oBAAC,SAAI,WAAU,yBACb,8BAAC,eAAY,SAAO,MAClB,8BAAC,UAAO,SAAQ,aAAY,MAAK,MAAK,qBAAO,GAC/C,GACF;AAAA,OACF,GACF;AAAA,KACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/admin/patterns/PatternInserter.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo, useState } from \"react\";\nimport { Dialog, DialogContent, DialogClose } from \"../ui/dialog\";\nimport { Button } from \"../ui/primitives\";\nimport { useRegistry } from \"../../blocks/context\";\nimport { builtinPatterns, instantiatePattern, collectPatternTypes, type Pattern } from \"../../patterns\";\n\n/**\n * \"Sectie toevoegen\" — kiest een kant-en-klaar patroon en voegt de blocks (met verse keys) in.\n * Hergebruikt in de visuele editor én het /beheer-paginapaneel.\n */\nexport function PatternInserter({\n patterns = builtinPatterns,\n onInsert,\n label = \"+ Sectie toevoegen\",\n}: {\n patterns?: Pattern[];\n onInsert: (blocks: Array<Record<string, unknown>>) => void;\n label?: string;\n}) {\n const [open, setOpen] = useState(false);\n const registry = useRegistry();\n const byCategory = useMemo(() => {\n // Verberg patronen waarvan een benodigd blok niet in de registry zit (module uitgeschakeld):\n // invoegen zou anders \"Onbekend blok\"-gaten geven.\n const available = patterns.filter((p) => collectPatternTypes(p).every((t) => registry.get(t)));\n const map = new Map<string, Pattern[]>();\n for (const p of available) {\n const arr = map.get(p.category) ?? [];\n arr.push(p);\n map.set(p.category, arr);\n }\n return [...map.entries()];\n }, [patterns, registry]);\n\n return (\n <>\n <Button type=\"button\" variant=\"secondary\" size=\"sm\" onClick={() => setOpen(true)}>\n {label}\n </Button>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent title=\"Sectie toevoegen\" width=\"min(92vw, 36rem)\">\n <p className=\"mb-3 text-sm text-slate-500\">Kies een kant-en-klare sectie; je past de inhoud daarna aan.</p>\n <div className=\"grid max-h-[60vh] gap-4 overflow-auto\">\n {byCategory.map(([cat, items]) => (\n <div key={cat}>\n <h4 className=\"mb-1 text-xs font-semibold uppercase tracking-wide text-slate-400\">{cat}</h4>\n <div className=\"grid gap-2\">\n {items.map((p) => (\n <button\n key={p.id}\n type=\"button\"\n className=\"rounded-lg border border-slate-200 p-3 text-left text-sm hover:border-brand-500 hover:bg-slate-50\"\n onClick={() => {\n onInsert(instantiatePattern(p));\n setOpen(false);\n }}\n >\n <span className=\"flex items-center justify-between gap-2\">\n <span className=\"font-medium\">{p.label}</span>\n {/* Mini-preview: de iconen van de blokken die dit patroon invoegt. */}\n <span className=\"shrink-0 text-base opacity-70\" aria-hidden>\n {p.blocks.slice(0, 6).map((b, idx) => (\n <span key={idx}>{registry.get(b._type)?.icon ?? \"🧩\"}</span>\n ))}\n </span>\n </span>\n {p.description && <span className=\"block text-xs text-slate-500\">{p.description}</span>}\n </button>\n ))}\n </div>\n </div>\n ))}\n </div>\n <div className=\"mt-4 flex justify-end\">\n <DialogClose asChild>\n <Button variant=\"secondary\" size=\"sm\">Sluiten</Button>\n </DialogClose>\n </div>\n </DialogContent>\n </Dialog>\n </>\n );\n}\n"],"mappings":";AAqCI,mBACE,KAqBgB,YAtBlB;AAnCJ,SAAS,SAAS,gBAAgB;AAClC,SAAS,QAAQ,eAAe,mBAAmB;AACnD,SAAS,cAAc;AACvB,SAAS,mBAAmB;AAC5B,SAAS,iBAAiB,oBAAoB,2BAAyC;AAMhF,SAAS,gBAAgB;AAAA,EAC9B,WAAW;AAAA,EACX;AAAA,EACA,QAAQ;AACV,GAIG;AACD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,WAAW,YAAY;AAC7B,QAAM,aAAa,QAAQ,MAAM;AAG/B,UAAM,YAAY,SAAS,OAAO,CAAC,MAAM,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,SAAS,IAAI,CAAC,CAAC,CAAC;AAC7F,UAAM,MAAM,oBAAI,IAAuB;AACvC,eAAW,KAAK,WAAW;AACzB,YAAM,MAAM,IAAI,IAAI,EAAE,QAAQ,KAAK,CAAC;AACpC,UAAI,KAAK,CAAC;AACV,UAAI,IAAI,EAAE,UAAU,GAAG;AAAA,IACzB;AACA,WAAO,CAAC,GAAG,IAAI,QAAQ,CAAC;AAAA,EAC1B,GAAG,CAAC,UAAU,QAAQ,CAAC;AAEvB,SACE,iCACE;AAAA,wBAAC,UAAO,MAAK,UAAS,SAAQ,aAAY,MAAK,MAAK,SAAS,MAAM,QAAQ,IAAI,GAC5E,iBACH;AAAA,IACA,oBAAC,UAAO,MAAY,cAAc,SAChC,+BAAC,iBAAc,OAAM,oBAAmB,OAAM,oBAC5C;AAAA,0BAAC,OAAE,WAAU,+BAA8B,0EAA4D;AAAA,MACvG,oBAAC,SAAI,WAAU,yCACZ,qBAAW,IAAI,CAAC,CAAC,KAAK,KAAK,MAC1B,qBAAC,SACC;AAAA,4BAAC,QAAG,WAAU,qEAAqE,eAAI;AAAA,QACvF,oBAAC,SAAI,WAAU,cACZ,gBAAM,IAAI,CAAC,MACV;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,SAAS,MAAM;AACb,uBAAS,mBAAmB,CAAC,CAAC;AAC9B,sBAAQ,KAAK;AAAA,YACf;AAAA,YAEA;AAAA,mCAAC,UAAK,WAAU,2CACd;AAAA,oCAAC,UAAK,WAAU,eAAe,YAAE,OAAM;AAAA,gBAEvC,oBAAC,UAAK,WAAU,iCAAgC,eAAW,MACxD,YAAE,OAAO,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,QAC5B,oBAAC,UAAgB,mBAAS,IAAI,EAAE,KAAK,GAAG,QAAQ,eAArC,GAA0C,CACtD,GACH;AAAA,iBACF;AAAA,cACC,EAAE,eAAe,oBAAC,UAAK,WAAU,gCAAgC,YAAE,aAAY;AAAA;AAAA;AAAA,UAjB3E,EAAE;AAAA,QAkBT,CACD,GACH;AAAA,WAzBQ,GA0BV,CACD,GACH;AAAA,MACA,oBAAC,SAAI,WAAU,yBACb,8BAAC,eAAY,SAAO,MAClB,8BAAC,UAAO,SAAQ,aAAY,MAAK,MAAK,qBAAO,GAC/C,GACF;AAAA,OACF,GACF;AAAA,KACF;AAEJ;","names":[]}
@@ -2,11 +2,11 @@
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useCallback, useEffect, useState } from "react";
4
4
  import { getFunctions, httpsCallable } from "firebase/functions";
5
- import { getFirebaseApp } from "../../firebase/client";
6
- import { functionsRegion } from "../../firebase/region";
7
- import { Badge, Button, Card, Field, Input } from "../ui/primitives";
8
- import { Switch } from "../ui/switch";
9
- import { useToast } from "../ui/toast";
5
+ import { getFirebaseApp } from "../../firebase/client.js";
6
+ import { functionsRegion } from "../../firebase/region.js";
7
+ import { Badge, Button, Card, Field, Input } from "../ui/primitives.js";
8
+ import { Switch } from "../ui/switch.js";
9
+ import { useToast } from "../ui/toast.js";
10
10
  function callFunction(name) {
11
11
  return async (data) => {
12
12
  const app = getFirebaseApp();
@@ -1,4 +1,4 @@
1
- import { type MailTemplateKey } from "../../schema/mail";
1
+ import { type MailTemplateKey } from "../../schema/mail.js";
2
2
  type MailTemplatesProps = {
3
3
  siteId: string;
4
4
  /** Welke templates tonen — bv. FORM_TEMPLATE_KEYS in Formulieren, SHOP_TEMPLATE_KEYS in de shop. */
@@ -3,11 +3,11 @@ import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useState } from "react";
4
4
  import { doc, getDoc, setDoc } from "firebase/firestore";
5
5
  import { getFunctions, httpsCallable } from "firebase/functions";
6
- import { getDb, getFirebaseApp } from "../../firebase/client";
7
- import { functionsRegion } from "../../firebase/region";
8
- import { MAIL_TEMPLATE_DEFS } from "../../schema/mail";
9
- import { Button, Card, Field, Input, Textarea } from "../ui/primitives";
10
- import { useToast } from "../ui/toast";
6
+ import { getDb, getFirebaseApp } from "../../firebase/client.js";
7
+ import { functionsRegion } from "../../firebase/region.js";
8
+ import { MAIL_TEMPLATE_DEFS } from "../../schema/mail.js";
9
+ import { Button, Card, Field, Input, Textarea } from "../ui/primitives.js";
10
+ import { useToast } from "../ui/toast.js";
11
11
  function callFunction(name) {
12
12
  return async (data) => {
13
13
  const app = getFirebaseApp();
@@ -1,22 +1,22 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useRef, useState } from "react";
4
- import { doc, onSnapshot, writeBatch } from "firebase/firestore";
4
+ import { doc, onSnapshot } from "firebase/firestore";
5
5
  import { getFunctions, httpsCallable } from "firebase/functions";
6
- import { getDb, getFirebaseApp } from "../../firebase/client";
7
- import { functionsRegion } from "../../firebase/region";
8
- import { SiteSettingsSchema, toPublicSettings } from "../../schema/settings";
9
- import { useModules } from "../../modules/context";
10
- import { builtinThemePresets } from "../../theme/presets";
11
- import { Button, Card, Field, Input } from "../ui/primitives";
12
- import { Select } from "../ui/select";
13
- import { Switch } from "../ui/switch";
14
- import { ArrayEditor } from "../ui/ArrayEditor";
15
- import { useToast } from "../ui/toast";
16
- import { useConfirm } from "../ui/ConfirmDialog";
17
- import { ModuleSettingsForm } from "./moduleSettingsForm";
18
- import { SnapshotsCard } from "./SnapshotsCard";
19
- import { MailSettings } from "./MailSettings";
6
+ import { getDb, getFirebaseApp } from "../../firebase/client.js";
7
+ import { functionsRegion } from "../../firebase/region.js";
8
+ import { SiteSettingsSchema } from "../../schema/settings.js";
9
+ import { useModules } from "../../modules/context.js";
10
+ import { builtinThemePresets } from "../../theme/presets.js";
11
+ import { Button, Card, Field, Input } from "../ui/primitives.js";
12
+ import { Select } from "../ui/select.js";
13
+ import { Switch } from "../ui/switch.js";
14
+ import { ArrayEditor } from "../ui/ArrayEditor.js";
15
+ import { useToast } from "../ui/toast.js";
16
+ import { useConfirm } from "../ui/ConfirmDialog.js";
17
+ import { ModuleSettingsForm } from "./moduleSettingsForm.js";
18
+ import { SnapshotsCard } from "./SnapshotsCard.js";
19
+ import { MailSettings } from "./MailSettings.js";
20
20
  function SettingsPanel({ siteId }) {
21
21
  const { toast } = useToast();
22
22
  const confirm = useConfirm();
@@ -117,11 +117,11 @@ function SettingsPanel({ siteId }) {
117
117
  return;
118
118
  }
119
119
  try {
120
- const batch = writeBatch(db);
121
- batch.set(doc(db, "sites", siteId, "settings", "site"), parsed.data, { merge: true });
122
- batch.set(doc(db, "sites", siteId, "settings", "public"), toPublicSettings(parsed.data), { merge: true });
123
- await batch.commit();
124
- toast({ title: "Opgeslagen", description: "Wijzigingen zijn direct live.", tone: "success" });
120
+ const app = getFirebaseApp();
121
+ if (!app) throw new Error("Firebase is niet geconfigureerd.");
122
+ const fn = httpsCallable(getFunctions(app, functionsRegion()), "saveSiteSettings");
123
+ const res = await fn({ siteId, settings: parsed.data });
124
+ toast({ title: "Opgeslagen", description: res.data?.message ?? "Wijzigingen zijn live.", tone: "success" });
125
125
  } catch (e) {
126
126
  toast({ title: "Opslaan mislukt", description: e.message, tone: "error" });
127
127
  }
@@ -129,6 +129,7 @@ function SettingsPanel({ siteId }) {
129
129
  if (!loaded) return /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Laden\u2026" });
130
130
  const forms = s.forms ?? { mode: "xicms" };
131
131
  const theme = s.theme ?? { primary: "#4f46e5" };
132
+ const donkerModus = theme.dark?.mode ?? (theme.dark?.auto ? "auto" : "light");
132
133
  const footer = s.footer ?? {};
133
134
  const moduleState = (id, defaultEnabled) => {
134
135
  const raw = s.modules?.[id];
@@ -166,6 +167,51 @@ function SettingsPanel({ siteId }) {
166
167
  /* @__PURE__ */ jsx(Field, { label: "Achtergrondkleur", children: /* @__PURE__ */ jsx(Input, { value: theme.background ?? "", onChange: (e) => setS({ ...s, theme: { ...theme, background: e.target.value } }), placeholder: "#ffffff" }) }),
167
168
  /* @__PURE__ */ jsx(Field, { label: "Tekstkleur", children: /* @__PURE__ */ jsx(Input, { value: theme.foreground ?? "", onChange: (e) => setS({ ...s, theme: { ...theme, foreground: e.target.value } }), placeholder: "#0f172a" }) })
168
169
  ] }),
170
+ /* @__PURE__ */ jsx(
171
+ Field,
172
+ {
173
+ label: "Donkere modus",
174
+ hint: donkerModus === "auto" ? "Volgt de voorkeur van de bezoeker: wie zijn telefoon of computer op donker heeft staan, ziet de site donker." : donkerModus === "dark" ? "De site is voor iedereen donker, ongeacht de voorkeur van de bezoeker." : "De site is voor iedereen licht.",
175
+ children: /* @__PURE__ */ jsx(
176
+ Select,
177
+ {
178
+ value: donkerModus,
179
+ onValueChange: (v) => setS({ ...s, theme: { ...theme, dark: { ...theme.dark ?? {}, mode: v } } }),
180
+ options: [
181
+ { value: "light", label: "Altijd licht" },
182
+ { value: "dark", label: "Altijd donker" },
183
+ { value: "auto", label: "Automatisch \u2014 volg de bezoeker" }
184
+ ]
185
+ }
186
+ )
187
+ }
188
+ ),
189
+ donkerModus !== "light" && /* @__PURE__ */ jsxs("div", { className: "grid gap-3 sm:grid-cols-3", children: [
190
+ /* @__PURE__ */ jsx(Field, { label: "Donkere achtergrond", children: /* @__PURE__ */ jsx(
191
+ Input,
192
+ {
193
+ value: theme.dark?.background ?? "",
194
+ onChange: (e) => setS({ ...s, theme: { ...theme, dark: { ...theme.dark ?? {}, background: e.target.value } } }),
195
+ placeholder: "#0f172a"
196
+ }
197
+ ) }),
198
+ /* @__PURE__ */ jsx(Field, { label: "Donkere tekstkleur", children: /* @__PURE__ */ jsx(
199
+ Input,
200
+ {
201
+ value: theme.dark?.foreground ?? "",
202
+ onChange: (e) => setS({ ...s, theme: { ...theme, dark: { ...theme.dark ?? {}, foreground: e.target.value } } }),
203
+ placeholder: "#e2e8f0"
204
+ }
205
+ ) }),
206
+ /* @__PURE__ */ jsx(Field, { label: "Donkere merkkleur", hint: "Laat leeg om dezelfde te gebruiken.", children: /* @__PURE__ */ jsx(
207
+ Input,
208
+ {
209
+ value: theme.dark?.primary ?? "",
210
+ onChange: (e) => setS({ ...s, theme: { ...theme, dark: { ...theme.dark ?? {}, primary: e.target.value } } }),
211
+ placeholder: theme.primary
212
+ }
213
+ ) })
214
+ ] }),
169
215
  /* @__PURE__ */ jsx(Field, { label: "Logo-URL (e-mail & factuur)", hint: "Absolute https-URL naar een PNG/JPG. Verschijnt in de mail-header en op de factuur-PDF.", children: /* @__PURE__ */ jsx(Input, { value: theme.logo ?? "", onChange: (e) => setS({ ...s, theme: { ...theme, logo: e.target.value } }), placeholder: "https://\u2026/logo.png" }) })
170
216
  ] })
171
217
  ] }),