Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/views/pages/admin-site-edit.ejs

    r0475b13 r72ec6a4  
    126126    <%# Live preview: selecting an accent / theme / palette re-themes this page
    127127        instantly (reverts on reload; persists on save). %>
    128     <%# Het script van deze pagina staat in assets/js/mod/admin-site-edit.js (shaer-bqr). %>
     128    <script>
     129    (function(){
     130      if (window.__themePreviewWired) return; window.__themePreviewWired = true;
     131      var html = document.documentElement;
     132      function applyAccent(hex){
     133        if(!/^#[0-9a-fA-F]{6}$/.test(hex)) return;
     134        var sa = document.getElementById('pcms-site-accent');
     135        if(!sa){ sa = document.createElement('style'); sa.id = 'pcms-site-accent'; document.head.appendChild(sa); }
     136        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);}';
     137      }
     138      document.addEventListener('change', function(e){
     139        var t = e.target; if(!t || !t.name) return;
     140        if(t.name === 'palette'){ html.setAttribute('data-palette', t.value); }
     141        else if(t.name === 'accent'){ applyAccent(t.value); }
     142        else if(t.name === 'theme_override'){
     143          if(t.value === 'light' || t.value === 'dark'){ html.setAttribute('data-theme', t.value); }
     144          else { var dk = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; html.setAttribute('data-theme', dk ? 'dark' : 'light'); }
     145        }
     146      });
     147    })();
     148    </script>
    129149
    130150    <fieldset>
     
    255275</div>
    256276
    257 
     277<script>
     278(function() {
     279  // Slug URL field: show the real host as a dimmed prefix (the slug itself is
     280  // coloured via CSS). Hub → host/user/<slug>, otherwise host/<slug>.
     281  var sh = document.getElementById('slug-host');
     282  if (sh) sh.textContent = location.host + (sh.dataset.prefix || '/');
     283})();
     284(function() {
     285  // Profile-links repeater: add row from <template>, remove on click.
     286  var rows = document.getElementById('profile-links-rows');
     287  var tpl  = document.getElementById('profile-link-template');
     288  var add  = document.getElementById('profile-link-add');
     289  if (!rows || !tpl || !add) return;
     290
     291  add.addEventListener('click', function() {
     292    var clone = tpl.content.cloneNode(true);
     293    rows.appendChild(clone);
     294  });
     295  rows.addEventListener('click', function(e) {
     296    if (e.target && e.target.classList.contains('pl-remove')) {
     297      var row = e.target.closest('.profile-link-row');
     298      if (row) row.remove();
     299    }
     300  });
     301})();
     302
     303// P63 — Profile photo picker: upload via /admin/sites/upload-photo,
     304// then write the returned URL into the visible input. Live thumb preview.
     305(function() {
     306  var picker  = document.getElementById('photo-picker');
     307  if (!picker) return;
     308  var thumb   = document.getElementById('photo-thumb');
     309  var preview = document.getElementById('photo-preview');
     310  var urlEl   = document.getElementById('photo-url');
     311  var trigger = document.getElementById('photo-upload-trigger');
     312  var clear   = document.getElementById('photo-clear');
     313  var field   = document.getElementById('photo-upload-field');
     314  var status  = document.getElementById('photo-status');
     315
     316  function showPreview(url) {
     317    if (url) {
     318      preview.src = url;
     319      preview.hidden = false;
     320      thumb.removeAttribute('data-empty');
     321      clear.hidden = false;
     322    } else {
     323      preview.src = '';
     324      preview.hidden = true;
     325      thumb.setAttribute('data-empty', '');
     326      clear.hidden = true;
     327    }
     328  }
     329
     330  if (urlEl) {
     331    urlEl.addEventListener('input', function() {
     332      showPreview(urlEl.value.trim());
     333    });
     334  }
     335
     336  if (trigger && field) {
     337    trigger.addEventListener('click', function() { field.click(); });
     338    field.addEventListener('change', async function() {
     339      var file = field.files && field.files[0];
     340      if (!file) return;
     341      status.classList.remove('is-error');
     342      status.textContent = 'Uploaden…';
     343      try {
     344        var fd = new FormData();
     345        fd.append('photo', file);
     346        var r = await fetch('/admin/sites/upload-photo', {
     347          method: 'POST',
     348          body: fd,
     349          credentials: 'same-origin',
     350        });
     351        var j = await r.json();
     352        if (!r.ok || !j.ok) throw new Error(j.error || ('Upload mislukt (' + r.status + ')'));
     353        urlEl.value = j.url;
     354        showPreview(j.url);
     355        status.textContent = 'Geüpload ✓';
     356        setTimeout(function() { status.textContent = ''; }, 2000);
     357      } catch (e) {
     358        status.classList.add('is-error');
     359        status.textContent = 'Mislukt: ' + e.message;
     360      } finally {
     361        field.value = '';
     362      }
     363    });
     364  }
     365
     366  if (clear) {
     367    clear.addEventListener('click', function() {
     368      urlEl.value = '';
     369      showPreview('');
     370      status.textContent = '';
     371      // Note: doesn't delete the file from disk — saving the form with empty
     372      // URL leaves the file orphaned on the server. Acceptable for now.
     373    });
     374  }
     375})();
     376</script>
    258377
    259378<style>
Note: See TracChangeset for help on using the changeset viewer.