domma-cms 0.9.1 → 0.9.5
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/block-editor.html +163 -163
- package/admin/js/templates/form-editor.html +245 -245
- package/admin/js/views/action-editor.js +1 -1
- package/admin/js/views/block-editor.js +8 -8
- package/admin/js/views/collection-editor.js +4 -4
- package/admin/js/views/collections.js +1 -1
- package/admin/js/views/form-editor.js +7 -7
- package/admin/js/views/forms.js +1 -1
- package/admin/js/views/navigation.js +14 -14
- package/admin/js/views/page-editor.js +35 -35
- package/admin/js/views/pages.js +5 -5
- package/admin/js/views/plugins.js +19 -10
- package/admin/js/views/view-editor.js +1 -1
- package/config/plugins.json +35 -0
- package/package.json +1 -1
- package/plugins/docs/data/documents/57e003f0-68f2-47dc-9c36-ed4b10ed3deb.json +4 -4
- package/plugins/docs/data/folders.json +3 -3
- package/plugins/docs/data/versions/57e003f0-68f2-47dc-9c36-ed4b10ed3deb/1.json +5 -0
- package/plugins/garage/admin/templates/garage.html +30 -0
- package/plugins/garage/admin/views/garage.js +62 -1
- package/plugins/garage/plugin.json +1 -1
- package/plugins/notes/admin/templates/notes.html +2 -11
- package/plugins/notes/admin/views/notes.js +107 -129
- package/plugins/notes/collections/user-notes/schema.json +2 -1
- package/plugins/notes/plugin.json +1 -1
- package/plugins/site-search/admin/templates/site-search.html +174 -46
- package/plugins/site-search/admin/views/site-search.js +72 -1
- package/plugins/site-search/config.js +6 -1
- package/plugins/site-search/plugin.json +1 -1
- package/plugins/site-search/public/inject-head.html +1 -1
- package/plugins/site-search/public/search.css +1 -1
- package/plugins/site-search/public/search.js +1 -1
- package/plugins/todo/admin/templates/todo.html +2 -8
- package/plugins/todo/admin/views/todo.js +122 -106
- package/plugins/todo/collections/todos/schema.json +2 -1
- package/plugins/todo/plugin.json +1 -1
- package/server/routes/api/media.js +127 -118
- package/server/routes/api/plugins.js +15 -4
- package/server/server.js +288 -285
- package/server/services/collections.js +17 -10
- package/server/services/plugins.js +77 -67
- package/server/services/renderer.js +3 -3
- package/plugins/docs/data/documents/452f49b7-9c93-4a67-874d-27f882891ad2.json +0 -11
package/admin/js/views/pages.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{api as p}from"../api.js";export const pagesView={templateUrl:"/admin/js/templates/pages.html",async onMount(
|
|
2
|
-
<a href="#/pages/edit${
|
|
3
|
-
<a href="${
|
|
4
|
-
<button class="btn btn-sm btn-danger btn-delete" data-path="${
|
|
5
|
-
`}],emptyMessage:'No pages found. <a href="#/pages/new">Create one</a>.'}),Domma.icons.scan(),document.querySelectorAll("#pages-table [data-tooltip]").forEach(
|
|
1
|
+
import{api as p}from"../api.js";export const pagesView={templateUrl:"/admin/js/templates/pages.html",async onMount(a){const c=E.loader(a.get(0),{type:"dots"});let i=await p.pages.list().catch(()=>[]);c.destroy();const l=s=>{T.create("#pages-table",{data:s,columns:[{key:"title",title:"Title",render:(t,e)=>{const n=e.bundled?'<span class="badge badge-outline" style="font-size:0.65rem;padding:1px 6px;color:var(--dm-info,#2563eb);border-color:var(--dm-info,#2563eb);margin-left:.35rem;">Bundled</span>':"",o=e.plugin?`<span class="badge badge-outline" style="font-size:0.65rem;padding:1px 6px;color:var(--dm-warning,#d97706);border-color:var(--dm-warning,#d97706);margin-left:.35rem;" title="Managed by the ${e.plugin} plugin">${e.plugin}</span>`:"";return`<a href="#/pages/edit${e.urlPath}" style="font-weight:600;">${t}</a>${n}${o}`}},{key:"urlPath",title:"URL",render:t=>`<code>${t}</code>`},{key:"layout",title:"Layout"},{key:"status",title:"Status",render:t=>`<span class="badge badge-${t==="published"?"success":"warning"}">${t}</span>`},{key:"tags",title:"Tags",render:t=>Array.isArray(t)&&t.length?t.map(e=>`<span class="badge badge-info badge-pill badge-sm">${e}</span>`).join(" "):"\u2014"},{key:"updatedAt",title:"Updated",render:t=>t?D(t).format("DD MMM YYYY"):"\u2014"},{key:"actions",title:"Actions",render:(t,e)=>`
|
|
2
|
+
<a href="#/pages/edit${e.urlPath}" class="btn btn-sm btn-primary">Edit</a>
|
|
3
|
+
<a href="${e.urlPath}" target="_blank" class="btn btn-sm btn-ghost" data-tooltip="View"><span data-icon="external-link"></span></a>
|
|
4
|
+
<button class="btn btn-sm btn-danger btn-delete" data-path="${e.urlPath}" data-plugin="${e.plugin||""}">Delete</button>
|
|
5
|
+
`}],emptyMessage:'No pages found. <a href="#/pages/new">Create one</a>.'}),Domma.icons.scan(),document.querySelectorAll("#pages-table [data-tooltip]").forEach(t=>{E.tooltip(t,{content:t.getAttribute("data-tooltip"),position:"top"})}),Domma.effects.reveal(".card",{animation:"fade",duration:350})};l(i);const g=s=>{const t=a.find("#pages-tree").empty().get(0);if(!s.length){t.textContent="No pages found.";return}const e=s.map(n=>{const o=n.urlPath.split("/").filter(Boolean),r=o.length>1?"/"+o.slice(0,-1).join("/"):null,u=r&&s.some(b=>b.urlPath===r);return{id:n.urlPath,parent_id:u?r:null,name:n.title||n.urlPath,icon:n.status==="published"?"check-circle":"file-text"}});E.treeView(t,{data:e,idKey:"id",parentKey:"parent_id",labelKey:"name",iconKey:"icon",expandedByDefault:!0,onSelect:n=>{R.navigate(`/pages/edit${n}`)}}),Domma.icons.scan(t)};a.find("#view-table-btn").on("click",function(){a.find("#pages-table").show(),a.find("#pages-tree").hide(),$(this).addClass("btn-primary").removeClass("btn-ghost"),a.find("#view-tree-btn").addClass("btn-ghost").removeClass("btn-primary")}),a.find("#view-tree-btn").on("click",function(){a.find("#pages-table").hide(),a.find("#pages-tree").show(),$(this).addClass("btn-primary").removeClass("btn-ghost"),a.find("#view-table-btn").addClass("btn-ghost").removeClass("btn-primary"),g(i)}),a.find("#view-table-btn, #view-tree-btn").each(function(){E.tooltip(this,{content:this.getAttribute("data-tooltip"),position:"top"})});const d=()=>{const s=a.find("#status-filter").val(),t=a.find("#pages-search").val().toLowerCase().trim(),e=i.filter(n=>!(s&&n.status!==s||t&&!`${n.title} ${n.urlPath} ${(n.tags||[]).join(" ")}`.toLowerCase().includes(t)));l(e)};a.find("#status-filter").off("change").on("change",d),a.find("#pages-search").get(0).addEventListener("input",d),a.off("click",".btn-delete").on("click",".btn-delete",async function(){const s=$(this).data("path"),t=$(this).data("plugin"),e=t?`This page is managed by the <strong>${t}</strong> plugin. Deleting it may cause the plugin to malfunction. Continue?`:`Delete page at <strong>${s}</strong>? This cannot be undone.`;if(await E.confirm(e))try{await p.pages.delete(s),E.toast("Page deleted.",{type:"success"}),i=i.filter(n=>n.urlPath!==s),l(i)}catch{E.toast("Failed to delete page.",{type:"error"})}})}};
|
|
@@ -1,35 +1,44 @@
|
|
|
1
|
-
import{api as
|
|
1
|
+
import{api as c}from"../api.js";export const pluginsView={templateUrl:"/admin/js/templates/plugins.html",async onMount(d){const m=E.loader(d.get(0),{type:"dots"});let i=[];try{i=await c.plugins.list()}catch(e){E.toast(`Failed to load plugins: ${e.message}`,{type:"error"})}if(m.destroy(),!i.length){d.find("#plugins-empty").show();return}p(d,i),Domma.icons.scan()}};function p(d,m){const i=d.find("#plugins-grid").empty();m.forEach(e=>{const n=e.enabled?"badge-success":"badge-secondary",l=e.enabled?"Enabled":"Disabled",s=e.enabled?"Disable":"Enable",g=e.enabled?"btn-ghost":"btn-primary",u=e.bundled?'<span class="badge badge-outline" style="font-size:0.65rem;padding:1px 6px;color:var(--dm-info,#2563eb);border-color:var(--dm-info,#2563eb);" title="Included on fresh installs">Bundled</span>':"",r=e.author?`by ${a(e.author)}`:"",t=e.date?a(e.date):"",o=`
|
|
2
2
|
<div class="card plugin-card" data-plugin="${e.name}">
|
|
3
3
|
<div class="card-body">
|
|
4
4
|
<div class="plugin-header">
|
|
5
5
|
<div class="plugin-icon"><span data-icon="${e.icon||"package"}"></span></div>
|
|
6
6
|
<div class="plugin-meta">
|
|
7
7
|
<div class="plugin-name">${a(e.displayName)}</div>
|
|
8
|
-
<div class="plugin-version">v${a(e.version)}${
|
|
8
|
+
<div class="plugin-version">v${a(e.version)}${r?` · ${r}`:""}</div>
|
|
9
|
+
</div>
|
|
10
|
+
<div style="display:flex;gap:.35rem;align-items:center;">
|
|
11
|
+
${u}
|
|
12
|
+
<span class="badge ${n}">${l}</span>
|
|
9
13
|
</div>
|
|
10
|
-
<span class="badge ${s}">${r}</span>
|
|
11
14
|
</div>
|
|
12
15
|
<p class="plugin-desc">${a(e.description)}</p>
|
|
13
|
-
${
|
|
16
|
+
${t?`<p class="plugin-date text-muted" style="font-size:.8rem;margin:0">Released ${t}</p>`:""}
|
|
14
17
|
</div>
|
|
15
18
|
<div class="plugin-footer">
|
|
16
19
|
<span class="text-muted" style="font-size:.8rem">Restart server after changes</span>
|
|
17
20
|
<div class="plugin-footer-actions">
|
|
18
21
|
${e.settingsSchema?`<button class="btn btn-sm btn-ghost btn-plugin-settings" data-name="${e.name}">Settings</button>`:""}
|
|
19
|
-
<button class="btn btn-sm
|
|
22
|
+
<button class="btn btn-sm btn-ghost btn-bundle-plugin"
|
|
23
|
+
data-name="${e.name}"
|
|
24
|
+
data-bundled="${e.bundled?"1":"0"}"
|
|
25
|
+
title="${e.bundled?"Remove from fresh install bundle":"Include in fresh install bundle"}">
|
|
26
|
+
${e.bundled?"Unbundle":"Bundle"}
|
|
27
|
+
</button>
|
|
28
|
+
<button class="btn btn-sm ${g} btn-toggle-plugin"
|
|
20
29
|
data-name="${e.name}"
|
|
21
30
|
data-enabled="${e.enabled?"1":"0"}">
|
|
22
|
-
${
|
|
31
|
+
${s}
|
|
23
32
|
</button>
|
|
24
33
|
</div>
|
|
25
34
|
</div>
|
|
26
|
-
</div>`;
|
|
35
|
+
</div>`;i.append(o)}),Domma.icons.scan(),Domma.effects.reveal(".plugin-card",{animation:"fade",stagger:60,duration:400}),Domma.effects.ripple(".btn-toggle-plugin"),i.on("click",".btn-plugin-settings",async function(){const e=$(this).data("name"),n=m.find(t=>t.name===e);if(!n?.settingsSchema)return;const s=`<form id="plugin-settings-form">${n.settingsSchema.map(t=>{const o=n.settings?.[t.key]??t.default??"";if(t.type==="select"){const v=t.options.map(b=>`<option value="${a(b.value)}"${String(o)===String(b.value)?" selected":""}>${a(b.label)}</option>`).join("");return`<div class="form-group">
|
|
27
36
|
<label class="form-label">${a(t.label)}</label>
|
|
28
37
|
<select class="form-control" name="${a(t.key)}">${v}</select>
|
|
29
38
|
</div>`}return t.type==="number"?`<div class="form-group">
|
|
30
39
|
<label class="form-label">${a(t.label)}</label>
|
|
31
|
-
<input class="form-control" type="number" name="${a(t.key)}" value="${a(String(
|
|
40
|
+
<input class="form-control" type="number" name="${a(t.key)}" value="${a(String(o))}"${t.min!=null?` min="${t.min}"`:""}${t.max!=null?` max="${t.max}"`:""}>
|
|
32
41
|
</div>`:`<div class="form-group">
|
|
33
42
|
<label class="form-label">${a(t.label)}</label>
|
|
34
|
-
<input class="form-control" type="text" name="${a(t.key)}" value="${a(String(
|
|
35
|
-
</div>`}).join("")}</form>`;if(!await E.confirm(
|
|
43
|
+
<input class="form-control" type="text" name="${a(t.key)}" value="${a(String(o))}">
|
|
44
|
+
</div>`}).join("")}</form>`;if(!await E.confirm(s,{title:`${a(n.displayName)} Settings`,confirmText:"Save",cancelText:"Cancel",html:!0}))return;const u=document.getElementById("plugin-settings-form");if(!u)return;const r={};n.settingsSchema.forEach(t=>{const o=u.elements[t.key];o&&(r[t.key]=t.type==="number"?Number(o.value):o.value)});try{await c.plugins.update(e,{settings:r}),n.settings={...n.settings,...r},E.toast("Settings saved.",{type:"success"})}catch(t){E.toast(`Failed to save: ${t.message}`,{type:"error"})}}),i.on("click",".btn-bundle-plugin",async function(){const e=$(this),n=e.data("name"),l=e.data("bundled")!==1&&e.data("bundled")!=="1";e.prop("disabled",!0).text(l?"Bundling\u2026":"Unbundling\u2026");try{await c.plugins.update(n,{bundled:l}),E.toast(`Plugin ${l?"marked as bundled":"unbundled"}.`,{type:"success"});const s=await c.plugins.list();p(d,s)}catch(s){E.toast(`Failed: ${s.message}`,{type:"error"}),e.prop("disabled",!1)}}),i.on("click",".btn-toggle-plugin",async function(){const e=$(this),n=e.data("name"),l=e.data("enabled")!==1&&e.data("enabled")!=="1";e.prop("disabled",!0).text(l?"Enabling\u2026":"Disabling\u2026");try{await c.plugins.update(n,{enabled:l}),E.toast(`Plugin ${l?"enabled":"disabled"}. Restart the server to apply changes.`,{type:"success"});const s=await c.plugins.list();p(d,s)}catch(s){E.toast(`Failed: ${s.message}`,{type:"error"}),e.prop("disabled",!1)}})}function a(d){return String(d).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{api as h}from"../api.js";let b=null,f=null;export const viewEditorView={templateUrl:"/admin/js/templates/view-editor.html",async onMount(e){b=null,f=null;const l=window.location.hash.match(/\/views\/edit\/([^/?#]+)/);l&&(b=l[1]),E.tabs(e.find("#view-editor-tabs").get(0)),await O(e),await D(e),await j(e),await T(e),e.find("#view-source").get(0)?.addEventListener("change",async()=>{await x(e)}),b&&(e.find("#view-editor-title").text("Edit View"),await I(e,b)),e.find("#add-stage-btn").off("click").on("click",()=>{const n=e.find("#add-stage-type").val()||"$match";q(e,{type:n,config:{}})}),e.find("#save-view-btn").off("click").on("click",async()=>{await Q(e)}),H(e),Y(e),Domma.icons.scan()}};async function O(e){const t=e.find("#view-source").get(0);if(t)try{(await h.collections.list()).forEach(n=>{const a=document.createElement("option");a.value=n.slug,a.textContent=`${n.title} (${n.slug})`,t.appendChild(a)})}catch{}}async function T(e){const t=e.find("#view-block-name").get(0);if(t)try{(await h.blocks.list()).forEach(n=>{const a=document.createElement("option");a.value=n.name,a.textContent=`${n.name}.html`,t.appendChild(a)})}catch{}}async function D(e){const t=e.find("#view-connection").get(0);if(t)try{const l=await h.collections.getConnections();Object.keys(l).forEach(n=>{if(!t.querySelector(`option[value="${n}"]`)){const a=document.createElement("option");a.value=n,a.textContent=n,t.appendChild(a)}})}catch{}}async function j(e){const t=e.find("#view-roles-checkboxes").get(0);if(!t)return;["admin","manager","editor","subscriber"].forEach(n=>{const a=document.createElement("label");a.style.cssText="display:flex;align-items:center;gap:.5rem;cursor:pointer;";const s=document.createElement("input");s.type="checkbox",s.value=n,s.dataset.role=n,s.className="view-role-cb",s.checked=n==="admin",a.appendChild(s),a.appendChild(document.createTextNode(n)),t.appendChild(a)})}async function x(e){const t=e.find("#view-source").val();if(!t){f=null;return}try{f=(await h.collections.get(t))?.fields||[]}catch{f=null}e.find(".stage-card[data-guided]").each(function(){const l=this.dataset.stageType,n=this.querySelector(".stage-guided");n&&(l==="$match"&&M(n),l==="$sort"&&z(n))}),A(e,null)}async function I(e,t){try{const l=await h.views.get(t);if(!l){E.toast("View not found.",{type:"error"}),R.navigate("/views");return}const n=l.pipeline?.source;n&&(e.find("#view-source").val(n),await x(e)),F(e,l)}catch(l){E.toast(l.message||"Failed to load view.",{type:"error"}),R.navigate("/views")}}function F(e,t){e.find("#view-title").val(t.title||""),e.find("#view-slug").val(t.slug||""),e.find("#view-description").val(t.description||""),e.find("#view-bundled").prop("checked",!!t.bundled),e.find("#view-source").val(t.pipeline?.source||""),e.find("#view-connection").val(t.connection||"default"),e.find("#view-display-mode").val(t.display?.mode||"table"),e.find("#view-page-size").val(t.display?.pageSize||25),e.find("#view-block-name").val(t.display?.block||"");const l=t.access?.roles||["admin"];e.find(".view-role-cb").each(function(){this.checked=l.includes(this.value)}),e.find("#view-public").prop("checked",t.access?.public||!1);const n=t.access?.rowLevel;n&&(e.find("#view-rowlevel-enabled").prop("checked",!0),e.find("#view-rowlevel-config").css("display","flex"),e.find("#view-rowlevel-mode").val(n.mode||"owner"),e.find("#view-rowlevel-userkey").val(n.userKey||"id"),n.mode==="field"&&(e.find("#view-rowlevel-field-group").css("display",""),e.find("#view-rowlevel-field").val(n.field||"")));const a=e.find("#pipeline-stages-list").get(0);if(a)for(;a.firstChild;)a.removeChild(a.firstChild);(t.pipeline?.stages||[]).forEach(s=>q(e,s)),A(e,t.display?.columns||[]),S(e)}const _=[{value:"eq",label:"= equals"},{value:"ne",label:"\u2260 not equals"},{value:"gt",label:"> greater than"},{value:"lt",label:"< less than"},{value:"gte",label:"\u2265 greater or equal"},{value:"lte",label:"\u2264 less or equal"},{value:"contains",label:"~ contains (regex)"},{value:"in",label:"\u2208 in (comma list)"}];function g(e=!1){const t=[];return e&&t.push({value:"",label:"\u2014 select field \u2014"}),(f||[]).forEach(l=>{t.push({value:`data.${l.name}`,label:`${l.label||l.name} (${l.name})`})}),t.length===0||t.length===1&&e?(t.push({value:"__meta.createdAt",label:"Created At"}),t.push({value:"__meta.updatedAt",label:"Updated At"})):(t.push({value:"__meta.createdAt",label:"Created At (meta)"}),t.push({value:"__meta.updatedAt",label:"Updated At (meta)"})),t}function v(e,t){const l=document.createElement("select");return l.className="form-input form-input--sm",e.forEach(n=>{const a=document.createElement("option");a.value=n.value,a.textContent=n.label,String(n.value)===String(t)&&(a.selected=!0),l.appendChild(a)}),l}function N(e,t,l="text"){const n=document.createElement("input");return n.type=l,n.className="form-input form-input--sm",n.placeholder=e,n.value=t??"",n}function B(e){if(!f?.length)return null;const t=e?.startsWith("data.")?e.slice(5):null;return t&&f.find(l=>l.name===t)||null}function y(e,t,l=""){const n=B(e);if(n?.type==="select"&&(t==="eq"||t==="ne")&&n.options?.length){const i=document.createElement("select");i.className="form-input form-input--sm match-val",i.style.flex="1";const c=document.createElement("option");return c.value="",c.textContent="\u2014 select value \u2014",i.appendChild(c),(n.options||[]).forEach(o=>{const d=typeof o=="string"?o:o.value??"",u=typeof o=="string"?o:o.label||d;if(!d||d==="undefined")return;const m=document.createElement("option");m.value=d,m.textContent=u,d===l&&(m.selected=!0),i.appendChild(m)}),i}const s=t==="in"?"val1, val2, val3":t==="contains"?"search pattern (regex)":t==="gt"||t==="lt"||t==="gte"||t==="lte"?"0":"value",r=document.createElement("input");return r.type="text",r.className="form-input form-input--sm match-val",r.style.flex="1",r.placeholder=s,r.value=l,r}function k(e){const t=[],l={$eq:"eq",$ne:"ne",$gt:"gt",$lt:"lt",$gte:"gte",$lte:"lte",$in:"in"};return Object.entries(e||{}).forEach(([n,a])=>{if(typeof a=="object"&&a!==null&&!Array.isArray(a)){if("$regex"in a){t.push({field:n,op:"contains",val:String(a.$regex??"")});return}Object.entries(a).forEach(([s,r])=>{const i=l[s];i&&t.push({field:n,op:i,val:Array.isArray(r)?r.join(", "):String(r)})})}else t.push({field:n,op:"eq",val:String(a??"")})}),t}function C(e,t,l="",n="eq",a=""){const s=document.createElement("div");s.className="match-condition-row",s.style.cssText="display:flex;gap:.4rem;align-items:center;margin-bottom:.4rem;flex-wrap:wrap;";const r=v([{value:"",label:"\u2014 field \u2014"},...g()],l);r.className+=" match-field",r.style.minWidth="140px";const i=v(_,n);i.className+=" match-op",i.style.minWidth="160px";const c=document.createElement("div");c.className="match-val-wrap",c.style.cssText="flex:1;min-width:120px;display:flex;",c.appendChild(y(l,n,a));const o=()=>{const m=c.querySelector(".match-val")?.value??"";for(;c.firstChild;)c.removeChild(c.firstChild);c.appendChild(y(r.value,i.value,m))};r.addEventListener("change",o),i.addEventListener("change",o);const d=document.createElement("button");d.type="button",d.className="btn btn-sm btn-ghost",d.title="Remove";const u=document.createElement("span");u.setAttribute("data-icon","x"),d.appendChild(u),d.addEventListener("click",()=>s.remove()),s.appendChild(r),s.appendChild(i),s.appendChild(c),s.appendChild(d),e.insertBefore(s,t),Domma.icons.scan(s)}function W(e,t={}){for(;e.firstChild;)e.removeChild(e.firstChild);const l=t.$match||t||{},n="$or"in l;let a=[];n?(l.$or||[]).forEach(d=>k(d).forEach(u=>a.push(u))):a=k(l);const s=document.createElement("div");s.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.6rem;";const r=document.createElement("label");r.className="form-label",r.style.marginBottom="0",r.textContent="Match:";const i=document.createElement("select");i.className="form-input form-input--sm match-logic",i.style.width="auto",[{value:"and",label:"ALL conditions (AND)"},{value:"or",label:"ANY condition (OR)"}].forEach(d=>{const u=document.createElement("option");u.value=d.value,u.textContent=d.label,(n?"or":"and")===d.value&&(u.selected=!0),i.appendChild(u)}),s.appendChild(r),s.appendChild(i),e.appendChild(s);const c=document.createElement("button");c.type="button",c.className="btn btn-ghost btn-sm match-add-btn";const o=document.createElement("span");o.setAttribute("data-icon","plus"),c.appendChild(o),c.appendChild(document.createTextNode(" Add Condition")),c.addEventListener("click",()=>{C(e,c),Domma.icons.scan(e)}),e.appendChild(c),a.forEach(d=>C(e,c,d.field,d.op,d.val)),a.length===0&&C(e,c),Domma.icons.scan(e)}function M(e){e.querySelectorAll(".match-condition-row").forEach(t=>{const l=t.querySelector(".match-field");if(l){const r=l.value,i=v([{value:"",label:"\u2014 field \u2014"},...g()],r);i.className=l.className,i.style.cssText=l.style.cssText;const c=t.querySelector(".match-val-wrap"),o=t.querySelector(".match-op");c&&o&&i.addEventListener("change",()=>{const d=c.querySelector(".match-val")?.value??"";for(;c.firstChild;)c.removeChild(c.firstChild);c.appendChild(y(i.value,o.value,d))}),l.parentNode.replaceChild(i,l)}const n=t.querySelector(".match-val-wrap"),a=t.querySelector(".match-field")?.value,s=t.querySelector(".match-op")?.value;if(n&&a&&s){const r=n.querySelector(".match-val")?.value??"";for(;n.firstChild;)n.removeChild(n.firstChild);n.appendChild(y(a,s,r))}})}function U(e){const t=e.querySelector(".match-logic")?.value||"and",l=a=>{const s=a.querySelector(".match-field")?.value?.trim(),r=a.querySelector(".match-op")?.value,i=a.querySelector(".match-val")?.value?.trim()??"";if(!s)return null;const c=s.startsWith("__")?s.slice(2):s,o={};switch(r){case"eq":o[c]=i;break;case"ne":o[c]={$ne:i};break;case"gt":o[c]={$gt:isNaN(i)?i:Number(i)};break;case"lt":o[c]={$lt:isNaN(i)?i:Number(i)};break;case"gte":o[c]={$gte:isNaN(i)?i:Number(i)};break;case"lte":o[c]={$lte:isNaN(i)?i:Number(i)};break;case"contains":o[c]={$regex:i,$options:"i"};break;case"in":o[c]={$in:i.split(",").map(d=>d.trim()).filter(Boolean)};break}return Object.keys(o).length?o:null},n=[];return e.querySelectorAll(".match-condition-row").forEach(a=>{const s=l(a);s&&n.push(s)}),n.length===0?{}:t==="or"?{$or:n}:Object.assign({},...n)}function V(e,t={}){for(;e.firstChild;)e.removeChild(e.firstChild);const l=t.$sort||t||{},n=Object.entries(l),a=document.createElement("button");a.type="button",a.className="btn btn-ghost btn-sm";const s=document.createElement("span");s.setAttribute("data-icon","plus"),a.appendChild(s),a.appendChild(document.createTextNode(" Add Sort")),e.appendChild(a);const r=(i="",c=1)=>{const o=document.createElement("div");o.className="sort-row",o.style.cssText="display:flex;gap:.4rem;align-items:center;margin-bottom:.4rem;";const d=v([{value:"",label:"\u2014 field \u2014"},...g()],i);d.className+=" sort-field";const u=v([{value:"1",label:"\u2191 Ascending"},{value:"-1",label:"\u2193 Descending"}],String(c));u.className+=" sort-dir";const m=document.createElement("button");m.type="button",m.className="btn btn-sm btn-ghost",m.title="Remove";const p=document.createElement("span");p.setAttribute("data-icon","x"),m.appendChild(p),m.addEventListener("click",()=>{o.remove()}),o.appendChild(d),o.appendChild(u),o.appendChild(m),e.insertBefore(o,a),Domma.icons.scan(o)};a.addEventListener("click",()=>{r()}),n.forEach(([i,c])=>r(i,c)),n.length===0&&r(),Domma.icons.scan(e)}function z(e){e.querySelectorAll(".sort-row").forEach(t=>{const l=t.querySelector(".sort-field"),n=l?.value,a=v([{value:"",label:"\u2014 field \u2014"},...g()],n);a.className=l.className,l.parentNode.replaceChild(a,l)})}function J(e){const t={};return e.querySelectorAll(".sort-row").forEach(l=>{const n=l.querySelector(".sort-field")?.value?.trim(),a=parseInt(l.querySelector(".sort-dir")?.value,10)||1;if(!n)return;const s=n.startsWith("__")?n.slice(2):n;t[s]=a}),t}function q(e,t){const l=e.find("#pipeline-stages-list").get(0);if(!l)return;const n=l.querySelector(".stage-empty-placeholder");n&&n.remove();const a=["$match","$sort"].includes(t.type),s=document.createElement("div");s.className="card mb-2 stage-card",s.dataset.stageType=t.type,a&&(s.dataset.guided="true");const r=document.createElement("div");r.className="card-header",r.style.cssText="display:flex;align-items:center;gap:.5rem;";const i=document.createElement("code");i.textContent=t.type,i.style.cssText="flex:1;font-size:.85rem;";const c=document.createElement("button");c.type="button",c.className="btn btn-sm btn-danger";const o=document.createElement("span");o.setAttribute("data-icon","trash-2"),c.appendChild(o),c.addEventListener("click",()=>{s.remove(),l.querySelector(".stage-card")||l.appendChild(G())}),r.appendChild(i),r.appendChild(c);const d=document.createElement("div");if(d.className="card-body",a){const u=document.createElement("div");u.className="stage-guided",d.appendChild(u),t.type==="$match"&&W(u,t.config),t.type==="$sort"&&V(u,t.config)}else{const u=document.createElement("label");u.className="form-label",u.textContent="Stage Config (JSON)";const m=document.createElement("small");m.className="text-muted",m.style.cssText="display:block;margin-bottom:.4rem;",m.textContent=`Enter the inner config for ${t.type} \u2014 e.g. for $lookup: { from, localField, foreignField, as }. Do not wrap in { "${t.type}": ... }.`;const p=document.createElement("textarea");p.className="form-input stage-config",p.rows=5,p.style.cssText="font-family:monospace;font-size:.8rem;resize:vertical;",p.placeholder="{}",p.value=Object.keys(t.config||{}).length?JSON.stringify(t.config,null,2):"",d.appendChild(u),d.appendChild(m),d.appendChild(p)}s.appendChild(r),s.appendChild(d),l.appendChild(s),Domma.icons.scan(s)}function G(){const e=document.createElement("p");return e.className="text-muted stage-empty-placeholder",e.textContent="No stages yet. Add a stage to filter, join, or transform your data.",e.style.cssText="text-align:center;padding:2rem 0;",e}function K(e){const t=[];return e.find(".stage-card").each(function(){const l=this.dataset.stageType,n=this.querySelector(".stage-guided");let a;if(n)l==="$match"&&(a=U(n)),l==="$sort"&&(a=J(n));else{const s=this.querySelector(".stage-config")?.value?.trim()||"{}";try{a=JSON.parse(s)}catch{throw new Error(`Invalid JSON in ${l} stage config`)}}t.push({type:l,config:a})}),t}function A(e,t){const l=e.find("#view-columns-builder").get(0);if(!l)return;for(;l.firstChild;)l.removeChild(l.firstChild);const n=t??(f||[]).map(i=>({key:`data.${i.name}`,label:i.label||i.name})),a=(i="",c="")=>{const o=document.createElement("div");o.className="col-row",o.style.cssText="display:flex;gap:.4rem;align-items:center;margin-bottom:.4rem;";const d=f?.length?v([{value:"",label:"\u2014 field \u2014"},...g()],i):N("data.fieldName",i);d.className+=" col-key";const u=N("Display label",c);u.className+=" col-label",u.style.flex="1",f?.length&&d.tagName==="SELECT"&&d.addEventListener("change",()=>{if(!u.value){const w=(f||[]).find(L=>`data.${L.name}`===d.value);w&&(u.value=w.label||w.name)}});const m=document.createElement("button");m.type="button",m.className="btn btn-sm btn-ghost",m.title="Remove column";const p=document.createElement("span");p.setAttribute("data-icon","x"),m.appendChild(p),m.addEventListener("click",()=>{o.remove(),Domma.icons.scan(l)}),o.appendChild(d),o.appendChild(u),o.appendChild(m),l.insertBefore(o,l.lastChild),Domma.icons.scan(o)};n.forEach(i=>a(i.key,i.label));const s=document.createElement("button");s.type="button",s.className="btn btn-ghost btn-sm";const r=document.createElement("span");r.setAttribute("data-icon","plus"),s.appendChild(r),s.appendChild(document.createTextNode(" Add Column")),s.addEventListener("click",()=>{a(),Domma.icons.scan(l)}),l.appendChild(s),Domma.icons.scan(l)}function P(e){const t=[];return e.find(".col-row").each(function(){const l=this.querySelector(".col-key")?.value?.trim(),n=this.querySelector(".col-label")?.value?.trim();l&&t.push({key:l,label:n||l})}),t}function Y(e){const t=e.find("#view-display-mode").get(0);t&&(t.addEventListener("change",()=>S(e)),S(e))}function S(e){const t=e.find("#view-display-mode").val()||"table",l=e.find("#view-columns-section").get(0),n=e.find("#view-block-section").get(0);l&&(l.style.display=t==="table"?"":"none"),n&&(n.style.display=t==="block"?"":"none")}function H(e){const t=e.find("#view-rowlevel-enabled").get(0),l=e.find("#view-rowlevel-config").get(0),n=e.find("#view-rowlevel-mode").get(0),a=e.find("#view-rowlevel-field-group").get(0);t&&(t.addEventListener("change",()=>{l&&(l.style.display=t.checked?"flex":"none")}),n&&n.addEventListener("change",()=>{a&&(a.style.display=n.value==="field"?"":"none")}))}async function Q(e){const t=e.find("#view-title").val().trim();if(!t){E.toast("Title is required.",{type:"warning"});return}const l=e.find("#view-source").val();if(!l){E.toast("Source collection is required (Source tab).",{type:"warning"});return}let n;try{n=K(e)}catch(d){E.toast(d.message,{type:"error"});return}const a=P(e),s=[];e.find(".view-role-cb:checked").each(function(){s.push(this.value)});const r=e.find("#view-rowlevel-enabled").is(":checked");let i=null;if(r){const d=e.find("#view-rowlevel-mode").val()||"owner",u=e.find("#view-rowlevel-userkey").val()||"id";if(i={mode:d,userKey:u},d==="field"){const m=e.find("#view-rowlevel-field").val().trim();if(!m){E.toast("Field name is required for Field Match mode.",{type:"warning"});return}i.field=m}}const c={title:t,slug:e.find("#view-slug").val().trim()||void 0,description:e.find("#view-description").val().trim(),...e.find("#view-bundled").is(":checked")?{bundled:!0}:{},connection:e.find("#view-connection").val()||"default",pipeline:{source:l,stages:n},display:{mode:e.find("#view-display-mode").val()||"table",columns:a,pageSize:parseInt(e.find("#view-page-size").val(),10)||25,block:e.find("#view-block-name").val()||""},access:{roles:s,public:e.find("#view-public").is(":checked"),rowLevel:i}},o=e.find("#save-view-btn").get(0);o&&(o.disabled=!0);try{if(b)await h.views.update(b,c),E.toast("View updated.",{type:"success"});else{const d=await h.views.create(c);E.toast("View created.",{type:"success"}),R.navigate(`/views/edit/${d.slug}`)}}catch(d){E.toast(d.message||"Failed to save view.",{type:"error"})}finally{o&&(o.disabled=!1)}}
|
|
1
|
+
import{api as v}from"../api.js";let g=null,f=null;export const viewEditorView={templateUrl:"/admin/js/templates/view-editor.html",async onMount(e){g=null,f=null;const t=window.location.hash.match(/\/views\/edit\/([^/?#]+)/);t&&(g=t[1]),E.tabs(e.find("#view-editor-tabs").get(0)),await T(e),await D(e),await O(e),await L(e),e.find("#view-source").get(0)?.addEventListener("change",async()=>{await N(e)}),g&&(e.find("#view-editor-title").text("Edit View"),await _(e,g)),e.find("#add-stage-btn").off("click").on("click",()=>{const n=e.find("#add-stage-type").val()||"$match";$(e,{type:n,config:{}})}),e.find("#save-view-btn").off("click").on("click",async()=>{await P(e)}),H(e),G(e),Domma.icons.scan()}};async function T(e){const t=e.find("#view-source").get(0);if(t)try{(await v.collections.list()).forEach(n=>{const a=document.createElement("option");a.value=n.slug,a.textContent=`${n.title} (${n.slug})`,t.appendChild(a)})}catch{}}async function L(e){const t=e.find("#view-block-name").get(0);if(t)try{(await v.blocks.list()).forEach(n=>{const a=document.createElement("option");a.value=n.name,a.textContent=`${n.name}.html`,t.appendChild(a)})}catch{}}async function D(e){const t=e.find("#view-connection").get(0);if(t)try{const n=await v.collections.getConnections();Object.keys(n).forEach(a=>{if(!t.querySelector(`option[value="${a}"]`)){const l=document.createElement("option");l.value=a,l.textContent=a,t.appendChild(l)}})}catch{}}async function O(e){const t=e.find("#view-roles-checkboxes").get(0);t&&["admin","manager","editor","subscriber"].forEach(n=>{const a=document.createElement("label");a.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="view-role-cb",l.checked=n==="admin",a.appendChild(l),a.appendChild(document.createTextNode(n)),t.appendChild(a)})}async function N(e){const t=e.find("#view-source").val();if(!t){f=null;return}try{f=(await v.collections.get(t))?.fields||[]}catch{f=null}e.find(".stage-card[data-guided]").each(function(){const n=this.dataset.stageType,a=this.querySelector(".stage-guided");a&&(n==="$match"&&V(a),n==="$sort"&&I(a))}),q(e,null)}async function _(e,t){try{const n=await v.views.get(t);if(!n){E.toast("View not found.",{type:"error"}),R.navigate("/views");return}const a=n.pipeline?.source;a&&(e.find("#view-source").val(a),await N(e)),j(e,n)}catch(n){E.toast(n.message||"Failed to load view.",{type:"error"}),R.navigate("/views")}}function j(e,t){e.find("#view-title").val(t.title||""),e.find("#view-slug").val(t.slug||""),e.find("#view-description").val(t.description||""),e.find("#view-source").val(t.pipeline?.source||""),e.find("#view-bundled").prop("checked",!!t.bundled),e.find("#view-connection").val(t.connection||"default"),e.find("#view-display-mode").val(t.display?.mode||"table"),e.find("#view-page-size").val(t.display?.pageSize||25),e.find("#view-block-name").val(t.display?.block||"");const n=t.access?.roles||["admin"];e.find(".view-role-cb").each(function(){this.checked=n.includes(this.value)}),e.find("#view-public").prop("checked",t.access?.public||!1);const a=t.access?.rowLevel;a&&(e.find("#view-rowlevel-enabled").prop("checked",!0),e.find("#view-rowlevel-config").css("display","flex"),e.find("#view-rowlevel-mode").val(a.mode||"owner"),e.find("#view-rowlevel-userkey").val(a.userKey||"id"),a.mode==="field"&&(e.find("#view-rowlevel-field-group").css("display",""),e.find("#view-rowlevel-field").val(a.field||"")));const l=e.find("#pipeline-stages-list").get(0);if(l)for(;l.firstChild;)l.removeChild(l.firstChild);(t.pipeline?.stages||[]).forEach(i=>$(e,i)),q(e,t.display?.columns||[]),x(e)}const z=[{value:"eq",label:"= equals"},{value:"ne",label:"\u2260 not equals"},{value:"gt",label:"> greater than"},{value:"lt",label:"< less than"},{value:"gte",label:"\u2265 greater or equal"},{value:"lte",label:"\u2264 less or equal"},{value:"contains",label:"~ contains (regex)"},{value:"in",label:"\u2208 in (comma list)"}];function y(e=!1){const t=[];return e&&t.push({value:"",label:"\u2014 select field \u2014"}),(f||[]).forEach(n=>{t.push({value:`data.${n.name}`,label:`${n.label||n.name} (${n.name})`})}),t.length===0||t.length===1&&e?(t.push({value:"__meta.createdAt",label:"Created At"}),t.push({value:"__meta.updatedAt",label:"Updated At"})):(t.push({value:"__meta.createdAt",label:"Created At (meta)"}),t.push({value:"__meta.updatedAt",label:"Updated At (meta)"})),t}function h(e,t){const n=document.createElement("select");return n.className="form-input form-input--sm",e.forEach(a=>{const l=document.createElement("option");l.value=a.value,l.textContent=a.label,String(a.value)===String(t)&&(l.selected=!0),n.appendChild(l)}),n}function k(e,t,n="text"){const a=document.createElement("input");return a.type=n,a.className="form-input form-input--sm",a.placeholder=e,a.value=t??"",a}function F(e){if(!f?.length)return null;const t=e?.startsWith("data.")?e.slice(5):null;return t&&f.find(n=>n.name===t)||null}function b(e,t,n=""){const a=F(e);if(a?.type==="select"&&(t==="eq"||t==="ne")&&a.options?.length){const d=document.createElement("select");d.className="form-input form-input--sm match-val",d.style.flex="1";const c=document.createElement("option");return c.value="",c.textContent="\u2014 select value \u2014",d.appendChild(c),(a.options||[]).forEach(o=>{const s=typeof o=="string"?o:o.value??"",r=typeof o=="string"?o:o.label||s;if(!s||s==="undefined")return;const u=document.createElement("option");u.value=s,u.textContent=r,s===n&&(u.selected=!0),d.appendChild(u)}),d}const l=t==="in"?"val1, val2, val3":t==="contains"?"search pattern (regex)":t==="gt"||t==="lt"||t==="gte"||t==="lte"?"0":"value",i=document.createElement("input");return i.type="text",i.className="form-input form-input--sm match-val",i.style.flex="1",i.placeholder=l,i.value=n,i}function S(e){const t=[],n={$eq:"eq",$ne:"ne",$gt:"gt",$lt:"lt",$gte:"gte",$lte:"lte",$in:"in"};return Object.entries(e||{}).forEach(([a,l])=>{if(typeof l=="object"&&l!==null&&!Array.isArray(l)){if("$regex"in l){t.push({field:a,op:"contains",val:String(l.$regex??"")});return}Object.entries(l).forEach(([i,d])=>{const c=n[i];c&&t.push({field:a,op:c,val:Array.isArray(d)?d.join(", "):String(d)})})}else t.push({field:a,op:"eq",val:String(l??"")})}),t}function C(e,t,n="",a="eq",l=""){const i=document.createElement("div");i.className="match-condition-row",i.style.cssText="display:flex;gap:.4rem;align-items:center;margin-bottom:.4rem;flex-wrap:wrap;";const d=h([{value:"",label:"\u2014 field \u2014"},...y()],n);d.className+=" match-field",d.style.minWidth="140px";const c=h(z,a);c.className+=" match-op",c.style.minWidth="160px";const o=document.createElement("div");o.className="match-val-wrap",o.style.cssText="flex:1;min-width:120px;display:flex;",o.appendChild(b(n,a,l));const s=()=>{const m=o.querySelector(".match-val")?.value??"";for(;o.firstChild;)o.removeChild(o.firstChild);o.appendChild(b(d.value,c.value,m))};d.addEventListener("change",s),c.addEventListener("change",s);const r=document.createElement("button");r.type="button",r.className="btn btn-sm btn-ghost",r.title="Remove";const u=document.createElement("span");u.setAttribute("data-icon","x"),r.appendChild(u),r.addEventListener("click",()=>i.remove()),i.appendChild(d),i.appendChild(c),i.appendChild(o),i.appendChild(r),e.insertBefore(i,t),Domma.icons.scan(i)}function B(e,t={}){for(;e.firstChild;)e.removeChild(e.firstChild);const n=t.$match||t||{},a="$or"in n;let l=[];a?(n.$or||[]).forEach(r=>S(r).forEach(u=>l.push(u))):l=S(n);const i=document.createElement("div");i.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.6rem;";const d=document.createElement("label");d.className="form-label",d.style.marginBottom="0",d.textContent="Match:";const c=document.createElement("select");c.className="form-input form-input--sm match-logic",c.style.width="auto",[{value:"and",label:"ALL conditions (AND)"},{value:"or",label:"ANY condition (OR)"}].forEach(r=>{const u=document.createElement("option");u.value=r.value,u.textContent=r.label,(a?"or":"and")===r.value&&(u.selected=!0),c.appendChild(u)}),i.appendChild(d),i.appendChild(c),e.appendChild(i);const o=document.createElement("button");o.type="button",o.className="btn btn-ghost btn-sm match-add-btn";const s=document.createElement("span");s.setAttribute("data-icon","plus"),o.appendChild(s),o.appendChild(document.createTextNode(" Add Condition")),o.addEventListener("click",()=>{C(e,o),Domma.icons.scan(e)}),e.appendChild(o),l.forEach(r=>C(e,o,r.field,r.op,r.val)),l.length===0&&C(e,o),Domma.icons.scan(e)}function V(e){e.querySelectorAll(".match-condition-row").forEach(t=>{const n=t.querySelector(".match-field");if(n){const d=n.value,c=h([{value:"",label:"\u2014 field \u2014"},...y()],d);c.className=n.className,c.style.cssText=n.style.cssText;const o=t.querySelector(".match-val-wrap"),s=t.querySelector(".match-op");o&&s&&c.addEventListener("change",()=>{const r=o.querySelector(".match-val")?.value??"";for(;o.firstChild;)o.removeChild(o.firstChild);o.appendChild(b(c.value,s.value,r))}),n.parentNode.replaceChild(c,n)}const a=t.querySelector(".match-val-wrap"),l=t.querySelector(".match-field")?.value,i=t.querySelector(".match-op")?.value;if(a&&l&&i){const d=a.querySelector(".match-val")?.value??"";for(;a.firstChild;)a.removeChild(a.firstChild);a.appendChild(b(l,i,d))}})}function W(e){const t=e.querySelector(".match-logic")?.value||"and",n=l=>{const i=l.querySelector(".match-field")?.value?.trim(),d=l.querySelector(".match-op")?.value,c=l.querySelector(".match-val")?.value?.trim()??"";if(!i)return null;const o=i.startsWith("__")?i.slice(2):i,s={};switch(d){case"eq":s[o]=c;break;case"ne":s[o]={$ne:c};break;case"gt":s[o]={$gt:isNaN(c)?c:Number(c)};break;case"lt":s[o]={$lt:isNaN(c)?c:Number(c)};break;case"gte":s[o]={$gte:isNaN(c)?c:Number(c)};break;case"lte":s[o]={$lte:isNaN(c)?c:Number(c)};break;case"contains":s[o]={$regex:c,$options:"i"};break;case"in":s[o]={$in:c.split(",").map(r=>r.trim()).filter(Boolean)};break}return Object.keys(s).length?s:null},a=[];return e.querySelectorAll(".match-condition-row").forEach(l=>{const i=n(l);i&&a.push(i)}),a.length===0?{}:t==="or"?{$or:a}:Object.assign({},...a)}function J(e,t={}){for(;e.firstChild;)e.removeChild(e.firstChild);const n=t.$sort||t||{},a=Object.entries(n),l=document.createElement("button");l.type="button",l.className="btn btn-ghost btn-sm";const i=document.createElement("span");i.setAttribute("data-icon","plus"),l.appendChild(i),l.appendChild(document.createTextNode(" Add Sort")),e.appendChild(l);const d=(c="",o=1)=>{const s=document.createElement("div");s.className="sort-row",s.style.cssText="display:flex;gap:.4rem;align-items:center;margin-bottom:.4rem;";const r=h([{value:"",label:"\u2014 field \u2014"},...y()],c);r.className+=" sort-field";const u=h([{value:"1",label:"\u2191 Ascending"},{value:"-1",label:"\u2193 Descending"}],String(o));u.className+=" sort-dir";const m=document.createElement("button");m.type="button",m.className="btn btn-sm btn-ghost",m.title="Remove";const p=document.createElement("span");p.setAttribute("data-icon","x"),m.appendChild(p),m.addEventListener("click",()=>{s.remove()}),s.appendChild(r),s.appendChild(u),s.appendChild(m),e.insertBefore(s,l),Domma.icons.scan(s)};l.addEventListener("click",()=>{d()}),a.forEach(([c,o])=>d(c,o)),a.length===0&&d(),Domma.icons.scan(e)}function I(e){e.querySelectorAll(".sort-row").forEach(t=>{const n=t.querySelector(".sort-field"),a=n?.value,l=h([{value:"",label:"\u2014 field \u2014"},...y()],a);l.className=n.className,n.parentNode.replaceChild(l,n)})}function M(e){const t={};return e.querySelectorAll(".sort-row").forEach(n=>{const a=n.querySelector(".sort-field")?.value?.trim(),l=parseInt(n.querySelector(".sort-dir")?.value,10)||1;if(!a)return;const i=a.startsWith("__")?a.slice(2):a;t[i]=l}),t}function $(e,t){const n=e.find("#pipeline-stages-list").get(0);if(!n)return;const a=n.querySelector(".stage-empty-placeholder");a&&a.remove();const l=["$match","$sort"].includes(t.type),i=document.createElement("div");i.className="card mb-2 stage-card",i.dataset.stageType=t.type,l&&(i.dataset.guided="true");const d=document.createElement("div");d.className="card-header",d.style.cssText="display:flex;align-items:center;gap:.5rem;";const c=document.createElement("code");c.textContent=t.type,c.style.cssText="flex:1;font-size:.85rem;";const o=document.createElement("button");o.type="button",o.className="btn btn-sm btn-danger";const s=document.createElement("span");s.setAttribute("data-icon","trash-2"),o.appendChild(s),o.addEventListener("click",()=>{i.remove(),n.querySelector(".stage-card")||n.appendChild(U())}),d.appendChild(c),d.appendChild(o);const r=document.createElement("div");if(r.className="card-body",l){const u=document.createElement("div");u.className="stage-guided",r.appendChild(u),t.type==="$match"&&B(u,t.config),t.type==="$sort"&&J(u,t.config)}else{const u=document.createElement("label");u.className="form-label",u.textContent="Stage Config (JSON)";const m=document.createElement("small");m.className="text-muted",m.style.cssText="display:block;margin-bottom:.4rem;",m.textContent=`Enter the inner config for ${t.type} \u2014 e.g. for $lookup: { from, localField, foreignField, as }. Do not wrap in { "${t.type}": ... }.`;const p=document.createElement("textarea");p.className="form-input stage-config",p.rows=5,p.style.cssText="font-family:monospace;font-size:.8rem;resize:vertical;",p.placeholder="{}",p.value=Object.keys(t.config||{}).length?JSON.stringify(t.config,null,2):"",r.appendChild(u),r.appendChild(m),r.appendChild(p)}i.appendChild(d),i.appendChild(r),n.appendChild(i),Domma.icons.scan(i)}function U(){const e=document.createElement("p");return e.className="text-muted stage-empty-placeholder",e.textContent="No stages yet. Add a stage to filter, join, or transform your data.",e.style.cssText="text-align:center;padding:2rem 0;",e}function K(e){const t=[];return e.find(".stage-card").each(function(){const n=this.dataset.stageType,a=this.querySelector(".stage-guided");let l;if(a)n==="$match"&&(l=W(a)),n==="$sort"&&(l=M(a));else{const i=this.querySelector(".stage-config")?.value?.trim()||"{}";try{l=JSON.parse(i)}catch{throw new Error(`Invalid JSON in ${n} stage config`)}}t.push({type:n,config:l})}),t}function q(e,t){const n=e.find("#view-columns-builder").get(0);if(!n)return;for(;n.firstChild;)n.removeChild(n.firstChild);const a=t??(f||[]).map(c=>({key:`data.${c.name}`,label:c.label||c.name})),l=(c="",o="")=>{const s=document.createElement("div");s.className="col-row",s.style.cssText="display:flex;gap:.4rem;align-items:center;margin-bottom:.4rem;";const r=f?.length?h([{value:"",label:"\u2014 field \u2014"},...y()],c):k("data.fieldName",c);r.className+=" col-key";const u=k("Display label",o);u.className+=" col-label",u.style.flex="1",f?.length&&r.tagName==="SELECT"&&r.addEventListener("change",()=>{if(!u.value){const w=(f||[]).find(A=>`data.${A.name}`===r.value);w&&(u.value=w.label||w.name)}});const m=document.createElement("button");m.type="button",m.className="btn btn-sm btn-ghost",m.title="Remove column";const p=document.createElement("span");p.setAttribute("data-icon","x"),m.appendChild(p),m.addEventListener("click",()=>{s.remove(),Domma.icons.scan(n)}),s.appendChild(r),s.appendChild(u),s.appendChild(m),n.insertBefore(s,n.lastChild),Domma.icons.scan(s)};a.forEach(c=>l(c.key,c.label));const i=document.createElement("button");i.type="button",i.className="btn btn-ghost btn-sm";const d=document.createElement("span");d.setAttribute("data-icon","plus"),i.appendChild(d),i.appendChild(document.createTextNode(" Add Column")),i.addEventListener("click",()=>{l(),Domma.icons.scan(n)}),n.appendChild(i),Domma.icons.scan(n)}function Y(e){const t=[];return e.find(".col-row").each(function(){const n=this.querySelector(".col-key")?.value?.trim(),a=this.querySelector(".col-label")?.value?.trim();n&&t.push({key:n,label:a||n})}),t}function G(e){const t=e.find("#view-display-mode").get(0);t&&(t.addEventListener("change",()=>x(e)),x(e))}function x(e){const t=e.find("#view-display-mode").val()||"table",n=e.find("#view-columns-section").get(0),a=e.find("#view-block-section").get(0);n&&(n.style.display=t==="table"?"":"none"),a&&(a.style.display=t==="block"?"":"none")}function H(e){const t=e.find("#view-rowlevel-enabled").get(0),n=e.find("#view-rowlevel-config").get(0),a=e.find("#view-rowlevel-mode").get(0),l=e.find("#view-rowlevel-field-group").get(0);t&&(t.addEventListener("change",()=>{n&&(n.style.display=t.checked?"flex":"none")}),a&&a.addEventListener("change",()=>{l&&(l.style.display=a.value==="field"?"":"none")}))}async function P(e){const t=e.find("#view-title").val().trim();if(!t){E.toast("Title is required.",{type:"warning"});return}const n=e.find("#view-source").val();if(!n){E.toast("Source collection is required (Source tab).",{type:"warning"});return}let a;try{a=K(e)}catch(r){E.toast(r.message,{type:"error"});return}const l=Y(e),i=[];e.find(".view-role-cb:checked").each(function(){i.push(this.value)});const d=e.find("#view-rowlevel-enabled").is(":checked");let c=null;if(d){const r=e.find("#view-rowlevel-mode").val()||"owner",u=e.find("#view-rowlevel-userkey").val()||"id";if(c={mode:r,userKey:u},r==="field"){const m=e.find("#view-rowlevel-field").val().trim();if(!m){E.toast("Field name is required for Field Match mode.",{type:"warning"});return}c.field=m}}const o={title:t,slug:e.find("#view-slug").val().trim()||void 0,description:e.find("#view-description").val().trim(),...e.find("#view-bundled").is(":checked")?{bundled:!0}:{},connection:e.find("#view-connection").val()||"default",pipeline:{source:n,stages:a},display:{mode:e.find("#view-display-mode").val()||"table",columns:l,pageSize:parseInt(e.find("#view-page-size").val(),10)||25,block:e.find("#view-block-name").val()||""},access:{roles:i,public:e.find("#view-public").is(":checked"),rowLevel:c}},s=e.find("#save-view-btn").get(0);s&&(s.disabled=!0);try{if(g)await v.views.update(g,o),E.toast("View updated.",{type:"success"});else{const r=await v.views.create(o);E.toast("View created.",{type:"success"}),R.navigate(`/views/edit/${r.slug}`)}}catch(r){E.toast(r.message||"Failed to save view.",{type:"error"})}finally{s&&(s.disabled=!1)}}
|
package/config/plugins.json
CHANGED
|
@@ -2,5 +2,40 @@
|
|
|
2
2
|
"theme-switcher": {
|
|
3
3
|
"enabled": false,
|
|
4
4
|
"settings": {}
|
|
5
|
+
},
|
|
6
|
+
"contacts": {
|
|
7
|
+
"enabled": true,
|
|
8
|
+
"settings": {}
|
|
9
|
+
},
|
|
10
|
+
"garage": {
|
|
11
|
+
"enabled": true,
|
|
12
|
+
"settings": {}
|
|
13
|
+
},
|
|
14
|
+
"site-search": {
|
|
15
|
+
"enabled": true,
|
|
16
|
+
"settings": {
|
|
17
|
+
"placeholder": "Search pages...",
|
|
18
|
+
"keyboardShortcut": true,
|
|
19
|
+
"maxResults": 10,
|
|
20
|
+
"minQueryLength": 2,
|
|
21
|
+
"debounceMs": 300,
|
|
22
|
+
"displayMode": "floating",
|
|
23
|
+
"position": "top-right",
|
|
24
|
+
"offsetX": 16,
|
|
25
|
+
"offsetY": 64,
|
|
26
|
+
"iconOnly": true
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"notes": {
|
|
30
|
+
"enabled": true,
|
|
31
|
+
"settings": {}
|
|
32
|
+
},
|
|
33
|
+
"todo": {
|
|
34
|
+
"enabled": true,
|
|
35
|
+
"settings": {}
|
|
36
|
+
},
|
|
37
|
+
"docs": {
|
|
38
|
+
"enabled": true,
|
|
39
|
+
"settings": {}
|
|
5
40
|
}
|
|
6
41
|
}
|