domma-cms 0.77.0 → 0.77.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.
|
@@ -1 +1 @@
|
|
|
1
|
-
export const OP_LABELS={eq:"=",ne:"\u2260",contains:"contains",gt:">",lt:"<"},MAX_COLUMNS=5,PAGE_SIZE=500,SHOW_STEP=100;export function matchesFilter(e
|
|
1
|
+
export const OP_LABELS={eq:"=",ne:"\u2260",contains:"contains",gt:">",lt:"<"},MAX_COLUMNS=5,PAGE_SIZE=500,SHOW_STEP=100;export function matchesFilter(t,e){const r=t?.data?.[e.field],n=String(r??"");switch(e.op){case"eq":return n===String(e.value);case"ne":return n!==String(e.value);case"contains":return n.toLowerCase().includes(String(e.value).toLowerCase());case"gt":case"lt":{const i=o=>String(o).trim()!==""&&Number.isFinite(Number(o))?Number(o):NaN,s=i(n),c=i(e.value);if(!Number.isNaN(s)&&!Number.isNaN(c))return e.op==="gt"?s>c:s<c;const a=Date.parse(n),u=Date.parse(e.value);return!Number.isNaN(a)&&!Number.isNaN(u)?e.op==="gt"?a>u:a<u:e.op==="gt"?n>String(e.value):n<String(e.value)}default:return!0}}export function valueText(t){return t==null?"":typeof t=="boolean"?t?"Yes":"No":Array.isArray(t)?t.map(e=>e&&typeof e=="object"?JSON.stringify(e):String(e)).join(", "):typeof t=="object"?JSON.stringify(t):String(t)}export function cellText(t,e=60){const r=valueText(t).replace(/\s+/g," ").trim();return r.length>e?`${r.slice(0,e-1)}\u2026`:r}export function arrangeEntries(t,{filters:e=[],query:r="",sort:n={key:"created",dir:"desc"}}={}){const i=String(r||"").toLowerCase().trim();let s=(t||[]).filter(o=>e.every(p=>matchesFilter(o,p)));i&&(s=s.filter(o=>Object.values(o?.data||{}).some(p=>valueText(p).toLowerCase().includes(i))));const c=n?.dir==="asc"?1:-1,a=n?.key||"created",u=o=>a==="created"?String(o?.meta?.createdAt||""):o?.data?.[a];return[...s].sort((o,p)=>{const l=u(o),m=u(p),g=typeof l=="number"?l:parseFloat(l),b=typeof m=="number"?m:parseFloat(m);return a!=="created"&&!Number.isNaN(g)&&!Number.isNaN(b)&&String(g)===String(l??"").trim()&&String(b)===String(m??"").trim()?(g-b)*c:valueText(l).localeCompare(valueText(m),void 0,{numeric:!0,sensitivity:"base"})*c})}export function filterChoices(t,e){const r=valueText(e);if(r==="")return[];const n=r.length>28?`${r.slice(0,27)}\u2026`:r,i=[{op:"eq",value:r,label:`= \u201C${n}\u201D`},{op:"ne",value:r,label:`\u2260 \u201C${n}\u201D`}];return["number","date","time","datetime"].includes(t?.type)&&i.push({op:"gt",value:r,label:`> \u201C${n}\u201D`},{op:"lt",value:r,label:`< \u201C${n}\u201D`}),i}export function filterText(t){const e=String(t.value);return`${t.label} ${OP_LABELS[t.op]||t.op} \u201C${e.length>24?`${e.slice(0,23)}\u2026`:e}\u201D`}export function typedValue(t,e){if(t?.type==="number"&&typeof e=="string"&&e.trim()!==""){const r=Number(e);if(Number.isFinite(r))return r}return e}export function isSecretField(t){return t?.type==="password"||/pass(word)?|secret|token|api[-_]?key|private|credential/i.test(String(t?.name||""))}export function draftable(t,e=[]){const r=new Set(e.filter(isSecretField).map(n=>n.name));return Object.fromEntries(Object.entries(t||{}).filter(([n])=>!r.has(n)))}export function snapshot(t){const e=Object.keys(t||{}).sort();return JSON.stringify(e.map(r=>[r,typeof t[r]=="boolean"?t[r]:valueText(t[r])]))}export const hasForm=t=>!["roles","user-profiles"].includes(t),canClear=t=>t!=="roles";export function countText(t,e,r){const n=c=>c.toLocaleString("en-GB"),i=c=>`entr${c===1?"y":"ies"}`,s=t===e?`${n(e)} ${i(e)}`:`Showing ${n(t)} of ${n(e)}`;return r>e?`${s} \xB7 ${n(e)} of ${n(r)} loaded`:s}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import{api as g,apiRequest as lt}from"../api.js";import{addBannerAction as U}from"../lib/plugin-chrome.js";import{helpPopovers as Ne}from"../lib/help-popover.js";import{onceClosed as rt,openMenuAt as Ie,sidebarCount as z,viewLifetime as it}from"../lib/tool-kit.js";import{MAX_COLUMNS as dt,PAGE_SIZE as se,SHOW_STEP as W,arrangeEntries as ut,canClear as pt,cellText as He,countText as mt,draftable as ft,filterChoices as ht,filterText as yt,hasForm as vt,snapshot as Oe,valueText as Re}from"../lib/collection-entries-arrange.js";const Ue=/^#\/collections\/[^/?#]+\/entries\/?(\?.*)?$/,j=m=>`collection-entries.draft.${m}`,N=m=>String(m??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");function oe(m){const h=document.createElement("template");return h.innerHTML=m.trim(),h.content.firstElementChild}const
|
|
1
|
+
import{api as g,apiRequest as lt}from"../api.js";import{addBannerAction as U}from"../lib/plugin-chrome.js";import{helpPopovers as Ne}from"../lib/help-popover.js";import{onceClosed as rt,openMenuAt as Ie,sidebarCount as z,viewLifetime as it}from"../lib/tool-kit.js";import{MAX_COLUMNS as dt,PAGE_SIZE as se,SHOW_STEP as W,arrangeEntries as ut,canClear as pt,cellText as He,countText as mt,draftable as ft,filterChoices as ht,filterText as yt,hasForm as vt,snapshot as Oe,typedValue as bt,valueText as Re}from"../lib/collection-entries-arrange.js";const Ue=/^#\/collections\/[^/?#]+\/entries\/?(\?.*)?$/,j=m=>`collection-entries.draft.${m}`,N=m=>String(m??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");function oe(m){const h=document.createElement("template");return h.innerHTML=m.trim(),h.content.firstElementChild}const gt='<span class="cex-chip">{{text}}<button type="button" data-idx="{{idx}}" title="Remove this filter" aria-label="Remove filter {{text}}">\xD7</button></span>';function Et(m){return`
|
|
2
2
|
<tr class="cex-row" data-id="{{id}}" tabindex="0" data-bind-class="rowCls">
|
|
3
3
|
<td class="cex-check"><input type="checkbox" class="cex-sel" aria-label="Select this entry" data-bind-checked="sel"></td>
|
|
4
4
|
${Array.from({length:m},(v,a)=>`<td data-col="${a}" title="{{t${a}}}">{{c${a}}}</td>`).join("")}
|
|
5
5
|
<td class="cex-when" title="{{whenFull}}">{{when}}</td>
|
|
6
6
|
<td class="cex-tools"><button type="button" class="cex-run" data-bind-hidden="!runs" title="Run one of this collection's actions">\u26A1 {{runs}}</button><button type="button" class="cex-kebab" aria-label="Actions for this entry" title="Actions">\u22EE</button></td>
|
|
7
|
-
</tr>`}function
|
|
7
|
+
</tr>`}function kt(m,h){const v={};return m.forEach(a=>{const u={};a.fullWidth?u.span=h:a.span>1&&(u.span=a.span),a.placeholder&&(u.placeholder=a.placeholder),a.helper&&(u.hint=a.helper);const i=a.type==="checkbox"?"boolean":a.type==="date"?"string":a.type;v[a.name]={type:i,label:a.label,required:a.required,options:a.options,...Object.keys(u).length?{formConfig:u}:{}}}),v}function ce(m,h){const v={};for(const a of h){const u=[...m.querySelectorAll(`[name="${CSS.escape(a.name)}"]`)];if(!u.length)continue;const i=u[0];i.type==="radio"?v[a.name]=u.find(p=>p.checked)?.value??"":i.type==="checkbox"&&u.length>1?v[a.name]=u.filter(p=>p.checked).map(p=>p.value):i.type==="checkbox"?v[a.name]=i.checked:i.tagName==="SELECT"&&i.multiple?v[a.name]=[...i.selectedOptions].map(p=>p.value):v[a.name]=bt(a,i.value)}return v}function ze(m,h,v){for(const a of h){if(!(a.name in v))continue;const u=v[a.name],i=[...m.querySelectorAll(`[name="${CSS.escape(a.name)}"]`)];if(!i.length)continue;const p=i[0];p.type==="radio"?i.forEach(c=>{c.checked=c.value===String(u)}):p.type==="checkbox"&&i.length>1?i.forEach(c=>{c.checked=Array.isArray(u)&&u.includes(c.value)}):p.type==="checkbox"?p.checked=u===!0:p.tagName==="SELECT"&&p.multiple?[...p.options].forEach(c=>{c.selected=Array.isArray(u)&&u.includes(c.value)}):p.value=u??"",i.forEach(c=>c.dispatchEvent(new Event("input",{bubbles:!0})))}}const je=it(Ue);export const collectionEntriesView={templateUrl:"/admin/js/templates/collection-entries.html",async onMount(m){je.reset();const h=m.get(0).querySelector(".cex")||m.get(0),v=location.hash.match(/^#\/collections\/([^/?#]+)\/entries/),a=v?decodeURIComponent(v[1]):null,u=()=>Ue.test(location.hash)&&h.isConnected&&location.hash.includes(`/collections/${encodeURIComponent(a)}/entries`),i=e=>(je.add(e),e);if(!a){E.toast("No collection selected.",{type:"error"});return}const p=M.observable(null),c=M.observable([]),x=M.observable(0),X=M.observable(!0),B=M.observable(""),Fe=M.computed(()=>p.value!==null),le=M.observable(""),w=M.observable([]),Z=M.observable({key:"created",dir:"desc"}),y=M.observable([]),q=M.observable(W),P=M.observable([]),Q=M.observable({}),A=M.observable(!1);let ee=1;const re=()=>p.peek()?.fields||[],ie=M.computed(()=>(p.value?.fields||[]).slice(0,dt)),_=e=>c.peek().find(t=>t.id===e),he=e=>P.peek().filter(t=>{const n=Q.peek()[t.slug];return n==null||n.has(e)}),T=M.observable([]),ye=M.observable([]);i(M.effect(()=>{const e=ut(c.value,{filters:w.value,query:le.value,sort:Z.value});T.value=e})),i(M.effect(()=>{const e=ie.value,t=new Set(y.value);Q.value,P.value,ye.value=T.value.slice(0,q.value).map(n=>{const s={id:n.id,sel:t.has(n.id),rowCls:t.has(n.id)?"is-selected":""};e.forEach((r,d)=>{s[`c${d}`]=He(n.data?.[r.name]),s[`t${d}`]=Re(n.data?.[r.name])});const o=n.meta?.createdAt;s.when=o?D(o).format("D MMM YYYY"):"",s.whenFull=o?`Created ${D(o).format("D MMM YYYY HH:mm")}${n.meta?.updatedAt?` \xB7 changed ${D(n.meta.updatedAt).format("D MMM YYYY HH:mm")}`:""}`:"";const l=he(n.id).length;return s.runs=l?String(l):"",s})}));const Be=M.computed(()=>w.value.map((e,t)=>({key:`${t}:${e.field}:${e.op}:${e.value}`,idx:t,text:yt(e)}))),Pe=M.computed(()=>y.value.length),_e={schema:p,loaded:c,loading:X,loadError:B,ready:Fe,query:le,rows:ye,chips:Be,busy:A,selectedCount:Pe,countLine:M.computed(()=>mt(T.value.length,c.value.length,x.value)),selectedText:M.computed(()=>`${y.value.length} selected`),canClear:M.computed(()=>pt(a)),introText:M.computed(()=>{const e=p.value;return e?`${e.description?`${e.description} - `:""}${(e.fields||[]).length} field${(e.fields||[]).length===1?"":"s"}${e.storage?.adapter==="mongodb"?", on MongoDB":""}.`:""}),emptyText:M.computed(()=>c.value.length?"No entries match the search or filters.":"No entries yet - Add entry in the banner makes one, or Import brings many."),moreText:M.computed(()=>{const e=T.value.length-q.value;if(e>0)return`Show ${Math.min(W,e)} more (${e} not shown)`;const t=x.value-c.value.length;return t>0?`Load the next ${Math.min(se,t)} from the server`:""})};async function J({reset:e=!0}={}){X.value=!0;try{const t=e?Math.max(se,c.peek().length):se;e&&(ee=1);const n=await g.collections.listEntries(a,{limit:t,page:ee});if(!u())return;const s=n?.entries||(Array.isArray(n)?n:[]);c.value=e?s:[...c.peek(),...s.filter(l=>!_(l.id))],x.value=Number(n?.total)||c.peek().length,e&&(ee=Math.max(1,Math.ceil(c.peek().length/se)));const o=new Set(c.peek().map(l=>l.id));y.value=y.peek().filter(l=>o.has(l)),B.value="",await Ve()}catch(t){B.value=`Could not load the entries: ${t.message||t}`}finally{X.value=!1}}async function Je(){ee+=1,await J({reset:!1})}async function Ke(){try{if((await lt("/actions/_count",{method:"GET"}).catch(()=>null))?.available===!1)return;const t=await g.actions.forCollection(a);P.value=Array.isArray(t)?t:[]}catch{P.value=[]}}async function Ve(){const e=P.peek().filter(s=>s.access?.rowLevel);if(!e.length||!c.peek().length)return;const t=c.peek().map(s=>s.id),n={...Q.peek()};await Promise.all(e.map(async s=>{try{n[s.slug]=new Set((await g.actions.checkAccess(s.slug,t)).allowed)}catch{n[s.slug]=null}})),Q.value=n}const K=e=>{const t=re()[0];return(t?He(e?.data?.[t.name],40):"")||`Entry ${String(e?.id||"").slice(0,8)}`};async function ve(e){if(await E.confirm(`Delete \u201C${N(K(e))}\u201D? This cannot be undone.`))try{await g.collections.deleteEntry(a,e.id),c.value=c.peek().filter(t=>t.id!==e.id),x.value=Math.max(0,x.peek()-1),y.value=y.peek().filter(t=>t!==e.id),z("#/collections"),E.toast("Entry deleted.",{type:"success"})}catch(t){E.toast(`Not deleted: ${t.message||t}`,{type:"error"})}}async function Ge(){const e=[...y.peek()];if(!e.length||!await E.confirm(`Delete ${e.length} entr${e.length===1?"y":"ies"}? This cannot be undone.`))return;A.value=!0;let t=0;const n=[];for(const s of e)try{await g.collections.deleteEntry(a,s),t++}catch(o){n.push(o.message||String(o))}A.value=!1,y.value=[],z("#/collections"),E.toast(n.length?`Deleted ${t}; ${n.length} refused (${n[0]}).`:`Deleted ${t} entr${t===1?"y":"ies"}.`,{type:n.length?"warning":"success",duration:n.length?7e3:4e3}),u()&&await J()}async function We(){const e=p.peek();if(await E.confirm(`Delete ALL ${x.peek()} entries in \u201C${N(e?.title||a)}\u201D? This cannot be undone.`)){A.value=!0;try{await g.collections.clearEntries(a),c.value=[],x.value=0,y.value=[],z("#/collections"),E.toast("All entries deleted.",{type:"success"})}catch(t){E.toast(`Not cleared: ${t.message||t}`,{type:"error"})}finally{A.value=!1}}}async function Xe(e){try{const t=await g.collections.createEntry(a,{...e.data||{}});c.value=[t,...c.peek()],x.value=x.peek()+1,z("#/collections"),E.toast("Duplicated - the copy is at the top.",{type:"success"})}catch(t){E.toast(`Not duplicated: ${t.message||t}`,{type:"error"})}}async function Ze(e,t){const n=e.trigger?.confirmMessage;if(!(n&&!await E.confirm(N(n)))){A.value=!0;try{const s=await g.actions.execute(e.slug,t.id);if(s?.success){const o=s.stepsCompleted??0;E.toast(`${e.trigger?.label||e.title||"Action"} done (${o} step${o===1?"":"s"}).`,{type:"success"}),u()&&await J()}else E.toast(s?.results?.find(o=>!o.success)?.error||"The action failed.",{type:"error"})}catch(s){E.toast(s.message||"The action failed.",{type:"error"})}finally{A.value=!1}}}async function be(e){try{await navigator.clipboard.writeText(e),E.toast("Copied.",{type:"success",duration:1500})}catch{E.toast("Copy failed - your browser refused the clipboard.",{type:"error"})}}function de(e){const t=y.peek();y.value=t.includes(e)?t.filter(n=>n!==e):[...t,e]}function ge(e,t,n){w.value=[...w.peek(),{field:e.name,label:e.label||e.name,op:t,value:n}],q.value=W}const ue=new Set;let Y=null,Ee=!1;function ke(e,t,n){const s=e.close.bind(e);let o=!1,l=!1;e.close=async r=>{if(!o&&r!==!0&&!Ee&&t()){if(l)return;l=!0;const d=await E.confirm(n);if(l=!1,!d)return}return o=!0,s()}}function te(e,t=()=>{}){ue.add(e),rt(e,()=>{ue.delete(e),t()})}function H(e,t=null){const n=p.peek(),s=re(),o=!!e,l=E.slideover({title:o?`Edit ${N(K(e))}`:"New entry",size:"md",position:"right"}),r=oe(`
|
|
8
8
|
<div class="cex-form">
|
|
9
9
|
<div class="cex-fields"></div>
|
|
10
10
|
<small class="cex-hint cex-when-line"></small>
|
|
@@ -15,7 +15,7 @@ import{api as g,apiRequest as lt}from"../api.js";import{addBannerAction as U}fro
|
|
|
15
15
|
<button type="button" class="btn btn-sm btn-ghost" data-act="cancel">Cancel</button>
|
|
16
16
|
<button type="button" class="btn btn-sm btn-primary" data-act="save"><span data-icon="check" data-icon-size="13"></span> <span class="cex-save-text"></span></button>
|
|
17
17
|
</div>
|
|
18
|
-
</div>`);r.querySelector(".cex-save-text").textContent=o?"Save":"Add entry";const d=r.querySelector(".cex-fields");F.create(
|
|
18
|
+
</div>`);r.querySelector(".cex-save-text").textContent=o?"Save":"Add entry";const d=r.querySelector(".cex-fields");F.create(kt(s,n?.columns||2),e?.data||{},{layout:n?.layout||"stacked",columns:n?.columns||2,showSubmitButton:!1}).renderTo(d),s.forEach(k=>{if(k.type!=="date"||!k.name)return;const O=d.querySelector(`[name="${CSS.escape(k.name)}"]`);O&&O.type!=="date"&&(O.type="date")}),e?.data&&ze(d,s,e.data),o&&e.meta?.createdAt&&(r.querySelector(".cex-when-line").textContent=`Created ${D(e.meta.createdAt).format("D MMM YYYY HH:mm")}${e.meta.updatedAt?`, last changed ${D(e.meta.updatedAt).format("D MMM YYYY HH:mm")}`:""}${e.meta.source?` \xB7 via ${e.meta.source}`:""}.`),l.element.appendChild(r);const f=Oe(ce(d,s));t&&ze(d,s,t);const b=()=>Oe(ce(d,s))!==f,ct=r.querySelector(".cex-dirty"),me=()=>{ct.hidden=!b()};d.addEventListener("input",me),d.addEventListener("change",me),me();let fe=!1;async function De(){if(fe)return;document.activeElement?.dispatchEvent?.(new Event("change",{bubbles:!0}));const k=ce(d,s);fe=!0;const O=r.querySelector('[data-act="save"]');O.disabled=!0;try{if(o){const G=await g.collections.updateEntry(a,e.id,k);c.value=c.peek().map(Ye=>Ye.id===e.id?G:Ye),E.toast("Entry saved.",{type:"success"})}else{const G=await g.collections.createEntry(a,k);c.value=[G,...c.peek()],x.value=x.peek()+1,z("#/collections"),E.toast("Entry added.",{type:"success"})}try{S.remove?S.remove(j(a)):S.set(j(a),null)}catch{}l.close(!0)}catch(G){E.toast(G.message||"Not saved.",{type:"error"})}finally{fe=!1,O.disabled=!1}}r.querySelector('[data-act="save"]').addEventListener("click",De),r.querySelector('[data-act="cancel"]').addEventListener("click",()=>l.close()),r.addEventListener("keydown",k=>{(k.ctrlKey||k.metaKey)&&(k.key==="Enter"||k.key==="s")&&(k.preventDefault(),De())});const Te={id:e?.id??null,dirty:b,values:()=>ft(ce(d,s),s)};Y=Te,te(l,()=>{Y===Te&&(Y=null)}),ke(l,b,"Discard the changes you have not saved?"),I.scan(r),l.open(),d.querySelector("input, select, textarea")?.focus()}function Qe(e){const t=E.slideover({title:N(K(e)),size:"md",position:"right"}),n=oe(`
|
|
19
19
|
<div class="cex-form">
|
|
20
20
|
<dl class="cex-detail"></dl>
|
|
21
21
|
<div class="cex-meta"></div>
|
|
@@ -47,4 +47,4 @@ import{api as g,apiRequest as lt}from"../api.js";import{addBannerAction as U}fro
|
|
|
47
47
|
<button type="button" class="btn btn-ghost" data-fmt="json"><span data-icon="download" data-icon-size="14"></span> Export as JSON</button>
|
|
48
48
|
<button type="button" class="btn btn-ghost" data-fmt="csv"><span data-icon="download" data-icon-size="14"></span> Export as CSV</button>
|
|
49
49
|
</div>
|
|
50
|
-
</div>`);t.querySelectorAll("[data-fmt]").forEach(n=>n.addEventListener("click",async()=>{const s=n.dataset.fmt;try{const o=await fetch(`/api/collections/${encodeURIComponent(a)}/export?format=${s}`,{headers:{Authorization:`Bearer ${S.get("auth_token")||""}`}});if(!o.ok)throw new Error(`${o.status}`);const l=await o.blob(),r=document.createElement("a");r.href=URL.createObjectURL(l),r.download=`${a}-entries.${s}`,document.body.appendChild(r),r.click(),r.remove(),setTimeout(()=>URL.revokeObjectURL(r.href),1e3),e.close(!0)}catch(o){E.toast(`Export failed (${o.message}).`,{type:"error"})}})),e.element.appendChild(t),te(e),I.scan(t),e.open()}function nt(e,t){const n=_(e.dataset.id);if(!n)return[];const s=t?.event?.target?.closest?.("td[data-col]"),o=s?ie.peek()[Number(s.dataset.col)]:null,l=he(n.id),r=y.peek().includes(n.id),d=o?ht(o,n.data?.[o.name]):[];return[{header:K(n)},{label:"Edit\u2026",icon:"edit",action:()=>H(n)},{label:"See all of it\u2026",icon:"eye",action:()=>Qe(n)},{label:"Duplicate",icon:"copy",action:()=>Xe(n)},{label:"Copy id",icon:"hash",action:()=>be(n.id)},{label:r?"Deselect":"Select",icon:"check",action:()=>de(n.id)},...l.length?[{type:"divider"},...l.map(f=>({label:`${f.trigger?.label||f.title||f.slug}\u2026`,icon:f.trigger?.icon||"zap",disabled:A.peek(),action:()=>Ze(f,n)}))]:[],...o?[{type:"divider"},{label:`Filter by ${o.label||o.name}`,icon:"filter",submenu:[...d.map(f=>({label:f.label,action:()=>ge(o,f.op,f.value)})),{label:`${o.label||o.name} contains\u2026`,action:async()=>{const f=await E.prompt(`Show entries whose ${N(o.label||o.name)} contains`,{inputPlaceholder:"text"});f&&String(f).trim()&&ge(o,"contains",String(f).trim())}}]}]:[],...w.peek().length?[{label:`Clear filters (${w.peek().length})`,icon:"x",action:()=>{w.value=[]}}]:[],{type:"divider"},{label:"Delete\u2026",icon:"trash",danger:!0,action:()=>ve(n)}]}const L=h.querySelector(".cex-rows"),V=h.querySelector(".cex-head"),pe=h.querySelector(".cex-chips-list");pe.innerHTML=
|
|
50
|
+
</div>`);t.querySelectorAll("[data-fmt]").forEach(n=>n.addEventListener("click",async()=>{const s=n.dataset.fmt;try{const o=await fetch(`/api/collections/${encodeURIComponent(a)}/export?format=${s}`,{headers:{Authorization:`Bearer ${S.get("auth_token")||""}`}});if(!o.ok)throw new Error(`${o.status}`);const l=await o.blob(),r=document.createElement("a");r.href=URL.createObjectURL(l),r.download=`${a}-entries.${s}`,document.body.appendChild(r),r.click(),r.remove(),setTimeout(()=>URL.revokeObjectURL(r.href),1e3),e.close(!0)}catch(o){E.toast(`Export failed (${o.message}).`,{type:"error"})}})),e.element.appendChild(t),te(e),I.scan(t),e.open()}function nt(e,t){const n=_(e.dataset.id);if(!n)return[];const s=t?.event?.target?.closest?.("td[data-col]"),o=s?ie.peek()[Number(s.dataset.col)]:null,l=he(n.id),r=y.peek().includes(n.id),d=o?ht(o,n.data?.[o.name]):[];return[{header:K(n)},{label:"Edit\u2026",icon:"edit",action:()=>H(n)},{label:"See all of it\u2026",icon:"eye",action:()=>Qe(n)},{label:"Duplicate",icon:"copy",action:()=>Xe(n)},{label:"Copy id",icon:"hash",action:()=>be(n.id)},{label:r?"Deselect":"Select",icon:"check",action:()=>de(n.id)},...l.length?[{type:"divider"},...l.map(f=>({label:`${f.trigger?.label||f.title||f.slug}\u2026`,icon:f.trigger?.icon||"zap",disabled:A.peek(),action:()=>Ze(f,n)}))]:[],...o?[{type:"divider"},{label:`Filter by ${o.label||o.name}`,icon:"filter",submenu:[...d.map(f=>({label:f.label,action:()=>ge(o,f.op,f.value)})),{label:`${o.label||o.name} contains\u2026`,action:async()=>{const f=await E.prompt(`Show entries whose ${N(o.label||o.name)} contains`,{inputPlaceholder:"text"});f&&String(f).trim()&&ge(o,"contains",String(f).trim())}}]}]:[],...w.peek().length?[{label:`Clear filters (${w.peek().length})`,icon:"x",action:()=>{w.value=[]}}]:[],{type:"divider"},{label:"Delete\u2026",icon:"trash",danger:!0,action:()=>ve(n)}]}const L=h.querySelector(".cex-rows"),V=h.querySelector(".cex-head"),pe=h.querySelector(".cex-chips-list");pe.innerHTML=gt;const at=E.loader(h,{type:"dots"});try{p.value=await g.collections.get(a)}catch(e){B.value=`Could not load the collection: ${e.message||e}`}finally{at.destroy()}if(!u())return;if(!p.peek()&&!B.peek()){E.toast("Collection not found.",{type:"error"}),R.navigate("/collections");return}const xe=ie.peek();L.innerHTML=Et(xe.length),V.replaceChildren();const ne=(e,t)=>{const n=document.createElement("th");return e&&(n.className=e),t&&n.append(t),V.appendChild(n),n},$=document.createElement("input");$.type="checkbox",$.title="Select every entry shown",$.setAttribute("aria-label","Select every entry shown"),ne("cex-check",$);const we=(e,t)=>{const n=document.createElement("button");return n.type="button",n.dataset.sort=e,n.append(document.createTextNode(t),Object.assign(document.createElement("span"),{className:"cex-sort"})),n};xe.forEach(e=>ne("",we(e.name,e.label||e.name))),ne("cex-when",we("created","Created")),ne("cex-tools"),i(M.effect(()=>{const e=Z.value;V.querySelectorAll("[data-sort]").forEach(t=>{t.querySelector(".cex-sort").textContent=t.dataset.sort===e.key?e.dir==="asc"?"\u25B2":"\u25BC":"",t.parentElement.setAttribute("aria-sort",t.dataset.sort===e.key?e.dir==="asc"?"ascending":"descending":"none")})})),i(M.effect(()=>{const e=T.value.map(n=>n.id),t=new Set(y.value);$.checked=e.length>0&&e.every(n=>t.has(n)),$.indeterminate=!$.checked&&e.some(n=>t.has(n))}));const Se=e=>{const t=e.target.closest("[data-sort]");if(!t)return;const n=Z.peek();Z.value={key:t.dataset.sort,dir:n.key===t.dataset.sort&&n.dir==="asc"?"desc":"asc"}};V.addEventListener("click",Se),$.addEventListener("change",()=>{const e=T.peek().map(t=>t.id);y.value=$.checked?[...new Set([...y.peek(),...e])]:y.peek().filter(t=>!e.includes(t))}),i(()=>V.removeEventListener("click",Se));const st=M.applyBindings(_e,h,{methods:{clearSelection:()=>{y.value=[]},deleteSelected:()=>Ge(),clearAll:()=>We(),clearFilters:()=>{w.value=[]},showMore:()=>{T.peek().length>q.peek()?q.value=q.peek()+W:Je()}}});i(()=>st.dispose()),i(Ne(h,{position:"bottom"})),i(M.effect(()=>{le.value,w.value,q.value=W}));const Me=e=>{const t=e.target.closest("button[data-idx]");if(!t)return;const n=Number(t.dataset.idx);w.value=w.peek().filter((s,o)=>o!==n)};pe.addEventListener("click",Me),i(()=>pe.removeEventListener("click",Me));const ot=E.contextMenu(L,{cursor:"glyph",match:".cex-row",inherit:!1,density:"compact",items:(e,t)=>nt(e,t)});i(()=>ot?.destroy?.());const Ce=e=>{const t=e.target.closest(".cex-row");if(!t)return;if(e.target.closest(".cex-kebab, .cex-run")){Ie(e.target.closest(".cex-kebab, .cex-run"));return}if(e.target.closest(".cex-check"))return;const n=_(t.dataset.id);n&&H(n)},$e=e=>{if(!e.target.matches(".cex-sel"))return;const t=e.target.closest(".cex-row");t&&de(t.dataset.id)},Ae=e=>{const t=e.target.closest(".cex-row");if(!t||e.target!==t)return;const n=_(t.dataset.id);n&&(e.key==="F10"&&e.shiftKey?(e.preventDefault(),Ie(t)):e.key==="Enter"?(e.preventDefault(),H(n)):e.key===" "?(e.preventDefault(),de(n.id)):e.key==="Delete"&&(e.preventDefault(),ve(n)))};L.addEventListener("click",Ce),L.addEventListener("change",$e),L.addEventListener("keydown",Ae),i(()=>{L.removeEventListener("click",Ce),L.removeEventListener("change",$e),L.removeEventListener("keydown",Ae)});const Le=p.peek(),ae=m.get(0).querySelector(".dm-plugin-banner-name")?.querySelector(".dm-plugin-banner-sep");if(ae&&Le?.title){for(;ae.nextSibling;)ae.nextSibling.remove();ae.after(document.createTextNode(Le.title))}U(m,{id:"entries-back",icon:"arrow-left",title:"All collections",onClick:()=>{location.hash="#/collections"}}),U(m,{id:"entries-fields",icon:"edit-3",title:"Edit its fields and settings",onClick:()=>{location.hash=`#/collections/edit/${encodeURIComponent(a)}`}}),vt(a)&&U(m,{id:"entries-form",icon:"layout",title:"Open its form",onClick:()=>{location.hash=`#/forms/edit/${encodeURIComponent(a)}`}}),U(m,{id:"entries-export",icon:"download",title:"Export",onClick:()=>tt()}),U(m,{id:"entries-import",icon:"upload",title:"Import",onClick:()=>et()}),U(m,{id:"entries-add",icon:"plus",title:"Add entry",onClick:()=>H(null)});const qe=e=>{Y?.dirty()&&(e.preventDefault(),e.returnValue="")};if(window.addEventListener("beforeunload",qe),i(()=>window.removeEventListener("beforeunload",qe)),i(()=>{if(Y?.dirty())try{S.set(j(a),{id:Y.id,values:Y.values(),at:new Date().toISOString()})}catch{}Ee=!0,[...ue].forEach(e=>{try{e.close(!0)}catch{}})}),I.scan(h),!p.peek()){X.value=!1;return}if(await Ke(),!u()||(await J(),!u()))return;let C=null;try{C=S.get(j(a))}catch{}if(C?.values){try{S.remove?S.remove(j(a)):S.set(j(a),null)}catch{}let e=C.id?_(C.id):null;if(C.id&&!e&&(e=await g.collections.getEntry(a,C.id).catch(()=>null)),!u()||C.id&&!e)return;const t=e?`your changes to \u201C${N(K(e))}\u201D`:"a new entry",n=e?.meta?.updatedAt&&C.at&&e.meta.updatedAt>C.at?" It has been changed since - check before saving.":"";await E.confirm(`You left ${t} unsaved.${n} Pick up where you left off?`)&&u()&&H(e,C.values)}}};
|