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

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

Beheer: de terugknop op alle vijf dezelfde maat

Robin zag het (16-8): de knop naar /admin verschilde van grootte. Drie
pagina's kregen bij de omzetting class="ax-btn", audio en playlists
hielden de globale class="btn". Dat scheelt zichtbaar --

.btn padding 0.55rem 1rem · radius 5 · geen minimumhoogte
.ax-btn padding 0.6rem 1.1rem · radius 8 · min-height 44px

Het wordt .ax-btn: dit zijn ax-pagina's, en 44px is de maat waar een
vinger op past.

Nu een partial, voor een regel van niets, om dezelfde reden als bij de
tabbalk: een gedeeld element dat op elke pagina los staat loopt uiteen.
Dit is de tweede keer vandaag dat dat gebeurt, en de eerste keer had ik
het kunnen zien -- ik heb de tabbalk toen wel gedeeld gemaakt en de knop
ernaast niet.

scripts/render-admin-views.mjs dekt nu alle vijf de pagina's en
controleert de terugknop op zijn KLASSENAAM. Dat verschil ziet er in de
bron uit als hetzelfde, dus een controle die alleen kijkt of er een link
staat zou het niet vinden. Nagelopen dat hij kan falen: met .btn erin
meldt hij 10 stuk, hersteld nul.

Opmaak nagemeten met admin-css-gelijk.mjs: geen regel verdwenen, geen
regel veranderd, geen regel erbij op alle vijf.

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

  • Property mode set to 100644
File size: 4.5 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.ax-btn-primary:hover { opacity: 0.92; }
90.ax-track-cover {
91 width: 64px; height: 64px; border-radius: 8px;
92 background: var(--paper-2);
93 display: flex; align-items: center; justify-content: center;
94 font-family: var(--font-display, serif);
95 font-size: 1.5rem; color: var(--accent);
96 flex-shrink: 0;
97}
98
99/* ─── Mobile ──────────────────────────────────────────────────── */
100@media (max-width: 480px) {
101}
102</style>
103
104<%# Het script staat in assets/js/mod/admin-playlists.js; de gegevens via partials/page-data.ejs (shaer-bqr). %>
105<%- 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.