| [7bc636b] | 1 | <div class="container admin-site-edit-page">
|
|---|
| [8b014ef] | 2 | <p><a href="/admin" class="btn">← Beheer</a></p>
|
|---|
| [544cd86] | 3 | <h1><%= isNew ? 'Nieuwe site' : 'Uiterlijk: ' + site.title %></h1>
|
|---|
| [7bc636b] | 4 |
|
|---|
| 5 | <% if (typeof success !== 'undefined' && success) { %><div class="audio-flash audio-flash--ok"><%= success %></div><% } %>
|
|---|
| 6 | <% if (error) { %><div class="audio-flash audio-flash--err"><%= error %></div><% } %>
|
|---|
| 7 |
|
|---|
| 8 | <form method="post" action="<%= isNew ? '/admin/sites/create' : '/admin/sites/' + site.slug + '/save' %>" class="site-form">
|
|---|
| 9 |
|
|---|
| 10 | <fieldset>
|
|---|
| [544cd86] | 11 | <legend>Identiteit</legend>
|
|---|
| [7bc636b] | 12 | <label>
|
|---|
| [3068cc0] | 13 | <span>Slug (URL) <% if (!isNew) { %><em>(vast)</em><% } %></span>
|
|---|
| 14 | <span class="slug-url">
|
|---|
| 15 | <span class="slug-url-host" id="slug-host"
|
|---|
| 16 | data-prefix="<%= (typeof tenancy !== 'undefined' && tenancy === 'hub') ? '/user/' : '/' %>">website.com/</span><input
|
|---|
| 17 | type="text" name="slug" id="slug-input" class="slug-url-input" value="<%= site.slug %>" required <% if (!isNew) { %>readonly<% } %>
|
|---|
| 18 | pattern="[a-z0-9_-]{2,40}" placeholder="jouwslug"
|
|---|
| 19 | autocapitalize="none" autocorrect="off" spellcheck="false">
|
|---|
| 20 | </span>
|
|---|
| [7bc636b] | 21 | </label>
|
|---|
| 22 | <label>
|
|---|
| [544cd86] | 23 | <span>Titel <small class="form-hint-inline">β getoond in de kop en als weergavenaam</small></span>
|
|---|
| [7bc636b] | 24 | <input type="text" name="title" value="<%= site.title || '' %>" required maxlength="200">
|
|---|
| 25 | </label>
|
|---|
| [98ecf51] | 26 | <%# Eigenaar (her)toewijzen β ALLEEN god. In hub-modus geef je zo elke
|
|---|
| 27 | gebruiker z'n eigen, zelf te beheren Klonkt. Niet-god ziet dit niet. %>
|
|---|
| 28 | <% if (user && user.role === 'god') { %>
|
|---|
| 29 | <label>
|
|---|
| 30 | <span>Eigenaar <small class="form-hint-inline">β wie deze Klonkt zelf mag beheren</small></span>
|
|---|
| 31 | <select name="owner_id">
|
|---|
| 32 | <% (typeof users !== 'undefined' ? users : []).forEach(function(u){ %>
|
|---|
| 33 | <option value="<%= u.id %>" <%= (site.owner_id === u.id) ? 'selected' : '' %>><%= u.username %><%= u.role === 'god' ? ' (god)' : '' %></option>
|
|---|
| 34 | <% }); %>
|
|---|
| 35 | </select>
|
|---|
| 36 | </label>
|
|---|
| 37 | <% } %>
|
|---|
| [7bc636b] | 38 | <label>
|
|---|
| [544cd86] | 39 | <span>Tagline <small class="form-hint-inline">β korte oneliner</small></span>
|
|---|
| [7bc636b] | 40 | <input type="text" name="tagline" value="<%= site.tagline || '' %>" maxlength="200">
|
|---|
| 41 | </label>
|
|---|
| 42 | <label>
|
|---|
| [544cd86] | 43 | <span>Bio / omschrijving <small class="form-hint-inline">β getoond in de profielkop en gebruikt voor SEO</small></span>
|
|---|
| [7bc636b] | 44 | <textarea name="description" rows="3" maxlength="500"><%= site.description || '' %></textarea>
|
|---|
| 45 | </label>
|
|---|
| 46 | <label>
|
|---|
| [544cd86] | 47 | <span>Profielfoto</span>
|
|---|
| [7bc636b] | 48 | <div class="photo-picker" id="photo-picker">
|
|---|
| 49 | <div class="photo-thumb" id="photo-thumb" <%= site.profile_photo ? '' : 'data-empty' %>>
|
|---|
| 50 | <img id="photo-preview" src="<%= site.profile_photo || '' %>" alt="" <%= site.profile_photo ? '' : 'hidden' %>>
|
|---|
| 51 | <span class="photo-empty" aria-hidden="true">
|
|---|
| 52 | <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
|---|
| 53 | </span>
|
|---|
| 54 | </div>
|
|---|
| 55 | <div class="photo-controls">
|
|---|
| 56 | <input type="text" name="profile_photo" id="photo-url" value="<%= site.profile_photo || '' %>" placeholder="/media/avatars/foo.jpg of https://β¦">
|
|---|
| 57 | <div class="photo-actions">
|
|---|
| [544cd86] | 58 | <button type="button" class="btn" id="photo-upload-trigger">π· Uploaden</button>
|
|---|
| [7bc636b] | 59 | <button type="button" class="btn" id="photo-clear" <%= site.profile_photo ? '' : 'hidden' %>>Verwijder</button>
|
|---|
| 60 | <small id="photo-status" class="form-hint photo-status"></small>
|
|---|
| 61 | </div>
|
|---|
| 62 | <input type="file" id="photo-upload-field" accept="image/jpeg,image/png,image/webp,image/gif" hidden>
|
|---|
| 63 | </div>
|
|---|
| 64 | </div>
|
|---|
| 65 | </label>
|
|---|
| 66 | <label>
|
|---|
| [544cd86] | 67 | <span>Taal (ISO-code)</span>
|
|---|
| [7bc636b] | 68 | <input type="text" name="language" value="<%= site.language || 'nl' %>" maxlength="8">
|
|---|
| 69 | </label>
|
|---|
| 70 | <label class="cb">
|
|---|
| 71 | <input type="checkbox" name="profile_enabled" value="1" <%= site.profile_enabled ? 'checked' : '' %>>
|
|---|
| [544cd86] | 72 | <span>Profielkop tonen onder de navigatie</span>
|
|---|
| [7bc636b] | 73 | </label>
|
|---|
| 74 | </fieldset>
|
|---|
| 75 |
|
|---|
| 76 | <fieldset>
|
|---|
| [544cd86] | 77 | <legend>Vormgeving</legend>
|
|---|
| [7bc636b] | 78 |
|
|---|
| 79 | <%# ββ Curated accent picker βββββββββββββββββββββββββββββββββββββββ
|
|---|
| 80 | Was a free-form <input type="color">; now a fixed set of 8 hand-
|
|---|
| 81 | picked colors that work in both light and dark themes. The hex is
|
|---|
| 82 | still what's stored in the DB β we just constrain WHICH hex. %>
|
|---|
| 83 | <fieldset class="accent-grid">
|
|---|
| [544cd86] | 84 | <legend>Accentkleur</legend>
|
|---|
| [7bc636b] | 85 | <% const _curAccent = (site.accent || '#c2410c').toLowerCase(); %>
|
|---|
| 86 | <% accents.forEach(function(a) { %>
|
|---|
| 87 | <label class="accent-swatch" title="<%= a.name %>">
|
|---|
| 88 | <input type="radio" name="accent" value="<%= a.color %>" <%= a.color.toLowerCase() === _curAccent ? 'checked' : '' %>>
|
|---|
| 89 | <span class="accent-dot" style="background: <%= a.color %>;" aria-hidden="true"></span>
|
|---|
| 90 | <span class="accent-swatch-name"><%= a.name %></span>
|
|---|
| 91 | </label>
|
|---|
| 92 | <% }); %>
|
|---|
| 93 | </fieldset>
|
|---|
| 94 |
|
|---|
| 95 | <%# ββ Default theme for new visitors βββββββββββββββββββββββββββ
|
|---|
| 96 | 'auto' (empty value) lets the browser/OS decide via prefers-color-scheme.
|
|---|
| 97 | 'light' / 'dark' force the site default for visitors who haven't picked
|
|---|
| 98 | their own preference yet. Logged-in users with users.theme set, and
|
|---|
| 99 | visitors with a localStorage override, still win over this default. %>
|
|---|
| 100 | <fieldset class="theme-default">
|
|---|
| [544cd86] | 101 | <legend>Standaardthema voor nieuwe bezoekers</legend>
|
|---|
| [7bc636b] | 102 | <% const td = site.theme_override || ''; %>
|
|---|
| 103 | <label class="cb">
|
|---|
| 104 | <input type="radio" name="theme_override" value="" <%= td === '' ? 'checked' : '' %>>
|
|---|
| 105 | <span>Auto (volg device-voorkeur)</span>
|
|---|
| 106 | </label>
|
|---|
| 107 | <label class="cb">
|
|---|
| 108 | <input type="radio" name="theme_override" value="light" <%= td === 'light' ? 'checked' : '' %>>
|
|---|
| [544cd86] | 109 | <span>Licht</span>
|
|---|
| [7bc636b] | 110 | </label>
|
|---|
| 111 | <label class="cb">
|
|---|
| 112 | <input type="radio" name="theme_override" value="dark" <%= td === 'dark' ? 'checked' : '' %>>
|
|---|
| [544cd86] | 113 | <span>Donker</span>
|
|---|
| [7bc636b] | 114 | </label>
|
|---|
| 115 | </fieldset>
|
|---|
| 116 |
|
|---|
| 117 | <fieldset class="palette-grid">
|
|---|
| [544cd86] | 118 | <legend>Palet</legend>
|
|---|
| [7bc636b] | 119 | <% palettes.forEach(function(p) { %>
|
|---|
| 120 | <label class="palette-swatch">
|
|---|
| 121 | <input type="radio" name="palette" value="<%= p.key %>" <%= (site.palette || 'sage') === p.key ? 'checked' : '' %>>
|
|---|
| 122 | <span class="palette-preview" aria-hidden="true">
|
|---|
| 123 | <span class="pp-half pp-light" style="background: <%= p.light.paper %>; color: <%= p.light.ink %>;">
|
|---|
| 124 | <span class="pp-letter">Aa</span>
|
|---|
| 125 | <span class="pp-accent" style="background: <%= p.light.accent %>;"></span>
|
|---|
| 126 | </span>
|
|---|
| 127 | <span class="pp-half pp-dark" style="background: <%= p.dark.paper %>; color: <%= p.dark.ink %>;">
|
|---|
| 128 | <span class="pp-letter">Aa</span>
|
|---|
| 129 | <span class="pp-accent" style="background: <%= p.dark.accent %>;"></span>
|
|---|
| 130 | </span>
|
|---|
| 131 | </span>
|
|---|
| 132 | <span class="palette-swatch-name"><%= p.name %></span>
|
|---|
| 133 | </label>
|
|---|
| 134 | <% }); %>
|
|---|
| 135 | </fieldset>
|
|---|
| 136 | </fieldset>
|
|---|
| 137 |
|
|---|
| 138 | <fieldset>
|
|---|
| [544cd86] | 139 | <legend>Gedrag</legend>
|
|---|
| [7bc636b] | 140 | <label class="cb">
|
|---|
| 141 | <input type="checkbox" name="is_public" value="1" <%= site.is_public ? 'checked' : '' %>>
|
|---|
| [544cd86] | 142 | <span>Publieke site (uitvinken voor een besloten kring)</span>
|
|---|
| [7bc636b] | 143 | </label>
|
|---|
| 144 | <label class="cb">
|
|---|
| 145 | <input type="checkbox" name="robots_index" value="1" <%= site.robots_index ? 'checked' : '' %>>
|
|---|
| [544cd86] | 146 | <span>Zoekmachines mogen indexeren (sitemap.xml is verborgen indien uit)</span>
|
|---|
| [7bc636b] | 147 | </label>
|
|---|
| 148 | <label class="cb">
|
|---|
| 149 | <input type="checkbox" name="require_login_to_comment" value="1" <%= site.require_login_to_comment ? 'checked' : '' %>>
|
|---|
| [544cd86] | 150 | <span>Inloggen vereist om te reageren</span>
|
|---|
| [7bc636b] | 151 | </label>
|
|---|
| 152 | <label>
|
|---|
| [544cd86] | 153 | <span>Reactie-moderatie</span>
|
|---|
| [7bc636b] | 154 | <select name="comments_moderation_mode">
|
|---|
| [544cd86] | 155 | <option value="trust" <%= (site.comments_moderation_mode || 'trust') === 'trust' ? 'selected' : '' %>>Vertrouwen (automatisch goedkeuren)</option>
|
|---|
| 156 | <option value="moderate" <%= site.comments_moderation_mode === 'moderate' ? 'selected' : '' %>>Modereren (eerst in de wachtrij)</option>
|
|---|
| [7bc636b] | 157 | </select>
|
|---|
| 158 | </label>
|
|---|
| 159 | <label class="cb">
|
|---|
| 160 | <input type="checkbox" name="enable_audio_player" value="1" <%= site.enable_audio_player ? 'checked' : '' %>>
|
|---|
| [544cd86] | 161 | <span>Audiospeler + embeds inschakelen</span>
|
|---|
| [7bc636b] | 162 | </label>
|
|---|
| [2c246d4] | 163 | <%# Prutter is een Hub-only premium-functie β toggle alleen tonen in Hub-modus. %>
|
|---|
| 164 | <% if (typeof tenancy !== 'undefined' && tenancy === 'hub') { %>
|
|---|
| [7bc636b] | 165 | <label class="cb">
|
|---|
| 166 | <input type="checkbox" name="enable_prutter" value="1" <%= site.enable_prutter ? 'checked' : '' %>>
|
|---|
| [2c246d4] | 167 | <span>Prutter (DM’s tussen leden) inschakelen <small class="form-hint-inline">β Hub-functie, premium</small></span>
|
|---|
| [7bc636b] | 168 | </label>
|
|---|
| [2c246d4] | 169 | <% } %>
|
|---|
| [7bc636b] | 170 | </fieldset>
|
|---|
| 171 |
|
|---|
| 172 | <fieldset class="profile-links-fieldset">
|
|---|
| [544cd86] | 173 | <legend>Social / streaming-links</legend>
|
|---|
| 174 | <p class="form-hint">Getoond als merk-iconen op de profielkop. Voeg er zoveel toe als je wilt.</p>
|
|---|
| [7bc636b] | 175 |
|
|---|
| 176 | <div id="profile-links-rows">
|
|---|
| 177 | <% (parsedLinks || []).forEach(function(l) { %>
|
|---|
| 178 | <div class="profile-link-row">
|
|---|
| 179 | <select name="profile_link_platform[]">
|
|---|
| 180 | <% platforms.forEach(function(p) { %>
|
|---|
| 181 | <option value="<%= p.key %>" <%= l.platform === p.key ? 'selected' : '' %>><%= p.label %></option>
|
|---|
| 182 | <% }); %>
|
|---|
| 183 | </select>
|
|---|
| 184 | <input type="text" name="profile_link_url[]" value="<%= l.url %>" placeholder="https://β¦" required>
|
|---|
| 185 | <button type="button" class="btn btn-danger pl-remove">Γ</button>
|
|---|
| 186 | </div>
|
|---|
| 187 | <% }); %>
|
|---|
| 188 | </div>
|
|---|
| 189 |
|
|---|
| 190 | <template id="profile-link-template">
|
|---|
| 191 | <div class="profile-link-row">
|
|---|
| 192 | <select name="profile_link_platform[]">
|
|---|
| 193 | <% platforms.forEach(function(p) { %>
|
|---|
| 194 | <option value="<%= p.key %>"><%= p.label %></option>
|
|---|
| 195 | <% }); %>
|
|---|
| 196 | </select>
|
|---|
| 197 | <input type="text" name="profile_link_url[]" placeholder="https://β¦" required>
|
|---|
| 198 | <button type="button" class="btn btn-danger pl-remove">Γ</button>
|
|---|
| 199 | </div>
|
|---|
| 200 | </template>
|
|---|
| 201 |
|
|---|
| [544cd86] | 202 | <button type="button" id="profile-link-add" class="btn">+ Link toevoegen</button>
|
|---|
| [7bc636b] | 203 | </fieldset>
|
|---|
| 204 |
|
|---|
| 205 | <fieldset>
|
|---|
| [544cd86] | 206 | <legend>Feed-weergave</legend>
|
|---|
| [7bc636b] | 207 | <label>
|
|---|
| [544cd86] | 208 | <span>Standaardweergave voor de homepage</span>
|
|---|
| [7bc636b] | 209 | <select name="feed_view_default">
|
|---|
| [544cd86] | 210 | <option value="timeline" <%= (site.feed_view_default || 'timeline') === 'timeline' ? 'selected' : '' %>>Tijdlijn (chronologische lijst)</option>
|
|---|
| 211 | <option value="grid" <%= site.feed_view_default === 'grid' ? 'selected' : '' %>>Grid (kaarten)</option>
|
|---|
| [7bc636b] | 212 | </select>
|
|---|
| 213 | </label>
|
|---|
| 214 | <label class="cb">
|
|---|
| 215 | <input type="checkbox" name="feed_view_switch" value="1" <%= site.feed_view_switch ? 'checked' : '' %>>
|
|---|
| [544cd86] | 216 | <span>Tijdlijn β grid-schakelaar boven de feed tonen</span>
|
|---|
| [7bc636b] | 217 | </label>
|
|---|
| 218 | <label class="cb">
|
|---|
| 219 | <input type="checkbox" name="show_search" value="1" <%= site.show_search ? 'checked' : '' %>>
|
|---|
| [544cd86] | 220 | <span>Zoekknop in de navigatie tonen</span>
|
|---|
| [7bc636b] | 221 | </label>
|
|---|
| 222 | <label class="cb">
|
|---|
| 223 | <input type="checkbox" name="show_archive_link" value="1" <%= site.show_archive_link ? 'checked' : '' %>>
|
|---|
| [544cd86] | 224 | <span>Archief-link in de navigatie tonen</span>
|
|---|
| [7bc636b] | 225 | </label>
|
|---|
| 226 | </fieldset>
|
|---|
| 227 |
|
|---|
| [6623453] | 228 | <fieldset class="seo-pointer">
|
|---|
| [7bc636b] | 229 | <legend>SEO & social</legend>
|
|---|
| [6623453] | 230 | <p>Titel-sjabloon, canonical, deel-afbeelding, verificatie-metas en meer staan
|
|---|
| 231 | nu op een eigen pagina: <a href="/admin/seo">π SEO & vindbaarheid</a>.</p>
|
|---|
| [7bc636b] | 232 | </fieldset>
|
|---|
| 233 |
|
|---|
| 234 | <fieldset>
|
|---|
| [544cd86] | 235 | <legend>Eigen CSS & HTML <em>(optioneel)</em></legend>
|
|---|
| [7bc636b] | 236 | <label>
|
|---|
| [544cd86] | 237 | <span>Eigen CSS (geΓ―njecteerd als <style> in <head>)</span>
|
|---|
| [7bc636b] | 238 | <textarea name="custom_css" rows="3" placeholder=".foo { color: red; }"><%= site.custom_css || '' %></textarea>
|
|---|
| 239 | </label>
|
|---|
| 240 | <label>
|
|---|
| [544cd86] | 241 | <span>Eigen <head>-HTML (analytics, extra meta’s)</span>
|
|---|
| [7bc636b] | 242 | <textarea name="custom_head_html" rows="3" placeholder="<meta name="...">"><%= site.custom_head_html || '' %></textarea>
|
|---|
| 243 | </label>
|
|---|
| 244 | <label>
|
|---|
| [544cd86] | 245 | <span>Eigen footer-HTML</span>
|
|---|
| [7bc636b] | 246 | <textarea name="custom_foot_html" rows="3"><%= site.custom_foot_html || '' %></textarea>
|
|---|
| 247 | </label>
|
|---|
| 248 | </fieldset>
|
|---|
| 249 |
|
|---|
| [544cd86] | 250 | <button type="submit" class="btn btn-primary"><%= isNew ? 'Site aanmaken' : 'Wijzigingen opslaan' %></button>
|
|---|
| [7bc636b] | 251 | </form>
|
|---|
| 252 | </div>
|
|---|
| 253 |
|
|---|
| 254 | <script>
|
|---|
| [3068cc0] | 255 | (function() {
|
|---|
| 256 | // Slug-URL-veld: toon de echte host als gedimde prefix (de slug zelf is
|
|---|
| 257 | // gekleurd via CSS). Hub β host/user/<slug>, anders host/<slug>.
|
|---|
| 258 | var sh = document.getElementById('slug-host');
|
|---|
| 259 | if (sh) sh.textContent = location.host + (sh.dataset.prefix || '/');
|
|---|
| 260 | })();
|
|---|
| [7bc636b] | 261 | (function() {
|
|---|
| 262 | // Profile-links repeater: add row from <template>, remove on click.
|
|---|
| 263 | var rows = document.getElementById('profile-links-rows');
|
|---|
| 264 | var tpl = document.getElementById('profile-link-template');
|
|---|
| 265 | var add = document.getElementById('profile-link-add');
|
|---|
| 266 | if (!rows || !tpl || !add) return;
|
|---|
| 267 |
|
|---|
| 268 | add.addEventListener('click', function() {
|
|---|
| 269 | var clone = tpl.content.cloneNode(true);
|
|---|
| 270 | rows.appendChild(clone);
|
|---|
| 271 | });
|
|---|
| 272 | rows.addEventListener('click', function(e) {
|
|---|
| 273 | if (e.target && e.target.classList.contains('pl-remove')) {
|
|---|
| 274 | var row = e.target.closest('.profile-link-row');
|
|---|
| 275 | if (row) row.remove();
|
|---|
| 276 | }
|
|---|
| 277 | });
|
|---|
| 278 | })();
|
|---|
| 279 |
|
|---|
| 280 | // P63 β Profile photo picker: upload via /admin/sites/upload-photo,
|
|---|
| 281 | // then write the returned URL into the visible input. Live thumb preview.
|
|---|
| 282 | (function() {
|
|---|
| 283 | var picker = document.getElementById('photo-picker');
|
|---|
| 284 | if (!picker) return;
|
|---|
| 285 | var thumb = document.getElementById('photo-thumb');
|
|---|
| 286 | var preview = document.getElementById('photo-preview');
|
|---|
| 287 | var urlEl = document.getElementById('photo-url');
|
|---|
| 288 | var trigger = document.getElementById('photo-upload-trigger');
|
|---|
| 289 | var clear = document.getElementById('photo-clear');
|
|---|
| 290 | var field = document.getElementById('photo-upload-field');
|
|---|
| 291 | var status = document.getElementById('photo-status');
|
|---|
| 292 |
|
|---|
| 293 | function showPreview(url) {
|
|---|
| 294 | if (url) {
|
|---|
| 295 | preview.src = url;
|
|---|
| 296 | preview.hidden = false;
|
|---|
| 297 | thumb.removeAttribute('data-empty');
|
|---|
| 298 | clear.hidden = false;
|
|---|
| 299 | } else {
|
|---|
| 300 | preview.src = '';
|
|---|
| 301 | preview.hidden = true;
|
|---|
| 302 | thumb.setAttribute('data-empty', '');
|
|---|
| 303 | clear.hidden = true;
|
|---|
| 304 | }
|
|---|
| 305 | }
|
|---|
| 306 |
|
|---|
| 307 | if (urlEl) {
|
|---|
| 308 | urlEl.addEventListener('input', function() {
|
|---|
| 309 | showPreview(urlEl.value.trim());
|
|---|
| 310 | });
|
|---|
| 311 | }
|
|---|
| 312 |
|
|---|
| 313 | if (trigger && field) {
|
|---|
| 314 | trigger.addEventListener('click', function() { field.click(); });
|
|---|
| 315 | field.addEventListener('change', async function() {
|
|---|
| 316 | var file = field.files && field.files[0];
|
|---|
| 317 | if (!file) return;
|
|---|
| 318 | status.classList.remove('is-error');
|
|---|
| 319 | status.textContent = 'Uploadenβ¦';
|
|---|
| 320 | try {
|
|---|
| 321 | var fd = new FormData();
|
|---|
| 322 | fd.append('photo', file);
|
|---|
| 323 | var r = await fetch('/admin/sites/upload-photo', {
|
|---|
| 324 | method: 'POST',
|
|---|
| 325 | body: fd,
|
|---|
| 326 | credentials: 'same-origin',
|
|---|
| 327 | });
|
|---|
| 328 | var j = await r.json();
|
|---|
| 329 | if (!r.ok || !j.ok) throw new Error(j.error || ('Upload mislukt (' + r.status + ')'));
|
|---|
| 330 | urlEl.value = j.url;
|
|---|
| 331 | showPreview(j.url);
|
|---|
| 332 | status.textContent = 'GeΓΌpload β';
|
|---|
| 333 | setTimeout(function() { status.textContent = ''; }, 2000);
|
|---|
| 334 | } catch (e) {
|
|---|
| 335 | status.classList.add('is-error');
|
|---|
| 336 | status.textContent = 'Mislukt: ' + e.message;
|
|---|
| 337 | } finally {
|
|---|
| 338 | field.value = '';
|
|---|
| 339 | }
|
|---|
| 340 | });
|
|---|
| 341 | }
|
|---|
| 342 |
|
|---|
| 343 | if (clear) {
|
|---|
| 344 | clear.addEventListener('click', function() {
|
|---|
| 345 | urlEl.value = '';
|
|---|
| 346 | showPreview('');
|
|---|
| 347 | status.textContent = '';
|
|---|
| 348 | // Note: doesn't delete the file from disk β saving the form with empty
|
|---|
| 349 | // URL leaves the file orphaned on the server. Acceptable for now.
|
|---|
| 350 | });
|
|---|
| 351 | }
|
|---|
| 352 | })();
|
|---|
| 353 | </script>
|
|---|
| 354 |
|
|---|
| 355 | <style>
|
|---|
| 356 | .admin-site-edit-page { max-width: 760px; margin: 3rem auto; padding: 0 1rem; }
|
|---|
| 357 | .admin-site-edit-page h1 { font-family: var(--font-display, serif); font-size: 1.8rem; margin: 0 0 1.5rem; }
|
|---|
| 358 |
|
|---|
| 359 | .site-form { display: flex; flex-direction: column; gap: 1.5rem; }
|
|---|
| 360 | .site-form fieldset {
|
|---|
| 361 | border: 1px solid var(--rule);
|
|---|
| 362 | border-radius: 8px;
|
|---|
| 363 | padding: 1rem 1.25rem;
|
|---|
| 364 | background: var(--paper-2);
|
|---|
| 365 | display: flex;
|
|---|
| 366 | flex-direction: column;
|
|---|
| 367 | gap: 0.75rem;
|
|---|
| 368 | }
|
|---|
| 369 | .site-form fieldset.palette-grid {
|
|---|
| 370 | display: grid;
|
|---|
| 371 | grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
|---|
| 372 | background: var(--paper);
|
|---|
| 373 | margin-top: 0.5rem;
|
|---|
| 374 | }
|
|---|
| 375 | /* Theme default radios β horizontal, sits between accent + palette */
|
|---|
| 376 | .site-form fieldset.theme-default {
|
|---|
| 377 | display: flex; flex-direction: row; flex-wrap: wrap; gap: 1rem;
|
|---|
| 378 | background: var(--paper);
|
|---|
| 379 | margin-top: 0.5rem;
|
|---|
| 380 | }
|
|---|
| 381 | .site-form fieldset.theme-default label.cb { font-size: 0.9rem; color: var(--ink); }
|
|---|
| 382 | .site-form fieldset legend { font-family: var(--font-display, serif); font-size: 1rem; padding: 0 0.4rem; }
|
|---|
| 383 | .site-form label { display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.85rem; color: var(--ink-muted, var(--ink-soft)); }
|
|---|
| 384 | .site-form label.cb { flex-direction: row; align-items: center; gap: 0.5rem; }
|
|---|
| 385 | .site-form input[type="text"], .site-form input[type="color"], .site-form textarea {
|
|---|
| 386 | padding: 0.55rem 0.85rem;
|
|---|
| 387 | border: 1px solid var(--rule);
|
|---|
| 388 | border-radius: 5px;
|
|---|
| 389 | background: var(--paper);
|
|---|
| 390 | color: var(--ink);
|
|---|
| 391 | font-family: inherit;
|
|---|
| 392 | font-size: 0.95rem;
|
|---|
| 393 | }
|
|---|
| 394 | .site-form input[type="color"] { padding: 0.2rem; height: 38px; }
|
|---|
| 395 | .site-form textarea { resize: vertical; min-height: 60px; font-family: var(--font-mono, monospace); font-size: 0.85rem; }
|
|---|
| 396 | .site-form input[readonly] { opacity: 0.6; cursor: not-allowed; background: var(--paper-2); }
|
|---|
| 397 |
|
|---|
| [3068cc0] | 398 | /* Slug als URL-veld: gedimde host-prefix + de slug zelf in de accentkleur. */
|
|---|
| 399 | .slug-url {
|
|---|
| 400 | display: flex; align-items: stretch;
|
|---|
| 401 | border: 1px solid var(--rule); border-radius: 5px;
|
|---|
| 402 | background: var(--paper); overflow: hidden;
|
|---|
| 403 | transition: border-color 120ms;
|
|---|
| 404 | }
|
|---|
| 405 | .slug-url:focus-within { border-color: var(--accent); }
|
|---|
| 406 | .slug-url-host {
|
|---|
| 407 | display: inline-flex; align-items: center;
|
|---|
| 408 | padding-left: 0.85rem;
|
|---|
| 409 | color: var(--ink-muted, var(--ink-soft));
|
|---|
| 410 | white-space: nowrap; user-select: none;
|
|---|
| 411 | font-size: 0.95rem;
|
|---|
| 412 | }
|
|---|
| 413 | .site-form .slug-url-input {
|
|---|
| 414 | flex: 1 1 auto; min-width: 0;
|
|---|
| 415 | border: none !important; border-radius: 0 !important;
|
|---|
| 416 | background: transparent !important;
|
|---|
| 417 | color: var(--accent) !important; font-weight: 700;
|
|---|
| 418 | padding: 0.55rem 0.85rem 0.55rem 0.1rem;
|
|---|
| 419 | }
|
|---|
| 420 | .site-form .slug-url-input:focus { outline: none; }
|
|---|
| 421 | .site-form .slug-url-input[readonly] {
|
|---|
| 422 | opacity: 1; cursor: default; background: transparent !important;
|
|---|
| 423 | }
|
|---|
| 424 |
|
|---|
| [974de34] | 425 | /* Selects: zelfde look als de inputs, met een eigen chevron i.p.v. de kale
|
|---|
| 426 | native pijl. De geopende optielijst blijft door de browser gerenderd. */
|
|---|
| 427 | .site-form select {
|
|---|
| 428 | appearance: none;
|
|---|
| 429 | -webkit-appearance: none;
|
|---|
| 430 | padding: 0.55rem 2.2rem 0.55rem 0.85rem;
|
|---|
| 431 | border: 1px solid var(--rule);
|
|---|
| 432 | border-radius: 5px;
|
|---|
| 433 | background-color: var(--paper);
|
|---|
| 434 | color: var(--ink);
|
|---|
| 435 | font-family: inherit;
|
|---|
| 436 | font-size: 0.95rem;
|
|---|
| 437 | cursor: pointer;
|
|---|
| 438 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
|
|---|
| 439 | background-repeat: no-repeat;
|
|---|
| 440 | background-position: right 0.7rem center;
|
|---|
| 441 | transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
|---|
| 442 | }
|
|---|
| 443 | .site-form select:hover { border-color: var(--ink-soft, #888); }
|
|---|
| 444 | .site-form select:focus,
|
|---|
| 445 | .site-form input[type="text"]:focus,
|
|---|
| 446 | .site-form textarea:focus {
|
|---|
| 447 | outline: none;
|
|---|
| 448 | border-color: var(--accent, #c2410c);
|
|---|
| 449 | box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent, #c2410c) 22%, transparent);
|
|---|
| 450 | }
|
|---|
| 451 |
|
|---|
| [7bc636b] | 452 | /* Reuse the palette-swatch styles from account.ejs */
|
|---|
| 453 | .palette-swatch {
|
|---|
| 454 | display: flex; flex-direction: column; gap: 0.4rem;
|
|---|
| 455 | padding: 0.5rem;
|
|---|
| 456 | border: 1px solid var(--rule); border-radius: 6px; background: var(--paper);
|
|---|
| 457 | cursor: pointer; font-size: 0.85rem; transition: border-color 120ms;
|
|---|
| 458 | }
|
|---|
| 459 | .palette-swatch:hover { border-color: var(--accent); }
|
|---|
| 460 | .palette-swatch input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; }
|
|---|
| 461 | .palette-swatch:has(input[type="radio"]:checked) { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent); }
|
|---|
| 462 | .palette-preview {
|
|---|
| 463 | display: grid; grid-template-columns: 1fr 1fr;
|
|---|
| 464 | width: 100%; height: 44px; border-radius: 4px; overflow: hidden;
|
|---|
| 465 | border: 1px solid var(--rule);
|
|---|
| 466 | }
|
|---|
| 467 | .pp-half {
|
|---|
| 468 | position: relative;
|
|---|
| 469 | display: flex; align-items: center; justify-content: center;
|
|---|
| 470 | font-family: var(--font-display, serif); font-weight: 600; font-size: 1rem;
|
|---|
| 471 | }
|
|---|
| 472 | .pp-letter { line-height: 1; }
|
|---|
| 473 | .pp-accent {
|
|---|
| 474 | position: absolute; bottom: 4px; right: 4px;
|
|---|
| 475 | width: 9px; height: 9px; border-radius: 50%;
|
|---|
| 476 | border: 1px solid rgba(0,0,0,0.12);
|
|---|
| 477 | }
|
|---|
| 478 | .palette-swatch-name { font-weight: 500; color: var(--ink); text-align: center; }
|
|---|
| 479 |
|
|---|
| 480 | /* Accent picker: row of small colored swatches */
|
|---|
| 481 | .site-form fieldset.accent-grid {
|
|---|
| 482 | display: grid;
|
|---|
| 483 | grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
|
|---|
| 484 | gap: 0.5rem;
|
|---|
| 485 | background: var(--paper);
|
|---|
| 486 | margin-top: 0.5rem;
|
|---|
| 487 | }
|
|---|
| 488 | .accent-swatch {
|
|---|
| 489 | display: flex; flex-direction: column; align-items: center; gap: 0.4rem;
|
|---|
| 490 | padding: 0.55rem 0.4rem;
|
|---|
| 491 | border: 1px solid var(--rule); border-radius: 6px; background: var(--paper);
|
|---|
| 492 | cursor: pointer; font-size: 0.78rem;
|
|---|
| 493 | transition: border-color 120ms, box-shadow 120ms;
|
|---|
| 494 | }
|
|---|
| 495 | .accent-swatch:hover { border-color: var(--accent); }
|
|---|
| 496 | .accent-swatch input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; }
|
|---|
| 497 | .accent-swatch:has(input[type="radio"]:checked) {
|
|---|
| 498 | border-color: var(--accent);
|
|---|
| 499 | box-shadow: 0 0 0 2px var(--accent);
|
|---|
| 500 | }
|
|---|
| 501 | .accent-dot {
|
|---|
| 502 | width: 28px; height: 28px; border-radius: 50%;
|
|---|
| 503 | border: 1px solid rgba(0,0,0,0.1);
|
|---|
| 504 | box-shadow: inset 0 -2px 4px rgba(0,0,0,0.12);
|
|---|
| 505 | }
|
|---|
| 506 | .accent-swatch-name { color: var(--ink); text-align: center; font-weight: 500; }
|
|---|
| 507 |
|
|---|
| 508 | .audio-flash { padding: 0.75rem 1rem; border-radius: 6px; margin-bottom: 1rem; font-size: 0.9rem; }
|
|---|
| 509 | .audio-flash--ok { background: rgba(40, 160, 90, 0.15); color: #2a9d5e; border: 1px solid rgba(40, 160, 90, 0.3); }
|
|---|
| 510 | .audio-flash--err { background: rgba(200, 60, 60, 0.15); color: #c33; border: 1px solid rgba(200, 60, 60, 0.3); }
|
|---|
| 511 |
|
|---|
| 512 | .form-hint { font-size: 0.8rem; color: var(--ink-muted, var(--ink-soft)); margin: 0; }
|
|---|
| 513 | .form-hint-inline { font-size: 0.78rem; color: var(--ink-muted, var(--ink-soft)); font-weight: 400; margin-left: .25rem; }
|
|---|
| 514 |
|
|---|
| 515 | /* P63 β Profile photo picker
|
|---|
| 516 | Mobile-first: thumb stacks above input/buttons. β₯520px the thumb floats
|
|---|
| 517 | to the left and the controls fill the remaining width. */
|
|---|
| 518 | .photo-picker {
|
|---|
| 519 | display: flex;
|
|---|
| 520 | flex-direction: column;
|
|---|
| 521 | gap: .65rem;
|
|---|
| 522 | align-items: stretch;
|
|---|
| 523 | }
|
|---|
| 524 | .photo-thumb {
|
|---|
| 525 | position: relative;
|
|---|
| 526 | width: 88px; height: 88px;
|
|---|
| 527 | border-radius: 50%;
|
|---|
| 528 | border: 1px solid var(--rule);
|
|---|
| 529 | background: var(--paper-2);
|
|---|
| 530 | overflow: hidden;
|
|---|
| 531 | flex-shrink: 0;
|
|---|
| 532 | display: flex; align-items: center; justify-content: center;
|
|---|
| 533 | }
|
|---|
| 534 | .photo-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
|---|
| 535 | .photo-thumb .photo-empty {
|
|---|
| 536 | display: flex; color: var(--ink-muted, var(--ink-soft)); opacity: .55;
|
|---|
| 537 | }
|
|---|
| 538 | .photo-thumb .photo-empty svg { width: 36px; height: 36px; }
|
|---|
| 539 | .photo-thumb:not([data-empty]) .photo-empty { display: none; }
|
|---|
| 540 | .photo-thumb[data-empty] img { display: none; }
|
|---|
| 541 |
|
|---|
| 542 | .photo-controls {
|
|---|
| 543 | display: flex;
|
|---|
| 544 | flex-direction: column;
|
|---|
| 545 | gap: .5rem;
|
|---|
| 546 | flex: 1;
|
|---|
| 547 | min-width: 0;
|
|---|
| 548 | }
|
|---|
| 549 | .photo-controls input[type="text"] { width: 100%; }
|
|---|
| 550 | .photo-actions {
|
|---|
| 551 | display: flex; align-items: center; flex-wrap: wrap;
|
|---|
| 552 | gap: .4rem;
|
|---|
| 553 | }
|
|---|
| 554 | .photo-actions .btn { padding: .45rem .75rem; font-size: .85rem; }
|
|---|
| 555 | .photo-status { margin-left: .25rem; }
|
|---|
| 556 | .photo-status.is-error { color: #c2410c; }
|
|---|
| 557 |
|
|---|
| 558 | @media (min-width: 520px) {
|
|---|
| 559 | .photo-picker { flex-direction: row; align-items: flex-start; gap: 1rem; }
|
|---|
| 560 | .photo-controls { gap: .55rem; }
|
|---|
| 561 | }
|
|---|
| 562 | .profile-link-row {
|
|---|
| 563 | display: grid; grid-template-columns: 160px 1fr auto; gap: 0.5rem; margin-bottom: 0.5rem;
|
|---|
| 564 | }
|
|---|
| 565 | .profile-link-row select { padding: 0.45rem 0.6rem; }
|
|---|
| 566 | .profile-links-fieldset details > label { margin-top: 0.4rem; }
|
|---|
| 567 | </style>
|
|---|