domma-cms 0.76.0 → 0.77.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +1 -1
- package/admin/js/api.js +1 -1
- package/admin/js/app.js +2 -2
- package/admin/js/lib/action-editor-arrange.js +1 -0
- package/admin/js/lib/api-endpoint-editor-arrange.js +2 -0
- package/admin/js/lib/block-editor-arrange.js +2 -0
- package/admin/js/lib/collection-editor-arrange.js +3 -0
- package/admin/js/lib/collection-entries-arrange.js +1 -0
- package/admin/js/lib/component-editor-arrange.js +27 -0
- package/admin/js/lib/context-menu-editor-arrange.js +1 -0
- package/admin/js/lib/dashboard-arrange.js +1 -0
- package/admin/js/lib/form-editor-arrange.js +1 -0
- package/admin/js/lib/form-submissions-arrange.js +2 -0
- package/admin/js/lib/menu-editor-arrange.js +2 -0
- package/admin/js/lib/page-editor-arrange.js +1 -0
- package/admin/js/lib/plugin-code-arrange.js +1 -0
- package/admin/js/lib/project-page-arrange.js +1 -0
- package/admin/js/lib/role-editor-arrange.js +3 -0
- package/admin/js/lib/user-editor-arrange.js +1 -0
- package/admin/js/lib/view-editor-arrange.js +14 -0
- package/admin/js/templates/action-editor.html +223 -147
- package/admin/js/templates/api-endpoint-editor.html +172 -117
- package/admin/js/templates/block-editor.html +194 -203
- package/admin/js/templates/collection-editor.html +191 -113
- package/admin/js/templates/collection-entries.html +99 -36
- package/admin/js/templates/component-editor.html +128 -126
- package/admin/js/templates/context-menu-editor.html +208 -148
- package/admin/js/templates/dashboard.html +184 -22
- package/admin/js/templates/form-editor.html +41 -26
- package/admin/js/templates/form-submissions.html +78 -26
- package/admin/js/templates/menu-editor.html +57 -17
- package/admin/js/templates/menu-locations.html +54 -11
- package/admin/js/templates/page-editor.html +47 -26
- package/admin/js/templates/plugin-code.html +91 -73
- package/admin/js/templates/project-detail.html +83 -145
- package/admin/js/templates/project-settings.html +88 -53
- package/admin/js/templates/role-editor.html +158 -83
- package/admin/js/templates/user-editor.html +150 -14
- package/admin/js/templates/view-editor.html +203 -180
- package/admin/js/templates/view-preview.html +45 -45
- package/admin/js/views/action-editor.js +7 -1
- package/admin/js/views/api-endpoint-editor.js +2 -3
- package/admin/js/views/block-editor.js +14 -8
- package/admin/js/views/collection-editor.js +64 -5
- package/admin/js/views/collection-entries.js +50 -1
- package/admin/js/views/component-editor.js +2 -28
- package/admin/js/views/context-menu-editor.js +54 -55
- package/admin/js/views/dashboard.js +7 -1
- package/admin/js/views/form-editor.js +7 -7
- package/admin/js/views/form-submissions.js +11 -1
- package/admin/js/views/index.js +1 -1
- package/admin/js/views/login.js +2 -2
- package/admin/js/views/menu-editor.js +21 -43
- package/admin/js/views/menu-locations.js +16 -1
- package/admin/js/views/page-editor.js +26 -26
- package/admin/js/views/plugin-code.js +23 -4
- package/admin/js/views/project-detail.js +9 -1
- package/admin/js/views/project-settings.js +1 -1
- package/admin/js/views/role-editor.js +11 -3
- package/admin/js/views/user-editor.js +1 -1
- package/admin/js/views/view-editor.js +1 -1
- package/admin/js/views/view-preview.js +1 -1
- package/package.json +1 -1
- package/server/routes/api/dashboard.js +32 -4
- package/server/routes/api/forms.js +104 -13
- package/server/routes/api/users.js +12 -2
- package/server/routes/api/views.js +55 -1
- package/server/services/components.js +9 -0
- package/server/services/users.js +2 -2
- package/server/services/views.js +14 -0
- package/admin/js/templates/dashboard/activity-feed.html +0 -3
- package/admin/js/templates/dashboard/cache.html +0 -32
- package/admin/js/templates/dashboard/health-detail.html +0 -2
- package/admin/js/templates/dashboard/journeys.html +0 -17
- package/admin/js/templates/dashboard/kpi-strip.html +0 -34
- package/admin/js/templates/dashboard/spike-feed.html +0 -3
- package/admin/js/templates/dashboard/top-pages.html +0 -3
- package/admin/js/templates/dashboard/traffic-chart.html +0 -3
- package/admin/js/views/block-editor-enhance.js +0 -1
- package/admin/js/views/dashboard/lib/escape.js +0 -1
- package/admin/js/views/dashboard/widgets/activity-feed.js +0 -1
- package/admin/js/views/dashboard/widgets/cache.js +0 -1
- package/admin/js/views/dashboard/widgets/health-detail.js +0 -1
- package/admin/js/views/dashboard/widgets/journeys.js +0 -1
- package/admin/js/views/dashboard/widgets/kpi-strip.js +0 -1
- package/admin/js/views/dashboard/widgets/spike-feed.js +0 -6
- package/admin/js/views/dashboard/widgets/top-pages.js +0 -1
- package/admin/js/views/dashboard/widgets/traffic-chart.js +0 -1
|
@@ -1,32 +1,194 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
<!-- The core Dashboard. Bound with M.applyBindings - see admin/views/dashboard.js, which also inserts the
|
|
2
|
+
row templates (the router renders a templateUrl through _.render, eating any {{…}}). No page heading:
|
|
3
|
+
the Tool banner above is the heading; Refresh and New page are banner actions. -->
|
|
4
|
+
<div class="dbx">
|
|
5
|
+
<div class="dbx-top">
|
|
6
|
+
<small class="dbx-asof" data-bind-text="asOf.value"></small>
|
|
7
|
+
<span data-help-title="Dashboard" data-help="What the site is doing and what needs you, at a glance. Traffic comes from the site's own analytics (no cookies, no third party). The numbers refresh every 30 seconds while this screen is open; Refresh in the banner reloads everything."></span>
|
|
6
8
|
</div>
|
|
7
|
-
|
|
9
|
+
<p class="dbx-note is-warn" data-bind-hidden="!warning.value" data-bind-text="warning.value"></p>
|
|
8
10
|
|
|
9
|
-
<div
|
|
11
|
+
<div class="dbx-kpis">
|
|
12
|
+
<div class="dbx-card dbx-kpi">
|
|
13
|
+
<span class="dbx-kpi-icon" aria-hidden="true"><span data-icon="trending-up" data-icon-size="18"></span></span>
|
|
14
|
+
<div class="dbx-kpi-body">
|
|
15
|
+
<strong class="dbx-kpi-value" data-bind-text="kpi.value.hits">—</strong>
|
|
16
|
+
<span class="dbx-kpi-label">Page views today
|
|
17
|
+
<span data-help="Every page view recorded today (UTC), compared with the whole of yesterday."></span></span>
|
|
18
|
+
<small class="dbx-delta" data-bind-hidden="!kpi.value.hitsDelta" data-bind-text="kpi.value.hitsDelta" data-bind-class="kpi.value.hitsUp ? 'is-up' : 'is-down'"></small>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="dbx-card dbx-kpi">
|
|
22
|
+
<span class="dbx-kpi-icon" aria-hidden="true"><span data-icon="users" data-icon-size="18"></span></span>
|
|
23
|
+
<div class="dbx-kpi-body">
|
|
24
|
+
<strong class="dbx-kpi-value" data-bind-text="kpi.value.active">—</strong>
|
|
25
|
+
<span class="dbx-kpi-label">On the site now
|
|
26
|
+
<span data-help="Visitors seen in the last 5 minutes."></span></span>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
<a class="dbx-card dbx-kpi is-link" href="#/pages">
|
|
30
|
+
<span class="dbx-kpi-icon" aria-hidden="true"><span data-icon="file-text" data-icon-size="18"></span></span>
|
|
31
|
+
<div class="dbx-kpi-body">
|
|
32
|
+
<strong class="dbx-kpi-value" data-bind-text="kpi.value.pages">—</strong>
|
|
33
|
+
<span class="dbx-kpi-label">Pages</span>
|
|
34
|
+
<small class="dbx-kpi-sub" data-bind-text="kpi.value.pagesSub"></small>
|
|
35
|
+
</div>
|
|
36
|
+
</a>
|
|
37
|
+
<a class="dbx-card dbx-kpi is-link" href="#/forms">
|
|
38
|
+
<span class="dbx-kpi-icon" aria-hidden="true"><span data-icon="inbox" data-icon-size="18"></span></span>
|
|
39
|
+
<div class="dbx-kpi-body">
|
|
40
|
+
<strong class="dbx-kpi-value" data-bind-text="kpi.value.submissions">—</strong>
|
|
41
|
+
<span class="dbx-kpi-label">Form submissions this week
|
|
42
|
+
<span data-help="Submissions to the site's forms in the last 7 days, counted where each form's submissions are kept."></span></span>
|
|
43
|
+
<small class="dbx-kpi-sub" data-bind-text="kpi.value.submissionsSub"></small>
|
|
44
|
+
</div>
|
|
45
|
+
</a>
|
|
46
|
+
<div class="dbx-card dbx-kpi">
|
|
47
|
+
<span class="dbx-kpi-icon" aria-hidden="true"><span data-icon="activity" data-icon-size="18"></span></span>
|
|
48
|
+
<div class="dbx-kpi-body">
|
|
49
|
+
<strong class="dbx-kpi-value"><span class="dbx-chip" data-bind-class="'is-' + kpi.value.healthTone" data-bind-text="kpi.value.health">—</span></strong>
|
|
50
|
+
<span class="dbx-kpi-label">Site health
|
|
51
|
+
<span data-help="The server's own checks - disk, storage, plugins, the database when one is used. Anything that is not OK is listed under Needs attention."></span></span>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
10
55
|
|
|
11
|
-
<div class="
|
|
12
|
-
|
|
56
|
+
<div class="dbx-split">
|
|
57
|
+
<section class="dbx-card">
|
|
58
|
+
<div class="dbx-card-title">Needs attention
|
|
59
|
+
<span data-help-title="Needs attention" data-help="What the rest of the admin is flagging: the red and amber badges from the sidebar (a missing layout, an expired API token, a plugin that is not running, settings quietly wrong…), unread notifications, and any health check that is not OK. Click one to go there."></span>
|
|
60
|
+
<span class="dbx-count" data-bind-text="attention.value.length"></span></div>
|
|
61
|
+
<p class="dbx-note is-ok" data-bind-hidden="!loaded.value || attention.value.length">
|
|
62
|
+
<span data-icon="check-circle" data-icon-size="15"></span> Nothing needs you right now.</p>
|
|
63
|
+
<div class="dbx-list dbx-attention" data-each="attention.value key=id"></div>
|
|
64
|
+
</section>
|
|
65
|
+
<section class="dbx-card">
|
|
66
|
+
<div class="dbx-card-title">Recent activity
|
|
67
|
+
<span data-help="Pages edited or published, and new entries in the site's collections - newest first. Click one to open it."></span></div>
|
|
68
|
+
<p class="dbx-note" data-bind-hidden="!loaded.value || activity.value.length">Nothing yet.</p>
|
|
69
|
+
<div class="dbx-list dbx-activity" data-each="activity.value key=id"></div>
|
|
70
|
+
</section>
|
|
71
|
+
</div>
|
|
13
72
|
|
|
14
|
-
<section class="
|
|
15
|
-
<div
|
|
16
|
-
|
|
73
|
+
<section class="dbx-card">
|
|
74
|
+
<div class="dbx-card-title">Traffic - the last 30 days
|
|
75
|
+
<span data-help="Page views per day, across the whole site."></span></div>
|
|
76
|
+
<div class="dbx-chart" data-bind-hidden="!hasChart.value"><canvas class="dbx-canvas"></canvas></div>
|
|
77
|
+
<p class="dbx-note" data-bind-hidden="hasChart.value" data-bind-text="chartNote.value"></p>
|
|
17
78
|
</section>
|
|
18
79
|
|
|
19
|
-
<
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
80
|
+
<div class="dbx-split">
|
|
81
|
+
<section class="dbx-card">
|
|
82
|
+
<div class="dbx-card-title">Top pages - the last 7 days
|
|
83
|
+
<span data-help="The most-viewed addresses this week, with the change on the week before. Right-click (or ⋮) one to open it, copy its address or edit the page."></span></div>
|
|
84
|
+
<p class="dbx-note" data-bind-hidden="!loaded.value || topPages.value.length">No page views recorded yet.</p>
|
|
85
|
+
<div class="dbx-list dbx-top-pages" data-each="topPages.value key=id"></div>
|
|
86
|
+
</section>
|
|
87
|
+
<section class="dbx-card">
|
|
88
|
+
<div class="dbx-card-title">Spikes - the last hour
|
|
89
|
+
<span data-help="Addresses getting far more views than usual this hour - a link shared somewhere, or a bot. The number beside each is how many times its normal rate."></span></div>
|
|
90
|
+
<p class="dbx-note" data-bind-hidden="!loaded.value || spikes.value.length">Nothing unusual.</p>
|
|
91
|
+
<div class="dbx-list dbx-spikes" data-each="spikes.value key=id"></div>
|
|
92
|
+
</section>
|
|
93
|
+
</div>
|
|
23
94
|
|
|
24
|
-
<section class="
|
|
25
|
-
<div
|
|
26
|
-
|
|
95
|
+
<section class="dbx-card">
|
|
96
|
+
<div class="dbx-card-title">Visits - the last 7 days
|
|
97
|
+
<span data-help-title="Visits" data-help="Where visits start and end, the commonest moves between pages, and the bounce rate - visits that viewed a single page."></span>
|
|
98
|
+
<span class="dbx-spacer"></span>
|
|
99
|
+
<small class="dbx-muted" data-bind-hidden="!bounce.value" data-bind-text="bounce.value"></small></div>
|
|
100
|
+
<p class="dbx-note" data-bind-hidden="!loaded.value || hasJourneys.value">No visits recorded yet.</p>
|
|
101
|
+
<div class="dbx-journeys" data-bind-hidden="!hasJourneys.value">
|
|
102
|
+
<div><h4>Arrived at</h4><div class="dbx-list dbx-entry" data-each="entry.value key=id"></div></div>
|
|
103
|
+
<div><h4>Left from</h4><div class="dbx-list dbx-exit" data-each="exit.value key=id"></div></div>
|
|
104
|
+
<div><h4>Moved between</h4><div class="dbx-list dbx-paths" data-each="paths.value key=id"></div></div>
|
|
105
|
+
</div>
|
|
27
106
|
</section>
|
|
28
107
|
|
|
29
|
-
<
|
|
30
|
-
<
|
|
31
|
-
|
|
108
|
+
<div class="dbx-split">
|
|
109
|
+
<section class="dbx-card">
|
|
110
|
+
<div class="dbx-card-title">Health checks
|
|
111
|
+
<span data-help="Each check the server runs on itself, with what it found."></span></div>
|
|
112
|
+
<p class="dbx-note" data-bind-hidden="!loaded.value || checks.value.length">No checks reported.</p>
|
|
113
|
+
<div class="dbx-list dbx-checks" data-each="checks.value key=id"></div>
|
|
114
|
+
</section>
|
|
115
|
+
<section class="dbx-card" data-bind-hidden="!cache.value">
|
|
116
|
+
<div class="dbx-card-title">Response cache
|
|
117
|
+
<span data-help-title="Response cache" data-help="Pages are kept ready-rendered so repeat visits are fast; editing anything clears what it affects. Hit rate is how often a visit was served from it. Turning it off makes every visit render afresh."></span>
|
|
118
|
+
<span class="dbx-spacer"></span>
|
|
119
|
+
<label class="dbx-switch" title="Turn the cache on or off">
|
|
120
|
+
<input type="checkbox" class="form-switch" data-bind-checked="cacheOn.value" data-on-change="toggleCache">
|
|
121
|
+
<span data-bind-text="cacheOn.value ? 'On' : 'Off'"></span>
|
|
122
|
+
</label></div>
|
|
123
|
+
<div class="dbx-stats">
|
|
124
|
+
<div><small>Hit rate</small><strong data-bind-text="cacheView.value.hitRate"></strong><small data-bind-text="cacheView.value.hitsMisses"></small></div>
|
|
125
|
+
<div><small>Entries</small><strong data-bind-text="cacheView.value.size"></strong><small data-bind-text="cacheView.value.driver"></small></div>
|
|
126
|
+
<div><small>Writes</small><strong data-bind-text="cacheView.value.writes"></strong><small data-bind-text="cacheView.value.invalidations"></small></div>
|
|
127
|
+
<div><small>Last cleared</small><strong data-bind-text="cacheView.value.cleared"></strong></div>
|
|
128
|
+
</div>
|
|
129
|
+
<div class="dbx-actions">
|
|
130
|
+
<button type="button" class="btn btn-sm btn-ghost" data-on-click="clearCache"><span data-icon="trash" data-icon-size="13"></span> Clear the cache</button>
|
|
131
|
+
</div>
|
|
132
|
+
</section>
|
|
133
|
+
</div>
|
|
32
134
|
</div>
|
|
135
|
+
|
|
136
|
+
<style>
|
|
137
|
+
.dbx { display: flex; flex-direction: column; gap: 1rem; }
|
|
138
|
+
.dbx [hidden] { display: none !important; }
|
|
139
|
+
.dbx-top { display: flex; align-items: center; gap: .4rem; justify-content: flex-end; }
|
|
140
|
+
.dbx-asof, .dbx-muted { color: var(--dm-text-muted); font-size: var(--dm-text-xs); }
|
|
141
|
+
.dbx-note { margin: 0; color: var(--dm-text-muted); font-size: var(--dm-text-sm); display: flex; align-items: center; gap: .35rem; }
|
|
142
|
+
.dbx-note.is-warn { padding: .55rem .8rem; border-radius: var(--dm-radius-md); background: color-mix(in srgb, var(--dm-warning) 14%, transparent); color: var(--dm-text); }
|
|
143
|
+
.dbx-note.is-ok { color: var(--dm-success); }
|
|
144
|
+
.dbx-card { border: 1px solid var(--dm-border); border-radius: var(--dm-radius-md); padding: .8rem .95rem; display: flex; flex-direction: column; gap: .6rem;
|
|
145
|
+
background: var(--dm-surface); min-width: 0; }
|
|
146
|
+
.dbx-card-title { display: flex; align-items: center; gap: .35rem; font-weight: 600; }
|
|
147
|
+
.dbx-card h4 { margin: 0 0 .35rem; font-size: var(--dm-text-sm); color: var(--dm-text-muted); font-weight: 600; }
|
|
148
|
+
.dbx-count { font-size: var(--dm-text-xs); color: var(--dm-text-muted); font-weight: 500; }
|
|
149
|
+
.dbx-spacer { flex: 1; }
|
|
150
|
+
.dbx-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: 1rem; }
|
|
151
|
+
.dbx-kpi { flex-direction: row; align-items: center; gap: .8rem; color: inherit; text-decoration: none; }
|
|
152
|
+
.dbx-kpi.is-link:hover, .dbx-kpi.is-link:focus-visible { border-color: var(--dm-primary); outline: none; }
|
|
153
|
+
.dbx-kpi-icon { display: inline-flex; align-items: center; justify-content: center; width: 2.4rem; height: 2.4rem; flex-shrink: 0;
|
|
154
|
+
border-radius: var(--dm-radius-md); color: var(--dm-primary); background: color-mix(in srgb, var(--dm-primary) 12%, transparent); }
|
|
155
|
+
.dbx-kpi-body { display: flex; flex-direction: column; min-width: 0; }
|
|
156
|
+
.dbx-kpi-value { font-size: var(--dm-text-2xl, 1.5rem); line-height: 1.2; font-variant-numeric: tabular-nums; }
|
|
157
|
+
.dbx-kpi-label { display: flex; align-items: center; gap: .3rem; color: var(--dm-text-muted); font-size: var(--dm-text-sm); }
|
|
158
|
+
.dbx-kpi-sub { color: var(--dm-text-muted); font-size: var(--dm-text-xs); }
|
|
159
|
+
.dbx-delta { font-size: var(--dm-text-xs); font-weight: 600; }
|
|
160
|
+
.dbx-delta.is-up { color: var(--dm-success); }
|
|
161
|
+
.dbx-delta.is-down { color: var(--dm-danger); }
|
|
162
|
+
.dbx-split { display: grid; grid-template-columns: repeat(auto-fit, minmax(22rem, 1fr)); gap: 1rem; }
|
|
163
|
+
.dbx-list { display: flex; flex-direction: column; }
|
|
164
|
+
.dbx-row { display: flex; align-items: center; gap: .6rem; padding: .45rem .3rem; border-bottom: 1px solid var(--dm-border); cursor: pointer; min-width: 0; }
|
|
165
|
+
.dbx-row:last-child { border-bottom: 0; }
|
|
166
|
+
.dbx-row:hover, .dbx-row:focus-visible { background: var(--dm-table-hover-bg, color-mix(in srgb, var(--dm-text) 6%, transparent)); outline: none; }
|
|
167
|
+
.dbx-row.is-static { cursor: default; }
|
|
168
|
+
.dbx-row.is-static:hover { background: none; }
|
|
169
|
+
.dbx-row.is-danger { box-shadow: inset 3px 0 0 var(--dm-danger); }
|
|
170
|
+
.dbx-row.is-warn { box-shadow: inset 3px 0 0 var(--dm-warning); }
|
|
171
|
+
.dbx-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
|
|
172
|
+
.dbx-main strong, .dbx-main small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
173
|
+
.dbx-main strong { font-weight: 500; }
|
|
174
|
+
.dbx-main small { color: var(--dm-text-muted); font-size: var(--dm-text-xs); }
|
|
175
|
+
.dbx-value { font-variant-numeric: tabular-nums; font-weight: 600; flex-shrink: 0; }
|
|
176
|
+
.dbx-chip { display: inline-flex; align-items: center; font-size: var(--dm-text-xs); font-weight: 600; padding: .1rem .5rem; border-radius: 999px; white-space: nowrap;
|
|
177
|
+
background: color-mix(in srgb, var(--dm-text) 10%, transparent); color: var(--dm-text-muted); }
|
|
178
|
+
.dbx-chip.is-ok { background: color-mix(in srgb, var(--dm-success) 16%, transparent); color: var(--dm-success); }
|
|
179
|
+
.dbx-chip.is-warn { background: color-mix(in srgb, var(--dm-warning) 18%, transparent); color: var(--dm-warning); }
|
|
180
|
+
.dbx-chip.is-danger { background: color-mix(in srgb, var(--dm-danger) 16%, transparent); color: var(--dm-danger); }
|
|
181
|
+
.dbx-kpi-value .dbx-chip { font-size: var(--dm-text-sm); padding: .15rem .6rem; }
|
|
182
|
+
.dbx-kebab { border: 0; background: none; cursor: pointer; font-size: var(--dm-text-lg); color: var(--dm-text-muted); border-radius: var(--dm-radius-sm); padding: 0 .3rem; flex-shrink: 0; }
|
|
183
|
+
.dbx-kebab:hover { color: var(--dm-primary); }
|
|
184
|
+
.dbx-chart { height: 240px; position: relative; }
|
|
185
|
+
.dbx-journeys { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: 1rem; }
|
|
186
|
+
.dbx-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); gap: .8rem; }
|
|
187
|
+
.dbx-stats > div { display: flex; flex-direction: column; }
|
|
188
|
+
.dbx-stats small { color: var(--dm-text-muted); font-size: var(--dm-text-xs); }
|
|
189
|
+
.dbx-stats strong { font-size: var(--dm-text-lg); font-variant-numeric: tabular-nums; }
|
|
190
|
+
.dbx-switch { display: inline-flex; align-items: center; gap: .4rem; font-weight: 400; font-size: var(--dm-text-sm); cursor: pointer; }
|
|
191
|
+
.dbx-actions { display: flex; justify-content: flex-end; }
|
|
192
|
+
.dbx-actions .btn { display: inline-flex; align-items: center; gap: .3rem; }
|
|
193
|
+
@media (max-width: 640px) { .dbx-split { grid-template-columns: 1fr; } }
|
|
194
|
+
</style>
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
</button>
|
|
10
|
-
</div>
|
|
1
|
+
<!-- Form editor. No page heading: the Tool banner ("Forms › <title>") is the heading, and Save / All forms /
|
|
2
|
+
Submissions are banner actions. The bar below is bound with M.applyBindings (admin/js/views/form-editor.js). -->
|
|
3
|
+
<div class="fex-bar" data-bind-hidden="!dirty.value">
|
|
4
|
+
<span class="fex-chip">Unsaved changes</span>
|
|
5
|
+
<small class="fex-note">Ctrl+S saves. Leaving asks first and keeps a draft of this form.</small>
|
|
6
|
+
<span class="fex-spacer"></span>
|
|
7
|
+
<button type="button" class="btn btn-sm btn-ghost" data-on-click="discard">Discard</button>
|
|
8
|
+
<button type="button" id="save-form-btn" class="btn btn-sm btn-primary" data-on-click="save" data-bind-disabled="saving.value">
|
|
9
|
+
<span data-icon="save" data-icon-size="13"></span> Save</button>
|
|
11
10
|
</div>
|
|
12
11
|
|
|
13
12
|
<div class="tabs" id="editor-tabs">
|
|
@@ -24,20 +23,20 @@
|
|
|
24
23
|
<div class="fb-fields-layout">
|
|
25
24
|
<div class="card mb-4">
|
|
26
25
|
<div class="card-header" style="display:flex;justify-content:space-between;align-items:center;">
|
|
27
|
-
<h2>Fields
|
|
26
|
+
<h2>Fields <span data-help-title="Fields" data-help="Click a field to open it; drag its header to reorder. Right-click a field (or ⋮, or Shift+F10 on it) to edit it, open its logic and triggers, duplicate it, move it up or down, copy its name or delete it. A Page Break splits the form into wizard steps; a Spacer adds room between fields."></span></h2>
|
|
28
27
|
<div id="add-element-dropdown" style="position:relative;">
|
|
29
28
|
<button id="add-element-btn" class="btn btn-ghost btn-sm" type="button">
|
|
30
29
|
<span data-icon="plus"></span> Add <span data-icon="chevron-down" data-icon-size="12"
|
|
31
30
|
style="opacity:.6;margin-left:.1rem;display:inline-flex;align-items:center;vertical-align:middle;"></span>
|
|
32
31
|
</button>
|
|
33
32
|
<div id="add-element-menu"
|
|
34
|
-
style="display:none;position:absolute;right:0;top:calc(100% + .2rem);background:var(--dm-card-bg
|
|
33
|
+
style="display:none;position:absolute;right:0;top:calc(100% + .2rem);background:var(--dm-card-bg);border:1px solid var(--dm-border);border-radius:6px;min-width:140px;z-index:100;box-shadow:var(--dm-shadow-lg);overflow:hidden;">
|
|
35
34
|
<button id="add-field-btn" type="button"
|
|
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
|
|
35
|
+
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);">
|
|
37
36
|
Field
|
|
38
37
|
</button>
|
|
39
38
|
<button id="add-spacer-btn" type="button"
|
|
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
|
|
39
|
+
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);">
|
|
41
40
|
Spacer
|
|
42
41
|
</button>
|
|
43
42
|
<button id="add-page-break-btn" type="button"
|
|
@@ -49,8 +48,8 @@
|
|
|
49
48
|
</div>
|
|
50
49
|
<div class="card-body" style="padding:0;">
|
|
51
50
|
<div id="fields-list" style="padding:1rem;">
|
|
52
|
-
<p class="text-muted" id="fields-empty-msg" style="text-align:center;padding:2rem 0;">No fields yet
|
|
53
|
-
|
|
51
|
+
<p class="text-muted" id="fields-empty-msg" style="text-align:center;padding:2rem 0;">No fields yet - use
|
|
52
|
+
Add to put the first one in.</p>
|
|
54
53
|
</div>
|
|
55
54
|
</div>
|
|
56
55
|
</div>
|
|
@@ -58,9 +57,9 @@
|
|
|
58
57
|
<aside class="fb-preview-pane">
|
|
59
58
|
<div class="card mb-4" id="preview-card">
|
|
60
59
|
<div class="card-header" style="display:flex;justify-content:space-between;align-items:center;">
|
|
61
|
-
<h2><span data-icon="eye"></span> Preview
|
|
60
|
+
<h2><span data-icon="eye"></span> Preview <span data-help-title="Preview" data-help="The form as visitors will see it, updated as you edit - conditional logic and triggers included. Once the form is saved, a test submission here is stored like a real one."></span></h2>
|
|
62
61
|
<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
|
|
62
|
+
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);">Test Mode - submissions are stored</span>
|
|
64
63
|
</div>
|
|
65
64
|
<div class="card-body">
|
|
66
65
|
<div id="preview-container"></div>
|
|
@@ -83,7 +82,7 @@
|
|
|
83
82
|
<input id="field-title" type="text" class="form-input" placeholder="My Form">
|
|
84
83
|
</div>
|
|
85
84
|
<div class="col-5">
|
|
86
|
-
<label class="form-label">Slug
|
|
85
|
+
<label class="form-label">Slug <span data-help="The form's id - how pages embed it ([form name="slug"]) and where its submissions are filed. Made from the title for a new form; changing it later breaks existing embeds."></span></label>
|
|
87
86
|
<input id="field-slug" type="text" class="form-input" placeholder="my-form">
|
|
88
87
|
<p class="form-hint text-muted" style="margin-top:.3rem;font-size:.8rem;">Used in embed: <code>data-form="slug"</code>
|
|
89
88
|
</p>
|
|
@@ -98,7 +97,7 @@
|
|
|
98
97
|
</div>
|
|
99
98
|
<div class="row mt-3">
|
|
100
99
|
<div class="col-6">
|
|
101
|
-
<label class="form-label">Project
|
|
100
|
+
<label class="form-label">Project <span data-help="Files the form under a project: it shows in that project's lists, and users limited to other projects cannot see it."></span></label>
|
|
102
101
|
<select id="field-project" class="form-input">
|
|
103
102
|
<option value="">- none -</option>
|
|
104
103
|
</select>
|
|
@@ -131,7 +130,7 @@
|
|
|
131
130
|
placeholder="Thank you for your submission."></textarea>
|
|
132
131
|
</div>
|
|
133
132
|
<div class="mb-3">
|
|
134
|
-
<label class="form-label">Success Redirect URL
|
|
133
|
+
<label class="form-label">Success Redirect URL <span data-help="Where the visitor goes after a successful submission - a page on this site (/thank-you) or a full address. Leave empty to show the success message in place."></span></label>
|
|
135
134
|
<input id="setting-success-redirect" type="text" class="form-input"
|
|
136
135
|
placeholder="/thank-you">
|
|
137
136
|
<p class="form-hint text-muted" style="margin-top:.3rem;font-size:.8rem;">
|
|
@@ -139,7 +138,7 @@
|
|
|
139
138
|
</p>
|
|
140
139
|
</div>
|
|
141
140
|
<div class="mb-3">
|
|
142
|
-
<label class="form-label">Layout
|
|
141
|
+
<label class="form-label">Layout <span data-help="Grid lays fields out in columns (each field can span several); Stacked puts one field per row; Inline puts short fields side by side."></span></label>
|
|
143
142
|
<select id="setting-layout" class="form-select">
|
|
144
143
|
<option value="grid">Grid</option>
|
|
145
144
|
<option value="stacked">Stacked</option>
|
|
@@ -181,7 +180,7 @@
|
|
|
181
180
|
</label>
|
|
182
181
|
</div>
|
|
183
182
|
<div class="mb-3">
|
|
184
|
-
<label class="form-label">Recipients
|
|
183
|
+
<label class="form-label">Recipients <span data-help="Who is emailed each submission - one or more addresses, comma-separated. Sent through the site's SMTP settings (System › Site Settings › Email); without them nothing is delivered."></span></label>
|
|
185
184
|
<input id="action-email-recipients" type="text" class="form-input" placeholder="admin@example.com">
|
|
186
185
|
<p class="form-hint text-muted" style="margin-top:.3rem;font-size:.8rem;">Comma-separated. Uses global
|
|
187
186
|
SMTP settings.</p>
|
|
@@ -203,7 +202,7 @@
|
|
|
203
202
|
</label>
|
|
204
203
|
</div>
|
|
205
204
|
<div class="mb-3">
|
|
206
|
-
<label class="form-label">URL
|
|
205
|
+
<label class="form-label">URL <span data-help="Each submission is sent here as JSON. The address is not kept in the editor's draft, in case it carries a secret token."></span></label>
|
|
207
206
|
<input id="action-webhook-url" type="url" class="form-input"
|
|
208
207
|
placeholder="https://hooks.example.com/form">
|
|
209
208
|
</div>
|
|
@@ -222,7 +221,7 @@
|
|
|
222
221
|
<div class="card-header"><h2>CMS Action</h2></div>
|
|
223
222
|
<div class="card-body">
|
|
224
223
|
<div class="mb-3">
|
|
225
|
-
<label class="form-label">Action on Submit
|
|
224
|
+
<label class="form-label">Action on Submit <span data-help="A CMS Action (Data › Actions) run after the submission is stored - for example to create an entry elsewhere or move one on a step. It needs MongoDB; a failure never loses the submission."></span></label>
|
|
226
225
|
<select id="action-cms-slug" class="form-select">
|
|
227
226
|
<option value="">- None -</option>
|
|
228
227
|
</select>
|
|
@@ -247,7 +246,7 @@
|
|
|
247
246
|
that fill a hidden field.</p>
|
|
248
247
|
</div>
|
|
249
248
|
<div>
|
|
250
|
-
<label class="form-label">Rate Limit (per minute)
|
|
249
|
+
<label class="form-label">Rate Limit (per minute) <span data-help="How many submissions one visitor's address may make in a minute; more are refused. Keeps a form from being flooded."></span></label>
|
|
251
250
|
<input id="setting-rate-limit" type="number" class="form-input" min="1" max="60" value="3">
|
|
252
251
|
<p class="form-hint text-muted" style="margin-top:.3rem;font-size:.8rem;">Max submissions per IP per
|
|
253
252
|
minute.</p>
|
|
@@ -258,3 +257,19 @@
|
|
|
258
257
|
|
|
259
258
|
</div>
|
|
260
259
|
</div>
|
|
260
|
+
|
|
261
|
+
<style>
|
|
262
|
+
.fex-bar { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; padding: .5rem .75rem; margin-bottom: .75rem;
|
|
263
|
+
border: 1px solid color-mix(in srgb, var(--dm-warning) 45%, var(--dm-border)); border-radius: var(--dm-radius-md);
|
|
264
|
+
background: color-mix(in srgb, var(--dm-warning) 10%, transparent); position: sticky; top: 0; z-index: 5; }
|
|
265
|
+
.fex-bar[hidden] { display: none !important; }
|
|
266
|
+
.fex-chip { font-size: var(--dm-text-xs); font-weight: 600; padding: .1rem .55rem; border-radius: 999px;
|
|
267
|
+
background: color-mix(in srgb, var(--dm-warning) 22%, transparent); color: var(--dm-warning); white-space: nowrap; }
|
|
268
|
+
.fex-note { color: var(--dm-text-muted); }
|
|
269
|
+
.fex-spacer { flex: 1; }
|
|
270
|
+
.fex-bar .btn { display: inline-flex; align-items: center; gap: .3rem; }
|
|
271
|
+
.fex-kebab { border: 0; background: none; cursor: pointer; font-size: var(--dm-text-lg); line-height: 1; color: var(--dm-text-muted);
|
|
272
|
+
border-radius: var(--dm-radius-sm); padding: 0 .35rem; flex-shrink: 0; }
|
|
273
|
+
.fex-kebab:hover { color: var(--dm-primary); }
|
|
274
|
+
#fields-list .fb-field-card:focus-visible { outline: 2px solid var(--dm-primary); outline-offset: 2px; }
|
|
275
|
+
</style>
|
|
@@ -1,30 +1,82 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
<
|
|
8
|
-
<span data-icon="download"></span> Export
|
|
9
|
-
</button>
|
|
10
|
-
<button id="clear-all-btn" class="btn btn-danger btn-sm">
|
|
11
|
-
<span data-icon="trash"></span> Clear All
|
|
12
|
-
</button>
|
|
13
|
-
</div>
|
|
14
|
-
</div>
|
|
1
|
+
<!-- Form submissions. Bound with M.applyBindings - see admin/views/form-submissions.js, which also inserts the
|
|
2
|
+
row template (the router renders a templateUrl through _.render, eating any {{…}}). No page heading:
|
|
3
|
+
the Tool banner ("Forms › <form> submissions") is the heading; All forms, Export and Clear all are banner
|
|
4
|
+
actions. -->
|
|
5
|
+
<div class="fsx">
|
|
6
|
+
<p class="fsx-note"><span data-bind-text="intro.value">What people sent through this form.</span>
|
|
7
|
+
<span data-help-title="Submissions" data-help="Every answer sent through this form, newest first, read from the collection the form saves to (its collection action, or one named after the form). Click one for everything that was sent; right-click it (or ⋮, or Shift+F10) to copy it, copy the sender's email, mark it as spam or delete it. Spam is kept out of the inbox but not deleted - you can put it back."></span></p>
|
|
15
8
|
|
|
16
|
-
<div class="card
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
9
|
+
<div class="fsx-card">
|
|
10
|
+
<div class="fsx-bar">
|
|
11
|
+
<div class="fsx-seg" role="group" aria-label="Show">
|
|
12
|
+
<button type="button" data-on-click="showInbox" data-bind-class="filter.value === 'inbox' && 'is-on'">Inbox <b data-bind-text="counts.value.inbox"></b></button>
|
|
13
|
+
<button type="button" data-on-click="showNew" data-bind-class="filter.value === 'new' && 'is-on'">New this week <b data-bind-text="counts.value.new"></b></button>
|
|
14
|
+
<button type="button" data-on-click="showSpam" data-bind-class="filter.value === 'spam' && 'is-on'">Spam <b data-bind-text="counts.value.spam"></b></button>
|
|
15
|
+
</div>
|
|
16
|
+
<input type="search" class="form-input fsx-search" placeholder="Search answers…" aria-label="Search answers" data-model="query.value">
|
|
17
|
+
<span class="fsx-dates">
|
|
18
|
+
<input type="date" class="form-input" aria-label="From" data-model="from.value">
|
|
19
|
+
<span class="fsx-muted">to</span>
|
|
20
|
+
<input type="date" class="form-input" aria-label="To" data-model="to.value">
|
|
21
|
+
<button type="button" class="btn btn-sm btn-ghost" data-on-click="clearDates" data-bind-hidden="!from.value && !to.value" title="Clear the dates">×</button>
|
|
22
|
+
</span>
|
|
23
|
+
<span data-help-title="Finding one" data-help="Search looks through every answer. The dates are the day it was sent, both ends included. New this week is the last 7 days - the same count as the badge on Forms in the sidebar."></span>
|
|
24
|
+
<span class="fsx-spacer"></span>
|
|
25
|
+
<small class="fsx-muted" data-bind-text="summary.value"></small>
|
|
26
|
+
</div>
|
|
27
|
+
<p class="fsx-note" data-bind-hidden="!loading.value">Loading…</p>
|
|
28
|
+
<p class="fsx-note is-bad" data-bind-hidden="!loadError.value" data-bind-text="loadError.value"></p>
|
|
29
|
+
<p class="fsx-note" data-bind-hidden="loading.value || loadError.value || rows.value.length" data-bind-text="emptyText.value"></p>
|
|
30
|
+
<div class="fsx-list" data-each="rows.value key=id"></div>
|
|
23
31
|
</div>
|
|
24
32
|
</div>
|
|
25
33
|
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
<style>
|
|
35
|
+
.fsx { display: flex; flex-direction: column; gap: .75rem; }
|
|
36
|
+
.fsx [hidden], .fsx-detail [hidden] { display: none !important; }
|
|
37
|
+
.fsx-note { margin: 0; color: var(--dm-text-muted); font-size: var(--dm-text-sm); }
|
|
38
|
+
.fsx-note.is-bad { color: var(--dm-danger); }
|
|
39
|
+
.fsx-muted { color: var(--dm-text-muted); font-size: var(--dm-text-sm); }
|
|
40
|
+
.fsx-card { border: 1px solid var(--dm-border); border-radius: var(--dm-radius-md); padding: .75rem .9rem; display: flex; flex-direction: column; gap: .6rem; }
|
|
41
|
+
.fsx-bar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
|
|
42
|
+
.fsx-spacer { flex: 1; }
|
|
43
|
+
.fsx-search { flex: 1; min-width: 10rem; max-width: 18rem; }
|
|
44
|
+
.fsx-dates { display: inline-flex; align-items: center; gap: .35rem; }
|
|
45
|
+
.fsx-dates .form-input { width: auto; }
|
|
46
|
+
.fsx-seg { display: inline-flex; border: 1px solid var(--dm-border); border-radius: var(--dm-radius-md); overflow: hidden; }
|
|
47
|
+
.fsx-seg button { border: 0; background: none; color: var(--dm-text); padding: .3rem .7rem; cursor: pointer; font: inherit; font-size: var(--dm-text-sm); }
|
|
48
|
+
.fsx-seg button + button { border-left: 1px solid var(--dm-border); }
|
|
49
|
+
.fsx-seg button b { font-weight: 500; opacity: .65; margin-left: .2rem; }
|
|
50
|
+
.fsx-seg button:hover { background: var(--dm-table-hover-bg, color-mix(in srgb, var(--dm-text) 6%, transparent)); }
|
|
51
|
+
.fsx-seg button.is-on { background: var(--dm-primary); color: var(--dm-primary-text); }
|
|
52
|
+
.fsx-list { display: flex; flex-direction: column; }
|
|
53
|
+
.fsx-row { display: flex; align-items: center; gap: .75rem; padding: .55rem .35rem; border-bottom: 1px solid var(--dm-border); cursor: pointer; }
|
|
54
|
+
.fsx-row:last-child { border-bottom: 0; }
|
|
55
|
+
.fsx-row:hover, .fsx-row:focus-visible { background: var(--dm-table-hover-bg, color-mix(in srgb, var(--dm-text) 6%, transparent)); outline: none; }
|
|
56
|
+
.fsx-dot { width: .55rem; height: .55rem; border-radius: 50%; flex-shrink: 0; background: transparent; }
|
|
57
|
+
.fsx-row.is-new .fsx-dot { background: var(--dm-success); }
|
|
58
|
+
.fsx-row.is-spam .fsx-main strong { color: var(--dm-text-muted); text-decoration: line-through; }
|
|
59
|
+
.fsx-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
|
|
60
|
+
.fsx-main strong, .fsx-main small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
61
|
+
.fsx-main small { color: var(--dm-text-muted); font-size: var(--dm-text-xs); }
|
|
62
|
+
.fsx-chips { display: inline-flex; gap: .3rem; flex-shrink: 0; }
|
|
63
|
+
.fsx-chip { display: inline-flex; align-items: center; font-size: var(--dm-text-xs); font-weight: 600; padding: .1rem .5rem; border-radius: 999px; white-space: nowrap;
|
|
64
|
+
background: color-mix(in srgb, var(--dm-text) 10%, transparent); color: var(--dm-text-muted); }
|
|
65
|
+
.fsx-chip.is-ok { background: color-mix(in srgb, var(--dm-success) 16%, transparent); color: var(--dm-success); }
|
|
66
|
+
.fsx-chip.is-warn { background: color-mix(in srgb, var(--dm-warning) 18%, transparent); color: var(--dm-warning); }
|
|
67
|
+
.fsx-when { min-width: 7.5rem; text-align: right; color: var(--dm-text-muted); font-size: var(--dm-text-sm); font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
68
|
+
.fsx-kebab { border: 0; background: none; cursor: pointer; font-size: var(--dm-text-lg); color: var(--dm-text-muted); border-radius: var(--dm-radius-sm); padding: 0 .3rem; }
|
|
69
|
+
.fsx-kebab:hover { color: var(--dm-primary); }
|
|
70
|
+
/* The details slideover */
|
|
71
|
+
.fsx-detail { display: flex; flex-direction: column; gap: .85rem; }
|
|
72
|
+
.fsx-answers { display: flex; flex-direction: column; margin: 0; }
|
|
73
|
+
.fsx-answer { padding: .55rem 0; border-bottom: 1px solid var(--dm-border); }
|
|
74
|
+
.fsx-answer dt { font-size: var(--dm-text-xs); font-weight: 600; color: var(--dm-text-muted); margin-bottom: .15rem; }
|
|
75
|
+
.fsx-answer dd { margin: 0; white-space: pre-wrap; word-break: break-word; }
|
|
76
|
+
.fsx-facts { display: flex; flex-wrap: wrap; gap: .35rem .9rem; color: var(--dm-text-muted); font-size: var(--dm-text-xs); }
|
|
77
|
+
.fsx-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; padding-top: .6rem; border-top: 1px solid var(--dm-border); }
|
|
78
|
+
.fsx-actions .btn { display: inline-flex; align-items: center; gap: .3rem; }
|
|
79
|
+
.fsx-export { display: flex; flex-direction: column; gap: .6rem; }
|
|
80
|
+
.fsx-export .btn { justify-content: flex-start; display: inline-flex; align-items: center; gap: .4rem; }
|
|
81
|
+
@media (max-width: 720px) { .fsx-when { display: none; } .fsx-dates { flex-basis: 100%; } }
|
|
82
|
+
</style>
|