From abb3f47a5711377a6754d1e257190f2076872367 Mon Sep 17 00:00:00 2001 From: Greg Date: Sun, 8 Mar 2026 12:55:41 +0100 Subject: [PATCH] feat: add admin page for date and player management - Add active flag to Player model with idempotent ALTER TABLE migration - Filter inactive players from main attendance table and API - New /admin page with date generation (preview + bulk add) and player management (add/deactivate/reactivate) - New API endpoints: GET/POST /api/players, PATCH /api/players/ - Add Admin nav link on main page Co-Authored-By: Claude Sonnet 4.6 --- app.py | 42 +++- templates/admin.html | 456 +++++++++++++++++++++++++++++++++++++++++++ templates/index.html | 1 + 3 files changed, 498 insertions(+), 1 deletion(-) create mode 100644 templates/admin.html diff --git a/app.py b/app.py index c67f79d..851b2c7 100644 --- a/app.py +++ b/app.py @@ -50,6 +50,7 @@ db = SQLAlchemy(app) class Player(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(50), nullable=False, unique=True) + active = db.Column(db.Boolean, nullable=False, default=True) class Date(db.Model): id = db.Column(db.Integer, primary_key=True) @@ -75,6 +76,12 @@ class GuestName(db.Model): def get_initial_data(): # Ensure tables exist db.create_all() + # Safe migration: add 'active' column if it doesn't exist yet + with db.engine.connect() as conn: + conn.execute(db.text( + "ALTER TABLE player ADD COLUMN IF NOT EXISTS active BOOLEAN NOT NULL DEFAULT TRUE" + )) + conn.commit() # If no players, insert defaults if Player.query.count() == 0: for name in ["Alice", "Bob", "Charlie", "David", "Eve", "Frank", "Grace", "Hannah"]: @@ -88,7 +95,7 @@ def parse_date(date_str): return datetime.strptime(date_str, '%d/%m/%y') def db_to_json(): - players = [p.name for p in Player.query.order_by(Player.id)] + players = [p.name for p in Player.query.filter_by(active=True).order_by(Player.id)] guest = "Guest" dates = sorted([d.date_str for d in Date.query.all()], key=parse_date) attendance = {} @@ -231,6 +238,39 @@ def export_data(): headers={'Content-Disposition': 'attachment;filename=attendance_data.json'} ) +@app.route('/admin') +def admin(): + return render_template('admin.html') + +@app.route('/api/players', methods=['GET']) +def get_players(): + players = Player.query.order_by(Player.id).all() + return jsonify([{'id': p.id, 'name': p.name, 'active': p.active} for p in players]) + +@app.route('/api/players', methods=['POST']) +@csrf.exempt +def add_player(): + data = request.json + name = (data.get('name') or '').strip() + if not re.fullmatch(r'[a-zA-Z0-9 .\-]{1,50}', name): + return jsonify({'status': 'error', 'message': 'Invalid name'}), 400 + if Player.query.filter_by(name=name).first(): + return jsonify({'status': 'error', 'message': 'Player already exists'}), 400 + db.session.add(Player(name=name, active=True)) + db.session.commit() + return jsonify({'status': 'success'}) + +@app.route('/api/players/', methods=['PATCH']) +@csrf.exempt +def update_player(player_id): + player = Player.query.get_or_404(player_id) + data = request.json + if 'active' not in data: + return jsonify({'status': 'error', 'message': 'Missing active field'}), 400 + player.active = bool(data['active']) + db.session.commit() + return jsonify({'status': 'success'}) + if __name__ == '__main__': import os port = int(os.environ.get('PORT', 5000)) diff --git a/templates/admin.html b/templates/admin.html new file mode 100644 index 0000000..5dea50c --- /dev/null +++ b/templates/admin.html @@ -0,0 +1,456 @@ + + + + + + + + Admin — Padel Nivelles + + + +
+

Padel Nivelles — Admin

+
+ + + +
+

Date Management

+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+
+ +
+
+
+ + +
+

Player Management

+
+ ⚠ Player changes should only be made at season start (April). Mid-season changes may cause inconsistencies in historical data display. +
+ +
+ Active Players +
    +
    + +
    + Add New Player +
    + + +
    +
    +
    + + +
    + + + + diff --git a/templates/index.html b/templates/index.html index 217c749..7396034 100644 --- a/templates/index.html +++ b/templates/index.html @@ -119,6 +119,7 @@
    Reports + Admin