Régler problèmes d'affichage des matchs récents:
Ajout des match qui arrivent pour les coach, president et manager Légèrement alléger la quantité de pages (merge certaines ensembles)
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -156,8 +156,9 @@ class User(UserMixin, db.Model):
|
||||
def can_manage_this_tryout(self, tryout):
|
||||
"""Check if user can manage a specific tryout.
|
||||
|
||||
Presidents can manage all tryouts. Managers can manage their own tryouts.
|
||||
Coaches can manage tryouts targeting their coached team.
|
||||
Presidents can manage all tryouts. Managers can manage their own tryouts
|
||||
or tryouts where they are assigned as manager. Coaches can manage tryouts
|
||||
targeting their coached team or where they are assigned as coach.
|
||||
|
||||
Args:
|
||||
tryout: The Tryout object to check permissions for.
|
||||
@@ -167,12 +168,14 @@ class User(UserMixin, db.Model):
|
||||
"""
|
||||
if self.role == 'president':
|
||||
return True
|
||||
if self.role == 'manager' and tryout.created_by == self.id:
|
||||
if self.role == 'manager' and (tryout.created_by == self.id or tryout.manager_id == self.id):
|
||||
return True
|
||||
if self.role == 'coach':
|
||||
org_team = OrgTeam.query.filter_by(coach_id=self.id).first()
|
||||
if org_team and tryout.target_org_team_id == org_team.id:
|
||||
return True
|
||||
if tryout.coach_id == self.id:
|
||||
return True
|
||||
return False
|
||||
|
||||
def can_manage_this_org_team(self, org_team):
|
||||
@@ -392,6 +395,8 @@ class Tryout(db.Model):
|
||||
max_players: Maximum number of players allowed.
|
||||
created_by: Foreign key to the creating manager/president.
|
||||
target_org_team_id: Foreign key to target organization team.
|
||||
manager_id: Foreign key to the assigned manager.
|
||||
coach_id: Foreign key to the assigned coach.
|
||||
created_at: Timestamp of creation.
|
||||
"""
|
||||
__tablename__ = 'tryouts'
|
||||
@@ -405,9 +410,13 @@ class Tryout(db.Model):
|
||||
max_players = db.Column(db.Integer, nullable=True)
|
||||
created_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
target_org_team_id = db.Column(db.Integer, db.ForeignKey('org_teams.id'), nullable=True)
|
||||
manager_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||
coach_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
|
||||
creator = db.relationship('User', backref='created_tryouts')
|
||||
creator = db.relationship('User', foreign_keys=[created_by], backref='created_tryouts')
|
||||
manager = db.relationship('User', foreign_keys=[manager_id], backref='managed_tryouts')
|
||||
coach = db.relationship('User', foreign_keys=[coach_id], backref='coached_tryouts')
|
||||
registrations = db.relationship('TryoutRegistration', backref='tryout', lazy='dynamic')
|
||||
evaluations = db.relationship('Evaluation', backref='tryout', lazy='dynamic')
|
||||
teams = db.relationship('Team', backref='tryout', lazy='dynamic')
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+51
-27
@@ -52,12 +52,24 @@ def dashboard():
|
||||
stats['completed_tryouts'] = Tryout.query.filter_by(status='completed').count()
|
||||
stats['recent_users'] = User.query.order_by(User.created_at.desc()).limit(10).all()
|
||||
stats['recent_tryouts'] = Tryout.query.order_by(Tryout.created_at.desc()).limit(10).all()
|
||||
today = date.today()
|
||||
stats['upcoming_matches'] = Match.query.filter(
|
||||
Match.status == 'scheduled',
|
||||
Match.date >= today
|
||||
).order_by(Match.date, Match.start_time).limit(5).all()
|
||||
|
||||
elif user.role == 'manager':
|
||||
stats['total_tryouts'] = Tryout.query.filter_by(created_by=user.id).count()
|
||||
stats['active_tryouts'] = Tryout.query.filter_by(created_by=user.id, status='in_progress').count()
|
||||
stats['total_evaluations'] = Evaluation.query.filter_by(evaluator_id=user.id).count()
|
||||
stats['my_tryouts'] = Tryout.query.filter_by(created_by=user.id).order_by(Tryout.date.desc()).limit(5).all()
|
||||
today = date.today()
|
||||
manager_tryout_ids = [t.id for t in Tryout.query.filter_by(created_by=user.id).all()]
|
||||
stats['upcoming_matches'] = Match.query.filter(
|
||||
Match.tryout_id.in_(manager_tryout_ids),
|
||||
Match.status == 'scheduled',
|
||||
Match.date >= today
|
||||
).order_by(Match.date, Match.start_time).limit(5).all() if manager_tryout_ids else []
|
||||
|
||||
elif user.role == 'coach':
|
||||
stats['my_evaluations'] = Evaluation.query.filter_by(evaluator_id=user.id).count()
|
||||
@@ -67,54 +79,66 @@ def dashboard():
|
||||
evaluated_player_ids = [e.player_id for e in Evaluation.query.filter_by(evaluator_id=user.id).all()]
|
||||
stats['pending_evaluations'] = len(set(registered_player_ids) - set(evaluated_player_ids))
|
||||
stats['my_recent_evaluations'] = Evaluation.query.filter_by(evaluator_id=user.id).order_by(Evaluation.created_at.desc()).limit(10).all()
|
||||
today = date.today()
|
||||
org_team = OrgTeam.query.filter_by(coach_id=user.id).first()
|
||||
coach_tryout_ids = [t.id for t in Tryout.query.filter_by(target_org_team_id=org_team.id).all()] if org_team else []
|
||||
stats['upcoming_matches'] = Match.query.filter(
|
||||
Match.tryout_id.in_(coach_tryout_ids),
|
||||
Match.status == 'scheduled',
|
||||
Match.date >= today
|
||||
).order_by(Match.date, Match.start_time).limit(5).all() if coach_tryout_ids else []
|
||||
|
||||
elif user.role == 'player':
|
||||
stats['my_tryouts'] = TryoutRegistration.query.filter_by(player_id=user.id).count()
|
||||
stats['my_registrations'] = TryoutRegistration.query.filter_by(player_id=user.id).order_by(TryoutRegistration.registered_at.desc()).limit(5).all()
|
||||
|
||||
# Get next match for each tryout the player is in
|
||||
# Get upcoming matches for the player
|
||||
today = date.today()
|
||||
next_matches = []
|
||||
for reg in stats['my_registrations']:
|
||||
tryout = reg.tryout
|
||||
# Find matches where player participates (team membership or direct participant)
|
||||
player_teams = Team.query.join(TeamMember).filter(
|
||||
Team.tryout_id == tryout.id,
|
||||
TeamMember.player_id == user.id
|
||||
).all()
|
||||
|
||||
# Get matches for this tryout that include the player
|
||||
tryout_matches = Match.query.filter(
|
||||
Match.tryout_id == tryout.id,
|
||||
Match.status == 'scheduled'
|
||||
# Get all tryouts the player is registered for (not just the 5 most recent)
|
||||
all_registrations = TryoutRegistration.query.filter_by(player_id=user.id).all()
|
||||
registered_tryout_ids = [r.tryout_id for r in all_registrations]
|
||||
|
||||
# Get all matches where player is a participant (any match type)
|
||||
player_participant_matches = MatchParticipant.query.filter_by(player_id=user.id).all()
|
||||
player_match_ids = [p.match_id for p in player_participant_matches]
|
||||
|
||||
# Get all team memberships for this player
|
||||
player_team_memberships = TeamMember.query.filter_by(player_id=user.id).all()
|
||||
player_team_ids = [tm.team_id for tm in player_team_memberships]
|
||||
|
||||
# Find all scheduled matches in registered tryouts
|
||||
upcoming_matches = Match.query.filter(
|
||||
Match.tryout_id.in_(registered_tryout_ids),
|
||||
Match.status == 'scheduled',
|
||||
Match.date >= today
|
||||
).order_by(Match.date, Match.start_time).all()
|
||||
|
||||
for match in tryout_matches:
|
||||
# Check if player is in this match
|
||||
for match in upcoming_matches:
|
||||
is_participant = False
|
||||
team = None
|
||||
|
||||
if match.match_type == 'team_vs_team':
|
||||
# Check if player is on team1 or team2
|
||||
if match.team1_id in [t.id for t in player_teams] or match.team2_id in [t.id for t in player_teams]:
|
||||
# Check if player is on either team
|
||||
if match.team1_id in player_team_ids:
|
||||
is_participant = True
|
||||
team = next((tm for tm in player_team_memberships if tm.team_id == match.team1_id), None)
|
||||
elif match.team2_id in player_team_ids:
|
||||
is_participant = True
|
||||
team = next((tm for tm in player_team_memberships if tm.team_id == match.team2_id), None)
|
||||
else:
|
||||
# Check if player is in match participants
|
||||
participant = MatchParticipant.query.filter_by(
|
||||
match_id=match.id,
|
||||
player_id=user.id
|
||||
).first()
|
||||
if participant:
|
||||
# For player_vs_player and player_scrim, check MatchParticipant
|
||||
if match.id in player_match_ids:
|
||||
is_participant = True
|
||||
|
||||
if is_participant:
|
||||
# Check if match is upcoming
|
||||
match_date = match.date
|
||||
if match_date >= today:
|
||||
tryout = match.tryout
|
||||
next_matches.append({
|
||||
'tryout': tryout,
|
||||
'match': match,
|
||||
'team': player_teams[0] if player_teams else None
|
||||
'team': team.team if team else None
|
||||
})
|
||||
break # Only get the next match per tryout
|
||||
|
||||
stats['next_matches'] = next_matches
|
||||
|
||||
|
||||
+7
-7
@@ -309,13 +309,13 @@ def create_match(tryout_id):
|
||||
# Start time is now mandatory
|
||||
if not start_time_str:
|
||||
flash('Start time is required. Please select a time slot.', 'danger')
|
||||
return render_template('pages/create_match.html', tryout=tryout, teams=teams, all_players=all_players)
|
||||
return render_template('pages/match_form.html', tryout=tryout, teams=teams, all_players=all_players)
|
||||
|
||||
try:
|
||||
date_obj = datetime.strptime(date_str, '%Y-%m-%d').date() if date_str else tryout.date
|
||||
except (ValueError, TypeError):
|
||||
flash('Invalid date format.', 'danger')
|
||||
return render_template('pages/create_match.html', tryout=tryout, teams=teams, all_players=all_players)
|
||||
return render_template('pages/match_form.html', tryout=tryout, teams=teams, all_players=all_players)
|
||||
|
||||
start_time = None
|
||||
end_time = None
|
||||
@@ -331,7 +331,7 @@ def create_match(tryout_id):
|
||||
end_time = end_dt.time()
|
||||
except ValueError:
|
||||
flash('Invalid time format.', 'danger')
|
||||
return render_template('pages/create_match.html', tryout=tryout, teams=teams, all_players=all_players)
|
||||
return render_template('pages/match_form.html', tryout=tryout, teams=teams, all_players=all_players)
|
||||
|
||||
match = Match(
|
||||
tryout_id=tryout_id,
|
||||
@@ -425,7 +425,7 @@ def create_match(tryout_id):
|
||||
flash('Match scheduled successfully!', 'success')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id))
|
||||
|
||||
return render_template('pages/create_match.html', tryout=tryout, teams=teams, all_players=all_players)
|
||||
return render_template('pages/match_form.html', tryout=tryout, teams=teams, all_players=all_players)
|
||||
|
||||
|
||||
@matches_bp.route('/<int:match_id>/edit', methods=['GET', 'POST'])
|
||||
@@ -472,12 +472,12 @@ def edit_match(match_id):
|
||||
match.date = datetime.strptime(date_str, '%Y-%m-%d').date()
|
||||
except (ValueError, TypeError):
|
||||
flash('Invalid date format.', 'danger')
|
||||
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/match_form.html', match=match, tryout=tryout, teams=teams, all_players=all_players, current_player_ids=current_player_ids)
|
||||
|
||||
# Start time is now mandatory
|
||||
if not start_time_str:
|
||||
flash('Start time is required. Please select a time slot.', 'danger')
|
||||
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/match_form.html', match=match, tryout=tryout, teams=teams, all_players=all_players, current_player_ids=current_player_ids)
|
||||
|
||||
try:
|
||||
match.start_time = datetime.strptime(start_time_str, '%H:%M').time()
|
||||
@@ -578,7 +578,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, team1_player_ids=team1_player_ids, team2_player_ids=team2_player_ids)
|
||||
return render_template('pages/match_form.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('/<int:match_id>/delete', methods=['POST'])
|
||||
|
||||
+17
-5
@@ -73,6 +73,8 @@ def create_tryout():
|
||||
return redirect(url_for('tryouts.list_tryouts'))
|
||||
|
||||
org_teams = OrgTeam.query.order_by(OrgTeam.name).all()
|
||||
managers = User.query.filter_by(role='manager', is_active_account=True).order_by(User.full_name).all()
|
||||
coaches = User.query.filter_by(role='coach', is_active_account=True).order_by(User.full_name).all()
|
||||
|
||||
if request.method == 'POST':
|
||||
title = request.form.get('title')
|
||||
@@ -82,12 +84,14 @@ def create_tryout():
|
||||
location = request.form.get('location')
|
||||
max_players = request.form.get('max_players')
|
||||
target_org_team_id = request.form.get('target_org_team_id')
|
||||
manager_id = request.form.get('manager_id')
|
||||
coach_id = request.form.get('coach_id')
|
||||
|
||||
try:
|
||||
date_obj = datetime.strptime(date_str, '%Y-%m-%d').date()
|
||||
except (ValueError, TypeError):
|
||||
flash('Invalid date format.', 'danger')
|
||||
return render_template('pages/create_tryout.html', org_teams=org_teams)
|
||||
return render_template('pages/tryout_form.html', tryout=None, org_teams=org_teams, managers=managers, coaches=coaches, esport_games=ESPORT_GAMES)
|
||||
|
||||
tryout = Tryout(
|
||||
title=title,
|
||||
@@ -98,14 +102,16 @@ def create_tryout():
|
||||
max_players=int(max_players) if max_players else None,
|
||||
created_by=current_user.id,
|
||||
status='upcoming',
|
||||
target_org_team_id=int(target_org_team_id) if target_org_team_id else None
|
||||
target_org_team_id=int(target_org_team_id) if target_org_team_id else None,
|
||||
manager_id=int(manager_id) if manager_id else None,
|
||||
coach_id=int(coach_id) if coach_id else None
|
||||
)
|
||||
db.session.add(tryout)
|
||||
db.session.commit()
|
||||
flash('Tryout created successfully!', 'success')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout.id))
|
||||
|
||||
return render_template('pages/create_tryout.html', org_teams=org_teams, esport_games=ESPORT_GAMES)
|
||||
return render_template('pages/tryout_form.html', tryout=None, org_teams=org_teams, managers=managers, coaches=coaches, esport_games=ESPORT_GAMES)
|
||||
|
||||
|
||||
@tryouts_bp.route('/<int:tryout_id>/edit', methods=['GET', 'POST'])
|
||||
@@ -132,6 +138,8 @@ def edit_tryout(tryout_id):
|
||||
return redirect(url_for('tryouts.list_tryouts'))
|
||||
|
||||
org_teams = OrgTeam.query.order_by(OrgTeam.name).all()
|
||||
managers = User.query.filter_by(role='manager', is_active_account=True).order_by(User.full_name).all()
|
||||
coaches = User.query.filter_by(role='coach', is_active_account=True).order_by(User.full_name).all()
|
||||
|
||||
if request.method == 'POST':
|
||||
title = request.form.get('title')
|
||||
@@ -141,12 +149,14 @@ def edit_tryout(tryout_id):
|
||||
location = request.form.get('location')
|
||||
max_players = request.form.get('max_players')
|
||||
target_org_team_id = request.form.get('target_org_team_id')
|
||||
manager_id = request.form.get('manager_id')
|
||||
coach_id = request.form.get('coach_id')
|
||||
|
||||
try:
|
||||
date_obj = datetime.strptime(date_str, '%Y-%m-%d').date()
|
||||
except (ValueError, TypeError):
|
||||
flash('Invalid date format.', 'danger')
|
||||
return render_template('pages/edit_tryout.html', tryout=tryout, org_teams=org_teams, esport_games=ESPORT_GAMES)
|
||||
return render_template('pages/tryout_form.html', tryout=tryout, org_teams=org_teams, managers=managers, coaches=coaches, esport_games=ESPORT_GAMES)
|
||||
|
||||
tryout.title = title
|
||||
tryout.description = description
|
||||
@@ -155,11 +165,13 @@ def edit_tryout(tryout_id):
|
||||
tryout.location = location
|
||||
tryout.max_players = int(max_players) if max_players else None
|
||||
tryout.target_org_team_id = int(target_org_team_id) if target_org_team_id else None
|
||||
tryout.manager_id = int(manager_id) if manager_id else None
|
||||
tryout.coach_id = int(coach_id) if coach_id else None
|
||||
db.session.commit()
|
||||
flash('Tryout updated successfully!', 'success')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout.id))
|
||||
|
||||
return render_template('pages/edit_tryout.html', tryout=tryout, org_teams=org_teams, esport_games=ESPORT_GAMES)
|
||||
return render_template('pages/tryout_form.html', tryout=tryout, org_teams=org_teams, managers=managers, coaches=coaches, esport_games=ESPORT_GAMES)
|
||||
|
||||
|
||||
@tryouts_bp.route('/<int:tryout_id>')
|
||||
|
||||
+9
-6
@@ -1433,12 +1433,13 @@ def add_personal_note():
|
||||
flash('Personal note added successfully!', 'success')
|
||||
return redirect(url_for('users.notes_dashboard'))
|
||||
|
||||
return render_template('pages/add_personal_note.html',
|
||||
return render_template('pages/add_note.html',
|
||||
players=players,
|
||||
matches=matches,
|
||||
tryouts=tryouts,
|
||||
teams=teams,
|
||||
org_team=org_team)
|
||||
org_team=org_team,
|
||||
context_type='general')
|
||||
|
||||
|
||||
@users_bp.route('/match/<int:match_id>/add-note', methods=['GET', 'POST'])
|
||||
@@ -1509,11 +1510,12 @@ def add_note_from_match(match_id):
|
||||
flash('Personal note added successfully!', 'success')
|
||||
return redirect(url_for('tryouts.view_tryout', tryout_id=tryout.id))
|
||||
|
||||
return render_template('pages/add_note_from_match.html',
|
||||
return render_template('pages/add_note.html',
|
||||
match=match,
|
||||
tryout=tryout,
|
||||
players=players,
|
||||
team_notes=team_notes)
|
||||
team_notes=team_notes,
|
||||
context_type='match')
|
||||
|
||||
|
||||
@users_bp.route('/tryout/<int:tryout_id>/add-note', methods=['GET', 'POST'])
|
||||
@@ -1583,8 +1585,9 @@ def add_note_from_tryout(tryout_id):
|
||||
# Allow pre-selecting a player via query parameter
|
||||
preselected_player_id = request.args.get('player_id', type=int)
|
||||
|
||||
return render_template('pages/add_note_from_tryout.html',
|
||||
return render_template('pages/add_note.html',
|
||||
tryout=tryout,
|
||||
players=players,
|
||||
team_notes=team_notes,
|
||||
preselected_player_id=preselected_player_id)
|
||||
preselected_player_id=preselected_player_id,
|
||||
context_type='tryout')
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}Add Personal Note - TryoutPro{% endblock %}
|
||||
{% block page_title %}Add Personal Note{% endblock %}
|
||||
{% block breadcrumb %}
|
||||
<span class="breadcrumb">
|
||||
Home / <a href="{{ url_for('tryouts.list_tryouts') }}">Tryouts</a>
|
||||
{% if context_type == 'tryout' %}
|
||||
/ <a href="{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}">{{ tryout.title }}</a>
|
||||
/ Add Note
|
||||
{% elif context_type == 'match' %}
|
||||
/ <a href="{{ url_for('tryouts.view_tryout', tryout_id=tryout.tryout.id) }}">{{ tryout.tryout.title }}</a>
|
||||
/ Add Note
|
||||
{% else %}
|
||||
/ Add Note
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-sticky-note"></i> Add Personal Note</h3>
|
||||
{% if context_type == 'tryout' %}
|
||||
<p class="text-muted small">Context: <strong>Tryout - {{ tryout.title }}</strong></p>
|
||||
{% elif context_type == 'match' %}
|
||||
<p class="text-muted small">Context: <strong>Match - {{ match.title }}</strong></p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="
|
||||
{% if context_type == 'tryout' %}
|
||||
{{ url_for('users.add_note_from_tryout', tryout_id=tryout.id) }}
|
||||
{% elif context_type == 'match' %}
|
||||
{{ url_for('users.add_note_from_match', match_id=match.id) }}
|
||||
{% else %}
|
||||
{{ url_for('users.add_personal_note') }}
|
||||
{% endif %}
|
||||
" class="form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
{% if context_type == 'tryout' %}
|
||||
<input type="hidden" name="tryout_id" value="{{ tryout.id }}">
|
||||
{% elif context_type == 'match' %}
|
||||
<input type="hidden" name="match_id" value="{{ match.id }}">
|
||||
{% endif %}
|
||||
|
||||
<div class="form-group">
|
||||
<label for="player_id">Player</label>
|
||||
<select name="player_id" id="player_id" class="form-select" required>
|
||||
<option value="">-- Select a player --</option>
|
||||
{% for player in players %}
|
||||
<option value="{{ player.id }}" {% if preselected_player_id == player.id %}selected{% endif %}>
|
||||
{{ player.full_name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="content">Note Content</label>
|
||||
<textarea name="content" id="content" rows="6" class="form-textarea" placeholder="Enter your coaching notes..." required></textarea>
|
||||
</div>
|
||||
|
||||
{% if context_type != 'tryout' and context_type != 'match' %}
|
||||
<div class="form-group">
|
||||
<label for="tryout_id">Link to Tryout (Optional)</label>
|
||||
<select name="tryout_id" id="tryout_id" class="form-select">
|
||||
<option value="">-- No tryout --</option>
|
||||
{% for t in tryouts %}
|
||||
<option value="{{ t.id }}">{{ t.title }} ({{ t.date.strftime('%Y-%m-%d') }})</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="match_id">Link to Match (Optional)</label>
|
||||
<select name="match_id" id="match_id" class="form-select">
|
||||
<option value="">-- No match --</option>
|
||||
{% for m in matches %}
|
||||
<option value="{{ m.id }}">{{ m.title }} ({{ m.date.strftime('%Y-%m-%d') }})</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="team_id">Link to Team (Optional)</label>
|
||||
<select name="team_id" id="team_id" class="form-select">
|
||||
<option value="">-- No team --</option>
|
||||
{% for team in teams %}
|
||||
<option value="{{ team.id }}">{{ team.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if team_notes %}
|
||||
<div class="form-group">
|
||||
<label>Team Notes (Reference)</label>
|
||||
<div class="team-notes-reference">
|
||||
{% for note in team_notes %}
|
||||
<div class="note-reference-item">
|
||||
<small class="text-muted">{{ note.created_at.strftime('%Y-%m-%d') }} - {{ note.coach.full_name }}:</small>
|
||||
<p>{{ note.content[:200] }}{% if note.content|length > 200 %}...{% endif %}</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="
|
||||
{% if context_type == 'tryout' %}
|
||||
{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}
|
||||
{% elif context_type == 'match' %}
|
||||
{{ url_for('tryouts.view_tryout', tryout_id=tryout.tryout.id) }}
|
||||
{% else %}
|
||||
{{ url_for('users.notes_dashboard') }}
|
||||
{% endif %}
|
||||
" class="btn btn-secondary">Cancel</a>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i> Add Note
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block styles %}
|
||||
<style>
|
||||
.team-notes-reference {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
.note-reference-item {
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
.note-reference-item:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.note-reference-item p {
|
||||
margin: 5px 0 0 0;
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
@@ -1,63 +0,0 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}Add Note from Match - TryoutPro{% endblock %}
|
||||
{% block page_title %}Add Note from Match{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('tryouts.list_tryouts') }}">Tryouts</a> / <a href="{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}">{{ tryout.title }}</a> / Add Note</span>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-futbol"></i> Add Note for Match: {{ match.title }}</h3>
|
||||
<span class="badge badge-info">{{ match.date.strftime('%m/%d/%Y') }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ url_for('users.add_note_from_match', match_id=match.id) }}" class="form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="player_id">Select Player</label>
|
||||
<select name="player_id" id="player_id" class="form-select" required>
|
||||
<option value="">-- Select a Player --</option>
|
||||
{% for player in players %}
|
||||
<option value="{{ player.id }}">{{ player.full_name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="content">Note Content</label>
|
||||
<textarea name="content" id="content" class="form-textarea" rows="4" placeholder="Enter feedback or coaching tips for this player from the match..." required></textarea>
|
||||
<p class="form-text">This note will be linked to this match and visible to the selected player.</p>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i> Add Note
|
||||
</button>
|
||||
<a href="{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Tryout
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if team_notes %}
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-users"></i> Team Notes Reference</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="detail-grid">
|
||||
{% for note in team_notes %}
|
||||
<div class="detail-item full-width mb-3">
|
||||
<span class="detail-label">
|
||||
<i class="fas fa-user"></i> {{ note.coach.full_name if note.coach else 'Unknown Coach' }}
|
||||
</span>
|
||||
<span class="detail-value">{{ note.content | nl2br }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -1,63 +0,0 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}Add Note from Tryout - TryoutPro{% endblock %}
|
||||
{% block page_title %}Add Note from Tryout{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('tryouts.list_tryouts') }}">Tryouts</a> / <a href="{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}">{{ tryout.title }}</a> / Add Note</span>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-calendar-alt"></i> Add Note for Tryout: {{ tryout.title }}</h3>
|
||||
<span class="badge badge-success">{{ tryout.date.strftime('%m/%d/%Y') }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ url_for('users.add_note_from_tryout', tryout_id=tryout.id) }}" class="form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="player_id">Select Player</label>
|
||||
<select name="player_id" id="player_id" class="form-select" required>
|
||||
<option value="">-- Select a Player --</option>
|
||||
{% for player in players %}
|
||||
<option value="{{ player.id }}" {% if preselected_player_id == player.id %}selected{% endif %}>{{ player.full_name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="content">Note Content</label>
|
||||
<textarea name="content" id="content" class="form-textarea" rows="4" placeholder="Enter feedback or coaching tips for this player from the tryout..." required></textarea>
|
||||
<p class="form-text">This note will be linked to this tryout and visible to the selected player.</p>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i> Add Note
|
||||
</button>
|
||||
<a href="{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Tryout
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if team_notes %}
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-users"></i> Team Notes Reference</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="detail-grid">
|
||||
{% for note in team_notes %}
|
||||
<div class="detail-item full-width mb-3">
|
||||
<span class="detail-label">
|
||||
<i class="fas fa-user"></i> {{ note.coach.full_name if note.coach else 'Unknown Coach' }}
|
||||
</span>
|
||||
<span class="detail-value">{{ note.content | nl2br }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -1,80 +0,0 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}Add Note - TryoutPro{% endblock %}
|
||||
{% block page_title %}Add Personal Note{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('users.my_notes') }}">My Notes</a> / Add Note</span>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-sticky-note"></i> Add Personal Note</h3>
|
||||
{% if org_team %}
|
||||
<span class="badge badge-esport">{{ org_team.name }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ url_for('users.add_personal_note') }}" class="form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="player_id">Select Player</label>
|
||||
<select name="player_id" id="player_id" class="form-select" required>
|
||||
<option value="">-- Select a Player --</option>
|
||||
{% for player in players %}
|
||||
<option value="{{ player.id }}">{{ player.full_name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="content">Note Content</label>
|
||||
<textarea name="content" id="content" class="form-textarea" rows="4" placeholder="Enter personal feedback or coaching tips for this player..." required></textarea>
|
||||
<p class="form-text">These notes will only be visible to the selected player.</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="context">Context (Optional)</label>
|
||||
<p class="form-text text-muted">Link this note to a specific match, tryout, or team for better organization.</p>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="match_id">Match</label>
|
||||
<select name="match_id" id="match_id" class="form-select">
|
||||
<option value="">-- Select Match --</option>
|
||||
{% for match in matches %}
|
||||
<option value="{{ match.id }}">{{ match.title }} - {{ match.date.strftime('%m/%d/%Y') }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="tryout_id">Tryout</label>
|
||||
<select name="tryout_id" id="tryout_id" class="form-select">
|
||||
<option value="">-- Select Tryout --</option>
|
||||
{% for tryout in tryouts %}
|
||||
<option value="{{ tryout.id }}">{{ tryout.title }} - {{ tryout.date.strftime('%m/%d/%Y') }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="team_id">Team</label>
|
||||
<select name="team_id" id="team_id" class="form-select">
|
||||
<option value="">-- Select Team --</option>
|
||||
{% for team in teams %}
|
||||
<option value="{{ team.id }}">{{ team.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i> Add Note
|
||||
</button>
|
||||
<a href="{{ url_for('users.my_notes') }}" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Notes
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,848 +0,0 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}Schedule Match - {{ tryout.title }} - TryoutPro{% endblock %}
|
||||
{% block page_title %}Schedule Match{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('tryouts.list_tryouts') }}">Tryouts</a> / <a href="{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}">{{ tryout.title }}</a> / Schedule Match</span>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-futbol"></i> Schedule Match for {{ tryout.title }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ url_for('matches.create_match', tryout_id=tryout.id) }}" class="form" id="createMatchForm">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="match_type">Match Type</label>
|
||||
<select name="match_type" id="match_type" class="form-select" onchange="toggleMatchType()" required>
|
||||
<option value="team_vs_team">Team vs Team</option>
|
||||
<option value="player_vs_player">Player vs Player</option>
|
||||
<option value="player_scrim">Player Scrim</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="title">Match Title</label>
|
||||
<input type="text" name="title" id="title" class="form-input" placeholder="e.g., Alpha vs Bravo Scrimmage" required>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="date">Date</label>
|
||||
<input type="date" name="date" id="date" class="form-input" value="{{ tryout.date.strftime('%Y-%m-%d') }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="location">Location</label>
|
||||
<input type="text" name="location" id="location" class="form-input" placeholder="Match location">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Merged Availability Grid -->
|
||||
{% if current_user.can_manage_teams() or current_user.can_schedule_matches() %}
|
||||
<hr class="section-divider">
|
||||
<h4 class="section-title"><i class="fas fa-clock"></i> Select Match Time</h4>
|
||||
<p class="text-muted small">Click time slots consecutively to set match duration. Players available in all selected time blocks will be preselected.</p>
|
||||
|
||||
<div id="merged-disponibility-selected" class="merged-disponibility-selected-display hidden">
|
||||
<span>Selected time:</span>
|
||||
<span class="selected-time-badge" id="selected-time-badge"></span>
|
||||
<button type="button" class="btn btn-sm btn-outline" onclick="clearTimeSelection()">Change</button>
|
||||
</div>
|
||||
|
||||
<div id="merged-disponibility-grid" class="merged-disponibility-grid">
|
||||
<p class="text-muted">Loading availability...</p>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="start_time">Start Time <span class="text-muted">(Required)</span></label>
|
||||
<input type="time" name="start_time" id="start_time" class="form-input" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="end_time">End Time <span class="text-muted">(Set by clicking consecutive slots)</span></label>
|
||||
<input type="time" name="end_time" id="end_time" class="form-input" required>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<!-- Fallback time inputs for users without disponibility access -->
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="start_time">Start Time</label>
|
||||
<input type="time" name="start_time" id="start_time" class="form-input">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="end_time">End Time</label>
|
||||
<input type="time" name="end_time" id="end_time" class="form-input">
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="form-group">
|
||||
<label for="description">Description</label>
|
||||
<textarea name="description" id="description" class="form-textarea" placeholder="Optional notes about this match"></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Team vs Team Selection -->
|
||||
<div id="team-vs-team-section">
|
||||
<hr class="section-divider">
|
||||
<h4 class="section-title"><i class="fas fa-users"></i> Select Teams</h4>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="team1_id">Team 1</label>
|
||||
<select name="team1_id" id="team1_id" class="form-select">
|
||||
<option value="">-- Select Team 1 --</option>
|
||||
{% for team in teams %}
|
||||
<option value="{{ team.id }}">{{ team.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="team2_id">Team 2</label>
|
||||
<select name="team2_id" id="team2_id" class="form-select">
|
||||
<option value="">-- Select Team 2 --</option>
|
||||
{% for team in teams %}
|
||||
<option value="{{ team.id }}">{{ team.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Player vs Player Selection -->
|
||||
<div id="player-vs-player-section" class="hidden">
|
||||
<hr class="section-divider">
|
||||
<h4 class="section-title"><i class="fas fa-user-friends"></i> Select Players</h4>
|
||||
|
||||
<!-- Randomize Teams Section -->
|
||||
<div class="randomize-section">
|
||||
<div class="randomize-controls">
|
||||
<label><i class="fas fa-random"></i> Randomize Teams:</label>
|
||||
<input type="number" id="team1-size" class="randomize-input" min="1" value="1" onchange="updateRandomizePreview()">
|
||||
<span>vs</span>
|
||||
<span id="team2-size-preview" class="randomize-preview">0</span>
|
||||
<button type="button" class="btn btn-sm randomize-btn" onclick="randomizeTeams()">
|
||||
<i class="fas fa-random"></i> Randomize
|
||||
</button>
|
||||
</div>
|
||||
<p class="text-muted small" id="randomize-hint">Select players then click Randomize to split them into teams.</p>
|
||||
</div>
|
||||
|
||||
<div class="pvp-layout">
|
||||
<div class="team-column">
|
||||
<h5 class="team-header">Team 1</h5>
|
||||
<div id="team1-selection" class="team-selection"></div>
|
||||
</div>
|
||||
|
||||
<div class="player-pool">
|
||||
<div class="player-pool-header">Available Players</div>
|
||||
<div id="available-players-pool" class="available-players-pool">
|
||||
<p class="text-muted small">All registered players are shown. Click time slots to filter available players.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="team-column">
|
||||
<h5 class="team-header">Team 2</h5>
|
||||
<div id="team2-selection" class="team-selection"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="team1_player_ids" id="team1-player-ids-input">
|
||||
<input type="hidden" name="team2_player_ids" id="team2-player-ids-input">
|
||||
</div>
|
||||
|
||||
<!-- Player Scrim Selection -->
|
||||
<div id="player-scrim-section" class="hidden">
|
||||
<hr class="section-divider">
|
||||
<h4 class="section-title"><i class="fas fa-users"></i> Select Players</h4>
|
||||
|
||||
<p class="text-muted small"><i class="fas fa-info-circle"></i> Green indicators show player availability for the match date/time</p>
|
||||
|
||||
<div class="checkbox-grid" id="scrim-players-list">
|
||||
{% for player in all_players %}
|
||||
<label class="checkbox-label player-checkbox" data-player-id="{{ player.id }}">
|
||||
<input type="checkbox" name="player_ids" value="{{ player.id }}">
|
||||
{{ player.full_name }}
|
||||
<span class="disponibility-indicator" data-player-id="{{ player.id }}"></span>
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i> Schedule Match
|
||||
</button>
|
||||
<a href="{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}" class="btn btn-secondary">
|
||||
<i class="fas fa-times"></i> Cancel
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<style>
|
||||
.pvp-layout {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.team-column {
|
||||
flex: 1;
|
||||
min-width: 150px;
|
||||
}
|
||||
.team-header {
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.team-selection {
|
||||
min-height: 200px;
|
||||
border: 2px dashed var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
}
|
||||
.team-selection .player-item {
|
||||
padding: 8px 12px;
|
||||
margin: 5px 0;
|
||||
background: var(--bg-tertiary);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
.team-selection .player-item:hover {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
.player-pool {
|
||||
flex: 2;
|
||||
min-width: 200px;
|
||||
}
|
||||
.player-pool-header {
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.available-players-pool {
|
||||
min-height: 200px;
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.available-players-pool .player-item {
|
||||
padding: 8px 12px;
|
||||
margin: 5px 0;
|
||||
background: var(--success-light);
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
.available-players-pool .player-item:hover {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
.available-players-pool .player-item.available {
|
||||
background: var(--success-light);
|
||||
border-color: var(--success);
|
||||
}
|
||||
.available-players-pool .player-item.unavailable {
|
||||
background: var(--gray-100);
|
||||
border-color: var(--gray-300);
|
||||
opacity: 0.6;
|
||||
}
|
||||
.player-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.player-item .remove-btn {
|
||||
opacity: 0.5;
|
||||
cursor: pointer;
|
||||
}
|
||||
.player-item .remove-btn:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
.player-actions {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
.player-actions .btn {
|
||||
padding: 4px 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.player-name {
|
||||
flex: 1;
|
||||
}
|
||||
.team-selection .player-actions {
|
||||
display: none;
|
||||
}
|
||||
.team-selection .player-item:hover .remove-btn {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.randomize-section {
|
||||
margin: 15px 0;
|
||||
padding: 10px;
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 8px;
|
||||
}
|
||||
.randomize-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.randomize-controls label {
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
.randomize-input {
|
||||
width: 60px;
|
||||
padding: 4px 8px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.randomize-preview {
|
||||
font-weight: bold;
|
||||
min-width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.randomize-btn {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
.randomize-btn:hover {
|
||||
background: var(--primary-dark);
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
// Player data as simple JS object (id -> full_name)
|
||||
var playerDataById = {
|
||||
player_data: {
|
||||
{%- for p in all_players %}
|
||||
{{ p.id }}: "{{ p.full_name | escape }}",
|
||||
{%- endfor %}
|
||||
}
|
||||
};
|
||||
|
||||
// All registered player IDs
|
||||
var allRegisteredPlayers = [
|
||||
{%- for p in all_players %}
|
||||
{{ p.id }},
|
||||
{%- endfor %}
|
||||
];
|
||||
|
||||
// Time slots from 12pm (12:00) to 12am (24:00)
|
||||
var TIME_SLOTS = [];
|
||||
for (var h = 12; h <= 24; h++) {
|
||||
for (var m = 0; m < 60; m += 30) {
|
||||
if (h === 24 && m > 0) continue;
|
||||
var displayHour;
|
||||
var displayAmpm;
|
||||
if (h === 24) {
|
||||
displayHour = 12;
|
||||
displayAmpm = 'AM';
|
||||
} else if (h > 12) {
|
||||
displayHour = h - 12;
|
||||
displayAmpm = 'PM';
|
||||
} else {
|
||||
displayHour = h;
|
||||
displayAmpm = 'PM';
|
||||
}
|
||||
var timeStr = (h < 10 ? '0' : '') + h + ':' + (m < 10 ? '0' : '') + m;
|
||||
var displayTime = displayHour + ':' + (m < 10 ? '0' : '') + m + ' ' + displayAmpm;
|
||||
TIME_SLOTS.push({ time: timeStr, display: displayTime });
|
||||
}
|
||||
}
|
||||
|
||||
var DAYS = [
|
||||
{ value: 0, name: 'Monday' },
|
||||
{ value: 1, name: 'Tuesday' },
|
||||
{ value: 2, name: 'Wednesday' },
|
||||
{ value: 3, name: 'Thursday' },
|
||||
{ value: 4, name: 'Friday' },
|
||||
{ value: 5, name: 'Saturday' },
|
||||
{ value: 6, name: 'Sunday' }
|
||||
];
|
||||
|
||||
var selectedDate = '';
|
||||
var selectedSlots = [];
|
||||
var allDisponibilities = {};
|
||||
var canViewDisponibilities = {% if current_user.can_manage_teams() or current_user.can_schedule_matches() %}true{% else %}false{% endif %};
|
||||
var totalPlayers = {{ all_players|length }};
|
||||
var availablePlayersForSlots = []; // Players available in ALL selected slots
|
||||
|
||||
// Initialize
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
{% if current_user.can_manage_teams() or current_user.can_schedule_matches() %}
|
||||
fetchDisponibilities();
|
||||
{% endif %}
|
||||
|
||||
document.getElementById('date').addEventListener('change', function() {
|
||||
selectedDate = this.value;
|
||||
if (allDisponibilitiesInitialized()) {
|
||||
renderMergedDisponibilityGrid();
|
||||
}
|
||||
});
|
||||
|
||||
selectedDate = document.getElementById('date').value;
|
||||
toggleMatchType();
|
||||
// Show all players initially
|
||||
updatePlayerPool();
|
||||
});
|
||||
|
||||
function allDisponibilitiesInitialized() {
|
||||
return Object.keys(allDisponibilities).length > 0;
|
||||
}
|
||||
|
||||
function fetchDisponibilities() {
|
||||
if (!canViewDisponibilities) return;
|
||||
fetch('{{ url_for("users.get_disponibilities") }}')
|
||||
.then(function(response) { return response.json(); })
|
||||
.then(function(data) {
|
||||
allDisponibilities = data;
|
||||
renderMergedDisponibilityGrid();
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error('Error fetching disponibilities:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function renderMergedDisponibilityGrid() {
|
||||
var grid = document.getElementById('merged-disponibility-grid');
|
||||
if (!grid || !canViewDisponibilities) return;
|
||||
|
||||
var dateParts = selectedDate.split('-');
|
||||
var dateObj = new Date(dateParts[0], dateParts[1] - 1, dateParts[2]);
|
||||
var jsDay = dateObj.getDay();
|
||||
var dayOfWeek = jsDay === 0 ? 6 : jsDay - 1;
|
||||
|
||||
var html = '<div style="margin-bottom: 10px;"><strong>Click time slots consecutively to set match duration</strong></div>';
|
||||
|
||||
DAYS.forEach(function(day) {
|
||||
if (day.value !== dayOfWeek) return;
|
||||
|
||||
var dayRow = '<div class="merged-disponibility-day-row">';
|
||||
dayRow += '<div class="merged-disponibility-day-label">' + day.name + '</div>';
|
||||
dayRow += '<div class="merged-disponibility-time-blocks">';
|
||||
|
||||
TIME_SLOTS.forEach(function(slot) {
|
||||
var count = getAvailabilityCount(day.value, slot.time);
|
||||
var percentage = totalPlayers > 0 ? count / totalPlayers : 0;
|
||||
var cssClass = 'merged-disponibility-time-block';
|
||||
|
||||
if (percentage >= 0.75) {
|
||||
cssClass += ' high-availability';
|
||||
} else if (percentage >= 0.5) {
|
||||
cssClass += ' medium-availability';
|
||||
} else {
|
||||
cssClass += ' low-availability';
|
||||
}
|
||||
|
||||
if (selectedSlots.includes(slot.time)) {
|
||||
cssClass += ' selected';
|
||||
}
|
||||
|
||||
dayRow += '<div class="' + cssClass + '" data-day="' + day.value + '" data-time="' + slot.time + '" ' +
|
||||
'onclick="toggleTimeSlot(' + day.value + ', \'' + slot.time + '\', this)">' +
|
||||
slot.display +
|
||||
'<span class="merged-disponibility-count">' + count + '</span>' +
|
||||
'</div>';
|
||||
});
|
||||
|
||||
dayRow += '</div></div>';
|
||||
html += dayRow;
|
||||
});
|
||||
|
||||
grid.innerHTML = html;
|
||||
}
|
||||
|
||||
function getAvailabilityCount(dayOfWeek, timeStr) {
|
||||
var count = 0;
|
||||
var timeParts = timeStr.split(':');
|
||||
var minutes = parseInt(timeParts[0]) * 60 + parseInt(timeParts[1]);
|
||||
|
||||
for (var playerId in allDisponibilities) {
|
||||
var playerData = allDisponibilities[playerId];
|
||||
if (playerData && playerData.disponibilities) {
|
||||
var isAvailable = playerData.disponibilities.some(function(d) {
|
||||
if (d.day_of_week !== dayOfWeek) return false;
|
||||
var startParts = d.start_time.split(':');
|
||||
var endParts = d.end_time.split(':');
|
||||
var dispMinutes = parseInt(startParts[0]) * 60 + parseInt(startParts[1]);
|
||||
var endMinutes = parseInt(endParts[0]) * 60 + parseInt(endParts[1]);
|
||||
|
||||
return minutes >= dispMinutes && minutes < endMinutes;
|
||||
});
|
||||
if (isAvailable) count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
function getSlotsForPlayer(playerId, dayOfWeek) {
|
||||
var playerData = allDisponibilities[playerId];
|
||||
if (!playerData || !playerData.disponibilities) return [];
|
||||
|
||||
var availableSlots = [];
|
||||
TIME_SLOTS.forEach(function(slot) {
|
||||
var timeParts = slot.time.split(':');
|
||||
var minutes = parseInt(timeParts[0]) * 60 + parseInt(timeParts[1]);
|
||||
|
||||
var isAvailable = playerData.disponibilities.some(function(d) {
|
||||
if (d.day_of_week !== dayOfWeek) return false;
|
||||
var startParts = d.start_time.split(':');
|
||||
var endParts = d.end_time.split(':');
|
||||
var dispMinutes = parseInt(startParts[0]) * 60 + parseInt(startParts[1]);
|
||||
var endMinutes = parseInt(endParts[0]) * 60 + parseInt(endParts[1]);
|
||||
|
||||
return minutes >= dispMinutes && minutes < endMinutes;
|
||||
});
|
||||
|
||||
if (isAvailable) {
|
||||
availableSlots.push(slot.time);
|
||||
}
|
||||
});
|
||||
|
||||
return availableSlots;
|
||||
}
|
||||
|
||||
function toggleTimeSlot(dayOfWeek, timeStr, element) {
|
||||
var slotIndex = TIME_SLOTS.findIndex(function(s) { return s.time === timeStr; });
|
||||
|
||||
if (selectedSlots.length === 0) {
|
||||
selectedSlots = [timeStr];
|
||||
} else {
|
||||
var firstSelectedIndex = TIME_SLOTS.findIndex(function(s) { return s.time === selectedSlots[0]; });
|
||||
|
||||
if (slotIndex === firstSelectedIndex) {
|
||||
selectedSlots = [timeStr];
|
||||
} else if (slotIndex < firstSelectedIndex) {
|
||||
var newSlots = [];
|
||||
for (var i = slotIndex; i <= firstSelectedIndex; i++) {
|
||||
newSlots.push(TIME_SLOTS[i].time);
|
||||
}
|
||||
selectedSlots = newSlots;
|
||||
} else if (slotIndex > firstSelectedIndex) {
|
||||
var newSlots = [];
|
||||
for (var i = firstSelectedIndex; i <= slotIndex; i++) {
|
||||
newSlots.push(TIME_SLOTS[i].time);
|
||||
}
|
||||
selectedSlots = newSlots;
|
||||
} else {
|
||||
selectedSlots = selectedSlots.filter(function(t) { return t !== timeStr; });
|
||||
}
|
||||
}
|
||||
|
||||
var startTime = selectedSlots[0] || '';
|
||||
var endTime = selectedSlots[selectedSlots.length - 1] || '';
|
||||
|
||||
if (endTime) {
|
||||
var timeParts = endTime.split(':');
|
||||
var endHour = parseInt(timeParts[0]);
|
||||
var endMin = parseInt(timeParts[1]) + 30;
|
||||
if (endMin >= 60) {
|
||||
endMin = 0;
|
||||
endHour++;
|
||||
}
|
||||
endTime = (endHour < 10 ? '0' : '') + endHour + ':' + (endMin < 10 ? '0' : '') + endMin;
|
||||
}
|
||||
|
||||
document.getElementById('start_time').value = startTime;
|
||||
document.getElementById('end_time').value = endTime;
|
||||
|
||||
if (selectedSlots.length > 0) {
|
||||
document.getElementById('merged-disponibility-selected').classList.remove('hidden');
|
||||
document.getElementById('selected-time-badge').textContent =
|
||||
formatTimeDisplay(selectedSlots[0]) + ' - ' + formatTimeDisplay(endTime);
|
||||
} else {
|
||||
document.getElementById('merged-disponibility-selected').classList.add('hidden');
|
||||
}
|
||||
|
||||
document.querySelectorAll('.merged-disponibility-time-block').forEach(function(el) {
|
||||
el.classList.remove('selected');
|
||||
});
|
||||
selectedSlots.forEach(function(slot) {
|
||||
var selectedEl = document.querySelector('.merged-disponibility-time-block[data-time="' + slot + '"]');
|
||||
if (selectedEl) selectedEl.classList.add('selected');
|
||||
});
|
||||
|
||||
if (selectedSlots.length > 0) {
|
||||
fetchAvailablePlayersForAllSlots();
|
||||
} else {
|
||||
availablePlayersForSlots = [];
|
||||
updatePlayerPool();
|
||||
}
|
||||
}
|
||||
|
||||
function formatTimeDisplay(timeStr) {
|
||||
var parts = timeStr.split(':');
|
||||
var h = parseInt(parts[0]);
|
||||
var m = parts[1];
|
||||
var displayHour;
|
||||
var ampm;
|
||||
if (h === 0) {
|
||||
displayHour = 12;
|
||||
ampm = 'AM';
|
||||
} else if (h < 12) {
|
||||
displayHour = h;
|
||||
ampm = 'AM';
|
||||
} else if (h === 12) {
|
||||
displayHour = 12;
|
||||
ampm = 'PM';
|
||||
} else {
|
||||
displayHour = h - 12;
|
||||
ampm = 'PM';
|
||||
}
|
||||
return displayHour + ':' + m + ' ' + ampm;
|
||||
}
|
||||
|
||||
function fetchAvailablePlayersForAllSlots() {
|
||||
if (!selectedDate || selectedSlots.length === 0) {
|
||||
availablePlayersForSlots = [];
|
||||
updatePlayerPool();
|
||||
return;
|
||||
}
|
||||
|
||||
var dateParts = selectedDate.split('-');
|
||||
var dateObj = new Date(dateParts[0], dateParts[1] - 1, dateParts[2]);
|
||||
var jsDay = dateObj.getDay();
|
||||
var dayOfWeek = jsDay === 0 ? 6 : jsDay - 1;
|
||||
|
||||
// Find players available in ALL selected slots
|
||||
availablePlayersForSlots = [];
|
||||
for (var playerId in allDisponibilities) {
|
||||
if (!allRegisteredPlayers.includes(parseInt(playerId))) continue;
|
||||
var playerSlots = getSlotsForPlayer(parseInt(playerId), dayOfWeek);
|
||||
var isAvailableInAll = selectedSlots.every(function(slot) {
|
||||
return playerSlots.includes(slot);
|
||||
});
|
||||
if (isAvailableInAll) {
|
||||
availablePlayersForSlots.push(parseInt(playerId));
|
||||
}
|
||||
}
|
||||
|
||||
// Preselect available players in Team 1
|
||||
preselectAvailablePlayers();
|
||||
}
|
||||
|
||||
function preselectAvailablePlayers() {
|
||||
// Just update the player pool - don't auto-assign to Team 1
|
||||
// Players will be shown in the pool and can be manually assigned to either team
|
||||
updatePlayerPool();
|
||||
}
|
||||
|
||||
function updatePlayerPool() {
|
||||
var pool = document.getElementById('available-players-pool');
|
||||
if (!pool) return;
|
||||
|
||||
var team1Ids = getSelectedTeamIds(1);
|
||||
var team2Ids = getSelectedTeamIds(2);
|
||||
var assignedIds = [...team1Ids, ...team2Ids];
|
||||
|
||||
// When time slots are selected, show only players available in all slots who aren't assigned yet
|
||||
// When no time slots selected, show all registered players
|
||||
var playersToShow = selectedSlots.length > 0 ? availablePlayersForSlots : allRegisteredPlayers;
|
||||
|
||||
if (playersToShow.length === 0) {
|
||||
pool.innerHTML = '<p class="text-muted small">No players available</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
var dateParts = selectedDate.split('-');
|
||||
var dateObj = new Date(dateParts[0], dateParts[1] - 1, dateParts[2]);
|
||||
var jsDay = dateObj.getDay();
|
||||
var dayOfWeek = jsDay === 0 ? 6 : jsDay - 1;
|
||||
|
||||
var html = '';
|
||||
playersToShow.forEach(function(pid) {
|
||||
if (assignedIds.includes(pid)) return;
|
||||
|
||||
var playerName = playerDataById.player_data[pid];
|
||||
if (!playerName) return;
|
||||
|
||||
// Check if player is available during selected time slots
|
||||
var isAvailable = true;
|
||||
if (selectedSlots.length > 0 && allDisponibilities[pid]) {
|
||||
var playerSlots = getSlotsForPlayer(pid, dayOfWeek);
|
||||
isAvailable = selectedSlots.every(function(slot) {
|
||||
return playerSlots.includes(slot);
|
||||
});
|
||||
}
|
||||
|
||||
var availabilityClass = isAvailable ? 'available' : 'unavailable';
|
||||
|
||||
html += '<div class="player-item ' + availabilityClass + '" data-player-id="' + pid + '">';
|
||||
html += '<span class="player-name">' + playerName + '</span>';
|
||||
html += '<div class="player-actions">';
|
||||
html += '<button type="button" class="btn btn-sm btn-primary" onclick="assignToTeam(' + pid + ', 1)">T1</button>';
|
||||
html += '<button type="button" class="btn btn-sm btn-secondary" onclick="assignToTeam(' + pid + ', 2)">T2</button>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
});
|
||||
|
||||
pool.innerHTML = html || '<p class="text-muted small">No players available</p>';
|
||||
updateRandomizePreview();
|
||||
}
|
||||
|
||||
function assignToTeam(playerId, teamSide) {
|
||||
// First, remove player from any team they're already in
|
||||
document.querySelectorAll('[data-player-id="' + playerId + '"]').forEach(function(el) {
|
||||
el.remove();
|
||||
});
|
||||
|
||||
var teamDiv = document.getElementById('team' + teamSide + '-selection');
|
||||
var playerName = playerDataById.player_data[playerId];
|
||||
if (!playerName) return;
|
||||
|
||||
var html = '<div class="player-item" data-player-id="' + playerId + '" onclick="returnToPool(' + playerId + ', event)">';
|
||||
html += playerName;
|
||||
html += '<span class="remove-btn">↺</span>';
|
||||
html += '</div>';
|
||||
|
||||
teamDiv.insertAdjacentHTML('beforeend', html);
|
||||
updateHiddenInputs();
|
||||
updatePlayerPool();
|
||||
}
|
||||
|
||||
function returnToPool(playerId, event) {
|
||||
if (event) event.stopPropagation();
|
||||
document.querySelectorAll('[data-player-id="' + playerId + '"]').forEach(function(el) {
|
||||
el.remove();
|
||||
});
|
||||
updateHiddenInputs();
|
||||
updatePlayerPool();
|
||||
}
|
||||
|
||||
function getSelectedTeamIds(teamSide) {
|
||||
var ids = [];
|
||||
document.querySelectorAll('#team' + teamSide + '-selection [data-player-id]').forEach(function(el) {
|
||||
ids.push(parseInt(el.getAttribute('data-player-id')));
|
||||
});
|
||||
return ids;
|
||||
}
|
||||
|
||||
function updateHiddenInputs() {
|
||||
var team1Ids = getSelectedTeamIds(1);
|
||||
var team2Ids = getSelectedTeamIds(2);
|
||||
document.getElementById('team1-player-ids-input').value = team1Ids.join(',');
|
||||
document.getElementById('team2-player-ids-input').value = team2Ids.join(',');
|
||||
}
|
||||
|
||||
function clearTimeSelection() {
|
||||
selectedSlots = [];
|
||||
document.getElementById('start_time').value = '';
|
||||
document.getElementById('end_time').value = '';
|
||||
document.getElementById('merged-disponibility-selected').classList.add('hidden');
|
||||
document.querySelectorAll('.merged-disponibility-time-block').forEach(function(el) {
|
||||
el.classList.remove('selected');
|
||||
});
|
||||
availablePlayersForSlots = [];
|
||||
// Clear both team selections
|
||||
document.querySelectorAll('#team1-selection [data-player-id], #team2-selection [data-player-id]').forEach(function(el) {
|
||||
el.remove();
|
||||
});
|
||||
updateHiddenInputs();
|
||||
updatePlayerPool();
|
||||
}
|
||||
|
||||
function toggleMatchType() {
|
||||
var matchType = document.getElementById('match_type').value;
|
||||
var teamSection = document.getElementById('team-vs-team-section');
|
||||
var playerVsPlayerSection = document.getElementById('player-vs-player-section');
|
||||
var scrimSection = document.getElementById('player-scrim-section');
|
||||
|
||||
teamSection.classList.add('hidden');
|
||||
playerVsPlayerSection.classList.add('hidden');
|
||||
scrimSection.classList.add('hidden');
|
||||
|
||||
if (matchType === 'team_vs_team') {
|
||||
teamSection.classList.remove('hidden');
|
||||
} else if (matchType === 'player_vs_player') {
|
||||
playerVsPlayerSection.classList.remove('hidden');
|
||||
updatePlayerPool();
|
||||
} else if (matchType === 'player_scrim') {
|
||||
scrimSection.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function updateRandomizePreview() {
|
||||
var checkedCount = document.querySelectorAll('#team1-selection [data-player-id], #team2-selection [data-player-id]').length;
|
||||
var team1Size = parseInt(document.getElementById('team1-size').value) || 1;
|
||||
var team2Size = checkedCount - team1Size;
|
||||
|
||||
if (team2Size < 0) team2Size = 0;
|
||||
|
||||
document.getElementById('team2-size-preview').textContent = team2Size;
|
||||
}
|
||||
|
||||
function randomizeTeams() {
|
||||
var allAssigned = [];
|
||||
|
||||
// Get all players currently in either team
|
||||
document.querySelectorAll('#team1-selection [data-player-id], #team2-selection [data-player-id]').forEach(function(el) {
|
||||
allAssigned.push(parseInt(el.getAttribute('data-player-id')));
|
||||
});
|
||||
|
||||
if (allAssigned.length === 0) {
|
||||
alert('Please assign at least one player before randomizing teams.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Shuffle the array using Fisher-Yates
|
||||
for (var i = allAssigned.length - 1; i > 0; i--) {
|
||||
var j = Math.floor(Math.random() * (i + 1));
|
||||
var temp = allAssigned[i];
|
||||
allAssigned[i] = allAssigned[j];
|
||||
allAssigned[j] = temp;
|
||||
}
|
||||
|
||||
var team1Size = parseInt(document.getElementById('team1-size').value) || 1;
|
||||
var team1Players = allAssigned.slice(0, team1Size);
|
||||
var team2Players = allAssigned.slice(team1Size);
|
||||
|
||||
// Clear both teams
|
||||
document.querySelectorAll('#team1-selection [data-player-id], #team2-selection [data-player-id]').forEach(function(el) {
|
||||
el.remove();
|
||||
});
|
||||
|
||||
// Assign shuffled players to teams
|
||||
team1Players.forEach(function(pid) {
|
||||
var teamDiv = document.getElementById('team1-selection');
|
||||
var playerName = playerDataById.player_data[pid];
|
||||
if (playerName) {
|
||||
var html = '<div class="player-item" data-player-id="' + pid + '" onclick="returnToPool(' + pid + ', event)">';
|
||||
html += playerName;
|
||||
html += '<span class="remove-btn">↺</span>';
|
||||
html += '</div>';
|
||||
teamDiv.insertAdjacentHTML('beforeend', html);
|
||||
}
|
||||
});
|
||||
|
||||
team2Players.forEach(function(pid) {
|
||||
var teamDiv = document.getElementById('team2-selection');
|
||||
var playerName = playerDataById.player_data[pid];
|
||||
if (playerName) {
|
||||
var html = '<div class="player-item" data-player-id="' + pid + '" onclick="returnToPool(' + pid + ', event)">';
|
||||
html += playerName;
|
||||
html += '<span class="remove-btn">↺</span>';
|
||||
html += '</div>';
|
||||
teamDiv.insertAdjacentHTML('beforeend', html);
|
||||
}
|
||||
});
|
||||
|
||||
updateHiddenInputs();
|
||||
updateRandomizePreview();
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -1,64 +0,0 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}Create Tryout - TryoutPro{% endblock %}
|
||||
{% block page_title %}Create Tryout{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('tryouts.list_tryouts') }}">Tryouts</a> / Create</span>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ url_for('tryouts.create_tryout') }}" class="form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-12">
|
||||
<label for="title">Tryout Title</label>
|
||||
<input type="text" id="title" name="title" placeholder="e.g., Spring Season Tryouts" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-6">
|
||||
<label for="game">Game</label>
|
||||
<select id="game" name="game" class="form-select" required>
|
||||
<option value="">-- Select a game --</option>
|
||||
{% for game in esport_games %}
|
||||
<option value="{{ game }}">{{ game }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-6">
|
||||
<label for="date">Date</label>
|
||||
<input type="date" id="date" name="date" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-6">
|
||||
<label for="location">Location</label>
|
||||
<input type="text" id="location" name="location" placeholder="e.g., Online">
|
||||
</div>
|
||||
<div class="form-group col-6">
|
||||
<label for="max_players">Max Players</label>
|
||||
<input type="number" id="max_players" name="max_players" placeholder="Leave blank for unlimited" min="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-6">
|
||||
<label for="target_org_team_id">Target Team</label>
|
||||
<select id="target_org_team_id" name="target_org_team_id" class="form-select">
|
||||
<option value="">-- No target team --</option>
|
||||
{% for team in org_teams %}
|
||||
<option value="{{ team.id }}">{{ team.name }}{% if team.coach %} (Coach: {{ team.coach.full_name }}){% endif %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description">Description</label>
|
||||
<textarea id="description" name="description" rows="4" placeholder="Enter any details about the tryout..."></textarea>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<a href="{{ url_for('tryouts.list_tryouts') }}" class="btn btn-secondary">Cancel</a>
|
||||
<button type="submit" class="btn btn-primary">Create Tryout</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -108,6 +108,32 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if stats.upcoming_matches %}
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-futbol"></i> Upcoming Matches</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table">
|
||||
<thead><tr><th>Match</th><th>Tryout</th><th>Date & Time</th><th>Status</th></tr></thead>
|
||||
<tbody>
|
||||
{% for match in stats.upcoming_matches %}
|
||||
<tr>
|
||||
<td>{{ match.title }}</td>
|
||||
<td><a href="{{ url_for('tryouts.view_tryout', tryout_id=match.tryout_id) }}">{{ match.tryout.title }}</a></td>
|
||||
<td>
|
||||
{{ match.date.strftime('%m/%d/%Y') }}
|
||||
{% if match.start_time %} at {{ match.start_time.strftime('%I:%M %p') }}{% endif %}
|
||||
</td>
|
||||
<td><span class="badge badge-{{ match.status }}">{{ match.status }}</span></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% elif user.role == 'manager' %}
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
@@ -166,6 +192,32 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if stats.upcoming_matches %}
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-futbol"></i> Upcoming Matches</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table">
|
||||
<thead><tr><th>Match</th><th>Tryout</th><th>Date & Time</th><th>Status</th></tr></thead>
|
||||
<tbody>
|
||||
{% for match in stats.upcoming_matches %}
|
||||
<tr>
|
||||
<td>{{ match.title }}</td>
|
||||
<td><a href="{{ url_for('tryouts.view_tryout', tryout_id=match.tryout_id) }}">{{ match.tryout.title }}</a></td>
|
||||
<td>
|
||||
{{ match.date.strftime('%m/%d/%Y') }}
|
||||
{% if match.start_time %} at {{ match.start_time.strftime('%I:%M %p') }}{% endif %}
|
||||
</td>
|
||||
<td><span class="badge badge-{{ match.status }}">{{ match.status }}</span></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% elif user.role == 'coach' %}
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}Edit Tryout - TryoutPro{% endblock %}
|
||||
{% block page_title %}Edit Tryout{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('tryouts.list_tryouts') }}">Tryouts</a> / <a href="{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}">{{ tryout.title }}</a> / Edit</span>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ url_for('tryouts.edit_tryout', tryout_id=tryout.id) }}" class="form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-12">
|
||||
<label for="title">Tryout Title</label>
|
||||
<input type="text" id="title" name="title" value="{{ tryout.title }}" placeholder="e.g., Spring Season Tryouts" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-6">
|
||||
<label for="game">Game</label>
|
||||
<select id="game" name="game" class="form-select" required>
|
||||
<option value="">-- Select a game --</option>
|
||||
{% for game in esport_games %}
|
||||
<option value="{{ game }}" {% if tryout.game == game %}selected{% endif %}>{{ game }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-6">
|
||||
<label for="date">Date</label>
|
||||
<input type="date" id="date" name="date" value="{{ tryout.date.strftime('%Y-%m-%d') }}" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-6">
|
||||
<label for="location">Location</label>
|
||||
<input type="text" id="location" name="location" value="{{ tryout.location or '' }}" placeholder="e.g., Online">
|
||||
</div>
|
||||
<div class="form-group col-6">
|
||||
<label for="max_players">Max Players</label>
|
||||
<input type="number" id="max_players" name="max_players" value="{{ tryout.max_players or '' }}" placeholder="Leave blank for unlimited" min="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-6">
|
||||
<label for="target_org_team_id">Target Team</label>
|
||||
<select id="target_org_team_id" name="target_org_team_id" class="form-select">
|
||||
<option value="">-- No target team --</option>
|
||||
{% for team in org_teams %}
|
||||
<option value="{{ team.id }}" {% if tryout.target_org_team_id == team.id %}selected{% endif %}>
|
||||
{{ team.name }}{% if team.coach %} (Coach: {{ team.coach.full_name }}){% endif %}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description">Description</label>
|
||||
<textarea id="description" name="description" rows="4" placeholder="Enter any details about the tryout...">{{ tryout.description or '' }}</textarea>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<a href="{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}" class="btn btn-secondary">Cancel</a>
|
||||
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,28 +1,54 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}Edit Match - TryoutPro{% endblock %}
|
||||
{% block page_title %}Edit Match{% endblock %}
|
||||
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('tryouts.list_tryouts') }}">Tryouts</a> / <a href="{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}">{{ tryout.title }}</a> / Edit Match</span>{% endblock %}
|
||||
{% block title %}{% if match %}Edit Match{% else %}Schedule Match{% endif %} - {{ tryout.title }} - TryoutPro{% endblock %}
|
||||
{% block page_title %}{% if match %}Edit Match{% else %}Schedule Match{% endif %}{% endblock %}
|
||||
{% block breadcrumb %}
|
||||
<span class="breadcrumb">
|
||||
Home / <a href="{{ url_for('tryouts.list_tryouts') }}">Tryouts</a>
|
||||
/ <a href="{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}">{{ tryout.title }}</a>
|
||||
{% if match %}
|
||||
/ Edit Match
|
||||
{% else %}
|
||||
/ Schedule Match
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-futbol"></i> Edit Match</h3>
|
||||
<p class="text-muted small">Match Type: <strong>{{ match.match_type.replace('_', ' ').title() }}</strong></p>
|
||||
<h3><i class="fas fa-futbol"></i> {% if match %}Edit Match{% else %}Schedule Match for {{ tryout.title }}{% endif %}</h3>
|
||||
{% if match %}
|
||||
<p class="text-muted small">Match Type: <strong>{{ match.match_type.replace('_', ' ') | title }}</strong></p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ url_for('matches.edit_match', match_id=match.id) }}" class="form" id="editMatchForm">
|
||||
<form method="POST" action="{% if match %}{{ url_for('matches.edit_match', match_id=match.id) }}{% else %}{{ url_for('matches.create_match', tryout_id=tryout.id) }}{% endif %}" class="form" id="matchForm">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
|
||||
{% if not match %}
|
||||
<div class="form-group">
|
||||
<label for="match_type">Match Type</label>
|
||||
<select name="match_type" id="match_type" class="form-select" onchange="toggleMatchType()" required>
|
||||
<option value="team_vs_team">Team vs Team</option>
|
||||
<option value="player_vs_player">Player vs Player</option>
|
||||
<option value="player_scrim">Player Scrim</option>
|
||||
</select>
|
||||
</div>
|
||||
{% else %}
|
||||
<input type="hidden" name="match_type" value="{{ match.match_type }}">
|
||||
{% endif %}
|
||||
|
||||
<div class="form-group">
|
||||
<label for="title">Match Title</label>
|
||||
<input type="text" name="title" id="title" class="form-input" value="{{ match.title }}" required>
|
||||
<input type="text" name="title" id="title" class="form-input" value="{{ match.title if match else '' }}" placeholder="e.g., Alpha vs Bravo Scrimmage" required>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="date">Date</label>
|
||||
<input type="date" name="date" id="date" class="form-input" value="{{ match.date.strftime('%Y-%m-%d') }}" required>
|
||||
<input type="date" name="date" id="date" class="form-input" value="{{ match.date.strftime('%Y-%m-%d') if match else tryout.date.strftime('%Y-%m-%d') }}" required>
|
||||
</div>
|
||||
{% if match %}
|
||||
<div class="form-group">
|
||||
<label for="status">Status</label>
|
||||
<select name="status" id="status" class="form-select">
|
||||
@@ -31,6 +57,11 @@
|
||||
<option value="cancelled" {% if match.status == 'cancelled' %}selected{% endif %}>Cancelled</option>
|
||||
</select>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="form-group">
|
||||
<label for="location">Location</label>
|
||||
<input type="text" name="location" id="location" class="form-input" value="{{ match.location or '' if match else '' }}" placeholder="Match location">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Merged Availability Grid -->
|
||||
@@ -52,11 +83,11 @@
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="start_time">Start Time <span class="text-muted">(Required)</span></label>
|
||||
<input type="time" name="start_time" id="start_time" class="form-input" value="{{ match.start_time.strftime('%H:%M') if match.start_time else '' }}" required>
|
||||
<input type="time" name="start_time" id="start_time" class="form-input" value="{{ match.start_time.strftime('%H:%M') if match and match.start_time else '' }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="end_time">End Time <span class="text-muted">(Set by clicking consecutive slots)</span></label>
|
||||
<input type="time" name="end_time" id="end_time" class="form-input" value="{{ match.end_time.strftime('%H:%M') if match.end_time else '' }}" required>
|
||||
<input type="time" name="end_time" id="end_time" class="form-input" value="{{ match.end_time.strftime('%H:%M') if match and match.end_time else '' }}" required>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
@@ -64,29 +95,24 @@
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="start_time">Start Time</label>
|
||||
<input type="time" name="start_time" id="start_time" class="form-input" value="{{ match.start_time.strftime('%H:%M') if match.start_time else '' }}">
|
||||
<input type="time" name="start_time" id="start_time" class="form-input" value="{{ match.start_time.strftime('%H:%M') if match and match.start_time else '' }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="end_time">End Time</label>
|
||||
<input type="time" name="end_time" id="end_time" class="form-input" value="{{ match.end_time.strftime('%H:%M') if match.end_time else '' }}">
|
||||
<input type="time" name="end_time" id="end_time" class="form-input" value="{{ match.end_time.strftime('%H:%M') if match and match.end_time else '' }}">
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="form-group">
|
||||
<label for="location">Location</label>
|
||||
<input type="text" name="location" id="location" class="form-input" value="{{ match.location or '' }}" placeholder="Match location">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="description">Description</label>
|
||||
<textarea name="description" id="description" class="form-textarea" placeholder="Optional notes about this match">{{ match.description or '' }}</textarea>
|
||||
<textarea name="description" id="description" class="form-textarea" placeholder="Optional notes about this match">{% if match %}{{ match.description or '' }}{% endif %}</textarea>
|
||||
</div>
|
||||
|
||||
<!-- Team vs Team Selection -->
|
||||
<div id="team-vs-team-section" {% if match.match_type != 'team_vs_team' %}class="hidden"{% endif %}>
|
||||
<div id="team-vs-team-section" {% if match and match.match_type != 'team_vs_team' %}class="hidden"{% endif %}>
|
||||
<hr class="section-divider">
|
||||
<h4 class="section-title"><i class="fas fa-users"></i> Teams</h4>
|
||||
<h4 class="section-title"><i class="fas fa-users"></i> Select Teams</h4>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
@@ -94,7 +120,7 @@
|
||||
<select name="team1_id" id="team1_id" class="form-select">
|
||||
<option value="">-- Select Team 1 --</option>
|
||||
{% for team in teams %}
|
||||
<option value="{{ team.id }}" {% if match.team1_id == team.id %}selected{% endif %}>{{ team.name }}</option>
|
||||
<option value="{{ team.id }}" {% if match and match.team1_id == team.id %}selected{% endif %}>{{ team.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
@@ -103,15 +129,14 @@
|
||||
<select name="team2_id" id="team2_id" class="form-select">
|
||||
<option value="">-- Select Team 2 --</option>
|
||||
{% for team in teams %}
|
||||
<option value="{{ team.id }}" {% if match.team2_id == team.id %}selected{% endif %}>{{ team.name }}</option>
|
||||
<option value="{{ team.id }}" {% if match and match.team2_id == team.id %}selected{% endif %}>{{ team.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Show team rosters -->
|
||||
{% if match and match.team1 %}
|
||||
<div class="team-rosters mt-3">
|
||||
{% if match.team1 %}
|
||||
<div class="team-roster">
|
||||
<h5>{{ match.team1.name }}</h5>
|
||||
<ul class="team-members-list">
|
||||
@@ -122,8 +147,10 @@
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if match.team2 %}
|
||||
{% if match and match.team2 %}
|
||||
<div class="team-rosters mt-3">
|
||||
<div class="team-roster">
|
||||
<h5>{{ match.team2.name }}</h5>
|
||||
<ul class="team-members-list">
|
||||
@@ -134,12 +161,12 @@
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Player vs Player Selection -->
|
||||
<div id="player-vs-player-section" {% if match.match_type != 'player_vs_player' %}class="hidden"{% endif %}>
|
||||
<div id="player-vs-player-section" {% if match and match.match_type != 'player_vs_player' %}class="hidden"{% endif %}>
|
||||
<hr class="section-divider">
|
||||
<h4 class="section-title"><i class="fas fa-user-friends"></i> Select Players</h4>
|
||||
|
||||
@@ -181,7 +208,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Player Scrim Selection -->
|
||||
<div id="player-scrim-section" {% if match.match_type != 'player_scrim' %}class="hidden"{% endif %}>
|
||||
<div id="player-scrim-section" {% if match and match.match_type != 'player_scrim' %}class="hidden"{% endif %}>
|
||||
<hr class="section-divider">
|
||||
<h4 class="section-title"><i class="fas fa-users"></i> Select Players</h4>
|
||||
|
||||
@@ -192,7 +219,7 @@
|
||||
<div class="checkbox-grid" id="scrim-players-list">
|
||||
{% for player in all_players %}
|
||||
<label class="checkbox-label player-checkbox" data-player-id="{{ player.id }}">
|
||||
<input type="checkbox" name="player_ids" value="{{ player.id }}" {% if player.id in current_player_ids %}checked{% endif %}>
|
||||
<input type="checkbox" name="player_ids" value="{{ player.id }}" {% if match and player.id in current_player_ids %}checked{% endif %}>
|
||||
{{ player.full_name }}
|
||||
<span class="disponibility-indicator" data-player-id="{{ player.id }}"></span>
|
||||
</label>
|
||||
@@ -202,7 +229,7 @@
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i> Save Changes
|
||||
<i class="fas fa-save"></i> {% if match %}Save Changes{% else %}Schedule Match{% endif %}
|
||||
</button>
|
||||
<a href="{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}" class="btn btn-secondary">
|
||||
<i class="fas fa-times"></i> Cancel
|
||||
@@ -373,9 +400,11 @@ var allRegisteredPlayers = [
|
||||
{%- endfor %}
|
||||
];
|
||||
|
||||
{% if match %}
|
||||
// Current team assignments from server
|
||||
var initialTeam1Ids = {{ team1_player_ids|tojson }};
|
||||
var initialTeam2Ids = {{ team2_player_ids|tojson }};
|
||||
{% endif %}
|
||||
|
||||
// Time slots from 12pm (12:00) to 12am (24:00)
|
||||
var TIME_SLOTS = [];
|
||||
@@ -427,11 +456,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
selectedDate = this.value;
|
||||
if (allDisponibilitiesInitialized()) {
|
||||
renderMergedDisponibilityGrid();
|
||||
{% if match %}
|
||||
updatePlayerPool();
|
||||
{% endif %}
|
||||
}
|
||||
});
|
||||
|
||||
selectedDate = document.getElementById('date').value;
|
||||
{% if match %}
|
||||
selectedStartTime = document.getElementById('start_time').value;
|
||||
selectedEndTime = document.getElementById('end_time').value;
|
||||
|
||||
@@ -459,6 +491,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
// Initialize team assignments for player_vs_player matches
|
||||
// Populate Team 1
|
||||
{% if match %}
|
||||
initialTeam1Ids.forEach(function(pid) {
|
||||
var teamDiv = document.getElementById('team1-selection');
|
||||
var playerName = playerDataById.player_data[pid];
|
||||
@@ -486,6 +519,13 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
updateHiddenInputs();
|
||||
updateRandomizePreview();
|
||||
{% endif %}
|
||||
|
||||
{% if not match %}
|
||||
toggleMatchType();
|
||||
// Show all players initially
|
||||
updatePlayerPool();
|
||||
{% endif %}
|
||||
});
|
||||
|
||||
function allDisponibilitiesInitialized() {
|
||||
@@ -499,7 +539,9 @@ function fetchDisponibilities() {
|
||||
.then(function(data) {
|
||||
allDisponibilities = data;
|
||||
renderMergedDisponibilityGrid();
|
||||
{% if match %}
|
||||
updatePlayerPool();
|
||||
{% endif %}
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error('Error fetching disponibilities:', error);
|
||||
@@ -666,7 +708,16 @@ function toggleTimeSlot(dayOfWeek, timeStr, element) {
|
||||
});
|
||||
|
||||
if (selectedSlots.length > 0) {
|
||||
{% if match %}
|
||||
fetchAvailablePlayersForSlots();
|
||||
{% else %}
|
||||
fetchAvailablePlayersForAllSlots();
|
||||
{% endif %}
|
||||
} else {
|
||||
availablePlayersForSlots = [];
|
||||
{% if match %}
|
||||
updatePlayerPool();
|
||||
{% endif %}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -692,6 +743,35 @@ function formatTimeDisplay(timeStr) {
|
||||
return displayHour + ':' + m + ' ' + ampm;
|
||||
}
|
||||
|
||||
function fetchAvailablePlayersForAllSlots() {
|
||||
if (!selectedDate || selectedSlots.length === 0) {
|
||||
availablePlayersForSlots = [];
|
||||
updatePlayerPool();
|
||||
return;
|
||||
}
|
||||
|
||||
var dateParts = selectedDate.split('-');
|
||||
var dateObj = new Date(dateParts[0], dateParts[1] - 1, dateParts[2]);
|
||||
var jsDay = dateObj.getDay();
|
||||
var dayOfWeek = jsDay === 0 ? 6 : jsDay - 1;
|
||||
|
||||
// Find players available in ALL selected slots
|
||||
availablePlayersForSlots = [];
|
||||
for (var playerId in allDisponibilities) {
|
||||
if (!allRegisteredPlayers.includes(parseInt(playerId))) continue;
|
||||
var playerSlots = getSlotsForPlayer(parseInt(playerId), dayOfWeek);
|
||||
var isAvailableInAll = selectedSlots.every(function(slot) {
|
||||
return playerSlots.includes(slot);
|
||||
});
|
||||
if (isAvailableInAll) {
|
||||
availablePlayersForSlots.push(parseInt(playerId));
|
||||
}
|
||||
}
|
||||
|
||||
// Preselect available players in Team 1
|
||||
preselectAvailablePlayers();
|
||||
}
|
||||
|
||||
function fetchAvailablePlayersForSlots() {
|
||||
if (!selectedDate || selectedSlots.length === 0) return;
|
||||
|
||||
@@ -713,20 +793,15 @@ function fetchAvailablePlayersForSlots() {
|
||||
}
|
||||
}
|
||||
|
||||
availablePlayersForSlots = availableIds;
|
||||
// Update player pool for PvP section
|
||||
updatePlayerPool();
|
||||
updateRandomizePreview();
|
||||
}
|
||||
|
||||
function clearTimeSelection() {
|
||||
selectedSlots = [];
|
||||
document.getElementById('start_time').value = '';
|
||||
document.getElementById('end_time').value = '';
|
||||
document.getElementById('merged-disponibility-selected').classList.add('hidden');
|
||||
document.querySelectorAll('.merged-disponibility-time-block').forEach(function(el) {
|
||||
el.classList.remove('selected');
|
||||
});
|
||||
availablePlayersForSlots = [];
|
||||
function preselectAvailablePlayers() {
|
||||
// Just update the player pool - don't auto-assign to Team 1
|
||||
// Players will be shown in the pool and can be manually assigned to either team
|
||||
updatePlayerPool();
|
||||
}
|
||||
|
||||
@@ -827,10 +902,47 @@ function updateHiddenInputs() {
|
||||
document.getElementById('team2-player-ids-input').value = team2Ids.join(',');
|
||||
}
|
||||
|
||||
function clearTimeSelection() {
|
||||
selectedSlots = [];
|
||||
document.getElementById('start_time').value = '';
|
||||
document.getElementById('end_time').value = '';
|
||||
document.getElementById('merged-disponibility-selected').classList.add('hidden');
|
||||
document.querySelectorAll('.merged-disponibility-time-block').forEach(function(el) {
|
||||
el.classList.remove('selected');
|
||||
});
|
||||
availablePlayersForSlots = [];
|
||||
// Clear both team selections
|
||||
document.querySelectorAll('#team1-selection [data-player-id], #team2-selection [data-player-id]').forEach(function(el) {
|
||||
el.remove();
|
||||
});
|
||||
updateHiddenInputs();
|
||||
updatePlayerPool();
|
||||
}
|
||||
|
||||
function toggleMatchType() {
|
||||
var matchType = document.getElementById('match_type').value;
|
||||
var teamSection = document.getElementById('team-vs-team-section');
|
||||
var playerVsPlayerSection = document.getElementById('player-vs-player-section');
|
||||
var scrimSection = document.getElementById('player-scrim-section');
|
||||
|
||||
teamSection.classList.add('hidden');
|
||||
playerVsPlayerSection.classList.add('hidden');
|
||||
scrimSection.classList.add('hidden');
|
||||
|
||||
if (matchType === 'team_vs_team') {
|
||||
teamSection.classList.remove('hidden');
|
||||
} else if (matchType === 'player_vs_player') {
|
||||
playerVsPlayerSection.classList.remove('hidden');
|
||||
updatePlayerPool();
|
||||
} else if (matchType === 'player_scrim') {
|
||||
scrimSection.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function updateRandomizePreview() {
|
||||
var assignedCount = document.querySelectorAll('#team1-selection [data-player-id], #team2-selection [data-player-id]').length;
|
||||
var checkedCount = document.querySelectorAll('#team1-selection [data-player-id], #team2-selection [data-player-id]').length;
|
||||
var team1Size = parseInt(document.getElementById('team1-size').value) || 1;
|
||||
var team2Size = assignedCount - team1Size;
|
||||
var team2Size = checkedCount - team1Size;
|
||||
|
||||
if (team2Size < 0) team2Size = 0;
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}{% if tryout %}Edit Tryout{% else %}Create Tryout{% endif %} - TryoutPro{% endblock %}
|
||||
{% block page_title %}{% if tryout %}Edit Tryout{% else %}Create Tryout{% endif %}{% endblock %}
|
||||
{% block breadcrumb %}
|
||||
<span class="breadcrumb">
|
||||
Home / <a href="{{ url_for('tryouts.list_tryouts') }}">Tryouts</a>
|
||||
{% if tryout %}
|
||||
/ <a href="{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}">{{ tryout.title }}</a>
|
||||
/ Edit
|
||||
{% else %}
|
||||
/ Create
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{% if tryout %}{{ url_for('tryouts.edit_tryout', tryout_id=tryout.id) }}{% else %}{{ url_for('tryouts.create_tryout') }}{% endif %}" class="form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-12">
|
||||
<label for="title">Tryout Title</label>
|
||||
<input type="text" id="title" name="title" value="{{ tryout.title if tryout else '' }}" placeholder="e.g., Spring Season Tryouts" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-6">
|
||||
<label for="game">Game</label>
|
||||
<select id="game" name="game" class="form-select" required>
|
||||
<option value="">-- Select a game --</option>
|
||||
{% for game in esport_games %}
|
||||
<option value="{{ game }}" {% if tryout and tryout.game == game %}selected{% endif %}>{{ game }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-6">
|
||||
<label for="date">Date</label>
|
||||
<input type="date" id="date" name="date" value="{{ tryout.date.strftime('%Y-%m-%d') if tryout else '' }}" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-6">
|
||||
<label for="location">Location</label>
|
||||
<input type="text" id="location" name="location" value="{{ tryout.location or '' if tryout else '' }}" placeholder="e.g., Online">
|
||||
</div>
|
||||
<div class="form-group col-6">
|
||||
<label for="max_players">Max Players</label>
|
||||
<input type="number" id="max_players" name="max_players" value="{{ tryout.max_players or '' if tryout else '' }}" placeholder="Leave blank for unlimited" min="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-6">
|
||||
<label for="target_org_team_id">Target Team</label>
|
||||
<select id="target_org_team_id" name="target_org_team_id" class="form-select">
|
||||
<option value="">-- No target team --</option>
|
||||
{% for team in org_teams %}
|
||||
<option value="{{ team.id }}" {% if tryout and tryout.target_org_team_id == team.id %}selected{% endif %}>
|
||||
{{ team.name }}{% if team.coach %} (Coach: {{ team.coach.full_name }}){% endif %}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-6">
|
||||
<label for="manager_id">Assigned Manager</label>
|
||||
<select id="manager_id" name="manager_id" class="form-select">
|
||||
<option value="">-- No manager assigned --</option>
|
||||
{% for manager in managers %}
|
||||
<option value="{{ manager.id }}" {% if tryout and tryout.manager_id == manager.id %}selected{% endif %}>
|
||||
{{ manager.full_name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-12">
|
||||
<label for="coach_id">Assigned Coach</label>
|
||||
<select id="coach_id" name="coach_id" class="form-select">
|
||||
<option value="">-- No coach assigned --</option>
|
||||
{% for coach in coaches %}
|
||||
<option value="{{ coach.id }}" {% if tryout and tryout.coach_id == coach.id %}selected{% endif %}>
|
||||
{{ coach.full_name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description">Description</label>
|
||||
<textarea id="description" name="description" rows="4" placeholder="Enter any details about the tryout...">{% if tryout %}{{ tryout.description or '' }}{% endif %}</textarea>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<a href="{% if tryout %}{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}{% else %}{{ url_for('tryouts.list_tryouts') }}{% endif %}" class="btn btn-secondary">
|
||||
Cancel
|
||||
</a>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
{% if tryout %}Save Changes{% else %}Create Tryout{% endif %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -55,6 +55,14 @@
|
||||
<span class="detail-label">Target Team</span>
|
||||
<span class="detail-value">{{ tryout.target_org_team.name if tryout.target_org_team else 'Not specified' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">Manager</span>
|
||||
<span class="detail-value">{{ tryout.manager.full_name if tryout.manager else 'Not assigned' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">Coach</span>
|
||||
<span class="detail-value">{{ tryout.coach.full_name if tryout.coach else 'Not assigned' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">Registered Players</span>
|
||||
<span class="detail-value">{{ registered_players | length }}</span>
|
||||
|
||||
Reference in New Issue
Block a user