domma-cms 0.3.0 → 0.5.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 (145) hide show
  1. package/README.md +3 -3
  2. package/admin/css/admin.css +1 -1
  3. package/admin/dist/domma/domma-tools.css +2313 -0
  4. package/admin/dist/domma/domma-tools.min.js +10 -0
  5. package/admin/index.html +4 -0
  6. package/admin/js/api.js +1 -1
  7. package/admin/js/app.js +8 -4
  8. package/admin/js/config/sidebar-config.js +1 -1
  9. package/admin/js/lib/markdown-toolbar.js +18 -10
  10. package/admin/js/templates/action-editor.html +171 -0
  11. package/admin/js/templates/actions-list.html +19 -0
  12. package/admin/js/templates/api-reference.html +1411 -0
  13. package/admin/js/templates/block-editor.html +158 -0
  14. package/admin/js/templates/blocks.html +8 -0
  15. package/admin/js/templates/collection-editor.html +47 -0
  16. package/admin/js/templates/collection-entries.html +3 -0
  17. package/admin/js/templates/collections.html +51 -4
  18. package/admin/js/templates/documentation.html +258 -0
  19. package/{plugins/form-builder/admin → admin/js}/templates/form-editor.html +238 -199
  20. package/{plugins/form-builder/admin → admin/js}/templates/form-submissions.html +30 -30
  21. package/{plugins/form-builder/admin/templates/forms-list.html → admin/js/templates/forms.html} +17 -17
  22. package/admin/js/templates/login.html +29 -4
  23. package/admin/js/templates/my-profile.html +17 -0
  24. package/admin/js/templates/page-editor.html +39 -0
  25. package/admin/js/templates/pages.html +6 -1
  26. package/admin/js/templates/pro-docs.html +259 -0
  27. package/admin/js/templates/role-editor.html +59 -0
  28. package/admin/js/templates/roles.html +10 -0
  29. package/admin/js/templates/settings.html +123 -21
  30. package/admin/js/templates/tutorials.html +81 -0
  31. package/admin/js/templates/user-editor.html +7 -0
  32. package/admin/js/templates/users.html +3 -26
  33. package/admin/js/templates/view-editor.html +201 -0
  34. package/admin/js/templates/view-preview.html +51 -0
  35. package/admin/js/templates/views-list.html +19 -0
  36. package/admin/js/views/action-editor.js +1 -0
  37. package/admin/js/views/actions-list.js +1 -0
  38. package/admin/js/views/api-reference.js +1 -0
  39. package/admin/js/views/block-editor.js +8 -0
  40. package/admin/js/views/blocks.js +4 -0
  41. package/admin/js/views/collection-editor.js +3 -3
  42. package/admin/js/views/collection-entries.js +1 -1
  43. package/admin/js/views/collections.js +1 -1
  44. package/admin/js/views/dashboard.js +1 -1
  45. package/admin/js/views/form-editor.js +8 -0
  46. package/admin/js/views/form-submissions.js +1 -0
  47. package/admin/js/views/forms.js +1 -0
  48. package/admin/js/views/index.js +1 -1
  49. package/admin/js/views/login.js +2 -2
  50. package/admin/js/views/media.js +1 -1
  51. package/admin/js/views/my-profile.js +1 -0
  52. package/admin/js/views/page-editor.js +34 -15
  53. package/admin/js/views/pages.js +5 -5
  54. package/admin/js/views/plugins.js +10 -10
  55. package/admin/js/views/pro-docs.js +1 -0
  56. package/admin/js/views/role-editor.js +1 -0
  57. package/admin/js/views/roles.js +4 -0
  58. package/admin/js/views/settings.js +3 -1
  59. package/admin/js/views/user-editor.js +1 -1
  60. package/admin/js/views/users.js +4 -7
  61. package/admin/js/views/view-editor.js +1 -0
  62. package/admin/js/views/view-preview.js +1 -0
  63. package/admin/js/views/views-list.js +1 -0
  64. package/bin/cli.js +1 -1
  65. package/config/auth.json +1 -0
  66. package/config/connections.json.bak +9 -0
  67. package/config/connections.json.example +9 -0
  68. package/config/plugins.json +19 -29
  69. package/config/server.json +6 -6
  70. package/config/site.json +12 -2
  71. package/package.json +24 -10
  72. package/plugins/example-analytics/stats.json +17 -12
  73. package/plugins/theme-roller/admin/templates/theme-roller.html +71 -0
  74. package/plugins/theme-roller/admin/views/theme-roller-view.js +403 -0
  75. package/plugins/theme-roller/config.js +1 -0
  76. package/plugins/theme-roller/plugin.js +233 -0
  77. package/plugins/theme-roller/plugin.json +31 -0
  78. package/plugins/theme-roller/public/active-theme.css +0 -0
  79. package/plugins/theme-roller/public/inject-head-late.html +1 -0
  80. package/public/css/forms.css +1 -0
  81. package/public/css/site.css +1 -1
  82. package/public/js/forms.js +1 -0
  83. package/public/js/site.js +1 -1
  84. package/scripts/build.js +194 -129
  85. package/scripts/pro.js +254 -0
  86. package/scripts/reset.js +33 -8
  87. package/scripts/seed.js +343 -78
  88. package/scripts/setup.js +1 -0
  89. package/server/middleware/auth.js +136 -120
  90. package/server/routes/api/actions.js +200 -0
  91. package/server/routes/api/auth.js +292 -146
  92. package/server/routes/api/blocks.js +84 -0
  93. package/server/routes/api/collections.js +79 -27
  94. package/{plugins/form-builder/plugin.js → server/routes/api/forms.js} +483 -505
  95. package/server/routes/api/layouts.js +49 -39
  96. package/server/routes/api/media.js +118 -92
  97. package/server/routes/api/navigation.js +40 -36
  98. package/server/routes/api/pages.js +132 -118
  99. package/server/routes/api/plugins.js +6 -3
  100. package/server/routes/api/settings.js +104 -88
  101. package/server/routes/api/users.js +27 -19
  102. package/server/routes/api/views.js +148 -0
  103. package/server/routes/public.js +124 -108
  104. package/server/server.js +269 -181
  105. package/server/services/actions.js +387 -0
  106. package/server/services/adapterRegistry.js +98 -0
  107. package/server/services/adapters/FileAdapter.js +192 -0
  108. package/server/services/adapters/MongoAdapter.js +220 -0
  109. package/server/services/blocks.js +162 -0
  110. package/server/services/collections.js +74 -86
  111. package/server/services/connectionManager.js +102 -0
  112. package/server/services/content.js +312 -307
  113. package/server/services/email.js +126 -0
  114. package/server/services/forms.js +173 -0
  115. package/server/services/markdown.js +1378 -747
  116. package/server/services/permissionRegistry.js +173 -0
  117. package/server/services/presetCollections.js +251 -0
  118. package/server/services/renderer.js +75 -1
  119. package/server/services/roles.js +227 -0
  120. package/server/services/rowAccess.js +104 -0
  121. package/server/services/userProfiles.js +199 -0
  122. package/server/services/users.js +281 -212
  123. package/server/services/views.js +280 -0
  124. package/server/templates/page.html +119 -113
  125. package/plugins/form-builder/admin/templates/form-settings.html +0 -29
  126. package/plugins/form-builder/admin/views/form-editor.js +0 -1444
  127. package/plugins/form-builder/admin/views/form-settings.js +0 -38
  128. package/plugins/form-builder/admin/views/form-submissions.js +0 -295
  129. package/plugins/form-builder/admin/views/forms-list.js +0 -164
  130. package/plugins/form-builder/config.js +0 -9
  131. package/plugins/form-builder/data/forms/consent.json +0 -104
  132. package/plugins/form-builder/data/forms/contact-details.json +0 -99
  133. package/plugins/form-builder/data/forms/contacts.json +0 -66
  134. package/plugins/form-builder/data/forms/feedback.json +0 -130
  135. package/plugins/form-builder/data/submissions/consent.json +0 -13
  136. package/plugins/form-builder/data/submissions/contact-details.json +0 -1
  137. package/plugins/form-builder/data/submissions/contacts.json +0 -26
  138. package/plugins/form-builder/data/submissions/feedback.json +0 -1
  139. package/plugins/form-builder/plugin.json +0 -52
  140. package/plugins/form-builder/public/inject-body.html +0 -352
  141. package/plugins/form-builder/public/inject-head.html +0 -58
  142. package/plugins/form-builder/public/package.json +0 -1
  143. package/scripts/copy-domma.js +0 -48
  144. package/server/services/userTypes.js +0 -167
  145. /package/{plugins/form-builder/public → public/js}/form-logic-engine.js +0 -0
@@ -1 +1 @@
1
- import{api as b}from"../api.js";let h=null,x=null,i=[],L=1;const M=50;export const collectionEntriesView={templateUrl:"/admin/js/templates/collection-entries.html",async onMount(a){i=[],L=1,x=null;const m=window.location.hash.match(/\/collections\/([^/?#]+)\/entries/);if(h=m?m[1]:null,!h){E.toast("No collection selected.",{type:"error"});return}try{if(x=await b.collections.get(h),!x){E.toast("Collection not found.",{type:"error"}),R.navigate("/collections");return}const r=a.find("#entries-title").get(0);r&&(r.textContent=x.title+" \u2014 Entries");const c=a.find("#edit-schema-btn").get(0);c&&c.setAttribute("href",`#/collections/edit/${h}`)}catch{E.toast("Failed to load collection schema.",{type:"error"});return}await k(a);const p=a.find("#entry-search").get(0);p&&p.addEventListener("input",()=>{const r=p.value.toLowerCase().trim(),c=r?i.filter(e=>Object.values(e.data||{}).some(o=>String(o).toLowerCase().includes(r))):i;C(c,a)}),a.find("#add-entry-btn").off("click").on("click",()=>{w(null,a)}),a.find("#export-btn").off("click").on("click",()=>{const r=E.slideover({title:"Export Entries",size:"sm",position:"right"}),c=document.createElement("div");c.style.cssText="padding:1.25rem;display:flex;flex-direction:column;gap:1rem;",["json","csv"].forEach(e=>{const o=document.createElement("button");o.className="btn btn-ghost",o.style.cssText="justify-content:flex-start;gap:.5rem;",o.textContent=e==="json"?"Export as JSON":"Export as CSV",o.addEventListener("click",()=>{fetch(`/api/collections/${h}/export?format=${e}`,{headers:{Authorization:`Bearer ${S.get("auth_token")||""}`}}).then(l=>l.blob()).then(l=>{const n=document.createElement("a");n.href=URL.createObjectURL(l),n.download=`${h}-entries.${e}`,document.body.appendChild(n),n.click(),document.body.removeChild(n),URL.revokeObjectURL(n.href),r.close()}).catch(()=>E.toast("Export failed.",{type:"error"}))}),c.appendChild(o)}),r.setContent(c),r.open()}),a.find("#import-btn").off("click").on("click",()=>{j(a)}),a.find("#clear-all-btn").off("click").on("click",async()=>{if(await E.confirm("Delete ALL entries? This cannot be undone."))try{await b.collections.clearEntries(h),i=[],E.toast("All entries cleared.",{type:"success"}),C([],a)}catch{E.toast("Failed to clear entries.",{type:"error"})}}),Domma.icons.scan()}};async function k(a){try{const u=await b.collections.listEntries(h,{limit:500});i=u.entries||u||[]}catch{i=[],E.toast("Could not load entries.",{type:"error"})}C(i,a)}function C(a,u){const m=u.find("#entry-count").get(0);m&&(m.textContent=a.length===i.length?`${i.length} entr${i.length!==1?"ies":"y"}`:`Showing ${a.length} of ${i.length}`);const r=[...(x?.fields||[]).slice(0,5).map(e=>({key:`data.${e.name}`,title:e.label||e.name,render:(o,l)=>{const n=l.data?.[e.name]??"",t=String(n),d=document.createElement("span");return d.title=t,d.textContent=t.length>60?t.slice(0,60)+"\u2026":t,d.outerHTML}})),{key:"meta",title:"Created",render:e=>D(e?.createdAt).format("DD MMM YYYY HH:mm")},{key:"id",title:"",render:e=>{const o=document.createElement("div");o.style.cssText="display:flex;gap:.3rem;justify-content:flex-end;";const l=document.createElement("button");l.className="btn btn-sm btn-ghost js-edit-entry",l.dataset.id=e,l.textContent="Edit";const n=document.createElement("button");return n.className="btn btn-sm btn-danger js-delete-entry",n.dataset.id=e,n.textContent="Delete",n.style.whiteSpace="nowrap",o.appendChild(l),o.appendChild(n),o.outerHTML}}];T.create("#entries-table",{data:a,columns:r,emptyMessage:'No entries yet. Click "Add Entry" to get started.'});const c=document.querySelector("#entries-table");c&&(c.querySelectorAll(".js-edit-entry").forEach(e=>{e.addEventListener("click",o=>{o.stopPropagation();const l=i.find(n=>n.id===e.dataset.id);l&&w(l,null)})}),c.querySelectorAll(".js-delete-entry").forEach(e=>{e.addEventListener("click",async o=>{if(o.stopPropagation(),!!await E.confirm("Delete this entry?"))try{await b.collections.deleteEntry(h,e.dataset.id),i=i.filter(n=>n.id!==e.dataset.id),E.toast("Entry deleted.",{type:"success"}),C(i,{find:n=>({get:t=>document.querySelector(n)})})}catch{E.toast("Failed to delete entry.",{type:"error"})}})}),c.querySelectorAll("tbody tr").forEach((e,o)=>{const l=a[o];l&&(e.style.cursor="pointer",e.addEventListener("click",n=>{n.target.closest(".js-edit-entry, .js-delete-entry")||N(l)}))}))}function w(a,u){const m=!!a,p=E.modal({title:m?"Edit Entry":"Add Entry",size:"md"}),r=document.createElement("div");r.style.cssText="padding:.25rem 0 .5rem;display:flex;flex-direction:column;gap:.75rem;";const c=x?.fields||[],e={};c.forEach(t=>{const d=document.createElement("div"),f=document.createElement("label");if(f.className="form-label",f.textContent=t.label||t.name,t.required){const y=document.createElement("span");y.textContent=" *",y.style.cssText="color:var(--danger,#f87171);",f.appendChild(y)}d.appendChild(f);let s;if(t.type==="textarea")s=document.createElement("textarea"),s.rows=3,s.value=a?.data?.[t.name]??"";else if(["select","radio","checkbox-group"].includes(t.type)){s=document.createElement("select");const y=document.createElement("option");y.value="",y.textContent="\u2014 select \u2014",s.appendChild(y),(t.options||[]).forEach(v=>{const g=document.createElement("option");g.value=v.value,g.textContent=v.label,v.value===(a?.data?.[t.name]??"")&&(g.selected=!0),s.appendChild(g)})}else if(t.type==="checkbox"){const y=document.createElement("label");y.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;",s=document.createElement("input"),s.type="checkbox",s.checked=!!a?.data?.[t.name],y.appendChild(s),y.appendChild(document.createTextNode(t.placeholder||t.label||t.name)),d.appendChild(y),e[t.name]=s,r.appendChild(d);return}else s=document.createElement("input"),s.type=t.type==="email"?"email":t.type==="number"?"number":"text",s.value=a?.data?.[t.name]??"",t.placeholder&&(s.placeholder=t.placeholder);s.className="form-input",d.appendChild(s),e[t.name]=s,r.appendChild(d)});const o=document.createElement("div");o.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:.5rem;";const l=document.createElement("button");l.className="btn btn-ghost",l.textContent="Cancel";const n=document.createElement("button");n.className="btn btn-primary",n.textContent=m?"Save Changes":"Add Entry",o.appendChild(l),o.appendChild(n),r.appendChild(o),p.element.appendChild(r),p.open(),l.addEventListener("click",()=>p.close()),n.addEventListener("click",async()=>{const t={};c.forEach(d=>{const f=e[d.name];f&&(t[d.name]=d.type==="checkbox"?f.checked:f.value)}),n.disabled=!0;try{if(m){const d=await b.collections.updateEntry(h,a.id,t),f=i.findIndex(s=>s.id===a.id);f!==-1&&(i[f]=d),E.toast("Entry updated.",{type:"success"})}else{const d=await b.collections.createEntry(h,t);i.unshift(d),E.toast("Entry added.",{type:"success"})}p.close(),C(i,u||{find:()=>({get:()=>null})})}catch(d){E.toast(d.message||"Failed to save entry.",{type:"error"}),n.disabled=!1}})}function N(a){const u=document.createElement("div"),m=x?.fields||[],p=document.createElement("div");p.style.cssText="display:flex;flex-direction:column;gap:.75rem;margin-bottom:1.25rem;",m.forEach(e=>{const o=a.data?.[e.name];if(o==null||o==="")return;const l=document.createElement("div");l.style.cssText="border-bottom:1px solid var(--border-color,#333);padding-bottom:.6rem;";const n=document.createElement("strong");n.textContent=e.label||e.name,n.style.cssText="display:block;font-size:.8rem;color:var(--text-muted,#888);margin-bottom:.2rem;";const t=document.createElement("p");t.textContent=String(o),t.style.cssText="margin:0;word-break:break-word;",l.appendChild(n),l.appendChild(t),p.appendChild(l)});const r=document.createElement("div");if(r.style.cssText="font-size:.8rem;color:var(--text-muted,#888);display:flex;flex-direction:column;gap:.3rem;border-top:1px solid var(--border-color,#333);padding-top:.75rem;",a.meta?.createdAt){const e=document.createElement("span");e.textContent=`Created: ${D(a.meta.createdAt).format("DD MMM YYYY HH:mm")}`,r.appendChild(e)}if(a.meta?.source){const e=document.createElement("span");e.textContent=`Source: ${a.meta.source}`,r.appendChild(e)}u.appendChild(p),u.appendChild(r);const c=E.modal({title:"Entry Details",size:"md"});c.element.appendChild(u),c.open()}function j(a){const u=E.modal({title:"Import Entries",size:"md"}),m=document.createElement("div");m.style.cssText="padding:.25rem 0 .5rem;display:flex;flex-direction:column;gap:.75rem;";const p=document.createElement("p");p.textContent='Paste a JSON array of entries. Each item should have a "data" object with field values.',p.style.cssText="font-size:.875rem;color:var(--text-muted,#888);margin:0;",m.appendChild(p);const r=document.createElement("textarea");r.className="form-input",r.rows=10,r.placeholder='[{"data": {"name": "Example"}}, ...]',m.appendChild(r);const c=document.createElement("p");c.style.cssText="font-size:.875rem;margin:0;display:none;",m.appendChild(c);const e=document.createElement("div");e.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;";const o=document.createElement("button");o.className="btn btn-ghost",o.textContent="Cancel";const l=document.createElement("button");l.className="btn btn-primary",l.textContent="Import",e.appendChild(o),e.appendChild(l),m.appendChild(e),u.element.appendChild(m),u.open(),o.addEventListener("click",()=>u.close()),l.addEventListener("click",async()=>{let n;try{if(n=JSON.parse(r.value),!Array.isArray(n))throw new Error("Must be a JSON array")}catch(t){c.style.display="",c.style.color="var(--danger,#f87171)",c.textContent=`Invalid JSON: ${t.message}`;return}l.disabled=!0;try{const t=await b.collections.import(h,n);c.style.display="",c.style.color="var(--success,#4ade80)",c.textContent=`Imported ${t.imported} entries. Skipped: ${t.skipped}.`,await k(a||{find:()=>({get:()=>null,off:()=>({on:()=>{}})})}),setTimeout(()=>u.close(),1500)}catch(t){c.style.display="",c.style.color="var(--danger,#f87171)",c.textContent=t.message||"Import failed."}finally{l.disabled=!1}})}
1
+ import{api as h}from"../api.js";function M(t){return String(t).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}let f=null,g=null,p=[],j=1,w=[],x={};const z=50;export const collectionEntriesView={templateUrl:"/admin/js/templates/collection-entries.html",async onMount(t){p=[],j=1,g=null,w=[],x={};const d=window.location.hash.match(/\/collections\/([^/?#]+)\/entries/);if(f=d?d[1]:null,!f){E.toast("No collection selected.",{type:"error"});return}try{if(g=await h.collections.get(f),!g){E.toast("Collection not found.",{type:"error"}),R.navigate("/collections");return}const a=t.find("#entries-title").get(0);a&&(a.textContent=g.title+" \u2014 Entries");const r=t.find("#edit-schema-btn").get(0);r&&r.setAttribute("href",`#/collections/edit/${f}`);const e=t.find("#form-btn").get(0);e&&(new Set(["roles","user-profiles"]).has(f)?e.style.display="none":e.setAttribute("href",`#/forms/edit/${f}`))}catch{E.toast("Failed to load collection schema.",{type:"error"});return}try{w=await h.actions.forCollection(f)}catch{w=[]}await v(t),await k(t);const n=t.find("#entry-search").get(0);n&&n.addEventListener("input",()=>{const a=n.value.toLowerCase().trim(),r=a?p.filter(e=>Object.values(e.data||{}).some(l=>String(l).toLowerCase().includes(a))):p;C(r,t)}),t.find("#add-entry-btn").off("click").on("click",()=>{A(null,t)}),t.find("#export-btn").off("click").on("click",()=>{const a=E.slideover({title:"Export Entries",size:"sm",position:"right"}),r=document.createElement("div");r.style.cssText="padding:1.25rem;display:flex;flex-direction:column;gap:1rem;",["json","csv"].forEach(e=>{const l=document.createElement("button");l.className="btn btn-ghost",l.style.cssText="justify-content:flex-start;gap:.5rem;",l.textContent=e==="json"?"Export as JSON":"Export as CSV",l.addEventListener("click",()=>{fetch(`/api/collections/${f}/export?format=${e}`,{headers:{Authorization:`Bearer ${S.get("auth_token")||""}`}}).then(s=>s.blob()).then(s=>{const o=document.createElement("a");o.href=URL.createObjectURL(s),o.download=`${f}-entries.${e}`,document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(o.href),a.close()}).catch(()=>E.toast("Export failed.",{type:"error"}))}),r.appendChild(l)}),a.setContent(r),a.open()}),t.find("#import-btn").off("click").on("click",()=>{B(t)}),t.find("#clear-all-btn").off("click").on("click",async()=>{if(await E.confirm("Delete ALL entries? This cannot be undone."))try{await h.collections.clearEntries(f),p=[],E.toast("All entries cleared.",{type:"success"}),C([],t)}catch{E.toast("Failed to clear entries.",{type:"error"})}}),Domma.icons.scan()}};async function k(t){const m=w.filter(n=>n.access?.rowLevel);if(m.length===0)return;const d=p.map(n=>n.id);d.length!==0&&(await Promise.all(m.map(async n=>{try{const{allowed:a}=await h.actions.checkAccess(n.slug,d);x[n.slug]=new Set(a)}catch{x[n.slug]=null}})),C(p,t))}async function v(t){try{const m=await h.collections.listEntries(f,{limit:500});p=m.entries||m||[]}catch{p=[],E.toast("Could not load entries.",{type:"error"})}C(p,t),await k(t)}function C(t,m){const d=m.find("#entry-count").get(0);d&&(d.textContent=t.length===p.length?`${p.length} entr${p.length!==1?"ies":"y"}`:`Showing ${t.length} of ${p.length}`);const a=[...(g?.fields||[]).slice(0,5).map(e=>({key:`data.${e.name}`,title:e.label||e.name,render:(l,s)=>{const o=s.data?.[e.name]??"",c=String(o),i=document.createElement("span");return i.title=c,i.textContent=c.length>60?c.slice(0,60)+"\u2026":c,i.outerHTML}})),{key:"meta",title:"Created",render:e=>D(e?.createdAt).format("DD MMM YYYY HH:mm")},{key:"id",title:"",render:e=>{const l=document.createElement("div");l.style.cssText="display:flex;gap:.3rem;justify-content:flex-end;flex-wrap:wrap;",w.forEach(c=>{if(x[c.slug]!==void 0&&x[c.slug]!==null&&!x[c.slug].has(e))return;const i=document.createElement("button");i.className="btn btn-sm btn-ghost js-run-action",i.dataset.actionSlug=c.slug,i.dataset.entryId=e,i.dataset.confirmMessage=c.trigger?.confirmMessage||"",i.title=c.title||c.slug;const y=document.createElement("span");y.setAttribute("data-icon",M(c.trigger?.icon||"zap")),i.appendChild(y),i.appendChild(document.createTextNode(" "+(c.trigger?.label||c.title))),l.appendChild(i)});const s=document.createElement("button");s.className="btn btn-sm btn-primary js-edit-entry",s.dataset.id=e,s.textContent="Edit";const o=document.createElement("button");return o.className="btn btn-sm btn-danger js-delete-entry",o.dataset.id=e,o.textContent="Delete",o.style.whiteSpace="nowrap",l.appendChild(s),l.appendChild(o),l.outerHTML}}];T.create("#entries-table",{data:t,columns:a,emptyMessage:'No entries yet. Click "Add Entry" to get started.'});const r=document.querySelector("#entries-table");r&&(r.querySelectorAll(".js-run-action").forEach(e=>{e.addEventListener("click",async l=>{l.stopPropagation();const{actionSlug:s,entryId:o,confirmMessage:c}=e.dataset;if(!(c&&!await E.confirm(c))){e.disabled=!0;try{const i=await h.actions.execute(s,o);if(i.success)E.toast(`Action completed (${i.stepsCompleted} step${i.stepsCompleted!==1?"s":""}).`,{type:"success"}),await v({find:y=>({get:u=>document.querySelector(y)})});else{const y=i.results?.find(u=>!u.success);E.toast(y?.error||"Action failed.",{type:"error"})}}catch(i){E.toast(i.message||"Action failed.",{type:"error"})}finally{e.disabled=!1}}})}),r.querySelectorAll(".js-edit-entry").forEach(e=>{e.addEventListener("click",l=>{l.stopPropagation();const s=p.find(o=>o.id===e.dataset.id);s&&A(s,null)})}),r.querySelectorAll(".js-delete-entry").forEach(e=>{e.addEventListener("click",async l=>{if(l.stopPropagation(),!!await E.confirm("Delete this entry?"))try{await h.collections.deleteEntry(f,e.dataset.id),p=p.filter(o=>o.id!==e.dataset.id),E.toast("Entry deleted.",{type:"success"}),C(p,{find:o=>({get:c=>document.querySelector(o)})})}catch{E.toast("Failed to delete entry.",{type:"error"})}})}),r.querySelectorAll("tbody tr").forEach((e,l)=>{const s=t[l];s&&(e.style.cursor="pointer",e.addEventListener("click",o=>{o.target.closest(".js-edit-entry, .js-delete-entry")||q(s)}))}))}function N(t,m){const d={};return t.forEach(n=>{const a={};n.fullWidth?a.span=m:n.span>1&&(a.span=n.span),n.placeholder&&(a.placeholder=n.placeholder),n.helper&&(a.hint=n.helper);const r=n.type==="checkbox"?"boolean":n.type==="date"?"string":n.type;d[n.name]={type:r,label:n.label,required:n.required,options:n.options,...Object.keys(a).length?{formConfig:a}:{}}}),d}function A(t,m){const d=!!t,n=E.modal({title:d?"Edit Entry":"Add Entry",size:"md"}),a=document.createElement("div");a.style.cssText="padding:.25rem 0 .5rem;";const r=g?.fields||[],e=g?.columns||2,l=g?.layout||"stacked",s=document.createElement("div");F.create(N(r,e),t?.data||{},{layout:l,columns:e,showSubmitButton:!1}).renderTo(s),(r||[]).forEach(y=>{if(y.type!=="date"||!y.name)return;const u=s.querySelector(`[name="${y.name}"]`);u&&u.type!=="date"&&(u.type="date")}),a.appendChild(s);const o=document.createElement("div");o.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:.5rem;";const c=document.createElement("button");c.className="btn btn-ghost",c.textContent="Cancel";const i=document.createElement("button");i.className="btn btn-primary",i.textContent=d?"Save Changes":"Add Entry",o.appendChild(c),o.appendChild(i),a.appendChild(o),n.element.appendChild(a),n.open(),c.addEventListener("click",()=>n.close()),i.addEventListener("click",async()=>{const y={};r.forEach(u=>{const b=s.querySelector(`[name="${u.name}"]`);b&&(y[u.name]=u.type==="checkbox"?b.checked:b.value)}),i.disabled=!0;try{if(d){const u=await h.collections.updateEntry(f,t.id,y),b=p.findIndex(L=>L.id===t.id);b!==-1&&(p[b]=u),E.toast("Entry updated.",{type:"success"})}else{const u=await h.collections.createEntry(f,y);p.unshift(u),E.toast("Entry added.",{type:"success"})}n.close(),C(p,m||{find:()=>({get:()=>null})})}catch(u){E.toast(u.message||"Failed to save entry.",{type:"error"}),i.disabled=!1}})}function q(t){const m=document.createElement("div"),d=g?.fields||[],n=document.createElement("div");n.style.cssText="display:flex;flex-direction:column;gap:.75rem;margin-bottom:1.25rem;",d.forEach(e=>{const l=t.data?.[e.name];if(l==null||l==="")return;const s=document.createElement("div");s.style.cssText="border-bottom:1px solid var(--border-color,#333);padding-bottom:.6rem;";const o=document.createElement("strong");o.textContent=e.label||e.name,o.style.cssText="display:block;font-size:.8rem;color:var(--text-muted,#888);margin-bottom:.2rem;";const c=document.createElement("p");c.textContent=String(l),c.style.cssText="margin:0;word-break:break-word;",s.appendChild(o),s.appendChild(c),n.appendChild(s)});const a=document.createElement("div");if(a.style.cssText="font-size:.8rem;color:var(--text-muted,#888);display:flex;flex-direction:column;gap:.3rem;border-top:1px solid var(--border-color,#333);padding-top:.75rem;",t.meta?.createdAt){const e=document.createElement("span");e.textContent=`Created: ${D(t.meta.createdAt).format("DD MMM YYYY HH:mm")}`,a.appendChild(e)}if(t.meta?.source){const e=document.createElement("span");e.textContent=`Source: ${t.meta.source}`,a.appendChild(e)}m.appendChild(n),m.appendChild(a);const r=E.modal({title:"Entry Details",size:"md"});r.element.appendChild(m),r.open()}function B(t){const m=E.modal({title:"Import Entries",size:"md"}),d=document.createElement("div");d.style.cssText="padding:.25rem 0 .5rem;display:flex;flex-direction:column;gap:.75rem;";const n=document.createElement("p");n.textContent='Paste a JSON array of entries. Each item should have a "data" object with field values.',n.style.cssText="font-size:.875rem;color:var(--text-muted,#888);margin:0;",d.appendChild(n);const a=document.createElement("textarea");a.className="form-input",a.rows=10,a.placeholder='[{"data": {"name": "Example"}}, ...]',d.appendChild(a);const r=document.createElement("p");r.style.cssText="font-size:.875rem;margin:0;display:none;",d.appendChild(r);const e=document.createElement("div");e.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;";const l=document.createElement("button");l.className="btn btn-ghost",l.textContent="Cancel";const s=document.createElement("button");s.className="btn btn-primary",s.textContent="Import",e.appendChild(l),e.appendChild(s),d.appendChild(e),m.element.appendChild(d),m.open(),l.addEventListener("click",()=>m.close()),s.addEventListener("click",async()=>{let o;try{if(o=JSON.parse(a.value),!Array.isArray(o))throw new Error("Must be a JSON array")}catch(c){r.style.display="",r.style.color="var(--danger,#f87171)",r.textContent=`Invalid JSON: ${c.message}`;return}s.disabled=!0;try{const c=await h.collections.import(f,o);r.style.display="",r.style.color="var(--success,#4ade80)",r.textContent=`Imported ${c.imported} entries. Skipped: ${c.skipped}.`,await v(t||{find:()=>({get:()=>null,off:()=>({on:()=>{}})})}),setTimeout(()=>m.close(),1500)}catch(c){r.style.display="",r.style.color="var(--danger,#f87171)",r.textContent=c.message||"Import failed."}finally{s.disabled=!1}})}
@@ -1 +1 @@
1
- import{api as d}from"../api.js";function m(a){return String(a).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}export const collectionsView={templateUrl:"/admin/js/templates/collections.html",async onMount(a){await p(a),a.find("#create-collection-btn").off("click").on("click",()=>{const c=E.modal({title:"New Collection",size:"sm"}),e=document.createElement("div");e.style.cssText="padding:.25rem 0 .5rem;";const t=document.createElement("div");F.create({title:{type:"string",label:"Collection Title",placeholder:"e.g. Products, Blog Posts\u2026",required:!0}},{},{showSubmitButton:!1}).renderTo(t),e.appendChild(t);const n=document.createElement("div");n.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 l=document.createElement("button");l.className="btn btn-primary",l.textContent="Create",n.appendChild(o),n.appendChild(l),e.appendChild(n),c.element.appendChild(e),c.open();const i=t.querySelector('input[name="title"]');setTimeout(()=>i?.focus(),50);async function u(){const s=i?.value.trim();if(s)try{const r=await d.collections.create({title:s});c.close(),R.navigate(`/collections/edit/${r.slug}`)}catch(r){E.toast(r.message||"Failed to create collection.",{type:"error"})}}o.addEventListener("click",()=>c.close()),l.addEventListener("click",u),i?.addEventListener("keydown",s=>{s.key==="Enter"&&u()})}),Domma.icons.scan()}};async function p(a){let c=[];try{c=await d.collections.list()}catch{E.toast("Could not load collections.",{type:"error"})}T.create("#collections-table",{data:c,columns:[{key:"title",title:"Title",render:(e,t)=>{const n=document.createElement("a");return n.href=`#/collections/${m(t.slug)}/entries`,n.textContent=e,n.style.fontWeight="600",n.outerHTML}},{key:"slug",title:"Slug",render:e=>{const t=document.createElement("code");return t.textContent=e,t.outerHTML}},{key:"fields",title:"Field Count",render:e=>String(e?.length??0)},{key:"entryCount",title:"Entry Count",render:e=>String(e??0)},{key:"slug",title:"Actions",render:e=>{const t=document.createElement("div");t.style.cssText="display:flex;gap:.4rem;justify-content:flex-end;";const n=document.createElement("a");n.href=`#/collections/edit/${m(e)}`,n.className="btn btn-sm btn-ghost",n.textContent="Edit Schema";const o=document.createElement("a");o.href=`#/collections/${m(e)}/entries`,o.className="btn btn-sm btn-ghost",o.textContent="Entries";const l=document.createElement("button");return l.className="btn btn-sm btn-danger js-delete-collection",l.dataset.slug=e,l.textContent="Delete",t.appendChild(n),t.appendChild(o),t.appendChild(l),t.outerHTML}}],emptyMessage:'No collections yet. Click "New Collection" to get started.'}),document.querySelectorAll(".js-delete-collection").forEach(e=>{e.addEventListener("click",async()=>{const t=e.dataset.slug;if(await E.confirm(`Delete collection "${t}" and all its data? This cannot be undone.`))try{await d.collections.delete(t),E.toast("Collection deleted.",{type:"success"}),await p(a)}catch{E.toast("Failed to delete collection.",{type:"error"})}})}),Domma.icons.scan()}
1
+ import{api as h}from"../api.js";function b(e){return String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}let i={};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 o=e.find("#collections-tabs").get(0)?.querySelector(".tab-list");if(o){const t=e.find("#collections-header-actions").get(0);Array.from(o.querySelectorAll(".tab-item")).forEach((n,c)=>{n.addEventListener("click",()=>{t&&(t.style.display=c===0?"":"none")})})}await Promise.all([j(e),q(e)]),a.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 d=document.createElement("div");d.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:.75rem;";const s=document.createElement("button");s.className="btn btn-ghost",s.textContent="Cancel";const l=document.createElement("button");l.className="btn btn-primary",l.textContent="Create",d.appendChild(s),d.appendChild(l),n.appendChild(d),t.element.appendChild(n),t.open();const r=c.querySelector('input[name="title"]');setTimeout(()=>r?.focus(),50);async function m(){const p=r?.value.trim();if(p)try{const u=await h.collections.create({title:p});t.close(),R.navigate(`/collections/edit/${u.slug}`)}catch(u){E.toast(u.message||"Failed to create collection.",{type:"error"})}}s.addEventListener("click",()=>t.close()),l.addEventListener("click",m),r?.addEventListener("keydown",p=>{p.key==="Enter"&&m()})}),e.find("#connections-raw-toggle").on("change",function(){if(this.checked)i=w(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{i=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",()=>{i=w(e);let t=Object.keys(i).length+1,n=`connection${t}`;for(;i[n];)t++,n=`connection${t}`;i[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=w(e);try{await h.collections.saveConnections(n),i=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 a=[];try{a=await h.collections.list()}catch{E.toast("Could not load collections.",{type:"error"})}T.create("#collections-table",{data:a,columns:[{key:"title",title:"Title",render:(o,t)=>{const n=document.createElement("a");return n.href=`#/collections/${b(t.slug)}/entries`,n.textContent=o,n.style.fontWeight="600",n.outerHTML}},{key:"slug",title:"Slug",render:o=>{const t=document.createElement("code");return t.textContent=o,t.outerHTML}},{key:"fields",title:"Field Count",render:o=>String(o?.length??0)},{key:"entryCount",title:"Entry Count",render:o=>String(o??0)},{key:"slug",title:"Actions",render:o=>{const t=document.createElement("div");t.style.cssText="display:flex;gap:.4rem;justify-content:flex-end;flex-wrap:wrap;";const n=document.createElement("a");n.href=`#/collections/edit/${b(o)}`,n.className="btn btn-sm btn-ghost",n.textContent="Edit Schema";const c=document.createElement("a");if(c.href=`#/collections/${b(o)}/entries`,c.className="btn btn-sm btn-ghost",c.textContent="Entries",t.appendChild(n),t.appendChild(c),!new Set(["roles","user-profiles"]).has(o)){const l=document.createElement("a");l.href=`#/forms/edit/${b(o)}`,l.className="btn btn-sm btn-ghost",l.textContent="Form",t.appendChild(l)}const s=document.createElement("button");return s.className="btn btn-sm btn-danger js-delete-collection",s.dataset.slug=o,s.textContent="Delete",t.appendChild(s),t.outerHTML}}],emptyMessage:'No collections yet. Click "New Collection" to get started.'}),document.querySelectorAll(".js-delete-collection").forEach(o=>{o.addEventListener("click",async()=>{const t=o.dataset.slug;if(await E.confirm(`Delete collection "${t}" and all its data? This cannot be undone.`))try{await h.collections.delete(t),E.toast("Collection deleted.",{type:"success"}),await j(e)}catch{E.toast("Failed to delete collection.",{type:"error"})}})}),Domma.icons.scan()}async function q(e){try{i=await h.collections.getConnections()}catch{i={}}C(e),v(e)}function C(e){const a=e.find("#connections-list").get(0),o=e.find("#connections-empty").get(0);if(!a)return;a.textContent="";const t=Object.keys(i);o&&(o.style.display=t.length?"none":""),t.forEach(n=>{const c=i[n],d=document.createElement("div");d.className="conn-card card mb-3",d.dataset.connKey=n;const s=document.createElement("div");s.className="card-header",s.style.cssText="display:flex;align-items:center;gap:.5rem;";const l=document.createElement("input");l.type="text",l.className="form-input conn-name",l.value=n,l.placeholder="Connection name",l.style.cssText="flex:1;";const r=document.createElement("button");r.type="button",r.className="btn btn-sm btn-danger",r.textContent="Delete",r.addEventListener("click",()=>{i=w(e);const L=l.value.trim();L&&delete i[L],C(e),v(e)}),s.appendChild(l),s.appendChild(r);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=c.type||"mongodb",f.disabled=!0,p.appendChild(u),p.appendChild(f);const x=document.createElement("div"),S=document.createElement("label");S.className="form-label",S.textContent="URI";const y=document.createElement("input");y.type="text",y.className="form-input conn-uri",y.value=c.uri||"",y.placeholder="mongodb://localhost:27017",x.appendChild(S),x.appendChild(y);const N=document.createElement("div"),k=document.createElement("label");k.className="form-label",k.textContent="Database";const g=document.createElement("input");g.type="text",g.className="form-input conn-database",g.value=c.database||"",g.placeholder="mydb",N.appendChild(k),N.appendChild(g),m.appendChild(p),m.appendChild(x),m.appendChild(N),d.appendChild(s),d.appendChild(m),a.appendChild(d)})}function w(e){const a={},o=e.find("#connections-list").get(0)?.querySelectorAll(".conn-card")||[];for(const t of o){const n=t.querySelector(".conn-name")?.value.trim(),c=t.querySelector(".conn-uri")?.value.trim()||"",d=t.querySelector(".conn-database")?.value.trim()||"";n&&(a[n]={type:"mongodb",uri:c,database:d,options:{}})}return a}function v(e){const a=e.find("#connections-raw-json").get(0);a&&(a.value=JSON.stringify(i,null,2))}
@@ -1 +1 @@
1
- import{api as l}from"../api.js";export const dashboardView={templateUrl:"/admin/js/templates/dashboard.html",async onMount(a){const e=await l.pages.list().catch(()=>[]),s=e.length,d=e.filter(t=>t.status==="published").length,r=e.filter(t=>t.status==="draft").length;a.find("#stat-total").text(s),a.find("#stat-published").text(d),a.find("#stat-drafts").text(r),Domma.effects.counter("#stat-total, #stat-published, #stat-drafts",{trigger:"immediate",duration:1e3,stagger:120,easing:"ease-out"});const i=[...e].sort((t,n)=>(n.updatedAt||"").localeCompare(t.updatedAt||"")).slice(0,10);T.create("#recent-pages-table",{data:i,columns:[{key:"title",title:"Title"},{key:"urlPath",title:"URL"},{key:"status",title:"Status",render:t=>`<span class="badge badge-${t==="published"?"success":"warning"}">${t}</span>`},{key:"updatedAt",title:"Updated",render:t=>t?D(t).format("DD MMM YYYY"):"\u2014"},{key:"urlPath",title:"Actions",render:t=>`<a href="#/pages/edit${t}" class="btn btn-sm btn-outline">Edit</a>`}],emptyMessage:'No pages yet. <a href="#/pages/new">Create your first page</a>.'}),Domma.icons.scan(),Domma.effects.reveal(".stat-card",{animation:"fade",stagger:80,duration:400}),Domma.effects.reveal(".card.mt-4",{animation:"fade",delay:200,duration:400})}};
1
+ import{api as n}from"../api.js";export const dashboardView={templateUrl:"/admin/js/templates/dashboard.html",async onMount(e){const s=E.loader(e.get(0),{type:"dots"}),a=await n.pages.list().catch(()=>[]);s.destroy();const d=a.length,r=a.filter(t=>t.status==="published").length,i=a.filter(t=>t.status==="draft").length;e.find("#stat-total").text(d),e.find("#stat-published").text(r),e.find("#stat-drafts").text(i),Domma.effects.counter("#stat-total, #stat-published, #stat-drafts",{trigger:"immediate",duration:1e3,stagger:120,easing:"ease-out"});const o=[...a].sort((t,l)=>(l.updatedAt||"").localeCompare(t.updatedAt||"")).slice(0,10);T.create("#recent-pages-table",{data:o,columns:[{key:"title",title:"Title"},{key:"urlPath",title:"URL"},{key:"status",title:"Status",render:t=>`<span class="badge badge-${t==="published"?"success":"warning"}">${t}</span>`},{key:"updatedAt",title:"Updated",render:t=>t?D(t).format("DD MMM YYYY"):"\u2014"},{key:"urlPath",title:"Actions",render:t=>`<a href="#/pages/edit${t}" class="btn btn-sm btn-primary">Edit</a>`}],emptyMessage:'No pages yet. <a href="#/pages/new">Create your first page</a>.'}),Domma.icons.scan(),Domma.effects.reveal(".stat-card",{animation:"fade",stagger:80,duration:400}),Domma.effects.reveal(".card.mt-4",{animation:"fade",delay:200,duration:400})}};
@@ -0,0 +1,8 @@
1
+ import{apiRequest as T}from"/admin/js/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:"password",label:"Password"},{value:"file",label:"File upload"},{value:"hidden",label:"Hidden field"}],I=new Set(["select","radio","checkbox-group"]);let b=[],C=null,L=null,q=null;function O(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_|_$/g,"")}function P(e){return z.find(t=>t.value===e)?.label||e}function M(e){return e?!!(e.visibility?.conditions?.length||e.requirement?.conditions?.length||e.validation?.length||e.cascade?.sourceField):!1}function G(e){const t={...b[e]};if(t.type==="spacer")return t;if(t.type==="page-break"){const g=document.getElementById(`fb-pb-label-${e}`),h=document.getElementById(`fb-pb-desc-${e}`);return g&&(t.label=g.value.trim()||t.label),h&&(t.description=h.value.trim()),t}const c=document.getElementById(`fb-label-${e}`),n=document.getElementById(`fb-name-${e}`),s=document.getElementById(`fb-type-${e}`),a=document.getElementById(`fb-required-${e}`),l=document.getElementById(`fb-placeholder-${e}`),d=document.getElementById(`fb-helper-${e}`);if(c&&(t.label=c.value.trim()||t.label),n&&(t.name=n.value.trim()||t.name),s&&(t.type=s.value||t.type),a&&(t.required=a.checked),l&&(t.placeholder=l.value.trim()),d&&(t.helper=d.value.trim()),I.has(t.type)){const g=document.getElementById(`fb-options-${e}`);g&&(t.options=g.value.split(`
2
+ `).filter(h=>h.trim()).map(h=>{const[y,...S]=h.split(":");return{value:y.trim(),label:S.join(":").trim()||y.trim()}}))}if(t.type==="textarea"){const g=parseInt(document.getElementById(`fb-rows-${e}`)?.value,10);g>0&&(t.formConfig={...t.formConfig||{},rows:g})}const o=document.getElementById(`fb-span-${e}`),i=document.getElementById(`fb-fullwidth-${e}`);if(o||i){const g={...t.formConfig||{}};if(i?.checked)g.span="full";else{const h=parseInt(o?.value,10);h>1?g.span=h:delete g.span}t.formConfig=Object.keys(g).length?g:void 0}const m=document.getElementById(`fb-minlength-${e}`)?.value;m&&(t.minLength=parseInt(m,10));const p=document.getElementById(`fb-maxlength-${e}`)?.value;p&&(t.maxLength=parseInt(p,10));const f=document.getElementById(`fb-min-${e}`)?.value;f!==""&&f!==void 0&&(t.min=parseFloat(f));const r=document.getElementById(`fb-max-${e}`)?.value;r!==""&&r!==void 0&&(t.max=parseFloat(r));const u=J(e);return u?t.logic=u:delete t.logic,t}function J(e){const t=document.querySelector(`.fb-field-card[data-index="${e}"]`);if(!t)return;const c=t.querySelector(".fb-field-logic");if(!c)return;const n={};let s=!1;const a=c.querySelector('[data-logic-section="visibility"]');if(a){const i=a.querySelector(".fb-logic-vis-default"),m=a.querySelector(".fb-logic-vis-transition"),p=Array.from(a.querySelectorAll(".fb-logic-cond-row")).map(u=>{const g=u.querySelector(".fb-logic-cond-field"),h=u.querySelector(".fb-logic-cond-op"),y=u.querySelector(".fb-logic-cond-val"),S=u.querySelector(".fb-logic-vis-then");return g?.value?{when:{all:[{field:g.value,operator:h.value,value:y.value}]},then:S.value}:null}).filter(Boolean),f=i?.value||"visible",r=m?.value||"none";(f!=="visible"||p.length>0||r!=="none")&&(n.visibility={default:f,conditions:p},r!=="none"&&(n.visibility.transition=r),s=!0)}const l=c.querySelector('[data-logic-section="requirement"]');if(l){const i=l.querySelector(".fb-logic-req-default"),m=Array.from(l.querySelectorAll(".fb-logic-cond-row")).map(p=>{const f=p.querySelector(".fb-logic-cond-field"),r=p.querySelector(".fb-logic-cond-op"),u=p.querySelector(".fb-logic-cond-val"),g=p.querySelector(".fb-logic-req-then");return f?.value?{when:{all:[{field:f.value,operator:r.value,value:u.value}]},then:g.value==="true"}:null}).filter(Boolean);m.length>0&&(n.requirement={default:i?.checked===!0,conditions:m},s=!0)}const d=c.querySelector('[data-logic-section="validation"]');if(d){const i=Array.from(d.querySelectorAll(".fb-logic-val-rule")).map(m=>{const p=m.querySelector(".fb-logic-val-type"),f=m.querySelector(".fb-logic-val-pattern"),r=m.querySelector(".fb-logic-val-flags"),u=m.querySelector(".fb-logic-val-message");if(!f?.value.trim())return null;const g=p?.value||"regex",h={type:g,message:u?.value.trim()||"Invalid value."};return g==="regex"?(h.pattern=f.value.trim(),r?.value.trim()&&(h.flags=r.value.trim())):h.field=f.value.trim(),h}).filter(Boolean);i.length>0&&(n.validation=i,s=!0)}const o=c.querySelector('[data-logic-section="cascade"]');if(o){const i=o.querySelector(".fb-logic-cascade-source"),m=o.querySelector(".fb-logic-cascade-mapping"),p=o.querySelector(".fb-logic-cascade-defaults"),f=i?.value?.trim();if(f){let r={};try{r=JSON.parse(m?.value||"{}")}catch{}const u=(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:f,mapping:r,defaultOptions:u},s=!0}}return s?n:void 0}function v(){b=b.map((e,t)=>G(t))}function x(e){const t=e.find("#fields-list").get(0),c=e.find("#fields-empty-msg").get(0);if(t){if(Array.from(t.querySelectorAll(".fb-field-card")).forEach(n=>n.remove()),b.length===0){c&&(c.style.display="");return}c&&(c.style.display="none"),b.forEach((n,s)=>{const a=n.type==="page-break"?W(n,s,e):n.type==="spacer"?Y(n,s,e):K(n,s,e);t.appendChild(a)})}}function W(e,t,c){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 s=document.createElement("div");s.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.6rem .8rem;cursor:pointer;user-select:none;";const a=document.createElement("span");a.textContent="\u2014 Page Break \u2014",a.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 l=document.createElement("span");l.textContent=e.label||"Untitled Step",l.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 d=document.createElement("div");if(d.style.cssText="display:flex;gap:.25rem;flex-shrink:0;margin-left:.5rem;",t>0){const r=document.createElement("button");r.className="btn btn-xs btn-ghost",r.title="Move up",r.textContent="\u2191",r.addEventListener("click",u=>{u.stopPropagation(),v(),[b[t-1],b[t]]=[b[t],b[t-1]],x(c)}),d.appendChild(r)}if(t<b.length-1){const r=document.createElement("button");r.className="btn btn-xs btn-ghost",r.title="Move down",r.textContent="\u2193",r.addEventListener("click",u=>{u.stopPropagation(),v(),[b[t],b[t+1]]=[b[t+1],b[t]],x(c)}),d.appendChild(r)}const o=document.createElement("button");o.className="btn btn-xs btn-ghost",o.title="Edit step",o.textContent="\u22EF",o.addEventListener("click",r=>{r.stopPropagation(),m.style.display=m.style.display==="none"?"":"none"}),d.appendChild(o);const i=document.createElement("button");i.className="btn btn-xs btn-danger",i.title="Remove page break",i.textContent="\u2715",i.addEventListener("click",async r=>{r.stopPropagation(),await E.confirm("Remove this page break?")&&(v(),b.splice(t,1),x(c))}),d.appendChild(i),s.appendChild(a),s.appendChild(l),s.appendChild(d),s.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=k([w("Step Title",`fb-pb-label-${t}`,"text",e.label||"","Shown as the wizard step heading"),w("Step Description",`fb-pb-desc-${t}`,"text",e.description||"","Optional sub-heading")]),f=p.querySelector(`#fb-pb-label-${t}`);return f&&f.addEventListener("input",()=>{l.textContent=f.value||"Untitled Step"}),m.appendChild(p),n.appendChild(s),n.appendChild(m),n}function Y(e,t,c){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 s=document.createElement("div");s.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.4rem .8rem;";const a=document.createElement("div");a.style.cssText="flex:1;height:1px;background:var(--border-color,#444);";const l=document.createElement("span");l.textContent="Spacer",l.style.cssText="font-size:.7rem;color:var(--text-muted,#888);white-space:nowrap;padding:0 .4rem;font-style:italic;";const d=document.createElement("div");d.style.cssText="flex:1;height:1px;background:var(--border-color,#444);";const o=document.createElement("div");if(o.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(),v(),[b[t-1],b[t]]=[b[t],b[t-1]],x(c)}),o.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(),v(),[b[t],b[t+1]]=[b[t+1],b[t]],x(c)}),o.appendChild(m)}const i=document.createElement("button");return i.className="btn btn-xs btn-danger",i.title="Remove spacer",i.textContent="\u2715",i.addEventListener("click",async m=>{m.stopPropagation(),v(),b.splice(t,1),x(c)}),o.appendChild(i),s.appendChild(a),s.appendChild(l),s.appendChild(d),s.appendChild(o),n.appendChild(s),n}function K(e,t,c){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 s=document.createElement("div");s.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 a=document.createElement("span");a.textContent=P(e.type),a.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 l=document.createElement("span");l.textContent=e.label||"(unlabelled)",l.style.cssText="flex:1;font-weight:600;font-size:.9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;";const d=document.createElement("div");if(d.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",f=>{f.stopPropagation(),v(),[b[t-1],b[t]]=[b[t],b[t-1]],x(c)}),d.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",f=>{f.stopPropagation(),v(),[b[t],b[t+1]]=[b[t+1],b[t]],x(c)}),d.appendChild(p)}const o=document.createElement("button");o.className="btn btn-xs btn-ghost",o.title="Edit field",o.textContent="\u22EF",o.addEventListener("click",p=>{p.stopPropagation(),m.style.display=m.style.display==="none"?"":"none"}),d.appendChild(o);const i=document.createElement("button");if(i.className="btn btn-xs btn-danger",i.title="Remove field",i.textContent="\u2715",i.addEventListener("click",async p=>{p.stopPropagation(),await E.confirm("Remove this field?")&&(v(),b.splice(t,1),x(c))}),d.appendChild(i),s.appendChild(a),s.appendChild(l),e.required){const p=document.createElement("span");p.textContent="required",p.style.cssText="font-size:.7rem;color:var(--danger,#ef4444);flex-shrink:0;",s.appendChild(p)}if(M(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;",s.appendChild(p)}s.appendChild(d),s.addEventListener("click",()=>{m.style.display=m.style.display==="none"?"":"none"});const m=Q(e,t,l);return m.style.display="none",n.appendChild(s),n.appendChild(m),n}function Q(e,t,c){const n=document.createElement("div");n.className="fb-field-body",n.style.cssText="padding:.8rem;border-top:1px solid var(--border-color,#333);";const s=k([w("Label",`fb-label-${t}`,"text",e.label||"","Shown above the field"),w("Field Name",`fb-name-${t}`,"text",e.name||"","Used as data key")]),a=k([le("Type",`fb-type-${t}`,z,e.type||"string"),D("Required",`fb-required-${t}`,e.required||!1)]),l=k([w("Placeholder",`fb-placeholder-${t}`,"text",e.placeholder||"","Hint text inside the field"),w("Helper Text",`fb-helper-${t}`,"text",e.helper||"","Shown below the field")]),d=e.formConfig?.span,o=k([w("Column Span",`fb-span-${t}`,"number",d&&d!=="full"?String(d):"1","Columns to span (grid only)"),D("Full Width",`fb-fullwidth-${t}`,d==="full")]);o.classList.add("fb-grid-row"),o.style.display=document.getElementById("setting-layout")?.value==="grid"?"flex":"none",n.appendChild(s),n.appendChild(a),n.appendChild(l),n.appendChild(o);const i=n.querySelector(`#fb-label-${t}`),m=n.querySelector(`#fb-name-${t}`);i&&i.addEventListener("input",()=>{c&&(c.textContent=i.value||"(unlabelled)"),m&&!m.dataset.manuallyEdited&&(m.value=O(i.value))}),m&&m.addEventListener("input",()=>{m.dataset.manuallyEdited="1"});const p=n.querySelector(`#fb-type-${t}`);p&&p.addEventListener("change",()=>{const r=n.closest(".fb-field-card");if(r){const h=r.querySelector("span");h&&(h.textContent=P(p.value))}const u=n.querySelector(".fb-field-extras");u&&u.remove();const g=j(p.value,e,t);g&&n.appendChild(g)});const f=j(e.type,e,t);return f&&n.appendChild(f),n.appendChild(ne(e,t)),n}const X=[{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"}],_=new Set(["is_empty","is_not_empty"]);function N(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 B(e,t,c,n,s){const a=document.createElement("div");a.className="fb-logic-cond-row",a.style.cssText="display:flex;gap:.35rem;align-items:center;margin-bottom:.35rem;flex-wrap:wrap;";const l=document.createElement("span");l.textContent="When",l.style.cssText="font-size:.73rem;color:var(--text-muted,#888);flex-shrink:0;";const d=document.createElement("select");d.className="form-input fb-logic-cond-field",d.style.cssText="flex:2;min-width:80px;font-size:.78rem;padding:.2rem .35rem;",t.forEach(r=>{const u=document.createElement("option");u.value=r.value,u.textContent=r.label,e&&r.value===e.field&&(u.selected=!0),d.appendChild(u)});const o=document.createElement("select");o.className="form-input fb-logic-cond-op",o.style.cssText="flex:2;min-width:80px;font-size:.78rem;padding:.2rem .35rem;",X.forEach(r=>{const u=document.createElement("option");u.value=r.value,u.textContent=r.label,e&&r.value===e.operator&&(u.selected=!0),o.appendChild(u)});const i=document.createElement("input");i.type="text",i.className="form-input fb-logic-cond-val",i.placeholder="value",i.style.cssText="flex:2;min-width:60px;font-size:.78rem;padding:.2rem .35rem;",i.value=e?.value||"",e&&_.has(e.operator)&&(i.style.display="none"),o.addEventListener("change",()=>{i.style.display=_.has(o.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-input ${n}`,p.style.cssText="flex:2;min-width:80px;font-size:.78rem;padding:.2rem .35rem;",c.forEach(r=>{const u=document.createElement("option");u.value=r.value,u.textContent=r.label,r.value===s&&(u.selected=!0),p.appendChild(u)});const f=document.createElement("button");return f.type="button",f.className="btn btn-xs btn-danger",f.textContent="\u2715",f.style.flexShrink="0",f.addEventListener("click",()=>a.remove()),a.appendChild(l),a.appendChild(d),a.appendChild(o),a.appendChild(i),a.appendChild(m),a.appendChild(p),a.appendChild(f),a}function Z(e,t,c){const n=document.createElement("div");n.dataset.logicSection="visibility",n.appendChild(N("Visibility"));const s=document.createElement("div");s.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem;";const a=document.createElement("span");a.textContent="Default:",a.style.cssText="font-size:.8rem;flex-shrink:0;";const l=document.createElement("select");l.className="form-input fb-logic-vis-default",l.style.cssText="font-size:.8rem;padding:.25rem .4rem;",[{value:"visible",label:"Visible"},{value:"hidden",label:"Hidden"}].forEach(r=>{const u=document.createElement("option");u.value=r.value,u.textContent=r.label,r.value===(e.default||"visible")&&(u.selected=!0),l.appendChild(u)}),s.appendChild(a),s.appendChild(l),n.appendChild(s);const d=document.createElement("div");d.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem;";const o=document.createElement("span");o.textContent="Transition:",o.style.cssText="font-size:.8rem;flex-shrink:0;";const i=document.createElement("select");i.className="form-input fb-logic-vis-transition",i.style.cssText="font-size:.8rem;padding:.25rem .4rem;",[{value:"none",label:"None (instant)"},{value:"fade",label:"Fade"},{value:"slide",label:"Slide"},{value:"scale",label:"Scale"}].forEach(r=>{const u=document.createElement("option");u.value=r.value,u.textContent=r.label,r.value===(e.transition||"none")&&(u.selected=!0),i.appendChild(u)}),d.appendChild(o),d.appendChild(i),n.appendChild(d);const m=document.createElement("div");m.className="fb-logic-vis-rules";const p=c.map(r=>({value:r.name,label:r.label||r.name})),f=[{value:"visible",label:"Show"},{value:"hidden",label:"Hide"}];if((e.conditions||[]).forEach(r=>{const u=(r.when?.all||r.when?.any||[])[0],g=r.then==="hidden"?"hidden":"visible";p.length>0&&m.appendChild(B(u,p,f,"fb-logic-vis-then",g))}),n.appendChild(m),p.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 visibility rule",r.addEventListener("click",()=>m.appendChild(B(null,p,f,"fb-logic-vis-then","visible"))),n.appendChild(r)}return n}function $(e,t,c){const n=document.createElement("div");n.dataset.logicSection="requirement",n.appendChild(N("Conditional Requirement"));const s=document.createElement("label");s.style.cssText="display:flex;align-items:center;gap:.4rem;font-size:.8rem;cursor:pointer;margin-bottom:.4rem;";const a=document.createElement("input");a.type="checkbox",a.className="fb-logic-req-default",a.checked=e.default===!0,s.appendChild(a),s.appendChild(document.createTextNode("Required by default")),n.appendChild(s);const l=document.createElement("div");l.className="fb-logic-req-rules";const d=c.map(i=>({value:i.name,label:i.label||i.name})),o=[{value:"true",label:"Make required"},{value:"false",label:"Make optional"}];if((e.conditions||[]).forEach(i=>{const m=(i.when?.all||i.when?.any||[])[0],p=i.then===!0?"true":"false";d.length>0&&l.appendChild(B(m,d,o,"fb-logic-req-then",p))}),n.appendChild(l),d.length>0){const i=document.createElement("button");i.type="button",i.className="btn btn-xs btn-ghost",i.style.cssText="font-size:.73rem;margin-top:.2rem;",i.textContent="+ Add requirement rule",i.addEventListener("click",()=>l.appendChild(B(null,d,o,"fb-logic-req-then","true"))),n.appendChild(i)}return n}function A(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 c=document.createElement("select");c.className="form-input fb-logic-val-type",c.style.cssText="flex:0 0 auto;font-size:.78rem;padding:.2rem .35rem;",[{value:"regex",label:"Regex"},{value:"match",label:"Match field"}].forEach(d=>{const o=document.createElement("option");o.value=d.value,o.textContent=d.label,d.value===(e?.type||"regex")&&(o.selected=!0),c.appendChild(o)});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;font-size:.78rem;padding:.2rem .35rem;";const s=document.createElement("input");s.type="text",s.className="form-input fb-logic-val-flags",s.placeholder="flags",s.value=e?.flags||"",s.style.cssText="flex:0 0 55px;font-size:.78rem;padding:.2rem .35rem;",e?.type==="match"&&(s.style.display="none"),c.addEventListener("change",()=>{s.style.display=c.value==="match"?"none":"",n.placeholder=c.value==="match"?"field name":"pattern"});const a=document.createElement("input");a.type="text",a.className="form-input fb-logic-val-message",a.placeholder="Error message",a.value=e?.message||"",a.style.cssText="flex:4;font-size:.78rem;padding:.2rem .35rem;";const l=document.createElement("button");return l.type="button",l.className="btn btn-xs btn-danger",l.textContent="\u2715",l.style.flexShrink="0",l.addEventListener("click",()=>t.remove()),t.appendChild(c),t.appendChild(n),t.appendChild(s),t.appendChild(a),t.appendChild(l),t}function ee(e,t,c){const n=document.createElement("div");n.dataset.logicSection="validation",n.appendChild(N("Custom Validation"));const s=document.createElement("div");s.className="fb-logic-val-rules",(e||[]).forEach(l=>s.appendChild(A(l))),n.appendChild(s);const a=document.createElement("button");return a.type="button",a.className="btn btn-xs btn-ghost",a.style.cssText="font-size:.73rem;margin-top:.2rem;",a.textContent="+ Add validation rule",a.addEventListener("click",()=>s.appendChild(A(null))),n.appendChild(a),n}function te(e,t,c){const n=document.createElement("div");n.dataset.logicSection="cascade",n.appendChild(N("Cascade Options"));const s=document.createElement("div");s.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.5rem;";const a=document.createElement("span");a.textContent="Source field:",a.style.cssText="font-size:.8rem;flex-shrink:0;";const l=document.createElement("select");l.className="form-input fb-logic-cascade-source",l.style.cssText="flex:1;font-size:.8rem;padding:.25rem .4rem;";const d=document.createElement("option");d.value="",d.textContent="\u2014 none \u2014",l.appendChild(d),c.forEach(f=>{const r=document.createElement("option");r.value=f.name,r.textContent=f.label||f.name,f.name===e.sourceField&&(r.selected=!0),l.appendChild(r)}),s.appendChild(a),s.appendChild(l),n.appendChild(s);const o=document.createElement("p");o.textContent='Mapping JSON \u2014 {"value":[{"value":"...","label":"..."}]}',o.style.cssText="font-size:.73rem;color:var(--text-muted,#888);margin:.3rem 0 .2rem;";const i=document.createElement("textarea");i.className="form-input fb-logic-cascade-mapping",i.rows=4,i.style.cssText="font-family:monospace;font-size:.78rem;",i.placeholder='{"uk": [{"value": "london", "label": "London"}]}',i.value=e.mapping?JSON.stringify(e.mapping,null,2):"",n.appendChild(o),n.appendChild(i);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;font-size:.78rem;",p.placeholder=`option1:Option 1
4
+ option2:Option 2`,p.value=(e.defaultOptions||[]).map(f=>{const r=typeof f=="string"?f:f.value??"",u=typeof f=="string"?f:f.label??r;return r===u?r:`${r}:${u}`}).join(`
5
+ `),n.appendChild(m),n.appendChild(p),n}function ne(e,t){const c=e.logic||{},n=b.filter((p,f)=>f!==t&&p.type!=="page-break"&&p.type!=="spacer"),s=document.createElement("div");s.className="fb-field-logic",s.style.cssText="margin-top:.75rem;border-top:1px solid var(--border-color,#333);padding-top:.5rem;";const a=document.createElement("div");a.style.cssText="display:flex;align-items:center;justify-content:space-between;cursor:pointer;padding:.15rem 0;";const l=document.createElement("span");l.style.cssText="font-size:.8rem;font-weight:600;color:var(--text-muted,#888);",l.textContent="\u26A1 Conditional Logic";const d=M(c),o=document.createElement("button");o.type="button",o.className="btn btn-xs btn-ghost",o.textContent=d?"\u25BE":"\u25B8";const i=document.createElement("div");i.className="fb-logic-body",i.style.cssText="padding:.25rem 0 .25rem;"+(d?"":"display:none;"),a.addEventListener("click",()=>{const p=i.style.display==="none";i.style.display=p?"":"none",o.textContent=p?"\u25BE":"\u25B8"}),a.appendChild(l),a.appendChild(o),s.appendChild(a),i.appendChild(Z(c.visibility||{},t,n)),i.appendChild($(c.requirement||{},t,n)),i.appendChild(ee(c.validation||[],t,n));const m=document.getElementById(`fb-type-${t}`)?.value||e.type;return I.has(m)&&i.appendChild(te(c.cascade||{},t,n)),s.appendChild(i),s}function j(e,t,c){const n=document.createElement("div");return n.className="fb-field-extras",I.has(e)&&n.appendChild(ae(t.options||[],c)),e==="textarea"&&n.appendChild(k([w("Rows",`fb-rows-${c}`,"number",t.formConfig?.rows||4,"Height of textarea")])),(e==="string"||e==="textarea")&&n.appendChild(k([w("Min Length",`fb-minlength-${c}`,"number",t.minLength||"",""),w("Max Length",`fb-maxlength-${c}`,"number",t.maxLength||"","")])),e==="number"&&n.appendChild(k([w("Min",`fb-min-${c}`,"number",t.min??"",""),w("Max",`fb-max-${c}`,"number",t.max??"","")])),n.children.length?n:null}function k(e){const t=document.createElement("div");return t.style.cssText="display:flex;gap:.75rem;margin-bottom:.6rem;",e.forEach(c=>{c&&t.appendChild(c)}),t}function w(e,t,c,n,s){const a=document.createElement("div");a.style.flex="1";const l=document.createElement("label");l.htmlFor=t,l.className="form-label",l.textContent=e,l.style.fontSize=".8rem";const d=document.createElement("input");if(d.id=t,d.type=c||"text",d.className="form-input",d.value=n??"",a.appendChild(l),a.appendChild(d),s){const o=document.createElement("p");o.className="form-hint text-muted",o.textContent=s,o.style.cssText="font-size:.73rem;margin-top:.2rem;",a.appendChild(o)}return a}function le(e,t,c,n){const s=document.createElement("div");s.style.flex="1";const a=document.createElement("label");a.htmlFor=t,a.className="form-label",a.textContent=e,a.style.fontSize=".8rem";const l=document.createElement("select");return l.id=t,l.className="form-input",c.forEach(d=>{const o=document.createElement("option");o.value=d.value,o.textContent=d.label,d.value===n&&(o.selected=!0),l.appendChild(o)}),s.appendChild(a),s.appendChild(l),s}function D(e,t,c){const n=document.createElement("div");n.style.cssText="flex:0;min-width:80px;display:flex;flex-direction:column;justify-content:flex-end;";const s=document.createElement("label");s.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;font-size:.8rem;white-space:nowrap;";const a=document.createElement("input");return a.id=t,a.type="checkbox",a.checked=c,s.appendChild(a),s.appendChild(document.createTextNode(e)),n.appendChild(s),n}function ae(e,t){const c=document.createElement("div");c.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 s=document.createElement("textarea");return s.id=`fb-options-${t}`,s.className="form-input",s.rows=4,s.placeholder=`yes:Yes
6
+ no:No
7
+ maybe:Maybe`,s.value=(e||[]).map(a=>{const l=typeof a=="string"?a:a.value??"",d=typeof a=="string"?a:a.label??l;return l===d?l:`${l}:${d}`}).join(`
8
+ `),s.style.fontFamily="monospace",c.appendChild(n),c.appendChild(s),c}function V(e){return v(),{title:e.find("#field-title").val().trim(),slug:e.find("#field-slug").val().trim(),description:e.find("#field-description").val().trim(),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()},...L!==null&&{collection:L}}}}function se(e){const t=[];let c=[],n="Step 1",s="";return e.forEach(a=>{a.type==="page-break"?(t.push({title:n,description:s,fields:c}),c=[],n=a.label||`Step ${t.length+1}`,s=a.description||""):a.type!=="spacer"&&c.push(a)}),(c.length||t.length===0)&&t.push({title:n,description:s,fields:c}),t}function H(e,t){const c={};return e.forEach(n=>{if(n.type==="page-break"||n.type==="spacer")return;const s={...n.formConfig||{}};s.span==="full"&&t&&(s.span=t);const a=n.type==="checkbox"?"boolean":n.type==="date"?"string":n.type;c[n.name]={type:a,label:n.label,required:n.required,options:n.options,formConfig:{...n.placeholder&&{placeholder:n.placeholder},...n.helper&&{hint:n.helper},...s}}}),c}function U(e,t){const c=typeof e=="string"?document.querySelector(e):e;c&&(t||[]).forEach(n=>{if(n.type!=="date"||!n.name)return;const s=c.querySelector(`[name="${n.name}"]`);s&&s.type!=="date"&&(s.type="date")})}export const formEditorView={templateUrl:"/admin/js/templates/form-editor.html",async onMount(e){b=[],C=null,L=null;const c=window.location.hash.match(/\/forms\/edit\/([^/?#]+)/);C=c?c[1]:null;let n=null;if(C)try{n=await T(`/forms/${C}`),b=n.fields||[],L=n.actions?.collection??null}catch{E.toast("Could not load form.",{type:"error"})}if(n?e.find("#editor-title").get(0).textContent=`Edit: ${n.title}`:e.find("#editor-title").get(0).textContent="New Form",C||e.find("#field-title").get(0).addEventListener("input",function(){e.find("#field-slug").val(O(this.value))}),E.tabs(e.find("#editor-tabs").get(0)),n){e.find("#field-title").val(n.title),e.find("#field-slug").val(n.slug),e.find("#field-description").val(n.description||"");const l=n.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 d=n.actions?.email||{};e.find("#action-email-enabled").prop("checked",d.enabled||!1),e.find("#action-email-recipients").val(d.recipients||""),e.find("#action-email-subject-prefix").val(d.subjectPrefix||"");const o=n.actions?.webhook||{};e.find("#action-webhook-enabled").prop("checked",o.enabled||!1),e.find("#action-webhook-url").val(o.url||""),e.find("#action-webhook-method").val(o.method||"POST")}const s=n?.settings?.actionSlug||"";try{const l=await T("/actions").catch(()=>[]),d=e.find("#action-cms-slug").get(0);if((Array.isArray(l)?l:[]).forEach(o=>{const i=document.createElement("option");i.value=o.slug,i.textContent=o.title||o.slug,o.slug===s&&(i.selected=!0),d.appendChild(i)}),!l.length){const o=document.createElement("option");o.value="",o.textContent="No actions available",o.disabled=!0,d.appendChild(o)}}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(d=>{d.style.display=l?"flex":"none"})}),x(e);const a=e.find("#add-element-menu").get(0);e.find("#add-element-btn").get(0).addEventListener("click",l=>{l.stopPropagation(),a.style.display=a.style.display==="none"?"":"none"}),q&&document.removeEventListener("click",q),q=()=>{a&&(a.style.display="none")},document.addEventListener("click",q),e.find("#add-field-btn").get(0).addEventListener("click",()=>{a&&(a.style.display="none"),v();const l=b.length;b.push({name:`field_${l+1}`,type:"string",label:"New Field",required:!1,placeholder:""}),x(e);const o=e.find("#fields-list").get(0)?.lastElementChild;if(o){const i=o.querySelector(".fb-field-body");i&&(i.style.display="")}}),e.find("#add-spacer-btn").get(0).addEventListener("click",()=>{a&&(a.style.display="none"),v(),b.push({type:"spacer"}),x(e)}),e.find("#add-page-break-btn").get(0).addEventListener("click",()=>{a&&(a.style.display="none"),v();const l=b.filter(d=>d.type==="page-break").length+2;b.push({type:"page-break",label:`Step ${l}`,description:""}),x(e)}),e.find("#save-form-btn").get(0).addEventListener("click",async()=>{const l=V(e);if(!l.title){E.toast("Please enter a form title.",{type:"error"});return}try{C?(await T(`/forms/${C}`,{method:"PUT",body:JSON.stringify(l)}),E.toast("Form saved.",{type:"success"})):(C=(await T("/forms",{method:"POST",body:JSON.stringify(l)})).slug,R.navigate(`/forms/edit/${C}`),E.toast("Form created.",{type:"success"}))}catch(d){E.toast(d.message||"Failed to save form.",{type:"error"})}}),e.find("#preview-btn").get(0).addEventListener("click",()=>{const l=V(e),d=e.find("#preview-container").get(0);if(!d)return;const o=e.find("#preview-test-result").get(0),i=e.find("#preview-test-badge").get(0);o&&(o.style.display="none",o.textContent=""),i&&(i.style.display=C?"":"none"),e.find("#preview-card").get(0).style.display="",d.textContent="";const m=document.createElement("div");m.id="fb-preview-form",d.appendChild(m);const p=C?async r=>{o&&(o.style.display="none",o.textContent="");try{const u=await fetch(`/api/forms/submit/${C}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)}),g=await u.json();if(!u.ok)throw new Error(g.error||"Submission failed.");o&&(o.textContent=g.message||l.settings?.successMessage||"Submitted successfully.",o.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(u){o&&(o.textContent=u.message,o.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(u.message,{type:"error"})}return!1}:()=>!1,f=l.fields.some(r=>r.type==="page-break");if(typeof F<"u"){const r=l.settings?.columns||2,u=l.settings?.layout||"stacked";if(f&&F.wizard){const g=se(l.fields).map(h=>({title:h.title,description:h.description,fields:H(h.fields,r)}));F.wizard("#fb-preview-form",{schema:{steps:g},onSubmit:p}),U("#fb-preview-form",l.fields)}else if(F.render){const g=H(l.fields,r),h={};if(l.fields.forEach(y=>{if(!(!y.name||y.type==="page-break"||y.type==="spacer")&&(y.type==="select"||y.type==="multiselect")&&y.required){const S=(y.options||[])[0];S&&(h[y.name]=typeof S=="object"?S.value:S)}}),F.render("#fb-preview-form",g,h,{submitText:l.settings?.submitText||"Submit",layout:u,columns:r,onSubmit:p}),u==="grid"&&l.settings?.submitSpan==="full"){const y=document.querySelector("#fb-preview-form .form-buttons");y&&y.classList.add("col-span-full")}U("#fb-preview-form",l.fields)}window.FormLogicEngine&&l.fields.some(g=>g.logic)&&requestAnimationFrame(()=>{new window.FormLogicEngine.FormLogicRuntime({fields:l.fields},m).init()})}else{const r=document.createElement("p");r.textContent=`${l.fields.filter(u=>u.type!=="page-break").length} field(s): ${l.fields.filter(u=>u.type!=="page-break").map(u=>u.label).join(", ")}`,r.style.cssText="color:var(--muted);font-style:italic;",m.appendChild(r)}e.find("#preview-card").get(0).scrollIntoView({behavior:"smooth",block:"start"})})}};
@@ -0,0 +1 @@
1
+ import{apiRequest as x}from"/admin/js/api.js";let c=null,w=[],i=[];export const formSubmissionsView={templateUrl:"/admin/js/templates/form-submissions.html",async onMount(s){const r=window.location.hash.match(/\/forms\/([^/?#]+)\/submissions/);if(c=r?r[1]:null,!c){E.toast("No form selected.",{type:"error"});return}try{const e=await x(`/forms/${c}`);w=(e.fields||[]).filter(n=>n.type!=="page-break"),s.find("#submissions-title").get(0).textContent=`${e.title} \u2014 Submissions`}catch{E.toast("Could not load form definition.",{type:"error"})}await L(s),s.find("#export-btn").off("click").on("click",()=>{const e=E.slideover({title:"Export Submissions",size:"sm",position:"right"}),n=document.createElement("div");n.style.cssText="padding:1.25rem;display:flex;flex-direction:column;gap:1rem;";function a(l,u,f,y){const C=document.createElement("div");C.style.cssText="display:flex;flex-direction:column;gap:.4rem;";const b=document.createElement("button");b.className="btn btn-ghost",b.style.cssText="justify-content:flex-start;gap:.5rem;";const k=document.createElement("span");return k.setAttribute("data-icon",u),b.appendChild(k),b.appendChild(document.createTextNode(" "+l)),b.addEventListener("click",()=>{fetch(f,{headers:{Authorization:"Bearer "+(S.get("auth_token")||"")}}).then(v=>v.blob()).then(v=>{const h=document.createElement("a");h.href=URL.createObjectURL(v),h.download=y,document.body.appendChild(h),h.click(),document.body.removeChild(h),URL.revokeObjectURL(h.href),e.close()}).catch(()=>E.toast("Export failed.",{type:"error"}))}),C.appendChild(b),C}n.appendChild(a("Export as CSV","file-text",`/api/forms/${c}/submissions/export`,`${c}-submissions.csv`)),n.appendChild(a("Export as JSON","code",`/api/forms/${c}/submissions/export/json`,`${c}-submissions.json`)),e.setContent(n),Domma.icons.scan(n),e.open()}),s.find("#clear-all-btn").off("click").on("click",async()=>{if(await E.confirm("Delete all submissions? This cannot be undone."))try{await x(`/forms/${c}/submissions`,{method:"DELETE"}),i=[],E.toast("All submissions cleared.",{type:"success"}),g([],s)}catch{E.toast("Failed to clear submissions.",{type:"error"})}});const d=s.find("#sub-search").get(0),m=s.find("#sub-date-from").get(0),o=s.find("#sub-date-to").get(0);function t(){const e=d?.value.toLowerCase()||"",n=m?.value?new Date(m.value):null,a=o?.value?new Date(o.value+"T23:59:59"):null,l=i.filter(u=>{if(e&&!Object.values(u.data||{}).map(y=>String(y).toLowerCase()).some(y=>y.includes(e)))return!1;if(n||a){const f=u.meta?.createdAt?new Date(u.meta.createdAt):null;if(!f||n&&f<n||a&&f>a)return!1}return!0});g(l,s)}d&&d.addEventListener("input",t),m&&m.addEventListener("change",t),o&&o.addEventListener("change",t),Domma.icons.scan()}};async function L(s){try{i=await x(`/forms/${c}/submissions`)}catch{i=[],E.toast("Could not load submissions.",{type:"error"})}g(i,s)}function g(s,p){const r=p.find("#sub-count").get(0);r&&(s.length===i.length?r.textContent=`${i.length} submission${i.length!==1?"s":""}`:r.textContent=`Showing ${s.length} of ${i.length}`);const m=[...w.map(t=>({key:`data.${t.name}`,title:t.label||t.name,render:(e,n)=>{const a=n.data?.[t.name]??"",l=String(a),u=document.createElement("span");return u.title=l,u.textContent=l.length>80?l.slice(0,80)+"\u2026":l,u.outerHTML}})),{key:"meta",title:"Date",render:t=>D(t?.createdAt).format("DD MMM YYYY HH:mm")},{key:"id",title:"",render:t=>{const e=document.createElement("button");return e.className="btn btn-sm btn-danger js-delete-submission",e.dataset.id=t,e.textContent="Delete",e.style.whiteSpace="nowrap",e.outerHTML}}];T.create("#submissions-table",{data:s,columns:m,emptyMessage:"No submissions yet."});const o=document.querySelector("#submissions-table");o&&(o.querySelectorAll(".js-delete-submission").forEach(t=>{t.addEventListener("click",async e=>{e.stopPropagation();const n=t.dataset.id;if(await E.confirm("Delete this submission?"))try{await x(`/forms/${c}/submissions/${n}`,{method:"DELETE"}),i=i.filter(l=>l.id!==n),E.toast("Submission deleted.",{type:"success"}),g(i,p)}catch{E.toast("Failed to delete submission.",{type:"error"})}})}),o.querySelectorAll("tbody tr").forEach((t,e)=>{const n=s[e];n&&(t.style.cursor="pointer",t.addEventListener("click",a=>{a.target.closest(".js-delete-submission")||M(n)}))}))}function M(s){const p=document.createElement("div"),r=document.createElement("div");r.style.cssText="display:flex;flex-direction:column;gap:.75rem;margin-bottom:1.25rem;",w.forEach(o=>{const t=s.data?.[o.name];if(t==null||t==="")return;const e=document.createElement("div");e.style.cssText="border-bottom:1px solid var(--border-color,#333);padding-bottom:.6rem;";const n=document.createElement("strong");n.textContent=o.label||o.name,n.style.cssText="display:block;font-size:.8rem;color:var(--text-muted,#888);margin-bottom:.2rem;";const a=document.createElement("p");a.textContent=String(t),a.style.cssText="margin:0;word-break:break-word;",e.appendChild(n),e.appendChild(a),r.appendChild(e)});const d=document.createElement("div");if(d.style.cssText="font-size:.8rem;color:var(--text-muted,#888);display:flex;flex-direction:column;gap:.3rem;border-top:1px solid var(--border-color,#333);padding-top:.75rem;",s.meta?.createdAt){const o=document.createElement("span");o.textContent=`Submitted: ${D(s.meta.createdAt).format("DD MMM YYYY HH:mm")}`,d.appendChild(o)}if(s.meta?.ip){const o=document.createElement("span");o.textContent=`IP: ${s.meta.ip}`,d.appendChild(o)}p.appendChild(r),p.appendChild(d);const m=E.modal({title:"Submission Details",size:"md"});m.element.appendChild(p),m.open()}
@@ -0,0 +1 @@
1
+ import{apiRequest as p}from"/admin/js/api.js";function u(i){return String(i).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}export const formsView={templateUrl:"/admin/js/templates/forms.html",async onMount(i){await y(i),i.find("#create-form-btn").off("click").on("click",()=>{const c=E.modal({title:"Create Form",size:"sm"}),t=document.createElement("div");t.style.cssText="padding:.25rem 0 .5rem;";const e=document.createElement("div");F.create({title:{type:"string",label:"Form Title",placeholder:"e.g. Contact, Feedback\u2026",required:!0}},{},{showSubmitButton:!1}).renderTo(e),t.appendChild(e);const n=document.createElement("div");n.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:.75rem;";const s=document.createElement("button");s.className="btn btn-ghost",s.textContent="Cancel";const o=document.createElement("button");o.className="btn btn-primary",o.textContent="Create",n.appendChild(s),n.appendChild(o),t.appendChild(n),c.element.appendChild(t),c.open();const a=e.querySelector('input[name="title"]');setTimeout(()=>a?.focus(),50);async function m(){const r=a?.value.trim();if(r)try{const l=await p("/forms",{method:"POST",body:JSON.stringify({title:r})});c.close(),R.navigate(`/forms/edit/${l.slug}`)}catch(l){E.toast(l.message||"Failed to create form.",{type:"error"})}}s.addEventListener("click",()=>c.close()),o.addEventListener("click",m),a?.addEventListener("keydown",r=>{r.key==="Enter"&&m()})}),i.find("#test-email-btn").off("click").on("click",async()=>{const t=(window.__CMS_SITE__?.smtp||{}).fromAddress||"",e=E.modal({title:"Send Test Email",size:"sm"}),n=document.createElement("div");n.style.cssText="padding:.25rem 0 .5rem;";const s=document.createElement("div");s.className="mb-3";const o=document.createElement("label");o.className="form-label",o.textContent="Send to";const a=document.createElement("input");a.type="email",a.className="form-input",a.value=t,a.placeholder="test@example.com",s.appendChild(o),s.appendChild(a),n.appendChild(s);const m=document.createElement("p");m.className="text-muted",m.style.cssText="font-size:.8rem;margin-bottom:.75rem;",m.textContent="SMTP settings are configured in Site Settings.",n.appendChild(m);const r=document.createElement("div");r.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:.75rem;";const l=document.createElement("button");l.className="btn btn-ghost",l.textContent="Cancel";const d=document.createElement("button");d.className="btn btn-primary",d.textContent="Send",r.appendChild(l),r.appendChild(d),n.appendChild(r),e.element.appendChild(n),e.open(),setTimeout(()=>a?.focus(),50),l.addEventListener("click",()=>e.close()),d.addEventListener("click",async()=>{const f=a.value.trim();if(f){d.disabled=!0;try{await p("/forms/test-email",{method:"POST",body:JSON.stringify({to:f})}),e.close(),E.toast("Test email sent.",{type:"success"})}catch(b){E.toast(b.message||"Failed to send test email.",{type:"error"})}finally{d.disabled=!1}}})}),Domma.icons.scan()}};async function y(i){let c=[];try{c=await p("/forms")}catch{E.toast("Could not load forms.",{type:"error"})}T.create("#forms-table",{data:c,columns:[{key:"title",title:"Title",render:(t,e)=>{const n=document.createElement("a");return n.href=`#/forms/edit/${u(e.slug)}`,n.textContent=t,n.style.fontWeight="600",n.outerHTML}},{key:"slug",title:"Slug",render:t=>{const e=document.createElement("code");return e.textContent=t,e.outerHTML}},{key:"fields",title:"Field Count",render:t=>String(t?.length??0)},{key:"submissionCount",title:"Submission Count",render:t=>String(t??0)},{key:"slug",title:"Actions",render:t=>{const e=document.createElement("div");e.style.cssText="display:flex;gap:.4rem;justify-content:flex-end;";const n=document.createElement("a");n.href=`#/forms/edit/${u(t)}`,n.className="btn btn-sm btn-primary",n.textContent="Edit";const s=document.createElement("a");s.href=`#/forms/${u(t)}/submissions`,s.className="btn btn-sm btn-ghost",s.textContent="Submissions";const o=document.createElement("button");return o.className="btn btn-sm btn-danger js-delete-form",o.dataset.slug=t,o.textContent="Delete",e.appendChild(n),e.appendChild(s),e.appendChild(o),e.outerHTML}}],emptyMessage:'No forms yet. Click "Create Form" to get started.'}),document.querySelectorAll(".js-delete-form").forEach(t=>{t.addEventListener("click",async()=>{const e=t.dataset.slug;if(await E.confirm(`Delete form "${e}" and all its submissions? This cannot be undone.`))try{await p(`/forms/${e}`,{method:"DELETE"}),E.toast("Form deleted.",{type:"success"}),await y(i)}catch{E.toast("Failed to delete form.",{type:"error"})}})}),Domma.icons.scan()}
@@ -1 +1 @@
1
- import{dashboardView as o}from"./dashboard.js";import{pagesView as i}from"./pages.js";import{pageEditorView as r}from"./page-editor.js";import{settingsView as t}from"./settings.js";import{navigationView as e}from"./navigation.js";import{layoutsView as m}from"./layouts.js";import{mediaView as s}from"./media.js";import{loginView as n}from"./login.js";import{usersView as p}from"./users.js";import{userEditorView as a}from"./user-editor.js";import{pluginsView as l}from"./plugins.js";import{documentationView as w}from"./documentation.js";import{tutorialsView as f}from"./tutorials.js";import{collectionsView as V}from"./collections.js";import{collectionEditorView as c}from"./collection-editor.js";import{collectionEntriesView as d}from"./collection-entries.js";export const views={dashboard:o,pages:i,pageEditor:r,settings:t,navigation:e,layouts:m,media:s,login:n,users:p,userEditor:a,plugins:l,documentation:w,tutorials:f,collections:V,collectionEditor:c,collectionEntries:d};
1
+ import{dashboardView as o}from"./dashboard.js";import{pagesView as i}from"./pages.js";import{pageEditorView as r}from"./page-editor.js";import{settingsView as t}from"./settings.js";import{navigationView as e}from"./navigation.js";import{layoutsView as m}from"./layouts.js";import{mediaView as s}from"./media.js";import{loginView as p}from"./login.js";import{usersView as f}from"./users.js";import{userEditorView as w}from"./user-editor.js";import{pluginsView as n}from"./plugins.js";import{documentationView as V}from"./documentation.js";import{tutorialsView as l}from"./tutorials.js";import{apiReferenceView as c}from"./api-reference.js";import{collectionsView as a}from"./collections.js";import{collectionEditorView as d}from"./collection-editor.js";import{collectionEntriesView as E}from"./collection-entries.js";import{formsView as u}from"./forms.js";import{formEditorView as g}from"./form-editor.js";import{formSubmissionsView as v}from"./form-submissions.js";import{viewsListView as b}from"./views-list.js";import{viewEditorView as k}from"./view-editor.js";import{viewPreviewView as y}from"./view-preview.js";import{actionsListView as L}from"./actions-list.js";import{actionEditorView as P}from"./action-editor.js";import{proDocsView as h}from"./pro-docs.js";import{blocksView as D}from"./blocks.js";import{blockEditorView as R}from"./block-editor.js";import{myProfileView as S}from"./my-profile.js";import{rolesView as x}from"./roles.js";import{roleEditorView as j}from"./role-editor.js";export const views={dashboard:o,pages:i,pageEditor:r,settings:t,navigation:e,layouts:m,media:s,login:p,users:f,userEditor:w,plugins:n,documentation:V,tutorials:l,apiReference:c,collections:a,collectionEditor:d,collectionEntries:E,forms:u,formEditor:g,formSubmissions:v,viewsList:b,viewEditor:k,viewPreview:y,actionsList:L,actionEditor:P,proDocs:h,blocks:D,blockEditor:R,myProfile:S,roles:x,roleEditor:j};
@@ -1,4 +1,4 @@
1
- import{api as i,isAuthenticated as c,setAuthData as o}from"../api.js";const f={name:{type:"string",required:!0,minLength:2,label:"Full Name",formConfig:{placeholder:"Your name",autocomplete:"name"}},email:{type:"email",required:!0,label:"Email Address",formConfig:{placeholder:"admin@example.com",autocomplete:"email"}},password:{type:"password",required:!0,minLength:8,label:"Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"new-password",tooltip:"Minimum 8 characters"}}},b={email:{type:"email",required:!0,label:"Email Address",formConfig:{placeholder:"you@example.com",autocomplete:"email"}},password:{type:"password",required:!0,label:"Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"current-password"}}},p=[{id:"charcoal-dark",label:"Charcoal",dark:!0,primary:"#5b8cff",bg:"#1a1d23"},{id:"charcoal-light",label:"Charcoal",dark:!1,primary:"#4a7aff",bg:"#f4f5f7"},{id:"ocean-dark",label:"Ocean",dark:!0,primary:"#00b4d8",bg:"#0d1b2a"},{id:"ocean-light",label:"Ocean",dark:!1,primary:"#0096c7",bg:"#e8f4f8"},{id:"forest-dark",label:"Forest",dark:!0,primary:"#52b788",bg:"#1a231e"},{id:"forest-light",label:"Forest",dark:!1,primary:"#40916c",bg:"#f0f7f4"},{id:"sunset-dark",label:"Sunset",dark:!0,primary:"#ff6b6b",bg:"#1f1a1a"},{id:"sunset-light",label:"Sunset",dark:!1,primary:"#e05555",bg:"#fff0f0"},{id:"royal-dark",label:"Royal",dark:!0,primary:"#9b59b6",bg:"#1a1525"},{id:"royal-light",label:"Royal",dark:!1,primary:"#8e44ad",bg:"#f5f0fa"},{id:"lemon-dark",label:"Lemon",dark:!0,primary:"#f1c40f",bg:"#1a1a10"},{id:"lemon-light",label:"Lemon",dark:!1,primary:"#d4ac0d",bg:"#fffef0"},{id:"silver-dark",label:"Silver",dark:!0,primary:"#95a5a6",bg:"#1c1e20"},{id:"silver-light",label:"Silver",dark:!1,primary:"#7f8c8d",bg:"#f2f3f4"},{id:"grayve-dark",label:"Grayve",dark:!0,primary:"#00bcd4",bg:"#1a1e21"},{id:"grayve-light",label:"Grayve",dark:!1,primary:"#00838f",bg:"#ffffff"}];export const loginView={templateUrl:"/admin/js/templates/login.html",async onMount(e){if(c()){R.navigate("/");return}let t=!1;try{t=(await i.auth.setupStatus()).needsSetup}catch{E.toast("Could not reach the server.",{type:"error"})}t?(s(e,"setup"),g(e)):(s(e,"login"),y(e)),Domma.icons.scan()}};const u=["setup","onboarding-site","onboarding-theme","onboarding-done","login"];function s(e,t){u.forEach(a=>e.find(`#${a}-panel`).hide()),e.find(`#${t}-panel`).show(),Domma.icons.scan()}function g(e){F.render("#setup-form-container",f,{},{layout:"stacked",submitText:"Create admin account",onSubmit:async t=>{try{const a=await i.auth.setup(t);o(a),s(e,"onboarding-site"),h(e)}catch(a){return E.toast(a.message||"Setup failed. Please try again.",{type:"error"}),!1}}})}function h(e){e.find("#ob-site-skip").on("click",t=>{t.preventDefault(),s(e,"onboarding-theme"),n(e),m(e)}),e.find("#ob-site-btn").on("click",async()=>{e.find("#ob-site-error").hide();const t=e.find("#ob-title").val().trim(),a=e.find("#ob-tagline").val().trim(),r=e.find("#ob-site-btn").prop("disabled",!0).text("Saving\u2026");try{const l=await i.settings.get();await i.settings.save({...l,title:t||l.title,tagline:a||l.tagline,seo:{...l.seo||{},defaultTitle:t||l.seo&&l.seo.defaultTitle}});const d=await i.navigation.get();await i.navigation.save({...d,brand:{...d.brand||{},text:t||d.brand.text}}),s(e,"onboarding-theme"),n(e),m(e)}catch(l){showError(e,"ob-site-error",l.message||"Could not save site details.")}finally{r.prop("disabled",!1).text("Continue")}})}function n(e){const t=e.find("#theme-grid").empty();p.forEach(a=>{const r=$(`
1
+ import{api as s,isAuthenticated as m,setAuthData as d}from"../api.js";const u={name:{type:"string",required:!0,minLength:2,label:"Full Name",formConfig:{placeholder:"Your name",autocomplete:"name"}},email:{type:"email",required:!0,label:"Email Address",formConfig:{placeholder:"admin@example.com",autocomplete:"email"}},password:{type:"password",required:!0,minLength:8,label:"Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"new-password",tooltip:"Minimum 8 characters"}}},p={email:{type:"email",required:!0,label:"Email Address",formConfig:{placeholder:"you@example.com",autocomplete:"email"}},password:{type:"password",required:!0,label:"Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"current-password"}}},b=[{id:"charcoal-dark",label:"Charcoal",dark:!0,primary:"#5b8cff",bg:"#1a1d23"},{id:"charcoal-light",label:"Charcoal",dark:!1,primary:"#4a7aff",bg:"#f4f5f7"},{id:"ocean-dark",label:"Ocean",dark:!0,primary:"#00b4d8",bg:"#0d1b2a"},{id:"ocean-light",label:"Ocean",dark:!1,primary:"#0096c7",bg:"#e8f4f8"},{id:"forest-dark",label:"Forest",dark:!0,primary:"#52b788",bg:"#1a231e"},{id:"forest-light",label:"Forest",dark:!1,primary:"#40916c",bg:"#f0f7f4"},{id:"sunset-dark",label:"Sunset",dark:!0,primary:"#ff6b6b",bg:"#1f1a1a"},{id:"sunset-light",label:"Sunset",dark:!1,primary:"#e05555",bg:"#fff0f0"},{id:"royal-dark",label:"Royal",dark:!0,primary:"#9b59b6",bg:"#1a1525"},{id:"royal-light",label:"Royal",dark:!1,primary:"#8e44ad",bg:"#f5f0fa"},{id:"lemon-dark",label:"Lemon",dark:!0,primary:"#f1c40f",bg:"#1a1a10"},{id:"lemon-light",label:"Lemon",dark:!1,primary:"#d4ac0d",bg:"#fffef0"},{id:"silver-dark",label:"Silver",dark:!0,primary:"#95a5a6",bg:"#1c1e20"},{id:"silver-light",label:"Silver",dark:!1,primary:"#7f8c8d",bg:"#f2f3f4"},{id:"grayve-dark",label:"Grayve",dark:!0,primary:"#00bcd4",bg:"#1a1e21"},{id:"grayve-light",label:"Grayve",dark:!1,primary:"#00838f",bg:"#ffffff"},{id:"mint-dark",label:"Mint",dark:!0,primary:"#00c896",bg:"#0f1f1a"},{id:"mint-light",label:"Mint",dark:!1,primary:"#00a878",bg:"#f0faf6"}];export const loginView={templateUrl:"/admin/js/templates/login.html",async onMount(e){if(m()){R.navigate("/");return}const t=window.location.hash,a=t.match(/[?&]token=([a-f0-9]{64})/);if(t.startsWith("#/reset-password")&&a){i(e,"reset"),C(e,a[1]),Domma.icons.scan();return}if(t.startsWith("#/reset-password")){R.navigate("/login");return}let r=!1;try{r=(await s.auth.setupStatus()).needsSetup}catch{E.toast("Could not reach the server.",{type:"error"})}r?(i(e,"setup"),w(e)):(i(e,"login"),v(e)),Domma.icons.scan()}};const g={email:{type:"email",required:!0,label:"Email Address",formConfig:{placeholder:"you@example.com",autocomplete:"email"}}},h={password:{type:"password",required:!0,minLength:8,label:"New Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"new-password",tooltip:"Minimum 8 characters"}},confirmPassword:{type:"password",required:!0,label:"Confirm Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"new-password"}}},y=["setup","onboarding-site","onboarding-theme","onboarding-done","login","forgot","forgot-success","reset"];function i(e,t){y.forEach(a=>e.find(`#${a}-panel`).hide()),e.find(`#${t}-panel`).show(),Domma.icons.scan()}function n(e,t,a){e.find(`#${t}`).text(a).show()}function w(e){F.render("#setup-form-container",u,{},{layout:"stacked",submitText:"Create admin account",onSubmit:async t=>{try{const a=await s.auth.setup(t);d(a),i(e,"onboarding-site"),k(e)}catch(a){return E.toast(a.message||"Setup failed. Please try again.",{type:"error"}),!1}}})}function k(e){e.find("#ob-site-skip").on("click",t=>{t.preventDefault(),i(e,"onboarding-theme"),c(e),f(e)}),e.find("#ob-site-btn").on("click",async()=>{e.find("#ob-site-error").hide();const t=e.find("#ob-title").val().trim(),a=e.find("#ob-tagline").val().trim(),r=e.find("#ob-site-btn").prop("disabled",!0).text("Saving\u2026");try{const o=await s.settings.get();await s.settings.save({...o,title:t||o.title,tagline:a||o.tagline,seo:{...o.seo||{},defaultTitle:t||o.seo&&o.seo.defaultTitle}});const l=await s.navigation.get();await s.navigation.save({...l,brand:{...l.brand||{},text:t||l.brand.text}}),i(e,"onboarding-theme"),c(e),f(e)}catch(o){n(e,"ob-site-error",o.message||"Could not save site details.")}finally{r.prop("disabled",!1).text("Continue")}})}function c(e){const t=e.find("#theme-grid").empty();b.forEach(a=>{const r=$(`
2
2
  <div class="theme-swatch" data-theme="${a.id}" title="${a.id}">
3
3
  <div class="theme-swatch-preview" style="background:${a.bg}">
4
4
  <div class="theme-swatch-accent" style="background:${a.primary}"></div>
@@ -8,4 +8,4 @@ import{api as i,isAuthenticated as c,setAuthData as o}from"../api.js";const f={n
8
8
  <span class="theme-swatch-mode">${a.dark?"Dark":"Light"}</span>
9
9
  </div>
10
10
  </div>
11
- `);r.on("click",()=>{e.find(".theme-swatch").removeClass("selected"),r.addClass("selected")}),t.append(r)}),e.find('[data-theme="charcoal-dark"]').addClass("selected")}function m(e){e.find("#ob-theme-skip").on("click",t=>{t.preventDefault(),s(e,"onboarding-done")}),e.find("#ob-theme-btn").on("click",async()=>{e.find("#ob-theme-error").hide();const t=e.find(".theme-swatch.selected").attr("data-theme")||"charcoal-dark",a=e.find("#ob-theme-btn").prop("disabled",!0).text("Applying\u2026");try{const r=await i.settings.get();await i.settings.save({...r,theme:t}),s(e,"onboarding-done")}catch(r){showError(e,"ob-theme-error",r.message||"Could not save theme.")}finally{a.prop("disabled",!1).text("Apply theme")}})}function y(e){F.render("#login-form-container",b,{},{layout:"stacked",submitText:"Sign in",onSubmit:async t=>{try{const a=await i.auth.login(t);o(a),R.navigate("/")}catch(a){return E.toast(a.message||"Invalid credentials. Please try again.",{type:"error"}),!1}}})}
11
+ `);r.on("click",()=>{e.find(".theme-swatch").removeClass("selected"),r.addClass("selected")}),t.append(r)}),e.find('[data-theme="charcoal-dark"]').addClass("selected")}function f(e){e.find("#ob-theme-skip").on("click",t=>{t.preventDefault(),i(e,"onboarding-done")}),e.find("#ob-theme-btn").on("click",async()=>{e.find("#ob-theme-error").hide();const t=e.find(".theme-swatch.selected").attr("data-theme")||"charcoal-dark",a=e.find("#ob-theme-btn").prop("disabled",!0).text("Applying\u2026");try{const r=await s.settings.get();await s.settings.save({...r,theme:t}),i(e,"onboarding-done")}catch(r){n(e,"ob-theme-error",r.message||"Could not save theme.")}finally{a.prop("disabled",!1).text("Apply theme")}})}function v(e){F.render("#login-form-container",p,{},{layout:"stacked",submitText:"Sign in",onSubmit:async t=>{try{const a=await s.auth.login(t);d(a),R.navigate("/")}catch(a){return E.toast(a.message||"Invalid credentials. Please try again.",{type:"error"}),!1}}}),e.find("#forgot-link").on("click",t=>{t.preventDefault(),i(e,"forgot"),S(e)})}function S(e){F.render("#forgot-form-container",g,{},{layout:"stacked",submitText:"Send reset link",onSubmit:async a=>{try{await s.auth.forgotPassword(a.email)}catch{}i(e,"forgot-success"),Domma.icons.scan()}});const t=a=>{a.preventDefault(),i(e,"login")};e.find("#forgot-back-link").on("click",t),e.find("#forgot-success-back-link").on("click",t)}function C(e,t){F.render("#reset-form-container",h,{},{layout:"stacked",submitText:"Set new password",onSubmit:async a=>{if(a.password!==a.confirmPassword)return E.toast("Passwords do not match.",{type:"error"}),!1;try{await s.auth.resetPassword(t,a.password),E.toast("Password updated. Please sign in.",{type:"success"}),window.location.hash="#/login"}catch(r){return E.toast(r.message||"Link expired or invalid.",{type:"error"}),!1}}})}
@@ -1 +1 @@
1
- import{api as r}from"../api.js";import{openImageEditor as D}from"../lib/image-editor.js";export const mediaView={templateUrl:"/admin/js/templates/media.html",async onMount(n){const o=a=>{const d=n.find("#media-grid").empty().get(0);if(!a.length){const e=document.createElement("p");e.className="text-muted",e.textContent="No media files yet. Upload one above.",d.appendChild(e);return}a.forEach(e=>{const t=/\.(png|jpe?g|gif|webp|svg)$/i.test(e.name),y=/\.(png|jpe?g|gif|webp|tiff?)$/i.test(e.name),s=document.createElement("div");s.className="media-card";const i=document.createElement("div");if(i.className="media-preview",t){const c=document.createElement("img");c.src=e.url+"?t="+Date.now(),c.alt=e.name,c.className="media-thumb",i.appendChild(c)}else{const c=document.createElement("div");c.className="media-thumb media-thumb--file";const v=document.createElement("span");v.setAttribute("data-icon","file"),c.appendChild(v),i.appendChild(c)}s.appendChild(i);const p=document.createElement("div");p.className="media-info";const m=document.createElement("span");m.className="media-name",m.title="Double-click to rename",m.textContent=e.name;const C=document.createElement("span");C.className="media-size",C.textContent=B(e.size),p.appendChild(m),p.appendChild(C),s.appendChild(p),m.addEventListener("dblclick",()=>x(m,e,o));const u=document.createElement("div");u.className="media-actions";const f=document.createElement("button");f.className="btn btn-sm btn-ghost";const w=document.createElement("span");w.setAttribute("data-icon","copy"),f.appendChild(w),f.addEventListener("click",()=>{navigator.clipboard.writeText(window.location.origin+e.url).then(()=>E.toast("URL copied.",{type:"success"})).catch(()=>E.toast("Copy failed.",{type:"error"}))});const b=document.createElement("button");b.className="btn btn-sm btn-ghost";const N=document.createElement("span");N.setAttribute("data-icon","type"),b.appendChild(N),b.addEventListener("click",()=>x(m,e,o));let l=null;if(y){l=document.createElement("button"),l.className="btn btn-sm btn-ghost";const c=document.createElement("span");c.setAttribute("data-icon","edit"),l.appendChild(c),l.addEventListener("click",async()=>{await D(e)&&o(await r.media.list().catch(()=>[]))})}const g=document.createElement("button");g.className="btn btn-sm btn-danger";const k=document.createElement("span");k.setAttribute("data-icon","trash"),g.appendChild(k),g.addEventListener("click",async()=>{if(await E.confirm(`Delete "${e.name}"?`))try{await r.media.delete(e.name),E.toast("File deleted.",{type:"success"}),o(await r.media.list().catch(()=>[]))}catch{E.toast("Delete failed.",{type:"error"})}}),u.appendChild(f),u.appendChild(b),l&&u.appendChild(l),u.appendChild(g),s.appendChild(u),d.appendChild(s),E.tooltip(f,{content:"Copy URL",position:"top"}),E.tooltip(b,{content:"Rename",position:"top"}),l&&E.tooltip(l,{content:"Edit image",position:"top"}),E.tooltip(g,{content:"Delete file",position:"top"})}),Domma.icons.scan(),Domma.effects.reveal(".media-card",{animation:"fade",stagger:40,duration:350})},h=await r.media.list().catch(()=>[]);o(h),Domma.effects.ripple("#upload-btn"),n.find("#upload-btn").on("click",()=>n.find("#file-input").get(0).click()),n.find("#file-input").on("change",async function(){if(!this.files.length)return;const a=new FormData;for(const t of this.files)a.append("file",t);const d=n.find("#upload-btn").get(0),e=d.lastChild;d.disabled=!0,e.textContent=" Uploading\u2026";try{if(!(await fetch("/api/media",{method:"POST",body:a,headers:{Authorization:"Bearer "+(S.get("auth_token")||"")}})).ok)throw new Error("Upload failed");E.toast("File uploaded.",{type:"success"}),o(await r.media.list().catch(()=>[]))}catch{E.toast("Upload failed.",{type:"error"})}finally{d.disabled=!1,e.textContent=" Upload",this.value=""}})}};function x(n,o,h){if(n.querySelector("input"))return;const a=o.name,d=a.lastIndexOf("."),e=d>0?a.slice(0,d):a,t=document.createElement("input");t.type="text",t.className="media-rename-input",t.value=a,t.title="",n.textContent="",n.appendChild(t),t.focus(),t.setSelectionRange(0,e.length);async function y(){const i=t.value.trim();if(!i||i===a)return s();t.disabled=!0;try{await r.media.rename(a,i),E.toast("File renamed.",{type:"success"}),h(await r.media.list().catch(()=>[]))}catch(p){E.toast(p.message||"Rename failed.",{type:"error"}),s()}}function s(){n.textContent=a,n.title="Double-click to rename"}t.addEventListener("keydown",i=>{i.key==="Enter"&&(i.preventDefault(),y()),i.key==="Escape"&&(i.preventDefault(),s())}),t.addEventListener("blur",()=>{setTimeout(()=>{n.contains(t)&&s()},150)})}function B(n){if(!n)return"0 B";const o=1024,h=["B","KB","MB","GB"],a=Math.floor(Math.log(n)/Math.log(o));return`${(n/Math.pow(o,a)).toFixed(1)} ${h[a]}`}
1
+ import{api as m}from"../api.js";import{openImageEditor as B}from"../lib/image-editor.js";export const mediaView={templateUrl:"/admin/js/templates/media.html",async onMount(t){const i=c=>{const d=t.find("#media-grid").empty().get(0);if(!c.length){const e=document.createElement("p");e.className="text-muted",e.textContent="No media files yet. Upload one above.",d.appendChild(e);return}c.forEach(e=>{const r=/\.(png|jpe?g|gif|webp|svg)$/i.test(e.name),p=/\.(png|jpe?g|gif|webp|tiff?)$/i.test(e.name),n=document.createElement("div");n.className="media-card";const u=document.createElement("div");if(u.className="media-preview",r){const o=document.createElement("img");o.src=e.url+"?t="+Date.now(),o.alt=e.name,o.className="media-thumb",u.appendChild(o)}else{const o=document.createElement("div");o.className="media-thumb media-thumb--file";const w=document.createElement("span");w.setAttribute("data-icon","file"),o.appendChild(w),u.appendChild(o)}n.appendChild(u);const C=document.createElement("div");C.className="media-info";const l=document.createElement("span");l.className="media-name",l.title="Double-click to rename",l.textContent=e.name;const v=document.createElement("span");v.className="media-size",v.textContent=L(e.size),C.appendChild(l),C.appendChild(v),n.appendChild(C),l.addEventListener("dblclick",()=>D(l,e,i));const h=document.createElement("div");h.className="media-actions";const b=document.createElement("button");b.className="btn btn-sm btn-ghost";const N=document.createElement("span");N.setAttribute("data-icon","copy"),b.appendChild(N),b.addEventListener("click",()=>{navigator.clipboard.writeText(window.location.origin+e.url).then(()=>E.toast("URL copied.",{type:"success"})).catch(()=>E.toast("Copy failed.",{type:"error"}))});const g=document.createElement("button");g.className="btn btn-sm btn-ghost";const k=document.createElement("span");k.setAttribute("data-icon","type"),g.appendChild(k),g.addEventListener("click",()=>D(l,e,i));let s=null;if(p){s=document.createElement("button"),s.className="btn btn-sm btn-ghost";const o=document.createElement("span");o.setAttribute("data-icon","edit"),s.appendChild(o),s.addEventListener("click",async()=>{await B(e)&&i(await m.media.list().catch(()=>[]))})}const y=document.createElement("button");y.className="btn btn-sm btn-danger";const x=document.createElement("span");x.setAttribute("data-icon","trash"),y.appendChild(x),y.addEventListener("click",async()=>{if(await E.confirm(`Delete "${e.name}"?`))try{await m.media.delete(e.name),E.toast("File deleted.",{type:"success"}),i(await m.media.list().catch(()=>[]))}catch{E.toast("Delete failed.",{type:"error"})}}),h.appendChild(b),h.appendChild(g),s&&h.appendChild(s),h.appendChild(y),n.appendChild(h),d.appendChild(n),E.tooltip(b,{content:"Copy URL",position:"top"}),E.tooltip(g,{content:"Rename",position:"top"}),s&&E.tooltip(s,{content:"Edit image",position:"top"}),E.tooltip(y,{content:"Delete file",position:"top"})}),Domma.icons.scan(),Domma.effects.reveal(".media-card",{animation:"fade",stagger:40,duration:350})},f=E.loader(t.get(0),{type:"dots"}),a=await m.media.list().catch(()=>[]);f.destroy(),i(a),Domma.effects.ripple("#upload-btn"),t.find("#upload-btn").on("click",()=>t.find("#file-input").get(0).click()),t.find("#file-input").on("change",async function(){if(!this.files.length)return;const c=new FormData;for(const r of this.files)c.append("file",r);const d=t.find("#upload-btn").get(0),e=d.lastChild;d.disabled=!0,e.textContent=" Uploading\u2026";try{if(!(await fetch("/api/media",{method:"POST",body:c,headers:{Authorization:"Bearer "+(S.get("auth_token")||"")}})).ok)throw new Error("Upload failed");E.toast("File uploaded.",{type:"success"}),i(await m.media.list().catch(()=>[]))}catch{E.toast("Upload failed.",{type:"error"})}finally{d.disabled=!1,e.textContent=" Upload",this.value=""}})}};function D(t,i,f){if(t.querySelector("input"))return;const a=i.name,c=a.lastIndexOf("."),d=c>0?a.slice(0,c):a,e=document.createElement("input");e.type="text",e.className="media-rename-input",e.value=a,e.title="",t.textContent="",t.appendChild(e),e.focus(),e.setSelectionRange(0,d.length);async function r(){const n=e.value.trim();if(!n||n===a)return p();e.disabled=!0;try{await m.media.rename(a,n),E.toast("File renamed.",{type:"success"}),f(await m.media.list().catch(()=>[]))}catch(u){E.toast(u.message||"Rename failed.",{type:"error"}),p()}}function p(){t.textContent=a,t.title="Double-click to rename"}e.addEventListener("keydown",n=>{n.key==="Enter"&&(n.preventDefault(),r()),n.key==="Escape"&&(n.preventDefault(),p())}),e.addEventListener("blur",()=>{setTimeout(()=>{t.contains(e)&&p()},150)})}function L(t){if(!t)return"0 B";const i=1024,f=["B","KB","MB","GB"],a=Math.floor(Math.log(t)/Math.log(i));return`${(t/Math.pow(i,a)).toFixed(1)} ${f[a]}`}
@@ -0,0 +1 @@
1
+ import{api as l}from"../api.js";function d(s){return{string:"string",email:"email",tel:"string",number:"number",textarea:"textarea",select:"select",radio:"select",checkbox:"boolean","checkbox-group":"select",date:"string",time:"string",url:"string",hidden:"string"}[s]||"string"}export const myProfileView={templateUrl:"/admin/js/templates/my-profile.html",async onMount(s){const[r,p]=await Promise.all([l.auth.me().catch(()=>null),l.collections.get("user-profiles").catch(()=>null)]);if(!r){E.toast("Could not load your account details.",{type:"error"});return}const m={name:{type:"string",required:!0,minLength:2,label:"Full Name",formConfig:{placeholder:"Jane Smith"}},email:{type:"email",required:!0,label:"Email Address",formConfig:{placeholder:"jane@example.com"}},password:{type:"password",required:!1,minLength:0,label:"New Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"new-password",tooltip:"Leave blank to keep your current password"}}},u={name:r.name||"",email:r.email||""};F.render("#account-form-container",m,u,{layout:"stacked",submitText:"Save Account",onSubmit:async t=>{if(t.password&&t.password.length<8)return E.toast("Password must be at least 8 characters.",{type:"warning"}),!1;const a={name:t.name,email:t.email};t.password&&(a.password=t.password);try{await l.auth.updateMe(a),E.toast("Account updated.",{type:"success"})}catch(e){return E.toast(`Save failed: ${e.message||"Unknown error"}`,{type:"error"}),!1}}});const i=p?.fields||[];if(i.length>0){s.find("#profile-card").show();const t={},a={};for(const e of i){const o={type:d(e.type),label:e.label||e.name};e.required&&(o.required=!0),e.placeholder&&(o.formConfig={placeholder:e.placeholder}),e.options&&Array.isArray(e.options)&&(o.options=e.options.map(n=>typeof n=="string"?{value:n,label:n}:n)),t[e.name]=o,a[e.name]=r.profile?.[e.name]??""}F.render("#profile-form-container",t,a,{layout:"stacked",submitText:"Save Profile",onSubmit:async e=>{try{await l.auth.updateMe({profile:e}),E.toast("Profile saved.",{type:"success"})}catch(c){return E.toast(`Save failed: ${c.message||"Unknown error"}`,{type:"error"}),!1}}})}Domma.icons.scan()}};