"""Organization team management routes. Uses polymorphic isinstance checks instead of role-string comparisons. """ from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify from flask_login import login_required, current_user from app.extensions import db from app.models.models import ( Admin, Manager, Coach, Player, OrgTeam, User, Team, TeamMember, PersonalNote, TeamNote, Tryout, TeamPlayer, ) from datetime import datetime teams_bp = Blueprint('teams', __name__, url_prefix='/teams') @teams_bp.route('') @login_required def list_teams(): """List all organization teams visible to the current user.""" can_manage = current_user.can_manage_teams() if isinstance(current_user, Admin): teams = OrgTeam.query.order_by(OrgTeam.name).all() elif isinstance(current_user, Coach): teams = OrgTeam.query.filter( db.or_( OrgTeam.coaches.any(id=current_user.id), OrgTeam.coach_id == current_user.id, ) ).order_by(OrgTeam.name).all() elif isinstance(current_user, Manager): teams = OrgTeam.query.filter( db.or_( OrgTeam.managers.any(id=current_user.id), OrgTeam.manager_id == current_user.id, ) ).order_by(OrgTeam.name).all() elif isinstance(current_user, Player): flash('Use My Team(s) to view your teams.', 'info') return redirect(url_for('teams.my_teams')) else: flash('You do not have permission to view teams.', 'danger') return redirect(url_for('main.dashboard')) coaches = User.query.filter_by(role='coach', is_active_account=True).order_by(User.username).all() managers = User.query.filter_by(role='manager', is_active_account=True).order_by(User.username).all() all_players = User.query.filter_by(role='player').order_by(User.username).all() return render_template('pages/teams.html', teams=teams, coaches=coaches, managers=managers, all_players=all_players, can_manage=can_manage) @teams_bp.route('/my-teams') @login_required def my_teams(): """View the player's own teams with upcoming matches.""" if not isinstance(current_user, Player): flash('This page is for players.', 'info') return redirect(url_for('teams.list_teams')) from app.models.models import TeamMatch, TeamMatchParticipant player_teams = current_user.get_org_teams() now = datetime.utcnow() team_data = [] for org_team in player_teams: matches = TeamMatch.query.filter( TeamMatch.org_team_id == org_team.id, TeamMatch.status == 'scheduled', ).order_by(TeamMatch.date.asc(), TeamMatch.start_time.asc()).all() matches_data = [] for tm in matches: confirmed, total = tm.get_confirmed_count() participant = TeamMatchParticipant.query.filter_by( team_match_id=tm.id, player_id=current_user.id, ).first() matches_data.append({ 'match': tm, 'participant_id': participant.id if participant else None, 'is_confirmed': participant.is_confirmed if participant else False, 'confirmed_count': confirmed, 'total_count': total, }) team_data.append({ 'team': org_team, 'matches': matches_data, 'coaches': org_team.get_coaches(), 'managers': org_team.get_managers(), }) return render_template('pages/my_teams.html', team_data=team_data, now=now) @teams_bp.route('/create', methods=['POST']) @login_required def create_team(): """Create a new organization team.""" if not current_user.can_manage_teams(): flash('You do not have permission to create teams.', 'danger') return redirect(url_for('teams.list_teams')) name = request.form.get('name') coach_id = request.form.get('coach_id') manager_id = request.form.get('manager_id') if not name: flash('Team name is required.', 'danger') return redirect(url_for('teams.list_teams')) existing = OrgTeam.query.filter_by(name=name).first() if existing: flash(f'Team "{name}" already exists.', 'danger') return redirect(url_for('teams.list_teams')) team = OrgTeam( name=name, coach_id=int(coach_id) if coach_id else None, manager_id=int(manager_id) if manager_id else None, created_by=current_user.id, ) db.session.add(team) db.session.flush() if coach_id: coach_user = User.query.get(int(coach_id)) if coach_user: team.coaches.append(coach_user) if manager_id: manager_user = User.query.get(int(manager_id)) if manager_user: team.managers.append(manager_user) db.session.commit() flash(f'Team "{name}" created successfully!', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//edit', methods=['POST']) @login_required def edit_team(team_id): """Edit an existing organization team.""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): flash('You do not have permission to edit this team.', 'danger') return redirect(url_for('teams.list_teams')) name = request.form.get('name') coach_id = request.form.get('coach_id') manager_id = request.form.get('manager_id') if not name: flash('Team name is required.', 'danger') return redirect(url_for('teams.list_teams')) existing = OrgTeam.query.filter(OrgTeam.name == name, OrgTeam.id != team_id).first() if existing: flash(f'Team "{name}" already exists.', 'danger') return redirect(url_for('teams.list_teams')) if request.form.get('sync_staff') == '1': coach_ids = request.form.getlist('coach_ids') manager_ids = request.form.getlist('manager_ids') team.coaches = [] for cid in coach_ids: if cid and cid.strip(): coach_user = User.query.get(int(cid)) if coach_user and isinstance(coach_user, Coach): team.coaches.append(coach_user) coach_list = team.coaches.all() team.coach_id = coach_list[0].id if coach_list else None team.managers = [] for mid in manager_ids: if mid and mid.strip(): manager_user = User.query.get(int(mid)) if manager_user and isinstance(manager_user, Manager): team.managers.append(manager_user) manager_list = team.managers.all() team.manager_id = manager_list[0].id if manager_list else None else: team.coach_id = int(coach_id) if coach_id else None team.manager_id = int(manager_id) if manager_id else None if coach_id: coach_user = User.query.get(int(coach_id)) if coach_user and not team.coaches.filter_by(id=coach_user.id).first(): team.coaches.append(coach_user) if manager_id: manager_user = User.query.get(int(manager_id)) if manager_user and not team.managers.filter_by(id=manager_user.id).first(): team.managers.append(manager_user) db.session.commit() flash(f'Team "{name}" updated successfully!', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//delete', methods=['POST']) @login_required def delete_team(team_id): """Delete an organization team.""" if not current_user.can_manage_teams(): flash('You do not have permission to delete teams.', 'danger') return redirect(url_for('teams.list_teams')) team = OrgTeam.query.get_or_404(team_id) name = team.name tryouts = Tryout.query.filter_by(target_org_team_id=team_id).all() for t in tryouts: t.target_org_team_id = None db.session.commit() TeamPlayer.query.filter_by(org_team_id=team_id).delete() db.session.commit() db.session.delete(team) db.session.commit() flash(f'Team "{name}" deleted successfully.', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//add_coach', methods=['POST']) @login_required def add_coach(team_id): """Add a coach to an organization team.""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): flash('Permission denied.', 'danger') return redirect(url_for('teams.list_teams')) coach_id = request.form.get('coach_id') if not coach_id: flash('Please select a coach.', 'danger') return redirect(url_for('teams.list_teams')) coach = User.query.get_or_404(int(coach_id)) if not isinstance(coach, Coach): flash('Only coaches can be assigned as coach.', 'danger') return redirect(url_for('teams.list_teams')) if team.coaches.filter_by(id=coach.id).first(): flash(f'{coach.username} is already a coach of {team.name}.', 'info') return redirect(url_for('teams.list_teams')) team.coaches.append(coach) if not team.coach_id: team.coach_id = coach.id db.session.commit() flash(f'{coach.username} added as coach of {team.name}.', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//add_manager', methods=['POST']) @login_required def add_manager(team_id): """Add a manager to an organization team.""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): flash('Permission denied.', 'danger') return redirect(url_for('teams.list_teams')) manager_id = request.form.get('manager_id') if not manager_id: flash('Please select a manager.', 'danger') return redirect(url_for('teams.list_teams')) manager = User.query.get_or_404(int(manager_id)) if not isinstance(manager, Manager): flash('Only managers can be assigned as manager.', 'danger') return redirect(url_for('teams.list_teams')) if team.managers.filter_by(id=manager.id).first(): flash(f'{manager.username} is already a manager of {team.name}.', 'info') return redirect(url_for('teams.list_teams')) team.managers.append(manager) if not team.manager_id: team.manager_id = manager.id db.session.commit() flash(f'{manager.username} added as manager of {team.name}.', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//remove_coach', methods=['POST']) @login_required def remove_coach(team_id): """Remove a coach from an organization team.""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): flash('Permission denied.', 'danger') return redirect(url_for('teams.list_teams')) coach_id = request.form.get('coach_id') if coach_id: coach = User.query.get(int(coach_id)) if coach and team.coaches.filter_by(id=coach.id).first(): team.coaches.remove(coach) if team.coach_id == coach.id: team.coach_id = None else: team.coaches = [] team.coach_id = None db.session.commit() flash(f'Coach removed from {team.name}.', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//remove_manager', methods=['POST']) @login_required def remove_manager(team_id): """Remove a manager from an organization team.""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): flash('Permission denied.', 'danger') return redirect(url_for('teams.list_teams')) manager_id = request.form.get('manager_id') if manager_id: manager = User.query.get(int(manager_id)) if manager and team.managers.filter_by(id=manager.id).first(): team.managers.remove(manager) if team.manager_id == manager.id: team.manager_id = None else: team.managers = [] team.manager_id = None db.session.commit() flash(f'Manager removed from {team.name}.', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//add_player', methods=['POST']) @login_required def add_player(team_id): """Add a player to an organization team.""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): flash('Permission denied.', 'danger') return redirect(url_for('teams.list_teams')) player_id = request.form.get('player_id') status = request.form.get('status', 'starter') if not player_id: flash('Please select a player.', 'danger') return redirect(url_for('teams.list_teams')) player = User.query.get_or_404(int(player_id)) if not isinstance(player, Player): flash('Can only assign players to teams.', 'danger') return redirect(url_for('teams.list_teams')) existing = TeamPlayer.query.filter_by(player_id=player.id, org_team_id=team.id).first() if existing: flash(f'{player.username} is already on {team.name}.', 'info') return redirect(url_for('teams.list_teams')) tp = TeamPlayer(player_id=player.id, org_team_id=team.id, status=status) db.session.add(tp) db.session.commit() flash(f'{player.username} added to {team.name}!', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//remove_player/', methods=['POST']) @login_required def remove_player(team_id, player_id): """Remove a player from an organization team.""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): flash('Permission denied.', 'danger') return redirect(url_for('teams.list_teams')) player = User.query.get_or_404(player_id) tp = TeamPlayer.query.filter_by(player_id=player_id, org_team_id=team_id).first() if not tp: flash(f'{player.username} is not on {team.name}.', 'danger') return redirect(url_for('teams.list_teams')) db.session.delete(tp) db.session.commit() flash(f'{player.username} removed from {team.name}.', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//toggle_status/', methods=['POST']) @login_required def toggle_player_status(team_id, player_id): """Toggle a player's status between starter and substitute.""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): return jsonify({'error': 'Permission denied'}), 403 tp = TeamPlayer.query.filter_by(player_id=player_id, org_team_id=team_id).first() if not tp: return jsonify({'error': 'Player not found on this team'}), 404 tp.status = 'substitute' if tp.status == 'starter' else 'starter' db.session.commit() return jsonify({ 'success': True, 'player_id': player_id, 'new_status': tp.status, 'player_name': tp.player.username, }) @teams_bp.route('//add-team-note', methods=['POST']) @login_required def add_team_note(team_id): """Add a team improvement note (coaches only).""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): flash('You do not have permission to add notes to this team.', 'danger') return redirect(url_for('teams.list_teams')) content = request.form.get('content', '').strip() if content: note = TeamNote(org_team_id=team_id, coach_id=current_user.id, content=content) db.session.add(note) db.session.commit() flash('Team notes added successfully!', 'success') return redirect(url_for('teams.list_teams')) @teams_bp.route('//add-player-note/', methods=['POST']) @login_required def add_player_note(team_id, player_id): """Add a personal note for a player (coaches only).""" team = OrgTeam.query.get_or_404(team_id) if not current_user.can_manage_this_org_team(team): flash('You do not have permission to add notes to this team.', 'danger') return redirect(url_for('teams.list_teams')) player = User.query.get_or_404(player_id) if not isinstance(player, Player): flash('Can only add notes for players.', 'danger') return redirect(url_for('teams.list_teams')) tp = TeamPlayer.query.filter_by(player_id=player_id, org_team_id=team_id).first() if not tp: flash(f'{player.username} is not on {team.name}.', 'danger') return redirect(url_for('teams.list_teams')) content = request.form.get('content', '').strip() if content: note = PersonalNote(player_id=player_id, coach_id=current_user.id, content=content) db.session.add(note) db.session.commit() flash(f'Note added for {player.username}!', 'success') return redirect(url_for('teams.list_teams'))