diff --git a/.gitgnore b/.gitgnore new file mode 100644 index 0000000..4f509e5 --- /dev/null +++ b/.gitgnore @@ -0,0 +1 @@ +*.env \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e143bd6 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +### Plateforme centralisée de tryouts \ No newline at end of file diff --git a/__pycache__/models.cpython-313.pyc b/__pycache__/models.cpython-313.pyc index 2a51998..8822d0c 100644 Binary files a/__pycache__/models.cpython-313.pyc and b/__pycache__/models.cpython-313.pyc differ diff --git a/instance/team_tryouts.db b/instance/team_tryouts.db index 926066b..9723cc8 100644 Binary files a/instance/team_tryouts.db and b/instance/team_tryouts.db differ diff --git a/models.py b/models.py index 93228a7..1021da4 100644 --- a/models.py +++ b/models.py @@ -223,6 +223,8 @@ class MatchParticipant(db.Model): id = db.Column(db.Integer, primary_key=True) match_id = db.Column(db.Integer, db.ForeignKey('matches.id'), nullable=False) player_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) + team_side = db.Column(db.Integer, nullable=True) # 1 for team 1, 2 for team 2 (for player_vs_player matches) + position = db.Column(db.String(50), nullable=True) # Position for this match added_at = db.Column(db.DateTime, default=datetime.utcnow) player = db.relationship('User') diff --git a/routes/__pycache__/matches.cpython-313.pyc b/routes/__pycache__/matches.cpython-313.pyc index 83accc4..1a5d5a1 100644 Binary files a/routes/__pycache__/matches.cpython-313.pyc and b/routes/__pycache__/matches.cpython-313.pyc differ diff --git a/routes/__pycache__/tryouts.cpython-313.pyc b/routes/__pycache__/tryouts.cpython-313.pyc index ecbf1a6..b059970 100644 Binary files a/routes/__pycache__/tryouts.cpython-313.pyc and b/routes/__pycache__/tryouts.cpython-313.pyc differ diff --git a/routes/matches.py b/routes/matches.py index 480f9d7..e66ce79 100644 --- a/routes/matches.py +++ b/routes/matches.py @@ -1,7 +1,7 @@ from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify from flask_login import login_required, current_user from extensions import db -from models import User, Tryout, Match, MatchParticipant, Team, TeamMember, OrgTeam +from models import User, Tryout, Match, MatchParticipant, Team, TeamMember, OrgTeam, TryoutRegistration from datetime import datetime, time matches_bp = Blueprint('matches', __name__, url_prefix='/matches') @@ -97,30 +97,52 @@ def api_events_for_tryout(tryout_id): tryout = Tryout.query.get_or_404(tryout_id) # Check if user can view this tryout - if not current_user.can_manage_this_tryout(tryout): - # For players, check if they're registered or participating in the match - if current_user.role == 'player': - # Check if player is registered for this tryout - is_registered = TryoutRegistration.query.filter_by( - tryout_id=tryout_id, player_id=current_user.id - ).first() is not None - - # Check if player is participating in any matches for this tryout - match_participation = MatchParticipant.query.filter( - MatchParticipant.match_id == MatchParticipant.match_id - ).join(Match).filter(Match.tryout_id == tryout_id).all() - - player_in_match = any(mp.player_id == current_user.id for mp in match_participation) - - if not is_registered and not player_in_match: - return jsonify([]) + can_view = current_user.can_manage_this_tryout(tryout) + + # For players, check if they're registered or participating in a match + is_registered = False + player_in_match = False + if current_user.role == 'player': + is_registered = TryoutRegistration.query.filter_by( + tryout_id=tryout_id, player_id=current_user.id + ).first() is not None + + # Check if player is participating in any matches for this tryout + player_matches = Match.query.join(MatchParticipant).filter( + MatchParticipant.player_id == current_user.id, + Match.tryout_id == tryout_id + ).all() + player_in_match = len(player_matches) > 0 + + # Non-participating players cannot see the calendar + if not can_view and not is_registered and not player_in_match: + return jsonify([]) events = [] + # Add tryout date as an event (read-only, for context) + events.append({ + 'id': f'tryout_{tryout.id}', + 'title': f'Tryout: {tryout.title}', + 'date': tryout.date.strftime('%Y-%m-%d'), + 'type': 'tryout', + 'color': '#3b82f6', # Blue for tryouts + 'extendedProps': { + 'location': tryout.location or 'TBD', + 'status': tryout.status, + 'description': tryout.description or '', + 'tryout_id': tryout.id + } + }) + for match in tryout.matches: - match_color = '#10b981' if match.match_type == 'team_vs_team' else '#f59e0b' + # Determine color based on match type + if match.match_type == 'team_vs_team' or match.match_type == 'player_vs_player': + match_color = '#10b981' # Green for team matches + else: + match_color = '#f59e0b' # Orange for scrims - # Build participant string + # Build participant string with proper grouping participants_str = '' if match.match_type == 'team_vs_team': teams = [] @@ -129,10 +151,22 @@ def api_events_for_tryout(tryout_id): if match.team2: teams.append(match.team2.name) participants_str = f"{' vs '.join(teams)}" + elif match.match_type == 'player_vs_player': + # Get players grouped by team side + team1_players = [p.player.full_name for p in match.participants.filter_by(team_side=1).all()] + team2_players = [p.player.full_name for p in match.participants.filter_by(team_side=2).all()] + if team1_players and team2_players: + participants_str = f"{', '.join(team1_players)} vs {', '.join(team2_players)}" + else: + participants_str = 'TBD vs TBD' else: player_names = [p.player.full_name for p in match.participants.all()] participants_str = ', '.join(player_names) if player_names else 'No players' + # Include time for calendar display + start_time_str = match.start_time.strftime('%H:%M') if match.start_time else None + end_time_str = match.end_time.strftime('%H:%M') if match.end_time else None + events.append({ 'id': f'match_{match.id}', 'title': match.title + ' (' + participants_str + ')', @@ -144,7 +178,10 @@ def api_events_for_tryout(tryout_id): 'status': match.status, 'match_type': match.match_type, 'tryout_id': tryout.id, - 'match_id': match.id + 'match_id': match.id, + 'participants': participants_str, + 'start_time': start_time_str, + 'end_time': end_time_str } }) @@ -245,6 +282,17 @@ def create_match(tryout_id): match.team1_id = int(team1_id) if team1_id else None match.team2_id = int(team2_id) if team2_id else None + # Handle player vs player matches + elif match_type == 'player_vs_player': + team1_player_ids = request.form.getlist('team1_player_ids') + team2_player_ids = request.form.getlist('team2_player_ids') + for pid in team1_player_ids: + participant = MatchParticipant(match_id=match.id, player_id=int(pid), team_side=1) + db.session.add(participant) + for pid in team2_player_ids: + participant = MatchParticipant(match_id=match.id, player_id=int(pid), team_side=2) + db.session.add(participant) + # Handle player scrim matches elif match_type == 'player_scrim': player_ids = request.form.getlist('player_ids') @@ -273,6 +321,9 @@ def edit_match(match_id): teams = Team.query.filter_by(tryout_id=tryout.id).all() all_players = User.query.filter_by(role='player').order_by(User.full_name).all() current_player_ids = [p.player_id for p in match.participants.all()] + # Get players grouped by team side for player_vs_player matches + team1_player_ids = [p.player_id for p in match.participants.filter_by(team_side=1).all()] + team2_player_ids = [p.player_id for p in match.participants.filter_by(team_side=2).all()] if request.method == 'POST': match.title = request.form.get('title') @@ -308,6 +359,18 @@ def edit_match(match_id): match.team1_id = int(team1_id) if team1_id else None match.team2_id = int(team2_id) if team2_id else None + # Handle player vs player matches - update participants + elif match.match_type == 'player_vs_player': + MatchParticipant.query.filter_by(match_id=match.id).delete() + team1_player_ids = request.form.getlist('team1_player_ids') + team2_player_ids = request.form.getlist('team2_player_ids') + for pid in team1_player_ids: + participant = MatchParticipant(match_id=match.id, player_id=int(pid), team_side=1) + db.session.add(participant) + for pid in team2_player_ids: + participant = MatchParticipant(match_id=match.id, player_id=int(pid), team_side=2) + db.session.add(participant) + # Handle player scrim matches - update participants elif match.match_type == 'player_scrim': MatchParticipant.query.filter_by(match_id=match.id).delete() @@ -320,7 +383,7 @@ def edit_match(match_id): flash('Match updated successfully!', 'success') return redirect(url_for('tryouts.view_tryout', tryout_id=tryout.id)) - return render_template('pages/edit_match.html', match=match, tryout=tryout, teams=teams, all_players=all_players, current_player_ids=current_player_ids) + return render_template('pages/edit_match.html', match=match, tryout=tryout, teams=teams, all_players=all_players, current_player_ids=current_player_ids, team1_player_ids=team1_player_ids, team2_player_ids=team2_player_ids) @matches_bp.route('//delete', methods=['POST']) diff --git a/routes/tryouts.py b/routes/tryouts.py index 1142606..2fa670c 100644 --- a/routes/tryouts.py +++ b/routes/tryouts.py @@ -144,10 +144,22 @@ def view_tryout(tryout_id): # Determine if current user can edit this tryout can_edit = current_user.can_manage_this_tryout(tryout) - + + # Determine if current user can view the calendar (managers/coaches can always see it) + # Players need to be registered or participating in a match + can_view_calendar = can_edit + if current_user.role == 'player': + # Check if player is participating in any matches for this tryout + player_in_match = MatchParticipant.query.join(Match).filter( + MatchParticipant.player_id == current_user.id, + Match.tryout_id == tryout_id + ).first() is not None + + can_view_calendar = is_registered or player_in_match + # Get all players (for manager registration dropdown) all_players = User.query.filter_by(role='player').order_by(User.full_name).all() - + # Get matches for this tryout with participant info matches = Match.query.filter_by(tryout_id=tryout_id).order_by(Match.date).all() match_data = [] @@ -155,7 +167,18 @@ def view_tryout(tryout_id): if match.match_type == 'team_vs_team': participants = { 'team1': match.team1.name if match.team1 else 'TBD', - 'team2': match.team2.name if match.team2 else 'TBD' + 'team2': match.team2.name if match.team2 else 'TBD', + 'team1_players': [{'name': m.player.full_name, 'position': m.position} for m in match.team1.members.all()] if match.team1 else [], + 'team2_players': [{'name': m.player.full_name, 'position': m.position} for m in match.team2.members.all()] if match.team2 else [] + } + elif match.match_type == 'player_vs_player': + team1_players = [p.player.full_name for p in match.participants.filter_by(team_side=1).all()] + team2_players = [p.player.full_name for p in match.participants.filter_by(team_side=2).all()] + participants = { + 'team1': ', '.join(team1_players) if team1_players else 'TBD', + 'team2': ', '.join(team2_players) if team2_players else 'TBD', + 'team1_player_names': team1_players, + 'team2_player_names': team2_players } else: participants = [p.player.full_name for p in match.participants.all()] @@ -173,6 +196,7 @@ def view_tryout(tryout_id): registrations=registrations, team_data=team_data, can_edit=can_edit, + can_view_calendar=can_view_calendar, all_players=all_players, matches=matches, match_data=match_data, diff --git a/static/css/style.css b/static/css/style.css index a978463..341baad 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -949,6 +949,46 @@ a:hover { color: var(--primary-dark); } gap: 4px; } +/* Team Roster Display */ +.team-roster-display { + font-size: 0.85rem; + color: var(--gray-600); +} + +.team-roster-display small { + display: block; + margin-top: 2px; +} + +.team-rosters { + display: flex; + gap: 20px; + flex-wrap: wrap; +} + +.team-roster { + flex: 1; + min-width: 200px; +} + +.team-roster h5 { + font-size: 0.9rem; + margin-bottom: 8px; + color: var(--gray-700); +} + +.team-members-list { + list-style: none; + padding: 0; + margin: 0; + font-size: 0.85rem; +} + +.team-members-list li { + padding: 4px 0; + color: var(--gray-600); +} + @media (max-width: 768px) { .sidebar { width: var(--sidebar-collapsed); @@ -970,4 +1010,5 @@ a:hover { color: var(--primary-dark); } .content { padding: 16px; } .top-bar { padding: 12px 16px; } .flash-messages { padding: 0 16px; } + .team-rosters { flex-direction: column; } } diff --git a/templates/pages/create_match.html b/templates/pages/create_match.html index 0c6d45d..e17e922 100644 --- a/templates/pages/create_match.html +++ b/templates/pages/create_match.html @@ -12,10 +12,11 @@
-
+
@@ -79,10 +80,41 @@
+ + + + + +
+ {% if match.team1 %} +
+
{{ match.team1.name }} Players
+
    + {% for member in match.team1.members %} +
  • {{ member.player.full_name }}{% if member.position %} {{ member.position }}{% endif %}
  • + {% else %} +
  • No players assigned
  • + {% endfor %} +
+
+ {% endif %} + {% if match.team2 %} +
+
{{ match.team2.name }} Players
+
    + {% for member in match.team2.members %} +
  • {{ member.player.full_name }}{% if member.position %} {{ member.position }}{% endif %}
  • + {% else %} +
  • No players assigned
  • + {% endfor %} +
+
+ {% endif %} +
+ + + {% elif match.match_type == 'player_vs_player' %} +
+

Select Players

+ +
+
+ +
+ {% for player in all_players %} + + {% endfor %} +
+
+
+ +
+ {% for player in all_players %} + + {% endfor %} +
+
+
+ + {% else %}
-

Players

+

Select Players

@@ -105,4 +166,22 @@
-{% endblock %} \ No newline at end of file +{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/templates/pages/view_tryout.html b/templates/pages/view_tryout.html index abf3e1e..89bee35 100644 --- a/templates/pages/view_tryout.html +++ b/templates/pages/view_tryout.html @@ -231,10 +231,10 @@ - {% if matches %} + {% if can_view_calendar %}
-

Scheduled Matches

+

Schedule

{% if can_edit %} Schedule Match @@ -242,9 +242,18 @@ {% endif %}
+ {% if matches %}
+ {% else %} + +
+ +

No matches scheduled yet. Check back later!

+
+ {% endif %} +{% if matches %}
@@ -264,19 +273,42 @@ {% for item in match_data %} {% set m = item.match %} + {% set participants = item.participants %}
{{ m.title }} - - {{ 'Team vs Team' if m.match_type == 'team_vs_team' else 'Player Scrim' }} + + {% if m.match_type == 'team_vs_team' %} + Team vs Team + {% elif m.match_type == 'player_vs_player' %} + Player vs Player + {% else %} + Player Scrim + {% endif %} {{ m.date.strftime('%m/%d/%Y') }} {% if m.match_type == 'team_vs_team' %} - {{ item.participants.team1 }} vs {{ item.participants.team2 }} + {{ participants.team1 }} vs {{ participants.team2 }} + {% if participants.team1_players or participants.team2_players %} +
+ {% if participants.team1_players %} + {{ m.team1.name if m.team1 else 'Team 1' }}: + {% for pl in participants.team1_players %}{{ pl.name }}{% if not loop.last %}, {% endif %}{% endfor %} +
+ {% endif %} + {% if participants.team2_players %} + {{ m.team2.name if m.team2 else 'Team 2' }}: + {% for pl in participants.team2_players %}{{ pl.name }}{% if not loop.last %}, {% endif %}{% endfor %} + + {% endif %} +
+ {% endif %} + {% elif m.match_type == 'player_vs_player' %} + {{ participants.team1 }} vs {{ participants.team2 }} {% else %} - {{ item.participants | join(', ') }} + {{ participants | join(', ') }} {% endif %}
@@ -301,6 +333,7 @@
+ {% endif %}
{% endif %}