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

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

Mixtape kiezen kan nu ook echt, in het scherm

De soort was er wel maar nergens te kiezen. Drie plekken, en twee ervan waren
weer dezelfde vorm die een derde soort niet afwijst maar opslokt:

  • De keuzelijst kreeg een derde optie.
  • kind: kindEl.value === 'playlist' ? 'playlist' : 'album' sloeg een mixtape op als album. Nu gaat de waarde zelf mee; normKind() op de server beslist.
  • const album = kindEl.value !== 'playlist' toonde uitgavevelden bij een mixtape, die de server vervolgens leegmaakt -- een scherm dat iets anders belooft dan het doet. Nu expliciet === 'album'.
  • De lijstweergave koos teken en woord met een ternair die er twee kende; een mixtape kreeg daardoor het albumjasje. Nu een tabel per soort.

Labels in nl/en/de, en de sleutel doorgegeven vanuit de partial -- dat laatste
ving playlist-editor-i18n.test.js meteen, precies waarvoor die test bestaat.

MOD_V naar 50.

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

  • Property mode set to 100644
File size: 4.6 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 _aplMixtape = t('apl.pill_mixtape');
45 // Een soort met een eigen teken en een eigen woord. De ternair die hier
46 // stond kende er maar twee, en toonde een mixtape dus als album.
47 var _aplTeken = { album: '💿', playlist: '📃', mixtape: '📼' };
48 var _aplWoord = { album: _aplAlbum, playlist: _aplPlaylist, mixtape: _aplMixtape };
49 var _aplTrack = t('apl.track');
50 var _aplTracks = t('apl.tracks');
51 var _aplCopy = t('apl.copy_click');
52 var _aplEdit = t('apl.edit');
53 var _aplDelete = t('apl.delete');
54 %>
55 <ul class="ax-list">
56 <% playlists.forEach(function(p) { %>
57 <li class="ax-track" data-id="<%= p.id %>">
58 <div class="ax-track-cover" <%- p.cover ? `style="background-image:url('${p.cover}'); background-size:cover; background-position:center"` : '' %>>
59 <% if (!p.cover) { %><span><%= _aplTeken[p.kind] || _aplTeken.album %></span><% } %>
60 </div>
61
62 <div class="ax-track-meta">
63 <div class="ax-track-title">
64 <%= p.title %>
65 <span class="ax-pill"><%= (_aplTeken[p.kind] || _aplTeken.album) + ' ' + (_aplWoord[p.kind] || _aplAlbum) %></span>
66 </div>
67 <div class="ax-track-sub">
68 <span><%= p.track_count %> <%= p.track_count === 1 ? _aplTrack : _aplTracks %></span>
69 <% if (p.artist) { %><span class="ax-track-sep">·</span><span><%= p.artist %></span><% } %>
70 <% if (p.year) { %><span class="ax-track-sep">·</span><span><%= p.year %></span><% } %>
71 </div>
72 <code class="ax-embed" data-copy="[[playlist:<%= p.id %>]]" title="<%= _aplCopy %>">[[playlist:<%= p.id %>]]</code>
73 </div>
74
75 <div class="ax-track-actions">
76 <button type="button" class="ax-icon-btn" data-pl-edit data-id="<%= p.id %>" aria-label="<%= _aplEdit %>" title="<%= _aplEdit %>">✎</button>
77 <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>
78 </div>
79 </li>
80 <% }); %>
81 </ul>
82 <% } %>
83
84</div>
85
86<%# Editor modal lives in its own partial so the post-editor can include it too %>
87<%- include('../partials/playlist-editor', { csrfToken: _csrf }) %>
88
89<style>
90/* Re-use the admin-audio (.ax-*) primitives. Define them here too so this
91 page is self-contained — both pages get refreshed independently. */
92
93.ax-page { max-width: 880px; margin: 1.5rem auto 4rem; padding: 0 1rem; }
94
95/* ─── Mobile ──────────────────────────────────────────────────── */
96@media (max-width: 480px) {
97}
98</style>
99
100<%# Het script staat in assets/js/mod/admin-playlists.js; de gegevens via partials/page-data.ejs (shaer-bqr). %>
101<%- 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.