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,143 +1,221 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
<!-- Collection editor. Bound with M.applyBindings - see admin/js/views/collection-editor.js, which also inserts
|
|
2
|
+
the field row template (the router renders a templateUrl through _.render, eating any {{…}}).
|
|
3
|
+
No page heading: the Tool banner above is the heading ("Collections › <title>"); Save, All collections and
|
|
4
|
+
Open entries are banner actions. -->
|
|
5
|
+
<div class="cex">
|
|
6
|
+
<div class="cex-unsaved" data-bind-hidden="!dirty.value">
|
|
7
|
+
<span data-icon="alert-circle" data-icon-size="15"></span>
|
|
8
|
+
<strong>Unsaved changes</strong>
|
|
9
|
+
<small data-bind-hidden="!problemCount.value" data-bind-text="problemText.value"></small>
|
|
10
|
+
<span class="cex-spacer"></span>
|
|
11
|
+
<button type="button" class="btn btn-sm btn-ghost" data-on-click="discard">Discard</button>
|
|
12
|
+
<button type="button" class="btn btn-sm btn-primary" data-on-click="save" data-bind-disabled="saving.value">
|
|
13
|
+
<span data-icon="save" data-icon-size="13"></span> Save</button>
|
|
10
14
|
</div>
|
|
11
|
-
</div>
|
|
12
15
|
|
|
13
|
-
<div class="tabs
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
<div class="tab-content">
|
|
16
|
+
<div class="tabs cex-tabs">
|
|
17
|
+
<div class="tab-list">
|
|
18
|
+
<button type="button" class="tab-item active">Settings <i class="cex-dot" data-bind-hidden="!settingsBad.value" title="Something here needs fixing"></i></button>
|
|
19
|
+
<button type="button" class="tab-item">Fields <b class="cex-count" data-bind-text="fieldCount.value"></b> <i class="cex-dot" data-bind-hidden="!fieldsBad.value" title="A field needs fixing"></i></button>
|
|
20
|
+
<button type="button" class="tab-item">API & Export <i class="cex-dot" data-bind-hidden="!exportBad.value" title="Something here needs fixing"></i></button>
|
|
21
|
+
<button type="button" class="tab-item cex-storage-tab" data-bind-hidden="!showStorage.value">Storage</button>
|
|
22
|
+
</div>
|
|
21
23
|
|
|
22
|
-
<!-- Settings
|
|
24
|
+
<!-- Settings -->
|
|
23
25
|
<div class="tab-panel active">
|
|
24
|
-
<div class="card
|
|
25
|
-
<div class="card-
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
<div class="cex-card">
|
|
27
|
+
<div class="cex-card-title">Collection
|
|
28
|
+
<span data-help-title="Collections" data-help="A collection is a set of entries that share the same fields - products, bookings, team members. Its entries are edited under Data › Collections, shown on pages with the [collection] shortcode, and can be read by the public API if you allow it on the API & Export tab."></span></div>
|
|
29
|
+
<div class="cex-grid">
|
|
30
|
+
<div class="cex-wide">
|
|
31
|
+
<label class="form-label" for="field-title">Title <span class="text-danger">*</span></label>
|
|
32
|
+
<input id="field-title" type="text" class="form-input" maxlength="120" placeholder="e.g. Products" data-model="title.value">
|
|
33
|
+
<small class="cex-err" data-bind-hidden="!errTitle.value" data-bind-text="errTitle.value"></small>
|
|
34
|
+
</div>
|
|
35
|
+
<div>
|
|
36
|
+
<label class="form-label" for="field-slug">Slug
|
|
37
|
+
<span data-help-title="Slug" data-help="The collection's id - in its admin address, its API address (/api/collections/<slug>/public) and the [collection] shortcode. Made from the title as you type; it cannot be changed once the collection exists."></span></label>
|
|
38
|
+
<input id="field-slug" type="text" class="form-input font-mono" maxlength="80" placeholder="products" data-model="slug.value" data-bind-disabled="!isNew.value">
|
|
39
|
+
<small class="cex-err" data-bind-hidden="!errSlug.value" data-bind-text="errSlug.value"></small>
|
|
40
|
+
<small class="cex-hint" data-bind-hidden="errSlug.value" data-bind-text="slugHint.value"></small>
|
|
41
|
+
</div>
|
|
42
|
+
<div>
|
|
43
|
+
<label class="form-label" for="collection-project">Project
|
|
44
|
+
<span data-help-title="Project" data-help="Files the collection under a project, for the sidebar and for users limited to certain projects. Left empty it belongs to Core, which everyone can see."></span></label>
|
|
45
|
+
<select id="collection-project" class="form-input" data-model="project.value">
|
|
46
|
+
<option value="">- none (Core) -</option>
|
|
47
|
+
</select>
|
|
36
48
|
</div>
|
|
37
|
-
<div class="
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
<textarea id="field-description" class="form-input" rows="2" placeholder="Optional description…"></textarea>
|
|
41
|
-
</div>
|
|
49
|
+
<div class="cex-wide">
|
|
50
|
+
<label class="form-label" for="field-description">Description</label>
|
|
51
|
+
<textarea id="field-description" class="form-input" rows="2" maxlength="500" placeholder="Optional - what the entries are for" data-model="description.value"></textarea>
|
|
42
52
|
</div>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
<
|
|
52
|
-
|
|
53
|
-
<
|
|
54
|
-
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<div class="cex-card">
|
|
57
|
+
<div class="cex-card-title">Entry form
|
|
58
|
+
<span data-help-title="Entry form" data-help="How the form for adding and editing an entry is laid out. Stacked puts one field per row; Grid places them in columns, and each field can span several columns or the full width (set on the field)."></span></div>
|
|
59
|
+
<div class="cex-grid">
|
|
60
|
+
<div>
|
|
61
|
+
<label class="form-label" for="collection-layout">Layout</label>
|
|
62
|
+
<select id="collection-layout" class="form-input" data-model="layout.value">
|
|
63
|
+
<option value="stacked">Stacked - one field per row</option>
|
|
64
|
+
<option value="grid">Grid - fields in columns</option>
|
|
65
|
+
</select>
|
|
55
66
|
</div>
|
|
56
|
-
<div
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
<option value="">- none -</option>
|
|
61
|
-
</select>
|
|
62
|
-
<p class="form-hint text-muted" style="margin-top:.3rem;font-size:.8rem;">Tag this collection to a project for grouping.</p>
|
|
63
|
-
</div>
|
|
67
|
+
<div data-bind-hidden="layout.value !== 'grid'">
|
|
68
|
+
<label class="form-label" for="collection-columns">Columns</label>
|
|
69
|
+
<input id="collection-columns" type="number" class="form-input" min="1" max="6" data-model="columns.value">
|
|
70
|
+
<small class="cex-err" data-bind-hidden="!errColumns.value" data-bind-text="errColumns.value"></small>
|
|
64
71
|
</div>
|
|
65
|
-
<div class="
|
|
66
|
-
<
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
</label>
|
|
70
|
-
</div>
|
|
72
|
+
<div class="cex-setting cex-wide">
|
|
73
|
+
<label class="form-label" for="collection-bundled">Bundled
|
|
74
|
+
<span data-help-title="Bundled" data-help="Ships with the site: the seed script includes it in a fresh install. Leave it off for a collection that only this site needs."></span></label>
|
|
75
|
+
<input id="collection-bundled" type="checkbox" class="form-switch" data-model="bundled.value">
|
|
71
76
|
</div>
|
|
72
77
|
</div>
|
|
73
78
|
</div>
|
|
74
79
|
</div>
|
|
75
80
|
|
|
76
|
-
<!-- Fields
|
|
81
|
+
<!-- Fields -->
|
|
77
82
|
<div class="tab-panel">
|
|
78
|
-
<div class="card
|
|
79
|
-
<div class="card-
|
|
80
|
-
<
|
|
81
|
-
<
|
|
82
|
-
|
|
83
|
-
</button>
|
|
84
|
-
</div>
|
|
85
|
-
<div class="card-body" style="padding:0;">
|
|
86
|
-
<div id="fields-list" style="padding:1rem;">
|
|
87
|
-
<p class="text-muted" id="fields-empty-msg" style="text-align:center;padding:2rem 0;">No fields yet. Click "Add Field" to get started.</p>
|
|
88
|
-
</div>
|
|
83
|
+
<div class="cex-card">
|
|
84
|
+
<div class="cex-card-title">Fields
|
|
85
|
+
<span data-help-title="Fields" data-help="What every entry holds. Click a field to edit it; drag it by its grip, or right-click it (or ⋮, or Shift+F10) to move it up or down, duplicate it or remove it. Alt+↑ / Alt+↓ move the focused field. Changes are kept here until you Save."></span>
|
|
86
|
+
<span class="cex-spacer"></span>
|
|
87
|
+
<button type="button" class="btn btn-sm btn-ghost" data-on-click="addField"><span data-icon="plus" data-icon-size="13"></span> Add field</button>
|
|
89
88
|
</div>
|
|
89
|
+
<p class="cex-note" data-bind-hidden="fieldCount.value">No fields yet - Add field to give entries something to hold.</p>
|
|
90
|
+
<div class="cex-list" data-each="rows.value key=key"></div>
|
|
90
91
|
</div>
|
|
91
92
|
</div>
|
|
92
93
|
|
|
93
|
-
<!-- API
|
|
94
|
+
<!-- API & Export -->
|
|
94
95
|
<div class="tab-panel">
|
|
95
|
-
<div class="card
|
|
96
|
-
<div class="card-
|
|
97
|
-
<
|
|
96
|
+
<div class="cex-card">
|
|
97
|
+
<div class="cex-card-title">Public API
|
|
98
|
+
<span data-help-title="Public API" data-help="What the public API may do with this collection, and who may do it: Public is anyone; Token needs an API token (System › API Tokens); the others need a signed-in user with at least that role. The admin itself is always governed by roles and permissions, not by these switches."></span></div>
|
|
99
|
+
<p class="cex-note">At <code data-bind-text="apiPath.value"></code></p>
|
|
100
|
+
<div class="cex-ops">
|
|
101
|
+
<div class="cex-op"><strong>Create</strong>
|
|
102
|
+
<label class="cex-check"><input type="checkbox" data-model="createEnabled.value"> Allowed</label>
|
|
103
|
+
<select class="form-input" aria-label="Who may create" data-model="createAccess.value" data-bind-disabled="!createEnabled.value"></select></div>
|
|
104
|
+
<div class="cex-op"><strong>Read</strong>
|
|
105
|
+
<label class="cex-check"><input type="checkbox" data-model="readEnabled.value"> Allowed</label>
|
|
106
|
+
<select class="form-input" aria-label="Who may read" data-model="readAccess.value" data-bind-disabled="!readEnabled.value"></select></div>
|
|
107
|
+
<div class="cex-op cex-op-sub">
|
|
108
|
+
<label for="api-read-fields">Read fields
|
|
109
|
+
<span data-help-title="Read fields" data-help="Only these fields are returned by the public and external read endpoints - a comma-separated list of field names. Empty returns every field."></span></label>
|
|
110
|
+
<input id="api-read-fields" type="text" class="form-input" placeholder="Empty = every field" data-model="readFields.value">
|
|
111
|
+
<small class="cex-hint cex-wide" data-bind-hidden="!unknownReadFields.value" data-bind-text="unknownReadFields.value"></small>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="cex-op"><strong>Update</strong>
|
|
114
|
+
<label class="cex-check"><input type="checkbox" data-model="updateEnabled.value"> Allowed</label>
|
|
115
|
+
<select class="form-input" aria-label="Who may update" data-model="updateAccess.value" data-bind-disabled="!updateEnabled.value"></select></div>
|
|
116
|
+
<div class="cex-op"><strong>Delete</strong>
|
|
117
|
+
<label class="cex-check"><input type="checkbox" data-model="deleteEnabled.value"> Allowed</label>
|
|
118
|
+
<select class="form-input" aria-label="Who may delete" data-model="deleteAccess.value" data-bind-disabled="!deleteEnabled.value"></select></div>
|
|
98
119
|
</div>
|
|
99
|
-
|
|
100
|
-
<p class="text-muted mb-4" style="font-size:.875rem;">Control which operations are available via the public API (<code>/api/collections/{slug}/public</code>). Admin endpoints are always protected by role.</p>
|
|
120
|
+
</div>
|
|
101
121
|
|
|
102
|
-
|
|
103
|
-
|
|
122
|
+
<div class="cex-card">
|
|
123
|
+
<div class="cex-card-title">Export
|
|
124
|
+
<span data-help-title="Export" data-help="Lets visitors download the entries a [collection] display shows, from its right-click menu - as CSV and/or JSON. Who may export is a role: Public is anyone."></span></div>
|
|
125
|
+
<div class="cex-ops">
|
|
126
|
+
<div class="cex-op"><strong>Export</strong>
|
|
127
|
+
<label class="cex-check"><input id="export-enabled" type="checkbox" data-model="exportEnabled.value"> Allowed</label>
|
|
128
|
+
<select id="export-access" class="form-input" aria-label="Who may export" data-model="exportAccess.value" data-bind-disabled="!exportEnabled.value"></select></div>
|
|
129
|
+
<div class="cex-op cex-op-sub"><span>Formats</span>
|
|
130
|
+
<span class="cex-formats">
|
|
131
|
+
<label class="cex-check"><input id="export-format-csv" type="checkbox" data-model="exportCsv.value" data-bind-disabled="!exportEnabled.value"> CSV</label>
|
|
132
|
+
<label class="cex-check"><input id="export-format-json" type="checkbox" data-model="exportJson.value" data-bind-disabled="!exportEnabled.value"> JSON</label>
|
|
133
|
+
</span>
|
|
134
|
+
<small class="cex-err cex-wide" data-bind-hidden="!errExport.value" data-bind-text="errExport.value"></small>
|
|
104
135
|
</div>
|
|
105
136
|
</div>
|
|
106
137
|
</div>
|
|
107
138
|
</div>
|
|
108
139
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
</button>
|
|
132
|
-
<input type="hidden" id="storage-connection" value="default">
|
|
133
|
-
<p class="form-text text-muted mt-1">Named connection from config/connections.json</p>
|
|
134
|
-
</div>
|
|
135
|
-
<div id="storage-migration-warning" class="alert alert-warning mt-3" style="display:none;">
|
|
136
|
-
Changing the storage adapter does not migrate existing entries. Export your data before switching.
|
|
140
|
+
<!-- Storage (Pro) -->
|
|
141
|
+
<div class="tab-panel">
|
|
142
|
+
<div class="cex-card">
|
|
143
|
+
<div class="cex-card-title">Storage
|
|
144
|
+
<span data-help-title="Storage" data-help="Where this collection's entries live: in a file on this server (the default), or in MongoDB (Pro). Switching an existing collection asks whether to move its entries across when you Save - moving copies each entry to the new store. Export the entries first if they matter."></span></div>
|
|
145
|
+
<div class="cex-grid">
|
|
146
|
+
<div>
|
|
147
|
+
<label class="form-label" for="storage-adapter">Adapter</label>
|
|
148
|
+
<select id="storage-adapter" class="form-input" data-model="storageAdapter.value">
|
|
149
|
+
<option value="file">File (default)</option>
|
|
150
|
+
<option value="mongodb">MongoDB</option>
|
|
151
|
+
</select>
|
|
152
|
+
</div>
|
|
153
|
+
<div data-bind-hidden="storageAdapter.value !== 'mongodb'">
|
|
154
|
+
<label class="form-label" for="storage-connection">Connection
|
|
155
|
+
<span data-help="A named connection from config/connections.json."></span></label>
|
|
156
|
+
<select id="storage-connection" class="form-input" data-model="storageConnection.value"></select>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
<p class="cex-warn" data-bind-hidden="!storageNote.value">
|
|
160
|
+
<span data-icon="alert-triangle" data-icon-size="15"></span> <span data-bind-text="storageNote.value"></span>
|
|
161
|
+
</p>
|
|
137
162
|
</div>
|
|
138
|
-
</div>
|
|
139
163
|
</div>
|
|
140
|
-
</div>
|
|
141
|
-
|
|
142
164
|
</div>
|
|
143
165
|
</div>
|
|
166
|
+
|
|
167
|
+
<style>
|
|
168
|
+
.cex { display: flex; flex-direction: column; gap: .75rem; }
|
|
169
|
+
.cex [hidden], .cex-panel [hidden] { display: none !important; }
|
|
170
|
+
.cex-unsaved { position: sticky; top: 0; z-index: 5; display: flex; align-items: center; gap: .5rem; padding: .5rem .75rem;
|
|
171
|
+
border: 1px solid color-mix(in srgb, var(--dm-warning) 45%, var(--dm-border)); border-radius: var(--dm-radius-md);
|
|
172
|
+
background: color-mix(in srgb, var(--dm-warning) 12%, var(--dm-surface)); color: var(--dm-text); }
|
|
173
|
+
.cex-unsaved small { color: var(--dm-danger); }
|
|
174
|
+
.cex-unsaved .btn { display: inline-flex; align-items: center; gap: .3rem; }
|
|
175
|
+
.cex-spacer { flex: 1; }
|
|
176
|
+
.cex-tabs .tab-item { display: inline-flex; align-items: center; gap: .3rem; }
|
|
177
|
+
.cex-count { font-weight: 500; opacity: .65; font-size: var(--dm-text-xs); }
|
|
178
|
+
.cex-dot { width: .45rem; height: .45rem; border-radius: 50%; background: var(--dm-danger); display: inline-block; }
|
|
179
|
+
.cex-card { border: 1px solid var(--dm-border); border-radius: var(--dm-radius-md); padding: .75rem .9rem; display: flex; flex-direction: column; gap: .7rem; margin-top: .75rem; }
|
|
180
|
+
.cex-card-title { display: flex; align-items: center; gap: .35rem; font-weight: 600; }
|
|
181
|
+
.cex-card-title .btn { display: inline-flex; align-items: center; gap: .3rem; font-weight: 500; }
|
|
182
|
+
.cex-grid { display: grid; grid-template-columns: 1fr 1fr; gap: .8rem; }
|
|
183
|
+
.cex-wide { grid-column: 1 / -1; }
|
|
184
|
+
.cex .form-label, .cex-panel .form-label { display: flex; align-items: center; gap: .3rem; }
|
|
185
|
+
.cex-err { display: block; margin-top: .25rem; color: var(--dm-danger); font-size: var(--dm-text-xs); }
|
|
186
|
+
.cex-hint { display: block; margin-top: .25rem; color: var(--dm-text-muted); font-size: var(--dm-text-xs); }
|
|
187
|
+
.cex-note { margin: 0; color: var(--dm-text-muted); font-size: var(--dm-text-sm); }
|
|
188
|
+
.cex-warn { margin: 0; display: flex; gap: .45rem; align-items: flex-start; padding: .55rem .7rem; border-radius: var(--dm-radius-md);
|
|
189
|
+
background: color-mix(in srgb, var(--dm-warning) 14%, transparent); color: var(--dm-text); font-size: var(--dm-text-sm); }
|
|
190
|
+
.cex-setting { display: flex; align-items: center; justify-content: space-between; gap: .75rem; }
|
|
191
|
+
.cex-setting .form-label { margin: 0; }
|
|
192
|
+
.cex-list { display: flex; flex-direction: column; }
|
|
193
|
+
.cex-row { display: flex; align-items: center; gap: .6rem; padding: .5rem .35rem; border-bottom: 1px solid var(--dm-border); cursor: pointer; }
|
|
194
|
+
.cex-row:last-child { border-bottom: 0; }
|
|
195
|
+
.cex-row:hover, .cex-row:focus-visible { background: var(--dm-table-hover-bg, color-mix(in srgb, var(--dm-text) 6%, transparent)); outline: none; }
|
|
196
|
+
.cex-row.is-bad { box-shadow: inset 3px 0 0 var(--dm-danger); }
|
|
197
|
+
.cex-row.is-drag-over { box-shadow: inset 0 2px 0 var(--dm-primary); }
|
|
198
|
+
.cex-row.is-dragging { opacity: .45; }
|
|
199
|
+
.cex-grip { cursor: grab; color: var(--dm-text-muted); user-select: none; padding: 0 .15rem; }
|
|
200
|
+
.cex-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
|
|
201
|
+
.cex-main strong, .cex-main small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
202
|
+
.cex-main small { color: var(--dm-text-muted); font-size: var(--dm-text-xs); }
|
|
203
|
+
.cex-main small.cex-problem { color: var(--dm-danger); }
|
|
204
|
+
.cex-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;
|
|
205
|
+
background: color-mix(in srgb, var(--dm-text) 10%, transparent); color: var(--dm-text-muted); }
|
|
206
|
+
.cex-chip.is-ok { background: color-mix(in srgb, var(--dm-success) 16%, transparent); color: var(--dm-success); }
|
|
207
|
+
.cex-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; }
|
|
208
|
+
.cex-kebab:hover { color: var(--dm-primary); }
|
|
209
|
+
.cex-ops { display: flex; flex-direction: column; }
|
|
210
|
+
.cex-op { display: grid; grid-template-columns: 7.5rem 1fr 11rem; gap: .75rem; align-items: center; padding: .5rem 0; border-bottom: 1px solid var(--dm-border); }
|
|
211
|
+
.cex-op:last-child { border-bottom: 0; }
|
|
212
|
+
.cex-op-sub { grid-template-columns: 7.5rem 1fr; padding-left: 1rem; }
|
|
213
|
+
.cex-op-sub > label, .cex-op-sub > span:first-child { color: var(--dm-text-muted); font-size: var(--dm-text-sm); display: flex; align-items: center; gap: .3rem; }
|
|
214
|
+
.cex-check { display: inline-flex; align-items: center; gap: .4rem; cursor: pointer; font-size: var(--dm-text-sm); }
|
|
215
|
+
.cex-formats { display: flex; gap: 1rem; }
|
|
216
|
+
/* The field panel (slideover) */
|
|
217
|
+
.cex-panel { display: flex; flex-direction: column; gap: .85rem; }
|
|
218
|
+
.cex-panel-actions { display: flex; align-items: center; gap: .4rem; padding-top: .6rem; border-top: 1px solid var(--dm-border); }
|
|
219
|
+
.cex-panel-actions .btn { display: inline-flex; align-items: center; gap: .3rem; }
|
|
220
|
+
@media (max-width: 720px) { .cex-grid { grid-template-columns: 1fr; } .cex-op { grid-template-columns: 1fr; } }
|
|
221
|
+
</style>
|
|
@@ -1,40 +1,103 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<span data-icon="layout"></span> Form
|
|
12
|
-
</a>
|
|
13
|
-
<button id="import-btn" class="btn btn-ghost btn-sm">
|
|
14
|
-
<span data-icon="upload"></span> Import
|
|
15
|
-
</button>
|
|
16
|
-
<button id="export-btn" class="btn btn-ghost btn-sm">
|
|
17
|
-
<span data-icon="download"></span> Export
|
|
18
|
-
</button>
|
|
19
|
-
<button id="clear-all-btn" class="btn btn-danger btn-sm">
|
|
20
|
-
<span data-icon="trash"></span> Clear All
|
|
21
|
-
</button>
|
|
22
|
-
<button id="add-entry-btn" class="btn btn-primary">
|
|
23
|
-
<span data-icon="plus"></span> Add Entry
|
|
24
|
-
</button>
|
|
25
|
-
</div>
|
|
26
|
-
</div>
|
|
1
|
+
<!-- A collection's entries. Bound with M.applyBindings - see admin/views/collection-entries.js, which also
|
|
2
|
+
builds the table's head and row template from the collection's fields (the router renders a templateUrl
|
|
3
|
+
through _.render, eating any {{…}}). No page heading: the Tool banner is the heading (its section is the
|
|
4
|
+
collection's title), and Add / Import / Export / Edit fields / Form are banner actions. -->
|
|
5
|
+
<div class="cex">
|
|
6
|
+
<p class="cex-note" data-bind-hidden="!ready.value">
|
|
7
|
+
<span data-bind-text="introText.value"></span>
|
|
8
|
+
<span data-help-title="Entries" data-help="Click an entry to edit it. Right-click it (or ⋮, or Shift+F10) to see all of it, duplicate it, copy its id, run one of the collection's actions or delete it - and right-click a value to filter by it. Tick entries to delete several at once. Filters and the search box work on the entries loaded; a big collection loads 500 at a time."></span>
|
|
9
|
+
</p>
|
|
10
|
+
<p class="cex-note is-bad" data-bind-hidden="!loadError.value" data-bind-text="loadError.value"></p>
|
|
27
11
|
|
|
28
|
-
<div class="card
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
12
|
+
<div class="cex-card" data-bind-hidden="!ready.value">
|
|
13
|
+
<div class="cex-bar">
|
|
14
|
+
<input type="search" class="form-input cex-search" placeholder="Search entries…" aria-label="Search entries" data-model="query.value">
|
|
15
|
+
<span class="cex-count" data-bind-text="countLine.value"></span>
|
|
16
|
+
<span class="cex-spacer"></span>
|
|
17
|
+
<span class="cex-bulk" data-bind-hidden="!selectedCount.value">
|
|
18
|
+
<strong data-bind-text="selectedText.value"></strong>
|
|
19
|
+
<button type="button" class="btn btn-sm btn-ghost" data-on-click="clearSelection">Clear</button>
|
|
20
|
+
<button type="button" class="btn btn-sm btn-danger" data-on-click="deleteSelected" data-bind-disabled="busy.value">
|
|
21
|
+
<span data-icon="trash" data-icon-size="13"></span> Delete</button>
|
|
22
|
+
</span>
|
|
23
|
+
<button type="button" class="btn btn-sm btn-ghost text-danger" data-on-click="clearAll" data-bind-hidden="!canClear.value || !loaded.value.length || selectedCount.value" data-bind-disabled="busy.value">
|
|
24
|
+
Delete all…</button>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="cex-chips" data-bind-hidden="!chips.value.length">
|
|
27
|
+
<span class="cex-chips-list" data-each="chips.value key=key"></span>
|
|
28
|
+
<button type="button" class="btn btn-sm btn-ghost" data-on-click="clearFilters">Clear filters</button>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<p class="cex-note" data-bind-hidden="!loading.value">Loading…</p>
|
|
32
|
+
<p class="cex-note" data-bind-hidden="loading.value || rows.value.length" data-bind-text="emptyText.value"></p>
|
|
35
33
|
|
|
36
|
-
<div class="
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
<div class="cex-scroll" data-bind-hidden="!rows.value.length">
|
|
35
|
+
<table class="cex-table">
|
|
36
|
+
<thead><tr class="cex-head"></tr></thead>
|
|
37
|
+
<tbody class="cex-rows" data-each="rows.value key=id"></tbody>
|
|
38
|
+
</table>
|
|
39
|
+
</div>
|
|
40
|
+
<div class="cex-more" data-bind-hidden="!moreText.value">
|
|
41
|
+
<button type="button" class="btn btn-sm btn-ghost" data-on-click="showMore" data-bind-disabled="loading.value">
|
|
42
|
+
<span data-bind-text="moreText.value"></span></button>
|
|
43
|
+
<span data-help="Shown 100 at a time. When a collection has more entries than are loaded, the next 500 come from the server - filters and search only see what is loaded."></span>
|
|
44
|
+
</div>
|
|
39
45
|
</div>
|
|
40
46
|
</div>
|
|
47
|
+
|
|
48
|
+
<style>
|
|
49
|
+
.cex { display: flex; flex-direction: column; gap: .75rem; }
|
|
50
|
+
.cex [hidden], .cex-form [hidden] { display: none !important; }
|
|
51
|
+
.cex-note { margin: 0; color: var(--dm-text-muted); font-size: var(--dm-text-sm); }
|
|
52
|
+
.cex-note.is-bad { color: var(--dm-danger); }
|
|
53
|
+
.cex-card { border: 1px solid var(--dm-border); border-radius: var(--dm-radius-md); padding: .75rem .9rem; display: flex; flex-direction: column; gap: .6rem; }
|
|
54
|
+
.cex-bar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
|
|
55
|
+
.cex-bar .btn, .cex-more .btn { display: inline-flex; align-items: center; gap: .3rem; }
|
|
56
|
+
.cex-search { flex: 1; min-width: 10rem; max-width: 24rem; }
|
|
57
|
+
.cex-count { color: var(--dm-text-muted); font-size: var(--dm-text-sm); white-space: nowrap; }
|
|
58
|
+
.cex-spacer { flex: 1; }
|
|
59
|
+
.cex-bulk { display: inline-flex; align-items: center; gap: .4rem; font-size: var(--dm-text-sm); }
|
|
60
|
+
.cex-chips { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
|
|
61
|
+
.cex-chips-list { display: contents; }
|
|
62
|
+
.cex-chip { display: inline-flex; align-items: center; gap: .35rem; font-size: var(--dm-text-xs); font-weight: 600; padding: .15rem .55rem; border-radius: 999px;
|
|
63
|
+
background: color-mix(in srgb, var(--dm-primary) 14%, transparent); color: var(--dm-primary); }
|
|
64
|
+
.cex-chip button { border: 0; background: none; color: inherit; cursor: pointer; font-size: var(--dm-text-sm); line-height: 1; padding: 0; }
|
|
65
|
+
.cex-scroll { overflow-x: auto; }
|
|
66
|
+
.cex-table { width: 100%; border-collapse: collapse; font-size: var(--dm-text-sm); }
|
|
67
|
+
.cex-table th { text-align: left; font-weight: 600; color: var(--dm-text-muted); font-size: var(--dm-text-xs); padding: .35rem .5rem;
|
|
68
|
+
border-bottom: 1px solid var(--dm-border); white-space: nowrap; }
|
|
69
|
+
.cex-table th button { border: 0; background: none; color: inherit; font: inherit; cursor: pointer; padding: 0; display: inline-flex; gap: .25rem; align-items: center; }
|
|
70
|
+
.cex-table th button:hover { color: var(--dm-text); }
|
|
71
|
+
.cex-table th .cex-sort { opacity: .7; }
|
|
72
|
+
.cex-row { border-bottom: 1px solid var(--dm-border); cursor: pointer; }
|
|
73
|
+
.cex-row:last-child { border-bottom: 0; }
|
|
74
|
+
.cex-row:hover, .cex-row:focus-visible { background: var(--dm-table-hover-bg, color-mix(in srgb, var(--dm-text) 6%, transparent)); outline: none; }
|
|
75
|
+
.cex-row.is-selected { background: color-mix(in srgb, var(--dm-primary) 8%, transparent); }
|
|
76
|
+
.cex-row td { padding: .45rem .5rem; vertical-align: middle; max-width: 22rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
77
|
+
.cex-row td.cex-when { color: var(--dm-text-muted); font-size: var(--dm-text-xs); }
|
|
78
|
+
.cex-check { width: 1.75rem; }
|
|
79
|
+
.cex-tools { text-align: right; width: 1%; white-space: nowrap; }
|
|
80
|
+
.cex-run { border: 1px solid var(--dm-border); background: none; color: var(--dm-text-muted); border-radius: 999px; font-size: var(--dm-text-xs);
|
|
81
|
+
padding: .05rem .5rem; cursor: pointer; margin-right: .25rem; }
|
|
82
|
+
.cex-run:hover { color: var(--dm-primary); border-color: var(--dm-primary); }
|
|
83
|
+
.cex-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; }
|
|
84
|
+
.cex-kebab:hover { color: var(--dm-primary); }
|
|
85
|
+
.cex-more { display: flex; align-items: center; gap: .4rem; justify-content: center; }
|
|
86
|
+
/* Slideovers */
|
|
87
|
+
.cex-form { display: flex; flex-direction: column; gap: .85rem; }
|
|
88
|
+
.cex-hint { display: block; color: var(--dm-text-muted); font-size: var(--dm-text-xs); }
|
|
89
|
+
.cex-err { display: block; color: var(--dm-danger); font-size: var(--dm-text-xs); }
|
|
90
|
+
.cex-ok { display: block; color: var(--dm-success); font-size: var(--dm-text-xs); }
|
|
91
|
+
.cex-actions { display: flex; align-items: center; gap: .4rem; padding-top: .6rem; border-top: 1px solid var(--dm-border); }
|
|
92
|
+
.cex-actions .btn { display: inline-flex; align-items: center; gap: .3rem; }
|
|
93
|
+
.cex-dirty { font-size: var(--dm-text-xs); font-weight: 600; padding: .1rem .5rem; border-radius: 999px;
|
|
94
|
+
background: color-mix(in srgb, var(--dm-warning) 18%, transparent); color: var(--dm-warning); }
|
|
95
|
+
.cex-detail { display: flex; flex-direction: column; gap: .6rem; }
|
|
96
|
+
.cex-detail dt { font-size: var(--dm-text-xs); color: var(--dm-text-muted); font-weight: 600; }
|
|
97
|
+
.cex-detail dd { margin: 0 0 .5rem; word-break: break-word; white-space: pre-wrap; padding-bottom: .5rem; border-bottom: 1px solid var(--dm-border); }
|
|
98
|
+
.cex-meta { font-size: var(--dm-text-xs); color: var(--dm-text-muted); display: flex; flex-direction: column; gap: .2rem; }
|
|
99
|
+
.cex-import { width: 100%; font-family: var(--dm-font-mono, monospace); font-size: var(--dm-text-sm); min-height: 12rem; resize: vertical; }
|
|
100
|
+
.cex-export { display: flex; flex-direction: column; gap: .5rem; }
|
|
101
|
+
.cex-export .btn { justify-content: flex-start; display: inline-flex; gap: .4rem; align-items: center; }
|
|
102
|
+
@media (max-width: 640px) { .cex-row td.cex-when { display: none; } .cex-table th.cex-when { display: none; } }
|
|
103
|
+
</style>
|