source: Klonkt/src/views/pages/admin-playlists.ejs@ 5280532

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

Beheer: admin-audio neemt de maten van de gedeelde romp over

Robin zag dat /admin/audio nog de oude knop had en /admin/playlists de
goede. De markup was al gelijk -- beide gebruiken de terugknop-partial --
maar admin-audio had een EIGEN .ax-btn in zijn style-blok, en die staat
na de include en wint dus.

Het bleek geen los geval. admin-audio droeg een oudere, systematisch
kleinere kopie van het hele systeem:

.ax-btn 40px / radius 6 / 0.9rem -> 44px / radius 8 / 0.95rem
.ax-track hoes 56px / radius 10 -> 64px / radius 12
.ax-track-title 0.95rem -> 1.05rem
.ax-track-sub 0.8rem -> 0.85rem
@media 480px hoes 48px -> 56px

De partial kwam van admin-playlists, en dat is precies de versie die er
"goed" uitzag. Alle tien de afwijkingen zijn nu weg; audio en playlists
hebben nul regels die van de partial verschillen.

TWEE DINGEN DIE ECHT VERANDEREN, en niet alleen in maat:

  • Lange tracktitels op /admin/audio WRAPPEN nu in plaats van af te kappen met een ellipsis. De gedeelde .ax-track-title is een flex-regel (playlists zet er een pill in) en text-overflow werkt daar niet. De hele titel blijft zo zichtbaar; dat leek me de betere kant van de ruil, maar het is een keuze en geen bijvangst.
  • Het ♫-teken in de lege toestand verliest zijn display-font en eigen kleur, en erft nu van .ax-empty.

object-fit: cover staat nu OP .ax-track-cover en niet alleen op een img
erbinnen: admin-audio zet die klasse rechtstreeks op een <img>,
admin-listeners op een omhulsel. Op een niet-vervangen element doet
object-fit niets, dus de tweede vorm merkt er niets van. Zonder dit had
het weghalen van audio's kopie de hoezen uitgerekt.

scripts/admin-css.mjs heeft er --forceer bij: die haalt OOK de afwijkende
regels weg, zodat de pagina de vorm van de partial overneemt. Apart van
--doen, want --doen verplaatst alleen en dit VERANDERT hoe een pagina
eruitziet -- dat hoort een expliciete keuze te zijn. Met --toon laat hij
per eigenschap zien wat er verschilt; zo zijn bovenstaande maten
gevonden.

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

  • Property mode set to 100644
File size: 4.2 KB
Line 
1<%
2// Admin: Playlists management
3// Shows all playlists for current site, with new/edit/delete actions.
4// The editor itself is the same modal that's used from the post-editor.
5const _csrf = (typeof csrfToken !== 'undefined' && csrfToken) || '';
6%>
7<%# De gedeelde ax-*-primitieven. MOET boven het eigen style-blok van deze
8 pagina staan: wat hieronder blijft staan wijkt bewust af en hoort dus
9 later te komen, anders wint de partial ervan. %>
10<%- include('../partials/admin-styles') %>
11<div class="container ax-page">
12
13 <%- include('../partials/admin-back') %>
14 <%- include('../partials/media-tabs', { active: 'playlists', audioOn: true }) %>
15 <header class="ax-header">
16 <div>
17 <h1><%= t('apl.title') %></h1>
18 <p class="ax-tagline">
19 <%= t('apl.tagline_pre') %> <code>[[playlist:&lt;id&gt;]]</code><%= t('apl.tagline_post') %>
20 </p>
21 </div>
22 </header>
23
24 <%# Sticky-on-mobile create button — easier reach than a header button. %>
25 <div class="ax-actions-bar">
26 <button type="button" class="ax-btn ax-btn-primary" id="pl-new-btn">
27 + <%= t('apl.new_playlist') %>
28 </button>
29 </div>
30
31 <% if (!playlists.length) { %>
32 <div class="ax-card">
33 <div class="ax-empty">
34 <div class="ax-empty-icon">📃</div>
35 <p><%= t('apl.none') %></p>
36 <p class="ax-empty-sub"><%= t('apl.none_sub') %></p>
37 </div>
38 </div>
39 <% } else { %>
40 <%
41 // Pre-fetch loop labels (defensive — keep `t` calls out of the loop body).
42 var _aplPlaylist = t('apl.pill_playlist');
43 var _aplAlbum = t('apl.pill_album');
44 var _aplTrack = t('apl.track');
45 var _aplTracks = t('apl.tracks');
46 var _aplCopy = t('apl.copy_click');
47 var _aplEdit = t('apl.edit');
48 var _aplDelete = t('apl.delete');
49 %>
50 <ul class="ax-list">
51 <% playlists.forEach(function(p) { %>
52 <li class="ax-track" data-id="<%= p.id %>">
53 <div class="ax-track-cover" <%- p.cover ? `style="background-image:url('${p.cover}'); background-size:cover; background-position:center"` : '' %>>
54 <% if (!p.cover) { %><span><%= p.kind === 'playlist' ? '📃' : '💿' %></span><% } %>
55 </div>
56
57 <div class="ax-track-meta">
58 <div class="ax-track-title">
59 <%= p.title %>
60 <span class="ax-pill"><%= p.kind === 'playlist' ? '📃 ' + _aplPlaylist : '💿 ' + _aplAlbum %></span>
61 </div>
62 <div class="ax-track-sub">
63 <span><%= p.track_count %> <%= p.track_count === 1 ? _aplTrack : _aplTracks %></span>
64 <% if (p.artist) { %><span class="ax-track-sep">·</span><span><%= p.artist %></span><% } %>
65 <% if (p.year) { %><span class="ax-track-sep">·</span><span><%= p.year %></span><% } %>
66 </div>
67 <code class="ax-embed" data-copy="[[playlist:<%= p.id %>]]" title="<%= _aplCopy %>">[[playlist:<%= p.id %>]]</code>
68 </div>
69
70 <div class="ax-track-actions">
71 <button type="button" class="ax-icon-btn" data-pl-edit data-id="<%= p.id %>" aria-label="<%= _aplEdit %>" title="<%= _aplEdit %>">✎</button>
72 <button type="button" class="ax-icon-btn ax-icon-btn-danger" data-pl-delete data-id="<%= p.id %>" data-title="<%= p.title %>" aria-label="<%= _aplDelete %>" title="<%= _aplDelete %>">🗑</button>
73 </div>
74 </li>
75 <% }); %>
76 </ul>
77 <% } %>
78
79</div>
80
81<%# Editor modal lives in its own partial so the post-editor can include it too %>
82<%- include('../partials/playlist-editor', { csrfToken: _csrf }) %>
83
84<style>
85/* Re-use the admin-audio (.ax-*) primitives. Define them here too so this
86 page is self-contained — both pages get refreshed independently. */
87
88.ax-page { max-width: 880px; margin: 1.5rem auto 4rem; padding: 0 1rem; }
89
90/* ─── Mobile ──────────────────────────────────────────────────── */
91@media (max-width: 480px) {
92}
93</style>
94
95<%# Het script staat in assets/js/mod/admin-playlists.js; de gegevens via partials/page-data.ejs (shaer-bqr). %>
96<%- include('../partials/page-data', { pageData: { csrf: _csrf, copied: t('apl.copied'), delConfirm: t('apl.delete_confirm'), delFailed: t('apl.delete_failed') } }) %>
Note: See TracBrowser for help on using the repository browser.