gcp-hockey-results/motm_app/templates/vote_chart.html
2025-10-09 22:59:26 +08:00

356 lines
10 KiB
HTML

{% extends "base.html" %}
{% block title %}Vote Results - HKFC MOTM System{% endblock %}
{% block content %}
<!-- Page Header -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-body text-center">
<h1 class="card-title">
<i class="fas fa-chart-bar text-primary me-2"></i>Current Match Vote Results
</h1>
<p class="lead text-muted">MOTM and DotD votes for match on {{ _matchDate[:4] }}-{{ _matchDate[4:6] }}-{{ _matchDate[6:8] }}</p>
</div>
</div>
</div>
</div>
<!-- Chart Container -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header bg-success text-white">
<h5 class="card-title mb-0">
<i class="fas fa-poll me-2"></i>Vote Counts
</h5>
</div>
<div class="card-body">
<div id="vote-chart-container">
<!-- Chart will be loaded here -->
<div class="text-center py-5">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<p class="mt-3 text-muted">Loading vote data...</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Legend -->
<div class="row mt-4">
<div class="col-12">
<div class="card">
<div class="card-body">
<h6 class="card-title">
<i class="fas fa-info-circle me-2"></i>Legend
</h6>
<div class="row">
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<div class="motm-bar me-3" style="width: 20px; height: 20px; background: linear-gradient(135deg, #28a745, #20c997); border-radius: 4px;"></div>
<span><strong>MOTM Votes</strong> - Man of the Match</span>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<div class="dotd-bar me-3" style="width: 20px; height: 20px; background: linear-gradient(135deg, #dc3545, #fd7e14); border-radius: 4px;"></div>
<span><strong>DotD Votes</strong> - Dick of the Day</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Navigation -->
<div class="row mt-4">
<div class="col-12 text-center">
<a href="/admin" class="btn btn-primary">
<i class="fas fa-arrow-left me-2"></i>Back to Admin
</a>
</div>
</div>
<style>
.vote-player-card {
background: #fff;
border: 1px solid #e9ecef;
border-radius: 8px;
padding: 1rem;
margin-bottom: 1rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
transition: transform 0.2s, box-shadow 0.2s;
}
.vote-player-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
.vote-player-name {
font-size: 1.1rem;
font-weight: 600;
color: #2c3e50;
margin-bottom: 0.5rem;
}
.vote-stats {
display: flex;
gap: 1rem;
align-items: center;
}
.vote-stat {
display: flex;
align-items: center;
gap: 0.5rem;
}
.vote-stat-value {
font-size: 1.5rem;
font-weight: 700;
min-width: 2rem;
text-align: center;
}
.vote-stat-label {
font-size: 0.9rem;
color: #6c757d;
font-weight: 500;
}
.vote-bar-container {
flex: 1;
margin-left: 1rem;
}
.vote-bar {
height: 8px;
border-radius: 4px;
margin-bottom: 4px;
position: relative;
overflow: hidden;
background-color: #e9ecef; /* Light gray background for empty bars */
}
.vote-bar-motm {
background-color: rgba(40, 167, 69, 0.1); /* Very light green background */
}
.vote-bar-dotd {
background-color: rgba(220, 53, 69, 0.1); /* Very light red background */
}
.vote-bar-fill {
height: 100%;
border-radius: 4px;
transition: width 0.8s ease-in-out;
}
.vote-bar-motm .vote-bar-fill {
background: linear-gradient(90deg, #28a745, #20c997);
}
.vote-bar-dotd .vote-bar-fill {
background: linear-gradient(90deg, #dc3545, #fd7e14);
}
.vote-no-data {
text-align: center;
padding: 3rem;
color: #6c757d;
}
.vote-no-data i {
font-size: 3rem;
margin-bottom: 1rem;
color: #dee2e6;
}
.vote-ranking {
display: inline-flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
background: linear-gradient(135deg, #007bff, #0056b3);
color: white;
border-radius: 50%;
font-weight: 700;
font-size: 0.9rem;
margin-right: 1rem;
}
.vote-ranking.gold {
background: linear-gradient(135deg, #ffd700, #ffb347);
}
.vote-ranking.silver {
background: linear-gradient(135deg, #c0c0c0, #a8a8a8);
}
.vote-ranking.bronze {
background: linear-gradient(135deg, #cd7f32, #b8860b);
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
loadVoteData();
});
function loadVoteData() {
fetch('/api/vote-results')
.then(response => {
console.log('Vote response status:', response.status);
return response.json();
})
.then(data => {
console.log('Raw vote API data:', data);
renderVoteChart(data);
})
.catch(error => {
console.error('Error loading vote data:', error);
renderNoData();
});
}
function renderVoteChart(data) {
const container = document.getElementById('vote-chart-container');
console.log('Vote Data received:', data);
if (!data || data.length === 0) {
renderNoData();
return;
}
// Get the match date from the first item's keys
const matchDate = '{{ _matchDate }}';
const motmKey = 'motm_' + matchDate;
const dotdKey = 'dotd_' + matchDate;
console.log('Using keys:', motmKey, dotdKey);
// Sort by MOTM votes (descending), then by DotD votes (ascending for better ranking)
data.sort((a, b) => {
const aMotm = a[motmKey] || 0;
const bMotm = b[motmKey] || 0;
const aDotd = a[dotdKey] || 0;
const bDotd = b[dotdKey] || 0;
if (bMotm !== aMotm) {
return bMotm - aMotm;
}
return aDotd - bDotd;
});
// Find max values for independent scaling - each bar type scales to its own maximum
const maxMotm = Math.max(...data.map(p => p[motmKey] || 0));
const maxDotd = Math.max(...data.map(p => p[dotdKey] || 0));
let html = '';
console.log('Max values calculated:', {maxMotm, maxDotd});
data.forEach((player, index) => {
console.log('Processing vote player:', player);
const ranking = index + 1;
const rankingClass = ranking === 1 ? 'gold' : ranking === 2 ? 'silver' : ranking === 3 ? 'bronze' : '';
const motmVotes = player[motmKey] || 0;
const dotdVotes = player[dotdKey] || 0;
console.log(`${player.playerName}: MOTM=${motmVotes}, DotD=${dotdVotes}`);
html += '<div class="vote-player-card">';
html += '<div class="d-flex align-items-center">';
html += '<div class="vote-ranking ' + rankingClass + '">' + ranking + '</div>';
html += '<div class="flex-grow-1">';
html += '<div class="vote-player-name">' + (player.playerName || 'Unknown Player') + '</div>';
html += '<div class="vote-stats">';
html += '<div class="vote-stat">';
html += '<div class="vote-stat-value text-success">' + motmVotes + '</div>';
html += '<div class="vote-stat-label">MOTM</div>';
html += '</div>';
html += '<div class="vote-stat">';
html += '<div class="vote-stat-value text-danger">' + dotdVotes + '</div>';
html += '<div class="vote-stat-label">DotD</div>';
html += '</div>';
// Calculate percentages with debugging
const motmPercent = maxMotm > 0 ? (motmVotes / maxMotm * 100) : 0;
const dotdPercent = maxDotd > 0 ? (dotdVotes / maxDotd * 100) : 0;
console.log(`${player.playerName} percentages: MOTM=${motmPercent.toFixed(1)}%, DotD=${dotdPercent.toFixed(1)}%`);
html += '<div class="vote-bar-container">';
html += '<div class="vote-bar vote-bar-motm">';
html += '<div class="vote-bar-fill" style="width: ' + motmPercent + '%"></div>';
html += '</div>';
html += '<div class="vote-bar vote-bar-dotd">';
html += '<div class="vote-bar-fill" style="width: ' + dotdPercent + '%"></div>';
html += '</div>';
html += '</div>';
html += '</div>';
html += '</div>';
html += '</div>';
html += '</div>';
});
container.innerHTML = html;
// Verify bar widths are set correctly
setTimeout(() => {
verifyBarWidths();
}, 100);
// Animate bars after a short delay
setTimeout(() => {
const bars = container.querySelectorAll('.vote-bar-fill');
bars.forEach(bar => {
const width = bar.style.width;
bar.style.width = '0%';
setTimeout(() => {
bar.style.width = width;
}, 100);
});
}, 200);
}
function renderNoData() {
const container = document.getElementById('vote-chart-container');
container.innerHTML = '<div class="vote-no-data">' +
'<i class="fas fa-chart-line"></i>' +
'<h4>No Vote Data Available</h4>' +
'<p>No votes have been cast for this match yet.</p>' +
'<p class="text-muted">Votes will appear here once players start voting.</p>' +
'</div>';
}
// Test function to verify basic functionality
function testVoteChart() {
const testData = [
{playerName: 'Test Player 1', motm_{{ _matchDate }}: 3, dotd_{{ _matchDate }}: 1},
{playerName: 'Test Player 2', motm_{{ _matchDate }}: 2, dotd_{{ _matchDate }}: 2}
];
console.log('Testing with sample vote data:', testData);
renderVoteChart(testData);
}
// Add a simple test to verify bar widths are being set
function verifyBarWidths() {
const bars = document.querySelectorAll('.vote-bar-fill');
console.log('Found bars:', bars.length);
bars.forEach((bar, index) => {
const width = bar.style.width;
console.log(`Bar ${index}: width = ${width}`);
});
}
</script>
{% endblock %}