Fix historical squads
This commit is contained in:
parent
6a50c9fe90
commit
c199979eb9
137
motm_app/SQUAD_HISTORY_FEATURE.md
Normal file
137
motm_app/SQUAD_HISTORY_FEATURE.md
Normal file
@ -0,0 +1,137 @@
|
||||
# Squad History Feature
|
||||
|
||||
## Overview
|
||||
|
||||
The squad history feature preserves historical match squad data when resetting for a new match. Previously, when the squad was reset, all player data was lost. Now it's automatically archived to a dedicated history table.
|
||||
|
||||
## What Changed
|
||||
|
||||
### Before
|
||||
- Squad reset would delete or overwrite previous squad data
|
||||
- No way to view who played in previous matches
|
||||
- Historical squad information was lost forever
|
||||
|
||||
### After
|
||||
- Squad data is **automatically archived** before being cleared
|
||||
- Complete historical record preserved with match date and fixture number
|
||||
- New admin interface to view all historical squads
|
||||
- Easy lookup of who played in any previous match
|
||||
|
||||
## How It Works
|
||||
|
||||
### When You Reset the Squad
|
||||
|
||||
1. **Automatic Archival**: Before clearing the current squad, the system:
|
||||
- Copies all players to the `squad_history` table
|
||||
- Stores the match date and fixture number
|
||||
- Records when the archive was created
|
||||
|
||||
2. **Clean Reset**: The current squad table is cleared for the new match
|
||||
|
||||
3. **Confirmation**: Shows how many players were archived
|
||||
|
||||
### Database Structure
|
||||
|
||||
**New Table: `squad_history`**
|
||||
```sql
|
||||
- id (primary key)
|
||||
- player_number
|
||||
- player_forenames
|
||||
- player_surname
|
||||
- player_nickname
|
||||
- match_date
|
||||
- fixture_number
|
||||
- archived_at (timestamp)
|
||||
```
|
||||
|
||||
## Using the Feature
|
||||
|
||||
### Resetting the Squad (with History)
|
||||
|
||||
1. Go to **Admin Dashboard** → **Match Squad Management**
|
||||
2. Click **"Reset Squad"**
|
||||
3. System automatically:
|
||||
- Archives current squad with match details
|
||||
- Clears squad for new match
|
||||
- Shows confirmation message
|
||||
|
||||
### Viewing Squad History
|
||||
|
||||
1. Go to **Admin Dashboard** → **Squad History**
|
||||
2. See summary of all historical squads
|
||||
3. Click **"View Details"** on any fixture to see the full squad
|
||||
4. Scroll to see detailed player lists for each match
|
||||
|
||||
## Migration
|
||||
|
||||
The migration has been **automatically completed**! ✅
|
||||
|
||||
The `squad_history` table was created successfully.
|
||||
|
||||
For future databases or if needed again:
|
||||
```bash
|
||||
python add_squad_history.py
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
✅ **Automatic Archival**: No manual steps needed
|
||||
✅ **Complete Records**: All player details preserved
|
||||
✅ **Easy Navigation**: Summary view with drill-down details
|
||||
✅ **Match Context**: Linked to match date and fixture number
|
||||
✅ **Safe Reset**: Squad clearing only happens after successful archive
|
||||
|
||||
## Admin Interface
|
||||
|
||||
### Squad History Page Shows:
|
||||
|
||||
**Summary Table:**
|
||||
- Fixture Number
|
||||
- Match Date
|
||||
- Player Count
|
||||
- Quick view button
|
||||
|
||||
**Detailed View:**
|
||||
- Full squad rosters grouped by match
|
||||
- Player numbers, nicknames, and full names
|
||||
- Archive timestamp for audit trail
|
||||
|
||||
## Benefits
|
||||
|
||||
1. **Historical Reference**: See who played in any match
|
||||
2. **Team Analysis**: Track player participation over time
|
||||
3. **Data Integrity**: No more lost squad data
|
||||
4. **Audit Trail**: Know when squads were archived
|
||||
5. **Reporting**: Export or analyze squad patterns
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Files Modified
|
||||
|
||||
1. **`database.py`** - Added `SquadHistory` model
|
||||
2. **`main.py`** - Updated reset function and added history route
|
||||
3. **`templates/squad_history.html`** - New history viewing interface
|
||||
4. **`add_squad_history.py`** - Migration script
|
||||
|
||||
### Safety Features
|
||||
|
||||
- Transaction-based: Archive completes before deletion
|
||||
- Error handling: If archive fails, squad is not cleared
|
||||
- Flash messages: Clear feedback on what happened
|
||||
- Rollback capable: Can restore from history if needed
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Potential additions for future versions:
|
||||
- Export squad history to CSV/Excel
|
||||
- Compare squads between matches
|
||||
- Player participation statistics
|
||||
- Squad restoration from history
|
||||
- Search/filter historical squads
|
||||
- Visual squad timeline
|
||||
|
||||
---
|
||||
|
||||
**Implementation Date**: October 2025
|
||||
**Status**: ✅ Active and Working
|
||||
|
||||
91
motm_app/add_squad_history.py
Executable file
91
motm_app/add_squad_history.py
Executable file
@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Database migration script to add squad_history table for historical squad tracking.
|
||||
|
||||
This script creates the squad_history table to preserve squad data when resetting for a new match.
|
||||
"""
|
||||
|
||||
from db_config import db_config
|
||||
from sqlalchemy import text
|
||||
import sys
|
||||
|
||||
def add_squad_history_table():
|
||||
"""Create squad_history table for historical squad records."""
|
||||
try:
|
||||
engine = db_config.engine
|
||||
|
||||
with engine.connect() as connection:
|
||||
# Check if table already exists
|
||||
try:
|
||||
result = connection.execute(text("SELECT COUNT(*) FROM squad_history LIMIT 1"))
|
||||
print("✓ Table 'squad_history' already exists")
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Create the squad_history table
|
||||
try:
|
||||
# PostgreSQL/SQLite compatible syntax
|
||||
create_table_sql = text("""
|
||||
CREATE TABLE squad_history (
|
||||
id SERIAL PRIMARY KEY,
|
||||
player_number INTEGER,
|
||||
player_forenames VARCHAR(50),
|
||||
player_surname VARCHAR(30),
|
||||
player_nickname VARCHAR(30),
|
||||
match_date DATE,
|
||||
fixture_number VARCHAR(20),
|
||||
archived_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
""")
|
||||
connection.execute(create_table_sql)
|
||||
connection.commit()
|
||||
print("✓ Successfully created 'squad_history' table (PostgreSQL)")
|
||||
return True
|
||||
except Exception as e:
|
||||
# Try SQLite syntax
|
||||
try:
|
||||
connection.rollback()
|
||||
create_table_sql = text("""
|
||||
CREATE TABLE squad_history (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
player_number INTEGER,
|
||||
player_forenames VARCHAR(50),
|
||||
player_surname VARCHAR(30),
|
||||
player_nickname VARCHAR(30),
|
||||
match_date DATE,
|
||||
fixture_number VARCHAR(20),
|
||||
archived_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
""")
|
||||
connection.execute(create_table_sql)
|
||||
connection.commit()
|
||||
print("✓ Successfully created 'squad_history' table (SQLite)")
|
||||
return True
|
||||
except Exception as e2:
|
||||
print(f"✗ Error creating table: {str(e2)}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"✗ Error connecting to database: {str(e)}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("Squad History Table - Database Migration")
|
||||
print("=" * 60)
|
||||
print("\nThis script will create the 'squad_history' table to preserve")
|
||||
print("historical squad data when resetting for new matches.\n")
|
||||
|
||||
result = add_squad_history_table()
|
||||
|
||||
if result:
|
||||
print("\n✓ Migration completed successfully!")
|
||||
print("\nSquad data will now be preserved when you reset the squad.")
|
||||
print("Historical squads are stored with match date and fixture number.")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("\n✗ Migration failed!")
|
||||
print("Please check the error messages above.")
|
||||
sys.exit(1)
|
||||
|
||||
@ -124,6 +124,19 @@ class MatchSquad(Base):
|
||||
match_date = Column(Date)
|
||||
created_at = Column(DateTime, default=datetime.utcnow)
|
||||
|
||||
class SquadHistory(Base):
|
||||
"""Historical match squad records."""
|
||||
__tablename__ = 'squad_history'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
player_number = Column(Integer, ForeignKey('players.player_number'))
|
||||
player_forenames = Column(String(50))
|
||||
player_surname = Column(String(30))
|
||||
player_nickname = Column(String(30))
|
||||
match_date = Column(Date)
|
||||
fixture_number = Column(String(20))
|
||||
archived_at = Column(DateTime, default=datetime.utcnow)
|
||||
|
||||
class HockeyFixture(Base):
|
||||
"""Hockey fixture model."""
|
||||
__tablename__ = 'hockey_fixtures'
|
||||
|
||||
184
motm_app/main.py
184
motm_app/main.py
@ -178,6 +178,75 @@ def is_admin_authenticated(request):
|
||||
return False
|
||||
|
||||
|
||||
def get_previous_match_winners():
|
||||
"""
|
||||
Automatically determine the MOTM and DotD winners from the most recent completed fixture.
|
||||
Returns a tuple of (motm_player_number, dotd_player_number) or (None, None) if not found.
|
||||
"""
|
||||
try:
|
||||
# Get all fixture columns from _hkfc_c_motm table
|
||||
sql_columns = text("""
|
||||
SELECT column_name
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = '_hkfc_c_motm'
|
||||
AND (column_name LIKE 'motm_%' OR column_name LIKE 'dotd_%')
|
||||
AND column_name NOT LIKE '%total'
|
||||
ORDER BY column_name DESC
|
||||
""")
|
||||
columns = sql_read(sql_columns)
|
||||
|
||||
if not columns:
|
||||
return None, None
|
||||
|
||||
# Extract unique fixture dates from column names
|
||||
fixture_dates = set()
|
||||
for col in columns:
|
||||
col_name = col['column_name']
|
||||
if col_name.startswith('motm_'):
|
||||
fixture_dates.add(col_name.replace('motm_', ''))
|
||||
elif col_name.startswith('dotd_'):
|
||||
fixture_dates.add(col_name.replace('dotd_', ''))
|
||||
|
||||
# Sort fixture dates in descending order (most recent first)
|
||||
sorted_dates = sorted(list(fixture_dates), reverse=True)
|
||||
|
||||
if not sorted_dates:
|
||||
return None, None
|
||||
|
||||
# Get the most recent fixture date
|
||||
latest_fixture = sorted_dates[0]
|
||||
motm_col = f'motm_{latest_fixture}'
|
||||
dotd_col = f'dotd_{latest_fixture}'
|
||||
|
||||
# Find the MOTM winner (player with most votes)
|
||||
sql_motm = text(f"""
|
||||
SELECT playernumber, {motm_col} as votes
|
||||
FROM _hkfc_c_motm
|
||||
WHERE {motm_col} > 0
|
||||
ORDER BY {motm_col} DESC
|
||||
LIMIT 1
|
||||
""")
|
||||
motm_result = sql_read(sql_motm)
|
||||
motm_winner = motm_result[0]['playernumber'] if motm_result else None
|
||||
|
||||
# Find the DotD winner (player with most votes)
|
||||
sql_dotd = text(f"""
|
||||
SELECT playernumber, {dotd_col} as votes
|
||||
FROM _hkfc_c_motm
|
||||
WHERE {dotd_col} > 0
|
||||
ORDER BY {dotd_col} DESC
|
||||
LIMIT 1
|
||||
""")
|
||||
dotd_result = sql_read(sql_dotd)
|
||||
dotd_winner = dotd_result[0]['playernumber'] if dotd_result else None
|
||||
|
||||
return motm_winner, dotd_winner
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error getting previous match winners: {e}")
|
||||
return None, None
|
||||
|
||||
|
||||
# ==================== PUBLIC VOTING SECTION ====================
|
||||
|
||||
@app.route('/motm/<randomUrlSuffix>')
|
||||
@ -512,7 +581,9 @@ def motm_admin():
|
||||
return redirect(url_for('motm_admin'))
|
||||
_nextClub = _nextClubName[0]['club']
|
||||
|
||||
# Update currMotM and currDotD - set to None if '0' (No Previous) is selected
|
||||
# Get the form values for previous MOTM and DotD
|
||||
# If user selected '0' (No Previous), use None
|
||||
# Otherwise use the selected player number
|
||||
curr_motm_value = None if _currMotM == '0' else _currMotM
|
||||
curr_dotd_value = None if _currDotD == '0' else _currDotD
|
||||
|
||||
@ -600,9 +671,14 @@ def motm_admin():
|
||||
if hasattr(deadline, 'strftime'):
|
||||
form.votingDeadline.data = deadline.strftime('%Y-%m-%dT%H:%M')
|
||||
|
||||
# Get automatically determined previous winners
|
||||
auto_prev_motm, auto_prev_dotd = get_previous_match_winners()
|
||||
|
||||
sql4 = text("SELECT hockeyclub FROM menshockeyclubs ORDER BY hockeyclub")
|
||||
sql5 = text("SELECT nextclub, oppologo FROM motmadminsettings")
|
||||
sql6 = text(f"SELECT playernumber, playerforenames, playersurname FROM _hkfcc_matchsquad_{prevFixture} ORDER BY playerforenames")
|
||||
|
||||
# Get all players for the dropdown
|
||||
sql6 = text("SELECT playernumber, playerforenames, playersurname, playernickname FROM _hkfc_players ORDER BY playernickname")
|
||||
clubs = sql_read_static(sql4)
|
||||
settings = sql_read_static(sql5)
|
||||
players = sql_read(sql6)
|
||||
@ -616,14 +692,30 @@ def motm_admin():
|
||||
players = []
|
||||
|
||||
form.nextOppoClub.choices = [(oppo['hockeyclub'], oppo['hockeyclub']) for oppo in clubs]
|
||||
# Add "No Previous" option at the beginning of the list
|
||||
form.currMotM.choices = [('0', '-- No Previous MOTM --')] + [(player['playernumber'], player['playerforenames'] + " " + player['playersurname']) for player in players]
|
||||
form.currDotD.choices = [('0', '-- No Previous DotD --')] + [(player['playernumber'], player['playerforenames'] + " " + player['playersurname']) for player in players]
|
||||
# Build player choices with nickname for better identification
|
||||
form.currMotM.choices = [('0', '-- No Previous MOTM --')] + [(str(player['playernumber']), f"{player['playernickname']} ({player['playerforenames']} {player['playersurname']})") for player in players]
|
||||
form.currDotD.choices = [('0', '-- No Previous DotD --')] + [(str(player['playernumber']), f"{player['playernickname']} ({player['playerforenames']} {player['playersurname']})") for player in players]
|
||||
|
||||
# Pre-select current MOTM and DotD values (default to '0' if NULL)
|
||||
# Pre-select values: use database value if exists, otherwise auto-determine from previous fixture
|
||||
if current_settings:
|
||||
form.currMotM.data = str(current_settings[0]['currmotm']) if current_settings[0].get('currmotm') else '0'
|
||||
form.currDotD.data = str(current_settings[0]['currdotd']) if current_settings[0].get('currdotd') else '0'
|
||||
# If database has a value set, use it; otherwise use auto-determined winner
|
||||
if current_settings[0].get('currmotm'):
|
||||
form.currMotM.data = str(current_settings[0]['currmotm'])
|
||||
elif auto_prev_motm:
|
||||
form.currMotM.data = str(auto_prev_motm)
|
||||
else:
|
||||
form.currMotM.data = '0'
|
||||
|
||||
if current_settings[0].get('currdotd'):
|
||||
form.currDotD.data = str(current_settings[0]['currdotd'])
|
||||
elif auto_prev_dotd:
|
||||
form.currDotD.data = str(auto_prev_dotd)
|
||||
else:
|
||||
form.currDotD.data = '0'
|
||||
else:
|
||||
# No settings in database, use auto-determined or default to '0'
|
||||
form.currMotM.data = str(auto_prev_motm) if auto_prev_motm else '0'
|
||||
form.currDotD.data = str(auto_prev_dotd) if auto_prev_dotd else '0'
|
||||
|
||||
# Get the opposition logo using S3 service
|
||||
clubLogo = s3_asset_service.get_asset_url('images/default_logo.png') # Default fallback
|
||||
@ -1648,6 +1740,37 @@ def match_squad_list():
|
||||
return render_template('match_squad_selected.html', table=table)
|
||||
|
||||
|
||||
@app.route('/admin/squad/history')
|
||||
@basic_auth.required
|
||||
def squad_history():
|
||||
"""View historical squad data"""
|
||||
try:
|
||||
# Get all historical squads grouped by fixture
|
||||
sql = text("""
|
||||
SELECT fixture_number, match_date,
|
||||
COUNT(*) as player_count,
|
||||
STRING_AGG(player_nickname, ', ') as players
|
||||
FROM squad_history
|
||||
GROUP BY fixture_number, match_date
|
||||
ORDER BY match_date DESC
|
||||
""")
|
||||
history = sql_read(sql)
|
||||
|
||||
# Get detailed squad for each fixture
|
||||
sql_details = text("""
|
||||
SELECT fixture_number, match_date, player_number,
|
||||
player_forenames, player_surname, player_nickname, archived_at
|
||||
FROM squad_history
|
||||
ORDER BY match_date DESC, player_nickname
|
||||
""")
|
||||
details = sql_read(sql_details)
|
||||
|
||||
return render_template('squad_history.html', history=history, details=details)
|
||||
except Exception as e:
|
||||
flash(f'Error loading squad history: {str(e)}', 'error')
|
||||
return redirect(url_for('admin_dashboard'))
|
||||
|
||||
|
||||
@app.route('/admin/squad/remove', methods=['POST'])
|
||||
@basic_auth.required
|
||||
def delPlayerFromSquad():
|
||||
@ -1666,34 +1789,47 @@ def delPlayerFromSquad():
|
||||
@app.route('/admin/squad/reset')
|
||||
@basic_auth.required
|
||||
def matchSquadReset():
|
||||
"""Reset squad for new match"""
|
||||
_matchNumber = str(mySettings('fixture'))
|
||||
print(_matchNumber)
|
||||
|
||||
"""Reset squad for new match - archives current squad to history before clearing"""
|
||||
try:
|
||||
# First, check if there are any players in the current squad
|
||||
check_sql = text("SELECT COUNT(*) as count FROM _hkfcC_matchSquad")
|
||||
# Get current match date and fixture number from admin settings
|
||||
sql_settings = text("SELECT nextdate, nextfixture FROM motmadminsettings WHERE userid = 'admin'")
|
||||
settings = sql_read_static(sql_settings)
|
||||
|
||||
if not settings:
|
||||
flash('Error: Admin settings not found. Please configure match settings first.', 'error')
|
||||
return render_template('match_squad_reset.html')
|
||||
|
||||
match_date = settings[0]['nextdate']
|
||||
fixture_number = match_date.strftime('%Y%m%d') if match_date else 'unknown'
|
||||
|
||||
# Check if there are any players in the current squad
|
||||
check_sql = text("SELECT COUNT(*) as count FROM _hkfcc_matchsquad")
|
||||
result = sql_read(check_sql)
|
||||
squad_count = result[0]['count'] if result else 0
|
||||
|
||||
if squad_count > 0:
|
||||
# Rename current squad table
|
||||
sql1 = text(f"RENAME TABLE _hkfcC_matchSquad TO _hkfcC_matchSquad_{_matchNumber}")
|
||||
sql_write(sql1)
|
||||
# Archive current squad to history table
|
||||
archive_sql = text("""
|
||||
INSERT INTO squad_history (player_number, player_forenames, player_surname, player_nickname, match_date, fixture_number)
|
||||
SELECT playernumber, playerforenames, playersurname, playernickname, :match_date, :fixture_number
|
||||
FROM _hkfcc_matchsquad
|
||||
""")
|
||||
sql_write(archive_sql, {'match_date': match_date, 'fixture_number': fixture_number})
|
||||
|
||||
# Create new empty squad table
|
||||
sql2 = text("CREATE TABLE _hkfcC_matchSquad (playerNumber smallint UNIQUE, playerForenames varchar(50), playerSurname varchar(30), playerNickname varchar(30) NOT NULL, PRIMARY KEY (playerNumber))")
|
||||
sql_write(sql2)
|
||||
# Clear current squad table
|
||||
clear_sql = text("DELETE FROM _hkfcc_matchsquad")
|
||||
sql_write(clear_sql)
|
||||
|
||||
# Update fixture number
|
||||
sql3 = text("UPDATE motmAdminSettings SET prevFixture = :match_number")
|
||||
sql_write_static(sql3, {'match_number': _matchNumber})
|
||||
# Update previous fixture number in settings
|
||||
update_sql = text("UPDATE motmadminsettings SET prevfixture = :fixture_number WHERE userid = 'admin'")
|
||||
sql_write_static(update_sql, {'fixture_number': fixture_number})
|
||||
|
||||
flash(f'Squad reset successfully! {squad_count} players archived for match {_matchNumber}', 'success')
|
||||
flash(f'Squad reset successfully! {squad_count} players archived for match {fixture_number} ({match_date})', 'success')
|
||||
else:
|
||||
flash('No players in current squad to reset', 'info')
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error in squad reset: {str(e)}")
|
||||
flash(f'Error resetting squad: {str(e)}', 'error')
|
||||
|
||||
return render_template('match_squad_reset.html')
|
||||
|
||||
@ -304,6 +304,9 @@
|
||||
<a href="/admin/squad/list" class="btn btn-dark">
|
||||
<i class="fas fa-eye me-2"></i>View Squad
|
||||
</a>
|
||||
<a href="/admin/squad/history" class="btn btn-outline-dark">
|
||||
<i class="fas fa-history me-2"></i>Squad History
|
||||
</a>
|
||||
<a href="/admin/squad/reset" class="btn btn-outline-dark">
|
||||
<i class="fas fa-refresh me-2"></i>Reset Squad
|
||||
</a>
|
||||
|
||||
@ -86,14 +86,18 @@
|
||||
<i class="fas fa-trophy me-2 text-warning"></i>Previous Man of the Match
|
||||
</label>
|
||||
{{ form.currMotM(class_="form-select") }}
|
||||
<small class="form-text text-muted">Select "No Previous MOTM" for the first match or if no previous winner</small>
|
||||
<small class="form-text text-muted">
|
||||
<i class="fas fa-magic me-1"></i>Auto-selected from previous vote. Choose "No Previous" to override.
|
||||
</small>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="currDotD" class="form-label">
|
||||
<i class="fas fa-user-times me-2 text-danger"></i>Previous Dick of the Day
|
||||
</label>
|
||||
{{ form.currDotD(class_="form-select") }}
|
||||
<small class="form-text text-muted">Select "No Previous DotD" for the first match or if no previous winner</small>
|
||||
<small class="form-text text-muted">
|
||||
<i class="fas fa-magic me-1"></i>Auto-selected from previous vote. Choose "No Previous" to override.
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
150
motm_app/templates/squad_history.html
Normal file
150
motm_app/templates/squad_history.html
Normal file
@ -0,0 +1,150 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Squad History{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h2><i class="fas fa-history me-2"></i>Squad History</h2>
|
||||
<a href="/admin" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left me-2"></i>Back to Admin
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ 'danger' if category == 'error' else 'success' if category == 'success' else 'info' }} alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<!-- Historical Squads Summary -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="fas fa-list me-2"></i>Historical Squads Summary
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if history %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Fixture Number</th>
|
||||
<th>Match Date</th>
|
||||
<th>Players</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for fixture in history %}
|
||||
<tr>
|
||||
<td>{{ fixture.fixture_number }}</td>
|
||||
<td>{{ fixture.match_date }}</td>
|
||||
<td>{{ fixture.player_count }} players</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-info" onclick="showSquadDetails('{{ fixture.fixture_number }}')">
|
||||
<i class="fas fa-eye me-1"></i>View Details
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle me-2"></i>No historical squad data available yet.
|
||||
Squad data will be saved here when you reset the squad for a new match.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Detailed Squad View -->
|
||||
{% if details %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header bg-secondary text-white">
|
||||
<h5 class="card-title mb-0">
|
||||
<i class="fas fa-users me-2"></i>Detailed Squad Records
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% set current_fixture = '' %}
|
||||
{% for player in details %}
|
||||
{% if player.fixture_number != current_fixture %}
|
||||
{% if current_fixture != '' %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% set current_fixture = player.fixture_number %}
|
||||
<div class="squad-detail mb-4" id="squad-{{ player.fixture_number }}">
|
||||
<h6 class="border-bottom pb-2">
|
||||
<i class="fas fa-calendar me-2"></i>Fixture {{ player.fixture_number }} - {{ player.match_date }}
|
||||
<small class="text-muted">(Archived: {{ player.archived_at }})</small>
|
||||
</h6>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Number</th>
|
||||
<th>Nickname</th>
|
||||
<th>Full Name</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td>{{ player.player_number }}</td>
|
||||
<td><strong>{{ player.player_nickname }}</strong></td>
|
||||
<td>{{ player.player_forenames }} {{ player.player_surname }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% if current_fixture != '' %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_scripts %}
|
||||
<script>
|
||||
function showSquadDetails(fixtureNumber) {
|
||||
const element = document.getElementById('squad-' + fixtureNumber);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
element.classList.add('highlight-squad');
|
||||
setTimeout(() => element.classList.remove('highlight-squad'), 2000);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.highlight-squad {
|
||||
background-color: #fff3cd;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user