diff --git a/__pycache__/app.cpython-313.pyc b/__pycache__/app.cpython-313.pyc index fad2d49..f62c93d 100644 Binary files a/__pycache__/app.cpython-313.pyc and b/__pycache__/app.cpython-313.pyc differ diff --git a/app.py b/app.py index 29b0c62..6d029a9 100644 --- a/app.py +++ b/app.py @@ -88,7 +88,7 @@ def create_app(): response.headers['X-Content-Type-Options'] = 'nosniff' response.headers['X-Frame-Options'] = 'DENY' response.headers['X-XSS-Protection'] = '1; mode=block' - response.headers['Content-Security-Policy'] = "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; font-src 'self' https://cdnjs.cloudflare.com; img-src 'self' data:; connect-src 'self'; frame-ancestors 'none';" + response.headers['Content-Security-Policy'] = "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://cdn.jsdelivr.net; font-src 'self' https://cdnjs.cloudflare.com; img-src 'self' data:; connect-src 'self'; frame-ancestors 'none';" response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains' return response @@ -133,5 +133,5 @@ def create_app(): if __name__ == '__main__': app = create_app() # Debug mode should only be enabled via environment variable for security - debug_mode = os.environ.get('FLASK_DEBUG', 'false').lower() == 'true' + debug_mode = os.getenv('FLASK_DEBUG', 'false').lower() == 'true' app.run(debug=debug_mode, host='0.0.0.0', port=5000) \ No newline at end of file diff --git a/instance/team_tryouts.db b/instance/team_tryouts.db index 0fa567a..e42e0f7 100644 Binary files a/instance/team_tryouts.db and b/instance/team_tryouts.db differ diff --git a/routes/__pycache__/auth.cpython-313.pyc b/routes/__pycache__/auth.cpython-313.pyc index 7240504..e98dac6 100644 Binary files a/routes/__pycache__/auth.cpython-313.pyc and b/routes/__pycache__/auth.cpython-313.pyc differ diff --git a/routes/__pycache__/evaluations.cpython-313.pyc b/routes/__pycache__/evaluations.cpython-313.pyc index 59a7236..c945e3a 100644 Binary files a/routes/__pycache__/evaluations.cpython-313.pyc and b/routes/__pycache__/evaluations.cpython-313.pyc differ diff --git a/routes/__pycache__/tryouts.cpython-313.pyc b/routes/__pycache__/tryouts.cpython-313.pyc index af03231..5a80b27 100644 Binary files a/routes/__pycache__/tryouts.cpython-313.pyc and b/routes/__pycache__/tryouts.cpython-313.pyc differ diff --git a/routes/auth.py b/routes/auth.py index b34381a..b2f2efa 100644 --- a/routes/auth.py +++ b/routes/auth.py @@ -21,7 +21,7 @@ def is_safe_url(url): """ if not url: return False - parsed = url_parse(url) + parsed = urlparse(url) # Allow relative URLs (no netloc) or same-origin URLs return not parsed.netloc or parsed.netloc == request.host diff --git a/routes/evaluations.py b/routes/evaluations.py index d11ed3a..83f74c0 100644 --- a/routes/evaluations.py +++ b/routes/evaluations.py @@ -6,8 +6,9 @@ This module handles player evaluation creation, management, and viewing. from flask import Blueprint, render_template, redirect, url_for, flash, request from flask_login import login_required, current_user from extensions import db -from models import User, Tryout, Evaluation, TryoutRegistration, GAME_POSITIONS +from models import User, Tryout, Evaluation, TryoutRegistration, GAME_POSITIONS, OrgTeam from sqlalchemy import func +from sqlalchemy.orm import aliased evaluations_bp = Blueprint('evaluations', __name__, url_prefix='/evaluations') @@ -41,13 +42,55 @@ def list_evaluations(): Evaluators (coach/manager): Their given evaluations. Players: Their received evaluations. + Supports sorting by any column header via 'sort' and 'order' query parameters. + Returns: Response: Rendered evaluations list template. """ user = current_user + # Get sort parameters + sort_column = request.args.get('sort', 'created_at') + sort_order = request.args.get('order', 'desc') + + # Validate sort_order + if sort_order not in ('asc', 'desc'): + sort_order = 'desc' + + # Map sort columns to SQLAlchemy expressions using aliased User models for relationship sorting + player_alias = aliased(User, name='eval_player') + evaluator_alias = aliased(User, name='eval_evaluator') + + sort_map = { + 'tryout': Tryout.title, + 'player': player_alias.full_name, + 'evaluator': evaluator_alias.full_name, + 'mecanics_score': Evaluation.mecanics_score, + 'cohesion_score': Evaluation.cohesion_score, + 'communication_score': Evaluation.communication_score, + 'gamesense_score': Evaluation.gamesense_score, + 'versatility_score': Evaluation.versatility_score, + 'discipline_score': Evaluation.discipline_score, + 'analysis_score': Evaluation.analysis_score, + 'sport_ethics_score': Evaluation.sport_ethics_score, + 'mental_score': Evaluation.mental_score, + 'overall_score': Evaluation.overall_score, + 'position_recommendation': Evaluation.position_recommendation, + 'created_at': Evaluation.created_at, + } + + sort_expr = sort_map.get(sort_column, Evaluation.created_at) + if sort_order == 'asc': + sort_expr = sort_expr.asc() + else: + sort_expr = sort_expr.desc() + if user.role == 'president': - evaluations = Evaluation.query.order_by(Evaluation.created_at.desc()).all() + evaluations = Evaluation.query \ + .outerjoin(Tryout, Evaluation.tryout_id == Tryout.id) \ + .outerjoin(player_alias, Evaluation.player_id == player_alias.id) \ + .outerjoin(evaluator_alias, Evaluation.evaluator_id == evaluator_alias.id) \ + .order_by(sort_expr).all() avg_scores = db.session.query( Evaluation.player_id, func.count(Evaluation.id).label('eval_count'), @@ -60,13 +103,23 @@ def list_evaluations(): player_scores[p.id] = {'player': p, 'count': row.eval_count, 'avg': round(row.avg_score, 1) if row.avg_score else 0} elif user.can_evaluate(): - evaluations = Evaluation.query.filter_by(evaluator_id=user.id).order_by(Evaluation.created_at.desc()).all() + evaluations = Evaluation.query \ + .outerjoin(Tryout, Evaluation.tryout_id == Tryout.id) \ + .outerjoin(player_alias, Evaluation.player_id == player_alias.id) \ + .outerjoin(evaluator_alias, Evaluation.evaluator_id == evaluator_alias.id) \ + .filter(Evaluation.evaluator_id == user.id) \ + .order_by(sort_expr).all() player_scores = {} else: - evaluations = Evaluation.query.filter_by(player_id=user.id).order_by(Evaluation.created_at.desc()).all() + evaluations = Evaluation.query \ + .outerjoin(Tryout, Evaluation.tryout_id == Tryout.id) \ + .outerjoin(player_alias, Evaluation.player_id == player_alias.id) \ + .outerjoin(evaluator_alias, Evaluation.evaluator_id == evaluator_alias.id) \ + .filter(Evaluation.player_id == user.id) \ + .order_by(sort_expr).all() player_scores = {} - return render_template('pages/evaluations.html', evaluations=evaluations, player_scores=player_scores) + return render_template('pages/evaluations.html', evaluations=evaluations, player_scores=player_scores, sort_column=sort_column, sort_order=sort_order) @evaluations_bp.route('//', methods=['GET', 'POST']) @@ -219,4 +272,4 @@ def players_to_evaluate(tryout_id): ).first() players.append({'player': p, 'evaluated': existing is not None, 'registration': reg}) - return render_template('pages/players_to_evaluate.html', tryout=tryout, players=players) \ No newline at end of file + return render_template('pages/players_to_evaluate.html', tryout=tryout, players=players) diff --git a/routes/tryouts.py b/routes/tryouts.py index 76dc584..9bbc819 100644 --- a/routes/tryouts.py +++ b/routes/tryouts.py @@ -14,7 +14,7 @@ tryouts_bp = Blueprint('tryouts', __name__, url_prefix='/tryouts') def can_manage(): """Check if current user can manage tryouts. - + Returns: bool: True if user is president or manager. """ @@ -25,13 +25,13 @@ def can_manage(): @login_required def list_tryouts(): """List all tryouts visible to the current user. - + Shows tryouts filtered by user's role: - President: All tryouts - Manager: Only their created tryouts - Coach: Tryouts targeting their org team - - Player: Upcoming and in-progress tryouts - + - Player: Only tryouts they are registered for or participating in + Returns: Response: Rendered tryouts list template. """ @@ -47,7 +47,9 @@ def list_tryouts(): else: tryouts = [] elif current_user.role == 'player': - tryouts = Tryout.query.filter(Tryout.status.in_(['upcoming', 'in_progress'])).order_by(Tryout.date.desc()).all() + # Players only see tryouts they are registered for or participating in matches + from routes.matches import get_visible_tryouts_for_user + tryouts = get_visible_tryouts_for_user() else: tryouts = Tryout.query.order_by(Tryout.date.desc()).all() return render_template('pages/tryouts.html', tryouts=tryouts, now=datetime.utcnow()) @@ -57,12 +59,12 @@ def list_tryouts(): @login_required def create_tryout(): """Create a new tryout event. - + GET: Render the tryout creation form. POST: Create a tryout with the submitted details. - + Requires president or manager role. - + Returns: Response: Create form or redirect to the new tryout. """ @@ -110,15 +112,15 @@ def create_tryout(): @login_required def edit_tryout(tryout_id): """Edit an existing tryout event. - + GET: Render the tryout edit form with current data. POST: Update the tryout with submitted changes. - + Permission based on can_manage_this_tryout check. - + Args: tryout_id: The ID of the tryout to edit. - + Returns: Response: Edit form or redirect to tryout view. """ @@ -164,18 +166,18 @@ def edit_tryout(tryout_id): @login_required def view_tryout(tryout_id): """View a specific tryout with all details. - + Displays tryout information, registered players, evaluations, teams, matches, and evaluation status information. - + Args: tryout_id: The ID of the tryout to view. - + Returns: Response: Rendered tryout detail template. """ tryout = Tryout.query.get_or_404(tryout_id) - + # Check if user has permission to view this tryout can_view = False if current_user.role == 'president': @@ -197,11 +199,11 @@ def view_tryout(tryout_id): can_view = is_registered or player_in_match elif current_user.role == 'scout': can_view = True - + if not can_view: flash('You do not have permission to view this tryout.', 'danger') return redirect(url_for('tryouts.list_tryouts')) - + registrations = TryoutRegistration.query.filter_by(tryout_id=tryout_id).all() registered_players = [User.query.get(r.player_id) for r in registrations if r.player_id] evaluations = Evaluation.query.filter_by(tryout_id=tryout_id).all() @@ -235,7 +237,7 @@ 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 @@ -245,16 +247,16 @@ def view_tryout(tryout_id): MatchParticipant.player_id == current_user.id, Match.tryout_id == tryout_id ).first() is not None - + can_view_calendar = is_registered or player_in_match # Only expose all_players to users who can manage players in this tryout all_players = None if can_edit: 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() + matches = Match.query.filter_by(tryout_id=tryout_id).order_by(Match.date, Match.start_time).all() match_data = [] for match in matches: if match.match_type == 'team_vs_team': @@ -279,7 +281,7 @@ def view_tryout(tryout_id): 'match': match, 'participants': participants }) - + return render_template('pages/view_tryout.html', tryout=tryout, registered_players=registered_players, @@ -301,13 +303,13 @@ def view_tryout(tryout_id): @login_required def register_for_tryout(tryout_id): """Register a player for a tryout. - + Allows players to register for tryouts. Validates that the tryout is accepting registrations and not at capacity. - + Args: tryout_id: The ID of the tryout to register for. - + Returns: Response: Redirect to tryout view with status message. """ @@ -342,12 +344,12 @@ def register_for_tryout(tryout_id): @login_required def update_status(tryout_id): """Update the status of a tryout. - + Changes tryout status between upcoming, in_progress, and completed. - + Args: tryout_id: The ID of the tryout to update. - + Returns: Response: Redirect to tryout view. """ @@ -367,11 +369,11 @@ def update_status(tryout_id): @login_required def update_registration_status(tryout_id, player_id): """Update the attendance status of a tryout registration. - + Args: tryout_id: The ID of the tryout. player_id: The ID of the player whose status to update. - + Returns: Response: Redirect to tryout view. """ @@ -393,12 +395,12 @@ def update_registration_status(tryout_id, player_id): @login_required def register_player(tryout_id): """Manually register a player for a tryout (by managers/coaches). - + Allows authorized users to register players on their behalf. - + Args: tryout_id: The ID of the tryout. - + Returns: Response: Redirect to tryout view with status message. """ @@ -439,10 +441,10 @@ def register_player(tryout_id): @login_required def create_team(tryout_id): """Create a tryout-specific team. - + Args: tryout_id: The ID of the tryout to create the team for. - + Returns: Response: Redirect to tryout view with status message. """ @@ -464,11 +466,11 @@ def create_team(tryout_id): @login_required def add_to_team(tryout_id, team_id): """Add a player to a tryout team. - + Args: tryout_id: The ID of the tryout. team_id: The ID of the team to add the player to. - + Returns: Response: Redirect to tryout view with status message. """ @@ -490,4 +492,4 @@ def add_to_team(tryout_id, team_id): db.session.commit() flash('Player added to team!', 'success') - return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id)) \ No newline at end of file + return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id)) diff --git a/static/css/style.css b/static/css/style.css index 61ddc4c..91a51ab 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -1749,3 +1749,147 @@ a:hover { color: var(--primary-dark); } transform: translateY(-50%); margin: 0; } + +/* ===== Tryouts Grid (Card-based Layout) ===== */ +.tryouts-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); + gap: 20px; + margin-bottom: 20px; +} + +.tryout-card { + transition: transform 0.2s ease, box-shadow 0.2s ease; + border: 1px solid var(--border-color, #e0e0e0); +} + +.tryout-card:hover { + transform: translateY(-2px); + box-shadow: var(--shadow-md); +} + +.tryout-card .card-header { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; +} + +.tryout-title { + font-size: 1.1rem; + font-weight: 600; + margin: 0; + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.tryout-info-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 12px; +} + +.info-item { + display: flex; + align-items: center; + gap: 8px; + font-size: 0.85rem; +} + +.info-icon { + width: 16px; + text-align: center; + color: var(--text-muted, #888); + flex-shrink: 0; +} + +.info-label { + font-weight: 600; + color: var(--text-secondary, #555); + min-width: 50px; +} + +.info-value { + color: var(--text-primary, #333); + flex: 1; + text-align: right; +} + +.tryout-description { + font-size: 0.85rem; + color: var(--text-secondary, #555); + line-height: 1.4; +} + +.tryout-description p { + margin: 0; +} + +.tryout-card .card-footer { + display: flex; + gap: 8px; + padding: 12px 16px; + background: var(--bg-secondary, #f8f9fa); + border-top: 1px solid var(--border-color, #e0e0e0); +} + +/* ===== Full-width Schedule Card ===== */ +.tryout-schedule-card { + width: 100%; +} + +/* ===== Dark mode adjustments ===== */ +[data-theme="dark"] .tryout-card { + border-color: var(--border-color, #333); +} + +[data-theme="dark"] .tryout-card .card-footer { + background: var(--bg-secondary, #2a2a2a); + border-top-color: var(--border-color, #333); +} + +[data-theme="dark"] .info-label { + color: var(--text-secondary, #aaa); +} + + + + + +/* Sortable Table Headers */ +.sort-link { + display: inline-flex; + align-items: center; + gap: 4px; + color: var(--gray-500); + font-weight: 600; + text-decoration: none; + transition: var(--transition); +} + +.sort-link:hover { + color: var(--gray-800); +} + +.sort-link .fas { + font-size: 0.75rem; +} + +.sort-inactive { + opacity: 0.3; + font-size: 0.75rem; +} + +.sort-link:hover .sort-inactive { + opacity: 0.5; +} + +[data-theme="dark"] .sort-link { + color: var(--text-secondary); +} + +[data-theme="dark"] .sort-link:hover { + color: var(--text-primary); +} diff --git a/templates/pages/evaluations.html b/templates/pages/evaluations.html index d033399..34b991c 100644 --- a/templates/pages/evaluations.html +++ b/templates/pages/evaluations.html @@ -4,6 +4,23 @@ {% block breadcrumb %}Home / Evaluations{% endblock %} {% block content %} +{% set sort_column = sort_column | default('created_at') %} +{% set sort_order = sort_order | default('desc') %} + +{% macro sort_link(column, label) %} + + {% set new_order = 'asc' if (sort_column == column and sort_order == 'desc') else 'desc' %} + + {{ label }} + {% if sort_column == column %} + + {% else %} + + {% endif %} + + +{% endmacro %} + {% if current_user.role == 'president' and player_scores %}
{% for pid, data in player_scores.items() %} @@ -22,7 +39,7 @@
-

+

{% if current_user.role == 'player' %} My Evaluations {% else %} @@ -35,29 +52,29 @@ - - - - - - - - - - - - - - - + {{ sort_link('tryout', 'Tryout') }} + {{ sort_link('player', 'Player') }} + {{ sort_link('evaluator', 'Evaluator') }} + {{ sort_link('mecanics_score', 'Mecanics') }} + {{ sort_link('cohesion_score', 'Cohesion') }} + {{ sort_link('communication_score', 'Communication') }} + {{ sort_link('gamesense_score', 'Gamesense') }} + {{ sort_link('versatility_score', 'Versatility') }} + {{ sort_link('discipline_score', 'Discipline') }} + {{ sort_link('analysis_score', 'Analysis') }} + {{ sort_link('sport_ethics_score', 'Sport Ethics') }} + {{ sort_link('mental_score', 'Mental') }} + {{ sort_link('overall_score', 'Overall') }} + {{ sort_link('position_recommendation', 'Position') }} + {{ sort_link('created_at', 'Date') }} {% for eval in evaluations %} - - - + + + @@ -86,4 +103,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/pages/profile.html b/templates/pages/profile.html index f344ef0..c3a941c 100644 --- a/templates/pages/profile.html +++ b/templates/pages/profile.html @@ -77,22 +77,28 @@
- TRN (Tracker Network) + Games & TRN - {% if user.gamertags %} + {% set games_list = user.get_games_list() %} + {% if games_list %}
- {% for gamertag in user.gamertags %} + {% for game in games_list %} + {% set gt = user.gamertags | selectattr('game', 'equalto', game) | first %} {% endfor %}
{% else %} - Not connected + Not specified {% endif %}
diff --git a/templates/pages/tryouts.html b/templates/pages/tryouts.html index 087302d..bd49dde 100644 --- a/templates/pages/tryouts.html +++ b/templates/pages/tryouts.html @@ -12,64 +12,87 @@ {% endblock %} {% block content %} -
-
-
-
TryoutPlayerEvaluatorMecanicsCohesionCommunicationGamesenseVersatilityDisciplineAnalysisSport EthicsMentalOverallPositionDate
{{ eval.tryout.title }}{{ eval.player.full_name }}{{ eval.evaluator.full_name }}{{ eval.tryout.title if eval.tryout else 'Deleted Tryout' }}{{ eval.player.full_name if eval.player else 'Deleted Player' }}{{ eval.evaluator.full_name if eval.evaluator else 'Deleted Evaluator' }} {{ eval.mecanics_score or '-' }} {{ eval.cohesion_score or '-' }} {{ eval.communication_score or '-' }}
- - - - - - - - - - - - - - {% for tryout in tryouts %} - - - - - - - - - - - {% else %} - - - - {% endfor %} - -
TitleTarget TeamDateLocationStatusPlayersEvaluationsActions
{{ tryout.title }}{{ tryout.target_org_team.name if tryout.target_org_team else '-' }}{{ tryout.date.strftime('%b %d, %Y') }}{{ tryout.location or 'TBD' }} - - {{ tryout.status | replace('_', ' ') | title }} - - {{ tryout.registrations.count() }}{{ tryout.evaluations.count() }} - - View - - {% if current_user.can_evaluate() %} - - Evaluate - - {% endif %} -
-
- -

No tryouts found

- {% if current_user.can_manage_tryouts() %} -

Get started by creating a new tryout.

- Create Tryout - {% endif %} -
-
+
+ {% for tryout in tryouts %} +
+
+

{{ tryout.title }}

+ {{ tryout.status | replace('_', ' ') | title }} +
+
+
+
+ + Game + {{ tryout.game }} +
+
+ + Date + {{ tryout.date.strftime('%b %d, %Y') }} +
+
+ + Location + {{ tryout.location or 'TBD' }} +
+
+ + Players + {{ tryout.registrations.count() }} +
+ {% if current_user.can_evaluate() %} +
+ + Evaluations + {{ tryout.evaluations.count() }} +
+ {% endif %} + {% if tryout.target_org_team %} +
+ + Target Team + {{ tryout.target_org_team.name }} +
+ {% endif %} + {% if tryout.max_players %} +
+ + Max Players + {{ tryout.max_players }} +
+ {% endif %} +
+ {% if tryout.description %} +
+

{{ tryout.description | truncate(120) }}

+
+ {% endif %} +
+
+ {% else %} +
+
+
+ +

No tryouts found

+ {% if current_user.can_manage_tryouts() %} +

Get started by creating a new tryout.

+ Create Tryout + {% endif %} +
+
+
+ {% endfor %}
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/templates/pages/view_tryout.html b/templates/pages/view_tryout.html index 4ca00c4..282d175 100644 --- a/templates/pages/view_tryout.html +++ b/templates/pages/view_tryout.html @@ -246,204 +246,206 @@ {% endfor %}

+
- {% if can_view_calendar %} -
-
-

Schedule

- {% if can_edit %} - - {% endif %} -
-
- {% if matches %} - -
- {% else %} - -
- -

No matches scheduled yet. Check back later!

-
- {% endif %} - - {% if matches %} - -
- - - - - - - - - - {% if can_edit %} - - {% endif %} - - - - {% for item in match_data %} - {% set m = item.match %} - {% set participants = item.participants %} - - - - - - - - {% if can_edit %} - - {% endif %} - - {% endfor %} - -
MatchTypeDateParticipantsTimeStatusActions
{{ m.title }} - - {% 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' %} -
-
- {{ m.team1.name if m.team1 else 'Team 1' }} - {% if participants.team1_players %} -
    - {% for pl in participants.team1_players %} -
  • {{ pl.name }}{% if pl.position %} {{ pl.position }}{% endif %}
  • - {% endfor %} -
- {% endif %} -
-
vs
-
- {{ m.team2.name if m.team2 else 'Team 2' }} - {% if participants.team2_players %} -
    - {% for pl in participants.team2_players %} -
  • {{ pl.name }}{% if pl.position %} {{ pl.position }}{% endif %}
  • - {% endfor %} -
- {% endif %} -
-
- {% elif m.match_type == 'player_vs_player' %} -
-
- Team 1 - {% if participants.team1_players %} -
    - {% for pl in participants.team1_players %} -
  • {{ pl.name }}{% if pl.position %} {{ pl.position }}{% endif %}
  • - {% endfor %} -
- {% endif %} -
-
vs
-
- Team 2 - {% if participants.team2_players %} -
    - {% for pl in participants.team2_players %} -
  • {{ pl.name }}{% if pl.position %} {{ pl.position }}{% endif %}
  • - {% endfor %} -
- {% endif %} -
-
- {% else %} - {{ participants | join(', ') }} - {% endif %} -
- {% if m.start_time and m.end_time %} - {{ m.start_time.strftime('%H:%M') }} - {{ m.end_time.strftime('%H:%M') }} - {% else %} - TBD - {% endif %} - - {{ m.status }} - - - Edit - - - Note - -
-
- {% endif %} + {% if can_view_calendar %} +
+
+

Schedule

+ {% if can_edit %} + + {% endif %}
- {% endif %} - -
-
-

Evaluation Summary

+
+ {% if matches %} + +
+ + + + + + + + + + {% if can_edit %} + + {% endif %} + + + + {% for item in match_data %} + {% set m = item.match %} + {% set participants = item.participants %} + + + + + + + + {% if can_edit %} + + {% endif %} + + {% endfor %} + +
MatchTypeDateParticipantsTimeStatusActions
{{ m.title }} + + {% 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' %} +
+
+ {{ m.team1.name if m.team1 else 'Team 1' }} + {% if participants.team1_players %} +
    + {% for pl in participants.team1_players %} +
  • {{ pl.name }}{% if pl.position %} {{ pl.position }}{% endif %}
  • + {% endfor %} +
+ {% endif %} +
+
vs
+
+ {{ m.team2.name if m.team2 else 'Team 2' }} + {% if participants.team2_players %} +
    + {% for pl in participants.team2_players %} +
  • {{ pl.name }}{% if pl.position %} {{ pl.position }}{% endif %}
  • + {% endfor %} +
+ {% endif %} +
+
+ {% elif m.match_type == 'player_vs_player' %} +
+
+ Team 1 + {% if participants.team1_players %} +
    + {% for pl in participants.team1_players %} +
  • {{ pl.name }}{% if pl.position %} {{ pl.position }}{% endif %}
  • + {% endfor %} +
+ {% endif %} +
+
vs
+
+ Team 2 + {% if participants.team2_players %} +
    + {% for pl in participants.team2_players %} +
  • {{ pl.name }}{% if pl.position %} {{ pl.position }}{% endif %}
  • + {% endfor %} +
+ {% endif %} +
+
+ {% else %} + {{ participants | join(', ') }} + {% endif %} +
+ {% if m.start_time and m.end_time %} + {{ m.start_time.strftime('%H:%M') }} - {{ m.end_time.strftime('%H:%M') }} + {% else %} + TBD + {% endif %} + + {{ m.status }} + + + Edit + + + Note + +
-
-
- - - - - - - - - - - - - - - - - - - - {% for eval in evaluations %} - - - - - - - - - - - - - - - - {% else %} - - - - {% endfor %} - -
PlayerEvaluatorMecanicsCohesionCommunicationGamesenseVersatilityDisciplineAnalysisSport EthicsMentalOverallRecommendation
{{ eval.player.full_name }}{{ eval.evaluator.full_name }}{{ eval.mecanics_score or '-' }}{{ eval.cohesion_score or '-' }}{{ eval.communication_score or '-' }}{{ eval.gamesense_score or '-' }}{{ eval.versatility_score or '-' }}{{ eval.discipline_score or '-' }}{{ eval.analysis_score or '-' }}{{ eval.sport_ethics_score or '-' }}{{ eval.mental_score or '-' }}{{ eval.overall_score or '-' }}{{ eval.position_recommendation or '-' }}
No evaluations yet.
-
+ {% endif %} + + {% if matches %} + +
+ {% else %} + +
+ +

No matches scheduled yet. Check back later!

+
+ {% endif %} +
+
+ {% endif %} + + {% if current_user.can_evaluate() %} +
+
+

Evaluation Summary

+
+
+
+ + + + + + + + + + + + + + + + + + + + {% for eval in evaluations %} + + + + + + + + + + + + + + + + {% else %} + + + + {% endfor %} + +
PlayerEvaluatorMecanicsCohesionCommunicationGamesenseVersatilityDisciplineAnalysisSport EthicsMentalOverallRecommendation
{{ eval.player.full_name }}{{ eval.evaluator.full_name }}{{ eval.mecanics_score or '-' }}{{ eval.cohesion_score or '-' }}{{ eval.communication_score or '-' }}{{ eval.gamesense_score or '-' }}{{ eval.versatility_score or '-' }}{{ eval.discipline_score or '-' }}{{ eval.analysis_score or '-' }}{{ eval.sport_ethics_score or '-' }}{{ eval.mental_score or '-' }}{{ eval.overall_score or '-' }}{{ eval.position_recommendation or '-' }}
No evaluations yet.
+ {% endif %}
-{% endblock %} \ No newline at end of file +{% endblock %}