update: new view for concert history

This commit is contained in:
Nathan FONTEYNE 2026-07-08 16:04:22 +02:00
parent 08717c24bc
commit 0773f39763
6 changed files with 155 additions and 8 deletions

View file

@ -670,6 +670,31 @@ button:disabled {
border: 0;
}
/* ---------- History timeline ---------- */
.timeline-concert { margin-bottom: 1.5rem; }
.timeline-songs {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 0.9rem;
margin-top: 0.5rem;
}
.timeline-song-card {
background: var(--surface-alt);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 0.7rem;
}
.timeline-song-title { font-weight: 600; }
.timeline-embed {
max-width: none;
margin-top: 0.5rem;
}
/* ---------- Setlist ---------- */
.setlist-columns {

View file

@ -12,10 +12,17 @@
<nav id="main-nav"></nav>
</header>
<main>
<h1>Historique des concerts</h1>
<p class="page-subtitle">Retrouvez la setlist jouée à chaque concert précédent.</p>
<div class="section-header">
<div>
<h1 style="margin:0">Historique des concerts</h1>
<p class="page-subtitle" style="margin:0.25rem 0 0">Du plus récent au plus ancien en défilant vers le bas.</p>
</div>
<button type="button" class="secondary" id="toggle-view-btn">Vue réduite</button>
</div>
<div id="error"></div>
<div id="history-list" class="card-grid"></div>
<div id="timeline-view"></div>
<div id="compact-view" class="card-grid" style="display:none"></div>
</main>
<script src="/js/api.js"></script>

View file

@ -1,28 +1,114 @@
let concerts = [];
let viewMode = 'timeline';
function formatDate(dateStr) {
const d = new Date(dateStr);
return d.toLocaleDateString('fr-FR', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
}
function formatDateShort(dateStr) {
const d = new Date(dateStr);
return d.toLocaleDateString('fr-FR', { year: 'numeric', month: 'long', day: 'numeric' });
}
function concertTemplate(c) {
function timelineSongCard(song) {
const embed = youtubeEmbedUrl(song.youtube_url);
return `
<div class="timeline-song-card">
<div class="timeline-song-title">${escapeHtml(song.title)}</div>
<div class="card-subtitle">${escapeHtml(song.artist)}</div>
${embed
? `<div class="youtube-embed timeline-embed"><iframe src="${embed}" loading="lazy" allowfullscreen></iframe></div>`
: `<p class="empty">Pas de lien YouTube</p>`}
${song.spotify_url ? `<div class="song-links"><a class="pill-link spotify" href="${escapeHtml(song.spotify_url)}" target="_blank" rel="noopener">&#9835; Spotify</a></div>` : ''}
${song.note ? `<p class="note">${escapeHtml(song.note)}</p>` : ''}
</div>
`;
}
function timelineConcertBlock(concert) {
const main = concert.songs.filter((s) => !s.is_encore);
const encore = concert.songs.filter((s) => s.is_encore);
return `
<div class="card timeline-concert">
<div class="card-title">${escapeHtml(concert.name || 'Concert')}</div>
<div class="card-subtitle">${escapeHtml(concert.venue || '')} · ${formatDate(concert.concert_date)}</div>
<div class="setlist-section">
<h3>Setlist</h3>
${main.length
? `<div class="timeline-songs">${main.map(timelineSongCard).join('')}</div>`
: '<p class="empty">Aucun morceau enregistré.</p>'}
</div>
${encore.length ? `
<div class="setlist-section">
<h3>Rappel</h3>
<div class="timeline-songs">${encore.map(timelineSongCard).join('')}</div>
</div>` : ''}
<a class="back-link" href="/history-detail.html?id=${concert.id}">Voir / modifier ce concert &rarr;</a>
</div>
`;
}
function concertCardTemplate(c) {
return `
<div class="card">
<a href="/history-detail.html?id=${c.id}" style="text-decoration:none;color:inherit">
<div class="card-title">${escapeHtml(c.name || 'Concert')}</div>
<div class="card-subtitle">${escapeHtml(c.venue || '')} · ${formatDate(c.concert_date)}</div>
<div class="card-subtitle">${escapeHtml(c.venue || '')} · ${formatDateShort(c.concert_date)}</div>
</a>
</div>
`;
}
function renderTimeline() {
const container = document.getElementById('timeline-view');
if (!concerts.length) {
container.innerHTML = '<p class="empty">Aucun concert passé enregistré.</p>';
return;
}
container.innerHTML = concerts.map(timelineConcertBlock).join('');
}
function renderCompact() {
const container = document.getElementById('compact-view');
renderList(container, concerts, concertCardTemplate, 'Aucun concert passé enregistré.');
}
function applyViewMode() {
const timelineEl = document.getElementById('timeline-view');
const compactEl = document.getElementById('compact-view');
const btn = document.getElementById('toggle-view-btn');
if (viewMode === 'timeline') {
timelineEl.style.display = 'block';
compactEl.style.display = 'none';
btn.textContent = 'Vue réduite';
} else {
timelineEl.style.display = 'none';
compactEl.style.display = 'grid';
btn.textContent = 'Vue chronologique';
}
}
function showError(message) {
document.getElementById('error').innerHTML = `<div class="error-banner">${escapeHtml(message)}</div>`;
}
(async function init() {
await initNav('history');
document.getElementById('toggle-view-btn').addEventListener('click', () => {
viewMode = viewMode === 'timeline' ? 'compact' : 'timeline';
applyViewMode();
});
try {
const history = await api.get('/api/setlists/history');
renderList(document.getElementById('history-list'), history, concertTemplate, 'Aucun concert passé enregistré.');
concerts = await api.get('/api/setlists/history?full=1');
renderTimeline();
renderCompact();
applyViewMode();
} catch (err) {
showError(err.message);
}