let me = null; let suggestions = []; const expanded = new Set(); async function loadSuggestions() { suggestions = await api.get('/api/suggestions'); renderSuggestions(); } function statusLabel(status) { return { pending: 'En attente', approved: 'Approuvé', rejected: 'Rejeté' }[status] || status; } function suggestionTemplate(s) { const isOpen = expanded.has(s.id); const embed = youtubeEmbedUrl(s.youtube_url); return `
${escapeHtml(s.title)}${s.artist ? ` — ${escapeHtml(s.artist)}` : ''}
Proposé par ${escapeHtml(s.suggested_by_name)} · ${statusLabel(s.status)}
✔ ${s.approve_count} ✘ ${s.reject_count}

Chargement…

`; } function detailTemplate(s) { const embed = youtubeEmbedUrl(s.youtube_url); const myVote = s.votes.find((v) => v.user_id === me.id); return ` ${embed ? `
` : `

${escapeHtml(s.youtube_url)}

`}
${s.votes.length ? s.votes.map(voteItemTemplate).join('') : '

Aucun vote pour le moment.

'}
${me.isAdmin ? `
` : ''} `; } function voteItemTemplate(v) { const icon = v.vote === 'approve' ? '✔' : '✘'; return `
${escapeHtml(v.voter_name)} ${icon} ${v.comment ? `— ${escapeHtml(v.comment)}` : ''}
`; } function renderSuggestions() { const container = document.getElementById('suggestions-list'); renderList(container, suggestions, suggestionTemplate, 'Aucune suggestion pour le moment.'); document.querySelectorAll('.toggle-detail').forEach((btn) => { btn.addEventListener('click', () => onToggleDetail(parseInt(btn.dataset.id, 10))); }); } async function onToggleDetail(id) { if (expanded.has(id)) { expanded.delete(id); } else { expanded.add(id); } renderSuggestions(); if (expanded.has(id)) { await loadDetail(id); } } async function loadDetail(id) { try { const detail = await api.get(`/api/suggestions/${id}`); const container = document.querySelector(`[data-detail-for="${id}"]`); if (!container) return; container.innerHTML = detailTemplate(detail); container.querySelector('.vote-form').addEventListener('submit', (e) => onVote(e, id)); const promoteBtn = container.querySelector('.promote-btn'); if (promoteBtn) promoteBtn.addEventListener('click', () => onPromote(id)); const rejectBtn = container.querySelector('.reject-btn'); if (rejectBtn) rejectBtn.addEventListener('click', () => onReject(id)); const deleteBtn = container.querySelector('.delete-btn'); if (deleteBtn) deleteBtn.addEventListener('click', () => onDelete(id)); } catch (err) { showError(err.message); } } async function onVote(e, id) { e.preventDefault(); const form = e.target; const vote = form.vote.value; const comment = form.comment.value.trim(); try { await api.post(`/api/suggestions/${id}/vote`, { vote, comment }); await loadSuggestions(); expanded.add(id); renderSuggestions(); await loadDetail(id); } catch (err) { showError(err.message); } } async function onPromote(id) { try { await api.post(`/api/suggestions/${id}/promote`, {}); await loadSuggestions(); expanded.add(id); renderSuggestions(); await loadDetail(id); } catch (err) { showError(err.message); } } async function onReject(id) { try { await api.patch(`/api/suggestions/${id}`, { status: 'rejected' }); await loadSuggestions(); expanded.add(id); renderSuggestions(); await loadDetail(id); } catch (err) { showError(err.message); } } async function onDelete(id) { try { await api.del(`/api/suggestions/${id}`); expanded.delete(id); await loadSuggestions(); } catch (err) { showError(err.message); } } async function onAddSuggestion(e) { e.preventDefault(); const form = e.target; const title = form.title.value.trim(); const artist = form.artist.value.trim(); const youtubeUrl = form.youtubeUrl.value.trim(); try { await api.post('/api/suggestions', { title, artist, youtubeUrl }); form.reset(); await loadSuggestions(); } catch (err) { showError(err.message); } } function showError(message) { document.getElementById('error').innerHTML = `
${escapeHtml(message)}
`; } (async function init() { me = await initNav('suggestions'); document.getElementById('add-suggestion-form').addEventListener('submit', onAddSuggestion); await loadSuggestions(); })();