domma-cms 0.66.0 → 0.69.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +64 -1
- package/README.md +14 -10
- package/admin/css/admin.css +1 -1
- package/admin/index.html +1 -0
- package/admin/js/api.js +1 -1
- package/admin/js/app.js +4 -4
- package/admin/js/lib/analytics-shape.js +1 -0
- package/admin/js/lib/contacts-arrange.js +1 -0
- package/admin/js/lib/help-popover.js +1 -0
- package/admin/js/lib/notes-arrange.js +1 -0
- package/admin/js/lib/plugin-accent.js +1 -1
- package/admin/js/lib/plugin-chrome.js +1 -1
- package/admin/js/lib/sidebar-grouping.js +1 -1
- package/admin/js/lib/sidebar-grouping.test.js +1 -1
- package/admin/js/lib/sidebar-renderer.js +3 -3
- package/admin/js/lib/todo-arrange.js +1 -0
- package/admin/js/lib/tool-kit.js +1 -0
- package/admin/js/templates/analytics.html +138 -0
- package/admin/js/templates/contacts.html +156 -0
- package/admin/js/templates/notes.html +137 -0
- package/admin/js/templates/plugin-marketplace.html +17 -4
- package/admin/js/templates/plugins.html +13 -7
- package/admin/js/templates/theme.html +38 -0
- package/admin/js/templates/todo.html +113 -0
- package/admin/js/views/analytics.js +62 -0
- package/admin/js/views/contacts.js +174 -0
- package/admin/js/views/index.js +1 -1
- package/admin/js/views/notes.js +104 -0
- package/admin/js/views/plugin-marketplace.js +1 -1
- package/admin/js/views/plugins.js +24 -22
- package/admin/js/views/theme.js +1 -1
- package/admin/js/views/todo.js +96 -0
- package/bin/cli.js +4 -10
- package/bin/lib/plugin-version.js +28 -0
- package/bin/update.js +14 -0
- package/config/menus/admin-sidebar.json +1 -1
- package/config/server.json +3 -1
- package/package.json +3 -7
- package/plugins/_lib/admin/mail/reader-view.js +20 -0
- package/plugins/_lib/mail/store.js +21 -1
- package/plugins/blog/CLAUDE.md +237 -0
- package/plugins/blog/admin/css/index.css +178 -1
- package/plugins/blog/admin/templates/blog.html +35 -53
- package/plugins/blog/admin/templates/categories.html +19 -6
- package/plugins/blog/admin/templates/comments.html +26 -9
- package/plugins/blog/admin/templates/post-editor.html +36 -13
- package/plugins/blog/admin/templates/settings.html +2 -11
- package/plugins/blog/admin/views/blog.js +288 -162
- package/plugins/blog/admin/views/categories.js +212 -201
- package/plugins/blog/admin/views/comments.js +207 -158
- package/plugins/blog/admin/views/kit.js +372 -0
- package/plugins/blog/admin/views/post-editor.js +269 -21
- package/plugins/blog/admin/views/settings-cog.js +56 -0
- package/plugins/blog/admin/views/settings.js +16 -86
- package/plugins/blog/blocks/blog-card-compact.css +17 -0
- package/plugins/blog/blocks/blog-card-compact.html +8 -0
- package/plugins/blog/blocks/blog-card-row.css +22 -0
- package/plugins/blog/blocks/blog-card-row.html +11 -0
- package/plugins/blog/blocks/blog-card.css +35 -0
- package/plugins/blog/blocks/blog-card.html +16 -0
- package/plugins/blog/blocks/blog-post-classic.css +15 -0
- package/plugins/blog/blocks/blog-post-classic.html +28 -0
- package/plugins/blog/blocks/blog-post-feature.css +20 -0
- package/plugins/blog/blocks/blog-post-feature.html +30 -0
- package/plugins/blog/blocks/blog-post-minimal.css +10 -0
- package/plugins/blog/blocks/blog-post-minimal.html +23 -0
- package/plugins/blog/blocks/blog-post-sidebar.css +22 -0
- package/plugins/blog/blocks/blog-post-sidebar.html +36 -0
- package/plugins/blog/collections/posts/schema.json +3 -1
- package/plugins/blog/config.js +6 -0
- package/plugins/blog/lib/layouts.js +304 -0
- package/plugins/blog/lib/page.js +158 -0
- package/plugins/blog/lib/render.js +119 -0
- package/plugins/blog/lib/samples.js +234 -0
- package/plugins/blog/plugin.js +225 -15
- package/plugins/blog/plugin.json +21 -81
- package/plugins/blog/plugin.public.js +115 -210
- package/plugins/blog/public/blog.css +177 -0
- package/plugins/blog/public/blog.js +586 -0
- package/plugins/blog/public/samples/aurora.svg +7 -0
- package/plugins/blog/public/samples/dusk.svg +7 -0
- package/plugins/blog/public/samples/ember.svg +7 -0
- package/plugins/blog/public/samples/harbour.svg +7 -0
- package/plugins/blog/public/samples/meadow.svg +7 -0
- package/plugins/blog/public/samples/rosewood.svg +7 -0
- package/plugins/blog/templates/index.html +9 -8
- package/plugins/blog/templates/post.html +4 -17
- package/plugins/blog/tests/layouts.test.js +97 -0
- package/plugins/blog/tests/public.test.js +71 -0
- package/plugins/free-tier.lock.json +74 -0
- package/plugins/mail-reader/CLAUDE.md +78 -0
- package/plugins/mail-reader/plugin.json +2 -1
- package/public/css/theme-switcher.css +1 -0
- package/public/js/analytics.js +1 -0
- package/public/js/theme-switcher.js +1 -0
- package/scripts/build.js +41 -2
- package/scripts/run-tests.mjs +92 -0
- package/{plugins/analytics/plugin.js → server/routes/api/analytics.js} +89 -41
- package/{plugins/contacts/plugin.js → server/routes/api/contacts.js} +71 -43
- package/server/routes/api/dashboard.js +8 -2
- package/{plugins/notes/plugin.js → server/routes/api/notes.js} +61 -17
- package/server/routes/api/plugin-marketplace.js +78 -23
- package/server/routes/api/plugins.js +148 -3
- package/server/routes/api/todo.js +178 -0
- package/server/server.js +61 -1
- package/server/services/collections.js +13 -3
- package/server/services/managerClient.js +72 -136
- package/server/services/markdown.js +54 -1
- package/server/services/permissionRegistry.js +50 -0
- package/server/services/pluginEntitlement.js +171 -0
- package/server/services/pluginEnvelope.js +242 -0
- package/server/services/pluginInstaller.js +246 -26
- package/server/services/pluginScaffold.js +5 -0
- package/server/services/plugins.js +117 -7
- package/server/services/presetCollections.js +344 -0
- package/server/services/promoted-plugins-migration.js +254 -0
- package/server/services/publicCors.js +46 -0
- package/server/services/renderer.js +83 -3
- package/server/services/sidebarBadges.js +37 -0
- package/server/services/siteGitignore.js +307 -0
- package/server/services/themeSettings.js +39 -0
- package/config/theme.json +0 -18
- package/plugins/analytics/admin/css/index.css +0 -1
- package/plugins/analytics/admin/templates/analytics.html +0 -70
- package/plugins/analytics/admin/views/analytics.js +0 -176
- package/plugins/analytics/config.js +0 -14
- package/plugins/analytics/plugin.json +0 -60
- package/plugins/analytics/public/inject-body.html +0 -60
- package/plugins/analytics/public/inject-head.html +0 -1
- package/plugins/blog/templates/author.html +0 -9
- package/plugins/blog/templates/category.html +0 -9
- package/plugins/blog/templates/tag.html +0 -9
- package/plugins/contacts/admin/templates/contacts.html +0 -254
- package/plugins/contacts/admin/views/contacts.js +0 -943
- package/plugins/contacts/collections/user-contact-groups/schema.json +0 -35
- package/plugins/contacts/collections/user-contacts/schema.json +0 -179
- package/plugins/contacts/config.js +0 -6
- package/plugins/contacts/plugin.json +0 -36
- package/plugins/demo-viewer/config.js +0 -4
- package/plugins/demo-viewer/plugin.js +0 -36
- package/plugins/demo-viewer/plugin.json +0 -9
- package/plugins/invoice/admin/css/index.css +0 -1
- package/plugins/invoice/admin/templates/editor.html +0 -220
- package/plugins/invoice/admin/templates/index.html +0 -173
- package/plugins/invoice/admin/templates/issuers.html +0 -2
- package/plugins/invoice/admin/templates/receivers.html +0 -2
- package/plugins/invoice/admin/views/contacts-source.js +0 -266
- package/plugins/invoice/admin/views/editor.js +0 -434
- package/plugins/invoice/admin/views/export.js +0 -199
- package/plugins/invoice/admin/views/help-content.js +0 -61
- package/plugins/invoice/admin/views/index.js +0 -643
- package/plugins/invoice/admin/views/issuers.js +0 -30
- package/plugins/invoice/admin/views/media.js +0 -172
- package/plugins/invoice/admin/views/party-view.js +0 -386
- package/plugins/invoice/admin/views/payments.js +0 -127
- package/plugins/invoice/admin/views/print.js +0 -130
- package/plugins/invoice/admin/views/receivers.js +0 -55
- package/plugins/invoice/admin/views/send.js +0 -212
- package/plugins/invoice/admin/views/settings.js +0 -594
- package/plugins/invoice/admin/views/view-lifecycle.js +0 -33
- package/plugins/invoice/collections/invoice-issuers/schema.json +0 -82
- package/plugins/invoice/collections/invoice-receivers/schema.json +0 -16
- package/plugins/invoice/collections/invoices/schema.json +0 -33
- package/plugins/invoice/config.js +0 -37
- package/plugins/invoice/pdf.js +0 -164
- package/plugins/invoice/plugin.js +0 -1456
- package/plugins/invoice/plugin.json +0 -86
- package/plugins/invoice/templates/_base.css +0 -1
- package/plugins/invoice/templates/classic-nologo.html +0 -100
- package/plugins/invoice/templates/classic.html +0 -91
- package/plugins/invoice/templates/invoice-print.html +0 -237
- package/plugins/invoice/templates/minimal.html +0 -99
- package/plugins/invoice/templates/modern-nologo.html +0 -114
- package/plugins/invoice/templates/modern.html +0 -113
- package/plugins/invoice/templates/templates.json +0 -11
- package/plugins/notes/admin/templates/notes.html +0 -83
- package/plugins/notes/admin/views/notes.js +0 -281
- package/plugins/notes/collections/user-notes/schema.json +0 -54
- package/plugins/notes/config.js +0 -6
- package/plugins/notes/plugin.json +0 -33
- package/plugins/surveys/admin/templates/audience.html +0 -47
- package/plugins/surveys/admin/templates/results.html +0 -58
- package/plugins/surveys/admin/templates/survey-editor.html +0 -141
- package/plugins/surveys/admin/templates/surveys.html +0 -25
- package/plugins/surveys/admin/views/audience.js +0 -301
- package/plugins/surveys/admin/views/results.js +0 -172
- package/plugins/surveys/admin/views/survey-editor.js +0 -211
- package/plugins/surveys/admin/views/surveys.js +0 -161
- package/plugins/surveys/collections/survey-contacts/schema.json +0 -13
- package/plugins/surveys/collections/survey-groups/schema.json +0 -11
- package/plugins/surveys/collections/survey-invites/schema.json +0 -16
- package/plugins/surveys/collections/surveys/schema.json +0 -23
- package/plugins/surveys/config.js +0 -8
- package/plugins/surveys/lib/audience.js +0 -37
- package/plugins/surveys/lib/campaigns.js +0 -43
- package/plugins/surveys/lib/ledger.js +0 -110
- package/plugins/surveys/lib/sending.js +0 -106
- package/plugins/surveys/lib/stats.js +0 -62
- package/plugins/surveys/lib/submit.js +0 -95
- package/plugins/surveys/lib/tokens.js +0 -28
- package/plugins/surveys/plugin.js +0 -174
- package/plugins/surveys/plugin.json +0 -65
- package/plugins/surveys/plugin.public.js +0 -149
- package/plugins/surveys/public/dist-shared.mjs +0 -1
- package/plugins/surveys/public/survey.css +0 -1
- package/plugins/surveys/public/survey.mjs +0 -1
- package/plugins/surveys/templates/survey-page.html +0 -63
- package/plugins/theme-switcher/admin/templates/theme-switcher.html +0 -86
- package/plugins/theme-switcher/admin/views/theme-switcher.js +0 -65
- package/plugins/theme-switcher/config.js +0 -10
- package/plugins/theme-switcher/plugin.js +0 -26
- package/plugins/theme-switcher/plugin.json +0 -74
- package/plugins/theme-switcher/public/inject-body.html +0 -153
- package/plugins/theme-switcher/public/inject-head.html +0 -260
- package/plugins/todo/admin/templates/todo.html +0 -158
- package/plugins/todo/admin/views/todo.js +0 -343
- package/plugins/todo/collections/todos/schema.json +0 -60
- package/plugins/todo/config.js +0 -7
- package/plugins/todo/plugin.js +0 -102
- package/plugins/todo/plugin.json +0 -33
- /package/{plugins/contacts/admin/css/index.css → admin/css/contacts.css} +0 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<!-- No heading: the banner above (admin/js/lib/plugin-chrome.js) is the title.
|
|
2
|
+
Everything that changes is bound (M.applyBindings) - see admin/js/views/notes.js. -->
|
|
3
|
+
<div class="notes-tool">
|
|
4
|
+
|
|
5
|
+
<!-- One compact bar: find, filter, count, layout, new. -->
|
|
6
|
+
<div class="notes-bar">
|
|
7
|
+
<label class="notes-search">
|
|
8
|
+
<span data-icon="search" data-icon-size="14"></span>
|
|
9
|
+
<input type="search" class="form-input" placeholder="Search notes…" aria-label="Search notes"
|
|
10
|
+
data-model="query.value" data-focus="searchFocus.value">
|
|
11
|
+
</label>
|
|
12
|
+
<select class="form-input notes-cat-select" aria-label="Filter by category"
|
|
13
|
+
data-options="categories.value" data-options-caption="'All categories'"
|
|
14
|
+
data-model="category.value"></select>
|
|
15
|
+
<span data-help-title="Finding notes"
|
|
16
|
+
data-help="Search matches titles, content and categories as you type. Pick a category to narrow the list, or click a category chip on any note. Press / to jump to search."></span>
|
|
17
|
+
|
|
18
|
+
<span class="notes-count" data-bind-text="countText.value"></span>
|
|
19
|
+
|
|
20
|
+
<div class="notes-bar-end">
|
|
21
|
+
<div class="notes-layout" role="group" aria-label="Layout">
|
|
22
|
+
<button type="button" class="btn btn-sm btn-ghost" title="Cards" aria-label="Card layout"
|
|
23
|
+
data-bind-class="isCards.value && 'is-active'" data-on-click="useCards">
|
|
24
|
+
<span data-icon="grid" data-icon-size="15"></span>
|
|
25
|
+
</button>
|
|
26
|
+
<button type="button" class="btn btn-sm btn-ghost" title="List" aria-label="List layout"
|
|
27
|
+
data-bind-class="!isCards.value && 'is-active'" data-on-click="useList">
|
|
28
|
+
<span data-icon="list" data-icon-size="15"></span>
|
|
29
|
+
</button>
|
|
30
|
+
</div>
|
|
31
|
+
<button type="button" class="btn btn-sm btn-primary" data-on-click="create" title="New note (N)">
|
|
32
|
+
<span data-icon="plus" data-icon-size="14"></span> New
|
|
33
|
+
</button>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<p class="notes-state" data-if="loading.value">Loading notes…</p>
|
|
38
|
+
<p class="notes-state" data-if="isEmpty.value" data-bind-text="emptyText.value"></p>
|
|
39
|
+
|
|
40
|
+
<!-- Right-click a note (or its ⋮) for everything you can do to it. -->
|
|
41
|
+
<div class="notes-list" data-bind-class="listClass.value">
|
|
42
|
+
<!-- The item template is inserted by notes.js (CARD_TEMPLATE): the router
|
|
43
|
+
renders this file through _.render first, which would eat its {{…}}. -->
|
|
44
|
+
<div class="notes-items" data-each="visible.value key=id"></div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<style>
|
|
49
|
+
.notes-tool { display: flex; flex-direction: column; gap: .75rem; }
|
|
50
|
+
|
|
51
|
+
.notes-bar { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
|
|
52
|
+
.notes-search { position: relative; flex: 1 1 200px; max-width: 320px; margin: 0; }
|
|
53
|
+
.notes-search > svg, .notes-search > [data-icon] {
|
|
54
|
+
position: absolute; left: .6rem; top: 50%; transform: translateY(-50%);
|
|
55
|
+
color: var(--dm-text-muted); pointer-events: none;
|
|
56
|
+
}
|
|
57
|
+
.notes-search .form-input { padding-left: 1.9rem; height: 2rem; }
|
|
58
|
+
.notes-cat-select { width: auto; min-width: 9rem; max-width: 14rem; height: 2rem; padding-top: 0; padding-bottom: 0; }
|
|
59
|
+
.notes-count { font-size: var(--dm-text-xs); color: var(--dm-text-muted); white-space: nowrap; }
|
|
60
|
+
.notes-bar-end { display: flex; align-items: center; gap: .5rem; margin-left: auto; }
|
|
61
|
+
.notes-layout { display: inline-flex; border: 1px solid var(--dm-border); border-radius: var(--dm-radius-md); overflow: hidden; }
|
|
62
|
+
.notes-layout .btn { border-radius: 0; padding: .25rem .45rem; line-height: 1; color: var(--dm-text-muted); }
|
|
63
|
+
.notes-layout .btn.is-active { background: var(--dm-surface-hover, rgba(127,127,127,.15)); color: var(--dm-primary); }
|
|
64
|
+
|
|
65
|
+
.notes-state { margin: 1.5rem 0; text-align: center; color: var(--dm-text-muted); }
|
|
66
|
+
|
|
67
|
+
/* Cards: a responsive grid. */
|
|
68
|
+
.notes-list.is-cards .notes-items {
|
|
69
|
+
display: grid; gap: .75rem;
|
|
70
|
+
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
|
71
|
+
}
|
|
72
|
+
.note-card {
|
|
73
|
+
position: relative; display: flex; flex-direction: column; gap: .4rem; min-width: 0;
|
|
74
|
+
padding: .75rem .85rem; cursor: pointer;
|
|
75
|
+
background: var(--dm-surface); border: 1px solid var(--dm-border); border-radius: var(--dm-radius-md);
|
|
76
|
+
transition: border-color var(--dm-transition-fast);
|
|
77
|
+
}
|
|
78
|
+
.note-card:hover, .note-card:focus-visible { border-color: var(--dm-primary); outline: none; }
|
|
79
|
+
.note-title { margin: 0; padding-right: 1.5rem; font-size: var(--dm-text-sm); font-weight: 600; overflow-wrap: anywhere; }
|
|
80
|
+
.note-kebab {
|
|
81
|
+
position: absolute; top: .45rem; right: .4rem;
|
|
82
|
+
border: 0; background: none; cursor: pointer; padding: 0 .3rem;
|
|
83
|
+
font-size: var(--dm-text-lg); line-height: 1.2; color: var(--dm-text-muted); border-radius: var(--dm-radius-sm);
|
|
84
|
+
}
|
|
85
|
+
.note-kebab:hover { color: var(--dm-primary); background: var(--dm-surface-hover, rgba(127,127,127,.15)); }
|
|
86
|
+
.note-preview { margin: 0; font-size: var(--dm-text-sm); color: var(--dm-text-muted); white-space: pre-line; overflow-wrap: anywhere; }
|
|
87
|
+
.note-preview:empty { display: none; }
|
|
88
|
+
.note-foot { display: flex; align-items: center; gap: .5rem; margin-top: auto; }
|
|
89
|
+
.note-cats { display: flex; flex-wrap: wrap; gap: .25rem; flex: 1; min-width: 0; }
|
|
90
|
+
.note-cat {
|
|
91
|
+
font-size: var(--dm-text-xs); padding: .05rem .45rem; border-radius: 999px; cursor: pointer;
|
|
92
|
+
background: var(--dm-surface-hover, rgba(127,127,127,.15)); color: var(--dm-text);
|
|
93
|
+
}
|
|
94
|
+
.note-cat:hover { background: var(--dm-primary); color: var(--dm-text-inverse, #fff); }
|
|
95
|
+
.note-time { font-size: var(--dm-text-xs); color: var(--dm-text-muted); white-space: nowrap; }
|
|
96
|
+
.notes-list.hide-cats .note-cats { visibility: hidden; }
|
|
97
|
+
|
|
98
|
+
/* List: one dense row per note. */
|
|
99
|
+
.notes-list.is-list .notes-items {
|
|
100
|
+
display: flex; flex-direction: column;
|
|
101
|
+
border: 1px solid var(--dm-border); border-radius: var(--dm-radius-md); overflow: hidden;
|
|
102
|
+
}
|
|
103
|
+
.notes-list.is-list .note-card {
|
|
104
|
+
flex-direction: row; align-items: center; gap: .75rem;
|
|
105
|
+
padding: .45rem .5rem .45rem .75rem; border: 0; border-radius: 0; border-bottom: 1px solid var(--dm-border);
|
|
106
|
+
}
|
|
107
|
+
.notes-list.is-list .note-card:last-child { border-bottom: 0; }
|
|
108
|
+
.notes-list.is-list .note-card:hover { background: var(--dm-surface-hover, rgba(127,127,127,.08)); }
|
|
109
|
+
.notes-list.is-list .note-title {
|
|
110
|
+
flex: 0 0 28%; padding: 0; font-size: var(--dm-text-sm); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
111
|
+
}
|
|
112
|
+
.notes-list.is-list .note-preview { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
113
|
+
.notes-list.is-list .note-preview:empty { display: block; }
|
|
114
|
+
.notes-list.is-list .note-foot { flex: 0 0 auto; margin: 0; }
|
|
115
|
+
.notes-list.is-list .note-cats { flex: 0 1 auto; flex-wrap: nowrap; }
|
|
116
|
+
.notes-list.is-list .note-kebab { position: static; }
|
|
117
|
+
@media (max-width: 640px) {
|
|
118
|
+
.notes-list.is-list .note-preview { display: none; }
|
|
119
|
+
.notes-list.is-list .note-title { flex: 1; }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* The editor and settings slideovers. */
|
|
123
|
+
.notes-panel { display: flex; flex-direction: column; gap: 1rem; padding: .25rem 0 .5rem; height: 100%; }
|
|
124
|
+
.notes-panel .form-label { display: flex; align-items: center; font-weight: 500; margin-bottom: .3rem; }
|
|
125
|
+
.notes-panel textarea { min-height: 260px; resize: vertical; font-family: inherit; line-height: 1.5; }
|
|
126
|
+
.notes-panel .notes-field-grow { flex: 1; display: flex; flex-direction: column; }
|
|
127
|
+
.notes-panel .notes-field-grow textarea { flex: 1; }
|
|
128
|
+
.notes-panel-meta { font-size: var(--dm-text-xs); color: var(--dm-text-muted); display: flex; justify-content: space-between; gap: .5rem; }
|
|
129
|
+
.notes-panel-actions { display: flex; align-items: center; gap: .5rem; padding-top: .75rem; border-top: 1px solid var(--dm-border); }
|
|
130
|
+
.notes-panel-actions .spacer { flex: 1; }
|
|
131
|
+
.notes-setting { display: flex; flex-direction: column; gap: .3rem; }
|
|
132
|
+
.notes-setting-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
|
|
133
|
+
.notes-setting small { color: var(--dm-text-muted); }
|
|
134
|
+
.notes-range { display: flex; align-items: center; gap: .6rem; }
|
|
135
|
+
.notes-range input { flex: 1; }
|
|
136
|
+
.notes-range output { min-width: 3.5rem; text-align: right; font-variant-numeric: tabular-nums; font-size: var(--dm-text-sm); }
|
|
137
|
+
</style>
|
|
@@ -1,16 +1,29 @@
|
|
|
1
1
|
<div class="view-header">
|
|
2
|
-
<h2><span data-icon="store"></span>
|
|
3
|
-
<p class="text-muted">
|
|
2
|
+
<h2><span data-icon="store"></span> Browse</h2>
|
|
3
|
+
<p class="text-muted">Plugins available to this site.</p>
|
|
4
4
|
</div>
|
|
5
5
|
|
|
6
|
+
<!--
|
|
7
|
+
Shown when this site is not managed. Telling an unmanaged customer to set
|
|
8
|
+
MANAGER_URL is telling them to configure something they will never have: a
|
|
9
|
+
standalone purchase arrives as a file, and Install from File is the whole
|
|
10
|
+
procedure. The managed path is mentioned second because it is not theirs to
|
|
11
|
+
set up.
|
|
12
|
+
-->
|
|
6
13
|
<div id="marketplace-unavailable" style="display:none">
|
|
7
14
|
<div class="card">
|
|
8
|
-
<div class="card-body
|
|
9
|
-
<p>
|
|
15
|
+
<div class="card-body">
|
|
16
|
+
<p><strong>This site installs plugins from a file.</strong></p>
|
|
17
|
+
<p class="text-muted">Buy a plugin and you are sent a single <code>.dcmsplugin</code> file. Use
|
|
18
|
+
<strong>Install from File</strong> above to install it - no connection to anything is needed, and the
|
|
19
|
+
same button takes an update later.</p>
|
|
20
|
+
<p class="text-muted" style="margin-bottom:0">Sites managed for you browse and install here instead, over their
|
|
21
|
+
manager connection.</p>
|
|
10
22
|
</div>
|
|
11
23
|
</div>
|
|
12
24
|
</div>
|
|
13
25
|
|
|
14
26
|
<div id="marketplace-available" style="display:none">
|
|
27
|
+
<p id="marketplace-site" class="text-muted" style="display:none;font-size:.85rem;margin:0 0 .75rem"></p>
|
|
15
28
|
<div id="marketplace-grid" class="card-grid"></div>
|
|
16
29
|
</div>
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
<div class="view-header d-flex justify-content-between align-items-center">
|
|
2
2
|
<div>
|
|
3
|
-
<h1><span data-icon="
|
|
4
|
-
<p class="text-muted" style="margin:0;font-size:.875rem">
|
|
3
|
+
<h1><span data-icon="store"></span> Marketplace</h1>
|
|
4
|
+
<p class="text-muted" style="margin:0;font-size:.875rem">Install plugins to add features. Once enabled, a plugin appears under Tools. Changes take effect after a server restart.</p>
|
|
5
5
|
</div>
|
|
6
|
-
<
|
|
7
|
-
<
|
|
8
|
-
|
|
6
|
+
<div style="display:flex;gap:.5rem;align-items:center;">
|
|
7
|
+
<button id="install-file-btn" class="btn btn-secondary" title="Install a .dcmsplugin file">
|
|
8
|
+
<span data-icon="upload"></span> Install from File
|
|
9
|
+
</button>
|
|
10
|
+
<button id="new-plugin-btn" class="btn btn-primary">
|
|
11
|
+
<span data-icon="plus"></span> New Plugin
|
|
12
|
+
</button>
|
|
13
|
+
</div>
|
|
14
|
+
<input type="file" id="install-file-input" accept=".dcmsplugin" hidden>
|
|
9
15
|
</div>
|
|
10
16
|
|
|
11
17
|
<div id="plugin-tabs" class="tabs">
|
|
12
18
|
<div class="tab-list">
|
|
13
|
-
<button class="tab-item active" data-tab="installed">Installed
|
|
14
|
-
<button class="tab-item" data-tab="marketplace">
|
|
19
|
+
<button class="tab-item active" data-tab="installed">Installed</button>
|
|
20
|
+
<button class="tab-item" data-tab="marketplace">Browse</button>
|
|
15
21
|
</div>
|
|
16
22
|
|
|
17
23
|
<div class="tab-panel active" data-panel="installed">
|
|
@@ -74,6 +74,44 @@
|
|
|
74
74
|
</div>
|
|
75
75
|
</div>
|
|
76
76
|
|
|
77
|
+
<div class="card mb-4">
|
|
78
|
+
<div class="card-header"><h2>Theme Switcher</h2></div>
|
|
79
|
+
<div class="card-body">
|
|
80
|
+
<div class="row">
|
|
81
|
+
<div class="col-12">
|
|
82
|
+
<label class="form-check-label">
|
|
83
|
+
<input id="theme-switcher-enabled" type="checkbox" class="form-check">
|
|
84
|
+
Show a theme switcher on the public site
|
|
85
|
+
</label>
|
|
86
|
+
<p class="form-hint text-muted" style="margin-top:.35rem;font-size:.8rem;">
|
|
87
|
+
A floating disc of theme dots that lets a visitor pick their own theme.
|
|
88
|
+
It offers every theme the stylesheet ships.</p>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
<div id="theme-switcher-fields" class="row mt-3" style="display:none;">
|
|
92
|
+
<div class="col-4">
|
|
93
|
+
<label class="form-label">Position</label>
|
|
94
|
+
<select id="theme-switcher-position" class="form-select">
|
|
95
|
+
<option value="right-center">Right, centred</option>
|
|
96
|
+
<option value="left-center">Left, centred</option>
|
|
97
|
+
<option value="bottom-right">Bottom right</option>
|
|
98
|
+
<option value="bottom-left">Bottom left</option>
|
|
99
|
+
<option value="top-right">Top right</option>
|
|
100
|
+
<option value="top-left">Top left</option>
|
|
101
|
+
</select>
|
|
102
|
+
</div>
|
|
103
|
+
<div class="col-4">
|
|
104
|
+
<label class="form-label">Offset X (px)</label>
|
|
105
|
+
<input id="theme-switcher-offset-x" type="number" class="form-input" min="0" max="400">
|
|
106
|
+
</div>
|
|
107
|
+
<div class="col-4">
|
|
108
|
+
<label class="form-label">Offset Y (px)</label>
|
|
109
|
+
<input id="theme-switcher-offset-y" type="number" class="form-input" min="0" max="400">
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
77
115
|
<div class="card">
|
|
78
116
|
<div class="card-header"><h2>Typography</h2></div>
|
|
79
117
|
<div class="card-body">
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<!-- No heading: the banner above (admin/js/lib/plugin-chrome.js) is the title.
|
|
2
|
+
Everything that changes is bound (M.applyBindings) - see admin/js/views/todo.js, which also
|
|
3
|
+
inserts the row template: the router renders this file through _.render, eating any {{…}}. -->
|
|
4
|
+
<div class="todo-tool">
|
|
5
|
+
|
|
6
|
+
<!-- Quick add: type and press Enter. -->
|
|
7
|
+
<form class="todo-add" data-on-submit="add">
|
|
8
|
+
<input type="text" class="form-input todo-add-text" placeholder="Add a task and press Enter…"
|
|
9
|
+
aria-label="New task" autocomplete="off" data-model="draft.value" data-focus="addFocus.value">
|
|
10
|
+
<select class="form-input todo-add-priority" aria-label="Priority" data-model="draftPriority.value">
|
|
11
|
+
<option value="highest">Highest</option>
|
|
12
|
+
<option value="high">High</option>
|
|
13
|
+
<option value="medium">Medium</option>
|
|
14
|
+
<option value="low">Low</option>
|
|
15
|
+
<option value="lowest">Lowest</option>
|
|
16
|
+
</select>
|
|
17
|
+
<input type="date" class="form-input todo-add-due" aria-label="Due date (optional)" title="Due date (optional)"
|
|
18
|
+
data-model="draftDue.value">
|
|
19
|
+
<button type="submit" class="btn btn-sm btn-primary" data-bind-disabled="cannotAdd.value">
|
|
20
|
+
<span data-icon="plus" data-icon-size="14"></span> Add
|
|
21
|
+
</button>
|
|
22
|
+
<span data-help-title="Adding tasks"
|
|
23
|
+
data-help="Type and press Enter. Priority and due date are optional - the priority starts at the default from settings (the cog above). Press N anywhere on this screen to jump here."></span>
|
|
24
|
+
</form>
|
|
25
|
+
|
|
26
|
+
<div class="todo-bar">
|
|
27
|
+
<label class="todo-search">
|
|
28
|
+
<span data-icon="search" data-icon-size="14"></span>
|
|
29
|
+
<input type="search" class="form-input" placeholder="Search tasks…" aria-label="Search tasks"
|
|
30
|
+
data-model="query.value" data-focus="searchFocus.value">
|
|
31
|
+
</label>
|
|
32
|
+
<select class="form-input todo-filter" aria-label="Show" data-options="filterOptions.value"
|
|
33
|
+
data-options-text="label" data-options-value="value" data-model="filter.value"></select>
|
|
34
|
+
<span data-help-title="Working with tasks"
|
|
35
|
+
data-help="Tick a task to complete it. Right-click a task (or its ⋮, or click its status or priority) to change status, priority or due date, edit, or delete it. Completed tasks always sit at the bottom. Press / to search."></span>
|
|
36
|
+
<span class="todo-count" data-bind-text="countText.value"></span>
|
|
37
|
+
<button type="button" class="btn btn-sm btn-ghost todo-clear" data-if="counts.value.completed"
|
|
38
|
+
data-on-click="clearCompleted" title="Delete every completed task">
|
|
39
|
+
<span data-icon="trash" data-icon-size="13"></span> Clear completed
|
|
40
|
+
</button>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<p class="todo-state" data-if="loading.value">Loading tasks…</p>
|
|
44
|
+
<p class="todo-state" data-if="isEmpty.value" data-bind-text="emptyText.value"></p>
|
|
45
|
+
|
|
46
|
+
<div class="todo-list">
|
|
47
|
+
<div class="todo-rows" data-each="rows.value key=id"></div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<style>
|
|
52
|
+
.todo-tool { display: flex; flex-direction: column; gap: .75rem; }
|
|
53
|
+
.todo-add, .todo-bar { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin: 0; }
|
|
54
|
+
.todo-add .form-input, .todo-bar .form-input { height: 2rem; }
|
|
55
|
+
.todo-add-text { flex: 1 1 240px; }
|
|
56
|
+
.todo-add-priority { width: 7.5rem; padding-top: 0; padding-bottom: 0; }
|
|
57
|
+
.todo-add-due { width: 9.5rem; }
|
|
58
|
+
.todo-search { position: relative; flex: 1 1 180px; max-width: 280px; margin: 0; }
|
|
59
|
+
.todo-search > svg, .todo-search > [data-icon] {
|
|
60
|
+
position: absolute; left: .6rem; top: 50%; transform: translateY(-50%); color: var(--dm-text-muted); pointer-events: none;
|
|
61
|
+
}
|
|
62
|
+
.todo-search .form-input { padding-left: 1.9rem; }
|
|
63
|
+
.todo-filter { width: auto; min-width: 10rem; padding-top: 0; padding-bottom: 0; }
|
|
64
|
+
.todo-count { font-size: var(--dm-text-xs); color: var(--dm-text-muted); white-space: nowrap; }
|
|
65
|
+
.todo-clear { margin-left: auto; color: var(--dm-text-muted); }
|
|
66
|
+
.todo-state { margin: 1.5rem 0; text-align: center; color: var(--dm-text-muted); }
|
|
67
|
+
|
|
68
|
+
.todo-rows { display: flex; flex-direction: column; border: 1px solid var(--dm-border); border-radius: var(--dm-radius-md); overflow: hidden; }
|
|
69
|
+
.todo-rows:empty { display: none; }
|
|
70
|
+
.todo-row {
|
|
71
|
+
display: flex; align-items: center; gap: .65rem; padding: .45rem .5rem .45rem .75rem;
|
|
72
|
+
border-bottom: 1px solid var(--dm-border);
|
|
73
|
+
}
|
|
74
|
+
.todo-row:last-child { border-bottom: 0; }
|
|
75
|
+
.todo-row:hover, .todo-row:focus-within { background: var(--dm-surface-hover, rgba(127,127,127,.08)); }
|
|
76
|
+
.todo-row .todo-check { flex: 0 0 auto; width: 1rem; height: 1rem; cursor: pointer; margin: 0; }
|
|
77
|
+
.todo-text { flex: 1; min-width: 0; font-size: var(--dm-text-sm); overflow-wrap: anywhere; cursor: pointer; }
|
|
78
|
+
.todo-row.is-done .todo-text { text-decoration: line-through; opacity: .5; }
|
|
79
|
+
.todo-due { flex: 0 0 auto; font-size: var(--dm-text-xs); padding: .05rem .45rem; border-radius: 999px; white-space: nowrap;
|
|
80
|
+
background: var(--dm-surface-hover, rgba(127,127,127,.15)); color: var(--dm-text-muted); }
|
|
81
|
+
.todo-due:empty { display: none; }
|
|
82
|
+
.todo-due.is-today { color: var(--dm-warning, #d97706); }
|
|
83
|
+
.todo-due.is-overdue { background: color-mix(in srgb, var(--dm-danger, #dc2626) 16%, transparent); color: var(--dm-danger, #dc2626); }
|
|
84
|
+
.todo-pill {
|
|
85
|
+
flex: 0 0 auto; border: 0; cursor: pointer; font: inherit; font-size: var(--dm-text-xs); font-weight: 500;
|
|
86
|
+
padding: .1rem .55rem; border-radius: 999px; white-space: nowrap;
|
|
87
|
+
}
|
|
88
|
+
.todo-kebab { flex: 0 0 auto; border: 0; background: none; cursor: pointer; font-size: var(--dm-text-lg); line-height: 1.2;
|
|
89
|
+
color: var(--dm-text-muted); border-radius: var(--dm-radius-sm); padding: 0 .3rem; }
|
|
90
|
+
.todo-kebab:hover { color: var(--dm-primary); background: var(--dm-surface-hover, rgba(127,127,127,.15)); }
|
|
91
|
+
@media (max-width: 640px) { .todo-pill[data-kind="status"] { display: none; } }
|
|
92
|
+
|
|
93
|
+
/* Pills take the theme's own tokens, so they read in dark mode too. */
|
|
94
|
+
.todo-pill[data-value="not-started"] { background: var(--dm-surface-hover, rgba(127,127,127,.15)); color: var(--dm-text-muted); }
|
|
95
|
+
.todo-pill[data-value="in-progress"] { background: color-mix(in srgb, var(--dm-primary) 18%, transparent); color: var(--dm-primary); }
|
|
96
|
+
.todo-pill[data-value="completed"] { background: color-mix(in srgb, var(--dm-success, #16a34a) 18%, transparent); color: var(--dm-success, #16a34a); }
|
|
97
|
+
.todo-pill[data-value="highest"] { background: color-mix(in srgb, var(--dm-danger, #dc2626) 20%, transparent); color: var(--dm-danger, #dc2626); }
|
|
98
|
+
.todo-pill[data-value="high"] { background: color-mix(in srgb, #ea580c 18%, transparent); color: #ea580c; }
|
|
99
|
+
.todo-pill[data-value="medium"] { background: color-mix(in srgb, var(--dm-warning, #d97706) 18%, transparent); color: var(--dm-warning, #d97706); }
|
|
100
|
+
.todo-pill[data-value="low"] { background: color-mix(in srgb, var(--dm-primary) 14%, transparent); color: var(--dm-primary); }
|
|
101
|
+
.todo-pill[data-value="lowest"] { background: var(--dm-surface-hover, rgba(127,127,127,.15)); color: var(--dm-text-muted); }
|
|
102
|
+
|
|
103
|
+
/* Editor and settings slideovers */
|
|
104
|
+
.todo-panel { display: flex; flex-direction: column; gap: 1rem; padding: .25rem 0 .5rem; }
|
|
105
|
+
.todo-panel .form-label { display: flex; align-items: center; font-weight: 500; margin-bottom: .3rem; }
|
|
106
|
+
.todo-panel textarea { min-height: 90px; resize: vertical; font-family: inherit; }
|
|
107
|
+
.todo-panel-row { display: grid; grid-template-columns: 1fr 1fr; gap: .75rem; }
|
|
108
|
+
.todo-setting-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
|
|
109
|
+
.todo-panel-meta { font-size: var(--dm-text-xs); color: var(--dm-text-muted); }
|
|
110
|
+
.todo-panel-actions { display: flex; align-items: center; gap: .5rem; padding-top: .75rem; border-top: 1px solid var(--dm-border); }
|
|
111
|
+
.todo-panel-actions .spacer { flex: 1; }
|
|
112
|
+
.todo-quick-due { display: flex; gap: .35rem; flex-wrap: wrap; margin-top: .35rem; }
|
|
113
|
+
</style>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import{apiRequest as v,getToken as Z,refreshAccessToken as Q}from"../api.js";import{addBannerAction as X}from"../lib/plugin-chrome.js";import{helpPopovers as V}from"../lib/help-popover.js";import{onceClosed as ee,openMenuAt as te,settingsStore as ae,sidebarCount as se,viewLifetime as ne}from"../lib/tool-kit.js";import{DEFAULT_SETTINGS as oe,journeyRows as le,kpis as ie,LIMITS as re,normaliseSettings as O,RANGE_LABEL as B,RANGES as ce,seriesRange as de,topRows as pe}from"../lib/analytics-shape.js";const ue="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js",me=3e4,q=ae("analytics.settings",O),ve=`
|
|
2
|
+
<div class="an-row" data-url="{{url}}">
|
|
3
|
+
<span class="an-bar-fill" style="width: {{bar}}"></span>
|
|
4
|
+
<span class="an-rank">{{rank}}</span>
|
|
5
|
+
<span class="an-url" title="{{url}}">{{url}}</span>
|
|
6
|
+
<span class="an-num">{{hitsText}}</span>
|
|
7
|
+
<span class="an-share">{{share}}</span>
|
|
8
|
+
<button type="button" class="an-kebab" aria-label="Page actions" title="Actions">\u22EE</button>
|
|
9
|
+
</div>`,A=`
|
|
10
|
+
<div class="an-row" data-url="{{url}}">
|
|
11
|
+
<span class="an-bar-fill" style="width: {{bar}}"></span>
|
|
12
|
+
<span class="an-url" title="{{label}}">{{label}}</span>
|
|
13
|
+
<span class="an-num">{{count}}</span>
|
|
14
|
+
<button type="button" class="an-kebab" aria-label="Page actions" title="Actions">\u22EE</button>
|
|
15
|
+
</div>`,be=`
|
|
16
|
+
<div class="an-row" data-url="{{url}}">
|
|
17
|
+
<span class="an-url" title="{{url}}">{{url}}</span>
|
|
18
|
+
<span class="an-num" title="Views per hour today, against the usual {{baseline}}">{{hits}}/h</span>
|
|
19
|
+
<span class="an-share">\xD7{{ratio}}</span>
|
|
20
|
+
<button type="button" class="an-kebab" aria-label="Page actions" title="Actions">\u22EE</button>
|
|
21
|
+
</div>`;function he(){return window.Chart?Promise.resolve():new Promise((r,s)=>{const t=document.createElement("script");t.src=ue,t.async=!0,t.onload=()=>r(),t.onerror=()=>s(new Error("chart.js load failed")),document.head.appendChild(t)})}function fe(r){const s=document.createElement("template");return s.innerHTML=r.trim(),s.content.firstElementChild}async function ye(r){const s=b=>fetch("/api/analytics/export.csv?range="+encodeURIComponent(r),{headers:b?{Authorization:"Bearer "+b}:{}});let t=await s(Z());if(t.status===401&&(t=await s(await Q())),!t.ok)throw new Error("export failed");const c=await t.blob(),d=(t.headers.get("Content-Disposition")||"").match(/filename="?([^"]+)"?/),u=URL.createObjectURL(c),i=document.createElement("a");i.href=u,i.download=d?d[1]:"analytics.csv",document.body.appendChild(i),i.click(),i.remove(),URL.revokeObjectURL(u)}const l=ne(/^#\/analytics\b/);export const analyticsView={templateUrl:"/admin/js/templates/analytics.html",async onMount(r){l.reset();const s=r.get(0),t=M.observable(q.read()),c=M.observable(t.peek().range),d=M.observable("pages"),u=M.observable(""),i=M.observable(null),b=M.observable([]),y=M.observable(null),S=M.observable([]),g=M.observable(null),x=M.observable([]),j=M.observable("");l.add(M.effect(()=>{const e=i.value,{rows:a,matched:n}=pe(e?.top,{filter:u.value,limit:t.value.limit,total:e?.total});x.value=a,j.value=e?e.top.length?n?n>a.length?`Showing ${a.length} of ${n} pages - the cog changes how many.`:"":"No pages match that filter.":"No page views recorded in this range yet.":"Loading\u2026"}));const P=M.observable([]),U=M.observable([]),$=M.observable([]);l.add(M.effect(()=>{const e=le(y.value);P.value=e.entry,U.value=e.exit,$.value=e.paths}));const G={range:c,section:d,filter:u,spikes:S,pageRows:x,pagesNote:j,entryRows:P,exitRows:U,pathRows:$,kpi:M.computed(()=>ie(i.value,y.value)),isAllTime:M.computed(()=>c.value==="all"),sessionsCount:M.computed(()=>y.value?.totalSessions??0),liveText:M.computed(()=>g.value===null?"Active now: -":`${g.value} active now`),liveClass:M.computed(()=>g.value?"is-on":""),liveTitle:M.computed(()=>t.value.live?"Refreshes every 30 seconds":"Live refresh is off - see settings")};l.add(M.effect(()=>q.write(t.value)));const W=(e,a)=>{t.value=O({...t.peek(),[e]:a})};let k=0;async function w(){const e=c.peek(),a=++k;try{const[n,p,m,f]=await Promise.all([v("/analytics/stats?range="+encodeURIComponent(e)),v("/analytics/series?range="+encodeURIComponent(de(e))),v("/analytics/journeys?range="+encodeURIComponent(e)),v("/analytics/spikes")]);if(a!==k)return;i.value=n,b.value=p?.series??[],y.value=m,S.value=(f?.items??[]).map(o=>({...o,key:o.url}))}catch{a===k&&E.toast("Could not load analytics.",{type:"error"})}}async function C(){try{g.value=(await v("/analytics/realtime"))?.activeSessions??0,se("#/analytics")}catch{}}let _=!0;l.add(M.effect(()=>{if(c.value,_){_=!1;return}w()}));let L=null;l.add(M.effect(()=>{clearInterval(L),L=t.value.live?setInterval(()=>{document.hidden||C()},me):null})),l.add(()=>clearInterval(L));const H=s.querySelector(".an-chart");let R=null;l.add(()=>{R?.destroy(),R=null}),l.add(M.effect(()=>{const e=b.value,a=t.value.chart;if(!window.Chart||!H.isConnected)return;const n=getComputedStyle(document.documentElement),p=n.getPropertyValue("--dm-primary").trim()||"rgb(99, 102, 241)",m=n.getPropertyValue("--dm-text-muted").trim()||"#888",f=`color-mix(in srgb, ${n.getPropertyValue("--dm-border").trim()||"#888"} 45%, transparent)`;R?.destroy(),R=new window.Chart(H,{type:a,data:{labels:e.map(o=>D(o.date).format("D MMM")),datasets:[{label:"Page views",data:e.map(o=>o.hits),borderColor:p,backgroundColor:a==="bar"?p:`color-mix(in srgb, ${p} 18%, transparent)`,fill:a==="line",tension:.3,borderWidth:2,pointRadius:e.length>40?0:2,borderRadius:3}]},options:{responsive:!0,maintainAspectRatio:!1,animation:!1,plugins:{legend:{display:!1}},scales:{y:{beginAtZero:!0,ticks:{precision:0,color:m},grid:{color:f}},x:{ticks:{maxRotation:0,autoSkip:!0,maxTicksLimit:10,color:m},grid:{display:!1}}}}})}));function J(){const e=E.slideover({title:"Analytics settings",size:"sm",position:"right"}),a=fe(`
|
|
22
|
+
<div class="an-panel-form">
|
|
23
|
+
<div>
|
|
24
|
+
<label class="form-label" for="an-set-range">Open on</label>
|
|
25
|
+
<select id="an-set-range" class="form-input" data-model="range.value">
|
|
26
|
+
${ce.map(o=>`<option value="${o}">${B[o]}</option>`).join("")}
|
|
27
|
+
</select>
|
|
28
|
+
</div>
|
|
29
|
+
<div>
|
|
30
|
+
<label class="form-label" for="an-set-limit">Top pages shown
|
|
31
|
+
<span data-help="How many pages the Top pages list shows. The filter still searches all of them, and the CSV export always has every page."></span>
|
|
32
|
+
</label>
|
|
33
|
+
<select id="an-set-limit" class="form-input" data-model="limit.value">
|
|
34
|
+
${re.map(o=>`<option value="${o}">${o||"All"}</option>`).join("")}
|
|
35
|
+
</select>
|
|
36
|
+
</div>
|
|
37
|
+
<div>
|
|
38
|
+
<label class="form-label" for="an-set-chart">Chart</label>
|
|
39
|
+
<select id="an-set-chart" class="form-input" data-model="chart.value">
|
|
40
|
+
<option value="line">Line</option>
|
|
41
|
+
<option value="bar">Bars</option>
|
|
42
|
+
</select>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="an-setting-row">
|
|
45
|
+
<label class="form-label" for="an-set-live" style="margin:0">Live "active now"
|
|
46
|
+
<span data-help="Refresh the active-visitor count every 30 seconds while this screen is open and visible."></span>
|
|
47
|
+
</label>
|
|
48
|
+
<input id="an-set-live" type="checkbox" class="form-switch" data-model="live.value">
|
|
49
|
+
</div>
|
|
50
|
+
<div class="an-setting-row">
|
|
51
|
+
<label class="form-label" for="an-set-confirm" style="margin:0">Confirm before resetting
|
|
52
|
+
<span data-help="Reset deletes every counter this site has. Leave this on."></span>
|
|
53
|
+
</label>
|
|
54
|
+
<input id="an-set-confirm" type="checkbox" class="form-switch" data-model="confirmReset.value">
|
|
55
|
+
</div>
|
|
56
|
+
<small class="an-panel-meta">Saved in this browser as you change them.</small>
|
|
57
|
+
<div class="an-panel-actions">
|
|
58
|
+
<button type="button" class="btn btn-sm btn-ghost" data-on-click="reset">Reset to defaults</button>
|
|
59
|
+
<span class="spacer"></span>
|
|
60
|
+
<button type="button" class="btn btn-sm btn-primary" data-on-click="done">Done</button>
|
|
61
|
+
</div>
|
|
62
|
+
</div>`),n=(o,K=T=>T)=>M.computed({read:()=>t.value[o],write:T=>W(o,K(T))}),p={range:n("range"),limit:n("limit",Number),chart:n("chart"),live:n("live"),confirmReset:n("confirmReset")};e.element.appendChild(a);const m=V(a),f=M.applyBindings(p,a,{methods:{done:()=>e.close(),reset:()=>{t.value={...oe}}}});ee(e,()=>{f.dispose(),m()}),e.open()}X(r,{id:"analytics-settings",icon:"settings",title:"Analytics settings",onClick:J});const h=[...s.querySelectorAll(".an-list")];h[0].innerHTML=ve,h[1].innerHTML=A,h[2].innerHTML=A,h[3].innerHTML=A,h[4].innerHTML=be,l.add(V(s,{position:"bottom"}));const z=M.applyBindings(G,s,{methods:{refresh:()=>{w(),C()},showPages:()=>{d.value="pages"},showJourneys:()=>{d.value="journeys"},showSpikes:()=>{d.value="spikes"}}});l.add(()=>z.dispose());const N=e=>new URL(e,location.origin).href,Y=E.contextMenu(s,{match:".an-row",inherit:!1,density:"compact",items:e=>{const a=e.dataset.url;return[{label:"Open on the site",icon:"external-link",action:()=>window.open(N(a),"_blank","noopener")},{label:"Copy address",icon:"copy",action:async()=>{try{await navigator.clipboard.writeText(N(a)),E.toast("Address copied",{type:"success"})}catch{E.toast("The browser would not allow copying",{type:"error"})}}},{label:"Show only this page",icon:"filter",action:()=>{d.value="pages",u.value=a}}]}});l.add(()=>Y?.destroy?.());const F=E.contextMenu(s.querySelector(".an-bar-end"),{match:".an-more",inherit:!1,density:"compact",items:()=>[{label:`Export ${B[c.peek()].toLowerCase()} as CSV`,icon:"download",action:async()=>{try{await ye(c.peek())}catch{E.toast("Export failed.",{type:"error"})}}},{type:"divider"},{label:"Reset all statistics\u2026",icon:"trash",danger:!0,action:async()=>{if(!(t.peek().confirmReset&&!await E.confirm("Delete every analytics counter for this site? This cannot be undone.")))try{await v("/analytics/stats",{method:"DELETE"}),E.toast("Analytics reset.",{type:"success"}),w()}catch{E.toast("Reset failed.",{type:"error"})}}}]});l.add(()=>F?.destroy?.()),s.addEventListener("click",e=>{const a=e.target.closest(".an-kebab, .an-more");a&&te(a)}),I.scan(s);try{await he()}catch{E.toast("The chart library could not be loaded; figures still work.",{type:"warning"})}await Promise.all([w(),C()])}};
|