source: Klonkt/src/assets/js/mod/admin-site-edit.js@ 0824351

main
Last change on this file since 0824351 was 0824351, checked in by Robin <roboburr@…>, 4 weeks ago

Het restje van de verhuizing: een lege regel in admin-site-edit

Het paneel is daar weggehaald, maar de blanco regel bleef staan. Een diff van
een regel is precies wat je later niet meer kunt plaatsen.

Co-Authored-By: Claude Opus 5 <noreply@…>

  • Property mode set to 100644
File size: 5.1 KB
Line 
1// Site bewerken in beheer (pages/admin-site-edit.ejs) -- verplaatst uit inline script, shaer-bqr.
2//
3// Inline script in een pagina wordt door de CSP geweigerd zodra je die pagina via
4// een link BINNEN de site opent: de nonce rouleert per verzoek (shaer-0i6). Dit
5// bestand wordt door de bootstrap in shell.ejs geladen en heeft dat probleem niet.
6//
7// Alles hier hoort GEDELEGEERD te luisteren (op document, niet op een element dat
8// er nu staat) en tegen een tweede aanroep te kunnen.
9
10// Element-bedrading leeft zo lang als de pagina; de bootstrap roept init()
11// aan bij elke paginawissel waarop deze module actief is (shaer-5s1).
12export function init() { run(); }
13
14function run() {
15 (function(){
16 if (window.__themePreviewWired) return; window.__themePreviewWired = true;
17 var html = document.documentElement;
18 function applyAccent(hex){
19 if(!/^#[0-9a-fA-F]{6}$/.test(hex)) return;
20 var sa = document.getElementById('pcms-site-accent');
21 if(!sa){ sa = document.createElement('style'); sa.id = 'pcms-site-accent'; document.head.appendChild(sa); }
22 sa.textContent = ':root,[data-palette]{--accent:'+hex+';--accent-soft:color-mix(in srgb,'+hex+' 80%,white);--accent-tint:color-mix(in srgb,'+hex+' 12%,transparent);}';
23 }
24 document.addEventListener('change', function(e){
25 var t = e.target; if(!t || !t.name) return;
26 if(t.name === 'palette'){ html.setAttribute('data-palette', t.value); }
27 else if(t.name === 'accent'){ applyAccent(t.value); }
28 else if(t.name === 'theme_override'){
29 if(t.value === 'light' || t.value === 'dark'){ html.setAttribute('data-theme', t.value); }
30 else { var dk = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; html.setAttribute('data-theme', dk ? 'dark' : 'light'); }
31 }
32 });
33 })();
34
35
36// ── volgend blok ──
37
38(function() {
39 // Slug URL field: show the real host as a dimmed prefix (the slug itself is
40 // coloured via CSS). Hub → host/user/<slug>, otherwise host/<slug>.
41 var sh = document.getElementById('slug-host');
42 if (sh) sh.textContent = location.host + (sh.dataset.prefix || '/');
43})();
44(function() {
45 // Profile-links repeater: add row from <template>, remove on click.
46 var rows = document.getElementById('profile-links-rows');
47 var tpl = document.getElementById('profile-link-template');
48 var add = document.getElementById('profile-link-add');
49 if (!rows || !tpl || !add) return;
50
51 add.addEventListener('click', function() {
52 var clone = tpl.content.cloneNode(true);
53 rows.appendChild(clone);
54 });
55 rows.addEventListener('click', function(e) {
56 if (e.target && e.target.classList.contains('pl-remove')) {
57 var row = e.target.closest('.profile-link-row');
58 if (row) row.remove();
59 }
60 });
61})();
62
63// P63 — Profile photo picker: upload via /admin/sites/upload-photo,
64// then write the returned URL into the visible input. Live thumb preview.
65(function() {
66 var picker = document.getElementById('photo-picker');
67 if (!picker) return;
68 var thumb = document.getElementById('photo-thumb');
69 var preview = document.getElementById('photo-preview');
70 var urlEl = document.getElementById('photo-url');
71 var trigger = document.getElementById('photo-upload-trigger');
72 var clear = document.getElementById('photo-clear');
73 var field = document.getElementById('photo-upload-field');
74 var status = document.getElementById('photo-status');
75
76 function showPreview(url) {
77 if (url) {
78 preview.src = url;
79 preview.hidden = false;
80 thumb.removeAttribute('data-empty');
81 clear.hidden = false;
82 } else {
83 preview.src = '';
84 preview.hidden = true;
85 thumb.setAttribute('data-empty', '');
86 clear.hidden = true;
87 }
88 }
89
90 if (urlEl) {
91 urlEl.addEventListener('input', function() {
92 showPreview(urlEl.value.trim());
93 });
94 }
95
96 if (trigger && field) {
97 trigger.addEventListener('click', function() { field.click(); });
98 field.addEventListener('change', async function() {
99 var file = field.files && field.files[0];
100 if (!file) return;
101 status.classList.remove('is-error');
102 status.textContent = 'Uploaden…';
103 try {
104 var fd = new FormData();
105 fd.append('photo', file);
106 var r = await fetch('/admin/sites/upload-photo', {
107 method: 'POST',
108 body: fd,
109 credentials: 'same-origin',
110 });
111 var j = await r.json();
112 if (!r.ok || !j.ok) throw new Error(j.error || ('Upload mislukt (' + r.status + ')'));
113 urlEl.value = j.url;
114 showPreview(j.url);
115 status.textContent = 'Geüpload ✓';
116 setTimeout(function() { status.textContent = ''; }, 2000);
117 } catch (e) {
118 status.classList.add('is-error');
119 status.textContent = 'Mislukt: ' + e.message;
120 } finally {
121 field.value = '';
122 }
123 });
124 }
125
126 if (clear) {
127 clear.addEventListener('click', function() {
128 urlEl.value = '';
129 showPreview('');
130 status.textContent = '';
131 // Note: doesn't delete the file from disk — saving the form with empty
132 // URL leaves the file orphaned on the server. Acceptable for now.
133 });
134 }
135})();
136}
Note: See TracBrowser for help on using the repository browser.