Refactoring fixes and adding functionality back

This commit is contained in:
Jonny Ervine 2025-10-07 22:03:36 +08:00
parent d5350aa4cb
commit a6febae8a8
17 changed files with 458 additions and 298 deletions

View File

@ -261,9 +261,9 @@ class DatabaseConfigManager:
# Create sample fixtures (only if they don't exist) # Create sample fixtures (only if they don't exist)
fixtures_data = [ fixtures_data = [
{'fixture_number': 1, 'date': datetime(2024, 1, 15), 'home_team': 'HKFC C', 'away_team': 'KCC A', 'venue': 'HKFC'}, {'fixture_number': 1, 'date': datetime(2025, 1, 15), 'home_team': 'HKFC C', 'away_team': 'KCC A', 'venue': 'HKFC'},
{'fixture_number': 2, 'date': datetime(2024, 1, 22), 'home_team': 'USRC A', 'away_team': 'HKFC C', 'venue': 'USRC'}, {'fixture_number': 2, 'date': datetime(2025, 1, 22), 'home_team': 'USRC A', 'away_team': 'HKFC C', 'venue': 'USRC'},
{'fixture_number': 3, 'date': datetime(2024, 1, 29), 'home_team': 'HKFC C', 'away_team': 'Valley A', 'venue': 'HKFC'}, {'fixture_number': 3, 'date': datetime(2025, 1, 29), 'home_team': 'HKFC C', 'away_team': 'Valley A', 'venue': 'HKFC'},
] ]
for fixture_data in fixtures_data: for fixture_data in fixtures_data:

View File

@ -269,8 +269,21 @@ def match_comments():
return render_template('error.html', message="Database not initialized. Please go to Database Setup to initialize the database.") return render_template('error.html', message="Database not initialized. Please go to Database Setup to initialize the database.")
_oppo = row[0]['nextclub'] _oppo = row[0]['nextclub']
# Handle case where nextdate is None - use most recent comment date
if row[0]['nextdate']:
commentDate = row[0]['nextdate'].strftime('%Y-%m-%d') commentDate = row[0]['nextdate'].strftime('%Y-%m-%d')
_matchDate = row[0]['nextdate'].strftime('%Y_%m_%d') _matchDate = row[0]['nextdate'].strftime('%Y-%m-%d')
else:
# Get the most recent comment date
sql_recent = text("SELECT matchDate FROM _motmcomments ORDER BY matchDate DESC LIMIT 1")
recent_result = sql_read(sql_recent)
if recent_result:
commentDate = recent_result[0]['matchDate']
_matchDate = recent_result[0]['matchDate']
else:
commentDate = '2025-01-15' # Fallback
_matchDate = '2025-01-15'
# Get HKFC logo from clubs table using signed URLs (with authentication) # Get HKFC logo from clubs table using signed URLs (with authentication)
hkfcLogo = s3_asset_service.get_asset_url('images/hkfc_logo.png') # Default fallback hkfcLogo = s3_asset_service.get_asset_url('images/hkfc_logo.png') # Default fallback
@ -376,7 +389,24 @@ def vote_thanks():
sql3 = text("INSERT INTO _motmcomments (matchDate, comment) VALUES (:match_date, :comment)") sql3 = text("INSERT INTO _motmcomments (matchDate, comment) VALUES (:match_date, :comment)")
sql_write(sql3, {'match_date': _matchDate, 'comment': _fixed_comments}) sql_write(sql3, {'match_date': _matchDate, 'comment': _fixed_comments})
return render_template('vote_thanks.html') # Get Simpsons monkeys image URL with fallback
try:
# First try to get from S3
simpsons_url = s3_asset_service.get_asset_url('images/simpsons-monkeys.jpg')
print(f"DEBUG: Simpsons image URL: {simpsons_url}")
# If S3 is disabled or URL is fallback, use static
if simpsons_url.startswith('/static/'):
print("DEBUG: Using fallback static URL")
else:
print("DEBUG: Using S3 URL")
except Exception as e:
print(f"DEBUG: Error getting Simpsons image: {e}")
# Fallback to static URL
simpsons_url = "/static/images/simpsons-monkeys.jpg"
return render_template('vote_thanks.html', simpsons_image_url=simpsons_url)
else: else:
return 'Ouch ... something went wrong here' return 'Ouch ... something went wrong here'
except Exception as e: except Exception as e:
@ -513,7 +543,15 @@ def motm_admin():
form.nextOppoClub.choices = [(oppo['hockeyclub'], oppo['hockeyclub']) for oppo in clubs] form.nextOppoClub.choices = [(oppo['hockeyclub'], oppo['hockeyclub']) for oppo in clubs]
form.currMotM.choices = [(player['playernumber'], player['playerforenames'] + " " + player['playersurname']) for player in players] form.currMotM.choices = [(player['playernumber'], player['playerforenames'] + " " + player['playersurname']) for player in players]
form.currDotD.choices = [(player['playernumber'], player['playerforenames'] + " " + player['playersurname']) for player in players] form.currDotD.choices = [(player['playernumber'], player['playerforenames'] + " " + player['playersurname']) for player in players]
clubLogo = settings[0]['oppologo'] # Get the opposition logo using S3 service
clubLogo = s3_asset_service.get_asset_url('images/default_logo.png') # Default fallback
if settings and settings[0]['nextclub']:
nextClub = settings[0]['nextclub']
# Get the club logo from the clubs table
sql_club_logo = text("SELECT logo_url FROM clubs WHERE hockey_club = :club_name")
club_logo_result = sql_read(sql_club_logo, {'club_name': nextClub})
if club_logo_result and club_logo_result[0]['logo_url']:
clubLogo = s3_asset_service.get_logo_url(club_logo_result[0]['logo_url'], nextClub)
return render_template('motm_admin.html', form=form, nextOppoLogo=clubLogo) return render_template('motm_admin.html', form=form, nextOppoLogo=clubLogo)
@ -1451,6 +1489,11 @@ def get_next_fixture():
# Get opponent club information # Get opponent club information
opponent_club_info = get_opponent_club_info(fixture['opponent']) opponent_club_info = get_opponent_club_info(fixture['opponent'])
# Get the opponent logo URL using S3 service
opponent_logo_url = s3_asset_service.get_asset_url('images/default_logo.png') # Default fallback
if opponent_club_info and opponent_club_info.get('logo_url'):
opponent_logo_url = s3_asset_service.get_logo_url(opponent_club_info['logo_url'], opponent_club_info['club_name'])
# Format the fixture data for JSON response # Format the fixture data for JSON response
fixture_data = { fixture_data = {
'success': True, 'success': True,
@ -1461,6 +1504,7 @@ def get_next_fixture():
'opponent': fixture['opponent'], 'opponent': fixture['opponent'],
'opponent_club': get_opponent_club_name(fixture['opponent']), 'opponent_club': get_opponent_club_name(fixture['opponent']),
'opponent_club_info': opponent_club_info, 'opponent_club_info': opponent_club_info,
'opponent_logo_url': opponent_logo_url,
'is_home': fixture['is_home'], 'is_home': fixture['is_home'],
'home_team': fixture['home_team'], 'home_team': fixture['home_team'],
'away_team': fixture['away_team'], 'away_team': fixture['away_team'],
@ -1683,9 +1727,9 @@ def poty_results():
# Only include players with votes # Only include players with votes
if motm_total > 0 or dotd_total > 0: if motm_total > 0 or dotd_total > 0:
results.append({ results.append({
'playername': player['playername'], 'playerName': player['playername'], # Fixed field name to match JavaScript
'motmtotal': motm_total, 'motmTotal': motm_total, # Fixed field name to match JavaScript
'dotdtotal': dotd_total 'dotdTotal': dotd_total # Fixed field name to match JavaScript
}) })
print(f"Dynamic POTY Results: {results}") print(f"Dynamic POTY Results: {results}")
@ -1703,7 +1747,7 @@ def voting_chart():
if date_result: if date_result:
matchDate = str(date_result[0]['nextdate']).replace('-', '') matchDate = str(date_result[0]['nextdate']).replace('-', '')
else: else:
matchDate = '20251012' # Default fallback matchDate = '20251015' # Default fallback
return render_template('vote_chart.html', _matchDate=matchDate) return render_template('vote_chart.html', _matchDate=matchDate)

View File

@ -203,7 +203,13 @@ class S3AssetService:
region_name=config.get('aws_region', 'us-east-1'), region_name=config.get('aws_region', 'us-east-1'),
endpoint_url=endpoint_url, endpoint_url=endpoint_url,
use_ssl=use_ssl, use_ssl=use_ssl,
verify=True # Enable SSL certificate verification verify=True, # Enable SSL certificate verification
config=boto3.session.Config(
s3={
'addressing_style': 'path'
},
signature_version='s3v4'
)
) )
else: else:
# Create AWS S3 client # Create AWS S3 client

View File

@ -0,0 +1 @@
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==

View File

@ -56,6 +56,15 @@
</div> </div>
</a> </a>
</div> </div>
<div class="col-md-3 mb-3">
<a href="/admin/motm/manage" class="btn btn-outline-danger w-100">
<i class="fas fa-database me-2"></i>
<div class="text-start">
<div class="fw-bold">MOTM Management</div>
<small class="text-muted">Reset & drop columns</small>
</div>
</a>
</div>
<div class="col-md-3 mb-3"> <div class="col-md-3 mb-3">
<a href="/admin/profile" class="btn btn-outline-secondary w-100"> <a href="/admin/profile" class="btn btn-outline-secondary w-100">
<i class="fas fa-user-cog me-2"></i> <i class="fas fa-user-cog me-2"></i>
@ -150,6 +159,76 @@
</div> </div>
</div> </div>
<!-- Analytics & Reports Section -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header bg-info text-white">
<h5 class="card-title mb-0">
<i class="fas fa-chart-bar me-2"></i>Analytics & Reports
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-lg-4 mb-3">
<div class="card h-100 border-info">
<div class="card-header bg-light">
<h6 class="card-title mb-0">
<i class="fas fa-poll text-info me-2"></i>Vote Charts
</h6>
</div>
<div class="card-body">
<p class="card-text">View current vote counts and MOTM/DotD results for the current match.</p>
<div class="d-grid gap-2">
<a href="/admin/voting" class="btn btn-info">
<i class="fas fa-chart-pie me-2"></i>View Vote Charts
</a>
</div>
</div>
</div>
</div>
<div class="col-lg-4 mb-3">
<div class="card h-100 border-success">
<div class="card-header bg-light">
<h6 class="card-title mb-0">
<i class="fas fa-trophy text-success me-2"></i>Player of the Year
</h6>
</div>
<div class="card-body">
<p class="card-text">View Player of the Year standings and cumulative MOTM votes.</p>
<div class="d-grid gap-2">
<a href="/admin/poty" class="btn btn-success">
<i class="fas fa-medal me-2"></i>View POTY Chart
</a>
</div>
</div>
</div>
</div>
<div class="col-lg-4 mb-3">
<div class="card h-100 border-warning">
<div class="card-header bg-light">
<h6 class="card-title mb-0">
<i class="fas fa-futbol text-warning me-2"></i>Goals & Assists
</h6>
</div>
<div class="card-body">
<p class="card-text">Manage goals and assists statistics for matches.</p>
<div class="d-grid gap-2">
<a href="/admin/stats" class="btn btn-warning">
<i class="fas fa-plus me-2"></i>Add Stats
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- System Management Section --> <!-- System Management Section -->
<div class="row mb-4"> <div class="row mb-4">
<div class="col-12"> <div class="col-12">

View File

@ -278,3 +278,4 @@
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -306,7 +306,7 @@
<div class="col-md-6 text-md-end"> <div class="col-md-6 text-md-end">
<p class="mb-0"> <p class="mb-0">
<i class="fas fa-calendar-alt me-1"></i> <i class="fas fa-calendar-alt me-1"></i>
{% if current_year %}{{ current_year }}{% else %}2024{% endif %} {% if current_year %}{{ current_year }}{% else %}2025{% endif %}
</p> </p>
</div> </div>
</div> </div>

View File

@ -33,3 +33,4 @@
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -227,7 +227,7 @@
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<h6><i class="fas fa-calendar-alt me-2"></i>Current Season</h6> <h6><i class="fas fa-calendar-alt me-2"></i>Current Season</h6>
<p class="text-muted">2024-2025 Hockey Season</p> <p class="text-muted">2025-2026 Hockey Season</p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -227,7 +227,7 @@
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<h6><i class="fas fa-calendar-alt me-2"></i>Current Season</h6> <h6><i class="fas fa-calendar-alt me-2"></i>Current Season</h6>
<p class="text-muted">2024-2025 Hockey Season</p> <p class="text-muted">2025-2026 Hockey Season</p>
</div> </div>
</div> </div>
</div> </div>
@ -235,3 +235,4 @@
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -74,3 +74,4 @@
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,116 +1,146 @@
<html> {% extends "base.html" %}
<head>
<title>HKFC Men's C Team - MotM and DotD vote admin</title> {% block title %}MOTM Management - HKFC Men's C Team MOTM System{% endblock %}
<link rel="stylesheet" media="screen" href ="/static/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/css/bootstrap-theme.min.css"> {% block content %}
<meta name="viewport" content = "width=device-width, initial-scale=1.0"> <!-- Page Header -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div class="row mb-4">
<script src="/static/js/bootstrap.min.js"></script> <div class="col-12">
</head> <div class="card">
<h2>HKFC Men's C Team MotM and DotD online vote admin page</h2> <div class="card-body text-center">
<div style="margin-bottom: 15px;"> <h1 class="card-title">
<a href="/admin" class="btn btn-default btn-sm"> <i class="fas fa-trophy text-warning me-2"></i>
<span class="glyphicon glyphicon-arrow-left"></span> Back to Admin Dashboard MOTM Management
</h1>
<p class="lead text-muted">Manage Man of the Match and Dick of the Day settings</p>
<a href="/admin" class="btn btn-outline-primary">
<i class="fas fa-arrow-left me-2"></i>Back to Admin Dashboard
</a> </a>
</div> </div>
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-warning alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
{{ message }}
</div> </div>
{% endfor %} </div>
{% endif %} </div>
{% endwith %} <!-- Match Configuration Card -->
<body onload="myFunction()"> <div class="row mb-4">
<div class="col-12">
<dl> <div class="card">
<p> <div class="card-header bg-primary text-white">
<h5 class="card-title mb-0">
<i class="fas fa-calendar-alt me-2"></i>Next Match Configuration
</h5>
</div>
<div class="card-body">
{{ form.csrf_token }} {{ form.csrf_token }}
<b>HKFC C Next Opponent:</b> <form method="post" action="/admin/motm">
<br/> <!-- Load Next Fixture Section -->
<div class="row"> <div class="row mb-4">
<div class="col-xs-12"> <div class="col-12">
<form class="col-sm-6" method="post" action="/admin/motm"> <div class="alert alert-info">
<!-- Load Next Fixture Button --> <div class="d-flex align-items-center justify-content-between flex-wrap">
<div class="row"> <div class="d-flex align-items-center">
<div class="col-sm-12"> <button type="button" class="btn btn-info me-3" id="loadFixtureBtn" onclick="loadNextFixture()">
<div class="alert alert-info" style="margin-bottom: 15px;"> <i class="fas fa-download me-2"></i>Load Next HKFC C Fixture
<button type="button" class="btn btn-info btn-sm" id="loadFixtureBtn" onclick="loadNextFixture()">
<span class="glyphicon glyphicon-download-alt"></span> Load Next HKFC C Fixture
</button> </button>
<a href="https://hockey.org.hk/MenFixture.asp" target="_blank" class="btn btn-default btn-sm" style="margin-left: 5px;"> <a href="https://hockey.org.hk/MenFixture.asp" target="_blank" class="btn btn-outline-info">
<span class="glyphicon glyphicon-new-window"></span> View HK Hockey Fixtures <i class="fas fa-external-link-alt me-2"></i>View HK Hockey Fixtures
</a> </a>
<span id="fixtureStatus" style="margin-left: 10px;"></span> </div>
<div id="fixtureInfo" style="margin-top: 10px; display: none;"></div> <span id="fixtureStatus" class="ms-3"></span>
</div>
<div id="fixtureInfo" class="mt-3" style="display: none;"></div>
</div> </div>
</div> </div>
</div> </div>
<div class = "row"> <!-- Match Details Form -->
<div class = "col-sm-6"> <div class="row mb-4">
<div class="input-group"> <div class="col-md-6 mb-3">
<span class="input-group-addon" id="basic-addon1">Date:</span> <label for="nextMatchDate" class="form-label">
{{ form.nextMatchDate(class_="form-control", id="nextMatchDate") }} <i class="fas fa-calendar me-2"></i>Match Date
</label>
{{ form.nextMatchDate(class_="form-control", **{"id": "nextMatchDate"}) }}
</div>
<div class="col-md-6 mb-3">
<label for="nextOppoTeam" class="form-label">
<i class="fas fa-users me-2"></i>Opposition Team
</label>
{{ form.nextOppoTeam(class_="form-control", **{"id": "nextOppoTeam"}) }}
</div> </div>
</div> </div>
<!-- Current Winners Section -->
<div class="row mb-4">
<div class="col-md-6 mb-3">
<label for="currMotM" class="form-label">
<i class="fas fa-trophy me-2 text-warning"></i>Current Man of the Match
</label>
{{ form.currMotM(class_="form-select") }}
</div> </div>
</br> <div class="col-md-6 mb-3">
<div class = "row"> <label for="currDotD" class="form-label">
<div class = "col-sm-9"> <i class="fas fa-user-times me-2 text-danger"></i>Current Dick of the Day
<div class="input-group"> </label>
<span class="input-group-addon" id="basic-addon1">Opposition</span> {{ form.currDotD(class_="form-select") }}
{{ form.nextOppoTeam(class_="form-control", id="nextOppoTeam") }}
</div>
</div>
</div>
<div class = "row">
<div class = "col-sm-6">
<div class="input-group">
<span class="input-group-addon" id="basic-addon1">Current Man of the Match:</span>
{{ form.currMotM(class_="form-control") }}
</div>
</div>
<div class = "col-sm-6">
<div class="input-group">
<span class="input-group-addon" id="basic-addon1">Current Dick of the Day:</span>
{{ form.currDotD(class_="form-control") }}
</div>
</div> </div>
</div> </div>
<!-- Warning Message -->
{% if not form.currMotM.choices or form.currMotM.choices|length == 0 %} {% if not form.currMotM.choices or form.currMotM.choices|length == 0 %}
<div class="row"> <div class="row mb-4">
<div class="col-sm-12"> <div class="col-12">
<div class="alert alert-warning" style="margin-top: 10px;"> <div class="alert alert-warning">
<small><strong>Note:</strong> No players available for previous MOTM/DotD. This is normal if you haven't set up a match squad yet. You can still save the match details.</small> <i class="fas fa-exclamation-triangle me-2"></i>
<strong>Note:</strong> No players available for previous MOTM/DotD. This is normal if you haven't set up a match squad yet. You can still save the match details.
</div> </div>
</div> </div>
</div> </div>
{% endif %} {% endif %}
<p>
<!-- Action Buttons -->
<div class="row">
<div class="col-12">
<div class="d-flex gap-2 flex-wrap">
{{ form.saveButton(class_="btn btn-success") }} {{ form.saveButton(class_="btn btn-success") }}
{{ form.activateButton(class_="btn btn-primary") }} {{ form.activateButton(class_="btn btn-primary") }}
<a class="btn btn-danger" href="/" role="button">Cancel</a> <a class="btn btn-outline-secondary" href="/admin" role="button">
</p> <i class="fas fa-times me-2"></i>Cancel
</a>
</div>
</div>
</div>
</form> </form>
<div class="col-sm-4">
<img src="{{ nextOppoLogo }}" height="90" id="nextOppoLogo"/>
</div> </div>
</div> </div>
</div> </div>
</p> </div>
</dl>
<script> <!-- Opposition Logo Card -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header bg-info text-white">
<h5 class="card-title mb-0">
<i class="fas fa-image me-2"></i>Opposition Team Logo
</h5>
</div>
<div class="card-body text-center">
<img src="{{ nextOppoLogo }}" height="120" id="nextOppoLogo" class="img-fluid rounded" alt="Opposition Team Logo"/>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_scripts %}
<script>
function loadNextFixture() { function loadNextFixture() {
// Show loading status // Show loading status
var statusElement = document.getElementById('fixtureStatus'); const statusElement = document.getElementById('fixtureStatus');
var infoElement = document.getElementById('fixtureInfo'); const infoElement = document.getElementById('fixtureInfo');
var loadBtn = document.getElementById('loadFixtureBtn'); const loadBtn = document.getElementById('loadFixtureBtn');
statusElement.innerHTML = '<span class="text-info">Loading...</span>'; statusElement.innerHTML = '<span class="text-info"><i class="fas fa-spinner fa-spin me-2"></i>Loading...</span>';
loadBtn.disabled = true; loadBtn.disabled = true;
// Fetch the next fixture from the API // Fetch the next fixture from the API
@ -122,6 +152,14 @@
document.getElementById('nextMatchDate').value = data.date; document.getElementById('nextMatchDate').value = data.date;
document.getElementById('nextOppoTeam').value = data.opponent; document.getElementById('nextOppoTeam').value = data.opponent;
// Update the opponent logo if provided
if (data.opponent_logo_url) {
const logoElement = document.getElementById('nextOppoLogo');
if (logoElement) {
logoElement.src = data.opponent_logo_url;
}
}
// Show fixture information // Show fixture information
let clubInfo = ''; let clubInfo = '';
if (data.opponent_club_info) { if (data.opponent_club_info) {
@ -145,32 +183,30 @@
clubInfo; clubInfo;
infoElement.style.display = 'block'; infoElement.style.display = 'block';
statusElement.innerHTML = '<span class="text-success">Fixture loaded!</span>'; statusElement.innerHTML = '<span class="text-success"><i class="fas fa-check me-2"></i>Fixture loaded!</span>';
// Clear status message after 3 seconds // Clear status message after 3 seconds
setTimeout(function() { setTimeout(function() {
statusElement.innerHTML = ''; statusElement.innerHTML = '';
}, 3000); }, 3000);
} else { } else {
statusElement.innerHTML = '<span class="text-danger">' + data.message + '</span>'; statusElement.innerHTML = '<span class="text-danger"><i class="fas fa-times me-2"></i>' + data.message + '</span>';
infoElement.style.display = 'none'; infoElement.style.display = 'none';
} }
loadBtn.disabled = false; loadBtn.disabled = false;
}) })
.catch(error => { .catch(error => {
console.error('Error:', error); console.error('Error:', error);
statusElement.innerHTML = '<span class="text-danger">Error loading fixture</span>'; statusElement.innerHTML = '<span class="text-danger"><i class="fas fa-exclamation-triangle me-2"></i>Error loading fixture</span>';
infoElement.style.display = 'none'; infoElement.style.display = 'none';
loadBtn.disabled = false; loadBtn.disabled = false;
}); });
} }
// Auto-load fixture on page load // Auto-load fixture on page load (optional)
function myFunction() { document.addEventListener('DOMContentLoaded', function() {
// Optional: Auto-load the next fixture when the page loads // Uncomment the next line if you want to auto-load the fixture when the page loads
// Uncomment the next line if you want this behavior
// loadNextFixture(); // loadNextFixture();
} });
</script> </script>
</body> {% endblock %}
</html>

View File

@ -38,6 +38,7 @@
</div> </div>
<!-- Random Comment --> <!-- Random Comment -->
{% if comment and comment != "No comments added yet" %}
<div class="row mb-4"> <div class="row mb-4">
<div class="col-12"> <div class="col-12">
<div class="card"> <div class="card">
@ -47,28 +48,27 @@
</h5> </h5>
</div> </div>
<div class="card-body"> <div class="card-body">
{% for item in comment %}
<blockquote class="blockquote text-center"> <blockquote class="blockquote text-center">
<p class="mb-0"> <p class="mb-0">
<i class="fas fa-quote-left text-muted me-2"></i> <i class="fas fa-quote-left text-muted me-2"></i>
<em>{{ item.comment }}</em> <em>{{ comment }}</em>
<i class="fas fa-quote-right text-muted ms-2"></i> <i class="fas fa-quote-right text-muted ms-2"></i>
</p> </p>
</blockquote> </blockquote>
{% endfor %}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endif %}
<!-- Voting Forms --> <!-- Voting Forms -->
<div class="row"> <div class="row">
<div class="col-lg-6 mb-4"> <div class="col-lg-8 mx-auto mb-4">
<!-- Man of the Match --> <!-- Vote for MOTM and DotD -->
<div class="card h-100"> <div class="card h-100">
<div class="card-header bg-success text-white"> <div class="card-header bg-success text-white">
<h5 class="card-title mb-0"> <h5 class="card-title mb-0">
<i class="fas fa-trophy me-2"></i>Man of the Match <i class="fas fa-vote-yea me-2"></i>Vote for MOTM and DotD
</h5> </h5>
</div> </div>
<div class="card-body"> <div class="card-body">
@ -79,7 +79,7 @@
<div class="mb-3"> <div class="mb-3">
<label for="motmSelect" class="form-label">Select Player:</label> <label for="motmSelect" class="form-label">Select Player:</label>
<select class="form-select" id="motmSelect" name="motmSelect" required> <select class="form-select" id="motmSelect" name="motmVote" required>
<option value="">Choose a player...</option> <option value="">Choose a player...</option>
{% for player in data %} {% for player in data %}
<option value="{{ player.playernumber }}">{{ player.playerforenames }} {{ player.playersurname }} <option value="{{ player.playernumber }}">{{ player.playerforenames }} {{ player.playersurname }}
@ -89,45 +89,26 @@
</select> </select>
</div> </div>
<div class="mb-3">
<label for="dotdSelect" class="form-label">Dick of the Day:</label>
<select class="form-select" id="dotdSelect" name="dotdVote" required>
<option value="">Choose a player...</option>
{% for player in data %}
<option value="{{ player.playernumber }}">{{ player.playerforenames }} {{ player.playersurname }}
{% if player.playernickname %}"{{ player.playernickname }}"{% endif %}
</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label for="motmComment" class="form-label">Optional Comment:</label>
<textarea class="form-control" id="motmComment" name="motmComment" rows="3" placeholder="Share your thoughts about the match..."></textarea>
</div>
<div class="d-grid"> <div class="d-grid">
<button type="submit" class="btn btn-success btn-lg"> <button type="submit" class="btn btn-success btn-lg">
<i class="fas fa-vote-yea me-2"></i>Submit MOTM Vote <i class="fas fa-vote-yea me-2"></i>Submit Votes
</button>
</div>
</form>
</div>
</div>
</div>
<div class="col-lg-6 mb-4">
<!-- Dick of the Day -->
<div class="card h-100">
<div class="card-header bg-warning text-dark">
<h5 class="card-title mb-0">
<i class="fas fa-award me-2"></i>Dick of the Day
</h5>
</div>
<div class="card-body">
<form method="post" action="/motm/dotd-thanks" id="dotdForm">
{{ form.csrf_token }}
<input type="hidden" id="matchNumber" name="matchNumber" value="{{ matchNumber }}">
<input type="hidden" id="oppo" name="oppo" value="{{ oppo }}">
<div class="mb-3">
<label for="dotdSelect" class="form-label">Select Player:</label>
<select class="form-select" id="dotdSelect" name="dotdSelect" required>
<option value="">Choose a player...</option>
{% for player in data %}
<option value="{{ player.playernumber }}">{{ player.playerforenames }} {{ player.playersurname }}
{% if player.playernickname %}"{{ player.playernickname }}"{% endif %}
</option>
{% endfor %}
</select>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-warning btn-lg">
<i class="fas fa-vote-yea me-2"></i>Submit DotD Vote
</button> </button>
</div> </div>
</form> </form>
@ -198,18 +179,27 @@
<script> <script>
$(document).ready(function() { $(document).ready(function() {
// Form validation // Form validation
$('#motmForm, #dotdForm').on('submit', function(e) { $('#motmForm').on('submit', function(e) {
const selectElement = $(this).find('select'); const motmSelect = $('#motmSelect');
if (!selectElement.val()) { const dotdSelect = $('#dotdSelect');
if (!motmSelect.val()) {
e.preventDefault(); e.preventDefault();
alert('Please select a player before submitting your vote.'); alert('Please select a player for Man of the Match.');
selectElement.focus(); motmSelect.focus();
return false;
}
if (!dotdSelect.val()) {
e.preventDefault();
alert('Please select a player for Dick of the Day.');
dotdSelect.focus();
return false; return false;
} }
}); });
// Add loading state to buttons // Add loading state to button
$('#motmForm, #dotdForm').on('submit', function() { $('#motmForm').on('submit', function() {
const button = $(this).find('button[type="submit"]'); const button = $(this).find('button[type="submit"]');
const originalText = button.html(); const originalText = button.html();
button.html('<span class="loading-spinner"></span> Submitting...').prop('disabled', true); button.html('<span class="loading-spinner"></span> Submitting...').prop('disabled', true);

View File

@ -38,6 +38,7 @@
</div> </div>
<!-- Random Comment --> <!-- Random Comment -->
{% if comment and comment != "No comments added yet" %}
<div class="row mb-4"> <div class="row mb-4">
<div class="col-12"> <div class="col-12">
<div class="card"> <div class="card">
@ -47,28 +48,27 @@
</h5> </h5>
</div> </div>
<div class="card-body"> <div class="card-body">
{% for item in comment %}
<blockquote class="blockquote text-center"> <blockquote class="blockquote text-center">
<p class="mb-0"> <p class="mb-0">
<i class="fas fa-quote-left text-muted me-2"></i> <i class="fas fa-quote-left text-muted me-2"></i>
<em>{{ item.comment }}</em> <em>{{ comment }}</em>
<i class="fas fa-quote-right text-muted ms-2"></i> <i class="fas fa-quote-right text-muted ms-2"></i>
</p> </p>
</blockquote> </blockquote>
{% endfor %}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endif %}
<!-- Voting Forms --> <!-- Voting Forms -->
<div class="row"> <div class="row">
<div class="col-lg-6 mb-4"> <div class="col-lg-8 mx-auto mb-4">
<!-- Man of the Match --> <!-- Vote for MOTM and DotD -->
<div class="card h-100"> <div class="card h-100">
<div class="card-header bg-success text-white"> <div class="card-header bg-success text-white">
<h5 class="card-title mb-0"> <h5 class="card-title mb-0">
<i class="fas fa-trophy me-2"></i>Man of the Match <i class="fas fa-vote-yea me-2"></i>Vote for MOTM and DotD
</h5> </h5>
</div> </div>
<div class="card-body"> <div class="card-body">
@ -79,7 +79,7 @@
<div class="mb-3"> <div class="mb-3">
<label for="motmSelect" class="form-label">Select Player:</label> <label for="motmSelect" class="form-label">Select Player:</label>
<select class="form-select" id="motmSelect" name="motmSelect" required> <select class="form-select" id="motmSelect" name="motmVote" required>
<option value="">Choose a player...</option> <option value="">Choose a player...</option>
{% for player in data %} {% for player in data %}
<option value="{{ player.playernumber }}">{{ player.playerforenames }} {{ player.playersurname }} <option value="{{ player.playernumber }}">{{ player.playerforenames }} {{ player.playersurname }}
@ -89,45 +89,26 @@
</select> </select>
</div> </div>
<div class="mb-3">
<label for="dotdSelect" class="form-label">Dick of the Day:</label>
<select class="form-select" id="dotdSelect" name="dotdVote" required>
<option value="">Choose a player...</option>
{% for player in data %}
<option value="{{ player.playernumber }}">{{ player.playerforenames }} {{ player.playersurname }}
{% if player.playernickname %}"{{ player.playernickname }}"{% endif %}
</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label for="motmComment" class="form-label">Optional Comment:</label>
<textarea class="form-control" id="motmComment" name="motmComment" rows="3" placeholder="Share your thoughts about the match..."></textarea>
</div>
<div class="d-grid"> <div class="d-grid">
<button type="submit" class="btn btn-success btn-lg"> <button type="submit" class="btn btn-success btn-lg">
<i class="fas fa-vote-yea me-2"></i>Submit MOTM Vote <i class="fas fa-vote-yea me-2"></i>Submit Votes
</button>
</div>
</form>
</div>
</div>
</div>
<div class="col-lg-6 mb-4">
<!-- Dick of the Day -->
<div class="card h-100">
<div class="card-header bg-warning text-dark">
<h5 class="card-title mb-0">
<i class="fas fa-award me-2"></i>Dick of the Day
</h5>
</div>
<div class="card-body">
<form method="post" action="/motm/dotd-thanks" id="dotdForm">
{{ form.csrf_token }}
<input type="hidden" id="matchNumber" name="matchNumber" value="{{ matchNumber }}">
<input type="hidden" id="oppo" name="oppo" value="{{ oppo }}">
<div class="mb-3">
<label for="dotdSelect" class="form-label">Select Player:</label>
<select class="form-select" id="dotdSelect" name="dotdSelect" required>
<option value="">Choose a player...</option>
{% for player in data %}
<option value="{{ player.playernumber }}">{{ player.playerforenames }} {{ player.playersurname }}
{% if player.playernickname %}"{{ player.playernickname }}"{% endif %}
</option>
{% endfor %}
</select>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-warning btn-lg">
<i class="fas fa-vote-yea me-2"></i>Submit DotD Vote
</button> </button>
</div> </div>
</form> </form>
@ -198,18 +179,27 @@
<script> <script>
$(document).ready(function() { $(document).ready(function() {
// Form validation // Form validation
$('#motmForm, #dotdForm').on('submit', function(e) { $('#motmForm').on('submit', function(e) {
const selectElement = $(this).find('select'); const motmSelect = $('#motmSelect');
if (!selectElement.val()) { const dotdSelect = $('#dotdSelect');
if (!motmSelect.val()) {
e.preventDefault(); e.preventDefault();
alert('Please select a player before submitting your vote.'); alert('Please select a player for Man of the Match.');
selectElement.focus(); motmSelect.focus();
return false;
}
if (!dotdSelect.val()) {
e.preventDefault();
alert('Please select a player for Dick of the Day.');
dotdSelect.focus();
return false; return false;
} }
}); });
// Add loading state to buttons // Add loading state to button
$('#motmForm, #dotdForm').on('submit', function() { $('#motmForm').on('submit', function() {
const button = $(this).find('button[type="submit"]'); const button = $(this).find('button[type="submit"]');
const originalText = button.html(); const originalText = button.html();
button.html('<span class="loading-spinner"></span> Submitting...').prop('disabled', true); button.html('<span class="loading-spinner"></span> Submitting...').prop('disabled', true);
@ -217,3 +207,4 @@ $(document).ready(function() {
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@ -36,6 +36,12 @@ google.charts.load('current', {
// load json data // load json data
function loadData(jsonData) { function loadData(jsonData) {
if (jsonData.length === 0) {
// Show message when no data
$('#chart_div').html('<div class="alert alert-info text-center"><h4>No Vote Data Available</h4><p>No players have received MOTM or DotD votes yet.</p><p>Votes will appear here once players start receiving votes.</p></div>');
return;
}
$.each(jsonData, function(index, row) { $.each(jsonData, function(index, row) {
data.addRow([ data.addRow([
row.playerName, row.playerName,

View File

@ -23,10 +23,11 @@
</div> </div>
<div class="mb-4"> <div class="mb-4">
<img src="http://icarus.ipa.champion:9000/hockey-app/assets/simpsons-monkeys.jpg" <img src="{{ simpsons_image_url }}"
alt="Counting votes" alt="Counting votes"
class="img-fluid rounded" class="img-fluid rounded"
style="max-height: 300px;"> style="max-height: 300px;"
onerror="this.src='/static/images/simpsons-monkeys.jpg';">
</div> </div>
<div class="d-grid gap-2 d-md-flex justify-content-md-center"> <div class="d-grid gap-2 d-md-flex justify-content-md-center">

View File

@ -23,10 +23,11 @@
</div> </div>
<div class="mb-4"> <div class="mb-4">
<img src="http://icarus.ipa.champion:9000/hockey-app/assets/simpsons-monkeys.jpg" <img src="{{ simpsons_image_url }}"
alt="Counting votes" alt="Counting votes"
class="img-fluid rounded" class="img-fluid rounded"
style="max-height: 300px;"> style="max-height: 300px;"
onerror="this.src='/static/images/simpsons-monkeys.jpg';">
</div> </div>
<div class="d-grid gap-2 d-md-flex justify-content-md-center"> <div class="d-grid gap-2 d-md-flex justify-content-md-center">
@ -42,3 +43,4 @@
</div> </div>
</div> </div>
{% endblock %} {% endblock %}