Changeset 8afbdd6 in Klonkt for src/views/pages/account.ejs


Ignore:
Timestamp:
06/14/2026 06:07:51 PM (3 months ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
c869272
Parents:
b94c08e
Message:

feat: viewer role + artists directory + Klonkt Hub Beta rebrand

Viewer role (replaces the separate view-mode/readonly toggle):

  • 'kijker' is now a real role (VALID_ROLES) selectable in Admin. May view EVERYTHING including Admin, but cannot modify anything.
  • isViewer(user) (= role kijker or legacy readonly flag) is the source; requireGod/requireSiteManager(BySlug) let a viewer through (viewing), the global guard 403s every write. Existing readonly accounts are migrated on each role change (readonly=0).
  • Write leaks via GET patched: /prutter/new (INSERT) blocks for viewers, /prutter/:id skips markAsRead (UPDATE); WS upgrade rejects viewers (the HTTP guard doesn't cover WebSockets).
  • Clean "Viewer mode" page (viewer-blocked) instead of raw 403 text; styled sticky banner; account page shows read-only UI instead of an upload button that silently 403s. canMutate hides write buttons.

Scalability (>50 artists):

  • Hub home shows max 24 (most active first) + "All N artists ->".
  • New searchable, paginated /artiesten directory (hub only).
  • 'user' + 'artiesten' reserved as slugs.

Rebrand PrutFolio v1 -> Klonkt Hub Beta (footer, PWA manifest, account/
admin texts, default page title, startup, README; internal package +
PWA id 'prutfolio' remain for stability).

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/views/pages/account.ejs

    rb94c08e r8afbdd6  
    1212  <% if (error)   { %><div class="ax-flash ax-flash-err"><%= error %></div><% } %>
    1313
     14  <% if (isViewer) { %>
     15    <div class="ax-viewer-note" role="note">
     16      <span class="ax-viewer-ico" aria-hidden="true">👁️</span>
     17      <div>
     18        <strong>Kijker-modus</strong>
     19        <span>Dit is een demo-account. Je kunt alles bekijken, maar niets wijzigen — ook geen foto of bio.</span>
     20      </div>
     21    </div>
     22  <% } %>
     23
    1424  <%# ── PROFILE ────────────────────────────────────────────── %>
    1525  <section class="ax-card">
     
    1727
    1828    <div class="ax-profile-id">
    19       <form action="/account/avatar" method="post" enctype="multipart/form-data" class="ax-avatar-form">
    20         <label class="ax-avatar-wrap" title="Klik om je foto te wijzigen">
    21           <span class="ax-profile-avatar">
    22             <% if (account.avatar_url) { %>
    23               <img src="<%= account.avatar_url %>" alt="">
    24             <% } else { %>
    25               <span><%= account.username.charAt(0).toUpperCase() %></span>
    26             <% } %>
    27           </span>
    28           <span class="ax-avatar-edit" aria-hidden="true">
    29             <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
    30           </span>
    31           <input type="file" name="avatar" accept="image/jpeg,image/png,image/webp,image/gif" onchange="this.form.submit()" hidden>
    32         </label>
    33       </form>
     29      <% if (canMutate) { %>
     30        <form action="/account/avatar" method="post" enctype="multipart/form-data" class="ax-avatar-form">
     31          <label class="ax-avatar-wrap" title="Klik om je foto te wijzigen">
     32            <span class="ax-profile-avatar">
     33              <% if (account.avatar_url) { %>
     34                <img src="<%= account.avatar_url %>" alt="">
     35              <% } else { %>
     36                <span><%= account.username.charAt(0).toUpperCase() %></span>
     37              <% } %>
     38            </span>
     39            <span class="ax-avatar-edit" aria-hidden="true">
     40              <svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>
     41            </span>
     42            <input type="file" name="avatar" accept="image/jpeg,image/png,image/webp,image/gif" onchange="this.form.submit()" hidden>
     43          </label>
     44        </form>
     45      <% } else { %>
     46        <span class="ax-profile-avatar ax-profile-avatar-static">
     47          <% if (account.avatar_url) { %>
     48            <img src="<%= account.avatar_url %>" alt="">
     49          <% } else { %>
     50            <span><%= account.username.charAt(0).toUpperCase() %></span>
     51          <% } %>
     52        </span>
     53      <% } %>
    3454      <div class="ax-profile-meta">
    3555        <div class="ax-profile-name"><%= account.username %></div>
     
    3858          <div class="ax-profile-joined">Lid sinds <%= formatDate(account.created_at) %></div>
    3959        <% } %>
    40         <% if (account.avatar_url) { %>
     60        <% if (account.avatar_url && canMutate) { %>
    4161          <form action="/account/avatar/remove" method="post" class="ax-avatar-remove">
    4262            <button type="submit" class="ax-linkbtn">Foto verwijderen</button>
     
    4666    </div>
    4767
    48     <form action="/account/profile" method="post" class="ax-form ax-form-bio">
    49       <label class="ax-field">
    50         <span>Bio</span>
    51         <textarea name="bio" rows="3" maxlength="500" placeholder="Een korte regel over jezelf"><%= account.bio || '' %></textarea>
    52       </label>
    53       <div class="ax-form-actions">
    54         <button type="submit" class="ax-btn ax-btn-primary">Bio opslaan</button>
    55       </div>
    56     </form>
     68    <% if (canMutate) { %>
     69      <form action="/account/profile" method="post" class="ax-form ax-form-bio">
     70        <label class="ax-field">
     71          <span>Bio</span>
     72          <textarea name="bio" rows="3" maxlength="500" placeholder="Een korte regel over jezelf"><%= account.bio || '' %></textarea>
     73        </label>
     74        <div class="ax-form-actions">
     75          <button type="submit" class="ax-btn ax-btn-primary">Bio opslaan</button>
     76        </div>
     77      </form>
     78    <% } else { %>
     79      <div class="ax-form ax-form-bio">
     80        <label class="ax-field">
     81          <span>Bio</span>
     82          <textarea rows="3" disabled placeholder="Geen bio"><%= account.bio || '' %></textarea>
     83        </label>
     84      </div>
     85    <% } %>
    5786  </section>
    5887
    5988  <%# ── WACHTWOORD ────────────────────────────────────────── %>
     89  <%# In kijker-modus geen wachtwoord-sectie (niets te wijzigen). %>
     90  <% if (canMutate) { %>
    6091  <% if (hasPassword) { %>
    6192  <section class="ax-card">
     
    87118  </section>
    88119  <% } %>
     120  <% } %>
    89121
    90122</div>
     
    107139.ax-flash-ok  { background: rgba(40,160,90,.15); color: #2a9d5e; border: 1px solid rgba(40,160,90,.3); }
    108140.ax-flash-err { background: rgba(200,60,60,.15); color: #c33; border: 1px solid rgba(200,60,60,.3); }
     141
     142/* ─── Kijker-modus notice ──────────────────────────────────────── */
     143.ax-viewer-note {
     144  display: flex; align-items: flex-start; gap: 0.75rem;
     145  padding: 0.85rem 1rem; margin-bottom: 1rem;
     146  border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--rule));
     147  border-left: 3px solid var(--accent);
     148  border-radius: 10px;
     149  background: color-mix(in srgb, var(--accent) 8%, var(--paper));
     150}
     151.ax-viewer-ico { font-size: 1.1rem; line-height: 1.4; }
     152.ax-viewer-note strong { display: block; font-size: 0.92rem; }
     153.ax-viewer-note span { color: var(--ink-muted, var(--ink-soft)); font-size: 0.85rem; }
     154.ax-profile-avatar-static { flex-shrink: 0; }
     155.ax-form-bio textarea:disabled { opacity: 0.7; cursor: default; }
    109156
    110157/* ─── Cards ────────────────────────────────────────────────────── */
Note: See TracChangeset for help on using the changeset viewer.