Updated code to populate database

This commit is contained in:
Jonny Ervine 2025-09-29 22:39:57 +08:00
parent 1b36a283d5
commit aabc01382c
23 changed files with 1470 additions and 371 deletions

View File

@ -1,6 +1,6 @@
# HKFC Men's D Team - MOTM (Man of the Match) System
# HKFC Men's C Team - MOTM (Man of the Match) System
This is a standalone Flask application for managing Man of the Match and Dick of the Day voting for the HKFC Men's D Team hockey club.
This is a standalone Flask application for managing Man of the Match and Dick of the Day voting for the HKFC Men's C Team hockey club.
## Features
@ -113,11 +113,11 @@ The application will be available at `http://localhost:5000`
## Database Requirements
The application requires access to the following database tables:
- `_hkfcD_matchSquad` - Current match squad
- `_hkfcC_matchSquad` - Current match squad
- `_HKFC_players` - Player database
- `hkfcDAdminSettings` - Admin configuration
- `hkfcCAdminSettings` - Admin configuration
- `hockeyFixtures` - Match fixtures
- `_hkfc_d_motm` - MOTM/DotD voting results
- `_hkfc_c_motm` - MOTM/DotD voting results
- `_motmComments` - Match comments
- `_clubTeams` - Club and team information
- `mensHockeyClubs` - Club logos and information

View File

@ -231,7 +231,9 @@ def fetch_all(sql_command, params=None):
result = session.execute(sql_command, params)
else:
result = session.execute(sql_command)
return result.fetchall()
rows = result.fetchall()
# Convert to list of dictionaries for compatibility
return [dict(row._mapping) for row in rows] if rows else []
except Exception as e:
print(f"SQL Error: {e}")
return []
@ -246,7 +248,8 @@ def fetch_one(sql_command, params=None):
result = session.execute(sql_command, params)
else:
result = session.execute(sql_command)
return result.fetchone()
row = result.fetchone()
return dict(row._mapping) if row else None
except Exception as e:
print(f"SQL Error: {e}")
return None
@ -254,32 +257,31 @@ def fetch_one(sql_command, params=None):
session.close()
# Legacy compatibility functions
def sql_write(sql_cmd):
def sql_write(sql_cmd, params=None):
"""Legacy compatibility function for sql_write."""
try:
execute_sql(sql_cmd)
execute_sql(sql_cmd, params)
return True
except Exception as e:
print(f"Write Error: {e}")
return False
def sql_write_static(sql_cmd):
def sql_write_static(sql_cmd, params=None):
"""Legacy compatibility function for sql_write_static."""
return sql_write(sql_cmd)
return sql_write(sql_cmd, params)
def sql_read(sql_cmd):
def sql_read(sql_cmd, params=None):
"""Legacy compatibility function for sql_read."""
try:
result = fetch_all(sql_cmd)
# Convert to list of dictionaries for compatibility
return [dict(row) for row in result] if result else []
result = fetch_all(sql_cmd, params)
return result
except Exception as e:
print(f"Read Error: {e}")
return []
def sql_read_static(sql_cmd):
def sql_read_static(sql_cmd, params=None):
"""Legacy compatibility function for sql_read_static."""
return sql_read(sql_cmd)
return sql_read(sql_cmd, params)
# Initialize database tables
def init_database():

View File

@ -0,0 +1,19 @@
[DATABASE]
type = postgresql
sqlite_database_path = hockey_results.db
[MYSQL]
host = localhost
port = 3306
database = hockey_results
username = root
password =
charset = utf8mb4
[POSTGRESQL]
host = icarus.ipa.champion
port = 5432
database = motm
username = motm_user
password = q7y7f7Lv*sODJZ2wGiv0Wq5a

View File

@ -20,6 +20,9 @@ class DatabaseConfigManager:
"""Manages database configuration and setup."""
def __init__(self, config_file='database_config.ini'):
# Use absolute path to ensure we save in the right location
if not os.path.isabs(config_file):
config_file = os.path.join(os.path.dirname(__file__), config_file)
self.config_file = config_file
self.config = configparser.ConfigParser()
self.load_config()
@ -49,6 +52,8 @@ class DatabaseConfigManager:
'username': 'postgres',
'password': ''
}
# Save the default configuration to file
self._save_config_file()
def save_config(self, form_data):
"""Save configuration from form data."""
@ -60,38 +65,42 @@ class DatabaseConfigManager:
self.config['DATABASE']['sqlite_database_path'] = form_data['sqlite_database_path']
# Update MySQL settings
if 'mysql_host' in form_data:
self.config['MYSQL']['host'] = form_data['mysql_host']
if 'mysql_port' in form_data:
if 'mysql_host' in form_data and form_data['mysql_host'] is not None:
self.config['MYSQL']['host'] = str(form_data['mysql_host'])
if 'mysql_port' in form_data and form_data['mysql_port'] is not None:
self.config['MYSQL']['port'] = str(form_data['mysql_port'])
if 'mysql_database' in form_data:
self.config['MYSQL']['database'] = form_data['mysql_database']
if 'mysql_username' in form_data:
self.config['MYSQL']['username'] = form_data['mysql_username']
if 'mysql_password' in form_data:
self.config['MYSQL']['password'] = form_data['mysql_password']
if 'mysql_charset' in form_data:
self.config['MYSQL']['charset'] = form_data['mysql_charset']
if 'mysql_database' in form_data and form_data['mysql_database'] is not None:
self.config['MYSQL']['database'] = str(form_data['mysql_database'])
if 'mysql_username' in form_data and form_data['mysql_username'] is not None:
self.config['MYSQL']['username'] = str(form_data['mysql_username'])
if 'mysql_password' in form_data and form_data['mysql_password'] is not None:
self.config['MYSQL']['password'] = str(form_data['mysql_password'])
if 'mysql_charset' in form_data and form_data['mysql_charset'] is not None:
self.config['MYSQL']['charset'] = str(form_data['mysql_charset'])
# Update PostgreSQL settings
if 'postgres_host' in form_data:
self.config['POSTGRESQL']['host'] = form_data['postgres_host']
if 'postgres_port' in form_data:
if 'postgres_host' in form_data and form_data['postgres_host'] is not None:
self.config['POSTGRESQL']['host'] = str(form_data['postgres_host'])
if 'postgres_port' in form_data and form_data['postgres_port'] is not None:
self.config['POSTGRESQL']['port'] = str(form_data['postgres_port'])
if 'postgres_database' in form_data:
self.config['POSTGRESQL']['database'] = form_data['postgres_database']
if 'postgres_username' in form_data:
self.config['POSTGRESQL']['username'] = form_data['postgres_username']
if 'postgres_password' in form_data:
self.config['POSTGRESQL']['password'] = form_data['postgres_password']
if 'postgres_database' in form_data and form_data['postgres_database'] is not None:
self.config['POSTGRESQL']['database'] = str(form_data['postgres_database'])
if 'postgres_username' in form_data and form_data['postgres_username'] is not None:
self.config['POSTGRESQL']['username'] = str(form_data['postgres_username'])
if 'postgres_password' in form_data and form_data['postgres_password'] is not None:
self.config['POSTGRESQL']['password'] = str(form_data['postgres_password'])
# Save to file
with open(self.config_file, 'w') as f:
self.config.write(f)
self._save_config_file()
# Update environment variables
self._update_environment_variables()
def _save_config_file(self):
"""Save configuration to file."""
with open(self.config_file, 'w') as f:
self.config.write(f)
def _update_environment_variables(self):
"""Update environment variables based on configuration."""
db_type = self.config['DATABASE']['type']
@ -188,7 +197,7 @@ class DatabaseConfigManager:
session = get_db_session()
try:
# Create sample clubs
# Create sample clubs (only if they don't exist)
clubs_data = [
{'hockey_club': 'HKFC', 'logo_url': '/static/images/hkfc_logo.png'},
{'hockey_club': 'KCC', 'logo_url': '/static/images/kcc_logo.png'},
@ -197,59 +206,72 @@ class DatabaseConfigManager:
]
for club_data in clubs_data:
club = Club(**club_data)
session.add(club)
# Check if club already exists
existing_club = session.query(Club).filter_by(hockey_club=club_data['hockey_club']).first()
if not existing_club:
club = Club(**club_data)
session.add(club)
# Create sample teams
# Create sample teams (only if they don't exist)
teams_data = [
{'club': 'HKFC', 'team': 'A', 'display_name': 'HKFC A', 'league': 'Premier Division'},
{'club': 'HKFC', 'team': 'B', 'display_name': 'HKFC B', 'league': 'Division 1'},
{'club': 'HKFC', 'team': 'C', 'display_name': 'HKFC C', 'league': 'Division 2'},
{'club': 'HKFC', 'team': 'D', 'display_name': 'HKFC D', 'league': 'Division 3'},
]
for team_data in teams_data:
team = Team(**team_data)
session.add(team)
# Check if team already exists
existing_team = session.query(Team).filter_by(club=team_data['club'], team=team_data['team']).first()
if not existing_team:
team = Team(**team_data)
session.add(team)
# Create sample players
# Create sample players (only if they don't exist)
players_data = [
{'player_number': 1, 'player_forenames': 'John', 'player_surname': 'Smith', 'player_nickname': 'Smithers', 'player_team': 'HKFC D'},
{'player_number': 2, 'player_forenames': 'Mike', 'player_surname': 'Jones', 'player_nickname': 'Jonesy', 'player_team': 'HKFC D'},
{'player_number': 3, 'player_forenames': 'David', 'player_surname': 'Brown', 'player_nickname': 'Brownie', 'player_team': 'HKFC D'},
{'player_number': 4, 'player_forenames': 'Chris', 'player_surname': 'Wilson', 'player_nickname': 'Willy', 'player_team': 'HKFC D'},
{'player_number': 5, 'player_forenames': 'Tom', 'player_surname': 'Taylor', 'player_nickname': 'Tayls', 'player_team': 'HKFC D'},
{'player_number': 1, 'player_forenames': 'John', 'player_surname': 'Smith', 'player_nickname': 'Smithers', 'player_team': 'HKFC C'},
{'player_number': 2, 'player_forenames': 'Mike', 'player_surname': 'Jones', 'player_nickname': 'Jonesy', 'player_team': 'HKFC C'},
{'player_number': 3, 'player_forenames': 'David', 'player_surname': 'Brown', 'player_nickname': 'Brownie', 'player_team': 'HKFC C'},
{'player_number': 4, 'player_forenames': 'Chris', 'player_surname': 'Wilson', 'player_nickname': 'Willy', 'player_team': 'HKFC C'},
{'player_number': 5, 'player_forenames': 'Tom', 'player_surname': 'Taylor', 'player_nickname': 'Tayls', 'player_team': 'HKFC C'},
]
for player_data in players_data:
player = Player(**player_data)
session.add(player)
# Check if player already exists
existing_player = session.query(Player).filter_by(player_number=player_data['player_number']).first()
if not existing_player:
player = Player(**player_data)
session.add(player)
# Create sample admin settings
admin_settings = AdminSettings(
userid='admin',
next_fixture=1,
next_club='KCC',
next_team='KCC A',
curr_motm=1,
curr_dotd=2,
oppo_logo='/static/images/kcc_logo.png',
hkfc_logo='/static/images/hkfc_logo.png',
motm_url_suffix='abc123',
prev_fixture=0
)
session.add(admin_settings)
# Create sample admin settings (only if they don't exist)
existing_admin = session.query(AdminSettings).filter_by(userid='admin').first()
if not existing_admin:
admin_settings = AdminSettings(
userid='admin',
next_fixture=1,
next_club='KCC',
next_team='KCC A',
curr_motm=1,
curr_dotd=2,
oppo_logo='/static/images/kcc_logo.png',
hkfc_logo='/static/images/hkfc_logo.png',
motm_url_suffix='abc123',
prev_fixture=0
)
session.add(admin_settings)
# Create sample fixtures
# Create sample fixtures (only if they don't exist)
fixtures_data = [
{'fixture_number': 1, 'date': datetime(2024, 1, 15), 'home_team': 'HKFC D', 'away_team': 'KCC A', 'venue': 'HKFC'},
{'fixture_number': 2, 'date': datetime(2024, 1, 22), 'home_team': 'USRC A', 'away_team': 'HKFC D', 'venue': 'USRC'},
{'fixture_number': 3, 'date': datetime(2024, 1, 29), 'home_team': 'HKFC D', 'away_team': 'Valley A', 'venue': 'HKFC'},
{'fixture_number': 1, 'date': datetime(2024, 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': 3, 'date': datetime(2024, 1, 29), 'home_team': 'HKFC C', 'away_team': 'Valley A', 'venue': 'HKFC'},
]
for fixture_data in fixtures_data:
fixture = HockeyFixture(**fixture_data)
session.add(fixture)
# Check if fixture already exists
existing_fixture = session.query(HockeyFixture).filter_by(fixture_number=fixture_data['fixture_number']).first()
if not existing_fixture:
fixture = HockeyFixture(**fixture_data)
session.add(fixture)
session.commit()

View File

@ -18,7 +18,7 @@ class motmAdminForm(FlaskForm):
class adminSettingsForm2(FlaskForm):
nextMatch = SelectField('Fixture', choices=[])
nextMatchDate = DateField('Match Date', format='%Y-%m-%d')
nextOppoClub = StringField('Next Opposition Club:')
nextOppoTeam = StringField("Next Opposition Team:")
currMotM = SelectField('Current Man of the Match:', choices=[])
@ -47,8 +47,7 @@ class DatabaseSetupForm(FlaskForm):
# SQLite fields
sqlite_database_path = StringField('Database File Path',
default='hockey_results.db',
validators=[InputRequired()])
default='hockey_results.db')
# MySQL/MariaDB fields
mysql_host = StringField('Host', default='localhost')
@ -73,3 +72,57 @@ class DatabaseSetupForm(FlaskForm):
test_connection = SubmitField('Test Connection')
save_config = SubmitField('Save Configuration')
initialize_database = SubmitField('Initialize Database')
class PlayerForm(FlaskForm):
"""Form for adding/editing players."""
player_number = IntegerField('Player Number', validators=[InputRequired()])
player_forenames = StringField('First Names', validators=[InputRequired()])
player_surname = StringField('Surname', validators=[InputRequired()])
player_nickname = StringField('Nickname', validators=[InputRequired()])
player_team = SelectField('Team',
choices=[('HKFC A', 'HKFC A'),
('HKFC B', 'HKFC B'),
('HKFC C', 'HKFC C')],
default='HKFC C')
# Action buttons
save_player = SubmitField('Save Player')
cancel = SubmitField('Cancel')
class ClubForm(FlaskForm):
"""Form for adding/editing clubs."""
hockey_club = StringField('Club Name', validators=[InputRequired()])
logo_url = StringField('Logo URL', validators=[InputRequired()])
# Action buttons
save_club = SubmitField('Save Club')
cancel = SubmitField('Cancel')
class TeamForm(FlaskForm):
"""Form for adding/editing teams."""
club = StringField('Club', validators=[InputRequired()])
team = StringField('Team', validators=[InputRequired()])
display_name = StringField('Display Name', validators=[InputRequired()])
league = StringField('League', validators=[InputRequired()])
# Action buttons
save_team = SubmitField('Save Team')
cancel = SubmitField('Cancel')
class DataImportForm(FlaskForm):
"""Form for importing data from Hong Kong Hockey Association."""
import_clubs = BooleanField('Import Clubs', default=True)
import_teams = BooleanField('Import Teams', default=True)
import_players = BooleanField('Import Sample Players', default=False)
# Action buttons
import_data = SubmitField('Import Data')
cancel = SubmitField('Cancel')

View File

@ -15,8 +15,9 @@ from flask_basicauth import BasicAuth
from wtforms import StringField, PasswordField, BooleanField
from wtforms import DateField
from wtforms.validators import InputRequired, Email, Length
from forms import motmForm, adminSettingsForm2, goalsAssistsForm, DatabaseSetupForm
from forms import motmForm, adminSettingsForm2, goalsAssistsForm, DatabaseSetupForm, PlayerForm, ClubForm, TeamForm, DataImportForm
from db_config import sql_write, sql_write_static, sql_read, sql_read_static
from sqlalchemy import text
from tables import matchSquadTable
from readSettings import mySettings
from db_setup import db_config_manager
@ -37,10 +38,15 @@ def index():
@app.route('/motm/<randomUrlSuffix>')
def motm_vote(randomUrlSuffix):
"""Public voting page for Man of the Match and Dick of the Day"""
sql = "SELECT playerNumber, playerForenames, playerSurname, playerNickname FROM _hkfcD_matchSquad ORDER BY RAND()"
sql2 = "SELECT nextClub, nextTeam, nextDate, oppoLogo, hkfcLogo, currMotM, currDotD, nextFixture FROM hkfcDAdminSettings"
sql = "SELECT playerNumber, playerForenames, playerSurname, playerNickname FROM _hkfcC_matchSquad ORDER BY RAND()"
sql2 = "SELECT nextClub, nextTeam, nextDate, oppoLogo, hkfcLogo, currMotM, currDotD, nextFixture FROM motmAdminSettings"
rows = sql_read(sql)
nextInfo = sql_read_static(sql2)
# Handle empty results
if not nextInfo:
return render_template('error.html', message="Database not initialized. Please go to Database Setup to initialize the database.")
nextClub = nextInfo[0]['nextClub']
nextTeam = nextInfo[0]['nextTeam']
nextFixture = nextInfo[0]['nextFixture']
@ -49,25 +55,34 @@ def motm_vote(randomUrlSuffix):
currMotM = nextInfo[0]['currMotM']
currDotD = nextInfo[0]['currDotD']
oppo = nextTeam
sql3 = "SELECT hockeyResults2021.hockeyFixtures.date, hockeyResults.hkfcDAdminSettings.nextFixture FROM hockeyResults2021.hockeyFixtures INNER JOIN hockeyResults.hkfcDAdminSettings ON hockeyResults2021.hockeyFixtures.fixtureNumber = hockeyResults.hkfcDAdminSettings.nextFixture"
sql3 = "SELECT hockeyResults2021.hockeyFixtures.date, hockeyResults.motmAdminSettings.nextFixture FROM hockeyResults2021.hockeyFixtures INNER JOIN hockeyResults.motmAdminSettings ON hockeyResults2021.hockeyFixtures.fixtureNumber = hockeyResults.motmAdminSettings.nextFixture"
nextMatchDate = sql_read(sql3)
if not nextMatchDate:
return render_template('error.html', message="No fixtures found. Please initialize the database with sample data.")
nextDate = nextMatchDate[0]['date']
formatDate = datetime.strftime(nextDate, '%A, %d %B %Y')
sql3 = "SELECT playerPictureURL FROM _HKFC_players INNER JOIN hockeyResults.hkfcDAdminSettings ON _HKFC_players.playerNumber=hockeyResults.hkfcDAdminSettings.currMotM"
sql4 = "SELECT playerPictureURL FROM _HKFC_players INNER JOIN hockeyResults.hkfcDAdminSettings ON _HKFC_players.playerNumber=hockeyResults.hkfcDAdminSettings.currDotD"
sql3 = "SELECT playerPictureURL FROM _HKFC_players INNER JOIN hockeyResults.motmAdminSettings ON _HKFC_players.playerNumber=hockeyResults.motmAdminSettings.currMotM"
sql4 = "SELECT playerPictureURL FROM _HKFC_players INNER JOIN hockeyResults.motmAdminSettings ON _HKFC_players.playerNumber=hockeyResults.motmAdminSettings.currDotD"
motm = sql_read(sql3)
dotd = sql_read(sql4)
# Handle empty results
if not motm or not dotd:
return render_template('error.html', message="Player data not found. Please initialize the database with sample data.")
motmURL = motm[0]['playerPictureURL']
dotdURL = dotd[0]['playerPictureURL']
sql5 = "SELECT comment FROM _motmComments INNER JOIN hockeyResults.hkfcDAdminSettings ON _motmComments.matchDate=hockeyResults.hkfcDAdminSettings.nextDate ORDER BY RAND() LIMIT 1"
sql5 = "SELECT comment FROM _motmComments INNER JOIN hockeyResults.motmAdminSettings ON _motmComments.matchDate=hockeyResults.motmAdminSettings.nextDate ORDER BY RAND() LIMIT 1"
comment = sql_read(sql5)
if comment == "":
comment = "No comments added yet"
form = motmForm()
sql6 = "SELECT motmUrlSuffix FROM hockeyResults.hkfcDAdminSettings WHERE userid='admin'"
sql6 = "SELECT motmUrlSuffix FROM hockeyResults.motmAdminSettings WHERE userid='admin'"
urlSuff = sql_read_static(sql6)
if not urlSuff:
return render_template('error.html', message="Admin settings not found. Please initialize the database.")
randomSuff = urlSuff[0]['motmUrlSuffix']
print(randomSuff)
if randomSuff == randomUrlSuffix:
@ -79,8 +94,11 @@ def motm_vote(randomUrlSuffix):
@app.route('/motm/comments', methods=['GET', 'POST'])
def match_comments():
"""Display and allow adding match comments"""
sql = "SELECT nextClub, nextTeam, nextDate, oppoLogo, hkfcLogo FROM hkfcDAdminSettings"
sql = "SELECT nextClub, nextTeam, nextDate, oppoLogo, hkfcLogo FROM motmAdminSettings"
row = sql_read_static(sql)
if not row:
return render_template('error.html', message="Database not initialized. Please go to Database Setup to initialize the database.")
_oppo = row[0]['nextClub']
commentDate = row[0]['nextDate'].strftime('%Y-%m-%d')
_matchDate = row[0]['nextDate'].strftime('%Y_%m_%d')
@ -109,8 +127,8 @@ def vote_thanks():
_oppo = request.form['oppo']
if _motm and _dotd and request.method == 'POST':
sql = "INSERT INTO _hkfc_d_motm (playerNumber, playerName, motmTotal, motm_" + _matchDate + ") SELECT playerNumber, playerNickname, '1', '1' FROM _HKFC_players WHERE playerNumber='" + _motm + "' ON DUPLICATE KEY UPDATE motmTotal = motmTotal + 1, motm_" + _matchDate + " = motm_" + _matchDate + " + 1"
sql2 = "INSERT INTO _hkfc_d_motm (playerNumber, playerName, dotdTotal, dotd_" + _matchDate + ") SELECT playerNumber, playerNickname, '1', '1' FROM _HKFC_players WHERE playerNumber='" + _dotd + "' ON DUPLICATE KEY UPDATE dotdTotal = dotdTotal + 1, dotd_" + _matchDate + " = dotd_" + _matchDate + " + 1"
sql = "INSERT INTO _hkfc_c_motm (playerNumber, playerName, motmTotal, motm_" + _matchDate + ") SELECT playerNumber, playerNickname, '1', '1' FROM _HKFC_players WHERE playerNumber='" + _motm + "' ON DUPLICATE KEY UPDATE motmTotal = motmTotal + 1, motm_" + _matchDate + " = motm_" + _matchDate + " + 1"
sql2 = "INSERT INTO _hkfc_c_motm (playerNumber, playerName, dotdTotal, dotd_" + _matchDate + ") SELECT playerNumber, playerNickname, '1', '1' FROM _HKFC_players WHERE playerNumber='" + _dotd + "' ON DUPLICATE KEY UPDATE dotdTotal = dotdTotal + 1, dotd_" + _matchDate + " = dotd_" + _matchDate + " + 1"
if _comments == "":
print("No comment")
elif _comments == "Optional comments added here":
@ -147,28 +165,36 @@ def motm_admin():
else:
print('Activated')
_nextTeam = request.form['nextOppoTeam']
_nextFixture = request.form['nextMatch']
_nextMatchDate = request.form['nextMatchDate']
_currMotM = request.form['currMotM']
_currDotD = request.form['currDotD']
sql1 = "SELECT club FROM _clubTeams WHERE displayName='" + _nextTeam + "'"
_nextClubName = sql_read_static(sql1)
if not _nextClubName:
flash('Error: Club not found for team ' + _nextTeam, 'error')
return redirect(url_for('motm_admin'))
_nextClub = _nextClubName[0]['club']
sql = "UPDATE hkfcDAdminSettings SET nextFixture='" + _nextFixture + "', nextClub='" + _nextClub + "', nextTeam='" + _nextTeam + "', currMotM=" + _currMotM + ", currDotD=" + _currDotD + ""
sql = "UPDATE motmAdminSettings SET nextDate='" + _nextMatchDate + "', nextClub='" + _nextClub + "', nextTeam='" + _nextTeam + "', currMotM=" + _currMotM + ", currDotD=" + _currDotD + ""
sql_write_static(sql)
sql2 = "UPDATE hkfcDAdminSettings INNER JOIN mensHockeyClubs ON hkfcDAdminSettings.nextClub = mensHockeyClubs.hockeyClub SET hkfcDAdminSettings.oppoLogo = mensHockeyClubs.logoURL WHERE mensHockeyClubs.hockeyClub='" + _nextClub + "'"
sql2 = "UPDATE motmAdminSettings INNER JOIN mensHockeyClubs ON motmAdminSettings.nextClub = mensHockeyClubs.hockeyClub SET motmAdminSettings.oppoLogo = mensHockeyClubs.logoURL WHERE mensHockeyClubs.hockeyClub='" + _nextClub + "'"
sql_write_static(sql2)
if form.saveButton.data:
flash('Settings saved!')
urlSuffix = randomUrlSuffix(8)
print(urlSuffix)
sql3 = "UPDATE hkfcDAdminSettings SET motmUrlSuffix='" + urlSuffix + "' WHERE userid='admin'"
sql3 = "UPDATE motmAdminSettings SET motmUrlSuffix='" + urlSuffix + "' WHERE userid='admin'"
sql_write_static(sql3)
flash('MotM URL https://hockey.ervine.cloud/motm/'+urlSuffix)
elif form.activateButton.data:
sql4 = "ALTER TABLE _hkfc_d_motm ADD COLUMN motm_" + _nextFixture + " smallint DEFAULT 0, ADD COLUMN dotd_" + _nextFixture + " smallint DEFAULT 0, ADD COLUMN assists_" + _nextFixture + " smallint DEFAULT 0, ADD COLUMN goals_" + _nextFixture + " smallint DEFAULT 0 "
# Generate a fixture number based on the date
_nextFixture = _nextMatchDate.replace('-', '')
sql4 = "ALTER TABLE _hkfc_c_motm ADD COLUMN motm_" + _nextFixture + " smallint DEFAULT 0, ADD COLUMN dotd_" + _nextFixture + " smallint DEFAULT 0, ADD COLUMN assists_" + _nextFixture + " smallint DEFAULT 0, ADD COLUMN goals_" + _nextFixture + " smallint DEFAULT 0 "
sql_write(sql4)
sql5 = "SELECT motmUrlSuffix FROM hkfcDAdminSettings WHERE userid='admin'"
sql5 = "SELECT motmUrlSuffix FROM motmAdminSettings WHERE userid='admin'"
tempSuffix = sql_read_static(sql5)
if not tempSuffix:
flash('Error: Admin settings not found', 'error')
return redirect(url_for('motm_admin'))
currSuffix = tempSuffix[0]['motmUrlSuffix']
print(currSuffix)
flash('Man of the Match vote is now activated')
@ -176,15 +202,31 @@ def motm_admin():
else:
flash('Something went wrong - check with Smithers')
sql7 = "SELECT date, homeTeam, awayTeam, venue, fixtureNumber FROM hockeyFixtures WHERE homeTeam='HKFC D' OR awayTeam='HKFC D'"
matches = sql_read(sql7)
form.nextMatch.choices = [(match['fixtureNumber'], match['date']) for match in matches]
# Load current settings to populate the form
sql_current = "SELECT nextDate FROM motmAdminSettings WHERE userid='admin'"
current_settings = sql_read_static(sql_current)
if current_settings:
from datetime import datetime
try:
current_date = datetime.strptime(current_settings[0]['nextDate'], '%Y-%m-%d').date()
form.nextMatchDate.data = current_date
except:
pass
sql4 = "SELECT hockeyClub FROM mensHockeyClubs ORDER BY hockeyClub"
sql5 = "SELECT nextClub, oppoLogo FROM hkfcDAdminSettings"
sql6 = "SELECT playerNumber, playerForenames, playerSurname FROM _hkfcD_matchSquad_" + prevFixture + " ORDER BY playerForenames"
sql5 = "SELECT nextClub, oppoLogo FROM motmAdminSettings"
sql6 = "SELECT playerNumber, playerForenames, playerSurname FROM _hkfcC_matchSquad_" + prevFixture + " ORDER BY playerForenames"
clubs = sql_read_static(sql4)
settings = sql_read_static(sql5)
players = sql_read(sql6)
# Handle empty results gracefully
if not clubs:
clubs = []
if not settings:
settings = [{'nextClub': 'Unknown', 'oppoLogo': '/static/images/default_logo.png'}]
if not players:
players = []
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.currDotD.choices = [(player['playerNumber'], player['playerForenames'] + " " + player['playerSurname']) for player in players]
@ -193,15 +235,286 @@ def motm_admin():
return render_template('motm_admin.html', form=form, nextOppoLogo=clubLogo)
@app.route('/admin/players', methods=['GET'])
@basic_auth.required
def player_management():
"""Admin page for managing players"""
sql = text("SELECT playerNumber, playerForenames, playerSurname, playerNickname, playerTeam FROM _HKFC_players ORDER BY playerNumber")
players = sql_read(sql)
return render_template('player_management.html', players=players)
@app.route('/admin/players/add', methods=['GET', 'POST'])
@basic_auth.required
def add_player():
"""Add a new player"""
form = PlayerForm()
if form.validate_on_submit():
if form.save_player.data:
# Check if player number already exists
sql_check = text("SELECT playerNumber FROM _HKFC_players WHERE playerNumber = :player_number")
existing = sql_read(sql_check, {'player_number': form.player_number.data})
if existing:
flash('Player number already exists!', 'error')
return render_template('add_player.html', form=form)
# Insert new player
sql = text("INSERT INTO _HKFC_players (playerNumber, playerForenames, playerSurname, playerNickname, playerTeam) VALUES (:player_number, :forenames, :surname, :nickname, :team)")
sql_write(sql, {
'player_number': form.player_number.data,
'forenames': form.player_forenames.data,
'surname': form.player_surname.data,
'nickname': form.player_nickname.data,
'team': form.player_team.data
})
flash('Player added successfully!', 'success')
return redirect(url_for('player_management'))
elif form.cancel.data:
return redirect(url_for('player_management'))
return render_template('add_player.html', form=form)
@app.route('/admin/players/edit/<int:player_number>', methods=['GET', 'POST'])
@basic_auth.required
def edit_player(player_number):
"""Edit an existing player"""
form = PlayerForm()
if request.method == 'GET':
# Load player data
sql = text("SELECT playerNumber, playerForenames, playerSurname, playerNickname, playerTeam FROM _HKFC_players WHERE playerNumber = :player_number")
player_data = sql_read(sql, {'player_number': player_number})
if not player_data:
flash('Player not found!', 'error')
return redirect(url_for('player_management'))
player = player_data[0]
form.player_number.data = player['playerNumber']
form.player_forenames.data = player['playerForenames']
form.player_surname.data = player['playerSurname']
form.player_nickname.data = player['playerNickname']
form.player_team.data = player['playerTeam']
if form.validate_on_submit():
if form.save_player.data:
# Update player
sql = text("UPDATE _HKFC_players SET playerForenames = :forenames, playerSurname = :surname, playerNickname = :nickname, playerTeam = :team WHERE playerNumber = :player_number")
sql_write(sql, {
'forenames': form.player_forenames.data,
'surname': form.player_surname.data,
'nickname': form.player_nickname.data,
'team': form.player_team.data,
'player_number': player_number
})
flash('Player updated successfully!', 'success')
return redirect(url_for('player_management'))
elif form.cancel.data:
return redirect(url_for('player_management'))
return render_template('edit_player.html', form=form, player_number=player_number)
@app.route('/admin/players/delete/<int:player_number>', methods=['POST'])
@basic_auth.required
def delete_player(player_number):
"""Delete a player"""
sql = text("DELETE FROM _HKFC_players WHERE playerNumber = :player_number")
sql_write(sql, {'player_number': player_number})
flash('Player deleted successfully!', 'success')
return redirect(url_for('player_management'))
@app.route('/admin/squad', methods=['GET'])
@basic_auth.required
def match_squad():
"""Admin page for managing match squad"""
sql1 = "SELECT team from _clubTeams WHERE club='HKFC' ORDER BY team"
sql2 = "SELECT playerTeam, playerForenames, playerSurname, playerNickname, playerNumber FROM _HKFC_players"
teams = sql_read(sql1)
players = sql_read(sql2)
return render_template('match_squad.html', teams=teams, players=players)
sql = text("SELECT playerNumber, playerForenames, playerSurname, playerNickname, playerTeam FROM _HKFC_players ORDER BY playerTeam, playerNumber")
players = sql_read(sql)
return render_template('match_squad.html', players=players)
# ==================== CLUB MANAGEMENT ====================
@app.route('/admin/clubs', methods=['GET'])
@basic_auth.required
def club_management():
"""Admin page for managing clubs"""
sql = text("SELECT id, hockey_club, logo_url FROM clubs ORDER BY hockey_club")
clubs = sql_read(sql)
return render_template('club_management.html', clubs=clubs)
@app.route('/admin/clubs/add', methods=['GET', 'POST'])
@basic_auth.required
def add_club():
"""Add a new club"""
form = ClubForm()
if form.validate_on_submit():
if form.save_club.data:
# Check if club already exists
sql_check = text("SELECT hockey_club FROM clubs WHERE hockey_club = :club_name")
existing = sql_read(sql_check, {'club_name': form.hockey_club.data})
if existing:
flash('Club already exists!', 'error')
return render_template('add_club.html', form=form)
# Insert new club
sql = text("INSERT INTO clubs (hockey_club, logo_url) VALUES (:club_name, :logo_url)")
sql_write(sql, {
'club_name': form.hockey_club.data,
'logo_url': form.logo_url.data
})
flash('Club added successfully!', 'success')
return redirect(url_for('club_management'))
elif form.cancel.data:
return redirect(url_for('club_management'))
return render_template('add_club.html', form=form)
@app.route('/admin/clubs/edit/<int:club_id>', methods=['GET', 'POST'])
@basic_auth.required
def edit_club(club_id):
"""Edit an existing club"""
form = ClubForm()
if request.method == 'GET':
# Load club data
sql = text("SELECT id, hockey_club, logo_url FROM clubs WHERE id = :club_id")
club_data = sql_read(sql, {'club_id': club_id})
if not club_data:
flash('Club not found!', 'error')
return redirect(url_for('club_management'))
club = club_data[0]
form.hockey_club.data = club['hockey_club']
form.logo_url.data = club['logo_url']
if form.validate_on_submit():
if form.save_club.data:
# Update club
sql = text("UPDATE clubs SET hockey_club = :club_name, logo_url = :logo_url WHERE id = :club_id")
sql_write(sql, {
'club_name': form.hockey_club.data,
'logo_url': form.logo_url.data,
'club_id': club_id
})
flash('Club updated successfully!', 'success')
return redirect(url_for('club_management'))
elif form.cancel.data:
return redirect(url_for('club_management'))
return render_template('edit_club.html', form=form, club_id=club_id)
@app.route('/admin/clubs/delete/<int:club_id>', methods=['POST'])
@basic_auth.required
def delete_club(club_id):
"""Delete a club"""
sql = text("DELETE FROM clubs WHERE id = :club_id")
sql_write(sql, {'club_id': club_id})
flash('Club deleted successfully!', 'success')
return redirect(url_for('club_management'))
# ==================== TEAM MANAGEMENT ====================
@app.route('/admin/teams', methods=['GET'])
@basic_auth.required
def team_management():
"""Admin page for managing teams"""
sql = text("SELECT id, club, team, display_name, league FROM teams ORDER BY club, team")
teams = sql_read(sql)
return render_template('team_management.html', teams=teams)
@app.route('/admin/teams/add', methods=['GET', 'POST'])
@basic_auth.required
def add_team():
"""Add a new team"""
form = TeamForm()
if form.validate_on_submit():
if form.save_team.data:
# Check if team already exists
sql_check = text("SELECT club, team FROM teams WHERE club = :club AND team = :team")
existing = sql_read(sql_check, {'club': form.club.data, 'team': form.team.data})
if existing:
flash('Team already exists!', 'error')
return render_template('add_team.html', form=form)
# Insert new team
sql = text("INSERT INTO teams (club, team, display_name, league) VALUES (:club, :team, :display_name, :league)")
sql_write(sql, {
'club': form.club.data,
'team': form.team.data,
'display_name': form.display_name.data,
'league': form.league.data
})
flash('Team added successfully!', 'success')
return redirect(url_for('team_management'))
elif form.cancel.data:
return redirect(url_for('team_management'))
return render_template('add_team.html', form=form)
@app.route('/admin/teams/edit/<int:team_id>', methods=['GET', 'POST'])
@basic_auth.required
def edit_team(team_id):
"""Edit an existing team"""
form = TeamForm()
if request.method == 'GET':
# Load team data
sql = text("SELECT id, club, team, display_name, league FROM teams WHERE id = :team_id")
team_data = sql_read(sql, {'team_id': team_id})
if not team_data:
flash('Team not found!', 'error')
return redirect(url_for('team_management'))
team = team_data[0]
form.club.data = team['club']
form.team.data = team['team']
form.display_name.data = team['display_name']
form.league.data = team['league']
if form.validate_on_submit():
if form.save_team.data:
# Update team
sql = text("UPDATE teams SET club = :club, team = :team, display_name = :display_name, league = :league WHERE id = :team_id")
sql_write(sql, {
'club': form.club.data,
'team': form.team.data,
'display_name': form.display_name.data,
'league': form.league.data,
'team_id': team_id
})
flash('Team updated successfully!', 'success')
return redirect(url_for('team_management'))
elif form.cancel.data:
return redirect(url_for('team_management'))
return render_template('edit_team.html', form=form, team_id=team_id)
@app.route('/admin/teams/delete/<int:team_id>', methods=['POST'])
@basic_auth.required
def delete_team(team_id):
"""Delete a team"""
sql = text("DELETE FROM teams WHERE id = :team_id")
sql_write(sql, {'team_id': team_id})
flash('Team deleted successfully!', 'success')
return redirect(url_for('team_management'))
@app.route('/admin/squad/submit', methods=['POST'])
@ -210,9 +523,9 @@ def match_squad_submit():
"""Process squad selection"""
_playerNumbers = request.form.getlist('playerNumber')
for _playerNumber in _playerNumbers:
sql = "INSERT INTO _hkfcD_matchSquad (playerNumber, playerForenames, playerSurname, playerNickname) SELECT playerNumber, playerForenames, playerSurname, playerNickname FROM _HKFC_players WHERE playerNumber='" + _playerNumber + "'"
sql = "INSERT INTO _hkfcC_matchSquad (playerNumber, playerForenames, playerSurname, playerNickname) SELECT playerNumber, playerForenames, playerSurname, playerNickname FROM _HKFC_players WHERE playerNumber='" + _playerNumber + "'"
sql_write(sql)
sql2 = "SELECT playerNumber, playerForenames, playerSurname, playerNickname FROM _hkfcD_matchSquad"
sql2 = "SELECT playerNumber, playerForenames, playerSurname, playerNickname FROM _hkfcC_matchSquad"
players = sql_read(sql2)
table = matchSquadTable(players)
table.border = True
@ -224,7 +537,7 @@ def match_squad_submit():
@basic_auth.required
def match_squad_list():
"""Display current squad list"""
sql = "SELECT playerNumber, playerForenames, playerSurname, playerNickname FROM _hkfcD_matchSquad"
sql = "SELECT playerNumber, playerForenames, playerSurname, playerNickname FROM _hkfcC_matchSquad"
players = sql_read(sql)
table = matchSquadTable(players)
table.border = True
@ -237,7 +550,7 @@ def match_squad_list():
def delPlayerFromSquad():
"""Remove player from squad"""
_playerNumber = request.args['playerNumber']
sql = "DELETE FROM _hkfcD_matchSquad WHERE playerNumber=" + _playerNumber + ""
sql = "DELETE FROM _hkfcC_matchSquad WHERE playerNumber=" + _playerNumber + ""
sql_write(sql)
return render_template('player_removed.html', number=_playerNumber)
@ -248,9 +561,9 @@ def matchSquadReset():
"""Reset squad for new match"""
_matchNumber = str(mySettings('fixture'))
print(_matchNumber)
sql1 = "RENAME TABLE _hkfcD_matchSquad TO _hkfcD_matchSquad_" + _matchNumber + ""
sql2 = "CREATE TABLE _hkfcD_matchSquad (playerNumber smallint UNIQUE, playerForenames varchar(50), playerSurname varchar(30), playerNickname varchar(30) NOT NULL, PRIMARY KEY (playerNumber))"
sql3 = "UPDATE hkfcDAdminSettings SET prevFixture='" + _matchNumber + "'"
sql1 = "RENAME TABLE _hkfcC_matchSquad TO _hkfcC_matchSquad_" + _matchNumber + ""
sql2 = "CREATE TABLE _hkfcC_matchSquad (playerNumber smallint UNIQUE, playerForenames varchar(50), playerSurname varchar(30), playerNickname varchar(30) NOT NULL, PRIMARY KEY (playerNumber))"
sql3 = "UPDATE motmAdminSettings SET prevFixture='" + _matchNumber + "'"
sql_write(sql1)
sql_write(sql2)
sql_write_static(sql3)
@ -262,10 +575,10 @@ def matchSquadReset():
def stats_admin():
"""Admin page for managing goals and assists statistics"""
form = goalsAssistsForm()
sql = "SELECT date, homeTeam, awayTeam, venue, fixtureNumber FROM hockeyFixtures WHERE homeTeam='HKFC D' OR awayTeam='HKFC D'"
sql = "SELECT date, homeTeam, awayTeam, venue, fixtureNumber FROM hockeyFixtures WHERE homeTeam='HKFC C' OR awayTeam='HKFC C'"
matches = sql_read(sql)
form.match.choices = [(match['fixtureNumber'], match['date']) for match in matches]
sql2 = "SELECT playerNumber, playerNickname FROM _hkfcD_matchSquad"
sql2 = "SELECT playerNumber, playerNickname FROM _hkfcC_matchSquad"
players = sql_read(sql2)
return render_template('goals_assists_admin.html', data=players, form=form)
@ -282,7 +595,7 @@ def goalsAssistsSubmit():
goals = request.form.getlist('goals')
match = request.form['match']
for idx, player in enumerate(playerNumber):
sql = "INSERT INTO _hkfc_d_motm (playerNumber, playerName, assistsTotal, goalsTotal, assists_" + match + ", goals_" + match + ") SELECT playerNumber, playerNickname, '" + assists[idx] + "', '" + goals[idx] + "', '" + assists[idx] + "', '" + goals[idx] + "' FROM _HKFC_players WHERE playerNumber='" + player + "' ON DUPLICATE KEY UPDATE assistsTotal = assistsTotal + " + assists[idx] + ", goalsTotal = goalsTotal + " + goals[idx] + ", assists_" + match + " = " + assists[idx] + ", goals_" + match + " = " + goals[idx] + ""
sql = "INSERT INTO _hkfc_c_motm (playerNumber, playerName, assistsTotal, goalsTotal, assists_" + match + ", goals_" + match + ") SELECT playerNumber, playerNickname, '" + assists[idx] + "', '" + goals[idx] + "', '" + assists[idx] + "', '" + goals[idx] + "' FROM _HKFC_players WHERE playerNumber='" + player + "' ON DUPLICATE KEY UPDATE assistsTotal = assistsTotal + " + assists[idx] + ", goalsTotal = goalsTotal + " + goals[idx] + ", assists_" + match + " = " + assists[idx] + ", goals_" + match + " = " + goals[idx] + ""
sql_write(sql)
except Exception as e:
print(e)
@ -313,6 +626,8 @@ def admin_fixture_lookup(fixture):
"""API endpoint for fixture team lookup"""
sql = "SELECT homeTeam, awayTeam FROM hockeyFixtures WHERE fixtureNumber='" + fixture + "'"
myteams = sql_read(sql)
if not myteams:
return jsonify({'error': 'Fixture not found'})
if myteams[0]['homeTeam'].startswith("HKFC"):
nextOppo = myteams[0]['awayTeam']
else:
@ -325,15 +640,21 @@ def admin_fixture_logo_lookup(fixture):
"""API endpoint for fixture logo lookup"""
sql = "SELECT homeTeam, awayTeam FROM hockeyFixtures WHERE fixtureNumber='" + fixture + "'"
myteams = sql_read(sql)
if myteams[0]['homeTeam'].startswith("HKFC D"):
if not myteams:
return jsonify({'error': 'Fixture not found'})
if myteams[0]['homeTeam'].startswith("HKFC C"):
nextOppo = myteams[0]['awayTeam']
else:
nextOppo = myteams[0]['homeTeam']
sql2 = "SELECT club FROM _clubTeams WHERE displayName ='" + nextOppo + "'"
clubs = sql_read_static(sql2)
if not clubs:
return jsonify({'error': 'Club not found'})
clubName = clubs[0]['club']
sql3 = "SELECT logoUrl FROM mensHockeyClubs WHERE hockeyClub ='" + clubName + "'"
logo = sql_read_static(sql3)
if not logo:
return jsonify({'error': 'Logo not found'})
clubLogo = logo[0]['logoUrl']
return jsonify(clubLogo)
@ -343,7 +664,7 @@ def vote_results():
"""API endpoint for voting results"""
_matchDate = str(mySettings('fixture'))
print(_matchDate)
sql = "SELECT playerName, motm_" + _matchDate + ", dotd_" + _matchDate + " FROM _hkfc_d_motm WHERE (motm_" + _matchDate + " > '0') OR (dotd_" + _matchDate + " > '0')"
sql = "SELECT playerName, motm_" + _matchDate + ", dotd_" + _matchDate + " FROM _hkfc_c_motm WHERE (motm_" + _matchDate + " > '0') OR (dotd_" + _matchDate + " > '0')"
print(sql)
rows = sql_read(sql)
print(rows)
@ -353,7 +674,7 @@ def vote_results():
@app.route('/api/poty-results')
def poty_results():
"""API endpoint for Player of the Year results"""
sql = "SELECT playerName, motmTotal, dotdTotal FROM _hkfc_d_motm WHERE (motmTotal > '0') OR (dotdTotal > '0')"
sql = "SELECT playerName, motmTotal, dotdTotal FROM _hkfc_c_motm WHERE (motmTotal > '0') OR (dotdTotal > '0')"
print(sql)
rows = sql_read(sql)
return json.dumps(rows)
@ -382,14 +703,6 @@ def database_setup():
"""Admin page for database setup and configuration"""
form = DatabaseSetupForm()
# Load current configuration
current_config = db_config_manager.get_config_dict()
# Populate form with current configuration
for field_name, value in current_config.items():
if hasattr(form, field_name):
getattr(form, field_name).data = value
if request.method == 'POST':
if form.test_connection.data:
# Test database connection
@ -452,6 +765,14 @@ def database_setup():
except Exception as e:
flash(f'❌ Database initialization failed: {str(e)}', 'error')
# Load current configuration for display
current_config = db_config_manager.get_config_dict()
# Populate form with current configuration (only for GET requests or after POST processing)
for field_name, value in current_config.items():
if hasattr(form, field_name):
getattr(form, field_name).data = value
return render_template('database_setup.html', form=form, current_config=current_config)
@ -460,8 +781,10 @@ def database_setup():
def database_status():
"""Admin page showing current database status and configuration"""
try:
from database import db_config
engine = db_config.engine
# Reload database configuration to get latest settings
from database import DatabaseConfig
current_db_config = DatabaseConfig()
engine = current_db_config.engine
# Test connection
with engine.connect() as conn:
@ -472,7 +795,7 @@ def database_status():
# Get database info
db_info = {
'database_url': str(db_config.database_url),
'database_url': str(current_db_config.database_url),
'database_type': os.getenv('DATABASE_TYPE', 'unknown'),
'connection_status': connection_status
}

View File

@ -5,7 +5,7 @@ from db_config import sql_read_static
def mySettings(setting):
try:
sql = "SELECT " + setting + " FROM hkfcDAdminSettings WHERE userid='admin'"
sql = "SELECT " + setting + " FROM motmAdminSettings WHERE userid='admin'"
rows = sql_read_static(sql)
if rows:
return rows[0][setting]

View File

@ -0,0 +1,74 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Add Club - HKFC Men's C Team</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3>Add New Club</h3>
</div>
<div class="card-body">
{% 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' }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.hockey_club.label(class="form-label") }}
{{ form.hockey_club(class="form-control") }}
{% if form.hockey_club.errors %}
<div class="text-danger">
{% for error in form.hockey_club.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.logo_url.label(class="form-label") }}
{{ form.logo_url(class="form-control") }}
{% if form.logo_url.errors %}
<div class="text-danger">
{% for error in form.logo_url.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
<small class="form-text text-muted">Enter the full URL to the club's logo image</small>
</div>
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
{{ form.cancel(class="btn btn-secondary me-md-2") }}
{{ form.save_club(class="btn btn-primary") }}
</div>
</form>
</div>
</div>
<div class="mt-3">
<a href="/admin/clubs" class="btn btn-outline-secondary">Back to Club Management</a>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,109 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Add Player - HKFC Men's C Team</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3>Add New Player</h3>
</div>
<div class="card-body">
{% 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' }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.player_number.label(class="form-label") }}
{{ form.player_number(class="form-control") }}
{% if form.player_number.errors %}
<div class="text-danger">
{% for error in form.player_number.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.player_forenames.label(class="form-label") }}
{{ form.player_forenames(class="form-control") }}
{% if form.player_forenames.errors %}
<div class="text-danger">
{% for error in form.player_forenames.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.player_surname.label(class="form-label") }}
{{ form.player_surname(class="form-control") }}
{% if form.player_surname.errors %}
<div class="text-danger">
{% for error in form.player_surname.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.player_nickname.label(class="form-label") }}
{{ form.player_nickname(class="form-control") }}
{% if form.player_nickname.errors %}
<div class="text-danger">
{% for error in form.player_nickname.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.player_team.label(class="form-label") }}
{{ form.player_team(class="form-select") }}
{% if form.player_team.errors %}
<div class="text-danger">
{% for error in form.player_team.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
{{ form.cancel(class="btn btn-secondary me-md-2") }}
{{ form.save_player(class="btn btn-primary") }}
</div>
</form>
</div>
</div>
<div class="mt-3">
<a href="/admin/players" class="btn btn-outline-secondary">Back to Player Management</a>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,101 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Add Team - HKFC Men's C Team</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3>Add New Team</h3>
</div>
<div class="card-body">
{% 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' }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.club.label(class="form-label") }}
{{ form.club(class="form-control") }}
{% if form.club.errors %}
<div class="text-danger">
{% for error in form.club.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
<small class="form-text text-muted">Enter the club name (e.g., HKFC, KCC, USRC)</small>
</div>
<div class="mb-3">
{{ form.team.label(class="form-label") }}
{{ form.team(class="form-control") }}
{% if form.team.errors %}
<div class="text-danger">
{% for error in form.team.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
<small class="form-text text-muted">Enter the team identifier (e.g., A, B, C, 1st, 2nd)</small>
</div>
<div class="mb-3">
{{ form.display_name.label(class="form-label") }}
{{ form.display_name(class="form-control") }}
{% if form.display_name.errors %}
<div class="text-danger">
{% for error in form.display_name.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
<small class="form-text text-muted">Enter the full display name (e.g., HKFC A, KCC 1st Team)</small>
</div>
<div class="mb-3">
{{ form.league.label(class="form-label") }}
{{ form.league(class="form-control") }}
{% if form.league.errors %}
<div class="text-danger">
{% for error in form.league.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
<small class="form-text text-muted">Enter the league/division (e.g., Premier Division, Division 1)</small>
</div>
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
{{ form.cancel(class="btn btn-secondary me-md-2") }}
{{ form.save_team(class="btn btn-primary") }}
</div>
</form>
</div>
</div>
<div class="mt-3">
<a href="/admin/teams" class="btn btn-outline-secondary">Back to Team Management</a>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Club Management - HKFC Men's C Team</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<div class="row">
<div class="col-md-12">
<h1>Club Management</h1>
<p class="lead">Manage hockey clubs in the database</p>
<div class="mb-3">
<a href="/admin/clubs/add" class="btn btn-primary">Add New Club</a>
<a href="/admin" class="btn btn-secondary">Back to Admin</a>
</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' }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
<div class="card">
<div class="card-header">
<h5>All Clubs</h5>
</div>
<div class="card-body">
{% if clubs %}
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Club Name</th>
<th>Logo URL</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for club in clubs %}
<tr>
<td>{{ club.id }}</td>
<td>{{ club.hockey_club }}</td>
<td>
<a href="{{ club.logo_url }}" target="_blank" class="text-decoration-none">
{{ club.logo_url }}
</a>
</td>
<td>
<a href="/admin/clubs/edit/{{ club.id }}" class="btn btn-sm btn-outline-primary">Edit</a>
<form method="POST" action="/admin/clubs/delete/{{ club.id }}" style="display: inline;" onsubmit="return confirm('Are you sure you want to delete this club?')">
<button type="submit" class="btn btn-sm btn-outline-danger">Delete</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="alert alert-info">
<h5>No clubs found</h5>
<p>There are no clubs in the database. <a href="/admin/clubs/add">Add the first club</a> to get started.</p>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,74 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Club - HKFC Men's C Team</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3>Edit Club #{{ club_id }}</h3>
</div>
<div class="card-body">
{% 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' }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.hockey_club.label(class="form-label") }}
{{ form.hockey_club(class="form-control") }}
{% if form.hockey_club.errors %}
<div class="text-danger">
{% for error in form.hockey_club.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.logo_url.label(class="form-label") }}
{{ form.logo_url(class="form-control") }}
{% if form.logo_url.errors %}
<div class="text-danger">
{% for error in form.logo_url.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
<small class="form-text text-muted">Enter the full URL to the club's logo image</small>
</div>
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
{{ form.cancel(class="btn btn-secondary me-md-2") }}
{{ form.save_club(class="btn btn-primary") }}
</div>
</form>
</div>
</div>
<div class="mt-3">
<a href="/admin/clubs" class="btn btn-outline-secondary">Back to Club Management</a>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,103 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Player - HKFC Men's C Team</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3>Edit Player #{{ player_number }}</h3>
</div>
<div class="card-body">
{% 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' }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.player_number.label(class="form-label") }}
{{ form.player_number(class="form-control", readonly=true) }}
<small class="form-text text-muted">Player number cannot be changed</small>
</div>
<div class="mb-3">
{{ form.player_forenames.label(class="form-label") }}
{{ form.player_forenames(class="form-control") }}
{% if form.player_forenames.errors %}
<div class="text-danger">
{% for error in form.player_forenames.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.player_surname.label(class="form-label") }}
{{ form.player_surname(class="form-control") }}
{% if form.player_surname.errors %}
<div class="text-danger">
{% for error in form.player_surname.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.player_nickname.label(class="form-label") }}
{{ form.player_nickname(class="form-control") }}
{% if form.player_nickname.errors %}
<div class="text-danger">
{% for error in form.player_nickname.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.player_team.label(class="form-label") }}
{{ form.player_team(class="form-select") }}
{% if form.player_team.errors %}
<div class="text-danger">
{% for error in form.player_team.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
</div>
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
{{ form.cancel(class="btn btn-secondary me-md-2") }}
{{ form.save_player(class="btn btn-primary") }}
</div>
</form>
</div>
</div>
<div class="mt-3">
<a href="/admin/players" class="btn btn-outline-secondary">Back to Player Management</a>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,101 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Team - HKFC Men's C Team</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3>Edit Team #{{ team_id }}</h3>
</div>
<div class="card-body">
{% 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' }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
<form method="POST">
{{ form.hidden_tag() }}
<div class="mb-3">
{{ form.club.label(class="form-label") }}
{{ form.club(class="form-control") }}
{% if form.club.errors %}
<div class="text-danger">
{% for error in form.club.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
<small class="form-text text-muted">Enter the club name (e.g., HKFC, KCC, USRC)</small>
</div>
<div class="mb-3">
{{ form.team.label(class="form-label") }}
{{ form.team(class="form-control") }}
{% if form.team.errors %}
<div class="text-danger">
{% for error in form.team.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
<small class="form-text text-muted">Enter the team identifier (e.g., A, B, C, 1st, 2nd)</small>
</div>
<div class="mb-3">
{{ form.display_name.label(class="form-label") }}
{{ form.display_name(class="form-control") }}
{% if form.display_name.errors %}
<div class="text-danger">
{% for error in form.display_name.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
<small class="form-text text-muted">Enter the full display name (e.g., HKFC A, KCC 1st Team)</small>
</div>
<div class="mb-3">
{{ form.league.label(class="form-label") }}
{{ form.league(class="form-control") }}
{% if form.league.errors %}
<div class="text-danger">
{% for error in form.league.errors %}
<small>{{ error }}</small>
{% endfor %}
</div>
{% endif %}
<small class="form-text text-muted">Enter the league/division (e.g., Premier Division, Division 1)</small>
</div>
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
{{ form.cancel(class="btn btn-secondary me-md-2") }}
{{ form.save_team(class="btn btn-primary") }}
</div>
</form>
</div>
</div>
<div class="mt-3">
<a href="/admin/teams" class="btn btn-outline-secondary">Back to Team Management</a>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -1,6 +1,6 @@
<html>
<head>
<title>HKFC Men's D Team - MOTM System</title>
<title>HKFC Men's C Team - MOTM System</title>
<link rel="stylesheet" media="screen" href ="/static/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/css/bootstrap-theme.min.css">
<meta name="viewport" content = "width=device-width, initial-scale=1.0">
@ -11,7 +11,7 @@
<div class="container">
<div class="row">
<div class="col-md-12">
<h1>HKFC Men's D Team - Man of the Match System</h1>
<h1>HKFC Men's C Team - Man of the Match System</h1>
<div class="jumbotron">
<h2>Welcome to the MOTM Voting System</h2>
<p>This system allows players to vote for Man of the Match and Dick of the Day, while providing admin tools for managing matches and squads.</p>
@ -31,18 +31,34 @@
<div class="col-md-6">
<h3>Admin Section</h3>
<div class="list-group">
<a href="/admin/players" class="list-group-item">
<h4 class="list-group-item-heading">Player Management</h4>
<p class="list-group-item-text">Add, edit, and manage players in the database</p>
</a>
<a href="/admin/clubs" class="list-group-item">
<h4 class="list-group-item-heading">Club Management</h4>
<p class="list-group-item-text">Add, edit, and manage hockey clubs</p>
</a>
<a href="/admin/teams" class="list-group-item">
<h4 class="list-group-item-heading">Team Management</h4>
<p class="list-group-item-text">Add, edit, and manage hockey teams</p>
</a>
<a href="/admin/squad" class="list-group-item">
<h4 class="list-group-item-heading">Match Squad Selection</h4>
<p class="list-group-item-text">Select players for the match squad</p>
</a>
<a href="/admin/squad/list" class="list-group-item">
<h4 class="list-group-item-heading">View Current Squad</h4>
<p class="list-group-item-text">View current match squad</p>
</a>
<a href="/admin/squad/reset" class="list-group-item">
<h4 class="list-group-item-heading">Reset Squad</h4>
<p class="list-group-item-text">Reset squad for new match</p>
</a>
<a href="/admin/motm" class="list-group-item">
<h4 class="list-group-item-heading">MOTM Admin</h4>
<p class="list-group-item-text">Manage match settings and activate voting</p>
</a>
<a href="/admin/squad" class="list-group-item">
<h4 class="list-group-item-heading">Squad Management</h4>
<p class="list-group-item-text">Add players to match squads</p>
</a>
<a href="/admin/squad/list" class="list-group-item">
<h4 class="list-group-item-heading">View Squad</h4>
<p class="list-group-item-text">View current match squad</p>
</a>
<a href="/admin/stats" class="list-group-item">
<h4 class="list-group-item-heading">Goals & Assists</h4>
<p class="list-group-item-text">Record goals and assists statistics</p>

View File

@ -1,6 +1,6 @@
<html>
<head>
<title>HKFC Men's D Team - Match Comments</title>
<title>HKFC Men's C Team - Match Comments</title>
<link rel="stylesheet" media="screen" href ="/static/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/css/bootstrap-theme.min.css">
<meta name="viewport" content = "width=device-width, initial-scale=1.0">

View File

@ -1,200 +1,73 @@
<html>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://fonts.googleapis.com/css?family=Raleway" rel="stylesheet">
<style>
* {
box-sizing: border-box;
}
body {
background-color: #f1f1f1;
}
#squadForm {
background-color: #ffffff;
margin: 100px auto;
font-family: Raleway;
padding: 40px;
width: 40%;
min-width: 300px;
}
h1 {
text-align: center;
}
input {
padding: 10px;
width: 10%;
font-size: 17px;
font-family: Raleway;
border: 1px solid #aaaaaa;
}
/* Mark input boxes that gets an error on validation: */
input.invalid {
background-color: #ffdddd;
}
/* Hide all steps by default: */
.tab {
display: none;
}
button {
background-color: #4CAF50;
color: #ffffff;
border: none;
padding: 10px 20px;
font-size: 17px;
font-family: Raleway;
cursor: pointer;
}
button:hover {
opacity: 0.8;
}
#prevBtn {
background-color: #bbbbbb;
}
/* Make circles that indicate the steps of the form: */
.step {
height: 15px;
width: 15px;
margin: 0 2px;
background-color: #bbbbbb;
border: none;
border-radius: 50%;
display: inline-block;
opacity: 0.5;
}
.step.active {
opacity: 1;
}
/* Mark the steps that are finished and valid: */
.step.finish {
background-color: #4CAF50;
}
</style>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Match Squad Selection - HKFC Men's C Team</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<form id="squadForm" action="/admin/squad/submit" method="post">
<h1>Add Players to Squad</h1>
<!-- One "tab" for each step in the form: -->
{% for team in teams %}
<div class="tab">{{ team.team }} Team Players
<p>
{% for item in players %}
{% if item.playerTeam == team.team %}
<label><input type="checkbox" name="playerNumber" value="{{ item.playerNumber }}">{{ item.playerNumber }} {{ item.playerForenames }} {{ item.playerSurname }}</label>
<br/>
{% endif %}
{% endfor %}
</p>
</div>
{% endfor %}
<div style="overflow:auto;">
<div style="float:right;">
<button type="button" id="prevBtn" onclick="nextPrev(-1)">Previous</button>
<button type="button" id="nextBtn" onclick="nextPrev(1)">Next</button>
</div>
</div>
<!-- Circles which indicates the steps of the form: -->
<div style="text-align:center;margin-top:40px;">
<span class="step"></span>
<span class="step"></span>
<span class="step"></span>
<span class="step"></span>
<span class="step"></span>
<span class="step"></span>
<span class="step"></span>
<span class="step"></span>
</div>
</form>
<script>
var currentTab = 0; // Current tab is set to be the first tab (0)
showTab(currentTab); // Display the crurrent tab
function showTab(n) {
// This function will display the specified tab of the form...
var x = document.getElementsByClassName("tab");
x[n].style.display = "block";
//... and fix the Previous/Next buttons:
if (n == 0) {
document.getElementById("prevBtn").style.display = "none";
} else {
document.getElementById("prevBtn").style.display = "inline";
}
if (n == (x.length - 1)) {
document.getElementById("nextBtn").innerHTML = "Submit";
} else {
document.getElementById("nextBtn").innerHTML = "Next";
}
//... and run a function that will display the correct step indicator:
fixStepIndicator(n)
}
function nextPrev(n) {
// This function will figure out which tab to display
var x = document.getElementsByClassName("tab");
// Exit the function if any field in the current tab is invalid:
// JDE ** Need to validate for this to work? ** if (n == 1 && !validateForm()) return false;
// Hide the current tab:
x[currentTab].style.display = "none";
// Increase or decrease the current tab by 1:
currentTab = currentTab + n;
// if you have reached the end of the form...
if (currentTab >= x.length) {
// ... the form gets submitted:
document.getElementById("squadForm").submit();
return false;
}
// Otherwise, display the correct tab:
showTab(currentTab);
}
function validateForm() {
// This function deals with validation of the form fields
var x, y, i, valid = true;
x = document.getElementsByClassName("tab");
y = x[currentTab].getElementsByTagName("input");
// A loop that checks every input field in the current tab:
for (i = 0; i < y.length; i++) {
// If a field is empty...
if (y[i].value == "") {
// add an "invalid" class to the field:
y[i].className += " invalid";
// and set the current valid status to false
valid = false;
}
}
// If the valid status is true, mark the step as finished and valid:
if (valid) {
document.getElementsByClassName("step")[currentTab].className += " finish";
}
return valid; // return the valid status
}
function fixStepIndicator(n) {
// This function removes the "active" class of all steps...
var i, x = document.getElementsByClassName("step");
for (i = 0; i < x.length; i++) {
x[i].className = x[i].className.replace(" active", "");
}
//... and adds the "active" class on the current step:
x[n].className += " active";
}
</script>
<div class="container mt-4">
<div class="row">
<div class="col-md-12">
<h1>Match Squad Selection</h1>
<p class="lead">Select players for the match squad from the available players</p>
<div class="mb-3">
<a href="/admin/players" class="btn btn-outline-primary">Manage Players</a>
<a href="/admin" class="btn btn-secondary">Back to Admin</a>
</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' }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
{% if players %}
<form action="/admin/squad/submit" method="post">
<div class="card">
<div class="card-header">
<h5>Available Players</h5>
<small class="text-muted">Select players to add to the match squad</small>
</div>
<div class="card-body">
<div class="row">
{% for player in players %}
<div class="col-md-4 mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="playerNumber" value="{{ player.playerNumber }}" id="player{{ player.playerNumber }}">
<label class="form-check-label" for="player{{ player.playerNumber }}">
<strong>#{{ player.playerNumber }}</strong> {{ player.playerForenames }} {{ player.playerSurname }}
<br>
<small class="text-muted">{{ player.playerNickname }} - {{ player.playerTeam }}</small>
</label>
</div>
</div>
{% endfor %}
</div>
</div>
<div class="card-footer">
<button type="submit" class="btn btn-primary">Add Selected Players to Squad</button>
<a href="/admin/squad/list" class="btn btn-outline-secondary">View Current Squad</a>
</div>
</div>
</form>
{% else %}
<div class="alert alert-warning">
<h5>No players available</h5>
<p>There are no players in the database. <a href="/admin/players/add">Add some players</a> before selecting a squad.</p>
</div>
{% endif %}
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -1,6 +1,6 @@
<html>
<head>
<title>HKFC Men's D Team - Match Squad Selected</title>
<title>HKFC Men's C Team - Match Squad Selected</title>
<link rel="stylesheet" media="screen" href ="/static/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/css/bootstrap-theme.min.css">
<meta name="viewport" content = "width=device-width, initial-scale=1.0">

View File

@ -1,13 +1,13 @@
<html>
<head>
<title>HKFC Men's D Team - MotM and DotD vote admin</title>
<title>HKFC Men's C Team - MotM and DotD vote admin</title>
<link rel="stylesheet" media="screen" href ="/static/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/css/bootstrap-theme.min.css">
<meta name="viewport" content = "width=device-width, initial-scale=1.0">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
</head>
<h2>HKFC Men's D Team MotM and DotD online vote admin page</h2>
<h2>HKFC Men's C Team MotM and DotD online vote admin page</h2>
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
@ -23,7 +23,7 @@
<dl>
<p>
{{ form.csrf_token }}
<b>HKFC D Next Opponent:</b>
<b>HKFC C Next Opponent:</b>
<br/>
<div class="row">
<div class="col-xs-12">
@ -32,7 +32,7 @@
<div class = "col-sm-6">
<div class="input-group">
<span class="input-group-addon" id="basic-addon1">Date:</span>
{{ form.nextMatch(class_="form-control") }}
{{ form.nextMatchDate(class_="form-control") }}
</div>
</div>
</div>
@ -74,32 +74,8 @@
</dl>
<script>
var match_select = document.getElementById("nextMatch");
var club_logo = document.getElementById("nextOppoLogo");
var team_select = document.getElementById("nextOppoTeam");
match_select.onchange = function() {myFunction()};
function myFunction() {
match = match_select.value;
fetch('/admin/api/fixture/' + match).then(function(response) {
response.json().then(function(team) {
var optionHTML = '';
optionHTML += team;
team_select.value = optionHTML;
})
});
fetch('/admin/api/fixture/' + match + '/logo').then(function(response) {
response.json().then(function(logo) {
var HTML = '';
HTML += logo;
club_logo.src = HTML;
})
});
}
// Date input functionality - no longer needed to fetch fixture data
// The date input will be handled by the browser's native date picker
</script>
</body>
</html>

View File

@ -1,13 +1,13 @@
<html>
<head>
<title>HKFC Men's D Team - MotM and DotD online vote</title>
<title>HKFC Men's C Team - MotM and DotD online vote</title>
<link rel="stylesheet" media="screen" href ="/static/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/css/bootstrap-theme.min.css">
<meta name="viewport" content = "width=device-width, initial-scale=1.0">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
</head>
<h3>HKFC Men's D Team MotM and DotD online vote</h3>
<h3>HKFC Men's C Team MotM and DotD online vote</h3>
<h5>{{ formatDate }}</h5>
<h4><img src="{{ hkfcLogo }}" height="150"></img> <b> </b> <img src="{{ oppoLogo }}" height="140"></img></h4>
<body>

View File

@ -0,0 +1,87 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Player Management - HKFC Men's C Team</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<div class="row">
<div class="col-md-12">
<h1>Player Management</h1>
<p class="lead">Manage players in the HKFC Men's C Team database</p>
<div class="mb-3">
<a href="/admin/players/add" class="btn btn-primary">Add New Player</a>
<a href="/admin" class="btn btn-secondary">Back to Admin</a>
</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' }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
<div class="card">
<div class="card-header">
<h5>All Players</h5>
</div>
<div class="card-body">
{% if players %}
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead class="table-dark">
<tr>
<th>Number</th>
<th>First Names</th>
<th>Surname</th>
<th>Nickname</th>
<th>Team</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for player in players %}
<tr>
<td>{{ player.playerNumber }}</td>
<td>{{ player.playerForenames }}</td>
<td>{{ player.playerSurname }}</td>
<td>{{ player.playerNickname }}</td>
<td>
<span class="badge bg-{{ 'primary' if player.playerTeam == 'HKFC C' else 'secondary' }}">
{{ player.playerTeam }}
</span>
</td>
<td>
<a href="/admin/players/edit/{{ player.playerNumber }}" class="btn btn-sm btn-outline-primary">Edit</a>
<form method="POST" action="/admin/players/delete/{{ player.playerNumber }}" style="display: inline;" onsubmit="return confirm('Are you sure you want to delete this player?')">
<button type="submit" class="btn btn-sm btn-outline-danger">Delete</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="alert alert-info">
<h5>No players found</h5>
<p>There are no players in the database. <a href="/admin/players/add">Add the first player</a> to get started.</p>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Team Management - HKFC Men's C Team</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<div class="row">
<div class="col-md-12">
<h1>Team Management</h1>
<p class="lead">Manage hockey teams in the database</p>
<div class="mb-3">
<a href="/admin/teams/add" class="btn btn-primary">Add New Team</a>
<a href="/admin" class="btn btn-secondary">Back to Admin</a>
</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' }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
<div class="card">
<div class="card-header">
<h5>All Teams</h5>
</div>
<div class="card-body">
{% if teams %}
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Club</th>
<th>Team</th>
<th>Display Name</th>
<th>League</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for team in teams %}
<tr>
<td>{{ team.id }}</td>
<td>{{ team.club }}</td>
<td>{{ team.team }}</td>
<td>{{ team.display_name }}</td>
<td>{{ team.league }}</td>
<td>
<a href="/admin/teams/edit/{{ team.id }}" class="btn btn-sm btn-outline-primary">Edit</a>
<form method="POST" action="/admin/teams/delete/{{ team.id }}" style="display: inline;" onsubmit="return confirm('Are you sure you want to delete this team?')">
<button type="submit" class="btn btn-sm btn-outline-danger">Delete</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="alert alert-info">
<h5>No teams found</h5>
<p>There are no teams in the database. <a href="/admin/teams/add">Add the first team</a> to get started.</p>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -1,6 +1,6 @@
<html>
<head>
<title>HKFC Men's D Team - MotM and DotD vote</title>
<title>HKFC Men's C Team - MotM and DotD vote</title>
<link rel="stylesheet" media="screen" href ="/static/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/css/bootstrap-theme.min.css">
<meta name="viewport" content = "width=device-width, initial-scale=1.0">