domma-cms 0.36.19 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/admin/js/lib/shortcode-context-menu.js +4 -4
- package/admin/js/templates/collection-entries.html +1 -0
- package/admin/js/views/collection-entries.js +1 -1
- package/admin/js/views/form-editor.js +6 -6
- package/admin/js/views/settings.js +1 -1
- package/admin/js/views/user-editor.js +1 -1
- package/admin/js/views/users.js +2 -2
- package/package.json +1 -1
- package/public/js/collection-context.js +2 -0
- package/public/js/forms.js +1 -1
- package/public/js/site.js +1 -1
- package/server/routes/api/settings.js +10 -8
- package/server/routes/api/users.js +12 -5
- package/server/services/markdown.js +302 -40
- package/server/templates/page.html +3 -2
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import{SHORTCODES as T}from"./shortcode-registry.js";import{EFFECTS as g}from"./effect-defs.js";import{findEnclosingShortcode as j,setAttr as S,setAttrs as D,effectAttrKeys as W}from"./shortcode-tags.js";import{insertAtCursor as _,replaceRange as Y}from"./markdown-toolbar.js";import{api as h}from"../api.js";const k=new Map,F={forms:async()=>(await h.forms.list()).map(e=>[e.slug,e.title||e.slug]),collections:async()=>(await h.collections.list()).map(e=>[e.slug,e.title||e.slug]),views:async()=>(await h.views.list()).map(e=>[e.slug,e.title||e.slug]),blocks:async()=>(await h.blocks.list()).map(e=>{const t=e?.name??e;return[t,t]}),actions:async()=>(await h.actions.list()).map(e=>[e.slug,e.name||e.title||e.slug]),menus:async()=>(await h.menus.list()).map(e=>[e.slug,e.name||e.title||e.slug]),"menu-locations":async()=>Object.keys(await h.menuLocations.get()||{}).map(e=>[e,e]),"collection-fields":async()=>{const e=i?.span?.attrs?.slug;return!e||e===!0?[]:((await h.collections.get(e)).fields||[]).map(n=>[n.name,n.label||n.name])}};function R(e){const t=e==="collection-fields"?`${e}:${i?.span?.attrs?.slug}`:e;return k.has(t)||k.set(t,F[e]().catch(()=>[])),k.get(t)}const B=T.map(e=>({name:e.name,selfClosing:!!e.selfClosing})),U=new Map(T.map(e=>[e.name,e])),K=["Layout","Content","Components","Data","Media","Effects"],V=new Map(g.map(e=>[e.name,e])),G=g.map(e=>({name:e.name,selfClosing:!!e.selfClosing})),X=[...B,...G],A=new Set(["card","hero","banner","col","button"]);let i=null;const M="position:fixed;z-index:10000;min-width:210px;max-height:70vh;overflow-y:auto;background:var(--dm-surface,#1c1c30);border:1px solid var(--dm-border,#2a2a40);border-radius:6px;box-shadow:var(--dm-shadow-lg);padding:.3rem;font-size:.83rem;color:var(--dm-text,#e5e5e5);",u="display:flex;align-items:center;gap:.5rem;width:100%;text-align:left;padding:.35rem .55rem;border:none;background:transparent;color:inherit;border-radius:4px;cursor:pointer;font-size:.83rem;",L="padding:.3rem .55rem;color:var(--dm-text-muted,#8a8a9a);font-size:.68rem;text-transform:uppercase;letter-spacing:.06em;",q="flex:1;min-width:0;padding:.2rem .4rem;background:var(--dm-input-bg,#12121e);border:1px solid var(--dm-border,#333);border-radius:3px;color:var(--dm-text,#eee);font-size:.8rem;",J="height:1px;background:var(--dm-border,#2a2a40);margin:.3rem .2rem;";function m(e){return e.addEventListener("mouseenter",()=>{e.style.background="var(--dm-primary,#4338ca)",e.style.color="var(--dm-primary-text,#fff)"}),e.addEventListener("mouseleave",()=>{e.style.background="transparent",e.style.color="inherit"}),e}export function attachContextMenu(e){e.addEventListener("contextmenu",t=>{t.preventDefault(),p();const n=j(e,X);Q(e,n,t.clientX,t.clientY)})}function p(){i&&($(),i.root.remove(),document.removeEventListener("mousedown",N,!0),document.removeEventListener("keydown",O,!0),window.removeEventListener("scroll",p,!0),i=null)}const N=e=>{i&&!i.root.contains(e.target)&&!(i.submenu&&i.submenu.contains(e.target))&&p()},O=e=>{e.key==="Escape"&&p()};function Q(e,t,n,o){k.clear();const s=document.createElement("div");s.className="dm-sc-menu",s.style.cssText=M,i={root:s,textarea:e,span:t,submenu:null};const a=t&&(U.get(t.name)||V.get(t.name)),l=t&&A.has(t.name)?g.filter(r=>r.name in t.attrs):[];a&&ee(s,a);for(const r of l)ce(s,r);(a||l.length)&&s.appendChild(Z()),s.appendChild(C("Add effect\u2026",()=>re())),s.appendChild(C("Insert\u2026",()=>pe()));const c=e.selectionStart!==e.selectionEnd;(t||c)&&s.appendChild(C(c?"Wrap selection in\u2026":"Wrap in\u2026",()=>me())),document.body.appendChild(s),z(s,n,o),document.addEventListener("mousedown",N,!0),document.addEventListener("keydown",O,!0),window.addEventListener("scroll",p,!0),window.I&&I.scan(s)}function Z(){const e=document.createElement("div");return e.style.cssText=J,e}function x(e){const t=document.createElement("div");return t.style.cssText=L,t.textContent=e,t}function ee(e,t){e.appendChild(x(`${t.label} options`));for(const n of t.attrs||[])e.appendChild(P(n))}function P(e,t=e.name){const n=i.span.attrs[t];switch(e.type){case"checkbox":return oe(e,t,te(n,e.default));case"select":return ie(e,t,n);case"lookup":return le(e,t,n);case"colour":return ae(e,t,n);default:return se(e,t,n)}}function v(e,t){i.span=S(i.textarea,i.span,e,t)}function te(e,t){return e==null?!!t:!(e===!1||e==="false"||e==="0")}function ne(e,t){return t===!!e.default?null:t?"true":"false"}function oe(e,t,n){const o=m(document.createElement("label"));o.style.cssText=u;const s=document.createElement("input");return s.type="checkbox",s.checked=n,s.addEventListener("change",()=>v(t,ne(e,s.checked))),o.appendChild(s),o.appendChild(document.createTextNode(e.label)),o}function se(e,t,n){const o=document.createElement("div");o.style.cssText=u+"cursor:default;";const s=document.createElement("span");s.textContent=e.label,s.style.cssText="white-space:nowrap;color:var(--dm-text-muted,#9a9aac);";const a=document.createElement("input");a.type=e.type==="number"?"number":"text",a.value=n===!0||n==null?"":n,e.default!=null&&e.default!==""&&(a.placeholder=String(e.default)),a.style.cssText=q;let l=null;return a.addEventListener("input",()=>{clearTimeout(l),l=setTimeout(()=>v(t,a.value.trim()),180)}),a.addEventListener("mousedown",c=>c.stopPropagation()),o.appendChild(s),o.appendChild(a),o}function ae(e,t,n){const o=document.createElement("div");o.style.cssText=u+"cursor:default;";const s=document.createElement("span");s.textContent=e.label,s.style.flex="1";const a=document.createElement("input");return a.type="color",typeof n=="string"&&/^#/.test(n)?a.value=n:typeof e.default=="string"&&/^#/.test(e.default)&&(a.value=e.default),a.addEventListener("input",()=>v(t,a.value)),o.appendChild(s),o.appendChild(a),o}function ie(e,t,n){const o=n==null||n===!0?e.default??"":n;return C(`${e.label}${o!==""?`: ${o}`:""}`,()=>{const s=w();for(const[a,l]of e.options||[]){const c=m(document.createElement("button"));c.type="button",c.style.cssText=u,c.textContent=l,a===o&&(c.style.fontWeight="700"),c.addEventListener("click",()=>{v(t,a),$()}),s.appendChild(c)}return s})}function le(e,t,n){const o=n==null||n===!0?e.default??"":n;return C(`${e.label}${o!==""?`: ${o}`:""}`,()=>{const s=w();s.style.minHeight="48px";const a=typeof E<"u"&&E.loader?E.loader(s,{type:"dots"}):null;return R(e.source).then(l=>{if(a&&a.destroy(),!i||i.submenu!==s)return;if(s.style.minHeight="",!l.length){const d=document.createElement("div");d.style.cssText=L,d.textContent="Nothing found",s.appendChild(d)}const c=l.some(([d])=>d===o),r=[["","\u2014 none \u2014"],...l];o!==""&&!c&&r.push([o,`${o} (missing)`]);for(const[d,b]of r){const f=m(document.createElement("button"));f.type="button",f.style.cssText=u,f.textContent=b,d===o&&(f.style.fontWeight="700"),f.addEventListener("click",()=>{v(t,d===""?null:d),$()}),s.appendChild(f)}}),s})}function ce(e,t){e.appendChild(x(`Effect: ${t.label}`));for(const o of t.attrs||[])e.appendChild(P(o,`${t.name}-${o.name}`));const n=m(document.createElement("button"));n.type="button",n.style.cssText=u,n.innerHTML='<span data-icon="trash" data-icon-size="13" style="display:inline-flex;align-items:center;"></span> Remove effect',n.addEventListener("click",()=>{const o={};for(const s of W(i.span.attrs,t.name))o[s]=null;D(i.textarea,i.span,o),p()}),e.appendChild(n)}function re(){const e=w(),t=i.span,n=t&&A.has(t.name)?t.name:null;for(const o of[...new Set(g.map(s=>s.category))]){const s=g.filter(a=>a.category===o);if(s.length){e.appendChild(x(o));for(const a of s){const l=n&&(a.hosts||[]).includes(n)&&!(a.name in t.attrs),c=m(document.createElement("button"));if(c.type="button",c.style.cssText=u+"justify-content:space-between;",c.appendChild(document.createTextNode(a.label)),l){const r=document.createElement("span");r.textContent=`inject \u2192 [${n}]`,r.style.cssText="opacity:.6;font-size:.7rem;",c.appendChild(r)}c.addEventListener("click",()=>{if(l)S(i.textarea,i.span,a.name,!0);else{const r=a.selfClosing?`[${a.name} /]`:`[${a.name}]
|
|
1
|
+
import{SHORTCODES as A}from"./shortcode-registry.js";import{EFFECTS as _}from"./effect-defs.js";import{findEnclosingShortcode as K,setAttr as N,setAttrs as V,effectAttrKeys as G}from"./shortcode-tags.js";import{insertAtCursor as R,replaceRange as X}from"./markdown-toolbar.js";import{api as g}from"../api.js";const M=new Map,q={forms:async()=>(await g.forms.list()).map(e=>[e.slug,e.title||e.slug]),collections:async()=>(await g.collections.list()).map(e=>[e.slug,e.title||e.slug]),views:async()=>(await g.views.list()).map(e=>[e.slug,e.title||e.slug]),blocks:async()=>(await g.blocks.list()).map(e=>{const t=e?.name??e;return[t,t]}),actions:async()=>(await g.actions.list()).map(e=>[e.slug,e.name||e.title||e.slug]),menus:async()=>(await g.menus.list()).map(e=>[e.slug,e.name||e.title||e.slug]),"menu-locations":async()=>Object.keys(await g.menuLocations.get()||{}).map(e=>[e,e]),"collection-fields":async()=>{const e=a?.span?.attrs?.slug;return!e||e===!0?[]:((await g.collections.get(e)).fields||[]).map(n=>[n.name,n.label||n.name])}};function O(e){const t=e==="collection-fields"?`${e}:${a?.span?.attrs?.slug}`:e;return M.has(t)||M.set(t,q[e]().catch(()=>[])),M.get(t)}const J=A.map(e=>({name:e.name,selfClosing:!!e.selfClosing})),Q=new Map(A.map(e=>[e.name,e])),Z=["Layout","Content","Components","Data","Media","Effects"],ee=new Map(_.map(e=>[e.name,e])),te=_.map(e=>({name:e.name,selfClosing:!!e.selfClosing})),ne=[...J,...te],z=new Set(["card","hero","banner","col","button"]);let a=null;const H="position:fixed;z-index:10000;min-width:210px;max-height:70vh;overflow-y:auto;background:var(--dm-surface,#1c1c30);border:1px solid var(--dm-border,#2a2a40);border-radius:6px;box-shadow:var(--dm-shadow-lg);padding:.3rem;font-size:.83rem;color:var(--dm-text,#e5e5e5);",p="display:flex;align-items:center;gap:.5rem;width:100%;text-align:left;padding:.35rem .55rem;border:none;background:transparent;color:inherit;border-radius:4px;cursor:pointer;font-size:.83rem;",L="padding:.3rem .55rem;color:var(--dm-text-muted,#8a8a9a);font-size:.68rem;text-transform:uppercase;letter-spacing:.06em;",j="flex:1;min-width:0;padding:.2rem .4rem;background:var(--dm-input-bg,#12121e);border:1px solid var(--dm-border,#333);border-radius:3px;color:var(--dm-text,#eee);font-size:.8rem;",oe="height:1px;background:var(--dm-border,#2a2a40);margin:.3rem .2rem;";function m(e){return e.addEventListener("mouseenter",()=>{e.style.background="var(--dm-primary,#4338ca)",e.style.color="var(--dm-primary-text,#fff)"}),e.addEventListener("mouseleave",()=>{e.style.background="transparent",e.style.color="inherit"}),e}export function attachContextMenu(e){e.addEventListener("contextmenu",t=>{t.preventDefault(),b();const n=K(e,ne);se(e,n,t.clientX,t.clientY)})}function b(){a&&(T(),a.root.remove(),document.removeEventListener("mousedown",W,!0),document.removeEventListener("keydown",Y,!0),window.removeEventListener("scroll",D,!0),a=null)}const W=e=>{a&&!a.root.contains(e.target)&&!(a.submenu&&a.submenu.contains(e.target))&&b()},D=e=>{a&&!a.root.contains(e.target)&&!(a.submenu&&a.submenu.contains(e.target))&&b()},Y=e=>{e.key==="Escape"&&b()};function se(e,t,n,o){M.clear();const s=document.createElement("div");s.className="dm-sc-menu",s.style.cssText=H,a={root:s,textarea:e,span:t,submenu:null};const i=t&&(Q.get(t.name)||ee.get(t.name)),l=t&&z.has(t.name)?_.filter(r=>r.name in t.attrs):[];i&&ae(s,i);for(const r of l)he(s,r);(i||l.length)&&s.appendChild(ie()),s.appendChild(v("Add effect\u2026",()=>ye())),s.appendChild(v("Insert\u2026",()=>xe()));const c=e.selectionStart!==e.selectionEnd;(t||c)&&s.appendChild(v(c?"Wrap selection in\u2026":"Wrap in\u2026",()=>Ee())),document.body.appendChild(s),U(s,n,o),document.addEventListener("mousedown",W,!0),document.addEventListener("keydown",Y,!0),window.addEventListener("scroll",D,!0),window.I&&I.scan(s)}function ie(){const e=document.createElement("div");return e.style.cssText=oe,e}function k(e){const t=document.createElement("div");return t.style.cssText=L,t.textContent=e,t}function ae(e,t){e.appendChild(k(`${t.label} options`));for(const n of t.attrs||[])e.appendChild(F(n))}function F(e,t=e.name){const n=a.span.attrs[t];switch(e.type){case"checkbox":return re(e,t,le(n,e.default));case"select":return pe(e,t,n);case"lookup":return fe(e,t,n);case"colour":return ue(e,t,n);case"icon":return me(e,t,n);default:return de(e,t,n)}}function C(e,t){a.span=N(a.textarea,a.span,e,t)}function le(e,t){return e==null?!!t:!(e===!1||e==="false"||e==="0")}function ce(e,t){return t===!!e.default?null:t?"true":"false"}function re(e,t,n){const o=m(document.createElement("label"));o.style.cssText=p;const s=document.createElement("input");return s.type="checkbox",s.checked=n,s.addEventListener("change",()=>C(t,ce(e,s.checked))),o.appendChild(s),o.appendChild(document.createTextNode(e.label)),o}function de(e,t,n){const o=document.createElement("div");o.style.cssText=p+"cursor:default;";const s=document.createElement("span");s.textContent=e.label,s.style.cssText="white-space:nowrap;color:var(--dm-text-muted,#9a9aac);";const i=document.createElement("input");i.type=e.type==="number"?"number":"text",i.value=n===!0||n==null?"":n,e.default!=null&&e.default!==""&&(i.placeholder=String(e.default)),i.style.cssText=j;let l=null;return i.addEventListener("input",()=>{clearTimeout(l),l=setTimeout(()=>C(t,i.value.trim()),180)}),i.addEventListener("mousedown",c=>c.stopPropagation()),o.appendChild(s),o.appendChild(i),o}function ue(e,t,n){const o=document.createElement("div");o.style.cssText=p+"cursor:default;";const s=document.createElement("span");s.textContent=e.label,s.style.flex="1";const i=document.createElement("input");return i.type="color",typeof n=="string"&&/^#/.test(n)?i.value=n:typeof e.default=="string"&&/^#/.test(e.default)&&(i.value=e.default),i.addEventListener("input",()=>C(t,i.value)),o.appendChild(s),o.appendChild(i),o}function pe(e,t,n){const o=n==null||n===!0?e.default??"":n;return v(`${e.label}${o!==""?`: ${o}`:""}`,()=>{const s=w();for(const[i,l]of e.options||[]){const c=m(document.createElement("button"));c.type="button",c.style.cssText=p,c.textContent=l,i===o&&(c.style.fontWeight="700"),c.addEventListener("click",()=>{C(t,i),T()}),s.appendChild(c)}return s})}function me(e,t,n){const o=n==null||n===!0?"":String(n),s=o?`${e.label}: ${o}`:e.label;return v(s,()=>{const i=w();i.style.width="232px";const l=document.createElement("input");l.type="text",l.placeholder="Search icons\u2026",l.style.cssText=j+"margin:6px;width:calc(100% - 12px);",l.addEventListener("mousedown",y=>y.stopPropagation()),i.appendChild(l);const c=m(document.createElement("button"));c.type="button",c.style.cssText=p,c.textContent="\u2014 none \u2014",o||(c.style.fontWeight="700"),c.addEventListener("click",()=>{C(t,null),T()}),i.appendChild(c);const r=document.createElement("div");r.style.cssText="display:flex;flex-wrap:wrap;gap:2px;padding:4px 6px;max-height:220px;overflow-y:auto;",i.appendChild(r);const d=96,h=()=>{const y=typeof I<"u"&&typeof I.list=="function"?I.list():[],P=l.value.trim().toLowerCase(),$=P?y.filter(u=>u.toLowerCase().includes(P)):y;r.replaceChildren();for(const u of $.slice(0,d)){const x=m(document.createElement("button"));x.type="button",x.title=u,x.style.cssText="width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;background:none;border:1px solid transparent;border-radius:4px;cursor:pointer;color:inherit;",u===o&&(x.style.borderColor="var(--dm-primary,#5b8cff)");const S=document.createElement("span");S.setAttribute("data-icon",u),S.setAttribute("data-icon-size","15"),S.style.cssText="display:inline-flex;align-items:center;",x.appendChild(S),x.addEventListener("click",()=>{C(t,u),T()}),r.appendChild(x)}if($.length>d){const u=document.createElement("div");u.style.cssText=L,u.textContent=`${$.length-d} more \u2014 refine search`,r.appendChild(u)}if(!$.length){const u=document.createElement("div");u.style.cssText=L,u.textContent="No icons match",r.appendChild(u)}window.I&&I.scan(r)};let f=null;return l.addEventListener("input",()=>{clearTimeout(f),f=setTimeout(h,120)}),h(),setTimeout(()=>l.focus(),0),i})}function fe(e,t,n){const o=n==null||n===!0?e.default??"":n;return v(`${e.label}${o!==""?`: ${o}`:""}`,()=>{const s=w();s.style.minHeight="48px";const i=typeof E<"u"&&E.loader?E.loader(s,{type:"dots"}):null;return O(e.source).then(l=>{if(i&&i.destroy(),!a||a.submenu!==s)return;if(s.style.minHeight="",!l.length){const d=document.createElement("div");d.style.cssText=L,d.textContent="Nothing found",s.appendChild(d)}const c=l.some(([d])=>d===o),r=[["","\u2014 none \u2014"],...l];o!==""&&!c&&r.push([o,`${o} (missing)`]);for(const[d,h]of r){const f=m(document.createElement("button"));f.type="button",f.style.cssText=p,f.textContent=h,d===o&&(f.style.fontWeight="700"),f.addEventListener("click",()=>{C(t,d===""?null:d),T()}),s.appendChild(f)}}),s})}function he(e,t){e.appendChild(k(`Effect: ${t.label}`));for(const o of t.attrs||[])e.appendChild(F(o,`${t.name}-${o.name}`));const n=m(document.createElement("button"));n.type="button",n.style.cssText=p,n.innerHTML='<span data-icon="trash" data-icon-size="13" style="display:inline-flex;align-items:center;"></span> Remove effect',n.addEventListener("click",()=>{const o={};for(const s of G(a.span.attrs,t.name))o[s]=null;V(a.textarea,a.span,o),b()}),e.appendChild(n)}function ye(){const e=w(),t=a.span,n=t&&z.has(t.name)?t.name:null;for(const o of[...new Set(_.map(s=>s.category))]){const s=_.filter(i=>i.category===o);if(s.length){e.appendChild(k(o));for(const i of s){const l=n&&(i.hosts||[]).includes(n)&&!(i.name in t.attrs),c=m(document.createElement("button"));if(c.type="button",c.style.cssText=p+"justify-content:space-between;",c.appendChild(document.createTextNode(i.label)),l){const r=document.createElement("span");r.textContent=`inject \u2192 [${n}]`,r.style.cssText="opacity:.6;font-size:.7rem;",c.appendChild(r)}c.addEventListener("click",()=>{if(l)N(a.textarea,a.span,i.name,!0);else{const r=i.selfClosing?`[${i.name} /]`:`[${i.name}]
|
|
2
2
|
Content
|
|
3
|
-
[/${
|
|
3
|
+
[/${i.name}]`;R(a.textarea,r)}b()}),e.appendChild(c)}}}return e}function v(e,t){const n=m(document.createElement("button"));n.type="button",n.style.cssText=p+"justify-content:space-between;",n.appendChild(document.createTextNode(e));const o=document.createElement("span");return o.setAttribute("data-icon","chevron-right"),o.setAttribute("data-icon-size","12"),o.style.cssText="opacity:.6;display:inline-flex;align-items:center;flex-shrink:0;",n.appendChild(o),n.addEventListener("mouseenter",()=>B(n,t)),n.addEventListener("click",s=>{s.stopPropagation(),B(n,t)}),n}function w(){const e=document.createElement("div");return e.className="dm-sc-submenu",e.style.cssText=H,e}function B(e,t){T();const n=t();a.submenu=n,document.body.appendChild(n);const o=e.getBoundingClientRect();U(n,o.right+2,o.top,o.left-2),window.I&&I.scan(n)}function T(){a&&a.submenu&&(a.submenu.remove(),a.submenu=null)}function be(e){return(e.attrs||[]).find(t=>t.type==="lookup"&&t.source!=="collection-fields")||null}function ge(e,t,n){const o=e.template||`[${e.name} /]`,s=new RegExp(`\\b${t}="[^"]*"`);return s.test(o)?o.replace(s,`${t}="${n}"`):o.replace(`[${e.name}`,`[${e.name} ${t}="${n}"`)}function xe(){const e=w();for(const t of Z){const n=A.filter(o=>o.category===t&&!o.child);if(n.length){e.appendChild(k(t));for(const o of n){const s=be(o),i=m(document.createElement("button"));if(i.type="button",i.style.cssText=p+(s?"justify-content:space-between;":""),i.appendChild(document.createTextNode(o.label)),s){const l=document.createElement("span");l.setAttribute("data-icon","chevron-right"),l.setAttribute("data-icon-size","12"),l.style.cssText="opacity:.6;display:inline-flex;align-items:center;flex-shrink:0;",i.appendChild(l)}i.addEventListener("click",async l=>{if(!s){R(a.textarea,o.template||`[${o.name} /]`),b();return}l.stopPropagation(),e.textContent="",e.style.minHeight="48px";const c=typeof E<"u"&&E.loader?E.loader(e,{type:"dots"}):null,r=await O(s.source);if(c&&c.destroy(),!a||a.submenu!==e)return;if(e.style.minHeight="",e.appendChild(k(`Insert ${o.label}`)),!r.length){const h=document.createElement("div");h.style.cssText=L,h.textContent="Nothing found \u2014 inserting the blank template instead.",e.appendChild(h)}for(const[h,f]of r){const y=m(document.createElement("button"));y.type="button",y.style.cssText=p,y.textContent=f,y.addEventListener("click",()=>{R(a.textarea,ge(o,s.name,h)),b()}),e.appendChild(y)}const d=m(document.createElement("button"));d.type="button",d.style.cssText=p+"opacity:.75;",d.textContent=r.length?"Blank template\u2026":"Insert blank template",d.addEventListener("click",()=>{R(a.textarea,o.template||`[${o.name} /]`),b()}),e.appendChild(d)}),e.appendChild(i)}}}return e}function Ee(){const e=w(),t=A.filter(n=>n.wrappable||n.container);for(const n of t){const o=m(document.createElement("button"));o.type="button",o.style.cssText=p,o.textContent=n.label,o.addEventListener("click",()=>{Ce(n),b()}),e.appendChild(o)}return e}function Ce(e){const t=a.textarea,n=t.selectionStart,o=t.selectionEnd,s=t.value.slice(n,o)||"Content";let i;e.name==="grid"?i=`[grid cols="2"]
|
|
4
4
|
[col]
|
|
5
5
|
${s}
|
|
6
6
|
[/col]
|
|
7
|
-
[/grid]`:
|
|
7
|
+
[/grid]`:i=`[${e.name}]
|
|
8
8
|
${s}
|
|
9
|
-
[/${e.name}]`,
|
|
9
|
+
[/${e.name}]`,X(t,n,o,i)}function U(e,t,n,o){e.style.left="0px",e.style.top="0px";const s=e.getBoundingClientRect();let i=t;t+s.width>window.innerWidth-8&&(i=o!=null?o-s.width:t-s.width);let l=n;n+s.height>window.innerHeight-8&&(l=Math.max(8,window.innerHeight-s.height-8)),e.style.left=`${Math.max(8,i)}px`,e.style.top=`${Math.max(8,l)}px`}
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
<div class="card-body" style="display:flex;gap:.75rem;align-items:center;flex-wrap:wrap;">
|
|
30
30
|
<input id="entry-search" type="text" class="form-input" placeholder="Search entries…" style="flex:1;min-width:200px;">
|
|
31
31
|
<span id="entry-count" class="text-muted" style="white-space:nowrap;font-size:.85rem;"></span>
|
|
32
|
+
<div id="entry-filter-chips" style="display:none;flex-basis:100%;gap:.4rem;flex-wrap:wrap;align-items:center;"></div>
|
|
32
33
|
</div>
|
|
33
34
|
</div>
|
|
34
35
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{api as h}from"../api.js";import{keepTableIconsAlive as M}from"../lib/table-icons.js";function L(t){return String(t).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}let u=null,g=null,d=[],N=1,w=[],x={};const O=50;export const collectionEntriesView={templateUrl:"/admin/js/templates/collection-entries.html",async onMount(t){d=[],N=1,g=null,w=[],x={};const i=window.location.hash.match(/\/collections\/([^/?#]+)\/entries/);if(u=i?i[1]:null,!u){E.toast("No collection selected.",{type:"error"});return}const s=E.loader(t.get(0),{type:"dots"});try{if(g=await h.collections.get(u),!g){s.destroy(),E.toast("Collection not found.",{type:"error"}),R.navigate("/collections");return}const o=t.find("#entries-title").get(0);o&&(o.textContent=g.title+" \u2014 Entries");const e=t.find("#edit-schema-btn").get(0);e&&e.setAttribute("href",`#/collections/edit/${u}`);const r=t.find("#form-btn").get(0);r&&(new Set(["roles","user-profiles"]).has(u)?r.style.display="none":r.setAttribute("href",`#/forms/edit/${u}`))}catch{s.destroy(),E.toast("Failed to load collection schema.",{type:"error"});return}try{w=await h.actions.forCollection(u)}catch{w=[]}try{await v(t)}finally{s.destroy()}await k(t);const l=t.find("#entry-search").get(0);l&&l.addEventListener("input",()=>{const o=l.value.toLowerCase().trim(),e=o?d.filter(r=>Object.values(r.data||{}).some(a=>String(a).toLowerCase().includes(o))):d;C(e,t)}),t.find("#add-entry-btn").off("click").on("click",()=>{A(null,t)}),t.find("#export-btn").off("click").on("click",()=>{const o=E.slideover({title:"Export Entries",size:"sm",position:"right"}),e=document.createElement("div");e.style.cssText="padding:1.25rem;display:flex;flex-direction:column;gap:1rem;",["json","csv"].forEach(r=>{const a=document.createElement("button");a.className="btn btn-ghost",a.style.cssText="justify-content:flex-start;gap:.5rem;",a.textContent=r==="json"?"Export as JSON":"Export as CSV",a.addEventListener("click",()=>{fetch(`/api/collections/${u}/export?format=${r}`,{headers:{Authorization:`Bearer ${S.get("auth_token")||""}`}}).then(c=>c.blob()).then(c=>{const n=document.createElement("a");n.href=URL.createObjectURL(c),n.download=`${u}-entries.${r}`,document.body.appendChild(n),n.click(),document.body.removeChild(n),URL.revokeObjectURL(n.href),o.close()}).catch(()=>E.toast("Export failed.",{type:"error"}))}),e.appendChild(a)}),o.setContent(e),o.open()}),t.find("#import-btn").off("click").on("click",()=>{I(t)}),t.find("#clear-all-btn").off("click").on("click",async()=>{if(await E.confirm("Delete ALL entries? This cannot be undone."))try{await h.collections.clearEntries(u),d=[],E.toast("All entries cleared.",{type:"success"}),C([],t)}catch{E.toast("Failed to clear entries.",{type:"error"})}}),t.off("click",".js-run-action").on("click",".js-run-action",async function(o){o.stopPropagation();const e=this,{actionSlug:r,entryId:a,confirmMessage:c}=e.dataset;if(!(c&&!await E.confirm(c))){e.disabled=!0;try{const n=await h.actions.execute(r,a);if(n.success)E.toast(`Action completed (${n.stepsCompleted} step${n.stepsCompleted!==1?"s":""}).`,{type:"success"}),await v(t);else{const p=n.results?.find(f=>!f.success);E.toast(p?.error||"Action failed.",{type:"error"})}}catch(n){E.toast(n.message||"Action failed.",{type:"error"})}finally{e.disabled=!1}}}),t.off("click",".js-edit-entry").on("click",".js-edit-entry",function(o){o.stopPropagation();const e=d.find(r=>r.id===this.dataset.id);e&&A(e,t)}),t.off("click",".js-delete-entry").on("click",".js-delete-entry",async function(o){o.stopPropagation();const e=this.dataset.id;if(await E.confirm("Delete this entry?"))try{await h.collections.deleteEntry(u,e),d=d.filter(a=>a.id!==e),E.toast("Entry deleted.",{type:"success"}),C(d,t)}catch{E.toast("Failed to delete entry.",{type:"error"})}}),t.off("click","#entries-table tbody tr").on("click","#entries-table tbody tr",function(o){if(o.target.closest(".js-run-action, .js-edit-entry, .js-delete-entry"))return;const e=this.querySelector(".js-edit-entry")?.dataset.id,r=d.find(a=>a.id===e);r&&B(r)}),Domma.icons.scan(),M(t.get(0))}};async function k(t){const m=w.filter(s=>s.access?.rowLevel);if(m.length===0)return;const i=d.map(s=>s.id);i.length!==0&&(await Promise.all(m.map(async s=>{try{const{allowed:l}=await h.actions.checkAccess(s.slug,i);x[s.slug]=new Set(l)}catch{x[s.slug]=null}})),C(d,t))}async function v(t){try{const m=await h.collections.listEntries(u,{limit:500});d=m.entries||m||[]}catch{d=[],E.toast("Could not load entries.",{type:"error"})}C(d,t),await k(t)}function C(t,m){const i=m.find("#entry-count").get(0);i&&(i.textContent=t.length===d.length?`${d.length} entr${d.length!==1?"ies":"y"}`:`Showing ${t.length} of ${d.length}`);const l=[...(g?.fields||[]).slice(0,5).map(e=>({key:`data.${e.name}`,title:e.label||e.name,render:(r,a)=>{const c=a.data?.[e.name]??"",n=String(c),p=document.createElement("span");return p.title=n,p.textContent=n.length>60?n.slice(0,60)+"\u2026":n,p.outerHTML}})),{key:"meta",title:"Created",render:e=>D(e?.createdAt).format("DD MMM YYYY HH:mm")},{key:"id",title:"",render:e=>{const r=document.createElement("div");r.style.cssText="display:flex;gap:.3rem;justify-content:flex-end;flex-wrap:wrap;",w.forEach(n=>{if(x[n.slug]!==void 0&&x[n.slug]!==null&&!x[n.slug].has(e))return;const p=document.createElement("button");p.className="btn btn-sm btn-ghost js-run-action",p.dataset.actionSlug=n.slug,p.dataset.entryId=e,p.dataset.confirmMessage=n.trigger?.confirmMessage||"",p.title=n.title||n.slug;const f=document.createElement("span");f.setAttribute("data-icon",L(n.trigger?.icon||"zap")),f.setAttribute("data-icon-size","14"),p.appendChild(f),p.appendChild(document.createTextNode(" "+(n.trigger?.label||n.title))),r.appendChild(p)});const a=document.createElement("button");a.className="btn btn-sm btn-ghost js-edit-entry",a.dataset.id=e,a.setAttribute("data-tooltip","Edit"),a.innerHTML='<span data-icon="edit" data-icon-size="14"></span>';const c=document.createElement("button");return c.className="btn btn-sm btn-ghost js-delete-entry",c.dataset.id=e,c.setAttribute("data-tooltip","Delete"),c.innerHTML='<span data-icon="trash" data-icon-size="14"></span>',r.appendChild(a),r.appendChild(c),r.outerHTML}}];T.create("#entries-table",{data:t,columns:l,emptyMessage:'No entries yet. Click "Add Entry" to get started.'});const o=document.querySelector("#entries-table");o&&(o.querySelectorAll("tbody tr").forEach(e=>{e.style.cursor="pointer"}),Domma.icons.scan(o),o.querySelectorAll("[data-tooltip]").forEach(e=>{E.tooltip(e,{content:e.getAttribute("data-tooltip"),position:"top"})}))}function z(t,m){const i={};return t.forEach(s=>{const l={};s.fullWidth?l.span=m:s.span>1&&(l.span=s.span),s.placeholder&&(l.placeholder=s.placeholder),s.helper&&(l.hint=s.helper);const o=s.type==="checkbox"?"boolean":s.type==="date"?"string":s.type;i[s.name]={type:o,label:s.label,required:s.required,options:s.options,...Object.keys(l).length?{formConfig:l}:{}}}),i}function A(t,m){const i=!!t,s=E.slideover({title:i?"Edit Entry":"Add Entry",size:"md",position:"right"}),l=document.createElement("div");l.style.cssText="padding:.25rem 0 .5rem;";const o=g?.fields||[],e=g?.columns||2,r=g?.layout||"stacked",a=document.createElement("div");F.create(z(o,e),t?.data||{},{layout:r,columns:e,showSubmitButton:!1}).renderTo(a),(o||[]).forEach(f=>{if(f.type!=="date"||!f.name)return;const y=a.querySelector(`[name="${f.name}"]`);y&&y.type!=="date"&&(y.type="date")}),l.appendChild(a);const c=document.createElement("div");c.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:.5rem;";const n=document.createElement("button");n.className="btn btn-ghost",n.textContent="Cancel";const p=document.createElement("button");p.className="btn btn-primary",p.textContent=i?"Save Changes":"Add Entry",c.appendChild(n),c.appendChild(p),l.appendChild(c),s.element.appendChild(l),s.open(),n.addEventListener("click",()=>s.close()),p.addEventListener("click",async()=>{const f={};o.forEach(y=>{const b=a.querySelector(`[name="${y.name}"]`);b&&(f[y.name]=y.type==="checkbox"?b.checked:b.value)}),p.disabled=!0;try{if(i){const y=await h.collections.updateEntry(u,t.id,f),b=d.findIndex(j=>j.id===t.id);b!==-1&&(d[b]=y),E.toast("Entry updated.",{type:"success"})}else{const y=await h.collections.createEntry(u,f);d.unshift(y),E.toast("Entry added.",{type:"success"})}s.close(),C(d,m||{find:()=>({get:()=>null})})}catch(y){E.toast(y.message||"Failed to save entry.",{type:"error"}),p.disabled=!1}})}function B(t){const m=document.createElement("div"),i=g?.fields||[],s=document.createElement("div");s.style.cssText="display:flex;flex-direction:column;gap:.75rem;margin-bottom:1.25rem;",i.forEach(e=>{const r=t.data?.[e.name];if(r==null||r==="")return;const a=document.createElement("div");a.style.cssText="border-bottom:1px solid var(--dm-border,#333);padding-bottom:.6rem;";const c=document.createElement("strong");c.textContent=e.label||e.name,c.style.cssText="display:block;font-size:.8rem;color:var(--dm-text-muted,#888);margin-bottom:.2rem;";const n=document.createElement("p");n.textContent=String(r),n.style.cssText="margin:0;word-break:break-word;",a.appendChild(c),a.appendChild(n),s.appendChild(a)});const l=document.createElement("div");if(l.style.cssText="font-size:.8rem;color:var(--dm-text-muted,#888);display:flex;flex-direction:column;gap:.3rem;border-top:1px solid var(--dm-border,#333);padding-top:.75rem;",t.meta?.createdAt){const e=document.createElement("span");e.textContent=`Created: ${D(t.meta.createdAt).format("DD MMM YYYY HH:mm")}`,l.appendChild(e)}if(t.meta?.source){const e=document.createElement("span");e.textContent=`Source: ${t.meta.source}`,l.appendChild(e)}m.appendChild(s),m.appendChild(l);const o=E.slideover({title:"Entry Details",size:"md",position:"right"});o.element.appendChild(m),o.open()}function I(t){const m=E.slideover({title:"Import Entries",size:"md",position:"right"}),i=document.createElement("div");i.style.cssText="padding:.25rem 0 .5rem;display:flex;flex-direction:column;gap:.75rem;";const s=document.createElement("p");s.textContent='Paste a JSON array of entries. Each item should have a "data" object with field values.',s.style.cssText="font-size:.875rem;color:var(--dm-text-muted,#888);margin:0;",i.appendChild(s);const l=document.createElement("textarea");l.className="form-input",l.rows=10,l.placeholder='[{"data": {"name": "Example"}}, ...]',i.appendChild(l);const o=document.createElement("p");o.style.cssText="font-size:.875rem;margin:0;display:none;",i.appendChild(o);const e=document.createElement("div");e.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;";const r=document.createElement("button");r.className="btn btn-ghost",r.textContent="Cancel";const a=document.createElement("button");a.className="btn btn-primary",a.textContent="Import",e.appendChild(r),e.appendChild(a),i.appendChild(e),m.element.appendChild(i),m.open(),r.addEventListener("click",()=>m.close()),a.addEventListener("click",async()=>{let c;try{if(c=JSON.parse(l.value),!Array.isArray(c))throw new Error("Must be a JSON array")}catch(n){o.style.display="",o.style.color="var(--dm-danger,#f87171)",o.textContent=`Invalid JSON: ${n.message}`;return}a.disabled=!0;try{const n=await h.collections.import(u,c);o.style.display="",o.style.color="var(--dm-success,#4ade80)",o.textContent=`Imported ${n.imported} entries. Skipped: ${n.skipped}.`,await v(t||{find:()=>({get:()=>null,off:()=>({on:()=>{}})})}),setTimeout(()=>m.close(),1500)}catch(n){o.style.display="",o.style.color="var(--dm-danger,#f87171)",o.textContent=n.message||"Import failed."}finally{a.disabled=!1}})}
|
|
1
|
+
import{api as h}from"../api.js";import{keepTableIconsAlive as _}from"../lib/table-icons.js";function O(e){return String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}let y=null,g=null,u=[],H=1,L=[],k={},b=[];const te=50,P={eq:"=",ne:"\u2260",contains:"contains",gt:">",lt:"<"};function U(e,r){const a=e.data?.[r.field],o=String(a??"");switch(r.op){case"eq":return o===String(r.value);case"ne":return o!==String(r.value);case"contains":return o.toLowerCase().includes(String(r.value).toLowerCase());case"gt":case"lt":{const c=parseFloat(o),t=parseFloat(r.value);if(!Number.isNaN(c)&&!Number.isNaN(t))return r.op==="gt"?c>t:c<t;const n=Date.parse(o),i=Date.parse(r.value);return!Number.isNaN(n)&&!Number.isNaN(i)?r.op==="gt"?n>i:n<i:r.op==="gt"?o>String(r.value):o<String(r.value)}default:return!0}}function x(e){const r=(e.find("#entry-search").get(0)?.value||"").toLowerCase().trim();let a=u;for(const o of b)a=a.filter(c=>U(c,o));r&&(a=a.filter(o=>Object.values(o.data||{}).some(c=>String(c).toLowerCase().includes(r)))),W(e),B(a,e)}function W(e){const r=e.find("#entry-filter-chips").get(0);if(!r)return;if(r.replaceChildren(),!b.length){r.style.display="none";return}r.style.display="flex",b.forEach((o,c)=>{const t=document.createElement("span");t.className="badge badge-secondary",t.style.cssText="display:inline-flex;align-items:center;gap:.35rem;font-size:.78rem;";const n=String(o.value);t.textContent=`${o.label} ${P[o.op]||o.op} \u201C${n.length>24?n.slice(0,24)+"\u2026":n}\u201D`;const i=document.createElement("button");i.type="button",i.textContent="\xD7",i.title="Remove filter",i.style.cssText="background:none;border:none;cursor:pointer;color:inherit;font-size:.9rem;line-height:1;padding:0;",i.addEventListener("click",()=>{b.splice(c,1),x(e)}),t.appendChild(i),r.appendChild(t)});const a=document.createElement("button");a.type="button",a.className="btn btn-ghost btn-sm",a.textContent="Clear all",a.addEventListener("click",()=>{b=[],x(e)}),r.appendChild(a)}let v=null;const J="position:fixed;z-index:10000;min-width:200px;max-height:60vh;overflow-y:auto;background:var(--dm-surface,#1c1c30);border:1px solid var(--dm-border,#2a2a40);border-radius:6px;box-shadow:var(--dm-shadow-lg);padding:.3rem;font-size:.83rem;color:var(--dm-text,#e5e5e5);",G="display:flex;align-items:center;gap:.5rem;width:100%;text-align:left;background:none;border:none;color:inherit;font:inherit;padding:.35rem .6rem;border-radius:4px;cursor:pointer;";function C(){v&&(v.remove(),v=null,document.removeEventListener("mousedown",A,!0),document.removeEventListener("keydown",j,!0),window.removeEventListener("scroll",z,!0))}const A=e=>{v&&!v.contains(e.target)&&C()},j=e=>{e.key==="Escape"&&C()},z=e=>{v&&!v.contains(e.target)&&C()};function N(e,r){const a=document.createElement("button");return a.type="button",a.style.cssText=G,a.textContent=e,a.addEventListener("mouseenter",()=>{a.style.background="var(--dm-surface-2,rgba(255,255,255,.06))"}),a.addEventListener("mouseleave",()=>{a.style.background="none"}),a.addEventListener("click",r),a}function V(e){const r=document.createElement("div");return r.style.cssText="padding:.3rem .6rem;font-size:.72rem;text-transform:uppercase;letter-spacing:.04em;color:var(--dm-text-muted,#9a9aac);",r.textContent=e,r}function K(e,r,a,o,c){C();const t=document.createElement("div");t.style.cssText=J,v=t;const n=a.label||a.name,i=String(o).length>28?String(o).slice(0,28)+"\u2026":String(o),l=(m,f)=>{b.push({field:a.name,label:n,op:m,value:f}),x(c),C()};t.appendChild(V(n)),o!==""&&(t.appendChild(N(`Filter: = \u201C${i}\u201D`,()=>l("eq",o))),t.appendChild(N(`Filter: \u2260 \u201C${i}\u201D`,()=>l("ne",o))),["number","date","time"].includes(a.type)&&(t.appendChild(N(`Filter: > \u201C${i}\u201D`,()=>l("gt",o))),t.appendChild(N(`Filter: < \u201C${i}\u201D`,()=>l("lt",o)))));const d=document.createElement("div");d.style.cssText="padding:.35rem .6rem;";const s=document.createElement("input");if(s.type="text",s.placeholder=`${n} contains\u2026`,s.className="form-input",s.style.cssText="width:100%;font-size:.8rem;padding:.3rem .5rem;",s.addEventListener("mousedown",m=>m.stopPropagation()),s.addEventListener("keydown",m=>{m.key==="Enter"&&s.value.trim()&&l("contains",s.value.trim())}),d.appendChild(s),t.appendChild(d),b.length){const m=document.createElement("div");m.style.cssText="border-top:1px solid var(--dm-border,#2a2a40);margin:.25rem 0;",t.appendChild(m),t.appendChild(N(`Clear all filters (${b.length})`,()=>{b=[],x(c),C()}))}document.body.appendChild(t);const p=t.getBoundingClientRect();t.style.left=Math.min(e,window.innerWidth-p.width-8)+"px",t.style.top=Math.min(r,window.innerHeight-p.height-8)+"px",document.addEventListener("mousedown",A,!0),document.addEventListener("keydown",j,!0),window.addEventListener("scroll",z,!0),setTimeout(()=>s.focus(),0)}export const collectionEntriesView={templateUrl:"/admin/js/templates/collection-entries.html",async onMount(e){u=[],H=1,g=null,L=[],k={},b=[],C();const a=window.location.hash.match(/\/collections\/([^/?#]+)\/entries/);if(y=a?a[1]:null,!y){E.toast("No collection selected.",{type:"error"});return}const o=E.loader(e.get(0),{type:"dots"});try{if(g=await h.collections.get(y),!g){o.destroy(),E.toast("Collection not found.",{type:"error"}),R.navigate("/collections");return}const t=e.find("#entries-title").get(0);t&&(t.textContent=g.title+" \u2014 Entries");const n=e.find("#edit-schema-btn").get(0);n&&n.setAttribute("href",`#/collections/edit/${y}`);const i=e.find("#form-btn").get(0);i&&(new Set(["roles","user-profiles"]).has(y)?i.style.display="none":i.setAttribute("href",`#/forms/edit/${y}`))}catch{o.destroy(),E.toast("Failed to load collection schema.",{type:"error"});return}try{L=await h.actions.forCollection(y)}catch{L=[]}try{await M(e)}finally{o.destroy()}await I(e);const c=e.find("#entry-search").get(0);c&&c.addEventListener("input",()=>x(e)),e.get(0).addEventListener("contextmenu",t=>{const n=t.target.closest("#entries-table tbody td");if(!n)return;const l=(g?.fields||[]).slice(0,5)[n.cellIndex];if(!l)return;t.preventDefault();const d=n.querySelector("span[title]")?.title??n.textContent.trim();K(t.clientX,t.clientY,l,d,e)}),e.find("#add-entry-btn").off("click").on("click",()=>{q(null,e)}),e.find("#export-btn").off("click").on("click",()=>{const t=E.slideover({title:"Export Entries",size:"sm",position:"right"}),n=document.createElement("div");n.style.cssText="padding:1.25rem;display:flex;flex-direction:column;gap:1rem;",["json","csv"].forEach(i=>{const l=document.createElement("button");l.className="btn btn-ghost",l.style.cssText="justify-content:flex-start;gap:.5rem;",l.textContent=i==="json"?"Export as JSON":"Export as CSV",l.addEventListener("click",()=>{fetch(`/api/collections/${y}/export?format=${i}`,{headers:{Authorization:`Bearer ${S.get("auth_token")||""}`}}).then(d=>d.blob()).then(d=>{const s=document.createElement("a");s.href=URL.createObjectURL(d),s.download=`${y}-entries.${i}`,document.body.appendChild(s),s.click(),document.body.removeChild(s),URL.revokeObjectURL(s.href),t.close()}).catch(()=>E.toast("Export failed.",{type:"error"}))}),n.appendChild(l)}),t.setContent(n),t.open()}),e.find("#import-btn").off("click").on("click",()=>{Q(e)}),e.find("#clear-all-btn").off("click").on("click",async()=>{if(await E.confirm("Delete ALL entries? This cannot be undone."))try{await h.collections.clearEntries(y),u=[],E.toast("All entries cleared.",{type:"success"}),x(e)}catch{E.toast("Failed to clear entries.",{type:"error"})}}),e.off("click",".js-run-action").on("click",".js-run-action",async function(t){t.stopPropagation();const n=this,{actionSlug:i,entryId:l,confirmMessage:d}=n.dataset;if(!(d&&!await E.confirm(d))){n.disabled=!0;try{const s=await h.actions.execute(i,l);if(s.success)E.toast(`Action completed (${s.stepsCompleted} step${s.stepsCompleted!==1?"s":""}).`,{type:"success"}),await M(e);else{const p=s.results?.find(m=>!m.success);E.toast(p?.error||"Action failed.",{type:"error"})}}catch(s){E.toast(s.message||"Action failed.",{type:"error"})}finally{n.disabled=!1}}}),e.off("click",".js-edit-entry").on("click",".js-edit-entry",function(t){t.stopPropagation();const n=u.find(i=>i.id===this.dataset.id);n&&q(n,e)}),e.off("click",".js-delete-entry").on("click",".js-delete-entry",async function(t){t.stopPropagation();const n=this.dataset.id;if(await E.confirm("Delete this entry?"))try{await h.collections.deleteEntry(y,n),u=u.filter(l=>l.id!==n),E.toast("Entry deleted.",{type:"success"}),x(e)}catch{E.toast("Failed to delete entry.",{type:"error"})}}),e.off("click","#entries-table tbody tr").on("click","#entries-table tbody tr",function(t){if(t.target.closest(".js-run-action, .js-edit-entry, .js-delete-entry"))return;const n=this.querySelector(".js-edit-entry")?.dataset.id,i=u.find(l=>l.id===n);i&&Z(i)}),Domma.icons.scan(),_(e.get(0))}};async function I(e){const r=L.filter(o=>o.access?.rowLevel);if(r.length===0)return;const a=u.map(o=>o.id);a.length!==0&&(await Promise.all(r.map(async o=>{try{const{allowed:c}=await h.actions.checkAccess(o.slug,a);k[o.slug]=new Set(c)}catch{k[o.slug]=null}})),x(e))}async function M(e){try{const r=await h.collections.listEntries(y,{limit:500});u=r.entries||r||[]}catch{u=[],E.toast("Could not load entries.",{type:"error"})}x(e),await I(e)}function B(e,r){const a=r.find("#entry-count").get(0);a&&(a.textContent=e.length===u.length?`${u.length} entr${u.length!==1?"ies":"y"}`:`Showing ${e.length} of ${u.length}`);const c=[...(g?.fields||[]).slice(0,5).map(n=>({key:`data.${n.name}`,title:n.label||n.name,render:(i,l)=>{const d=l.data?.[n.name]??"",s=String(d),p=document.createElement("span");return p.title=s,p.textContent=s.length>60?s.slice(0,60)+"\u2026":s,p.outerHTML}})),{key:"meta",title:"Created",render:n=>D(n?.createdAt).format("DD MMM YYYY HH:mm")},{key:"id",title:"",render:n=>{const i=document.createElement("div");i.style.cssText="display:flex;gap:.3rem;justify-content:flex-end;flex-wrap:wrap;",L.forEach(s=>{if(k[s.slug]!==void 0&&k[s.slug]!==null&&!k[s.slug].has(n))return;const p=document.createElement("button");p.className="btn btn-sm btn-ghost js-run-action",p.dataset.actionSlug=s.slug,p.dataset.entryId=n,p.dataset.confirmMessage=s.trigger?.confirmMessage||"",p.title=s.title||s.slug;const m=document.createElement("span");m.setAttribute("data-icon",O(s.trigger?.icon||"zap")),m.setAttribute("data-icon-size","14"),p.appendChild(m),p.appendChild(document.createTextNode(" "+(s.trigger?.label||s.title))),i.appendChild(p)});const l=document.createElement("button");l.className="btn btn-sm btn-ghost js-edit-entry",l.dataset.id=n,l.setAttribute("data-tooltip","Edit"),l.innerHTML='<span data-icon="edit" data-icon-size="14"></span>';const d=document.createElement("button");return d.className="btn btn-sm btn-ghost js-delete-entry",d.dataset.id=n,d.setAttribute("data-tooltip","Delete"),d.innerHTML='<span data-icon="trash" data-icon-size="14"></span>',i.appendChild(l),i.appendChild(d),i.outerHTML}}];T.create("#entries-table",{data:e,columns:c,emptyMessage:'No entries yet. Click "Add Entry" to get started.'});const t=document.querySelector("#entries-table");t&&(t.querySelectorAll("tbody tr").forEach(n=>{n.style.cursor="pointer"}),Domma.icons.scan(t),t.querySelectorAll("[data-tooltip]").forEach(n=>{E.tooltip(n,{content:n.getAttribute("data-tooltip"),position:"top"})}))}function X(e,r){const a={};return e.forEach(o=>{const c={};o.fullWidth?c.span=r:o.span>1&&(c.span=o.span),o.placeholder&&(c.placeholder=o.placeholder),o.helper&&(c.hint=o.helper);const t=o.type==="checkbox"?"boolean":o.type==="date"?"string":o.type;a[o.name]={type:t,label:o.label,required:o.required,options:o.options,...Object.keys(c).length?{formConfig:c}:{}}}),a}function q(e,r){const a=!!e,o=E.slideover({title:a?"Edit Entry":"Add Entry",size:"md",position:"right"}),c=document.createElement("div");c.style.cssText="padding:.25rem 0 .5rem;";const t=g?.fields||[],n=g?.columns||2,i=g?.layout||"stacked",l=document.createElement("div");F.create(X(t,n),e?.data||{},{layout:i,columns:n,showSubmitButton:!1}).renderTo(l),(t||[]).forEach(m=>{if(m.type!=="date"||!m.name)return;const f=l.querySelector(`[name="${m.name}"]`);f&&f.type!=="date"&&(f.type="date")}),c.appendChild(l);const d=document.createElement("div");d.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:.5rem;";const s=document.createElement("button");s.className="btn btn-ghost",s.textContent="Cancel";const p=document.createElement("button");p.className="btn btn-primary",p.textContent=a?"Save Changes":"Add Entry",d.appendChild(s),d.appendChild(p),c.appendChild(d),o.element.appendChild(c),o.open(),s.addEventListener("click",()=>o.close()),p.addEventListener("click",async()=>{const m={};t.forEach(f=>{const w=l.querySelector(`[name="${f.name}"]`);w&&(m[f.name]=f.type==="checkbox"?w.checked:w.value)}),p.disabled=!0;try{if(a){const f=await h.collections.updateEntry(y,e.id,m),w=u.findIndex(Y=>Y.id===e.id);w!==-1&&(u[w]=f),E.toast("Entry updated.",{type:"success"})}else{const f=await h.collections.createEntry(y,m);u.unshift(f),E.toast("Entry added.",{type:"success"})}o.close(),r?x(r):B(u,{find:()=>({get:()=>null})})}catch(f){E.toast(f.message||"Failed to save entry.",{type:"error"}),p.disabled=!1}})}function Z(e){const r=document.createElement("div"),a=g?.fields||[],o=document.createElement("div");o.style.cssText="display:flex;flex-direction:column;gap:.75rem;margin-bottom:1.25rem;",a.forEach(n=>{const i=e.data?.[n.name];if(i==null||i==="")return;const l=document.createElement("div");l.style.cssText="border-bottom:1px solid var(--dm-border,#333);padding-bottom:.6rem;";const d=document.createElement("strong");d.textContent=n.label||n.name,d.style.cssText="display:block;font-size:.8rem;color:var(--dm-text-muted,#888);margin-bottom:.2rem;";const s=document.createElement("p");s.textContent=String(i),s.style.cssText="margin:0;word-break:break-word;",l.appendChild(d),l.appendChild(s),o.appendChild(l)});const c=document.createElement("div");if(c.style.cssText="font-size:.8rem;color:var(--dm-text-muted,#888);display:flex;flex-direction:column;gap:.3rem;border-top:1px solid var(--dm-border,#333);padding-top:.75rem;",e.meta?.createdAt){const n=document.createElement("span");n.textContent=`Created: ${D(e.meta.createdAt).format("DD MMM YYYY HH:mm")}`,c.appendChild(n)}if(e.meta?.source){const n=document.createElement("span");n.textContent=`Source: ${e.meta.source}`,c.appendChild(n)}r.appendChild(o),r.appendChild(c);const t=E.slideover({title:"Entry Details",size:"md",position:"right"});t.element.appendChild(r),t.open()}function Q(e){const r=E.slideover({title:"Import Entries",size:"md",position:"right"}),a=document.createElement("div");a.style.cssText="padding:.25rem 0 .5rem;display:flex;flex-direction:column;gap:.75rem;";const o=document.createElement("p");o.textContent='Paste a JSON array of entries. Each item should have a "data" object with field values.',o.style.cssText="font-size:.875rem;color:var(--dm-text-muted,#888);margin:0;",a.appendChild(o);const c=document.createElement("textarea");c.className="form-input",c.rows=10,c.placeholder='[{"data": {"name": "Example"}}, ...]',a.appendChild(c);const t=document.createElement("p");t.style.cssText="font-size:.875rem;margin:0;display:none;",a.appendChild(t);const n=document.createElement("div");n.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;";const i=document.createElement("button");i.className="btn btn-ghost",i.textContent="Cancel";const l=document.createElement("button");l.className="btn btn-primary",l.textContent="Import",n.appendChild(i),n.appendChild(l),a.appendChild(n),r.element.appendChild(a),r.open(),i.addEventListener("click",()=>r.close()),l.addEventListener("click",async()=>{let d;try{if(d=JSON.parse(c.value),!Array.isArray(d))throw new Error("Must be a JSON array")}catch(s){t.style.display="",t.style.color="var(--dm-danger,#f87171)",t.textContent=`Invalid JSON: ${s.message}`;return}l.disabled=!0;try{const s=await h.collections.import(y,d);t.style.display="",t.style.color="var(--dm-success,#4ade80)",t.textContent=`Imported ${s.imported} entries. Skipped: ${s.skipped}.`,await M(e||{find:()=>({get:()=>null,off:()=>({on:()=>{}})})}),setTimeout(()=>r.close(),1500)}catch(s){t.style.display="",t.style.color="var(--dm-danger,#f87171)",t.textContent=s.message||"Import failed."}finally{l.disabled=!1}})}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{apiRequest as I}from"/admin/js/api.js";import{makeIconInput as
|
|
2
|
-
`).filter(b=>b.trim()).map(b=>{const[v,...C]=b.split(":");return{value:v.trim(),label:C.join(":").trim()||v.trim()}}))}if(
|
|
3
|
-
`).filter(y=>y.trim()).map(y=>{const[h,...b]=y.split(":");return{value:h.trim(),label:b.join(":").trim()||h.trim()}});n.cascade={sourceField:u,mapping:m,defaultOptions:f},o=!0}}return o?n:void 0}function $(){g=g.map((e,t)=>he(t))}let B=null;function ge(e,t,s){const n=e.firstElementChild;if(!n)return;const o=document.createElement("span");o.setAttribute("data-icon","menu"),o.setAttribute("data-icon-size","13"),o.title="Drag to reorder",o.style.cssText="cursor:grab;opacity:.35;flex-shrink:0;display:inline-flex;align-items:center;",n.insertBefore(o,n.firstChild),window.I?.scan?.(n),n.draggable=!0,n.addEventListener("dragstart",l=>{B=t,l.dataTransfer.effectAllowed="move",e.style.opacity="0.4"}),n.addEventListener("dragend",()=>{e.style.opacity="",document.querySelectorAll("#fields-list .fb-field-card").forEach(l=>l.classList.remove("fb-drag-over"))}),e.addEventListener("dragover",l=>{B!=null&&(l.preventDefault(),l.dataTransfer.dropEffect="move",document.querySelectorAll("#fields-list .fb-field-card").forEach(i=>i.classList.remove("fb-drag-over")),e.classList.add("fb-drag-over"))}),e.addEventListener("dragleave",()=>e.classList.remove("fb-drag-over")),e.addEventListener("drop",l=>{if(l.preventDefault(),e.classList.remove("fb-drag-over"),B==null||B===t){B=null;return}$();const[i]=g.splice(B,1);g.splice(t,0,i),B=null,L(s)})}function L(e){const t=e.find("#fields-list").get(0),s=e.find("#fields-empty-msg").get(0);if(t){if(Array.from(t.querySelectorAll(".fb-field-card")).forEach(n=>n.remove()),g.length===0){s&&(s.style.display=""),Y();return}s&&(s.style.display="none"),g.forEach((n,o)=>{const l=n.type==="page-break"?ye(n,o,e):n.type==="spacer"?ve(n,o,e):xe(n,o,e);ge(l,o,e),t.appendChild(l)}),Y()}}function ye(e,t,s){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:2px dashed var(--dm-border,#444);border-radius:8px;overflow:hidden;margin-bottom:.75rem;background:var(--dm-card-bg,rgba(255,255,255,.02));";const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.6rem .8rem;cursor:pointer;user-select:none;";const l=document.createElement("span");l.textContent="\u2014 Page Break \u2014",l.style.cssText="font-size:.7rem;padding:.15rem .5rem;border-radius:999px;background:color-mix(in srgb, var(--dm-text-muted) 20%, transparent);color:var(--dm-text-muted,#888);white-space:nowrap;flex-shrink:0;font-style:italic;";const i=document.createElement("span");i.textContent=e.label||"Untitled Step",i.style.cssText="flex:1;font-weight:600;font-size:.9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dm-text-muted,#888);";const d=document.createElement("div");d.style.cssText="display:flex;gap:.25rem;flex-shrink:0;margin-left:.5rem;";const r=document.createElement("button");r.className="btn btn-xs btn-ghost",r.title="Edit step",w(r,"more-horizontal"),r.addEventListener("click",m=>{m.stopPropagation(),p.style.display=p.style.display==="none"?"":"none"}),d.appendChild(r);const a=document.createElement("button");a.className="btn btn-xs btn-ghost",a.style.cssText="line-height:1;opacity:.6;",a.title="Remove page break",w(a,"x"),a.addEventListener("click",async m=>{m.stopPropagation(),await E.confirm("Remove this page break?")&&($(),g.splice(t,1),L(s))}),d.appendChild(a),o.appendChild(l),o.appendChild(i),o.appendChild(d),o.addEventListener("click",()=>{p.style.display=p.style.display==="none"?"":"none"});const p=document.createElement("div");p.className="fb-field-body",p.style.cssText="padding:.8rem;border-top:1px dashed var(--dm-border,#444);display:none;";const c=k([S("Step Title",`fb-pb-label-${t}`,"text",e.label||"","Shown as the wizard step heading"),S("Step Description",`fb-pb-desc-${t}`,"text",e.description||"","Optional sub-heading")]),u=c.querySelector(`#fb-pb-label-${t}`);return u&&u.addEventListener("input",()=>{i.textContent=u.value||"Untitled Step"}),p.appendChild(c),n.appendChild(o),n.appendChild(p),n}function ve(e,t,s){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:1px dashed var(--dm-border,#444);border-radius:6px;margin-bottom:.5rem;background:transparent;";const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.4rem .8rem;";const l=document.createElement("div");l.style.cssText="flex:1;height:1px;background:var(--dm-border,#444);";const i=document.createElement("span");i.textContent="Spacer",i.style.cssText="font-size:.7rem;color:var(--dm-text-muted,#888);white-space:nowrap;padding:0 .4rem;font-style:italic;";const d=document.createElement("div");d.style.cssText="flex:1;height:1px;background:var(--dm-border,#444);";const r=document.createElement("div");r.style.cssText="display:flex;gap:.25rem;flex-shrink:0;";const a=document.createElement("button");return a.className="btn btn-xs btn-ghost",a.style.cssText="line-height:1;opacity:.6;",a.title="Remove spacer",w(a,"x"),a.addEventListener("click",async p=>{p.stopPropagation(),$(),g.splice(t,1),L(s)}),r.appendChild(a),o.appendChild(l),o.appendChild(i),o.appendChild(d),o.appendChild(r),n.appendChild(o),n}function xe(e,t,s){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:1px solid var(--dm-border,#333);border-radius:8px;overflow:hidden;margin-bottom:.75rem;background:var(--dm-card-bg);box-shadow:var(--dm-shadow-sm);";const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.65rem .85rem;background:var(--dm-card-header-bg,rgba(255,255,255,.04));cursor:pointer;user-select:none;";const l=document.createElement("span");l.textContent=ne(e.type),l.style.cssText="font-size:.7rem;padding:.15rem .45rem;border-radius:999px;background:color-mix(in srgb, var(--dm-primary) 14%, transparent);color:var(--dm-primary,#6366f1);white-space:nowrap;flex-shrink:0;";const i=document.createElement("span");i.textContent=e.label||"(unlabelled)",i.style.cssText="flex:1;font-weight:600;font-size:.9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;";const d=document.createElement("div");d.style.cssText="display:flex;gap:.25rem;flex-shrink:0;margin-left:.5rem;";const r=document.createElement("button");r.className="btn btn-xs btn-ghost",r.title="Edit field",w(r,"more-horizontal"),r.addEventListener("click",c=>{c.stopPropagation(),p.style.display=p.style.display==="none"?"":"none"}),d.appendChild(r);const a=document.createElement("button");if(a.className="btn btn-xs btn-ghost",a.style.cssText="line-height:1;opacity:.6;",a.title="Remove field",w(a,"x"),a.addEventListener("click",async c=>{c.stopPropagation(),await E.confirm("Remove this field?")&&($(),g.splice(t,1),L(s))}),d.appendChild(a),o.appendChild(l),o.appendChild(i),e.required){const c=document.createElement("span");c.textContent="required",c.style.cssText="font-size:.7rem;color:var(--dm-danger,#ef4444);flex-shrink:0;",o.appendChild(c)}if(le(e.logic)){const c=document.createElement("span");w(c,"zap"),c.title="Has conditional logic",c.style.cssText="font-size:.75rem;color:var(--dm-primary,#6366f1);flex-shrink:0;",o.appendChild(c)}o.appendChild(d),o.addEventListener("click",()=>{p.style.display=p.style.display==="none"?"":"none"});const p=Ce(e,t,i);return p.style.display="none",n.appendChild(o),n.appendChild(p),n}function Ce(e,t,s){const n=document.createElement("div");n.className="fb-field-body",n.style.cssText="padding:1rem .9rem;border-top:1px solid var(--dm-border,#333);";const o=k([S("Label",`fb-label-${t}`,"text",e.label||"","Shown above the field"),S("Field Name",`fb-name-${t}`,"text",e.name||"","Used as data key")]),l=k([Ie("Type",`fb-type-${t}`,Z,e.type||"string"),de("Required",`fb-required-${t}`,e.required||!1)]),i=k([S("Placeholder",`fb-placeholder-${t}`,"text",e.placeholder||"","Hint text inside the field")]),d=k([S("Helper Text",`fb-helper-${t}`,"text",e.helper||"","Shown below the field"),S("Tooltip",`fb-tooltip-${t}`,"text",e.tooltip||"","Shown on hover via a help icon next to the label")]),r=e.formConfig?.span,a=k([S("Column Span",`fb-span-${t}`,"number",r&&r!=="full"?String(r):"1","Columns to span (grid only)"),de("Full Width",`fb-fullwidth-${t}`,r==="full")]);a.classList.add("fb-grid-row"),a.style.display=document.getElementById("setting-layout")?.value==="grid"?"flex":"none",n.appendChild(o),n.appendChild(l),n.appendChild(i),n.appendChild(d),n.appendChild(a);const p=n.querySelector(`#fb-label-${t}`),c=n.querySelector(`#fb-name-${t}`);p&&p.addEventListener("input",()=>{s&&(s.textContent=p.value||"(unlabelled)"),c&&!c.dataset.manuallyEdited&&(c.value=te(p.value))}),c&&c.addEventListener("input",()=>{c.dataset.manuallyEdited="1"});const u=n.querySelector(`#fb-type-${t}`);u&&u.addEventListener("change",()=>{const f=n.closest(".fb-field-card");if(f){const b=f.querySelector("span");b&&(b.textContent=ne(u.value))}const y=n.querySelector(".fb-field-extras");y&&y.remove();const h=se(u.value,e,t);h&&n.appendChild(h)});const m=se(e.type,e,t);return m&&n.appendChild(m),n.appendChild(Ne(e,t)),n}const Ee=[{value:"equals",label:"equals"},{value:"not_equals",label:"does not equal"},{value:"contains",label:"contains"},{value:"not_contains",label:"does not contain"},{value:"starts_with",label:"starts with"},{value:"ends_with",label:"ends with"},{value:"greater_than",label:"is greater than"},{value:"less_than",label:"is less than"},{value:"is_empty",label:"is empty"},{value:"is_not_empty",label:"is not empty"},{value:"in",label:"is one of (comma sep)"},{value:"not_in",label:"is not one of (comma sep)"},{value:"matches_regex",label:"matches regex"}],oe=new Set(["is_empty","is_not_empty"]);function H(e){const t=document.createElement("p");return t.textContent=e,t.style.cssText="font-size:.72rem;font-weight:700;color:var(--dm-text-muted,#888);margin:1rem 0 .45rem;text-transform:uppercase;letter-spacing:.06em;",t}function V(e,t,s,n,o){const l=document.createElement("div");l.className="fb-logic-cond-row",l.style.cssText="display:flex;gap:.35rem;align-items:center;margin-bottom:.35rem;flex-wrap:wrap;";const i=document.createElement("span");i.textContent="When",i.style.cssText="font-size:.73rem;color:var(--dm-text-muted,#888);flex-shrink:0;";const d=document.createElement("select");d.className="form-select fb-logic-cond-field",d.style.cssText="flex:1 1 140px;min-width:140px;",t.forEach(m=>{const f=document.createElement("option");f.value=m.value,f.textContent=m.label,e&&m.value===e.field&&(f.selected=!0),d.appendChild(f)});const r=document.createElement("select");r.className="form-select fb-logic-cond-op",r.style.cssText="flex:1 1 160px;min-width:140px;",Ee.forEach(m=>{const f=document.createElement("option");f.value=m.value,f.textContent=m.label,e&&m.value===e.operator&&(f.selected=!0),r.appendChild(f)});const a=document.createElement("input");a.type="text",a.className="form-input fb-logic-cond-val",a.placeholder="value",a.style.cssText="flex:1 1 120px;min-width:100px;",a.value=e?.value||"",e&&oe.has(e.operator)&&(a.style.display="none"),r.addEventListener("change",()=>{a.style.display=oe.has(r.value)?"none":""});const p=document.createElement("span");w(p,"arrow-right"),p.style.cssText="font-size:.73rem;color:var(--dm-text-muted,#888);flex-shrink:0;";const c=document.createElement("select");c.className=`form-select ${n}`,c.style.cssText="flex:1 1 140px;min-width:120px;",s.forEach(m=>{const f=document.createElement("option");f.value=m.value,f.textContent=m.label,m.value===o&&(f.selected=!0),c.appendChild(f)});const u=document.createElement("button");return u.type="button",u.className="btn btn-xs btn-ghost",u.style.cssText="line-height:1;opacity:.6;",w(u,"x"),u.style.flexShrink="0",u.addEventListener("click",()=>l.remove()),l.appendChild(i),l.appendChild(d),l.appendChild(r),l.appendChild(a),l.appendChild(p),l.appendChild(c),l.appendChild(u),l}function we(e,t,s){const n=document.createElement("div");n.dataset.logicSection="visibility",n.appendChild(H("Visibility"));const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem;";const l=document.createElement("span");l.textContent="Default:",l.style.cssText="font-size:.8rem;flex-shrink:0;";const i=document.createElement("select");i.className="form-select fb-logic-vis-default",i.style.cssText="max-width:240px;",[{value:"visible",label:"Visible"},{value:"hidden",label:"Hidden"}].forEach(m=>{const f=document.createElement("option");f.value=m.value,f.textContent=m.label,m.value===(e.default||"visible")&&(f.selected=!0),i.appendChild(f)}),o.appendChild(l),o.appendChild(i),n.appendChild(o);const d=document.createElement("div");d.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem;";const r=document.createElement("span");r.textContent="Transition:",r.style.cssText="font-size:.8rem;flex-shrink:0;";const a=document.createElement("select");a.className="form-select fb-logic-vis-transition",a.style.cssText="max-width:240px;",[{value:"none",label:"None (instant)"},{value:"fade",label:"Fade"},{value:"slide",label:"Slide"},{value:"scale",label:"Scale"}].forEach(m=>{const f=document.createElement("option");f.value=m.value,f.textContent=m.label,m.value===(e.transition||"none")&&(f.selected=!0),a.appendChild(f)}),d.appendChild(r),d.appendChild(a),n.appendChild(d);const p=document.createElement("div");p.className="fb-logic-vis-rules";const c=s.map(m=>({value:m.name,label:m.label||m.name})),u=[{value:"visible",label:"Show"},{value:"hidden",label:"Hide"}];if((e.conditions||[]).forEach(m=>{const f=(m.when?.all||m.when?.any||[])[0],y=m.then==="hidden"?"hidden":"visible";c.length>0&&p.appendChild(V(f,c,u,"fb-logic-vis-then",y))}),n.appendChild(p),c.length>0){const m=document.createElement("button");m.type="button",m.className="btn btn-xs btn-ghost",m.style.cssText="font-size:.73rem;margin-top:.2rem;",D(m,"plus","Add visibility rule"),m.addEventListener("click",()=>p.appendChild(V(null,c,u,"fb-logic-vis-then","visible"))),n.appendChild(m)}return n}function ke(e,t,s){const n=document.createElement("div");n.dataset.logicSection="requirement",n.appendChild(H("Conditional Requirement"));const o=document.createElement("label");o.style.cssText="display:flex;align-items:center;gap:.4rem;font-size:.8rem;cursor:pointer;margin-bottom:.4rem;";const l=document.createElement("input");l.type="checkbox",l.className="fb-logic-req-default",l.checked=e.default===!0,o.appendChild(l),o.appendChild(document.createTextNode("Required by default")),n.appendChild(o);const i=document.createElement("div");i.className="fb-logic-req-rules";const d=s.map(a=>({value:a.name,label:a.label||a.name})),r=[{value:"true",label:"Make required"},{value:"false",label:"Make optional"}];if((e.conditions||[]).forEach(a=>{const p=(a.when?.all||a.when?.any||[])[0],c=a.then===!0?"true":"false";d.length>0&&i.appendChild(V(p,d,r,"fb-logic-req-then",c))}),n.appendChild(i),d.length>0){const a=document.createElement("button");a.type="button",a.className="btn btn-xs btn-ghost",a.style.cssText="font-size:.73rem;margin-top:.2rem;",D(a,"plus","Add requirement rule"),a.addEventListener("click",()=>i.appendChild(V(null,d,r,"fb-logic-req-then","true"))),n.appendChild(a)}return n}function ae(e){const t=document.createElement("div");t.className="fb-logic-val-rule",t.style.cssText="display:flex;gap:.35rem;align-items:center;margin-bottom:.35rem;flex-wrap:wrap;";const s=document.createElement("select");s.className="form-select fb-logic-val-type",s.style.cssText="flex:0 0 140px;",[{value:"regex",label:"Regex"},{value:"match",label:"Match field"}].forEach(d=>{const r=document.createElement("option");r.value=d.value,r.textContent=d.label,d.value===(e?.type||"regex")&&(r.selected=!0),s.appendChild(r)});const n=document.createElement("input");n.type="text",n.className="form-input fb-logic-val-pattern",n.placeholder=e?.type==="match"?"field name":"pattern",n.value=e?.pattern||e?.field||"",n.style.cssText="flex:3;";const o=document.createElement("input");o.type="text",o.className="form-input fb-logic-val-flags",o.placeholder="flags",o.value=e?.flags||"",o.style.cssText="flex:0 0 55px;",e?.type==="match"&&(o.style.display="none"),s.addEventListener("change",()=>{o.style.display=s.value==="match"?"none":"",n.placeholder=s.value==="match"?"field name":"pattern"});const l=document.createElement("input");l.type="text",l.className="form-input fb-logic-val-message",l.placeholder="Error message",l.value=e?.message||"",l.style.cssText="flex:4;";const i=document.createElement("button");return i.type="button",i.className="btn btn-xs btn-ghost",i.style.cssText="line-height:1;opacity:.6;",w(i,"x"),i.style.flexShrink="0",i.addEventListener("click",()=>t.remove()),t.appendChild(s),t.appendChild(n),t.appendChild(o),t.appendChild(l),t.appendChild(i),t}function Se(e,t,s){const n=document.createElement("div");n.dataset.logicSection="validation",n.appendChild(H("Custom Validation"));const o=document.createElement("div");o.className="fb-logic-val-rules",(e||[]).forEach(i=>o.appendChild(ae(i))),n.appendChild(o);const l=document.createElement("button");return l.type="button",l.className="btn btn-xs btn-ghost",l.style.cssText="font-size:.73rem;margin-top:.2rem;",D(l,"plus","Add validation rule"),l.addEventListener("click",()=>o.appendChild(ae(null))),n.appendChild(l),n}function Te(e,t,s){const n=document.createElement("div");n.dataset.logicSection="cascade",n.appendChild(H("Cascade Options")),n.lastChild.appendChild(Q("Make this field's options depend on another field. When the source field changes, the options here are replaced by the mapping entry for its value; if nothing matches, the default options below are shown."));const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.5rem;";const l=document.createElement("span");l.textContent="Source field:",l.style.cssText="font-size:.8rem;flex-shrink:0;";const i=document.createElement("select");i.className="form-select fb-logic-cascade-source",i.style.cssText="flex:1;";const d=document.createElement("option");d.value="",d.textContent="\u2014 none \u2014",i.appendChild(d),s.forEach(u=>{const m=document.createElement("option");m.value=u.name,m.textContent=u.label||u.name,u.name===e.sourceField&&(m.selected=!0),i.appendChild(m)}),o.appendChild(l),o.appendChild(i),n.appendChild(o);const r=document.createElement("p");r.textContent='Mapping JSON \u2014 {"value":[{"value":"...","label":"..."}]}',r.appendChild(Q(`Each key is a value of the source field; its list becomes this field's options when that value is picked. Example: {"uk":[{"value":"london","label":"London"}]} shows London when the source is "uk".`)),r.style.cssText="font-size:.73rem;color:var(--dm-text-muted,#888);margin:.3rem 0 .2rem;";const a=document.createElement("textarea");a.className="form-input fb-logic-cascade-mapping",a.rows=4,a.style.cssText="font-family:monospace;",a.placeholder='{"uk": [{"value": "london", "label": "London"}]}',a.value=e.mapping?JSON.stringify(e.mapping,null,2):"",n.appendChild(r),n.appendChild(a);const p=document.createElement("p");p.textContent="Default options (one per line: value:Label)",p.appendChild(Q("Shown before the source field has a value, or when its value has no mapping entry.")),p.style.cssText="font-size:.73rem;color:var(--dm-text-muted,#888);margin:.3rem 0 .2rem;";const c=document.createElement("textarea");return c.className="form-input fb-logic-cascade-defaults",c.rows=3,c.style.cssText="font-family:monospace;",c.placeholder=`option1:Option 1
|
|
1
|
+
import{apiRequest as I}from"/admin/js/api.js";import{makeIconInput as he}from"/admin/js/lib/shortcode-modal.js";import{enhancePickerFields as Z}from"/public/js/forms.js";const ee=[{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:"chooser",label:"Chooser (visual options)"},{value:"icon",label:"Icon (icon picker)"},{value:"image",label:"Image (media library)"},{value:"date",label:"Date"},{value:"time",label:"Time"},{value:"url",label:"URL"},{value:"password",label:"Password"},{value:"file",label:"File upload"},{value:"hidden",label:"Hidden field"}],W=new Set(["select","radio","checkbox-group","chooser"]);let g=[],q=null,P=null,M=null,G={};function w(e,n,i=14){e.textContent="";const t=document.createElement("span");return t.setAttribute("data-icon",n),t.setAttribute("data-icon-size",String(i)),t.style.cssText="display:inline-flex;align-items:center;vertical-align:middle;",e.appendChild(t),window.I?.scan?.(e),e}function D(e,n,i,t=14){return w(e,n,t),e.appendChild(document.createTextNode(" "+i)),e}function Q(e){const n=document.createElement("span");return n.setAttribute("data-icon","help-circle"),n.setAttribute("data-icon-size","12"),n.style.cssText="display:inline-flex;align-items:center;vertical-align:middle;margin-left:.3rem;opacity:.55;cursor:help;",n.title=e,window.I?.scan?.(n),requestAnimationFrame(()=>{try{n.parentNode&&typeof E<"u"&&E.tooltip&&(n.removeAttribute("title"),E.tooltip(n,{content:e,position:"top"}))}catch{}}),n}let _=null,te=null;function Y(){clearTimeout(te),te=setTimeout(()=>{_&&_()},350)}function ne(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_|_$/g,"")}function le(e){return ee.find(n=>n.value===e)?.label||e}function oe(e){return e?!!(e.visibility?.conditions?.length||e.requirement?.conditions?.length||e.validation?.length||e.cascade?.sourceField):!1}function be(e){const n={...g[e]};if(n.type==="spacer")return n;if(n.type==="page-break"){const h=document.getElementById(`fb-pb-label-${e}`),b=document.getElementById(`fb-pb-desc-${e}`);return h&&(n.label=h.value.trim()||n.label),b&&(n.description=b.value.trim()),n}const i=document.getElementById(`fb-label-${e}`),t=document.getElementById(`fb-name-${e}`),o=document.getElementById(`fb-type-${e}`),l=document.getElementById(`fb-required-${e}`),s=document.getElementById(`fb-placeholder-${e}`),d=document.getElementById(`fb-helper-${e}`),r=document.getElementById(`fb-tooltip-${e}`);if(i&&(n.label=i.value.trim()||n.label),t&&(n.name=t.value.trim()||n.name),o&&(n.type=o.value||n.type),l&&(n.required=l.checked),s&&(n.placeholder=s.value.trim()),d&&(n.helper=d.value.trim()),r&&(n.tooltip=r.value.trim()),n.type==="chooser"){const h=document.getElementById(`fb-chooser-variant-${e}`)?.value;h&&(n.variant=h);const b=document.getElementById(`fb-chooser-multiple-${e}`);b&&(n.multiple=b.checked);const v=document.getElementById(`fb-chooser-density-${e}`)?.value;v&&(n.density=v);const C=document.getElementById(`fb-chooser-columns-${e}`)?.value;C!==""&&C!==void 0&&(n.columns=parseInt(C,10)||3);const x=document.getElementById(`fb-chooser-accent-${e}`)?.value.trim();x?n.accent=x:delete n.accent;const T=document.getElementById(`fb-chooser-accent-style-${e}`)?.value;T?n.accentStyle=T:delete n.accentStyle;const z=document.getElementById(`fb-chooser-glow-${e}`);z&&(n.glow=z.checked);const A=document.getElementById(`fb-chooser-glow-colour-${e}`)?.value.trim();A?n.glowColour=A:delete n.glowColour;const N=document.getElementById(`fb-chooser-shadow-${e}`)?.value;N&&N!=="none"?n.shadow=N:delete n.shadow;const O=document.getElementById(`fb-chooser-shadow-colour-${e}`)?.value.trim();O?n.shadowColour=O:delete n.shadowColour,J(e),g[e]&&Array.isArray(g[e].options)&&(n.options=g[e].options)}else if(W.has(n.type)){const h=document.getElementById(`fb-options-${e}`);h&&(n.options=h.value.split(`
|
|
2
|
+
`).filter(b=>b.trim()).map(b=>{const[v,...C]=b.split(":");return{value:v.trim(),label:C.join(":").trim()||v.trim()}}))}if(n.type==="textarea"){const h=parseInt(document.getElementById(`fb-rows-${e}`)?.value,10);h>0&&(n.formConfig={...n.formConfig||{},rows:h})}const a=document.getElementById(`fb-span-${e}`),p=document.getElementById(`fb-fullwidth-${e}`);if(a||p){const h={...n.formConfig||{}};if(p?.checked)h.span="full";else{const b=parseInt(a?.value,10);b>1?h.span=b:delete h.span}n.formConfig=Object.keys(h).length?h:void 0}const c=document.getElementById(`fb-minlength-${e}`)?.value;c&&(n.minLength=parseInt(c,10));const u=document.getElementById(`fb-maxlength-${e}`)?.value;u&&(n.maxLength=parseInt(u,10));const m=document.getElementById(`fb-min-${e}`)?.value;m!==""&&m!==void 0&&(n.min=parseFloat(m));const f=document.getElementById(`fb-max-${e}`)?.value;f!==""&&f!==void 0&&(n.max=parseFloat(f));const y=ge(e);return y?n.logic=y:delete n.logic,n}function ge(e){const n=document.querySelector(`.fb-field-card[data-index="${e}"]`);if(!n)return;const i=n.querySelector(".fb-field-logic");if(!i)return;const t={};let o=!1;const l=i.querySelector('[data-logic-section="visibility"]');if(l){const a=l.querySelector(".fb-logic-vis-default"),p=l.querySelector(".fb-logic-vis-transition"),c=Array.from(l.querySelectorAll(".fb-logic-cond-row")).map(f=>{const y=f.querySelector(".fb-logic-cond-field"),h=f.querySelector(".fb-logic-cond-op"),b=f.querySelector(".fb-logic-cond-val"),v=f.querySelector(".fb-logic-vis-then");return y?.value?{when:{all:[{field:y.value,operator:h.value,value:b.value}]},then:v.value}:null}).filter(Boolean),u=a?.value||"visible",m=p?.value||"none";(u!=="visible"||c.length>0||m!=="none")&&(t.visibility={default:u,conditions:c},m!=="none"&&(t.visibility.transition=m),o=!0)}const s=i.querySelector('[data-logic-section="requirement"]');if(s){const a=s.querySelector(".fb-logic-req-default"),p=Array.from(s.querySelectorAll(".fb-logic-cond-row")).map(c=>{const u=c.querySelector(".fb-logic-cond-field"),m=c.querySelector(".fb-logic-cond-op"),f=c.querySelector(".fb-logic-cond-val"),y=c.querySelector(".fb-logic-req-then");return u?.value?{when:{all:[{field:u.value,operator:m.value,value:f.value}]},then:y.value==="true"}:null}).filter(Boolean);p.length>0&&(t.requirement={default:a?.checked===!0,conditions:p},o=!0)}const d=i.querySelector('[data-logic-section="validation"]');if(d){const a=Array.from(d.querySelectorAll(".fb-logic-val-rule")).map(p=>{const c=p.querySelector(".fb-logic-val-type"),u=p.querySelector(".fb-logic-val-pattern"),m=p.querySelector(".fb-logic-val-flags"),f=p.querySelector(".fb-logic-val-message");if(!u?.value.trim())return null;const y=c?.value||"regex",h={type:y,message:f?.value.trim()||"Invalid value."};return y==="regex"?(h.pattern=u.value.trim(),m?.value.trim()&&(h.flags=m.value.trim())):h.field=u.value.trim(),h}).filter(Boolean);a.length>0&&(t.validation=a,o=!0)}const r=i.querySelector('[data-logic-section="cascade"]');if(r){const a=r.querySelector(".fb-logic-cascade-source"),p=r.querySelector(".fb-logic-cascade-mapping"),c=r.querySelector(".fb-logic-cascade-defaults"),u=a?.value?.trim();if(u){let m={};try{m=JSON.parse(p?.value||"{}")}catch{}const f=(c?.value||"").split(`
|
|
3
|
+
`).filter(y=>y.trim()).map(y=>{const[h,...b]=y.split(":");return{value:h.trim(),label:b.join(":").trim()||h.trim()}});t.cascade={sourceField:u,mapping:m,defaultOptions:f},o=!0}}return o?t:void 0}function $(){g=g.map((e,n)=>be(n))}let B=null;function ye(e,n,i){const t=e.firstElementChild;if(!t)return;const o=document.createElement("span");o.setAttribute("data-icon","menu"),o.setAttribute("data-icon-size","13"),o.title="Drag to reorder",o.style.cssText="cursor:grab;opacity:.35;flex-shrink:0;display:inline-flex;align-items:center;",t.insertBefore(o,t.firstChild),window.I?.scan?.(t),t.draggable=!0,t.addEventListener("dragstart",l=>{B=n,l.dataTransfer.effectAllowed="move",e.style.opacity="0.4"}),t.addEventListener("dragend",()=>{e.style.opacity="",document.querySelectorAll("#fields-list .fb-field-card").forEach(l=>l.classList.remove("fb-drag-over"))}),e.addEventListener("dragover",l=>{B!=null&&(l.preventDefault(),l.dataTransfer.dropEffect="move",document.querySelectorAll("#fields-list .fb-field-card").forEach(s=>s.classList.remove("fb-drag-over")),e.classList.add("fb-drag-over"))}),e.addEventListener("dragleave",()=>e.classList.remove("fb-drag-over")),e.addEventListener("drop",l=>{if(l.preventDefault(),e.classList.remove("fb-drag-over"),B==null||B===n){B=null;return}$();const[s]=g.splice(B,1);g.splice(n,0,s),B=null,L(i)})}function L(e){const n=e.find("#fields-list").get(0),i=e.find("#fields-empty-msg").get(0);if(n){if(Array.from(n.querySelectorAll(".fb-field-card")).forEach(t=>t.remove()),g.length===0){i&&(i.style.display=""),Y();return}i&&(i.style.display="none"),g.forEach((t,o)=>{const l=t.type==="page-break"?ve(t,o,e):t.type==="spacer"?xe(t,o,e):Ce(t,o,e);ye(l,o,e),n.appendChild(l)}),Y()}}function ve(e,n,i){const t=document.createElement("div");t.className="fb-field-card",t.dataset.index=n,t.style.cssText="border:2px dashed var(--dm-border,#444);border-radius:8px;overflow:hidden;margin-bottom:.75rem;background:var(--dm-card-bg,rgba(255,255,255,.02));";const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.6rem .8rem;cursor:pointer;user-select:none;";const l=document.createElement("span");l.textContent="\u2014 Page Break \u2014",l.style.cssText="font-size:.7rem;padding:.15rem .5rem;border-radius:999px;background:color-mix(in srgb, var(--dm-text-muted) 20%, transparent);color:var(--dm-text-muted,#888);white-space:nowrap;flex-shrink:0;font-style:italic;";const s=document.createElement("span");s.textContent=e.label||"Untitled Step",s.style.cssText="flex:1;font-weight:600;font-size:.9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dm-text-muted,#888);";const d=document.createElement("div");d.style.cssText="display:flex;gap:.25rem;flex-shrink:0;margin-left:.5rem;";const r=document.createElement("button");r.className="btn btn-xs btn-ghost",r.title="Edit step",w(r,"more-horizontal"),r.addEventListener("click",m=>{m.stopPropagation(),p.style.display=p.style.display==="none"?"":"none"}),d.appendChild(r);const a=document.createElement("button");a.className="btn btn-xs btn-ghost",a.style.cssText="line-height:1;opacity:.6;",a.title="Remove page break",w(a,"x"),a.addEventListener("click",async m=>{m.stopPropagation(),await E.confirm("Remove this page break?")&&($(),g.splice(n,1),L(i))}),d.appendChild(a),o.appendChild(l),o.appendChild(s),o.appendChild(d),o.addEventListener("click",()=>{p.style.display=p.style.display==="none"?"":"none"});const p=document.createElement("div");p.className="fb-field-body",p.style.cssText="padding:.8rem;border-top:1px dashed var(--dm-border,#444);display:none;";const c=k([S("Step Title",`fb-pb-label-${n}`,"text",e.label||"","Shown as the wizard step heading"),S("Step Description",`fb-pb-desc-${n}`,"text",e.description||"","Optional sub-heading")]),u=c.querySelector(`#fb-pb-label-${n}`);return u&&u.addEventListener("input",()=>{s.textContent=u.value||"Untitled Step"}),p.appendChild(c),t.appendChild(o),t.appendChild(p),t}function xe(e,n,i){const t=document.createElement("div");t.className="fb-field-card",t.dataset.index=n,t.style.cssText="border:1px dashed var(--dm-border,#444);border-radius:6px;margin-bottom:.5rem;background:transparent;";const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.4rem .8rem;";const l=document.createElement("div");l.style.cssText="flex:1;height:1px;background:var(--dm-border,#444);";const s=document.createElement("span");s.textContent="Spacer",s.style.cssText="font-size:.7rem;color:var(--dm-text-muted,#888);white-space:nowrap;padding:0 .4rem;font-style:italic;";const d=document.createElement("div");d.style.cssText="flex:1;height:1px;background:var(--dm-border,#444);";const r=document.createElement("div");r.style.cssText="display:flex;gap:.25rem;flex-shrink:0;";const a=document.createElement("button");return a.className="btn btn-xs btn-ghost",a.style.cssText="line-height:1;opacity:.6;",a.title="Remove spacer",w(a,"x"),a.addEventListener("click",async p=>{p.stopPropagation(),$(),g.splice(n,1),L(i)}),r.appendChild(a),o.appendChild(l),o.appendChild(s),o.appendChild(d),o.appendChild(r),t.appendChild(o),t}function Ce(e,n,i){const t=document.createElement("div");t.className="fb-field-card",t.dataset.index=n,t.style.cssText="border:1px solid var(--dm-border,#333);border-radius:8px;overflow:hidden;margin-bottom:.75rem;background:var(--dm-card-bg);box-shadow:var(--dm-shadow-sm);";const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.6rem;padding:.65rem .85rem;background:var(--dm-card-header-bg,rgba(255,255,255,.04));cursor:pointer;user-select:none;";const l=document.createElement("span");l.textContent=le(e.type),l.style.cssText="font-size:.7rem;padding:.15rem .45rem;border-radius:999px;background:color-mix(in srgb, var(--dm-primary) 14%, transparent);color:var(--dm-primary,#6366f1);white-space:nowrap;flex-shrink:0;";const s=document.createElement("span");s.textContent=e.label||"(unlabelled)",s.style.cssText="flex:1;font-weight:600;font-size:.9rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;";const d=document.createElement("div");d.style.cssText="display:flex;gap:.25rem;flex-shrink:0;margin-left:.5rem;";const r=document.createElement("button");r.className="btn btn-xs btn-ghost",r.title="Edit field",w(r,"more-horizontal"),r.addEventListener("click",c=>{c.stopPropagation(),p.style.display=p.style.display==="none"?"":"none"}),d.appendChild(r);const a=document.createElement("button");if(a.className="btn btn-xs btn-ghost",a.style.cssText="line-height:1;opacity:.6;",a.title="Remove field",w(a,"x"),a.addEventListener("click",async c=>{c.stopPropagation(),await E.confirm("Remove this field?")&&($(),g.splice(n,1),L(i))}),d.appendChild(a),o.appendChild(l),o.appendChild(s),e.required){const c=document.createElement("span");c.textContent="required",c.style.cssText="font-size:.7rem;color:var(--dm-danger,#ef4444);flex-shrink:0;",o.appendChild(c)}if(oe(e.logic)){const c=document.createElement("span");w(c,"zap"),c.title="Has conditional logic",c.style.cssText="font-size:.75rem;color:var(--dm-primary,#6366f1);flex-shrink:0;",o.appendChild(c)}o.appendChild(d),o.addEventListener("click",()=>{p.style.display=p.style.display==="none"?"":"none"});const p=Ee(e,n,s);return p.style.display="none",t.appendChild(o),t.appendChild(p),t}function Ee(e,n,i){const t=document.createElement("div");t.className="fb-field-body",t.style.cssText="padding:1rem .9rem;border-top:1px solid var(--dm-border,#333);";const o=k([S("Label",`fb-label-${n}`,"text",e.label||"","Shown above the field"),S("Field Name",`fb-name-${n}`,"text",e.name||"","Used as data key")]),l=k([Fe("Type",`fb-type-${n}`,ee,e.type||"string"),pe("Required",`fb-required-${n}`,e.required||!1)]),s=k([S("Placeholder",`fb-placeholder-${n}`,"text",e.placeholder||"","Hint text inside the field")]),d=k([S("Helper Text",`fb-helper-${n}`,"text",e.helper||"","Shown below the field"),S("Tooltip",`fb-tooltip-${n}`,"text",e.tooltip||"","Shown on hover via a help icon next to the label")]),r=e.formConfig?.span,a=k([S("Column Span",`fb-span-${n}`,"number",r&&r!=="full"?String(r):"1","Columns to span (grid only)"),pe("Full Width",`fb-fullwidth-${n}`,r==="full")]);a.classList.add("fb-grid-row"),a.style.display=document.getElementById("setting-layout")?.value==="grid"?"flex":"none",t.appendChild(o),t.appendChild(l),t.appendChild(s),t.appendChild(d),t.appendChild(a);const p=t.querySelector(`#fb-label-${n}`),c=t.querySelector(`#fb-name-${n}`);p&&p.addEventListener("input",()=>{i&&(i.textContent=p.value||"(unlabelled)"),c&&!c.dataset.manuallyEdited&&(c.value=ne(p.value))}),c&&c.addEventListener("input",()=>{c.dataset.manuallyEdited="1"});const u=t.querySelector(`#fb-type-${n}`);u&&u.addEventListener("change",()=>{const f=t.closest(".fb-field-card");if(f){const b=f.querySelector("span");b&&(b.textContent=le(u.value))}const y=t.querySelector(".fb-field-extras");y&&y.remove();const h=se(u.value,e,n);h&&t.appendChild(h)});const m=se(e.type,e,n);return m&&t.appendChild(m),t.appendChild(qe(e,n)),t}const we=[{value:"equals",label:"equals"},{value:"not_equals",label:"does not equal"},{value:"contains",label:"contains"},{value:"not_contains",label:"does not contain"},{value:"starts_with",label:"starts with"},{value:"ends_with",label:"ends with"},{value:"greater_than",label:"is greater than"},{value:"less_than",label:"is less than"},{value:"is_empty",label:"is empty"},{value:"is_not_empty",label:"is not empty"},{value:"in",label:"is one of (comma sep)"},{value:"not_in",label:"is not one of (comma sep)"},{value:"matches_regex",label:"matches regex"}],ae=new Set(["is_empty","is_not_empty"]);function H(e){const n=document.createElement("p");return n.textContent=e,n.style.cssText="font-size:.72rem;font-weight:700;color:var(--dm-text-muted,#888);margin:1rem 0 .45rem;text-transform:uppercase;letter-spacing:.06em;",n}function V(e,n,i,t,o){const l=document.createElement("div");l.className="fb-logic-cond-row",l.style.cssText="display:flex;gap:.35rem;align-items:center;margin-bottom:.35rem;flex-wrap:wrap;";const s=document.createElement("span");s.textContent="When",s.style.cssText="font-size:.73rem;color:var(--dm-text-muted,#888);flex-shrink:0;";const d=document.createElement("select");d.className="form-select fb-logic-cond-field",d.style.cssText="flex:1 1 140px;min-width:140px;",n.forEach(m=>{const f=document.createElement("option");f.value=m.value,f.textContent=m.label,e&&m.value===e.field&&(f.selected=!0),d.appendChild(f)});const r=document.createElement("select");r.className="form-select fb-logic-cond-op",r.style.cssText="flex:1 1 160px;min-width:140px;",we.forEach(m=>{const f=document.createElement("option");f.value=m.value,f.textContent=m.label,e&&m.value===e.operator&&(f.selected=!0),r.appendChild(f)});const a=document.createElement("input");a.type="text",a.className="form-input fb-logic-cond-val",a.placeholder="value",a.style.cssText="flex:1 1 120px;min-width:100px;",a.value=e?.value||"",e&&ae.has(e.operator)&&(a.style.display="none"),r.addEventListener("change",()=>{a.style.display=ae.has(r.value)?"none":""});const p=document.createElement("span");w(p,"arrow-right"),p.style.cssText="font-size:.73rem;color:var(--dm-text-muted,#888);flex-shrink:0;";const c=document.createElement("select");c.className=`form-select ${t}`,c.style.cssText="flex:1 1 140px;min-width:120px;",i.forEach(m=>{const f=document.createElement("option");f.value=m.value,f.textContent=m.label,m.value===o&&(f.selected=!0),c.appendChild(f)});const u=document.createElement("button");return u.type="button",u.className="btn btn-xs btn-ghost",u.style.cssText="line-height:1;opacity:.6;",w(u,"x"),u.style.flexShrink="0",u.addEventListener("click",()=>l.remove()),l.appendChild(s),l.appendChild(d),l.appendChild(r),l.appendChild(a),l.appendChild(p),l.appendChild(c),l.appendChild(u),l}function ke(e,n,i){const t=document.createElement("div");t.dataset.logicSection="visibility",t.appendChild(H("Visibility"));const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem;";const l=document.createElement("span");l.textContent="Default:",l.style.cssText="font-size:.8rem;flex-shrink:0;";const s=document.createElement("select");s.className="form-select fb-logic-vis-default",s.style.cssText="max-width:240px;",[{value:"visible",label:"Visible"},{value:"hidden",label:"Hidden"}].forEach(m=>{const f=document.createElement("option");f.value=m.value,f.textContent=m.label,m.value===(e.default||"visible")&&(f.selected=!0),s.appendChild(f)}),o.appendChild(l),o.appendChild(s),t.appendChild(o);const d=document.createElement("div");d.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem;";const r=document.createElement("span");r.textContent="Transition:",r.style.cssText="font-size:.8rem;flex-shrink:0;";const a=document.createElement("select");a.className="form-select fb-logic-vis-transition",a.style.cssText="max-width:240px;",[{value:"none",label:"None (instant)"},{value:"fade",label:"Fade"},{value:"slide",label:"Slide"},{value:"scale",label:"Scale"}].forEach(m=>{const f=document.createElement("option");f.value=m.value,f.textContent=m.label,m.value===(e.transition||"none")&&(f.selected=!0),a.appendChild(f)}),d.appendChild(r),d.appendChild(a),t.appendChild(d);const p=document.createElement("div");p.className="fb-logic-vis-rules";const c=i.map(m=>({value:m.name,label:m.label||m.name})),u=[{value:"visible",label:"Show"},{value:"hidden",label:"Hide"}];if((e.conditions||[]).forEach(m=>{const f=(m.when?.all||m.when?.any||[])[0],y=m.then==="hidden"?"hidden":"visible";c.length>0&&p.appendChild(V(f,c,u,"fb-logic-vis-then",y))}),t.appendChild(p),c.length>0){const m=document.createElement("button");m.type="button",m.className="btn btn-xs btn-ghost",m.style.cssText="font-size:.73rem;margin-top:.2rem;",D(m,"plus","Add visibility rule"),m.addEventListener("click",()=>p.appendChild(V(null,c,u,"fb-logic-vis-then","visible"))),t.appendChild(m)}return t}function Se(e,n,i){const t=document.createElement("div");t.dataset.logicSection="requirement",t.appendChild(H("Conditional Requirement"));const o=document.createElement("label");o.style.cssText="display:flex;align-items:center;gap:.4rem;font-size:.8rem;cursor:pointer;margin-bottom:.4rem;";const l=document.createElement("input");l.type="checkbox",l.className="fb-logic-req-default",l.checked=e.default===!0,o.appendChild(l),o.appendChild(document.createTextNode("Required by default")),t.appendChild(o);const s=document.createElement("div");s.className="fb-logic-req-rules";const d=i.map(a=>({value:a.name,label:a.label||a.name})),r=[{value:"true",label:"Make required"},{value:"false",label:"Make optional"}];if((e.conditions||[]).forEach(a=>{const p=(a.when?.all||a.when?.any||[])[0],c=a.then===!0?"true":"false";d.length>0&&s.appendChild(V(p,d,r,"fb-logic-req-then",c))}),t.appendChild(s),d.length>0){const a=document.createElement("button");a.type="button",a.className="btn btn-xs btn-ghost",a.style.cssText="font-size:.73rem;margin-top:.2rem;",D(a,"plus","Add requirement rule"),a.addEventListener("click",()=>s.appendChild(V(null,d,r,"fb-logic-req-then","true"))),t.appendChild(a)}return t}function ie(e){const n=document.createElement("div");n.className="fb-logic-val-rule",n.style.cssText="display:flex;gap:.35rem;align-items:center;margin-bottom:.35rem;flex-wrap:wrap;";const i=document.createElement("select");i.className="form-select fb-logic-val-type",i.style.cssText="flex:0 0 140px;",[{value:"regex",label:"Regex"},{value:"match",label:"Match field"}].forEach(d=>{const r=document.createElement("option");r.value=d.value,r.textContent=d.label,d.value===(e?.type||"regex")&&(r.selected=!0),i.appendChild(r)});const t=document.createElement("input");t.type="text",t.className="form-input fb-logic-val-pattern",t.placeholder=e?.type==="match"?"field name":"pattern",t.value=e?.pattern||e?.field||"",t.style.cssText="flex:3;";const o=document.createElement("input");o.type="text",o.className="form-input fb-logic-val-flags",o.placeholder="flags",o.value=e?.flags||"",o.style.cssText="flex:0 0 55px;",e?.type==="match"&&(o.style.display="none"),i.addEventListener("change",()=>{o.style.display=i.value==="match"?"none":"",t.placeholder=i.value==="match"?"field name":"pattern"});const l=document.createElement("input");l.type="text",l.className="form-input fb-logic-val-message",l.placeholder="Error message",l.value=e?.message||"",l.style.cssText="flex:4;";const s=document.createElement("button");return s.type="button",s.className="btn btn-xs btn-ghost",s.style.cssText="line-height:1;opacity:.6;",w(s,"x"),s.style.flexShrink="0",s.addEventListener("click",()=>n.remove()),n.appendChild(i),n.appendChild(t),n.appendChild(o),n.appendChild(l),n.appendChild(s),n}function Te(e,n,i){const t=document.createElement("div");t.dataset.logicSection="validation",t.appendChild(H("Custom Validation"));const o=document.createElement("div");o.className="fb-logic-val-rules",(e||[]).forEach(s=>o.appendChild(ie(s))),t.appendChild(o);const l=document.createElement("button");return l.type="button",l.className="btn btn-xs btn-ghost",l.style.cssText="font-size:.73rem;margin-top:.2rem;",D(l,"plus","Add validation rule"),l.addEventListener("click",()=>o.appendChild(ie(null))),t.appendChild(l),t}function Ne(e,n,i){const t=document.createElement("div");t.dataset.logicSection="cascade",t.appendChild(H("Cascade Options")),t.lastChild.appendChild(Q("Make this field's options depend on another field. When the source field changes, the options here are replaced by the mapping entry for its value; if nothing matches, the default options below are shown."));const o=document.createElement("div");o.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.5rem;";const l=document.createElement("span");l.textContent="Source field:",l.style.cssText="font-size:.8rem;flex-shrink:0;";const s=document.createElement("select");s.className="form-select fb-logic-cascade-source",s.style.cssText="flex:1;";const d=document.createElement("option");d.value="",d.textContent="\u2014 none \u2014",s.appendChild(d),i.forEach(u=>{const m=document.createElement("option");m.value=u.name,m.textContent=u.label||u.name,u.name===e.sourceField&&(m.selected=!0),s.appendChild(m)}),o.appendChild(l),o.appendChild(s),t.appendChild(o);const r=document.createElement("p");r.textContent='Mapping JSON \u2014 {"value":[{"value":"...","label":"..."}]}',r.appendChild(Q(`Each key is a value of the source field; its list becomes this field's options when that value is picked. Example: {"uk":[{"value":"london","label":"London"}]} shows London when the source is "uk".`)),r.style.cssText="font-size:.73rem;color:var(--dm-text-muted,#888);margin:.3rem 0 .2rem;";const a=document.createElement("textarea");a.className="form-input fb-logic-cascade-mapping",a.rows=4,a.style.cssText="font-family:monospace;",a.placeholder='{"uk": [{"value": "london", "label": "London"}]}',a.value=e.mapping?JSON.stringify(e.mapping,null,2):"",t.appendChild(r),t.appendChild(a);const p=document.createElement("p");p.textContent="Default options (one per line: value:Label)",p.appendChild(Q("Shown before the source field has a value, or when its value has no mapping entry.")),p.style.cssText="font-size:.73rem;color:var(--dm-text-muted,#888);margin:.3rem 0 .2rem;";const c=document.createElement("textarea");return c.className="form-input fb-logic-cascade-defaults",c.rows=3,c.style.cssText="font-family:monospace;",c.placeholder=`option1:Option 1
|
|
4
4
|
option2:Option 2`,c.value=(e.defaultOptions||[]).map(u=>{const m=typeof u=="string"?u:u.value??"",f=typeof u=="string"?u:u.label??m;return m===f?m:`${m}:${f}`}).join(`
|
|
5
|
-
`),n.appendChild(p),n.appendChild(c),n}function Ne(e,t){const s=e.logic||{},n=g.filter((c,u)=>u!==t&&c.type!=="page-break"&&c.type!=="spacer"),o=document.createElement("div");o.className="fb-field-logic",o.style.cssText="margin-top:.75rem;border-top:1px solid var(--dm-border,#333);padding-top:.5rem;";const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;justify-content:space-between;cursor:pointer;padding:.15rem 0;";const i=document.createElement("span");i.style.cssText="font-size:.8rem;font-weight:600;color:var(--dm-text-muted,#888);",i.textContent="\u26A1 Conditional Logic";const d=le(s),r=document.createElement("button");r.type="button",r.className="btn btn-xs btn-ghost",w(r,d?"chevron-down":"chevron-right",12);const a=document.createElement("div");a.className="fb-logic-body",a.style.cssText="padding:.25rem 0 .25rem;"+(d?"":"display:none;"),l.addEventListener("click",()=>{const c=a.style.display==="none";a.style.display=c?"":"none",w(r,c?"chevron-down":"chevron-right",12)}),l.appendChild(i),l.appendChild(r),o.appendChild(l),a.appendChild(we(s.visibility||{},t,n)),a.appendChild(ke(s.requirement||{},t,n)),a.appendChild(Se(s.validation||[],t,n));const p=document.getElementById(`fb-type-${t}`)?.value||e.type;return W.has(p)&&a.appendChild(Te(s.cascade||{},t,n)),o.appendChild(a),o}function se(e,t,s){const n=document.createElement("div");return n.className="fb-field-extras",e==="chooser"?(n.appendChild(qe(t,s)),n.appendChild(Le(t.options||[],s))):(W.has(e)&&n.appendChild(Fe(t.options||[],s)),e==="textarea"&&n.appendChild(k([S("Rows",`fb-rows-${s}`,"number",t.formConfig?.rows||4,"Height of textarea")])),(e==="string"||e==="textarea")&&n.appendChild(k([S("Min Length",`fb-minlength-${s}`,"number",t.minLength||"",""),S("Max Length",`fb-maxlength-${s}`,"number",t.maxLength||"","")])),e==="number"&&n.appendChild(k([S("Min",`fb-min-${s}`,"number",t.min??"",""),S("Max",`fb-max-${s}`,"number",t.max??"","")]))),n.children.length?n:null}function ie(e){const t=document.createElement("div");t.className="fb-chooser-section",t.style.cssText="border:1px solid var(--dm-border, #e5e7eb); border-radius:6px; margin-top:.5rem; overflow:hidden;";const s=document.createElement("div");s.style.cssText="padding:.4rem .6rem; background:color-mix(in srgb, var(--dm-background, #f9fafb) 60%, transparent); font-weight:600; font-size:.82rem; border-bottom:1px solid var(--dm-border, #e5e7eb);",s.textContent=e,t.appendChild(s);const n=document.createElement("div");return n.className="fb-chooser-section-body",n.style.cssText="padding:.55rem .6rem; display:flex; flex-direction:column; gap:.45rem;",t.appendChild(n),{wrap:t,body:n}}function U(e,t,s,n,o){const l=document.createElement("div");l.style.flex="1";const i=document.createElement("label");i.htmlFor=e,i.className="form-label",i.textContent=t,i.style.fontSize=".8rem";const d=document.createElement("select");if(d.id=e,d.className="form-input",n.forEach(([r,a])=>{const p=document.createElement("option");p.value=r,p.textContent=a,String(r)===String(s)&&(p.selected=!0),d.appendChild(p)}),l.appendChild(i),l.appendChild(d),o){const r=document.createElement("p");r.className="form-hint text-muted",r.textContent=o,r.style.cssText="font-size:.72rem;margin-top:.2rem;",l.appendChild(r)}return l}function ce(e,t,s,n){const o=document.createElement("div");o.style.flex="1";const l=document.createElement("label");l.style.cssText="display:inline-flex;align-items:center;gap:.4rem;font-size:.82rem;";const i=document.createElement("input");if(i.id=e,i.type="checkbox",i.checked=!!s,l.appendChild(i),l.appendChild(document.createTextNode(" "+t)),o.appendChild(l),n){const d=document.createElement("p");d.className="form-hint text-muted",d.textContent=n,d.style.cssText="font-size:.72rem;margin-top:.2rem;",o.appendChild(d)}return o}function K(e,t,s,n,o){const l=document.createElement("div");l.style.flex="1";const i=document.createElement("label");i.htmlFor=e,i.className="form-label",i.textContent=t,i.style.fontSize=".8rem",l.appendChild(i);const d=document.createElement("div");d.style.cssText="display:flex;gap:.4rem;align-items:stretch;";const r=document.createElement("input");r.id=e,r.type="text",r.className="form-input",r.value=s||"",n&&(r.placeholder=n),r.style.flex="1";const a=document.createElement("input");a.type="color",a.title="Pick a colour",a.style.cssText="width:36px;height:auto;padding:0;border:1px solid var(--dm-border,#e5e7eb);border-radius:4px;cursor:pointer;";const p=String(r.value).match(/^#[0-9a-fA-F]{3,8}$/);if(a.value=p?r.value:"#000000",a.addEventListener("input",()=>{r.value=a.value,r.dispatchEvent(new Event("input",{bubbles:!0}))}),d.appendChild(r),d.appendChild(a),l.appendChild(d),o){const c=document.createElement("p");c.className="form-hint text-muted",c.textContent=o,c.style.cssText="font-size:.72rem;margin-top:.2rem;",l.appendChild(c)}return l}function qe(e,t){const s=document.createElement("div");s.style.cssText="margin-top:.5rem; display:flex; flex-direction:column; gap:.4rem;";const n=ie("Layout");n.body.appendChild(k([U(`fb-chooser-variant-${t}`,"Variant",e.variant||"card",[["card","Card \u2014 rich tiles"],["chip","Chip \u2014 compact pills"]],"Card supports description and grid columns; chips wrap."),ce(`fb-chooser-multiple-${t}`,"Multi-select",!!e.multiple,"Off = single (radio); on = multi (checkbox).")])),n.body.appendChild(k([U(`fb-chooser-density-${t}`,"Density",e.density||"comfortable",[["comfortable","Comfortable"],["compact","Compact"]],"Compact strips description (card variant only)."),S("Columns",`fb-chooser-columns-${t}`,"number",e.columns||3,"Card grid (1\u20136). Ignored for chips.")])),s.appendChild(n.wrap);const o=ie("Visual");return o.body.appendChild(k([K(`fb-chooser-accent-${t}`,"Accent",e.accent||"","primary / success / #ec4899","Selected highlight colour \u2014 semantic name or any CSS colour."),U(`fb-chooser-accent-style-${t}`,"Accent style",e.accentStyle||"border",[["border","Border (default)"],["solid","Solid"],["glow","Glow"],["overlay","Overlay"],["underline","Underline"]],"Visual treatment of the selected state.")])),o.body.appendChild(k([ce(`fb-chooser-glow-${t}`,"Glow on selected",!!e.glow,"Soft outer glow."),K(`fb-chooser-glow-colour-${t}`,"Glow colour",e.glowColour||"","defaults to accent","Optional; semantic name or CSS colour.")])),o.body.appendChild(k([U(`fb-chooser-shadow-${t}`,"Shadow",e.shadow||"none",[["none","None"],["sm","Small"],["md","Medium"],["lg","Large"],["xl","Extra Large"]],"Drop shadow on every option."),K(`fb-chooser-shadow-colour-${t}`,"Shadow colour",e.shadowColour||"","rgba(0,0,0,0.1)","Optional shadow tint.")])),s.appendChild(o.wrap),s}function $e(e,t,s,n){const o=document.createElement("div");o.className="fb-chooser-option-row",o.dataset.optIdx=String(s),o.style.cssText="border:1px solid var(--dm-border,#e5e7eb); border-radius:4px; padding:.5rem; background:var(--dm-background-alt, color-mix(in srgb, var(--dm-card-bg, #fff) 92%, var(--dm-text, #111))); display:flex; flex-direction:column; gap:.35rem;";const l=document.createElement("div");l.style.cssText="display:flex; gap:.4rem; align-items:center;";const i=document.createElement("div");i.style.cssText="display:flex; flex-direction:column; gap:1px;";const d=document.createElement("button");d.type="button",d.className="btn btn-xs btn-ghost",d.title="Move up",w(d,"arrow-up"),d.addEventListener("click",N=>{N.preventDefault(),re(t,s,-1,n)});const r=document.createElement("button");r.type="button",r.className="btn btn-xs btn-ghost",r.title="Move down",w(r,"arrow-down"),r.addEventListener("click",N=>{N.preventDefault(),re(t,s,1,n)}),i.appendChild(d),i.appendChild(r),l.appendChild(i);const a=document.createElement("input");a.type="text",a.className="form-input fb-chooser-opt-value",a.placeholder="value",a.value=e.value||"",a.style.flex="1",l.appendChild(a);const p=document.createElement("input");p.type="text",p.className="form-input fb-chooser-opt-label",p.placeholder="label (visible to users)",p.value=e.label||"",p.style.flex="2",l.appendChild(p);const c=document.createElement("button");c.type="button",c.className="btn btn-xs btn-ghost",c.style.cssText="line-height:1;opacity:.6;",c.title="Remove option",w(c,"x"),c.addEventListener("click",N=>{N.preventDefault(),Be(t,s,n)}),l.appendChild(c),o.appendChild(l);const u=document.createElement("div");u.style.cssText="display:flex; gap:.4rem;";const m=fe("icon name (optional)",e.icon||"");m.input.classList.add("fb-chooser-opt-icon"),m.el.style.flex="1",u.appendChild(m.el);const f=document.createElement("input");f.type="text",f.className="form-input fb-chooser-opt-description",f.placeholder="description (card + comfortable only)",f.value=e.description||"",f.style.flex="2",u.appendChild(f),o.appendChild(u);const y=document.createElement("div");y.style.cssText="display:flex; gap:.4rem;";const h=document.createElement("input");h.type="text",h.className="form-input fb-chooser-opt-tooltip",h.placeholder="tooltip (hover hint)",h.value=e.tooltip||"",h.style.flex="2",y.appendChild(h);const b=document.createElement("input");b.type="text",b.className="form-input fb-chooser-opt-badge-text",b.placeholder="badge text",b.value=e.badge?.text||"",b.style.flex="1",y.appendChild(b);const v=document.createElement("select");v.className="form-input fb-chooser-opt-badge-type",v.style.flex="1",[["","no badge"],["primary","primary"],["success","success"],["info","info"],["warning","warning"],["danger","danger"]].forEach(([N,O])=>{const j=document.createElement("option");j.value=N,j.textContent=O,(e.badge?.type||"")===N&&(j.selected=!0),v.appendChild(j)}),y.appendChild(v),o.appendChild(y);const C=document.createElement("div");C.style.cssText="display:flex; gap:1rem; font-size:.8rem;";const x=document.createElement("label");x.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const T=document.createElement("input");T.type="checkbox",T.className="fb-chooser-opt-recommended",T.checked=!!e.recommended,x.appendChild(T),x.appendChild(document.createTextNode(" Recommended")),C.appendChild(x);const z=document.createElement("label");z.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const A=document.createElement("input");return A.type="checkbox",A.className="fb-chooser-opt-disabled",A.checked=!!e.disabled,z.appendChild(A),z.appendChild(document.createTextNode(" Disabled")),C.appendChild(z),o.appendChild(C),o}function Le(e,t){const s=document.createElement("div");s.style.cssText="margin-top:.5rem;";const n=document.createElement("div");n.style.cssText="display:flex; align-items:center; justify-content:space-between; margin-bottom:.4rem;";const o=document.createElement("p");o.textContent="Options",o.style.cssText="font-size:.85rem;font-weight:600;margin:0;",n.appendChild(o);const l=document.createElement("button");l.type="button",l.className="btn btn-xs btn-ghost",D(l,"plus","Add option"),n.appendChild(l),s.appendChild(n);const i=document.createElement("div");i.className="fb-chooser-options-list",i.id=`fb-chooser-options-${t}`,i.style.cssText="display:flex; flex-direction:column; gap:.4rem;",s.appendChild(i);const d=()=>{i.replaceChildren();const r=ze(t);(r.length?r:e||[]).forEach((p,c)=>{i.appendChild($e(p,t,c,d))})};return l.addEventListener("click",r=>{r.preventDefault(),Ae(t,d)}),d(),s}function ze(e){const t=g[e];return!t||!Array.isArray(t.options)?[]:t.options}function X(e){return g[e]?(Array.isArray(g[e].options)||(g[e].options=[]),g[e].options):[]}function Ae(e,t){J(e),X(e).push({value:"new",label:"New option"}),t()}function Be(e,t,s){J(e),X(e).splice(t,1),s()}function re(e,t,s,n){J(e);const o=X(e),l=t+s;if(l<0||l>=o.length)return;const[i]=o.splice(t,1);o.splice(l,0,i),n()}function J(e){const t=document.getElementById(`fb-chooser-options-${e}`);if(!t)return;const s=t.querySelectorAll(".fb-chooser-option-row"),n=[];s.forEach(o=>{const l={value:o.querySelector(".fb-chooser-opt-value")?.value.trim()||"",label:o.querySelector(".fb-chooser-opt-label")?.value.trim()||""},i=o.querySelector(".fb-chooser-opt-icon")?.value.trim();i&&(l.icon=i);const d=o.querySelector(".fb-chooser-opt-description")?.value.trim();d&&(l.description=d);const r=o.querySelector(".fb-chooser-opt-tooltip")?.value.trim();r&&(l.tooltip=r);const a=o.querySelector(".fb-chooser-opt-badge-text")?.value.trim(),p=o.querySelector(".fb-chooser-opt-badge-type")?.value;(a||p)&&(l.badge={},a&&(l.badge.text=a),p&&(l.badge.type=p)),o.querySelector(".fb-chooser-opt-recommended")?.checked&&(l.recommended=!0),o.querySelector(".fb-chooser-opt-disabled")?.checked&&(l.disabled=!0),n.push(l)}),g[e]&&(g[e].options=n)}function k(e){const t=document.createElement("div");return t.style.cssText="display:flex;gap:.85rem;margin-bottom:.75rem;",e.forEach(s=>{s&&t.appendChild(s)}),t}function S(e,t,s,n,o){const l=document.createElement("div");l.style.flex="1";const i=document.createElement("label");i.htmlFor=t,i.className="form-label",i.textContent=e,i.style.fontSize=".8rem";const d=document.createElement("input");if(d.id=t,d.type=s||"text",d.className="form-input",d.value=n??"",l.appendChild(i),l.appendChild(d),o){const r=document.createElement("p");r.className="form-hint text-muted",r.textContent=o,r.style.cssText="font-size:.73rem;margin-top:.2rem;",l.appendChild(r)}return l}function Ie(e,t,s,n){const o=document.createElement("div");o.style.flex="1";const l=document.createElement("label");l.htmlFor=t,l.className="form-label",l.textContent=e,l.style.fontSize=".8rem";const i=document.createElement("select");return i.id=t,i.className="form-select",s.forEach(d=>{const r=document.createElement("option");r.value=d.value,r.textContent=d.label,d.value===n&&(r.selected=!0),i.appendChild(r)}),o.appendChild(l),o.appendChild(i),o}function de(e,t,s){const n=document.createElement("div");n.style.cssText="flex:0;min-width:80px;display:flex;flex-direction:column;justify-content:flex-end;";const o=document.createElement("label");o.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;font-size:.8rem;white-space:nowrap;";const l=document.createElement("input");return l.id=t,l.type="checkbox",l.checked=s,o.appendChild(l),o.appendChild(document.createTextNode(e)),n.appendChild(o),n}function Fe(e,t){const s=document.createElement("div");s.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 o=document.createElement("textarea");return o.id=`fb-options-${t}`,o.className="form-input",o.rows=4,o.placeholder=`yes:Yes
|
|
5
|
+
`),t.appendChild(p),t.appendChild(c),t}function qe(e,n){const i=e.logic||{},t=g.filter((c,u)=>u!==n&&c.type!=="page-break"&&c.type!=="spacer"),o=document.createElement("div");o.className="fb-field-logic",o.style.cssText="margin-top:.75rem;border-top:1px solid var(--dm-border,#333);padding-top:.5rem;";const l=document.createElement("div");l.style.cssText="display:flex;align-items:center;justify-content:space-between;cursor:pointer;padding:.15rem 0;";const s=document.createElement("span");s.style.cssText="font-size:.8rem;font-weight:600;color:var(--dm-text-muted,#888);",s.textContent="\u26A1 Conditional Logic";const d=oe(i),r=document.createElement("button");r.type="button",r.className="btn btn-xs btn-ghost",w(r,d?"chevron-down":"chevron-right",12);const a=document.createElement("div");a.className="fb-logic-body",a.style.cssText="padding:.25rem 0 .25rem;"+(d?"":"display:none;"),l.addEventListener("click",()=>{const c=a.style.display==="none";a.style.display=c?"":"none",w(r,c?"chevron-down":"chevron-right",12)}),l.appendChild(s),l.appendChild(r),o.appendChild(l),a.appendChild(ke(i.visibility||{},n,t)),a.appendChild(Se(i.requirement||{},n,t)),a.appendChild(Te(i.validation||[],n,t));const p=document.getElementById(`fb-type-${n}`)?.value||e.type;return W.has(p)&&a.appendChild(Ne(i.cascade||{},n,t)),o.appendChild(a),o}function se(e,n,i){const t=document.createElement("div");return t.className="fb-field-extras",e==="chooser"?(t.appendChild($e(n,i)),t.appendChild(ze(n.options||[],i))):(W.has(e)&&t.appendChild(Oe(n.options||[],i)),e==="textarea"&&t.appendChild(k([S("Rows",`fb-rows-${i}`,"number",n.formConfig?.rows||4,"Height of textarea")])),(e==="string"||e==="textarea")&&t.appendChild(k([S("Min Length",`fb-minlength-${i}`,"number",n.minLength||"",""),S("Max Length",`fb-maxlength-${i}`,"number",n.maxLength||"","")])),e==="number"&&t.appendChild(k([S("Min",`fb-min-${i}`,"number",n.min??"",""),S("Max",`fb-max-${i}`,"number",n.max??"","")]))),t.children.length?t:null}function ce(e){const n=document.createElement("div");n.className="fb-chooser-section",n.style.cssText="border:1px solid var(--dm-border, #e5e7eb); border-radius:6px; margin-top:.5rem; overflow:hidden;";const i=document.createElement("div");i.style.cssText="padding:.4rem .6rem; background:color-mix(in srgb, var(--dm-background, #f9fafb) 60%, transparent); font-weight:600; font-size:.82rem; border-bottom:1px solid var(--dm-border, #e5e7eb);",i.textContent=e,n.appendChild(i);const t=document.createElement("div");return t.className="fb-chooser-section-body",t.style.cssText="padding:.55rem .6rem; display:flex; flex-direction:column; gap:.45rem;",n.appendChild(t),{wrap:n,body:t}}function U(e,n,i,t,o){const l=document.createElement("div");l.style.flex="1";const s=document.createElement("label");s.htmlFor=e,s.className="form-label",s.textContent=n,s.style.fontSize=".8rem";const d=document.createElement("select");if(d.id=e,d.className="form-input",t.forEach(([r,a])=>{const p=document.createElement("option");p.value=r,p.textContent=a,String(r)===String(i)&&(p.selected=!0),d.appendChild(p)}),l.appendChild(s),l.appendChild(d),o){const r=document.createElement("p");r.className="form-hint text-muted",r.textContent=o,r.style.cssText="font-size:.72rem;margin-top:.2rem;",l.appendChild(r)}return l}function re(e,n,i,t){const o=document.createElement("div");o.style.flex="1";const l=document.createElement("label");l.style.cssText="display:inline-flex;align-items:center;gap:.4rem;font-size:.82rem;";const s=document.createElement("input");if(s.id=e,s.type="checkbox",s.checked=!!i,l.appendChild(s),l.appendChild(document.createTextNode(" "+n)),o.appendChild(l),t){const d=document.createElement("p");d.className="form-hint text-muted",d.textContent=t,d.style.cssText="font-size:.72rem;margin-top:.2rem;",o.appendChild(d)}return o}function K(e,n,i,t,o){const l=document.createElement("div");l.style.flex="1";const s=document.createElement("label");s.htmlFor=e,s.className="form-label",s.textContent=n,s.style.fontSize=".8rem",l.appendChild(s);const d=document.createElement("div");d.style.cssText="display:flex;gap:.4rem;align-items:stretch;";const r=document.createElement("input");r.id=e,r.type="text",r.className="form-input",r.value=i||"",t&&(r.placeholder=t),r.style.flex="1";const a=document.createElement("input");a.type="color",a.title="Pick a colour",a.style.cssText="width:36px;height:auto;padding:0;border:1px solid var(--dm-border,#e5e7eb);border-radius:4px;cursor:pointer;";const p=String(r.value).match(/^#[0-9a-fA-F]{3,8}$/);if(a.value=p?r.value:"#000000",a.addEventListener("input",()=>{r.value=a.value,r.dispatchEvent(new Event("input",{bubbles:!0}))}),d.appendChild(r),d.appendChild(a),l.appendChild(d),o){const c=document.createElement("p");c.className="form-hint text-muted",c.textContent=o,c.style.cssText="font-size:.72rem;margin-top:.2rem;",l.appendChild(c)}return l}function $e(e,n){const i=document.createElement("div");i.style.cssText="margin-top:.5rem; display:flex; flex-direction:column; gap:.4rem;";const t=ce("Layout");t.body.appendChild(k([U(`fb-chooser-variant-${n}`,"Variant",e.variant||"card",[["card","Card \u2014 rich tiles"],["chip","Chip \u2014 compact pills"]],"Card supports description and grid columns; chips wrap."),re(`fb-chooser-multiple-${n}`,"Multi-select",!!e.multiple,"Off = single (radio); on = multi (checkbox).")])),t.body.appendChild(k([U(`fb-chooser-density-${n}`,"Density",e.density||"comfortable",[["comfortable","Comfortable"],["compact","Compact"]],"Compact strips description (card variant only)."),S("Columns",`fb-chooser-columns-${n}`,"number",e.columns||3,"Card grid (1\u20136). Ignored for chips.")])),i.appendChild(t.wrap);const o=ce("Visual");return o.body.appendChild(k([K(`fb-chooser-accent-${n}`,"Accent",e.accent||"","primary / success / #ec4899","Selected highlight colour \u2014 semantic name or any CSS colour."),U(`fb-chooser-accent-style-${n}`,"Accent style",e.accentStyle||"border",[["border","Border (default)"],["solid","Solid"],["glow","Glow"],["overlay","Overlay"],["underline","Underline"]],"Visual treatment of the selected state.")])),o.body.appendChild(k([re(`fb-chooser-glow-${n}`,"Glow on selected",!!e.glow,"Soft outer glow."),K(`fb-chooser-glow-colour-${n}`,"Glow colour",e.glowColour||"","defaults to accent","Optional; semantic name or CSS colour.")])),o.body.appendChild(k([U(`fb-chooser-shadow-${n}`,"Shadow",e.shadow||"none",[["none","None"],["sm","Small"],["md","Medium"],["lg","Large"],["xl","Extra Large"]],"Drop shadow on every option."),K(`fb-chooser-shadow-colour-${n}`,"Shadow colour",e.shadowColour||"","rgba(0,0,0,0.1)","Optional shadow tint.")])),i.appendChild(o.wrap),i}function Le(e,n,i,t){const o=document.createElement("div");o.className="fb-chooser-option-row",o.dataset.optIdx=String(i),o.style.cssText="border:1px solid var(--dm-border,#e5e7eb); border-radius:4px; padding:.5rem; background:var(--dm-background-alt, color-mix(in srgb, var(--dm-card-bg, #fff) 92%, var(--dm-text, #111))); display:flex; flex-direction:column; gap:.35rem;";const l=document.createElement("div");l.style.cssText="display:flex; gap:.4rem; align-items:center;";const s=document.createElement("div");s.style.cssText="display:flex; flex-direction:column; gap:1px;";const d=document.createElement("button");d.type="button",d.className="btn btn-xs btn-ghost",d.title="Move up",w(d,"arrow-up"),d.addEventListener("click",N=>{N.preventDefault(),de(n,i,-1,t)});const r=document.createElement("button");r.type="button",r.className="btn btn-xs btn-ghost",r.title="Move down",w(r,"arrow-down"),r.addEventListener("click",N=>{N.preventDefault(),de(n,i,1,t)}),s.appendChild(d),s.appendChild(r),l.appendChild(s);const a=document.createElement("input");a.type="text",a.className="form-input fb-chooser-opt-value",a.placeholder="value",a.value=e.value||"",a.style.flex="1",l.appendChild(a);const p=document.createElement("input");p.type="text",p.className="form-input fb-chooser-opt-label",p.placeholder="label (visible to users)",p.value=e.label||"",p.style.flex="2",l.appendChild(p);const c=document.createElement("button");c.type="button",c.className="btn btn-xs btn-ghost",c.style.cssText="line-height:1;opacity:.6;",c.title="Remove option",w(c,"x"),c.addEventListener("click",N=>{N.preventDefault(),Ie(n,i,t)}),l.appendChild(c),o.appendChild(l);const u=document.createElement("div");u.style.cssText="display:flex; gap:.4rem;";const m=he("icon name (optional)",e.icon||"");m.input.classList.add("fb-chooser-opt-icon"),m.el.style.flex="1",u.appendChild(m.el);const f=document.createElement("input");f.type="text",f.className="form-input fb-chooser-opt-description",f.placeholder="description (card + comfortable only)",f.value=e.description||"",f.style.flex="2",u.appendChild(f),o.appendChild(u);const y=document.createElement("div");y.style.cssText="display:flex; gap:.4rem;";const h=document.createElement("input");h.type="text",h.className="form-input fb-chooser-opt-tooltip",h.placeholder="tooltip (hover hint)",h.value=e.tooltip||"",h.style.flex="2",y.appendChild(h);const b=document.createElement("input");b.type="text",b.className="form-input fb-chooser-opt-badge-text",b.placeholder="badge text",b.value=e.badge?.text||"",b.style.flex="1",y.appendChild(b);const v=document.createElement("select");v.className="form-input fb-chooser-opt-badge-type",v.style.flex="1",[["","no badge"],["primary","primary"],["success","success"],["info","info"],["warning","warning"],["danger","danger"]].forEach(([N,O])=>{const j=document.createElement("option");j.value=N,j.textContent=O,(e.badge?.type||"")===N&&(j.selected=!0),v.appendChild(j)}),y.appendChild(v),o.appendChild(y);const C=document.createElement("div");C.style.cssText="display:flex; gap:1rem; font-size:.8rem;";const x=document.createElement("label");x.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const T=document.createElement("input");T.type="checkbox",T.className="fb-chooser-opt-recommended",T.checked=!!e.recommended,x.appendChild(T),x.appendChild(document.createTextNode(" Recommended")),C.appendChild(x);const z=document.createElement("label");z.style.cssText="display:inline-flex;align-items:center;gap:.3rem;";const A=document.createElement("input");return A.type="checkbox",A.className="fb-chooser-opt-disabled",A.checked=!!e.disabled,z.appendChild(A),z.appendChild(document.createTextNode(" Disabled")),C.appendChild(z),o.appendChild(C),o}function ze(e,n){const i=document.createElement("div");i.style.cssText="margin-top:.5rem;";const t=document.createElement("div");t.style.cssText="display:flex; align-items:center; justify-content:space-between; margin-bottom:.4rem;";const o=document.createElement("p");o.textContent="Options",o.style.cssText="font-size:.85rem;font-weight:600;margin:0;",t.appendChild(o);const l=document.createElement("button");l.type="button",l.className="btn btn-xs btn-ghost",D(l,"plus","Add option"),t.appendChild(l),i.appendChild(t);const s=document.createElement("div");s.className="fb-chooser-options-list",s.id=`fb-chooser-options-${n}`,s.style.cssText="display:flex; flex-direction:column; gap:.4rem;",i.appendChild(s);const d=()=>{s.replaceChildren();const r=Ae(n);(r.length?r:e||[]).forEach((p,c)=>{s.appendChild(Le(p,n,c,d))})};return l.addEventListener("click",r=>{r.preventDefault(),Be(n,d)}),d(),i}function Ae(e){const n=g[e];return!n||!Array.isArray(n.options)?[]:n.options}function X(e){return g[e]?(Array.isArray(g[e].options)||(g[e].options=[]),g[e].options):[]}function Be(e,n){J(e),X(e).push({value:"new",label:"New option"}),n()}function Ie(e,n,i){J(e),X(e).splice(n,1),i()}function de(e,n,i,t){J(e);const o=X(e),l=n+i;if(l<0||l>=o.length)return;const[s]=o.splice(n,1);o.splice(l,0,s),t()}function J(e){const n=document.getElementById(`fb-chooser-options-${e}`);if(!n)return;const i=n.querySelectorAll(".fb-chooser-option-row"),t=[];i.forEach(o=>{const l={value:o.querySelector(".fb-chooser-opt-value")?.value.trim()||"",label:o.querySelector(".fb-chooser-opt-label")?.value.trim()||""},s=o.querySelector(".fb-chooser-opt-icon")?.value.trim();s&&(l.icon=s);const d=o.querySelector(".fb-chooser-opt-description")?.value.trim();d&&(l.description=d);const r=o.querySelector(".fb-chooser-opt-tooltip")?.value.trim();r&&(l.tooltip=r);const a=o.querySelector(".fb-chooser-opt-badge-text")?.value.trim(),p=o.querySelector(".fb-chooser-opt-badge-type")?.value;(a||p)&&(l.badge={},a&&(l.badge.text=a),p&&(l.badge.type=p)),o.querySelector(".fb-chooser-opt-recommended")?.checked&&(l.recommended=!0),o.querySelector(".fb-chooser-opt-disabled")?.checked&&(l.disabled=!0),t.push(l)}),g[e]&&(g[e].options=t)}function k(e){const n=document.createElement("div");return n.style.cssText="display:flex;gap:.85rem;margin-bottom:.75rem;",e.forEach(i=>{i&&n.appendChild(i)}),n}function S(e,n,i,t,o){const l=document.createElement("div");l.style.flex="1";const s=document.createElement("label");s.htmlFor=n,s.className="form-label",s.textContent=e,s.style.fontSize=".8rem";const d=document.createElement("input");if(d.id=n,d.type=i||"text",d.className="form-input",d.value=t??"",l.appendChild(s),l.appendChild(d),o){const r=document.createElement("p");r.className="form-hint text-muted",r.textContent=o,r.style.cssText="font-size:.73rem;margin-top:.2rem;",l.appendChild(r)}return l}function Fe(e,n,i,t){const o=document.createElement("div");o.style.flex="1";const l=document.createElement("label");l.htmlFor=n,l.className="form-label",l.textContent=e,l.style.fontSize=".8rem";const s=document.createElement("select");return s.id=n,s.className="form-select",i.forEach(d=>{const r=document.createElement("option");r.value=d.value,r.textContent=d.label,d.value===t&&(r.selected=!0),s.appendChild(r)}),o.appendChild(l),o.appendChild(s),o}function pe(e,n,i){const t=document.createElement("div");t.style.cssText="flex:0;min-width:80px;display:flex;flex-direction:column;justify-content:flex-end;";const o=document.createElement("label");o.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;font-size:.8rem;white-space:nowrap;";const l=document.createElement("input");return l.id=n,l.type="checkbox",l.checked=i,o.appendChild(l),o.appendChild(document.createTextNode(e)),t.appendChild(o),t}function Oe(e,n){const i=document.createElement("div");i.style.cssText="margin-top:.4rem;";const t=document.createElement("p");t.textContent="Options (one per line: value or value:Label)",t.style.cssText="font-size:.8rem;font-weight:600;margin-bottom:.3rem;";const o=document.createElement("textarea");return o.id=`fb-options-${n}`,o.className="form-input",o.rows=4,o.placeholder=`yes:Yes
|
|
6
6
|
no:No
|
|
7
|
-
maybe:Maybe`,o.value=(e||[]).map(l=>{const
|
|
8
|
-
`),o.style.fontFamily="monospace",
|
|
7
|
+
maybe:Maybe`,o.value=(e||[]).map(l=>{const s=typeof l=="string"?l:l.value??"",d=typeof l=="string"?l:l.label??s;return s===d?s:`${s}:${d}`}).join(`
|
|
8
|
+
`),o.style.fontFamily="monospace",i.appendChild(t),i.appendChild(o),i}function me(e){const n=e.find("#field-project").val()||"";return $(),{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}:{},meta:{...G||{},project:n||null},fields:g,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()},...P!==null&&{collection:P}}}}function je(e){const n=[];let i=[],t="Step 1",o="";return e.forEach(l=>{l.type==="page-break"?(n.push({title:t,description:o,fields:i}),i=[],t=l.label||`Step ${n.length+1}`,o=l.description||""):l.type!=="spacer"&&i.push(l)}),(i.length||n.length===0)&&n.push({title:t,description:o,fields:i}),n}function ue(e,n){const i={};return e.forEach(t=>{if(t.type==="page-break"||t.type==="spacer")return;const o={...t.formConfig||{}};o.span==="full"&&n&&(o.span=n);const l=t.type==="chooser"||!!(t.variant&&["select","radio","checkbox-group"].includes(t.type)),d={type:t.type==="checkbox"?"boolean":t.type==="date"||t.type==="icon"||t.type==="image"?"string":l?"chooser":t.type,label:t.label,required:t.required,options:t.options,formConfig:{...t.placeholder&&{placeholder:t.placeholder},...t.helper&&{helperText:t.helper},...t.tooltip&&{tooltip:t.tooltip},...o}};l&&(t.variant&&(d.variant=t.variant),(t.multiple||t.type==="checkbox-group")&&(d.multiple=!0),t.density&&(d.density=t.density),t.columns&&(d.columns=t.columns),t.accent&&(d.accent=t.accent),t.accentStyle&&(d.accentStyle=t.accentStyle),t.glow&&(d.glow=!0),t.glowColour&&(d.glowColour=t.glowColour),t.shadow&&(d.shadow=t.shadow),t.shadowColour&&(d.shadowColour=t.shadowColour)),i[t.name]=d}),i}function fe(e,n){const i=typeof e=="string"?document.querySelector(e):e;i&&(n||[]).forEach(t=>{if(t.type!=="date"||!t.name)return;const o=i.querySelector(`[name="${t.name}"]`);o&&o.type!=="date"&&(o.type="date")})}export const formEditorView={templateUrl:"/admin/js/templates/form-editor.html",async onMount(e){g=[],q=null,P=null;const n=window.location.hash.match(/\/forms\/edit\/([^/?#]+)/);q=n?n[1]:null;let i=null;const t=E.loader(e.get(0),{type:"dots"});if(q)try{i=await I(`/forms/${q}`),g=i.fields||[],P=i.actions?.collection??null}catch{E.toast("Could not load form.",{type:"error"})}G={};try{const a=await I("/projects").catch(()=>[]),p=e.find("#field-project").get(0);p&&(Array.isArray(a)?a:[]).forEach(c=>{const u=document.createElement("option");u.value=c.slug,u.textContent=c.name||c.slug,p.appendChild(u)})}catch{}if(t.destroy(),i?e.find("#editor-title").get(0).textContent=`Edit: ${i.title}`:e.find("#editor-title").get(0).textContent="New Form",q||e.find("#field-title").get(0).addEventListener("input",function(){e.find("#field-slug").val(ne(this.value))}),E.tabs(e.find("#editor-tabs").get(0)),i){e.find("#field-title").val(i.title),e.find("#field-slug").val(i.slug),e.find("#field-description").val(i.description||""),e.find("#field-bundled").prop("checked",!!i.bundled),G=i.meta||{},e.find("#field-project").val(i.meta?.project||"");const a=i.settings||{};e.find("#setting-submit-text").val(a.submitText||"Submit"),e.find("#setting-success-message").val(a.successMessage||""),e.find("#setting-layout").val(a.layout||"stacked"),e.find("#setting-columns").val(a.columns||2),e.find("#setting-submit-span").val(a.submitSpan||""),e.find("#columns-group").get(0).style.display=a.layout==="grid"?"":"none",e.find("#setting-honeypot").prop("checked",a.honeypot!==!1),e.find("#setting-rate-limit").val(a.rateLimitPerMinute||3),e.find("#setting-success-redirect").val(a.successRedirect||"");const p=i.actions?.email||{};e.find("#action-email-enabled").prop("checked",p.enabled||!1),e.find("#action-email-recipients").val(p.recipients||""),e.find("#action-email-subject-prefix").val(p.subjectPrefix||"");const c=i.actions?.webhook||{};e.find("#action-webhook-enabled").prop("checked",c.enabled||!1),e.find("#action-webhook-url").val(c.url||""),e.find("#action-webhook-method").val(c.method||"POST")}const o=i?.settings?.actionSlug||"";try{const a=await I("/actions").catch(()=>[]),p=e.find("#action-cms-slug").get(0);if((Array.isArray(a)?a:[]).forEach(c=>{const u=document.createElement("option");u.value=c.slug,u.textContent=c.title||c.slug,c.slug===o&&(u.selected=!0),p.appendChild(u)}),!a.length){const c=document.createElement("option");c.value="",c.textContent="No actions available",c.disabled=!0,p.appendChild(c)}}catch{}e.find("#setting-layout").get(0)?.addEventListener("change",function(){const a=this.value==="grid";e.find("#columns-group").get(0).style.display=a?"":"none",document.querySelectorAll(".fb-grid-row").forEach(p=>{p.style.display=a?"flex":"none"})}),L(e);const l=e.find("#add-element-menu").get(0);e.find("#add-element-btn").get(0).addEventListener("click",a=>{a.stopPropagation(),l.style.display=l.style.display==="none"?"":"none"}),M&&document.removeEventListener("click",M),M=()=>{l&&(l.style.display="none")},document.addEventListener("click",M),e.find("#add-field-btn").get(0).addEventListener("click",()=>{l&&(l.style.display="none"),$();const a=g.length;g.push({name:`field_${a+1}`,type:"string",label:"New Field",required:!1,placeholder:""}),L(e);const p=e.find("#fields-list").get(0)?.lastElementChild;if(p){const c=p.querySelector(".fb-field-body");c&&(c.style.display="")}}),e.find("#add-spacer-btn").get(0).addEventListener("click",()=>{l&&(l.style.display="none"),$(),g.push({type:"spacer"}),L(e)}),e.find("#add-page-break-btn").get(0).addEventListener("click",()=>{l&&(l.style.display="none"),$();const a=g.filter(p=>p.type==="page-break").length+2;g.push({type:"page-break",label:`Step ${a}`,description:""}),L(e)}),e.find("#save-form-btn").get(0).addEventListener("click",async()=>{const a=me(e);if(!a.title){E.toast("Please enter a form title.",{type:"error"});return}try{q?(await I(`/forms/${q}`,{method:"PUT",body:JSON.stringify(a)}),E.toast("Form saved.",{type:"success"})):(q=(await I("/forms",{method:"POST",body:JSON.stringify(a)})).slug,R.navigate(`/forms/edit/${q}`),E.toast("Form created.",{type:"success"}))}catch(p){E.toast(p.message||"Failed to save form.",{type:"error"})}}),_=()=>{const a=me(e),p=e.find("#preview-container").get(0);if(!p)return;const c=e.find("#preview-test-result").get(0),u=e.find("#preview-test-badge").get(0);c&&(c.style.display="none",c.textContent=""),u&&(u.style.display=q?"":"none"),e.find("#preview-card").get(0).style.display="",p.textContent="";const m=document.createElement("div");m.id="fb-preview-form",p.appendChild(m);const f=q?async h=>{c&&(c.style.display="none",c.textContent="");try{const b=await fetch(`/api/forms/submit/${q}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(h)}),v=await b.json();if(!b.ok)throw new Error(v.error||"Submission failed.");c&&(c.textContent=v.message||a.settings?.successMessage||"Submitted successfully.",c.style.cssText="display:block;margin-top:.75rem;padding:.6rem .9rem;border-radius:6px;font-size:.9rem;background:color-mix(in srgb, var(--dm-success) 12%, transparent);color:var(--dm-success,#22c55e);"),E.toast("Test submission stored.",{type:"success"})}catch(b){c&&(c.textContent=b.message,c.style.cssText="display:block;margin-top:.75rem;padding:.6rem .9rem;border-radius:6px;font-size:.9rem;background:color-mix(in srgb, var(--dm-danger) 12%, transparent);color:var(--dm-danger,#ef4444);"),E.toast(b.message,{type:"error"})}return!1}:()=>!1,y=a.fields.some(h=>h.type==="page-break");if(typeof F<"u"){const h=a.settings?.columns||2,b=a.settings?.layout||"stacked";if(y&&F.wizard){const v=je(a.fields).map(C=>({title:C.title,description:C.description,fields:ue(C.fields,h)}));F.wizard("#fb-preview-form",{schema:{steps:v},onSubmit:f}),fe("#fb-preview-form",a.fields),Z(m,a.fields)}else if(F.render){const v=ue(a.fields,h),C={};if(a.fields.forEach(x=>{if(!(!x.name||x.type==="page-break"||x.type==="spacer")&&(x.type==="select"||x.type==="multiselect")&&x.required){const T=(x.options||[])[0];T&&(C[x.name]=typeof T=="object"?T.value:T)}}),F.render("#fb-preview-form",v,C,{submitText:a.settings?.submitText||"Submit",layout:b,columns:h,onSubmit:f}),b==="grid"&&a.settings?.submitSpan==="full"){const x=document.querySelector("#fb-preview-form .form-buttons");x&&x.classList.add("col-span-full")}fe("#fb-preview-form",a.fields),Z(m,a.fields)}window.FormLogicEngine&&a.fields.some(v=>v.logic)&&requestAnimationFrame(()=>{new window.FormLogicEngine.FormLogicRuntime({fields:a.fields},m).init()})}else{const h=document.createElement("p");h.textContent=`${a.fields.filter(b=>b.type!=="page-break").length} field(s): ${a.fields.filter(b=>b.type!=="page-break").map(b=>b.label).join(", ")}`,h.style.cssText="color:var(--dm-text-muted);font-style:italic;",m.appendChild(h)}},_();const s=e.find("#editor-tabs").get(0),d=e.find("#preview-card").get(0),r=a=>{d&&d.contains(a.target)||Y()};s&&(s.addEventListener("input",r),s.addEventListener("change",r))}};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import{api as C,apiRequest as v}from"../api.js";import{populateThemeSelect as w}from"../lib/themes.js";export const settingsView={templateUrl:"/admin/js/templates/settings.html",async onMount(e){E.tabs(e.find("#settings-tabs").get(0));const k=E.loader(e.get(0),{type:"dots"}),l=await C.settings.get().catch(()=>({}));if(k.destroy(),e.find("#field-site-title").val(l.title||""),e.find("#field-tagline").val(l.tagline||""),e.find("#field-font-family").val(l.fontFamily||"Roboto"),e.find("#field-font-size").val(l.fontSize||16),w(e.find("#field-theme").get(0)),w(e.find("#field-admin-theme").get(0),{includeAdmin:!0}),e.find("#field-theme").val(l.baseTheme||l.theme||"charcoal-dark"),l.baseTheme){e.find("#field-theme").prop("disabled",!0);const t=document.createElement("p");t.className="form-hint",t.style.cssText="margin-top:.4rem;font-size:.8rem;color:var(--dm-info);";const s=document.createElement("a");s.href="#/plugins/theme-roller",s.textContent="Theme Roller",t.appendChild(document.createTextNode(`Custom theme \u201C${l.theme}\u201D is active (based on ${l.baseTheme}). Manage via `)),t.appendChild(s),t.appendChild(document.createTextNode(".")),e.find("#field-theme").get(0).closest(".col-6").appendChild(t)}e.find("#field-admin-theme").val(l.adminTheme||"charcoal-dark");const p=l.autoTheme||{},h=!!p.enabled,b=e.find("#field-auto-theme-enabled"),y=e.find("#auto-theme-fields"),u=e.find("#field-theme"),f=u.get(0).innerHTML;e.find("#field-day-theme").html(f),e.find("#field-night-theme").html(f),l.baseTheme?(b.prop("disabled",!0),e.find("#auto-theme-roller-hint").show()):(b.prop("checked",h),h&&(y.show(),u.prop("disabled",!0)),b.on("change",function(){const t=this.checked;y.toggle(t),u.prop("disabled",t)})),e.find("#field-day-theme").val(p.dayTheme||"charcoal-light"),e.find("#field-night-theme").val(p.nightTheme||"charcoal-dark"),e.find("#field-day-start").val(p.dayStart||"07:00"),e.find("#field-night-start").val(p.nightStart||"19:00"),e.find("#field-spacer-size").val(l.layoutOptions?.spacerSize??40),e.find("#field-seo-title").val(l.seo?.defaultTitle||""),e.find("#field-seo-separator").val(l.seo?.titleSeparator||" | "),e.find("#field-seo-desc").val(l.seo?.defaultDescription||""),e.find("#field-footer-copy").val(l.footer?.copyright||""),e.find("#field-social-twitter").val(l.social?.twitter||""),e.find("#field-social-facebook").val(l.social?.facebook||""),e.find("#field-social-instagram").val(l.social?.instagram||""),e.find("#field-social-linkedin").val(l.social?.linkedin||""),e.find("#field-social-github").val(l.social?.github||""),e.find("#field-social-youtube").val(l.social?.youtube||""),e.find("#field-smtp-host").val(l.smtp?.host||""),e.find("#field-smtp-port").val(l.smtp?.port||587),e.find("#field-smtp-user").val(l.smtp?.user||""),e.find("#field-smtp-pass").val(l.smtp?.pass||""),e.find("#field-smtp-secure").prop("checked",l.smtp?.secure||!1),e.find("#field-smtp-from-address").val(l.smtp?.fromAddress||""),e.find("#field-smtp-from-name").val(l.smtp?.fromName||"");const i=l.backToTop||{};e.find("#field-btt-enabled").prop("checked",i.enabled!==!1),e.find("#field-btt-threshold").val(i.scrollThreshold??300),e.find("#field-btt-position").val(i.position||"bottom-right"),e.find("#field-btt-offset").val(i.offset??32),e.find("#field-btt-bottom-offset").val(i.bottomOffset??i.offset??32),e.find("#field-btt-label").val(i.label||""),e.find("#field-btt-smooth").prop("checked",i.smooth!==!1);const d=l.cookieConsent||{};e.find("#field-cc-enabled").prop("checked",d.enabled!==!1),e.find("#field-cc-message").val(d.message||""),e.find("#field-cc-accept-all").val(d.acceptAllText||"Accept All"),e.find("#field-cc-reject-all").val(d.rejectAllText||"Reject All"),e.find("#field-cc-customize").val(d.customizeText||"Customize"),e.find("#field-cc-save-prefs").val(d.savePreferencesText||"Save Preferences"),e.find("#field-cc-privacy-text").val(d.privacyPolicyText||"Privacy Policy"),e.find("#field-cc-privacy-url").val(d.privacyPolicyUrl||""),e.find("#field-cc-cookie-text").val(d.cookiePolicyText||"Cookie Policy"),e.find("#field-cc-cookie-url").val(d.cookiePolicyUrl||""),e.find("#field-cc-position").val(d.position||"bottom"),e.find("#field-cc-layout").val(d.layout||"bar"),e.find("#field-cc-theme").val(d.theme||"dark"),e.find("#field-cc-show-functional").prop("checked",d.showFunctional!==!1),e.find("#field-cc-show-analytics").prop("checked",d.showAnalytics!==!1),e.find("#field-cc-show-marketing").prop("checked",d.showMarketing!==!1),e.find("#field-cc-version").val(d.consentVersion||"1.0");const n=l.breadcrumbs||{};e.find("#field-breadcrumbs-enabled").prop("checked",n.enabled===!0),e.find("#field-breadcrumbs-home-label").val(n.homeLabel||"Home"),e.find("#field-breadcrumbs-position").val(n.position||"TL"),e.find("#field-bc-offset-x").val(n.offsetX??8),e.find("#field-bc-offset-y").val(n.offsetY??8);function c(t){e.find(".bc-pos-btn").each(function(){const s=this.dataset.pos===t;this.style.background=s?"var(--dm-primary, #5b8cff)":"",this.style.color=s?"var(--dm-primary-text)":""})}c(n.position||"TL"),e.find(".bc-pos-btn").on("click",function(){const t=this.dataset.pos;e.find("#field-breadcrumbs-position").val(t),c(t)});let a=null;try{const{css:t}=await v("/settings/custom-css");e.find("#field-custom-css").val(t||""),E.editor&&(a=E.editor(e.find("#field-custom-css").get(0),{mode:"code",language:"css",lineNumbers:!0,showToolbar:!1,minHeight:420,placeholder:"/* Add your custom CSS here */",characterCount:!0}))}catch{}a&&a._editorEl&&a._editorEl.addEventListener("keydown",t=>{if(!t.ctrlKey&&!t.metaKey)return;if(t.key==="s"){t.preventDefault(),e.find("#save-css-btn").get(0)?.click();return}const s=a._editorEl,m=s.selectionStart!==s.selectionEnd;if((t.key==="c"||t.key==="x")&&!m){t.preventDefault();const o=s.value,g=s.selectionStart,T=o.lastIndexOf(`
|
|
2
2
|
`,g-1)+1,x=o.indexOf(`
|
|
3
|
-
`,g),S=x===-1?o.slice(T):o.slice(T,x+1);if(navigator.clipboard.writeText(S),t.key==="x"){const z=o.slice(0,T),P=x===-1?"":o.slice(x+1);s.value=z+P,s.selectionStart=s.selectionEnd=T,s.dispatchEvent(new Event("input",{bubbles:!0})),E.toast("Line cut",{type:"info",duration:1500})}else E.toast("Line copied",{type:"info",duration:1500})}}),e.find("#save-settings-btn").on("click",async()=>{const t=e.find("#field-admin-theme").val(),s=!l.baseTheme&&e.find("#field-auto-theme-enabled").prop("checked"),m={enabled:s,dayTheme:e.find("#field-day-theme").val()||"charcoal-light",nightTheme:e.find("#field-night-theme").val()||"charcoal-dark",dayStart:e.find("#field-day-start").val()||"07:00",nightStart:e.find("#field-night-start").val()||"19:00"},o={title:e.find("#field-site-title").val().trim(),tagline:e.find("#field-tagline").val().trim(),fontFamily:e.find("#field-font-family").val()||"Roboto",fontSize:parseInt(e.find("#field-font-size").val(),10)||16,theme:l.baseTheme?l.theme:s?m.dayTheme:e.find("#field-theme").val(),...l.baseTheme?{baseTheme:l.baseTheme}:{},autoTheme:m,adminTheme:t,layoutOptions:{spacerSize:parseInt(e.find("#field-spacer-size").val(),10)||40},seo:{defaultTitle:e.find("#field-seo-title").val().trim(),titleSeparator:e.find("#field-seo-separator").val()||" | ",defaultDescription:e.find("#field-seo-desc").val().trim()},footer:{copyright:e.find("#field-footer-copy").val().trim(),links:l.footer?.links||[]},social:{twitter:e.find("#field-social-twitter").val().trim(),facebook:e.find("#field-social-facebook").val().trim(),instagram:e.find("#field-social-instagram").val().trim(),linkedin:e.find("#field-social-linkedin").val().trim(),github:e.find("#field-social-github").val().trim(),youtube:e.find("#field-social-youtube").val().trim()},smtp:{host:e.find("#field-smtp-host").val().trim(),port:parseInt(e.find("#field-smtp-port").val(),10)||587,user:e.find("#field-smtp-user").val().trim(),pass:e.find("#field-smtp-pass").val(),secure:e.find("#field-smtp-secure").prop("checked"),fromAddress:e.find("#field-smtp-from-address").val().trim(),fromName:e.find("#field-smtp-from-name").val().trim()},backToTop:{enabled:e.find("#field-btt-enabled").prop("checked"),scrollThreshold:parseInt(e.find("#field-btt-threshold").val(),10)||300,position:e.find("#field-btt-position").val()||"bottom-right",offset:parseInt(e.find("#field-btt-offset").val(),10)||32,bottomOffset:parseInt(e.find("#field-btt-bottom-offset").val(),10)||32,label:e.find("#field-btt-label").val().trim(),smooth:e.find("#field-btt-smooth").prop("checked")},cookieConsent:{enabled:e.find("#field-cc-enabled").prop("checked"),message:e.find("#field-cc-message").val().trim(),acceptAllText:e.find("#field-cc-accept-all").val().trim(),rejectAllText:e.find("#field-cc-reject-all").val().trim(),customizeText:e.find("#field-cc-customize").val().trim(),savePreferencesText:e.find("#field-cc-save-prefs").val().trim(),privacyPolicyText:e.find("#field-cc-privacy-text").val().trim(),privacyPolicyUrl:e.find("#field-cc-privacy-url").val().trim(),cookiePolicyText:e.find("#field-cc-cookie-text").val().trim(),cookiePolicyUrl:e.find("#field-cc-cookie-url").val().trim(),position:e.find("#field-cc-position").val(),layout:e.find("#field-cc-layout").val(),theme:e.find("#field-cc-theme").val(),showFunctional:e.find("#field-cc-show-functional").prop("checked"),showAnalytics:e.find("#field-cc-show-analytics").prop("checked"),showMarketing:e.find("#field-cc-show-marketing").prop("checked"),consentVersion:e.find("#field-cc-version").val().trim()||"1.0"}},g=e.find("#save-settings-btn");g.prop("disabled",!0);try{await C.settings.save(o),Domma.theme.set(t),E.toast("Settings saved.",{type:"success"})}catch{E.toast("Failed to save settings.",{type:"error"})}finally{g.prop("disabled",!1)}}),e.find("#send-test-email-btn").on("click",async()=>{const t=e.find("#field-test-email-to").val().trim(),s=e.find("#test-email-result").get(0),m=e.find("#send-test-email-btn");m.prop("disabled",!0),s&&(s.textContent="Sending\u2026",s.style.color="");try{const o=await v("/settings/test-email",{method:"POST",body:JSON.stringify({to:t||void 0})});s&&(s.textContent=o.message||"Test email sent.",s.style.color="var(--dm-success,#4ade80)")}catch(o){s&&(s.textContent=o.message||"Failed to send test email.",s.style.color="var(--dm-danger,#f87171)")}finally{m.prop("disabled",!1)}}),e.find("#save-breadcrumbs-btn").on("click",async()=>{const t=e.find("#save-breadcrumbs-btn");t.prop("disabled",!0);try{const s={enabled:e.find("#field-breadcrumbs-enabled").prop("checked"),homeLabel:e.find("#field-breadcrumbs-home-label").val().trim()||"Home",position:e.find("#field-breadcrumbs-position").val()||"TL",offsetX:parseInt(e.find("#field-bc-offset-x").val(),10)||8,offsetY:parseInt(e.find("#field-bc-offset-y").val(),10)||8};await C.settings.save({
|
|
3
|
+
`,g),S=x===-1?o.slice(T):o.slice(T,x+1);if(navigator.clipboard.writeText(S),t.key==="x"){const z=o.slice(0,T),P=x===-1?"":o.slice(x+1);s.value=z+P,s.selectionStart=s.selectionEnd=T,s.dispatchEvent(new Event("input",{bubbles:!0})),E.toast("Line cut",{type:"info",duration:1500})}else E.toast("Line copied",{type:"info",duration:1500})}}),e.find("#save-settings-btn").on("click",async()=>{const t=e.find("#field-admin-theme").val(),s=!l.baseTheme&&e.find("#field-auto-theme-enabled").prop("checked"),m={enabled:s,dayTheme:e.find("#field-day-theme").val()||"charcoal-light",nightTheme:e.find("#field-night-theme").val()||"charcoal-dark",dayStart:e.find("#field-day-start").val()||"07:00",nightStart:e.find("#field-night-start").val()||"19:00"},o={title:e.find("#field-site-title").val().trim(),tagline:e.find("#field-tagline").val().trim(),fontFamily:e.find("#field-font-family").val()||"Roboto",fontSize:parseInt(e.find("#field-font-size").val(),10)||16,theme:l.baseTheme?l.theme:s?m.dayTheme:e.find("#field-theme").val(),...l.baseTheme?{baseTheme:l.baseTheme}:{},autoTheme:m,adminTheme:t,layoutOptions:{spacerSize:parseInt(e.find("#field-spacer-size").val(),10)||40},seo:{defaultTitle:e.find("#field-seo-title").val().trim(),titleSeparator:e.find("#field-seo-separator").val()||" | ",defaultDescription:e.find("#field-seo-desc").val().trim()},footer:{copyright:e.find("#field-footer-copy").val().trim(),links:l.footer?.links||[]},social:{twitter:e.find("#field-social-twitter").val().trim(),facebook:e.find("#field-social-facebook").val().trim(),instagram:e.find("#field-social-instagram").val().trim(),linkedin:e.find("#field-social-linkedin").val().trim(),github:e.find("#field-social-github").val().trim(),youtube:e.find("#field-social-youtube").val().trim()},smtp:{host:e.find("#field-smtp-host").val().trim(),port:parseInt(e.find("#field-smtp-port").val(),10)||587,user:e.find("#field-smtp-user").val().trim(),pass:e.find("#field-smtp-pass").val(),secure:e.find("#field-smtp-secure").prop("checked"),fromAddress:e.find("#field-smtp-from-address").val().trim(),fromName:e.find("#field-smtp-from-name").val().trim()},backToTop:{enabled:e.find("#field-btt-enabled").prop("checked"),scrollThreshold:parseInt(e.find("#field-btt-threshold").val(),10)||300,position:e.find("#field-btt-position").val()||"bottom-right",offset:parseInt(e.find("#field-btt-offset").val(),10)||32,bottomOffset:parseInt(e.find("#field-btt-bottom-offset").val(),10)||32,label:e.find("#field-btt-label").val().trim(),smooth:e.find("#field-btt-smooth").prop("checked")},cookieConsent:{enabled:e.find("#field-cc-enabled").prop("checked"),message:e.find("#field-cc-message").val().trim(),acceptAllText:e.find("#field-cc-accept-all").val().trim(),rejectAllText:e.find("#field-cc-reject-all").val().trim(),customizeText:e.find("#field-cc-customize").val().trim(),savePreferencesText:e.find("#field-cc-save-prefs").val().trim(),privacyPolicyText:e.find("#field-cc-privacy-text").val().trim(),privacyPolicyUrl:e.find("#field-cc-privacy-url").val().trim(),cookiePolicyText:e.find("#field-cc-cookie-text").val().trim(),cookiePolicyUrl:e.find("#field-cc-cookie-url").val().trim(),position:e.find("#field-cc-position").val(),layout:e.find("#field-cc-layout").val(),theme:e.find("#field-cc-theme").val(),showFunctional:e.find("#field-cc-show-functional").prop("checked"),showAnalytics:e.find("#field-cc-show-analytics").prop("checked"),showMarketing:e.find("#field-cc-show-marketing").prop("checked"),consentVersion:e.find("#field-cc-version").val().trim()||"1.0"}},g=e.find("#save-settings-btn");g.prop("disabled",!0);try{await C.settings.save(o),Domma.theme.set(t),E.toast("Settings saved.",{type:"success"})}catch{E.toast("Failed to save settings.",{type:"error"})}finally{g.prop("disabled",!1)}}),e.find("#send-test-email-btn").on("click",async()=>{const t=e.find("#field-test-email-to").val().trim(),s=e.find("#test-email-result").get(0),m=e.find("#send-test-email-btn");m.prop("disabled",!0),s&&(s.textContent="Sending\u2026",s.style.color="");try{const o=await v("/settings/test-email",{method:"POST",body:JSON.stringify({to:t||void 0})});s&&(s.textContent=o.message||"Test email sent.",s.style.color="var(--dm-success,#4ade80)")}catch(o){s&&(s.textContent=o.message||"Failed to send test email.",s.style.color="var(--dm-danger,#f87171)")}finally{m.prop("disabled",!1)}}),e.find("#save-breadcrumbs-btn").on("click",async()=>{const t=e.find("#save-breadcrumbs-btn");t.prop("disabled",!0);try{const s={enabled:e.find("#field-breadcrumbs-enabled").prop("checked"),homeLabel:e.find("#field-breadcrumbs-home-label").val().trim()||"Home",position:e.find("#field-breadcrumbs-position").val()||"TL",offsetX:parseInt(e.find("#field-bc-offset-x").val(),10)||8,offsetY:parseInt(e.find("#field-bc-offset-y").val(),10)||8};await C.settings.save({breadcrumbs:s}),l.breadcrumbs=s,E.toast("Breadcrumbs settings saved.",{type:"success"})}catch{E.toast("Failed to save breadcrumbs settings.",{type:"error"})}finally{t.prop("disabled",!1)}}),e.find("#save-css-btn").on("click",async()=>{const t=a?a.getValue():e.find("#field-custom-css").val(),s=e.find("#save-css-btn");s.prop("disabled",!0);try{await v("/settings/custom-css",{method:"PUT",body:JSON.stringify({css:t})}),E.toast("Custom CSS saved.",{type:"success"})}catch(m){E.toast(m.message||"Failed to save CSS.",{type:"error"})}finally{s.prop("disabled",!1)}});async function r(){try{const t=await v("/cache/status"),s=t.enabled?`enabled (${t.driver})`:"disabled",m=t.maxItems?` \xB7 ${t.size} / ${t.maxItems} entries`:"";e.find("#cache-status").text(s+m)}catch{e.find("#cache-status").text("unknown")}try{const{entries:t}=await v("/cache/keys");A(e.find("#cache-keys-table"),t),e.find("#cache-key-count").text(`(${t.length})`)}catch(t){e.find("#cache-keys-table").get(0).textContent="Failed to load keys: "+t.message}}await r(),e.find("#btn-clear-cache").on("click",async()=>{if(!await E.confirm("Clear the entire response cache?"))return;const t=e.find("#btn-clear-cache");t.prop("disabled",!0);try{await v("/cache/clear",{method:"POST"}),E.toast("Cache cleared.",{type:"success"}),await r()}catch(s){E.toast(s.message||"Failed to clear cache.",{type:"error"})}finally{t.prop("disabled",!1)}}),e.find("#btn-refresh-cache-keys").on("click",r)}};function A(e,k){const l=e.get(0);if(!l)return;for(;l.firstChild;)l.removeChild(l.firstChild);if(!k.length){const f=document.createElement("p");f.className="text-muted",f.style.fontSize=".875rem",f.textContent="Cache is empty.",l.appendChild(f);return}const p=[...k].reverse(),h=document.createElement("table");h.className="table table-compact";const b=document.createElement("thead"),y=document.createElement("tr");for(const f of["Key","Tags","Expires"]){const i=document.createElement("th");i.textContent=f,y.appendChild(i)}b.appendChild(y),h.appendChild(b);const u=document.createElement("tbody");for(const f of p){const i=document.createElement("tr"),d=document.createElement("td");d.style.fontFamily="monospace",d.style.fontSize="12px",d.textContent=f.key,i.appendChild(d);const n=document.createElement("td");n.style.fontSize="12px";for(const a of f.tags){const r=document.createElement("span");r.className="badge",r.style.marginRight="4px",r.style.fontSize="11px",r.textContent=a,n.appendChild(r)}i.appendChild(n);const c=document.createElement("td");if(c.style.fontSize="12px",c.style.color="var(--dm-text-muted, #6b7280)",f.expiresAt===null)c.textContent="no expiry";else{const a=f.expiresAt-Date.now();a<=0?c.textContent="expired":a<6e4?c.textContent=`${Math.round(a/1e3)}s`:a<36e5?c.textContent=`${Math.round(a/6e4)}m`:c.textContent=`${Math.round(a/36e5)}h`}i.appendChild(c),u.appendChild(i)}h.appendChild(u),l.appendChild(h)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{api as a}from"../api.js";function
|
|
1
|
+
import{api as a,getUser as N}from"../api.js";function I(c){return{string:"string",email:"email",tel:"string",number:"number",textarea:"textarea",select:"select",radio:"select",checkbox:"boolean","checkbox-group":"select",date:"string",time:"string",url:"string",hidden:"string"}[c]||"string"}export const userEditorView={templateUrl:"/admin/js/templates/user-editor.html",async onMount(c){const b=window.location.hash.match(/#\/users\/edit\/([^/]+)/),u=b?b[1]:null,r=!!u,C=E.loader(c.get(0),{type:"dots"});let o,m,y,v;try{[o,m,y,v]=await Promise.all([r?a.users.get(u).catch(()=>null):Promise.resolve(null),a.collections.listEntries("roles",{limit:100}).catch(()=>null),a.collections.get("user-profiles").catch(()=>null),a.projects.list().catch(()=>[])])}finally{C.destroy()}if(r&&!o){E.toast("User not found.",{type:"error"}),R.navigate("/users");return}c.find("#editor-title").text(r?"Edit User":"New User"),c.find("#cancel-btn").on("click",()=>R.navigate("/users"));const O=N(),h=e=>(m?.entries||[]).find(i=>i.data.name===e)?.data?.level??99,B=h(O?.role),j=(m?.entries||[]).filter(e=>(e.data.level??99)>B||r&&e.data.name===o?.role).map(e=>({value:e.data.name,label:e.data.label})).sort((e,i)=>h(e.value)-h(i.value)),S=[{value:"admin",label:"Admin"},{value:"user",label:"User"}],A=j.length>0?j:S,P=(v||[]).map(e=>({value:e.slug,label:e.name||e.slug})),q=[{value:"",label:"(none)"},...P],L={name:{type:"string",required:!0,minLength:2,label:"Full Name",formConfig:{placeholder:"Jane Smith"}},email:{type:"email",required:!0,label:"Email Address",formConfig:{placeholder:"jane@example.com"}},role:{type:"select",required:!0,label:"Primary role",options:A,formConfig:{tooltip:"Shown as the user's badge. Used for hierarchy comparisons."}},additionalRoles:{type:"multiselect",label:"Additional roles",options:A,formConfig:{placeholder:"",tooltip:"Optional \u2014 extra roles whose permissions also apply. Useful for someone who is both, say, a recruiter AND a content editor."}},ownedByProject:{type:"select",label:"Owned by project",options:q,formConfig:{tooltip:"Administrative tag \u2014 controls where this user appears in the sidebar Projects section. Mapped to meta.project."}},projects:{type:"multiselect",label:"Project access",options:P,formConfig:{placeholder:"",tooltip:"Access scope \u2014 limits which artefacts the user can see and edit. Leave empty for no restriction."}},isActive:{type:"boolean",label:"Active account"},password:{type:"password",required:!r,minLength:r?0:8,label:r?"New Password":"Password",formConfig:{placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",autocomplete:"new-password",tooltip:r?"Leave blank to keep current password":"Minimum 8 characters"}}},T=o?{name:o.name||"",email:o.email||"",role:o.role||"user",additionalRoles:Array.isArray(o.additionalRoles)?o.additionalRoles:[],ownedByProject:o.meta?.project||"",projects:Array.isArray(o.projects)?o.projects:[],isActive:o.isActive!==!1}:{role:"user",additionalRoles:[],ownedByProject:"",projects:[],isActive:!0};F.render("#user-form-container",L,T,{layout:"stacked",submitText:r?"Update User":"Create User",onSubmit:async e=>{if(r&&e.password&&e.password.length<8)return E.toast("Password must be at least 8 characters.",{type:"warning"}),!1;const t=(Array.isArray(e.additionalRoles)?e.additionalRoles:[]).filter(s=>s&&s!==e.role).filter((s,d,n)=>n.indexOf(s)===d),l=(Array.isArray(e.projects)?e.projects:[]).filter(Boolean).filter((s,d,n)=>n.indexOf(s)===d),p=e.ownedByProject||null,g={name:e.name,email:e.email,role:e.role,additionalRoles:t,projects:l,meta:{...o?.meta||{},project:p},isActive:!!e.isActive};e.password&&(g.password=e.password);try{let s;r?s=await a.users.update(u,g):s=await a.users.create(g);const d=y?.fields||[];if(d.length>0){const n={};for(const w of d)w.name in e&&(n[w.name]=e[w.name]);const x=r?u:s?.id;x&&Object.keys(n).length>0&&await a.users.update(x,{profile:n})}E.toast("User saved successfully.",{type:"success"}),R.navigate("/users")}catch(s){return E.toast(`Save failed: ${s.message||"Unknown error"}`,{type:"error"}),!1}}});const U=y?.fields||[];if(U.length>0){c.find("#profile-card").show();const e={},i={};for(const t of U){const l={type:I(t.type),label:t.label||t.name};t.required&&(l.required=!0),t.placeholder&&(l.formConfig={placeholder:t.placeholder}),t.options&&Array.isArray(t.options)&&(l.options=t.options.map(p=>typeof p=="string"?{value:p,label:p}:p)),e[t.name]=l,i[t.name]=o?.profile?.[t.name]??""}F.render("#profile-form-container",e,i,{layout:"stacked",submitText:r?"Update Profile":"Save Profile",onSubmit:async t=>{const f=u;if(!f)return E.toast("Save the user first, then update the profile.",{type:"info"}),!1;try{await a.users.update(f,{profile:t}),E.toast("Profile saved.",{type:"success"})}catch(l){return E.toast(`Profile save failed: ${l.message||"Unknown error"}`,{type:"error"}),!1}}})}Domma.icons.scan()}};
|