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.
Files changed (88) hide show
  1. package/CLAUDE.md +1 -1
  2. package/admin/js/api.js +1 -1
  3. package/admin/js/app.js +2 -2
  4. package/admin/js/lib/action-editor-arrange.js +1 -0
  5. package/admin/js/lib/api-endpoint-editor-arrange.js +2 -0
  6. package/admin/js/lib/block-editor-arrange.js +2 -0
  7. package/admin/js/lib/collection-editor-arrange.js +3 -0
  8. package/admin/js/lib/collection-entries-arrange.js +1 -0
  9. package/admin/js/lib/component-editor-arrange.js +27 -0
  10. package/admin/js/lib/context-menu-editor-arrange.js +1 -0
  11. package/admin/js/lib/dashboard-arrange.js +1 -0
  12. package/admin/js/lib/form-editor-arrange.js +1 -0
  13. package/admin/js/lib/form-submissions-arrange.js +2 -0
  14. package/admin/js/lib/menu-editor-arrange.js +2 -0
  15. package/admin/js/lib/page-editor-arrange.js +1 -0
  16. package/admin/js/lib/plugin-code-arrange.js +1 -0
  17. package/admin/js/lib/project-page-arrange.js +1 -0
  18. package/admin/js/lib/role-editor-arrange.js +3 -0
  19. package/admin/js/lib/user-editor-arrange.js +1 -0
  20. package/admin/js/lib/view-editor-arrange.js +14 -0
  21. package/admin/js/templates/action-editor.html +223 -147
  22. package/admin/js/templates/api-endpoint-editor.html +172 -117
  23. package/admin/js/templates/block-editor.html +194 -203
  24. package/admin/js/templates/collection-editor.html +191 -113
  25. package/admin/js/templates/collection-entries.html +99 -36
  26. package/admin/js/templates/component-editor.html +128 -126
  27. package/admin/js/templates/context-menu-editor.html +208 -148
  28. package/admin/js/templates/dashboard.html +184 -22
  29. package/admin/js/templates/form-editor.html +41 -26
  30. package/admin/js/templates/form-submissions.html +78 -26
  31. package/admin/js/templates/menu-editor.html +57 -17
  32. package/admin/js/templates/menu-locations.html +54 -11
  33. package/admin/js/templates/page-editor.html +47 -26
  34. package/admin/js/templates/plugin-code.html +91 -73
  35. package/admin/js/templates/project-detail.html +83 -145
  36. package/admin/js/templates/project-settings.html +88 -53
  37. package/admin/js/templates/role-editor.html +158 -83
  38. package/admin/js/templates/user-editor.html +150 -14
  39. package/admin/js/templates/view-editor.html +203 -180
  40. package/admin/js/templates/view-preview.html +45 -45
  41. package/admin/js/views/action-editor.js +7 -1
  42. package/admin/js/views/api-endpoint-editor.js +2 -3
  43. package/admin/js/views/block-editor.js +14 -8
  44. package/admin/js/views/collection-editor.js +64 -5
  45. package/admin/js/views/collection-entries.js +50 -1
  46. package/admin/js/views/component-editor.js +2 -28
  47. package/admin/js/views/context-menu-editor.js +54 -55
  48. package/admin/js/views/dashboard.js +7 -1
  49. package/admin/js/views/form-editor.js +7 -7
  50. package/admin/js/views/form-submissions.js +11 -1
  51. package/admin/js/views/index.js +1 -1
  52. package/admin/js/views/login.js +2 -2
  53. package/admin/js/views/menu-editor.js +21 -43
  54. package/admin/js/views/menu-locations.js +16 -1
  55. package/admin/js/views/page-editor.js +26 -26
  56. package/admin/js/views/plugin-code.js +23 -4
  57. package/admin/js/views/project-detail.js +9 -1
  58. package/admin/js/views/project-settings.js +1 -1
  59. package/admin/js/views/role-editor.js +11 -3
  60. package/admin/js/views/user-editor.js +1 -1
  61. package/admin/js/views/view-editor.js +1 -1
  62. package/admin/js/views/view-preview.js +1 -1
  63. package/package.json +1 -1
  64. package/server/routes/api/dashboard.js +32 -4
  65. package/server/routes/api/forms.js +104 -13
  66. package/server/routes/api/users.js +12 -2
  67. package/server/routes/api/views.js +55 -1
  68. package/server/services/components.js +9 -0
  69. package/server/services/users.js +2 -2
  70. package/server/services/views.js +14 -0
  71. package/admin/js/templates/dashboard/activity-feed.html +0 -3
  72. package/admin/js/templates/dashboard/cache.html +0 -32
  73. package/admin/js/templates/dashboard/health-detail.html +0 -2
  74. package/admin/js/templates/dashboard/journeys.html +0 -17
  75. package/admin/js/templates/dashboard/kpi-strip.html +0 -34
  76. package/admin/js/templates/dashboard/spike-feed.html +0 -3
  77. package/admin/js/templates/dashboard/top-pages.html +0 -3
  78. package/admin/js/templates/dashboard/traffic-chart.html +0 -3
  79. package/admin/js/views/block-editor-enhance.js +0 -1
  80. package/admin/js/views/dashboard/lib/escape.js +0 -1
  81. package/admin/js/views/dashboard/widgets/activity-feed.js +0 -1
  82. package/admin/js/views/dashboard/widgets/cache.js +0 -1
  83. package/admin/js/views/dashboard/widgets/health-detail.js +0 -1
  84. package/admin/js/views/dashboard/widgets/journeys.js +0 -1
  85. package/admin/js/views/dashboard/widgets/kpi-strip.js +0 -1
  86. package/admin/js/views/dashboard/widgets/spike-feed.js +0 -6
  87. package/admin/js/views/dashboard/widgets/top-pages.js +0 -1
  88. package/admin/js/views/dashboard/widgets/traffic-chart.js +0 -1
@@ -1,152 +1,90 @@
1
- <style>
2
- .pd-wrap { display: flex; flex-direction: column; gap: 1.5rem; }
3
-
4
- /* ── Hero ─────────────────────────────────────────────────────────── */
5
- .pd-hero {
6
- display: flex;
7
- gap: 1.25rem;
8
- align-items: flex-start;
9
- padding: 1.5rem;
10
- border: 1px solid var(--dm-border, rgba(255,255,255,.08));
11
- border-radius: 14px;
12
- background:
13
- radial-gradient(120% 160% at 0% 0%, color-mix(in srgb, var(--dm-primary) 12%, transparent), transparent 55%),
14
- var(--dm-card-bg, rgba(255,255,255,.04));
15
- position: relative;
16
- overflow: hidden;
17
- }
18
- .pd-hero::before {
19
- content: '';
20
- position: absolute;
21
- top: 0; bottom: 0; left: 0;
22
- width: 4px;
23
- background: linear-gradient(180deg, var(--dm-primary), var(--dm-primary-dark));
24
- }
25
- .pd-hero-icon {
26
- width: 64px; height: 64px;
27
- flex-shrink: 0;
28
- border-radius: 16px;
29
- display: flex; align-items: center; justify-content: center;
30
- background: linear-gradient(135deg, color-mix(in srgb, var(--dm-primary) 28%, transparent), color-mix(in srgb, var(--dm-primary-dark) 28%, transparent));
31
- color: var(--dm-primary);
32
- box-shadow: 0 6px 18px color-mix(in srgb, var(--dm-primary) 18%, transparent);
33
- }
34
- .pd-hero-icon svg, .pd-hero-icon span[data-icon] { width: 32px; height: 32px; }
35
- .pd-hero-main { flex: 1; min-width: 0; }
36
- .pd-hero-top {
37
- display: flex; align-items: flex-start; justify-content: space-between;
38
- gap: 1rem; flex-wrap: wrap;
39
- }
40
- .pd-title { font-size: 1.6rem; font-weight: 700; margin: 0; line-height: 1.15; }
41
- .pd-actions { display: flex; gap: .5rem; flex-shrink: 0; }
42
- .pd-desc { color: var(--dm-text-muted, #888); margin: .5rem 0 0; max-width: 64ch; }
43
- .pd-desc:empty { display: none; }
44
-
45
- /* ── Meta badges ──────────────────────────────────────────────────── */
46
- .pd-meta { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: 1.1rem; }
47
- .pd-meta-badge {
48
- display: inline-flex; align-items: center; gap: .45rem;
49
- padding: .4rem .7rem;
50
- border-radius: 999px;
51
- font-size: .8rem; line-height: 1;
52
- border: 1px solid transparent;
53
- }
54
- .pd-meta-badge svg, .pd-meta-badge span[data-icon] { width: 14px; height: 14px; }
55
- .pd-meta-k { color: var(--dm-text-muted, #999); font-weight: 500; }
56
- .pd-meta-v { font-weight: 600; }
57
-
58
- /* ── Section header ───────────────────────────────────────────────── */
59
- .pd-section-head {
60
- display: flex; align-items: baseline; gap: .6rem;
61
- margin: 0 0 .35rem;
62
- }
63
- .pd-section-title {
64
- font-size: 1rem; font-weight: 600; margin: 0;
65
- display: flex; align-items: center; gap: .5rem;
66
- }
67
- .pd-section-title svg, .pd-section-title span[data-icon] {
68
- width: 17px; height: 17px; color: var(--dm-text-muted, #888);
69
- }
70
- .pd-total { font-size: .8rem; color: var(--dm-text-muted, #888); }
71
- .pd-hint { color: var(--dm-text-muted, #888); margin: 0 0 .9rem; font-size: .82rem; }
72
-
73
- /* ── Artefact stat tiles (each with a + to create one) ────────────── */
74
- .pd-tiles {
75
- display: grid;
76
- grid-template-columns: repeat(auto-fill, minmax(176px, 1fr));
77
- gap: .85rem;
78
- }
79
- .pd-tile {
80
- position: relative;
81
- border: 1px solid var(--dm-border, rgba(255,255,255,.08));
82
- border-left: 3px solid var(--c, #5b8cff);
83
- border-radius: 11px;
84
- background: var(--dm-card-bg, rgba(255,255,255,.04));
85
- transition: transform .14s ease, box-shadow .14s ease, border-color .14s ease;
86
- }
87
- .pd-tile--link:hover {
88
- transform: translateY(-2px);
89
- box-shadow: var(--dm-shadow-lg);
90
- border-color: var(--c, #5b8cff);
91
- }
92
- .pd-tile-link {
93
- display: flex; align-items: center; gap: .85rem;
94
- padding: .9rem 1rem; padding-right: 2.6rem; /* room for the + */
95
- text-decoration: none; color: inherit;
96
- }
97
- .pd-tile-link--empty { opacity: .55; }
98
- .pd-tile-chip {
99
- width: 40px; height: 40px; flex-shrink: 0;
100
- border-radius: 10px;
101
- display: flex; align-items: center; justify-content: center;
102
- }
103
- .pd-tile-chip svg, .pd-tile-chip span[data-icon] { width: 20px; height: 20px; }
104
- .pd-tile-body { display: flex; flex-direction: column; min-width: 0; }
105
- .pd-tile-count { font-size: 1.5rem; font-weight: 700; line-height: 1; }
106
- .pd-tile-label {
107
- font-size: .78rem; color: var(--dm-text-muted, #888);
108
- margin-top: .2rem; text-transform: uppercase; letter-spacing: .03em;
109
- }
110
- .pd-tile-add {
111
- position: absolute; top: 8px; right: 8px;
112
- width: 26px; height: 26px; border-radius: 7px;
113
- display: flex; align-items: center; justify-content: center;
114
- border: 1px solid var(--dm-border, rgba(255,255,255,.12));
115
- background: var(--dm-card-bg, rgba(255,255,255,.05));
116
- color: var(--c, #5b8cff);
117
- cursor: pointer; opacity: .7;
118
- transition: opacity .14s ease, background .14s ease, border-color .14s ease, transform .14s ease;
119
- }
120
- .pd-tile-add:hover {
121
- opacity: 1; transform: scale(1.08);
122
- border-color: var(--c, #5b8cff);
123
- background: var(--dm-hover-bg);
124
- }
125
- .pd-tile-add svg, .pd-tile-add span[data-icon] { width: 15px; height: 15px; }
126
- </style>
1
+ <!-- A project's overview. Bound with M.applyBindings - see admin/js/views/project-detail.js, which also
2
+ inserts the tile template (the router renders a templateUrl through _.render, eating any {{…}}).
3
+ No page heading: the Tool banner is the heading ("Projects › <name>"), with Settings in it. -->
4
+ <div class="pdx">
5
+ <p class="pdx-alert" data-bind-hidden="!off.value">
6
+ <span data-icon="alert-triangle" data-icon-size="16"></span>
7
+ <span>This project is <strong>disabled</strong>: its public pages 404 and its external API does not answer. Editing it here carries on.</span>
8
+ <button type="button" class="btn btn-sm btn-primary" data-on-click="enable" data-bind-disabled="busy.value">Enable</button>
9
+ </p>
10
+ <p class="pdx-alert is-bad" data-bind-hidden="!loadError.value">
11
+ <span data-bind-text="loadError.value"></span>
12
+ <button type="button" class="btn btn-sm btn-ghost" data-on-click="retry">Try again</button>
13
+ </p>
127
14
 
128
- <div class="pd-wrap">
129
- <div class="pd-hero">
130
- <div class="pd-hero-icon"><span id="pd-icon" data-icon="folder"></span></div>
131
- <div class="pd-hero-main">
132
- <div class="pd-hero-top">
133
- <h2 class="pd-title"><span id="pd-name">Project</span></h2>
134
- <div class="pd-actions">
135
- <a class="btn btn-secondary btn-sm" href="#/projects"><span data-icon="arrow-left"></span> Projects</a>
136
- <a class="btn btn-secondary btn-sm" id="pd-settings"><span data-icon="settings"></span> Settings</a>
137
- </div>
15
+ <div class="pdx-hero">
16
+ <div class="pdx-hero-icon" aria-hidden="true"><span data-icon="folder" data-icon-size="32"></span></div>
17
+ <div class="pdx-hero-main">
18
+ <strong class="pdx-name" data-bind-text="name.value"></strong>
19
+ <p class="pdx-desc" data-bind-hidden="!description.value" data-bind-text="description.value"></p>
20
+ <div class="pdx-chips">
21
+ <span class="pdx-chip is-warn" data-bind-hidden="!off.value">Disabled</span>
22
+ <span class="pdx-chip is-ok" data-bind-hidden="off.value || loading.value">Enabled</span>
23
+ <span class="pdx-chip" data-bind-hidden="!builtIn.value" title="Built in - cannot be deleted">Built in</span>
24
+ <span class="pdx-chip" data-bind-text="rootText.value"></span>
25
+ <span data-help-title="Root URL" data-help="Pages whose address starts with the project's root URL are filed under it without being tagged one by one. Change it, or the name, icon and order, on the project's Settings (banner)."></span>
26
+ </div>
27
+ <div class="pdx-meta">
28
+ <span data-bind-hidden="!created.value"><span data-icon="calendar" data-icon-size="13"></span> Created <b data-bind-text="created.value"></b></span>
29
+ <span data-bind-hidden="!creator.value"><span data-icon="user" data-icon-size="13"></span> by <b data-bind-text="creator.value"></b></span>
30
+ <span data-bind-hidden="!updated.value" data-bind-title="updatedTitle.value"><span data-icon="clock" data-icon-size="13"></span> Changed <b data-bind-text="updated.value"></b></span>
138
31
  </div>
139
- <p id="pd-desc" class="pd-desc"></p>
140
- <div id="pd-meta" class="pd-meta"></div>
141
32
  </div>
142
33
  </div>
143
34
 
144
- <section>
145
- <div class="pd-section-head">
146
- <h3 class="pd-section-title"><span data-icon="layers"></span> Artefacts in this project</h3>
147
- <span id="pd-total" class="pd-total"></span>
35
+ <div class="pdx-card">
36
+ <div class="pdx-card-title">
37
+ <span data-icon="layers" data-icon-size="16"></span> Artefacts in this project
38
+ <small class="pdx-muted" data-bind-text="totalText.value"></small>
39
+ <span data-help-title="Artefacts" data-help="Everything filed under this project, by type. Click a type to open its list (or, when there are none, to make the first). The + makes a new one filed under this project - pages, collections, menus and blocks in a quick panel here, the rest in their own editor. Right-click a type (or ⋮, or Shift+F10) for both."></span>
148
40
  </div>
149
- <p class="pd-hint">Click the <strong>+</strong> on any card to create one, filed under this project automatically.</p>
150
- <div id="pd-counts"></div>
151
- </section>
41
+ <p class="pdx-note" data-bind-hidden="!loading.value">Loading…</p>
42
+ <div class="pdx-tiles" data-each="tiles.value key=key"></div>
43
+ </div>
152
44
  </div>
45
+
46
+ <style>
47
+ .pdx { display: flex; flex-direction: column; gap: .9rem; }
48
+ .pdx [hidden] { display: none !important; }
49
+ .pdx-alert { margin: 0; display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; padding: .6rem .8rem; border-radius: var(--dm-radius-md);
50
+ background: color-mix(in srgb, var(--dm-warning, var(--dm-primary)) 16%, transparent); color: var(--dm-text); }
51
+ .pdx-alert.is-bad { background: color-mix(in srgb, var(--dm-danger) 14%, transparent); }
52
+ .pdx-alert > span:not([data-icon]) { flex: 1; min-width: 12rem; }
53
+ .pdx-hero { display: flex; gap: 1.1rem; align-items: flex-start; padding: 1.1rem 1.25rem; border: 1px solid var(--dm-border);
54
+ border-left: 4px solid var(--dm-primary); border-radius: var(--dm-radius-md);
55
+ background: radial-gradient(120% 160% at 0% 0%, color-mix(in srgb, var(--dm-primary) 10%, transparent), transparent 55%), var(--dm-surface); }
56
+ .pdx-hero-icon { width: 56px; height: 56px; flex-shrink: 0; border-radius: var(--dm-radius-md); display: flex; align-items: center; justify-content: center;
57
+ color: var(--dm-primary); background: color-mix(in srgb, var(--dm-primary) 16%, transparent); }
58
+ .pdx-hero-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .4rem; }
59
+ .pdx-name { font-size: var(--dm-text-xl, 1.4rem); line-height: 1.2; }
60
+ .pdx-desc { margin: 0; color: var(--dm-text-muted); max-width: 64ch; }
61
+ .pdx-chips { display: flex; flex-wrap: wrap; gap: .35rem; align-items: center; }
62
+ .pdx-chip { display: inline-flex; align-items: center; font-size: var(--dm-text-xs); font-weight: 600; padding: .1rem .55rem; border-radius: 999px; white-space: nowrap;
63
+ background: color-mix(in srgb, var(--dm-text) 10%, transparent); color: var(--dm-text-muted); }
64
+ .pdx-chip.is-ok { background: color-mix(in srgb, var(--dm-success) 16%, transparent); color: var(--dm-success); }
65
+ .pdx-chip.is-warn { background: color-mix(in srgb, var(--dm-warning, var(--dm-danger)) 18%, transparent); color: var(--dm-warning, var(--dm-danger)); }
66
+ .pdx-meta { display: flex; flex-wrap: wrap; gap: .9rem; color: var(--dm-text-muted); font-size: var(--dm-text-sm); }
67
+ .pdx-meta > span { display: inline-flex; align-items: center; gap: .3rem; }
68
+ .pdx-meta b { color: var(--dm-text); font-weight: 600; }
69
+ .pdx-card { border: 1px solid var(--dm-border); border-radius: var(--dm-radius-md); padding: .8rem .95rem; display: flex; flex-direction: column; gap: .7rem; }
70
+ .pdx-card-title { display: flex; align-items: center; gap: .45rem; font-weight: 600; }
71
+ .pdx-muted { color: var(--dm-text-muted); font-weight: 400; font-size: var(--dm-text-sm); }
72
+ .pdx-note { margin: 0; color: var(--dm-text-muted); font-size: var(--dm-text-sm); }
73
+ .pdx-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: .7rem; }
74
+ .pdx-tile { position: relative; display: flex; align-items: center; gap: .75rem; padding: .75rem .8rem; cursor: pointer;
75
+ border: 1px solid var(--dm-border); border-left: 3px solid var(--c, var(--dm-primary)); border-radius: var(--dm-radius-md); background: var(--dm-surface);
76
+ transition: border-color .14s ease, background .14s ease; }
77
+ .pdx-tile:hover, .pdx-tile:focus-visible { border-color: var(--c, var(--dm-primary)); background: var(--dm-table-hover-bg, var(--dm-surface-raised)); outline: none; }
78
+ .pdx-tile.is-empty .pdx-tile-chip, .pdx-tile.is-empty .pdx-tile-body { opacity: .55; }
79
+ .pdx-tile-chip { width: 38px; height: 38px; flex-shrink: 0; border-radius: var(--dm-radius-md); display: flex; align-items: center; justify-content: center;
80
+ color: var(--c, var(--dm-primary)); background: color-mix(in srgb, var(--c, var(--dm-primary)) 14%, transparent); }
81
+ .pdx-tile-body { flex: 1; min-width: 0; display: flex; flex-direction: column; }
82
+ .pdx-tile-body strong { font-size: 1.4rem; line-height: 1; font-variant-numeric: tabular-nums; }
83
+ .pdx-tile-body small { color: var(--dm-text-muted); font-size: var(--dm-text-xs); text-transform: uppercase; letter-spacing: .03em; margin-top: .2rem; }
84
+ .pdx-tile-actions { display: flex; flex-direction: column; gap: .2rem; align-self: flex-start; }
85
+ .pdx-add, .pdx-kebab { border: 1px solid transparent; background: none; cursor: pointer; color: var(--dm-text-muted); border-radius: var(--dm-radius-sm);
86
+ width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center; font-size: var(--dm-text-base); line-height: 1; padding: 0; }
87
+ .pdx-add { color: var(--c, var(--dm-primary)); border-color: var(--dm-border); font-weight: 700; }
88
+ .pdx-add:hover, .pdx-kebab:hover { color: var(--dm-primary); border-color: var(--c, var(--dm-primary)); }
89
+ @media (max-width: 640px) { .pdx-hero { flex-direction: column; } }
90
+ </style>
@@ -1,67 +1,102 @@
1
- <div class="page-header">
2
- <h2><span id="ps-icon" data-icon="settings"></span> <span id="ps-title">Project settings</span></h2>
3
- <div class="header-actions">
4
- <a class="btn btn-secondary" id="ps-back"><span data-icon="arrow-left"></span> Back to project</a>
5
- <button class="btn btn-primary" id="ps-save"><span data-icon="check"></span> Save</button>
6
- </div>
7
- </div>
1
+ <!-- A project's settings. Bound with M.applyBindings - see admin/js/views/project-settings.js.
2
+ No page heading: the Tool banner is the heading ("Projects › <name>"), with Save, Overview and All projects. -->
3
+ <div class="psx">
4
+ <p class="psx-alert is-bad" data-bind-hidden="!errorText.value" data-bind-text="errorText.value"></p>
8
5
 
9
- <div class="card">
10
- <div class="card-header">
11
- <span data-icon="info"></span> Project details
6
+ <div class="psx-unsaved" data-bind-hidden="!dirty.value">
7
+ <span><span data-icon="edit" data-icon-size="14"></span> You have unsaved changes.</span>
8
+ <span class="psx-spacer"></span>
9
+ <button type="button" class="btn btn-sm btn-ghost" data-on-click="discard">Discard</button>
10
+ <button type="button" class="btn btn-sm btn-primary" data-on-click="save" data-bind-disabled="cannot.value">
11
+ <span data-icon="save" data-icon-size="13"></span> Save</button>
12
12
  </div>
13
- <div class="card-body">
14
- <div class="grid grid-cols-2 dm-pe-grid">
15
- <div class="form-group">
16
- <label for="ps-slug">Slug</label>
17
- <input type="text" id="ps-slug" name="slug" class="form-input" disabled>
18
- <small class="form-hint">Immutable after create</small>
13
+
14
+ <div class="psx-card">
15
+ <div class="psx-card-title">Project details
16
+ <span data-help-title="Project details" data-help="A project groups pages, collections, forms and the rest for the sidebar, and can limit what a user sees. Ctrl+S saves; leaving with unsaved changes asks first and keeps them for your next visit."></span>
17
+ </div>
18
+ <div class="psx-grid">
19
+ <div>
20
+ <label class="form-label" for="psx-name">Name <span class="text-danger">*</span></label>
21
+ <input id="psx-name" type="text" class="form-input" maxlength="80" data-model="name.value">
22
+ <small class="psx-err" data-bind-hidden="!nameErr.value" data-bind-text="nameErr.value"></small>
19
23
  </div>
20
- <div class="form-group">
21
- <label for="ps-name">Name <span class="required">*</span></label>
22
- <input type="text" id="ps-name" name="name" class="form-input" required>
24
+ <div>
25
+ <label class="form-label" for="psx-slug">Slug
26
+ <span data-help="The project's id - in its admin addresses and in every artefact filed under it. It cannot be changed."></span></label>
27
+ <input id="psx-slug" type="text" class="form-input font-mono" disabled>
23
28
  </div>
24
- <div class="form-group col-span-2">
25
- <label for="ps-description">Description</label>
26
- <textarea id="ps-description" name="description" class="form-input" rows="2"></textarea>
29
+ <div class="psx-wide">
30
+ <label class="form-label" for="psx-description">Description</label>
31
+ <textarea id="psx-description" class="form-input" rows="2" maxlength="300" data-model="description.value"></textarea>
27
32
  </div>
28
- <div class="form-group">
29
- <label for="ps-icon">Icon</label>
30
- <div id="ps-icon-mount"></div>
31
- <small class="form-hint">Click <code>⊞</code> to browse Domma icons</small>
33
+ <div>
34
+ <label class="form-label" for="psx-icon">Icon
35
+ <span data-help="Shown beside the project in the sidebar and on its overview. Type a Domma icon name or browse with the grid button."></span></label>
36
+ <div class="psx-icon-mount"></div>
32
37
  </div>
33
- <div class="form-group">
34
- <label for="ps-rootUrl">Root URL</label>
35
- <input type="text" id="ps-rootUrl" name="rootUrl" class="form-input" placeholder="/members">
36
- <small class="form-hint">URL prefix for auto-inheritance (must start with /)</small>
38
+ <div>
39
+ <label class="form-label" for="psx-rootUrl">Root URL
40
+ <span data-help-title="Root URL" data-help="Pages under this address are filed under the project without being tagged one by one - with /members, a page at /members/join belongs here. Leave it empty to file pages by hand. The core project owns /."></span></label>
41
+ <input id="psx-rootUrl" type="text" class="form-input font-mono" placeholder="/members" data-model="rootUrl.value" data-bind-disabled="rootLocked">
42
+ <small class="psx-err" data-bind-hidden="!rootErr.value" data-bind-text="rootErr.value"></small>
43
+ <small class="psx-hint" data-bind-hidden="!rootLocked">Locked - a built-in project.</small>
37
44
  </div>
38
- <div class="form-group">
39
- <label for="ps-sortOrder">Sort order</label>
40
- <input type="number" id="ps-sortOrder" name="sortOrder" class="form-input" value="0">
41
- <small class="form-hint">Lower = higher in the sidebar</small>
45
+ <div>
46
+ <label class="form-label" for="psx-sortOrder">Sort order
47
+ <span data-help="Lower numbers come first, in Manage projects and in the sidebar."></span></label>
48
+ <input id="psx-sortOrder" type="number" class="form-input" data-model="sortOrder.value">
42
49
  </div>
43
- <div class="form-group col-span-2" id="ps-enabled-group">
44
- <label class="dm-checkbox-label" style="display:inline-flex;align-items:center;gap:.5rem;">
45
- <input type="checkbox" id="ps-enabled" name="enabled" checked>
46
- <span>Enabled</span>
47
- </label>
48
- <small class="form-hint">Off = hard kill-switch: public pages 404 and the external API stops responding. Admin editing of its artefacts is unaffected.</small>
50
+ <div class="psx-setting" data-bind-hidden="!canToggle">
51
+ <label class="form-label" for="psx-enabled">Enabled
52
+ <span data-help="Off is a hard switch: the project's public pages 404 and its external API stops answering. Editing it in the admin carries on."></span></label>
53
+ <input id="psx-enabled" type="checkbox" class="form-switch" data-model="enabled.value">
49
54
  </div>
50
55
  </div>
51
56
  </div>
52
- </div>
53
57
 
54
- <div class="card" id="ps-danger-zone" style="border-color:#c33;">
55
- <div class="card-header"><h3>Danger zone</h3></div>
56
- <div class="card-body">
57
- <p>Untagging removes <code>meta.project</code> from every artefact currently tagged with this project. The artefacts remain (untagged); the project record is unchanged.</p>
58
- <p>
59
- <button class="btn btn-warning" id="ps-untag-all"><span data-icon="tag"></span> Untag all artefacts</button>
60
- </p>
61
- <hr>
62
- <p>Deleting a project is only possible when no artefacts are tagged. The server returns an explanatory error otherwise.</p>
63
- <p>
64
- <button class="btn btn-danger" id="ps-delete"><span data-icon="trash"></span> Delete project</button>
65
- </p>
58
+ <div class="psx-card psx-danger" data-bind-hidden="!dangerShown">
59
+ <div class="psx-card-title">Danger zone
60
+ <span data-help-title="Danger zone" data-help="Untagging takes the project tag off everything filed under it: the artefacts stay and belong to Core. A project can only be deleted once nothing is filed under it. Both ask first."></span>
61
+ </div>
62
+ <p class="psx-note" data-bind-text="filedText.value"></p>
63
+ <div class="psx-danger-row" data-bind-hidden="!canUntag">
64
+ <span><strong>Untag everything</strong><small>Everything filed here goes back to Core. The project is kept.</small></span>
65
+ <button type="button" class="btn btn-sm btn-warning" data-on-click="untagAll" data-bind-disabled="noneFiled.value || busy.value">
66
+ <span data-icon="tag" data-icon-size="13"></span> Untag all…</button>
67
+ </div>
68
+ <div class="psx-danger-row" data-bind-hidden="!canDelete">
69
+ <span><strong>Delete this project</strong><small data-bind-text="deleteHint.value"></small></span>
70
+ <button type="button" class="btn btn-sm btn-danger" data-on-click="remove" data-bind-disabled="deleteBlocked.value">
71
+ <span data-icon="trash" data-icon-size="13"></span> Delete…</button>
72
+ </div>
66
73
  </div>
67
74
  </div>
75
+
76
+ <style>
77
+ .psx { display: flex; flex-direction: column; gap: .9rem; }
78
+ .psx [hidden] { display: none !important; }
79
+ .psx-alert { margin: 0; padding: .6rem .8rem; border-radius: var(--dm-radius-md); color: var(--dm-text); background: color-mix(in srgb, var(--dm-danger) 14%, transparent); }
80
+ .psx-unsaved { position: sticky; top: 0; z-index: 5; display: flex; align-items: center; gap: .5rem; padding: .5rem .8rem; border-radius: var(--dm-radius-md);
81
+ border: 1px solid color-mix(in srgb, var(--dm-warning, var(--dm-primary)) 40%, transparent);
82
+ background: color-mix(in srgb, var(--dm-warning, var(--dm-primary)) 14%, var(--dm-surface)); color: var(--dm-text); }
83
+ .psx-unsaved > span:first-child { display: inline-flex; align-items: center; gap: .4rem; }
84
+ .psx-unsaved .btn, .psx-danger-row .btn { display: inline-flex; align-items: center; gap: .3rem; }
85
+ .psx-spacer { flex: 1; }
86
+ .psx-card { border: 1px solid var(--dm-border); border-radius: var(--dm-radius-md); padding: .8rem .95rem; display: flex; flex-direction: column; gap: .75rem; }
87
+ .psx-card-title { display: flex; align-items: center; gap: .35rem; font-weight: 600; }
88
+ .psx-grid { display: grid; grid-template-columns: 1fr 1fr; gap: .85rem; }
89
+ .psx-wide { grid-column: 1 / -1; }
90
+ .psx .form-label { display: flex; align-items: center; gap: .3rem; }
91
+ .psx-err { display: block; color: var(--dm-danger); font-size: var(--dm-text-xs); margin-top: .25rem; }
92
+ .psx-hint { display: block; color: var(--dm-text-muted); font-size: var(--dm-text-xs); margin-top: .25rem; }
93
+ .psx-note { margin: 0; color: var(--dm-text-muted); font-size: var(--dm-text-sm); }
94
+ .psx-setting { display: flex; align-items: center; justify-content: space-between; gap: .75rem; }
95
+ .psx-setting .form-label { margin: 0; }
96
+ .psx-danger { border-color: color-mix(in srgb, var(--dm-danger) 45%, var(--dm-border)); }
97
+ .psx-danger .psx-card-title { color: var(--dm-danger); }
98
+ .psx-danger-row { display: flex; align-items: center; gap: .8rem; padding: .55rem 0; border-top: 1px solid var(--dm-border); }
99
+ .psx-danger-row > span { flex: 1; min-width: 0; display: flex; flex-direction: column; }
100
+ .psx-danger-row small { color: var(--dm-text-muted); font-size: var(--dm-text-xs); }
101
+ @media (max-width: 640px) { .psx-grid { grid-template-columns: 1fr; } .psx-danger-row { flex-wrap: wrap; } }
102
+ </style>