domma-cms 0.36.0 → 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 +2 -2
- package/admin/js/lib/crud-tutorial.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 +15 -15
- 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/shortcode-tags.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/effects.html +752 -752
- package/admin/js/templates/form-editor.html +4 -4
- package/admin/js/templates/form-submissions.html +1 -1
- package/admin/js/templates/forms.html +17 -17
- package/admin/js/templates/my-profile.html +17 -17
- 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/role-editor.html +70 -70
- package/admin/js/templates/roles.html +10 -10
- 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/bin/lib/config-merge.js +44 -44
- 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
- package/server/middleware/auth.js +253 -253
- package/server/routes/api/auth.js +309 -309
- package/server/routes/api/navigation.js +42 -42
- package/server/routes/api/settings.js +141 -141
- package/server/services/email.js +167 -167
- package/server/services/userProfiles.js +199 -199
- package/server/services/users.js +302 -302
- package/config/connections.json.bak +0 -9
|
@@ -33,13 +33,13 @@
|
|
|
33
33
|
style="font-size:.65rem;opacity:.6;margin-left:.1rem;">▾</span>
|
|
34
34
|
</button>
|
|
35
35
|
<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
|
|
36
|
+
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:0 4px 16px rgba(0,0,0,.3);overflow:hidden;">
|
|
37
37
|
<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
|
|
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(--dm-border,#333);">
|
|
39
39
|
Field
|
|
40
40
|
</button>
|
|
41
41
|
<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
|
|
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(--dm-border,#333);">
|
|
43
43
|
Spacer
|
|
44
44
|
</button>
|
|
45
45
|
<button id="add-page-break-btn" type="button"
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
<div class="card-header" style="display:flex;justify-content:space-between;align-items:center;">
|
|
62
62
|
<h2><span data-icon="eye"></span> Preview</h2>
|
|
63
63
|
<span id="preview-test-badge"
|
|
64
|
-
style="display:none;font-size:.75rem;padding:.2rem .6rem;border-radius:999px;background:rgba(99,102,241,.15);color:var(--primary,#6366f1);">Test Mode — submissions are stored</span>
|
|
64
|
+
style="display:none;font-size:.75rem;padding:.2rem .6rem;border-radius:999px;background:rgba(99,102,241,.15);color:var(--dm-primary,#6366f1);">Test Mode — submissions are stored</span>
|
|
65
65
|
</div>
|
|
66
66
|
<div class="card-body">
|
|
67
67
|
<div id="preview-container"></div>
|
|
@@ -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>
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
<div class="view-header">
|
|
2
|
-
<h1><span data-icon="layout"></span> Forms</h1>
|
|
3
|
-
<div style="display:flex;gap:.5rem;">
|
|
4
|
-
<button id="test-email-btn" class="btn btn-ghost btn-sm">
|
|
5
|
-
<span data-icon="mail"></span> Test Email
|
|
6
|
-
</button>
|
|
7
|
-
<button id="create-form-btn" class="btn btn-primary">
|
|
8
|
-
<span data-icon="plus"></span> Create Form
|
|
9
|
-
</button>
|
|
10
|
-
</div>
|
|
11
|
-
</div>
|
|
12
|
-
|
|
13
|
-
<div class="card">
|
|
14
|
-
<div class="card-body">
|
|
15
|
-
<div id="forms-table"></div>
|
|
16
|
-
</div>
|
|
17
|
-
</div>
|
|
1
|
+
<div class="view-header">
|
|
2
|
+
<h1><span data-icon="layout"></span> Forms</h1>
|
|
3
|
+
<div style="display:flex;gap:.5rem;">
|
|
4
|
+
<button id="test-email-btn" class="btn btn-ghost btn-sm">
|
|
5
|
+
<span data-icon="mail"></span> Test Email
|
|
6
|
+
</button>
|
|
7
|
+
<button id="create-form-btn" class="btn btn-primary">
|
|
8
|
+
<span data-icon="plus"></span> Create Form
|
|
9
|
+
</button>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<div class="card">
|
|
14
|
+
<div class="card-body">
|
|
15
|
+
<div id="forms-table"></div>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
<div class="view-header">
|
|
2
|
-
<h1><span data-icon="user"></span> My Profile</h1>
|
|
3
|
-
</div>
|
|
4
|
-
|
|
5
|
-
<div class="card">
|
|
6
|
-
<div class="card-header"><h2>Account</h2></div>
|
|
7
|
-
<div class="card-body">
|
|
8
|
-
<div id="account-form-container"></div>
|
|
9
|
-
</div>
|
|
10
|
-
</div>
|
|
11
|
-
|
|
12
|
-
<div class="card mt-4" id="profile-card" style="display:none;">
|
|
13
|
-
<div class="card-header"><h2>Profile</h2></div>
|
|
14
|
-
<div class="card-body">
|
|
15
|
-
<div id="profile-form-container"></div>
|
|
16
|
-
</div>
|
|
17
|
-
</div>
|
|
1
|
+
<div class="view-header">
|
|
2
|
+
<h1><span data-icon="user"></span> My Profile</h1>
|
|
3
|
+
</div>
|
|
4
|
+
|
|
5
|
+
<div class="card">
|
|
6
|
+
<div class="card-header"><h2>Account</h2></div>
|
|
7
|
+
<div class="card-body">
|
|
8
|
+
<div id="account-form-container"></div>
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div class="card mt-4" id="profile-card" style="display:none;">
|
|
13
|
+
<div class="card-header"><h2>Profile</h2></div>
|
|
14
|
+
<div class="card-body">
|
|
15
|
+
<div id="profile-form-container"></div>
|
|
16
|
+
</div>
|
|
17
|
+
</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>
|
|
@@ -1,70 +1,70 @@
|
|
|
1
|
-
<div class="view-header">
|
|
2
|
-
<h1><span data-icon="shield"></span> <span id="role-title">Edit Role</span></h1>
|
|
3
|
-
<div>
|
|
4
|
-
<a href="#/roles" class="btn btn-secondary"><span data-icon="arrow-left"></span> Back</a>
|
|
5
|
-
<button id="btn-save-role" class="btn btn-primary"><span data-icon="save"></span> Save</button>
|
|
6
|
-
</div>
|
|
7
|
-
</div>
|
|
8
|
-
|
|
9
|
-
<div class="tabs" id="role-tabs">
|
|
10
|
-
<div class="tab-list">
|
|
11
|
-
<button class="tab-item active">General</button>
|
|
12
|
-
<button class="tab-item">Permissions</button>
|
|
13
|
-
</div>
|
|
14
|
-
<div class="tab-content">
|
|
15
|
-
<!-- General Tab -->
|
|
16
|
-
<div class="tab-panel active" id="panel-general">
|
|
17
|
-
<div class="card">
|
|
18
|
-
<div class="card-body">
|
|
19
|
-
<div class="row">
|
|
20
|
-
<div class="col-6">
|
|
21
|
-
<div class="mb-3">
|
|
22
|
-
<label class="form-label">Name (slug)</label>
|
|
23
|
-
<input id="role-name" type="text" class="form-input" placeholder="e.g. moderator">
|
|
24
|
-
<p class="form-hint">Lowercase identifier. Cannot be changed for the root admin
|
|
25
|
-
role.</p>
|
|
26
|
-
</div>
|
|
27
|
-
</div>
|
|
28
|
-
<div class="col-6">
|
|
29
|
-
<div class="mb-3">
|
|
30
|
-
<label class="form-label">Display Label</label>
|
|
31
|
-
<input id="role-label" type="text" class="form-input" placeholder="e.g. Moderator">
|
|
32
|
-
</div>
|
|
33
|
-
</div>
|
|
34
|
-
</div>
|
|
35
|
-
<div class="row">
|
|
36
|
-
<div class="col-6">
|
|
37
|
-
<div class="mb-3">
|
|
38
|
-
<label class="form-label">Level</label>
|
|
39
|
-
<input id="role-level" type="number" class="form-input" min="0">
|
|
40
|
-
<p class="form-hint">Lower = more privileged. 0 is reserved for the root admin.</p>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
<div class="col-6">
|
|
44
|
-
<div class="mb-3">
|
|
45
|
-
<label class="form-label">Badge Colour</label>
|
|
46
|
-
<select id="role-badge" class="form-input"></select>
|
|
47
|
-
</div>
|
|
48
|
-
</div>
|
|
49
|
-
</div>
|
|
50
|
-
<div class="row">
|
|
51
|
-
<div class="col-6">
|
|
52
|
-
<div class="mb-3">
|
|
53
|
-
<label class="form-label">Project</label>
|
|
54
|
-
<select id="role-project" class="form-input">
|
|
55
|
-
<option value="">— none —</option>
|
|
56
|
-
</select>
|
|
57
|
-
<p class="form-hint">Tag this role to a project for grouping.</p>
|
|
58
|
-
</div>
|
|
59
|
-
</div>
|
|
60
|
-
</div>
|
|
61
|
-
</div>
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
|
|
65
|
-
<!-- Permissions Tab -->
|
|
66
|
-
<div class="tab-panel" id="panel-permissions">
|
|
67
|
-
<div id="permissions-container"></div>
|
|
68
|
-
</div>
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
1
|
+
<div class="view-header">
|
|
2
|
+
<h1><span data-icon="shield"></span> <span id="role-title">Edit Role</span></h1>
|
|
3
|
+
<div>
|
|
4
|
+
<a href="#/roles" class="btn btn-secondary"><span data-icon="arrow-left"></span> Back</a>
|
|
5
|
+
<button id="btn-save-role" class="btn btn-primary"><span data-icon="save"></span> Save</button>
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<div class="tabs" id="role-tabs">
|
|
10
|
+
<div class="tab-list">
|
|
11
|
+
<button class="tab-item active">General</button>
|
|
12
|
+
<button class="tab-item">Permissions</button>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="tab-content">
|
|
15
|
+
<!-- General Tab -->
|
|
16
|
+
<div class="tab-panel active" id="panel-general">
|
|
17
|
+
<div class="card">
|
|
18
|
+
<div class="card-body">
|
|
19
|
+
<div class="row">
|
|
20
|
+
<div class="col-6">
|
|
21
|
+
<div class="mb-3">
|
|
22
|
+
<label class="form-label">Name (slug)</label>
|
|
23
|
+
<input id="role-name" type="text" class="form-input" placeholder="e.g. moderator">
|
|
24
|
+
<p class="form-hint">Lowercase identifier. Cannot be changed for the root admin
|
|
25
|
+
role.</p>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="col-6">
|
|
29
|
+
<div class="mb-3">
|
|
30
|
+
<label class="form-label">Display Label</label>
|
|
31
|
+
<input id="role-label" type="text" class="form-input" placeholder="e.g. Moderator">
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="row">
|
|
36
|
+
<div class="col-6">
|
|
37
|
+
<div class="mb-3">
|
|
38
|
+
<label class="form-label">Level</label>
|
|
39
|
+
<input id="role-level" type="number" class="form-input" min="0">
|
|
40
|
+
<p class="form-hint">Lower = more privileged. 0 is reserved for the root admin.</p>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
<div class="col-6">
|
|
44
|
+
<div class="mb-3">
|
|
45
|
+
<label class="form-label">Badge Colour</label>
|
|
46
|
+
<select id="role-badge" class="form-input"></select>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
<div class="row">
|
|
51
|
+
<div class="col-6">
|
|
52
|
+
<div class="mb-3">
|
|
53
|
+
<label class="form-label">Project</label>
|
|
54
|
+
<select id="role-project" class="form-input">
|
|
55
|
+
<option value="">— none —</option>
|
|
56
|
+
</select>
|
|
57
|
+
<p class="form-hint">Tag this role to a project for grouping.</p>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<!-- Permissions Tab -->
|
|
66
|
+
<div class="tab-panel" id="panel-permissions">
|
|
67
|
+
<div id="permissions-container"></div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
<div class="view-header">
|
|
2
|
-
<h1><span data-icon="shield"></span> Roles & Permissions</h1>
|
|
3
|
-
<button id="btn-add-role" class="btn btn-primary"><span data-icon="plus"></span> Add Role</button>
|
|
4
|
-
</div>
|
|
5
|
-
|
|
6
|
-
<div class="card">
|
|
7
|
-
<div class="card-body">
|
|
8
|
-
<div id="roles-table"></div>
|
|
9
|
-
</div>
|
|
10
|
-
</div>
|
|
1
|
+
<div class="view-header">
|
|
2
|
+
<h1><span data-icon="shield"></span> Roles & Permissions</h1>
|
|
3
|
+
<button id="btn-add-role" class="btn btn-primary"><span data-icon="plus"></span> Add Role</button>
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
<div class="card">
|
|
7
|
+
<div class="card-body">
|
|
8
|
+
<div id="roles-table"></div>
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
@@ -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)}}
|