domma-cms 0.36.1 → 0.36.3
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/shortcode-registry.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 +19 -18
- 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 +7 -7
- package/admin/js/views/form-submissions.js +1 -1
- package/admin/js/views/forms.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
|
@@ -16,28 +16,28 @@
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
.method-get {
|
|
19
|
-
background:
|
|
20
|
-
color:
|
|
19
|
+
background: color-mix(in srgb, var(--dm-success) 15%, transparent);
|
|
20
|
+
color: var(--dm-success);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
.method-post {
|
|
24
|
-
background:
|
|
25
|
-
color:
|
|
24
|
+
background: color-mix(in srgb, var(--dm-primary) 15%, transparent);
|
|
25
|
+
color: var(--dm-primary);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
.method-put {
|
|
29
|
-
background:
|
|
30
|
-
color:
|
|
29
|
+
background: color-mix(in srgb, var(--dm-warning) 15%, transparent);
|
|
30
|
+
color: var(--dm-warning-dark);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
.method-patch {
|
|
34
|
-
background:
|
|
35
|
-
color:
|
|
34
|
+
background: color-mix(in srgb, var(--dm-info) 15%, transparent);
|
|
35
|
+
color: var(--dm-info);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
.method-delete {
|
|
39
|
-
background:
|
|
40
|
-
color:
|
|
39
|
+
background: color-mix(in srgb, var(--dm-danger) 15%, transparent);
|
|
40
|
+
color: var(--dm-danger);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
.endpoint-path {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
|
|
48
48
|
.auth-note {
|
|
49
49
|
font-size: 12px;
|
|
50
|
-
color: var(--dm-
|
|
50
|
+
color: var(--dm-text-muted, #6b7280);
|
|
51
51
|
margin: 4px 0 12px;
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -4,9 +4,6 @@
|
|
|
4
4
|
<a href="#/forms" class="btn btn-ghost btn-sm">
|
|
5
5
|
<span data-icon="arrow-left"></span> All Forms
|
|
6
6
|
</a>
|
|
7
|
-
<button id="preview-btn" class="btn btn-ghost btn-sm">
|
|
8
|
-
<span data-icon="eye"></span> Preview
|
|
9
|
-
</button>
|
|
10
7
|
<button id="save-form-btn" class="btn btn-primary">
|
|
11
8
|
<span data-icon="save"></span> Save
|
|
12
9
|
</button>
|
|
@@ -24,22 +21,23 @@
|
|
|
24
21
|
|
|
25
22
|
<!-- Tab 1: Fields -->
|
|
26
23
|
<div class="tab-panel active">
|
|
24
|
+
<div class="fb-fields-layout">
|
|
27
25
|
<div class="card mb-4">
|
|
28
26
|
<div class="card-header" style="display:flex;justify-content:space-between;align-items:center;">
|
|
29
27
|
<h2>Fields</h2>
|
|
30
28
|
<div id="add-element-dropdown" style="position:relative;">
|
|
31
29
|
<button id="add-element-btn" class="btn btn-ghost btn-sm" type="button">
|
|
32
|
-
<span data-icon="plus"></span> Add <span
|
|
33
|
-
style="
|
|
30
|
+
<span data-icon="plus"></span> Add <span data-icon="chevron-down" data-icon-size="12"
|
|
31
|
+
style="opacity:.6;margin-left:.1rem;display:inline-flex;align-items:center;vertical-align:middle;"></span>
|
|
34
32
|
</button>
|
|
35
33
|
<div id="add-element-menu"
|
|
36
|
-
style="display:none;position:absolute;right:0;top:calc(100% + .2rem);background:var(--card-bg,#1e1e2e);border:1px solid var(--border
|
|
34
|
+
style="display:none;position:absolute;right:0;top:calc(100% + .2rem);background:var(--dm-card-bg,#1e1e2e);border:1px solid var(--dm-border,#333);border-radius:6px;min-width:140px;z-index:100;box-shadow:var(--dm-shadow-lg);overflow:hidden;">
|
|
37
35
|
<button id="add-field-btn" type="button"
|
|
38
|
-
style="display:block;width:100%;text-align:left;padding:.5rem .85rem;background:none;border:none;color:inherit;cursor:pointer;font-size:.875rem;border-bottom:1px solid var(--border
|
|
36
|
+
style="display:block;width:100%;text-align:left;padding:.5rem .85rem;background:none;border:none;color:inherit;cursor:pointer;font-size:.875rem;border-bottom:1px solid var(--dm-border,#333);">
|
|
39
37
|
Field
|
|
40
38
|
</button>
|
|
41
39
|
<button id="add-spacer-btn" type="button"
|
|
42
|
-
style="display:block;width:100%;text-align:left;padding:.5rem .85rem;background:none;border:none;color:inherit;cursor:pointer;font-size:.875rem;border-bottom:1px solid var(--border
|
|
40
|
+
style="display:block;width:100%;text-align:left;padding:.5rem .85rem;background:none;border:none;color:inherit;cursor:pointer;font-size:.875rem;border-bottom:1px solid var(--dm-border,#333);">
|
|
43
41
|
Spacer
|
|
44
42
|
</button>
|
|
45
43
|
<button id="add-page-break-btn" type="button"
|
|
@@ -57,17 +55,20 @@
|
|
|
57
55
|
</div>
|
|
58
56
|
</div>
|
|
59
57
|
|
|
60
|
-
<
|
|
61
|
-
<div class="card-
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
<div
|
|
68
|
-
|
|
69
|
-
|
|
58
|
+
<aside class="fb-preview-pane">
|
|
59
|
+
<div class="card mb-4" id="preview-card">
|
|
60
|
+
<div class="card-header" style="display:flex;justify-content:space-between;align-items:center;">
|
|
61
|
+
<h2><span data-icon="eye"></span> Preview</h2>
|
|
62
|
+
<span id="preview-test-badge"
|
|
63
|
+
style="display:none;font-size:.75rem;padding:.2rem .6rem;border-radius:999px;background:color-mix(in srgb, var(--dm-primary) 15%, transparent);color:var(--dm-primary,#6366f1);">Test Mode — submissions are stored</span>
|
|
64
|
+
</div>
|
|
65
|
+
<div class="card-body">
|
|
66
|
+
<div id="preview-container"></div>
|
|
67
|
+
<div id="preview-test-result"
|
|
68
|
+
style="display:none;margin-top:.75rem;padding:.6rem .9rem;border-radius:6px;font-size:.9rem;"></div>
|
|
69
|
+
</div>
|
|
70
70
|
</div>
|
|
71
|
+
</aside>
|
|
71
72
|
</div>
|
|
72
73
|
</div>
|
|
73
74
|
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
<div class="card-body" style="display:flex;gap:.75rem;align-items:center;flex-wrap:wrap;">
|
|
18
18
|
<input id="sub-search" type="text" class="form-input" placeholder="Search submissions…" style="flex:1;min-width:200px;">
|
|
19
19
|
<input id="sub-date-from" type="date" class="form-input" style="width:auto;">
|
|
20
|
-
<span style="color:var(--text-muted,#888);">to</span>
|
|
20
|
+
<span style="color:var(--dm-text-muted,#888);">to</span>
|
|
21
21
|
<input id="sub-date-to" type="date" class="form-input" style="width:auto;">
|
|
22
22
|
<span id="sub-count" class="text-muted" style="white-space:nowrap;font-size:.85rem;"></span>
|
|
23
23
|
</div>
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
<p class="form-hint">Icon uses Domma icon names (e.g. <code>star</code>, <code>home</code>, <code>bolt</code>).
|
|
64
64
|
Logo URL overrides the icon when set. Leave both blank for text-only brand.</p>
|
|
65
65
|
|
|
66
|
-
<hr style="margin: 1.25rem 0; border-color: var(--border
|
|
66
|
+
<hr style="margin: 1.25rem 0; border-color: var(--dm-border, rgba(255,255,255,.1));">
|
|
67
67
|
<h3 style="font-size:.9rem;font-weight:600;margin-bottom:1rem;opacity:.7;text-transform:uppercase;letter-spacing:.05em;">
|
|
68
68
|
Admin Panel</h3>
|
|
69
69
|
<div class="row">
|
|
@@ -192,7 +192,7 @@
|
|
|
192
192
|
</div>
|
|
193
193
|
|
|
194
194
|
<div class="tab-panel">
|
|
195
|
-
<div style="display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;border-bottom:1px solid var(--border
|
|
195
|
+
<div style="display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;border-bottom:1px solid var(--dm-border,rgba(255,255,255,.08));">
|
|
196
196
|
<span class="text-muted" style="font-size:.85rem;">Add and arrange navbar menu items.</span>
|
|
197
197
|
<button id="add-nav-item" class="btn btn-sm btn-outline"><span data-icon="plus"></span> Add Item</button>
|
|
198
198
|
</div>
|
|
@@ -208,7 +208,7 @@
|
|
|
208
208
|
</div>
|
|
209
209
|
|
|
210
210
|
<div class="tab-panel">
|
|
211
|
-
<div style="display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;border-bottom:1px solid var(--border
|
|
211
|
+
<div style="display:flex;align-items:center;justify-content:space-between;padding:.75rem 1rem;border-bottom:1px solid var(--dm-border,rgba(255,255,255,.08));">
|
|
212
212
|
<span class="text-muted" style="font-size:.85rem;">Links shown in the site footer.</span>
|
|
213
213
|
<button id="add-footer-link" class="btn btn-sm btn-outline"><span data-icon="plus"></span> Add Link</button>
|
|
214
214
|
</div>
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<div id="notif-list"></div>
|
|
7
7
|
|
|
8
8
|
<div id="notif-empty" hidden class="card">
|
|
9
|
-
<div class="card-body" style="text-align:center;padding:3rem;color:var(--dm-
|
|
9
|
+
<div class="card-body" style="text-align:center;padding:3rem;color:var(--dm-text-muted)">
|
|
10
10
|
<span data-icon="check-circle" style="font-size:2rem;display:block;margin-bottom:.5rem"></span>
|
|
11
11
|
<p>No notifications — you're all caught up.</p>
|
|
12
12
|
</div>
|
|
@@ -178,7 +178,7 @@
|
|
|
178
178
|
<h3 style="margin:0;font-size:1rem;">Version History</h3>
|
|
179
179
|
<div style="display:flex;gap:.5rem;align-items:center;">
|
|
180
180
|
<button class="btn btn-ghost btn-sm" id="history-delete-selected-btn"
|
|
181
|
-
style="display:none;color:var(--dm-
|
|
181
|
+
style="display:none;color:var(--dm-danger,#e55);">
|
|
182
182
|
<span data-icon="trash"></span> Delete Selected
|
|
183
183
|
</button>
|
|
184
184
|
<button class="btn btn-ghost btn-sm" id="history-save-named-btn">
|
|
@@ -202,13 +202,13 @@
|
|
|
202
202
|
<p style="font-size:.8rem;font-weight:600;margin-bottom:.5rem;color:var(--dm-text-muted);">
|
|
203
203
|
SELECTED VERSION</p>
|
|
204
204
|
<pre id="version-compare-old"
|
|
205
|
-
style="background:var(--dm-
|
|
205
|
+
style="background:var(--dm-background-alt);padding:1rem;border-radius:6px;overflow:auto;white-space:pre-wrap;font-size:.8rem;max-height:60vh;"></pre>
|
|
206
206
|
</div>
|
|
207
207
|
<div class="col">
|
|
208
208
|
<p style="font-size:.8rem;font-weight:600;margin-bottom:.5rem;color:var(--dm-text-muted);">CURRENT
|
|
209
209
|
(UNSAVED)</p>
|
|
210
210
|
<pre id="version-compare-new"
|
|
211
|
-
style="background:var(--dm-
|
|
211
|
+
style="background:var(--dm-background-alt);padding:1rem;border-radius:6px;overflow:auto;white-space:pre-wrap;font-size:.8rem;max-height:60vh;"></pre>
|
|
212
212
|
</div>
|
|
213
213
|
</div>
|
|
214
214
|
</div>
|
|
@@ -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)}};
|