domma-cms 0.8.10 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/js/templates/action-editor.html +5 -0
- package/admin/js/templates/block-editor.html +5 -0
- package/admin/js/templates/collection-editor.html +7 -0
- package/admin/js/templates/form-editor.html +7 -0
- package/admin/js/templates/page-editor.html +5 -0
- package/admin/js/templates/view-editor.html +5 -0
- package/admin/js/views/action-editor.js +1 -1
- package/admin/js/views/block-editor.js +4 -4
- package/admin/js/views/collection-editor.js +4 -4
- package/admin/js/views/collections.js +1 -1
- package/admin/js/views/form-editor.js +1 -1
- package/admin/js/views/navigation.js +13 -12
- package/admin/js/views/page-editor.js +11 -11
- package/admin/js/views/pages.js +2 -2
- package/admin/js/views/view-editor.js +1 -1
- package/package.json +1 -1
- package/plugins/contacts/collections/user-contact-groups/schema.json +35 -0
- package/plugins/contacts/collections/user-contacts/schema.json +71 -0
- package/plugins/contacts/plugin.js +1 -55
- package/plugins/garage/collections/garage-vehicles/schema.json +101 -0
- package/plugins/garage/plugin.js +0 -40
- package/plugins/notes/collections/user-notes/schema.json +53 -0
- package/plugins/notes/plugin.js +1 -47
- package/plugins/todo/collections/todos/schema.json +59 -0
- package/plugins/todo/plugin.js +1 -48
- package/server/routes/api/blocks.js +2 -2
- package/server/services/blocks.js +22 -2
- package/server/services/collections.js +17 -3
- package/server/services/forms.js +2 -1
- package/server/services/plugins.js +166 -1
|
@@ -43,6 +43,11 @@
|
|
|
43
43
|
</select>
|
|
44
44
|
<small class="text-muted">The collection this action operates on.</small>
|
|
45
45
|
</div>
|
|
46
|
+
<div>
|
|
47
|
+
<label class="form-check-label" title="Included in fresh installs via the seed script">
|
|
48
|
+
<input id="action-bundled" type="checkbox" class="form-check"> Bundled
|
|
49
|
+
</label>
|
|
50
|
+
</div>
|
|
46
51
|
</div>
|
|
47
52
|
</div>
|
|
48
53
|
</div>
|
|
@@ -20,6 +20,11 @@
|
|
|
20
20
|
<label class="form-label">Name <span style="color:var(--dm-danger,#f87171);">*</span></label>
|
|
21
21
|
<input id="block-name" type="text" class="form-input" placeholder="e.g. feedback-card">
|
|
22
22
|
<small class="text-muted">Lowercase letters, digits, and hyphens only. Cannot be changed after creation.</small>
|
|
23
|
+
<div class="mt-3">
|
|
24
|
+
<label class="form-check-label" title="Included in fresh installs via the seed script">
|
|
25
|
+
<input id="block-bundled" type="checkbox" class="form-check"> Bundled
|
|
26
|
+
</label>
|
|
27
|
+
</div>
|
|
23
28
|
</div>
|
|
24
29
|
</div>
|
|
25
30
|
|
|
@@ -53,6 +53,13 @@
|
|
|
53
53
|
<input id="collection-columns" type="number" class="form-input" min="1" max="6" value="2">
|
|
54
54
|
</div>
|
|
55
55
|
</div>
|
|
56
|
+
<div class="row mt-3">
|
|
57
|
+
<div class="col-auto">
|
|
58
|
+
<label class="form-check-label" title="Included in fresh installs via the seed script">
|
|
59
|
+
<input id="collection-bundled" type="checkbox" class="form-check"> Bundled
|
|
60
|
+
</label>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
56
63
|
</div>
|
|
57
64
|
</div>
|
|
58
65
|
</div>
|
|
@@ -95,6 +95,13 @@
|
|
|
95
95
|
placeholder="Optional form description..."></textarea>
|
|
96
96
|
</div>
|
|
97
97
|
</div>
|
|
98
|
+
<div class="row mt-3">
|
|
99
|
+
<div class="col-auto">
|
|
100
|
+
<label class="form-check-label" title="Included in fresh installs via the seed script">
|
|
101
|
+
<input id="field-bundled" type="checkbox" class="form-check"> Bundled
|
|
102
|
+
</label>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
98
105
|
</div>
|
|
99
106
|
</div>
|
|
100
107
|
</div>
|
|
@@ -127,6 +127,11 @@
|
|
|
127
127
|
<input id="field-show-breadcrumbs" type="checkbox" class="form-check" checked> Show Breadcrumbs
|
|
128
128
|
</label>
|
|
129
129
|
</div>
|
|
130
|
+
<div class="col-auto">
|
|
131
|
+
<label class="form-check-label" title="Included in fresh installs via the seed script">
|
|
132
|
+
<input id="field-bundled" type="checkbox" class="form-check"> Bundled
|
|
133
|
+
</label>
|
|
134
|
+
</div>
|
|
130
135
|
</div>
|
|
131
136
|
</div>
|
|
132
137
|
|
|
@@ -44,6 +44,11 @@
|
|
|
44
44
|
<small class="text-muted">Changing the collection will refresh field lists in Pipeline and
|
|
45
45
|
Display tabs.</small>
|
|
46
46
|
</div>
|
|
47
|
+
<div>
|
|
48
|
+
<label class="form-check-label" title="Included in fresh installs via the seed script">
|
|
49
|
+
<input id="view-bundled" type="checkbox" class="form-check"> Bundled
|
|
50
|
+
</label>
|
|
51
|
+
</div>
|
|
47
52
|
<div>
|
|
48
53
|
<label class="form-label">Connection</label>
|
|
49
54
|
<select id="view-connection" class="form-input">
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{api as S}from"../api.js";let _=null;const z={deleteEntry:[],moveToCollection:[{name:"targetCollection",label:"Target Collection Slug",placeholder:"e.g. archived-applications"}],webhook:[{name:"url",label:"URL",placeholder:"https://hooks.example.com/notify"},{name:"method",label:"Method",placeholder:"POST"},{name:"body",label:"Body (JSON)",placeholder:'{"email": "{{entry.data.email}}"}',multiline:!0}],email:[{name:"to",label:"To",placeholder:"{{entry.data.email}}"},{name:"subject",label:"Subject",placeholder:"Your application update"},{name:"template",label:"Body",placeholder:"Your application has been approved.",multiline:!0}]};export const actionEditorView={templateUrl:"/admin/js/templates/action-editor.html",async onMount(e){_=null;const o=window.location.hash.match(/\/actions\/edit\/([^/?#]+)/);o&&(_=o[1]),E.tabs(e.find("#action-editor-tabs").get(0)),await D(e),await J(e),_&&(e.find("#action-editor-title").text("Edit Action"),await P(e,_)),e.find("#add-step-btn").off("click").on("click",()=>{const n=e.find("#add-step-type").val()||"updateField";B(e,{type:n,config:{}})}),e.find("#save-action-btn").off("click").on("click",async()=>{await K(e)}),U(e),Domma.icons.scan()}};async function D(e){const t=e.find("#action-collection").get(0);if(t)try{(await S.collections.list()).forEach(n=>{const i=document.createElement("option");i.value=n.slug,i.textContent=`${n.title} (${n.slug})`,t.appendChild(i)})}catch{}}async function J(e){const t=e.find("#action-roles-checkboxes").get(0);if(!t)return;["admin","manager","editor","subscriber"].forEach(n=>{const i=document.createElement("label");i.style.cssText="display:flex;align-items:center;gap:.5rem;cursor:pointer;";const l=document.createElement("input");l.type="checkbox",l.value=n,l.dataset.role=n,l.className="action-role-cb",l.checked=n==="admin",i.appendChild(l),i.appendChild(document.createTextNode(n)),t.appendChild(i)})}async function P(e,t){try{const o=await S.actions.get(t);if(!o){E.toast("Action not found.",{type:"error"}),R.navigate("/actions");return}j(e,o)}catch(o){E.toast(o.message||"Failed to load action.",{type:"error"}),R.navigate("/actions")}}function U(e){const t=e.find("#action-rowlevel-enabled").get(0),o=e.find("#action-rowlevel-config").get(0),n=e.find("#action-rowlevel-mode").get(0),i=e.find("#action-rowlevel-field-group").get(0);t&&(t.addEventListener("change",()=>{o&&(o.style.display=t.checked?"flex":"none")}),n&&n.addEventListener("change",()=>{i&&(i.style.display=n.value==="field"?"":"none")}))}function j(e,t){e.find("#action-title").val(t.title||""),e.find("#action-slug").val(t.slug||""),e.find("#action-description").val(t.description||""),e.find("#action-collection").val(t.collection||""),e.find("#action-trigger-type").val(t.trigger?.type||"manual"),e.find("#action-trigger-label").val(t.trigger?.label||"Run"),e.find("#action-trigger-icon").val(t.trigger?.icon||"zap"),e.find("#action-trigger-confirm").val(t.trigger?.confirmMessage||"");const o=t.access?.roles||["admin"];e.find(".action-role-cb").each(function(){this.checked=o.includes(this.value)});const n=t.access?.rowLevel;n&&(e.find("#action-rowlevel-enabled").prop("checked",!0),e.find("#action-rowlevel-config").css("display","flex"),e.find("#action-rowlevel-mode").val(n.mode||"owner"),e.find("#action-rowlevel-userkey").val(n.userKey||"id"),n.mode==="field"&&(e.find("#action-rowlevel-field-group").css("display",""),e.find("#action-rowlevel-field").val(n.field||"")));const i=e.find("#action-steps-list").get(0);if(i){const l=i.querySelector(".steps-empty-placeholder");for(l&&l.remove();i.firstChild;)i.removeChild(i.firstChild);const a=document.createElement("p");a.className="text-muted steps-empty-placeholder",a.textContent="No steps yet. Add a step to define what this action does.",a.style.cssText="text-align:center;padding:2rem 0;",i.appendChild(a)}(t.steps||[]).forEach(l=>B(e,l))}function B(e,t){const o=e.find("#action-steps-list").get(0);if(!o)return;const n=o.querySelector(".steps-empty-placeholder");n&&n.remove();const i=z[t.type]||[],l=document.createElement("div");l.className="card mb-2 step-card",l.dataset.stepType=t.type;const a=document.createElement("div");a.className="card-header",a.style.cssText="display:flex;align-items:center;gap:.5rem;";const s=document.createElement("code");s.textContent=t.type,s.style.cssText="flex:1;font-size:.85rem;";const p=document.createElement("button");p.type="button",p.className="btn btn-sm btn-danger";const g=document.createElement("span");g.setAttribute("data-icon","trash-2"),p.appendChild(g),p.addEventListener("click",()=>{if(l.remove(),!o.querySelector(".step-card")){const c=document.createElement("p");c.className="text-muted steps-empty-placeholder",c.textContent="No steps yet. Add a step to define what this action does.",c.style.cssText="text-align:center;padding:2rem 0;",o.appendChild(c)}}),a.appendChild(s),a.appendChild(p);const u=document.createElement("div");if(u.className="card-body",i.length===0){const c=document.createElement("p");c.className="text-muted",c.textContent=t.type==="deleteEntry"?"This step deletes the entry. No configuration required.":"No additional configuration required.",c.style.margin="0",u.appendChild(c)}t.type==="updateField"?G(u,t,e):i.forEach(c=>{const h=document.createElement("div");h.style.cssText="margin-bottom:.75rem;";const b=document.createElement("label");b.className="form-label",b.textContent=c.label,h.appendChild(b);let m;c.multiline?(m=document.createElement("textarea"),m.rows=3,m.style.cssText="font-family:monospace;font-size:.8rem;resize:vertical;",m.value=typeof t.config?.[c.name]=="object"?JSON.stringify(t.config[c.name],null,2):t.config?.[c.name]??""):(m=document.createElement("input"),m.type="text",m.value=t.config?.[c.name]??""),m.className=`form-input step-field-${c.name}`,m.placeholder=c.placeholder||"",m.dataset.field=c.name,h.appendChild(m),u.appendChild(h)}),l.appendChild(a),l.appendChild(u),o.appendChild(l),Domma.icons.scan(l)}async function G(e,t,o){const n=o.find("#action-collection").val(),i=document.createElement("div");i.style.cssText="margin-bottom:.75rem;";const l=document.createElement("label");l.className="form-label",l.textContent="Field",i.appendChild(l);const a=document.createElement("select");a.className="form-input step-field-field",a.dataset.field="field";const s=document.createElement("input");s.type="text",s.className="form-input mt-2 step-field-field-custom",s.placeholder="Field name, e.g. status",s.style.display="none";const p=document.createElement("div");p.style.cssText="margin-bottom:.75rem;";const g=document.createElement("label");g.className="form-label",g.textContent="New Value",p.appendChild(g);const u=document.createElement("div");p.appendChild(u);const c=document.createElement("button");c.type="button",c.className="btn btn-ghost btn-sm mt-2",c.style.cssText="font-size:.75rem;padding:.2rem .5rem;",c.textContent="Template Variables";const h=document.createElement("div");h.style.cssText="display:none;background:var(--dm-surface-2,#1e1e2e);border-radius:.4rem;padding:.75rem;margin-top:.5rem;font-size:.8rem;line-height:1.7;";const b=document.createElement("strong");b.textContent="Available template variables:",h.appendChild(b);const m=document.createElement("table");m.style.cssText="width:100%;border-collapse:collapse;margin-top:.4rem;",[["{{now}}","Current ISO timestamp"],["{{user.id}}","Executing user's ID"],["{{user.name}}","Executing user's name"],["{{user.email}}","Executing user's email"],["{{entry.id}}","Entry's unique ID"],["{{entry.data.fieldName}}","Any field value from the entry"],["{{env.CMS_PUBLIC_*}}","Public environment variables"]].forEach(([r,f])=>{const x=document.createElement("tr"),w=document.createElement("td");w.style.cssText="padding:.2rem .5rem .2rem 0;opacity:.7;white-space:nowrap;";const T=document.createElement("code");T.textContent=r,w.appendChild(T);const d=document.createElement("td");d.textContent=f,x.appendChild(w),x.appendChild(d),m.appendChild(x)}),h.appendChild(m),c.addEventListener("click",()=>{const r=h.style.display!=="none";h.style.display=r?"none":"",c.textContent=r?"Template Variables":"Hide Variables"}),p.appendChild(c),p.appendChild(h),e.appendChild(i),e.appendChild(p);let k=[];if(n)try{k=(await S.collections.get(n)).fields||[]}catch{}const F=document.createElement("option");F.value="",F.textContent=k.length?"\u2014 select a field \u2014":"\u2014 no fields available \u2014",a.appendChild(F),k.forEach(r=>{const f=document.createElement("option");f.value=r.name,f.textContent=`${r.label} (${r.name})`,f.dataset.fieldType=r.type,f.dataset.fieldOptions=r.type==="select"?JSON.stringify(r.options||[]):"",a.appendChild(f)});const L=document.createElement("option");L.value="__custom__",L.textContent="\u2014 enter manually \u2014",a.appendChild(L);const C=t.config?.field||"",I=C&&[...a.options].find(r=>r.value===C);I?a.value=C:C&&(a.value="__custom__",s.value=C,s.style.display=""),i.appendChild(a),i.appendChild(s);function V(r,f){u.textContent="";const x=r?[...a.options].find(d=>d.value===r):null,w=x?.dataset.fieldType==="select",T=w?JSON.parse(x.dataset.fieldOptions||"[]"):[];if(w&&T.length){const d=document.createElement("select");d.className="form-input step-field-value",d.dataset.field="value";const O=document.createElement("option");O.value="",O.textContent="\u2014 select a value \u2014",d.appendChild(O),T.forEach(v=>{const N=typeof v=="string"?v:v.value??"",M=typeof v=="string"?v:v.label||v.value||N;if(!N||N==="undefined")return;const A=document.createElement("option");A.value=N,A.textContent=M,d.appendChild(A)});const q=document.createElement("option");q.value="__custom__",q.textContent="\u2014 enter manually \u2014",d.appendChild(q);const y=document.createElement("input");y.type="text",y.className="form-input mt-2",y.placeholder="e.g. approved or {{now}}",y.style.display="none",f&&[...d.options].find(v=>v.value===f&&v.value!=="__custom__")?d.value=f:f&&(d.value="__custom__",y.value=f,y.style.display=""),d.addEventListener("change",()=>{const v=d.value==="__custom__";y.style.display=v?"":"none",v||(y.value="")}),u.appendChild(d),u.appendChild(y)}else{const d=document.createElement("input");d.type="text",d.className="form-input step-field-value",d.dataset.field="value",d.placeholder="e.g. approved or {{now}}",d.value=f||"",u.appendChild(d)}}V(I?C:null,t.config?.value||""),a.addEventListener("change",()=>{const r=a.value==="__custom__";s.style.display=r?"":"none",r||(s.value=""),V(r?null:a.value,"")})}function H(e){const t=[];return e.find(".step-card").each(function(){const o=this.dataset.stepType,n={};if(o==="updateField"){let l=this.querySelector(".step-field-field")?.value?.trim()||"";l==="__custom__"&&(l=this.querySelector(".step-field-field-custom")?.value?.trim()||""),n.field=l;const a=this.querySelector(".step-field-value");let s=a?.value?.trim()||"";s==="__custom__"&&(s=a?.nextElementSibling?.value?.trim()||""),n.value=s}else(z[o]||[]).forEach(l=>{const a=this.querySelector(`.step-field-${l.name}`);if(!a)return;const s=a.value.trim();if(l.multiline&&s)try{n[l.name]=JSON.parse(s)}catch{n[l.name]=s}else n[l.name]=s});t.push({type:o,config:n})}),t}async function K(e){const t=e.find("#action-title").val().trim();if(!t){E.toast("Title is required.",{type:"warning"});return}const o=e.find("#action-collection").val();if(!o){E.toast("Target collection is required (General tab).",{type:"warning"});return}const n=[];e.find(".action-role-cb:checked").each(function(){n.push(this.value)});const i=e.find("#action-rowlevel-enabled").is(":checked");let l=null;if(i){const p=e.find("#action-rowlevel-mode").val()||"owner",g=e.find("#action-rowlevel-userkey").val()||"id";if(l={mode:p,userKey:g},p==="field"){const u=e.find("#action-rowlevel-field").val().trim();if(!u){E.toast("Field name is required for Field Match mode.",{type:"warning"});return}l.field=u}}const a={title:t,slug:e.find("#action-slug").val().trim()||void 0,description:e.find("#action-description").val().trim(),collection:o,trigger:{type:e.find("#action-trigger-type").val()||"manual",label:e.find("#action-trigger-label").val().trim()||"Run",icon:e.find("#action-trigger-icon").val().trim()||"zap",confirmMessage:e.find("#action-trigger-confirm").val().trim()||null},steps:H(e),access:{roles:n,rowLevel:l}},s=e.find("#save-action-btn").get(0);s&&(s.disabled=!0);try{if(_)await S.actions.update(_,a),E.toast("Action updated.",{type:"success"});else{const p=await S.actions.create(a);E.toast("Action created.",{type:"success"}),R.navigate(`/actions/edit/${p.slug}`)}}catch(p){E.toast(p.message||"Failed to save action.",{type:"error"})}finally{s&&(s.disabled=!1)}}
|
|
1
|
+
import{api as S}from"../api.js";let _=null;const z={deleteEntry:[],moveToCollection:[{name:"targetCollection",label:"Target Collection Slug",placeholder:"e.g. archived-applications"}],webhook:[{name:"url",label:"URL",placeholder:"https://hooks.example.com/notify"},{name:"method",label:"Method",placeholder:"POST"},{name:"body",label:"Body (JSON)",placeholder:'{"email": "{{entry.data.email}}"}',multiline:!0}],email:[{name:"to",label:"To",placeholder:"{{entry.data.email}}"},{name:"subject",label:"Subject",placeholder:"Your application update"},{name:"template",label:"Body",placeholder:"Your application has been approved.",multiline:!0}]};export const actionEditorView={templateUrl:"/admin/js/templates/action-editor.html",async onMount(e){_=null;const o=window.location.hash.match(/\/actions\/edit\/([^/?#]+)/);o&&(_=o[1]),E.tabs(e.find("#action-editor-tabs").get(0)),await D(e),await J(e),_&&(e.find("#action-editor-title").text("Edit Action"),await P(e,_)),e.find("#add-step-btn").off("click").on("click",()=>{const n=e.find("#add-step-type").val()||"updateField";B(e,{type:n,config:{}})}),e.find("#save-action-btn").off("click").on("click",async()=>{await K(e)}),U(e),Domma.icons.scan()}};async function D(e){const t=e.find("#action-collection").get(0);if(t)try{(await S.collections.list()).forEach(n=>{const i=document.createElement("option");i.value=n.slug,i.textContent=`${n.title} (${n.slug})`,t.appendChild(i)})}catch{}}async function J(e){const t=e.find("#action-roles-checkboxes").get(0);if(!t)return;["admin","manager","editor","subscriber"].forEach(n=>{const i=document.createElement("label");i.style.cssText="display:flex;align-items:center;gap:.5rem;cursor:pointer;";const l=document.createElement("input");l.type="checkbox",l.value=n,l.dataset.role=n,l.className="action-role-cb",l.checked=n==="admin",i.appendChild(l),i.appendChild(document.createTextNode(n)),t.appendChild(i)})}async function P(e,t){try{const o=await S.actions.get(t);if(!o){E.toast("Action not found.",{type:"error"}),R.navigate("/actions");return}j(e,o)}catch(o){E.toast(o.message||"Failed to load action.",{type:"error"}),R.navigate("/actions")}}function U(e){const t=e.find("#action-rowlevel-enabled").get(0),o=e.find("#action-rowlevel-config").get(0),n=e.find("#action-rowlevel-mode").get(0),i=e.find("#action-rowlevel-field-group").get(0);t&&(t.addEventListener("change",()=>{o&&(o.style.display=t.checked?"flex":"none")}),n&&n.addEventListener("change",()=>{i&&(i.style.display=n.value==="field"?"":"none")}))}function j(e,t){e.find("#action-title").val(t.title||""),e.find("#action-slug").val(t.slug||""),e.find("#action-description").val(t.description||""),e.find("#action-bundled").prop("checked",!!t.bundled),e.find("#action-collection").val(t.collection||""),e.find("#action-trigger-type").val(t.trigger?.type||"manual"),e.find("#action-trigger-label").val(t.trigger?.label||"Run"),e.find("#action-trigger-icon").val(t.trigger?.icon||"zap"),e.find("#action-trigger-confirm").val(t.trigger?.confirmMessage||"");const o=t.access?.roles||["admin"];e.find(".action-role-cb").each(function(){this.checked=o.includes(this.value)});const n=t.access?.rowLevel;n&&(e.find("#action-rowlevel-enabled").prop("checked",!0),e.find("#action-rowlevel-config").css("display","flex"),e.find("#action-rowlevel-mode").val(n.mode||"owner"),e.find("#action-rowlevel-userkey").val(n.userKey||"id"),n.mode==="field"&&(e.find("#action-rowlevel-field-group").css("display",""),e.find("#action-rowlevel-field").val(n.field||"")));const i=e.find("#action-steps-list").get(0);if(i){const l=i.querySelector(".steps-empty-placeholder");for(l&&l.remove();i.firstChild;)i.removeChild(i.firstChild);const a=document.createElement("p");a.className="text-muted steps-empty-placeholder",a.textContent="No steps yet. Add a step to define what this action does.",a.style.cssText="text-align:center;padding:2rem 0;",i.appendChild(a)}(t.steps||[]).forEach(l=>B(e,l))}function B(e,t){const o=e.find("#action-steps-list").get(0);if(!o)return;const n=o.querySelector(".steps-empty-placeholder");n&&n.remove();const i=z[t.type]||[],l=document.createElement("div");l.className="card mb-2 step-card",l.dataset.stepType=t.type;const a=document.createElement("div");a.className="card-header",a.style.cssText="display:flex;align-items:center;gap:.5rem;";const s=document.createElement("code");s.textContent=t.type,s.style.cssText="flex:1;font-size:.85rem;";const p=document.createElement("button");p.type="button",p.className="btn btn-sm btn-danger";const g=document.createElement("span");g.setAttribute("data-icon","trash-2"),p.appendChild(g),p.addEventListener("click",()=>{if(l.remove(),!o.querySelector(".step-card")){const d=document.createElement("p");d.className="text-muted steps-empty-placeholder",d.textContent="No steps yet. Add a step to define what this action does.",d.style.cssText="text-align:center;padding:2rem 0;",o.appendChild(d)}}),a.appendChild(s),a.appendChild(p);const u=document.createElement("div");if(u.className="card-body",i.length===0){const d=document.createElement("p");d.className="text-muted",d.textContent=t.type==="deleteEntry"?"This step deletes the entry. No configuration required.":"No additional configuration required.",d.style.margin="0",u.appendChild(d)}t.type==="updateField"?G(u,t,e):i.forEach(d=>{const h=document.createElement("div");h.style.cssText="margin-bottom:.75rem;";const b=document.createElement("label");b.className="form-label",b.textContent=d.label,h.appendChild(b);let m;d.multiline?(m=document.createElement("textarea"),m.rows=3,m.style.cssText="font-family:monospace;font-size:.8rem;resize:vertical;",m.value=typeof t.config?.[d.name]=="object"?JSON.stringify(t.config[d.name],null,2):t.config?.[d.name]??""):(m=document.createElement("input"),m.type="text",m.value=t.config?.[d.name]??""),m.className=`form-input step-field-${d.name}`,m.placeholder=d.placeholder||"",m.dataset.field=d.name,h.appendChild(m),u.appendChild(h)}),l.appendChild(a),l.appendChild(u),o.appendChild(l),Domma.icons.scan(l)}async function G(e,t,o){const n=o.find("#action-collection").val(),i=document.createElement("div");i.style.cssText="margin-bottom:.75rem;";const l=document.createElement("label");l.className="form-label",l.textContent="Field",i.appendChild(l);const a=document.createElement("select");a.className="form-input step-field-field",a.dataset.field="field";const s=document.createElement("input");s.type="text",s.className="form-input mt-2 step-field-field-custom",s.placeholder="Field name, e.g. status",s.style.display="none";const p=document.createElement("div");p.style.cssText="margin-bottom:.75rem;";const g=document.createElement("label");g.className="form-label",g.textContent="New Value",p.appendChild(g);const u=document.createElement("div");p.appendChild(u);const d=document.createElement("button");d.type="button",d.className="btn btn-ghost btn-sm mt-2",d.style.cssText="font-size:.75rem;padding:.2rem .5rem;",d.textContent="Template Variables";const h=document.createElement("div");h.style.cssText="display:none;background:var(--dm-surface-2,#1e1e2e);border-radius:.4rem;padding:.75rem;margin-top:.5rem;font-size:.8rem;line-height:1.7;";const b=document.createElement("strong");b.textContent="Available template variables:",h.appendChild(b);const m=document.createElement("table");m.style.cssText="width:100%;border-collapse:collapse;margin-top:.4rem;",[["{{now}}","Current ISO timestamp"],["{{user.id}}","Executing user's ID"],["{{user.name}}","Executing user's name"],["{{user.email}}","Executing user's email"],["{{entry.id}}","Entry's unique ID"],["{{entry.data.fieldName}}","Any field value from the entry"],["{{env.CMS_PUBLIC_*}}","Public environment variables"]].forEach(([r,f])=>{const x=document.createElement("tr"),w=document.createElement("td");w.style.cssText="padding:.2rem .5rem .2rem 0;opacity:.7;white-space:nowrap;";const T=document.createElement("code");T.textContent=r,w.appendChild(T);const c=document.createElement("td");c.textContent=f,x.appendChild(w),x.appendChild(c),m.appendChild(x)}),h.appendChild(m),d.addEventListener("click",()=>{const r=h.style.display!=="none";h.style.display=r?"none":"",d.textContent=r?"Template Variables":"Hide Variables"}),p.appendChild(d),p.appendChild(h),e.appendChild(i),e.appendChild(p);let k=[];if(n)try{k=(await S.collections.get(n)).fields||[]}catch{}const F=document.createElement("option");F.value="",F.textContent=k.length?"\u2014 select a field \u2014":"\u2014 no fields available \u2014",a.appendChild(F),k.forEach(r=>{const f=document.createElement("option");f.value=r.name,f.textContent=`${r.label} (${r.name})`,f.dataset.fieldType=r.type,f.dataset.fieldOptions=r.type==="select"?JSON.stringify(r.options||[]):"",a.appendChild(f)});const L=document.createElement("option");L.value="__custom__",L.textContent="\u2014 enter manually \u2014",a.appendChild(L);const C=t.config?.field||"",I=C&&[...a.options].find(r=>r.value===C);I?a.value=C:C&&(a.value="__custom__",s.value=C,s.style.display=""),i.appendChild(a),i.appendChild(s);function V(r,f){u.textContent="";const x=r?[...a.options].find(c=>c.value===r):null,w=x?.dataset.fieldType==="select",T=w?JSON.parse(x.dataset.fieldOptions||"[]"):[];if(w&&T.length){const c=document.createElement("select");c.className="form-input step-field-value",c.dataset.field="value";const O=document.createElement("option");O.value="",O.textContent="\u2014 select a value \u2014",c.appendChild(O),T.forEach(v=>{const N=typeof v=="string"?v:v.value??"",M=typeof v=="string"?v:v.label||v.value||N;if(!N||N==="undefined")return;const A=document.createElement("option");A.value=N,A.textContent=M,c.appendChild(A)});const q=document.createElement("option");q.value="__custom__",q.textContent="\u2014 enter manually \u2014",c.appendChild(q);const y=document.createElement("input");y.type="text",y.className="form-input mt-2",y.placeholder="e.g. approved or {{now}}",y.style.display="none",f&&[...c.options].find(v=>v.value===f&&v.value!=="__custom__")?c.value=f:f&&(c.value="__custom__",y.value=f,y.style.display=""),c.addEventListener("change",()=>{const v=c.value==="__custom__";y.style.display=v?"":"none",v||(y.value="")}),u.appendChild(c),u.appendChild(y)}else{const c=document.createElement("input");c.type="text",c.className="form-input step-field-value",c.dataset.field="value",c.placeholder="e.g. approved or {{now}}",c.value=f||"",u.appendChild(c)}}V(I?C:null,t.config?.value||""),a.addEventListener("change",()=>{const r=a.value==="__custom__";s.style.display=r?"":"none",r||(s.value=""),V(r?null:a.value,"")})}function H(e){const t=[];return e.find(".step-card").each(function(){const o=this.dataset.stepType,n={};if(o==="updateField"){let l=this.querySelector(".step-field-field")?.value?.trim()||"";l==="__custom__"&&(l=this.querySelector(".step-field-field-custom")?.value?.trim()||""),n.field=l;const a=this.querySelector(".step-field-value");let s=a?.value?.trim()||"";s==="__custom__"&&(s=a?.nextElementSibling?.value?.trim()||""),n.value=s}else(z[o]||[]).forEach(l=>{const a=this.querySelector(`.step-field-${l.name}`);if(!a)return;const s=a.value.trim();if(l.multiline&&s)try{n[l.name]=JSON.parse(s)}catch{n[l.name]=s}else n[l.name]=s});t.push({type:o,config:n})}),t}async function K(e){const t=e.find("#action-title").val().trim();if(!t){E.toast("Title is required.",{type:"warning"});return}const o=e.find("#action-collection").val();if(!o){E.toast("Target collection is required (General tab).",{type:"warning"});return}const n=[];e.find(".action-role-cb:checked").each(function(){n.push(this.value)});const i=e.find("#action-rowlevel-enabled").is(":checked");let l=null;if(i){const p=e.find("#action-rowlevel-mode").val()||"owner",g=e.find("#action-rowlevel-userkey").val()||"id";if(l={mode:p,userKey:g},p==="field"){const u=e.find("#action-rowlevel-field").val().trim();if(!u){E.toast("Field name is required for Field Match mode.",{type:"warning"});return}l.field=u}}const a={title:t,slug:e.find("#action-slug").val().trim()||void 0,description:e.find("#action-description").val().trim(),...e.find("#action-bundled").is(":checked")?{bundled:!0}:{},collection:o,trigger:{type:e.find("#action-trigger-type").val()||"manual",label:e.find("#action-trigger-label").val().trim()||"Run",icon:e.find("#action-trigger-icon").val().trim()||"zap",confirmMessage:e.find("#action-trigger-confirm").val().trim()||null},steps:H(e),access:{roles:n,rowLevel:l}},s=e.find("#save-action-btn").get(0);s&&(s.disabled=!0);try{if(_)await S.actions.update(_,a),E.toast("Action updated.",{type:"success"});else{const p=await S.actions.create(a);E.toast("Action created.",{type:"success"}),R.navigate(`/actions/edit/${p.slug}`)}}catch(p){E.toast(p.message||"Failed to save action.",{type:"error"})}finally{s&&(s.disabled=!1)}}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{api as
|
|
1
|
+
import{api as m}from"../api.js";let u=null;export const blockEditorView={templateUrl:"/admin/js/templates/block-editor.html",async onMount(e){u=null;const s=window.location.hash.match(/\/blocks\/edit\/([^/?#]+)/);s&&(u=decodeURIComponent(s[1]));const i=e.find("#block-name").get(0),n=e.find("#block-content").get(0);if(u){e.find("#block-editor-title").text("Edit Block"),i&&(i.value=u,i.disabled=!0);try{const c=await m.blocks.get(u);n&&(n.value=c.content||""),e.find("#block-bundled").prop("checked",!!c.bundled)}catch(c){E.toast(c.message||"Block not found.",{type:"error"}),R.navigate("/blocks");return}}n&&(v(n,e),w(n,e)),e.find("#save-block-btn").off("click").on("click",async()=>{await L(e)}),Domma.icons.scan()}};function v(e,t){const s=t.find("#block-line-numbers").get(0),i=t.find("#block-cursor-pos").get(0);function n(){const l=e.value.split(`
|
|
2
2
|
`).length;s.textContent=Array.from({length:l},(o,r)=>r+1).join(`
|
|
3
3
|
`),s.scrollTop=e.scrollTop}n(),e.addEventListener("input",n),e.addEventListener("scroll",()=>{s.scrollTop=e.scrollTop});function c(){if(!i)return;const o=e.value.slice(0,e.selectionStart).split(`
|
|
4
4
|
`);i.textContent=`Ln ${o.length}, Col ${o[o.length-1].length+1}`}e.addEventListener("keyup",c),e.addEventListener("click",c),e.addEventListener("keydown",l=>{if(l.key==="Tab"){l.preventDefault();const o=e.selectionStart,r=e.selectionEnd;if(!l.shiftKey)e.value=e.value.slice(0,o)+" "+e.value.slice(r),e.selectionStart=e.selectionEnd=o+2;else{const a=e.value.lastIndexOf(`
|
|
5
|
-
`,o-1)+1,
|
|
5
|
+
`,o-1)+1,p=e.value.slice(a,a+2),f=p===" "?2:p[0]===" "?1:0;f>0&&(e.value=e.value.slice(0,a)+e.value.slice(a+f),e.selectionStart=e.selectionEnd=Math.max(a,o-f))}e.dispatchEvent(new Event("input"))}if(l.key==="Enter"){l.preventDefault();const o=e.selectionStart,r=e.value.lastIndexOf(`
|
|
6
6
|
`,o-1)+1,a=e.value.slice(r,o).match(/^(\s*)/)[1];e.value=e.value.slice(0,o)+`
|
|
7
|
-
`+a+e.value.slice(e.selectionEnd),e.selectionStart=e.selectionEnd=o+1+a.length,e.dispatchEvent(new Event("input"))}});const
|
|
8
|
-
`),
|
|
7
|
+
`+a+e.value.slice(e.selectionEnd),e.selectionStart=e.selectionEnd=o+1+a.length,e.dispatchEvent(new Event("input"))}});const d=t.find("#block-editor-toolbar").get(0);d&&d.addEventListener("click",l=>{const o=l.target.closest("[data-action]");o&&(b(o.dataset.action,e),e.focus())})}function b(e,t){switch(e){case"undo":document.execCommand("undo");break;case"redo":document.execCommand("redo");break;case"cut":t.selectionStart!==t.selectionEnd&&(navigator.clipboard.writeText(t.value.slice(t.selectionStart,t.selectionEnd)).catch(()=>{}),document.execCommand("cut"));break;case"copy":t.selectionStart!==t.selectionEnd&&navigator.clipboard.writeText(t.value.slice(t.selectionStart,t.selectionEnd)).catch(()=>{});break;case"paste":navigator.clipboard.readText().then(s=>{const i=t.selectionStart;t.value=t.value.slice(0,i)+s+t.value.slice(t.selectionEnd),t.selectionStart=t.selectionEnd=i+s.length,t.dispatchEvent(new Event("input"))}).catch(()=>E.toast("Use Ctrl+V to paste.",{type:"info"}));break;case"select-all":t.select();break;case"format":g(t);break}}function g(e){const t=e.value.match(/(<[^>]+>|[^<]+)/g)||[],s=[];let i=0;const n=" ";for(const l of t){const o=l.trim();if(o)if(o.startsWith("</"))i=Math.max(0,i-1),s.push(n.repeat(i)+o);else if(o.startsWith("<")&&!o.startsWith("<!")&&!o.endsWith("/>")){s.push(n.repeat(i)+o);const r=(o.match(/^<(\w+)/)||[])[1]||"";k.has(r.toLowerCase())||i++}else s.push(n.repeat(i)+o)}const c=s.join(`
|
|
8
|
+
`),d=e.selectionStart;e.value=c,e.selectionStart=e.selectionEnd=Math.min(d,c.length),e.dispatchEvent(new Event("input"))}const k=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]);function w(e,t){const s=t.find("#block-editor-body").get(0),i=t.find("#block-sample-data").get(0),n=t.find("#block-preview-output");t.find("[data-mode]").each(function(){this.addEventListener("click",()=>{t.find("[data-mode]").each(function(){this.classList.remove("active")}),this.classList.add("active"),s.className=`editor-body editor-mode-${this.dataset.mode}`,this.dataset.mode!=="write"&&c()})}),e.addEventListener("input",()=>{h(i,e),c()}),e.value.trim()&&h(i,e);function c(){if(!!s.classList.contains("editor-mode-write"))return;const l=e.value.replace(/\{\{([\w_]+)\}\}/g,(o,r)=>{const a=i.querySelector(`[data-placeholder="${CSS.escape(r)}"]`);return C(a?.value??`[${r}]`)});n.html(l,{safe:!1}),Domma.icons.scan(n.get(0))}i._renderPreview=c}function h(e,t){const s=y(t.value),i=new Set([...e.querySelectorAll("[data-placeholder]")].map(n=>n.dataset.placeholder));if(s.size>0&&e.querySelector("p.text-muted")?.remove(),s.forEach(n=>{i.has(n)||S(e,n)}),e.querySelectorAll("[data-placeholder]").forEach(n=>{s.has(n.dataset.placeholder)||n.closest(".block-sample-row")?.remove()}),s.size===0&&!e.querySelector("p.text-muted")){const n=document.createElement("p");n.className="text-muted",n.style.cssText="font-size:.8rem;margin:0;",n.textContent="No {{placeholders}} detected in template.",e.appendChild(n)}}function S(e,t){const s=document.createElement("div");s.className="block-sample-row",s.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.35rem;";const i=document.createElement("label");i.style.cssText="font-size:.75rem;color:var(--dm-text-muted,#888);white-space:nowrap;min-width:90px;text-align:right;flex-shrink:0;",i.textContent=`{{${t}}}`;const n=document.createElement("input");n.type="text",n.className="form-input form-input--sm",n.style.flex="1",n.dataset.placeholder=t,n.placeholder=`Sample ${t}`,n.value=x(t),n.addEventListener("input",()=>{e._renderPreview&&e._renderPreview()}),s.appendChild(i),s.appendChild(n),e.appendChild(s)}function x(e){const t=e.toLowerCase();return t==="_id"?"a1b2c3d4-e5f6-7890-abcd-ef1234567890":t==="_createdat"?new Date().toISOString():t==="_updatedat"?new Date().toISOString():t.includes("email")?"user@example.com":t.includes("phone")?"+44 7700 900000":t.includes("name")?"Jane Smith":t.includes("title")?"Sample Title":t.includes("message")||t.includes("content")||t.includes("description")?"This is a sample value for preview purposes.":t.includes("rating")?"excellent":t.includes("status")?"active":t.includes("priority")?"high":t.includes("date")?new Date().toLocaleDateString():t.includes("tag")?"tag1, tag2":t.includes("subject")?"Sample Subject":t.includes("category")?"general":`Sample ${e}`}function y(e){const t=new Set;for(const[,s]of e.matchAll(/\{\{([\w_]+)\}\}/g))t.add(s);return t}function C(e){return String(e??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}async function L(e){const t=e.find("#block-name").get(0),s=e.find("#block-content").get(0),i=(t?.value||"").trim(),n=s?.value??"";if(!i){E.toast("Block name is required.",{type:"warning"});return}if(!/^[a-z0-9][a-z0-9-]*$/.test(i)){E.toast("Name must start with a letter or digit and contain only lowercase letters, digits, and hyphens.",{type:"warning"});return}const c=e.find("#save-block-btn").get(0);c&&(c.disabled=!0);try{const d=!!e.find("#block-bundled").is(":checked");await m.blocks.put(i,{content:n,bundled:d}),E.toast(u?"Block updated.":"Block created.",{type:"success"}),u||(u=i,R.navigate(`/blocks/edit/${encodeURIComponent(i)}`))}catch(d){E.toast(d.message||"Failed to save block.",{type:"error"})}finally{c&&(c.disabled=!1)}}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{api as
|
|
2
|
-
`).filter(
|
|
3
|
-
`),
|
|
1
|
+
import{api as S}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"]),ae=["public","subscriber","editor","manager","admin"],ee=["create","read","update","delete"];let f=[],y=null,q=!0,M=null,I="file";function te(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function le(e){return Z.find(t=>t.value===e)?.label||e}function ne(e){const t={...f[e]},a=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(a&&(t.label=a.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()),Q.has(t.type)){const c=document.getElementById(`fb-options-${e}`);c&&(t.options=c.value.split(`
|
|
2
|
+
`).filter(g=>g.trim()).map(g=>{const[b,...u]=g.split(":");return{value:b.trim(),label:u.join(":").trim()||b.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 f.map((e,t)=>ne(t))}function oe(e,t){const a=document.createElement("div");a.className="fb-field-card",a.dataset.index=t,a.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;flex-shrink:0;",a.draggable=!0,i.addEventListener("mousedown",()=>{a.draggable=!0}),a.addEventListener("dragstart",s=>{M=t,s.dataTransfer.effectAllowed="move",a.style.opacity="0.4"}),a.addEventListener("dragend",()=>{a.style.opacity="",document.querySelectorAll(".fb-field-card").forEach(s=>s.classList.remove("fb-drag-over"))}),a.addEventListener("dragover",s=>{s.preventDefault(),s.dataTransfer.dropEffect="move",document.querySelectorAll(".fb-field-card").forEach(L=>L.classList.remove("fb-drag-over")),a.classList.add("fb-drag-over")}),a.addEventListener("dragleave",()=>{a.classList.remove("fb-drag-over")}),a.addEventListener("drop",s=>{if(s.preventDefault(),a.classList.remove("fb-drag-over"),M===null||M===t)return;f=X();const[L]=f.splice(M,1);f.splice(t,0,L),M=null,_(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=le(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(),f.splice(t,1),_(document.getElementById("fields-list"))}),n.appendChild(i),n.appendChild(p),n.appendChild(r),n.appendChild(l),n.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 g=document.createElement("div"),b=document.createElement("label");b.className="form-label",b.textContent="Label";const u=document.createElement("input");u.id=`fb-label-${t}`,u.type="text",u.className="form-input",u.value=e.label||"",u.addEventListener("input",()=>{p.textContent=u.value.trim()||"(Untitled field)";const s=document.getElementById(`fb-name-${t}`);s&&!s.dataset.manual&&(s.value=te(u.value).replace(/-/g,"_"))}),g.appendChild(b),g.appendChild(u);const m=document.createElement("div"),k=document.createElement("label");k.className="form-label",k.textContent="Name (key)";const h=document.createElement("input");h.id=`fb-name-${t}`,h.type="text",h.className="form-input",h.value=e.name||"",h.addEventListener("input",()=>{h.dataset.manual="1"}),m.appendChild(k),m.appendChild(h);const v=document.createElement("div"),w=document.createElement("label");w.className="form-label",w.textContent="Type";const C=document.createElement("select");C.id=`fb-type-${t}`,C.className="form-input",Z.forEach(s=>{const L=document.createElement("option");L.value=s.value,L.textContent=s.label,s.value===e.type&&(L.selected=!0),C.appendChild(L)}),C.addEventListener("change",()=>{r.textContent=le(C.value);const s=d.querySelector(".fb-options-wrap");s&&(s.style.display=Q.has(C.value)?"":"none")}),v.appendChild(w),v.appendChild(C),c.appendChild(g),c.appendChild(m),c.appendChild(v);const B=document.createElement("div");B.style.cssText="display:grid;grid-template-columns:1fr 1fr auto;gap:.6rem;align-items:end;margin-bottom:.6rem;";const T=document.createElement("div"),j=document.createElement("label");j.className="form-label",j.textContent="Placeholder";const A=document.createElement("input");A.id=`fb-placeholder-${t}`,A.type="text",A.className="form-input",A.value=e.placeholder||"",T.appendChild(j),T.appendChild(A);const V=document.createElement("div"),Y=document.createElement("label");Y.className="form-label",Y.textContent="Helper text";const W=document.createElement("input");W.id=`fb-helper-${t}`,W.type="text",W.className="form-input",W.value=e.helper||"",V.appendChild(Y),V.appendChild(W);const H=document.createElement("div");H.style.cssText="padding-bottom:.35rem;";const z=document.createElement("label");z.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;white-space:nowrap;";const P=document.createElement("input");P.id=`fb-required-${t}`,P.type="checkbox",P.checked=!!e.required,z.appendChild(P),z.appendChild(document.createTextNode("Required")),H.appendChild(z),B.appendChild(T),B.appendChild(V),B.appendChild(H);const F=document.createElement("div");F.className="fb-options-wrap",F.style.display=Q.has(e.type)?"":"none";const $=document.createElement("label");$.className="form-label",$.textContent="Options (one per line: value: Label)";const D=document.createElement("textarea");D.id=`fb-options-${t}`,D.className="form-input",D.rows=4,D.value=(e.options||[]).map(s=>typeof s=="string"?`${s}: ${s}`:`${s.value??""}: ${s.label??s.value??""}`).join(`
|
|
3
|
+
`),F.appendChild($),F.appendChild(D);const x=document.createElement("div");x.className="fb-grid-row",x.style.gridTemplateColumns="1fr auto",x.style.gap=".6rem",x.style.alignItems="end",x.style.marginBottom=".6rem",x.style.display=document.getElementById("collection-layout")?.value==="grid"?"grid":"none";const G=document.createElement("div"),J=document.createElement("label");J.className="form-label",J.textContent="Column Span";const N=document.createElement("input");N.id=`fb-span-${t}`,N.type="number",N.className="form-input",N.min="1",N.max="6",N.value=e.span>1?String(e.span):"1",G.appendChild(J),G.appendChild(N);const K=document.createElement("div");K.style.cssText="padding-bottom:.35rem;";const O=document.createElement("label");O.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;white-space:nowrap;";const U=document.createElement("input");return U.id=`fb-fullwidth-${t}`,U.type="checkbox",U.checked=!!e.fullWidth,O.appendChild(U),O.appendChild(document.createTextNode("Full Width")),K.appendChild(O),x.appendChild(G),x.appendChild(K),d.appendChild(c),d.appendChild(B),d.appendChild(F),d.appendChild(x),n.addEventListener("click",()=>{const s=d.style.display!=="none";d.style.display=s?"none":"",l.style.transform=s?"":"rotate(180deg)"}),a.appendChild(n),a.appendChild(d),a}function _(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,a)=>{e.appendChild(oe(t,a))})}}function se(e,t){t.textContent="",ee.forEach(a=>{const n=e?.[a]||{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=a.charAt(0).toUpperCase()+a.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-${a}-enabled`,l.checked=!!n.enabled,r.appendChild(l),r.appendChild(document.createTextNode("Enable public access"));const o=document.createElement("select");o.id=`api-${a}-access`,o.className="form-input",ae.forEach(d=>{const c=document.createElement("option");c.value=d,c.textContent=d.charAt(0).toUpperCase()+d.slice(1),d===n.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: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&&!q)}});const a=t.map(n=>({label:n,value:n}));E.dropdown("#storage-connection-trigger",{items:a.length?a:[{label:"default",value:"default"}],onSelect:({item:n})=>{e.find("#storage-connection").val(n.value),e.find("#storage-connection-label").text(n.label)}})}function ce(){return(document.getElementById("storage-adapter")?.value||"file")==="mongodb"?{adapter:"mongodb",connection:document.getElementById("storage-connection")?.value||"default"}:{adapter:"file"}}function ie(){const e={};return ee.forEach(t=>{const a=document.getElementById(`api-${t}-enabled`)?.checked??!1,n=document.getElementById(`api-${t}-access`)?.value||"admin";e[t]={enabled:a,access:n}}),e}export const collectionEditorView={templateUrl:"/admin/js/templates/collection-editor.html",async onMount(e){f=[],y=null,q=!0;const a=window.location.hash.match(/\/collections\/edit\/([^/?#]+)/);a&&(y=a[1],q=!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 n=e.find("#fields-list").get(0),i=e.find("#api-access-rows").get(0),p=await S.collections.proStatus();p?.pro&&y!=="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(q){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=te(l.value))}),o.addEventListener("input",()=>{o.dataset.manual="1"}))}else try{const l=await S.collections.get(y);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-bundled").prop("checked",!!l.bundled),e.find("#collection-columns-group").get(0).style.display=l.layout==="grid"?"":"none",f=l.fields||[],r=l.api||r,I=l.storage?.adapter||"file",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"))),y==="roles"&&e.find("#storage-tab-btn").hide()}catch{E.toast("Failed to load collection.",{type:"error"}),R.navigate("/collections");return}_(n),se(r,i),e.find("#add-field-btn").off("click").on("click",()=>{f=X(),f.push({id:`field-${Date.now()}`,name:"",label:"",type:"string",required:!1,placeholder:"",helper:"",options:[],validation:[],logic:null}),_(n);const l=n.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-${f.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(),g=ie(),b=e.find("#collection-layout").val()||"stacked",u=parseInt(e.find("#collection-columns").val(),10)||2,m=ce(),k=e.find("#collection-bundled").is(":checked"),h=e.find("#save-collection-btn");h.prop("disabled",!0);try{if(q){const v=await S.collections.create({title:l,slug:o,description:d,layout:b,columns:u,fields:c,api:g,storage:m,...k?{bundled:!0}:{}});y=v.slug,I=m.adapter||"file",q=!1,E.toast("Collection created.",{type:"success"}),R.navigate(`/collections/edit/${v.slug}`)}else if((m.adapter||"file")!==I){let w=0;try{w=(await S.collections.listEntries(y,{limit:1}))?.total??0}catch{}const C=I==="file"?`file \u2192 ${m.adapter}`:`${I} \u2192 ${m.adapter||"file"}`;if(w>0&&await E.confirm(`You changed the storage adapter (${C}).
|
|
4
4
|
|
|
5
|
-
Migrate ${
|
|
5
|
+
Migrate ${w} existing ${w===1?"entry":"entries"} to the new storage?`)){const T=await S.collections.migrateStorage(y,m);I=m.adapter||"file",E.toast(`Migrated ${T.migrated} of ${T.total} entries.`,{type:"success"})}else await S.collections.update(y,{title:l,description:d,layout:b,columns:u,fields:c,api:g,storage:m,...k?{bundled:!0}:{bundled:!1}}),I=m.adapter||"file",E.toast("Collection saved.",{type:"success"})}else await S.collections.update(y,{title:l,description:d,layout:b,columns:u,fields:c,api:g,storage:m}),E.toast("Collection saved.",{type:"success"})}catch(v){E.toast(v.message||"Failed to save.",{type:"error"})}finally{h.prop("disabled",!1)}}),Domma.icons.scan()}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{api as b}from"../api.js";function h(e){return String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}let
|
|
1
|
+
import{api as b}from"../api.js";function h(e){return String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}let i={};export const collectionsView={templateUrl:"/admin/js/templates/collections.html",async onMount(e){const s=E.loader(e.get(0),{type:"dots"});E.tabs(e.find("#collections-tabs").get(0));const c=e.find("#collections-tabs").get(0)?.querySelector(".tab-list");if(c){const t=e.find("#collections-header-actions").get(0);Array.from(c.querySelectorAll(".tab-item")).forEach((n,l)=>{n.addEventListener("click",()=>{t&&(t.style.display=l===0?"":"none")})})}await Promise.all([B(e),j(e)]),s.destroy(),e.find("#create-collection-btn").off("click").on("click",()=>{const t=E.modal({title:"New Collection",size:"sm"}),n=document.createElement("div");n.style.cssText="padding:.25rem 0 .5rem;";const l=document.createElement("div");F.create({title:{type:"string",label:"Collection Title",placeholder:"e.g. Products, Blog Posts\u2026",required:!0}},{},{showSubmitButton:!1}).renderTo(l),n.appendChild(l);const o=document.createElement("div");o.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:.75rem;";const d=document.createElement("button");d.className="btn btn-ghost",d.textContent="Cancel";const a=document.createElement("button");a.className="btn btn-primary",a.textContent="Create",o.appendChild(d),o.appendChild(a),n.appendChild(o),t.element.appendChild(n),t.open();const r=l.querySelector('input[name="title"]');setTimeout(()=>r?.focus(),50);async function m(){const p=r?.value.trim();if(p)try{const u=await b.collections.create({title:p});t.close(),R.navigate(`/collections/edit/${u.slug}`)}catch(u){E.toast(u.message||"Failed to create collection.",{type:"error"})}}d.addEventListener("click",()=>t.close()),a.addEventListener("click",m),r?.addEventListener("keydown",p=>{p.key==="Enter"&&m()})}),e.find("#connections-raw-toggle").on("change",function(){if(this.checked)i=x(e),v(e),e.find("#connections-form-view").hide(),e.find("#connections-raw-view").show();else{const t=e.find("#connections-raw-json").get(0)?.value||"{}";try{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=x(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=x(e);try{await b.collections.saveConnections(n),i=n,E.toast("Connections saved.",{type:"success"})}catch(l){E.toast(l.message||"Failed to save connections.",{type:"error"})}}),Domma.icons.scan()}};async function B(e){let s=[];try{s=await b.collections.list()}catch{E.toast("Could not load collections.",{type:"error"})}T.create("#collections-table",{data:s,columns:[{key:"title",title:"Title",render:(c,t)=>{const n=document.createElement("span");n.style.cssText="display:flex;align-items:center;gap:.4rem;";const l=document.createElement("a");if(l.href=`#/collections/${h(t.slug)}/entries`,l.textContent=c,l.style.fontWeight="600",n.appendChild(l),t.storage?.adapter==="mongodb"){const o=document.createElement("span");o.className="badge badge-outline",o.textContent="MongoDB",o.title=`Connection: ${t.storage.connection||"default"}`,o.style.cssText="font-size:0.65rem;padding:1px 6px;color:var(--dm-success,#16a34a);border-color:var(--dm-success,#16a34a);flex-shrink:0;",n.appendChild(o)}if(t.bundled){const o=document.createElement("span");o.className="badge badge-outline",o.textContent="Bundled",o.title="Seeded on fresh installs",o.style.cssText="font-size:0.65rem;padding:1px 6px;color:var(--dm-info,#2563eb);border-color:var(--dm-info,#2563eb);flex-shrink:0;",n.appendChild(o)}if(t.plugin){const o=document.createElement("span");o.className="badge badge-outline",o.textContent=t.plugin,o.title=`Managed by the ${t.plugin} plugin`,o.style.cssText="font-size:0.65rem;padding:1px 6px;color:var(--dm-warning,#d97706);border-color:var(--dm-warning,#d97706);flex-shrink:0;",n.appendChild(o)}return n.outerHTML}},{key:"slug",title:"Slug",render:c=>{const t=document.createElement("code");return t.textContent=c,t.outerHTML}},{key:"fields",title:"Field Count",render:c=>String(c?.length??0)},{key:"entryCount",title:"Entry Count",render:c=>String(c??0)},{key:"slug",title:"Actions",render:(c,t)=>{const n=document.createElement("div");n.style.cssText="display:flex;gap:.4rem;justify-content:flex-end;flex-wrap:wrap;";const l=document.createElement("a");l.href=`#/collections/edit/${h(c)}`,l.className="btn btn-sm btn-ghost",l.textContent="Edit Schema";const o=document.createElement("a");if(o.href=`#/collections/${h(c)}/entries`,o.className="btn btn-sm btn-ghost",o.textContent="Entries",n.appendChild(l),n.appendChild(o),!new Set(["roles","user-profiles"]).has(c)){const a=document.createElement("a");a.href=`#/forms/edit/${h(c)}`,a.className="btn btn-sm btn-ghost",a.textContent="Form",n.appendChild(a)}if(!t.plugin){const a=document.createElement("button");a.className="btn btn-sm btn-danger js-delete-collection",a.dataset.slug=c,a.textContent="Delete",n.appendChild(a)}return n.outerHTML}}],emptyMessage:'No collections yet. Click "New Collection" to get started.'}),document.querySelectorAll(".js-delete-collection").forEach(c=>{c.addEventListener("click",async()=>{const t=c.dataset.slug;if(await E.confirm(`Delete collection "${t}" and all its data? This cannot be undone.`))try{await b.collections.delete(t),E.toast("Collection deleted.",{type:"success"}),await B(e)}catch{E.toast("Failed to delete collection.",{type:"error"})}})}),Domma.icons.scan()}async function j(e){try{i=await b.collections.getConnections()}catch{i={}}C(e),v(e)}function C(e){const s=e.find("#connections-list").get(0),c=e.find("#connections-empty").get(0);if(!s)return;s.textContent="";const t=Object.keys(i);c&&(c.style.display=t.length?"none":""),t.forEach(n=>{const l=i[n],o=document.createElement("div");o.className="conn-card card mb-3",o.dataset.connKey=n;const d=document.createElement("div");d.className="card-header",d.style.cssText="display:flex;align-items:center;gap:.5rem;";const a=document.createElement("input");a.type="text",a.className="form-input conn-name",a.value=n,a.placeholder="Connection name",a.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=x(e);const L=a.value.trim();L&&delete i[L],C(e),v(e)}),d.appendChild(a),d.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=l.type||"mongodb",f.disabled=!0,p.appendChild(u),p.appendChild(f);const w=document.createElement("div"),N=document.createElement("label");N.className="form-label",N.textContent="URI";const g=document.createElement("input");g.type="text",g.className="form-input conn-uri",g.value=l.uri||"",g.placeholder="mongodb://localhost:27017",w.appendChild(N),w.appendChild(g);const S=document.createElement("div"),k=document.createElement("label");k.className="form-label",k.textContent="Database";const y=document.createElement("input");y.type="text",y.className="form-input conn-database",y.value=l.database||"",y.placeholder="mydb",S.appendChild(k),S.appendChild(y),m.appendChild(p),m.appendChild(w),m.appendChild(S),o.appendChild(d),o.appendChild(m),s.appendChild(o)})}function x(e){const s={},c=e.find("#connections-list").get(0)?.querySelectorAll(".conn-card")||[];for(const t of c){const n=t.querySelector(".conn-name")?.value.trim(),l=t.querySelector(".conn-uri")?.value.trim()||"",o=t.querySelector(".conn-database")?.value.trim()||"";n&&(s[n]={type:"mongodb",uri:l,database:o,options:{}})}return s}function v(e){const s=e.find("#connections-raw-json").get(0);s&&(s.value=JSON.stringify(i,null,2))}
|
|
@@ -5,4 +5,4 @@ option2:Option 2`,p.value=(e.defaultOptions||[]).map(f=>{const r=typeof f=="stri
|
|
|
5
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
6
|
no:No
|
|
7
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"})})}};
|
|
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(),...e.find("#field-bundled").is(":checked")?{bundled:!0}:{},fields:b,settings:{submitText:e.find("#setting-submit-text").val().trim()||"Submit",successMessage:e.find("#setting-success-message").val().trim()||"Thank you.",layout:e.find("#setting-layout").val()||"stacked",columns:parseInt(e.find("#setting-columns").val(),10)||2,submitSpan:e.find("#setting-submit-span").val()||"",honeypot:e.find("#setting-honeypot").prop("checked"),rateLimitPerMinute:parseInt(e.find("#setting-rate-limit").val(),10)||3,successRedirect:e.find("#setting-success-redirect").val().trim()||null,actionSlug:e.find("#action-cms-slug").val()||null},actions:{email:{enabled:e.find("#action-email-enabled").prop("checked"),recipients:e.find("#action-email-recipients").val().trim(),subjectPrefix:e.find("#action-email-subject-prefix").val().trim()},webhook:{enabled:e.find("#action-webhook-enabled").prop("checked"),url:e.find("#action-webhook-url").val().trim(),method:e.find("#action-webhook-method").val()},...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||""),e.find("#field-bundled").prop("checked",!!n.bundled);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"})})}};
|
|
@@ -1,22 +1,23 @@
|
|
|
1
|
-
import{api as x}from"../api.js";let I=1;const v=t=>String(t||"").replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<");function S(t){const
|
|
2
|
-
<div class="nav-item-row${r?" nav-item-row--child":""}${f?" nav-item-row--hidden":""}" data-id="${
|
|
1
|
+
import{api as x}from"../api.js";let I=1;const v=t=>String(t||"").replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<");function S(t){const e=[];return(t||[]).forEach(l=>{const o=I++;e.push({_id:o,text:l.text||"",url:l.url||"",icon:l.icon||"",hidden:l.hidden||!1,bundled:l.bundled||!1,parentId:null}),(l.items||l.children||[]).forEach(a=>{e.push({_id:I++,text:a.text||"",url:a.url||"",icon:a.icon||"",hidden:a.hidden||!1,bundled:a.bundled||!1,parentId:o})})}),e}function F(t){return t.filter(e=>e.parentId===null).map(e=>{const l=t.filter(a=>a.parentId===e._id).map(a=>({text:a.text,url:a.url,...a.icon&&{icon:a.icon},...a.hidden&&{hidden:!0},...a.bundled&&{bundled:!0}})),o={text:e.text,url:e.url,...e.icon&&{icon:e.icon},...e.hidden&&{hidden:!0},...e.bundled&&{bundled:!0}};return l.length&&(o.items=l),o})}function A(t){const e=[];return t.filter(l=>l.parentId===null).forEach(l=>{e.push(l),t.filter(o=>o.parentId===l._id).forEach(o=>e.push(o))}),e}export const navigationView={templateUrl:"/admin/js/templates/navigation.html",async onMount(t){let[e,l]=await Promise.all([x.navigation.get().catch(()=>({brand:{},items:[]})),x.settings.get().catch(()=>({}))]),o=S(e.items),a=(l.footer?.links||[]).map(d=>({text:d.text||"",url:d.url||"",...d.hidden&&{hidden:!0}}));const u=()=>{t.find("#nav-items-list .nav-item-row").each(function(){const i=parseInt($(this).data("id"),10),n=o.find(f=>f._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(),n.hidden=$(this).find(".btn-toggle-hidden").attr("data-hidden")==="true",n.bundled=$(this).find(".btn-toggle-bundled").attr("data-bundled")==="true";const r=$(this).find(".item-parent").val();n.parentId=r?parseInt(r,10):null});const d=new Set(o.filter(i=>i.parentId!==null).map(i=>i._id));o.forEach(i=>{i.parentId!==null&&d.has(i.parentId)&&(i.parentId=null)})},p=()=>{const d=t.find("#nav-items-list").empty(),i=o.filter(n=>n.parentId===null);A(o).forEach(n=>{const r=n.parentId!==null,f=n.hidden===!0,g='<option value="">\u2014 top-level \u2014</option>'+i.filter(c=>c._id!==n._id).map(c=>`<option value="${c._id}"${c._id===n.parentId?" selected":""}>${v(c.text)||"(untitled)"}</option>`).join("");d.append(`
|
|
2
|
+
<div class="nav-item-row${r?" nav-item-row--child":""}${f?" nav-item-row--hidden":""}" data-id="${n._id}">
|
|
3
3
|
<span class="nav-col-indent">${r?"\u21B3":""}</span>
|
|
4
|
-
<input type="text" class="form-input item-text nav-col-main" value="${v(
|
|
5
|
-
<input type="text" class="form-input item-url nav-col-main" value="${v(
|
|
6
|
-
<input type="text" class="form-input item-icon nav-col-icon" value="${v(
|
|
4
|
+
<input type="text" class="form-input item-text nav-col-main" value="${v(n.text)}" placeholder="Label">
|
|
5
|
+
<input type="text" class="form-input item-url nav-col-main" value="${v(n.url)}" placeholder="/url">
|
|
6
|
+
<input type="text" class="form-input item-icon nav-col-icon" value="${v(n.icon)}" placeholder="icon">
|
|
7
7
|
<select class="form-select item-parent nav-col-parent">${g}</select>
|
|
8
8
|
<span class="nav-col-action">
|
|
9
|
-
<button class="btn btn-sm btn-ghost btn-toggle-
|
|
10
|
-
<button class="btn btn-sm btn-
|
|
9
|
+
<button class="btn btn-sm btn-ghost btn-toggle-bundled${n.bundled?" active":""}" data-id="${n._id}" data-bundled="${!!n.bundled}" data-tooltip="${n.bundled?"Unbundle":"Bundle (seed on install)"}"><span data-icon="package"></span></button>
|
|
10
|
+
<button class="btn btn-sm btn-ghost btn-toggle-hidden${f?" active":""}" data-id="${n._id}" data-hidden="${f}" data-tooltip="${f?"Show":"Hide"}"><span data-icon="eye-off"></span></button>
|
|
11
|
+
<button class="btn btn-sm btn-danger btn-remove-item" data-id="${n._id}" data-tooltip="Remove"><span data-icon="trash"></span></button>
|
|
11
12
|
</span>
|
|
12
13
|
</div>
|
|
13
|
-
`)}),Domma.icons.scan("#nav-items-list"),document.querySelectorAll("#nav-items-list [data-tooltip]").forEach(
|
|
14
|
-
<div class="nav-item-row${r?" nav-item-row--hidden":""}" data-footer-idx="${
|
|
14
|
+
`)}),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"})})},b=()=>{const d=t.find("#footer-links-list").empty();a.forEach((i,n)=>{const r=i.hidden===!0;d.append(`
|
|
15
|
+
<div class="nav-item-row${r?" nav-item-row--hidden":""}" data-footer-idx="${n}">
|
|
15
16
|
<input type="text" class="form-input footer-link-text nav-col-main" value="${v(i.text)}" placeholder="Label">
|
|
16
17
|
<input type="text" class="form-input footer-link-url nav-col-main" value="${v(i.url)}" placeholder="/url">
|
|
17
18
|
<span class="nav-col-action">
|
|
18
|
-
<button class="btn btn-sm btn-ghost btn-toggle-hidden${r?" active":""}" data-idx="${
|
|
19
|
-
<button class="btn btn-sm btn-danger btn-remove-footer" data-idx="${
|
|
19
|
+
<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>
|
|
20
|
+
<button class="btn btn-sm btn-danger btn-remove-footer" data-idx="${n}" data-tooltip="Remove"><span data-icon="trash"></span></button>
|
|
20
21
|
</span>
|
|
21
22
|
</div>
|
|
22
|
-
`)}),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"})})},
|
|
23
|
+
`)}),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=()=>{a=[],t.find("#footer-links-list .nav-item-row").each(function(){const d=$(this).find(".btn-toggle-hidden").attr("data-hidden")==="true",i={text:$(this).find(".footer-link-text").val().trim(),url:$(this).find(".footer-link-url").val().trim()};d&&(i.hidden=!0),a.push(i)})};t.find("#add-footer-link").on("click",()=>{h(),a.push({text:"",url:"",hidden:!1}),b()}),t.off("click",".btn-remove-footer").on("click",".btn-remove-footer",function(){h();const d=parseInt($(this).data("idx"),10);a.splice(d,1),b()}),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"),t.find("#field-brand-tagline").val(e.brand?.tagline||""),t.find("#field-brand-logo").val(e.brand?.logo||""),t.find("#field-nav-position").val(e.position||"sticky"),t.find("#field-brand-size").val(e.brand?.size||"md"),t.find("#field-nav-font-family").val(e.style?.fontFamily||""),t.find("#field-nav-font-size").val(e.style?.fontSize||""),t.find("#field-nav-font-weight").val(e.style?.fontWeight||""),t.find("#field-nav-letter-spacing").val(e.style?.letterSpacing||""),t.find("#field-nav-icon-size").val(e.style?.iconSize||""),t.find("#field-admin-brand-title").val(l.adminBrand?.title||""),t.find("#field-admin-brand-icon").val(l.adminBrand?.icon||""),t.find("#field-admin-brand-logo").val(l.adminBrand?.logo||""),p(),b(),E.tabs(t.find("#nav-tabs").get(0)),t.find("#add-nav-item").on("click",()=>{u(),o.push({_id:I++,text:"",url:"",icon:"",hidden:!1,parentId:null}),p()}),t.off("click",".btn-remove-item").on("click",".btn-remove-item",function(){u();const d=parseInt($(this).data("id"),10);o=o.filter(i=>i._id!==d&&i.parentId!==d),p()}),t.off("click","#nav-items-list .btn-toggle-bundled").on("click","#nav-items-list .btn-toggle-bundled",function(d){d.stopPropagation(),u();const i=parseInt($(this).data("id"),10),n=o.find(r=>r._id===i);n&&(n.bundled=!n.bundled),p()}),t.off("click","#nav-items-list .btn-toggle-hidden").on("click","#nav-items-list .btn-toggle-hidden",function(d){d.stopPropagation(),u();const i=parseInt($(this).data("id"),10),n=o.find(r=>r._id===i);n&&(n.hidden=!n.hidden),p()}),t.off("click","#footer-links-list .btn-toggle-hidden").on("click","#footer-links-list .btn-toggle-hidden",function(d){d.stopPropagation(),h();const i=parseInt($(this).data("idx"),10);a[i]&&(a[i].hidden=!a[i].hidden),b()}),t.off("change",".item-parent").on("change",".item-parent",function(){u(),p()}),t.find("#save-nav-btn").on("click",async()=>{u(),h();const d=F(o.map(s=>({...s,text:s.text.trim(),url:s.url.trim(),icon:s.icon.trim()}))).filter(s=>s.text||s.url),i=t.find("#field-brand-icon").val().trim(),n=t.find("#field-brand-tagline").val().trim(),r=t.find("#field-brand-logo").val().trim(),f=t.find("#field-brand-size").val(),g={brand:{text:t.find("#field-brand-text").val().trim(),url:t.find("#field-brand-url").val().trim()||"/",...r&&{logo:r},...i&&!r&&{icon:i},...n&&{tagline:n},...f!=="md"&&{size:f}},items:d,variant:t.find("#field-nav-variant").val(),position:t.find("#field-nav-position").val()||"sticky",style:{...t.find("#field-nav-font-family").val()&&{fontFamily:t.find("#field-nav-font-family").val()},...t.find("#field-nav-font-size").val()&&{fontSize:t.find("#field-nav-font-size").val()},...t.find("#field-nav-font-weight").val()&&{fontWeight:t.find("#field-nav-font-weight").val()},...t.find("#field-nav-letter-spacing").val()&&{letterSpacing:t.find("#field-nav-letter-spacing").val()},...t.find("#field-nav-icon-size").val()&&{iconSize:t.find("#field-nav-icon-size").val()}}},c=a.filter(s=>s.text||s.url);try{await x.navigation.save(g),e=g,o=S(e.items),p(),E.toast("Navigation saved.",{type:"success"})}catch(s){console.error("[navigation] save failed:",s),E.toast("Failed to save navigation.",{type:"error"});return}const y=t.find("#field-admin-brand-logo").val().trim(),k=t.find("#field-admin-brand-icon").val().trim(),_=t.find("#field-admin-brand-title").val().trim(),m={..._&&{title:_},...y&&{logo:y},...!y&&k&&{icon:k}};try{await x.settings.save({adminBrand:m,footer:{links:c}}),l={...l,adminBrand:m},a=c,b();const s=m.title||"CMS Admin",L=m.icon||"layout",w=m.logo||"",z=$("#admin-topbar .topbar-brand");w?z.html(`<img src="${w}" class="topbar-brand-logo" alt="${s}"><span class="topbar-brand-text">${s}</span>`):z.html(`<span data-icon="${L}"></span><span class="topbar-brand-text">${s}</span>`),Domma.icons.scan("#admin-topbar .topbar-brand")}catch(s){console.error("[navigation] admin brand / footer save failed:",s),E.toast(`Admin brand save failed: ${s.message||s}`,{type:"error"})}})}};
|