domma-cms 0.44.0 → 0.45.1

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 (254) hide show
  1. package/CLAUDE.md +53 -53
  2. package/README.md +27 -27
  3. package/admin/css/admin.css +1 -1
  4. package/admin/dist/domma/domma-tools.css +3 -3
  5. package/admin/dist/domma/domma-tools.min.js +3 -3
  6. package/admin/index.html +2 -2
  7. package/admin/js/app.js +1 -1
  8. package/admin/js/lib/card-builder.js +2 -2
  9. package/admin/js/lib/crud-tutorial.js +1 -1
  10. package/admin/js/lib/effect-defs.js +1 -1
  11. package/admin/js/lib/effects-builder.js +1 -1
  12. package/admin/js/lib/image-editor.js +1 -1
  13. package/admin/js/lib/markdown-toolbar.js +2 -2
  14. package/admin/js/lib/project-context.js +1 -1
  15. package/admin/js/lib/project-quick-create.js +1 -1
  16. package/admin/js/lib/scribe-composer.js +1 -1
  17. package/admin/js/lib/server-restart.js +1 -1
  18. package/admin/js/lib/shortcode-context-menu.js +2 -2
  19. package/admin/js/lib/simple-editor.js +1 -1
  20. package/admin/js/lib/themes.js +1 -1
  21. package/admin/js/lib/timeline-builder.js +1 -1
  22. package/admin/js/templates/action-editor.html +8 -8
  23. package/admin/js/templates/api-endpoint-editor.html +7 -7
  24. package/admin/js/templates/api-endpoints.html +1 -1
  25. package/admin/js/templates/api-reference.html +1 -1
  26. package/admin/js/templates/block-editor.html +6 -6
  27. package/admin/js/templates/collection-editor.html +1 -1
  28. package/admin/js/templates/component-editor.html +2 -2
  29. package/admin/js/templates/dashboard/cache.html +3 -3
  30. package/admin/js/templates/dashboard/journeys.html +3 -3
  31. package/admin/js/templates/dashboard/kpi-strip.html +3 -3
  32. package/admin/js/templates/dashboard/top-pages.html +1 -1
  33. package/admin/js/templates/dashboard/traffic-chart.html +1 -1
  34. package/admin/js/templates/docs/api-actions.html +1 -1
  35. package/admin/js/templates/docs/api-collections.html +4 -4
  36. package/admin/js/templates/docs/api-pages.html +2 -2
  37. package/admin/js/templates/docs/api-plugins.html +1 -1
  38. package/admin/js/templates/docs/api-settings.html +2 -2
  39. package/admin/js/templates/docs/api-users.html +1 -1
  40. package/admin/js/templates/docs/components-howto.html +4 -4
  41. package/admin/js/templates/docs/components-reference.html +21 -21
  42. package/admin/js/templates/docs/components-rules.html +14 -14
  43. package/admin/js/templates/docs/components-walkthrough.html +13 -13
  44. package/admin/js/templates/docs/tutorial-crud.html +49 -49
  45. package/admin/js/templates/docs/tutorial-forms.html +4 -4
  46. package/admin/js/templates/docs/tutorial-plugin.html +10 -10
  47. package/admin/js/templates/docs/usage-actions.html +6 -6
  48. package/admin/js/templates/docs/usage-cta-shortcode.html +9 -9
  49. package/admin/js/templates/docs/usage-dconfig.html +4 -4
  50. package/admin/js/templates/docs/usage-navigation.html +1 -1
  51. package/admin/js/templates/docs/usage-pages.html +1 -1
  52. package/admin/js/templates/docs/usage-shortcodes.html +24 -24
  53. package/admin/js/templates/docs/usage-users-roles.html +3 -3
  54. package/admin/js/templates/docs/usage-views.html +4 -4
  55. package/admin/js/templates/documentation.html +1 -1
  56. package/admin/js/templates/effects.html +752 -752
  57. package/admin/js/templates/form-editor.html +5 -5
  58. package/admin/js/templates/forms.html +17 -17
  59. package/admin/js/templates/login.html +5 -5
  60. package/admin/js/templates/menu-editor.html +10 -10
  61. package/admin/js/templates/my-profile.html +17 -17
  62. package/admin/js/templates/navigation.html +11 -11
  63. package/admin/js/templates/notifications.html +1 -1
  64. package/admin/js/templates/page-editor.html +4 -4
  65. package/admin/js/templates/pro-docs.html +14 -14
  66. package/admin/js/templates/role-editor.html +70 -70
  67. package/admin/js/templates/roles.html +10 -10
  68. package/admin/js/templates/settings.html +10 -10
  69. package/admin/js/templates/tutorials.html +3 -3
  70. package/admin/js/templates/view-editor.html +15 -15
  71. package/admin/js/views/action-editor.js +1 -1
  72. package/admin/js/views/actions-list.js +1 -1
  73. package/admin/js/views/api-endpoint-editor.js +2 -2
  74. package/admin/js/views/api-endpoints.js +2 -2
  75. package/admin/js/views/api-tokens.js +3 -3
  76. package/admin/js/views/blocks.js +2 -2
  77. package/admin/js/views/collection-editor.js +1 -1
  78. package/admin/js/views/collection-entries.js +1 -1
  79. package/admin/js/views/collections.js +1 -1
  80. package/admin/js/views/component-editor.js +1 -1
  81. package/admin/js/views/components.js +4 -4
  82. package/admin/js/views/dashboard/widgets/cache.js +1 -1
  83. package/admin/js/views/dashboard/widgets/journeys.js +1 -1
  84. package/admin/js/views/dashboard/widgets/kpi-strip.js +1 -1
  85. package/admin/js/views/form-editor.js +7 -7
  86. package/admin/js/views/form-submissions.js +1 -1
  87. package/admin/js/views/forms.js +1 -1
  88. package/admin/js/views/layouts.js +1 -1
  89. package/admin/js/views/login.js +2 -2
  90. package/admin/js/views/menu-editor.js +3 -3
  91. package/admin/js/views/menu-locations.js +1 -1
  92. package/admin/js/views/menus.js +3 -3
  93. package/admin/js/views/navigation.js +8 -8
  94. package/admin/js/views/page-editor.js +40 -40
  95. package/admin/js/views/pages.js +3 -3
  96. package/admin/js/views/plugin-code.js +2 -2
  97. package/admin/js/views/plugins.js +1 -1
  98. package/admin/js/views/project-settings.js +1 -1
  99. package/admin/js/views/projects.js +1 -1
  100. package/admin/js/views/roles.js +1 -1
  101. package/admin/js/views/user-editor.js +1 -1
  102. package/admin/js/views/users.js +3 -3
  103. package/admin/js/views/view-editor.js +1 -1
  104. package/admin/js/views/view-preview.js +1 -1
  105. package/admin/js/views/views-list.js +1 -1
  106. package/bin/cli.js +8 -8
  107. package/bin/lib/config-merge.js +44 -44
  108. package/bin/update.js +16 -16
  109. package/config/plugins.json +1 -1
  110. package/package.json +2 -2
  111. package/plugins/_template/admin/views/index.js +1 -1
  112. package/plugins/_template/config.js +1 -1
  113. package/plugins/_template/plugin.js +1 -1
  114. package/plugins/analytics/admin/templates/analytics.html +3 -3
  115. package/plugins/analytics/admin/views/analytics.js +1 -1
  116. package/plugins/analytics/config.js +1 -1
  117. package/plugins/analytics/plugin.js +4 -4
  118. package/plugins/analytics/plugin.json +1 -1
  119. package/plugins/analytics/public/inject-body.html +2 -2
  120. package/plugins/analytics/public/inject-head.html +1 -1
  121. package/plugins/blog/admin/templates/blog.html +4 -4
  122. package/plugins/blog/admin/views/blog.js +3 -3
  123. package/plugins/blog/admin/views/categories.js +2 -2
  124. package/plugins/blog/admin/views/comments.js +4 -4
  125. package/plugins/blog/admin/views/post-editor.js +1 -1
  126. package/plugins/blog/plugin.js +2 -2
  127. package/plugins/blog/plugin.json +4 -4
  128. package/plugins/contacts/admin/views/contacts.js +8 -8
  129. package/plugins/contacts/plugin.js +4 -4
  130. package/plugins/contacts/plugin.json +1 -1
  131. package/plugins/invoice/admin/templates/editor.html +6 -6
  132. package/plugins/invoice/admin/templates/index.html +4 -4
  133. package/plugins/invoice/admin/views/editor.js +3 -3
  134. package/plugins/invoice/admin/views/index.js +4 -4
  135. package/plugins/invoice/admin/views/issuers.js +1 -1
  136. package/plugins/invoice/admin/views/party-view.js +3 -3
  137. package/plugins/invoice/admin/views/receivers.js +1 -1
  138. package/plugins/invoice/config.js +1 -1
  139. package/plugins/invoice/plugin.js +1 -1
  140. package/plugins/invoice/plugin.json +4 -4
  141. package/plugins/notes/admin/views/notes.js +4 -4
  142. package/plugins/notes/plugin.js +1 -1
  143. package/plugins/notes/plugin.json +1 -1
  144. package/plugins/site-search/admin/templates/site-search.html +3 -3
  145. package/plugins/site-search/admin/views/site-search.js +1 -1
  146. package/plugins/site-search/config.js +1 -1
  147. package/plugins/site-search/plugin.js +4 -4
  148. package/plugins/site-search/plugin.json +1 -1
  149. package/plugins/surveys/admin/templates/results.html +1 -1
  150. package/plugins/surveys/admin/templates/survey-editor.html +4 -4
  151. package/plugins/surveys/admin/views/audience.js +6 -6
  152. package/plugins/surveys/admin/views/results.js +3 -3
  153. package/plugins/surveys/admin/views/survey-editor.js +1 -1
  154. package/plugins/surveys/admin/views/surveys.js +2 -2
  155. package/plugins/surveys/plugin.js +1 -1
  156. package/plugins/surveys/plugin.json +4 -4
  157. package/plugins/theme-switcher/admin/views/theme-switcher.js +1 -1
  158. package/plugins/theme-switcher/plugin.json +3 -3
  159. package/plugins/theme-switcher/public/inject-body.html +28 -28
  160. package/plugins/theme-switcher/public/inject-head.html +1 -1
  161. package/plugins/todo/plugin.js +1 -1
  162. package/public/css/forms.css +1 -1
  163. package/public/js/collection-browser.js +1 -1
  164. package/public/js/collection-context.js +1 -1
  165. package/public/js/form-logic-engine.js +1 -1
  166. package/public/js/forms.js +2 -1
  167. package/public/js/site.js +1 -1
  168. package/scripts/build.js +14 -14
  169. package/scripts/create-plugin.js +2 -2
  170. package/scripts/fresh.js +2 -2
  171. package/scripts/gen-instance-secret.js +1 -1
  172. package/scripts/pro.js +12 -12
  173. package/scripts/reset.js +2 -2
  174. package/scripts/setup.js +15 -15
  175. package/scripts/users.js +5 -5
  176. package/scripts/verify-assets.mjs +6 -6
  177. package/server/config.js +1 -1
  178. package/server/middleware/auth.js +253 -253
  179. package/server/middleware/managerAuth.js +3 -3
  180. package/server/routes/api/actions.js +4 -4
  181. package/server/routes/api/api-endpoints.js +7 -7
  182. package/server/routes/api/api-tokens.js +5 -5
  183. package/server/routes/api/auth.js +309 -309
  184. package/server/routes/api/blocks.js +3 -3
  185. package/server/routes/api/collections.js +18 -18
  186. package/server/routes/api/dashboard.js +5 -5
  187. package/server/routes/api/effects.js +2 -2
  188. package/server/routes/api/endpoints-public.js +5 -5
  189. package/server/routes/api/forms.js +47 -47
  190. package/server/routes/api/menu-locations.js +3 -3
  191. package/server/routes/api/menus.js +7 -7
  192. package/server/routes/api/navigation.js +42 -42
  193. package/server/routes/api/notifications.js +2 -2
  194. package/server/routes/api/pages.js +14 -2
  195. package/server/routes/api/plugin-marketplace.js +3 -3
  196. package/server/routes/api/plugins.js +6 -6
  197. package/server/routes/api/projects.js +9 -9
  198. package/server/routes/api/scaffold.js +7 -7
  199. package/server/routes/api/settings.js +6 -6
  200. package/server/routes/api/sidebar.js +1 -1
  201. package/server/routes/api/users.js +5 -5
  202. package/server/routes/api/versions.js +1 -1
  203. package/server/routes/api/views.js +1 -1
  204. package/server/routes/docs-public.js +3 -3
  205. package/server/routes/public.js +14 -14
  206. package/server/server.js +34 -16
  207. package/server/services/actions.js +19 -19
  208. package/server/services/adapters/FileAdapter.js +2 -2
  209. package/server/services/adapters/MongoAdapter.js +5 -5
  210. package/server/services/apiEndpoints.js +15 -15
  211. package/server/services/apiTokens.js +7 -7
  212. package/server/services/blocks.js +13 -13
  213. package/server/services/cache/drivers/MemoryDriver.js +3 -3
  214. package/server/services/cache/drivers/NoneDriver.js +1 -1
  215. package/server/services/cache/index.js +5 -5
  216. package/server/services/collections.js +14 -14
  217. package/server/services/components.js +9 -9
  218. package/server/services/connectionManager.js +4 -4
  219. package/server/services/content.js +25 -2
  220. package/server/services/docs.js +14 -14
  221. package/server/services/email.js +167 -167
  222. package/server/services/filterEngine.js +6 -6
  223. package/server/services/forms.js +18 -10
  224. package/server/services/health.js +1 -1
  225. package/server/services/hooks.js +7 -7
  226. package/server/services/images.js +4 -4
  227. package/server/services/managerClient.js +1 -1
  228. package/server/services/markdown.js +79 -79
  229. package/server/services/menuRender.js +5 -5
  230. package/server/services/menus-migration.js +3 -3
  231. package/server/services/menus.js +31 -31
  232. package/server/services/permissionRegistry.js +1 -1
  233. package/server/services/pluginFiles.js +5 -5
  234. package/server/services/pluginInstaller.js +17 -17
  235. package/server/services/pluginScaffold.js +1 -1
  236. package/server/services/plugins.js +17 -17
  237. package/server/services/presetCollections.js +2 -2
  238. package/server/services/projects.js +35 -26
  239. package/server/services/recipes/contact-list.json +1 -1
  240. package/server/services/recipes/onboarding.json +8 -8
  241. package/server/services/references.js +8 -8
  242. package/server/services/renderer.js +9 -9
  243. package/server/services/roles.js +9 -9
  244. package/server/services/rowAccess.js +10 -10
  245. package/server/services/scaffolder.js +30 -30
  246. package/server/services/sidebar-migration.js +10 -10
  247. package/server/services/sitemap.js +1 -1
  248. package/server/services/userProfiles.js +199 -199
  249. package/server/services/userRoles.js +7 -7
  250. package/server/services/users.js +302 -302
  251. package/server/services/versions.js +3 -3
  252. package/server/services/viewPipeline.js +6 -6
  253. package/server/services/views.js +11 -11
  254. package/server/templates/page.html +5 -5
@@ -1,8 +1,8 @@
1
- import{apiRequest as A}from"/admin/js/api.js";import{makeIconInput as ge}from"/admin/js/lib/shortcode-modal.js";import{enhancePickerFields as Z}from"/public/js/forms.js";const ee=[{value:"string",label:"Text (single line)"},{value:"email",label:"Email"},{value:"tel",label:"Phone"},{value:"number",label:"Number"},{value:"textarea",label:"Textarea (multi-line)"},{value:"select",label:"Dropdown (select)"},{value:"radio",label:"Radio buttons"},{value:"checkbox",label:"Single checkbox"},{value:"checkbox-group",label:"Checkbox group"},{value:"chooser",label:"Chooser (visual options)"},{value:"icon",label:"Icon (icon picker)"},{value:"image",label:"Image (media library)"},{value:"date",label:"Date"},{value:"time",label:"Time"},{value:"url",label:"URL"},{value:"password",label:"Password"},{value:"file",label:"File upload"},{value:"hidden",label:"Hidden field"}],G=new Set(["select","radio","checkbox-group","chooser"]);let v=[],q=null,P=null,M=null,J={};function w(e,n,i=14){e.textContent="";const t=document.createElement("span");return t.setAttribute("data-icon",n),t.setAttribute("data-icon-size",String(i)),t.style.cssText="display:inline-flex;align-items:center;vertical-align:middle;",e.appendChild(t),window.I?.scan?.(e),e}function j(e,n,i,t=14){return w(e,n,t),e.appendChild(document.createTextNode(" "+i)),e}function Y(e){const n=document.createElement("span");return n.setAttribute("data-icon","help-circle"),n.setAttribute("data-icon-size","12"),n.style.cssText="display:inline-flex;align-items:center;vertical-align:middle;margin-left:.3rem;opacity:.55;cursor:help;",n.title=e,window.I?.scan?.(n),requestAnimationFrame(()=>{try{n.parentNode&&typeof E<"u"&&E.tooltip&&(n.removeAttribute("title"),E.tooltip(n,{content:e,position:"top"}))}catch{}}),n}let D=null,te=null;function Q(){clearTimeout(te),te=setTimeout(()=>{D&&D()},350)}function ne(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_|_$/g,"")}function le(e){return ee.find(n=>n.value===e)?.label||e}function oe(e){return e?!!(e.visibility?.conditions?.length||e.requirement?.conditions?.length||e.validation?.length||e.cascade?.sourceField):!1}function ye(e){const n={...v[e]};if(n.type==="spacer")return n;if(n.type==="page-break"){const h=document.getElementById(`fb-pb-label-${e}`),m=document.getElementById(`fb-pb-desc-${e}`);return h&&(n.label=h.value.trim()||n.label),m&&(n.description=m.value.trim()),n}const i=document.getElementById(`fb-label-${e}`),t=document.getElementById(`fb-name-${e}`),o=document.getElementById(`fb-type-${e}`),l=document.getElementById(`fb-required-${e}`),s=document.getElementById(`fb-placeholder-${e}`),d=document.getElementById(`fb-helper-${e}`),r=document.getElementById(`fb-tooltip-${e}`);if(i&&(n.label=i.value.trim()||n.label),t&&(n.name=t.value.trim()||n.name),o&&(n.type=o.value||n.type),l&&(n.required=l.checked),s&&(n.placeholder=s.value.trim()),d&&(n.helper=d.value.trim()),r&&(n.tooltip=r.value.trim()),n.type==="chooser"){const h=document.getElementById(`fb-chooser-variant-${e}`)?.value;h&&(n.variant=h);const m=document.getElementById(`fb-chooser-multiple-${e}`);m&&(n.multiple=m.checked);const y=document.getElementById(`fb-chooser-density-${e}`)?.value;y&&(n.density=y);const x=document.getElementById(`fb-chooser-columns-${e}`)?.value;x!==""&&x!==void 0&&(n.columns=parseInt(x,10)||3);const C=document.getElementById(`fb-chooser-accent-${e}`)?.value.trim();C?n.accent=C:delete n.accent;const T=document.getElementById(`fb-chooser-accent-style-${e}`)?.value;T?n.accentStyle=T:delete n.accentStyle;const z=document.getElementById(`fb-chooser-glow-${e}`);z&&(n.glow=z.checked);const I=document.getElementById(`fb-chooser-glow-colour-${e}`)?.value.trim();I?n.glowColour=I:delete n.glowColour;const N=document.getElementById(`fb-chooser-shadow-${e}`)?.value;N&&N!=="none"?n.shadow=N:delete n.shadow;const O=document.getElementById(`fb-chooser-shadow-colour-${e}`)?.value.trim();O?n.shadowColour=O:delete n.shadowColour,W(e),v[e]&&Array.isArray(v[e].options)&&(n.options=v[e].options)}else if(G.has(n.type)){const h=document.getElementById(`fb-options-${e}`);h&&(n.options=h.value.split(`
2
- `).filter(m=>m.trim()).map(m=>{const[y,...x]=m.split(":");return{value:y.trim(),label:x.join(":").trim()||y.trim()}}))}if(n.type==="textarea"){const h=parseInt(document.getElementById(`fb-rows-${e}`)?.value,10);h>0&&(n.formConfig={...n.formConfig||{},rows:h})}const a=document.getElementById(`fb-span-${e}`),p=document.getElementById(`fb-fullwidth-${e}`);if(a||p){const h={...n.formConfig||{}};if(p?.checked)h.span="full";else{const m=parseInt(a?.value,10);m>1?h.span=m:delete h.span}n.formConfig=Object.keys(h).length?h:void 0}const c=document.getElementById(`fb-minlength-${e}`)?.value;c&&(n.minLength=parseInt(c,10));const u=document.getElementById(`fb-maxlength-${e}`)?.value;u&&(n.maxLength=parseInt(u,10));const f=document.getElementById(`fb-min-${e}`)?.value;f!==""&&f!==void 0&&(n.min=parseFloat(f));const g=document.getElementById(`fb-max-${e}`)?.value;g!==""&&g!==void 0&&(n.max=parseFloat(g));const b=ve(e);return b?n.logic=b:delete n.logic,n}function ve(e){const n=document.querySelector(`.fb-field-card[data-index="${e}"]`);if(!n)return;const i=n.querySelector(".fb-field-logic");if(!i)return;const t={};let o=!1;const l=i.querySelector('[data-logic-section="visibility"]');if(l){const a=l.querySelector(".fb-logic-vis-default"),p=l.querySelector(".fb-logic-vis-transition"),c=Array.from(l.querySelectorAll(".fb-logic-cond-row")).map(b=>{const h=b.querySelector(".fb-logic-cond-field"),m=b.querySelector(".fb-logic-cond-op"),y=b.querySelector(".fb-logic-cond-val"),x=b.querySelector(".fb-logic-vis-then");return h?.value?{when:{all:[{field:h.value,operator:m.value,value:y.value}]},then:x.value}:null}).filter(Boolean),u=a?.value||"visible",f=p?.value||"none",g=parseInt(l.querySelector(".fb-logic-vis-duration")?.value||"",10);(u!=="visible"||c.length>0||f!=="none")&&(t.visibility={default:u,conditions:c},f!=="none"&&(t.visibility.transition=f),f!=="none"&&Number.isFinite(g)&&(t.visibility.duration=g),o=!0)}const s=i.querySelector('[data-logic-section="requirement"]');if(s){const a=s.querySelector(".fb-logic-req-default"),p=Array.from(s.querySelectorAll(".fb-logic-cond-row")).map(c=>{const u=c.querySelector(".fb-logic-cond-field"),f=c.querySelector(".fb-logic-cond-op"),g=c.querySelector(".fb-logic-cond-val"),b=c.querySelector(".fb-logic-req-then");return u?.value?{when:{all:[{field:u.value,operator:f.value,value:g.value}]},then:b.value==="true"}:null}).filter(Boolean);p.length>0&&(t.requirement={default:a?.checked===!0,conditions:p},o=!0)}const d=i.querySelector('[data-logic-section="validation"]');if(d){const a=Array.from(d.querySelectorAll(".fb-logic-val-rule")).map(p=>{const c=p.querySelector(".fb-logic-val-type"),u=p.querySelector(".fb-logic-val-pattern"),f=p.querySelector(".fb-logic-val-flags"),g=p.querySelector(".fb-logic-val-message");if(!u?.value.trim())return null;const b=c?.value||"regex",h={type:b,message:g?.value.trim()||"Invalid value."};return b==="regex"?(h.pattern=u.value.trim(),f?.value.trim()&&(h.flags=f.value.trim())):h.field=u.value.trim(),h}).filter(Boolean);a.length>0&&(t.validation=a,o=!0)}const r=i.querySelector('[data-logic-section="cascade"]');if(r){const a=r.querySelector(".fb-logic-cascade-source"),p=r.querySelector(".fb-logic-cascade-mapping"),c=r.querySelector(".fb-logic-cascade-defaults"),u=a?.value?.trim();if(u){let f={};try{f=JSON.parse(p?.value||"{}")}catch{}const g=(c?.value||"").split(`
3
- `).filter(b=>b.trim()).map(b=>{const[h,...m]=b.split(":");return{value:h.trim(),label:m.join(":").trim()||h.trim()}});t.cascade={sourceField:u,mapping:f,defaultOptions:g},o=!0}}return o?t:void 0}function L(){v=v.map((e,n)=>ye(n))}let B=null;function xe(e,n,i){const t=e.firstElementChild;if(!t)return;const o=document.createElement("span");o.setAttribute("data-icon","menu"),o.setAttribute("data-icon-size","13"),o.title="Drag to reorder",o.style.cssText="cursor:grab;opacity:.35;flex-shrink:0;display:inline-flex;align-items:center;",t.insertBefore(o,t.firstChild),window.I?.scan?.(t),t.draggable=!0,t.addEventListener("dragstart",l=>{B=n,l.dataTransfer.effectAllowed="move",e.style.opacity="0.4"}),t.addEventListener("dragend",()=>{e.style.opacity="",document.querySelectorAll("#fields-list .fb-field-card").forEach(l=>l.classList.remove("fb-drag-over"))}),e.addEventListener("dragover",l=>{B!=null&&(l.preventDefault(),l.dataTransfer.dropEffect="move",document.querySelectorAll("#fields-list .fb-field-card").forEach(s=>s.classList.remove("fb-drag-over")),e.classList.add("fb-drag-over"))}),e.addEventListener("dragleave",()=>e.classList.remove("fb-drag-over")),e.addEventListener("drop",l=>{if(l.preventDefault(),e.classList.remove("fb-drag-over"),B==null||B===n){B=null;return}L();const[s]=v.splice(B,1);v.splice(n,0,s),B=null,$(i)})}function $(e){const n=e.find("#fields-list").get(0),i=e.find("#fields-empty-msg").get(0);if(n){if(Array.from(n.querySelectorAll(".fb-field-card")).forEach(t=>t.remove()),v.length===0){i&&(i.style.display=""),Q();return}i&&(i.style.display="none"),v.forEach((t,o)=>{const l=t.type==="page-break"?Ce(t,o,e):t.type==="spacer"?Ee(t,o,e):we(t,o,e);xe(l,o,e),n.appendChild(l)}),Q()}}function Ce(e,n,i){const t=document.createElement("div");t.className="fb-field-card",t.dataset.index=n,t.style.cssText="border:2px dashed var(--dm-border,#444);border-radius:8px;overflow:hidden;margin-bottom:.75rem;background:var(--dm-card-bg,rgba(255,255,255,.02));";const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.6rem .8rem;cursor:pointer;user-select:none;";const l=document.createElement("span");l.textContent="\u2014 Page Break \u2014",l.style.cssText="font-size:.7rem;padding:.15rem .5rem;border-radius:999px;background:color-mix(in srgb, var(--dm-text-muted) 20%, transparent);color:var(--dm-text-muted,#888);white-space:nowrap;flex-shrink:0;font-style:italic;";const s=document.createElement("span");s.textContent=e.label||"Untitled Step",s.style.cssText="flex:1;font-weight:600;font-size:.9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dm-text-muted,#888);";const d=document.createElement("div");d.style.cssText="display:flex;gap:.25rem;flex-shrink:0;margin-left:.5rem;";const r=document.createElement("button");r.className="btn btn-xs btn-ghost",r.title="Edit step",w(r,"more-horizontal"),r.addEventListener("click",f=>{f.stopPropagation(),p.style.display=p.style.display==="none"?"":"none"}),d.appendChild(r);const a=document.createElement("button");a.className="btn btn-xs btn-ghost",a.style.cssText="line-height:1;opacity:.6;",a.title="Remove page break",w(a,"x"),a.addEventListener("click",async f=>{f.stopPropagation(),await E.confirm("Remove this page break?")&&(L(),v.splice(n,1),$(i))}),d.appendChild(a),o.appendChild(l),o.appendChild(s),o.appendChild(d),o.addEventListener("click",()=>{p.style.display=p.style.display==="none"?"":"none"});const p=document.createElement("div");p.className="fb-field-body",p.style.cssText="padding:.8rem;border-top:1px dashed var(--dm-border,#444);display:none;";const c=S([k("Step Title",`fb-pb-label-${n}`,"text",e.label||"","Shown as the wizard step heading"),k("Step Description",`fb-pb-desc-${n}`,"text",e.description||"","Optional sub-heading")]),u=c.querySelector(`#fb-pb-label-${n}`);return u&&u.addEventListener("input",()=>{s.textContent=u.value||"Untitled Step"}),p.appendChild(c),t.appendChild(o),t.appendChild(p),t}function Ee(e,n,i){const t=document.createElement("div");t.className="fb-field-card",t.dataset.index=n,t.style.cssText="border:1px dashed var(--dm-border,#444);border-radius:6px;margin-bottom:.5rem;background:transparent;";const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.4rem .8rem;";const l=document.createElement("div");l.style.cssText="flex:1;height:1px;background:var(--dm-border,#444);";const s=document.createElement("span");s.textContent="Spacer",s.style.cssText="font-size:.7rem;color:var(--dm-text-muted,#888);white-space:nowrap;padding:0 .4rem;font-style:italic;";const d=document.createElement("div");d.style.cssText="flex:1;height:1px;background:var(--dm-border,#444);";const r=document.createElement("div");r.style.cssText="display:flex;gap:.25rem;flex-shrink:0;";const a=document.createElement("button");return a.className="btn btn-xs btn-ghost",a.style.cssText="line-height:1;opacity:.6;",a.title="Remove spacer",w(a,"x"),a.addEventListener("click",async p=>{p.stopPropagation(),L(),v.splice(n,1),$(i)}),r.appendChild(a),o.appendChild(l),o.appendChild(s),o.appendChild(d),o.appendChild(r),t.appendChild(o),t}function we(e,n,i){const t=document.createElement("div");t.className="fb-field-card",t.dataset.index=n,t.style.cssText="border:1px solid var(--dm-border,#333);border-radius:8px;overflow:hidden;margin-bottom:.75rem;background:var(--dm-card-bg);box-shadow:var(--dm-shadow-sm);";const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.65rem .85rem;background:var(--dm-card-header-bg,rgba(255,255,255,.04));cursor:pointer;user-select:none;";const l=document.createElement("span");l.textContent=le(e.type),l.style.cssText="font-size:.7rem;padding:.15rem .45rem;border-radius:999px;background:color-mix(in srgb, var(--dm-primary) 14%, transparent);color:var(--dm-primary,#6366f1);white-space:nowrap;flex-shrink:0;";const s=document.createElement("span");s.textContent=e.label||"(unlabelled)",s.style.cssText="flex:1;font-weight:600;font-size:.9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;";const d=document.createElement("div");d.style.cssText="display:flex;gap:.25rem;flex-shrink:0;margin-left:.5rem;";const r=document.createElement("button");r.className="btn btn-xs btn-ghost",r.title="Edit field",w(r,"more-horizontal"),r.addEventListener("click",c=>{c.stopPropagation(),p.style.display=p.style.display==="none"?"":"none"}),d.appendChild(r);const a=document.createElement("button");if(a.className="btn btn-xs btn-ghost",a.style.cssText="line-height:1;opacity:.6;",a.title="Remove field",w(a,"x"),a.addEventListener("click",async c=>{c.stopPropagation(),await E.confirm("Remove this field?")&&(L(),v.splice(n,1),$(i))}),d.appendChild(a),o.appendChild(l),o.appendChild(s),e.required){const c=document.createElement("span");c.textContent="required",c.style.cssText="font-size:.7rem;color:var(--dm-danger,#ef4444);flex-shrink:0;",o.appendChild(c)}if(oe(e.logic)){const c=document.createElement("span");w(c,"zap"),c.title="Has conditional logic",c.style.cssText="font-size:.75rem;color:var(--dm-primary,#6366f1);flex-shrink:0;",o.appendChild(c)}o.appendChild(d),o.addEventListener("click",()=>{p.style.display=p.style.display==="none"?"":"none"});const p=Se(e,n,s);return p.style.display="none",t.appendChild(o),t.appendChild(p),t}function Se(e,n,i){const t=document.createElement("div");t.className="fb-field-body",t.style.cssText="padding:1rem .9rem;border-top:1px solid var(--dm-border,#333);";const o=S([k("Label",`fb-label-${n}`,"text",e.label||"","Shown above the field"),k("Field Name",`fb-name-${n}`,"text",e.name||"","Used as data key")]),l=S([_e("Type",`fb-type-${n}`,ee,e.type||"string"),ue("Required",`fb-required-${n}`,e.required||!1)]),s=S([k("Placeholder",`fb-placeholder-${n}`,"text",e.placeholder||"","Hint text inside the field")]),d=S([k("Helper Text",`fb-helper-${n}`,"text",e.helper||"","Shown below the field"),k("Tooltip",`fb-tooltip-${n}`,"text",e.tooltip||"","Shown on hover via a help icon next to the label")]),r=e.formConfig?.span,a=S([k("Column Span",`fb-span-${n}`,"number",r&&r!=="full"?String(r):"1","Columns to span (grid only)"),ue("Full Width",`fb-fullwidth-${n}`,r==="full")]);a.classList.add("fb-grid-row"),a.style.display=document.getElementById("setting-layout")?.value==="grid"?"flex":"none",t.appendChild(o),t.appendChild(l),t.appendChild(s),t.appendChild(d),t.appendChild(a);const p=t.querySelector(`#fb-label-${n}`),c=t.querySelector(`#fb-name-${n}`);p&&p.addEventListener("input",()=>{i&&(i.textContent=p.value||"(unlabelled)"),c&&!c.dataset.manuallyEdited&&(c.value=ne(p.value))}),c&&c.addEventListener("input",()=>{c.dataset.manuallyEdited="1"});const u=t.querySelector(`#fb-type-${n}`);u&&u.addEventListener("change",()=>{const g=t.closest(".fb-field-card");if(g){const m=g.querySelector("span");m&&(m.textContent=le(u.value))}const b=t.querySelector(".fb-field-extras");b&&b.remove();const h=re(u.value,e,n);h&&t.appendChild(h)});const f=re(e.type,e,n);return f&&t.appendChild(f),t.appendChild($e(e,n)),t}const ae=[{value:"equals",label:"equals"},{value:"not_equals",label:"does not equal"},{value:"contains",label:"contains"},{value:"not_contains",label:"does not contain"},{value:"starts_with",label:"starts with"},{value:"ends_with",label:"ends with"},{value:"greater_than",label:"is greater than"},{value:"less_than",label:"is less than"},{value:"is_empty",label:"is empty"},{value:"is_not_empty",label:"is not empty"},{value:"in",label:"is one of (comma sep)"},{value:"not_in",label:"is not one of (comma sep)"},{value:"matches_regex",label:"matches regex"}],ie=new Set(["is_empty","is_not_empty"]),ke={equals:"includes",not_equals:"does not include",contains:"any value contains",not_contains:"no value contains",starts_with:"any value starts with",ends_with:"any value ends with",greater_than:"any value is greater than",less_than:"any value is less than",is_empty:"nothing selected",is_not_empty:"something selected",in:"includes one of (comma sep)",not_in:"includes none of (comma sep)",matches_regex:"any value matches regex"};function se(e){return!!e&&(e.type==="checkbox-group"||!!e.multiple)}function V(e){const n=document.createElement("p");return n.textContent=e,n.style.cssText="font-size:.72rem;font-weight:700;color:var(--dm-text-muted,#888);margin:1rem 0 .45rem;text-transform:uppercase;letter-spacing:.06em;",n}function H(e,n,i,t,o){const l=document.createElement("div");l.className="fb-logic-cond-row",l.style.cssText="display:flex;gap:.35rem;align-items:center;margin-bottom:.35rem;flex-wrap:wrap;";const s=document.createElement("span");s.textContent="When",s.style.cssText="font-size:.73rem;color:var(--dm-text-muted,#888);flex-shrink:0;";const d=document.createElement("select");d.className="form-select fb-logic-cond-field",d.style.cssText="flex:1 1 140px;min-width:140px;",n.forEach(g=>{const b=document.createElement("option");b.value=g.value,b.textContent=g.label,e&&g.value===e.field&&(b.selected=!0),d.appendChild(b)});const r=document.createElement("select");r.className="form-select fb-logic-cond-op",r.style.cssText="flex:1 1 160px;min-width:140px;",ae.forEach(g=>{const b=document.createElement("option");b.value=g.value,b.textContent=g.label,e&&g.value===e.operator&&(b.selected=!0),r.appendChild(b)});const a=()=>{const g=n.find(b=>b.value===d.value);Array.from(r.options).forEach(b=>{const h=ae.find(m=>m.value===b.value);b.textContent=g?.multi&&ke[b.value]||h?.label||b.value})};a(),d.addEventListener("change",a);const p=document.createElement("input");p.type="text",p.className="form-input fb-logic-cond-val",p.placeholder="value",p.style.cssText="flex:1 1 120px;min-width:100px;",p.value=e?.value||"",e&&ie.has(e.operator)&&(p.style.display="none"),r.addEventListener("change",()=>{p.style.display=ie.has(r.value)?"none":""});const c=document.createElement("span");w(c,"arrow-right"),c.style.cssText="font-size:.73rem;color:var(--dm-text-muted,#888);flex-shrink:0;";const u=document.createElement("select");u.className=`form-select ${t}`,u.style.cssText="flex:1 1 140px;min-width:120px;",i.forEach(g=>{const b=document.createElement("option");b.value=g.value,b.textContent=g.label,g.value===o&&(b.selected=!0),u.appendChild(b)});const f=document.createElement("button");return f.type="button",f.className="btn btn-xs btn-ghost",f.style.cssText="line-height:1;opacity:.6;",w(f,"x"),f.style.flexShrink="0",f.addEventListener("click",()=>l.remove()),l.appendChild(s),l.appendChild(d),l.appendChild(r),l.appendChild(p),l.appendChild(c),l.appendChild(u),l.appendChild(f),l}function Te(e,n,i){const t=document.createElement("div");t.dataset.logicSection="visibility",t.appendChild(V("Visibility"));const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem;";const l=document.createElement("span");l.textContent="Default:",l.style.cssText="font-size:.8rem;flex-shrink:0;";const s=document.createElement("select");s.className="form-select fb-logic-vis-default",s.style.cssText="max-width:240px;",[{value:"visible",label:"Visible"},{value:"hidden",label:"Hidden"}].forEach(m=>{const y=document.createElement("option");y.value=m.value,y.textContent=m.label,m.value===(e.default||"visible")&&(y.selected=!0),s.appendChild(y)}),o.appendChild(l),o.appendChild(s),t.appendChild(o);const d=document.createElement("div");d.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem;";const r=document.createElement("span");r.textContent="Transition:",r.style.cssText="font-size:.8rem;flex-shrink:0;";const a=document.createElement("select");a.className="form-select fb-logic-vis-transition",a.style.cssText="max-width:240px;",[{value:"none",label:"None (instant)"},{value:"fade",label:"Fade"},{value:"slide",label:"Slide"},{value:"scale",label:"Scale"}].forEach(m=>{const y=document.createElement("option");y.value=m.value,y.textContent=m.label,m.value===(e.transition||"none")&&(y.selected=!0),a.appendChild(y)}),d.appendChild(r),d.appendChild(a),t.appendChild(d);const p=document.createElement("div");p.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem;";const c=document.createElement("span");c.textContent="Speed:",c.style.cssText="font-size:.8rem;flex-shrink:0;";const u=document.createElement("select");u.className="form-select fb-logic-vis-duration",u.style.cssText="max-width:240px;",[{value:"",label:"Default"},{value:"120",label:"Very fast (120ms)"},{value:"200",label:"Fast (200ms)"},{value:"350",label:"Medium (350ms)"},{value:"600",label:"Slow (600ms)"},{value:"1000",label:"Very slow (1s)"}].forEach(m=>{const y=document.createElement("option");y.value=m.value,y.textContent=m.label,m.value===String(e.duration??"")&&(y.selected=!0),u.appendChild(y)}),p.appendChild(c),p.appendChild(u),t.appendChild(p);const f=()=>{p.style.display=a.value==="none"?"none":""};f(),a.addEventListener("change",f);const g=document.createElement("div");g.className="fb-logic-vis-rules";const b=i.map(m=>({value:m.name,label:m.label||m.name,multi:se(m)})),h=[{value:"visible",label:"Show"},{value:"hidden",label:"Hide"}];if((e.conditions||[]).forEach(m=>{const y=(m.when?.all||m.when?.any||[])[0],x=m.then==="hidden"?"hidden":"visible";b.length>0&&g.appendChild(H(y,b,h,"fb-logic-vis-then",x))}),t.appendChild(g),b.length>0){const m=document.createElement("button");m.type="button",m.className="btn btn-xs btn-ghost",m.style.cssText="font-size:.73rem;margin-top:.2rem;",j(m,"plus","Add visibility rule"),m.addEventListener("click",()=>g.appendChild(H(null,b,h,"fb-logic-vis-then","visible"))),t.appendChild(m)}return t}function Ne(e,n,i){const t=document.createElement("div");t.dataset.logicSection="requirement",t.appendChild(V("Conditional Requirement"));const o=document.createElement("label");o.style.cssText="display:flex;align-items:center;gap:.4rem;font-size:.8rem;cursor:pointer;margin-bottom:.4rem;";const l=document.createElement("input");l.type="checkbox",l.className="fb-logic-req-default",l.checked=e.default===!0,o.appendChild(l),o.appendChild(document.createTextNode("Required by default")),t.appendChild(o);const s=document.createElement("div");s.className="fb-logic-req-rules";const d=i.map(a=>({value:a.name,label:a.label||a.name,multi:se(a)})),r=[{value:"true",label:"Make required"},{value:"false",label:"Make optional"}];if((e.conditions||[]).forEach(a=>{const p=(a.when?.all||a.when?.any||[])[0],c=a.then===!0?"true":"false";d.length>0&&s.appendChild(H(p,d,r,"fb-logic-req-then",c))}),t.appendChild(s),d.length>0){const a=document.createElement("button");a.type="button",a.className="btn btn-xs btn-ghost",a.style.cssText="font-size:.73rem;margin-top:.2rem;",j(a,"plus","Add requirement rule"),a.addEventListener("click",()=>s.appendChild(H(null,d,r,"fb-logic-req-then","true"))),t.appendChild(a)}return t}function ce(e){const n=document.createElement("div");n.className="fb-logic-val-rule",n.style.cssText="display:flex;gap:.35rem;align-items:center;margin-bottom:.35rem;flex-wrap:wrap;";const i=document.createElement("select");i.className="form-select fb-logic-val-type",i.style.cssText="flex:0 0 140px;",[{value:"regex",label:"Regex"},{value:"match",label:"Match field"}].forEach(d=>{const r=document.createElement("option");r.value=d.value,r.textContent=d.label,d.value===(e?.type||"regex")&&(r.selected=!0),i.appendChild(r)});const t=document.createElement("input");t.type="text",t.className="form-input fb-logic-val-pattern",t.placeholder=e?.type==="match"?"field name":"pattern",t.value=e?.pattern||e?.field||"",t.style.cssText="flex:3;";const o=document.createElement("input");o.type="text",o.className="form-input fb-logic-val-flags",o.placeholder="flags",o.value=e?.flags||"",o.style.cssText="flex:0 0 55px;",e?.type==="match"&&(o.style.display="none"),i.addEventListener("change",()=>{o.style.display=i.value==="match"?"none":"",t.placeholder=i.value==="match"?"field name":"pattern"});const l=document.createElement("input");l.type="text",l.className="form-input fb-logic-val-message",l.placeholder="Error message",l.value=e?.message||"",l.style.cssText="flex:4;";const s=document.createElement("button");return s.type="button",s.className="btn btn-xs btn-ghost",s.style.cssText="line-height:1;opacity:.6;",w(s,"x"),s.style.flexShrink="0",s.addEventListener("click",()=>n.remove()),n.appendChild(i),n.appendChild(t),n.appendChild(o),n.appendChild(l),n.appendChild(s),n}function qe(e,n,i){const t=document.createElement("div");t.dataset.logicSection="validation",t.appendChild(V("Custom Validation"));const o=document.createElement("div");o.className="fb-logic-val-rules",(e||[]).forEach(s=>o.appendChild(ce(s))),t.appendChild(o);const l=document.createElement("button");return l.type="button",l.className="btn btn-xs btn-ghost",l.style.cssText="font-size:.73rem;margin-top:.2rem;",j(l,"plus","Add validation rule"),l.addEventListener("click",()=>o.appendChild(ce(null))),t.appendChild(l),t}function Le(e,n,i){const t=document.createElement("div");t.dataset.logicSection="cascade",t.appendChild(V("Cascade Options")),t.lastChild.appendChild(Y("Make this field's options depend on another field. When the source field changes, the options here are replaced by the mapping entry for its value; if nothing matches, the default options below are shown."));const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.5rem;";const l=document.createElement("span");l.textContent="Source field:",l.style.cssText="font-size:.8rem;flex-shrink:0;";const s=document.createElement("select");s.className="form-select fb-logic-cascade-source",s.style.cssText="flex:1;";const d=document.createElement("option");d.value="",d.textContent="\u2014 none \u2014",s.appendChild(d),i.forEach(u=>{const f=document.createElement("option");f.value=u.name,f.textContent=u.label||u.name,u.name===e.sourceField&&(f.selected=!0),s.appendChild(f)}),o.appendChild(l),o.appendChild(s),t.appendChild(o);const r=document.createElement("p");r.textContent='Mapping JSON \u2014 {"value":[{"value":"...","label":"..."}]}',r.appendChild(Y(`Each key is a value of the source field; its list becomes this field's options when that value is picked. Example: {"uk":[{"value":"london","label":"London"}]} shows London when the source is "uk".`)),r.style.cssText="font-size:.73rem;color:var(--dm-text-muted,#888);margin:.3rem 0 .2rem;";const a=document.createElement("textarea");a.className="form-input fb-logic-cascade-mapping",a.rows=4,a.style.cssText="font-family:monospace;",a.placeholder='{"uk": [{"value": "london", "label": "London"}]}',a.value=e.mapping?JSON.stringify(e.mapping,null,2):"",t.appendChild(r),t.appendChild(a);const p=document.createElement("p");p.textContent="Default options (one per line: value:Label)",p.appendChild(Y("Shown before the source field has a value, or when its value has no mapping entry.")),p.style.cssText="font-size:.73rem;color:var(--dm-text-muted,#888);margin:.3rem 0 .2rem;";const c=document.createElement("textarea");return c.className="form-input fb-logic-cascade-defaults",c.rows=3,c.style.cssText="font-family:monospace;",c.placeholder=`option1:Option 1
4
- option2:Option 2`,c.value=(e.defaultOptions||[]).map(u=>{const f=typeof u=="string"?u:u.value??"",g=typeof u=="string"?u:u.label??f;return f===g?f:`${f}:${g}`}).join(`
5
- `),t.appendChild(p),t.appendChild(c),t}function $e(e,n){const i=e.logic||{},t=v.filter((c,u)=>u!==n&&c.type!=="page-break"&&c.type!=="spacer"),o=document.createElement("div");o.className="fb-field-logic",o.style.cssText="margin-top:.75rem;border-top:1px solid var(--dm-border,#333);padding-top:.5rem;";const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;justify-content:space-between;cursor:pointer;padding:.15rem 0;";const s=document.createElement("span");s.style.cssText="font-size:.8rem;font-weight:600;color:var(--dm-text-muted,#888);",s.textContent="\u26A1 Conditional Logic";const d=oe(i),r=document.createElement("button");r.type="button",r.className="btn btn-xs btn-ghost",w(r,d?"chevron-down":"chevron-right",12);const a=document.createElement("div");a.className="fb-logic-body",a.style.cssText="padding:.25rem 0 .25rem;"+(d?"":"display:none;"),l.addEventListener("click",()=>{const c=a.style.display==="none";a.style.display=c?"":"none",w(r,c?"chevron-down":"chevron-right",12)}),l.appendChild(s),l.appendChild(r),o.appendChild(l),a.appendChild(Te(i.visibility||{},n,t)),a.appendChild(Ne(i.requirement||{},n,t)),a.appendChild(qe(i.validation||[],n,t));const p=document.getElementById(`fb-type-${n}`)?.value||e.type;return G.has(p)&&a.appendChild(Le(i.cascade||{},n,t)),o.appendChild(a),o}function re(e,n,i){const t=document.createElement("div");return t.className="fb-field-extras",e==="chooser"?(t.appendChild(Fe(n,i)),t.appendChild(Ie(n.options||[],i))):(G.has(e)&&t.appendChild(Pe(n.options||[],i)),e==="textarea"&&t.appendChild(S([k("Rows",`fb-rows-${i}`,"number",n.formConfig?.rows||4,"Height of textarea")])),(e==="string"||e==="textarea")&&t.appendChild(S([k("Min Length",`fb-minlength-${i}`,"number",n.minLength||"",""),k("Max Length",`fb-maxlength-${i}`,"number",n.maxLength||"","")])),e==="number"&&t.appendChild(S([k("Min",`fb-min-${i}`,"number",n.min??"",""),k("Max",`fb-max-${i}`,"number",n.max??"","")]))),t.children.length?t:null}function de(e){const n=document.createElement("div");n.className="fb-chooser-section",n.style.cssText="border:1px solid var(--dm-border, #e5e7eb); border-radius:6px; margin-top:.5rem; overflow:hidden;";const i=document.createElement("div");i.style.cssText="padding:.4rem .6rem; background:color-mix(in srgb, var(--dm-background, #f9fafb) 60%, transparent); font-weight:600; font-size:.82rem; border-bottom:1px solid var(--dm-border, #e5e7eb);",i.textContent=e,n.appendChild(i);const t=document.createElement("div");return t.className="fb-chooser-section-body",t.style.cssText="padding:.55rem .6rem; display:flex; flex-direction:column; gap:.45rem;",n.appendChild(t),{wrap:n,body:t}}function U(e,n,i,t,o){const l=document.createElement("div");l.style.flex="1";const s=document.createElement("label");s.htmlFor=e,s.className="form-label",s.textContent=n,s.style.fontSize=".8rem";const d=document.createElement("select");if(d.id=e,d.className="form-input",t.forEach(([r,a])=>{const p=document.createElement("option");p.value=r,p.textContent=a,String(r)===String(i)&&(p.selected=!0),d.appendChild(p)}),l.appendChild(s),l.appendChild(d),o){const r=document.createElement("p");r.className="form-hint text-muted",r.textContent=o,r.style.cssText="font-size:.72rem;margin-top:.2rem;",l.appendChild(r)}return l}function pe(e,n,i,t){const o=document.createElement("div");o.style.flex="1";const l=document.createElement("label");l.style.cssText="display:inline-flex;align-items:center;gap:.4rem;font-size:.82rem;";const s=document.createElement("input");if(s.id=e,s.type="checkbox",s.checked=!!i,l.appendChild(s),l.appendChild(document.createTextNode(" "+n)),o.appendChild(l),t){const d=document.createElement("p");d.className="form-hint text-muted",d.textContent=t,d.style.cssText="font-size:.72rem;margin-top:.2rem;",o.appendChild(d)}return o}function X(e,n,i,t,o){const l=document.createElement("div");l.style.flex="1";const s=document.createElement("label");s.htmlFor=e,s.className="form-label",s.textContent=n,s.style.fontSize=".8rem",l.appendChild(s);const d=document.createElement("div");d.style.cssText="display:flex;gap:.4rem;align-items:stretch;";const r=document.createElement("input");r.id=e,r.type="text",r.className="form-input",r.value=i||"",t&&(r.placeholder=t),r.style.flex="1";const a=document.createElement("input");a.type="color",a.title="Pick a colour",a.style.cssText="width:36px;height:auto;padding:0;border:1px solid var(--dm-border,#e5e7eb);border-radius:4px;cursor:pointer;";const p=String(r.value).match(/^#[0-9a-fA-F]{3,8}$/);if(a.value=p?r.value:"#000000",a.addEventListener("input",()=>{r.value=a.value,r.dispatchEvent(new Event("input",{bubbles:!0}))}),d.appendChild(r),d.appendChild(a),l.appendChild(d),o){const c=document.createElement("p");c.className="form-hint text-muted",c.textContent=o,c.style.cssText="font-size:.72rem;margin-top:.2rem;",l.appendChild(c)}return l}function Fe(e,n){const i=document.createElement("div");i.style.cssText="margin-top:.5rem; display:flex; flex-direction:column; gap:.4rem;";const t=de("Layout");t.body.appendChild(S([U(`fb-chooser-variant-${n}`,"Variant",e.variant||"card",[["card","Card \u2014 rich tiles"],["chip","Chip \u2014 compact pills"]],"Card supports description and grid columns; chips wrap."),pe(`fb-chooser-multiple-${n}`,"Multi-select",!!e.multiple,"Off = single (radio); on = multi (checkbox).")])),t.body.appendChild(S([U(`fb-chooser-density-${n}`,"Density",e.density||"comfortable",[["comfortable","Comfortable"],["compact","Compact"]],"Compact strips description (card variant only)."),k("Columns",`fb-chooser-columns-${n}`,"number",e.columns||3,"Card grid (1\u20136). Ignored for chips.")])),i.appendChild(t.wrap);const o=de("Visual");return o.body.appendChild(S([X(`fb-chooser-accent-${n}`,"Accent",e.accent||"","primary / success / #ec4899","Selected highlight colour \u2014 semantic name or any CSS colour."),U(`fb-chooser-accent-style-${n}`,"Accent style",e.accentStyle||"border",[["border","Border (default)"],["solid","Solid"],["glow","Glow"],["overlay","Overlay"],["underline","Underline"]],"Visual treatment of the selected state.")])),o.body.appendChild(S([pe(`fb-chooser-glow-${n}`,"Glow on selected",!!e.glow,"Soft outer glow."),X(`fb-chooser-glow-colour-${n}`,"Glow colour",e.glowColour||"","defaults to accent","Optional; semantic name or CSS colour.")])),o.body.appendChild(S([U(`fb-chooser-shadow-${n}`,"Shadow",e.shadow||"none",[["none","None"],["sm","Small"],["md","Medium"],["lg","Large"],["xl","Extra Large"]],"Drop shadow on every option."),X(`fb-chooser-shadow-colour-${n}`,"Shadow colour",e.shadowColour||"","rgba(0,0,0,0.1)","Optional shadow tint.")])),i.appendChild(o.wrap),i}function ze(e,n,i,t){const o=document.createElement("div");o.className="fb-chooser-option-row",o.dataset.optIdx=String(i),o.style.cssText="border:1px solid var(--dm-border,#e5e7eb); border-radius:4px; padding:.5rem; background:var(--dm-background-alt, color-mix(in srgb, var(--dm-card-bg, #fff) 92%, var(--dm-text, #111))); display:flex; flex-direction:column; gap:.35rem;";const l=document.createElement("div");l.style.cssText="display:flex; gap:.4rem; align-items:center;";const s=document.createElement("div");s.style.cssText="display:flex; flex-direction:column; gap:1px;";const d=document.createElement("button");d.type="button",d.className="btn btn-xs btn-ghost",d.title="Move up",w(d,"arrow-up"),d.addEventListener("click",N=>{N.preventDefault(),me(n,i,-1,t)});const r=document.createElement("button");r.type="button",r.className="btn btn-xs btn-ghost",r.title="Move down",w(r,"arrow-down"),r.addEventListener("click",N=>{N.preventDefault(),me(n,i,1,t)}),s.appendChild(d),s.appendChild(r),l.appendChild(s);const a=document.createElement("input");a.type="text",a.className="form-input fb-chooser-opt-value",a.placeholder="value",a.value=e.value||"",a.style.flex="1",l.appendChild(a);const p=document.createElement("input");p.type="text",p.className="form-input fb-chooser-opt-label",p.placeholder="label (visible to users)",p.value=e.label||"",p.style.flex="2",l.appendChild(p);const c=document.createElement("button");c.type="button",c.className="btn btn-xs btn-ghost",c.style.cssText="line-height:1;opacity:.6;",c.title="Remove option",w(c,"x"),c.addEventListener("click",N=>{N.preventDefault(),Oe(n,i,t)}),l.appendChild(c),o.appendChild(l);const u=document.createElement("div");u.style.cssText="display:flex; gap:.4rem;";const f=ge("icon name (optional)",e.icon||"");f.input.classList.add("fb-chooser-opt-icon"),f.el.style.flex="1",u.appendChild(f.el);const g=document.createElement("input");g.type="text",g.className="form-input fb-chooser-opt-description",g.placeholder="description (card + comfortable only)",g.value=e.description||"",g.style.flex="2",u.appendChild(g),o.appendChild(u);const b=document.createElement("div");b.style.cssText="display:flex; gap:.4rem;";const h=document.createElement("input");h.type="text",h.className="form-input fb-chooser-opt-tooltip",h.placeholder="tooltip (hover hint)",h.value=e.tooltip||"",h.style.flex="2",b.appendChild(h);const m=document.createElement("input");m.type="text",m.className="form-input fb-chooser-opt-badge-text",m.placeholder="badge text",m.value=e.badge?.text||"",m.style.flex="1",b.appendChild(m);const y=document.createElement("select");y.className="form-input fb-chooser-opt-badge-type",y.style.flex="1",[["","no badge"],["primary","primary"],["success","success"],["info","info"],["warning","warning"],["danger","danger"]].forEach(([N,O])=>{const _=document.createElement("option");_.value=N,_.textContent=O,(e.badge?.type||"")===N&&(_.selected=!0),y.appendChild(_)}),b.appendChild(y),o.appendChild(b);const x=document.createElement("div");x.style.cssText="display:flex; gap:1rem; font-size:.8rem;";const C=document.createElement("label");C.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const T=document.createElement("input");T.type="checkbox",T.className="fb-chooser-opt-recommended",T.checked=!!e.recommended,C.appendChild(T),C.appendChild(document.createTextNode(" Recommended")),x.appendChild(C);const z=document.createElement("label");z.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const I=document.createElement("input");return I.type="checkbox",I.className="fb-chooser-opt-disabled",I.checked=!!e.disabled,z.appendChild(I),z.appendChild(document.createTextNode(" Disabled")),x.appendChild(z),o.appendChild(x),o}function Ie(e,n){const i=document.createElement("div");i.style.cssText="margin-top:.5rem;";const t=document.createElement("div");t.style.cssText="display:flex; align-items:center; justify-content:space-between; margin-bottom:.4rem;";const o=document.createElement("p");o.textContent="Options",o.style.cssText="font-size:.85rem;font-weight:600;margin:0;",t.appendChild(o);const l=document.createElement("button");l.type="button",l.className="btn btn-xs btn-ghost",j(l,"plus","Add option"),t.appendChild(l),i.appendChild(t);const s=document.createElement("div");s.className="fb-chooser-options-list",s.id=`fb-chooser-options-${n}`,s.style.cssText="display:flex; flex-direction:column; gap:.4rem;",i.appendChild(s);const d=()=>{s.replaceChildren();const r=Be(n);(r.length?r:e||[]).forEach((p,c)=>{s.appendChild(ze(p,n,c,d))})};return l.addEventListener("click",r=>{r.preventDefault(),Ae(n,d)}),d(),i}function Be(e){const n=v[e];return!n||!Array.isArray(n.options)?[]:n.options}function K(e){return v[e]?(Array.isArray(v[e].options)||(v[e].options=[]),v[e].options):[]}function Ae(e,n){W(e),K(e).push({value:"new",label:"New option"}),n()}function Oe(e,n,i){W(e),K(e).splice(n,1),i()}function me(e,n,i,t){W(e);const o=K(e),l=n+i;if(l<0||l>=o.length)return;const[s]=o.splice(n,1);o.splice(l,0,s),t()}function W(e){const n=document.getElementById(`fb-chooser-options-${e}`);if(!n)return;const i=n.querySelectorAll(".fb-chooser-option-row"),t=[];i.forEach(o=>{const l={value:o.querySelector(".fb-chooser-opt-value")?.value.trim()||"",label:o.querySelector(".fb-chooser-opt-label")?.value.trim()||""},s=o.querySelector(".fb-chooser-opt-icon")?.value.trim();s&&(l.icon=s);const d=o.querySelector(".fb-chooser-opt-description")?.value.trim();d&&(l.description=d);const r=o.querySelector(".fb-chooser-opt-tooltip")?.value.trim();r&&(l.tooltip=r);const a=o.querySelector(".fb-chooser-opt-badge-text")?.value.trim(),p=o.querySelector(".fb-chooser-opt-badge-type")?.value;(a||p)&&(l.badge={},a&&(l.badge.text=a),p&&(l.badge.type=p)),o.querySelector(".fb-chooser-opt-recommended")?.checked&&(l.recommended=!0),o.querySelector(".fb-chooser-opt-disabled")?.checked&&(l.disabled=!0),t.push(l)}),v[e]&&(v[e].options=t)}function S(e){const n=document.createElement("div");return n.style.cssText="display:flex;gap:.85rem;margin-bottom:.75rem;",e.forEach(i=>{i&&n.appendChild(i)}),n}function k(e,n,i,t,o){const l=document.createElement("div");l.style.flex="1";const s=document.createElement("label");s.htmlFor=n,s.className="form-label",s.textContent=e,s.style.fontSize=".8rem";const d=document.createElement("input");if(d.id=n,d.type=i||"text",d.className="form-input",d.value=t??"",l.appendChild(s),l.appendChild(d),o){const r=document.createElement("p");r.className="form-hint text-muted",r.textContent=o,r.style.cssText="font-size:.73rem;margin-top:.2rem;",l.appendChild(r)}return l}function _e(e,n,i,t){const o=document.createElement("div");o.style.flex="1";const l=document.createElement("label");l.htmlFor=n,l.className="form-label",l.textContent=e,l.style.fontSize=".8rem";const s=document.createElement("select");return s.id=n,s.className="form-select",i.forEach(d=>{const r=document.createElement("option");r.value=d.value,r.textContent=d.label,d.value===t&&(r.selected=!0),s.appendChild(r)}),o.appendChild(l),o.appendChild(s),o}function ue(e,n,i){const t=document.createElement("div");t.style.cssText="flex:0;min-width:80px;display:flex;flex-direction:column;justify-content:flex-end;";const o=document.createElement("label");o.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;font-size:.8rem;white-space:nowrap;";const l=document.createElement("input");return l.id=n,l.type="checkbox",l.checked=i,o.appendChild(l),o.appendChild(document.createTextNode(e)),t.appendChild(o),t}function Pe(e,n){const i=document.createElement("div");i.style.cssText="margin-top:.4rem;";const t=document.createElement("p");t.textContent="Options (one per line: value or value:Label)",t.style.cssText="font-size:.8rem;font-weight:600;margin-bottom:.3rem;";const o=document.createElement("textarea");return o.id=`fb-options-${n}`,o.className="form-input",o.rows=4,o.placeholder=`yes:Yes
1
+ import{apiRequest as A}from"/admin/js/api.js";import{makeIconInput as ge}from"/admin/js/lib/shortcode-modal.js";import{enhancePickerFields as Z}from"/public/js/forms.js";const ee=[{value:"string",label:"Text (single line)"},{value:"email",label:"Email"},{value:"tel",label:"Phone"},{value:"number",label:"Number"},{value:"textarea",label:"Textarea (multi-line)"},{value:"select",label:"Dropdown (select)"},{value:"radio",label:"Radio buttons"},{value:"checkbox",label:"Single checkbox"},{value:"checkbox-group",label:"Checkbox group"},{value:"chooser",label:"Chooser (visual options)"},{value:"icon",label:"Icon (icon picker)"},{value:"image",label:"Image (media library)"},{value:"date",label:"Date"},{value:"time",label:"Time"},{value:"url",label:"URL"},{value:"password",label:"Password"},{value:"file",label:"File upload"},{value:"hidden",label:"Hidden field"}],J=new Set(["select","radio","checkbox-group","chooser"]);let v=[],L=null,P=null,M=null,G={};function w(e,t,a=14){e.textContent="";const n=document.createElement("span");return n.setAttribute("data-icon",t),n.setAttribute("data-icon-size",String(a)),n.style.cssText="display:inline-flex;align-items:center;vertical-align:middle;",e.appendChild(n),window.I?.scan?.(e),e}function j(e,t,a,n=14){return w(e,t,n),e.appendChild(document.createTextNode(" "+a)),e}function Y(e){const t=document.createElement("span");return t.setAttribute("data-icon","help-circle"),t.setAttribute("data-icon-size","12"),t.style.cssText="display:inline-flex;align-items:center;vertical-align:middle;margin-left:.3rem;opacity:.55;cursor:help;",t.title=e,window.I?.scan?.(t),requestAnimationFrame(()=>{try{t.parentNode&&typeof E<"u"&&E.tooltip&&(t.removeAttribute("title"),E.tooltip(t,{content:e,position:"top"}))}catch{}}),t}let D=null,te=null;function Q(){clearTimeout(te),te=setTimeout(()=>{D&&D()},350)}function ne(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_|_$/g,"")}function le(e){return ee.find(t=>t.value===e)?.label||e}function oe(e){return e?!!(e.visibility?.conditions?.length||e.requirement?.conditions?.length||e.validation?.length||e.cascade?.sourceField):!1}function ye(e){const t={...v[e]};if(t.type==="spacer")return t;if(t.type==="page-break"){const g=document.getElementById(`fb-pb-label-${e}`),u=document.getElementById(`fb-pb-desc-${e}`);return g&&(t.label=g.value.trim()||t.label),u&&(t.description=u.value.trim()),t}const a=document.getElementById(`fb-label-${e}`),n=document.getElementById(`fb-name-${e}`),l=document.getElementById(`fb-type-${e}`),o=document.getElementById(`fb-required-${e}`),s=document.getElementById(`fb-placeholder-${e}`),r=document.getElementById(`fb-helper-${e}`),c=document.getElementById(`fb-tooltip-${e}`);if(a&&(t.label=a.value.trim()||t.label),n&&(t.name=n.value.trim()||t.name),l&&(t.type=l.value||t.type),o&&(t.required=o.checked),s&&(t.placeholder=s.value.trim()),r&&(t.helper=r.value.trim()),c&&(t.tooltip=c.value.trim()),t.type==="chooser"){const g=document.getElementById(`fb-chooser-variant-${e}`)?.value;g&&(t.variant=g);const u=document.getElementById(`fb-chooser-multiple-${e}`);u&&(t.multiple=u.checked);const y=document.getElementById(`fb-chooser-density-${e}`)?.value;y&&(t.density=y);const x=document.getElementById(`fb-chooser-columns-${e}`)?.value;x!==""&&x!==void 0&&(t.columns=parseInt(x,10)||3);const S=document.getElementById(`fb-chooser-accent-${e}`)?.value.trim();S?t.accent=S:delete t.accent;const C=document.getElementById(`fb-chooser-accent-style-${e}`)?.value;C?t.accentStyle=C:delete t.accentStyle;const N=document.getElementById(`fb-chooser-glow-${e}`);N&&(t.glow=N.checked);const I=document.getElementById(`fb-chooser-glow-colour-${e}`)?.value.trim();I?t.glowColour=I:delete t.glowColour;const q=document.getElementById(`fb-chooser-shadow-${e}`)?.value;q&&q!=="none"?t.shadow=q:delete t.shadow;const O=document.getElementById(`fb-chooser-shadow-colour-${e}`)?.value.trim();O?t.shadowColour=O:delete t.shadowColour,W(e),v[e]&&Array.isArray(v[e].options)&&(t.options=v[e].options)}else if(J.has(t.type)){["variant","density","columns","accent","accentStyle","glow","glowColour","shadow","shadowColour"].forEach(u=>delete t[u]),t.type!=="checkbox-group"&&t.type!=="multiselect"&&delete t.multiple;const g=document.getElementById(`fb-options-${e}`);g&&(t.options=g.value.split(`
2
+ `).filter(u=>u.trim()).map(u=>{const[y,...x]=u.split(":");return{value:y.trim(),label:x.join(":").trim()||y.trim()}}))}if(t.type==="textarea"){const g=parseInt(document.getElementById(`fb-rows-${e}`)?.value,10);g>0&&(t.formConfig={...t.formConfig||{},rows:g})}const d=document.getElementById(`fb-span-${e}`),i=document.getElementById(`fb-fullwidth-${e}`);if(d||i){const g={...t.formConfig||{}};if(i?.checked)g.span="full";else{const u=parseInt(d?.value,10);u>1?g.span=u:delete g.span}t.formConfig=Object.keys(g).length?g:void 0}const p=document.getElementById(`fb-minlength-${e}`)?.value;p&&(t.minLength=parseInt(p,10));const m=document.getElementById(`fb-maxlength-${e}`)?.value;m&&(t.maxLength=parseInt(m,10));const f=document.getElementById(`fb-min-${e}`)?.value;f!==""&&f!==void 0&&(t.min=parseFloat(f));const b=document.getElementById(`fb-max-${e}`)?.value;b!==""&&b!==void 0&&(t.max=parseFloat(b));const h=ve(e);return h?t.logic=h:delete t.logic,t}function ve(e){const t=document.querySelector(`.fb-field-card[data-index="${e}"]`);if(!t)return;const a=t.querySelector(".fb-field-logic");if(!a)return;const n={};let l=!1;const o=a.querySelector('[data-logic-section="visibility"]');if(o){const d=o.querySelector(".fb-logic-vis-default"),i=o.querySelector(".fb-logic-vis-transition"),p=Array.from(o.querySelectorAll(".fb-logic-cond-row")).map(h=>{const g=h.querySelector(".fb-logic-cond-field"),u=h.querySelector(".fb-logic-cond-op"),y=h.querySelector(".fb-logic-cond-val"),x=h.querySelector(".fb-logic-vis-then");return g?.value?{when:{all:[{field:g.value,operator:u.value,value:y.value}]},then:x.value}:null}).filter(Boolean),m=d?.value||"visible",f=i?.value||"none",b=parseInt(o.querySelector(".fb-logic-vis-duration")?.value||"",10);(m!=="visible"||p.length>0||f!=="none")&&(n.visibility={default:m,conditions:p},f!=="none"&&(n.visibility.transition=f),f!=="none"&&Number.isFinite(b)&&(n.visibility.duration=b),l=!0)}const s=a.querySelector('[data-logic-section="requirement"]');if(s){const d=s.querySelector(".fb-logic-req-default"),i=Array.from(s.querySelectorAll(".fb-logic-cond-row")).map(p=>{const m=p.querySelector(".fb-logic-cond-field"),f=p.querySelector(".fb-logic-cond-op"),b=p.querySelector(".fb-logic-cond-val"),h=p.querySelector(".fb-logic-req-then");return m?.value?{when:{all:[{field:m.value,operator:f.value,value:b.value}]},then:h.value==="true"}:null}).filter(Boolean);i.length>0&&(n.requirement={default:d?.checked===!0,conditions:i},l=!0)}const r=a.querySelector('[data-logic-section="validation"]');if(r){const d=Array.from(r.querySelectorAll(".fb-logic-val-rule")).map(i=>{const p=i.querySelector(".fb-logic-val-type"),m=i.querySelector(".fb-logic-val-pattern"),f=i.querySelector(".fb-logic-val-flags"),b=i.querySelector(".fb-logic-val-message");if(!m?.value.trim())return null;const h=p?.value||"regex",g={type:h,message:b?.value.trim()||"Invalid value."};return h==="regex"?(g.pattern=m.value.trim(),f?.value.trim()&&(g.flags=f.value.trim())):g.field=m.value.trim(),g}).filter(Boolean);d.length>0&&(n.validation=d,l=!0)}const c=a.querySelector('[data-logic-section="cascade"]');if(c){const d=c.querySelector(".fb-logic-cascade-source"),i=c.querySelector(".fb-logic-cascade-mapping"),p=c.querySelector(".fb-logic-cascade-defaults"),m=d?.value?.trim();if(m){let f={};try{f=JSON.parse(i?.value||"{}")}catch{}const b=(p?.value||"").split(`
3
+ `).filter(h=>h.trim()).map(h=>{const[g,...u]=h.split(":");return{value:g.trim(),label:u.join(":").trim()||g.trim()}});n.cascade={sourceField:m,mapping:f,defaultOptions:b},l=!0}}return l?n:void 0}function $(){v=v.map((e,t)=>ye(t))}let B=null;function xe(e,t,a){const n=e.firstElementChild;if(!n)return;const l=document.createElement("span");l.setAttribute("data-icon","menu"),l.setAttribute("data-icon-size","13"),l.title="Drag to reorder",l.style.cssText="cursor:grab;opacity:.35;flex-shrink:0;display:inline-flex;align-items:center;",n.insertBefore(l,n.firstChild),window.I?.scan?.(n),n.draggable=!0,n.addEventListener("dragstart",o=>{B=t,o.dataTransfer.effectAllowed="move",e.style.opacity="0.4"}),n.addEventListener("dragend",()=>{e.style.opacity="",document.querySelectorAll("#fields-list .fb-field-card").forEach(o=>o.classList.remove("fb-drag-over"))}),e.addEventListener("dragover",o=>{B!=null&&(o.preventDefault(),o.dataTransfer.dropEffect="move",document.querySelectorAll("#fields-list .fb-field-card").forEach(s=>s.classList.remove("fb-drag-over")),e.classList.add("fb-drag-over"))}),e.addEventListener("dragleave",()=>e.classList.remove("fb-drag-over")),e.addEventListener("drop",o=>{if(o.preventDefault(),e.classList.remove("fb-drag-over"),B==null||B===t){B=null;return}$();const[s]=v.splice(B,1);v.splice(t,0,s),B=null,z(a)})}function z(e){const t=e.find("#fields-list").get(0),a=e.find("#fields-empty-msg").get(0);if(t){if(Array.from(t.querySelectorAll(".fb-field-card")).forEach(n=>n.remove()),v.length===0){a&&(a.style.display=""),Q();return}a&&(a.style.display="none"),v.forEach((n,l)=>{const o=n.type==="page-break"?Ce(n,l,e):n.type==="spacer"?Ee(n,l,e):we(n,l,e);xe(o,l,e),t.appendChild(o)}),Q()}}function Ce(e,t,a){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:2px dashed var(--dm-border,#444);border-radius:8px;overflow:hidden;margin-bottom:.75rem;background:var(--dm-card-bg,rgba(255,255,255,.02));";const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.6rem .8rem;cursor:pointer;user-select:none;";const o=document.createElement("span");o.textContent="- Page Break -",o.style.cssText="font-size:.7rem;padding:.15rem .5rem;border-radius:999px;background:color-mix(in srgb, var(--dm-text-muted) 20%, transparent);color:var(--dm-text-muted,#888);white-space:nowrap;flex-shrink:0;font-style:italic;";const s=document.createElement("span");s.textContent=e.label||"Untitled Step",s.style.cssText="flex:1;font-weight:600;font-size:.9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dm-text-muted,#888);";const r=document.createElement("div");r.style.cssText="display:flex;gap:.25rem;flex-shrink:0;margin-left:.5rem;";const c=document.createElement("button");c.className="btn btn-xs btn-ghost",c.title="Edit step",w(c,"more-horizontal"),c.addEventListener("click",f=>{f.stopPropagation(),i.style.display=i.style.display==="none"?"":"none"}),r.appendChild(c);const d=document.createElement("button");d.className="btn btn-xs btn-ghost",d.style.cssText="line-height:1;opacity:.6;",d.title="Remove page break",w(d,"x"),d.addEventListener("click",async f=>{f.stopPropagation(),await E.confirm("Remove this page break?")&&($(),v.splice(t,1),z(a))}),r.appendChild(d),l.appendChild(o),l.appendChild(s),l.appendChild(r),l.addEventListener("click",()=>{i.style.display=i.style.display==="none"?"":"none"});const i=document.createElement("div");i.className="fb-field-body",i.style.cssText="padding:.8rem;border-top:1px dashed var(--dm-border,#444);display:none;";const p=k([T("Step Title",`fb-pb-label-${t}`,"text",e.label||"","Shown as the wizard step heading"),T("Step Description",`fb-pb-desc-${t}`,"text",e.description||"","Optional sub-heading")]),m=p.querySelector(`#fb-pb-label-${t}`);return m&&m.addEventListener("input",()=>{s.textContent=m.value||"Untitled Step"}),i.appendChild(p),n.appendChild(l),n.appendChild(i),n}function Ee(e,t,a){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:1px dashed var(--dm-border,#444);border-radius:6px;margin-bottom:.5rem;background:transparent;";const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.4rem .8rem;";const o=document.createElement("div");o.style.cssText="flex:1;height:1px;background:var(--dm-border,#444);";const s=document.createElement("span");s.textContent="Spacer",s.style.cssText="font-size:.7rem;color:var(--dm-text-muted,#888);white-space:nowrap;padding:0 .4rem;font-style:italic;";const r=document.createElement("div");r.style.cssText="flex:1;height:1px;background:var(--dm-border,#444);";const c=document.createElement("div");c.style.cssText="display:flex;gap:.25rem;flex-shrink:0;";const d=document.createElement("button");return d.className="btn btn-xs btn-ghost",d.style.cssText="line-height:1;opacity:.6;",d.title="Remove spacer",w(d,"x"),d.addEventListener("click",async i=>{i.stopPropagation(),$(),v.splice(t,1),z(a)}),c.appendChild(d),l.appendChild(o),l.appendChild(s),l.appendChild(r),l.appendChild(c),n.appendChild(l),n}function we(e,t,a){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:1px solid var(--dm-border,#333);border-radius:8px;overflow:hidden;margin-bottom:.75rem;background:var(--dm-card-bg);box-shadow:var(--dm-shadow-sm);";const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.65rem .85rem;background:var(--dm-card-header-bg,rgba(255,255,255,.04));cursor:pointer;user-select:none;";const o=document.createElement("span");o.textContent=le(e.type),o.style.cssText="font-size:.7rem;padding:.15rem .45rem;border-radius:999px;background:color-mix(in srgb, var(--dm-primary) 14%, transparent);color:var(--dm-primary,#6366f1);white-space:nowrap;flex-shrink:0;";const s=document.createElement("span");s.textContent=e.label||"(unlabelled)",s.style.cssText="flex:1;font-weight:600;font-size:.9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;";const r=document.createElement("div");r.style.cssText="display:flex;gap:.25rem;flex-shrink:0;margin-left:.5rem;";const c=document.createElement("button");c.className="btn btn-xs btn-ghost",c.title="Edit field",w(c,"more-horizontal"),c.addEventListener("click",p=>{p.stopPropagation(),i.style.display=i.style.display==="none"?"":"none"}),r.appendChild(c);const d=document.createElement("button");if(d.className="btn btn-xs btn-ghost",d.style.cssText="line-height:1;opacity:.6;",d.title="Remove field",w(d,"x"),d.addEventListener("click",async p=>{p.stopPropagation(),await E.confirm("Remove this field?")&&($(),v.splice(t,1),z(a))}),r.appendChild(d),l.appendChild(o),l.appendChild(s),e.required){const p=document.createElement("span");p.textContent="required",p.style.cssText="font-size:.7rem;color:var(--dm-danger,#ef4444);flex-shrink:0;",l.appendChild(p)}if(oe(e.logic)){const p=document.createElement("span");w(p,"zap"),p.title="Has conditional logic",p.style.cssText="font-size:.75rem;color:var(--dm-primary,#6366f1);flex-shrink:0;",l.appendChild(p)}l.appendChild(r),l.addEventListener("click",()=>{i.style.display=i.style.display==="none"?"":"none"});const i=Se(e,t,s);return i.style.display="none",n.appendChild(l),n.appendChild(i),n}function Se(e,t,a){const n=document.createElement("div");n.className="fb-field-body",n.style.cssText="padding:1rem .9rem;border-top:1px solid var(--dm-border,#333);";const l=k([T("Label",`fb-label-${t}`,"text",e.label||"","Shown above the field"),T("Field Name",`fb-name-${t}`,"text",e.name||"","Used as data key")]),o=k([_e("Type",`fb-type-${t}`,ee,e.type||"string"),ue("Required",`fb-required-${t}`,e.required||!1)]),s=k([T("Placeholder",`fb-placeholder-${t}`,"text",e.placeholder||"","Hint text inside the field")]),r=k([T("Helper Text",`fb-helper-${t}`,"text",e.helper||"","Shown below the field"),T("Tooltip",`fb-tooltip-${t}`,"text",e.tooltip||"","Shown on hover via a help icon next to the label")]),c=e.formConfig?.span,d=k([T("Column Span",`fb-span-${t}`,"number",c&&c!=="full"?String(c):"1","Columns to span (grid only)"),ue("Full Width",`fb-fullwidth-${t}`,c==="full")]);d.classList.add("fb-grid-row"),d.style.display=document.getElementById("setting-layout")?.value==="grid"?"flex":"none",n.appendChild(l),n.appendChild(o),n.appendChild(s),n.appendChild(r),n.appendChild(d);const i=n.querySelector(`#fb-label-${t}`),p=n.querySelector(`#fb-name-${t}`);i&&i.addEventListener("input",()=>{a&&(a.textContent=i.value||"(unlabelled)"),p&&!p.dataset.manuallyEdited&&(p.value=ne(i.value))}),p&&p.addEventListener("input",()=>{p.dataset.manuallyEdited="1"});const m=n.querySelector(`#fb-type-${t}`);m&&m.addEventListener("change",()=>{const b=n.closest(".fb-field-card");if(b){const u=b.querySelector("span");u&&(u.textContent=le(m.value))}const h=n.querySelector(".fb-field-extras");h&&h.remove();const g=re(m.value,e,t);g&&n.appendChild(g)});const f=re(e.type,e,t);return f&&n.appendChild(f),n.appendChild($e(e,t)),n}const ae=[{value:"equals",label:"equals"},{value:"not_equals",label:"does not equal"},{value:"contains",label:"contains"},{value:"not_contains",label:"does not contain"},{value:"starts_with",label:"starts with"},{value:"ends_with",label:"ends with"},{value:"greater_than",label:"is greater than"},{value:"less_than",label:"is less than"},{value:"is_empty",label:"is empty"},{value:"is_not_empty",label:"is not empty"},{value:"in",label:"is one of (comma sep)"},{value:"not_in",label:"is not one of (comma sep)"},{value:"matches_regex",label:"matches regex"}],ie=new Set(["is_empty","is_not_empty"]),ke={equals:"includes",not_equals:"does not include",contains:"any value contains",not_contains:"no value contains",starts_with:"any value starts with",ends_with:"any value ends with",greater_than:"any value is greater than",less_than:"any value is less than",is_empty:"nothing selected",is_not_empty:"something selected",in:"includes one of (comma sep)",not_in:"includes none of (comma sep)",matches_regex:"any value matches regex"};function se(e){return!!e&&(e.type==="checkbox-group"||!!e.multiple)}function V(e){const t=document.createElement("p");return t.textContent=e,t.style.cssText="font-size:.75rem;font-weight:700;color:var(--dm-text-muted,#888);margin:0 0 .7rem;text-transform:uppercase;letter-spacing:.06em;",t}function H(e,t,a,n,l){const o=document.createElement("div");o.className="fb-logic-cond-row",o.style.cssText="display:flex;gap:.55rem;align-items:center;margin-bottom:.6rem;flex-wrap:wrap;";const s=document.createElement("span");s.textContent="When",s.style.cssText="font-size:.73rem;color:var(--dm-text-muted,#888);flex-shrink:0;";const r=document.createElement("select");r.className="form-select fb-logic-cond-field",r.style.cssText="flex:1 1 140px;min-width:140px;",t.forEach(b=>{const h=document.createElement("option");h.value=b.value,h.textContent=b.label,e&&b.value===e.field&&(h.selected=!0),r.appendChild(h)});const c=document.createElement("select");c.className="form-select fb-logic-cond-op",c.style.cssText="flex:1 1 160px;min-width:140px;",ae.forEach(b=>{const h=document.createElement("option");h.value=b.value,h.textContent=b.label,e&&b.value===e.operator&&(h.selected=!0),c.appendChild(h)});const d=()=>{const b=t.find(h=>h.value===r.value);Array.from(c.options).forEach(h=>{const g=ae.find(u=>u.value===h.value);h.textContent=b?.multi&&ke[h.value]||g?.label||h.value})};d(),r.addEventListener("change",d);const i=document.createElement("input");i.type="text",i.className="form-input fb-logic-cond-val",i.placeholder="value",i.style.cssText="flex:1 1 120px;min-width:100px;",i.value=e?.value||"",e&&ie.has(e.operator)&&(i.style.display="none"),c.addEventListener("change",()=>{i.style.display=ie.has(c.value)?"none":""});const p=document.createElement("span");w(p,"arrow-right"),p.style.cssText="font-size:.73rem;color:var(--dm-text-muted,#888);flex-shrink:0;";const m=document.createElement("select");m.className=`form-select ${n}`,m.style.cssText="flex:1 1 140px;min-width:120px;",a.forEach(b=>{const h=document.createElement("option");h.value=b.value,h.textContent=b.label,b.value===l&&(h.selected=!0),m.appendChild(h)});const f=document.createElement("button");return f.type="button",f.className="btn btn-xs btn-ghost",f.style.cssText="line-height:1;opacity:.6;",w(f,"x"),f.style.flexShrink="0",f.addEventListener("click",()=>o.remove()),o.appendChild(s),o.appendChild(r),o.appendChild(c),o.appendChild(i),o.appendChild(p),o.appendChild(m),o.appendChild(f),o}function Te(e,t,a){const n=document.createElement("div");n.dataset.logicSection="visibility",n.appendChild(V("Visibility"));const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;gap:.6rem;margin-bottom:.65rem;flex-wrap:wrap;";const o=document.createElement("span");o.textContent="Default:",o.style.cssText="font-size:.8rem;flex-shrink:0;";const s=document.createElement("select");s.className="form-select fb-logic-vis-default",s.style.cssText="max-width:240px;",[{value:"visible",label:"Visible"},{value:"hidden",label:"Hidden"}].forEach(u=>{const y=document.createElement("option");y.value=u.value,y.textContent=u.label,u.value===(e.default||"visible")&&(y.selected=!0),s.appendChild(y)}),l.appendChild(o),l.appendChild(s),n.appendChild(l);const r=document.createElement("div");r.style.cssText="display:flex;align-items:center;gap:.6rem;margin-bottom:.65rem;flex-wrap:wrap;";const c=document.createElement("span");c.textContent="Transition:",c.style.cssText="font-size:.8rem;flex-shrink:0;";const d=document.createElement("select");d.className="form-select fb-logic-vis-transition",d.style.cssText="max-width:240px;",[{value:"none",label:"None (instant)"},{value:"fade",label:"Fade"},{value:"slide",label:"Slide"},{value:"scale",label:"Scale"}].forEach(u=>{const y=document.createElement("option");y.value=u.value,y.textContent=u.label,u.value===(e.transition||"none")&&(y.selected=!0),d.appendChild(y)}),r.appendChild(c),r.appendChild(d),n.appendChild(r);const i=document.createElement("div");i.style.cssText="display:flex;align-items:center;gap:.6rem;margin-bottom:.65rem;flex-wrap:wrap;";const p=document.createElement("span");p.textContent="Speed:",p.style.cssText="font-size:.8rem;flex-shrink:0;";const m=document.createElement("select");m.className="form-select fb-logic-vis-duration",m.style.cssText="max-width:240px;",[{value:"",label:"Default"},{value:"120",label:"Very fast (120ms)"},{value:"200",label:"Fast (200ms)"},{value:"350",label:"Medium (350ms)"},{value:"600",label:"Slow (600ms)"},{value:"1000",label:"Very slow (1s)"}].forEach(u=>{const y=document.createElement("option");y.value=u.value,y.textContent=u.label,u.value===String(e.duration??"")&&(y.selected=!0),m.appendChild(y)}),i.appendChild(p),i.appendChild(m),n.appendChild(i);const f=()=>{i.style.display=d.value==="none"?"none":""};f(),d.addEventListener("change",f);const b=document.createElement("div");b.className="fb-logic-vis-rules";const h=a.map(u=>({value:u.name,label:u.label||u.name,multi:se(u)})),g=[{value:"visible",label:"Show"},{value:"hidden",label:"Hide"}];if((e.conditions||[]).forEach(u=>{const y=(u.when?.all||u.when?.any||[])[0],x=u.then==="hidden"?"hidden":"visible";h.length>0&&b.appendChild(H(y,h,g,"fb-logic-vis-then",x))}),n.appendChild(b),h.length>0){const u=document.createElement("button");u.type="button",u.className="btn btn-xs btn-ghost",u.style.cssText="font-size:.73rem;margin-top:.35rem;",j(u,"plus","Add visibility rule"),u.addEventListener("click",()=>b.appendChild(H(null,h,g,"fb-logic-vis-then","visible"))),n.appendChild(u)}return n}function Ne(e,t,a){const n=document.createElement("div");n.dataset.logicSection="requirement",n.appendChild(V("Conditional Requirement"));const l=document.createElement("label");l.style.cssText="display:flex;align-items:center;gap:.5rem;font-size:.8rem;cursor:pointer;margin-bottom:.65rem;";const o=document.createElement("input");o.type="checkbox",o.className="fb-logic-req-default",o.checked=e.default===!0,l.appendChild(o),l.appendChild(document.createTextNode("Required by default")),n.appendChild(l);const s=document.createElement("div");s.className="fb-logic-req-rules";const r=a.map(d=>({value:d.name,label:d.label||d.name,multi:se(d)})),c=[{value:"true",label:"Make required"},{value:"false",label:"Make optional"}];if((e.conditions||[]).forEach(d=>{const i=(d.when?.all||d.when?.any||[])[0],p=d.then===!0?"true":"false";r.length>0&&s.appendChild(H(i,r,c,"fb-logic-req-then",p))}),n.appendChild(s),r.length>0){const d=document.createElement("button");d.type="button",d.className="btn btn-xs btn-ghost",d.style.cssText="font-size:.73rem;margin-top:.35rem;",j(d,"plus","Add requirement rule"),d.addEventListener("click",()=>s.appendChild(H(null,r,c,"fb-logic-req-then","true"))),n.appendChild(d)}return n}function ce(e){const t=document.createElement("div");t.className="fb-logic-val-rule",t.style.cssText="display:flex;gap:.55rem;align-items:center;margin-bottom:.6rem;flex-wrap:wrap;";const a=document.createElement("select");a.className="form-select fb-logic-val-type",a.style.cssText="flex:0 0 140px;",[{value:"regex",label:"Regex"},{value:"match",label:"Match field"}].forEach(r=>{const c=document.createElement("option");c.value=r.value,c.textContent=r.label,r.value===(e?.type||"regex")&&(c.selected=!0),a.appendChild(c)});const n=document.createElement("input");n.type="text",n.className="form-input fb-logic-val-pattern",n.placeholder=e?.type==="match"?"field name":"pattern",n.value=e?.pattern||e?.field||"",n.style.cssText="flex:3;";const l=document.createElement("input");l.type="text",l.className="form-input fb-logic-val-flags",l.placeholder="flags",l.value=e?.flags||"",l.style.cssText="flex:0 0 55px;",e?.type==="match"&&(l.style.display="none"),a.addEventListener("change",()=>{l.style.display=a.value==="match"?"none":"",n.placeholder=a.value==="match"?"field name":"pattern"});const o=document.createElement("input");o.type="text",o.className="form-input fb-logic-val-message",o.placeholder="Error message",o.value=e?.message||"",o.style.cssText="flex:4;";const s=document.createElement("button");return s.type="button",s.className="btn btn-xs btn-ghost",s.style.cssText="line-height:1;opacity:.6;",w(s,"x"),s.style.flexShrink="0",s.addEventListener("click",()=>t.remove()),t.appendChild(a),t.appendChild(n),t.appendChild(l),t.appendChild(o),t.appendChild(s),t}function qe(e,t,a){const n=document.createElement("div");n.dataset.logicSection="validation",n.appendChild(V("Custom Validation"));const l=document.createElement("div");l.className="fb-logic-val-rules",(e||[]).forEach(s=>l.appendChild(ce(s))),n.appendChild(l);const o=document.createElement("button");return o.type="button",o.className="btn btn-xs btn-ghost",o.style.cssText="font-size:.73rem;margin-top:.35rem;",j(o,"plus","Add validation rule"),o.addEventListener("click",()=>l.appendChild(ce(null))),n.appendChild(o),n}function Le(e,t,a){const n=document.createElement("div");n.dataset.logicSection="cascade",n.appendChild(V("Cascade Options")),n.lastChild.appendChild(Y("Make this field's options depend on another field. When the source field changes, the options here are replaced by the mapping entry for its value; if nothing matches, the default options below are shown."));const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.5rem;";const o=document.createElement("span");o.textContent="Source field:",o.style.cssText="font-size:.8rem;flex-shrink:0;";const s=document.createElement("select");s.className="form-select fb-logic-cascade-source",s.style.cssText="flex:1;";const r=document.createElement("option");r.value="",r.textContent="- none -",s.appendChild(r),a.forEach(m=>{const f=document.createElement("option");f.value=m.name,f.textContent=m.label||m.name,m.name===e.sourceField&&(f.selected=!0),s.appendChild(f)}),l.appendChild(o),l.appendChild(s),n.appendChild(l);const c=document.createElement("p");c.textContent='Mapping JSON - {"value":[{"value":"...","label":"..."}]}',c.appendChild(Y(`Each key is a value of the source field; its list becomes this field's options when that value is picked. Example: {"uk":[{"value":"london","label":"London"}]} shows London when the source is "uk".`)),c.style.cssText="font-size:.73rem;color:var(--dm-text-muted,#888);margin:.3rem 0 .2rem;";const d=document.createElement("textarea");d.className="form-input fb-logic-cascade-mapping",d.rows=4,d.style.cssText="font-family:monospace;",d.placeholder='{"uk": [{"value": "london", "label": "London"}]}',d.value=e.mapping?JSON.stringify(e.mapping,null,2):"",n.appendChild(c),n.appendChild(d);const i=document.createElement("p");i.textContent="Default options (one per line: value:Label)",i.appendChild(Y("Shown before the source field has a value, or when its value has no mapping entry.")),i.style.cssText="font-size:.73rem;color:var(--dm-text-muted,#888);margin:.3rem 0 .2rem;";const p=document.createElement("textarea");return p.className="form-input fb-logic-cascade-defaults",p.rows=3,p.style.cssText="font-family:monospace;",p.placeholder=`option1:Option 1
4
+ option2:Option 2`,p.value=(e.defaultOptions||[]).map(m=>{const f=typeof m=="string"?m:m.value??"",b=typeof m=="string"?m:m.label??f;return f===b?f:`${f}:${b}`}).join(`
5
+ `),n.appendChild(i),n.appendChild(p),n}function $e(e,t){const a=e.logic||{},n=v.filter((p,m)=>m!==t&&p.type!=="page-break"&&p.type!=="spacer"),l=document.createElement("div");l.className="fb-field-logic",l.style.cssText="margin-top:.75rem;border-top:1px solid var(--dm-border,#333);padding-top:.5rem;";const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;justify-content:space-between;cursor:pointer;padding:.15rem 0;";const s=document.createElement("span");s.style.cssText="font-size:.8rem;font-weight:600;color:var(--dm-text-muted,#888);",s.textContent="\u26A1 Conditional Logic";const r=oe(a),c=document.createElement("button");c.type="button",c.className="btn btn-xs btn-ghost",w(c,r?"chevron-down":"chevron-right",12);const d=document.createElement("div");d.className="fb-logic-body",d.style.cssText="padding:.25rem 0 .25rem;"+(r?"":"display:none;"),o.addEventListener("click",()=>{const p=d.style.display==="none";d.style.display=p?"":"none",w(c,p?"chevron-down":"chevron-right",12)}),o.appendChild(s),o.appendChild(c),l.appendChild(o),d.appendChild(Te(a.visibility||{},t,n)),d.appendChild(Ne(a.requirement||{},t,n)),d.appendChild(qe(a.validation||[],t,n));const i=document.getElementById(`fb-type-${t}`)?.value||e.type;return J.has(i)&&d.appendChild(Le(a.cascade||{},t,n)),l.appendChild(d),l}function re(e,t,a){const n=document.createElement("div");return n.className="fb-field-extras",e==="chooser"?(n.appendChild(Fe(t,a)),n.appendChild(Ie(t.options||[],a))):(J.has(e)&&n.appendChild(Pe(t.options||[],a)),e==="textarea"&&n.appendChild(k([T("Rows",`fb-rows-${a}`,"number",t.formConfig?.rows||4,"Height of textarea")])),(e==="string"||e==="textarea")&&n.appendChild(k([T("Min Length",`fb-minlength-${a}`,"number",t.minLength||"",""),T("Max Length",`fb-maxlength-${a}`,"number",t.maxLength||"","")])),e==="number"&&n.appendChild(k([T("Min",`fb-min-${a}`,"number",t.min??"",""),T("Max",`fb-max-${a}`,"number",t.max??"","")]))),n.children.length?n:null}function de(e){const t=document.createElement("div");t.className="fb-chooser-section",t.style.cssText="border:1px solid var(--dm-border, #e5e7eb); border-radius:6px; margin-top:.5rem; overflow:hidden;";const a=document.createElement("div");a.style.cssText="padding:.4rem .6rem; background:color-mix(in srgb, var(--dm-background, #f9fafb) 60%, transparent); font-weight:600; font-size:.82rem; border-bottom:1px solid var(--dm-border, #e5e7eb);",a.textContent=e,t.appendChild(a);const n=document.createElement("div");return n.className="fb-chooser-section-body",n.style.cssText="padding:.55rem .6rem; display:flex; flex-direction:column; gap:.45rem;",t.appendChild(n),{wrap:t,body:n}}function U(e,t,a,n,l){const o=document.createElement("div");o.style.flex="1";const s=document.createElement("label");s.htmlFor=e,s.className="form-label",s.textContent=t,s.style.fontSize=".8rem";const r=document.createElement("select");if(r.id=e,r.className="form-input",n.forEach(([c,d])=>{const i=document.createElement("option");i.value=c,i.textContent=d,String(c)===String(a)&&(i.selected=!0),r.appendChild(i)}),o.appendChild(s),o.appendChild(r),l){const c=document.createElement("p");c.className="form-hint text-muted",c.textContent=l,c.style.cssText="font-size:.72rem;margin-top:.2rem;",o.appendChild(c)}return o}function pe(e,t,a,n){const l=document.createElement("div");l.style.flex="1";const o=document.createElement("label");o.style.cssText="display:inline-flex;align-items:center;gap:.4rem;font-size:.82rem;";const s=document.createElement("input");if(s.id=e,s.type="checkbox",s.checked=!!a,o.appendChild(s),o.appendChild(document.createTextNode(" "+t)),l.appendChild(o),n){const r=document.createElement("p");r.className="form-hint text-muted",r.textContent=n,r.style.cssText="font-size:.72rem;margin-top:.2rem;",l.appendChild(r)}return l}function X(e,t,a,n,l){const o=document.createElement("div");o.style.flex="1";const s=document.createElement("label");s.htmlFor=e,s.className="form-label",s.textContent=t,s.style.fontSize=".8rem",o.appendChild(s);const r=document.createElement("div");r.style.cssText="display:flex;gap:.4rem;align-items:stretch;";const c=document.createElement("input");c.id=e,c.type="text",c.className="form-input",c.value=a||"",n&&(c.placeholder=n),c.style.flex="1";const d=document.createElement("input");d.type="color",d.title="Pick a colour",d.style.cssText="width:36px;height:auto;padding:0;border:1px solid var(--dm-border,#e5e7eb);border-radius:4px;cursor:pointer;";const i=String(c.value).match(/^#[0-9a-fA-F]{3,8}$/);if(d.value=i?c.value:"#000000",d.addEventListener("input",()=>{c.value=d.value,c.dispatchEvent(new Event("input",{bubbles:!0}))}),r.appendChild(c),r.appendChild(d),o.appendChild(r),l){const p=document.createElement("p");p.className="form-hint text-muted",p.textContent=l,p.style.cssText="font-size:.72rem;margin-top:.2rem;",o.appendChild(p)}return o}function Fe(e,t){const a=document.createElement("div");a.style.cssText="margin-top:.5rem; display:flex; flex-direction:column; gap:.4rem;";const n=de("Layout");n.body.appendChild(k([U(`fb-chooser-variant-${t}`,"Variant",e.variant||"card",[["card","Card - rich tiles"],["chip","Chip - compact pills"]],"Card supports description and grid columns; chips wrap."),pe(`fb-chooser-multiple-${t}`,"Multi-select",!!e.multiple,"Off = single (radio); on = multi (checkbox).")])),n.body.appendChild(k([U(`fb-chooser-density-${t}`,"Density",e.density||"comfortable",[["comfortable","Comfortable"],["compact","Compact"]],"Compact strips description (card variant only)."),T("Columns",`fb-chooser-columns-${t}`,"number",e.columns||3,"Card grid (1-6). Ignored for chips.")])),a.appendChild(n.wrap);const l=de("Visual");return l.body.appendChild(k([X(`fb-chooser-accent-${t}`,"Accent",e.accent||"","primary / success / #ec4899","Selected highlight colour - semantic name or any CSS colour."),U(`fb-chooser-accent-style-${t}`,"Accent style",e.accentStyle||"border",[["border","Border (default)"],["solid","Solid"],["glow","Glow"],["overlay","Overlay"],["underline","Underline"]],"Visual treatment of the selected state.")])),l.body.appendChild(k([pe(`fb-chooser-glow-${t}`,"Glow on selected",!!e.glow,"Soft outer glow."),X(`fb-chooser-glow-colour-${t}`,"Glow colour",e.glowColour||"","defaults to accent","Optional; semantic name or CSS colour.")])),l.body.appendChild(k([U(`fb-chooser-shadow-${t}`,"Shadow",e.shadow||"none",[["none","None"],["sm","Small"],["md","Medium"],["lg","Large"],["xl","Extra Large"]],"Drop shadow on every option."),X(`fb-chooser-shadow-colour-${t}`,"Shadow colour",e.shadowColour||"","rgba(0,0,0,0.1)","Optional shadow tint.")])),a.appendChild(l.wrap),a}function ze(e,t,a,n){const l=document.createElement("div");l.className="fb-chooser-option-row",l.dataset.optIdx=String(a),l.style.cssText="border:1px solid var(--dm-border,#e5e7eb); border-radius:4px; padding:.5rem; background:var(--dm-background-alt, color-mix(in srgb, var(--dm-card-bg, #fff) 92%, var(--dm-text, #111))); display:flex; flex-direction:column; gap:.35rem;";const o=document.createElement("div");o.style.cssText="display:flex; gap:.4rem; align-items:center;";const s=document.createElement("div");s.style.cssText="display:flex; flex-direction:column; gap:1px;";const r=document.createElement("button");r.type="button",r.className="btn btn-xs btn-ghost",r.title="Move up",w(r,"arrow-up"),r.addEventListener("click",q=>{q.preventDefault(),me(t,a,-1,n)});const c=document.createElement("button");c.type="button",c.className="btn btn-xs btn-ghost",c.title="Move down",w(c,"arrow-down"),c.addEventListener("click",q=>{q.preventDefault(),me(t,a,1,n)}),s.appendChild(r),s.appendChild(c),o.appendChild(s);const d=document.createElement("input");d.type="text",d.className="form-input fb-chooser-opt-value",d.placeholder="value",d.value=e.value||"",d.style.flex="1",o.appendChild(d);const i=document.createElement("input");i.type="text",i.className="form-input fb-chooser-opt-label",i.placeholder="label (visible to users)",i.value=e.label||"",i.style.flex="2",o.appendChild(i);const p=document.createElement("button");p.type="button",p.className="btn btn-xs btn-ghost",p.style.cssText="line-height:1;opacity:.6;",p.title="Remove option",w(p,"x"),p.addEventListener("click",q=>{q.preventDefault(),Oe(t,a,n)}),o.appendChild(p),l.appendChild(o);const m=document.createElement("div");m.style.cssText="display:flex; gap:.4rem;";const f=ge("icon name (optional)",e.icon||"");f.input.classList.add("fb-chooser-opt-icon"),f.el.style.flex="1",m.appendChild(f.el);const b=document.createElement("input");b.type="text",b.className="form-input fb-chooser-opt-description",b.placeholder="description (card + comfortable only)",b.value=e.description||"",b.style.flex="2",m.appendChild(b),l.appendChild(m);const h=document.createElement("div");h.style.cssText="display:flex; gap:.4rem;";const g=document.createElement("input");g.type="text",g.className="form-input fb-chooser-opt-tooltip",g.placeholder="tooltip (hover hint)",g.value=e.tooltip||"",g.style.flex="2",h.appendChild(g);const u=document.createElement("input");u.type="text",u.className="form-input fb-chooser-opt-badge-text",u.placeholder="badge text",u.value=e.badge?.text||"",u.style.flex="1",h.appendChild(u);const y=document.createElement("select");y.className="form-input fb-chooser-opt-badge-type",y.style.flex="1",[["","no badge"],["primary","primary"],["success","success"],["info","info"],["warning","warning"],["danger","danger"]].forEach(([q,O])=>{const _=document.createElement("option");_.value=q,_.textContent=O,(e.badge?.type||"")===q&&(_.selected=!0),y.appendChild(_)}),h.appendChild(y),l.appendChild(h);const x=document.createElement("div");x.style.cssText="display:flex; gap:1rem; font-size:.8rem;";const S=document.createElement("label");S.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const C=document.createElement("input");C.type="checkbox",C.className="fb-chooser-opt-recommended",C.checked=!!e.recommended,S.appendChild(C),S.appendChild(document.createTextNode(" Recommended")),x.appendChild(S);const N=document.createElement("label");N.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const I=document.createElement("input");return I.type="checkbox",I.className="fb-chooser-opt-disabled",I.checked=!!e.disabled,N.appendChild(I),N.appendChild(document.createTextNode(" Disabled")),x.appendChild(N),l.appendChild(x),l}function Ie(e,t){const a=document.createElement("div");a.style.cssText="margin-top:.5rem;";const n=document.createElement("div");n.style.cssText="display:flex; align-items:center; justify-content:space-between; margin-bottom:.4rem;";const l=document.createElement("p");l.textContent="Options",l.style.cssText="font-size:.85rem;font-weight:600;margin:0;",n.appendChild(l);const o=document.createElement("button");o.type="button",o.className="btn btn-xs btn-ghost",j(o,"plus","Add option"),n.appendChild(o),a.appendChild(n);const s=document.createElement("div");s.className="fb-chooser-options-list",s.id=`fb-chooser-options-${t}`,s.style.cssText="display:flex; flex-direction:column; gap:.4rem;",a.appendChild(s);const r=()=>{s.replaceChildren();const c=Be(t);(c.length?c:e||[]).forEach((i,p)=>{s.appendChild(ze(i,t,p,r))})};return o.addEventListener("click",c=>{c.preventDefault(),Ae(t,r)}),r(),a}function Be(e){const t=v[e];return!t||!Array.isArray(t.options)?[]:t.options}function K(e){return v[e]?(Array.isArray(v[e].options)||(v[e].options=[]),v[e].options):[]}function Ae(e,t){W(e),K(e).push({value:"new",label:"New option"}),t()}function Oe(e,t,a){W(e),K(e).splice(t,1),a()}function me(e,t,a,n){W(e);const l=K(e),o=t+a;if(o<0||o>=l.length)return;const[s]=l.splice(t,1);l.splice(o,0,s),n()}function W(e){const t=document.getElementById(`fb-chooser-options-${e}`);if(!t)return;const a=t.querySelectorAll(".fb-chooser-option-row"),n=[];a.forEach(l=>{const o={value:l.querySelector(".fb-chooser-opt-value")?.value.trim()||"",label:l.querySelector(".fb-chooser-opt-label")?.value.trim()||""},s=l.querySelector(".fb-chooser-opt-icon")?.value.trim();s&&(o.icon=s);const r=l.querySelector(".fb-chooser-opt-description")?.value.trim();r&&(o.description=r);const c=l.querySelector(".fb-chooser-opt-tooltip")?.value.trim();c&&(o.tooltip=c);const d=l.querySelector(".fb-chooser-opt-badge-text")?.value.trim(),i=l.querySelector(".fb-chooser-opt-badge-type")?.value;(d||i)&&(o.badge={},d&&(o.badge.text=d),i&&(o.badge.type=i)),l.querySelector(".fb-chooser-opt-recommended")?.checked&&(o.recommended=!0),l.querySelector(".fb-chooser-opt-disabled")?.checked&&(o.disabled=!0),n.push(o)}),v[e]&&(v[e].options=n)}function k(e){const t=document.createElement("div");return t.style.cssText="display:flex;gap:.85rem;margin-bottom:.75rem;",e.forEach(a=>{a&&t.appendChild(a)}),t}function T(e,t,a,n,l){const o=document.createElement("div");o.style.flex="1";const s=document.createElement("label");s.htmlFor=t,s.className="form-label",s.textContent=e,s.style.fontSize=".8rem";const r=document.createElement("input");if(r.id=t,r.type=a||"text",r.className="form-input",r.value=n??"",o.appendChild(s),o.appendChild(r),l){const c=document.createElement("p");c.className="form-hint text-muted",c.textContent=l,c.style.cssText="font-size:.73rem;margin-top:.35rem;",o.appendChild(c)}return o}function _e(e,t,a,n){const l=document.createElement("div");l.style.flex="1";const o=document.createElement("label");o.htmlFor=t,o.className="form-label",o.textContent=e,o.style.fontSize=".8rem";const s=document.createElement("select");return s.id=t,s.className="form-select",a.forEach(r=>{const c=document.createElement("option");c.value=r.value,c.textContent=r.label,r.value===n&&(c.selected=!0),s.appendChild(c)}),l.appendChild(o),l.appendChild(s),l}function ue(e,t,a){const n=document.createElement("div");n.style.cssText="flex:0;min-width:80px;display:flex;flex-direction:column;justify-content:flex-end;";const l=document.createElement("label");l.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;font-size:.8rem;white-space:nowrap;";const o=document.createElement("input");return o.id=t,o.type="checkbox",o.checked=a,l.appendChild(o),l.appendChild(document.createTextNode(e)),n.appendChild(l),n}function Pe(e,t){const a=document.createElement("div");a.style.cssText="margin-top:.4rem;";const n=document.createElement("p");n.textContent="Options (one per line: value or value:Label)",n.style.cssText="font-size:.8rem;font-weight:600;margin-bottom:.3rem;";const l=document.createElement("textarea");return l.id=`fb-options-${t}`,l.className="form-input",l.rows=4,l.placeholder=`yes:Yes
6
6
  no:No
7
- maybe:Maybe`,o.value=(e||[]).map(l=>{const s=typeof l=="string"?l:l.value??"",d=typeof l=="string"?l:l.label??s;return s===d?s:`${s}:${d}`}).join(`
8
- `),o.style.fontFamily="monospace",i.appendChild(t),i.appendChild(o),i}function fe(e){const n=e.find("#field-project").val()||"";return L(),{title:e.find("#field-title").val().trim(),slug:e.find("#field-slug").val().trim(),description:e.find("#field-description").val().trim(),...e.find("#field-bundled").is(":checked")?{bundled:!0}:{},meta:{...J||{},project:n||null},fields:v,settings:{submitText:e.find("#setting-submit-text").val().trim()||"Submit",successMessage:e.find("#setting-success-message").val().trim()||"Thank you.",layout:e.find("#setting-layout").val()||"stacked",columns:parseInt(e.find("#setting-columns").val(),10)||2,submitSpan:e.find("#setting-submit-span").val()||"",honeypot:e.find("#setting-honeypot").prop("checked"),rateLimitPerMinute:parseInt(e.find("#setting-rate-limit").val(),10)||3,successRedirect:e.find("#setting-success-redirect").val().trim()||null,actionSlug:e.find("#action-cms-slug").val()||null},actions:{email:{enabled:e.find("#action-email-enabled").prop("checked"),recipients:e.find("#action-email-recipients").val().trim(),subjectPrefix:e.find("#action-email-subject-prefix").val().trim()},webhook:{enabled:e.find("#action-webhook-enabled").prop("checked"),url:e.find("#action-webhook-url").val().trim(),method:e.find("#action-webhook-method").val()},...P!==null&&{collection:P}}}}function Me(e){const n=[];let i=[],t="Step 1",o="";return e.forEach(l=>{l.type==="page-break"?(n.push({title:t,description:o,fields:i}),i=[],t=l.label||`Step ${n.length+1}`,o=l.description||""):l.type!=="spacer"&&i.push(l)}),(i.length||n.length===0)&&n.push({title:t,description:o,fields:i}),n}function he(e,n){const i={};return e.forEach(t=>{if(t.type==="page-break"||t.type==="spacer")return;const o={...t.formConfig||{}};o.span==="full"&&n&&(o.span=n);const l=t.type==="chooser"||!!(t.variant&&["select","radio","checkbox-group"].includes(t.type)),d={type:t.type==="checkbox"?"boolean":t.type==="date"||t.type==="icon"||t.type==="image"?"string":l?"chooser":t.type,label:t.label,required:t.required,options:t.options,formConfig:{...t.placeholder&&{placeholder:t.placeholder},...t.helper&&{helperText:t.helper},...t.tooltip&&{tooltip:t.tooltip},...o}};l&&(t.variant&&(d.variant=t.variant),(t.multiple||t.type==="checkbox-group")&&(d.multiple=!0),t.density&&(d.density=t.density),t.columns&&(d.columns=t.columns),t.accent&&(d.accent=t.accent),t.accentStyle&&(d.accentStyle=t.accentStyle),t.glow&&(d.glow=!0),t.glowColour&&(d.glowColour=t.glowColour),t.shadow&&(d.shadow=t.shadow),t.shadowColour&&(d.shadowColour=t.shadowColour)),i[t.name]=d}),i}function be(e,n){const i=typeof e=="string"?document.querySelector(e):e;i&&(n||[]).forEach(t=>{if(t.type!=="date"||!t.name)return;const o=i.querySelector(`[name="${t.name}"]`);o&&o.type!=="date"&&(o.type="date")})}export const formEditorView={templateUrl:"/admin/js/templates/form-editor.html",async onMount(e){v=[],q=null,P=null;const n=window.location.hash.match(/\/forms\/edit\/([^/?#]+)/);q=n?n[1]:null;let i=null;const t=E.loader(e.get(0),{type:"dots"});if(q)try{i=await A(`/forms/${q}`),v=i.fields||[],P=i.actions?.collection??null}catch{E.toast("Could not load form.",{type:"error"})}J={};try{const a=await A("/projects").catch(()=>[]),p=e.find("#field-project").get(0);p&&(Array.isArray(a)?a:[]).forEach(c=>{const u=document.createElement("option");u.value=c.slug,u.textContent=c.name||c.slug,p.appendChild(u)})}catch{}if(t.destroy(),i?e.find("#editor-title").get(0).textContent=`Edit: ${i.title}`:e.find("#editor-title").get(0).textContent="New Form",q||e.find("#field-title").get(0).addEventListener("input",function(){e.find("#field-slug").val(ne(this.value))}),E.tabs(e.find("#editor-tabs").get(0)),i){e.find("#field-title").val(i.title),e.find("#field-slug").val(i.slug),e.find("#field-description").val(i.description||""),e.find("#field-bundled").prop("checked",!!i.bundled),J=i.meta||{},e.find("#field-project").val(i.meta?.project||"");const a=i.settings||{};e.find("#setting-submit-text").val(a.submitText||"Submit"),e.find("#setting-success-message").val(a.successMessage||""),e.find("#setting-layout").val(a.layout||"stacked"),e.find("#setting-columns").val(a.columns||2),e.find("#setting-submit-span").val(a.submitSpan||""),e.find("#columns-group").get(0).style.display=a.layout==="grid"?"":"none",e.find("#setting-honeypot").prop("checked",a.honeypot!==!1),e.find("#setting-rate-limit").val(a.rateLimitPerMinute||3),e.find("#setting-success-redirect").val(a.successRedirect||"");const p=i.actions?.email||{};e.find("#action-email-enabled").prop("checked",p.enabled||!1),e.find("#action-email-recipients").val(p.recipients||""),e.find("#action-email-subject-prefix").val(p.subjectPrefix||"");const c=i.actions?.webhook||{};e.find("#action-webhook-enabled").prop("checked",c.enabled||!1),e.find("#action-webhook-url").val(c.url||""),e.find("#action-webhook-method").val(c.method||"POST")}const o=i?.settings?.actionSlug||"";try{const a=await A("/actions").catch(()=>[]),p=e.find("#action-cms-slug").get(0);if((Array.isArray(a)?a:[]).forEach(c=>{const u=document.createElement("option");u.value=c.slug,u.textContent=c.title||c.slug,c.slug===o&&(u.selected=!0),p.appendChild(u)}),!a.length){const c=document.createElement("option");c.value="",c.textContent="No actions available",c.disabled=!0,p.appendChild(c)}}catch{}e.find("#setting-layout").get(0)?.addEventListener("change",function(){const a=this.value==="grid";e.find("#columns-group").get(0).style.display=a?"":"none",document.querySelectorAll(".fb-grid-row").forEach(p=>{p.style.display=a?"flex":"none"})}),$(e);const l=e.find("#add-element-menu").get(0);e.find("#add-element-btn").get(0).addEventListener("click",a=>{a.stopPropagation(),l.style.display=l.style.display==="none"?"":"none"}),M&&document.removeEventListener("click",M),M=()=>{l&&(l.style.display="none")},document.addEventListener("click",M),e.find("#add-field-btn").get(0).addEventListener("click",()=>{l&&(l.style.display="none"),L();const a=v.length;v.push({name:`field_${a+1}`,type:"string",label:"New Field",required:!1,placeholder:""}),$(e);const p=e.find("#fields-list").get(0)?.lastElementChild;if(p){const c=p.querySelector(".fb-field-body");c&&(c.style.display="")}}),e.find("#add-spacer-btn").get(0).addEventListener("click",()=>{l&&(l.style.display="none"),L(),v.push({type:"spacer"}),$(e)}),e.find("#add-page-break-btn").get(0).addEventListener("click",()=>{l&&(l.style.display="none"),L();const a=v.filter(p=>p.type==="page-break").length+2;v.push({type:"page-break",label:`Step ${a}`,description:""}),$(e)}),e.find("#save-form-btn").get(0).addEventListener("click",async()=>{const a=fe(e);if(!a.title){E.toast("Please enter a form title.",{type:"error"});return}try{q?(await A(`/forms/${q}`,{method:"PUT",body:JSON.stringify(a)}),E.toast("Form saved.",{type:"success"})):(q=(await A("/forms",{method:"POST",body:JSON.stringify(a)})).slug,R.navigate(`/forms/edit/${q}`),E.toast("Form created.",{type:"success"}))}catch(p){E.toast(p.message||"Failed to save form.",{type:"error"})}}),D=()=>{const a=fe(e),p=e.find("#preview-container").get(0);if(!p)return;const c=e.find("#preview-test-result").get(0),u=e.find("#preview-test-badge").get(0);c&&(c.style.display="none",c.textContent=""),u&&(u.style.display=q?"":"none"),e.find("#preview-card").get(0).style.display="",p.textContent="";const f=document.createElement("div");f.id="fb-preview-form",p.appendChild(f);const g=q?async h=>{c&&(c.style.display="none",c.textContent="");try{const m=await fetch(`/api/forms/submit/${q}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(h)}),y=await m.json();if(!m.ok)throw new Error(y.error||"Submission failed.");c&&(c.textContent=y.message||a.settings?.successMessage||"Submitted successfully.",c.style.cssText="display:block;margin-top:.75rem;padding:.6rem .9rem;border-radius:6px;font-size:.9rem;background:color-mix(in srgb, var(--dm-success) 12%, transparent);color:var(--dm-success,#22c55e);"),E.toast("Test submission stored.",{type:"success"})}catch(m){c&&(c.textContent=m.message,c.style.cssText="display:block;margin-top:.75rem;padding:.6rem .9rem;border-radius:6px;font-size:.9rem;background:color-mix(in srgb, var(--dm-danger) 12%, transparent);color:var(--dm-danger,#ef4444);"),E.toast(m.message,{type:"error"})}return!1}:()=>!1,b=a.fields.some(h=>h.type==="page-break");if(typeof F<"u"){const h=a.settings?.columns||2,m=a.settings?.layout||"stacked";if(b&&F.wizard){const y=Me(a.fields).map(x=>({title:x.title,description:x.description,fields:he(x.fields,h)}));F.wizard("#fb-preview-form",{schema:{steps:y},onSubmit:g}),be("#fb-preview-form",a.fields),Z(f,a.fields)}else if(F.render){const y=he(a.fields,h),x={};if(a.fields.forEach(C=>{if(!(!C.name||C.type==="page-break"||C.type==="spacer")&&(C.type==="select"||C.type==="multiselect")&&C.required){const T=(C.options||[])[0];T&&(x[C.name]=typeof T=="object"?T.value:T)}}),F.render("#fb-preview-form",y,x,{submitText:a.settings?.submitText||"Submit",layout:m,columns:h,onSubmit:g}),m==="grid"&&a.settings?.submitSpan==="full"){const C=document.querySelector("#fb-preview-form .form-buttons");C&&C.classList.add("col-span-full")}be("#fb-preview-form",a.fields),Z(f,a.fields)}window.FormLogicEngine&&a.fields.some(y=>y.logic)&&requestAnimationFrame(()=>{new window.FormLogicEngine.FormLogicRuntime({fields:a.fields},f).init()})}else{const h=document.createElement("p");h.textContent=`${a.fields.filter(m=>m.type!=="page-break").length} field(s): ${a.fields.filter(m=>m.type!=="page-break").map(m=>m.label).join(", ")}`,h.style.cssText="color:var(--dm-text-muted);font-style:italic;",f.appendChild(h)}},D();const s=e.find("#editor-tabs").get(0),d=e.find("#preview-card").get(0),r=a=>{d&&d.contains(a.target)||Q()};s&&(s.addEventListener("input",r),s.addEventListener("change",r))}};
7
+ maybe:Maybe`,l.value=(e||[]).map(o=>{const s=typeof o=="string"?o:o.value??"",r=typeof o=="string"?o:o.label??s;return s===r?s:`${s}:${r}`}).join(`
8
+ `),l.style.fontFamily="monospace",a.appendChild(n),a.appendChild(l),a}function fe(e){const t=e.find("#field-project").val()||"";return $(),{title:e.find("#field-title").val().trim(),slug:e.find("#field-slug").val().trim(),description:e.find("#field-description").val().trim(),...e.find("#field-bundled").is(":checked")?{bundled:!0}:{},meta:{...G||{},project:t||null},fields:v,settings:{submitText:e.find("#setting-submit-text").val().trim()||"Submit",successMessage:e.find("#setting-success-message").val().trim()||"Thank you.",layout:e.find("#setting-layout").val()||"stacked",columns:parseInt(e.find("#setting-columns").val(),10)||2,submitSpan:e.find("#setting-submit-span").val()||"",honeypot:e.find("#setting-honeypot").prop("checked"),rateLimitPerMinute:parseInt(e.find("#setting-rate-limit").val(),10)||3,successRedirect:e.find("#setting-success-redirect").val().trim()||null,actionSlug:e.find("#action-cms-slug").val()||null},actions:{email:{enabled:e.find("#action-email-enabled").prop("checked"),recipients:e.find("#action-email-recipients").val().trim(),subjectPrefix:e.find("#action-email-subject-prefix").val().trim()},webhook:{enabled:e.find("#action-webhook-enabled").prop("checked"),url:e.find("#action-webhook-url").val().trim(),method:e.find("#action-webhook-method").val()},...P!==null&&{collection:P}}}}function Me(e){const t=[];let a=[],n="Step 1",l="";return e.forEach(o=>{o.type==="page-break"?(t.push({title:n,description:l,fields:a}),a=[],n=o.label||`Step ${t.length+1}`,l=o.description||""):o.type!=="spacer"&&a.push(o)}),(a.length||t.length===0)&&t.push({title:n,description:l,fields:a}),t}function he(e,t){const a={};return e.forEach(n=>{if(n.type==="page-break"||n.type==="spacer")return;const l={...n.formConfig||{}};l.span==="full"&&t&&(l.span=t);const o=n.type==="chooser"||!!(n.variant&&["select","radio","checkbox-group"].includes(n.type)),r={type:n.type==="checkbox"?"boolean":n.type==="date"||n.type==="icon"||n.type==="image"?"string":o?"chooser":n.type,label:n.label,required:n.required,options:n.options,formConfig:{...n.placeholder&&{placeholder:n.placeholder},...n.helper&&{helperText:n.helper},...n.tooltip&&{tooltip:n.tooltip},...l}};o&&(n.variant&&(r.variant=n.variant),(n.multiple||n.type==="checkbox-group")&&(r.multiple=!0),n.density&&(r.density=n.density),n.columns&&(r.columns=n.columns),n.accent&&(r.accent=n.accent),n.accentStyle&&(r.accentStyle=n.accentStyle),n.glow&&(r.glow=!0),n.glowColour&&(r.glowColour=n.glowColour),n.shadow&&(r.shadow=n.shadow),n.shadowColour&&(r.shadowColour=n.shadowColour)),a[n.name]=r}),a}function be(e,t){const a=typeof e=="string"?document.querySelector(e):e;a&&(t||[]).forEach(n=>{if(n.type!=="date"||!n.name)return;const l=a.querySelector(`[name="${n.name}"]`);l&&l.type!=="date"&&(l.type="date")})}export const formEditorView={templateUrl:"/admin/js/templates/form-editor.html",async onMount(e){v=[],L=null,P=null;const t=window.location.hash.match(/\/forms\/edit\/([^/?#]+)/);L=t?t[1]:null;let a=null;const n=E.loader(e.get(0),{type:"dots"});if(L)try{a=await A(`/forms/${L}`),v=a.fields||[],P=a.actions?.collection??null}catch{E.toast("Could not load form.",{type:"error"})}G={};try{const i=await A("/projects").catch(()=>[]),p=e.find("#field-project").get(0);p&&(Array.isArray(i)?i:[]).forEach(m=>{const f=document.createElement("option");f.value=m.slug,f.textContent=m.name||m.slug,p.appendChild(f)})}catch{}if(n.destroy(),a?e.find("#editor-title").get(0).textContent=`Edit: ${a.title}`:e.find("#editor-title").get(0).textContent="New Form",L||e.find("#field-title").get(0).addEventListener("input",function(){e.find("#field-slug").val(ne(this.value))}),E.tabs(e.find("#editor-tabs").get(0)),a){e.find("#field-title").val(a.title),e.find("#field-slug").val(a.slug),e.find("#field-description").val(a.description||""),e.find("#field-bundled").prop("checked",!!a.bundled),G=a.meta||{},e.find("#field-project").val(a.meta?.project||"");const i=a.settings||{};e.find("#setting-submit-text").val(i.submitText||"Submit"),e.find("#setting-success-message").val(i.successMessage||""),e.find("#setting-layout").val(i.layout||"stacked"),e.find("#setting-columns").val(i.columns||2),e.find("#setting-submit-span").val(i.submitSpan||""),e.find("#columns-group").get(0).style.display=i.layout==="grid"?"":"none",e.find("#setting-honeypot").prop("checked",i.honeypot!==!1),e.find("#setting-rate-limit").val(i.rateLimitPerMinute||3),e.find("#setting-success-redirect").val(i.successRedirect||"");const p=a.actions?.email||{};e.find("#action-email-enabled").prop("checked",p.enabled||!1),e.find("#action-email-recipients").val(p.recipients||""),e.find("#action-email-subject-prefix").val(p.subjectPrefix||"");const m=a.actions?.webhook||{};e.find("#action-webhook-enabled").prop("checked",m.enabled||!1),e.find("#action-webhook-url").val(m.url||""),e.find("#action-webhook-method").val(m.method||"POST")}const l=a?.settings?.actionSlug||"";try{const i=await A("/actions").catch(()=>[]),p=e.find("#action-cms-slug").get(0);if((Array.isArray(i)?i:[]).forEach(m=>{const f=document.createElement("option");f.value=m.slug,f.textContent=m.title||m.slug,m.slug===l&&(f.selected=!0),p.appendChild(f)}),!i.length){const m=document.createElement("option");m.value="",m.textContent="No actions available",m.disabled=!0,p.appendChild(m)}}catch{}const o=i=>{e.find("#columns-group").get(0).style.display=i?"":"none",document.querySelectorAll(".fb-grid-row").forEach(p=>{p.style.display=i?"flex":"none"})};e.find("#setting-layout").get(0)?.addEventListener("change",function(){o(this.value==="grid")}),o(e.find("#setting-layout").val()==="grid"),z(e);const s=e.find("#add-element-menu").get(0);e.find("#add-element-btn").get(0).addEventListener("click",i=>{i.stopPropagation(),s.style.display=s.style.display==="none"?"":"none"}),M&&document.removeEventListener("click",M),M=()=>{s&&(s.style.display="none")},document.addEventListener("click",M),e.find("#add-field-btn").get(0).addEventListener("click",()=>{s&&(s.style.display="none"),$();const i=v.length;v.push({name:`field_${i+1}`,type:"string",label:"New Field",required:!1,placeholder:""}),z(e);const p=e.find("#fields-list").get(0)?.lastElementChild;if(p){const m=p.querySelector(".fb-field-body");m&&(m.style.display="")}}),e.find("#add-spacer-btn").get(0).addEventListener("click",()=>{s&&(s.style.display="none"),$(),v.push({type:"spacer"}),z(e)}),e.find("#add-page-break-btn").get(0).addEventListener("click",()=>{s&&(s.style.display="none"),$();const i=v.filter(p=>p.type==="page-break").length+2;v.push({type:"page-break",label:`Step ${i}`,description:""}),z(e)}),e.find("#save-form-btn").get(0).addEventListener("click",async()=>{const i=fe(e);if(!i.title){E.toast("Please enter a form title.",{type:"error"});return}try{L?(await A(`/forms/${L}`,{method:"PUT",body:JSON.stringify(i)}),E.toast("Form saved.",{type:"success"})):(L=(await A("/forms",{method:"POST",body:JSON.stringify(i)})).slug,R.navigate(`/forms/edit/${L}`),E.toast("Form created.",{type:"success"}))}catch(p){E.toast(p.message||"Failed to save form.",{type:"error"})}}),D=()=>{const i=fe(e),p=e.find("#preview-container").get(0);if(!p)return;const m=e.find("#preview-test-result").get(0),f=e.find("#preview-test-badge").get(0);m&&(m.style.display="none",m.textContent=""),f&&(f.style.display=L?"":"none"),e.find("#preview-card").get(0).style.display="",p.textContent="";const b=document.createElement("div");b.id="fb-preview-form",p.appendChild(b);const h=L?async u=>{m&&(m.style.display="none",m.textContent="");try{const y=await fetch(`/api/forms/submit/${L}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(u)}),x=await y.json();if(!y.ok)throw new Error(x.error||"Submission failed.");m&&(m.textContent=x.message||i.settings?.successMessage||"Submitted successfully.",m.style.cssText="display:block;margin-top:.75rem;padding:.6rem .9rem;border-radius:6px;font-size:.9rem;background:color-mix(in srgb, var(--dm-success) 12%, transparent);color:var(--dm-success,#22c55e);"),E.toast("Test submission stored.",{type:"success"})}catch(y){m&&(m.textContent=y.message,m.style.cssText="display:block;margin-top:.75rem;padding:.6rem .9rem;border-radius:6px;font-size:.9rem;background:color-mix(in srgb, var(--dm-danger) 12%, transparent);color:var(--dm-danger,#ef4444);"),E.toast(y.message,{type:"error"})}return!1}:()=>!1,g=i.fields.some(u=>u.type==="page-break");if(typeof F<"u"){const u=i.settings?.columns||2,y=i.settings?.layout||"stacked";if(g&&F.wizard){const x=Me(i.fields).map(S=>({title:S.title,description:S.description,fields:he(S.fields,u)}));F.wizard("#fb-preview-form",{schema:{steps:x},onSubmit:h}),be("#fb-preview-form",i.fields),Z(b,i.fields)}else if(F.render){const x=he(i.fields,u),S={};if(i.fields.forEach(C=>{if(!(!C.name||C.type==="page-break"||C.type==="spacer")&&(C.type==="select"||C.type==="multiselect")&&C.required){const N=(C.options||[])[0];N&&(S[C.name]=typeof N=="object"?N.value:N)}}),F.render("#fb-preview-form",x,S,{submitText:i.settings?.submitText||"Submit",layout:y,columns:u,onSubmit:h}),y==="grid"&&i.settings?.submitSpan==="full"){const C=document.querySelector("#fb-preview-form .form-buttons");C&&C.classList.add("col-span-full")}be("#fb-preview-form",i.fields),Z(b,i.fields)}window.FormLogicEngine&&i.fields.some(x=>x.logic)&&requestAnimationFrame(()=>{new window.FormLogicEngine.FormLogicRuntime({fields:i.fields},b).init()})}else{const u=document.createElement("p");u.textContent=`${i.fields.filter(y=>y.type!=="page-break").length} field(s): ${i.fields.filter(y=>y.type!=="page-break").map(y=>y.label).join(", ")}`,u.style.cssText="color:var(--dm-text-muted);font-style:italic;",b.appendChild(u)}},D();const r=e.find("#editor-tabs").get(0),c=e.find("#preview-card").get(0),d=i=>{c&&c.contains(i.target)||Q()};r&&(r.addEventListener("input",d),r.addEventListener("change",d))}};
@@ -1 +1 @@
1
- import{apiRequest as x}from"/admin/js/api.js";import{keepTableIconsAlive as A}from"/admin/js/lib/table-icons.js";let c=null,k=[],r=[];export const formSubmissionsView={templateUrl:"/admin/js/templates/form-submissions.html",async onMount(e){const l=window.location.hash.match(/\/forms\/([^/?#]+)\/submissions/);if(c=l?l[1]:null,!c){E.toast("No form selected.",{type:"error"});return}const u=E.loader(e.get(0),{type:"dots"});try{const o=await x(`/forms/${c}`);k=(o.fields||[]).filter(s=>s.type!=="page-break"),e.find("#submissions-title").get(0).textContent=`${o.title} \u2014 Submissions`}catch{E.toast("Could not load form definition.",{type:"error"})}try{await M(e)}finally{u.destroy()}A(e.get(0)),e.off("click",".js-delete-submission").on("click",".js-delete-submission",async function(o){o.stopPropagation();const s=this.dataset.id;if(await E.confirm("Delete this submission?"))try{await x(`/forms/${c}/submissions/${s}`,{method:"DELETE"}),r=r.filter(d=>d.id!==s),E.toast("Submission deleted.",{type:"success"}),v(r,e)}catch{E.toast("Failed to delete submission.",{type:"error"})}}),e.find("#export-btn").off("click").on("click",()=>{const o=E.slideover({title:"Export Submissions",size:"sm",position:"right"}),s=document.createElement("div");s.style.cssText="padding:1.25rem;display:flex;flex-direction:column;gap:1rem;";function i(d,y,f,g){const C=document.createElement("div");C.style.cssText="display:flex;flex-direction:column;gap:.4rem;";const b=document.createElement("button");b.className="btn btn-ghost",b.style.cssText="justify-content:flex-start;gap:.5rem;";const L=document.createElement("span");return L.setAttribute("data-icon",y),b.appendChild(L),b.appendChild(document.createTextNode(" "+d)),b.addEventListener("click",()=>{fetch(f,{headers:{Authorization:"Bearer "+(S.get("auth_token")||"")}}).then(w=>w.blob()).then(w=>{const h=document.createElement("a");h.href=URL.createObjectURL(w),h.download=g,document.body.appendChild(h),h.click(),document.body.removeChild(h),URL.revokeObjectURL(h.href),o.close()}).catch(()=>E.toast("Export failed.",{type:"error"}))}),C.appendChild(b),C}s.appendChild(i("Export as CSV","file-text",`/api/forms/${c}/submissions/export`,`${c}-submissions.csv`)),s.appendChild(i("Export as JSON","code",`/api/forms/${c}/submissions/export/json`,`${c}-submissions.json`)),o.setContent(s),Domma.icons.scan(s),o.open()}),e.find("#clear-all-btn").off("click").on("click",async()=>{if(await E.confirm("Delete all submissions? This cannot be undone."))try{await x(`/forms/${c}/submissions`,{method:"DELETE"}),r=[],E.toast("All submissions cleared.",{type:"success"}),v([],e)}catch{E.toast("Failed to clear submissions.",{type:"error"})}});const m=e.find("#sub-search").get(0),n=e.find("#sub-date-from").get(0),t=e.find("#sub-date-to").get(0);function a(){const o=m?.value.toLowerCase()||"",s=n?.value?new Date(n.value):null,i=t?.value?new Date(t.value+"T23:59:59"):null,d=r.filter(y=>{if(o&&!Object.values(y.data||{}).map(g=>String(g).toLowerCase()).some(g=>g.includes(o)))return!1;if(s||i){const f=y.meta?.createdAt?new Date(y.meta.createdAt):null;if(!f||s&&f<s||i&&f>i)return!1}return!0});v(d,e)}m&&m.addEventListener("input",a),n&&n.addEventListener("change",a),t&&t.addEventListener("change",a),Domma.icons.scan()}};async function M(e){try{r=await x(`/forms/${c}/submissions`)}catch{r=[],E.toast("Could not load submissions.",{type:"error"})}v(r,e)}function v(e,p){const l=p.find("#sub-count").get(0);l&&(e.length===r.length?l.textContent=`${r.length} submission${r.length!==1?"s":""}`:l.textContent=`Showing ${e.length} of ${r.length}`);const m=[...k.map(t=>({key:`data.${t.name}`,title:t.label||t.name,render:(a,o)=>{const s=o.data?.[t.name]??"",i=String(s),d=document.createElement("span");return d.title=i,d.textContent=i.length>80?i.slice(0,80)+"\u2026":i,d.outerHTML}})),{key:"meta",title:"Date",render:t=>D(t?.createdAt).format("DD MMM YYYY HH:mm")},{key:"id",title:"",render:t=>{const a=document.createElement("button");return a.className="btn btn-sm btn-ghost js-delete-submission",a.dataset.id=t,a.setAttribute("data-tooltip","Delete"),a.innerHTML='<span data-icon="trash" data-icon-size="14"></span>',a.outerHTML}}];T.create("#submissions-table",{data:e,columns:m,emptyMessage:"No submissions yet."});const n=document.querySelector("#submissions-table");n&&(n.querySelectorAll("tbody tr").forEach((t,a)=>{const o=e[a];o&&(t.style.cursor="pointer",t.addEventListener("click",s=>{s.target.closest(".js-delete-submission")||j(o)}))}),Domma.icons.scan(n),n.querySelectorAll("[data-tooltip]").forEach(t=>{E.tooltip(t,{content:t.getAttribute("data-tooltip"),position:"top"})}))}function j(e){const p=document.createElement("div"),l=document.createElement("div");l.style.cssText="display:flex;flex-direction:column;gap:.75rem;margin-bottom:1.25rem;",k.forEach(n=>{const t=e.data?.[n.name];if(t==null||t==="")return;const a=document.createElement("div");a.style.cssText="border-bottom:1px solid var(--dm-border,#333);padding-bottom:.6rem;";const o=document.createElement("strong");o.textContent=n.label||n.name,o.style.cssText="display:block;font-size:.8rem;color:var(--dm-text-muted,#888);margin-bottom:.2rem;";const s=document.createElement("p");s.textContent=String(t),s.style.cssText="margin:0;word-break:break-word;",a.appendChild(o),a.appendChild(s),l.appendChild(a)});const u=document.createElement("div");if(u.style.cssText="font-size:.8rem;color:var(--dm-text-muted,#888);display:flex;flex-direction:column;gap:.3rem;border-top:1px solid var(--dm-border,#333);padding-top:.75rem;",e.meta?.createdAt){const n=document.createElement("span");n.textContent=`Submitted: ${D(e.meta.createdAt).format("DD MMM YYYY HH:mm")}`,u.appendChild(n)}if(e.meta?.ip){const n=document.createElement("span");n.textContent=`IP: ${e.meta.ip}`,u.appendChild(n)}p.appendChild(l),p.appendChild(u);const m=E.slideover({title:"Submission Details",size:"md",position:"right"});m.element.appendChild(p),m.open()}
1
+ import{apiRequest as x}from"/admin/js/api.js";import{keepTableIconsAlive as A}from"/admin/js/lib/table-icons.js";let c=null,k=[],r=[];export const formSubmissionsView={templateUrl:"/admin/js/templates/form-submissions.html",async onMount(e){const l=window.location.hash.match(/\/forms\/([^/?#]+)\/submissions/);if(c=l?l[1]:null,!c){E.toast("No form selected.",{type:"error"});return}const u=E.loader(e.get(0),{type:"dots"});try{const o=await x(`/forms/${c}`);k=(o.fields||[]).filter(s=>s.type!=="page-break"),e.find("#submissions-title").get(0).textContent=`${o.title} - Submissions`}catch{E.toast("Could not load form definition.",{type:"error"})}try{await M(e)}finally{u.destroy()}A(e.get(0)),e.off("click",".js-delete-submission").on("click",".js-delete-submission",async function(o){o.stopPropagation();const s=this.dataset.id;if(await E.confirm("Delete this submission?"))try{await x(`/forms/${c}/submissions/${s}`,{method:"DELETE"}),r=r.filter(d=>d.id!==s),E.toast("Submission deleted.",{type:"success"}),v(r,e)}catch{E.toast("Failed to delete submission.",{type:"error"})}}),e.find("#export-btn").off("click").on("click",()=>{const o=E.slideover({title:"Export Submissions",size:"sm",position:"right"}),s=document.createElement("div");s.style.cssText="padding:1.25rem;display:flex;flex-direction:column;gap:1rem;";function i(d,y,f,g){const C=document.createElement("div");C.style.cssText="display:flex;flex-direction:column;gap:.4rem;";const b=document.createElement("button");b.className="btn btn-ghost",b.style.cssText="justify-content:flex-start;gap:.5rem;";const L=document.createElement("span");return L.setAttribute("data-icon",y),b.appendChild(L),b.appendChild(document.createTextNode(" "+d)),b.addEventListener("click",()=>{fetch(f,{headers:{Authorization:"Bearer "+(S.get("auth_token")||"")}}).then(w=>w.blob()).then(w=>{const h=document.createElement("a");h.href=URL.createObjectURL(w),h.download=g,document.body.appendChild(h),h.click(),document.body.removeChild(h),URL.revokeObjectURL(h.href),o.close()}).catch(()=>E.toast("Export failed.",{type:"error"}))}),C.appendChild(b),C}s.appendChild(i("Export as CSV","file-text",`/api/forms/${c}/submissions/export`,`${c}-submissions.csv`)),s.appendChild(i("Export as JSON","code",`/api/forms/${c}/submissions/export/json`,`${c}-submissions.json`)),o.setContent(s),Domma.icons.scan(s),o.open()}),e.find("#clear-all-btn").off("click").on("click",async()=>{if(await E.confirm("Delete all submissions? This cannot be undone."))try{await x(`/forms/${c}/submissions`,{method:"DELETE"}),r=[],E.toast("All submissions cleared.",{type:"success"}),v([],e)}catch{E.toast("Failed to clear submissions.",{type:"error"})}});const m=e.find("#sub-search").get(0),n=e.find("#sub-date-from").get(0),t=e.find("#sub-date-to").get(0);function a(){const o=m?.value.toLowerCase()||"",s=n?.value?new Date(n.value):null,i=t?.value?new Date(t.value+"T23:59:59"):null,d=r.filter(y=>{if(o&&!Object.values(y.data||{}).map(g=>String(g).toLowerCase()).some(g=>g.includes(o)))return!1;if(s||i){const f=y.meta?.createdAt?new Date(y.meta.createdAt):null;if(!f||s&&f<s||i&&f>i)return!1}return!0});v(d,e)}m&&m.addEventListener("input",a),n&&n.addEventListener("change",a),t&&t.addEventListener("change",a),Domma.icons.scan()}};async function M(e){try{r=await x(`/forms/${c}/submissions`)}catch{r=[],E.toast("Could not load submissions.",{type:"error"})}v(r,e)}function v(e,p){const l=p.find("#sub-count").get(0);l&&(e.length===r.length?l.textContent=`${r.length} submission${r.length!==1?"s":""}`:l.textContent=`Showing ${e.length} of ${r.length}`);const m=[...k.map(t=>({key:`data.${t.name}`,title:t.label||t.name,render:(a,o)=>{const s=o.data?.[t.name]??"",i=String(s),d=document.createElement("span");return d.title=i,d.textContent=i.length>80?i.slice(0,80)+"\u2026":i,d.outerHTML}})),{key:"meta",title:"Date",render:t=>D(t?.createdAt).format("DD MMM YYYY HH:mm")},{key:"id",title:"",render:t=>{const a=document.createElement("button");return a.className="btn btn-sm btn-ghost js-delete-submission",a.dataset.id=t,a.setAttribute("data-tooltip","Delete"),a.innerHTML='<span data-icon="trash" data-icon-size="14"></span>',a.outerHTML}}];T.create("#submissions-table",{pageSize:25,data:e,columns:m,emptyMessage:"No submissions yet."});const n=document.querySelector("#submissions-table");n&&(n.querySelectorAll("tbody tr").forEach((t,a)=>{const o=e[a];o&&(t.style.cursor="pointer",t.addEventListener("click",s=>{s.target.closest(".js-delete-submission")||j(o)}))}),Domma.icons.scan(n),n.querySelectorAll("[data-tooltip]").forEach(t=>{E.tooltip(t,{content:t.getAttribute("data-tooltip"),position:"top"})}))}function j(e){const p=document.createElement("div"),l=document.createElement("div");l.style.cssText="display:flex;flex-direction:column;gap:.75rem;margin-bottom:1.25rem;",k.forEach(n=>{const t=e.data?.[n.name];if(t==null||t==="")return;const a=document.createElement("div");a.style.cssText="border-bottom:1px solid var(--dm-border,#333);padding-bottom:.6rem;";const o=document.createElement("strong");o.textContent=n.label||n.name,o.style.cssText="display:block;font-size:.8rem;color:var(--dm-text-muted,#888);margin-bottom:.2rem;";const s=document.createElement("p");s.textContent=String(t),s.style.cssText="margin:0;word-break:break-word;",a.appendChild(o),a.appendChild(s),l.appendChild(a)});const u=document.createElement("div");if(u.style.cssText="font-size:.8rem;color:var(--dm-text-muted,#888);display:flex;flex-direction:column;gap:.3rem;border-top:1px solid var(--dm-border,#333);padding-top:.75rem;",e.meta?.createdAt){const n=document.createElement("span");n.textContent=`Submitted: ${D(e.meta.createdAt).format("DD MMM YYYY HH:mm")}`,u.appendChild(n)}if(e.meta?.ip){const n=document.createElement("span");n.textContent=`IP: ${e.meta.ip}`,u.appendChild(n)}p.appendChild(l),p.appendChild(u);const m=E.slideover({title:"Submission Details",size:"md",position:"right"});m.element.appendChild(p),m.open()}
@@ -1 +1 @@
1
- import{apiRequest as p}from"/admin/js/api.js";import{filterByProject as b,getProjectFromHash as h}from"/admin/js/lib/project-context.js";import{keepTableIconsAlive as C}from"/admin/js/lib/table-icons.js";function u(d){return String(d).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}export const formsView={templateUrl:"/admin/js/templates/forms.html",async onMount(d){await g(d),C(d.get(0)),d.off("click",".js-delete-form").on("click",".js-delete-form",async function(){const c=this.dataset.slug,e=this.dataset.plugin,r=e?`This form is managed by the <strong>${e}</strong> plugin. Deleting it may cause the plugin to malfunction. Continue?`:`Delete form "${c}" and all its submissions? This cannot be undone.`;if(await E.confirm(r))try{await p(`/forms/${c}`,{method:"DELETE"}),E.toast("Form deleted.",{type:"success"}),await g(d)}catch{E.toast("Failed to delete form.",{type:"error"})}}),d.off("click",".js-clone-form").on("click",".js-clone-form",async function(){const c=this.dataset.slug;try{const e=await p(`/forms/${c}`),{collection:r,...t}=e.actions||{},n=await p("/forms",{method:"POST",body:JSON.stringify({title:`${e.title} (Copy)`,description:e.description||"",fields:e.fields||[],settings:e.settings||{},actions:t})});E.toast(`Form duplicated as "${n.title}".`,{type:"success"}),R.navigate(`/forms/edit/${n.slug}`)}catch(e){E.toast(e.message||"Failed to duplicate form.",{type:"error"})}}),d.find("#create-form-btn").off("click").on("click",()=>{const c=E.slideover({title:"Create Form",size:"sm",position:"right"}),e=document.createElement("div");e.style.cssText="padding:.25rem 0 .5rem;";const r=document.createElement("div");F.create({title:{type:"string",label:"Form Title",placeholder:"e.g. Contact, Feedback\u2026",required:!0}},{},{showSubmitButton:!1}).renderTo(r),e.appendChild(r);const t=document.createElement("div");t.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:.75rem;";const n=document.createElement("button");n.className="btn btn-ghost",n.textContent="Cancel";const o=document.createElement("button");o.className="btn btn-primary",o.textContent="Create",t.appendChild(n),t.appendChild(o),e.appendChild(t),c.element.appendChild(e),c.open();const s=r.querySelector('input[name="title"]');setTimeout(()=>s?.focus(),50);async function a(){const i=s?.value.trim();if(i)try{const l=await p("/forms",{method:"POST",body:JSON.stringify({title:i})});c.close(),R.navigate(`/forms/edit/${l.slug}`)}catch(l){E.toast(l.message||"Failed to create form.",{type:"error"})}}n.addEventListener("click",()=>c.close()),o.addEventListener("click",a),s?.addEventListener("keydown",i=>{i.key==="Enter"&&a()})}),d.find("#test-email-btn").off("click").on("click",async()=>{const e=(window.__CMS_SITE__?.smtp||{}).fromAddress||"",r=E.slideover({title:"Send Test Email",size:"sm",position:"right"}),t=document.createElement("div");t.style.cssText="padding:.25rem 0 .5rem;";const n=document.createElement("div");n.className="mb-3";const o=document.createElement("label");o.className="form-label",o.textContent="Send to";const s=document.createElement("input");s.type="email",s.className="form-input",s.value=e,s.placeholder="test@example.com",n.appendChild(o),n.appendChild(s),t.appendChild(n);const a=document.createElement("p");a.className="text-muted",a.style.cssText="font-size:.8rem;margin-bottom:.75rem;",a.textContent="SMTP settings are configured in Site Settings.",t.appendChild(a);const i=document.createElement("div");i.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:.75rem;";const l=document.createElement("button");l.className="btn btn-ghost",l.textContent="Cancel";const m=document.createElement("button");m.className="btn btn-primary",m.textContent="Send",i.appendChild(l),i.appendChild(m),t.appendChild(i),r.element.appendChild(t),r.open(),setTimeout(()=>s?.focus(),50),l.addEventListener("click",()=>r.close()),m.addEventListener("click",async()=>{const f=s.value.trim();if(f){m.disabled=!0;try{await p("/forms/test-email",{method:"POST",body:JSON.stringify({to:f})}),r.close(),E.toast("Test email sent.",{type:"success"})}catch(y){E.toast(y.message||"Failed to send test email.",{type:"error"})}finally{m.disabled=!1}}})}),Domma.icons.scan()}};async function g(d){const c=E.loader(d.get(0),{type:"dots"});let e=[];try{e=await p("/forms")}catch{E.toast("Could not load forms.",{type:"error"})}finally{c.destroy()}const r=h();r&&(e=b(e,r)),T.create("#forms-table",{data:e,columns:[{key:"title",title:"Title",render:(t,n)=>{const o=document.createElement("span");o.style.cssText="display:inline-flex;align-items:center;gap:.4rem;flex-wrap:wrap;";const s=document.createElement("a");if(s.href=`#/forms/edit/${u(n.slug)}`,s.textContent=t,s.style.fontWeight="600",o.appendChild(s),n.plugin){const a=document.createElement("span");a.className="badge badge-outline",a.textContent=n.plugin,a.title=`Managed by the ${n.plugin} plugin`,a.style.cssText="font-size:0.65rem;padding:1px 6px;color:var(--dm-warning,#d97706);border-color:var(--dm-warning,#d97706);flex-shrink:0;",o.appendChild(a)}return o.outerHTML}},{key:"slug",title:"Slug",render:t=>{const n=document.createElement("code");return n.textContent=t,n.outerHTML}},{key:"fields",title:"Field Count",render:t=>String(t?.length??0)},{key:"submissionCount",title:"Submission Count",render:t=>String(t??0)},{key:"slug",title:"Actions",render:(t,n)=>{const o=document.createElement("div");o.style.cssText="display:flex;gap:.4rem;justify-content:flex-end;";const s=document.createElement("a");s.href=`#/forms/edit/${u(t)}`,s.className="btn btn-sm btn-ghost",s.setAttribute("data-tooltip","Edit"),s.innerHTML='<span data-icon="edit" data-icon-size="14"></span>';const a=document.createElement("a");a.href=`#/forms/${u(t)}/submissions`,a.className="btn btn-sm btn-ghost",a.setAttribute("data-tooltip","Submissions"),a.innerHTML='<span data-icon="inbox" data-icon-size="14"></span>';const i=document.createElement("button");i.className="btn btn-sm btn-ghost js-clone-form",i.dataset.slug=t,i.setAttribute("data-tooltip","Duplicate"),i.innerHTML='<span data-icon="copy" data-icon-size="14"></span>';const l=document.createElement("button");return l.className="btn btn-sm btn-ghost js-delete-form",l.dataset.slug=t,l.dataset.plugin=n.plugin||"",l.setAttribute("data-tooltip","Delete"),l.innerHTML='<span data-icon="trash" data-icon-size="14"></span>',o.appendChild(s),o.appendChild(a),o.appendChild(i),o.appendChild(l),o.outerHTML}}],emptyMessage:'No forms yet. Click "Create Form" to get started.'}),Domma.icons.scan(),document.querySelectorAll("#forms-table [data-tooltip]").forEach(t=>{E.tooltip(t,{content:t.getAttribute("data-tooltip"),position:"top"})})}
1
+ import{apiRequest as p}from"/admin/js/api.js";import{filterByProject as b,getProjectFromHash as h,renderProjectFilterNotice as C}from"/admin/js/lib/project-context.js";import{keepTableIconsAlive as x}from"/admin/js/lib/table-icons.js";function u(c){return String(c).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}export const formsView={templateUrl:"/admin/js/templates/forms.html",async onMount(c){await g(c),x(c.get(0)),c.off("click",".js-delete-form").on("click",".js-delete-form",async function(){const d=this.dataset.slug,e=this.dataset.plugin,i=e?`This form is managed by the <strong>${e}</strong> plugin. Deleting it may cause the plugin to malfunction. Continue?`:`Delete form "${d}" and all its submissions? This cannot be undone.`;if(await E.confirm(i))try{await p(`/forms/${d}`,{method:"DELETE"}),E.toast("Form deleted.",{type:"success"}),await g(c)}catch{E.toast("Failed to delete form.",{type:"error"})}}),c.off("click",".js-clone-form").on("click",".js-clone-form",async function(){const d=this.dataset.slug;try{const e=await p(`/forms/${d}`),{collection:i,...t}=e.actions||{},n=await p("/forms",{method:"POST",body:JSON.stringify({title:`${e.title} (Copy)`,description:e.description||"",fields:e.fields||[],settings:e.settings||{},actions:t})});E.toast(`Form duplicated as "${n.title}".`,{type:"success"}),R.navigate(`/forms/edit/${n.slug}`)}catch(e){E.toast(e.message||"Failed to duplicate form.",{type:"error"})}}),c.find("#create-form-btn").off("click").on("click",()=>{const d=E.slideover({title:"Create Form",size:"sm",position:"right"}),e=document.createElement("div");e.style.cssText="padding:.25rem 0 .5rem;";const i=document.createElement("div");F.create({title:{type:"string",label:"Form Title",placeholder:"e.g. Contact, Feedback\u2026",required:!0}},{},{showSubmitButton:!1}).renderTo(i),e.appendChild(i);const t=document.createElement("div");t.style.cssText="display:flex;justify-content:flex-end;gap:1rem;margin-top:.75rem;";const n=document.createElement("button");n.className="btn btn-ghost",n.textContent="Cancel";const o=document.createElement("button");o.className="btn btn-primary",o.textContent="Create",t.appendChild(n),t.appendChild(o),e.appendChild(t),d.element.appendChild(e),d.open();const s=i.querySelector('input[name="title"]');setTimeout(()=>s?.focus(),50);async function a(){const l=s?.value.trim();if(l)try{const r=await p("/forms",{method:"POST",body:JSON.stringify({title:l})});d.close(),R.navigate(`/forms/edit/${r.slug}`)}catch(r){E.toast(r.message||"Failed to create form.",{type:"error"})}}n.addEventListener("click",()=>d.close()),o.addEventListener("click",a),s?.addEventListener("keydown",l=>{l.key==="Enter"&&a()})}),c.find("#test-email-btn").off("click").on("click",async()=>{const e=(window.__CMS_SITE__?.smtp||{}).fromAddress||"",i=E.slideover({title:"Send Test Email",size:"sm",position:"right"}),t=document.createElement("div");t.style.cssText="padding:.25rem 0 .5rem;";const n=document.createElement("div");n.className="mb-3";const o=document.createElement("label");o.className="form-label",o.textContent="Send to";const s=document.createElement("input");s.type="email",s.className="form-input",s.value=e,s.placeholder="test@example.com",n.appendChild(o),n.appendChild(s),t.appendChild(n);const a=document.createElement("p");a.className="text-muted",a.style.cssText="font-size:.8rem;margin-bottom:.75rem;",a.textContent="SMTP settings are configured in Site Settings.",t.appendChild(a);const l=document.createElement("div");l.style.cssText="display:flex;justify-content:flex-end;gap:1rem;margin-top:.75rem;";const r=document.createElement("button");r.className="btn btn-ghost",r.textContent="Cancel";const m=document.createElement("button");m.className="btn btn-primary",m.textContent="Send",l.appendChild(r),l.appendChild(m),t.appendChild(l),i.element.appendChild(t),i.open(),setTimeout(()=>s?.focus(),50),r.addEventListener("click",()=>i.close()),m.addEventListener("click",async()=>{const f=s.value.trim();if(f){m.disabled=!0;try{await p("/forms/test-email",{method:"POST",body:JSON.stringify({to:f})}),i.close(),E.toast("Test email sent.",{type:"success"})}catch(y){E.toast(y.message||"Failed to send test email.",{type:"error"})}finally{m.disabled=!1}}})}),Domma.icons.scan()}};async function g(c){const d=E.loader(c.get(0),{type:"dots"});let e=[];try{e=await p("/forms")}catch{E.toast("Could not load forms.",{type:"error"})}finally{d.destroy()}const i=h();if(i){const t=e.length;e=b(e,i),C(c,{total:t,shown:e.length,slug:i,allHref:"#/forms",noun:"forms"})}T.create("#forms-table",{pageSize:25,data:e,columns:[{key:"title",title:"Title",render:(t,n)=>{const o=document.createElement("span");o.style.cssText="display:inline-flex;align-items:center;gap:.4rem;flex-wrap:wrap;";const s=document.createElement("a");if(s.href=`#/forms/edit/${u(n.slug)}`,s.textContent=t,s.style.fontWeight="600",o.appendChild(s),n.plugin){const a=document.createElement("span");a.className="badge badge-outline",a.textContent=n.plugin,a.title=`Managed by the ${n.plugin} plugin`,a.style.cssText="font-size:0.65rem;padding:1px 6px;color:var(--dm-warning,#d97706);border-color:var(--dm-warning,#d97706);flex-shrink:0;",o.appendChild(a)}return o.outerHTML}},{key:"slug",title:"Slug",render:t=>{const n=document.createElement("code");return n.textContent=t,n.outerHTML}},{key:"fields",title:"Field Count",render:t=>String(t?.length??0)},{key:"submissionCount",title:"Submission Count",render:t=>String(t??0)},{key:"slug",title:"Actions",render:(t,n)=>{const o=document.createElement("div");o.style.cssText="display:flex;gap:.4rem;justify-content:flex-end;";const s=document.createElement("a");s.href=`#/forms/edit/${u(t)}`,s.className="btn btn-sm btn-ghost",s.setAttribute("data-tooltip","Edit"),s.innerHTML='<span data-icon="edit" data-icon-size="14"></span>';const a=document.createElement("a");a.href=`#/forms/${u(t)}/submissions`,a.className="btn btn-sm btn-ghost",a.setAttribute("data-tooltip","Submissions"),a.innerHTML='<span data-icon="inbox" data-icon-size="14"></span>';const l=document.createElement("button");l.className="btn btn-sm btn-ghost js-clone-form",l.dataset.slug=t,l.setAttribute("data-tooltip","Duplicate"),l.innerHTML='<span data-icon="copy" data-icon-size="14"></span>';const r=document.createElement("button");return r.className="btn btn-sm btn-ghost js-delete-form",r.dataset.slug=t,r.dataset.plugin=n.plugin||"",r.setAttribute("data-tooltip","Delete"),r.innerHTML='<span data-icon="trash" data-icon-size="14"></span>',o.appendChild(s),o.appendChild(a),o.appendChild(l),o.appendChild(r),o.outerHTML}}],emptyMessage:'No forms yet. Click "Create Form" to get started.'}),Domma.icons.scan(),document.querySelectorAll("#forms-table [data-tooltip]").forEach(t=>{E.tooltip(t,{content:t.getAttribute("data-tooltip"),position:"top"})})}
@@ -1 +1 @@
1
- import{api as f}from"../api.js";const N={narrow:"Narrow",normal:"Normal",wide:"Wide",full:"Full"},B="display:block;font-size:.7rem;font-weight:600;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;margin-bottom:.25rem;",S="width:100%;padding:.4rem .6rem;background:var(--dm-input-bg,#1a1a1a);border:1px solid var(--dm-border,#333);border-radius:4px;color:var(--dm-text,#eee);font-size:.9em;box-sizing:border-box;";function x(n,t){const e=document.createElement("div");e.style.cssText="display:flex;flex-direction:column;gap:.25rem;";const a=document.createElement("label");return a.style.cssText=B,a.textContent=n,e.appendChild(a),e.appendChild(t),e}function L(n,t){const e=document.createElement("input");return e.type="text",e.placeholder=n||"",e.value=t||"",e.style.cssText=S,e}function k(n,t){const e=document.createElement("label");e.style.cssText="display:flex;align-items:center;gap:.5rem;cursor:pointer;";const a=document.createElement("input");a.type="checkbox",a.checked=!!t;const o=document.createElement("span");return o.style.cssText="font-size:.9em;color:var(--dm-text,#eee);",o.textContent=n,e.appendChild(a),e.appendChild(o),{wrap:e,cb:a}}function F(n){const t=document.createElement("select");return t.style.cssText=S,[["narrow","Narrow (768px)"],["normal","Normal (1100px)"],["wide","Wide (75% of viewport, max 2000px)"],["full","Full width"]].forEach(([e,a])=>{const o=document.createElement("option");o.value=e,o.textContent=a,e===(n||"normal")&&(o.selected=!0),t.appendChild(o)}),t}function D(n){const t=document.createElement("div");t.style.cssText="display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;";const e=document.createElement("input");e.type="color",e.value=n||"#ffffff",e.disabled=!n,e.style.cssText="width:38px;height:32px;padding:2px;border:1px solid var(--dm-border,#333);border-radius:4px;background:transparent;cursor:pointer;";const a=document.createElement("span");a.style.cssText="font-size:.85em;color:var(--dm-text-muted,#aaa);min-width:4rem;",a.textContent=n||"None";const o=document.createElement("button");o.type="button",o.textContent="Choose",o.style.cssText="padding:.25rem .6rem;font-size:.8em;background:transparent;border:1px solid var(--dm-border,#333);border-radius:4px;color:var(--dm-text-muted,#aaa);cursor:pointer;";const r=document.createElement("button");return r.type="button",r.textContent="Clear",r.style.cssText=o.style.cssText,r.disabled=!n,o.addEventListener("click",()=>{e.disabled=!1,r.disabled=!1,e.click()}),e.addEventListener("input",()=>{a.textContent=e.value,r.disabled=!1}),r.addEventListener("click",()=>{e.value="#ffffff",e.disabled=!0,r.disabled=!0,a.textContent="None"}),t.appendChild(e),t.appendChild(a),t.appendChild(o),t.appendChild(r),{wrap:t,getValue(){return e.disabled?"":e.value}}}function O(n,t,e){const a=document.createElement("div");a.className="card preset-card",a.style.cssText="cursor:pointer;transition:box-shadow .15s;",a.addEventListener("mouseenter",()=>{a.style.boxShadow="0 0 0 2px var(--dm-primary,#4f7fff)"}),a.addEventListener("mouseleave",()=>{a.style.boxShadow=""}),a.addEventListener("click",()=>e(n,t));const o=document.createElement("div");o.className="card-header",o.style.cssText="display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;";const r=document.createElement("strong");if(r.textContent=t.label||n,o.appendChild(r),t.builtin){const s=document.createElement("span");s.className="badge",s.textContent="built-in",s.style.cssText="font-size:.65rem;",o.appendChild(s)}const d=document.createElement("span");d.className="badge badge-outline",d.textContent=N[t.width]||"Normal",d.style.cssText="margin-left:auto;font-size:.65rem;",o.appendChild(d),a.appendChild(o);const c=document.createElement("div");if(c.className="card-body",t.description){const s=document.createElement("p");s.className="text-muted",s.style.cssText="font-size:.85em;margin:0 0 .5rem;",s.textContent=t.description,c.appendChild(s)}const l=document.createElement("div");if(l.style.cssText="display:flex;gap:.75rem;flex-wrap:wrap;",[["Navbar",t.navbar],["Footer",t.footer],["Sidebar",t.sidebar]].forEach(([s,u])=>{const p=document.createElement("span");p.style.cssText=`font-size:.75em;color:${u?"var(--dm-success,#4caf50)":"var(--dm-text-muted,#666)"};display:flex;align-items:center;gap:.25rem;`,p.textContent=(u?"\u25CF ":"\u25CB ")+s,l.appendChild(p)}),c.appendChild(l),t.bgColor){const s=document.createElement("span");s.title=t.bgColor,s.style.cssText=`display:inline-block;width:14px;height:14px;border-radius:3px;background:${t.bgColor};border:1px solid var(--dm-border,#333);margin-top:.4rem;vertical-align:middle;`,c.appendChild(s)}return a.appendChild(c),a}function z(n,t,e,a,o){const r=E.slideover({title:e?"New Layout":"Edit Layout",size:"md"}),d=document.createElement("div");d.style.cssText="display:flex;flex-direction:column;gap:1rem;padding:1rem;";const c=L("e.g. Dark Landing",t.label||""),l=L("Optional description",t.description||""),s=F(t.width),u=k("Show Navbar",t.navbar!==!1),p=k("Show Footer",t.footer!==!1),g=k("Show Sidebar",t.sidebar===!0),C=D(t.bgColor||""),b=L("https://...",t.bgImage||""),v=L("e.g. no-hero wide-content",t.class||"");d.appendChild(x("Label",c)),d.appendChild(x("Description",l)),d.appendChild(x("Width",s));const y=document.createElement("div");y.style.cssText="display:flex;gap:1.5rem;flex-wrap:wrap;",y.appendChild(u.wrap),y.appendChild(p.wrap),y.appendChild(g.wrap),d.appendChild(y),d.appendChild(x("Background Colour",C.wrap)),d.appendChild(x("Background Image URL",b)),d.appendChild(x("Custom CSS Class",v));const w=document.createElement("div");w.style.cssText="display:flex;gap:.5rem;flex-wrap:wrap;padding-top:.5rem;border-top:1px solid var(--dm-border,#333);";const h=document.createElement("button");if(h.type="button",h.className="btn btn-primary",h.textContent="Save",w.appendChild(h),!e&&!t.builtin){const i=document.createElement("button");i.type="button",i.className="btn btn-danger",i.style.marginLeft="auto",i.textContent="Delete",i.addEventListener("click",async()=>{if(await E.confirm(`Delete the "${t.label||n}" layout?`))try{await f.layouts.remove(n),E.toast("Layout deleted.",{type:"success"}),r.close(),o(n)}catch(m){E.toast(m?.message||"Failed to delete layout.",{type:"error"})}}),w.appendChild(i)}d.appendChild(w),h.addEventListener("click",async()=>{const i=c.value.trim();if(!i){E.toast("Label is required.",{type:"warning"});return}const T={label:i,description:l.value.trim(),width:s.value,navbar:u.cb.checked,footer:p.cb.checked,sidebar:g.cb.checked,bgColor:C.getValue(),bgImage:b.value.trim(),class:v.value.trim()};try{let m;e?m=await f.layouts.create(T):m=await f.layouts.update(n,T),E.toast("Layout saved.",{type:"success"}),r.close(),a(e?m.key:n,m.preset||{...t,...T})}catch(m){E.toast(m?.message||"Failed to save layout.",{type:"error"})}}),r.setContent(d),r.open()}export const layoutsView={templateUrl:"/admin/js/templates/layouts.html",async onMount(n){E.tabs(n.find("#layouts-tabs").get(0));const t=E.loader(n.get(0),{type:"dots"});let e=await f.layouts.get().catch(()=>({}));const a=await f.layouts.getOptions().catch(()=>({spacerSize:8,spacerClass:""}));t.destroy();const o=n.find("#presets-grid");function r(){o.empty();const l=o.get(0);Object.entries(e).forEach(([s,u])=>{const p=O(s,u,(g,C)=>{z(g,C,!1,(b,v)=>{e[b]={...e[b],...v},r()},b=>{delete e[b],r()})});l.appendChild(p)}),I.scan(l)}r(),n.find("#create-layout-btn").on("click",()=>{z(null,{},!0,(l,s)=>{if(!l){E.toast("Layout saved but key missing \u2014 please refresh.",{type:"warning"});return}e[l]=s,r()},()=>{})});const d=n.find("#spacer-size-input"),c=n.find("#spacer-class-input");d.val(a.spacerSize??8),c.val(a.spacerClass??""),n.find("#save-options-btn").on("click",async()=>{const l=parseInt(d.val(),10)||8,s=c.val().trim();try{await f.layouts.saveOptions({spacerSize:l,spacerClass:s}),E.toast("Layout options saved.",{type:"success"})}catch{E.toast("Failed to save layout options.",{type:"error"})}})}};
1
+ import{api as f}from"../api.js";const N={narrow:"Narrow",normal:"Normal",wide:"Wide",full:"Full"},B="display:block;font-size:.7rem;font-weight:600;color:var(--dm-text-muted,#aaa);text-transform:uppercase;letter-spacing:.05em;margin-bottom:.25rem;",S="width:100%;padding:.4rem .6rem;background:var(--dm-input-bg,#1a1a1a);border:1px solid var(--dm-border,#333);border-radius:4px;color:var(--dm-text,#eee);font-size:.9em;box-sizing:border-box;";function x(n,t){const e=document.createElement("div");e.style.cssText="display:flex;flex-direction:column;gap:.25rem;";const a=document.createElement("label");return a.style.cssText=B,a.textContent=n,e.appendChild(a),e.appendChild(t),e}function L(n,t){const e=document.createElement("input");return e.type="text",e.placeholder=n||"",e.value=t||"",e.style.cssText=S,e}function k(n,t){const e=document.createElement("label");e.style.cssText="display:flex;align-items:center;gap:.5rem;cursor:pointer;";const a=document.createElement("input");a.type="checkbox",a.checked=!!t;const o=document.createElement("span");return o.style.cssText="font-size:.9em;color:var(--dm-text,#eee);",o.textContent=n,e.appendChild(a),e.appendChild(o),{wrap:e,cb:a}}function F(n){const t=document.createElement("select");return t.style.cssText=S,[["narrow","Narrow (768px)"],["normal","Normal (1100px)"],["wide","Wide (75% of viewport, max 2000px)"],["full","Full width"]].forEach(([e,a])=>{const o=document.createElement("option");o.value=e,o.textContent=a,e===(n||"normal")&&(o.selected=!0),t.appendChild(o)}),t}function D(n){const t=document.createElement("div");t.style.cssText="display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;";const e=document.createElement("input");e.type="color",e.value=n||"#ffffff",e.disabled=!n,e.style.cssText="width:38px;height:32px;padding:2px;border:1px solid var(--dm-border,#333);border-radius:4px;background:transparent;cursor:pointer;";const a=document.createElement("span");a.style.cssText="font-size:.85em;color:var(--dm-text-muted,#aaa);min-width:4rem;",a.textContent=n||"None";const o=document.createElement("button");o.type="button",o.textContent="Choose",o.style.cssText="padding:.25rem .6rem;font-size:.8em;background:transparent;border:1px solid var(--dm-border,#333);border-radius:4px;color:var(--dm-text-muted,#aaa);cursor:pointer;";const r=document.createElement("button");return r.type="button",r.textContent="Clear",r.style.cssText=o.style.cssText,r.disabled=!n,o.addEventListener("click",()=>{e.disabled=!1,r.disabled=!1,e.click()}),e.addEventListener("input",()=>{a.textContent=e.value,r.disabled=!1}),r.addEventListener("click",()=>{e.value="#ffffff",e.disabled=!0,r.disabled=!0,a.textContent="None"}),t.appendChild(e),t.appendChild(a),t.appendChild(o),t.appendChild(r),{wrap:t,getValue(){return e.disabled?"":e.value}}}function O(n,t,e){const a=document.createElement("div");a.className="card preset-card",a.style.cssText="cursor:pointer;transition:box-shadow .15s;",a.addEventListener("mouseenter",()=>{a.style.boxShadow="0 0 0 2px var(--dm-primary,#4f7fff)"}),a.addEventListener("mouseleave",()=>{a.style.boxShadow=""}),a.addEventListener("click",()=>e(n,t));const o=document.createElement("div");o.className="card-header",o.style.cssText="display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;";const r=document.createElement("strong");if(r.textContent=t.label||n,o.appendChild(r),t.builtin){const s=document.createElement("span");s.className="badge",s.textContent="built-in",s.style.cssText="font-size:.65rem;",o.appendChild(s)}const d=document.createElement("span");d.className="badge badge-outline",d.textContent=N[t.width]||"Normal",d.style.cssText="margin-left:auto;font-size:.65rem;",o.appendChild(d),a.appendChild(o);const c=document.createElement("div");if(c.className="card-body",t.description){const s=document.createElement("p");s.className="text-muted",s.style.cssText="font-size:.85em;margin:0 0 .5rem;",s.textContent=t.description,c.appendChild(s)}const l=document.createElement("div");if(l.style.cssText="display:flex;gap:.75rem;flex-wrap:wrap;",[["Navbar",t.navbar],["Footer",t.footer],["Sidebar",t.sidebar]].forEach(([s,u])=>{const p=document.createElement("span");p.style.cssText=`font-size:.75em;color:${u?"var(--dm-success,#4caf50)":"var(--dm-text-muted,#666)"};display:flex;align-items:center;gap:.25rem;`,p.textContent=(u?"\u25CF ":"\u25CB ")+s,l.appendChild(p)}),c.appendChild(l),t.bgColor){const s=document.createElement("span");s.title=t.bgColor,s.style.cssText=`display:inline-block;width:14px;height:14px;border-radius:3px;background:${t.bgColor};border:1px solid var(--dm-border,#333);margin-top:.4rem;vertical-align:middle;`,c.appendChild(s)}return a.appendChild(c),a}function z(n,t,e,a,o){const r=E.slideover({title:e?"New Layout":"Edit Layout",size:"md"}),d=document.createElement("div");d.style.cssText="display:flex;flex-direction:column;gap:1rem;padding:1rem;";const c=L("e.g. Dark Landing",t.label||""),l=L("Optional description",t.description||""),s=F(t.width),u=k("Show Navbar",t.navbar!==!1),p=k("Show Footer",t.footer!==!1),g=k("Show Sidebar",t.sidebar===!0),C=D(t.bgColor||""),b=L("https://...",t.bgImage||""),v=L("e.g. no-hero wide-content",t.class||"");d.appendChild(x("Label",c)),d.appendChild(x("Description",l)),d.appendChild(x("Width",s));const y=document.createElement("div");y.style.cssText="display:flex;gap:1.5rem;flex-wrap:wrap;",y.appendChild(u.wrap),y.appendChild(p.wrap),y.appendChild(g.wrap),d.appendChild(y),d.appendChild(x("Background Colour",C.wrap)),d.appendChild(x("Background Image URL",b)),d.appendChild(x("Custom CSS Class",v));const w=document.createElement("div");w.style.cssText="display:flex;gap:.5rem;flex-wrap:wrap;padding-top:.5rem;border-top:1px solid var(--dm-border,#333);";const h=document.createElement("button");if(h.type="button",h.className="btn btn-primary",h.textContent="Save",w.appendChild(h),!e&&!t.builtin){const i=document.createElement("button");i.type="button",i.className="btn btn-danger",i.style.marginLeft="auto",i.textContent="Delete",i.addEventListener("click",async()=>{if(await E.confirm(`Delete the "${t.label||n}" layout?`))try{await f.layouts.remove(n),E.toast("Layout deleted.",{type:"success"}),r.close(),o(n)}catch(m){E.toast(m?.message||"Failed to delete layout.",{type:"error"})}}),w.appendChild(i)}d.appendChild(w),h.addEventListener("click",async()=>{const i=c.value.trim();if(!i){E.toast("Label is required.",{type:"warning"});return}const T={label:i,description:l.value.trim(),width:s.value,navbar:u.cb.checked,footer:p.cb.checked,sidebar:g.cb.checked,bgColor:C.getValue(),bgImage:b.value.trim(),class:v.value.trim()};try{let m;e?m=await f.layouts.create(T):m=await f.layouts.update(n,T),E.toast("Layout saved.",{type:"success"}),r.close(),a(e?m.key:n,m.preset||{...t,...T})}catch(m){E.toast(m?.message||"Failed to save layout.",{type:"error"})}}),r.setContent(d),r.open()}export const layoutsView={templateUrl:"/admin/js/templates/layouts.html",async onMount(n){E.tabs(n.find("#layouts-tabs").get(0));const t=E.loader(n.get(0),{type:"dots"});let e=await f.layouts.get().catch(()=>({}));const a=await f.layouts.getOptions().catch(()=>({spacerSize:8,spacerClass:""}));t.destroy();const o=n.find("#presets-grid");function r(){o.empty();const l=o.get(0);Object.entries(e).forEach(([s,u])=>{const p=O(s,u,(g,C)=>{z(g,C,!1,(b,v)=>{e[b]={...e[b],...v},r()},b=>{delete e[b],r()})});l.appendChild(p)}),I.scan(l)}r(),n.find("#create-layout-btn").on("click",()=>{z(null,{},!0,(l,s)=>{if(!l){E.toast("Layout saved but key missing - please refresh.",{type:"warning"});return}e[l]=s,r()},()=>{})});const d=n.find("#spacer-size-input"),c=n.find("#spacer-class-input");d.val(a.spacerSize??8),c.val(a.spacerClass??""),n.find("#save-options-btn").on("click",async()=>{const l=parseInt(d.val(),10)||8,s=c.val().trim();try{await f.layouts.saveOptions({spacerSize:l,spacerClass:s}),E.toast("Layout options saved.",{type:"success"})}catch{E.toast("Failed to save layout options.",{type:"error"})}})}};
@@ -1,4 +1,4 @@
1
- import{api as o,isAuthenticated as u,setAuthData as l}from"../api.js";import{THEMES as p}from"../lib/themes.js";const f={name:{type:"string",required:!0,minLength:2,label:"Full Name",formConfig:{placeholder:"Your name",autocomplete:"name"}},email:{type:"email",required:!0,label:"Email Address",formConfig:{placeholder:"admin@example.com",autocomplete:"email"}},password:{type:"password",required:!0,minLength:8,label:"Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"new-password",tooltip:"Minimum 8 characters"}}},h={email:{type:"email",required:!0,label:"Email Address",formConfig:{placeholder:"you@example.com",autocomplete:"email"}},password:{type:"password",required:!0,label:"Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"current-password"}}};export const loginView={templateUrl:"/admin/js/templates/login.html",async onMount(e){if(u()){R.navigate("/");return}const a=window.location.hash,t=a.match(/[?&]token=([a-f0-9]{64})/);if(a.startsWith("#/reset-password")&&t){r(e,"reset"),S(e,t[1]),Domma.icons.scan();return}if(a.startsWith("#/reset-password")){R.navigate("/login");return}let s=!1;const i=E.loader(e.get(0),{type:"dots"});try{s=(await o.auth.setupStatus()).needsSetup}catch{E.toast("Could not reach the server.",{type:"error"})}finally{i.destroy()}s?(r(e,"setup"),y(e)):(r(e,"login"),k(e)),Domma.icons.scan()}};const g={email:{type:"email",required:!0,label:"Email Address",formConfig:{placeholder:"you@example.com",autocomplete:"email"}}},b={password:{type:"password",required:!0,minLength:8,label:"New Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"new-password",tooltip:"Minimum 8 characters"}},confirmPassword:{type:"password",required:!0,label:"Confirm Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"new-password"}}},w=["setup","onboarding-site","onboarding-theme","onboarding-done","login","forgot","forgot-success","reset"];function r(e,a){w.forEach(t=>e.find(`#${t}-panel`).hide()),e.find(`#${a}-panel`).show(),Domma.icons.scan()}function d(e,a,t){e.find(`#${a}`).text(t).show()}function y(e){F.render("#setup-form-container",f,{},{layout:"stacked",submitText:"Create admin account",onSubmit:async a=>{try{const t=await o.auth.setup(a);l(t),r(e,"onboarding-site"),v(e)}catch(t){return E.toast(t.message||"Setup failed. Please try again.",{type:"error"}),!1}}})}function v(e){e.find("#ob-site-skip").on("click",a=>{a.preventDefault(),r(e,"onboarding-theme"),c(e),m(e)}),e.find("#ob-site-btn").on("click",async()=>{e.find("#ob-site-error").hide();const a=e.find("#ob-title").val().trim(),t=e.find("#ob-tagline").val().trim(),s=e.find("#ob-site-btn").prop("disabled",!0).text("Saving\u2026");try{const i=await o.settings.get();await o.settings.save({...i,title:a||i.title,tagline:t||i.tagline,seo:{...i.seo||{},defaultTitle:a||i.seo&&i.seo.defaultTitle}});const n=await o.navigation.get();await o.navigation.save({...n,brand:{...n.brand||{},text:a||n.brand.text}}),r(e,"onboarding-theme"),c(e),m(e)}catch(i){d(e,"ob-site-error",i.message||"Could not save site details.")}finally{s.prop("disabled",!1).text("Continue")}})}function c(e){const a=e.find("#theme-grid").empty();p.forEach(t=>{const s=$(`
1
+ import{api as o,isAuthenticated as u,setAuthData as l}from"../api.js";import{themesInDisplayOrder as p}from"../lib/themes.js";const f={name:{type:"string",required:!0,minLength:2,label:"Full Name",formConfig:{placeholder:"Your name",autocomplete:"name"}},email:{type:"email",required:!0,label:"Email Address",formConfig:{placeholder:"admin@example.com",autocomplete:"email"}},password:{type:"password",required:!0,minLength:8,label:"Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"new-password",tooltip:"Minimum 8 characters"}}},h={email:{type:"email",required:!0,label:"Email Address",formConfig:{placeholder:"you@example.com",autocomplete:"email"}},password:{type:"password",required:!0,label:"Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"current-password"}}};export const loginView={templateUrl:"/admin/js/templates/login.html",async onMount(e){if(u()){R.navigate("/");return}const s=window.location.hash,t=s.match(/[?&]token=([a-f0-9]{64})/);if(s.startsWith("#/reset-password")&&t){r(e,"reset"),x(e,t[1]),Domma.icons.scan();return}if(s.startsWith("#/reset-password")){R.navigate("/login");return}let a=!1;const i=E.loader(e.get(0),{type:"dots"});try{a=(await o.auth.setupStatus()).needsSetup}catch{E.toast("Could not reach the server.",{type:"error"})}finally{i.destroy()}a?(r(e,"setup"),y(e)):(r(e,"login"),k(e)),Domma.icons.scan()}};const g={email:{type:"email",required:!0,label:"Email Address",formConfig:{placeholder:"you@example.com",autocomplete:"email"}}},b={password:{type:"password",required:!0,minLength:8,label:"New Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"new-password",tooltip:"Minimum 8 characters"}},confirmPassword:{type:"password",required:!0,label:"Confirm Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"new-password"}}},w=["setup","onboarding-site","onboarding-theme","onboarding-done","login","forgot","forgot-success","reset"];function r(e,s){w.forEach(t=>e.find(`#${t}-panel`).hide()),e.find(`#${s}-panel`).show(),Domma.icons.scan()}function d(e,s,t){e.find(`#${s}`).text(t).show()}function y(e){F.render("#setup-form-container",f,{},{layout:"stacked",submitText:"Create admin account",onSubmit:async s=>{try{const t=await o.auth.setup(s);l(t),r(e,"onboarding-site"),v(e)}catch(t){return E.toast(t.message||"Setup failed. Please try again.",{type:"error"}),!1}}})}function v(e){e.find("#ob-site-skip").on("click",s=>{s.preventDefault(),r(e,"onboarding-theme"),c(e),m(e)}),e.find("#ob-site-btn").on("click",async()=>{e.find("#ob-site-error").hide();const s=e.find("#ob-title").val().trim(),t=e.find("#ob-tagline").val().trim(),a=e.find("#ob-site-btn").prop("disabled",!0).text("Saving\u2026");try{const i=await o.settings.get();await o.settings.save({...i,title:s||i.title,tagline:t||i.tagline,seo:{...i.seo||{},defaultTitle:s||i.seo&&i.seo.defaultTitle}});const n=await o.navigation.get();await o.navigation.save({...n,brand:{...n.brand||{},text:s||n.brand.text}}),r(e,"onboarding-theme"),c(e),m(e)}catch(i){d(e,"ob-site-error",i.message||"Could not save site details.")}finally{a.prop("disabled",!1).text("Continue")}})}function c(e){const s=e.find("#theme-grid").empty();p().forEach(t=>{const a=$(`
2
2
  <div class="theme-swatch" data-theme="${t.id}" title="${t.id}">
3
3
  <div class="theme-swatch-preview" style="background:${t.bg}">
4
4
  <div class="theme-swatch-accent" style="background:${t.primary}"></div>
@@ -8,4 +8,4 @@ import{api as o,isAuthenticated as u,setAuthData as l}from"../api.js";import{THE
8
8
  <span class="theme-swatch-mode">${t.dark?"Dark":"Light"}</span>
9
9
  </div>
10
10
  </div>
11
- `);s.on("click",()=>{e.find(".theme-swatch").removeClass("selected"),s.addClass("selected")}),a.append(s)}),e.find('[data-theme="charcoal-dark"]').addClass("selected")}function m(e){e.find("#ob-theme-skip").on("click",a=>{a.preventDefault(),r(e,"onboarding-done")}),e.find("#ob-theme-btn").on("click",async()=>{e.find("#ob-theme-error").hide();const a=e.find(".theme-swatch.selected").attr("data-theme")||"charcoal-dark",t=e.find("#ob-theme-btn").prop("disabled",!0).text("Applying\u2026");try{const s=await o.settings.get();await o.settings.save({...s,theme:a}),r(e,"onboarding-done")}catch(s){d(e,"ob-theme-error",s.message||"Could not save theme.")}finally{t.prop("disabled",!1).text("Apply theme")}})}function k(e){F.render("#login-form-container",h,{},{layout:"stacked",submitText:"Sign in",onSubmit:async a=>{try{const t=await o.auth.login(a);l(t),R.navigate("/")}catch(t){return E.toast(t.message||"Invalid credentials. Please try again.",{type:"error"}),!1}}}),e.find("#forgot-link").on("click",a=>{a.preventDefault(),r(e,"forgot"),C(e)})}function C(e){F.render("#forgot-form-container",g,{},{layout:"stacked",submitText:"Send reset link",onSubmit:async t=>{try{await o.auth.forgotPassword(t.email)}catch{}r(e,"forgot-success"),Domma.icons.scan()}});const a=t=>{t.preventDefault(),r(e,"login")};e.find("#forgot-back-link").on("click",a),e.find("#forgot-success-back-link").on("click",a)}function S(e,a){F.render("#reset-form-container",b,{},{layout:"stacked",submitText:"Set new password",onSubmit:async t=>{if(t.password!==t.confirmPassword)return E.toast("Passwords do not match.",{type:"error"}),!1;try{await o.auth.resetPassword(a,t.password),E.toast("Password updated. Please sign in.",{type:"success"}),window.location.hash="#/login"}catch(s){return E.toast(s.message||"Link expired or invalid.",{type:"error"}),!1}}})}
11
+ `);a.on("click",()=>{e.find(".theme-swatch").removeClass("selected"),a.addClass("selected")}),s.append(a)}),e.find('[data-theme="charcoal-dark"]').addClass("selected")}function m(e){e.find("#ob-theme-skip").on("click",s=>{s.preventDefault(),r(e,"onboarding-done")}),e.find("#ob-theme-btn").on("click",async()=>{e.find("#ob-theme-error").hide();const s=e.find(".theme-swatch.selected").attr("data-theme")||"charcoal-dark",t=e.find("#ob-theme-btn").prop("disabled",!0).text("Applying\u2026");try{const a=await o.settings.get();await o.settings.save({...a,theme:s}),r(e,"onboarding-done")}catch(a){d(e,"ob-theme-error",a.message||"Could not save theme.")}finally{t.prop("disabled",!1).text("Apply theme")}})}function k(e){F.render("#login-form-container",h,{},{layout:"stacked",submitText:"Sign in",onSubmit:async s=>{try{const t=await o.auth.login(s);l(t),R.navigate("/")}catch(t){return E.toast(t.message||"Invalid credentials. Please try again.",{type:"error"}),!1}}}),e.find("#forgot-link").on("click",s=>{s.preventDefault(),r(e,"forgot"),C(e)})}function C(e){F.render("#forgot-form-container",g,{},{layout:"stacked",submitText:"Send reset link",onSubmit:async t=>{try{await o.auth.forgotPassword(t.email)}catch{}r(e,"forgot-success"),Domma.icons.scan()}});const s=t=>{t.preventDefault(),r(e,"login")};e.find("#forgot-back-link").on("click",s),e.find("#forgot-success-back-link").on("click",s)}function x(e,s){F.render("#reset-form-container",b,{},{layout:"stacked",submitText:"Set new password",onSubmit:async t=>{if(t.password!==t.confirmPassword)return E.toast("Passwords do not match.",{type:"error"}),!1;try{await o.auth.resetPassword(s,t.password),E.toast("Password updated. Please sign in.",{type:"success"}),window.location.hash="#/login"}catch(a){return E.toast(a.message||"Link expired or invalid.",{type:"error"}),!1}}})}