mejora vista de votación: modal, alineación y auto-reproducción del ganador

- Tarjetas de votación ahora abren el modal de canciones al hacer clic en la imagen/nombre
- Botón "Votar" siempre alineado al fondo independiente del alto de cada tarjeta
- Muestra descripción de la playlist en modo votación
- Emoji de playlist escala proporcionalmente usando container queries (55cqi)
- Al cerrar la votación, reproduce automáticamente la playlist con más votos

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-23 13:59:06 -04:00
parent a1b9c0139d
commit 94cda7293f
2 changed files with 29 additions and 5 deletions
+27 -4
View File
@@ -243,9 +243,18 @@ async function fetchVotingStatus() {
const res = await fetch('/voting/status'); const res = await fetch('/voting/status');
const data = await res.json(); const data = await res.json();
const wasOpen = votingOpen;
votingOpen = data.is_open; votingOpen = data.is_open;
myVotedPlaylistId = data.my_last_vote_playlist_id; myVotedPlaylistId = data.my_last_vote_playlist_id;
if (wasOpen && !votingOpen && data.playlists?.length) {
const totalVotes = data.playlists.reduce((s, p) => s + p.votes, 0);
if (totalVotes > 0) {
const winner = data.playlists.reduce((best, p) => p.votes > best.votes ? p : best);
playItem(winner.spotify_id, winner.spotify_type);
}
}
// Solo sincronizar cooldown desde servidor si no hay timer local activo // Solo sincronizar cooldown desde servidor si no hay timer local activo
if (cooldownRemaining <= 0 && data.cooldown_remaining > 0) { if (cooldownRemaining <= 0 && data.cooldown_remaining > 0) {
startCooldownTick(data.cooldown_remaining); startCooldownTick(data.cooldown_remaining);
@@ -291,9 +300,12 @@ function renderPlaylists(playlists, isVoting, myVote) {
const btnLabel = cooldownRemaining > 0 ? `${cooldownRemaining}s` : '👍 Votar'; const btnLabel = cooldownRemaining > 0 ? `${cooldownRemaining}s` : '👍 Votar';
const btnDisabled = cooldownRemaining > 0 ? 'disabled' : ''; const btnDisabled = cooldownRemaining > 0 ? 'disabled' : '';
return ` return `
<div class="playlist-card clickable voting-card ${isVoted ? 'voted' : ''}"> <div class="playlist-card voting-card ${isVoted ? 'voted' : ''}">
${img} <div class="voting-card-top clickable" onclick='openTracksModal(${JSON.stringify(pl)})'>
<div class="playlist-card-name">${pl.name}</div> ${img}
<div class="playlist-card-name">${pl.name}</div>
${pl.description ? `<div class="playlist-card-desc">${pl.description}</div>` : ''}
</div>
<div class="vote-bar-wrap"><div class="vote-bar" style="width:${pct}%"></div></div> <div class="vote-bar-wrap"><div class="vote-bar" style="width:${pct}%"></div></div>
<div class="vote-info"> <div class="vote-info">
<span class="vote-count">${pl.votes} votos</span> <span class="vote-count">${pl.votes} votos</span>
@@ -416,7 +428,18 @@ setInterval(fetchVotingStatus, 5000);
gap: .5rem; gap: .5rem;
} }
.voting-card { cursor: pointer; position: relative; } .voting-card { position: relative; }
.voting-card-top { cursor: pointer; flex: 1; width: 100%; display: flex; flex-direction: column; align-items: center; gap: 0.4rem; }
.voting-card-top:hover .playlist-card-name { color: var(--green); }
.playlist-card-desc {
font-size: 0.7rem;
color: var(--text-muted);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
text-align: center;
}
.voting-card.voted { border: 2px solid var(--green); } .voting-card.voted { border: 2px solid var(--green); }
.vote-bar-wrap { .vote-bar-wrap {
+2 -1
View File
@@ -197,8 +197,9 @@ a:hover { text-decoration: underline; }
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: 2rem; font-size: 2rem;
container-type: inline-size;
} }
.playlist-thumb-emoji { font-size: 2.4rem; } .playlist-thumb-emoji { font-size: 55cqi; }
.playlist-card-name { .playlist-card-name {
font-size: 0.78rem; font-size: 0.78rem;