domma-cms 0.36.1 → 0.36.2
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/css/admin.css +1 -1
- package/admin/css/dashboard.css +1 -1
- package/admin/js/lib/card-builder.js +1 -1
- package/admin/js/lib/effects-builder.js +2 -2
- package/admin/js/lib/embed-builder.js +1 -1
- package/admin/js/lib/markdown-toolbar.js +2 -2
- package/admin/js/lib/project-quick-create.js +2 -2
- package/admin/js/lib/scribe-composer.js +3 -3
- package/admin/js/lib/shortcode-context-menu.js +6 -6
- package/admin/js/lib/shortcode-modal.js +1 -1
- package/admin/js/lib/sidebar-renderer.js +2 -2
- package/admin/js/lib/timeline-builder.js +2 -2
- package/admin/js/templates/api-endpoint-editor.html +2 -2
- package/admin/js/templates/block-editor.html +1 -1
- package/admin/js/templates/component-editor.html +4 -4
- package/admin/js/templates/docs/api-actions.html +11 -11
- package/admin/js/templates/docs/api-authentication.html +11 -11
- package/admin/js/templates/docs/api-collections.html +11 -11
- package/admin/js/templates/docs/api-layouts.html +11 -11
- package/admin/js/templates/docs/api-media.html +11 -11
- package/admin/js/templates/docs/api-navigation.html +11 -11
- package/admin/js/templates/docs/api-pages.html +11 -11
- package/admin/js/templates/docs/api-plugins.html +11 -11
- package/admin/js/templates/docs/api-settings.html +11 -11
- package/admin/js/templates/docs/api-users.html +11 -11
- package/admin/js/templates/docs/api-views.html +11 -11
- package/admin/js/templates/form-editor.html +4 -4
- package/admin/js/templates/form-submissions.html +1 -1
- package/admin/js/templates/navigation.html +3 -3
- package/admin/js/templates/notifications.html +1 -1
- package/admin/js/templates/page-editor.html +3 -3
- package/admin/js/templates/project-detail.html +19 -19
- package/admin/js/templates/settings.html +6 -6
- package/admin/js/views/action-editor.js +1 -1
- package/admin/js/views/api-endpoint-editor.js +3 -3
- package/admin/js/views/collection-editor.js +2 -2
- package/admin/js/views/collection-entries.js +1 -1
- package/admin/js/views/component-editor.js +3 -3
- package/admin/js/views/form-editor.js +3 -3
- package/admin/js/views/form-submissions.js +1 -1
- package/admin/js/views/page-editor.js +4 -4
- package/admin/js/views/project-detail.js +1 -1
- package/admin/js/views/settings.js +2 -2
- package/package.json +1 -1
- package/public/css/forms.css +1 -1
- package/public/css/site.css +1 -1
- package/public/js/menu-decor.mjs +1 -1
- package/public/js/site.js +1 -1
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
gap: 1.25rem;
|
|
8
8
|
align-items: flex-start;
|
|
9
9
|
padding: 1.5rem;
|
|
10
|
-
border: 1px solid var(--border
|
|
10
|
+
border: 1px solid var(--dm-border, rgba(255,255,255,.08));
|
|
11
11
|
border-radius: 14px;
|
|
12
12
|
background:
|
|
13
|
-
radial-gradient(120% 160% at 0% 0%,
|
|
14
|
-
var(--card-bg, rgba(255,255,255,.04));
|
|
13
|
+
radial-gradient(120% 160% at 0% 0%, color-mix(in srgb, var(--dm-primary) 12%, transparent), transparent 55%),
|
|
14
|
+
var(--dm-card-bg, rgba(255,255,255,.04));
|
|
15
15
|
position: relative;
|
|
16
16
|
overflow: hidden;
|
|
17
17
|
}
|
|
@@ -20,16 +20,16 @@
|
|
|
20
20
|
position: absolute;
|
|
21
21
|
top: 0; bottom: 0; left: 0;
|
|
22
22
|
width: 4px;
|
|
23
|
-
background: linear-gradient(180deg,
|
|
23
|
+
background: linear-gradient(180deg, var(--dm-primary), var(--dm-primary-dark));
|
|
24
24
|
}
|
|
25
25
|
.pd-hero-icon {
|
|
26
26
|
width: 64px; height: 64px;
|
|
27
27
|
flex-shrink: 0;
|
|
28
28
|
border-radius: 16px;
|
|
29
29
|
display: flex; align-items: center; justify-content: center;
|
|
30
|
-
background: linear-gradient(135deg,
|
|
31
|
-
color:
|
|
32
|
-
box-shadow: 0 6px 18px
|
|
30
|
+
background: linear-gradient(135deg, color-mix(in srgb, var(--dm-primary) 28%, transparent), color-mix(in srgb, var(--dm-primary-dark) 28%, transparent));
|
|
31
|
+
color: var(--dm-primary);
|
|
32
|
+
box-shadow: 0 6px 18px color-mix(in srgb, var(--dm-primary) 18%, transparent);
|
|
33
33
|
}
|
|
34
34
|
.pd-hero-icon svg, .pd-hero-icon span[data-icon] { width: 32px; height: 32px; }
|
|
35
35
|
.pd-hero-main { flex: 1; min-width: 0; }
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
}
|
|
40
40
|
.pd-title { font-size: 1.6rem; font-weight: 700; margin: 0; line-height: 1.15; }
|
|
41
41
|
.pd-actions { display: flex; gap: .5rem; flex-shrink: 0; }
|
|
42
|
-
.pd-desc { color: var(--text-muted, #888); margin: .5rem 0 0; max-width: 64ch; }
|
|
42
|
+
.pd-desc { color: var(--dm-text-muted, #888); margin: .5rem 0 0; max-width: 64ch; }
|
|
43
43
|
.pd-desc:empty { display: none; }
|
|
44
44
|
|
|
45
45
|
/* ── Meta badges ──────────────────────────────────────────────────── */
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
border: 1px solid transparent;
|
|
53
53
|
}
|
|
54
54
|
.pd-meta-badge svg, .pd-meta-badge span[data-icon] { width: 14px; height: 14px; }
|
|
55
|
-
.pd-meta-k { color: var(--text-muted, #999); font-weight: 500; }
|
|
55
|
+
.pd-meta-k { color: var(--dm-text-muted, #999); font-weight: 500; }
|
|
56
56
|
.pd-meta-v { font-weight: 600; }
|
|
57
57
|
|
|
58
58
|
/* ── Section header ───────────────────────────────────────────────── */
|
|
@@ -65,10 +65,10 @@
|
|
|
65
65
|
display: flex; align-items: center; gap: .5rem;
|
|
66
66
|
}
|
|
67
67
|
.pd-section-title svg, .pd-section-title span[data-icon] {
|
|
68
|
-
width: 17px; height: 17px; color: var(--text-muted, #888);
|
|
68
|
+
width: 17px; height: 17px; color: var(--dm-text-muted, #888);
|
|
69
69
|
}
|
|
70
|
-
.pd-total { font-size: .8rem; color: var(--text-muted, #888); }
|
|
71
|
-
.pd-hint { color: var(--text-muted, #888); margin: 0 0 .9rem; font-size: .82rem; }
|
|
70
|
+
.pd-total { font-size: .8rem; color: var(--dm-text-muted, #888); }
|
|
71
|
+
.pd-hint { color: var(--dm-text-muted, #888); margin: 0 0 .9rem; font-size: .82rem; }
|
|
72
72
|
|
|
73
73
|
/* ── Artefact stat tiles (each with a + to create one) ────────────── */
|
|
74
74
|
.pd-tiles {
|
|
@@ -78,15 +78,15 @@
|
|
|
78
78
|
}
|
|
79
79
|
.pd-tile {
|
|
80
80
|
position: relative;
|
|
81
|
-
border: 1px solid var(--border
|
|
81
|
+
border: 1px solid var(--dm-border, rgba(255,255,255,.08));
|
|
82
82
|
border-left: 3px solid var(--c, #5b8cff);
|
|
83
83
|
border-radius: 11px;
|
|
84
|
-
background: var(--card-bg, rgba(255,255,255,.04));
|
|
84
|
+
background: var(--dm-card-bg, rgba(255,255,255,.04));
|
|
85
85
|
transition: transform .14s ease, box-shadow .14s ease, border-color .14s ease;
|
|
86
86
|
}
|
|
87
87
|
.pd-tile--link:hover {
|
|
88
88
|
transform: translateY(-2px);
|
|
89
|
-
box-shadow:
|
|
89
|
+
box-shadow: var(--dm-shadow-lg);
|
|
90
90
|
border-color: var(--c, #5b8cff);
|
|
91
91
|
}
|
|
92
92
|
.pd-tile-link {
|
|
@@ -104,15 +104,15 @@
|
|
|
104
104
|
.pd-tile-body { display: flex; flex-direction: column; min-width: 0; }
|
|
105
105
|
.pd-tile-count { font-size: 1.5rem; font-weight: 700; line-height: 1; }
|
|
106
106
|
.pd-tile-label {
|
|
107
|
-
font-size: .78rem; color: var(--text-muted, #888);
|
|
107
|
+
font-size: .78rem; color: var(--dm-text-muted, #888);
|
|
108
108
|
margin-top: .2rem; text-transform: uppercase; letter-spacing: .03em;
|
|
109
109
|
}
|
|
110
110
|
.pd-tile-add {
|
|
111
111
|
position: absolute; top: 8px; right: 8px;
|
|
112
112
|
width: 26px; height: 26px; border-radius: 7px;
|
|
113
113
|
display: flex; align-items: center; justify-content: center;
|
|
114
|
-
border: 1px solid var(--border
|
|
115
|
-
background: var(--card-bg, rgba(255,255,255,.05));
|
|
114
|
+
border: 1px solid var(--dm-border, rgba(255,255,255,.12));
|
|
115
|
+
background: var(--dm-card-bg, rgba(255,255,255,.05));
|
|
116
116
|
color: var(--c, #5b8cff);
|
|
117
117
|
cursor: pointer; opacity: .7;
|
|
118
118
|
transition: opacity .14s ease, background .14s ease, border-color .14s ease, transform .14s ease;
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
.pd-tile-add:hover {
|
|
121
121
|
opacity: 1; transform: scale(1.08);
|
|
122
122
|
border-color: var(--c, #5b8cff);
|
|
123
|
-
background:
|
|
123
|
+
background: var(--dm-hover-bg);
|
|
124
124
|
}
|
|
125
125
|
.pd-tile-add svg, .pd-tile-add span[data-icon] { width: 15px; height: 15px; }
|
|
126
126
|
</style>
|
|
@@ -226,7 +226,7 @@
|
|
|
226
226
|
<input id="field-smtp-from-name" type="text" class="form-input" placeholder="My Website">
|
|
227
227
|
</div>
|
|
228
228
|
</div>
|
|
229
|
-
<div style="border-top:1px solid var(--border
|
|
229
|
+
<div style="border-top:1px solid var(--dm-border,#333);padding-top:1rem;">
|
|
230
230
|
<p class="form-label" style="margin-bottom:.5rem;">Send Test Email</p>
|
|
231
231
|
<p class="text-muted" style="font-size:.8rem;margin-bottom:.75rem;">Save your SMTP settings first, then send a test to verify the configuration.</p>
|
|
232
232
|
<div style="display:flex;gap:.5rem;align-items:center;flex-wrap:wrap;">
|
|
@@ -421,27 +421,27 @@
|
|
|
421
421
|
<!-- Corner picker -->
|
|
422
422
|
<div class="col-auto" style="margin-right:1.5rem;margin-bottom:1rem;">
|
|
423
423
|
<div id="bc-pos-grid"
|
|
424
|
-
style="display:grid;grid-template-columns:1fr 1fr;gap:2px;border:1px solid var(--border
|
|
424
|
+
style="display:grid;grid-template-columns:1fr 1fr;gap:2px;border:1px solid var(--dm-border,rgba(255,255,255,.1));border-radius:10px;overflow:hidden;background:var(--dm-border,rgba(255,255,255,.07));width:fit-content;">
|
|
425
425
|
<button type="button" class="bc-pos-btn" data-pos="TL"
|
|
426
|
-
style="display:flex;flex-direction:column;align-items:flex-start;padding:.55rem .8rem;background:none;border:none;cursor:pointer;font-size:.78rem;color:var(--text-muted,#888);border-right:1px solid var(--border
|
|
426
|
+
style="display:flex;flex-direction:column;align-items:flex-start;padding:.55rem .8rem;background:none;border:none;cursor:pointer;font-size:.78rem;color:var(--dm-text-muted,#888);border-right:1px solid var(--dm-border,rgba(255,255,255,.08));border-bottom:1px solid var(--dm-border,rgba(255,255,255,.08));border-radius:0;gap:.05rem;user-select:none;transition:background .15s,color .15s;min-width:72px;">
|
|
427
427
|
<span
|
|
428
428
|
style="font-size:.6rem;opacity:.55;letter-spacing:.06em;text-transform:uppercase;line-height:1.2;">Top</span>
|
|
429
429
|
<span style="font-weight:600;">↖ Left</span>
|
|
430
430
|
</button>
|
|
431
431
|
<button type="button" class="bc-pos-btn" data-pos="TR"
|
|
432
|
-
style="display:flex;flex-direction:column;align-items:flex-end;padding:.55rem .8rem;background:none;border:none;cursor:pointer;font-size:.78rem;color:var(--text-muted,#888);border-bottom:1px solid var(--border
|
|
432
|
+
style="display:flex;flex-direction:column;align-items:flex-end;padding:.55rem .8rem;background:none;border:none;cursor:pointer;font-size:.78rem;color:var(--dm-text-muted,#888);border-bottom:1px solid var(--dm-border,rgba(255,255,255,.08));border-radius:0;gap:.05rem;user-select:none;transition:background .15s,color .15s;min-width:72px;">
|
|
433
433
|
<span
|
|
434
434
|
style="font-size:.6rem;opacity:.55;letter-spacing:.06em;text-transform:uppercase;line-height:1.2;">Top</span>
|
|
435
435
|
<span style="font-weight:600;">Right ↗</span>
|
|
436
436
|
</button>
|
|
437
437
|
<button type="button" class="bc-pos-btn" data-pos="BL"
|
|
438
|
-
style="display:flex;flex-direction:column;align-items:flex-start;padding:.55rem .8rem;background:none;border:none;cursor:pointer;font-size:.78rem;color:var(--text-muted,#888);border-right:1px solid var(--border
|
|
438
|
+
style="display:flex;flex-direction:column;align-items:flex-start;padding:.55rem .8rem;background:none;border:none;cursor:pointer;font-size:.78rem;color:var(--dm-text-muted,#888);border-right:1px solid var(--dm-border,rgba(255,255,255,.08));border-radius:0;gap:.05rem;user-select:none;transition:background .15s,color .15s;min-width:72px;">
|
|
439
439
|
<span style="font-weight:600;">↙ Left</span>
|
|
440
440
|
<span
|
|
441
441
|
style="font-size:.6rem;opacity:.55;letter-spacing:.06em;text-transform:uppercase;line-height:1.2;">Bottom</span>
|
|
442
442
|
</button>
|
|
443
443
|
<button type="button" class="bc-pos-btn" data-pos="BR"
|
|
444
|
-
style="display:flex;flex-direction:column;align-items:flex-end;padding:.55rem .8rem;background:none;border:none;cursor:pointer;font-size:.78rem;color:var(--text-muted,#888);border-radius:0;gap:.05rem;user-select:none;transition:background .15s,color .15s;min-width:72px;">
|
|
444
|
+
style="display:flex;flex-direction:column;align-items:flex-end;padding:.55rem .8rem;background:none;border:none;cursor:pointer;font-size:.78rem;color:var(--dm-text-muted,#888);border-radius:0;gap:.05rem;user-select:none;transition:background .15s,color .15s;min-width:72px;">
|
|
445
445
|
<span style="font-weight:600;">Right ↘</span>
|
|
446
446
|
<span
|
|
447
447
|
style="font-size:.6rem;opacity:.55;letter-spacing:.06em;text-transform:uppercase;line-height:1.2;">Bottom</span>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{api as _}from"../api.js";let N=null,O={};async function D(e){const t=e.find("#action-project").get(0);if(t)try{(await _.projects.list()).forEach(a=>{const n=document.createElement("option");n.value=a.slug,n.textContent=a.name||a.slug,t.appendChild(n)})}catch{}}const $={deleteEntry:[],moveToCollection:[{name:"targetCollection",label:"Target Collection Slug",placeholder:"e.g. archived-applications"}],webhook:[{name:"url",label:"URL",placeholder:"https://hooks.example.com/notify"},{name:"method",label:"Method",placeholder:"POST"},{name:"body",label:"Body (JSON)",placeholder:'{"email": "{{entry.data.email}}"}',multiline:!0}],email:[{name:"to",label:"To",placeholder:"{{entry.data.email}}"},{name:"subject",label:"Subject",placeholder:"Your application update"},{name:"template",label:"Body",placeholder:"Your application has been approved.",multiline:!0}]};export const actionEditorView={templateUrl:"/admin/js/templates/action-editor.html",async onMount(e){N=null,O={};const t=window.location.hash.match(/\/actions\/edit\/([^/?#]+)/);t&&(N=t[1]),E.tabs(e.find("#action-editor-tabs").get(0)),await I(e),await P(e),await D(e),N&&(e.find("#action-editor-title").text("Edit Action"),await B(e,N)),e.find("#add-step-btn").off("click").on("click",()=>{const l=e.find("#add-step-type").val()||"updateField";J(e,{type:l,config:{}})}),e.find("#save-action-btn").off("click").on("click",async()=>{await Y(e)}),U(e),Domma.icons.scan()}};async function I(e){const t=e.find("#action-collection").get(0);if(t)try{(await _.collections.list()).forEach(l=>{const a=document.createElement("option");a.value=l.slug,a.textContent=`${l.title} (${l.slug})`,t.appendChild(a)})}catch{}}async function P(e){const t=e.find("#action-roles-checkboxes").get(0);t&&["admin","manager","editor","subscriber"].forEach(l=>{const a=document.createElement("label");a.style.cssText="display:flex;align-items:center;gap:.5rem;cursor:pointer;";const n=document.createElement("input");n.type="checkbox",n.value=l,n.dataset.role=l,n.className="action-role-cb",n.checked=l==="admin",a.appendChild(n),a.appendChild(document.createTextNode(l)),t.appendChild(a)})}async function B(e,t){try{const l=await _.actions.get(t);if(!l){E.toast("Action not found.",{type:"error"}),R.navigate("/actions");return}K(e,l)}catch(l){E.toast(l.message||"Failed to load action.",{type:"error"}),R.navigate("/actions")}}function U(e){const t=e.find("#action-rowlevel-enabled").get(0),l=e.find("#action-rowlevel-config").get(0),a=e.find("#action-rowlevel-mode").get(0),n=e.find("#action-rowlevel-field-group").get(0);t&&(t.addEventListener("change",()=>{l&&(l.style.display=t.checked?"flex":"none")}),a&&a.addEventListener("change",()=>{n&&(n.style.display=a.value==="field"?"":"none")}))}function K(e,t){e.find("#action-title").val(t.title||""),e.find("#action-slug").val(t.slug||""),e.find("#action-description").val(t.description||""),e.find("#action-collection").val(t.collection||""),O=t.meta||{},e.find("#action-project").val(t.meta?.project||""),e.find("#action-bundled").prop("checked",!!t.bundled),e.find("#action-trigger-type").val(t.trigger?.type||"manual"),e.find("#action-trigger-label").val(t.trigger?.label||"Run"),e.find("#action-trigger-icon").val(t.trigger?.icon||"zap"),e.find("#action-trigger-confirm").val(t.trigger?.confirmMessage||"");const l=t.access?.roles||["admin"];e.find(".action-role-cb").each(function(){this.checked=l.includes(this.value)});const a=t.access?.rowLevel;a&&(e.find("#action-rowlevel-enabled").prop("checked",!0),e.find("#action-rowlevel-config").css("display","flex"),e.find("#action-rowlevel-mode").val(a.mode||"owner"),e.find("#action-rowlevel-userkey").val(a.userKey||"id"),a.mode==="field"&&(e.find("#action-rowlevel-field-group").css("display",""),e.find("#action-rowlevel-field").val(a.field||"")));const n=e.find("#action-steps-list").get(0);if(n){const c=n.querySelector(".steps-empty-placeholder");for(c&&c.remove();n.firstChild;)n.removeChild(n.firstChild);const i=document.createElement("p");i.className="text-muted steps-empty-placeholder",i.textContent="No steps yet. Add a step to define what this action does.",i.style.cssText="text-align:center;padding:2rem 0;",n.appendChild(i)}(t.steps||[]).forEach(c=>J(e,c))}function J(e,t){const l=e.find("#action-steps-list").get(0);if(!l)return;const a=l.querySelector(".steps-empty-placeholder");a&&a.remove();const n=$[t.type]||[],c=document.createElement("div");c.className="card mb-2 step-card",c.dataset.stepType=t.type;const i=document.createElement("div");i.className="card-header",i.style.cssText="display:flex;align-items:center;gap:.5rem;";const p=document.createElement("code");p.textContent=t.type,p.style.cssText="flex:1;font-size:.85rem;";const r=document.createElement("button");r.type="button",r.className="btn btn-sm btn-danger";const f=document.createElement("span");f.setAttribute("data-icon","trash"),r.appendChild(f),r.addEventListener("click",()=>{if(c.remove(),!l.querySelector(".step-card")){const o=document.createElement("p");o.className="text-muted steps-empty-placeholder",o.textContent="No steps yet. Add a step to define what this action does.",o.style.cssText="text-align:center;padding:2rem 0;",l.appendChild(o)}}),i.appendChild(p),i.appendChild(r);const h=document.createElement("div");if(h.className="card-body",n.length===0){const o=document.createElement("p");o.className="text-muted",o.textContent=t.type==="deleteEntry"?"This step deletes the entry. No configuration required.":"No additional configuration required.",o.style.margin="0",h.appendChild(o)}t.type==="updateField"?G(h,t,e):n.forEach(o=>{const g=document.createElement("div");g.style.cssText="margin-bottom:.75rem;";const b=document.createElement("label");b.className="form-label",b.textContent=o.label,g.appendChild(b);let m;o.multiline?(m=document.createElement("textarea"),m.rows=3,m.style.cssText="font-family:monospace;font-size:.8rem;resize:vertical;",m.value=typeof t.config?.[o.name]=="object"?JSON.stringify(t.config[o.name],null,2):t.config?.[o.name]??""):(m=document.createElement("input"),m.type="text",m.value=t.config?.[o.name]??""),m.className=`form-input step-field-${o.name}`,m.placeholder=o.placeholder||"",m.dataset.field=o.name,g.appendChild(m),h.appendChild(g)}),c.appendChild(i),c.appendChild(h),l.appendChild(c),Domma.icons.scan(c)}async function G(e,t,l){const a=l.find("#action-collection").val(),n=document.createElement("div");n.style.cssText="margin-bottom:.75rem;";const c=document.createElement("label");c.className="form-label",c.textContent="Field",n.appendChild(c);const i=document.createElement("select");i.className="form-input step-field-field",i.dataset.field="field";const p=document.createElement("input");p.type="text",p.className="form-input mt-2 step-field-field-custom",p.placeholder="Field name, e.g. status",p.style.display="none";const r=document.createElement("div");r.style.cssText="margin-bottom:.75rem;";const f=document.createElement("label");f.className="form-label",f.textContent="New Value",r.appendChild(f);const h=document.createElement("div");r.appendChild(h);const o=document.createElement("button");o.type="button",o.className="btn btn-ghost btn-sm mt-2",o.style.cssText="font-size:.75rem;padding:.2rem .5rem;",o.textContent="Template Variables";const g=document.createElement("div");g.style.cssText="display:none;background:var(--dm-surface-2,#1e1e2e);border-radius:.4rem;padding:.75rem;margin-top:.5rem;font-size:.8rem;line-height:1.7;";const b=document.createElement("strong");b.textContent="Available template variables:",g.appendChild(b);const m=document.createElement("table");m.style.cssText="width:100%;border-collapse:collapse;margin-top:.4rem;",[["{{now}}","Current ISO timestamp"],["{{user.id}}","Executing user's ID"],["{{user.name}}","Executing user's name"],["{{user.email}}","Executing user's email"],["{{entry.id}}","Entry's unique ID"],["{{entry.data.fieldName}}","Any field value from the entry"],["{{env.CMS_PUBLIC_*}}","Public environment variables"]].forEach(([d,u])=>{const x=document.createElement("tr"),w=document.createElement("td");w.style.cssText="padding:.2rem .5rem .2rem 0;opacity:.7;white-space:nowrap;";const T=document.createElement("code");T.textContent=d,w.appendChild(T);const s=document.createElement("td");s.textContent=u,x.appendChild(w),x.appendChild(s),m.appendChild(x)}),g.appendChild(m),o.addEventListener("click",()=>{const d=g.style.display!=="none";g.style.display=d?"none":"",o.textContent=d?"Template Variables":"Hide Variables"}),r.appendChild(o),r.appendChild(g),e.appendChild(n),e.appendChild(r);let S=[];if(a)try{S=(await _.collections.get(a)).fields||[]}catch{}const j=document.createElement("option");j.value="",j.textContent=S.length?"\u2014 select a field \u2014":"\u2014 no fields available \u2014",i.appendChild(j),S.forEach(d=>{const u=document.createElement("option");u.value=d.name,u.textContent=`${d.label} (${d.name})`,u.dataset.fieldType=d.type,u.dataset.fieldOptions=d.type==="select"?JSON.stringify(d.options||[]):"",i.appendChild(u)});const q=document.createElement("option");q.value="__custom__",q.textContent="\u2014 enter manually \u2014",i.appendChild(q);const C=t.config?.field||"",z=C&&[...i.options].find(d=>d.value===C);z?i.value=C:C&&(i.value="__custom__",p.value=C,p.style.display=""),n.appendChild(i),n.appendChild(p);function M(d,u){h.textContent="";const x=d?[...i.options].find(s=>s.value===d):null,w=x?.dataset.fieldType==="select",T=w?JSON.parse(x.dataset.fieldOptions||"[]"):[];if(w&&T.length){const s=document.createElement("select");s.className="form-input step-field-value",s.dataset.field="value";const A=document.createElement("option");A.value="",A.textContent="\u2014 select a value \u2014",s.appendChild(A),T.forEach(y=>{const k=typeof y=="string"?y:y.value??"",V=typeof y=="string"?y:y.label||y.value||k;if(!k||k==="undefined")return;const F=document.createElement("option");F.value=k,F.textContent=V,s.appendChild(F)});const L=document.createElement("option");L.value="__custom__",L.textContent="\u2014 enter manually \u2014",s.appendChild(L);const v=document.createElement("input");v.type="text",v.className="form-input mt-2",v.placeholder="e.g. approved or {{now}}",v.style.display="none",u&&[...s.options].find(y=>y.value===u&&y.value!=="__custom__")?s.value=u:u&&(s.value="__custom__",v.value=u,v.style.display=""),s.addEventListener("change",()=>{const y=s.value==="__custom__";v.style.display=y?"":"none",y||(v.value="")}),h.appendChild(s),h.appendChild(v)}else{const s=document.createElement("input");s.type="text",s.className="form-input step-field-value",s.dataset.field="value",s.placeholder="e.g. approved or {{now}}",s.value=u||"",h.appendChild(s)}}M(z?C:null,t.config?.value||""),i.addEventListener("change",()=>{const d=i.value==="__custom__";p.style.display=d?"":"none",d||(p.value=""),M(d?null:i.value,"")})}function H(e){const t=[];return e.find(".step-card").each(function(){const l=this.dataset.stepType,a={};if(l==="updateField"){let n=this.querySelector(".step-field-field")?.value?.trim()||"";n==="__custom__"&&(n=this.querySelector(".step-field-field-custom")?.value?.trim()||""),a.field=n;const c=this.querySelector(".step-field-value");let i=c?.value?.trim()||"";i==="__custom__"&&(i=c?.nextElementSibling?.value?.trim()||""),a.value=i}else($[l]||[]).forEach(n=>{const c=this.querySelector(`.step-field-${n.name}`);if(!c)return;const i=c.value.trim();if(n.multiline&&i)try{a[n.name]=JSON.parse(i)}catch{a[n.name]=i}else a[n.name]=i});t.push({type:l,config:a})}),t}async function Y(e){const t=e.find("#action-title").val().trim();if(!t){E.toast("Title is required.",{type:"warning"});return}const l=e.find("#action-collection").val();if(!l){E.toast("Target collection is required (General tab).",{type:"warning"});return}const a=[];e.find(".action-role-cb:checked").each(function(){a.push(this.value)});const n=e.find("#action-rowlevel-enabled").is(":checked");let c=null;if(n){const f=e.find("#action-rowlevel-mode").val()||"owner",h=e.find("#action-rowlevel-userkey").val()||"id";if(c={mode:f,userKey:h},f==="field"){const o=e.find("#action-rowlevel-field").val().trim();if(!o){E.toast("Field name is required for Field Match mode.",{type:"warning"});return}c.field=o}}const i=e.find("#action-project").val()||"",p={title:t,slug:e.find("#action-slug").val().trim()||void 0,description:e.find("#action-description").val().trim(),collection:l,...e.find("#action-bundled").is(":checked")?{bundled:!0}:{},trigger:{type:e.find("#action-trigger-type").val()||"manual",label:e.find("#action-trigger-label").val().trim()||"Run",icon:e.find("#action-trigger-icon").val().trim()||"zap",confirmMessage:e.find("#action-trigger-confirm").val().trim()||null},steps:H(e),access:{roles:a,rowLevel:c},meta:{...O||{},project:i||null}},r=e.find("#save-action-btn").get(0);r&&(r.disabled=!0);try{if(N)await _.actions.update(N,p),E.toast("Action updated.",{type:"success"});else{const f=await _.actions.create(p);E.toast("Action created.",{type:"success"}),R.navigate(`/actions/edit/${f.slug}`)}}catch(f){E.toast(f.message||"Failed to save action.",{type:"error"})}finally{r&&(r.disabled=!1)}}
|
|
1
|
+
import{api as _}from"../api.js";let N=null,O={};async function D(e){const t=e.find("#action-project").get(0);if(t)try{(await _.projects.list()).forEach(a=>{const n=document.createElement("option");n.value=a.slug,n.textContent=a.name||a.slug,t.appendChild(n)})}catch{}}const $={deleteEntry:[],moveToCollection:[{name:"targetCollection",label:"Target Collection Slug",placeholder:"e.g. archived-applications"}],webhook:[{name:"url",label:"URL",placeholder:"https://hooks.example.com/notify"},{name:"method",label:"Method",placeholder:"POST"},{name:"body",label:"Body (JSON)",placeholder:'{"email": "{{entry.data.email}}"}',multiline:!0}],email:[{name:"to",label:"To",placeholder:"{{entry.data.email}}"},{name:"subject",label:"Subject",placeholder:"Your application update"},{name:"template",label:"Body",placeholder:"Your application has been approved.",multiline:!0}]};export const actionEditorView={templateUrl:"/admin/js/templates/action-editor.html",async onMount(e){N=null,O={};const t=window.location.hash.match(/\/actions\/edit\/([^/?#]+)/);t&&(N=t[1]),E.tabs(e.find("#action-editor-tabs").get(0)),await I(e),await P(e),await D(e),N&&(e.find("#action-editor-title").text("Edit Action"),await B(e,N)),e.find("#add-step-btn").off("click").on("click",()=>{const l=e.find("#add-step-type").val()||"updateField";J(e,{type:l,config:{}})}),e.find("#save-action-btn").off("click").on("click",async()=>{await Y(e)}),U(e),Domma.icons.scan()}};async function I(e){const t=e.find("#action-collection").get(0);if(t)try{(await _.collections.list()).forEach(l=>{const a=document.createElement("option");a.value=l.slug,a.textContent=`${l.title} (${l.slug})`,t.appendChild(a)})}catch{}}async function P(e){const t=e.find("#action-roles-checkboxes").get(0);t&&["admin","manager","editor","subscriber"].forEach(l=>{const a=document.createElement("label");a.style.cssText="display:flex;align-items:center;gap:.5rem;cursor:pointer;";const n=document.createElement("input");n.type="checkbox",n.value=l,n.dataset.role=l,n.className="action-role-cb",n.checked=l==="admin",a.appendChild(n),a.appendChild(document.createTextNode(l)),t.appendChild(a)})}async function B(e,t){try{const l=await _.actions.get(t);if(!l){E.toast("Action not found.",{type:"error"}),R.navigate("/actions");return}K(e,l)}catch(l){E.toast(l.message||"Failed to load action.",{type:"error"}),R.navigate("/actions")}}function U(e){const t=e.find("#action-rowlevel-enabled").get(0),l=e.find("#action-rowlevel-config").get(0),a=e.find("#action-rowlevel-mode").get(0),n=e.find("#action-rowlevel-field-group").get(0);t&&(t.addEventListener("change",()=>{l&&(l.style.display=t.checked?"flex":"none")}),a&&a.addEventListener("change",()=>{n&&(n.style.display=a.value==="field"?"":"none")}))}function K(e,t){e.find("#action-title").val(t.title||""),e.find("#action-slug").val(t.slug||""),e.find("#action-description").val(t.description||""),e.find("#action-collection").val(t.collection||""),O=t.meta||{},e.find("#action-project").val(t.meta?.project||""),e.find("#action-bundled").prop("checked",!!t.bundled),e.find("#action-trigger-type").val(t.trigger?.type||"manual"),e.find("#action-trigger-label").val(t.trigger?.label||"Run"),e.find("#action-trigger-icon").val(t.trigger?.icon||"zap"),e.find("#action-trigger-confirm").val(t.trigger?.confirmMessage||"");const l=t.access?.roles||["admin"];e.find(".action-role-cb").each(function(){this.checked=l.includes(this.value)});const a=t.access?.rowLevel;a&&(e.find("#action-rowlevel-enabled").prop("checked",!0),e.find("#action-rowlevel-config").css("display","flex"),e.find("#action-rowlevel-mode").val(a.mode||"owner"),e.find("#action-rowlevel-userkey").val(a.userKey||"id"),a.mode==="field"&&(e.find("#action-rowlevel-field-group").css("display",""),e.find("#action-rowlevel-field").val(a.field||"")));const n=e.find("#action-steps-list").get(0);if(n){const c=n.querySelector(".steps-empty-placeholder");for(c&&c.remove();n.firstChild;)n.removeChild(n.firstChild);const i=document.createElement("p");i.className="text-muted steps-empty-placeholder",i.textContent="No steps yet. Add a step to define what this action does.",i.style.cssText="text-align:center;padding:2rem 0;",n.appendChild(i)}(t.steps||[]).forEach(c=>J(e,c))}function J(e,t){const l=e.find("#action-steps-list").get(0);if(!l)return;const a=l.querySelector(".steps-empty-placeholder");a&&a.remove();const n=$[t.type]||[],c=document.createElement("div");c.className="card mb-2 step-card",c.dataset.stepType=t.type;const i=document.createElement("div");i.className="card-header",i.style.cssText="display:flex;align-items:center;gap:.5rem;";const p=document.createElement("code");p.textContent=t.type,p.style.cssText="flex:1;font-size:.85rem;";const r=document.createElement("button");r.type="button",r.className="btn btn-sm btn-danger";const f=document.createElement("span");f.setAttribute("data-icon","trash"),r.appendChild(f),r.addEventListener("click",()=>{if(c.remove(),!l.querySelector(".step-card")){const o=document.createElement("p");o.className="text-muted steps-empty-placeholder",o.textContent="No steps yet. Add a step to define what this action does.",o.style.cssText="text-align:center;padding:2rem 0;",l.appendChild(o)}}),i.appendChild(p),i.appendChild(r);const h=document.createElement("div");if(h.className="card-body",n.length===0){const o=document.createElement("p");o.className="text-muted",o.textContent=t.type==="deleteEntry"?"This step deletes the entry. No configuration required.":"No additional configuration required.",o.style.margin="0",h.appendChild(o)}t.type==="updateField"?G(h,t,e):n.forEach(o=>{const g=document.createElement("div");g.style.cssText="margin-bottom:.75rem;";const b=document.createElement("label");b.className="form-label",b.textContent=o.label,g.appendChild(b);let m;o.multiline?(m=document.createElement("textarea"),m.rows=3,m.style.cssText="font-family:monospace;font-size:.8rem;resize:vertical;",m.value=typeof t.config?.[o.name]=="object"?JSON.stringify(t.config[o.name],null,2):t.config?.[o.name]??""):(m=document.createElement("input"),m.type="text",m.value=t.config?.[o.name]??""),m.className=`form-input step-field-${o.name}`,m.placeholder=o.placeholder||"",m.dataset.field=o.name,g.appendChild(m),h.appendChild(g)}),c.appendChild(i),c.appendChild(h),l.appendChild(c),Domma.icons.scan(c)}async function G(e,t,l){const a=l.find("#action-collection").val(),n=document.createElement("div");n.style.cssText="margin-bottom:.75rem;";const c=document.createElement("label");c.className="form-label",c.textContent="Field",n.appendChild(c);const i=document.createElement("select");i.className="form-input step-field-field",i.dataset.field="field";const p=document.createElement("input");p.type="text",p.className="form-input mt-2 step-field-field-custom",p.placeholder="Field name, e.g. status",p.style.display="none";const r=document.createElement("div");r.style.cssText="margin-bottom:.75rem;";const f=document.createElement("label");f.className="form-label",f.textContent="New Value",r.appendChild(f);const h=document.createElement("div");r.appendChild(h);const o=document.createElement("button");o.type="button",o.className="btn btn-ghost btn-sm mt-2",o.style.cssText="font-size:.75rem;padding:.2rem .5rem;",o.textContent="Template Variables";const g=document.createElement("div");g.style.cssText="display:none;background:var(--dm-surface-raised,#1e1e2e);border-radius:.4rem;padding:.75rem;margin-top:.5rem;font-size:.8rem;line-height:1.7;";const b=document.createElement("strong");b.textContent="Available template variables:",g.appendChild(b);const m=document.createElement("table");m.style.cssText="width:100%;border-collapse:collapse;margin-top:.4rem;",[["{{now}}","Current ISO timestamp"],["{{user.id}}","Executing user's ID"],["{{user.name}}","Executing user's name"],["{{user.email}}","Executing user's email"],["{{entry.id}}","Entry's unique ID"],["{{entry.data.fieldName}}","Any field value from the entry"],["{{env.CMS_PUBLIC_*}}","Public environment variables"]].forEach(([d,u])=>{const x=document.createElement("tr"),w=document.createElement("td");w.style.cssText="padding:.2rem .5rem .2rem 0;opacity:.7;white-space:nowrap;";const T=document.createElement("code");T.textContent=d,w.appendChild(T);const s=document.createElement("td");s.textContent=u,x.appendChild(w),x.appendChild(s),m.appendChild(x)}),g.appendChild(m),o.addEventListener("click",()=>{const d=g.style.display!=="none";g.style.display=d?"none":"",o.textContent=d?"Template Variables":"Hide Variables"}),r.appendChild(o),r.appendChild(g),e.appendChild(n),e.appendChild(r);let S=[];if(a)try{S=(await _.collections.get(a)).fields||[]}catch{}const j=document.createElement("option");j.value="",j.textContent=S.length?"\u2014 select a field \u2014":"\u2014 no fields available \u2014",i.appendChild(j),S.forEach(d=>{const u=document.createElement("option");u.value=d.name,u.textContent=`${d.label} (${d.name})`,u.dataset.fieldType=d.type,u.dataset.fieldOptions=d.type==="select"?JSON.stringify(d.options||[]):"",i.appendChild(u)});const q=document.createElement("option");q.value="__custom__",q.textContent="\u2014 enter manually \u2014",i.appendChild(q);const C=t.config?.field||"",z=C&&[...i.options].find(d=>d.value===C);z?i.value=C:C&&(i.value="__custom__",p.value=C,p.style.display=""),n.appendChild(i),n.appendChild(p);function M(d,u){h.textContent="";const x=d?[...i.options].find(s=>s.value===d):null,w=x?.dataset.fieldType==="select",T=w?JSON.parse(x.dataset.fieldOptions||"[]"):[];if(w&&T.length){const s=document.createElement("select");s.className="form-input step-field-value",s.dataset.field="value";const A=document.createElement("option");A.value="",A.textContent="\u2014 select a value \u2014",s.appendChild(A),T.forEach(y=>{const k=typeof y=="string"?y:y.value??"",V=typeof y=="string"?y:y.label||y.value||k;if(!k||k==="undefined")return;const F=document.createElement("option");F.value=k,F.textContent=V,s.appendChild(F)});const L=document.createElement("option");L.value="__custom__",L.textContent="\u2014 enter manually \u2014",s.appendChild(L);const v=document.createElement("input");v.type="text",v.className="form-input mt-2",v.placeholder="e.g. approved or {{now}}",v.style.display="none",u&&[...s.options].find(y=>y.value===u&&y.value!=="__custom__")?s.value=u:u&&(s.value="__custom__",v.value=u,v.style.display=""),s.addEventListener("change",()=>{const y=s.value==="__custom__";v.style.display=y?"":"none",y||(v.value="")}),h.appendChild(s),h.appendChild(v)}else{const s=document.createElement("input");s.type="text",s.className="form-input step-field-value",s.dataset.field="value",s.placeholder="e.g. approved or {{now}}",s.value=u||"",h.appendChild(s)}}M(z?C:null,t.config?.value||""),i.addEventListener("change",()=>{const d=i.value==="__custom__";p.style.display=d?"":"none",d||(p.value=""),M(d?null:i.value,"")})}function H(e){const t=[];return e.find(".step-card").each(function(){const l=this.dataset.stepType,a={};if(l==="updateField"){let n=this.querySelector(".step-field-field")?.value?.trim()||"";n==="__custom__"&&(n=this.querySelector(".step-field-field-custom")?.value?.trim()||""),a.field=n;const c=this.querySelector(".step-field-value");let i=c?.value?.trim()||"";i==="__custom__"&&(i=c?.nextElementSibling?.value?.trim()||""),a.value=i}else($[l]||[]).forEach(n=>{const c=this.querySelector(`.step-field-${n.name}`);if(!c)return;const i=c.value.trim();if(n.multiline&&i)try{a[n.name]=JSON.parse(i)}catch{a[n.name]=i}else a[n.name]=i});t.push({type:l,config:a})}),t}async function Y(e){const t=e.find("#action-title").val().trim();if(!t){E.toast("Title is required.",{type:"warning"});return}const l=e.find("#action-collection").val();if(!l){E.toast("Target collection is required (General tab).",{type:"warning"});return}const a=[];e.find(".action-role-cb:checked").each(function(){a.push(this.value)});const n=e.find("#action-rowlevel-enabled").is(":checked");let c=null;if(n){const f=e.find("#action-rowlevel-mode").val()||"owner",h=e.find("#action-rowlevel-userkey").val()||"id";if(c={mode:f,userKey:h},f==="field"){const o=e.find("#action-rowlevel-field").val().trim();if(!o){E.toast("Field name is required for Field Match mode.",{type:"warning"});return}c.field=o}}const i=e.find("#action-project").val()||"",p={title:t,slug:e.find("#action-slug").val().trim()||void 0,description:e.find("#action-description").val().trim(),collection:l,...e.find("#action-bundled").is(":checked")?{bundled:!0}:{},trigger:{type:e.find("#action-trigger-type").val()||"manual",label:e.find("#action-trigger-label").val().trim()||"Run",icon:e.find("#action-trigger-icon").val().trim()||"zap",confirmMessage:e.find("#action-trigger-confirm").val().trim()||null},steps:H(e),access:{roles:a,rowLevel:c},meta:{...O||{},project:i||null}},r=e.find("#save-action-btn").get(0);r&&(r.disabled=!0);try{if(N)await _.actions.update(N,p),E.toast("Action updated.",{type:"success"});else{const f=await _.actions.create(p);E.toast("Action created.",{type:"success"}),R.navigate(`/actions/edit/${f.slug}`)}}catch(f){E.toast(f.message||"Failed to save action.",{type:"error"})}finally{r&&(r.disabled=!1)}}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{api as _}from"../api.js";const de=[["_eq","equals"],["_ne","not equal"],["_gt",">"],["_gte",">="],["_lt","<"],["_lte","<="],["_in","in list"],["_nin","not in list"],["_contains","contains"],["_starts","starts with"],["_ends","ends with"],["_exists","exists"]],xe=["id","createdBy"],we=/\{\{\s*(params|query)\.([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;function Se(C){const b=new Set;for(const n of Object.values(C?.filter||{}))if(typeof n=="string")for(const N of n.matchAll(we))N[1]==="query"&&b.add(N[2]);return[...b]}function je(C){return String(C??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}let O=null;function qe(C){for(const[b]of de)if(C.endsWith(b))return{field:C.slice(0,-b.length),op:b};return{field:C,op:"_eq"}}export const apiEndpointEditorView={templateUrl:"/admin/js/templates/api-endpoint-editor.html",async onMount(C){const b=C.get(0),n=e=>b.querySelector("#"+e)||document.getElementById(e),N=location.hash.split("?")[0].match(/\/api-endpoints\/edit\/([^/?#]+)/),q=N?decodeURIComponent(N[1]):null;n("ep-title").textContent=q?"Edit API Endpoint":"New API Endpoint",E.tabs(n("ep-editor-tabs"));const[pe,ue,me,u]=await Promise.all([_.projects.list().catch(()=>[]),_.collections.list().catch(()=>[]),_.get("/collections/roles/entries?limit=100").catch(()=>({entries:[]})),q?_.apiEndpoints.get(q).catch(()=>null):Promise.resolve(null)]);if(q&&!u){E.toast("Endpoint not found.",{type:"error"}),location.hash="#/api-endpoints";return}const fe=ue.filter(e=>!e.systemManaged&&!e.preset),ye=(me.entries||[]).map(e=>e.data?.name).filter(Boolean),k=n("endpoint-project");for(const e of pe){const t=document.createElement("option");t.value=e.slug,t.textContent=`${e.name} (${e.slug})`,k.appendChild(t)}const y=n("ep-collection");function G(e){return e.meta&&e.meta.project||"core"}function z(e){const t=e!==void 0?e:y.value,r=k.value,o=fe.filter(a=>{const s=G(a);return s===r||s==="core"});y.textContent="";for(const a of o){const s=document.createElement("option");s.value=a.slug,s.textContent=`${a.title} (${a.slug})${G(a)==="core"&&r!=="core"?" \u2014 core":""}`,y.appendChild(s)}if(!o.length){const a=document.createElement("option");a.value="",a.textContent="No collections available in this project",y.appendChild(a)}t&&[...y.options].some(a=>a.value===t)&&(y.value=t)}z();const L=n("ep-auth");for(const[e,t]of[["public","Public \u2014 no credentials"],["token","Token \u2014 project API token"]]){const r=document.createElement("option");r.value=e,r.textContent=t,L.appendChild(r)}for(const e of ye){const t=document.createElement("option");t.value=e,t.textContent=`Role: ${e}`,L.appendChild(t)}let T=[];async function R(){const e=y.value;if(T=[],e)try{T=((await _.collections.get(e))?.fields||[]).map(r=>r.name).filter(Boolean)}catch{}ee();for(const t of K.querySelectorAll(".ep-filter-row"))U(t.querySelector(".ep-f-field"));te()}function F(){return[...T,...xe]}function ee(e=[]){const t=n("ep-fields-list");t.textContent="";for(const r of T){const o=document.createElement("label");o.style.cssText="display:flex;align-items:center;gap:.35rem;cursor:pointer;font-size:.9rem;";const a=document.createElement("input");a.type="checkbox",a.className="ep-field-cb",a.value=r,a.checked=e.includes(r),a.addEventListener("change",h),o.appendChild(a),o.appendChild(document.createTextNode(r)),t.appendChild(o)}if(!T.length){const r=document.createElement("p");r.className="text-muted",r.style.cssText="font-size:.85rem;margin:0;",r.textContent="No fields found on this collection.",t.appendChild(r)}}function te(e){const t=n("ep-sort"),r=e!==void 0?e:t.value;t.textContent="";for(const o of["createdAt",...F()]){const a=document.createElement("option");a.value=o==="createdAt"?"":o,a.textContent=o==="createdAt"?"createdAt (default)":o,t.appendChild(a)}t.value=r||""}function U(e,t){const r=t!==void 0?t:e.value;e.textContent="";for(const a of F()){const s=document.createElement("option");s.value=a,s.textContent=a,e.appendChild(s)}const o=document.createElement("option");if(o.value="__custom__",o.textContent="custom dot-path\u2026",e.appendChild(o),r&&[...e.options].some(a=>a.value===r))e.value=r;else if(r){const a=document.createElement("option");a.value=r,a.textContent=r,e.insertBefore(a,o),e.value=r}}const K=n("ep-filter-rows");function M(e="",t="_eq",r=""){const o=document.createElement("div");o.className="ep-filter-row",o.style.cssText="display:grid;grid-template-columns:2fr 1.2fr 2fr auto;gap:.5rem;margin-bottom:.5rem;align-items:center;";const a=document.createElement("select");a.className="form-input ep-f-field",U(a,e||void 0),a.addEventListener("change",()=>{if(a.value==="__custom__"){const i=prompt("Dot-path field (e.g. address.city):","");i&&i.trim()?U(a,i.trim()):a.value=F()[0]||""}h()});const s=document.createElement("select");s.className="form-input ep-f-op";for(const[i,j]of de){const w=document.createElement("option");w.value=i,w.textContent=j,s.appendChild(w)}s.value=t,s.addEventListener("change",h);const c=document.createElement("input");c.type="text",c.className="form-input ep-f-value",c.placeholder="e.g. {{params.date}} or {{query.team}} or a literal",c.value=r,c.addEventListener("input",h);const d=document.createElement("button");d.className="btn btn-sm btn-ghost",d.innerHTML='<span data-icon="trash"></span>',d.addEventListener("click",()=>{o.remove(),h()}),o.appendChild(a),o.appendChild(s),o.appendChild(c),o.appendChild(d),K.appendChild(o),Domma.icons.scan(o)}function he(){const e={};for(const t of K.querySelectorAll(".ep-filter-row")){const r=t.querySelector(".ep-f-field").value,o=t.querySelector(".ep-f-op").value,a=t.querySelector(".ep-f-value").value;!r||r==="__custom__"||(e[o==="_eq"?r:r+o]=a)}return e}function I(){return{name:n("ep-name").value.trim(),project:k.value,path:n("ep-path").value.trim(),collection:y.value,auth:L.value,mode:n("ep-mode").value,filter:he(),sort:n("ep-sort").value||null,order:n("ep-order").value,limit:parseInt(n("ep-limit").value,10)||0,fields:[...b.querySelectorAll(".ep-field-cb:checked")].map(e=>e.value),enabled:n("ep-enabled").checked}}let B=null,v=null;function D(){return!B||JSON.stringify(I())!==B}function h(){ne(),Z()}function ne(){n("ep-url-preview").textContent=`/api/x/${k.value||"<project>"}${n("ep-path").value||"/\u2026"}`}function ve(e){return(e.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g)||[]).map(t=>t.slice(1))}function Z(){const e=D();n("tryit-dirty-note").style.display=e?"":"none",n("tryit-send").disabled=e,n("tryit-token-group").style.display=v?.auth==="token"?"":"none";const t=n("tryit-params"),r={};for(const s of t.querySelectorAll("input"))r[s.dataset.param]=s.value;t.textContent="";for(const s of ve(v?.path||"")){const c=document.createElement("div");c.className="form-group";const d=document.createElement("label");d.className="form-label",d.textContent=`:${s}`;const i=document.createElement("input");i.type="text",i.className="form-input",i.dataset.param=s,i.value=r[s]||"",c.appendChild(d),c.appendChild(i),t.appendChild(c)}const o=n("tryit-query-params"),a={};for(const s of o.querySelectorAll("input"))a[s.dataset.queryParam]=s.value;o.textContent="";for(const s of Se(v)){const c=document.createElement("div");c.className="form-group";const d=document.createElement("label");d.className="form-label",d.textContent=`?${s}`;const i=document.createElement("input");i.type="text",i.className="form-input",i.dataset.queryParam=s,i.value=a[s]||"",i.placeholder=`optional \u2014 filters by ${s}`,c.appendChild(d),c.appendChild(i),o.appendChild(c)}}let J="";const W=`apitryit:hist:${q||"new"}`,ge=12;function oe(){const e=S.get(W);return Array.isArray(e)?e:[]}function ae(e){const t=oe().filter(o=>o.url!==e.url),r=[e,...t].slice(0,ge);S.set(W,r),Y()}function Ee(e){for(const t of n("tryit-params").querySelectorAll("input"))t.value=e.params?.[t.dataset.param]??"";for(const t of n("tryit-query-params").querySelectorAll("input"))t.value=e.queryParams?.[t.dataset.queryParam]??"";n("tryit-query").value=e.extra||"",Q()}function Y(){const e=n("tryit-history-list"),t=n("tryit-history-card");if(!e||!t)return;const r=oe();t.style.display=r.length?"":"none",e.textContent="";for(const o of r){const a=document.createElement("button");a.type="button",a.className="btn btn-ghost btn-sm",a.style.cssText="display:flex;align-items:center;gap:.5rem;width:100%;justify-content:flex-start;text-align:left;font-family:var(--font-mono,monospace);font-size:.78rem;padding:.35rem .5rem;";const s=document.createElement("span"),c=o.ok;s.className="badge "+(c?"badge-success":o.status>=500||o.status===0?"badge-danger":"badge-warning"),s.style.cssText="flex:0 0 auto;",s.textContent=o.status||"ERR";const d=document.createElement("span");d.style.cssText="flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;",d.textContent=o.url.replace(`/api/x/${v?.project||""}`,"")||o.url,d.title=o.url,a.appendChild(s),a.appendChild(d),a.addEventListener("click",()=>Ee(o)),e.appendChild(a)}}const m={key:"#4098d7",index:"#9aa4b2",string:"#3f9d63",number:"#c0803a",boolean:"#9a6dce",nul:"#c05b52",punc:"rgba(127,127,127,.8)",preview:"#9aa4b2"};function f(e,t,r){const o=document.createElement("span");return o.style.color=e,r&&(o.style.cssText+=";"+r),o.textContent=t,o}function re(e,t){return t?f(m.index,String(e)):f(m.key,JSON.stringify(String(e)))}function Ce(e){return e===null?f(m.nul,"null"):typeof e=="string"?f(m.string,JSON.stringify(e)):typeof e=="number"?f(m.number,String(e)):typeof e=="boolean"?f(m.boolean,String(e)):f(m.string,JSON.stringify(e))}function se(e,t,r){const o=e&&typeof e=="object",a=o&&(Array.isArray(e)?e.length===0:Object.keys(e).length===0);if(!o||a){const p=document.createElement("div");return p.style.cssText="white-space:pre-wrap;word-break:break-word;padding-left:1.2em;",t!==void 0&&(p.appendChild(re(t,r)),p.appendChild(f(m.punc,": "))),p.appendChild(o?f(m.punc,Array.isArray(e)?"[]":"{}"):Ce(e)),p}const s=Array.isArray(e),c=s?e.map((p,x)=>[x,p,!0]):Object.entries(e).map(([p,x])=>[p,x,!1]),d=document.createElement("div"),i=document.createElement("div");i.style.cssText="white-space:pre-wrap;cursor:pointer;";const j=f(m.punc,"\u25BE","display:inline-block;width:1.2em;");i.appendChild(j),t!==void 0&&(i.appendChild(re(t,r)),i.appendChild(f(m.punc,": "))),i.appendChild(f(m.punc,s?"[":"{"));const w=f(m.preview,` ${c.length} ${s?"item":"key"}${c.length===1?"":"s"} `,"font-style:italic;"),P=f(m.punc,s?"]":"}");i.appendChild(w),i.appendChild(P),d.appendChild(i);const A=document.createElement("div");A.style.cssText="padding-left:1.2em;border-left:1px solid rgba(127,127,127,.18);";for(const[p,x,X]of c)A.appendChild(se(x,p,X));d.appendChild(A);const $=document.createElement("div");$.style.paddingLeft="1.2em",$.appendChild(f(m.punc,s?"]":"}")),d.appendChild($);let l=!1;const g=p=>{l=p,j.textContent=p?"\u25B8":"\u25BE",w.style.display=p?"":"none",P.style.display=p?"":"none",A.style.display=p?"none":"",$.style.display=p?"none":""};return g(!1),i.addEventListener("click",()=>g(!l)),d}let V="";function ie(e){n("tryit-json").style.display=e?"":"none",n("tryit-body").style.display=e?"none":"",n("tryit-json-toggle").textContent=e?"Raw":"Tree"}function le(e){V=e;const t=n("tryit-results-placeholder");t&&(t.style.display="none"),n("tryit-response-card").style.display="";let r,o=!0;try{r=JSON.parse(e)}catch{o=!1}n("tryit-body").textContent=o?JSON.stringify(r,null,2):e;const a=n("tryit-json");a.textContent="",o&&a.appendChild(se(r)),n("tryit-json-toggle").style.display=o?"":"none",ie(o)}async function Q(){if(!v)return;const e={},t={};let r=v.path;for(const l of n("tryit-params").querySelectorAll("input")){e[l.dataset.param]=l.value;const g=new RegExp(":"+l.dataset.param.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"(?=/|$)","g");r=r.replace(g,encodeURIComponent(l.value))}const o=[];for(const l of n("tryit-query-params").querySelectorAll("input")){const g=l.value.trim();t[l.dataset.queryParam]=l.value,g&&o.push(`${encodeURIComponent(l.dataset.queryParam)}=${encodeURIComponent(g)}`)}const a=n("tryit-query").value.trim();a&&o.push(a);const s=o.join("&"),c=`/api/x/${v.project}${r}${s?"?"+s:""}`,d={ts:Date.now(),url:c,params:e,queryParams:t,extra:a},i={},j=n("tryit-token").value.trim();v.auth==="token"&&j&&(i.Authorization=`Bearer ${j}`);const w=location.origin+c,P=l=>`'${String(l).replace(/'/g,"'\\''")}'`,A=[`curl ${P(w)}`];for(const[l,g]of Object.entries(i))A.push(` -H ${P(`${l}: ${g}`)}`);J=A.join(` \\
|
|
2
|
-
`),n("tryit-result").style.display="",n("tryit-curl").textContent=J;const $=n("tryit-send");$.disabled=!0;try{const l=await fetch(c,{headers:i}),g=await l.text(),p=n("tryit-status");p.textContent=`${l.status} ${l.statusText}`,p.className="badge "+(l.ok?"badge-success":l.status>=500?"badge-danger":"badge-warning");const
|
|
3
|
-
`):"(none exposed)",le(g),ae({...d,status:l.status,statusText:l.statusText,ok:l.ok})}catch(l){n("tryit-status").textContent="Network error",n("tryit-status").className="badge badge-danger",n("tryit-headers-group").style.display="none",le(String(l.message||l)),ae({...d,status:0,statusText:"Network error",ok:!1})}finally{$.disabled=D()}}if(u){if(n("ep-name").value=u.name||"",k.value=u.project,k.disabled=!0,n("ep-path").value=u.path||"",z(u.collection),y.value!==u.collection){const e=document.createElement("option");e.value=u.collection,e.textContent=`${u.collection} (other project \u2014 no longer allowed)`,y.appendChild(e),y.value=u.collection}L.value=u.auth||"public",n("ep-mode").value=u.mode||"list",n("ep-order").value=u.order||"desc",n("ep-limit").value=u.limit??50,n("ep-enabled").checked=u.enabled!==!1,await R(),ee(u.fields||[]),te(u.sort||"");for(const[e,t]of Object.entries(u.filter||{})){const{field:r,op:o}=qe(e);M(r,o,String(t))}v=u,B=JSON.stringify(I())}else await R(),M();ne(),Z();for(const e of["ep-name","ep-path","ep-limit"])n(e).addEventListener("input",h);for(const e of["ep-mode","ep-order","ep-sort"])n(e).addEventListener("change",h);n("ep-enabled").addEventListener("change",h),k.addEventListener("change",async()=>{z(),await R(),h()}),L.addEventListener("change",h),y.addEventListener("change",async()=>{await R(),h()}),n("ep-add-filter").addEventListener("click",()=>{M(),h()}),n("tryit-send").addEventListener("click",Q),n("tryit-history-clear").addEventListener("click",()=>{S.set(W,[]),Y()}),n("tryit-json-toggle").addEventListener("click",()=>{ie(n("tryit-json").style.display==="none")}),n("tryit-body-copy").addEventListener("click",async()=>{if(!V){E.toast("Send a request first.",{type:"info"});return}try{await navigator.clipboard.writeText(V),E.toast("Response copied.",{type:"success"})}catch{E.toast("Copy failed.",{type:"error"})}}),n("tryit-copy-curl").addEventListener("click",async()=>{if(!J){E.toast("Send a request first.",{type:"info"});return}try{await navigator.clipboard.writeText(J),E.toast("curl command copied.",{type:"success"})}catch{E.toast("Copy failed.",{type:"error"})}});async function ce(){const e=I();if(!e.name){E.toast("A name is required.",{type:"error"});return}if(!e.path.startsWith("/")){E.toast('The path must start with "/".',{type:"error"});return}try{if(q)v=await _.apiEndpoints.update(q,e),B=JSON.stringify(I()),E.toast("Endpoint saved.",{type:"success"}),Z();else{const t=await _.apiEndpoints.create(e);E.toast("Endpoint created.",{type:"success"}),location.hash="#/api-endpoints/edit/"+encodeURIComponent(t.id)}}catch(t){E.toast(`Save failed: ${t.message||t}`,{type:"error"})}}n("ep-save").addEventListener("click",ce);function H(e){if(!document.getElementById("ep-save")){document.removeEventListener("keydown",H),O===H&&(O=null);return}if(!(e.metaKey||e.ctrlKey))return;const r=e.key.toLowerCase();r==="s"?(e.preventDefault(),ce()):r==="enter"&&(e.preventDefault(),v&&!D()?Q():D()&&E.toast("Save first \u2014 Send tests the saved definition.",{type:"info"}))}O&&document.removeEventListener("keydown",O),O=H,document.addEventListener("keydown",H),Y(),Domma.icons.scan(
|
|
1
|
+
import{api as _}from"../api.js";const de=[["_eq","equals"],["_ne","not equal"],["_gt",">"],["_gte",">="],["_lt","<"],["_lte","<="],["_in","in list"],["_nin","not in list"],["_contains","contains"],["_starts","starts with"],["_ends","ends with"],["_exists","exists"]],be=["id","createdBy"],we=/\{\{\s*(params|query)\.([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;function Se(C){const x=new Set;for(const n of Object.values(C?.filter||{}))if(typeof n=="string")for(const N of n.matchAll(we))N[1]==="query"&&x.add(N[2]);return[...x]}function je(C){return String(C??"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}let O=null;function qe(C){for(const[x]of de)if(C.endsWith(x))return{field:C.slice(0,-x.length),op:x};return{field:C,op:"_eq"}}export const apiEndpointEditorView={templateUrl:"/admin/js/templates/api-endpoint-editor.html",async onMount(C){const x=C.get(0),n=e=>x.querySelector("#"+e)||document.getElementById(e),N=location.hash.split("?")[0].match(/\/api-endpoints\/edit\/([^/?#]+)/),q=N?decodeURIComponent(N[1]):null;n("ep-title").textContent=q?"Edit API Endpoint":"New API Endpoint",E.tabs(n("ep-editor-tabs"));const[pe,ue,me,u]=await Promise.all([_.projects.list().catch(()=>[]),_.collections.list().catch(()=>[]),_.get("/collections/roles/entries?limit=100").catch(()=>({entries:[]})),q?_.apiEndpoints.get(q).catch(()=>null):Promise.resolve(null)]);if(q&&!u){E.toast("Endpoint not found.",{type:"error"}),location.hash="#/api-endpoints";return}const fe=ue.filter(e=>!e.systemManaged&&!e.preset),ye=(me.entries||[]).map(e=>e.data?.name).filter(Boolean),k=n("endpoint-project");for(const e of pe){const t=document.createElement("option");t.value=e.slug,t.textContent=`${e.name} (${e.slug})`,k.appendChild(t)}const y=n("ep-collection");function G(e){return e.meta&&e.meta.project||"core"}function z(e){const t=e!==void 0?e:y.value,r=k.value,o=fe.filter(a=>{const s=G(a);return s===r||s==="core"});y.textContent="";for(const a of o){const s=document.createElement("option");s.value=a.slug,s.textContent=`${a.title} (${a.slug})${G(a)==="core"&&r!=="core"?" \u2014 core":""}`,y.appendChild(s)}if(!o.length){const a=document.createElement("option");a.value="",a.textContent="No collections available in this project",y.appendChild(a)}t&&[...y.options].some(a=>a.value===t)&&(y.value=t)}z();const L=n("ep-auth");for(const[e,t]of[["public","Public \u2014 no credentials"],["token","Token \u2014 project API token"]]){const r=document.createElement("option");r.value=e,r.textContent=t,L.appendChild(r)}for(const e of ye){const t=document.createElement("option");t.value=e,t.textContent=`Role: ${e}`,L.appendChild(t)}let T=[];async function R(){const e=y.value;if(T=[],e)try{T=((await _.collections.get(e))?.fields||[]).map(r=>r.name).filter(Boolean)}catch{}ee();for(const t of K.querySelectorAll(".ep-filter-row"))U(t.querySelector(".ep-f-field"));te()}function F(){return[...T,...be]}function ee(e=[]){const t=n("ep-fields-list");t.textContent="";for(const r of T){const o=document.createElement("label");o.style.cssText="display:flex;align-items:center;gap:.35rem;cursor:pointer;font-size:.9rem;";const a=document.createElement("input");a.type="checkbox",a.className="ep-field-cb",a.value=r,a.checked=e.includes(r),a.addEventListener("change",h),o.appendChild(a),o.appendChild(document.createTextNode(r)),t.appendChild(o)}if(!T.length){const r=document.createElement("p");r.className="text-muted",r.style.cssText="font-size:.85rem;margin:0;",r.textContent="No fields found on this collection.",t.appendChild(r)}}function te(e){const t=n("ep-sort"),r=e!==void 0?e:t.value;t.textContent="";for(const o of["createdAt",...F()]){const a=document.createElement("option");a.value=o==="createdAt"?"":o,a.textContent=o==="createdAt"?"createdAt (default)":o,t.appendChild(a)}t.value=r||""}function U(e,t){const r=t!==void 0?t:e.value;e.textContent="";for(const a of F()){const s=document.createElement("option");s.value=a,s.textContent=a,e.appendChild(s)}const o=document.createElement("option");if(o.value="__custom__",o.textContent="custom dot-path\u2026",e.appendChild(o),r&&[...e.options].some(a=>a.value===r))e.value=r;else if(r){const a=document.createElement("option");a.value=r,a.textContent=r,e.insertBefore(a,o),e.value=r}}const K=n("ep-filter-rows");function M(e="",t="_eq",r=""){const o=document.createElement("div");o.className="ep-filter-row",o.style.cssText="display:grid;grid-template-columns:2fr 1.2fr 2fr auto;gap:.5rem;margin-bottom:.5rem;align-items:center;";const a=document.createElement("select");a.className="form-input ep-f-field",U(a,e||void 0),a.addEventListener("change",()=>{if(a.value==="__custom__"){const i=prompt("Dot-path field (e.g. address.city):","");i&&i.trim()?U(a,i.trim()):a.value=F()[0]||""}h()});const s=document.createElement("select");s.className="form-input ep-f-op";for(const[i,j]of de){const w=document.createElement("option");w.value=i,w.textContent=j,s.appendChild(w)}s.value=t,s.addEventListener("change",h);const c=document.createElement("input");c.type="text",c.className="form-input ep-f-value",c.placeholder="e.g. {{params.date}} or {{query.team}} or a literal",c.value=r,c.addEventListener("input",h);const d=document.createElement("button");d.className="btn btn-sm btn-ghost",d.innerHTML='<span data-icon="trash"></span>',d.addEventListener("click",()=>{o.remove(),h()}),o.appendChild(a),o.appendChild(s),o.appendChild(c),o.appendChild(d),K.appendChild(o),Domma.icons.scan(o)}function he(){const e={};for(const t of K.querySelectorAll(".ep-filter-row")){const r=t.querySelector(".ep-f-field").value,o=t.querySelector(".ep-f-op").value,a=t.querySelector(".ep-f-value").value;!r||r==="__custom__"||(e[o==="_eq"?r:r+o]=a)}return e}function I(){return{name:n("ep-name").value.trim(),project:k.value,path:n("ep-path").value.trim(),collection:y.value,auth:L.value,mode:n("ep-mode").value,filter:he(),sort:n("ep-sort").value||null,order:n("ep-order").value,limit:parseInt(n("ep-limit").value,10)||0,fields:[...x.querySelectorAll(".ep-field-cb:checked")].map(e=>e.value),enabled:n("ep-enabled").checked}}let B=null,v=null;function D(){return!B||JSON.stringify(I())!==B}function h(){ne(),Z()}function ne(){n("ep-url-preview").textContent=`/api/x/${k.value||"<project>"}${n("ep-path").value||"/\u2026"}`}function ve(e){return(e.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g)||[]).map(t=>t.slice(1))}function Z(){const e=D();n("tryit-dirty-note").style.display=e?"":"none",n("tryit-send").disabled=e,n("tryit-token-group").style.display=v?.auth==="token"?"":"none";const t=n("tryit-params"),r={};for(const s of t.querySelectorAll("input"))r[s.dataset.param]=s.value;t.textContent="";for(const s of ve(v?.path||"")){const c=document.createElement("div");c.className="form-group";const d=document.createElement("label");d.className="form-label",d.textContent=`:${s}`;const i=document.createElement("input");i.type="text",i.className="form-input",i.dataset.param=s,i.value=r[s]||"",c.appendChild(d),c.appendChild(i),t.appendChild(c)}const o=n("tryit-query-params"),a={};for(const s of o.querySelectorAll("input"))a[s.dataset.queryParam]=s.value;o.textContent="";for(const s of Se(v)){const c=document.createElement("div");c.className="form-group";const d=document.createElement("label");d.className="form-label",d.textContent=`?${s}`;const i=document.createElement("input");i.type="text",i.className="form-input",i.dataset.queryParam=s,i.value=a[s]||"",i.placeholder=`optional \u2014 filters by ${s}`,c.appendChild(d),c.appendChild(i),o.appendChild(c)}}let J="";const W=`apitryit:hist:${q||"new"}`,ge=12;function oe(){const e=S.get(W);return Array.isArray(e)?e:[]}function ae(e){const t=oe().filter(o=>o.url!==e.url),r=[e,...t].slice(0,ge);S.set(W,r),Y()}function Ee(e){for(const t of n("tryit-params").querySelectorAll("input"))t.value=e.params?.[t.dataset.param]??"";for(const t of n("tryit-query-params").querySelectorAll("input"))t.value=e.queryParams?.[t.dataset.queryParam]??"";n("tryit-query").value=e.extra||"",Q()}function Y(){const e=n("tryit-history-list"),t=n("tryit-history-card");if(!e||!t)return;const r=oe();t.style.display=r.length?"":"none",e.textContent="";for(const o of r){const a=document.createElement("button");a.type="button",a.className="btn btn-ghost btn-sm",a.style.cssText="display:flex;align-items:center;gap:.5rem;width:100%;justify-content:flex-start;text-align:left;font-family:var(--dm-font-mono,monospace);font-size:.78rem;padding:.35rem .5rem;";const s=document.createElement("span"),c=o.ok;s.className="badge "+(c?"badge-success":o.status>=500||o.status===0?"badge-danger":"badge-warning"),s.style.cssText="flex:0 0 auto;",s.textContent=o.status||"ERR";const d=document.createElement("span");d.style.cssText="flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;",d.textContent=o.url.replace(`/api/x/${v?.project||""}`,"")||o.url,d.title=o.url,a.appendChild(s),a.appendChild(d),a.addEventListener("click",()=>Ee(o)),e.appendChild(a)}}const m={key:"var(--dm-info)",index:"var(--dm-text-muted)",string:"var(--dm-success)",number:"var(--dm-warning-dark)",boolean:"var(--dm-secondary)",nul:"var(--dm-danger)",punc:"var(--dm-text-muted)",preview:"var(--dm-text-muted)"};function f(e,t,r){const o=document.createElement("span");return o.style.color=e,r&&(o.style.cssText+=";"+r),o.textContent=t,o}function re(e,t){return t?f(m.index,String(e)):f(m.key,JSON.stringify(String(e)))}function Ce(e){return e===null?f(m.nul,"null"):typeof e=="string"?f(m.string,JSON.stringify(e)):typeof e=="number"?f(m.number,String(e)):typeof e=="boolean"?f(m.boolean,String(e)):f(m.string,JSON.stringify(e))}function se(e,t,r){const o=e&&typeof e=="object",a=o&&(Array.isArray(e)?e.length===0:Object.keys(e).length===0);if(!o||a){const p=document.createElement("div");return p.style.cssText="white-space:pre-wrap;word-break:break-word;padding-left:1.2em;",t!==void 0&&(p.appendChild(re(t,r)),p.appendChild(f(m.punc,": "))),p.appendChild(o?f(m.punc,Array.isArray(e)?"[]":"{}"):Ce(e)),p}const s=Array.isArray(e),c=s?e.map((p,b)=>[b,p,!0]):Object.entries(e).map(([p,b])=>[p,b,!1]),d=document.createElement("div"),i=document.createElement("div");i.style.cssText="white-space:pre-wrap;cursor:pointer;";const j=f(m.punc,"\u25BE","display:inline-block;width:1.2em;");i.appendChild(j),t!==void 0&&(i.appendChild(re(t,r)),i.appendChild(f(m.punc,": "))),i.appendChild(f(m.punc,s?"[":"{"));const w=f(m.preview,` ${c.length} ${s?"item":"key"}${c.length===1?"":"s"} `,"font-style:italic;"),P=f(m.punc,s?"]":"}");i.appendChild(w),i.appendChild(P),d.appendChild(i);const A=document.createElement("div");A.style.cssText="padding-left:1.2em;border-left:1px solid var(--dm-border-light);";for(const[p,b,X]of c)A.appendChild(se(b,p,X));d.appendChild(A);const $=document.createElement("div");$.style.paddingLeft="1.2em",$.appendChild(f(m.punc,s?"]":"}")),d.appendChild($);let l=!1;const g=p=>{l=p,j.textContent=p?"\u25B8":"\u25BE",w.style.display=p?"":"none",P.style.display=p?"":"none",A.style.display=p?"none":"",$.style.display=p?"none":""};return g(!1),i.addEventListener("click",()=>g(!l)),d}let V="";function ie(e){n("tryit-json").style.display=e?"":"none",n("tryit-body").style.display=e?"none":"",n("tryit-json-toggle").textContent=e?"Raw":"Tree"}function le(e){V=e;const t=n("tryit-results-placeholder");t&&(t.style.display="none"),n("tryit-response-card").style.display="";let r,o=!0;try{r=JSON.parse(e)}catch{o=!1}n("tryit-body").textContent=o?JSON.stringify(r,null,2):e;const a=n("tryit-json");a.textContent="",o&&a.appendChild(se(r)),n("tryit-json-toggle").style.display=o?"":"none",ie(o)}async function Q(){if(!v)return;const e={},t={};let r=v.path;for(const l of n("tryit-params").querySelectorAll("input")){e[l.dataset.param]=l.value;const g=new RegExp(":"+l.dataset.param.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"(?=/|$)","g");r=r.replace(g,encodeURIComponent(l.value))}const o=[];for(const l of n("tryit-query-params").querySelectorAll("input")){const g=l.value.trim();t[l.dataset.queryParam]=l.value,g&&o.push(`${encodeURIComponent(l.dataset.queryParam)}=${encodeURIComponent(g)}`)}const a=n("tryit-query").value.trim();a&&o.push(a);const s=o.join("&"),c=`/api/x/${v.project}${r}${s?"?"+s:""}`,d={ts:Date.now(),url:c,params:e,queryParams:t,extra:a},i={},j=n("tryit-token").value.trim();v.auth==="token"&&j&&(i.Authorization=`Bearer ${j}`);const w=location.origin+c,P=l=>`'${String(l).replace(/'/g,"'\\''")}'`,A=[`curl ${P(w)}`];for(const[l,g]of Object.entries(i))A.push(` -H ${P(`${l}: ${g}`)}`);J=A.join(` \\
|
|
2
|
+
`),n("tryit-result").style.display="",n("tryit-curl").textContent=J;const $=n("tryit-send");$.disabled=!0;try{const l=await fetch(c,{headers:i}),g=await l.text(),p=n("tryit-status");p.textContent=`${l.status} ${l.statusText}`,p.className="badge "+(l.ok?"badge-success":l.status>=500?"badge-danger":"badge-warning");const b=[];l.headers.forEach((X,xe)=>b.push(`${xe}: ${X}`)),n("tryit-headers-group").style.display="",n("tryit-headers").textContent=b.length?b.join(`
|
|
3
|
+
`):"(none exposed)",le(g),ae({...d,status:l.status,statusText:l.statusText,ok:l.ok})}catch(l){n("tryit-status").textContent="Network error",n("tryit-status").className="badge badge-danger",n("tryit-headers-group").style.display="none",le(String(l.message||l)),ae({...d,status:0,statusText:"Network error",ok:!1})}finally{$.disabled=D()}}if(u){if(n("ep-name").value=u.name||"",k.value=u.project,k.disabled=!0,n("ep-path").value=u.path||"",z(u.collection),y.value!==u.collection){const e=document.createElement("option");e.value=u.collection,e.textContent=`${u.collection} (other project \u2014 no longer allowed)`,y.appendChild(e),y.value=u.collection}L.value=u.auth||"public",n("ep-mode").value=u.mode||"list",n("ep-order").value=u.order||"desc",n("ep-limit").value=u.limit??50,n("ep-enabled").checked=u.enabled!==!1,await R(),ee(u.fields||[]),te(u.sort||"");for(const[e,t]of Object.entries(u.filter||{})){const{field:r,op:o}=qe(e);M(r,o,String(t))}v=u,B=JSON.stringify(I())}else await R(),M();ne(),Z();for(const e of["ep-name","ep-path","ep-limit"])n(e).addEventListener("input",h);for(const e of["ep-mode","ep-order","ep-sort"])n(e).addEventListener("change",h);n("ep-enabled").addEventListener("change",h),k.addEventListener("change",async()=>{z(),await R(),h()}),L.addEventListener("change",h),y.addEventListener("change",async()=>{await R(),h()}),n("ep-add-filter").addEventListener("click",()=>{M(),h()}),n("tryit-send").addEventListener("click",Q),n("tryit-history-clear").addEventListener("click",()=>{S.set(W,[]),Y()}),n("tryit-json-toggle").addEventListener("click",()=>{ie(n("tryit-json").style.display==="none")}),n("tryit-body-copy").addEventListener("click",async()=>{if(!V){E.toast("Send a request first.",{type:"info"});return}try{await navigator.clipboard.writeText(V),E.toast("Response copied.",{type:"success"})}catch{E.toast("Copy failed.",{type:"error"})}}),n("tryit-copy-curl").addEventListener("click",async()=>{if(!J){E.toast("Send a request first.",{type:"info"});return}try{await navigator.clipboard.writeText(J),E.toast("curl command copied.",{type:"success"})}catch{E.toast("Copy failed.",{type:"error"})}});async function ce(){const e=I();if(!e.name){E.toast("A name is required.",{type:"error"});return}if(!e.path.startsWith("/")){E.toast('The path must start with "/".',{type:"error"});return}try{if(q)v=await _.apiEndpoints.update(q,e),B=JSON.stringify(I()),E.toast("Endpoint saved.",{type:"success"}),Z();else{const t=await _.apiEndpoints.create(e);E.toast("Endpoint created.",{type:"success"}),location.hash="#/api-endpoints/edit/"+encodeURIComponent(t.id)}}catch(t){E.toast(`Save failed: ${t.message||t}`,{type:"error"})}}n("ep-save").addEventListener("click",ce);function H(e){if(!document.getElementById("ep-save")){document.removeEventListener("keydown",H),O===H&&(O=null);return}if(!(e.metaKey||e.ctrlKey))return;const r=e.key.toLowerCase();r==="s"?(e.preventDefault(),ce()):r==="enter"&&(e.preventDefault(),v&&!D()?Q():D()&&E.toast("Save first \u2014 Send tests the saved definition.",{type:"info"}))}O&&document.removeEventListener("keydown",O),O=H,document.addEventListener("keydown",H),Y(),Domma.icons.scan(x)}};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import{api as w}from"../api.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:"date",label:"Date"},{value:"time",label:"Time"},{value:"url",label:"URL"},{value:"hidden",label:"Hidden field"}],Q=new Set(["select","radio","checkbox-group"]),ae=["public","token","subscriber","editor","manager","admin"],te=["create","read","update","delete"];let g=[],h=null,S=!0,F=null,B="file",X={};function le(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")}function ne(e){return ee.find(t=>t.value===e)?.label||e}function oe(e){const t={...g[e]},n=document.getElementById(`fb-label-${e}`),d=document.getElementById(`fb-name-${e}`),s=document.getElementById(`fb-type-${e}`),r=document.getElementById(`fb-required-${e}`),u=document.getElementById(`fb-placeholder-${e}`),l=document.getElementById(`fb-helper-${e}`);if(n&&(t.label=n.value.trim()||t.label),d&&(t.name=d.value.trim()||t.name),s&&(t.type=s.value||t.type),r&&(t.required=r.checked),u&&(t.placeholder=u.value.trim()),l&&(t.helper=l.value.trim()),Q.has(t.type)){const a=document.getElementById(`fb-options-${e}`);a&&(t.options=a.value.split(`
|
|
2
|
-
`).filter(c=>c.trim()).map(c=>{const[p,...y]=c.split(":");return{value:p.trim(),label:y.join(":").trim()||p.trim()}}))}const o=document.getElementById(`fb-span-${e}`);if(document.getElementById(`fb-fullwidth-${e}`)?.checked)t.fullWidth=!0,delete t.span;else{delete t.fullWidth;const a=parseInt(o?.value,10);a>1?t.span=a:delete t.span}return t}function Z(){return g.map((e,t)=>oe(t))}function de(e,t){const n=document.createElement("div");n.className="fb-field-card",n.dataset.index=t,n.style.cssText="border:1px solid var(--border
|
|
3
|
-
`),A.appendChild(_),A.appendChild(D);const x=document.createElement("div");x.className="fb-grid-row",x.style.gridTemplateColumns="1fr auto",x.style.gap=".6rem",x.style.alignItems="end",x.style.marginBottom=".6rem",x.style.display=document.getElementById("collection-layout")?.value==="grid"?"grid":"none";const G=document.createElement("div"),J=document.createElement("label");J.className="form-label",J.textContent="Column Span";const k=document.createElement("input");k.id=`fb-span-${t}`,k.type="number",k.className="form-input",k.min="1",k.max="6",k.value=e.span>1?String(e.span):"1",G.appendChild(J),G.appendChild(k);const K=document.createElement("div");K.style.cssText="padding-bottom:.35rem;";const U=document.createElement("label");U.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;white-space:nowrap;";const W=document.createElement("input");return W.id=`fb-fullwidth-${t}`,W.type="checkbox",W.checked=!!e.fullWidth,U.appendChild(W),U.appendChild(document.createTextNode("Full Width")),K.appendChild(U),x.appendChild(G),x.appendChild(K),a.appendChild(c),a.appendChild(N),a.appendChild(A),a.appendChild(x),d.addEventListener("click",()=>{const i=a.style.display!=="none";a.style.display=i?"none":"",l.style.transform=i?"":"rotate(180deg)"}),n.appendChild(d),n.appendChild(a),n}function H(e){if(e){if(e.textContent="",g.length===0){const t=document.createElement("p");t.className="text-muted",t.id="fields-empty-msg",t.style.cssText="text-align:center;padding:2rem 0;",t.textContent='No fields yet. Click "Add Field" to get started.',e.appendChild(t);return}g.forEach((t,n)=>{e.appendChild(de(t,n))})}}function ie(e,t){t.textContent="",te.forEach(n=>{const d=e?.[n]||{enabled:!1,access:"admin"},s=document.createElement("div");s.style.cssText="display:grid;grid-template-columns:140px 1fr 160px;gap:.75rem;align-items:center;padding:.6rem 0;border-bottom:1px solid var(--border
|
|
2
|
+
`).filter(c=>c.trim()).map(c=>{const[p,...y]=c.split(":");return{value:p.trim(),label:y.join(":").trim()||p.trim()}}))}const o=document.getElementById(`fb-span-${e}`);if(document.getElementById(`fb-fullwidth-${e}`)?.checked)t.fullWidth=!0,delete t.span;else{delete t.fullWidth;const a=parseInt(o?.value,10);a>1?t.span=a:delete t.span}return t}function Z(){return g.map((e,t)=>oe(t))}function de(e,t){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;margin-bottom:.75rem;overflow:hidden;";const d=document.createElement("div");d.className="fb-field-header",d.style.cssText="display:flex;align-items:center;gap:.5rem;padding:.6rem .75rem;background:var(--dm-card-header-bg,rgba(255,255,255,.03));cursor:pointer;user-select:none;";const s=document.createElement("span");s.textContent="\u283F",s.style.cssText="cursor:grab;opacity:.4;font-size:1.1rem;flex-shrink:0;",n.draggable=!0,s.addEventListener("mousedown",()=>{n.draggable=!0}),n.addEventListener("dragstart",i=>{F=t,i.dataTransfer.effectAllowed="move",n.style.opacity="0.4"}),n.addEventListener("dragend",()=>{n.style.opacity="",document.querySelectorAll(".fb-field-card").forEach(i=>i.classList.remove("fb-drag-over"))}),n.addEventListener("dragover",i=>{i.preventDefault(),i.dataTransfer.dropEffect="move",document.querySelectorAll(".fb-field-card").forEach(C=>C.classList.remove("fb-drag-over")),n.classList.add("fb-drag-over")}),n.addEventListener("dragleave",()=>{n.classList.remove("fb-drag-over")}),n.addEventListener("drop",i=>{if(i.preventDefault(),n.classList.remove("fb-drag-over"),F===null||F===t)return;g=Z();const[C]=g.splice(F,1);g.splice(t,0,C),F=null,H(document.getElementById("fields-list"))});const r=document.createElement("span");r.className="fb-field-summary",r.style.cssText="flex:1;font-weight:500;font-size:.9rem;",r.textContent=e.label||"(Untitled field)";const u=document.createElement("span");u.style.cssText="font-size:.75rem;opacity:.5;",u.textContent=ne(e.type);const l=document.createElement("span");l.className="fb-field-chevron",l.textContent="\u25BE",l.style.cssText="opacity:.5;transition:transform .2s;";const o=document.createElement("button");o.type="button",o.innerHTML='<span data-icon="x"></span>',o.className="btn btn-sm btn-ghost",o.style.cssText="padding:.15rem .45rem;line-height:1;opacity:.6;",o.title="Remove field",o.addEventListener("click",i=>{i.stopPropagation(),g.splice(t,1),H(document.getElementById("fields-list"))}),d.appendChild(s),d.appendChild(r),d.appendChild(u),d.appendChild(l),d.appendChild(o),Domma.icons.scan(o);const a=document.createElement("div");a.className="fb-field-body",a.style.cssText="padding:.75rem;display:none;";const c=document.createElement("div");c.style.cssText="display:grid;grid-template-columns:1fr 1fr 1fr;gap:.6rem;margin-bottom:.6rem;";const p=document.createElement("div"),y=document.createElement("label");y.className="form-label",y.textContent="Label";const b=document.createElement("input");b.id=`fb-label-${t}`,b.type="text",b.className="form-input",b.value=e.label||"",b.addEventListener("input",()=>{r.textContent=b.value.trim()||"(Untitled field)";const i=document.getElementById(`fb-name-${t}`);i&&!i.dataset.manual&&(i.value=le(b.value).replace(/-/g,"_"))}),p.appendChild(y),p.appendChild(b);const f=document.createElement("div"),$=document.createElement("label");$.className="form-label",$.textContent="Name (key)";const v=document.createElement("input");v.id=`fb-name-${t}`,v.type="text",v.className="form-input",v.value=e.name||"",v.addEventListener("input",()=>{v.dataset.manual="1"}),f.appendChild($),f.appendChild(v);const T=document.createElement("div"),L=document.createElement("label");L.className="form-label",L.textContent="Type";const m=document.createElement("select");m.id=`fb-type-${t}`,m.className="form-input",ee.forEach(i=>{const C=document.createElement("option");C.value=i.value,C.textContent=i.label,i.value===e.type&&(C.selected=!0),m.appendChild(C)}),m.addEventListener("change",()=>{u.textContent=ne(m.value);const i=a.querySelector(".fb-options-wrap");i&&(i.style.display=Q.has(m.value)?"":"none")}),T.appendChild(L),T.appendChild(m),c.appendChild(p),c.appendChild(f),c.appendChild(T);const N=document.createElement("div");N.style.cssText="display:grid;grid-template-columns:1fr 1fr auto;gap:.6rem;align-items:end;margin-bottom:.6rem;";const I=document.createElement("div"),P=document.createElement("label");P.className="form-label",P.textContent="Placeholder";const j=document.createElement("input");j.id=`fb-placeholder-${t}`,j.type="text",j.className="form-input",j.value=e.placeholder||"",I.appendChild(P),I.appendChild(j);const O=document.createElement("div"),V=document.createElement("label");V.className="form-label",V.textContent="Helper text";const q=document.createElement("input");q.id=`fb-helper-${t}`,q.type="text",q.className="form-input",q.value=e.helper||"",O.appendChild(V),O.appendChild(q);const Y=document.createElement("div");Y.style.cssText="padding-bottom:.35rem;";const M=document.createElement("label");M.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;white-space:nowrap;";const z=document.createElement("input");z.id=`fb-required-${t}`,z.type="checkbox",z.checked=!!e.required,M.appendChild(z),M.appendChild(document.createTextNode("Required")),Y.appendChild(M),N.appendChild(I),N.appendChild(O),N.appendChild(Y);const A=document.createElement("div");A.className="fb-options-wrap",A.style.display=Q.has(e.type)?"":"none";const _=document.createElement("label");_.className="form-label",_.textContent="Options (one per line: value: Label)";const D=document.createElement("textarea");D.id=`fb-options-${t}`,D.className="form-input",D.rows=4,D.value=(e.options||[]).map(i=>typeof i=="string"?`${i}: ${i}`:`${i.value??""}: ${i.label??i.value??""}`).join(`
|
|
3
|
+
`),A.appendChild(_),A.appendChild(D);const x=document.createElement("div");x.className="fb-grid-row",x.style.gridTemplateColumns="1fr auto",x.style.gap=".6rem",x.style.alignItems="end",x.style.marginBottom=".6rem",x.style.display=document.getElementById("collection-layout")?.value==="grid"?"grid":"none";const G=document.createElement("div"),J=document.createElement("label");J.className="form-label",J.textContent="Column Span";const k=document.createElement("input");k.id=`fb-span-${t}`,k.type="number",k.className="form-input",k.min="1",k.max="6",k.value=e.span>1?String(e.span):"1",G.appendChild(J),G.appendChild(k);const K=document.createElement("div");K.style.cssText="padding-bottom:.35rem;";const U=document.createElement("label");U.style.cssText="display:flex;align-items:center;gap:.4rem;cursor:pointer;white-space:nowrap;";const W=document.createElement("input");return W.id=`fb-fullwidth-${t}`,W.type="checkbox",W.checked=!!e.fullWidth,U.appendChild(W),U.appendChild(document.createTextNode("Full Width")),K.appendChild(U),x.appendChild(G),x.appendChild(K),a.appendChild(c),a.appendChild(N),a.appendChild(A),a.appendChild(x),d.addEventListener("click",()=>{const i=a.style.display!=="none";a.style.display=i?"none":"",l.style.transform=i?"":"rotate(180deg)"}),n.appendChild(d),n.appendChild(a),n}function H(e){if(e){if(e.textContent="",g.length===0){const t=document.createElement("p");t.className="text-muted",t.id="fields-empty-msg",t.style.cssText="text-align:center;padding:2rem 0;",t.textContent='No fields yet. Click "Add Field" to get started.',e.appendChild(t);return}g.forEach((t,n)=>{e.appendChild(de(t,n))})}}function ie(e,t){t.textContent="",te.forEach(n=>{const d=e?.[n]||{enabled:!1,access:"admin"},s=document.createElement("div");s.style.cssText="display:grid;grid-template-columns:140px 1fr 160px;gap:.75rem;align-items:center;padding:.6rem 0;border-bottom:1px solid var(--dm-border,#333);";const r=document.createElement("strong");r.textContent=n.charAt(0).toUpperCase()+n.slice(1),r.style.cssText="font-size:.9rem;";const u=document.createElement("label");u.style.cssText="display:flex;align-items:center;gap:.45rem;cursor:pointer;font-size:.875rem;";const l=document.createElement("input");l.type="checkbox",l.id=`api-${n}-enabled`,l.checked=!!d.enabled,u.appendChild(l),u.appendChild(document.createTextNode("Enable public access"));const o=document.createElement("select");if(o.id=`api-${n}-access`,o.className="form-input",ae.forEach(a=>{const c=document.createElement("option");c.value=a,c.textContent=a.charAt(0).toUpperCase()+a.slice(1),a===d.access&&(c.selected=!0),o.appendChild(c)}),s.appendChild(r),s.appendChild(u),s.appendChild(o),t.appendChild(s),n==="read"){const a=document.createElement("div");a.style.cssText="display:grid;grid-template-columns:140px 1fr;gap:.75rem;align-items:center;padding:.6rem 0;border-bottom:1px solid var(--dm-border,#333);";const c=document.createElement("label");c.htmlFor="api-read-fields",c.style.cssText="font-size:.85rem;color:var(--dm-text-muted,#aaa);",c.textContent="Read fields";const p=document.createElement("input");p.type="text",p.id="api-read-fields",p.className="form-input",p.placeholder="Comma-separated allowlist \u2014 empty = all fields",p.value=Array.isArray(d.fields)?d.fields.join(", "):"",a.appendChild(c),a.appendChild(p),t.appendChild(a)}})}function ce(e,t){E.dropdown("#storage-adapter-trigger",{items:[{label:"File (default)",value:"file"},{label:"MongoDB",value:"mongodb"}],onSelect:({item:d})=>{e.find("#storage-adapter").val(d.value),e.find("#storage-adapter-label").text(d.label);const s=d.value==="mongodb";e.find("#storage-connection-group").toggle(s),e.find("#storage-migration-warning").toggle(s&&!S)}});const n=t.map(d=>({label:d,value:d}));E.dropdown("#storage-connection-trigger",{items:n.length?n:[{label:"default",value:"default"}],onSelect:({item:d})=>{e.find("#storage-connection").val(d.value),e.find("#storage-connection-label").text(d.label)}})}function se(){return(document.getElementById("storage-adapter")?.value||"file")==="mongodb"?{adapter:"mongodb",connection:document.getElementById("storage-connection")?.value||"default"}:{adapter:"file"}}function re(){const e={};return te.forEach(t=>{const n=document.getElementById(`api-${t}-enabled`)?.checked??!1,d=document.getElementById(`api-${t}-access`)?.value||"admin";if(e[t]={enabled:n,access:d},t==="read"){const s=(document.getElementById("api-read-fields")?.value||"").split(",").map(r=>r.trim()).filter(Boolean);s.length&&(e[t].fields=s)}}),e}export const collectionEditorView={templateUrl:"/admin/js/templates/collection-editor.html",async onMount(e){g=[],h=null,S=!0;const t=window.location.hash.match(/\/collections\/edit\/([^/?#]+)/);t&&(h=t[1],S=!1),E.tabs(e.find("#collection-tabs").get(0)),e.find("#collection-layout").get(0)?.addEventListener("change",function(){const l=this.value==="grid";e.find("#collection-columns-group").get(0).style.display=l?"":"none",document.querySelectorAll(".fb-grid-row").forEach(o=>{o.style.display=l?"grid":"none"})});const n=e.find("#fields-list").get(0),d=e.find("#api-access-rows").get(0);X={};const s=e.find("#collection-project").get(0);if(s)try{(await w.projects.list()).forEach(o=>{const a=document.createElement("option");a.value=o.slug,a.textContent=o.name||o.slug,s.appendChild(a)})}catch{}const r=await w.collections.proStatus();r?.pro&&h!=="roles"&&(e.find("#storage-tab-btn").show(),ce(e,r.connections));let u={create:{enabled:!1,access:"admin"},read:{enabled:!0,access:"public"},update:{enabled:!1,access:"admin"},delete:{enabled:!1,access:"admin"}};if(S){const l=e.find("#field-title").get(0),o=e.find("#field-slug").get(0);l&&o&&(l.addEventListener("input",()=>{o.dataset.manual||(o.value=le(l.value))}),o.addEventListener("input",()=>{o.dataset.manual="1"}))}else try{const l=await w.collections.get(h);if(!l){E.toast("Collection not found.",{type:"error"}),R.navigate("/collections");return}const o=e.find("#editor-title-text").get(0);o&&(o.textContent=l.title),e.find("#field-title").val(l.title||""),e.find("#field-slug").val(l.slug||""),e.find("#field-slug").prop("readonly",!0),e.find("#slug-hint").get(0).textContent="Slug cannot be changed after creation.",e.find("#field-description").val(l.description||""),e.find("#collection-layout").val(l.layout||"stacked"),e.find("#collection-columns").val(l.columns||2),e.find("#collection-bundled").prop("checked",!!l.bundled),X=l.meta||{},e.find("#collection-project").val(l.meta?.project||""),e.find("#collection-columns-group").get(0).style.display=l.layout==="grid"?"":"none",g=l.fields||[],u=l.api||u,B=l.storage?.adapter||"file",l.storage&&(e.find("#storage-adapter").val(l.storage.adapter||"file"),e.find("#storage-adapter-label").text(l.storage.adapter==="mongodb"?"MongoDB":"File (default)"),l.storage.adapter==="mongodb"&&(e.find("#storage-connection-group").show(),e.find("#storage-connection").val(l.storage.connection||"default"),e.find("#storage-connection-label").text(l.storage.connection||"default"))),h==="roles"&&e.find("#storage-tab-btn").hide()}catch{E.toast("Failed to load collection.",{type:"error"}),R.navigate("/collections");return}H(n),ie(u,d),e.find("#add-field-btn").off("click").on("click",()=>{g=Z(),g.push({id:`field-${Date.now()}`,name:"",label:"",type:"string",required:!1,placeholder:"",helper:"",options:[],validation:[],logic:null}),H(n);const l=n.querySelectorAll(".fb-field-card");if(l.length){const o=l[l.length-1],a=o.querySelector(".fb-field-body"),c=o.querySelector(".fb-field-chevron");a&&(a.style.display=""),c&&(c.style.transform="rotate(180deg)"),o.querySelector(`#fb-label-${g.length-1}`)?.focus()}}),e.find("#save-collection-btn").off("click").on("click",async()=>{const l=e.find("#field-title").val().trim(),o=e.find("#field-slug").val().trim(),a=e.find("#field-description").val().trim();if(!l){E.toast("Title is required.",{type:"warning"});return}const c=Z(),p=re(),y=e.find("#collection-layout").val()||"stacked",b=parseInt(e.find("#collection-columns").val(),10)||2,f=se(),$=e.find("#collection-bundled").is(":checked"),v=e.find("#collection-project").val()||"",T={...X||{},project:v||null},L=e.find("#save-collection-btn");L.prop("disabled",!0);try{if(S){const m=await w.collections.create({title:l,slug:o,description:a,layout:y,columns:b,fields:c,api:p,storage:f,meta:T,...$?{bundled:!0}:{}});h=m.slug,B=f.adapter||"file",S=!1,E.toast("Collection created.",{type:"success"}),R.navigate(`/collections/edit/${m.slug}`)}else if((f.adapter||"file")!==B){let m=0;try{m=(await w.collections.listEntries(h,{limit:1}))?.total??0}catch{}const N=B==="file"?`file \u2192 ${f.adapter}`:`${B} \u2192 ${f.adapter||"file"}`;if(m>0&&await E.confirm(`You changed the storage adapter (${N}).
|
|
4
4
|
|
|
5
5
|
Migrate ${m} existing ${m===1?"entry":"entries"} to the new storage?`)){const I=await w.collections.migrateStorage(h,f);B=f.adapter||"file",E.toast(`Migrated ${I.migrated} of ${I.total} entries.`,{type:"success"})}else await w.collections.update(h,{title:l,description:a,layout:y,columns:b,fields:c,api:p,storage:f,meta:T,...$?{bundled:!0}:{bundled:!1}}),B=f.adapter||"file",E.toast("Collection saved.",{type:"success"})}else await w.collections.update(h,{title:l,description:a,layout:y,columns:b,fields:c,api:p,storage:f,meta:T,...$?{bundled:!0}:{bundled:!1}}),E.toast("Collection saved.",{type:"success"})}catch(m){E.toast(m.message||"Failed to save.",{type:"error"})}finally{L.prop("disabled",!1)}}),Domma.icons.scan()}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{api as h}from"../api.js";function M(t){return String(t).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}let f=null,g=null,p=[],j=1,w=[],x={};const z=50;export const collectionEntriesView={templateUrl:"/admin/js/templates/collection-entries.html",async onMount(t){p=[],j=1,g=null,w=[],x={};const d=window.location.hash.match(/\/collections\/([^/?#]+)\/entries/);if(f=d?d[1]:null,!f){E.toast("No collection selected.",{type:"error"});return}try{if(g=await h.collections.get(f),!g){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 a=t.find("#edit-schema-btn").get(0);a&&a.setAttribute("href",`#/collections/edit/${f}`);const e=t.find("#form-btn").get(0);e&&(new Set(["roles","user-profiles"]).has(f)?e.style.display="none":e.setAttribute("href",`#/forms/edit/${f}`))}catch{E.toast("Failed to load collection schema.",{type:"error"});return}try{w=await h.actions.forCollection(f)}catch{w=[]}await v(t),await k(t);const n=t.find("#entry-search").get(0);n&&n.addEventListener("input",()=>{const o=n.value.toLowerCase().trim(),a=o?p.filter(e=>Object.values(e.data||{}).some(l=>String(l).toLowerCase().includes(o))):p;C(a,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"}),a=document.createElement("div");a.style.cssText="padding:1.25rem;display:flex;flex-direction:column;gap:1rem;",["json","csv"].forEach(e=>{const l=document.createElement("button");l.className="btn btn-ghost",l.style.cssText="justify-content:flex-start;gap:.5rem;",l.textContent=e==="json"?"Export as JSON":"Export as CSV",l.addEventListener("click",()=>{fetch(`/api/collections/${f}/export?format=${e}`,{headers:{Authorization:`Bearer ${S.get("auth_token")||""}`}}).then(s=>s.blob()).then(s=>{const r=document.createElement("a");r.href=URL.createObjectURL(s),r.download=`${f}-entries.${e}`,document.body.appendChild(r),r.click(),document.body.removeChild(r),URL.revokeObjectURL(r.href),o.close()}).catch(()=>E.toast("Export failed.",{type:"error"}))}),a.appendChild(l)}),o.setContent(a),o.open()}),t.find("#import-btn").off("click").on("click",()=>{B(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(f),p=[],E.toast("All entries cleared.",{type:"success"}),C([],t)}catch{E.toast("Failed to clear entries.",{type:"error"})}}),Domma.icons.scan()}};async function k(t){const m=w.filter(n=>n.access?.rowLevel);if(m.length===0)return;const d=p.map(n=>n.id);d.length!==0&&(await Promise.all(m.map(async n=>{try{const{allowed:o}=await h.actions.checkAccess(n.slug,d);x[n.slug]=new Set(o)}catch{x[n.slug]=null}})),C(p,t))}async function v(t){try{const m=await h.collections.listEntries(f,{limit:500});p=m.entries||m||[]}catch{p=[],E.toast("Could not load entries.",{type:"error"})}C(p,t),await k(t)}function C(t,m){const d=m.find("#entry-count").get(0);d&&(d.textContent=t.length===p.length?`${p.length} entr${p.length!==1?"ies":"y"}`:`Showing ${t.length} of ${p.length}`);const o=[...(g?.fields||[]).slice(0,5).map(e=>({key:`data.${e.name}`,title:e.label||e.name,render:(l,s)=>{const r=s.data?.[e.name]??"",c=String(r),i=document.createElement("span");return i.title=c,i.textContent=c.length>60?c.slice(0,60)+"\u2026":c,i.outerHTML}})),{key:"meta",title:"Created",render:e=>D(e?.createdAt).format("DD MMM YYYY HH:mm")},{key:"id",title:"",render:e=>{const l=document.createElement("div");l.style.cssText="display:flex;gap:.3rem;justify-content:flex-end;flex-wrap:wrap;",w.forEach(c=>{if(x[c.slug]!==void 0&&x[c.slug]!==null&&!x[c.slug].has(e))return;const i=document.createElement("button");i.className="btn btn-sm btn-ghost js-run-action",i.dataset.actionSlug=c.slug,i.dataset.entryId=e,i.dataset.confirmMessage=c.trigger?.confirmMessage||"",i.title=c.title||c.slug;const y=document.createElement("span");y.setAttribute("data-icon",M(c.trigger?.icon||"zap")),i.appendChild(y),i.appendChild(document.createTextNode(" "+(c.trigger?.label||c.title))),l.appendChild(i)});const s=document.createElement("button");s.className="btn btn-sm btn-ghost js-edit-entry",s.dataset.id=e,s.setAttribute("data-tooltip","Edit"),s.innerHTML='<span data-icon="edit"></span>';const r=document.createElement("button");return r.className="btn btn-sm btn-danger js-delete-entry",r.dataset.id=e,r.setAttribute("data-tooltip","Delete"),r.innerHTML='<span data-icon="trash"></span>',l.appendChild(s),l.appendChild(r),l.outerHTML}}];T.create("#entries-table",{data:t,columns:o,emptyMessage:'No entries yet. Click "Add Entry" to get started.'});const a=document.querySelector("#entries-table");a&&(a.querySelectorAll(".js-run-action").forEach(e=>{e.addEventListener("click",async l=>{l.stopPropagation();const{actionSlug:s,entryId:r,confirmMessage:c}=e.dataset;if(!(c&&!await E.confirm(c))){e.disabled=!0;try{const i=await h.actions.execute(s,r);if(i.success)E.toast(`Action completed (${i.stepsCompleted} step${i.stepsCompleted!==1?"s":""}).`,{type:"success"}),await v({find:y=>({get:u=>document.querySelector(y)})});else{const y=i.results?.find(u=>!u.success);E.toast(y?.error||"Action failed.",{type:"error"})}}catch(i){E.toast(i.message||"Action failed.",{type:"error"})}finally{e.disabled=!1}}})}),a.querySelectorAll(".js-edit-entry").forEach(e=>{e.addEventListener("click",l=>{l.stopPropagation();const s=p.find(r=>r.id===e.dataset.id);s&&A(s,null)})}),a.querySelectorAll(".js-delete-entry").forEach(e=>{e.addEventListener("click",async l=>{if(l.stopPropagation(),!!await E.confirm("Delete this entry?"))try{await h.collections.deleteEntry(f,e.dataset.id),p=p.filter(r=>r.id!==e.dataset.id),E.toast("Entry deleted.",{type:"success"}),C(p,{find:r=>({get:c=>document.querySelector(r)})})}catch{E.toast("Failed to delete entry.",{type:"error"})}})}),a.querySelectorAll("tbody tr").forEach((e,l)=>{const s=t[l];s&&(e.style.cursor="pointer",e.addEventListener("click",r=>{r.target.closest(".js-edit-entry, .js-delete-entry")||q(s)}))}),Domma.icons.scan(a),a.querySelectorAll("[data-tooltip]").forEach(e=>{E.tooltip(e,{content:e.getAttribute("data-tooltip"),position:"top"})}))}function N(t,m){const d={};return t.forEach(n=>{const o={};n.fullWidth?o.span=m:n.span>1&&(o.span=n.span),n.placeholder&&(o.placeholder=n.placeholder),n.helper&&(o.hint=n.helper);const a=n.type==="checkbox"?"boolean":n.type==="date"?"string":n.type;d[n.name]={type:a,label:n.label,required:n.required,options:n.options,...Object.keys(o).length?{formConfig:o}:{}}}),d}function A(t,m){const d=!!t,n=E.modal({title:d?"Edit Entry":"Add Entry",size:"md"}),o=document.createElement("div");o.style.cssText="padding:.25rem 0 .5rem;";const a=g?.fields||[],e=g?.columns||2,l=g?.layout||"stacked",s=document.createElement("div");F.create(N(a,e),t?.data||{},{layout:l,columns:e,showSubmitButton:!1}).renderTo(s),(a||[]).forEach(y=>{if(y.type!=="date"||!y.name)return;const u=s.querySelector(`[name="${y.name}"]`);u&&u.type!=="date"&&(u.type="date")}),o.appendChild(s);const r=document.createElement("div");r.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:.5rem;";const c=document.createElement("button");c.className="btn btn-ghost",c.textContent="Cancel";const i=document.createElement("button");i.className="btn btn-primary",i.textContent=d?"Save Changes":"Add Entry",r.appendChild(c),r.appendChild(i),o.appendChild(r),n.element.appendChild(o),n.open(),c.addEventListener("click",()=>n.close()),i.addEventListener("click",async()=>{const y={};a.forEach(u=>{const b=s.querySelector(`[name="${u.name}"]`);b&&(y[u.name]=u.type==="checkbox"?b.checked:b.value)}),i.disabled=!0;try{if(d){const u=await h.collections.updateEntry(f,t.id,y),b=p.findIndex(L=>L.id===t.id);b!==-1&&(p[b]=u),E.toast("Entry updated.",{type:"success"})}else{const u=await h.collections.createEntry(f,y);p.unshift(u),E.toast("Entry added.",{type:"success"})}n.close(),C(p,m||{find:()=>({get:()=>null})})}catch(u){E.toast(u.message||"Failed to save entry.",{type:"error"}),i.disabled=!1}})}function q(t){const m=document.createElement("div"),d=g?.fields||[],n=document.createElement("div");n.style.cssText="display:flex;flex-direction:column;gap:.75rem;margin-bottom:1.25rem;",d.forEach(e=>{const l=t.data?.[e.name];if(l==null||l==="")return;const s=document.createElement("div");s.style.cssText="border-bottom:1px solid var(--border-color,#333);padding-bottom:.6rem;";const r=document.createElement("strong");r.textContent=e.label||e.name,r.style.cssText="display:block;font-size:.8rem;color:var(--text-muted,#888);margin-bottom:.2rem;";const c=document.createElement("p");c.textContent=String(l),c.style.cssText="margin:0;word-break:break-word;",s.appendChild(r),s.appendChild(c),n.appendChild(s)});const o=document.createElement("div");if(o.style.cssText="font-size:.8rem;color:var(--text-muted,#888);display:flex;flex-direction:column;gap:.3rem;border-top:1px solid var(--border-color,#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")}`,o.appendChild(e)}if(t.meta?.source){const e=document.createElement("span");e.textContent=`Source: ${t.meta.source}`,o.appendChild(e)}m.appendChild(n),m.appendChild(o);const a=E.modal({title:"Entry Details",size:"md"});a.element.appendChild(m),a.open()}function B(t){const m=E.modal({title:"Import Entries",size:"md"}),d=document.createElement("div");d.style.cssText="padding:.25rem 0 .5rem;display:flex;flex-direction:column;gap:.75rem;";const n=document.createElement("p");n.textContent='Paste a JSON array of entries. Each item should have a "data" object with field values.',n.style.cssText="font-size:.875rem;color:var(--text-muted,#888);margin:0;",d.appendChild(n);const o=document.createElement("textarea");o.className="form-input",o.rows=10,o.placeholder='[{"data": {"name": "Example"}}, ...]',d.appendChild(o);const a=document.createElement("p");a.style.cssText="font-size:.875rem;margin:0;display:none;",d.appendChild(a);const e=document.createElement("div");e.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;";const l=document.createElement("button");l.className="btn btn-ghost",l.textContent="Cancel";const s=document.createElement("button");s.className="btn btn-primary",s.textContent="Import",e.appendChild(l),e.appendChild(s),d.appendChild(e),m.element.appendChild(d),m.open(),l.addEventListener("click",()=>m.close()),s.addEventListener("click",async()=>{let r;try{if(r=JSON.parse(o.value),!Array.isArray(r))throw new Error("Must be a JSON array")}catch(c){a.style.display="",a.style.color="var(--danger,#f87171)",a.textContent=`Invalid JSON: ${c.message}`;return}s.disabled=!0;try{const c=await h.collections.import(f,r);a.style.display="",a.style.color="var(--success,#4ade80)",a.textContent=`Imported ${c.imported} entries. Skipped: ${c.skipped}.`,await v(t||{find:()=>({get:()=>null,off:()=>({on:()=>{}})})}),setTimeout(()=>m.close(),1500)}catch(c){a.style.display="",a.style.color="var(--danger,#f87171)",a.textContent=c.message||"Import failed."}finally{s.disabled=!1}})}
|
|
1
|
+
import{api as h}from"../api.js";function M(t){return String(t).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}let f=null,g=null,p=[],j=1,w=[],x={};const z=50;export const collectionEntriesView={templateUrl:"/admin/js/templates/collection-entries.html",async onMount(t){p=[],j=1,g=null,w=[],x={};const d=window.location.hash.match(/\/collections\/([^/?#]+)\/entries/);if(f=d?d[1]:null,!f){E.toast("No collection selected.",{type:"error"});return}try{if(g=await h.collections.get(f),!g){E.toast("Collection not found.",{type:"error"}),R.navigate("/collections");return}const a=t.find("#entries-title").get(0);a&&(a.textContent=g.title+" \u2014 Entries");const o=t.find("#edit-schema-btn").get(0);o&&o.setAttribute("href",`#/collections/edit/${f}`);const e=t.find("#form-btn").get(0);e&&(new Set(["roles","user-profiles"]).has(f)?e.style.display="none":e.setAttribute("href",`#/forms/edit/${f}`))}catch{E.toast("Failed to load collection schema.",{type:"error"});return}try{w=await h.actions.forCollection(f)}catch{w=[]}await v(t),await k(t);const n=t.find("#entry-search").get(0);n&&n.addEventListener("input",()=>{const a=n.value.toLowerCase().trim(),o=a?p.filter(e=>Object.values(e.data||{}).some(l=>String(l).toLowerCase().includes(a))):p;C(o,t)}),t.find("#add-entry-btn").off("click").on("click",()=>{A(null,t)}),t.find("#export-btn").off("click").on("click",()=>{const a=E.slideover({title:"Export Entries",size:"sm",position:"right"}),o=document.createElement("div");o.style.cssText="padding:1.25rem;display:flex;flex-direction:column;gap:1rem;",["json","csv"].forEach(e=>{const l=document.createElement("button");l.className="btn btn-ghost",l.style.cssText="justify-content:flex-start;gap:.5rem;",l.textContent=e==="json"?"Export as JSON":"Export as CSV",l.addEventListener("click",()=>{fetch(`/api/collections/${f}/export?format=${e}`,{headers:{Authorization:`Bearer ${S.get("auth_token")||""}`}}).then(s=>s.blob()).then(s=>{const r=document.createElement("a");r.href=URL.createObjectURL(s),r.download=`${f}-entries.${e}`,document.body.appendChild(r),r.click(),document.body.removeChild(r),URL.revokeObjectURL(r.href),a.close()}).catch(()=>E.toast("Export failed.",{type:"error"}))}),o.appendChild(l)}),a.setContent(o),a.open()}),t.find("#import-btn").off("click").on("click",()=>{B(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(f),p=[],E.toast("All entries cleared.",{type:"success"}),C([],t)}catch{E.toast("Failed to clear entries.",{type:"error"})}}),Domma.icons.scan()}};async function k(t){const m=w.filter(n=>n.access?.rowLevel);if(m.length===0)return;const d=p.map(n=>n.id);d.length!==0&&(await Promise.all(m.map(async n=>{try{const{allowed:a}=await h.actions.checkAccess(n.slug,d);x[n.slug]=new Set(a)}catch{x[n.slug]=null}})),C(p,t))}async function v(t){try{const m=await h.collections.listEntries(f,{limit:500});p=m.entries||m||[]}catch{p=[],E.toast("Could not load entries.",{type:"error"})}C(p,t),await k(t)}function C(t,m){const d=m.find("#entry-count").get(0);d&&(d.textContent=t.length===p.length?`${p.length} entr${p.length!==1?"ies":"y"}`:`Showing ${t.length} of ${p.length}`);const a=[...(g?.fields||[]).slice(0,5).map(e=>({key:`data.${e.name}`,title:e.label||e.name,render:(l,s)=>{const r=s.data?.[e.name]??"",c=String(r),i=document.createElement("span");return i.title=c,i.textContent=c.length>60?c.slice(0,60)+"\u2026":c,i.outerHTML}})),{key:"meta",title:"Created",render:e=>D(e?.createdAt).format("DD MMM YYYY HH:mm")},{key:"id",title:"",render:e=>{const l=document.createElement("div");l.style.cssText="display:flex;gap:.3rem;justify-content:flex-end;flex-wrap:wrap;",w.forEach(c=>{if(x[c.slug]!==void 0&&x[c.slug]!==null&&!x[c.slug].has(e))return;const i=document.createElement("button");i.className="btn btn-sm btn-ghost js-run-action",i.dataset.actionSlug=c.slug,i.dataset.entryId=e,i.dataset.confirmMessage=c.trigger?.confirmMessage||"",i.title=c.title||c.slug;const y=document.createElement("span");y.setAttribute("data-icon",M(c.trigger?.icon||"zap")),i.appendChild(y),i.appendChild(document.createTextNode(" "+(c.trigger?.label||c.title))),l.appendChild(i)});const s=document.createElement("button");s.className="btn btn-sm btn-ghost js-edit-entry",s.dataset.id=e,s.setAttribute("data-tooltip","Edit"),s.innerHTML='<span data-icon="edit"></span>';const r=document.createElement("button");return r.className="btn btn-sm btn-danger js-delete-entry",r.dataset.id=e,r.setAttribute("data-tooltip","Delete"),r.innerHTML='<span data-icon="trash"></span>',l.appendChild(s),l.appendChild(r),l.outerHTML}}];T.create("#entries-table",{data:t,columns:a,emptyMessage:'No entries yet. Click "Add Entry" to get started.'});const o=document.querySelector("#entries-table");o&&(o.querySelectorAll(".js-run-action").forEach(e=>{e.addEventListener("click",async l=>{l.stopPropagation();const{actionSlug:s,entryId:r,confirmMessage:c}=e.dataset;if(!(c&&!await E.confirm(c))){e.disabled=!0;try{const i=await h.actions.execute(s,r);if(i.success)E.toast(`Action completed (${i.stepsCompleted} step${i.stepsCompleted!==1?"s":""}).`,{type:"success"}),await v({find:y=>({get:u=>document.querySelector(y)})});else{const y=i.results?.find(u=>!u.success);E.toast(y?.error||"Action failed.",{type:"error"})}}catch(i){E.toast(i.message||"Action failed.",{type:"error"})}finally{e.disabled=!1}}})}),o.querySelectorAll(".js-edit-entry").forEach(e=>{e.addEventListener("click",l=>{l.stopPropagation();const s=p.find(r=>r.id===e.dataset.id);s&&A(s,null)})}),o.querySelectorAll(".js-delete-entry").forEach(e=>{e.addEventListener("click",async l=>{if(l.stopPropagation(),!!await E.confirm("Delete this entry?"))try{await h.collections.deleteEntry(f,e.dataset.id),p=p.filter(r=>r.id!==e.dataset.id),E.toast("Entry deleted.",{type:"success"}),C(p,{find:r=>({get:c=>document.querySelector(r)})})}catch{E.toast("Failed to delete entry.",{type:"error"})}})}),o.querySelectorAll("tbody tr").forEach((e,l)=>{const s=t[l];s&&(e.style.cursor="pointer",e.addEventListener("click",r=>{r.target.closest(".js-edit-entry, .js-delete-entry")||q(s)}))}),Domma.icons.scan(o),o.querySelectorAll("[data-tooltip]").forEach(e=>{E.tooltip(e,{content:e.getAttribute("data-tooltip"),position:"top"})}))}function N(t,m){const d={};return t.forEach(n=>{const a={};n.fullWidth?a.span=m:n.span>1&&(a.span=n.span),n.placeholder&&(a.placeholder=n.placeholder),n.helper&&(a.hint=n.helper);const o=n.type==="checkbox"?"boolean":n.type==="date"?"string":n.type;d[n.name]={type:o,label:n.label,required:n.required,options:n.options,...Object.keys(a).length?{formConfig:a}:{}}}),d}function A(t,m){const d=!!t,n=E.modal({title:d?"Edit Entry":"Add Entry",size:"md"}),a=document.createElement("div");a.style.cssText="padding:.25rem 0 .5rem;";const o=g?.fields||[],e=g?.columns||2,l=g?.layout||"stacked",s=document.createElement("div");F.create(N(o,e),t?.data||{},{layout:l,columns:e,showSubmitButton:!1}).renderTo(s),(o||[]).forEach(y=>{if(y.type!=="date"||!y.name)return;const u=s.querySelector(`[name="${y.name}"]`);u&&u.type!=="date"&&(u.type="date")}),a.appendChild(s);const r=document.createElement("div");r.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;margin-top:.5rem;";const c=document.createElement("button");c.className="btn btn-ghost",c.textContent="Cancel";const i=document.createElement("button");i.className="btn btn-primary",i.textContent=d?"Save Changes":"Add Entry",r.appendChild(c),r.appendChild(i),a.appendChild(r),n.element.appendChild(a),n.open(),c.addEventListener("click",()=>n.close()),i.addEventListener("click",async()=>{const y={};o.forEach(u=>{const b=s.querySelector(`[name="${u.name}"]`);b&&(y[u.name]=u.type==="checkbox"?b.checked:b.value)}),i.disabled=!0;try{if(d){const u=await h.collections.updateEntry(f,t.id,y),b=p.findIndex(L=>L.id===t.id);b!==-1&&(p[b]=u),E.toast("Entry updated.",{type:"success"})}else{const u=await h.collections.createEntry(f,y);p.unshift(u),E.toast("Entry added.",{type:"success"})}n.close(),C(p,m||{find:()=>({get:()=>null})})}catch(u){E.toast(u.message||"Failed to save entry.",{type:"error"}),i.disabled=!1}})}function q(t){const m=document.createElement("div"),d=g?.fields||[],n=document.createElement("div");n.style.cssText="display:flex;flex-direction:column;gap:.75rem;margin-bottom:1.25rem;",d.forEach(e=>{const l=t.data?.[e.name];if(l==null||l==="")return;const s=document.createElement("div");s.style.cssText="border-bottom:1px solid var(--dm-border,#333);padding-bottom:.6rem;";const r=document.createElement("strong");r.textContent=e.label||e.name,r.style.cssText="display:block;font-size:.8rem;color:var(--dm-text-muted,#888);margin-bottom:.2rem;";const c=document.createElement("p");c.textContent=String(l),c.style.cssText="margin:0;word-break:break-word;",s.appendChild(r),s.appendChild(c),n.appendChild(s)});const a=document.createElement("div");if(a.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")}`,a.appendChild(e)}if(t.meta?.source){const e=document.createElement("span");e.textContent=`Source: ${t.meta.source}`,a.appendChild(e)}m.appendChild(n),m.appendChild(a);const o=E.modal({title:"Entry Details",size:"md"});o.element.appendChild(m),o.open()}function B(t){const m=E.modal({title:"Import Entries",size:"md"}),d=document.createElement("div");d.style.cssText="padding:.25rem 0 .5rem;display:flex;flex-direction:column;gap:.75rem;";const n=document.createElement("p");n.textContent='Paste a JSON array of entries. Each item should have a "data" object with field values.',n.style.cssText="font-size:.875rem;color:var(--dm-text-muted,#888);margin:0;",d.appendChild(n);const a=document.createElement("textarea");a.className="form-input",a.rows=10,a.placeholder='[{"data": {"name": "Example"}}, ...]',d.appendChild(a);const o=document.createElement("p");o.style.cssText="font-size:.875rem;margin:0;display:none;",d.appendChild(o);const e=document.createElement("div");e.style.cssText="display:flex;justify-content:flex-end;gap:.5rem;";const l=document.createElement("button");l.className="btn btn-ghost",l.textContent="Cancel";const s=document.createElement("button");s.className="btn btn-primary",s.textContent="Import",e.appendChild(l),e.appendChild(s),d.appendChild(e),m.element.appendChild(d),m.open(),l.addEventListener("click",()=>m.close()),s.addEventListener("click",async()=>{let r;try{if(r=JSON.parse(a.value),!Array.isArray(r))throw new Error("Must be a JSON array")}catch(c){o.style.display="",o.style.color="var(--dm-danger,#f87171)",o.textContent=`Invalid JSON: ${c.message}`;return}s.disabled=!0;try{const c=await h.collections.import(f,r);o.style.display="",o.style.color="var(--dm-success,#4ade80)",o.textContent=`Imported ${c.imported} entries. Skipped: ${c.skipped}.`,await v(t||{find:()=>({get:()=>null,off:()=>({on:()=>{}})})}),setTimeout(()=>m.close(),1500)}catch(c){o.style.display="",o.style.color="var(--dm-danger,#f87171)",o.textContent=c.message||"Import failed."}finally{s.disabled=!1}})}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{api as u}from"../api.js";import{createSimpleEditor as S}from"../lib/simple-editor.js";let p=null,b=null,f=null,l={},
|
|
1
|
+
import{api as u}from"../api.js";import{createSimpleEditor as S}from"../lib/simple-editor.js";let p=null,b=null,f=null,l={},v={};const c={};export const componentEditorView={templateUrl:"/admin/js/templates/component-editor.html",async onMount(e){p=null,f=null,l={},v={};for(const t of Object.keys(c))c[t]?.destroy?.(),delete c[t];const n=window.location.hash.match(/\/components\/edit\/([^/?#]+)/);n&&(p=decodeURIComponent(n[1])),A(e),await T(e),L(e),z(e),await D(e);const o=e.find("#component-preview-iframe").get(0);o.contentDocument?.readyState==="complete"?h(e):o.addEventListener("load",()=>h(e),{once:!0}),Domma.icons.scan(e.get(0))}};async function T(e){const n=e.find("#component-name").get(0),o=e.find("#component-tag-hint").get(0),t=e.find("#component-editor-title").get(0),s=e.find("#export-component-btn").get(0),r=()=>{const i=(n.value||"").trim()||"\u2026";o.textContent=`<dm-${i}>`},a=e.find("#component-project").get(0);if(a)try{const i=await u.projects.list();for(const m of i){const d=document.createElement("option");d.value=m.slug,d.textContent=m.name||m.slug,a.appendChild(d)}}catch{}if(p){t.textContent="Edit Component",n.value=p,n.disabled=!0,s&&(s.style.display="");try{const i=await u.components.get(p);w(e,i.source),e.find("#component-bundled").prop("checked",!!i.bundled),v=i.meta||{},a&&(a.value=i.meta?.project||"")}catch(i){E.toast(i.message||"Component not found.",{type:"error"}),R.navigate("/components");return}}else t.textContent="New Component",w(e,V);r(),n.addEventListener("input",r)}const V=`<template>
|
|
2
2
|
<div>Hello, <span>{{name}}</span>.</div>
|
|
3
3
|
</template>
|
|
4
4
|
<props>
|
|
@@ -24,5 +24,5 @@ ${e.script}
|
|
|
24
24
|
${e.style}
|
|
25
25
|
</style>`:""].filter(Boolean).join(`
|
|
26
26
|
`)+`
|
|
27
|
-
`}function w(e,n){const o=I(n);c.template?.setValue(o.template),c.props?.setValue(o.props),c.script?.setValue(o.script),c.style?.setValue(o.style)}function N(e){return M({template:c.template?.getValue()??"",props:c.props?.getValue()??"",script:c.script?.getValue()??"",style:c.style?.getValue()??""})}const O=["template","props","script","style"];function A(e){const n=()=>{b&&clearTimeout(b),b=setTimeout(()=>
|
|
28
|
-
`);g(e,"error",s),f=null;return}f=t.js,g(e,"ok"),P(e),B(e,{compiledJs:f,tagName:`dm-${n}`,props:l})}let y=null;function B(e,n){const o=e.find("#component-preview-iframe").get(0);if(!o)return;if(!o.dataset.dmMounted){o.dataset.dmMounted="true",C(e,{type:"mount",payload:n});return}y=n;const t=()=>{o.removeEventListener("load",t),y&&(C(e,{type:"mount",payload:y}),y=null)};o.addEventListener("load",t),o.src=o.src}function g(e,n,o=""){const t=e.find("#component-compile-indicator").get(0),s=e.find("#component-compile-status").get(0),
|
|
27
|
+
`}function w(e,n){const o=I(n);c.template?.setValue(o.template),c.props?.setValue(o.props),c.script?.setValue(o.script),c.style?.setValue(o.style)}function N(e){return M({template:c.template?.getValue()??"",props:c.props?.getValue()??"",script:c.script?.getValue()??"",style:c.style?.getValue()??""})}const O=["template","props","script","style"];function A(e){const n=()=>{b&&clearTimeout(b),b=setTimeout(()=>h(e),300)};for(const o of O){const t=e.find(`#component-src-${o}`).get(0);t&&(c[o]=S(t,{initialValue:"",onChange:n}))}}async function h(e){const n=(e.find("#component-name").val()||"").trim()||"preview",o=N(e);g(e,"compiling");let t;try{t=await u.components.compile(n,o)}catch(s){g(e,"error",s.message||"Compile request failed.");return}if(t.errors?.length){const s=t.errors.map(r=>`[${r.type}] ${r.message}`).join(`
|
|
28
|
+
`);g(e,"error",s),f=null;return}f=t.js,g(e,"ok"),P(e),B(e,{compiledJs:f,tagName:`dm-${n}`,props:l})}let y=null;function B(e,n){const o=e.find("#component-preview-iframe").get(0);if(!o)return;if(!o.dataset.dmMounted){o.dataset.dmMounted="true",C(e,{type:"mount",payload:n});return}y=n;const t=()=>{o.removeEventListener("load",t),y&&(C(e,{type:"mount",payload:y}),y=null)};o.addEventListener("load",t),o.src=o.src}function g(e,n,o=""){const t=e.find("#component-compile-indicator").get(0),s=e.find("#component-compile-status").get(0),r=e.find("#component-compile-errors").get(0),a=e.find("#save-component-btn").get(0);n==="compiling"?(t.style.background="var(--dm-text-muted)",s.textContent="Compiling\u2026",r.hidden=!0,a&&(a.disabled=!0)):n==="ok"?(t.style.background="var(--dm-success)",s.textContent="Compile OK",r.hidden=!0,a&&(a.disabled=!1)):(t.style.background="var(--dm-danger)",s.textContent="Compile failed \u2014 see below",r.hidden=!1,r.textContent=o,a&&(a.disabled=!0))}function j(e){for(;e.firstChild;)e.removeChild(e.firstChild)}function k(e,n,o=!1){j(e);const t=document.createElement("p");if(t.className="text-muted",t.style.cssText="font-size:.8rem;margin:0;",o){t.appendChild(document.createTextNode("Invalid "));const s=document.createElement("code");s.textContent="<props>",t.appendChild(s),t.appendChild(document.createTextNode(" JSON \u2014 fix the tab above to see the props panel."))}else t.textContent=n;e.appendChild(t)}function P(e){const n=e.find("#component-preview-props-panel").get(0);if(!n)return;let o={};try{o=JSON.parse(c.props?.getValue()||"{}")}catch{k(n,"",!0);return}const t={};j(n);const s=Object.entries(o);if(s.length===0){k(n,"No props declared."),l={};return}for(const[r,a]of s){const i=document.createElement("div");i.style.cssText="display:flex;align-items:center;gap:.5rem;margin-bottom:.5rem;";const m=document.createElement("label");m.style.cssText="font-size:.75rem;color:var(--dm-text-muted,#aaa);min-width:120px;",m.textContent=a.label||r,i.appendChild(m);const d=F(a,r,()=>{l[r]=J(d,a),C(e,{type:"update",payload:{props:l}})}),x=r in l?l[r]:a.default!==void 0?a.default:_(a.type);U(d,a,x),t[r]=x,i.appendChild(d),n.appendChild(i)}l=t}function F(e,n,o){let t;return e.type==="number"?(t=document.createElement("input"),t.type="number",t.className="form-input form-input--sm"):e.type==="boolean"?(t=document.createElement("input"),t.type="checkbox",t.className="form-check"):e.type==="array"||e.type==="object"?(t=document.createElement("textarea"),t.rows=2,t.className="form-input form-input--sm",t.style.fontFamily="var(--dm-font-mono, monospace)"):(t=document.createElement("input"),t.type="text",t.className="form-input form-input--sm"),t.style.flex="1",t.dataset.propName=n,t.addEventListener("input",o),t.addEventListener("change",o),t}function J(e,n){if(n.type==="number")return e.value===""?null:Number(e.value);if(n.type==="boolean")return!!e.checked;if(n.type==="array"||n.type==="object")try{return JSON.parse(e.value||"null")}catch{return null}return e.value}function U(e,n,o){n.type==="boolean"?e.checked=!!o:n.type==="array"||n.type==="object"?e.value=JSON.stringify(o??null):e.value=o??""}function _(e){return e==="number"?0:e==="boolean"?!1:e==="array"?[]:e==="object"?{}:""}function C(e,n){const o=e.find("#component-preview-iframe").get(0);o?.contentWindow&&o.contentWindow.postMessage(n,"*")}function z(e){const n=e.find("#save-component-btn").get(0),o=e.find("#export-component-btn").get(0);n&&n.addEventListener("click",()=>q(e)),o&&o.addEventListener("click",async()=>{if(p)try{await u.components.exportBundle(p)}catch(t){E.toast(t.message||"Export failed.",{type:"error"})}})}async function q(e){const n=(e.find("#component-name").val()||"").trim();if(!n){E.toast("Component name is required.",{type:"warning"});return}if(!/^[a-z][a-z0-9-]*$/.test(n)){E.toast("Name must start with a letter and contain only lowercase letters, digits, and hyphens.",{type:"warning"});return}const o=N(e),t=!!e.find("#component-bundled").is(":checked"),s={...v||{}};delete s.bundled;const r=(e.find("#component-project").val()||"").trim();r?s.project=r:delete s.project;const a=e.find("#save-component-btn").get(0);a&&(a.disabled=!0);try{await u.components.put(n,{source:o,bundled:t,meta:s}),E.toast(p?"Component updated.":"Component created.",{type:"success"}),p||(p=n,R.navigate(`/components/edit/${encodeURIComponent(n)}`))}catch(i){E.toast(i.message||"Failed to save component.",{type:"error"})}finally{a&&(a.disabled=!1)}}async function D(e){if(p)try{const n=await u.components.list(),o=e.find("#component-quick-switch").get(0);if(!o||!n?.length)return;const t=[...n].sort((s,r)=>s.name.localeCompare(r.name));for(const s of t){const r=document.createElement("option");r.value=s.name,r.textContent=s.name,s.name===p&&(r.selected=!0),o.appendChild(r)}o.style.display="",o.addEventListener("change",()=>{const s=o.value;s&&s!==p&&R.navigate(`/components/edit/${encodeURIComponent(s)}`)})}catch{}}
|