domma-cms 0.18.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/CLAUDE.md +37 -3
  2. package/admin/css/admin.css +1 -1
  3. package/admin/js/api.js +1 -1
  4. package/admin/js/app.js +4 -4
  5. package/admin/js/config/sidebar-config.js +1 -1
  6. package/admin/js/lib/crud-tutorial.js +1 -0
  7. package/admin/js/lib/markdown-toolbar.js +5 -5
  8. package/admin/js/lib/project-context.js +1 -0
  9. package/admin/js/lib/sidebar-renderer.js +4 -0
  10. package/admin/js/templates/action-editor.html +7 -0
  11. package/admin/js/templates/block-editor.html +7 -0
  12. package/admin/js/templates/collection-editor.html +9 -0
  13. package/admin/js/templates/form-editor.html +9 -0
  14. package/admin/js/templates/menu-editor.html +98 -0
  15. package/admin/js/templates/menu-locations.html +14 -0
  16. package/admin/js/templates/menus.html +14 -0
  17. package/admin/js/templates/page-editor.html +9 -2
  18. package/admin/js/templates/project-detail.html +50 -0
  19. package/admin/js/templates/project-editor.html +45 -0
  20. package/admin/js/templates/project-settings.html +60 -0
  21. package/admin/js/templates/projects.html +13 -0
  22. package/admin/js/templates/role-editor.html +11 -0
  23. package/admin/js/templates/tutorials.html +335 -2
  24. package/admin/js/templates/view-editor.html +7 -0
  25. package/admin/js/views/action-editor.js +1 -1
  26. package/admin/js/views/actions-list.js +1 -1
  27. package/admin/js/views/block-editor.js +8 -8
  28. package/admin/js/views/blocks.js +2 -2
  29. package/admin/js/views/collection-editor.js +4 -4
  30. package/admin/js/views/collections.js +1 -1
  31. package/admin/js/views/form-editor.js +5 -5
  32. package/admin/js/views/forms.js +1 -1
  33. package/admin/js/views/index.js +1 -1
  34. package/admin/js/views/menu-editor.js +19 -0
  35. package/admin/js/views/menu-locations.js +1 -0
  36. package/admin/js/views/menus.js +5 -0
  37. package/admin/js/views/page-editor.js +24 -24
  38. package/admin/js/views/pages.js +3 -3
  39. package/admin/js/views/project-detail.js +4 -0
  40. package/admin/js/views/project-editor.js +1 -0
  41. package/admin/js/views/project-settings.js +1 -0
  42. package/admin/js/views/projects.js +7 -0
  43. package/admin/js/views/role-editor.js +1 -1
  44. package/admin/js/views/roles.js +3 -3
  45. package/admin/js/views/tutorials.js +1 -1
  46. package/admin/js/views/user-editor.js +1 -1
  47. package/admin/js/views/users.js +3 -3
  48. package/admin/js/views/view-editor.js +1 -1
  49. package/admin/js/views/views-list.js +1 -1
  50. package/config/menu-locations.json +5 -0
  51. package/config/menus/admin-sidebar.json +185 -0
  52. package/config/menus/footer.json +33 -0
  53. package/config/menus/main.json +35 -0
  54. package/config/menus/sproj-1779696558011-menu.json +17 -0
  55. package/config/menus/sproj-1779696960337-menu.json +18 -0
  56. package/config/menus/sproj-1779696985353-menu.json +18 -0
  57. package/config/site.json +6 -22
  58. package/package.json +4 -3
  59. package/plugins/analytics/daily.json +3 -0
  60. package/plugins/analytics/journeys.json +8 -0
  61. package/plugins/analytics/lifetime.json +1 -1
  62. package/public/css/site.css +1 -1
  63. package/public/js/collection-browser.js +4 -0
  64. package/public/js/forms.js +1 -1
  65. package/public/js/site.js +1 -1
  66. package/server/middleware/auth.js +88 -22
  67. package/server/routes/api/actions.js +58 -5
  68. package/server/routes/api/auth.js +2 -2
  69. package/server/routes/api/blocks.js +18 -3
  70. package/server/routes/api/collections.js +201 -8
  71. package/server/routes/api/forms.js +266 -21
  72. package/server/routes/api/menu-locations.js +46 -0
  73. package/server/routes/api/menus.js +115 -0
  74. package/server/routes/api/pages.js +1 -1
  75. package/server/routes/api/projects.js +107 -0
  76. package/server/routes/api/scaffold.js +86 -0
  77. package/server/routes/api/sidebar.js +23 -0
  78. package/server/routes/api/users.js +32 -7
  79. package/server/routes/api/views.js +10 -2
  80. package/server/routes/public.js +79 -6
  81. package/server/server.js +38 -0
  82. package/server/services/actions.js +137 -8
  83. package/server/services/adapters/FileAdapter.js +23 -8
  84. package/server/services/adapters/MongoAdapter.js +36 -18
  85. package/server/services/blocks.js +20 -8
  86. package/server/services/collections.js +85 -8
  87. package/server/services/content.js +23 -9
  88. package/server/services/filterEngine.js +281 -0
  89. package/server/services/hooks.js +48 -0
  90. package/server/services/markdown.js +686 -109
  91. package/server/services/menus-migration.js +107 -0
  92. package/server/services/menus.js +422 -0
  93. package/server/services/permissionRegistry.js +26 -0
  94. package/server/services/plugins.js +9 -2
  95. package/server/services/presetCollections.js +22 -0
  96. package/server/services/projects.js +429 -0
  97. package/server/services/recipes/contact-list.json +78 -0
  98. package/server/services/recipes/onboarding.json +426 -0
  99. package/server/services/references.js +174 -0
  100. package/server/services/renderer.js +237 -40
  101. package/server/services/roles.js +6 -1
  102. package/server/services/rowAccess.js +86 -13
  103. package/server/services/scaffolder.js +465 -0
  104. package/server/services/sidebar-migration.js +117 -0
  105. package/server/services/sitemap.js +112 -0
  106. package/server/services/userRoles.js +86 -0
  107. package/server/services/users.js +23 -2
  108. package/server/services/views.js +15 -4
  109. package/server/templates/page.html +7 -2
  110. /package/config/{navigation.json → navigation.json.bak} +0 -0
@@ -1,8 +1,8 @@
1
- import{api as g}from"../api.js";let u=null,f=!1;function h(){f=!0;const e=document.getElementById("block-quick-switch");e&&(e.disabled=!0)}function M(){f=!1;const e=document.getElementById("block-quick-switch");e&&(e.disabled=!1)}function x(e,t){if(e==null||e==="")return null;const n=e instanceof Date?e:new Date(e);if(isNaN(n.getTime()))return null;const s=(o,c=2)=>String(o).padStart(c,"0"),a=["January","February","March","April","May","June","July","August","September","October","November","December"],l=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],i={YYYY:()=>n.getFullYear(),YY:()=>s(n.getFullYear()%100),MMMM:()=>a[n.getMonth()],MMM:()=>a[n.getMonth()].slice(0,3),MM:()=>s(n.getMonth()+1),M:()=>n.getMonth()+1,DD:()=>s(n.getDate()),D:()=>n.getDate(),dddd:()=>l[n.getDay()],ddd:()=>l[n.getDay()].slice(0,3),HH:()=>s(n.getHours()),H:()=>n.getHours(),hh:()=>s((n.getHours()+11)%12+1),h:()=>(n.getHours()+11)%12+1,mm:()=>s(n.getMinutes()),m:()=>n.getMinutes(),ss:()=>s(n.getSeconds()),s:()=>n.getSeconds(),A:()=>n.getHours()<12?"AM":"PM",a:()=>n.getHours()<12?"am":"pm"};return t.replace(/YYYY|YY|MMMM|MMM|MM|M|DD|D|dddd|ddd|HH|H|hh|h|mm|m|ss|s|A|a/g,o=>String(i[o]()))}export const blockEditorView={templateUrl:"/admin/js/templates/block-editor.html",async onMount(e){I.register("edit-2",{viewBox:"0 0 24 24",path:"M17 3a2.85 2.85 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5z",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),requestAnimationFrame(()=>{document.querySelectorAll('[data-icon="edit-2"]').forEach(a=>{if(a.tagName.toLowerCase()==="svg")return;const l=I.render("edit-2",{size:parseInt(a.dataset.iconSize,10)||24});l&&a.replaceWith(l)})}),u=null,f=!1;const t=window.location.hash.match(/\/blocks\/edit\/([^/?#]+)/);t&&(u=decodeURIComponent(t[1]));const n=e.find("#block-name").get(0),s=e.find("#block-content").get(0);if(u){e.find("#block-editor-title").text("Edit Block"),n&&(n.value=u,n.disabled=!0);try{const a=await g.blocks.get(u);s&&(s.value=a.content||""),e.find("#block-bundled").prop("checked",!!a.bundled),e.find("#block-css").val(a.css||"");try{const l=await g.blocks.list(),i=e.find("#block-quick-switch").get(0);i&&Array.isArray(l)&&l.length&&(l.slice().sort((o,c)=>o.name.localeCompare(c.name)).forEach(o=>{const c=document.createElement("option");c.value=o.name,c.textContent=o.name,o.name===u&&(c.selected=!0),i.appendChild(c)}),i.style.display="",i.addEventListener("change",function(){const o=this.value;o&&o!==u&&!f&&R.navigate(`/blocks/edit/${encodeURIComponent(o)}`)}))}catch{}}catch(a){E.toast(a.message||"Block not found.",{type:"error"}),R.navigate("/blocks");return}}s&&(C(s,e),$(s,e)),e.find("#save-block-btn").off("click").on("click",async()=>{await H(e)}),Domma.icons.scan()}};function C(e,t){const n=t.find("#block-line-numbers").get(0),s=t.find("#block-cursor-pos").get(0);function a(){const o=e.value.split(`
2
- `).length;n.textContent=Array.from({length:o},(c,d)=>d+1).join(`
3
- `),n.scrollTop=e.scrollTop}a(),e.addEventListener("input",a),e.addEventListener("scroll",()=>{n.scrollTop=e.scrollTop});function l(){if(!s)return;const o=e.value.slice(0,e.selectionStart).split(`
4
- `);s.textContent=`Ln ${o.length}, Col ${o[o.length-1].length+1}`}e.addEventListener("keyup",l),e.addEventListener("click",l),e.addEventListener("keydown",o=>{if(o.key==="Tab"){o.preventDefault();const c=e.selectionStart,d=e.selectionEnd;if(!o.shiftKey)e.value=e.value.slice(0,c)+" "+e.value.slice(d),e.selectionStart=e.selectionEnd=c+2;else{const r=e.value.lastIndexOf(`
5
- `,c-1)+1,m=e.value.slice(r,r+2),p=m===" "?2:m[0]===" "?1:0;p>0&&(e.value=e.value.slice(0,r)+e.value.slice(r+p),e.selectionStart=e.selectionEnd=Math.max(r,c-p))}e.dispatchEvent(new Event("input"))}if(o.key==="Enter"){o.preventDefault();const c=e.selectionStart,d=e.value.lastIndexOf(`
6
- `,c-1)+1,r=e.value.slice(d,c).match(/^(\s*)/)[1];e.value=e.value.slice(0,c)+`
7
- `+r+e.value.slice(e.selectionEnd),e.selectionStart=e.selectionEnd=c+1+r.length,e.dispatchEvent(new Event("input"))}});const i=t.find("#block-editor-toolbar").get(0);i&&i.addEventListener("click",o=>{const c=o.target.closest("[data-action]");c&&(L(c.dataset.action,e),e.focus())})}function L(e,t){switch(e){case"undo":document.execCommand("undo");break;case"redo":document.execCommand("redo");break;case"cut":t.selectionStart!==t.selectionEnd&&(navigator.clipboard.writeText(t.value.slice(t.selectionStart,t.selectionEnd)).catch(()=>{}),document.execCommand("cut"));break;case"copy":t.selectionStart!==t.selectionEnd&&navigator.clipboard.writeText(t.value.slice(t.selectionStart,t.selectionEnd)).catch(()=>{});break;case"paste":navigator.clipboard.readText().then(n=>{const s=t.selectionStart;t.value=t.value.slice(0,s)+n+t.value.slice(t.selectionEnd),t.selectionStart=t.selectionEnd=s+n.length,t.dispatchEvent(new Event("input"))}).catch(()=>E.toast("Use Ctrl+V to paste.",{type:"info"}));break;case"select-all":t.select();break;case"format":D(t);break}}function D(e){const t=e.value.match(/(<[^>]+>|[^<]+)/g)||[],n=[];let s=0;const a=" ";for(const o of t){const c=o.trim();if(c)if(c.startsWith("</"))s=Math.max(0,s-1),n.push(a.repeat(s)+c);else if(c.startsWith("<")&&!c.startsWith("<!")&&!c.endsWith("/>")){n.push(a.repeat(s)+c);const d=(c.match(/^<(\w+)/)||[])[1]||"";T.has(d.toLowerCase())||s++}else n.push(a.repeat(s)+c)}const l=n.join(`
8
- `),i=e.selectionStart;e.value=l,e.selectionStart=e.selectionEnd=Math.min(i,l.length),e.dispatchEvent(new Event("input"))}const T=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]);function $(e,t){const n=t.find("#block-editor-body").get(0),s=t.find("#block-sample-data").get(0),a=t.find("#block-preview-output");t.find("[data-mode]").each(function(){this.addEventListener("click",()=>{t.find("[data-mode]").each(function(){this.classList.remove("active")}),this.classList.add("active"),n.className=`editor-body editor-mode-${this.dataset.mode}`,this.dataset.mode!=="write"&&l()})}),e.addEventListener("input",()=>{S(s,e),l(),h()});{const i=t.find("#block-css").get(0);i&&i.addEventListener("input",()=>{l(),h()});const o=t.find("#block-name").get(0);o&&o.addEventListener("input",()=>{l(),h()});const c=t.find("#block-bundled").get(0);c&&c.addEventListener("change",h)}e.value.trim()&&(S(s,e),l());function l(){if(n.classList.contains("editor-mode-write"))return;const i=e.value.replace(/\{\{([\w_]+)(?::([^{}]+))?\}\}/g,(p,b,k)=>{const y=s.querySelector(`[data-placeholder="${CSS.escape(b)}"]`)?.value??`[${b}]`;if(k){const w=x(y,k);if(w!==null)return v(w)}return v(y)}),o=(t.find("#block-name").val()||"preview").trim()||"preview",c=v(o),d=t.find("#block-css").val()||"",r=t.find("#block-css-scope-hint").get(0);r&&(r.textContent=`[data-block="${o}"]`);const m=d.trim()?`<style>[data-block="${c}"] { ${String(d).replace(/<\/style/gi,"<\\/style")} }</style>`:"";a.html(m+`<div data-block="${c}">${i}</div>`,{safe:!1}),Domma.icons.scan(a.get(0))}s._renderPreview=l}function S(e,t){const n=q(t.value),s=new Set([...e.querySelectorAll("[data-placeholder]")].map(a=>a.dataset.placeholder));if(n.size>0&&e.querySelector("p.text-muted")?.remove(),n.forEach(a=>{s.has(a)||A(e,a)}),e.querySelectorAll("[data-placeholder]").forEach(a=>{n.has(a.dataset.placeholder)||a.closest(".block-sample-row")?.remove()}),n.size===0&&!e.querySelector("p.text-muted")){const a=document.createElement("p");a.className="text-muted",a.style.cssText="font-size:.8rem;margin:0;",a.textContent="No {{placeholders}} detected in template.",e.appendChild(a)}}function A(e,t){const n=document.createElement("div");n.className="block-sample-row",n.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.35rem;";const s=document.createElement("label");s.style.cssText="font-size:.75rem;color:var(--dm-text-muted,#888);white-space:nowrap;min-width:90px;text-align:right;flex-shrink:0;",s.textContent=`{{${t}}}`;const a=document.createElement("input");a.type="text",a.className="form-input form-input--sm",a.style.flex="1",a.dataset.placeholder=t,a.placeholder=`Sample ${t}`,a.value=Y(t),a.addEventListener("input",()=>{e._renderPreview&&e._renderPreview()}),n.appendChild(s),n.appendChild(a),e.appendChild(n)}function Y(e){const t=e.toLowerCase();return t==="_id"?"a1b2c3d4-e5f6-7890-abcd-ef1234567890":t==="_createdat"?new Date().toISOString():t==="_updatedat"?new Date().toISOString():t.includes("email")?"user@example.com":t.includes("phone")?"+44 7700 900000":t.includes("name")?"Jane Smith":t.includes("title")?"Sample Title":t.includes("message")||t.includes("content")||t.includes("description")?"This is a sample value for preview purposes.":t.includes("rating")?"excellent":t.includes("status")?"active":t.includes("priority")?"high":t.includes("date")?new Date().toLocaleDateString():t.includes("tag")?"tag1, tag2":t.includes("subject")?"Sample Subject":t.includes("category")?"general":`Sample ${e}`}function q(e){const t=new Set;for(const[,n]of e.matchAll(/\{\{([\w_]+)(?::[^{}]+)?\}\}/g))t.add(n);return t}function v(e){return String(e??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function H(e){const t=e.find("#block-name").get(0),n=e.find("#block-content").get(0),s=(t?.value||"").trim(),a=n?.value??"";if(!s){E.toast("Block name is required.",{type:"warning"});return}if(!/^[a-z0-9][a-z0-9-]*$/.test(s)){E.toast("Name must start with a letter or digit and contain only lowercase letters, digits, and hyphens.",{type:"warning"});return}const l=!!e.find("#block-bundled").is(":checked"),i=e.find("#save-block-btn").get(0);i&&(i.disabled=!0);try{await g.blocks.put(s,{content:a,bundled:l,css:e.find("#block-css").val()||""}),M(),E.toast(u?"Block updated.":"Block created.",{type:"success"}),u||(u=s,R.navigate(`/blocks/edit/${encodeURIComponent(s)}`))}catch(o){E.toast(o.message||"Failed to save block.",{type:"error"})}finally{i&&(i.disabled=!1)}}
1
+ import{api as f}from"../api.js";let u=null,h=!1,v={};function g(){h=!0;const e=document.getElementById("block-quick-switch");e&&(e.disabled=!0)}function x(){h=!1;const e=document.getElementById("block-quick-switch");e&&(e.disabled=!1)}function C(e,t){if(e==null||e==="")return null;const n=e instanceof Date?e:new Date(e);if(isNaN(n.getTime()))return null;const c=(s,o=2)=>String(s).padStart(o,"0"),a=["January","February","March","April","May","June","July","August","September","October","November","December"],l=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],i={YYYY:()=>n.getFullYear(),YY:()=>c(n.getFullYear()%100),MMMM:()=>a[n.getMonth()],MMM:()=>a[n.getMonth()].slice(0,3),MM:()=>c(n.getMonth()+1),M:()=>n.getMonth()+1,DD:()=>c(n.getDate()),D:()=>n.getDate(),dddd:()=>l[n.getDay()],ddd:()=>l[n.getDay()].slice(0,3),HH:()=>c(n.getHours()),H:()=>n.getHours(),hh:()=>c((n.getHours()+11)%12+1),h:()=>(n.getHours()+11)%12+1,mm:()=>c(n.getMinutes()),m:()=>n.getMinutes(),ss:()=>c(n.getSeconds()),s:()=>n.getSeconds(),A:()=>n.getHours()<12?"AM":"PM",a:()=>n.getHours()<12?"am":"pm"};return t.replace(/YYYY|YY|MMMM|MMM|MM|M|DD|D|dddd|ddd|HH|H|hh|h|mm|m|ss|s|A|a/g,s=>String(i[s]()))}export const blockEditorView={templateUrl:"/admin/js/templates/block-editor.html",async onMount(e){I.register("edit-2",{viewBox:"0 0 24 24",path:"M17 3a2.85 2.85 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5z",stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),requestAnimationFrame(()=>{document.querySelectorAll('[data-icon="edit-2"]').forEach(l=>{if(l.tagName.toLowerCase()==="svg")return;const i=I.render("edit-2",{size:parseInt(l.dataset.iconSize,10)||24});i&&l.replaceWith(i)})}),u=null,h=!1,v={};const t=e.find("#block-project").get(0);if(t)try{(await f.projects.list()).forEach(i=>{const s=document.createElement("option");s.value=i.slug,s.textContent=i.name||i.slug,t.appendChild(s)})}catch{}const n=window.location.hash.match(/\/blocks\/edit\/([^/?#]+)/);n&&(u=decodeURIComponent(n[1]));const c=e.find("#block-name").get(0),a=e.find("#block-content").get(0);if(u){e.find("#block-editor-title").text("Edit Block"),c&&(c.value=u,c.disabled=!0);try{const l=await f.blocks.get(u);a&&(a.value=l.content||""),e.find("#block-bundled").prop("checked",!!l.bundled),e.find("#block-css").val(l.css||""),v=l.meta||{},e.find("#block-project").val(l.meta?.project||"");try{const i=await f.blocks.list(),s=e.find("#block-quick-switch").get(0);s&&Array.isArray(i)&&i.length&&(i.slice().sort((o,r)=>o.name.localeCompare(r.name)).forEach(o=>{const r=document.createElement("option");r.value=o.name,r.textContent=o.name,o.name===u&&(r.selected=!0),s.appendChild(r)}),s.style.display="",s.addEventListener("change",function(){const o=this.value;o&&o!==u&&!h&&R.navigate(`/blocks/edit/${encodeURIComponent(o)}`)}))}catch{}}catch(l){E.toast(l.message||"Block not found.",{type:"error"}),R.navigate("/blocks");return}}a&&(L(a,e),$(a,e)),e.find("#save-block-btn").off("click").on("click",async()=>{await H(e)}),Domma.icons.scan()}};function L(e,t){const n=t.find("#block-line-numbers").get(0),c=t.find("#block-cursor-pos").get(0);function a(){const s=e.value.split(`
2
+ `).length;n.textContent=Array.from({length:s},(o,r)=>r+1).join(`
3
+ `),n.scrollTop=e.scrollTop}a(),e.addEventListener("input",a),e.addEventListener("scroll",()=>{n.scrollTop=e.scrollTop});function l(){if(!c)return;const s=e.value.slice(0,e.selectionStart).split(`
4
+ `);c.textContent=`Ln ${s.length}, Col ${s[s.length-1].length+1}`}e.addEventListener("keyup",l),e.addEventListener("click",l),e.addEventListener("keydown",s=>{if(s.key==="Tab"){s.preventDefault();const o=e.selectionStart,r=e.selectionEnd;if(!s.shiftKey)e.value=e.value.slice(0,o)+" "+e.value.slice(r),e.selectionStart=e.selectionEnd=o+2;else{const d=e.value.lastIndexOf(`
5
+ `,o-1)+1,m=e.value.slice(d,d+2),p=m===" "?2:m[0]===" "?1:0;p>0&&(e.value=e.value.slice(0,d)+e.value.slice(d+p),e.selectionStart=e.selectionEnd=Math.max(d,o-p))}e.dispatchEvent(new Event("input"))}if(s.key==="Enter"){s.preventDefault();const o=e.selectionStart,r=e.value.lastIndexOf(`
6
+ `,o-1)+1,d=e.value.slice(r,o).match(/^(\s*)/)[1];e.value=e.value.slice(0,o)+`
7
+ `+d+e.value.slice(e.selectionEnd),e.selectionStart=e.selectionEnd=o+1+d.length,e.dispatchEvent(new Event("input"))}});const i=t.find("#block-editor-toolbar").get(0);i&&i.addEventListener("click",s=>{const o=s.target.closest("[data-action]");o&&(D(o.dataset.action,e),e.focus())})}function D(e,t){switch(e){case"undo":document.execCommand("undo");break;case"redo":document.execCommand("redo");break;case"cut":t.selectionStart!==t.selectionEnd&&(navigator.clipboard.writeText(t.value.slice(t.selectionStart,t.selectionEnd)).catch(()=>{}),document.execCommand("cut"));break;case"copy":t.selectionStart!==t.selectionEnd&&navigator.clipboard.writeText(t.value.slice(t.selectionStart,t.selectionEnd)).catch(()=>{});break;case"paste":navigator.clipboard.readText().then(n=>{const c=t.selectionStart;t.value=t.value.slice(0,c)+n+t.value.slice(t.selectionEnd),t.selectionStart=t.selectionEnd=c+n.length,t.dispatchEvent(new Event("input"))}).catch(()=>E.toast("Use Ctrl+V to paste.",{type:"info"}));break;case"select-all":t.select();break;case"format":T(t);break}}function T(e){const t=e.value.match(/(<[^>]+>|[^<]+)/g)||[],n=[];let c=0;const a=" ";for(const s of t){const o=s.trim();if(o)if(o.startsWith("</"))c=Math.max(0,c-1),n.push(a.repeat(c)+o);else if(o.startsWith("<")&&!o.startsWith("<!")&&!o.endsWith("/>")){n.push(a.repeat(c)+o);const r=(o.match(/^<(\w+)/)||[])[1]||"";j.has(r.toLowerCase())||c++}else n.push(a.repeat(c)+o)}const l=n.join(`
8
+ `),i=e.selectionStart;e.value=l,e.selectionStart=e.selectionEnd=Math.min(i,l.length),e.dispatchEvent(new Event("input"))}const j=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]);function $(e,t){const n=t.find("#block-editor-body").get(0),c=t.find("#block-sample-data").get(0),a=t.find("#block-preview-output");t.find("[data-mode]").each(function(){this.addEventListener("click",()=>{t.find("[data-mode]").each(function(){this.classList.remove("active")}),this.classList.add("active"),n.className=`editor-body editor-mode-${this.dataset.mode}`,this.dataset.mode!=="write"&&l()})}),e.addEventListener("input",()=>{M(c,e),l(),g()});{const i=t.find("#block-css").get(0);i&&i.addEventListener("input",()=>{l(),g()});const s=t.find("#block-name").get(0);s&&s.addEventListener("input",()=>{l(),g()});const o=t.find("#block-bundled").get(0);o&&o.addEventListener("change",g)}e.value.trim()&&(M(c,e),l());function l(){if(n.classList.contains("editor-mode-write"))return;const i=e.value.replace(/\{\{([\w_]+)(?::([^{}]+))?\}\}/g,(p,k,y)=>{const w=c.querySelector(`[data-placeholder="${CSS.escape(k)}"]`)?.value??`[${k}]`;if(y){const S=C(w,y);if(S!==null)return b(S)}return b(w)}),s=(t.find("#block-name").val()||"preview").trim()||"preview",o=b(s),r=t.find("#block-css").val()||"",d=t.find("#block-css-scope-hint").get(0);d&&(d.textContent=`[data-block="${s}"]`);const m=r.trim()?`<style>[data-block="${o}"] { ${String(r).replace(/<\/style/gi,"<\\/style")} }</style>`:"";a.html(m+`<div data-block="${o}">${i}</div>`,{safe:!1}),Domma.icons.scan(a.get(0))}c._renderPreview=l}function M(e,t){const n=q(t.value),c=new Set([...e.querySelectorAll("[data-placeholder]")].map(a=>a.dataset.placeholder));if(n.size>0&&e.querySelector("p.text-muted")?.remove(),n.forEach(a=>{c.has(a)||A(e,a)}),e.querySelectorAll("[data-placeholder]").forEach(a=>{n.has(a.dataset.placeholder)||a.closest(".block-sample-row")?.remove()}),n.size===0&&!e.querySelector("p.text-muted")){const a=document.createElement("p");a.className="text-muted",a.style.cssText="font-size:.8rem;margin:0;",a.textContent="No {{placeholders}} detected in template.",e.appendChild(a)}}function A(e,t){const n=document.createElement("div");n.className="block-sample-row",n.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.35rem;";const c=document.createElement("label");c.style.cssText="font-size:.75rem;color:var(--dm-text-muted,#888);white-space:nowrap;min-width:90px;text-align:right;flex-shrink:0;",c.textContent=`{{${t}}}`;const a=document.createElement("input");a.type="text",a.className="form-input form-input--sm",a.style.flex="1",a.dataset.placeholder=t,a.placeholder=`Sample ${t}`,a.value=Y(t),a.addEventListener("input",()=>{e._renderPreview&&e._renderPreview()}),n.appendChild(c),n.appendChild(a),e.appendChild(n)}function Y(e){const t=e.toLowerCase();return t==="_id"?"a1b2c3d4-e5f6-7890-abcd-ef1234567890":t==="_createdat"?new Date().toISOString():t==="_updatedat"?new Date().toISOString():t.includes("email")?"user@example.com":t.includes("phone")?"+44 7700 900000":t.includes("name")?"Jane Smith":t.includes("title")?"Sample Title":t.includes("message")||t.includes("content")||t.includes("description")?"This is a sample value for preview purposes.":t.includes("rating")?"excellent":t.includes("status")?"active":t.includes("priority")?"high":t.includes("date")?new Date().toLocaleDateString():t.includes("tag")?"tag1, tag2":t.includes("subject")?"Sample Subject":t.includes("category")?"general":`Sample ${e}`}function q(e){const t=new Set;for(const[,n]of e.matchAll(/\{\{([\w_]+)(?::[^{}]+)?\}\}/g))t.add(n);return t}function b(e){return String(e??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}async function H(e){const t=e.find("#block-name").get(0),n=e.find("#block-content").get(0),c=(t?.value||"").trim(),a=n?.value??"";if(!c){E.toast("Block name is required.",{type:"warning"});return}if(!/^[a-z0-9][a-z0-9-]*$/.test(c)){E.toast("Name must start with a letter or digit and contain only lowercase letters, digits, and hyphens.",{type:"warning"});return}const l=!!e.find("#block-bundled").is(":checked"),i=e.find("#save-block-btn").get(0);i&&(i.disabled=!0);try{await f.blocks.put(c,{content:a,bundled:l,css:e.find("#block-css").val()||"",meta:{...v||{},project:e.find("#block-project").val()||""||null}}),x(),E.toast(u?"Block updated.":"Block created.",{type:"success"}),u||(u=c,R.navigate(`/blocks/edit/${encodeURIComponent(c)}`))}catch(s){E.toast(s.message||"Failed to save block.",{type:"error"})}finally{i&&(i.disabled=!1)}}
@@ -1,4 +1,4 @@
1
- import{api as s}from"../api.js";function l(t){return String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function p(t){for(;t.firstChild;)t.removeChild(t.firstChild)}export const blocksView={templateUrl:"/admin/js/templates/blocks.html",async onMount(t){m(),await i(t),k(t),Domma.icons.scan(t.get(0))}};let d=!1;function m(){d||(d=!0,I.register("download",{viewBox:"0 0 24 24",paths:["M12 3v12","M7 10l5 5 5-5","M5 19h14"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("upload",{viewBox:"0 0 24 24",paths:["M12 21V9","M7 14l5-5 5 5","M5 5h14"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("plus",{viewBox:"0 0 24 24",paths:["M12 5v14","M5 12h14"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}))}async function i(t){const o=t.find("#blocks-table-container").get(0);if(!o)return;let a=[];try{a=await s.blocks.list()}catch(e){p(o);const r=document.createElement("p");r.className="text-muted",r.textContent=`Failed to load blocks: ${e.message}`,o.appendChild(r);return}T.create(o,{data:a,emptyMessage:'No blocks yet. Click "New Block" to create your first template.',columns:[{key:"name",title:"Name",render:e=>`<a href="#/blocks/edit/${l(e)}">${l(e)}.html</a>`},{key:"size",title:"Size",render:e=>`${e} B`},{key:"updatedAt",title:"Updated",render:e=>e?new Date(e).toLocaleString():"\u2014"},{key:"name",title:"Actions",render:e=>{const r=l(e);return`<div style="display:flex;gap:.4rem;justify-content:flex-end;">
1
+ import{api as s}from"../api.js";import{filterByProject as p,getProjectFromHash as m}from"../lib/project-context.js";function l(t){return String(t??"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function u(t){for(;t.firstChild;)t.removeChild(t.firstChild)}export const blocksView={templateUrl:"/admin/js/templates/blocks.html",async onMount(t){k(),await i(t),b(t),Domma.icons.scan(t.get(0))}};let d=!1;function k(){d||(d=!0,I.register("download",{viewBox:"0 0 24 24",paths:["M12 3v12","M7 10l5 5 5-5","M5 19h14"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("upload",{viewBox:"0 0 24 24",paths:["M12 21V9","M7 14l5-5 5 5","M5 5h14"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}),I.register("plus",{viewBox:"0 0 24 24",paths:["M12 5v14","M5 12h14"],stroke:"currentColor",fill:"none",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"}))}async function i(t){const o=t.find("#blocks-table-container").get(0);if(!o)return;let a=[];try{a=await s.blocks.list()}catch(e){u(o);const r=document.createElement("p");r.className="text-muted",r.textContent=`Failed to load blocks: ${e.message}`,o.appendChild(r);return}const n=m();n&&(a=p(a,n)),T.create(o,{data:a,emptyMessage:'No blocks yet. Click "New Block" to create your first template.',columns:[{key:"name",title:"Name",render:e=>`<a href="#/blocks/edit/${l(e)}">${l(e)}.html</a>`},{key:"size",title:"Size",render:e=>`${e} B`},{key:"updatedAt",title:"Updated",render:e=>e?new Date(e).toLocaleString():"\u2014"},{key:"name",title:"Actions",render:e=>{const r=l(e);return`<div style="display:flex;gap:.4rem;justify-content:flex-end;">
2
2
  <a href="#/blocks/edit/${r}" class="btn btn-sm btn-primary">
3
3
  <span data-icon="edit"></span> Edit
4
4
  </a>
@@ -8,4 +8,4 @@ import{api as s}from"../api.js";function l(t){return String(t??"").replace(/&/g,
8
8
  <button class="btn btn-sm btn-danger js-delete-block" data-name="${r}">
9
9
  <span data-icon="trash"></span> Delete
10
10
  </button>
11
- </div>`}}]}),o.querySelectorAll(".js-delete-block").forEach(e=>{e.addEventListener("click",async()=>{await u(e.dataset.name,t)})}),o.querySelectorAll(".js-export-block").forEach(e=>{e.addEventListener("click",async()=>{try{await s.blocks.exportBundle(e.dataset.name)}catch(r){E.toast(r.message||"Export failed.",{type:"error"})}})}),Domma.icons.scan(o)}async function u(t,o){if(await E.confirm(`Delete block "${t}"? This cannot be undone.`))try{await s.blocks.delete(t),E.toast("Block deleted.",{type:"success"}),await i(o)}catch(e){E.toast(e.message||"Failed to delete block.",{type:"error"})}}function k(t){const o=t.find("#import-block-btn").get(0),a=t.find("#import-block-file").get(0);!o||!a||(o.addEventListener("click",()=>a.click()),a.addEventListener("change",async()=>{const e=a.files?.[0];if(!e)return;a.value="";let r;try{const n=await e.text();r=JSON.parse(n)}catch{E.toast("Not a valid .dmblock.json file (could not parse JSON).",{type:"error"});return}if(!r||typeof r!="object"||!r.name||typeof r.html!="string"){E.toast("Bundle is missing a name or html field.",{type:"error"});return}try{const n=await s.blocks.importBundle(r);E.toast(`Imported "${n.name}".`,{type:"success"}),await i(t)}catch(n){if(n.code==="CONFLICT"){if(!await E.confirm(`A block named "${n.name}" already exists. Overwrite it with the imported version?`)){E.toast("Import cancelled.",{type:"info"});return}try{const c=await s.blocks.importBundle(r,{overwrite:!0});E.toast(`Overwrote "${c.name}".`,{type:"success"}),await i(t)}catch(c){E.toast(c.message||"Import failed.",{type:"error"})}return}E.toast(n.message||"Import failed.",{type:"error"})}}))}
11
+ </div>`}}]}),o.querySelectorAll(".js-delete-block").forEach(e=>{e.addEventListener("click",async()=>{await f(e.dataset.name,t)})}),o.querySelectorAll(".js-export-block").forEach(e=>{e.addEventListener("click",async()=>{try{await s.blocks.exportBundle(e.dataset.name)}catch(r){E.toast(r.message||"Export failed.",{type:"error"})}})}),Domma.icons.scan(o)}async function f(t,o){if(await E.confirm(`Delete block "${t}"? This cannot be undone.`))try{await s.blocks.delete(t),E.toast("Block deleted.",{type:"success"}),await i(o)}catch(n){E.toast(n.message||"Failed to delete block.",{type:"error"})}}function b(t){const o=t.find("#import-block-btn").get(0),a=t.find("#import-block-file").get(0);!o||!a||(o.addEventListener("click",()=>a.click()),a.addEventListener("change",async()=>{const n=a.files?.[0];if(!n)return;a.value="";let e;try{const r=await n.text();e=JSON.parse(r)}catch{E.toast("Not a valid .dmblock.json file (could not parse JSON).",{type:"error"});return}if(!e||typeof e!="object"||!e.name||typeof e.html!="string"){E.toast("Bundle is missing a name or html field.",{type:"error"});return}try{const r=await s.blocks.importBundle(e);E.toast(`Imported "${r.name}".`,{type:"success"}),await i(t)}catch(r){if(r.code==="CONFLICT"){if(!await E.confirm(`A block named "${r.name}" already exists. Overwrite it with the imported version?`)){E.toast("Import cancelled.",{type:"info"});return}try{const c=await s.blocks.importBundle(e,{overwrite:!0});E.toast(`Overwrote "${c.name}".`,{type:"success"}),await i(t)}catch(c){E.toast(c.message||"Import failed.",{type:"error"})}return}E.toast(r.message||"Import failed.",{type:"error"})}}))}
@@ -1,5 +1,5 @@
1
- import{api as w}from"../api.js";const Z=[{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:"date",label:"Date"},{value:"time",label:"Time"},{value:"url",label:"URL"},{value:"hidden",label:"Hidden field"}],Q=new Set(["select","radio","checkbox-group"]),le=["public","subscriber","editor","manager","admin"],ee=["create","read","update","delete"];let f=[],b=null,L=!0,z=null,N="file";function te(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function ne(e){return Z.find(t=>t.value===e)?.label||e}function ae(e){const t={...f[e]},l=document.getElementById(`fb-label-${e}`),d=document.getElementById(`fb-name-${e}`),s=document.getElementById(`fb-type-${e}`),u=document.getElementById(`fb-required-${e}`),n=document.getElementById(`fb-placeholder-${e}`),o=document.getElementById(`fb-helper-${e}`);if(l&&(t.label=l.value.trim()||t.label),d&&(t.name=d.value.trim()||t.name),s&&(t.type=s.value||t.type),u&&(t.required=u.checked),n&&(t.placeholder=n.value.trim()),o&&(t.helper=o.value.trim()),Q.has(t.type)){const i=document.getElementById(`fb-options-${e}`);i&&(t.options=i.value.split(`
2
- `).filter(p=>p.trim()).map(p=>{const[g,...h]=p.split(":");return{value:g.trim(),label:h.join(":").trim()||g.trim()}}))}const c=document.getElementById(`fb-span-${e}`);if(document.getElementById(`fb-fullwidth-${e}`)?.checked)t.fullWidth=!0,delete t.span;else{delete t.fullWidth;const i=parseInt(c?.value,10);i>1?t.span=i:delete t.span}return t}function X(){return f.map((e,t)=>ae(t))}function oe(e,t){const l=document.createElement("div");l.className="fb-field-card",l.dataset.index=t,l.style.cssText="border:1px solid var(--border-color,#333);border-radius:8px;margin-bottom:.75rem;overflow:hidden;";const d=document.createElement("div");d.className="fb-field-header",d.style.cssText="display:flex;align-items:center;gap:.5rem;padding:.6rem .75rem;background:var(--card-header-bg,rgba(255,255,255,.03));cursor:pointer;user-select:none;";const s=document.createElement("span");s.textContent="\u283F",s.style.cssText="cursor:grab;opacity:.4;font-size:1.1rem;flex-shrink:0;",l.draggable=!0,s.addEventListener("mousedown",()=>{l.draggable=!0}),l.addEventListener("dragstart",a=>{z=t,a.dataTransfer.effectAllowed="move",l.style.opacity="0.4"}),l.addEventListener("dragend",()=>{l.style.opacity="",document.querySelectorAll(".fb-field-card").forEach(a=>a.classList.remove("fb-drag-over"))}),l.addEventListener("dragover",a=>{a.preventDefault(),a.dataTransfer.dropEffect="move",document.querySelectorAll(".fb-field-card").forEach(v=>v.classList.remove("fb-drag-over")),l.classList.add("fb-drag-over")}),l.addEventListener("dragleave",()=>{l.classList.remove("fb-drag-over")}),l.addEventListener("drop",a=>{if(a.preventDefault(),l.classList.remove("fb-drag-over"),z===null||z===t)return;f=X();const[v]=f.splice(z,1);f.splice(t,0,v),z=null,j(document.getElementById("fields-list"))});const u=document.createElement("span");u.className="fb-field-summary",u.style.cssText="flex:1;font-weight:500;font-size:.9rem;",u.textContent=e.label||"(Untitled field)";const n=document.createElement("span");n.style.cssText="font-size:.75rem;opacity:.5;",n.textContent=ne(e.type);const o=document.createElement("span");o.className="fb-field-chevron",o.textContent="\u25BE",o.style.cssText="opacity:.5;transition:transform .2s;";const c=document.createElement("button");c.type="button",c.textContent="\xD7",c.className="btn btn-sm",c.style.cssText="padding:.15rem .45rem;line-height:1;font-size:1rem;opacity:.6;",c.title="Remove field",c.addEventListener("click",a=>{a.stopPropagation(),f.splice(t,1),j(document.getElementById("fields-list"))}),d.appendChild(s),d.appendChild(u),d.appendChild(n),d.appendChild(o),d.appendChild(c);const i=document.createElement("div");i.className="fb-field-body",i.style.cssText="padding:.75rem;display:none;";const p=document.createElement("div");p.style.cssText="display:grid;grid-template-columns:1fr 1fr 1fr;gap:.6rem;margin-bottom:.6rem;";const g=document.createElement("div"),h=document.createElement("label");h.className="form-label",h.textContent="Label";const r=document.createElement("input");r.id=`fb-label-${t}`,r.type="text",r.className="form-input",r.value=e.label||"",r.addEventListener("input",()=>{u.textContent=r.value.trim()||"(Untitled field)";const a=document.getElementById(`fb-name-${t}`);a&&!a.dataset.manual&&(a.value=te(r.value).replace(/-/g,"_"))}),g.appendChild(h),g.appendChild(r);const x=document.createElement("div"),T=document.createElement("label");T.className="form-label",T.textContent="Name (key)";const m=document.createElement("input");m.id=`fb-name-${t}`,m.type="text",m.className="form-input",m.value=e.name||"",m.addEventListener("input",()=>{m.dataset.manual="1"}),x.appendChild(T),x.appendChild(m);const B=document.createElement("div"),k=document.createElement("label");k.className="form-label",k.textContent="Type";const C=document.createElement("select");C.id=`fb-type-${t}`,C.className="form-input",Z.forEach(a=>{const v=document.createElement("option");v.value=a.value,v.textContent=a.label,a.value===e.type&&(v.selected=!0),C.appendChild(v)}),C.addEventListener("change",()=>{n.textContent=ne(C.value);const a=i.querySelector(".fb-options-wrap");a&&(a.style.display=Q.has(C.value)?"":"none")}),B.appendChild(k),B.appendChild(C),p.appendChild(g),p.appendChild(x),p.appendChild(B);const I=document.createElement("div");I.style.cssText="display:grid;grid-template-columns:1fr 1fr auto;gap:.6rem;align-items:end;margin-bottom:.6rem;";const P=document.createElement("div"),H=document.createElement("label");H.className="form-label",H.textContent="Placeholder";const S=document.createElement("input");S.id=`fb-placeholder-${t}`,S.type="text",S.className="form-input",S.value=e.placeholder||"",P.appendChild(H),P.appendChild(S);const O=document.createElement("div"),V=document.createElement("label");V.className="form-label",V.textContent="Helper text";const q=document.createElement("input");q.id=`fb-helper-${t}`,q.type="text",q.className="form-input",q.value=e.helper||"",O.appendChild(V),O.appendChild(q);const Y=document.createElement("div");Y.style.cssText="padding-bottom:.35rem;";const F=document.createElement("label");F.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;white-space:nowrap;";const U=document.createElement("input");U.id=`fb-required-${t}`,U.type="checkbox",U.checked=!!e.required,F.appendChild(U),F.appendChild(document.createTextNode("Required")),Y.appendChild(F),I.appendChild(P),I.appendChild(O),I.appendChild(Y);const D=document.createElement("div");D.className="fb-options-wrap",D.style.display=Q.has(e.type)?"":"none";const _=document.createElement("label");_.className="form-label",_.textContent="Options (one per line: value: Label)";const A=document.createElement("textarea");A.id=`fb-options-${t}`,A.className="form-input",A.rows=4,A.value=(e.options||[]).map(a=>typeof a=="string"?`${a}: ${a}`:`${a.value??""}: ${a.label??a.value??""}`).join(`
3
- `),D.appendChild(_),D.appendChild(A);const y=document.createElement("div");y.className="fb-grid-row",y.style.gridTemplateColumns="1fr auto",y.style.gap=".6rem",y.style.alignItems="end",y.style.marginBottom=".6rem",y.style.display=document.getElementById("collection-layout")?.value==="grid"?"grid":"none";const G=document.createElement("div"),J=document.createElement("label");J.className="form-label",J.textContent="Column Span";const $=document.createElement("input");$.id=`fb-span-${t}`,$.type="number",$.className="form-input",$.min="1",$.max="6",$.value=e.span>1?String(e.span):"1",G.appendChild(J),G.appendChild($);const K=document.createElement("div");K.style.cssText="padding-bottom:.35rem;";const M=document.createElement("label");M.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;white-space:nowrap;";const W=document.createElement("input");return W.id=`fb-fullwidth-${t}`,W.type="checkbox",W.checked=!!e.fullWidth,M.appendChild(W),M.appendChild(document.createTextNode("Full Width")),K.appendChild(M),y.appendChild(G),y.appendChild(K),i.appendChild(p),i.appendChild(I),i.appendChild(D),i.appendChild(y),d.addEventListener("click",()=>{const a=i.style.display!=="none";i.style.display=a?"none":"",o.style.transform=a?"":"rotate(180deg)"}),l.appendChild(d),l.appendChild(i),l}function j(e){if(e){if(e.textContent="",f.length===0){const t=document.createElement("p");t.className="text-muted",t.id="fields-empty-msg",t.style.cssText="text-align:center;padding:2rem 0;",t.textContent='No fields yet. Click "Add Field" to get started.',e.appendChild(t);return}f.forEach((t,l)=>{e.appendChild(oe(t,l))})}}function de(e,t){t.textContent="",ee.forEach(l=>{const d=e?.[l]||{enabled:!1,access:"admin"},s=document.createElement("div");s.style.cssText="display:grid;grid-template-columns:140px 1fr 160px;gap:.75rem;align-items:center;padding:.6rem 0;border-bottom:1px solid var(--border-color,#333);";const u=document.createElement("strong");u.textContent=l.charAt(0).toUpperCase()+l.slice(1),u.style.cssText="font-size:.9rem;";const n=document.createElement("label");n.style.cssText="display:flex;align-items:center;gap:.45rem;cursor:pointer;font-size:.875rem;";const o=document.createElement("input");o.type="checkbox",o.id=`api-${l}-enabled`,o.checked=!!d.enabled,n.appendChild(o),n.appendChild(document.createTextNode("Enable public access"));const c=document.createElement("select");c.id=`api-${l}-access`,c.className="form-input",le.forEach(i=>{const p=document.createElement("option");p.value=i,p.textContent=i.charAt(0).toUpperCase()+i.slice(1),i===d.access&&(p.selected=!0),c.appendChild(p)}),s.appendChild(u),s.appendChild(n),s.appendChild(c),t.appendChild(s)})}function ie(e,t){E.dropdown("#storage-adapter-trigger",{items:[{label:"File (default)",value:"file"},{label:"MongoDB",value:"mongodb"}],onSelect:({item:d})=>{e.find("#storage-adapter").val(d.value),e.find("#storage-adapter-label").text(d.label);const s=d.value==="mongodb";e.find("#storage-connection-group").toggle(s),e.find("#storage-migration-warning").toggle(s&&!L)}});const l=t.map(d=>({label:d,value:d}));E.dropdown("#storage-connection-trigger",{items:l.length?l:[{label:"default",value:"default"}],onSelect:({item:d})=>{e.find("#storage-connection").val(d.value),e.find("#storage-connection-label").text(d.label)}})}function ce(){return(document.getElementById("storage-adapter")?.value||"file")==="mongodb"?{adapter:"mongodb",connection:document.getElementById("storage-connection")?.value||"default"}:{adapter:"file"}}function se(){const e={};return ee.forEach(t=>{const l=document.getElementById(`api-${t}-enabled`)?.checked??!1,d=document.getElementById(`api-${t}-access`)?.value||"admin";e[t]={enabled:l,access:d}}),e}export const collectionEditorView={templateUrl:"/admin/js/templates/collection-editor.html",async onMount(e){f=[],b=null,L=!0;const t=window.location.hash.match(/\/collections\/edit\/([^/?#]+)/);t&&(b=t[1],L=!1),E.tabs(e.find("#collection-tabs").get(0)),e.find("#collection-layout").get(0)?.addEventListener("change",function(){const n=this.value==="grid";e.find("#collection-columns-group").get(0).style.display=n?"":"none",document.querySelectorAll(".fb-grid-row").forEach(o=>{o.style.display=n?"grid":"none"})});const l=e.find("#fields-list").get(0),d=e.find("#api-access-rows").get(0),s=await w.collections.proStatus();s?.pro&&b!=="roles"&&(e.find("#storage-tab-btn").show(),ie(e,s.connections));let u={create:{enabled:!1,access:"admin"},read:{enabled:!0,access:"public"},update:{enabled:!1,access:"admin"},delete:{enabled:!1,access:"admin"}};if(L){const n=e.find("#field-title").get(0),o=e.find("#field-slug").get(0);n&&o&&(n.addEventListener("input",()=>{o.dataset.manual||(o.value=te(n.value))}),o.addEventListener("input",()=>{o.dataset.manual="1"}))}else try{const n=await w.collections.get(b);if(!n){E.toast("Collection not found.",{type:"error"}),R.navigate("/collections");return}const o=e.find("#editor-title-text").get(0);o&&(o.textContent=n.title),e.find("#field-title").val(n.title||""),e.find("#field-slug").val(n.slug||""),e.find("#field-slug").prop("readonly",!0),e.find("#slug-hint").get(0).textContent="Slug cannot be changed after creation.",e.find("#field-description").val(n.description||""),e.find("#collection-layout").val(n.layout||"stacked"),e.find("#collection-columns").val(n.columns||2),e.find("#collection-bundled").prop("checked",!!n.bundled),e.find("#collection-columns-group").get(0).style.display=n.layout==="grid"?"":"none",f=n.fields||[],u=n.api||u,N=n.storage?.adapter||"file",n.storage&&(e.find("#storage-adapter").val(n.storage.adapter||"file"),e.find("#storage-adapter-label").text(n.storage.adapter==="mongodb"?"MongoDB":"File (default)"),n.storage.adapter==="mongodb"&&(e.find("#storage-connection-group").show(),e.find("#storage-connection").val(n.storage.connection||"default"),e.find("#storage-connection-label").text(n.storage.connection||"default"))),b==="roles"&&e.find("#storage-tab-btn").hide()}catch{E.toast("Failed to load collection.",{type:"error"}),R.navigate("/collections");return}j(l),de(u,d),e.find("#add-field-btn").off("click").on("click",()=>{f=X(),f.push({id:`field-${Date.now()}`,name:"",label:"",type:"string",required:!1,placeholder:"",helper:"",options:[],validation:[],logic:null}),j(l);const n=l.querySelectorAll(".fb-field-card");if(n.length){const o=n[n.length-1],c=o.querySelector(".fb-field-body"),i=o.querySelector(".fb-field-chevron");c&&(c.style.display=""),i&&(i.style.transform="rotate(180deg)"),o.querySelector(`#fb-label-${f.length-1}`)?.focus()}}),e.find("#save-collection-btn").off("click").on("click",async()=>{const n=e.find("#field-title").val().trim(),o=e.find("#field-slug").val().trim(),c=e.find("#field-description").val().trim();if(!n){E.toast("Title is required.",{type:"warning"});return}const i=X(),p=se(),g=e.find("#collection-layout").val()||"stacked",h=parseInt(e.find("#collection-columns").val(),10)||2,r=ce(),x=e.find("#collection-bundled").is(":checked"),T=e.find("#save-collection-btn");T.prop("disabled",!0);try{if(L){const m=await w.collections.create({title:n,slug:o,description:c,layout:g,columns:h,fields:i,api:p,storage:r,...x?{bundled:!0}:{}});b=m.slug,N=r.adapter||"file",L=!1,E.toast("Collection created.",{type:"success"}),R.navigate(`/collections/edit/${m.slug}`)}else if((r.adapter||"file")!==N){let m=0;try{m=(await w.collections.listEntries(b,{limit:1}))?.total??0}catch{}const B=N==="file"?`file \u2192 ${r.adapter}`:`${N} \u2192 ${r.adapter||"file"}`;if(m>0&&await E.confirm(`You changed the storage adapter (${B}).
1
+ import{api as w}from"../api.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:"date",label:"Date"},{value:"time",label:"Time"},{value:"url",label:"URL"},{value:"hidden",label:"Hidden field"}],Q=new Set(["select","radio","checkbox-group"]),ae=["public","subscriber","editor","manager","admin"],te=["create","read","update","delete"];let f=[],h=null,S=!0,z=null,L="file",X={};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){const t={...f[e]},l=document.getElementById(`fb-label-${e}`),i=document.getElementById(`fb-name-${e}`),s=document.getElementById(`fb-type-${e}`),m=document.getElementById(`fb-required-${e}`),p=document.getElementById(`fb-placeholder-${e}`),n=document.getElementById(`fb-helper-${e}`);if(l&&(t.label=l.value.trim()||t.label),i&&(t.name=i.value.trim()||t.name),s&&(t.type=s.value||t.type),m&&(t.required=m.checked),p&&(t.placeholder=p.value.trim()),n&&(t.helper=n.value.trim()),Q.has(t.type)){const o=document.getElementById(`fb-options-${e}`);o&&(t.options=o.value.split(`
2
+ `).filter(c=>c.trim()).map(c=>{const[b,...y]=c.split(":");return{value:b.trim(),label:y.join(":").trim()||b.trim()}}))}const a=document.getElementById(`fb-span-${e}`);if(document.getElementById(`fb-fullwidth-${e}`)?.checked)t.fullWidth=!0,delete t.span;else{delete t.fullWidth;const o=parseInt(a?.value,10);o>1?t.span=o:delete t.span}return t}function Z(){return f.map((e,t)=>oe(t))}function de(e,t){const l=document.createElement("div");l.className="fb-field-card",l.dataset.index=t,l.style.cssText="border:1px solid var(--border-color,#333);border-radius:8px;margin-bottom:.75rem;overflow:hidden;";const i=document.createElement("div");i.className="fb-field-header",i.style.cssText="display:flex;align-items:center;gap:.5rem;padding:.6rem .75rem;background:var(--card-header-bg,rgba(255,255,255,.03));cursor:pointer;user-select:none;";const s=document.createElement("span");s.textContent="\u283F",s.style.cssText="cursor:grab;opacity:.4;font-size:1.1rem;flex-shrink:0;",l.draggable=!0,s.addEventListener("mousedown",()=>{l.draggable=!0}),l.addEventListener("dragstart",d=>{z=t,d.dataTransfer.effectAllowed="move",l.style.opacity="0.4"}),l.addEventListener("dragend",()=>{l.style.opacity="",document.querySelectorAll(".fb-field-card").forEach(d=>d.classList.remove("fb-drag-over"))}),l.addEventListener("dragover",d=>{d.preventDefault(),d.dataTransfer.dropEffect="move",document.querySelectorAll(".fb-field-card").forEach(C=>C.classList.remove("fb-drag-over")),l.classList.add("fb-drag-over")}),l.addEventListener("dragleave",()=>{l.classList.remove("fb-drag-over")}),l.addEventListener("drop",d=>{if(d.preventDefault(),l.classList.remove("fb-drag-over"),z===null||z===t)return;f=Z();const[C]=f.splice(z,1);f.splice(t,0,C),z=null,P(document.getElementById("fields-list"))});const m=document.createElement("span");m.className="fb-field-summary",m.style.cssText="flex:1;font-weight:500;font-size:.9rem;",m.textContent=e.label||"(Untitled field)";const p=document.createElement("span");p.style.cssText="font-size:.75rem;opacity:.5;",p.textContent=le(e.type);const n=document.createElement("span");n.className="fb-field-chevron",n.textContent="\u25BE",n.style.cssText="opacity:.5;transition:transform .2s;";const a=document.createElement("button");a.type="button",a.textContent="\xD7",a.className="btn btn-sm",a.style.cssText="padding:.15rem .45rem;line-height:1;font-size:1rem;opacity:.6;",a.title="Remove field",a.addEventListener("click",d=>{d.stopPropagation(),f.splice(t,1),P(document.getElementById("fields-list"))}),i.appendChild(s),i.appendChild(m),i.appendChild(p),i.appendChild(n),i.appendChild(a);const o=document.createElement("div");o.className="fb-field-body",o.style.cssText="padding:.75rem;display:none;";const c=document.createElement("div");c.style.cssText="display:grid;grid-template-columns:1fr 1fr 1fr;gap:.6rem;margin-bottom:.6rem;";const b=document.createElement("div"),y=document.createElement("label");y.className="form-label",y.textContent="Label";const g=document.createElement("input");g.id=`fb-label-${t}`,g.type="text",g.className="form-input",g.value=e.label||"",g.addEventListener("input",()=>{m.textContent=g.value.trim()||"(Untitled field)";const d=document.getElementById(`fb-name-${t}`);d&&!d.dataset.manual&&(d.value=ne(g.value).replace(/-/g,"_"))}),b.appendChild(y),b.appendChild(g);const u=document.createElement("div"),$=document.createElement("label");$.className="form-label",$.textContent="Name (key)";const v=document.createElement("input");v.id=`fb-name-${t}`,v.type="text",v.className="form-input",v.value=e.name||"",v.addEventListener("input",()=>{v.dataset.manual="1"}),u.appendChild($),u.appendChild(v);const N=document.createElement("div"),B=document.createElement("label");B.className="form-label",B.textContent="Type";const r=document.createElement("select");r.id=`fb-type-${t}`,r.className="form-input",ee.forEach(d=>{const C=document.createElement("option");C.value=d.value,C.textContent=d.label,d.value===e.type&&(C.selected=!0),r.appendChild(C)}),r.addEventListener("change",()=>{p.textContent=le(r.value);const d=o.querySelector(".fb-options-wrap");d&&(d.style.display=Q.has(r.value)?"":"none")}),N.appendChild(B),N.appendChild(r),c.appendChild(b),c.appendChild(u),c.appendChild(N);const T=document.createElement("div");T.style.cssText="display:grid;grid-template-columns:1fr 1fr auto;gap:.6rem;align-items:end;margin-bottom:.6rem;";const I=document.createElement("div"),H=document.createElement("label");H.className="form-label",H.textContent="Placeholder";const j=document.createElement("input");j.id=`fb-placeholder-${t}`,j.type="text",j.className="form-input",j.value=e.placeholder||"",I.appendChild(H),I.appendChild(j);const O=document.createElement("div"),V=document.createElement("label");V.className="form-label",V.textContent="Helper text";const q=document.createElement("input");q.id=`fb-helper-${t}`,q.type="text",q.className="form-input",q.value=e.helper||"",O.appendChild(V),O.appendChild(q);const Y=document.createElement("div");Y.style.cssText="padding-bottom:.35rem;";const F=document.createElement("label");F.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;white-space:nowrap;";const M=document.createElement("input");M.id=`fb-required-${t}`,M.type="checkbox",M.checked=!!e.required,F.appendChild(M),F.appendChild(document.createTextNode("Required")),Y.appendChild(F),T.appendChild(I),T.appendChild(O),T.appendChild(Y);const D=document.createElement("div");D.className="fb-options-wrap",D.style.display=Q.has(e.type)?"":"none";const _=document.createElement("label");_.className="form-label",_.textContent="Options (one per line: value: Label)";const A=document.createElement("textarea");A.id=`fb-options-${t}`,A.className="form-input",A.rows=4,A.value=(e.options||[]).map(d=>typeof d=="string"?`${d}: ${d}`:`${d.value??""}: ${d.label??d.value??""}`).join(`
3
+ `),D.appendChild(_),D.appendChild(A);const x=document.createElement("div");x.className="fb-grid-row",x.style.gridTemplateColumns="1fr auto",x.style.gap=".6rem",x.style.alignItems="end",x.style.marginBottom=".6rem",x.style.display=document.getElementById("collection-layout")?.value==="grid"?"grid":"none";const G=document.createElement("div"),J=document.createElement("label");J.className="form-label",J.textContent="Column Span";const k=document.createElement("input");k.id=`fb-span-${t}`,k.type="number",k.className="form-input",k.min="1",k.max="6",k.value=e.span>1?String(e.span):"1",G.appendChild(J),G.appendChild(k);const K=document.createElement("div");K.style.cssText="padding-bottom:.35rem;";const U=document.createElement("label");U.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;white-space:nowrap;";const W=document.createElement("input");return W.id=`fb-fullwidth-${t}`,W.type="checkbox",W.checked=!!e.fullWidth,U.appendChild(W),U.appendChild(document.createTextNode("Full Width")),K.appendChild(U),x.appendChild(G),x.appendChild(K),o.appendChild(c),o.appendChild(T),o.appendChild(D),o.appendChild(x),i.addEventListener("click",()=>{const d=o.style.display!=="none";o.style.display=d?"none":"",n.style.transform=d?"":"rotate(180deg)"}),l.appendChild(i),l.appendChild(o),l}function P(e){if(e){if(e.textContent="",f.length===0){const t=document.createElement("p");t.className="text-muted",t.id="fields-empty-msg",t.style.cssText="text-align:center;padding:2rem 0;",t.textContent='No fields yet. Click "Add Field" to get started.',e.appendChild(t);return}f.forEach((t,l)=>{e.appendChild(de(t,l))})}}function ie(e,t){t.textContent="",te.forEach(l=>{const i=e?.[l]||{enabled:!1,access:"admin"},s=document.createElement("div");s.style.cssText="display:grid;grid-template-columns:140px 1fr 160px;gap:.75rem;align-items:center;padding:.6rem 0;border-bottom:1px solid var(--border-color,#333);";const m=document.createElement("strong");m.textContent=l.charAt(0).toUpperCase()+l.slice(1),m.style.cssText="font-size:.9rem;";const p=document.createElement("label");p.style.cssText="display:flex;align-items:center;gap:.45rem;cursor:pointer;font-size:.875rem;";const n=document.createElement("input");n.type="checkbox",n.id=`api-${l}-enabled`,n.checked=!!i.enabled,p.appendChild(n),p.appendChild(document.createTextNode("Enable public access"));const a=document.createElement("select");a.id=`api-${l}-access`,a.className="form-input",ae.forEach(o=>{const c=document.createElement("option");c.value=o,c.textContent=o.charAt(0).toUpperCase()+o.slice(1),o===i.access&&(c.selected=!0),a.appendChild(c)}),s.appendChild(m),s.appendChild(p),s.appendChild(a),t.appendChild(s)})}function ce(e,t){E.dropdown("#storage-adapter-trigger",{items:[{label:"File (default)",value:"file"},{label:"MongoDB",value:"mongodb"}],onSelect:({item:i})=>{e.find("#storage-adapter").val(i.value),e.find("#storage-adapter-label").text(i.label);const s=i.value==="mongodb";e.find("#storage-connection-group").toggle(s),e.find("#storage-migration-warning").toggle(s&&!S)}});const l=t.map(i=>({label:i,value:i}));E.dropdown("#storage-connection-trigger",{items:l.length?l:[{label:"default",value:"default"}],onSelect:({item:i})=>{e.find("#storage-connection").val(i.value),e.find("#storage-connection-label").text(i.label)}})}function se(){return(document.getElementById("storage-adapter")?.value||"file")==="mongodb"?{adapter:"mongodb",connection:document.getElementById("storage-connection")?.value||"default"}:{adapter:"file"}}function re(){const e={};return te.forEach(t=>{const l=document.getElementById(`api-${t}-enabled`)?.checked??!1,i=document.getElementById(`api-${t}-access`)?.value||"admin";e[t]={enabled:l,access:i}}),e}export const collectionEditorView={templateUrl:"/admin/js/templates/collection-editor.html",async onMount(e){f=[],h=null,S=!0;const t=window.location.hash.match(/\/collections\/edit\/([^/?#]+)/);t&&(h=t[1],S=!1),E.tabs(e.find("#collection-tabs").get(0)),e.find("#collection-layout").get(0)?.addEventListener("change",function(){const n=this.value==="grid";e.find("#collection-columns-group").get(0).style.display=n?"":"none",document.querySelectorAll(".fb-grid-row").forEach(a=>{a.style.display=n?"grid":"none"})});const l=e.find("#fields-list").get(0),i=e.find("#api-access-rows").get(0);X={};const s=e.find("#collection-project").get(0);if(s)try{(await w.projects.list()).forEach(a=>{const o=document.createElement("option");o.value=a.slug,o.textContent=a.name||a.slug,s.appendChild(o)})}catch{}const m=await w.collections.proStatus();m?.pro&&h!=="roles"&&(e.find("#storage-tab-btn").show(),ce(e,m.connections));let p={create:{enabled:!1,access:"admin"},read:{enabled:!0,access:"public"},update:{enabled:!1,access:"admin"},delete:{enabled:!1,access:"admin"}};if(S){const n=e.find("#field-title").get(0),a=e.find("#field-slug").get(0);n&&a&&(n.addEventListener("input",()=>{a.dataset.manual||(a.value=ne(n.value))}),a.addEventListener("input",()=>{a.dataset.manual="1"}))}else try{const n=await w.collections.get(h);if(!n){E.toast("Collection not found.",{type:"error"}),R.navigate("/collections");return}const a=e.find("#editor-title-text").get(0);a&&(a.textContent=n.title),e.find("#field-title").val(n.title||""),e.find("#field-slug").val(n.slug||""),e.find("#field-slug").prop("readonly",!0),e.find("#slug-hint").get(0).textContent="Slug cannot be changed after creation.",e.find("#field-description").val(n.description||""),e.find("#collection-layout").val(n.layout||"stacked"),e.find("#collection-columns").val(n.columns||2),e.find("#collection-bundled").prop("checked",!!n.bundled),X=n.meta||{},e.find("#collection-project").val(n.meta?.project||""),e.find("#collection-columns-group").get(0).style.display=n.layout==="grid"?"":"none",f=n.fields||[],p=n.api||p,L=n.storage?.adapter||"file",n.storage&&(e.find("#storage-adapter").val(n.storage.adapter||"file"),e.find("#storage-adapter-label").text(n.storage.adapter==="mongodb"?"MongoDB":"File (default)"),n.storage.adapter==="mongodb"&&(e.find("#storage-connection-group").show(),e.find("#storage-connection").val(n.storage.connection||"default"),e.find("#storage-connection-label").text(n.storage.connection||"default"))),h==="roles"&&e.find("#storage-tab-btn").hide()}catch{E.toast("Failed to load collection.",{type:"error"}),R.navigate("/collections");return}P(l),ie(p,i),e.find("#add-field-btn").off("click").on("click",()=>{f=Z(),f.push({id:`field-${Date.now()}`,name:"",label:"",type:"string",required:!1,placeholder:"",helper:"",options:[],validation:[],logic:null}),P(l);const n=l.querySelectorAll(".fb-field-card");if(n.length){const a=n[n.length-1],o=a.querySelector(".fb-field-body"),c=a.querySelector(".fb-field-chevron");o&&(o.style.display=""),c&&(c.style.transform="rotate(180deg)"),a.querySelector(`#fb-label-${f.length-1}`)?.focus()}}),e.find("#save-collection-btn").off("click").on("click",async()=>{const n=e.find("#field-title").val().trim(),a=e.find("#field-slug").val().trim(),o=e.find("#field-description").val().trim();if(!n){E.toast("Title is required.",{type:"warning"});return}const c=Z(),b=re(),y=e.find("#collection-layout").val()||"stacked",g=parseInt(e.find("#collection-columns").val(),10)||2,u=se(),$=e.find("#collection-bundled").is(":checked"),v=e.find("#collection-project").val()||"",N={...X||{},project:v||null},B=e.find("#save-collection-btn");B.prop("disabled",!0);try{if(S){const r=await w.collections.create({title:n,slug:a,description:o,layout:y,columns:g,fields:c,api:b,storage:u,meta:N,...$?{bundled:!0}:{}});h=r.slug,L=u.adapter||"file",S=!1,E.toast("Collection created.",{type:"success"}),R.navigate(`/collections/edit/${r.slug}`)}else if((u.adapter||"file")!==L){let r=0;try{r=(await w.collections.listEntries(h,{limit:1}))?.total??0}catch{}const T=L==="file"?`file \u2192 ${u.adapter}`:`${L} \u2192 ${u.adapter||"file"}`;if(r>0&&await E.confirm(`You changed the storage adapter (${T}).
4
4
 
5
- Migrate ${m} existing ${m===1?"entry":"entries"} to the new storage?`)){const k=await w.collections.migrateStorage(b,r);N=r.adapter||"file",E.toast(`Migrated ${k.migrated} of ${k.total} entries.`,{type:"success"})}else await w.collections.update(b,{title:n,description:c,layout:g,columns:h,fields:i,api:p,storage:r,...x?{bundled:!0}:{bundled:!1}}),N=r.adapter||"file",E.toast("Collection saved.",{type:"success"})}else await w.collections.update(b,{title:n,description:c,layout:g,columns:h,fields:i,api:p,storage:r,...x?{bundled:!0}:{bundled:!1}}),E.toast("Collection saved.",{type:"success"})}catch(m){E.toast(m.message||"Failed to save.",{type:"error"})}finally{T.prop("disabled",!1)}}),Domma.icons.scan()}};
5
+ Migrate ${r} existing ${r===1?"entry":"entries"} to the new storage?`)){const I=await w.collections.migrateStorage(h,u);L=u.adapter||"file",E.toast(`Migrated ${I.migrated} of ${I.total} entries.`,{type:"success"})}else await w.collections.update(h,{title:n,description:o,layout:y,columns:g,fields:c,api:b,storage:u,meta:N,...$?{bundled:!0}:{bundled:!1}}),L=u.adapter||"file",E.toast("Collection saved.",{type:"success"})}else await w.collections.update(h,{title:n,description:o,layout:y,columns:g,fields:c,api:b,storage:u,meta:N,...$?{bundled:!0}:{bundled:!1}}),E.toast("Collection saved.",{type:"success"})}catch(r){E.toast(r.message||"Failed to save.",{type:"error"})}finally{B.prop("disabled",!1)}}),Domma.icons.scan()}};
@@ -1 +1 @@
1
- import{api as b}from"../api.js";function h(e){return String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}let d={};export const collectionsView={templateUrl:"/admin/js/templates/collections.html",async onMount(e){const a=E.loader(e.get(0),{type:"dots"});E.tabs(e.find("#collections-tabs").get(0));const c=e.find("#collections-tabs").get(0)?.querySelector(".tab-list");if(c){const n=e.find("#collections-header-actions").get(0);Array.from(c.querySelectorAll(".tab-item")).forEach((t,l)=>{t.addEventListener("click",()=>{n&&(n.style.display=l===0?"":"none")})})}await Promise.all([B(e),j(e)]),a.destroy(),e.find("#create-collection-btn").off("click").on("click",()=>{const n=E.modal({title:"New Collection",size:"sm"}),t=document.createElement("div");t.style.cssText="padding:.25rem 0 .5rem;";const l=document.createElement("div");F.create({title:{type:"string",label:"Collection Title",placeholder:"e.g. Products, Blog Posts\u2026",required:!0}},{},{showSubmitButton:!1}).renderTo(l),t.appendChild(l);const o=document.createElement("div");o.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:.75rem;";const r=document.createElement("button");r.className="btn btn-ghost",r.textContent="Cancel";const s=document.createElement("button");s.className="btn btn-primary",s.textContent="Create",o.appendChild(r),o.appendChild(s),t.appendChild(o),n.element.appendChild(t),n.open();const i=l.querySelector('input[name="title"]');setTimeout(()=>i?.focus(),50);async function m(){const p=i?.value.trim();if(p)try{const u=await b.collections.create({title:p});n.close(),R.navigate(`/collections/edit/${u.slug}`)}catch(u){E.toast(u.message||"Failed to create collection.",{type:"error"})}}r.addEventListener("click",()=>n.close()),s.addEventListener("click",m),i?.addEventListener("keydown",p=>{p.key==="Enter"&&m()})}),e.find("#connections-raw-toggle").on("change",function(){if(this.checked)d=x(e),v(e),e.find("#connections-form-view").hide(),e.find("#connections-raw-view").show();else{const n=e.find("#connections-raw-json").get(0)?.value||"{}";try{d=JSON.parse(n),C(e),e.find("#connections-form-view").show(),e.find("#connections-raw-view").hide()}catch{E.toast("Invalid JSON \u2014 fix before switching to form view.",{type:"warning"}),this.checked=!0}}}),e.find("#add-connection-btn").on("click",()=>{d=x(e);let n=Object.keys(d).length+1,t=`connection${n}`;for(;d[t];)n++,t=`connection${n}`;d[t]={type:"mongodb",uri:"",database:"",options:{}},C(e),v(e)}),e.find("#save-connections-btn").on("click",async()=>{const n=e.find("#connections-raw-toggle").get(0)?.checked;let t;if(n)try{t=JSON.parse(e.find("#connections-raw-json").get(0)?.value||"{}")}catch{E.toast("Invalid JSON \u2014 fix before saving.",{type:"warning"});return}else t=x(e);try{await b.collections.saveConnections(t),d=t,E.toast("Connections saved.",{type:"success"})}catch(l){E.toast(l.message||"Failed to save connections.",{type:"error"})}}),Domma.icons.scan()}};async function B(e){let a=[];try{a=await b.collections.list()}catch{E.toast("Could not load collections.",{type:"error"})}T.create("#collections-table",{data:a,columns:[{key:"title",title:"Title",render:(c,n)=>{const t=document.createElement("span");t.style.cssText="display:flex;align-items:center;gap:.4rem;";const l=document.createElement("a");if(l.href=`#/collections/${h(n.slug)}/entries`,l.textContent=c,l.style.fontWeight="600",t.appendChild(l),n.storage?.adapter==="mongodb"){const o=document.createElement("span");o.className="badge badge-outline",o.textContent="MongoDB",o.title=`Connection: ${n.storage.connection||"default"}`,o.style.cssText="font-size:0.65rem;padding:1px 6px;color:var(--dm-success,#16a34a);border-color:var(--dm-success,#16a34a);flex-shrink:0;",t.appendChild(o)}if(n.bundled){const o=document.createElement("span");o.className="badge badge-outline",o.textContent="Bundled",o.title="Seeded on fresh installs",o.style.cssText="font-size:0.65rem;padding:1px 6px;color:var(--dm-info,#2563eb);border-color:var(--dm-info,#2563eb);flex-shrink:0;",t.appendChild(o)}if(n.plugin){const o=document.createElement("span");o.className="badge badge-outline",o.textContent=n.plugin,o.title=`Managed by the ${n.plugin} plugin`,o.style.cssText="font-size:0.65rem;padding:1px 6px;color:var(--dm-warning,#d97706);border-color:var(--dm-warning,#d97706);flex-shrink:0;",t.appendChild(o)}return t.outerHTML}},{key:"slug",title:"Slug",render:c=>{const n=document.createElement("code");return n.textContent=c,n.outerHTML}},{key:"fields",title:"Field Count",render:c=>String(c?.length??0)},{key:"entryCount",title:"Entry Count",render:c=>String(c??0)},{key:"slug",title:"Actions",render:(c,n)=>{const t=document.createElement("div");t.style.cssText="display:flex;gap:.4rem;justify-content:flex-end;flex-wrap:wrap;";const l=document.createElement("a");l.href=`#/collections/edit/${h(c)}`,l.className="btn btn-sm btn-ghost",l.textContent="Edit Schema";const o=document.createElement("a");if(o.href=`#/collections/${h(c)}/entries`,o.className="btn btn-sm btn-ghost",o.textContent="Entries",t.appendChild(l),t.appendChild(o),!new Set(["roles","user-profiles"]).has(c)){const i=document.createElement("a");i.href=`#/forms/edit/${h(c)}`,i.className="btn btn-sm btn-ghost",i.textContent="Form",t.appendChild(i)}const s=document.createElement("button");return s.className="btn btn-sm btn-danger js-delete-collection",s.dataset.slug=c,s.dataset.plugin=n.plugin||"",s.textContent="Delete",t.appendChild(s),t.outerHTML}}],emptyMessage:'No collections yet. Click "New Collection" to get started.'}),document.querySelectorAll(".js-delete-collection").forEach(c=>{c.addEventListener("click",async()=>{const n=c.dataset.slug,t=c.dataset.plugin,l=t?`This collection is managed by the <strong>${t}</strong> plugin. Deleting it may cause the plugin to malfunction. Continue?`:`Delete collection "${n}" and all its data? This cannot be undone.`;if(await E.confirm(l))try{await b.collections.delete(n),E.toast("Collection deleted.",{type:"success"}),await B(e)}catch{E.toast("Failed to delete collection.",{type:"error"})}})}),Domma.icons.scan()}async function j(e){try{d=await b.collections.getConnections()}catch{d={}}C(e),v(e)}function C(e){const a=e.find("#connections-list").get(0),c=e.find("#connections-empty").get(0);if(!a)return;a.textContent="";const n=Object.keys(d);c&&(c.style.display=n.length?"none":""),n.forEach(t=>{const l=d[t],o=document.createElement("div");o.className="conn-card card mb-3",o.dataset.connKey=t;const r=document.createElement("div");r.className="card-header",r.style.cssText="display:flex;align-items:center;gap:.5rem;";const s=document.createElement("input");s.type="text",s.className="form-input conn-name",s.value=t,s.placeholder="Connection name",s.style.cssText="flex:1;";const i=document.createElement("button");i.type="button",i.className="btn btn-sm btn-danger",i.textContent="Delete",i.addEventListener("click",()=>{d=x(e);const L=s.value.trim();L&&delete d[L],C(e),v(e)}),r.appendChild(s),r.appendChild(i);const m=document.createElement("div");m.className="card-body",m.style.cssText="display:grid;grid-template-columns:1fr 2fr 1fr;gap:.6rem;";const p=document.createElement("div"),u=document.createElement("label");u.className="form-label",u.textContent="Type";const f=document.createElement("input");f.type="text",f.className="form-input conn-type",f.value=l.type||"mongodb",f.disabled=!0,p.appendChild(u),p.appendChild(f);const w=document.createElement("div"),N=document.createElement("label");N.className="form-label",N.textContent="URI";const g=document.createElement("input");g.type="text",g.className="form-input conn-uri",g.value=l.uri||"",g.placeholder="mongodb://localhost:27017",w.appendChild(N),w.appendChild(g);const S=document.createElement("div"),k=document.createElement("label");k.className="form-label",k.textContent="Database";const y=document.createElement("input");y.type="text",y.className="form-input conn-database",y.value=l.database||"",y.placeholder="mydb",S.appendChild(k),S.appendChild(y),m.appendChild(p),m.appendChild(w),m.appendChild(S),o.appendChild(r),o.appendChild(m),a.appendChild(o)})}function x(e){const a={},c=e.find("#connections-list").get(0)?.querySelectorAll(".conn-card")||[];for(const n of c){const t=n.querySelector(".conn-name")?.value.trim(),l=n.querySelector(".conn-uri")?.value.trim()||"",o=n.querySelector(".conn-database")?.value.trim()||"";t&&(a[t]={type:"mongodb",uri:l,database:o,options:{}})}return a}function v(e){const a=e.find("#connections-raw-json").get(0);a&&(a.value=JSON.stringify(d,null,2))}
1
+ import{api as b}from"../api.js";import{filterByProject as B,getProjectFromHash as q}from"../lib/project-context.js";function h(e){return String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}let d={};export const collectionsView={templateUrl:"/admin/js/templates/collections.html",async onMount(e){const s=E.loader(e.get(0),{type:"dots"});E.tabs(e.find("#collections-tabs").get(0));const m=e.find("#collections-tabs").get(0)?.querySelector(".tab-list");if(m){const t=e.find("#collections-header-actions").get(0);Array.from(m.querySelectorAll(".tab-item")).forEach((n,c)=>{n.addEventListener("click",()=>{t&&(t.style.display=c===0?"":"none")})})}await Promise.all([j(e),M(e)]),s.destroy(),e.find("#create-collection-btn").off("click").on("click",()=>{const t=E.modal({title:"New Collection",size:"sm"}),n=document.createElement("div");n.style.cssText="padding:.25rem 0 .5rem;";const c=document.createElement("div");F.create({title:{type:"string",label:"Collection Title",placeholder:"e.g. Products, Blog Posts\u2026",required:!0}},{},{showSubmitButton:!1}).renderTo(c),n.appendChild(c);const l=document.createElement("div");l.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:.75rem;";const o=document.createElement("button");o.className="btn btn-ghost",o.textContent="Cancel";const r=document.createElement("button");r.className="btn btn-primary",r.textContent="Create",l.appendChild(o),l.appendChild(r),n.appendChild(l),t.element.appendChild(n),t.open();const a=c.querySelector('input[name="title"]');setTimeout(()=>a?.focus(),50);async function i(){const p=a?.value.trim();if(p)try{const u=await b.collections.create({title:p});t.close(),R.navigate(`/collections/edit/${u.slug}`)}catch(u){E.toast(u.message||"Failed to create collection.",{type:"error"})}}o.addEventListener("click",()=>t.close()),r.addEventListener("click",i),a?.addEventListener("keydown",p=>{p.key==="Enter"&&i()})}),e.find("#connections-raw-toggle").on("change",function(){if(this.checked)d=x(e),v(e),e.find("#connections-form-view").hide(),e.find("#connections-raw-view").show();else{const t=e.find("#connections-raw-json").get(0)?.value||"{}";try{d=JSON.parse(t),C(e),e.find("#connections-form-view").show(),e.find("#connections-raw-view").hide()}catch{E.toast("Invalid JSON \u2014 fix before switching to form view.",{type:"warning"}),this.checked=!0}}}),e.find("#add-connection-btn").on("click",()=>{d=x(e);let t=Object.keys(d).length+1,n=`connection${t}`;for(;d[n];)t++,n=`connection${t}`;d[n]={type:"mongodb",uri:"",database:"",options:{}},C(e),v(e)}),e.find("#save-connections-btn").on("click",async()=>{const t=e.find("#connections-raw-toggle").get(0)?.checked;let n;if(t)try{n=JSON.parse(e.find("#connections-raw-json").get(0)?.value||"{}")}catch{E.toast("Invalid JSON \u2014 fix before saving.",{type:"warning"});return}else n=x(e);try{await b.collections.saveConnections(n),d=n,E.toast("Connections saved.",{type:"success"})}catch(c){E.toast(c.message||"Failed to save connections.",{type:"error"})}}),Domma.icons.scan()}};async function j(e){let s=[];try{s=await b.collections.list()}catch{E.toast("Could not load collections.",{type:"error"})}const m=q();m&&(s=B(s,m)),T.create("#collections-table",{data:s,columns:[{key:"title",title:"Title",render:(t,n)=>{const c=document.createElement("span");c.style.cssText="display:flex;align-items:center;gap:.4rem;";const l=document.createElement("a");if(l.href=`#/collections/${h(n.slug)}/entries`,l.textContent=t,l.style.fontWeight="600",c.appendChild(l),n.storage?.adapter==="mongodb"){const o=document.createElement("span");o.className="badge badge-outline",o.textContent="MongoDB",o.title=`Connection: ${n.storage.connection||"default"}`,o.style.cssText="font-size:0.65rem;padding:1px 6px;color:var(--dm-success,#16a34a);border-color:var(--dm-success,#16a34a);flex-shrink:0;",c.appendChild(o)}if(n.bundled){const o=document.createElement("span");o.className="badge badge-outline",o.textContent="Bundled",o.title="Seeded on fresh installs",o.style.cssText="font-size:0.65rem;padding:1px 6px;color:var(--dm-info,#2563eb);border-color:var(--dm-info,#2563eb);flex-shrink:0;",c.appendChild(o)}if(n.plugin){const o=document.createElement("span");o.className="badge badge-outline",o.textContent=n.plugin,o.title=`Managed by the ${n.plugin} plugin`,o.style.cssText="font-size:0.65rem;padding:1px 6px;color:var(--dm-warning,#d97706);border-color:var(--dm-warning,#d97706);flex-shrink:0;",c.appendChild(o)}return c.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:"entryCount",title:"Entry Count",render:t=>String(t??0)},{key:"slug",title:"Actions",render:(t,n)=>{const c=document.createElement("div");c.style.cssText="display:flex;gap:.4rem;justify-content:flex-end;flex-wrap:wrap;";const l=document.createElement("a");l.href=`#/collections/edit/${h(t)}`,l.className="btn btn-sm btn-ghost",l.textContent="Edit Schema";const o=document.createElement("a");if(o.href=`#/collections/${h(t)}/entries`,o.className="btn btn-sm btn-ghost",o.textContent="Entries",c.appendChild(l),c.appendChild(o),!new Set(["roles","user-profiles"]).has(t)){const i=document.createElement("a");i.href=`#/forms/edit/${h(t)}`,i.className="btn btn-sm btn-ghost",i.textContent="Form",c.appendChild(i)}const a=document.createElement("button");return a.className="btn btn-sm btn-danger js-delete-collection",a.dataset.slug=t,a.dataset.plugin=n.plugin||"",a.textContent="Delete",c.appendChild(a),c.outerHTML}}],emptyMessage:'No collections yet. Click "New Collection" to get started.'}),document.querySelectorAll(".js-delete-collection").forEach(t=>{t.addEventListener("click",async()=>{const n=t.dataset.slug,c=t.dataset.plugin,l=c?`This collection is managed by the <strong>${c}</strong> plugin. Deleting it may cause the plugin to malfunction. Continue?`:`Delete collection "${n}" and all its data? This cannot be undone.`;if(await E.confirm(l))try{await b.collections.delete(n),E.toast("Collection deleted.",{type:"success"}),await j(e)}catch{E.toast("Failed to delete collection.",{type:"error"})}})}),Domma.icons.scan()}async function M(e){try{d=await b.collections.getConnections()}catch{d={}}C(e),v(e)}function C(e){const s=e.find("#connections-list").get(0),m=e.find("#connections-empty").get(0);if(!s)return;s.textContent="";const t=Object.keys(d);m&&(m.style.display=t.length?"none":""),t.forEach(n=>{const c=d[n],l=document.createElement("div");l.className="conn-card card mb-3",l.dataset.connKey=n;const o=document.createElement("div");o.className="card-header",o.style.cssText="display:flex;align-items:center;gap:.5rem;";const r=document.createElement("input");r.type="text",r.className="form-input conn-name",r.value=n,r.placeholder="Connection name",r.style.cssText="flex:1;";const a=document.createElement("button");a.type="button",a.className="btn btn-sm btn-danger",a.textContent="Delete",a.addEventListener("click",()=>{d=x(e);const L=r.value.trim();L&&delete d[L],C(e),v(e)}),o.appendChild(r),o.appendChild(a);const i=document.createElement("div");i.className="card-body",i.style.cssText="display:grid;grid-template-columns:1fr 2fr 1fr;gap:.6rem;";const p=document.createElement("div"),u=document.createElement("label");u.className="form-label",u.textContent="Type";const f=document.createElement("input");f.type="text",f.className="form-input conn-type",f.value=c.type||"mongodb",f.disabled=!0,p.appendChild(u),p.appendChild(f);const w=document.createElement("div"),S=document.createElement("label");S.className="form-label",S.textContent="URI";const g=document.createElement("input");g.type="text",g.className="form-input conn-uri",g.value=c.uri||"",g.placeholder="mongodb://localhost:27017",w.appendChild(S),w.appendChild(g);const N=document.createElement("div"),k=document.createElement("label");k.className="form-label",k.textContent="Database";const y=document.createElement("input");y.type="text",y.className="form-input conn-database",y.value=c.database||"",y.placeholder="mydb",N.appendChild(k),N.appendChild(y),i.appendChild(p),i.appendChild(w),i.appendChild(N),l.appendChild(o),l.appendChild(i),s.appendChild(l)})}function x(e){const s={},m=e.find("#connections-list").get(0)?.querySelectorAll(".conn-card")||[];for(const t of m){const n=t.querySelector(".conn-name")?.value.trim(),c=t.querySelector(".conn-uri")?.value.trim()||"",l=t.querySelector(".conn-database")?.value.trim()||"";n&&(s[n]={type:"mongodb",uri:c,database:l,options:{}})}return s}function v(e){const s=e.find("#connections-raw-json").get(0);s&&(s.value=JSON.stringify(d,null,2))}
@@ -1,8 +1,8 @@
1
- import{apiRequest as O}from"/admin/js/api.js";import{makeIconInput as se}from"/admin/js/lib/shortcode-modal.js";const J=[{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:"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"}],V=new Set(["select","radio","checkbox-group","chooser"]);let b=[],T=null,A=null,M=null;function G(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_|_$/g,"")}function W(e){return J.find(t=>t.value===e)?.label||e}function Y(e){return e?!!(e.visibility?.conditions?.length||e.requirement?.conditions?.length||e.validation?.length||e.cascade?.sourceField):!1}function ie(e){const t={...b[e]};if(t.type==="spacer")return t;if(t.type==="page-break"){const h=document.getElementById(`fb-pb-label-${e}`),y=document.getElementById(`fb-pb-desc-${e}`);return h&&(t.label=h.value.trim()||t.label),y&&(t.description=y.value.trim()),t}const s=document.getElementById(`fb-label-${e}`),n=document.getElementById(`fb-name-${e}`),o=document.getElementById(`fb-type-${e}`),l=document.getElementById(`fb-required-${e}`),a=document.getElementById(`fb-placeholder-${e}`),i=document.getElementById(`fb-helper-${e}`),c=document.getElementById(`fb-tooltip-${e}`);if(s&&(t.label=s.value.trim()||t.label),n&&(t.name=n.value.trim()||t.name),o&&(t.type=o.value||t.type),l&&(t.required=l.checked),a&&(t.placeholder=a.value.trim()),i&&(t.helper=i.value.trim()),c&&(t.tooltip=c.value.trim()),t.type==="chooser"){const h=document.getElementById(`fb-chooser-variant-${e}`)?.value;h&&(t.variant=h);const y=document.getElementById(`fb-chooser-multiple-${e}`);y&&(t.multiple=y.checked);const k=document.getElementById(`fb-chooser-density-${e}`)?.value;k&&(t.density=k);const N=document.getElementById(`fb-chooser-columns-${e}`)?.value;N!==""&&N!==void 0&&(t.columns=parseInt(N,10)||3);const q=document.getElementById(`fb-chooser-accent-${e}`)?.value.trim();q?t.accent=q:delete t.accent;const $=document.getElementById(`fb-chooser-accent-style-${e}`)?.value;$?t.accentStyle=$:delete t.accentStyle;const L=document.getElementById(`fb-chooser-glow-${e}`);L&&(t.glow=L.checked);const B=document.getElementById(`fb-chooser-glow-colour-${e}`)?.value.trim();B?t.glowColour=B:delete t.glowColour;const S=document.getElementById(`fb-chooser-shadow-${e}`)?.value;S&&S!=="none"?t.shadow=S:delete t.shadow;const z=document.getElementById(`fb-chooser-shadow-colour-${e}`)?.value.trim();z?t.shadowColour=z:delete t.shadowColour,D(e),b[e]&&Array.isArray(b[e].options)&&(t.options=b[e].options)}else if(V.has(t.type)){const h=document.getElementById(`fb-options-${e}`);h&&(t.options=h.value.split(`
2
- `).filter(y=>y.trim()).map(y=>{const[k,...N]=y.split(":");return{value:k.trim(),label:N.join(":").trim()||k.trim()}}))}if(t.type==="textarea"){const h=parseInt(document.getElementById(`fb-rows-${e}`)?.value,10);h>0&&(t.formConfig={...t.formConfig||{},rows:h})}const r=document.getElementById(`fb-span-${e}`),m=document.getElementById(`fb-fullwidth-${e}`);if(r||m){const h={...t.formConfig||{}};if(m?.checked)h.span="full";else{const y=parseInt(r?.value,10);y>1?h.span=y:delete h.span}t.formConfig=Object.keys(h).length?h:void 0}const p=document.getElementById(`fb-minlength-${e}`)?.value;p&&(t.minLength=parseInt(p,10));const u=document.getElementById(`fb-maxlength-${e}`)?.value;u&&(t.maxLength=parseInt(u,10));const d=document.getElementById(`fb-min-${e}`)?.value;d!==""&&d!==void 0&&(t.min=parseFloat(d));const f=document.getElementById(`fb-max-${e}`)?.value;f!==""&&f!==void 0&&(t.max=parseFloat(f));const g=ce(e);return g?t.logic=g:delete t.logic,t}function ce(e){const t=document.querySelector(`.fb-field-card[data-index="${e}"]`);if(!t)return;const s=t.querySelector(".fb-field-logic");if(!s)return;const n={};let o=!1;const l=s.querySelector('[data-logic-section="visibility"]');if(l){const r=l.querySelector(".fb-logic-vis-default"),m=l.querySelector(".fb-logic-vis-transition"),p=Array.from(l.querySelectorAll(".fb-logic-cond-row")).map(f=>{const g=f.querySelector(".fb-logic-cond-field"),h=f.querySelector(".fb-logic-cond-op"),y=f.querySelector(".fb-logic-cond-val"),k=f.querySelector(".fb-logic-vis-then");return g?.value?{when:{all:[{field:g.value,operator:h.value,value:y.value}]},then:k.value}:null}).filter(Boolean),u=r?.value||"visible",d=m?.value||"none";(u!=="visible"||p.length>0||d!=="none")&&(n.visibility={default:u,conditions:p},d!=="none"&&(n.visibility.transition=d),o=!0)}const a=s.querySelector('[data-logic-section="requirement"]');if(a){const r=a.querySelector(".fb-logic-req-default"),m=Array.from(a.querySelectorAll(".fb-logic-cond-row")).map(p=>{const u=p.querySelector(".fb-logic-cond-field"),d=p.querySelector(".fb-logic-cond-op"),f=p.querySelector(".fb-logic-cond-val"),g=p.querySelector(".fb-logic-req-then");return u?.value?{when:{all:[{field:u.value,operator:d.value,value:f.value}]},then:g.value==="true"}:null}).filter(Boolean);m.length>0&&(n.requirement={default:r?.checked===!0,conditions:m},o=!0)}const i=s.querySelector('[data-logic-section="validation"]');if(i){const r=Array.from(i.querySelectorAll(".fb-logic-val-rule")).map(m=>{const p=m.querySelector(".fb-logic-val-type"),u=m.querySelector(".fb-logic-val-pattern"),d=m.querySelector(".fb-logic-val-flags"),f=m.querySelector(".fb-logic-val-message");if(!u?.value.trim())return null;const g=p?.value||"regex",h={type:g,message:f?.value.trim()||"Invalid value."};return g==="regex"?(h.pattern=u.value.trim(),d?.value.trim()&&(h.flags=d.value.trim())):h.field=u.value.trim(),h}).filter(Boolean);r.length>0&&(n.validation=r,o=!0)}const c=s.querySelector('[data-logic-section="cascade"]');if(c){const r=c.querySelector(".fb-logic-cascade-source"),m=c.querySelector(".fb-logic-cascade-mapping"),p=c.querySelector(".fb-logic-cascade-defaults"),u=r?.value?.trim();if(u){let d={};try{d=JSON.parse(m?.value||"{}")}catch{}const f=(p?.value||"").split(`
3
- `).filter(g=>g.trim()).map(g=>{const[h,...y]=g.split(":");return{value:h.trim(),label:y.join(":").trim()||h.trim()}});n.cascade={sourceField:u,mapping:d,defaultOptions:f},o=!0}}return o?n:void 0}function C(){b=b.map((e,t)=>ie(t))}function w(e){const t=e.find("#fields-list").get(0),s=e.find("#fields-empty-msg").get(0);if(t){if(Array.from(t.querySelectorAll(".fb-field-card")).forEach(n=>n.remove()),b.length===0){s&&(s.style.display="");return}s&&(s.style.display="none"),b.forEach((n,o)=>{const l=n.type==="page-break"?re(n,o,e):n.type==="spacer"?de(n,o,e):pe(n,o,e);t.appendChild(l)})}}function re(e,t,s){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:2px dashed var(--border-color,#444);border-radius:6px;overflow:hidden;margin-bottom:.5rem;background:var(--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:rgba(120,120,120,.2);color:var(--text-muted,#888);white-space:nowrap;flex-shrink:0;font-style:italic;";const a=document.createElement("span");a.textContent=e.label||"Untitled Step",a.style.cssText="flex:1;font-weight:600;font-size:.9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-muted,#888);";const i=document.createElement("div");if(i.style.cssText="display:flex;gap:.25rem;flex-shrink:0;margin-left:.5rem;",t>0){const d=document.createElement("button");d.className="btn btn-xs btn-ghost",d.title="Move up",d.textContent="\u2191",d.addEventListener("click",f=>{f.stopPropagation(),C(),[b[t-1],b[t]]=[b[t],b[t-1]],w(s)}),i.appendChild(d)}if(t<b.length-1){const d=document.createElement("button");d.className="btn btn-xs btn-ghost",d.title="Move down",d.textContent="\u2193",d.addEventListener("click",f=>{f.stopPropagation(),C(),[b[t],b[t+1]]=[b[t+1],b[t]],w(s)}),i.appendChild(d)}const c=document.createElement("button");c.className="btn btn-xs btn-ghost",c.title="Edit step",c.textContent="\u22EF",c.addEventListener("click",d=>{d.stopPropagation(),m.style.display=m.style.display==="none"?"":"none"}),i.appendChild(c);const r=document.createElement("button");r.className="btn btn-xs btn-danger",r.title="Remove page break",r.textContent="\u2715",r.addEventListener("click",async d=>{d.stopPropagation(),await E.confirm("Remove this page break?")&&(C(),b.splice(t,1),w(s))}),i.appendChild(r),o.appendChild(l),o.appendChild(a),o.appendChild(i),o.addEventListener("click",()=>{m.style.display=m.style.display==="none"?"":"none"});const m=document.createElement("div");m.className="fb-field-body",m.style.cssText="padding:.8rem;border-top:1px dashed var(--border-color,#444);display:none;";const p=v([x("Step Title",`fb-pb-label-${t}`,"text",e.label||"","Shown as the wizard step heading"),x("Step Description",`fb-pb-desc-${t}`,"text",e.description||"","Optional sub-heading")]),u=p.querySelector(`#fb-pb-label-${t}`);return u&&u.addEventListener("input",()=>{a.textContent=u.value||"Untitled Step"}),m.appendChild(p),n.appendChild(o),n.appendChild(m),n}function de(e,t,s){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:1px dashed var(--border-color,#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(--border-color,#444);";const a=document.createElement("span");a.textContent="Spacer",a.style.cssText="font-size:.7rem;color:var(--text-muted,#888);white-space:nowrap;padding:0 .4rem;font-style:italic;";const i=document.createElement("div");i.style.cssText="flex:1;height:1px;background:var(--border-color,#444);";const c=document.createElement("div");if(c.style.cssText="display:flex;gap:.25rem;flex-shrink:0;",t>0){const m=document.createElement("button");m.className="btn btn-xs btn-ghost",m.title="Move up",m.textContent="\u2191",m.addEventListener("click",p=>{p.stopPropagation(),C(),[b[t-1],b[t]]=[b[t],b[t-1]],w(s)}),c.appendChild(m)}if(t<b.length-1){const m=document.createElement("button");m.className="btn btn-xs btn-ghost",m.title="Move down",m.textContent="\u2193",m.addEventListener("click",p=>{p.stopPropagation(),C(),[b[t],b[t+1]]=[b[t+1],b[t]],w(s)}),c.appendChild(m)}const r=document.createElement("button");return r.className="btn btn-xs btn-danger",r.title="Remove spacer",r.textContent="\u2715",r.addEventListener("click",async m=>{m.stopPropagation(),C(),b.splice(t,1),w(s)}),c.appendChild(r),o.appendChild(l),o.appendChild(a),o.appendChild(i),o.appendChild(c),n.appendChild(o),n}function pe(e,t,s){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:1px solid var(--border-color,#333);border-radius:6px;overflow:hidden;margin-bottom:.5rem;";const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.6rem .8rem;background:var(--card-header-bg,rgba(255,255,255,.04));cursor:pointer;user-select:none;";const l=document.createElement("span");l.textContent=W(e.type),l.style.cssText="font-size:.7rem;padding:.15rem .45rem;border-radius:999px;background:var(--primary-soft,rgba(99,102,241,.15));color:var(--primary,#6366f1);white-space:nowrap;flex-shrink:0;";const a=document.createElement("span");a.textContent=e.label||"(unlabelled)",a.style.cssText="flex:1;font-weight:600;font-size:.9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;";const i=document.createElement("div");if(i.style.cssText="display:flex;gap:.25rem;flex-shrink:0;margin-left:.5rem;",t>0){const p=document.createElement("button");p.className="btn btn-xs btn-ghost",p.title="Move up",p.textContent="\u2191",p.addEventListener("click",u=>{u.stopPropagation(),C(),[b[t-1],b[t]]=[b[t],b[t-1]],w(s)}),i.appendChild(p)}if(t<b.length-1){const p=document.createElement("button");p.className="btn btn-xs btn-ghost",p.title="Move down",p.textContent="\u2193",p.addEventListener("click",u=>{u.stopPropagation(),C(),[b[t],b[t+1]]=[b[t+1],b[t]],w(s)}),i.appendChild(p)}const c=document.createElement("button");c.className="btn btn-xs btn-ghost",c.title="Edit field",c.textContent="\u22EF",c.addEventListener("click",p=>{p.stopPropagation(),m.style.display=m.style.display==="none"?"":"none"}),i.appendChild(c);const r=document.createElement("button");if(r.className="btn btn-xs btn-danger",r.title="Remove field",r.textContent="\u2715",r.addEventListener("click",async p=>{p.stopPropagation(),await E.confirm("Remove this field?")&&(C(),b.splice(t,1),w(s))}),i.appendChild(r),o.appendChild(l),o.appendChild(a),e.required){const p=document.createElement("span");p.textContent="required",p.style.cssText="font-size:.7rem;color:var(--danger,#ef4444);flex-shrink:0;",o.appendChild(p)}if(Y(e.logic)){const p=document.createElement("span");p.textContent="\u26A1",p.title="Has conditional logic",p.style.cssText="font-size:.75rem;color:var(--primary,#6366f1);flex-shrink:0;",o.appendChild(p)}o.appendChild(i),o.addEventListener("click",()=>{m.style.display=m.style.display==="none"?"":"none"});const m=me(e,t,a);return m.style.display="none",n.appendChild(o),n.appendChild(m),n}function me(e,t,s){const n=document.createElement("div");n.className="fb-field-body",n.style.cssText="padding:.8rem;border-top:1px solid var(--border-color,#333);";const o=v([x("Label",`fb-label-${t}`,"text",e.label||"","Shown above the field"),x("Field Name",`fb-name-${t}`,"text",e.name||"","Used as data key")]),l=v([Se("Type",`fb-type-${t}`,J,e.type||"string"),ne("Required",`fb-required-${t}`,e.required||!1)]),a=v([x("Placeholder",`fb-placeholder-${t}`,"text",e.placeholder||"","Hint text inside the field")]),i=v([x("Helper Text",`fb-helper-${t}`,"text",e.helper||"","Shown below the field"),x("Tooltip",`fb-tooltip-${t}`,"text",e.tooltip||"","Shown on hover via a help icon next to the label")]),c=e.formConfig?.span,r=v([x("Column Span",`fb-span-${t}`,"number",c&&c!=="full"?String(c):"1","Columns to span (grid only)"),ne("Full Width",`fb-fullwidth-${t}`,c==="full")]);r.classList.add("fb-grid-row"),r.style.display=document.getElementById("setting-layout")?.value==="grid"?"flex":"none",n.appendChild(o),n.appendChild(l),n.appendChild(a),n.appendChild(i),n.appendChild(r);const m=n.querySelector(`#fb-label-${t}`),p=n.querySelector(`#fb-name-${t}`);m&&m.addEventListener("input",()=>{s&&(s.textContent=m.value||"(unlabelled)"),p&&!p.dataset.manuallyEdited&&(p.value=G(m.value))}),p&&p.addEventListener("input",()=>{p.dataset.manuallyEdited="1"});const u=n.querySelector(`#fb-type-${t}`);u&&u.addEventListener("change",()=>{const f=n.closest(".fb-field-card");if(f){const y=f.querySelector("span");y&&(y.textContent=W(u.value))}const g=n.querySelector(".fb-field-extras");g&&g.remove();const h=X(u.value,e,t);h&&n.appendChild(h)});const d=X(e.type,e,t);return d&&n.appendChild(d),n.appendChild(ye(e,t)),n}const ue=[{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"}],K=new Set(["is_empty","is_not_empty"]);function P(e){const t=document.createElement("p");return t.textContent=e,t.style.cssText="font-size:.75rem;font-weight:700;color:var(--text-muted,#888);margin:.6rem 0 .3rem;text-transform:uppercase;letter-spacing:.04em;",t}function _(e,t,s,n,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 a=document.createElement("span");a.textContent="When",a.style.cssText="font-size:.73rem;color:var(--text-muted,#888);flex-shrink:0;";const i=document.createElement("select");i.className="form-select fb-logic-cond-field",i.style.cssText="flex:1 1 140px;min-width:140px;",t.forEach(d=>{const f=document.createElement("option");f.value=d.value,f.textContent=d.label,e&&d.value===e.field&&(f.selected=!0),i.appendChild(f)});const c=document.createElement("select");c.className="form-select fb-logic-cond-op",c.style.cssText="flex:1 1 160px;min-width:140px;",ue.forEach(d=>{const f=document.createElement("option");f.value=d.value,f.textContent=d.label,e&&d.value===e.operator&&(f.selected=!0),c.appendChild(f)});const r=document.createElement("input");r.type="text",r.className="form-input fb-logic-cond-val",r.placeholder="value",r.style.cssText="flex:1 1 120px;min-width:100px;",r.value=e?.value||"",e&&K.has(e.operator)&&(r.style.display="none"),c.addEventListener("change",()=>{r.style.display=K.has(c.value)?"none":""});const m=document.createElement("span");m.textContent="\u2192",m.style.cssText="font-size:.73rem;color:var(--text-muted,#888);flex-shrink:0;";const p=document.createElement("select");p.className=`form-select ${n}`,p.style.cssText="flex:1 1 140px;min-width:120px;",s.forEach(d=>{const f=document.createElement("option");f.value=d.value,f.textContent=d.label,d.value===o&&(f.selected=!0),p.appendChild(f)});const u=document.createElement("button");return u.type="button",u.className="btn btn-xs btn-danger",u.textContent="\u2715",u.style.flexShrink="0",u.addEventListener("click",()=>l.remove()),l.appendChild(a),l.appendChild(i),l.appendChild(c),l.appendChild(r),l.appendChild(m),l.appendChild(p),l.appendChild(u),l}function fe(e,t,s){const n=document.createElement("div");n.dataset.logicSection="visibility",n.appendChild(P("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 a=document.createElement("select");a.className="form-select fb-logic-vis-default",a.style.cssText="max-width:240px;",[{value:"visible",label:"Visible"},{value:"hidden",label:"Hidden"}].forEach(d=>{const f=document.createElement("option");f.value=d.value,f.textContent=d.label,d.value===(e.default||"visible")&&(f.selected=!0),a.appendChild(f)}),o.appendChild(l),o.appendChild(a),n.appendChild(o);const i=document.createElement("div");i.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem;";const c=document.createElement("span");c.textContent="Transition:",c.style.cssText="font-size:.8rem;flex-shrink:0;";const r=document.createElement("select");r.className="form-select fb-logic-vis-transition",r.style.cssText="max-width:240px;",[{value:"none",label:"None (instant)"},{value:"fade",label:"Fade"},{value:"slide",label:"Slide"},{value:"scale",label:"Scale"}].forEach(d=>{const f=document.createElement("option");f.value=d.value,f.textContent=d.label,d.value===(e.transition||"none")&&(f.selected=!0),r.appendChild(f)}),i.appendChild(c),i.appendChild(r),n.appendChild(i);const m=document.createElement("div");m.className="fb-logic-vis-rules";const p=s.map(d=>({value:d.name,label:d.label||d.name})),u=[{value:"visible",label:"Show"},{value:"hidden",label:"Hide"}];if((e.conditions||[]).forEach(d=>{const f=(d.when?.all||d.when?.any||[])[0],g=d.then==="hidden"?"hidden":"visible";p.length>0&&m.appendChild(_(f,p,u,"fb-logic-vis-then",g))}),n.appendChild(m),p.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:.2rem;",d.textContent="+ Add visibility rule",d.addEventListener("click",()=>m.appendChild(_(null,p,u,"fb-logic-vis-then","visible"))),n.appendChild(d)}return n}function be(e,t,s){const n=document.createElement("div");n.dataset.logicSection="requirement",n.appendChild(P("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")),n.appendChild(o);const a=document.createElement("div");a.className="fb-logic-req-rules";const i=s.map(r=>({value:r.name,label:r.label||r.name})),c=[{value:"true",label:"Make required"},{value:"false",label:"Make optional"}];if((e.conditions||[]).forEach(r=>{const m=(r.when?.all||r.when?.any||[])[0],p=r.then===!0?"true":"false";i.length>0&&a.appendChild(_(m,i,c,"fb-logic-req-then",p))}),n.appendChild(a),i.length>0){const r=document.createElement("button");r.type="button",r.className="btn btn-xs btn-ghost",r.style.cssText="font-size:.73rem;margin-top:.2rem;",r.textContent="+ Add requirement rule",r.addEventListener("click",()=>a.appendChild(_(null,i,c,"fb-logic-req-then","true"))),n.appendChild(r)}return n}function Q(e){const t=document.createElement("div");t.className="fb-logic-val-rule",t.style.cssText="display:flex;gap:.35rem;align-items:center;margin-bottom:.35rem;flex-wrap:wrap;";const s=document.createElement("select");s.className="form-select fb-logic-val-type",s.style.cssText="flex:0 0 140px;",[{value:"regex",label:"Regex"},{value:"match",label:"Match field"}].forEach(i=>{const c=document.createElement("option");c.value=i.value,c.textContent=i.label,i.value===(e?.type||"regex")&&(c.selected=!0),s.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 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"),s.addEventListener("change",()=>{o.style.display=s.value==="match"?"none":"",n.placeholder=s.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 a=document.createElement("button");return a.type="button",a.className="btn btn-xs btn-danger",a.textContent="\u2715",a.style.flexShrink="0",a.addEventListener("click",()=>t.remove()),t.appendChild(s),t.appendChild(n),t.appendChild(o),t.appendChild(l),t.appendChild(a),t}function he(e,t,s){const n=document.createElement("div");n.dataset.logicSection="validation",n.appendChild(P("Custom Validation"));const o=document.createElement("div");o.className="fb-logic-val-rules",(e||[]).forEach(a=>o.appendChild(Q(a))),n.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;",l.textContent="+ Add validation rule",l.addEventListener("click",()=>o.appendChild(Q(null))),n.appendChild(l),n}function ge(e,t,s){const n=document.createElement("div");n.dataset.logicSection="cascade",n.appendChild(P("Cascade Options"));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 a=document.createElement("select");a.className="form-select fb-logic-cascade-source",a.style.cssText="flex:1;";const i=document.createElement("option");i.value="",i.textContent="\u2014 none \u2014",a.appendChild(i),s.forEach(u=>{const d=document.createElement("option");d.value=u.name,d.textContent=u.label||u.name,u.name===e.sourceField&&(d.selected=!0),a.appendChild(d)}),o.appendChild(l),o.appendChild(a),n.appendChild(o);const c=document.createElement("p");c.textContent='Mapping JSON \u2014 {"value":[{"value":"...","label":"..."}]}',c.style.cssText="font-size:.73rem;color:var(--text-muted,#888);margin:.3rem 0 .2rem;";const r=document.createElement("textarea");r.className="form-input fb-logic-cascade-mapping",r.rows=4,r.style.cssText="font-family:monospace;",r.placeholder='{"uk": [{"value": "london", "label": "London"}]}',r.value=e.mapping?JSON.stringify(e.mapping,null,2):"",n.appendChild(c),n.appendChild(r);const m=document.createElement("p");m.textContent="Default options (one per line: value:Label)",m.style.cssText="font-size:.73rem;color:var(--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
1
+ import{apiRequest as z}from"/admin/js/api.js";import{makeIconInput as ie}from"/admin/js/lib/shortcode-modal.js";const G=[{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:"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"}],V=new Set(["select","radio","checkbox-group","chooser"]);let b=[],T=null,O=null,M=null,H={};function W(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_|_$/g,"")}function Y(e){return G.find(t=>t.value===e)?.label||e}function K(e){return e?!!(e.visibility?.conditions?.length||e.requirement?.conditions?.length||e.validation?.length||e.cascade?.sourceField):!1}function ce(e){const t={...b[e]};if(t.type==="spacer")return t;if(t.type==="page-break"){const h=document.getElementById(`fb-pb-label-${e}`),y=document.getElementById(`fb-pb-desc-${e}`);return h&&(t.label=h.value.trim()||t.label),y&&(t.description=y.value.trim()),t}const s=document.getElementById(`fb-label-${e}`),n=document.getElementById(`fb-name-${e}`),o=document.getElementById(`fb-type-${e}`),l=document.getElementById(`fb-required-${e}`),a=document.getElementById(`fb-placeholder-${e}`),i=document.getElementById(`fb-helper-${e}`),c=document.getElementById(`fb-tooltip-${e}`);if(s&&(t.label=s.value.trim()||t.label),n&&(t.name=n.value.trim()||t.name),o&&(t.type=o.value||t.type),l&&(t.required=l.checked),a&&(t.placeholder=a.value.trim()),i&&(t.helper=i.value.trim()),c&&(t.tooltip=c.value.trim()),t.type==="chooser"){const h=document.getElementById(`fb-chooser-variant-${e}`)?.value;h&&(t.variant=h);const y=document.getElementById(`fb-chooser-multiple-${e}`);y&&(t.multiple=y.checked);const k=document.getElementById(`fb-chooser-density-${e}`)?.value;k&&(t.density=k);const N=document.getElementById(`fb-chooser-columns-${e}`)?.value;N!==""&&N!==void 0&&(t.columns=parseInt(N,10)||3);const q=document.getElementById(`fb-chooser-accent-${e}`)?.value.trim();q?t.accent=q:delete t.accent;const $=document.getElementById(`fb-chooser-accent-style-${e}`)?.value;$?t.accentStyle=$:delete t.accentStyle;const L=document.getElementById(`fb-chooser-glow-${e}`);L&&(t.glow=L.checked);const B=document.getElementById(`fb-chooser-glow-colour-${e}`)?.value.trim();B?t.glowColour=B:delete t.glowColour;const S=document.getElementById(`fb-chooser-shadow-${e}`)?.value;S&&S!=="none"?t.shadow=S:delete t.shadow;const I=document.getElementById(`fb-chooser-shadow-colour-${e}`)?.value.trim();I?t.shadowColour=I:delete t.shadowColour,D(e),b[e]&&Array.isArray(b[e].options)&&(t.options=b[e].options)}else if(V.has(t.type)){const h=document.getElementById(`fb-options-${e}`);h&&(t.options=h.value.split(`
2
+ `).filter(y=>y.trim()).map(y=>{const[k,...N]=y.split(":");return{value:k.trim(),label:N.join(":").trim()||k.trim()}}))}if(t.type==="textarea"){const h=parseInt(document.getElementById(`fb-rows-${e}`)?.value,10);h>0&&(t.formConfig={...t.formConfig||{},rows:h})}const r=document.getElementById(`fb-span-${e}`),m=document.getElementById(`fb-fullwidth-${e}`);if(r||m){const h={...t.formConfig||{}};if(m?.checked)h.span="full";else{const y=parseInt(r?.value,10);y>1?h.span=y:delete h.span}t.formConfig=Object.keys(h).length?h:void 0}const p=document.getElementById(`fb-minlength-${e}`)?.value;p&&(t.minLength=parseInt(p,10));const u=document.getElementById(`fb-maxlength-${e}`)?.value;u&&(t.maxLength=parseInt(u,10));const d=document.getElementById(`fb-min-${e}`)?.value;d!==""&&d!==void 0&&(t.min=parseFloat(d));const f=document.getElementById(`fb-max-${e}`)?.value;f!==""&&f!==void 0&&(t.max=parseFloat(f));const g=re(e);return g?t.logic=g:delete t.logic,t}function re(e){const t=document.querySelector(`.fb-field-card[data-index="${e}"]`);if(!t)return;const s=t.querySelector(".fb-field-logic");if(!s)return;const n={};let o=!1;const l=s.querySelector('[data-logic-section="visibility"]');if(l){const r=l.querySelector(".fb-logic-vis-default"),m=l.querySelector(".fb-logic-vis-transition"),p=Array.from(l.querySelectorAll(".fb-logic-cond-row")).map(f=>{const g=f.querySelector(".fb-logic-cond-field"),h=f.querySelector(".fb-logic-cond-op"),y=f.querySelector(".fb-logic-cond-val"),k=f.querySelector(".fb-logic-vis-then");return g?.value?{when:{all:[{field:g.value,operator:h.value,value:y.value}]},then:k.value}:null}).filter(Boolean),u=r?.value||"visible",d=m?.value||"none";(u!=="visible"||p.length>0||d!=="none")&&(n.visibility={default:u,conditions:p},d!=="none"&&(n.visibility.transition=d),o=!0)}const a=s.querySelector('[data-logic-section="requirement"]');if(a){const r=a.querySelector(".fb-logic-req-default"),m=Array.from(a.querySelectorAll(".fb-logic-cond-row")).map(p=>{const u=p.querySelector(".fb-logic-cond-field"),d=p.querySelector(".fb-logic-cond-op"),f=p.querySelector(".fb-logic-cond-val"),g=p.querySelector(".fb-logic-req-then");return u?.value?{when:{all:[{field:u.value,operator:d.value,value:f.value}]},then:g.value==="true"}:null}).filter(Boolean);m.length>0&&(n.requirement={default:r?.checked===!0,conditions:m},o=!0)}const i=s.querySelector('[data-logic-section="validation"]');if(i){const r=Array.from(i.querySelectorAll(".fb-logic-val-rule")).map(m=>{const p=m.querySelector(".fb-logic-val-type"),u=m.querySelector(".fb-logic-val-pattern"),d=m.querySelector(".fb-logic-val-flags"),f=m.querySelector(".fb-logic-val-message");if(!u?.value.trim())return null;const g=p?.value||"regex",h={type:g,message:f?.value.trim()||"Invalid value."};return g==="regex"?(h.pattern=u.value.trim(),d?.value.trim()&&(h.flags=d.value.trim())):h.field=u.value.trim(),h}).filter(Boolean);r.length>0&&(n.validation=r,o=!0)}const c=s.querySelector('[data-logic-section="cascade"]');if(c){const r=c.querySelector(".fb-logic-cascade-source"),m=c.querySelector(".fb-logic-cascade-mapping"),p=c.querySelector(".fb-logic-cascade-defaults"),u=r?.value?.trim();if(u){let d={};try{d=JSON.parse(m?.value||"{}")}catch{}const f=(p?.value||"").split(`
3
+ `).filter(g=>g.trim()).map(g=>{const[h,...y]=g.split(":");return{value:h.trim(),label:y.join(":").trim()||h.trim()}});n.cascade={sourceField:u,mapping:d,defaultOptions:f},o=!0}}return o?n:void 0}function C(){b=b.map((e,t)=>ce(t))}function w(e){const t=e.find("#fields-list").get(0),s=e.find("#fields-empty-msg").get(0);if(t){if(Array.from(t.querySelectorAll(".fb-field-card")).forEach(n=>n.remove()),b.length===0){s&&(s.style.display="");return}s&&(s.style.display="none"),b.forEach((n,o)=>{const l=n.type==="page-break"?de(n,o,e):n.type==="spacer"?pe(n,o,e):me(n,o,e);t.appendChild(l)})}}function de(e,t,s){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:2px dashed var(--border-color,#444);border-radius:6px;overflow:hidden;margin-bottom:.5rem;background:var(--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:rgba(120,120,120,.2);color:var(--text-muted,#888);white-space:nowrap;flex-shrink:0;font-style:italic;";const a=document.createElement("span");a.textContent=e.label||"Untitled Step",a.style.cssText="flex:1;font-weight:600;font-size:.9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-muted,#888);";const i=document.createElement("div");if(i.style.cssText="display:flex;gap:.25rem;flex-shrink:0;margin-left:.5rem;",t>0){const d=document.createElement("button");d.className="btn btn-xs btn-ghost",d.title="Move up",d.textContent="\u2191",d.addEventListener("click",f=>{f.stopPropagation(),C(),[b[t-1],b[t]]=[b[t],b[t-1]],w(s)}),i.appendChild(d)}if(t<b.length-1){const d=document.createElement("button");d.className="btn btn-xs btn-ghost",d.title="Move down",d.textContent="\u2193",d.addEventListener("click",f=>{f.stopPropagation(),C(),[b[t],b[t+1]]=[b[t+1],b[t]],w(s)}),i.appendChild(d)}const c=document.createElement("button");c.className="btn btn-xs btn-ghost",c.title="Edit step",c.textContent="\u22EF",c.addEventListener("click",d=>{d.stopPropagation(),m.style.display=m.style.display==="none"?"":"none"}),i.appendChild(c);const r=document.createElement("button");r.className="btn btn-xs btn-danger",r.title="Remove page break",r.textContent="\u2715",r.addEventListener("click",async d=>{d.stopPropagation(),await E.confirm("Remove this page break?")&&(C(),b.splice(t,1),w(s))}),i.appendChild(r),o.appendChild(l),o.appendChild(a),o.appendChild(i),o.addEventListener("click",()=>{m.style.display=m.style.display==="none"?"":"none"});const m=document.createElement("div");m.className="fb-field-body",m.style.cssText="padding:.8rem;border-top:1px dashed var(--border-color,#444);display:none;";const p=v([x("Step Title",`fb-pb-label-${t}`,"text",e.label||"","Shown as the wizard step heading"),x("Step Description",`fb-pb-desc-${t}`,"text",e.description||"","Optional sub-heading")]),u=p.querySelector(`#fb-pb-label-${t}`);return u&&u.addEventListener("input",()=>{a.textContent=u.value||"Untitled Step"}),m.appendChild(p),n.appendChild(o),n.appendChild(m),n}function pe(e,t,s){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:1px dashed var(--border-color,#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(--border-color,#444);";const a=document.createElement("span");a.textContent="Spacer",a.style.cssText="font-size:.7rem;color:var(--text-muted,#888);white-space:nowrap;padding:0 .4rem;font-style:italic;";const i=document.createElement("div");i.style.cssText="flex:1;height:1px;background:var(--border-color,#444);";const c=document.createElement("div");if(c.style.cssText="display:flex;gap:.25rem;flex-shrink:0;",t>0){const m=document.createElement("button");m.className="btn btn-xs btn-ghost",m.title="Move up",m.textContent="\u2191",m.addEventListener("click",p=>{p.stopPropagation(),C(),[b[t-1],b[t]]=[b[t],b[t-1]],w(s)}),c.appendChild(m)}if(t<b.length-1){const m=document.createElement("button");m.className="btn btn-xs btn-ghost",m.title="Move down",m.textContent="\u2193",m.addEventListener("click",p=>{p.stopPropagation(),C(),[b[t],b[t+1]]=[b[t+1],b[t]],w(s)}),c.appendChild(m)}const r=document.createElement("button");return r.className="btn btn-xs btn-danger",r.title="Remove spacer",r.textContent="\u2715",r.addEventListener("click",async m=>{m.stopPropagation(),C(),b.splice(t,1),w(s)}),c.appendChild(r),o.appendChild(l),o.appendChild(a),o.appendChild(i),o.appendChild(c),n.appendChild(o),n}function me(e,t,s){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:1px solid var(--border-color,#333);border-radius:6px;overflow:hidden;margin-bottom:.5rem;";const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.6rem .8rem;background:var(--card-header-bg,rgba(255,255,255,.04));cursor:pointer;user-select:none;";const l=document.createElement("span");l.textContent=Y(e.type),l.style.cssText="font-size:.7rem;padding:.15rem .45rem;border-radius:999px;background:var(--primary-soft,rgba(99,102,241,.15));color:var(--primary,#6366f1);white-space:nowrap;flex-shrink:0;";const a=document.createElement("span");a.textContent=e.label||"(unlabelled)",a.style.cssText="flex:1;font-weight:600;font-size:.9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;";const i=document.createElement("div");if(i.style.cssText="display:flex;gap:.25rem;flex-shrink:0;margin-left:.5rem;",t>0){const p=document.createElement("button");p.className="btn btn-xs btn-ghost",p.title="Move up",p.textContent="\u2191",p.addEventListener("click",u=>{u.stopPropagation(),C(),[b[t-1],b[t]]=[b[t],b[t-1]],w(s)}),i.appendChild(p)}if(t<b.length-1){const p=document.createElement("button");p.className="btn btn-xs btn-ghost",p.title="Move down",p.textContent="\u2193",p.addEventListener("click",u=>{u.stopPropagation(),C(),[b[t],b[t+1]]=[b[t+1],b[t]],w(s)}),i.appendChild(p)}const c=document.createElement("button");c.className="btn btn-xs btn-ghost",c.title="Edit field",c.textContent="\u22EF",c.addEventListener("click",p=>{p.stopPropagation(),m.style.display=m.style.display==="none"?"":"none"}),i.appendChild(c);const r=document.createElement("button");if(r.className="btn btn-xs btn-danger",r.title="Remove field",r.textContent="\u2715",r.addEventListener("click",async p=>{p.stopPropagation(),await E.confirm("Remove this field?")&&(C(),b.splice(t,1),w(s))}),i.appendChild(r),o.appendChild(l),o.appendChild(a),e.required){const p=document.createElement("span");p.textContent="required",p.style.cssText="font-size:.7rem;color:var(--danger,#ef4444);flex-shrink:0;",o.appendChild(p)}if(K(e.logic)){const p=document.createElement("span");p.textContent="\u26A1",p.title="Has conditional logic",p.style.cssText="font-size:.75rem;color:var(--primary,#6366f1);flex-shrink:0;",o.appendChild(p)}o.appendChild(i),o.addEventListener("click",()=>{m.style.display=m.style.display==="none"?"":"none"});const m=ue(e,t,a);return m.style.display="none",n.appendChild(o),n.appendChild(m),n}function ue(e,t,s){const n=document.createElement("div");n.className="fb-field-body",n.style.cssText="padding:.8rem;border-top:1px solid var(--border-color,#333);";const o=v([x("Label",`fb-label-${t}`,"text",e.label||"","Shown above the field"),x("Field Name",`fb-name-${t}`,"text",e.name||"","Used as data key")]),l=v([Te("Type",`fb-type-${t}`,G,e.type||"string"),le("Required",`fb-required-${t}`,e.required||!1)]),a=v([x("Placeholder",`fb-placeholder-${t}`,"text",e.placeholder||"","Hint text inside the field")]),i=v([x("Helper Text",`fb-helper-${t}`,"text",e.helper||"","Shown below the field"),x("Tooltip",`fb-tooltip-${t}`,"text",e.tooltip||"","Shown on hover via a help icon next to the label")]),c=e.formConfig?.span,r=v([x("Column Span",`fb-span-${t}`,"number",c&&c!=="full"?String(c):"1","Columns to span (grid only)"),le("Full Width",`fb-fullwidth-${t}`,c==="full")]);r.classList.add("fb-grid-row"),r.style.display=document.getElementById("setting-layout")?.value==="grid"?"flex":"none",n.appendChild(o),n.appendChild(l),n.appendChild(a),n.appendChild(i),n.appendChild(r);const m=n.querySelector(`#fb-label-${t}`),p=n.querySelector(`#fb-name-${t}`);m&&m.addEventListener("input",()=>{s&&(s.textContent=m.value||"(unlabelled)"),p&&!p.dataset.manuallyEdited&&(p.value=W(m.value))}),p&&p.addEventListener("input",()=>{p.dataset.manuallyEdited="1"});const u=n.querySelector(`#fb-type-${t}`);u&&u.addEventListener("change",()=>{const f=n.closest(".fb-field-card");if(f){const y=f.querySelector("span");y&&(y.textContent=Y(u.value))}const g=n.querySelector(".fb-field-extras");g&&g.remove();const h=Z(u.value,e,t);h&&n.appendChild(h)});const d=Z(e.type,e,t);return d&&n.appendChild(d),n.appendChild(ve(e,t)),n}const fe=[{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"}],Q=new Set(["is_empty","is_not_empty"]);function j(e){const t=document.createElement("p");return t.textContent=e,t.style.cssText="font-size:.75rem;font-weight:700;color:var(--text-muted,#888);margin:.6rem 0 .3rem;text-transform:uppercase;letter-spacing:.04em;",t}function P(e,t,s,n,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 a=document.createElement("span");a.textContent="When",a.style.cssText="font-size:.73rem;color:var(--text-muted,#888);flex-shrink:0;";const i=document.createElement("select");i.className="form-select fb-logic-cond-field",i.style.cssText="flex:1 1 140px;min-width:140px;",t.forEach(d=>{const f=document.createElement("option");f.value=d.value,f.textContent=d.label,e&&d.value===e.field&&(f.selected=!0),i.appendChild(f)});const c=document.createElement("select");c.className="form-select fb-logic-cond-op",c.style.cssText="flex:1 1 160px;min-width:140px;",fe.forEach(d=>{const f=document.createElement("option");f.value=d.value,f.textContent=d.label,e&&d.value===e.operator&&(f.selected=!0),c.appendChild(f)});const r=document.createElement("input");r.type="text",r.className="form-input fb-logic-cond-val",r.placeholder="value",r.style.cssText="flex:1 1 120px;min-width:100px;",r.value=e?.value||"",e&&Q.has(e.operator)&&(r.style.display="none"),c.addEventListener("change",()=>{r.style.display=Q.has(c.value)?"none":""});const m=document.createElement("span");m.textContent="\u2192",m.style.cssText="font-size:.73rem;color:var(--text-muted,#888);flex-shrink:0;";const p=document.createElement("select");p.className=`form-select ${n}`,p.style.cssText="flex:1 1 140px;min-width:120px;",s.forEach(d=>{const f=document.createElement("option");f.value=d.value,f.textContent=d.label,d.value===o&&(f.selected=!0),p.appendChild(f)});const u=document.createElement("button");return u.type="button",u.className="btn btn-xs btn-danger",u.textContent="\u2715",u.style.flexShrink="0",u.addEventListener("click",()=>l.remove()),l.appendChild(a),l.appendChild(i),l.appendChild(c),l.appendChild(r),l.appendChild(m),l.appendChild(p),l.appendChild(u),l}function be(e,t,s){const n=document.createElement("div");n.dataset.logicSection="visibility",n.appendChild(j("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 a=document.createElement("select");a.className="form-select fb-logic-vis-default",a.style.cssText="max-width:240px;",[{value:"visible",label:"Visible"},{value:"hidden",label:"Hidden"}].forEach(d=>{const f=document.createElement("option");f.value=d.value,f.textContent=d.label,d.value===(e.default||"visible")&&(f.selected=!0),a.appendChild(f)}),o.appendChild(l),o.appendChild(a),n.appendChild(o);const i=document.createElement("div");i.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem;";const c=document.createElement("span");c.textContent="Transition:",c.style.cssText="font-size:.8rem;flex-shrink:0;";const r=document.createElement("select");r.className="form-select fb-logic-vis-transition",r.style.cssText="max-width:240px;",[{value:"none",label:"None (instant)"},{value:"fade",label:"Fade"},{value:"slide",label:"Slide"},{value:"scale",label:"Scale"}].forEach(d=>{const f=document.createElement("option");f.value=d.value,f.textContent=d.label,d.value===(e.transition||"none")&&(f.selected=!0),r.appendChild(f)}),i.appendChild(c),i.appendChild(r),n.appendChild(i);const m=document.createElement("div");m.className="fb-logic-vis-rules";const p=s.map(d=>({value:d.name,label:d.label||d.name})),u=[{value:"visible",label:"Show"},{value:"hidden",label:"Hide"}];if((e.conditions||[]).forEach(d=>{const f=(d.when?.all||d.when?.any||[])[0],g=d.then==="hidden"?"hidden":"visible";p.length>0&&m.appendChild(P(f,p,u,"fb-logic-vis-then",g))}),n.appendChild(m),p.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:.2rem;",d.textContent="+ Add visibility rule",d.addEventListener("click",()=>m.appendChild(P(null,p,u,"fb-logic-vis-then","visible"))),n.appendChild(d)}return n}function he(e,t,s){const n=document.createElement("div");n.dataset.logicSection="requirement",n.appendChild(j("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")),n.appendChild(o);const a=document.createElement("div");a.className="fb-logic-req-rules";const i=s.map(r=>({value:r.name,label:r.label||r.name})),c=[{value:"true",label:"Make required"},{value:"false",label:"Make optional"}];if((e.conditions||[]).forEach(r=>{const m=(r.when?.all||r.when?.any||[])[0],p=r.then===!0?"true":"false";i.length>0&&a.appendChild(P(m,i,c,"fb-logic-req-then",p))}),n.appendChild(a),i.length>0){const r=document.createElement("button");r.type="button",r.className="btn btn-xs btn-ghost",r.style.cssText="font-size:.73rem;margin-top:.2rem;",r.textContent="+ Add requirement rule",r.addEventListener("click",()=>a.appendChild(P(null,i,c,"fb-logic-req-then","true"))),n.appendChild(r)}return n}function X(e){const t=document.createElement("div");t.className="fb-logic-val-rule",t.style.cssText="display:flex;gap:.35rem;align-items:center;margin-bottom:.35rem;flex-wrap:wrap;";const s=document.createElement("select");s.className="form-select fb-logic-val-type",s.style.cssText="flex:0 0 140px;",[{value:"regex",label:"Regex"},{value:"match",label:"Match field"}].forEach(i=>{const c=document.createElement("option");c.value=i.value,c.textContent=i.label,i.value===(e?.type||"regex")&&(c.selected=!0),s.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 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"),s.addEventListener("change",()=>{o.style.display=s.value==="match"?"none":"",n.placeholder=s.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 a=document.createElement("button");return a.type="button",a.className="btn btn-xs btn-danger",a.textContent="\u2715",a.style.flexShrink="0",a.addEventListener("click",()=>t.remove()),t.appendChild(s),t.appendChild(n),t.appendChild(o),t.appendChild(l),t.appendChild(a),t}function ge(e,t,s){const n=document.createElement("div");n.dataset.logicSection="validation",n.appendChild(j("Custom Validation"));const o=document.createElement("div");o.className="fb-logic-val-rules",(e||[]).forEach(a=>o.appendChild(X(a))),n.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;",l.textContent="+ Add validation rule",l.addEventListener("click",()=>o.appendChild(X(null))),n.appendChild(l),n}function ye(e,t,s){const n=document.createElement("div");n.dataset.logicSection="cascade",n.appendChild(j("Cascade Options"));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 a=document.createElement("select");a.className="form-select fb-logic-cascade-source",a.style.cssText="flex:1;";const i=document.createElement("option");i.value="",i.textContent="\u2014 none \u2014",a.appendChild(i),s.forEach(u=>{const d=document.createElement("option");d.value=u.name,d.textContent=u.label||u.name,u.name===e.sourceField&&(d.selected=!0),a.appendChild(d)}),o.appendChild(l),o.appendChild(a),n.appendChild(o);const c=document.createElement("p");c.textContent='Mapping JSON \u2014 {"value":[{"value":"...","label":"..."}]}',c.style.cssText="font-size:.73rem;color:var(--text-muted,#888);margin:.3rem 0 .2rem;";const r=document.createElement("textarea");r.className="form-input fb-logic-cascade-mapping",r.rows=4,r.style.cssText="font-family:monospace;",r.placeholder='{"uk": [{"value": "london", "label": "London"}]}',r.value=e.mapping?JSON.stringify(e.mapping,null,2):"",n.appendChild(c),n.appendChild(r);const m=document.createElement("p");m.textContent="Default options (one per line: value:Label)",m.style.cssText="font-size:.73rem;color:var(--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
4
  option2:Option 2`,p.value=(e.defaultOptions||[]).map(u=>{const d=typeof u=="string"?u:u.value??"",f=typeof u=="string"?u:u.label??d;return d===f?d:`${d}:${f}`}).join(`
5
- `),n.appendChild(m),n.appendChild(p),n}function ye(e,t){const s=e.logic||{},n=b.filter((p,u)=>u!==t&&p.type!=="page-break"&&p.type!=="spacer"),o=document.createElement("div");o.className="fb-field-logic",o.style.cssText="margin-top:.75rem;border-top:1px solid var(--border-color,#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 a=document.createElement("span");a.style.cssText="font-size:.8rem;font-weight:600;color:var(--text-muted,#888);",a.textContent="\u26A1 Conditional Logic";const i=Y(s),c=document.createElement("button");c.type="button",c.className="btn btn-xs btn-ghost",c.textContent=i?"\u25BE":"\u25B8";const r=document.createElement("div");r.className="fb-logic-body",r.style.cssText="padding:.25rem 0 .25rem;"+(i?"":"display:none;"),l.addEventListener("click",()=>{const p=r.style.display==="none";r.style.display=p?"":"none",c.textContent=p?"\u25BE":"\u25B8"}),l.appendChild(a),l.appendChild(c),o.appendChild(l),r.appendChild(fe(s.visibility||{},t,n)),r.appendChild(be(s.requirement||{},t,n)),r.appendChild(he(s.validation||[],t,n));const m=document.getElementById(`fb-type-${t}`)?.value||e.type;return V.has(m)&&r.appendChild(ge(s.cascade||{},t,n)),o.appendChild(r),o}function X(e,t,s){const n=document.createElement("div");return n.className="fb-field-extras",e==="chooser"?(n.appendChild(ve(t,s)),n.appendChild(Ce(t.options||[],s))):(V.has(e)&&n.appendChild(Te(t.options||[],s)),e==="textarea"&&n.appendChild(v([x("Rows",`fb-rows-${s}`,"number",t.formConfig?.rows||4,"Height of textarea")])),(e==="string"||e==="textarea")&&n.appendChild(v([x("Min Length",`fb-minlength-${s}`,"number",t.minLength||"",""),x("Max Length",`fb-maxlength-${s}`,"number",t.maxLength||"","")])),e==="number"&&n.appendChild(v([x("Min",`fb-min-${s}`,"number",t.min??"",""),x("Max",`fb-max-${s}`,"number",t.max??"","")]))),n.children.length?n:null}function Z(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 s=document.createElement("div");s.style.cssText="padding:.4rem .6rem; background:color-mix(in srgb, var(--dm-bg, #f9fafb) 60%, transparent); font-weight:600; font-size:.82rem; border-bottom:1px solid var(--dm-border, #e5e7eb);",s.textContent=e,t.appendChild(s);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 j(e,t,s,n,o){const l=document.createElement("div");l.style.flex="1";const a=document.createElement("label");a.htmlFor=e,a.className="form-label",a.textContent=t,a.style.fontSize=".8rem";const i=document.createElement("select");if(i.id=e,i.className="form-input",n.forEach(([c,r])=>{const m=document.createElement("option");m.value=c,m.textContent=r,String(c)===String(s)&&(m.selected=!0),i.appendChild(m)}),l.appendChild(a),l.appendChild(i),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 ee(e,t,s,n){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 a=document.createElement("input");if(a.id=e,a.type="checkbox",a.checked=!!s,l.appendChild(a),l.appendChild(document.createTextNode(" "+t)),o.appendChild(l),n){const i=document.createElement("p");i.className="form-hint text-muted",i.textContent=n,i.style.cssText="font-size:.72rem;margin-top:.2rem;",o.appendChild(i)}return o}function H(e,t,s,n,o){const l=document.createElement("div");l.style.flex="1";const a=document.createElement("label");a.htmlFor=e,a.className="form-label",a.textContent=t,a.style.fontSize=".8rem",l.appendChild(a);const i=document.createElement("div");i.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=s||"",n&&(c.placeholder=n),c.style.flex="1";const r=document.createElement("input");r.type="color",r.title="Pick a colour",r.style.cssText="width:36px;height:auto;padding:0;border:1px solid var(--dm-border,#e5e7eb);border-radius:4px;cursor:pointer;";const m=String(c.value).match(/^#[0-9a-fA-F]{3,8}$/);if(r.value=m?c.value:"#000000",r.addEventListener("input",()=>{c.value=r.value,c.dispatchEvent(new Event("input",{bubbles:!0}))}),i.appendChild(c),i.appendChild(r),l.appendChild(i),o){const p=document.createElement("p");p.className="form-hint text-muted",p.textContent=o,p.style.cssText="font-size:.72rem;margin-top:.2rem;",l.appendChild(p)}return l}function ve(e,t){const s=document.createElement("div");s.style.cssText="margin-top:.5rem; display:flex; flex-direction:column; gap:.4rem;";const n=Z("Layout");n.body.appendChild(v([j(`fb-chooser-variant-${t}`,"Variant",e.variant||"card",[["card","Card \u2014 rich tiles"],["chip","Chip \u2014 compact pills"]],"Card supports description and grid columns; chips wrap."),ee(`fb-chooser-multiple-${t}`,"Multi-select",!!e.multiple,"Off = single (radio); on = multi (checkbox).")])),n.body.appendChild(v([j(`fb-chooser-density-${t}`,"Density",e.density||"comfortable",[["comfortable","Comfortable"],["compact","Compact"]],"Compact strips description (card variant only)."),x("Columns",`fb-chooser-columns-${t}`,"number",e.columns||3,"Card grid (1\u20136). Ignored for chips.")])),s.appendChild(n.wrap);const o=Z("Visual");return o.body.appendChild(v([H(`fb-chooser-accent-${t}`,"Accent",e.accent||"","primary / success / #ec4899","Selected highlight colour \u2014 semantic name or any CSS colour."),j(`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.")])),o.body.appendChild(v([ee(`fb-chooser-glow-${t}`,"Glow on selected",!!e.glow,"Soft outer glow."),H(`fb-chooser-glow-colour-${t}`,"Glow colour",e.glowColour||"","defaults to accent","Optional; semantic name or CSS colour.")])),o.body.appendChild(v([j(`fb-chooser-shadow-${t}`,"Shadow",e.shadow||"none",[["none","None"],["sm","Small"],["md","Medium"],["lg","Large"],["xl","Extra Large"]],"Drop shadow on every option."),H(`fb-chooser-shadow-colour-${t}`,"Shadow colour",e.shadowColour||"","rgba(0,0,0,0.1)","Optional shadow tint.")])),s.appendChild(o.wrap),s}function xe(e,t,s,n){const o=document.createElement("div");o.className="fb-chooser-option-row",o.dataset.optIdx=String(s),o.style.cssText="border:1px solid var(--dm-border,#e5e7eb); border-radius:4px; padding:.5rem; background:var(--dm-bg-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 a=document.createElement("div");a.style.cssText="display:flex; flex-direction:column; gap:1px;";const i=document.createElement("button");i.type="button",i.className="btn btn-secondary btn-sm",i.style.padding=".1rem .3rem",i.title="Move up",i.textContent="\u25B2",i.addEventListener("click",S=>{S.preventDefault(),te(t,s,-1,n)});const c=document.createElement("button");c.type="button",c.className="btn btn-secondary btn-sm",c.style.padding=".1rem .3rem",c.title="Move down",c.textContent="\u25BC",c.addEventListener("click",S=>{S.preventDefault(),te(t,s,1,n)}),a.appendChild(i),a.appendChild(c),l.appendChild(a);const r=document.createElement("input");r.type="text",r.className="form-input fb-chooser-opt-value",r.placeholder="value",r.value=e.value||"",r.style.flex="1",l.appendChild(r);const m=document.createElement("input");m.type="text",m.className="form-input fb-chooser-opt-label",m.placeholder="label (visible to users)",m.value=e.label||"",m.style.flex="2",l.appendChild(m);const p=document.createElement("button");p.type="button",p.className="btn btn-secondary btn-sm",p.title="Remove option",p.textContent="\u2715",p.addEventListener("click",S=>{S.preventDefault(),ke(t,s,n)}),l.appendChild(p),o.appendChild(l);const u=document.createElement("div");u.style.cssText="display:flex; gap:.4rem;";const d=se("icon name (optional)",e.icon||"");d.input.classList.add("fb-chooser-opt-icon"),d.el.style.flex="1",u.appendChild(d.el);const f=document.createElement("input");f.type="text",f.className="form-input fb-chooser-opt-description",f.placeholder="description (card + comfortable only)",f.value=e.description||"",f.style.flex="2",u.appendChild(f),o.appendChild(u);const g=document.createElement("div");g.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",g.appendChild(h);const y=document.createElement("input");y.type="text",y.className="form-input fb-chooser-opt-badge-text",y.placeholder="badge text",y.value=e.badge?.text||"",y.style.flex="1",g.appendChild(y);const k=document.createElement("select");k.className="form-input fb-chooser-opt-badge-type",k.style.flex="1",[["","no badge"],["primary","primary"],["success","success"],["info","info"],["warning","warning"],["danger","danger"]].forEach(([S,z])=>{const I=document.createElement("option");I.value=S,I.textContent=z,(e.badge?.type||"")===S&&(I.selected=!0),k.appendChild(I)}),g.appendChild(k),o.appendChild(g);const N=document.createElement("div");N.style.cssText="display:flex; gap:1rem; font-size:.8rem;";const q=document.createElement("label");q.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const $=document.createElement("input");$.type="checkbox",$.className="fb-chooser-opt-recommended",$.checked=!!e.recommended,q.appendChild($),q.appendChild(document.createTextNode(" Recommended")),N.appendChild(q);const L=document.createElement("label");L.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const B=document.createElement("input");return B.type="checkbox",B.className="fb-chooser-opt-disabled",B.checked=!!e.disabled,L.appendChild(B),L.appendChild(document.createTextNode(" Disabled")),N.appendChild(L),o.appendChild(N),o}function Ce(e,t){const s=document.createElement("div");s.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 o=document.createElement("p");o.textContent="Options",o.style.cssText="font-size:.85rem;font-weight:600;margin:0;",n.appendChild(o);const l=document.createElement("button");l.type="button",l.className="btn btn-secondary btn-sm",l.textContent="+ Add option",n.appendChild(l),s.appendChild(n);const a=document.createElement("div");a.className="fb-chooser-options-list",a.id=`fb-chooser-options-${t}`,a.style.cssText="display:flex; flex-direction:column; gap:.4rem;",s.appendChild(a);const i=()=>{a.replaceChildren();const c=Ee(t);(c.length?c:e||[]).forEach((m,p)=>{a.appendChild(xe(m,t,p,i))})};return l.addEventListener("click",c=>{c.preventDefault(),we(t,i)}),i(),s}function Ee(e){const t=b[e];return!t||!Array.isArray(t.options)?[]:t.options}function U(e){return b[e]?(Array.isArray(b[e].options)||(b[e].options=[]),b[e].options):[]}function we(e,t){D(e),U(e).push({value:"new",label:"New option"}),t()}function ke(e,t,s){D(e),U(e).splice(t,1),s()}function te(e,t,s,n){D(e);const o=U(e),l=t+s;if(l<0||l>=o.length)return;const[a]=o.splice(t,1);o.splice(l,0,a),n()}function D(e){const t=document.getElementById(`fb-chooser-options-${e}`);if(!t)return;const s=t.querySelectorAll(".fb-chooser-option-row"),n=[];s.forEach(o=>{const l={value:o.querySelector(".fb-chooser-opt-value")?.value.trim()||"",label:o.querySelector(".fb-chooser-opt-label")?.value.trim()||""},a=o.querySelector(".fb-chooser-opt-icon")?.value.trim();a&&(l.icon=a);const i=o.querySelector(".fb-chooser-opt-description")?.value.trim();i&&(l.description=i);const c=o.querySelector(".fb-chooser-opt-tooltip")?.value.trim();c&&(l.tooltip=c);const r=o.querySelector(".fb-chooser-opt-badge-text")?.value.trim(),m=o.querySelector(".fb-chooser-opt-badge-type")?.value;(r||m)&&(l.badge={},r&&(l.badge.text=r),m&&(l.badge.type=m)),o.querySelector(".fb-chooser-opt-recommended")?.checked&&(l.recommended=!0),o.querySelector(".fb-chooser-opt-disabled")?.checked&&(l.disabled=!0),n.push(l)}),b[e]&&(b[e].options=n)}function v(e){const t=document.createElement("div");return t.style.cssText="display:flex;gap:.75rem;margin-bottom:.6rem;",e.forEach(s=>{s&&t.appendChild(s)}),t}function x(e,t,s,n,o){const l=document.createElement("div");l.style.flex="1";const a=document.createElement("label");a.htmlFor=t,a.className="form-label",a.textContent=e,a.style.fontSize=".8rem";const i=document.createElement("input");if(i.id=t,i.type=s||"text",i.className="form-input",i.value=n??"",l.appendChild(a),l.appendChild(i),o){const c=document.createElement("p");c.className="form-hint text-muted",c.textContent=o,c.style.cssText="font-size:.73rem;margin-top:.2rem;",l.appendChild(c)}return l}function Se(e,t,s,n){const o=document.createElement("div");o.style.flex="1";const l=document.createElement("label");l.htmlFor=t,l.className="form-label",l.textContent=e,l.style.fontSize=".8rem";const a=document.createElement("select");return a.id=t,a.className="form-select",s.forEach(i=>{const c=document.createElement("option");c.value=i.value,c.textContent=i.label,i.value===n&&(c.selected=!0),a.appendChild(c)}),o.appendChild(l),o.appendChild(a),o}function ne(e,t,s){const n=document.createElement("div");n.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=t,l.type="checkbox",l.checked=s,o.appendChild(l),o.appendChild(document.createTextNode(e)),n.appendChild(o),n}function Te(e,t){const s=document.createElement("div");s.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 o=document.createElement("textarea");return o.id=`fb-options-${t}`,o.className="form-input",o.rows=4,o.placeholder=`yes:Yes
5
+ `),n.appendChild(m),n.appendChild(p),n}function ve(e,t){const s=e.logic||{},n=b.filter((p,u)=>u!==t&&p.type!=="page-break"&&p.type!=="spacer"),o=document.createElement("div");o.className="fb-field-logic",o.style.cssText="margin-top:.75rem;border-top:1px solid var(--border-color,#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 a=document.createElement("span");a.style.cssText="font-size:.8rem;font-weight:600;color:var(--text-muted,#888);",a.textContent="\u26A1 Conditional Logic";const i=K(s),c=document.createElement("button");c.type="button",c.className="btn btn-xs btn-ghost",c.textContent=i?"\u25BE":"\u25B8";const r=document.createElement("div");r.className="fb-logic-body",r.style.cssText="padding:.25rem 0 .25rem;"+(i?"":"display:none;"),l.addEventListener("click",()=>{const p=r.style.display==="none";r.style.display=p?"":"none",c.textContent=p?"\u25BE":"\u25B8"}),l.appendChild(a),l.appendChild(c),o.appendChild(l),r.appendChild(be(s.visibility||{},t,n)),r.appendChild(he(s.requirement||{},t,n)),r.appendChild(ge(s.validation||[],t,n));const m=document.getElementById(`fb-type-${t}`)?.value||e.type;return V.has(m)&&r.appendChild(ye(s.cascade||{},t,n)),o.appendChild(r),o}function Z(e,t,s){const n=document.createElement("div");return n.className="fb-field-extras",e==="chooser"?(n.appendChild(xe(t,s)),n.appendChild(Ee(t.options||[],s))):(V.has(e)&&n.appendChild(Ne(t.options||[],s)),e==="textarea"&&n.appendChild(v([x("Rows",`fb-rows-${s}`,"number",t.formConfig?.rows||4,"Height of textarea")])),(e==="string"||e==="textarea")&&n.appendChild(v([x("Min Length",`fb-minlength-${s}`,"number",t.minLength||"",""),x("Max Length",`fb-maxlength-${s}`,"number",t.maxLength||"","")])),e==="number"&&n.appendChild(v([x("Min",`fb-min-${s}`,"number",t.min??"",""),x("Max",`fb-max-${s}`,"number",t.max??"","")]))),n.children.length?n:null}function ee(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 s=document.createElement("div");s.style.cssText="padding:.4rem .6rem; background:color-mix(in srgb, var(--dm-bg, #f9fafb) 60%, transparent); font-weight:600; font-size:.82rem; border-bottom:1px solid var(--dm-border, #e5e7eb);",s.textContent=e,t.appendChild(s);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 _(e,t,s,n,o){const l=document.createElement("div");l.style.flex="1";const a=document.createElement("label");a.htmlFor=e,a.className="form-label",a.textContent=t,a.style.fontSize=".8rem";const i=document.createElement("select");if(i.id=e,i.className="form-input",n.forEach(([c,r])=>{const m=document.createElement("option");m.value=c,m.textContent=r,String(c)===String(s)&&(m.selected=!0),i.appendChild(m)}),l.appendChild(a),l.appendChild(i),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 te(e,t,s,n){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 a=document.createElement("input");if(a.id=e,a.type="checkbox",a.checked=!!s,l.appendChild(a),l.appendChild(document.createTextNode(" "+t)),o.appendChild(l),n){const i=document.createElement("p");i.className="form-hint text-muted",i.textContent=n,i.style.cssText="font-size:.72rem;margin-top:.2rem;",o.appendChild(i)}return o}function U(e,t,s,n,o){const l=document.createElement("div");l.style.flex="1";const a=document.createElement("label");a.htmlFor=e,a.className="form-label",a.textContent=t,a.style.fontSize=".8rem",l.appendChild(a);const i=document.createElement("div");i.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=s||"",n&&(c.placeholder=n),c.style.flex="1";const r=document.createElement("input");r.type="color",r.title="Pick a colour",r.style.cssText="width:36px;height:auto;padding:0;border:1px solid var(--dm-border,#e5e7eb);border-radius:4px;cursor:pointer;";const m=String(c.value).match(/^#[0-9a-fA-F]{3,8}$/);if(r.value=m?c.value:"#000000",r.addEventListener("input",()=>{c.value=r.value,c.dispatchEvent(new Event("input",{bubbles:!0}))}),i.appendChild(c),i.appendChild(r),l.appendChild(i),o){const p=document.createElement("p");p.className="form-hint text-muted",p.textContent=o,p.style.cssText="font-size:.72rem;margin-top:.2rem;",l.appendChild(p)}return l}function xe(e,t){const s=document.createElement("div");s.style.cssText="margin-top:.5rem; display:flex; flex-direction:column; gap:.4rem;";const n=ee("Layout");n.body.appendChild(v([_(`fb-chooser-variant-${t}`,"Variant",e.variant||"card",[["card","Card \u2014 rich tiles"],["chip","Chip \u2014 compact pills"]],"Card supports description and grid columns; chips wrap."),te(`fb-chooser-multiple-${t}`,"Multi-select",!!e.multiple,"Off = single (radio); on = multi (checkbox).")])),n.body.appendChild(v([_(`fb-chooser-density-${t}`,"Density",e.density||"comfortable",[["comfortable","Comfortable"],["compact","Compact"]],"Compact strips description (card variant only)."),x("Columns",`fb-chooser-columns-${t}`,"number",e.columns||3,"Card grid (1\u20136). Ignored for chips.")])),s.appendChild(n.wrap);const o=ee("Visual");return o.body.appendChild(v([U(`fb-chooser-accent-${t}`,"Accent",e.accent||"","primary / success / #ec4899","Selected highlight colour \u2014 semantic name or any CSS colour."),_(`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.")])),o.body.appendChild(v([te(`fb-chooser-glow-${t}`,"Glow on selected",!!e.glow,"Soft outer glow."),U(`fb-chooser-glow-colour-${t}`,"Glow colour",e.glowColour||"","defaults to accent","Optional; semantic name or CSS colour.")])),o.body.appendChild(v([_(`fb-chooser-shadow-${t}`,"Shadow",e.shadow||"none",[["none","None"],["sm","Small"],["md","Medium"],["lg","Large"],["xl","Extra Large"]],"Drop shadow on every option."),U(`fb-chooser-shadow-colour-${t}`,"Shadow colour",e.shadowColour||"","rgba(0,0,0,0.1)","Optional shadow tint.")])),s.appendChild(o.wrap),s}function Ce(e,t,s,n){const o=document.createElement("div");o.className="fb-chooser-option-row",o.dataset.optIdx=String(s),o.style.cssText="border:1px solid var(--dm-border,#e5e7eb); border-radius:4px; padding:.5rem; background:var(--dm-bg-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 a=document.createElement("div");a.style.cssText="display:flex; flex-direction:column; gap:1px;";const i=document.createElement("button");i.type="button",i.className="btn btn-secondary btn-sm",i.style.padding=".1rem .3rem",i.title="Move up",i.textContent="\u25B2",i.addEventListener("click",S=>{S.preventDefault(),ne(t,s,-1,n)});const c=document.createElement("button");c.type="button",c.className="btn btn-secondary btn-sm",c.style.padding=".1rem .3rem",c.title="Move down",c.textContent="\u25BC",c.addEventListener("click",S=>{S.preventDefault(),ne(t,s,1,n)}),a.appendChild(i),a.appendChild(c),l.appendChild(a);const r=document.createElement("input");r.type="text",r.className="form-input fb-chooser-opt-value",r.placeholder="value",r.value=e.value||"",r.style.flex="1",l.appendChild(r);const m=document.createElement("input");m.type="text",m.className="form-input fb-chooser-opt-label",m.placeholder="label (visible to users)",m.value=e.label||"",m.style.flex="2",l.appendChild(m);const p=document.createElement("button");p.type="button",p.className="btn btn-secondary btn-sm",p.title="Remove option",p.textContent="\u2715",p.addEventListener("click",S=>{S.preventDefault(),Se(t,s,n)}),l.appendChild(p),o.appendChild(l);const u=document.createElement("div");u.style.cssText="display:flex; gap:.4rem;";const d=ie("icon name (optional)",e.icon||"");d.input.classList.add("fb-chooser-opt-icon"),d.el.style.flex="1",u.appendChild(d.el);const f=document.createElement("input");f.type="text",f.className="form-input fb-chooser-opt-description",f.placeholder="description (card + comfortable only)",f.value=e.description||"",f.style.flex="2",u.appendChild(f),o.appendChild(u);const g=document.createElement("div");g.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",g.appendChild(h);const y=document.createElement("input");y.type="text",y.className="form-input fb-chooser-opt-badge-text",y.placeholder="badge text",y.value=e.badge?.text||"",y.style.flex="1",g.appendChild(y);const k=document.createElement("select");k.className="form-input fb-chooser-opt-badge-type",k.style.flex="1",[["","no badge"],["primary","primary"],["success","success"],["info","info"],["warning","warning"],["danger","danger"]].forEach(([S,I])=>{const A=document.createElement("option");A.value=S,A.textContent=I,(e.badge?.type||"")===S&&(A.selected=!0),k.appendChild(A)}),g.appendChild(k),o.appendChild(g);const N=document.createElement("div");N.style.cssText="display:flex; gap:1rem; font-size:.8rem;";const q=document.createElement("label");q.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const $=document.createElement("input");$.type="checkbox",$.className="fb-chooser-opt-recommended",$.checked=!!e.recommended,q.appendChild($),q.appendChild(document.createTextNode(" Recommended")),N.appendChild(q);const L=document.createElement("label");L.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const B=document.createElement("input");return B.type="checkbox",B.className="fb-chooser-opt-disabled",B.checked=!!e.disabled,L.appendChild(B),L.appendChild(document.createTextNode(" Disabled")),N.appendChild(L),o.appendChild(N),o}function Ee(e,t){const s=document.createElement("div");s.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 o=document.createElement("p");o.textContent="Options",o.style.cssText="font-size:.85rem;font-weight:600;margin:0;",n.appendChild(o);const l=document.createElement("button");l.type="button",l.className="btn btn-secondary btn-sm",l.textContent="+ Add option",n.appendChild(l),s.appendChild(n);const a=document.createElement("div");a.className="fb-chooser-options-list",a.id=`fb-chooser-options-${t}`,a.style.cssText="display:flex; flex-direction:column; gap:.4rem;",s.appendChild(a);const i=()=>{a.replaceChildren();const c=we(t);(c.length?c:e||[]).forEach((m,p)=>{a.appendChild(Ce(m,t,p,i))})};return l.addEventListener("click",c=>{c.preventDefault(),ke(t,i)}),i(),s}function we(e){const t=b[e];return!t||!Array.isArray(t.options)?[]:t.options}function J(e){return b[e]?(Array.isArray(b[e].options)||(b[e].options=[]),b[e].options):[]}function ke(e,t){D(e),J(e).push({value:"new",label:"New option"}),t()}function Se(e,t,s){D(e),J(e).splice(t,1),s()}function ne(e,t,s,n){D(e);const o=J(e),l=t+s;if(l<0||l>=o.length)return;const[a]=o.splice(t,1);o.splice(l,0,a),n()}function D(e){const t=document.getElementById(`fb-chooser-options-${e}`);if(!t)return;const s=t.querySelectorAll(".fb-chooser-option-row"),n=[];s.forEach(o=>{const l={value:o.querySelector(".fb-chooser-opt-value")?.value.trim()||"",label:o.querySelector(".fb-chooser-opt-label")?.value.trim()||""},a=o.querySelector(".fb-chooser-opt-icon")?.value.trim();a&&(l.icon=a);const i=o.querySelector(".fb-chooser-opt-description")?.value.trim();i&&(l.description=i);const c=o.querySelector(".fb-chooser-opt-tooltip")?.value.trim();c&&(l.tooltip=c);const r=o.querySelector(".fb-chooser-opt-badge-text")?.value.trim(),m=o.querySelector(".fb-chooser-opt-badge-type")?.value;(r||m)&&(l.badge={},r&&(l.badge.text=r),m&&(l.badge.type=m)),o.querySelector(".fb-chooser-opt-recommended")?.checked&&(l.recommended=!0),o.querySelector(".fb-chooser-opt-disabled")?.checked&&(l.disabled=!0),n.push(l)}),b[e]&&(b[e].options=n)}function v(e){const t=document.createElement("div");return t.style.cssText="display:flex;gap:.75rem;margin-bottom:.6rem;",e.forEach(s=>{s&&t.appendChild(s)}),t}function x(e,t,s,n,o){const l=document.createElement("div");l.style.flex="1";const a=document.createElement("label");a.htmlFor=t,a.className="form-label",a.textContent=e,a.style.fontSize=".8rem";const i=document.createElement("input");if(i.id=t,i.type=s||"text",i.className="form-input",i.value=n??"",l.appendChild(a),l.appendChild(i),o){const c=document.createElement("p");c.className="form-hint text-muted",c.textContent=o,c.style.cssText="font-size:.73rem;margin-top:.2rem;",l.appendChild(c)}return l}function Te(e,t,s,n){const o=document.createElement("div");o.style.flex="1";const l=document.createElement("label");l.htmlFor=t,l.className="form-label",l.textContent=e,l.style.fontSize=".8rem";const a=document.createElement("select");return a.id=t,a.className="form-select",s.forEach(i=>{const c=document.createElement("option");c.value=i.value,c.textContent=i.label,i.value===n&&(c.selected=!0),a.appendChild(c)}),o.appendChild(l),o.appendChild(a),o}function le(e,t,s){const n=document.createElement("div");n.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=t,l.type="checkbox",l.checked=s,o.appendChild(l),o.appendChild(document.createTextNode(e)),n.appendChild(o),n}function Ne(e,t){const s=document.createElement("div");s.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 o=document.createElement("textarea");return o.id=`fb-options-${t}`,o.className="form-input",o.rows=4,o.placeholder=`yes:Yes
6
6
  no:No
7
7
  maybe:Maybe`,o.value=(e||[]).map(l=>{const a=typeof l=="string"?l:l.value??"",i=typeof l=="string"?l:l.label??a;return a===i?a:`${a}:${i}`}).join(`
8
- `),o.style.fontFamily="monospace",s.appendChild(n),s.appendChild(o),s}function le(e){return C(),{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}:{},fields:b,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()},...A!==null&&{collection:A}}}}function Ne(e){const t=[];let s=[],n="Step 1",o="";return e.forEach(l=>{l.type==="page-break"?(t.push({title:n,description:o,fields:s}),s=[],n=l.label||`Step ${t.length+1}`,o=l.description||""):l.type!=="spacer"&&s.push(l)}),(s.length||t.length===0)&&t.push({title:n,description:o,fields:s}),t}function oe(e,t){const s={};return e.forEach(n=>{if(n.type==="page-break"||n.type==="spacer")return;const o={...n.formConfig||{}};o.span==="full"&&t&&(o.span=t);const a={type:n.type==="checkbox"?"boolean":n.type==="date"?"string":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},...o}};n.type==="chooser"&&(n.variant&&(a.variant=n.variant),n.multiple&&(a.multiple=!0),n.density&&(a.density=n.density),n.columns&&(a.columns=n.columns),n.accent&&(a.accent=n.accent),n.accentStyle&&(a.accentStyle=n.accentStyle),n.glow&&(a.glow=!0),n.glowColour&&(a.glowColour=n.glowColour),n.shadow&&(a.shadow=n.shadow),n.shadowColour&&(a.shadowColour=n.shadowColour)),s[n.name]=a}),s}function ae(e,t){const s=typeof e=="string"?document.querySelector(e):e;s&&(t||[]).forEach(n=>{if(n.type!=="date"||!n.name)return;const o=s.querySelector(`[name="${n.name}"]`);o&&o.type!=="date"&&(o.type="date")})}export const formEditorView={templateUrl:"/admin/js/templates/form-editor.html",async onMount(e){b=[],T=null,A=null;const t=window.location.hash.match(/\/forms\/edit\/([^/?#]+)/);T=t?t[1]:null;let s=null;if(T)try{s=await O(`/forms/${T}`),b=s.fields||[],A=s.actions?.collection??null}catch{E.toast("Could not load form.",{type:"error"})}if(s?e.find("#editor-title").get(0).textContent=`Edit: ${s.title}`:e.find("#editor-title").get(0).textContent="New Form",T||e.find("#field-title").get(0).addEventListener("input",function(){e.find("#field-slug").val(G(this.value))}),E.tabs(e.find("#editor-tabs").get(0)),s){e.find("#field-title").val(s.title),e.find("#field-slug").val(s.slug),e.find("#field-description").val(s.description||""),e.find("#field-bundled").prop("checked",!!s.bundled);const l=s.settings||{};e.find("#setting-submit-text").val(l.submitText||"Submit"),e.find("#setting-success-message").val(l.successMessage||""),e.find("#setting-layout").val(l.layout||"stacked"),e.find("#setting-columns").val(l.columns||2),e.find("#setting-submit-span").val(l.submitSpan||""),e.find("#columns-group").get(0).style.display=l.layout==="grid"?"":"none",e.find("#setting-honeypot").prop("checked",l.honeypot!==!1),e.find("#setting-rate-limit").val(l.rateLimitPerMinute||3),e.find("#setting-success-redirect").val(l.successRedirect||"");const a=s.actions?.email||{};e.find("#action-email-enabled").prop("checked",a.enabled||!1),e.find("#action-email-recipients").val(a.recipients||""),e.find("#action-email-subject-prefix").val(a.subjectPrefix||"");const i=s.actions?.webhook||{};e.find("#action-webhook-enabled").prop("checked",i.enabled||!1),e.find("#action-webhook-url").val(i.url||""),e.find("#action-webhook-method").val(i.method||"POST")}const n=s?.settings?.actionSlug||"";try{const l=await O("/actions").catch(()=>[]),a=e.find("#action-cms-slug").get(0);if((Array.isArray(l)?l:[]).forEach(i=>{const c=document.createElement("option");c.value=i.slug,c.textContent=i.title||i.slug,i.slug===n&&(c.selected=!0),a.appendChild(c)}),!l.length){const i=document.createElement("option");i.value="",i.textContent="No actions available",i.disabled=!0,a.appendChild(i)}}catch{}e.find("#setting-layout").get(0)?.addEventListener("change",function(){const l=this.value==="grid";e.find("#columns-group").get(0).style.display=l?"":"none",document.querySelectorAll(".fb-grid-row").forEach(a=>{a.style.display=l?"flex":"none"})}),w(e);const o=e.find("#add-element-menu").get(0);e.find("#add-element-btn").get(0).addEventListener("click",l=>{l.stopPropagation(),o.style.display=o.style.display==="none"?"":"none"}),M&&document.removeEventListener("click",M),M=()=>{o&&(o.style.display="none")},document.addEventListener("click",M),e.find("#add-field-btn").get(0).addEventListener("click",()=>{o&&(o.style.display="none"),C();const l=b.length;b.push({name:`field_${l+1}`,type:"string",label:"New Field",required:!1,placeholder:""}),w(e);const a=e.find("#fields-list").get(0)?.lastElementChild;if(a){const i=a.querySelector(".fb-field-body");i&&(i.style.display="")}}),e.find("#add-spacer-btn").get(0).addEventListener("click",()=>{o&&(o.style.display="none"),C(),b.push({type:"spacer"}),w(e)}),e.find("#add-page-break-btn").get(0).addEventListener("click",()=>{o&&(o.style.display="none"),C();const l=b.filter(a=>a.type==="page-break").length+2;b.push({type:"page-break",label:`Step ${l}`,description:""}),w(e)}),e.find("#save-form-btn").get(0).addEventListener("click",async()=>{const l=le(e);if(!l.title){E.toast("Please enter a form title.",{type:"error"});return}try{T?(await O(`/forms/${T}`,{method:"PUT",body:JSON.stringify(l)}),E.toast("Form saved.",{type:"success"})):(T=(await O("/forms",{method:"POST",body:JSON.stringify(l)})).slug,R.navigate(`/forms/edit/${T}`),E.toast("Form created.",{type:"success"}))}catch(a){E.toast(a.message||"Failed to save form.",{type:"error"})}}),e.find("#preview-btn").get(0).addEventListener("click",()=>{const l=le(e),a=e.find("#preview-container").get(0);if(!a)return;const i=e.find("#preview-test-result").get(0),c=e.find("#preview-test-badge").get(0);i&&(i.style.display="none",i.textContent=""),c&&(c.style.display=T?"":"none"),e.find("#preview-card").get(0).style.display="",a.textContent="";const r=document.createElement("div");r.id="fb-preview-form",a.appendChild(r);const m=T?async u=>{i&&(i.style.display="none",i.textContent="");try{const d=await fetch(`/api/forms/submit/${T}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(u)}),f=await d.json();if(!d.ok)throw new Error(f.error||"Submission failed.");i&&(i.textContent=f.message||l.settings?.successMessage||"Submitted successfully.",i.style.cssText="display:block;margin-top:.75rem;padding:.6rem .9rem;border-radius:6px;font-size:.9rem;background:rgba(34,197,94,.12);color:var(--success,#22c55e);"),E.toast("Test submission stored.",{type:"success"})}catch(d){i&&(i.textContent=d.message,i.style.cssText="display:block;margin-top:.75rem;padding:.6rem .9rem;border-radius:6px;font-size:.9rem;background:rgba(239,68,68,.12);color:var(--danger,#ef4444);"),E.toast(d.message,{type:"error"})}return!1}:()=>!1,p=l.fields.some(u=>u.type==="page-break");if(typeof F<"u"){const u=l.settings?.columns||2,d=l.settings?.layout||"stacked";if(p&&F.wizard){const f=Ne(l.fields).map(g=>({title:g.title,description:g.description,fields:oe(g.fields,u)}));F.wizard("#fb-preview-form",{schema:{steps:f},onSubmit:m}),ae("#fb-preview-form",l.fields)}else if(F.render){const f=oe(l.fields,u),g={};if(l.fields.forEach(h=>{if(!(!h.name||h.type==="page-break"||h.type==="spacer")&&(h.type==="select"||h.type==="multiselect")&&h.required){const y=(h.options||[])[0];y&&(g[h.name]=typeof y=="object"?y.value:y)}}),F.render("#fb-preview-form",f,g,{submitText:l.settings?.submitText||"Submit",layout:d,columns:u,onSubmit:m}),d==="grid"&&l.settings?.submitSpan==="full"){const h=document.querySelector("#fb-preview-form .form-buttons");h&&h.classList.add("col-span-full")}ae("#fb-preview-form",l.fields)}window.FormLogicEngine&&l.fields.some(f=>f.logic)&&requestAnimationFrame(()=>{new window.FormLogicEngine.FormLogicRuntime({fields:l.fields},r).init()})}else{const u=document.createElement("p");u.textContent=`${l.fields.filter(d=>d.type!=="page-break").length} field(s): ${l.fields.filter(d=>d.type!=="page-break").map(d=>d.label).join(", ")}`,u.style.cssText="color:var(--muted);font-style:italic;",r.appendChild(u)}e.find("#preview-card").get(0).scrollIntoView({behavior:"smooth",block:"start"})})}};
8
+ `),o.style.fontFamily="monospace",s.appendChild(n),s.appendChild(o),s}function oe(e){const t=e.find("#field-project").val()||"";return C(),{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:{...H||{},project:t||null},fields:b,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()},...O!==null&&{collection:O}}}}function qe(e){const t=[];let s=[],n="Step 1",o="";return e.forEach(l=>{l.type==="page-break"?(t.push({title:n,description:o,fields:s}),s=[],n=l.label||`Step ${t.length+1}`,o=l.description||""):l.type!=="spacer"&&s.push(l)}),(s.length||t.length===0)&&t.push({title:n,description:o,fields:s}),t}function ae(e,t){const s={};return e.forEach(n=>{if(n.type==="page-break"||n.type==="spacer")return;const o={...n.formConfig||{}};o.span==="full"&&t&&(o.span=t);const a={type:n.type==="checkbox"?"boolean":n.type==="date"?"string":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},...o}};n.type==="chooser"&&(n.variant&&(a.variant=n.variant),n.multiple&&(a.multiple=!0),n.density&&(a.density=n.density),n.columns&&(a.columns=n.columns),n.accent&&(a.accent=n.accent),n.accentStyle&&(a.accentStyle=n.accentStyle),n.glow&&(a.glow=!0),n.glowColour&&(a.glowColour=n.glowColour),n.shadow&&(a.shadow=n.shadow),n.shadowColour&&(a.shadowColour=n.shadowColour)),s[n.name]=a}),s}function se(e,t){const s=typeof e=="string"?document.querySelector(e):e;s&&(t||[]).forEach(n=>{if(n.type!=="date"||!n.name)return;const o=s.querySelector(`[name="${n.name}"]`);o&&o.type!=="date"&&(o.type="date")})}export const formEditorView={templateUrl:"/admin/js/templates/form-editor.html",async onMount(e){b=[],T=null,O=null;const t=window.location.hash.match(/\/forms\/edit\/([^/?#]+)/);T=t?t[1]:null;let s=null;if(T)try{s=await z(`/forms/${T}`),b=s.fields||[],O=s.actions?.collection??null}catch{E.toast("Could not load form.",{type:"error"})}H={};try{const l=await z("/projects").catch(()=>[]),a=e.find("#field-project").get(0);a&&(Array.isArray(l)?l:[]).forEach(i=>{const c=document.createElement("option");c.value=i.slug,c.textContent=i.name||i.slug,a.appendChild(c)})}catch{}if(s?e.find("#editor-title").get(0).textContent=`Edit: ${s.title}`:e.find("#editor-title").get(0).textContent="New Form",T||e.find("#field-title").get(0).addEventListener("input",function(){e.find("#field-slug").val(W(this.value))}),E.tabs(e.find("#editor-tabs").get(0)),s){e.find("#field-title").val(s.title),e.find("#field-slug").val(s.slug),e.find("#field-description").val(s.description||""),e.find("#field-bundled").prop("checked",!!s.bundled),H=s.meta||{},e.find("#field-project").val(s.meta?.project||"");const l=s.settings||{};e.find("#setting-submit-text").val(l.submitText||"Submit"),e.find("#setting-success-message").val(l.successMessage||""),e.find("#setting-layout").val(l.layout||"stacked"),e.find("#setting-columns").val(l.columns||2),e.find("#setting-submit-span").val(l.submitSpan||""),e.find("#columns-group").get(0).style.display=l.layout==="grid"?"":"none",e.find("#setting-honeypot").prop("checked",l.honeypot!==!1),e.find("#setting-rate-limit").val(l.rateLimitPerMinute||3),e.find("#setting-success-redirect").val(l.successRedirect||"");const a=s.actions?.email||{};e.find("#action-email-enabled").prop("checked",a.enabled||!1),e.find("#action-email-recipients").val(a.recipients||""),e.find("#action-email-subject-prefix").val(a.subjectPrefix||"");const i=s.actions?.webhook||{};e.find("#action-webhook-enabled").prop("checked",i.enabled||!1),e.find("#action-webhook-url").val(i.url||""),e.find("#action-webhook-method").val(i.method||"POST")}const n=s?.settings?.actionSlug||"";try{const l=await z("/actions").catch(()=>[]),a=e.find("#action-cms-slug").get(0);if((Array.isArray(l)?l:[]).forEach(i=>{const c=document.createElement("option");c.value=i.slug,c.textContent=i.title||i.slug,i.slug===n&&(c.selected=!0),a.appendChild(c)}),!l.length){const i=document.createElement("option");i.value="",i.textContent="No actions available",i.disabled=!0,a.appendChild(i)}}catch{}e.find("#setting-layout").get(0)?.addEventListener("change",function(){const l=this.value==="grid";e.find("#columns-group").get(0).style.display=l?"":"none",document.querySelectorAll(".fb-grid-row").forEach(a=>{a.style.display=l?"flex":"none"})}),w(e);const o=e.find("#add-element-menu").get(0);e.find("#add-element-btn").get(0).addEventListener("click",l=>{l.stopPropagation(),o.style.display=o.style.display==="none"?"":"none"}),M&&document.removeEventListener("click",M),M=()=>{o&&(o.style.display="none")},document.addEventListener("click",M),e.find("#add-field-btn").get(0).addEventListener("click",()=>{o&&(o.style.display="none"),C();const l=b.length;b.push({name:`field_${l+1}`,type:"string",label:"New Field",required:!1,placeholder:""}),w(e);const a=e.find("#fields-list").get(0)?.lastElementChild;if(a){const i=a.querySelector(".fb-field-body");i&&(i.style.display="")}}),e.find("#add-spacer-btn").get(0).addEventListener("click",()=>{o&&(o.style.display="none"),C(),b.push({type:"spacer"}),w(e)}),e.find("#add-page-break-btn").get(0).addEventListener("click",()=>{o&&(o.style.display="none"),C();const l=b.filter(a=>a.type==="page-break").length+2;b.push({type:"page-break",label:`Step ${l}`,description:""}),w(e)}),e.find("#save-form-btn").get(0).addEventListener("click",async()=>{const l=oe(e);if(!l.title){E.toast("Please enter a form title.",{type:"error"});return}try{T?(await z(`/forms/${T}`,{method:"PUT",body:JSON.stringify(l)}),E.toast("Form saved.",{type:"success"})):(T=(await z("/forms",{method:"POST",body:JSON.stringify(l)})).slug,R.navigate(`/forms/edit/${T}`),E.toast("Form created.",{type:"success"}))}catch(a){E.toast(a.message||"Failed to save form.",{type:"error"})}}),e.find("#preview-btn").get(0).addEventListener("click",()=>{const l=oe(e),a=e.find("#preview-container").get(0);if(!a)return;const i=e.find("#preview-test-result").get(0),c=e.find("#preview-test-badge").get(0);i&&(i.style.display="none",i.textContent=""),c&&(c.style.display=T?"":"none"),e.find("#preview-card").get(0).style.display="",a.textContent="";const r=document.createElement("div");r.id="fb-preview-form",a.appendChild(r);const m=T?async u=>{i&&(i.style.display="none",i.textContent="");try{const d=await fetch(`/api/forms/submit/${T}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(u)}),f=await d.json();if(!d.ok)throw new Error(f.error||"Submission failed.");i&&(i.textContent=f.message||l.settings?.successMessage||"Submitted successfully.",i.style.cssText="display:block;margin-top:.75rem;padding:.6rem .9rem;border-radius:6px;font-size:.9rem;background:rgba(34,197,94,.12);color:var(--success,#22c55e);"),E.toast("Test submission stored.",{type:"success"})}catch(d){i&&(i.textContent=d.message,i.style.cssText="display:block;margin-top:.75rem;padding:.6rem .9rem;border-radius:6px;font-size:.9rem;background:rgba(239,68,68,.12);color:var(--danger,#ef4444);"),E.toast(d.message,{type:"error"})}return!1}:()=>!1,p=l.fields.some(u=>u.type==="page-break");if(typeof F<"u"){const u=l.settings?.columns||2,d=l.settings?.layout||"stacked";if(p&&F.wizard){const f=qe(l.fields).map(g=>({title:g.title,description:g.description,fields:ae(g.fields,u)}));F.wizard("#fb-preview-form",{schema:{steps:f},onSubmit:m}),se("#fb-preview-form",l.fields)}else if(F.render){const f=ae(l.fields,u),g={};if(l.fields.forEach(h=>{if(!(!h.name||h.type==="page-break"||h.type==="spacer")&&(h.type==="select"||h.type==="multiselect")&&h.required){const y=(h.options||[])[0];y&&(g[h.name]=typeof y=="object"?y.value:y)}}),F.render("#fb-preview-form",f,g,{submitText:l.settings?.submitText||"Submit",layout:d,columns:u,onSubmit:m}),d==="grid"&&l.settings?.submitSpan==="full"){const h=document.querySelector("#fb-preview-form .form-buttons");h&&h.classList.add("col-span-full")}se("#fb-preview-form",l.fields)}window.FormLogicEngine&&l.fields.some(f=>f.logic)&&requestAnimationFrame(()=>{new window.FormLogicEngine.FormLogicRuntime({fields:l.fields},r).init()})}else{const u=document.createElement("p");u.textContent=`${l.fields.filter(d=>d.type!=="page-break").length} field(s): ${l.fields.filter(d=>d.type!=="page-break").map(d=>d.label).join(", ")}`,u.style.cssText="color:var(--muted);font-style:italic;",r.appendChild(u)}e.find("#preview-card").get(0).scrollIntoView({behavior:"smooth",block:"start"})})}};