domma-cms 0.6.7 → 0.6.9

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.
@@ -1,3 +1,3 @@
1
- import{api as D}from"../api.js";const Q=[{value:"string",label:"Text (single line)"},{value:"email",label:"Email"},{value:"tel",label:"Phone"},{value:"number",label:"Number"},{value:"textarea",label:"Textarea (multi-line)"},{value:"select",label:"Dropdown (select)"},{value:"radio",label:"Radio buttons"},{value:"checkbox",label:"Single checkbox"},{value:"checkbox-group",label:"Checkbox group"},{value:"date",label:"Date"},{value:"time",label:"Time"},{value:"url",label:"URL"},{value:"hidden",label:"Hidden field"}],J=new Set(["select","radio","checkbox-group"]),le=["public","subscriber","editor","manager","admin"],X=["create","read","update","delete"];let f=[],x=null,T=!0;function Z(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function $(e){return Q.find(t=>t.value===e)?.label||e}function ne(e){const t={...f[e]},o=document.getElementById(`fb-label-${e}`),n=document.getElementById(`fb-name-${e}`),i=document.getElementById(`fb-type-${e}`),p=document.getElementById(`fb-required-${e}`),r=document.getElementById(`fb-placeholder-${e}`),l=document.getElementById(`fb-helper-${e}`);if(o&&(t.label=o.value.trim()||t.label),n&&(t.name=n.value.trim()||t.name),i&&(t.type=i.value||t.type),p&&(t.required=p.checked),r&&(t.placeholder=r.value.trim()),l&&(t.helper=l.value.trim()),J.has(t.type)){const c=document.getElementById(`fb-options-${e}`);c&&(t.options=c.value.split(`
2
- `).filter(u=>u.trim()).map(u=>{const[g,...m]=u.split(":");return{value:g.trim(),label:m.join(":").trim()||g.trim()}}))}const a=document.getElementById(`fb-span-${e}`);if(document.getElementById(`fb-fullwidth-${e}`)?.checked)t.fullWidth=!0,delete t.span;else{delete t.fullWidth;const c=parseInt(a?.value,10);c>1?t.span=c:delete t.span}return t}function ee(){return f.map((e,t)=>ne(t))}function ae(e,t){const o=document.createElement("div");o.className="fb-field-card",o.dataset.index=t,o.style.cssText="border:1px solid var(--border-color,#333);border-radius:8px;margin-bottom:.75rem;overflow:hidden;";const n=document.createElement("div");n.className="fb-field-header",n.style.cssText="display:flex;align-items:center;gap:.5rem;padding:.6rem .75rem;background:var(--card-header-bg,rgba(255,255,255,.03));cursor:pointer;user-select:none;";const i=document.createElement("span");i.textContent="\u283F",i.style.cssText="cursor:grab;opacity:.4;font-size:1.1rem;";const p=document.createElement("span");p.className="fb-field-summary",p.style.cssText="flex:1;font-weight:500;font-size:.9rem;",p.textContent=e.label||"(Untitled field)";const r=document.createElement("span");r.style.cssText="font-size:.75rem;opacity:.5;",r.textContent=$(e.type);const l=document.createElement("span");l.className="fb-field-chevron",l.textContent="\u25BE",l.style.cssText="opacity:.5;transition:transform .2s;";const a=document.createElement("button");a.type="button",a.textContent="\xD7",a.className="btn btn-sm",a.style.cssText="padding:.15rem .45rem;line-height:1;font-size:1rem;opacity:.6;",a.title="Remove field",a.addEventListener("click",d=>{d.stopPropagation(),f.splice(t,1),K(document.getElementById("fields-list"))}),n.appendChild(i),n.appendChild(p),n.appendChild(r),n.appendChild(l),n.appendChild(a);const s=document.createElement("div");s.className="fb-field-body",s.style.cssText="padding:.75rem;display:none;";const c=document.createElement("div");c.style.cssText="display:grid;grid-template-columns:1fr 1fr 1fr;gap:.6rem;margin-bottom:.6rem;";const u=document.createElement("div"),g=document.createElement("label");g.className="form-label",g.textContent="Label";const m=document.createElement("input");m.id=`fb-label-${t}`,m.type="text",m.className="form-input",m.value=e.label||"",m.addEventListener("input",()=>{p.textContent=m.value.trim()||"(Untitled field)";const d=document.getElementById(`fb-name-${t}`);d&&!d.dataset.manual&&(d.value=Z(m.value).replace(/-/g,"_"))}),u.appendChild(g),u.appendChild(m);const w=document.createElement("div"),h=document.createElement("label");h.className="form-label",h.textContent="Name (key)";const y=document.createElement("input");y.id=`fb-name-${t}`,y.type="text",y.className="form-input",y.value=e.name||"",y.addEventListener("input",()=>{y.dataset.manual="1"}),w.appendChild(h),w.appendChild(y);const z=document.createElement("div"),P=document.createElement("label");P.className="form-label",P.textContent="Type";const v=document.createElement("select");v.id=`fb-type-${t}`,v.className="form-input",Q.forEach(d=>{const F=document.createElement("option");F.value=d.value,F.textContent=d.label,d.value===e.type&&(F.selected=!0),v.appendChild(F)}),v.addEventListener("change",()=>{r.textContent=$(v.value);const d=s.querySelector(".fb-options-wrap");d&&(d.style.display=J.has(v.value)?"":"none")}),z.appendChild(P),z.appendChild(v),c.appendChild(u),c.appendChild(w),c.appendChild(z);const N=document.createElement("div");N.style.cssText="display:grid;grid-template-columns:1fr 1fr auto;gap:.6rem;align-items:end;margin-bottom:.6rem;";const O=document.createElement("div"),U=document.createElement("label");U.className="form-label",U.textContent="Placeholder";const I=document.createElement("input");I.id=`fb-placeholder-${t}`,I.type="text",I.className="form-input",I.value=e.placeholder||"",O.appendChild(U),O.appendChild(I);const M=document.createElement("div"),_=document.createElement("label");_.className="form-label",_.textContent="Helper text";const k=document.createElement("input");k.id=`fb-helper-${t}`,k.type="text",k.className="form-input",k.value=e.helper||"",M.appendChild(_),M.appendChild(k);const j=document.createElement("div");j.style.cssText="padding-bottom:.35rem;";const B=document.createElement("label");B.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;white-space:nowrap;";const q=document.createElement("input");q.id=`fb-required-${t}`,q.type="checkbox",q.checked=!!e.required,B.appendChild(q),B.appendChild(document.createTextNode("Required")),j.appendChild(B),N.appendChild(O),N.appendChild(M),N.appendChild(j);const L=document.createElement("div");L.className="fb-options-wrap",L.style.display=J.has(e.type)?"":"none";const V=document.createElement("label");V.className="form-label",V.textContent="Options (one per line: value: Label)";const S=document.createElement("textarea");S.id=`fb-options-${t}`,S.className="form-input",S.rows=4,S.value=(e.options||[]).map(d=>typeof d=="string"?`${d}: ${d}`:`${d.value??""}: ${d.label??d.value??""}`).join(`
3
- `),L.appendChild(V),L.appendChild(S);const b=document.createElement("div");b.className="fb-grid-row",b.style.gridTemplateColumns="1fr auto",b.style.gap=".6rem",b.style.alignItems="end",b.style.marginBottom=".6rem",b.style.display=document.getElementById("collection-layout")?.value==="grid"?"grid":"none";const H=document.createElement("div"),Y=document.createElement("label");Y.className="form-label",Y.textContent="Column Span";const C=document.createElement("input");C.id=`fb-span-${t}`,C.type="number",C.className="form-input",C.min="1",C.max="6",C.value=e.span>1?String(e.span):"1",H.appendChild(Y),H.appendChild(C);const G=document.createElement("div");G.style.cssText="padding-bottom:.35rem;";const W=document.createElement("label");W.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;white-space:nowrap;";const A=document.createElement("input");return A.id=`fb-fullwidth-${t}`,A.type="checkbox",A.checked=!!e.fullWidth,W.appendChild(A),W.appendChild(document.createTextNode("Full Width")),G.appendChild(W),b.appendChild(H),b.appendChild(G),s.appendChild(c),s.appendChild(N),s.appendChild(L),s.appendChild(b),n.addEventListener("click",()=>{const d=s.style.display!=="none";s.style.display=d?"none":"",l.style.transform=d?"":"rotate(180deg)"}),o.appendChild(n),o.appendChild(s),o}function K(e){if(e){if(e.textContent="",f.length===0){const t=document.createElement("p");t.className="text-muted",t.id="fields-empty-msg",t.style.cssText="text-align:center;padding:2rem 0;",t.textContent='No fields yet. Click "Add Field" to get started.',e.appendChild(t);return}f.forEach((t,o)=>{e.appendChild(ae(t,o))})}}function oe(e,t){t.textContent="",X.forEach(o=>{const n=e?.[o]||{enabled:!1,access:"admin"},i=document.createElement("div");i.style.cssText="display:grid;grid-template-columns:140px 1fr 160px;gap:.75rem;align-items:center;padding:.6rem 0;border-bottom:1px solid var(--border-color,#333);";const p=document.createElement("strong");p.textContent=o.charAt(0).toUpperCase()+o.slice(1),p.style.cssText="font-size:.9rem;";const r=document.createElement("label");r.style.cssText="display:flex;align-items:center;gap:.45rem;cursor:pointer;font-size:.875rem;";const l=document.createElement("input");l.type="checkbox",l.id=`api-${o}-enabled`,l.checked=!!n.enabled,r.appendChild(l),r.appendChild(document.createTextNode("Enable public access"));const a=document.createElement("select");a.id=`api-${o}-access`,a.className="form-input",le.forEach(s=>{const c=document.createElement("option");c.value=s,c.textContent=s.charAt(0).toUpperCase()+s.slice(1),s===n.access&&(c.selected=!0),a.appendChild(c)}),i.appendChild(p),i.appendChild(r),i.appendChild(a),t.appendChild(i)})}function se(e,t){E.dropdown("#storage-adapter-trigger",{items:[{label:"File (default)",value:"file"},{label:"MongoDB",value:"mongodb"}],onSelect:({item:n})=>{e.find("#storage-adapter").val(n.value),e.find("#storage-adapter-label").text(n.label);const i=n.value==="mongodb";e.find("#storage-connection-group").toggle(i),e.find("#storage-migration-warning").toggle(i&&!T)}});const o=t.map(n=>({label:n,value:n}));E.dropdown("#storage-connection-trigger",{items:o.length?o:[{label:"default",value:"default"}],onSelect:({item:n})=>{e.find("#storage-connection").val(n.value),e.find("#storage-connection-label").text(n.label)}})}function te(){return(document.getElementById("storage-adapter")?.value||"file")==="mongodb"?{adapter:"mongodb",connection:document.getElementById("storage-connection")?.value||"default"}:{adapter:"file"}}function ce(){const e={};return X.forEach(t=>{const o=document.getElementById(`api-${t}-enabled`)?.checked??!1,n=document.getElementById(`api-${t}-access`)?.value||"admin";e[t]={enabled:o,access:n}}),e}export const collectionEditorView={templateUrl:"/admin/js/templates/collection-editor.html",async onMount(e){f=[],x=null,T=!0;const o=window.location.hash.match(/\/collections\/edit\/([^/?#]+)/);o&&(x=o[1],T=!1),E.tabs(e.find("#collection-tabs").get(0)),e.find("#collection-layout").get(0)?.addEventListener("change",function(){const l=this.value==="grid";e.find("#collection-columns-group").get(0).style.display=l?"":"none",document.querySelectorAll(".fb-grid-row").forEach(a=>{a.style.display=l?"grid":"none"})});const n=e.find("#fields-list").get(0),i=e.find("#api-access-rows").get(0),p=await D.collections.proStatus();p?.pro&&x!=="roles"&&(e.find("#storage-tab-btn").show(),se(e,p.connections));let r={create:{enabled:!1,access:"admin"},read:{enabled:!0,access:"public"},update:{enabled:!1,access:"admin"},delete:{enabled:!1,access:"admin"}};if(T){const l=e.find("#field-title").get(0),a=e.find("#field-slug").get(0);l&&a&&(l.addEventListener("input",()=>{a.dataset.manual||(a.value=Z(l.value))}),a.addEventListener("input",()=>{a.dataset.manual="1"}))}else try{const l=await D.collections.get(x);if(!l){E.toast("Collection not found.",{type:"error"}),R.navigate("/collections");return}const a=e.find("#editor-title-text").get(0);a&&(a.textContent=l.title),e.find("#field-title").val(l.title||""),e.find("#field-slug").val(l.slug||""),e.find("#field-slug").prop("readonly",!0),e.find("#slug-hint").get(0).textContent="Slug cannot be changed after creation.",e.find("#field-description").val(l.description||""),e.find("#collection-layout").val(l.layout||"stacked"),e.find("#collection-columns").val(l.columns||2),e.find("#collection-columns-group").get(0).style.display=l.layout==="grid"?"":"none",f=l.fields||[],r=l.api||r,l.storage&&(e.find("#storage-adapter").val(l.storage.adapter||"file"),e.find("#storage-adapter-label").text(l.storage.adapter==="mongodb"?"MongoDB":"File (default)"),l.storage.adapter==="mongodb"&&(e.find("#storage-connection-group").show(),e.find("#storage-connection").val(l.storage.connection||"default"),e.find("#storage-connection-label").text(l.storage.connection||"default"))),x==="roles"&&e.find("#storage-tab-btn").hide()}catch{E.toast("Failed to load collection.",{type:"error"}),R.navigate("/collections");return}K(n),oe(r,i),e.find("#add-field-btn").off("click").on("click",()=>{f=ee(),f.push({id:`field-${Date.now()}`,name:"",label:"",type:"string",required:!1,placeholder:"",helper:"",options:[],validation:[],logic:null}),K(n);const l=n.querySelectorAll(".fb-field-card");if(l.length){const a=l[l.length-1],s=a.querySelector(".fb-field-body"),c=a.querySelector(".fb-field-chevron");s&&(s.style.display=""),c&&(c.style.transform="rotate(180deg)"),a.querySelector(`#fb-label-${f.length-1}`)?.focus()}}),e.find("#save-collection-btn").off("click").on("click",async()=>{const l=e.find("#field-title").val().trim(),a=e.find("#field-slug").val().trim(),s=e.find("#field-description").val().trim();if(!l){E.toast("Title is required.",{type:"warning"});return}const c=ee(),u=ce(),g=e.find("#collection-layout").val()||"stacked",m=parseInt(e.find("#collection-columns").val(),10)||2,w=e.find("#save-collection-btn");w.prop("disabled",!0);try{if(T){const h=await D.collections.create({title:l,slug:a,description:s,layout:g,columns:m,fields:c,api:u,storage:te()});x=h.slug,T=!1,E.toast("Collection created.",{type:"success"}),R.navigate(`/collections/edit/${h.slug}`)}else await D.collections.update(x,{title:l,description:s,layout:g,columns:m,fields:c,api:u,storage:te()}),E.toast("Collection saved.",{type:"success"})}catch(h){E.toast(h.message||"Failed to save.",{type:"error"})}finally{w.prop("disabled",!1)}}),Domma.icons.scan()}};
1
+ import{api as z}from"../api.js";const Z=[{value:"string",label:"Text (single line)"},{value:"email",label:"Email"},{value:"tel",label:"Phone"},{value:"number",label:"Number"},{value:"textarea",label:"Textarea (multi-line)"},{value:"select",label:"Dropdown (select)"},{value:"radio",label:"Radio buttons"},{value:"checkbox",label:"Single checkbox"},{value:"checkbox-group",label:"Checkbox group"},{value:"date",label:"Date"},{value:"time",label:"Time"},{value:"url",label:"URL"},{value:"hidden",label:"Hidden field"}],Q=new Set(["select","radio","checkbox-group"]),ne=["public","subscriber","editor","manager","admin"],$=["create","read","update","delete"];let u=[],w=null,T=!0,q=null;function ee(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function te(e){return Z.find(t=>t.value===e)?.label||e}function ae(e){const t={...u[e]},n=document.getElementById(`fb-label-${e}`),a=document.getElementById(`fb-name-${e}`),i=document.getElementById(`fb-type-${e}`),p=document.getElementById(`fb-required-${e}`),r=document.getElementById(`fb-placeholder-${e}`),l=document.getElementById(`fb-helper-${e}`);if(n&&(t.label=n.value.trim()||t.label),a&&(t.name=a.value.trim()||t.name),i&&(t.type=i.value||t.type),p&&(t.required=p.checked),r&&(t.placeholder=r.value.trim()),l&&(t.helper=l.value.trim()),Q.has(t.type)){const c=document.getElementById(`fb-options-${e}`);c&&(t.options=c.value.split(`
2
+ `).filter(f=>f.trim()).map(f=>{const[g,...m]=f.split(":");return{value:g.trim(),label:m.join(":").trim()||g.trim()}}))}const o=document.getElementById(`fb-span-${e}`);if(document.getElementById(`fb-fullwidth-${e}`)?.checked)t.fullWidth=!0,delete t.span;else{delete t.fullWidth;const c=parseInt(o?.value,10);c>1?t.span=c:delete t.span}return t}function X(){return u.map((e,t)=>ae(t))}function oe(e,t){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:8px;margin-bottom:.75rem;overflow:hidden;";const a=document.createElement("div");a.className="fb-field-header",a.style.cssText="display:flex;align-items:center;gap:.5rem;padding:.6rem .75rem;background:var(--card-header-bg,rgba(255,255,255,.03));cursor:pointer;user-select:none;";const i=document.createElement("span");i.textContent="\u283F",i.style.cssText="cursor:grab;opacity:.4;font-size:1.1rem;flex-shrink:0;",n.draggable=!0,i.addEventListener("mousedown",()=>{n.draggable=!0}),n.addEventListener("dragstart",s=>{q=t,s.dataTransfer.effectAllowed="move",n.style.opacity="0.4"}),n.addEventListener("dragend",()=>{n.style.opacity="",document.querySelectorAll(".fb-field-card").forEach(s=>s.classList.remove("fb-drag-over"))}),n.addEventListener("dragover",s=>{s.preventDefault(),s.dataTransfer.dropEffect="move",document.querySelectorAll(".fb-field-card").forEach(y=>y.classList.remove("fb-drag-over")),n.classList.add("fb-drag-over")}),n.addEventListener("dragleave",()=>{n.classList.remove("fb-drag-over")}),n.addEventListener("drop",s=>{if(s.preventDefault(),n.classList.remove("fb-drag-over"),q===null||q===t)return;u=X();const[y]=u.splice(q,1);u.splice(t,0,y),q=null,P(document.getElementById("fields-list"))});const p=document.createElement("span");p.className="fb-field-summary",p.style.cssText="flex:1;font-weight:500;font-size:.9rem;",p.textContent=e.label||"(Untitled field)";const r=document.createElement("span");r.style.cssText="font-size:.75rem;opacity:.5;",r.textContent=te(e.type);const l=document.createElement("span");l.className="fb-field-chevron",l.textContent="\u25BE",l.style.cssText="opacity:.5;transition:transform .2s;";const o=document.createElement("button");o.type="button",o.textContent="\xD7",o.className="btn btn-sm",o.style.cssText="padding:.15rem .45rem;line-height:1;font-size:1rem;opacity:.6;",o.title="Remove field",o.addEventListener("click",s=>{s.stopPropagation(),u.splice(t,1),P(document.getElementById("fields-list"))}),a.appendChild(i),a.appendChild(p),a.appendChild(r),a.appendChild(l),a.appendChild(o);const d=document.createElement("div");d.className="fb-field-body",d.style.cssText="padding:.75rem;display:none;";const c=document.createElement("div");c.style.cssText="display:grid;grid-template-columns:1fr 1fr 1fr;gap:.6rem;margin-bottom:.6rem;";const f=document.createElement("div"),g=document.createElement("label");g.className="form-label",g.textContent="Label";const m=document.createElement("input");m.id=`fb-label-${t}`,m.type="text",m.className="form-input",m.value=e.label||"",m.addEventListener("input",()=>{p.textContent=m.value.trim()||"(Untitled field)";const s=document.getElementById(`fb-name-${t}`);s&&!s.dataset.manual&&(s.value=ee(m.value).replace(/-/g,"_"))}),f.appendChild(g),f.appendChild(m);const L=document.createElement("div"),h=document.createElement("label");h.className="form-label",h.textContent="Name (key)";const v=document.createElement("input");v.id=`fb-name-${t}`,v.type="text",v.className="form-input",v.value=e.name||"",v.addEventListener("input",()=>{v.dataset.manual="1"}),L.appendChild(h),L.appendChild(v);const O=document.createElement("div"),U=document.createElement("label");U.className="form-label",U.textContent="Type";const C=document.createElement("select");C.id=`fb-type-${t}`,C.className="form-input",Z.forEach(s=>{const y=document.createElement("option");y.value=s.value,y.textContent=s.label,s.value===e.type&&(y.selected=!0),C.appendChild(y)}),C.addEventListener("change",()=>{r.textContent=te(C.value);const s=d.querySelector(".fb-options-wrap");s&&(s.style.display=Q.has(C.value)?"":"none")}),O.appendChild(U),O.appendChild(C),c.appendChild(f),c.appendChild(L),c.appendChild(O);const N=document.createElement("div");N.style.cssText="display:grid;grid-template-columns:1fr 1fr auto;gap:.6rem;align-items:end;margin-bottom:.6rem;";const _=document.createElement("div"),M=document.createElement("label");M.className="form-label",M.textContent="Placeholder";const I=document.createElement("input");I.id=`fb-placeholder-${t}`,I.type="text",I.className="form-input",I.value=e.placeholder||"",_.appendChild(M),_.appendChild(I);const j=document.createElement("div"),V=document.createElement("label");V.className="form-label",V.textContent="Helper text";const S=document.createElement("input");S.id=`fb-helper-${t}`,S.type="text",S.className="form-input",S.value=e.helper||"",j.appendChild(V),j.appendChild(S);const H=document.createElement("div");H.style.cssText="padding-bottom:.35rem;";const A=document.createElement("label");A.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;white-space:nowrap;";const W=document.createElement("input");W.id=`fb-required-${t}`,W.type="checkbox",W.checked=!!e.required,A.appendChild(W),A.appendChild(document.createTextNode("Required")),H.appendChild(A),N.appendChild(_),N.appendChild(j),N.appendChild(H);const k=document.createElement("div");k.className="fb-options-wrap",k.style.display=Q.has(e.type)?"":"none";const Y=document.createElement("label");Y.className="form-label",Y.textContent="Options (one per line: value: Label)";const B=document.createElement("textarea");B.id=`fb-options-${t}`,B.className="form-input",B.rows=4,B.value=(e.options||[]).map(s=>typeof s=="string"?`${s}: ${s}`:`${s.value??""}: ${s.label??s.value??""}`).join(`
3
+ `),k.appendChild(Y),k.appendChild(B);const b=document.createElement("div");b.className="fb-grid-row",b.style.gridTemplateColumns="1fr auto",b.style.gap=".6rem",b.style.alignItems="end",b.style.marginBottom=".6rem",b.style.display=document.getElementById("collection-layout")?.value==="grid"?"grid":"none";const G=document.createElement("div"),J=document.createElement("label");J.className="form-label",J.textContent="Column Span";const x=document.createElement("input");x.id=`fb-span-${t}`,x.type="number",x.className="form-input",x.min="1",x.max="6",x.value=e.span>1?String(e.span):"1",G.appendChild(J),G.appendChild(x);const K=document.createElement("div");K.style.cssText="padding-bottom:.35rem;";const F=document.createElement("label");F.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;white-space:nowrap;";const D=document.createElement("input");return D.id=`fb-fullwidth-${t}`,D.type="checkbox",D.checked=!!e.fullWidth,F.appendChild(D),F.appendChild(document.createTextNode("Full Width")),K.appendChild(F),b.appendChild(G),b.appendChild(K),d.appendChild(c),d.appendChild(N),d.appendChild(k),d.appendChild(b),a.addEventListener("click",()=>{const s=d.style.display!=="none";d.style.display=s?"none":"",l.style.transform=s?"":"rotate(180deg)"}),n.appendChild(a),n.appendChild(d),n}function P(e){if(e){if(e.textContent="",u.length===0){const t=document.createElement("p");t.className="text-muted",t.id="fields-empty-msg",t.style.cssText="text-align:center;padding:2rem 0;",t.textContent='No fields yet. Click "Add Field" to get started.',e.appendChild(t);return}u.forEach((t,n)=>{e.appendChild(oe(t,n))})}}function se(e,t){t.textContent="",$.forEach(n=>{const a=e?.[n]||{enabled:!1,access:"admin"},i=document.createElement("div");i.style.cssText="display:grid;grid-template-columns:140px 1fr 160px;gap:.75rem;align-items:center;padding:.6rem 0;border-bottom:1px solid var(--border-color,#333);";const p=document.createElement("strong");p.textContent=n.charAt(0).toUpperCase()+n.slice(1),p.style.cssText="font-size:.9rem;";const r=document.createElement("label");r.style.cssText="display:flex;align-items:center;gap:.45rem;cursor:pointer;font-size:.875rem;";const l=document.createElement("input");l.type="checkbox",l.id=`api-${n}-enabled`,l.checked=!!a.enabled,r.appendChild(l),r.appendChild(document.createTextNode("Enable public access"));const o=document.createElement("select");o.id=`api-${n}-access`,o.className="form-input",ne.forEach(d=>{const c=document.createElement("option");c.value=d,c.textContent=d.charAt(0).toUpperCase()+d.slice(1),d===a.access&&(c.selected=!0),o.appendChild(c)}),i.appendChild(p),i.appendChild(r),i.appendChild(o),t.appendChild(i)})}function de(e,t){E.dropdown("#storage-adapter-trigger",{items:[{label:"File (default)",value:"file"},{label:"MongoDB",value:"mongodb"}],onSelect:({item:a})=>{e.find("#storage-adapter").val(a.value),e.find("#storage-adapter-label").text(a.label);const i=a.value==="mongodb";e.find("#storage-connection-group").toggle(i),e.find("#storage-migration-warning").toggle(i&&!T)}});const n=t.map(a=>({label:a,value:a}));E.dropdown("#storage-connection-trigger",{items:n.length?n:[{label:"default",value:"default"}],onSelect:({item:a})=>{e.find("#storage-connection").val(a.value),e.find("#storage-connection-label").text(a.label)}})}function le(){return(document.getElementById("storage-adapter")?.value||"file")==="mongodb"?{adapter:"mongodb",connection:document.getElementById("storage-connection")?.value||"default"}:{adapter:"file"}}function ce(){const e={};return $.forEach(t=>{const n=document.getElementById(`api-${t}-enabled`)?.checked??!1,a=document.getElementById(`api-${t}-access`)?.value||"admin";e[t]={enabled:n,access:a}}),e}export const collectionEditorView={templateUrl:"/admin/js/templates/collection-editor.html",async onMount(e){u=[],w=null,T=!0;const n=window.location.hash.match(/\/collections\/edit\/([^/?#]+)/);n&&(w=n[1],T=!1),E.tabs(e.find("#collection-tabs").get(0)),e.find("#collection-layout").get(0)?.addEventListener("change",function(){const l=this.value==="grid";e.find("#collection-columns-group").get(0).style.display=l?"":"none",document.querySelectorAll(".fb-grid-row").forEach(o=>{o.style.display=l?"grid":"none"})});const a=e.find("#fields-list").get(0),i=e.find("#api-access-rows").get(0),p=await z.collections.proStatus();p?.pro&&w!=="roles"&&(e.find("#storage-tab-btn").show(),de(e,p.connections));let r={create:{enabled:!1,access:"admin"},read:{enabled:!0,access:"public"},update:{enabled:!1,access:"admin"},delete:{enabled:!1,access:"admin"}};if(T){const l=e.find("#field-title").get(0),o=e.find("#field-slug").get(0);l&&o&&(l.addEventListener("input",()=>{o.dataset.manual||(o.value=ee(l.value))}),o.addEventListener("input",()=>{o.dataset.manual="1"}))}else try{const l=await z.collections.get(w);if(!l){E.toast("Collection not found.",{type:"error"}),R.navigate("/collections");return}const o=e.find("#editor-title-text").get(0);o&&(o.textContent=l.title),e.find("#field-title").val(l.title||""),e.find("#field-slug").val(l.slug||""),e.find("#field-slug").prop("readonly",!0),e.find("#slug-hint").get(0).textContent="Slug cannot be changed after creation.",e.find("#field-description").val(l.description||""),e.find("#collection-layout").val(l.layout||"stacked"),e.find("#collection-columns").val(l.columns||2),e.find("#collection-columns-group").get(0).style.display=l.layout==="grid"?"":"none",u=l.fields||[],r=l.api||r,l.storage&&(e.find("#storage-adapter").val(l.storage.adapter||"file"),e.find("#storage-adapter-label").text(l.storage.adapter==="mongodb"?"MongoDB":"File (default)"),l.storage.adapter==="mongodb"&&(e.find("#storage-connection-group").show(),e.find("#storage-connection").val(l.storage.connection||"default"),e.find("#storage-connection-label").text(l.storage.connection||"default"))),w==="roles"&&e.find("#storage-tab-btn").hide()}catch{E.toast("Failed to load collection.",{type:"error"}),R.navigate("/collections");return}P(a),se(r,i),e.find("#add-field-btn").off("click").on("click",()=>{u=X(),u.push({id:`field-${Date.now()}`,name:"",label:"",type:"string",required:!1,placeholder:"",helper:"",options:[],validation:[],logic:null}),P(a);const l=a.querySelectorAll(".fb-field-card");if(l.length){const o=l[l.length-1],d=o.querySelector(".fb-field-body"),c=o.querySelector(".fb-field-chevron");d&&(d.style.display=""),c&&(c.style.transform="rotate(180deg)"),o.querySelector(`#fb-label-${u.length-1}`)?.focus()}}),e.find("#save-collection-btn").off("click").on("click",async()=>{const l=e.find("#field-title").val().trim(),o=e.find("#field-slug").val().trim(),d=e.find("#field-description").val().trim();if(!l){E.toast("Title is required.",{type:"warning"});return}const c=X(),f=ce(),g=e.find("#collection-layout").val()||"stacked",m=parseInt(e.find("#collection-columns").val(),10)||2,L=e.find("#save-collection-btn");L.prop("disabled",!0);try{if(T){const h=await z.collections.create({title:l,slug:o,description:d,layout:g,columns:m,fields:c,api:f,storage:le()});w=h.slug,T=!1,E.toast("Collection created.",{type:"success"}),R.navigate(`/collections/edit/${h.slug}`)}else await z.collections.update(w,{title:l,description:d,layout:g,columns:m,fields:c,api:f,storage:le()}),E.toast("Collection saved.",{type:"success"})}catch(h){E.toast(h.message||"Failed to save.",{type:"error"})}finally{L.prop("disabled",!1)}}),Domma.icons.scan()}};
@@ -1,11 +1,11 @@
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
- <div class="theme-swatch" data-theme="${a.id}" title="${a.id}">
3
- <div class="theme-swatch-preview" style="background:${a.bg}">
4
- <div class="theme-swatch-accent" style="background:${a.primary}"></div>
1
+ import{api as o,isAuthenticated as u,setAuthData as l}from"../api.js";import{THEMES as p}from"../lib/themes.js";const f={name:{type:"string",required:!0,minLength:2,label:"Full Name",formConfig:{placeholder:"Your name",autocomplete:"name"}},email:{type:"email",required:!0,label:"Email Address",formConfig:{placeholder:"admin@example.com",autocomplete:"email"}},password:{type:"password",required:!0,minLength:8,label:"Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"new-password",tooltip:"Minimum 8 characters"}}},h={email:{type:"email",required:!0,label:"Email Address",formConfig:{placeholder:"you@example.com",autocomplete:"email"}},password:{type:"password",required:!0,label:"Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"current-password"}}};export const loginView={templateUrl:"/admin/js/templates/login.html",async onMount(e){if(u()){R.navigate("/");return}const a=window.location.hash,t=a.match(/[?&]token=([a-f0-9]{64})/);if(a.startsWith("#/reset-password")&&t){r(e,"reset"),S(e,t[1]),Domma.icons.scan();return}if(a.startsWith("#/reset-password")){R.navigate("/login");return}let s=!1;try{s=(await o.auth.setupStatus()).needsSetup}catch{E.toast("Could not reach the server.",{type:"error"})}s?(r(e,"setup"),y(e)):(r(e,"login"),k(e)),Domma.icons.scan()}};const g={email:{type:"email",required:!0,label:"Email Address",formConfig:{placeholder:"you@example.com",autocomplete:"email"}}},b={password:{type:"password",required:!0,minLength:8,label:"New Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"new-password",tooltip:"Minimum 8 characters"}},confirmPassword:{type:"password",required:!0,label:"Confirm Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"new-password"}}},w=["setup","onboarding-site","onboarding-theme","onboarding-done","login","forgot","forgot-success","reset"];function r(e,a){w.forEach(t=>e.find(`#${t}-panel`).hide()),e.find(`#${a}-panel`).show(),Domma.icons.scan()}function d(e,a,t){e.find(`#${a}`).text(t).show()}function y(e){F.render("#setup-form-container",f,{},{layout:"stacked",submitText:"Create admin account",onSubmit:async a=>{try{const t=await o.auth.setup(a);l(t),r(e,"onboarding-site"),v(e)}catch(t){return E.toast(t.message||"Setup failed. Please try again.",{type:"error"}),!1}}})}function v(e){e.find("#ob-site-skip").on("click",a=>{a.preventDefault(),r(e,"onboarding-theme"),c(e),m(e)}),e.find("#ob-site-btn").on("click",async()=>{e.find("#ob-site-error").hide();const a=e.find("#ob-title").val().trim(),t=e.find("#ob-tagline").val().trim(),s=e.find("#ob-site-btn").prop("disabled",!0).text("Saving\u2026");try{const i=await o.settings.get();await o.settings.save({...i,title:a||i.title,tagline:t||i.tagline,seo:{...i.seo||{},defaultTitle:a||i.seo&&i.seo.defaultTitle}});const n=await o.navigation.get();await o.navigation.save({...n,brand:{...n.brand||{},text:a||n.brand.text}}),r(e,"onboarding-theme"),c(e),m(e)}catch(i){d(e,"ob-site-error",i.message||"Could not save site details.")}finally{s.prop("disabled",!1).text("Continue")}})}function c(e){const a=e.find("#theme-grid").empty();p.forEach(t=>{const s=$(`
2
+ <div class="theme-swatch" data-theme="${t.id}" title="${t.id}">
3
+ <div class="theme-swatch-preview" style="background:${t.bg}">
4
+ <div class="theme-swatch-accent" style="background:${t.primary}"></div>
5
5
  </div>
6
6
  <div class="theme-swatch-label">
7
- <span>${a.label}</span>
8
- <span class="theme-swatch-mode">${a.dark?"Dark":"Light"}</span>
7
+ <span>${t.label}</span>
8
+ <span class="theme-swatch-mode">${t.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 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}}})}
11
+ `);s.on("click",()=>{e.find(".theme-swatch").removeClass("selected"),s.addClass("selected")}),a.append(s)}),e.find('[data-theme="charcoal-dark"]').addClass("selected")}function m(e){e.find("#ob-theme-skip").on("click",a=>{a.preventDefault(),r(e,"onboarding-done")}),e.find("#ob-theme-btn").on("click",async()=>{e.find("#ob-theme-error").hide();const a=e.find(".theme-swatch.selected").attr("data-theme")||"charcoal-dark",t=e.find("#ob-theme-btn").prop("disabled",!0).text("Applying\u2026");try{const s=await o.settings.get();await o.settings.save({...s,theme:a}),r(e,"onboarding-done")}catch(s){d(e,"ob-theme-error",s.message||"Could not save theme.")}finally{t.prop("disabled",!1).text("Apply theme")}})}function k(e){F.render("#login-form-container",h,{},{layout:"stacked",submitText:"Sign in",onSubmit:async a=>{try{const t=await o.auth.login(a);l(t),R.navigate("/")}catch(t){return E.toast(t.message||"Invalid credentials. Please try again.",{type:"error"}),!1}}}),e.find("#forgot-link").on("click",a=>{a.preventDefault(),r(e,"forgot"),C(e)})}function C(e){F.render("#forgot-form-container",g,{},{layout:"stacked",submitText:"Send reset link",onSubmit:async t=>{try{await o.auth.forgotPassword(t.email)}catch{}r(e,"forgot-success"),Domma.icons.scan()}});const a=t=>{t.preventDefault(),r(e,"login")};e.find("#forgot-back-link").on("click",a),e.find("#forgot-success-back-link").on("click",a)}function S(e,a){F.render("#reset-form-container",b,{},{layout:"stacked",submitText:"Set new password",onSubmit:async t=>{if(t.password!==t.confirmPassword)return E.toast("Passwords do not match.",{type:"error"}),!1;try{await o.auth.resetPassword(a,t.password),E.toast("Password updated. Please sign in.",{type:"success"}),window.location.hash="#/login"}catch(s){return E.toast(s.message||"Link expired or invalid.",{type:"error"}),!1}}})}
@@ -1,20 +1,22 @@
1
- import{api as f}from"../api.js";let x=1;const d=t=>String(t||"").replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;");function b(t){const e=[];return(t||[]).forEach(r=>{const o=x++;e.push({_id:o,text:r.text||"",url:r.url||"",icon:r.icon||"",parentId:null}),(r.items||r.children||[]).forEach(l=>{e.push({_id:x++,text:l.text||"",url:l.url||"",icon:l.icon||"",parentId:o})})}),e}function I(t){return t.filter(e=>e.parentId===null).map(e=>{const r=t.filter(l=>l.parentId===e._id).map(l=>({text:l.text,url:l.url,...l.icon&&{icon:l.icon}})),o={text:e.text,url:e.url,...e.icon&&{icon:e.icon}};return r.length&&(o.items=r),o})}function g(t){const e=[];return t.filter(r=>r.parentId===null).forEach(r=>{e.push(r),t.filter(o=>o.parentId===r._id).forEach(o=>e.push(o))}),e}export const navigationView={templateUrl:"/admin/js/templates/navigation.html",async onMount(t){let[e,r]=await Promise.all([f.navigation.get().catch(()=>({brand:{},items:[]})),f.settings.get().catch(()=>({}))]),o=b(e.items),l=(r.footer?.links||[]).map(s=>({text:s.text||"",url:s.url||""}));const v=()=>{t.find(".nav-item-row").each(function(){const i=parseInt($(this).data("id"),10),n=o.find(a=>a._id===i);if(!n)return;n.text=$(this).find(".item-text").val(),n.url=$(this).find(".item-url").val(),n.icon=$(this).find(".item-icon").val();const c=$(this).find(".item-parent").val();n.parentId=c?parseInt(c,10):null});const s=new Set(o.filter(i=>i.parentId!==null).map(i=>i._id));o.forEach(i=>{i.parentId!==null&&s.has(i.parentId)&&(i.parentId=null)})},p=()=>{const s=t.find("#nav-items-list").empty(),i=o.filter(n=>n.parentId===null);g(o).forEach(n=>{const c=n.parentId!==null,a='<option value="">\u2014 top-level \u2014</option>'+i.filter(u=>u._id!==n._id).map(u=>`<option value="${u._id}"${u._id===n.parentId?" selected":""}>${d(u.text)||"(untitled)"}</option>`).join("");s.append(`
2
- <div class="nav-item-row${c?" nav-item-row--child":""}" data-id="${n._id}">
3
- <span class="nav-col-indent">${c?"\u21B3":""}</span>
4
- <input type="text" class="form-input item-text nav-col-main" value="${d(n.text)}" placeholder="Label">
5
- <input type="text" class="form-input item-url nav-col-main" value="${d(n.url)}" placeholder="/url">
6
- <input type="text" class="form-input item-icon nav-col-icon" value="${d(n.icon)}" placeholder="icon">
7
- <select class="form-select item-parent nav-col-parent">${a}</select>
1
+ import{api as v}from"../api.js";let b=1;const f=e=>String(e||"").replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;");function g(e){const i=[];return(e||[]).forEach(s=>{const l=b++;i.push({_id:l,text:s.text||"",url:s.url||"",icon:s.icon||"",hidden:s.hidden||!1,parentId:null}),(s.items||s.children||[]).forEach(o=>{i.push({_id:b++,text:o.text||"",url:o.url||"",icon:o.icon||"",hidden:o.hidden||!1,parentId:l})})}),i}function k(e){return e.filter(i=>i.parentId===null).map(i=>{const s=e.filter(o=>o.parentId===i._id).map(o=>({text:o.text,url:o.url,...o.icon&&{icon:o.icon},...o.hidden&&{hidden:!0}})),l={text:i.text,url:i.url,...i.icon&&{icon:i.icon},...i.hidden&&{hidden:!0}};return s.length&&(l.items=s),l})}function I(e){const i=[];return e.filter(s=>s.parentId===null).forEach(s=>{i.push(s),e.filter(l=>l.parentId===s._id).forEach(l=>i.push(l))}),i}export const navigationView={templateUrl:"/admin/js/templates/navigation.html",async onMount(e){let[i,s]=await Promise.all([v.navigation.get().catch(()=>({brand:{},items:[]})),v.settings.get().catch(()=>({}))]),l=g(i.items),o=(s.footer?.links||[]).map(d=>({text:d.text||"",url:d.url||"",...d.hidden&&{hidden:!0}}));const p=()=>{e.find("#nav-items-list .nav-item-row").each(function(){const t=parseInt($(this).data("id"),10),n=l.find(a=>a._id===t);if(!n)return;n.text=$(this).find(".item-text").val(),n.url=$(this).find(".item-url").val(),n.icon=$(this).find(".item-icon").val(),n.hidden=$(this).find(".btn-toggle-hidden").attr("data-hidden")==="true";const r=$(this).find(".item-parent").val();n.parentId=r?parseInt(r,10):null});const d=new Set(l.filter(t=>t.parentId!==null).map(t=>t._id));l.forEach(t=>{t.parentId!==null&&d.has(t.parentId)&&(t.parentId=null)})},c=()=>{const d=e.find("#nav-items-list").empty(),t=l.filter(n=>n.parentId===null);I(l).forEach(n=>{const r=n.parentId!==null,a=n.hidden===!0,x='<option value="">\u2014 top-level \u2014</option>'+t.filter(h=>h._id!==n._id).map(h=>`<option value="${h._id}"${h._id===n.parentId?" selected":""}>${f(h.text)||"(untitled)"}</option>`).join("");d.append(`
2
+ <div class="nav-item-row${r?" nav-item-row--child":""}${a?" nav-item-row--hidden":""}" data-id="${n._id}">
3
+ <span class="nav-col-indent">${r?"\u21B3":""}</span>
4
+ <input type="text" class="form-input item-text nav-col-main" value="${f(n.text)}" placeholder="Label">
5
+ <input type="text" class="form-input item-url nav-col-main" value="${f(n.url)}" placeholder="/url">
6
+ <input type="text" class="form-input item-icon nav-col-icon" value="${f(n.icon)}" placeholder="icon">
7
+ <select class="form-select item-parent nav-col-parent">${x}</select>
8
8
  <span class="nav-col-action">
9
+ <button class="btn btn-sm btn-ghost btn-toggle-hidden${a?" active":""}" data-id="${n._id}" data-hidden="${a}" data-tooltip="${a?"Show":"Hide"}"><span data-icon="eye-off"></span></button>
9
10
  <button class="btn btn-sm btn-danger btn-remove-item" data-id="${n._id}" data-tooltip="Remove"><span data-icon="trash"></span></button>
10
11
  </span>
11
12
  </div>
12
- `)}),Domma.icons.scan("#nav-items-list"),document.querySelectorAll("#nav-items-list [data-tooltip]").forEach(n=>{E.tooltip(n,{content:n.getAttribute("data-tooltip"),position:"top"})})},m=()=>{const s=t.find("#footer-links-list").empty();l.forEach((i,n)=>{s.append(`
13
- <div class="nav-item-row" data-footer-idx="${n}">
14
- <input type="text" class="form-input footer-link-text nav-col-main" value="${d(i.text)}" placeholder="Label">
15
- <input type="text" class="form-input footer-link-url nav-col-main" value="${d(i.url)}" placeholder="/url">
16
- <span class="nav-col-action">
17
- <button class="btn btn-sm btn-danger btn-remove-footer" data-idx="${n}" data-tooltip="Remove"><span data-icon="trash"></span></button>
18
- </span>
19
- </div>
20
- `)}),Domma.icons.scan("#footer-links-list"),document.querySelectorAll("#footer-links-list [data-tooltip]").forEach(i=>{E.tooltip(i,{content:i.getAttribute("data-tooltip"),position:"top"})})},h=()=>{l=[],t.find("#footer-links-list .nav-item-row").each(function(){l.push({text:$(this).find(".footer-link-text").val().trim(),url:$(this).find(".footer-link-url").val().trim()})})};t.find("#add-footer-link").on("click",()=>{h(),l.push({text:"",url:""}),m()}),t.off("click",".btn-remove-footer").on("click",".btn-remove-footer",function(){h();const s=parseInt($(this).data("idx"),10);l.splice(s,1),m()}),t.find("#field-brand-text").val(e.brand?.text||""),t.find("#field-brand-url").val(e.brand?.url||"/"),t.find("#field-brand-icon").val(e.brand?.icon||""),t.find("#field-nav-variant").val(e.variant||"dark"),p(),m(),t.find("#add-nav-item").on("click",()=>{v(),o.push({_id:x++,text:"",url:"",icon:"",parentId:null}),p()}),t.off("click",".btn-remove-item").on("click",".btn-remove-item",function(){v();const s=parseInt($(this).data("id"),10);o=o.filter(i=>i._id!==s&&i.parentId!==s),p()}),t.off("change",".item-parent").on("change",".item-parent",function(){v(),p()}),t.find("#save-nav-btn").on("click",async()=>{v(),h();const s=I(o.map(a=>({...a,text:a.text.trim(),url:a.url.trim(),icon:a.icon.trim()}))).filter(a=>a.text||a.url),i=t.find("#field-brand-icon").val().trim(),n={brand:{text:t.find("#field-brand-text").val().trim(),url:t.find("#field-brand-url").val().trim()||"/",...i&&{icon:i}},items:s,variant:t.find("#field-nav-variant").val(),position:e.position||"sticky"},c=l.filter(a=>a.text||a.url);try{await f.navigation.save(n),e=n,o=b(e.items),p(),E.toast("Navigation saved.",{type:"success"})}catch(a){console.error("[navigation] save failed:",a),E.toast("Failed to save navigation.",{type:"error"});return}try{const a=await f.settings.get().catch(()=>({}));await f.settings.save({...a,footer:{...a.footer||{},links:c}}),l=c,m()}catch(a){console.error("[navigation] footer links save failed:",a),E.toast("Footer links could not be saved.",{type:"warning"})}})}};
13
+ `)}),Domma.icons.scan("#nav-items-list"),document.querySelectorAll("#nav-items-list [data-tooltip]").forEach(n=>{E.tooltip(n,{content:n.getAttribute("data-tooltip"),position:"top"})})},u=()=>{const d=e.find("#footer-links-list").empty();o.forEach((t,n)=>{const r=t.hidden===!0;d.append(`
14
+ <div class="nav-item-row${r?" nav-item-row--hidden":""}" data-footer-idx="${n}">
15
+ <input type="text" class="form-input footer-link-text nav-col-main" value="${f(t.text)}" placeholder="Label">
16
+ <input type="text" class="form-input footer-link-url nav-col-main" value="${f(t.url)}" placeholder="/url">
17
+ <span class="nav-col-action">
18
+ <button class="btn btn-sm btn-ghost btn-toggle-hidden${r?" active":""}" data-idx="${n}" data-hidden="${r}" data-tooltip="${r?"Show":"Hide"}"><span data-icon="eye-off"></span></button>
19
+ <button class="btn btn-sm btn-danger btn-remove-footer" data-idx="${n}" data-tooltip="Remove"><span data-icon="trash"></span></button>
20
+ </span>
21
+ </div>
22
+ `)}),Domma.icons.scan("#footer-links-list"),document.querySelectorAll("#footer-links-list [data-tooltip]").forEach(t=>{E.tooltip(t,{content:t.getAttribute("data-tooltip"),position:"top"})})},m=()=>{o=[],e.find("#footer-links-list .nav-item-row").each(function(){const d=$(this).find(".btn-toggle-hidden").attr("data-hidden")==="true",t={text:$(this).find(".footer-link-text").val().trim(),url:$(this).find(".footer-link-url").val().trim()};d&&(t.hidden=!0),o.push(t)})};e.find("#add-footer-link").on("click",()=>{m(),o.push({text:"",url:"",hidden:!1}),u()}),e.off("click",".btn-remove-footer").on("click",".btn-remove-footer",function(){m();const d=parseInt($(this).data("idx"),10);o.splice(d,1),u()}),e.find("#field-brand-text").val(i.brand?.text||""),e.find("#field-brand-url").val(i.brand?.url||"/"),e.find("#field-brand-icon").val(i.brand?.icon||""),e.find("#field-nav-variant").val(i.variant||"dark"),c(),u(),e.find("#add-nav-item").on("click",()=>{p(),l.push({_id:b++,text:"",url:"",icon:"",hidden:!1,parentId:null}),c()}),e.off("click",".btn-remove-item").on("click",".btn-remove-item",function(){p();const d=parseInt($(this).data("id"),10);l=l.filter(t=>t._id!==d&&t.parentId!==d),c()}),e.off("click","#nav-items-list .btn-toggle-hidden").on("click","#nav-items-list .btn-toggle-hidden",function(d){d.stopPropagation(),p();const t=parseInt($(this).data("id"),10),n=l.find(r=>r._id===t);n&&(n.hidden=!n.hidden),c()}),e.off("click","#footer-links-list .btn-toggle-hidden").on("click","#footer-links-list .btn-toggle-hidden",function(d){d.stopPropagation(),m();const t=parseInt($(this).data("idx"),10);o[t]&&(o[t].hidden=!o[t].hidden),u()}),e.off("change",".item-parent").on("change",".item-parent",function(){p(),c()}),e.find("#save-nav-btn").on("click",async()=>{p(),m();const d=k(l.map(a=>({...a,text:a.text.trim(),url:a.url.trim(),icon:a.icon.trim()}))).filter(a=>a.text||a.url),t=e.find("#field-brand-icon").val().trim(),n={brand:{text:e.find("#field-brand-text").val().trim(),url:e.find("#field-brand-url").val().trim()||"/",...t&&{icon:t}},items:d,variant:e.find("#field-nav-variant").val(),position:i.position||"sticky"},r=o.filter(a=>a.text||a.url);try{await v.navigation.save(n),i=n,l=g(i.items),c(),E.toast("Navigation saved.",{type:"success"})}catch(a){console.error("[navigation] save failed:",a),E.toast("Failed to save navigation.",{type:"error"});return}try{const a=await v.settings.get().catch(()=>({}));await v.settings.save({...a,footer:{...a.footer||{},links:r}}),o=r,u()}catch(a){console.error("[navigation] footer links save failed:",a),E.toast("Footer links could not be saved.",{type:"warning"})}})}};