from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify from flask_login import login_required, current_user from extensions import db, hash_password from models import User, ROLES, ESPORT_GAMES, PlayerDisponibility from datetime import datetime, timedelta users_bp = Blueprint('users', __name__, url_prefix='/users') @users_bp.route('') @login_required def list_users(): if current_user.role != 'president': flash('Only the president can manage users.', 'danger') return redirect(url_for('main.dashboard')) users = User.query.order_by(User.role, User.full_name).all() return render_template('pages/users.html', users=users, roles=ROLES) @users_bp.route('//edit', methods=['GET', 'POST']) @login_required def edit_user(user_id): if current_user.role != 'president': flash('Only the president can edit users.', 'danger') return redirect(url_for('main.dashboard')) user = User.query.get_or_404(user_id) if request.method == 'POST': full_name = request.form.get('full_name') email = request.form.get('email') phone = request.form.get('phone') role = request.form.get('role') is_active = request.form.get('is_active_account') == 'on' if role not in ROLES: flash('Invalid role selected.', 'danger') return render_template('pages/edit_user.html', user=user, roles=ROLES, esport_games=ESPORT_GAMES) selected_games = request.form.getlist('games') trn_username = request.form.get('trn_username', '').strip() discord_username = request.form.get('discord_username', '').strip() league_os_profile = request.form.get('league_os_profile', '').strip() user.full_name = full_name user.email = email user.phone = phone user.role = role user.is_active_account = is_active user.games = ','.join(selected_games) if selected_games else None user.trn_username = trn_username or None user.discord_username = discord_username or None user.league_os_profile = league_os_profile or None password = request.form.get('password') if password: user.password_hash = hash_password(password) db.session.commit() flash(f'User {user.full_name} updated successfully!', 'success') return redirect(url_for('users.list_users')) return render_template('pages/edit_user.html', user=user, roles=ROLES, esport_games=ESPORT_GAMES) @users_bp.route('//delete', methods=['POST']) @login_required def delete_user(user_id): if current_user.role != 'president': flash('Only the president can delete users.', 'danger') return redirect(url_for('main.dashboard')) if current_user.id == user_id: flash('You cannot delete your own account.', 'danger') return redirect(url_for('users.list_users')) user = User.query.get_or_404(user_id) db.session.delete(user) db.session.commit() flash(f'User {user.full_name} has been removed.', 'success') return redirect(url_for('users.list_users')) @users_bp.route('/create', methods=['GET', 'POST']) @login_required def create_user(): if current_user.role != 'president': flash('Only the president can create users.', 'danger') return redirect(url_for('main.dashboard')) if request.method == 'POST': username = request.form.get('username') email = request.form.get('email') password = request.form.get('password') full_name = request.form.get('full_name') phone = request.form.get('phone') role = request.form.get('role') if role not in ROLES: flash('Invalid role selected.', 'danger') return render_template('pages/create_user.html', roles=ROLES) if User.query.filter_by(username=username).first(): flash('Username already exists.', 'danger') return render_template('pages/create_user.html', roles=ROLES) if User.query.filter_by(email=email).first(): flash('Email already registered.', 'danger') return render_template('pages/create_user.html', roles=ROLES) hashed_password = hash_password(password) user = User( username=username, password_hash=hashed_password, role=role, full_name=full_name, email=email, phone=phone ) db.session.add(user) db.session.commit() flash(f'User {full_name} created as {role}!', 'success') return redirect(url_for('users.list_users')) return render_template('pages/create_user.html', roles=ROLES) @users_bp.route('/profile') @login_required def profile(): return render_template('pages/profile.html', user=current_user) @users_bp.route('/profile/edit', methods=['GET', 'POST']) @login_required def edit_profile(): if request.method == 'POST': full_name = request.form.get('full_name') email = request.form.get('email') phone = request.form.get('phone') selected_games = request.form.getlist('games') trn_username = request.form.get('trn_username', '').strip() discord_username = request.form.get('discord_username', '').strip() league_os_profile = request.form.get('league_os_profile', '').strip() if email != current_user.email and User.query.filter_by(email=email).first(): flash('Email already in use.', 'danger') return render_template('pages/edit_profile.html', user=current_user, esport_games=ESPORT_GAMES) current_user.full_name = full_name current_user.email = email current_user.phone = phone current_user.games = ','.join(selected_games) if selected_games else None current_user.trn_username = trn_username or None current_user.discord_username = discord_username or None current_user.league_os_profile = league_os_profile or None password = request.form.get('password') if password: current_user.password_hash = hash_password(password) db.session.commit() flash('Profile updated successfully!', 'success') return redirect(url_for('users.profile')) return render_template('pages/edit_profile.html', user=current_user, esport_games=ESPORT_GAMES) # Day names for disponibility DAY_NAMES = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] def add_30_minutes(t): """Add 30 minutes to a time object.""" return (datetime.combine(datetime.today(), t) + timedelta(minutes=30)).time() @users_bp.route('/disponibilities') @login_required def get_disponibilities(): """API endpoint to get all player disponibilities for scheduling.""" # Only managers and above can view disponibilities for scheduling if not current_user.can_manage_teams() and not current_user.can_schedule_matches(): return jsonify({'error': 'Unauthorized'}), 403 players = User.query.filter_by(role='player', is_active_account=True).order_by(User.full_name).all() result = {} for player in players: disponibilities = list(player.disponibilities) result[player.id] = { 'full_name': player.full_name, 'disponibilities': [ { 'id': d.id, 'day_of_week': d.day_of_week, 'day_name': DAY_NAMES[d.day_of_week], 'start_time': d.start_time.strftime('%H:%M'), 'end_time': d.end_time.strftime('%H:%M') } for d in disponibilities ] } return jsonify(result) @users_bp.route('/disponibilities/my') @login_required def get_my_disponibilities(): """API endpoint for players to get their own disponibilities.""" disponibilities = PlayerDisponibility.query.filter_by(player_id=current_user.id).all() # Group by day for easier display result = {} for d in disponibilities: day = d.day_of_week if day not in result: result[day] = [] result[day].append({ 'id': d.id, 'day_of_week': d.day_of_week, 'day_name': DAY_NAMES[d.day_of_week], 'start_time': d.start_time.strftime('%H:%M'), 'end_time': d.end_time.strftime('%H:%M') }) return jsonify(result) @users_bp.route('/disponibilities/add', methods=['POST']) @login_required def add_disponibility(): """Add a disponibility block for the current player.""" day_of_week = request.form.get('day_of_week', type=int) start_time_str = request.form.get('start_time') if day_of_week is None or day_of_week < 0 or day_of_week > 6: return jsonify({'error': 'Invalid day of week'}), 400 try: start_time = datetime.strptime(start_time_str, '%H:%M').time() except (ValueError, TypeError): return jsonify({'error': 'Invalid time format'}), 400 end_time = add_30_minutes(start_time) disponibility = PlayerDisponibility( player_id=current_user.id, day_of_week=day_of_week, start_time=start_time, end_time=end_time ) db.session.add(disponibility) db.session.commit() return jsonify({ 'id': disponibility.id, 'day_of_week': disponibility.day_of_week, 'day_name': DAY_NAMES[disponibility.day_of_week], 'start_time': disponibility.start_time.strftime('%H:%M'), 'end_time': disponibility.end_time.strftime('%H:%M') }) @users_bp.route('/disponibilities/add_bulk', methods=['POST']) @login_required def add_disponibilities_bulk(): """Add multiple disponibility blocks at once (for grid selection).""" data = request.get_json() slots = data.get('slots', []) # List of {day_of_week, start_time} created = [] for slot in slots: day_of_week = slot.get('day_of_week') start_time_str = slot.get('start_time') if day_of_week is None or day_of_week < 0 or day_of_week > 6: continue try: start_time = datetime.strptime(start_time_str, '%H:%M').time() except (ValueError, TypeError): continue end_time = add_30_minutes(start_time) # Check if this slot already exists for this player existing = PlayerDisponibility.query.filter_by( player_id=current_user.id, day_of_week=day_of_week, start_time=start_time ).first() if not existing: disponibility = PlayerDisponibility( player_id=current_user.id, day_of_week=day_of_week, start_time=start_time, end_time=end_time ) db.session.add(disponibility) db.session.flush() created.append({ 'id': disponibility.id, 'day_of_week': disponibility.day_of_week, 'day_name': DAY_NAMES[disponibility.day_of_week], 'start_time': disponibility.start_time.strftime('%H:%M'), 'end_time': disponibility.start_time.strftime('%H:%M') }) db.session.commit() return jsonify({'success': True, 'created': created}) @users_bp.route('/disponibilities/clear', methods=['POST']) @login_required def clear_disponibilities(): """Clear all disponibilities for the current player (for resetting).""" PlayerDisponibility.query.filter_by(player_id=current_user.id).delete() db.session.commit() return jsonify({'success': True}) @users_bp.route('/disponibilities//delete', methods=['POST']) @login_required def delete_disponibility(disponibility_id): """Delete a disponibility block.""" disponibility = PlayerDisponibility.query.get_or_404(disponibility_id) # Only the owner can delete their disponibility if disponibility.player_id != current_user.id: return jsonify({'error': 'Unauthorized'}), 403 db.session.delete(disponibility) db.session.commit() return jsonify({'success': True})