remodulation du projet et des classes

This commit is contained in:
cedrick2711
2026-07-28 23:33:31 -04:00
parent 90ac3eb804
commit 3feae80767
94 changed files with 2644 additions and 4366 deletions
+152
View File
@@ -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.username }}
</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.username }}:</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 %}
+361
View File
@@ -0,0 +1,361 @@
{% extends "layouts/base.html" %}
{% block title %}Calendar - TryoutPro{% endblock %}
{% block page_title %}Calendar{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / Calendar</span>{% endblock %}
{% block content %}
<div class="card">
<div class="card-header">
<h3><i class="fas fa-calendar-alt"></i> Schedule</h3>
<div class="header-actions">
<div class="btn-group" role="group">
<button type="button" class="btn btn-sm btn-outline" onclick="changeView('dayGridMonth')">
<i class="fas fa-calendar"></i> Month
</button>
<button type="button" class="btn btn-sm btn-outline" onclick="changeView('timeGridWeek')">
<i class="fas fa-calendar-week"></i> Week
</button>
<button type="button" class="btn btn-sm btn-outline" onclick="changeView('timeGridDay')">
<i class="fas fa-calendar-day"></i> Day
</button>
<button type="button" class="btn btn-sm btn-outline" onclick="changeView('listMonth')">
<i class="fas fa-list"></i> List
</button>
</div>
</div>
</div>
<div class="card-body">
<div id="calendar"></div>
</div>
</div>
<!-- Create Event Modal (for clicking empty days) -->
<div id="createEventModal" class="modal hidden">
<div class="modal-backdrop" onclick="hideCreateEventModal()"></div>
<div class="modal-content">
<div class="modal-header">
<h3><i class="fas fa-plus-circle"></i> Create New Event</h3>
<button class="modal-close" onclick="hideCreateEventModal()">&times;</button>
</div>
<div class="modal-body">
<p class="mb-3"><strong>Date:</strong> <span id="createEventDate"></span></p>
<input type="hidden" id="createEventDateInput"/>
<div class="card mb-3">
<div class="card-body">
<h5><i class="fas fa-futbol"></i> Schedule Tryout Match</h5>
<p class="text-muted small">Add a scrim/match inside an existing tryout</p>
<div class="form-inline">
<select id="createTryoutSelect" class="form-select" style="flex:1;">
<option value="">-- Select a tryout --</option>
</select>
<button class="btn btn-sm btn-primary ml-2" onclick="goToTryoutMatch()">
<i class="fas fa-arrow-right"></i> Go
</button>
</div>
</div>
</div>
<div class="card mb-3">
<div class="card-body">
<h5><i class="fas fa-users"></i> Schedule Team Match</h5>
<p class="text-muted small">Regular season match for an org team</p>
<div class="form-inline">
<select id="createTeamSelect" class="form-select" style="flex:1;">
<option value="">-- Select a team --</option>
</select>
<button class="btn btn-sm btn-success ml-2" onclick="goToTeamMatch()">
<i class="fas fa-arrow-right"></i> Go
</button>
</div>
</div>
</div>
<div class="card">
<div class="card-body">
<h5><i class="fas fa-calendar-plus"></i> Create New Tryout</h5>
<p class="text-muted small">Create a brand new tryout event</p>
<button class="btn btn-sm btn-info" onclick="goToCreateTryout()">
<i class="fas fa-plus"></i> Create Tryout
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Event Details Modal -->
<div id="eventModal" class="modal hidden">
<div class="modal-backdrop" onclick="hideEventModal()"></div>
<div class="modal-content">
<div class="modal-header">
<h3 id="modalTitle">Event Details</h3>
<button class="modal-close" onclick="hideEventModal()">&times;</button>
</div>
<div class="modal-body">
<div id="modalContent"></div>
<div id="modalPresenceToggle" class="form-actions mt-3" style="display: none; justify-content: center;">
<button class="btn btn-sm btn-outline" id="calPresenceBtn">Confirm</button>
</div>
<div id="modalActions" class="form-actions mt-3" style="display: none;">
<button class="btn btn-sm btn-danger" id="deleteMatchBtn" style="display: none;">
<i class="fas fa-trash"></i> Delete Match
</button>
<button class="btn btn-sm btn-primary" id="editMatchBtn" style="display: none;">
<i class="fas fa-edit"></i> Edit Match
</button>
<button class="btn btn-sm btn-primary" id="viewTryoutBtn" style="display: none;">
<i class="fas fa-eye"></i> View Tryout
</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<link href="https://cdn.jsdelivr.net/npm/[email protected]/index.global.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/index.global.min.js"></script>
<script>
var canScheduleMatches = {% if current_user.can_schedule_matches() %}true{% else %}false{% endif %};
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
initialView: 'dayGridMonth',
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay,listMonth'
},
events: '/matches/api/events',
eventClick: function(info) {
showEventModal(info.event);
},
dateClick: function(info) {
if (canScheduleMatches) {
var dateStr = info.dateStr;
showCreateEventModal(dateStr);
}
},
selectable: true,
select: function(info) {
if (canScheduleMatches) {
var dateStr = info.startStr;
showCreateEventModal(dateStr);
calendar.unselect();
}
},
slotMinTime: '12:00:00',
slotMaxTime: '24:00:00'
});
calendar.render();
window.fcCalendar = calendar;
// Pre-load tryout and team options for the create modal
if (canScheduleMatches) {
fetchTryoutOptions();
fetchTeamOptions();
}
});
function changeView(viewName) {
if (window.fcCalendar) {
window.fcCalendar.changeView(viewName);
}
}
// --- Create Event Modal ---
function showCreateEventModal(dateStr) {
document.getElementById('createEventDate').textContent = dateStr;
document.getElementById('createEventDateInput').value = dateStr;
// Reset dropdowns
document.getElementById('createTryoutSelect').value = '';
document.getElementById('createTeamSelect').value = '';
document.getElementById('createEventModal').classList.remove('hidden');
}
function hideCreateEventModal() {
document.getElementById('createEventModal').classList.add('hidden');
}
function goToTryoutMatch() {
var tryoutId = document.getElementById('createTryoutSelect').value;
if (!tryoutId) { alert('Please select a tryout.'); return; }
var date = document.getElementById('createEventDateInput').value;
window.location.href = '/matches/create/' + tryoutId + '?date=' + date;
}
function goToTeamMatch() {
var teamId = document.getElementById('createTeamSelect').value;
if (!teamId) { alert('Please select a team.'); return; }
var date = document.getElementById('createEventDateInput').value;
window.location.href = '/team-matches/' + teamId + '/create?date=' + date;
}
function goToCreateTryout() {
// Tryout creation doesn't support pre-filling date easily, just navigate
window.location.href = '/tryouts/create';
}
// Pre-fetch data for dropdowns
function fetchTryoutOptions() {
fetch('/matches/api/manageable-tryouts')
.then(function(r) { return r.json(); })
.then(function(data) {
var sel = document.getElementById('createTryoutSelect');
sel.innerHTML = '<option value="">-- Select a tryout --</option>';
data.forEach(function(t) {
sel.innerHTML += '<option value="' + t.id + '">' + t.title + ' (' + t.date + ')</option>';
});
})
.catch(function() {});
}
function fetchTeamOptions() {
fetch('/team-matches/api/manageable-teams')
.then(function(r) { return r.json(); })
.then(function(data) {
var sel = document.getElementById('createTeamSelect');
sel.innerHTML = '<option value="">-- Select a team --</option>';
data.forEach(function(t) {
sel.innerHTML += '<option value="' + t.id + '">' + t.name + '</option>';
});
})
.catch(function() {});
}
function showEventModal(event) {
var props = event.extendedProps;
var title = event.title;
var type = props.type;
var date = event.start ? event.start.toDateString() : '';
var content = '<div class="detail-grid">';
content += '<div class="detail-item"><span class="detail-label">Type</span><span class="detail-value">';
content += '<span class="badge badge-' + (type === 'tryout' ? 'info' : (props.match_type === 'team_vs_team' || props.match_type === 'player_vs_player' ? 'success' : 'warning')) + '">';
content += (type === 'tryout' ? 'Tryout' : (props.match_type === 'team_vs_team' ? 'Team Match' : (props.match_type === 'player_vs_player' ? 'Player Match' : 'Player Scrim'))) + '</span>';
content += '</span></div>';
content += '<div class="detail-item"><span class="detail-label">Title</span><span class="detail-value">' + title + '</span></div>';
content += '<div class="detail-item"><span class="detail-label">Date</span><span class="detail-value">' + date + '</span></div>';
content += '<div class="detail-item"><span class="detail-label">Location</span><span class="detail-value">' + (props.location || 'TBD') + '</span></div>';
content += '<div class="detail-item"><span class="detail-label">Status</span><span class="detail-value">';
content += '<span class="badge badge-' + (props.status || 'scheduled') + '">' + (props.status || 'scheduled') + '</span>';
content += '</span></div>';
// Add team separation for matches
if (type === 'match' && props.participants) {
content += '<div class="detail-item full-width"><span class="detail-label">Teams</span><span class="detail-value">';
if (props.match_type === 'team_vs_team') {
// For team vs team, participants is "Team1 vs Team2"
var teams = props.participants.split(' vs ');
if (teams.length >= 2) {
content += '<div class="match-teams">';
content += '<div class="match-team"><span class="team-name">' + teams[0] + '</span></div>';
content += '<div class="match-vs">vs</div>';
content += '<div class="match-team"><span class="team-name">' + teams[1] + '</span></div>';
content += '</div>';
} else {
content += props.participants;
}
} else if (props.match_type === 'player_vs_player') {
// For player vs player, we need to parse the participants
// The format is "player1, player2 vs player3, player4"
var parts = props.participants.split(' vs ');
if (parts.length >= 2) {
content += '<div class="match-teams">';
content += '<div class="match-team"><span class="team-name">Team 1</span><ul class="team-players-list"><li>' + parts[0].split(', ').join('</li><li>') + '</li></ul></div>';
content += '<div class="match-vs">vs</div>';
content += '<div class="match-team"><span class="team-name">Team 2</span><ul class="team-players-list"><li>' + parts[1].split(', ').join('</li><li>') + '</li></ul></div>';
content += '</div>';
} else {
content += props.participants;
}
} else {
// For player scrim, just show the list
content += props.participants;
}
content += '</span></div>';
}
if (props.description) {
content += '<div class="detail-item full-width"><span class="detail-label">Description</span><span class="detail-value">' + props.description + '</span></div>';
}
content += '</div>';
document.getElementById('modalTitle').textContent = type === 'tryout' ? 'Tryout Details' : 'Match Details';
document.getElementById('modalContent').innerHTML = content;
// Reset buttons
document.getElementById('deleteMatchBtn').style.display = 'none';
document.getElementById('editMatchBtn').style.display = 'none';
document.getElementById('viewTryoutBtn').style.display = 'none';
// Show action buttons for matches (coaches and above)
if (type === 'match' && canScheduleMatches) {
document.getElementById('modalActions').style.display = 'flex';
document.getElementById('editMatchBtn').style.display = 'inline-flex';
document.getElementById('editMatchBtn').onclick = function() {
window.location.href = '/matches/' + props.match_id + '/edit';
};
} else if (type === 'tryout' && canScheduleMatches) {
document.getElementById('modalActions').style.display = 'flex';
document.getElementById('viewTryoutBtn').style.display = 'inline-flex';
document.getElementById('viewTryoutBtn').onclick = function() {
window.location.href = '/tryouts/' + props.tryout_id;
};
} else {
document.getElementById('modalActions').style.display = 'none';
}
// Show presence toggle for matches where user is a participant
var presenceDiv = document.getElementById('modalPresenceToggle');
if (type === 'match' && props.user_participant_id) {
presenceDiv.style.display = 'flex';
var confirmed = props.user_attendance_confirmed || false;
var toggleBtn = document.getElementById('calPresenceBtn');
toggleBtn.textContent = confirmed ? '✅ Confirmed' : 'Confirm';
toggleBtn.className = 'btn btn-sm ' + (confirmed ? 'btn-success' : 'btn-outline');
toggleBtn.onclick = function() {
toggleCalendarPresence(props.match_id, props.user_participant_id, toggleBtn);
};
} else {
presenceDiv.style.display = 'none';
}
document.getElementById('eventModal').classList.remove('hidden');
}
function toggleCalendarPresence(matchId, participantId, btn) {
fetch('/matches/' + matchId + '/toggle-presence/' + participantId, {
method: 'POST',
headers: {
'X-CSRFToken': '{{ csrf_token() }}',
'Content-Type': 'application/json'
}
})
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.attendance_confirmed) {
btn.classList.add('btn-success');
btn.classList.remove('btn-outline');
btn.textContent = '✅ Confirmed';
} else {
btn.classList.remove('btn-success');
btn.classList.add('btn-outline');
btn.textContent = 'Confirm';
}
})
.catch(function(err) {
console.error('Error toggling presence:', err);
});
}
function hideEventModal() {
document.getElementById('eventModal').classList.add('hidden');
}
</script>
{% endblock %}
+219
View File
@@ -0,0 +1,219 @@
{% extends "layouts/base.html" %}
{% block title %}Manage Availability - TryoutPro{% endblock %}
{% block page_title %}Manage Availability{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / Coach Availability</span>{% endblock %}
{% block content %}
<div class="card">
<div class="card-header">
<h3><i class="fas fa-clock"></i> Set Your Weekly Availability</h3>
<p class="text-muted small">Select time slots when you're available for One on One sessions</p>
</div>
<div class="card-body">
<div class="availability-grid" id="availability-grid">
<p class="text-muted">Loading availability grid...</p>
</div>
<div class="form-actions mt-4">
<button type="button" class="btn btn-secondary" onclick="clearAllAvailability()">
<i class="fas fa-trash"></i> Clear All
</button>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<style>
.availability-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 12px;
margin-top: 10px;
}
.day-column {
background: var(--bg-secondary);
border-radius: 8px;
padding: 10px;
min-height: 300px;
}
.day-header {
text-align: center;
font-weight: 600;
padding: 8px 0;
border-bottom: 1px solid var(--border-color);
margin-bottom: 10px;
color: var(--primary);
}
.time-slot {
padding: 6px 8px;
margin: 4px 0;
border-radius: 4px;
font-size: 0.8rem;
text-align: center;
cursor: pointer;
transition: var(--transition);
background: var(--bg-secondary);
border: 1px solid var(--border-color);
}
.time-slot:hover {
background: var(--primary-light);
border-color: var(--primary);
}
.time-slot.selected {
background: var(--primary);
color: white;
border-color: var(--primary-dark);
}
.time-slot.selected:hover {
background: var(--danger);
}
@media (max-width: 768px) {
.availability-grid {
grid-template-columns: repeat(3, 1fr);
}
}
@media (max-width: 480px) {
.availability-grid {
grid-template-columns: 1fr;
}
}
</style>
<script>
// Time slots from 8:00 AM to 10:00 PM (30-minute intervals)
const TIME_SLOTS = [];
for (let h = 8; h <= 22; h++) {
for (let m = 0; m < 60; m += 30) {
const timeStr = (h < 10 ? '0' : '') + h + ':' + (m < 10 ? '0' : '') + m;
const displayHour = h > 12 ? h - 12 : h;
const displayAmpm = h >= 12 ? 'PM' : 'AM';
const displayTime = displayHour + ':' + (m < 10 ? '0' : '') + m + ' ' + displayAmpm;
TIME_SLOTS.push({ time: timeStr, display: displayTime });
}
}
// Day names
const DAYS = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
// Track selected slots: {day_of_week: [time_strings]}
let selectedSlots = {};
// Initialize
document.addEventListener('DOMContentLoaded', function() {
loadExistingAvailability();
renderGrid();
});
function loadExistingAvailability() {
// Load from existing data
{% for av in existing_availability %}
if (!selectedSlots[{{ av.day_of_week }}]) {
selectedSlots[{{ av.day_of_week }}] = [];
}
selectedSlots[{{ av.day_of_week }}].push('{{ av.start_time.strftime('%H:%M') }}');
{% endfor %}
}
function renderGrid() {
const grid = document.getElementById('availability-grid');
let html = '';
DAYS.forEach((day, dayIndex) => {
html += '<div class="day-column">';
html += '<div class="day-header">' + day.substring(0, 3) + '</div>';
TIME_SLOTS.forEach(slot => {
const isSelected = selectedSlots[dayIndex] && selectedSlots[dayIndex].includes(slot.time);
const cssClass = isSelected ? 'time-slot selected' : 'time-slot';
html += '<div class="' + cssClass + '" data-day="' + dayIndex + '" data-time="' + slot.time + '" onclick="toggleSlot(' + dayIndex + ', \'' + slot.time + '\', this)">' + slot.display + '</div>';
});
html += '</div>';
});
grid.innerHTML = html;
}
function toggleSlot(dayOfWeek, timeStr, element) {
if (!selectedSlots[dayOfWeek]) {
selectedSlots[dayOfWeek] = [];
}
const index = selectedSlots[dayOfWeek].indexOf(timeStr);
if (index === -1) {
selectedSlots[dayOfWeek].push(timeStr);
element.classList.add('selected');
} else {
selectedSlots[dayOfWeek].splice(index, 1);
element.classList.remove('selected');
}
}
function saveAvailability() {
const slots = [];
for (let day in selectedSlots) {
selectedSlots[day].forEach(time => {
slots.push({ day_of_week: parseInt(day), start_time: time });
});
}
fetch('{{ url_for("users.manage_coach_availability") }}', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ slots: slots })
})
.then(response => response.json())
.then(data => {
if (data.success) {
flash('Availability saved!', 'success');
}
})
.catch(function(error) {
console.error('Save error:', error);
flash('Error saving availability.', 'danger');
});
}
function clearAllAvailability() {
if (!confirm('Are you sure you want to clear all your availability slots?')) {
return;
}
fetch('{{ url_for("users.clear_coach_availability") }}', { method: 'POST' })
.then(response => response.json())
.then(data => {
if (data.success) {
selectedSlots = {};
renderGrid();
flash('Availability cleared!', 'success');
}
});
}
function flash(message, type) {
const flashContainer = document.querySelector('.flash-messages');
const alert = document.createElement('div');
alert.className = 'alert alert-' + type + ' alert-dismissible';
alert.innerHTML = '<span>' + message + '</span><button type="button" class="alert-close" onclick="this.parentElement.remove()">&times;</button>';
flashContainer.appendChild(alert);
}
// Auto-save on change (debounced)
let saveTimeout;
document.addEventListener('click', function(e) {
if (e.target.classList.contains('time-slot')) {
clearTimeout(saveTimeout);
saveTimeout = setTimeout(saveAvailability, 1000);
}
});
</script>
{% endblock %}
+120
View File
@@ -0,0 +1,120 @@
{% extends "layouts/base.html" %}
{% block title %}Contracts - TryoutPro{% endblock %}
{% block page_title %}Contracts{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('users.profile') }}">Profile</a> / Contracts</span>{% endblock %}
{% block header_actions %}
{% if current_user.role in ['admin', 'manager', 'coach'] %}
<a href="{{ url_for('users.upload_contract') }}" class="btn btn-primary">
<i class="fas fa-upload"></i> Upload Contract
</a>
{% endif %}
{% endblock %}
{% block content %}
<div class="card">
<div class="card-header">
<h3><i class="fas fa-file-contract"></i> Contract Dropbox</h3>
</div>
<div class="card-body">
{% if contracts %}
<div class="table-container">
<table class="table">
<thead>
<tr>
<th>Player</th>
<th>Team</th>
<th>Contract</th>
<th>Status</th>
<th>Uploaded</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for contract in contracts %}
<tr>
<td>{{ contract.player.username }}</td>
<td>{{ contract.team.name if contract.team else 'N/A' }}</td>
<td>{{ contract.original_filename }}</td>
<td>
{% if contract.status == 'signed' %}
<span class="badge badge-success">Signed</span>
{% else %}
<span class="badge badge-warning">Pending Signature</span>
{% endif %}
</td>
<td>{{ contract.uploaded_at.strftime('%Y-%m-%d %H:%M') }}</td>
<td>
<div class="btn-group">
<a href="{{ url_for('users.download_contract', contract_id=contract.id) }}" class="btn btn-sm btn-primary" title="Download">
<i class="fas fa-download"></i> Download
</a>
{% if contract.signed_file_path %}
<a href="{{ url_for('users.download_signed_contract', contract_id=contract.id) }}" class="btn btn-sm btn-success" title="Download Signed">
<i class="fas fa-file-signature"></i> Signed
</a>
{% endif %}
{% if current_user.role == 'player' and contract.status == 'pending' %}
<button class="btn btn-sm btn-warning" onclick="showUploadSignedForm({{ contract.id }})" title="Upload Signed Contract">
<i class="fas fa-upload"></i> Return Signed
</button>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="empty-state">
<i class="fas fa-file-contract"></i>
<h4>No contracts found</h4>
{% if current_user.role == 'player' %}
<p>No contracts have been uploaded for you yet. Contact your coach or manager.</p>
{% else %}
<p>No contracts have been uploaded yet. Upload a contract using the button above.</p>
{% endif %}
</div>
{% endif %}
</div>
</div>
{% if current_user.role == 'player' %}
<!-- Upload Signed Contract Modal -->
<div id="uploadSignedModal" class="modal hidden">
<div class="modal-backdrop" onclick="hideUploadSignedForm()"></div>
<div class="modal-content">
<div class="modal-header">
<h3>Upload Signed Contract</h3>
<button class="modal-close" onclick="hideUploadSignedForm()">&times;</button>
</div>
<div class="modal-body">
<form id="uploadSignedForm" method="POST" enctype="multipart/form-data" class="form">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div class="form-group">
<label for="signed_file">Select Signed Contract File</label>
<input type="file" id="signed_file" name="signed_file" accept=".pdf,.doc,.docx,.jpg,.jpeg,.png" required>
<small class="form-text text-muted">Accepted formats: PDF, DOC, DOCX, JPG, PNG</small>
</div>
<div class="form-actions">
<button type="button" class="btn btn-secondary" onclick="hideUploadSignedForm()">Cancel</button>
<button type="submit" class="btn btn-primary">Upload Signed Contract</button>
</div>
</form>
</div>
</div>
</div>
{% endif %}
<script>
function showUploadSignedForm(contractId) {
document.getElementById('uploadSignedForm').action = '/users/contracts/' + contractId + '/upload_signed';
document.getElementById('uploadSignedModal').classList.remove('hidden');
}
function hideUploadSignedForm() {
document.getElementById('uploadSignedModal').classList.add('hidden');
}
</script>
{% endblock %}
+52
View File
@@ -0,0 +1,52 @@
{% extends "layouts/base.html" %}
{% block title %}Create User - TryoutPro{% endblock %}
{% block page_title %}Create User{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('users.list_users') }}">Users</a> / Create</span>{% endblock %}
{% block content %}
<div class="card">
<div class="card-body">
<form method="POST" action="{{ url_for('users.create_user') }}" class="form">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div class="form-row">
<div class="form-group col-6">
<label for="full_name">Full Name</label>
<input type="text" id="full_name" name="full_name" placeholder="Enter full name" required>
</div>
<div class="form-group col-6">
<label for="username">Username</label>
<input type="text" id="username" name="username" placeholder="Choose username" required>
</div>
</div>
<div class="form-row">
<div class="form-group col-6">
<label for="email">Email</label>
<input type="email" id="email" name="email" placeholder="Enter email" required>
</div>
<div class="form-group col-3">
<label for="phone">Phone</label>
<input type="tel" id="phone" name="phone" placeholder="Phone number">
</div>
<div class="form-group col-3">
<label for="role">Role</label>
<select id="role" name="role" required>
{% for r in roles %}
<option value="{{ r }}">{{ r | capitalize }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-row">
<div class="form-group col-6">
<label for="password">Password</label>
<input type="password" id="password" name="password" placeholder="Set password" required minlength="6">
</div>
</div>
<div class="form-actions">
<a href="{{ url_for('users.list_users') }}" class="btn btn-secondary">Cancel</a>
<button type="submit" class="btn btn-primary">Create User</button>
</div>
</form>
</div>
</div>
{% endblock %}
+381
View File
@@ -0,0 +1,381 @@
{% extends "layouts/base.html" %}
{% block title %}Dashboard - TryoutPro{% endblock %}
{% block page_title %}Dashboard{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / Dashboard</span>{% endblock %}
{% block content %}
<div class="dashboard">
{% if user.role == 'admin' %}
<div class="stats-grid">
<div class="stat-card">
<div class="stat-icon bg-primary">
<i class="fas fa-users"></i>
</div>
<div class="stat-info">
<h3>{{ stats.total_users }}</h3>
<p>Total Users</p>
</div>
</div>
<div class="stat-card">
<div class="stat-icon bg-success">
<i class="fas fa-user-friends"></i>
</div>
<div class="stat-info">
<h3>{{ stats.total_players }}</h3>
<p>Players</p>
</div>
</div>
<div class="stat-card">
<div class="stat-icon bg-warning">
<i class="fas fa-calendar-check"></i>
</div>
<div class="stat-info">
<h3>{{ stats.total_tryouts }}</h3>
<p>Total Tryouts</p>
</div>
</div>
<div class="stat-card">
<div class="stat-icon bg-info">
<i class="fas fa-clipboard-list"></i>
</div>
<div class="stat-info">
<h3>{{ stats.total_evaluations }}</h3>
<p>Evaluations</p>
</div>
</div>
<div class="stat-card">
<div class="stat-icon bg-danger">
<i class="fas fa-play-circle"></i>
</div>
<div class="stat-info">
<h3>{{ stats.active_tryouts }}</h3>
<p>Active Tryouts</p>
</div>
</div>
<div class="stat-card">
<div class="stat-icon bg-secondary">
<i class="fas fa-clock"></i>
</div>
<div class="stat-info">
<h3>{{ stats.upcoming_tryouts }}</h3>
<p>Upcoming</p>
</div>
</div>
</div>
<div class="dashboard-grid">
<div class="card">
<div class="card-header">
<h3><i class="fas fa-user-plus"></i> Recent Users</h3>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr><th>Name</th><th>Role</th><th>Joined</th></tr>
</thead>
<tbody>
{% for u in stats.recent_users %}
<tr>
<td>{{ u.username }}</td>
<td><span class="badge badge-{{ u.role }}">{{ u.role }}</span></td>
<td>{{ u.created_at.strftime('%m/%d/%Y') }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="card">
<div class="card-header">
<h3><i class="fas fa-calendar-alt"></i> Recent Tryouts</h3>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr><th>Title</th><th>Date</th><th>Status</th></tr>
</thead>
<tbody>
{% for t in stats.recent_tryouts %}
<tr>
<td><a href="{{ url_for('tryouts.view_tryout', tryout_id=t.id) }}">{{ t.title }}</a></td>
<td>{{ t.date.strftime('%m/%d/%Y') }}</td>
<td><span class="badge badge-{{ t.status }}">{{ t.status }}</span></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</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">
<div class="stat-icon bg-primary">
<i class="fas fa-calendar-alt"></i>
</div>
<div class="stat-info">
<h3>{{ stats.total_tryouts }}</h3>
<p>My Tryouts</p>
</div>
</div>
<div class="stat-card">
<div class="stat-icon bg-success">
<i class="fas fa-play-circle"></i>
</div>
<div class="stat-info">
<h3>{{ stats.active_tryouts }}</h3>
<p>Active</p>
</div>
</div>
<div class="stat-card">
<div class="stat-icon bg-info">
<i class="fas fa-clipboard-check"></i>
</div>
<div class="stat-info">
<h3>{{ stats.total_evaluations }}</h3>
<p>My Evaluations</p>
</div>
</div>
</div>
<div class="card mt-4">
<div class="card-header">
<h3><i class="fas fa-calendar"></i> My Tryouts</h3>
<a href="{{ url_for('tryouts.create_tryout') }}" class="btn btn-sm btn-primary">+ New Tryout</a>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr><th>Title</th><th>Date</th><th>Status</th><th>Players</th><th>Actions</th></tr>
</thead>
<tbody>
{% for t in stats.my_tryouts %}
<tr>
<td>{{ t.title }}</td>
<td>{{ t.date.strftime('%m/%d/%Y') }}</td>
<td><span class="badge badge-{{ t.status }}">{{ t.status }}</span></td>
<td>{{ t.registrations.count() }}</td>
<td><a href="{{ url_for('tryouts.view_tryout', tryout_id=t.id) }}" class="btn btn-sm btn-outline">View</a></td>
</tr>
{% endfor %}
{% if not stats.my_tryouts %}
<tr><td colspan="5" class="text-center">No tryouts yet. <a href="{{ url_for('tryouts.create_tryout') }}">Create one</a></td></tr>
{% endif %}
</tbody>
</table>
</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">
<div class="stat-icon bg-primary">
<i class="fas fa-clipboard-check"></i>
</div>
<div class="stat-info">
<h3>{{ stats.my_evaluations }}</h3>
<p>Evaluations Done</p>
</div>
</div>
<div class="stat-card">
<div class="stat-icon bg-warning">
<i class="fas fa-hourglass-half"></i>
</div>
<div class="stat-info">
<h3>{{ stats.pending_evaluations }}</h3>
<p>Pending</p>
</div>
</div>
</div>
<div class="card mt-4">
<div class="card-header">
<h3><i class="fas fa-history"></i> Recent Evaluations</h3>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr><th>Player</th><th>Tryout</th><th>Score</th><th>Date</th></tr>
</thead>
<tbody>
{% for e in stats.my_recent_evaluations %}
<tr>
<td>{{ e.player.username }}</td>
<td>{{ e.tryout.title }}</td>
<td><span class="score">{{ e.overall_score }}</span></td>
<td>{{ e.created_at.strftime('%m/%d/%Y') }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% elif user.role == 'player' %}
<div class="stats-grid">
<div class="stat-card">
<div class="stat-icon bg-primary">
<i class="fas fa-calendar-check"></i>
</div>
<div class="stat-info">
<h3>{{ stats.my_tryouts }}</h3>
<p>My Tryouts</p>
</div>
</div>
</div>
<div class="dashboard-grid">
<div class="card">
<div class="card-header">
<h3><i class="fas fa-futbol"></i> My Next Matches</h3>
</div>
<div class="card-body">
{% if stats.next_matches %}
<table class="table">
<thead><tr><th>Tryout</th><th>Match</th><th>Date & Time</th><th>Opponent</th></tr></thead>
<tbody>
{% for item in stats.next_matches %}
<tr>
<td><a href="{{ url_for('tryouts.view_tryout', tryout_id=item.tryout.id) }}">{{ item.tryout.title }}</a></td>
<td>{{ item.match.title }}</td>
<td>
{{ item.match.date.strftime('%m/%d/%Y') }}
{% if item.match.start_time %} at {{ item.match.start_time.strftime('%I:%M %p') }}{% endif %}
</td>
<td>
{% if item.match.match_type == 'team_vs_team' and item.team %}
{% if item.match.team1_id == item.team.id %}
vs {{ item.match.team2.name if item.match.team2 else 'TBD' }}
{% else %}
vs {{ item.match.team1.name if item.match.team1 else 'TBD' }}
{% endif %}
{% else %}
-
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="text-muted">No upcoming matches yet.</p>
{% endif %}
</div>
</div>
<div class="card">
<div class="card-header">
<h3><i class="fas fa-clipboard-list"></i> My Registrations</h3>
</div>
<div class="card-body">
<table class="table">
<thead><tr><th>Tryout</th><th>Date</th><th>Status</th></tr></thead>
<tbody>
{% for r in stats.my_registrations %}
<tr>
<td><a href="{{ url_for('tryouts.view_tryout', tryout_id=r.tryout.id) }}">{{ r.tryout.title }}</a></td>
<td>{{ r.tryout.date.strftime('%m/%d/%Y') }}</td>
<td><span class="badge badge-{{ r.status }}">{{ r.status }}</span></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% elif user.role == 'scout' %}
<div class="stats-grid">
<div class="stat-card">
<div class="stat-icon bg-primary">
<i class="fas fa-user-friends"></i>
</div>
<div class="stat-info">
<h3>{{ stats.total_players }}</h3>
<p>Total Players</p>
</div>
</div>
<div class="stat-card">
<div class="stat-icon bg-info">
<i class="fas fa-clipboard-list"></i>
</div>
<div class="stat-info">
<h3>{{ stats.total_evaluations }}</h3>
<p>Total Evaluations</p>
</div>
</div>
</div>
<div class="card mt-4">
<div class="card-header">
<h3><i class="fas fa-trophy"></i> Top Rated Players</h3>
</div>
<div class="card-body">
<table class="table">
<thead><tr><th>#</th><th>Player</th><th>Avg Score</th></tr></thead>
<tbody>
{% for player, score in stats.top_players %}
<tr>
<td>{{ loop.index }}</td>
<td>{{ player.username }}</td>
<td><span class="score">{{ score }}</span></td>
</tr>
{% endfor %}
{% if not stats.top_players %}
<tr><td colspan="3" class="text-center">No evaluations yet.</td></tr>
{% endif %}
</tbody>
</table>
</div>
</div>
{% endif %}
</div>
{% endblock %}
+341
View File
@@ -0,0 +1,341 @@
{% extends "layouts/base.html" %}
{% block title %}Edit Profile - TryoutPro{% endblock %}
{% block page_title %}Edit Profile{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('users.profile') }}">Profile</a> / Edit</span>{% endblock %}
{% block content %}
<div class="card">
<div class="card-body">
<form method="POST" action="{{ url_for('users.edit_profile') }}" class="form">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div class="form-row">
<div class="form-group col-6">
<label for="username">Username</label>
<input type="text" id="username" name="username" value="{{ user.username }}" required>
</div>
<div class="form-group col-6">
<label for="full_name">Full Name</label>
<input type="text" id="full_name" name="full_name" value="{{ user.full_name }}" required>
</div>
</div>
<div class="form-row">
<div class="form-group col-6">
<label for="email">Email</label>
<input type="email" id="email" name="email" value="{{ user.email }}" required>
</div>
<div class="form-group col-6">
<label for="phone">Phone</label>
<input type="tel" id="phone" name="phone" value="{{ user.phone or '' }}">
</div>
</div>
<hr class="section-divider">
<h4 class="section-title"><i class="fas fa-gamepad"></i> E-Sports Profile</h4>
<p class="text-muted small">Update your competitive gaming profile for tryouts.</p>
<div class="form-group">
<label><i class="fas fa-headset"></i> Games You Play</label>
<div class="checkbox-grid">
{% for game in esport_games %}
{% set games_list = user.get_games_list() %}
<label class="checkbox-label">
<input type="checkbox" name="games" value="{{ game }}" {% if game in games_list %}checked{% endif %}>
<span>{{ game }}</span>
</label>
{% endfor %}
</div>
<small class="form-text text-muted">Select all games you're signing in for.</small>
</div>
<!-- Gamertag inputs - will be shown when game is selected -->
<div id="gamertag-section" style="display: none;">
<hr class="section-divider">
<h4 class="section-title"><i class="fas fa-chart-line"></i> Gamertags for TRN</h4>
<p class="text-muted small">Enter your gamertag for each selected game to link to your Tracker Network profile.</p>
{% for game in esport_games %}
{% set gamertag_data = user_gamertags.get(game) %}
<div class="gamertag-input-row" data-game="{{ game }}" style="display: none;">
<h5>{{ game }}</h5>
<div class="form-row">
<div class="form-group col-6">
<label for="gamertag_{{ game }}">Gamertag</label>
<input type="text" id="gamertag_{{ game }}" name="gamertag_{{ game }}" value="{{ gamertag_data.gamertag if gamertag_data else '' }}" placeholder="Your {{ game }} gamertag">
</div>
{% if game_platforms.get(game) %}
<div class="form-group col-6">
<label for="platform_{{ game }}">Platform</label>
<select id="platform_{{ game }}" name="platform_{{ game }}">
<option value="">Select Platform</option>
{% for platform in game_platforms[game] %}
<option value="{{ platform }}" {% if gamertag_data and gamertag_data.platform == platform %}selected{% endif %}>{{ platform }}</option>
{% endfor %}
</select>
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
<div class="form-row">
<div class="form-group col-6">
<label for="discord_username"><i class="fab fa-discord"></i> Discord Username</label>
<input type="text" id="discord_username" name="discord_username" value="{{ user.discord_username or '' }}" placeholder="e.g. Name#1234">
</div>
<div class="form-group col-6">
<label for="discord_user_id"><i class="fab fa-discord"></i> Discord User ID <small>(for DMs)</small></label>
<input type="text" id="discord_user_id" name="discord_user_id" value="{{ user.discord_user_id or '' }}" placeholder="Numeric ID (e.g. 123456789012345678)">
<small class="text-muted">Enable Developer Mode in Discord → Right-click profile → Copy ID</small>
</div>
</div>
<div class="form-row">
<div class="form-group col-6">
<label for="league_os_profile"><i class="fas fa-link"></i> League OS Connection</label>
<input type="text" id="league_os_profile" name="league_os_profile" value="{{ user.league_os_profile or '' }}" placeholder="League OS profile link or ID">
</div>
</div>
<hr class="section-divider">
<h4 class="section-title"><i class="fas fa-lock"></i> Change Password</h4>
<p class="text-muted small">Leave blank to keep your current password.</p>
<div class="form-group">
<label for="password">New Password</label>
<input type="password" id="password" name="password" placeholder="Enter new password">
</div>
{% if user.role == 'player' %}
<hr class="section-divider">
<h4 class="section-title"><i class="fas fa-clock"></i> My Disponibilities</h4>
<p class="text-muted small">Select your available time blocks for matches (5pm to 12am). Green = selected, Gray = available to select.</p>
<div id="disponibilities-grid">
<p class="text-muted">Loading...</p>
</div>
<div class="form-actions">
<button type="button" class="btn btn-primary" onclick="saveDisponibilities()">
<i class="fas fa-save"></i> Save Disponibilities
</button>
<button type="button" class="btn btn-secondary" onclick="clearDisponibilities()">
<i class="fas fa-trash"></i> Clear All
</button>
</div>
{% endif %}
<div class="form-actions">
<a href="{{ url_for('users.profile') }}" class="btn btn-secondary">Cancel</a>
<button type="submit" class="btn btn-primary">Save Changes</button>
</div>
</form>
</div>
</div>
<script>
var GAME_PLATFORMS = {{ game_platforms|tojson }};
// Show/hide gamertag inputs when games are checked
function toggleGamertagInputs() {
var selectedGames = [];
document.querySelectorAll('input[name="games"]:checked').forEach(function(cb) {
selectedGames.push(cb.value);
});
if (selectedGames.length > 0) {
document.getElementById('gamertag-section').style.display = 'block';
} else {
document.getElementById('gamertag-section').style.display = 'none';
}
document.querySelectorAll('.gamertag-input-row').forEach(function(row) {
if (selectedGames.includes(row.dataset.game)) {
row.style.display = 'block';
} else {
row.style.display = 'none';
}
});
}
// Initialize on page load
document.addEventListener('DOMContentLoaded', function() {
// Set up event listeners for game checkboxes
document.querySelectorAll('input[name="games"]').forEach(function(cb) {
cb.addEventListener('change', toggleGamertagInputs);
});
// Show gamertag inputs for already selected games
toggleGamertagInputs();
{% if user.role == 'player' %}
renderDisponibilityGrid();
{% endif %}
});
{% if user.role == 'player' %}
// Generate time slots from 5pm (17:00) to 12am (24:00)
var TIME_SLOTS = [];
for (var h = 17; 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' }
];
// Store selected slots: {day: [time, time, ...]}
var selectedSlots = {};
function renderDisponibilityGrid() {
var grid = document.getElementById('disponibilities-grid');
grid.innerHTML = '<div style="margin-bottom: 10px;"><strong>Click time blocks to select your available hours</strong></div>';
var container = document.createElement('div');
container.className = 'disponibility-grid';
DAYS.forEach(function(day) {
var dayRow = document.createElement('div');
dayRow.className = 'disponibility-day-row';
var dayLabel = document.createElement('div');
dayLabel.className = 'disponibility-day-label';
dayLabel.textContent = day.name;
dayRow.appendChild(dayLabel);
var timeBlocks = document.createElement('div');
timeBlocks.className = 'disponibility-time-blocks';
TIME_SLOTS.forEach(function(slot) {
var block = document.createElement('div');
block.className = 'disponibility-time-block';
block.dataset.day = day.value;
block.dataset.time = slot.time;
block.textContent = slot.display;
block.onclick = function() {
toggleSlot(day.value, slot.time, block);
};
timeBlocks.appendChild(block);
});
dayRow.appendChild(timeBlocks);
container.appendChild(dayRow);
});
grid.appendChild(container);
loadMyDisponibilities();
}
function toggleSlot(day, time, element) {
if (!selectedSlots[day]) selectedSlots[day] = [];
var index = selectedSlots[day].indexOf(time);
if (index > -1) {
selectedSlots[day].splice(index, 1);
element.classList.remove('selected');
} else {
selectedSlots[day].push(time);
element.classList.add('selected');
}
}
function loadMyDisponibilities() {
fetch('{{ url_for("users.get_my_disponibilities") }}')
.then(function(response) { return response.json(); })
.then(function(data) {
selectedSlots = {};
for (var day in data) {
var slots = data[day];
slots.forEach(function(slot) {
selectedSlots[day] = selectedSlots[day] || [];
selectedSlots[day].push(slot.start_time);
});
}
document.querySelectorAll('.disponibility-time-block').forEach(function(block) {
var day = block.dataset.day;
var time = block.dataset.time;
if (selectedSlots[day] && selectedSlots[day].indexOf(time) > -1) {
block.classList.add('selected');
} else {
block.classList.remove('selected');
}
});
})
.catch(function(error) {
console.error('Error loading disponibilities:', error);
});
}
function saveDisponibilities() {
var slots = [];
for (var day in selectedSlots) {
selectedSlots[day].forEach(function(time) {
slots.push({ day_of_week: parseInt(day), start_time: time });
});
}
fetch('{{ url_for("users.add_disponibilities_bulk") }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ slots: slots })
})
.then(function(response) { return response.json(); })
.then(function(data) {
if (data.success) {
var msg = document.createElement('div');
msg.className = 'alert alert-success';
msg.style.marginTop = '10px';
msg.innerHTML = '<i class="fas fa-check"></i> Disponibilities saved successfully!';
document.getElementById('disponibilities-grid').appendChild(msg);
setTimeout(function() { msg.remove(); }, 3000);
}
})
.catch(function(error) {
console.error('Error saving disponibilities:', error);
});
}
function clearDisponibilities() {
if (!confirm('Are you sure you want to clear all your disponibilities?')) return;
fetch('{{ url_for("users.clear_disponibilities") }}', {
method: 'POST'
})
.then(function(response) { return response.json(); })
.then(function(data) {
if (data.success) {
selectedSlots = {};
document.querySelectorAll('.disponibility-time-block').forEach(function(block) {
block.classList.remove('selected');
});
}
});
}
{% endif %}
</script>
{% endblock %}
+151
View File
@@ -0,0 +1,151 @@
{% extends "layouts/base.html" %}
{% block title %}Edit {{ user.username }} - TryoutPro{% endblock %}
{% block page_title %}Edit User{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('users.list_users') }}">Users</a> / Edit</span>{% endblock %}
{% block content %}
<div class="card">
<div class="card-body">
<form method="POST" action="{{ url_for('users.edit_user', user_id=user.id) }}" class="form">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div class="form-row">
<div class="form-group col-6">
<label for="full_name">Full Name</label>
<input type="text" id="full_name" name="full_name" value="{{ user.username }}" required>
</div>
<div class="form-group col-6">
<label for="email">Email</label>
<input type="email" id="email" name="email" value="{{ user.email }}" required>
</div>
</div>
<div class="form-row">
<div class="form-group col-4">
<label for="phone">Phone</label>
<input type="tel" id="phone" name="phone" value="{{ user.phone or '' }}">
</div>
<div class="form-group col-4">
<label for="role">Role</label>
<select id="role" name="role" required>
{% for r in roles %}
<option value="{{ r }}" {% if user.role == r %}selected{% endif %}>{{ r | capitalize }}</option>
{% endfor %}
</select>
</div>
<div class="form-group col-4">
<label for="is_active_account">
<input type="checkbox" id="is_active_account" name="is_active_account" {% if user.is_active_account %}checked{% endif %}>
Active Account
</label>
</div>
</div>
<hr class="section-divider">
<h4 class="section-title"><i class="fas fa-gamepad"></i> E-Sports Profile</h4>
<div class="form-group">
<label>Games</label>
<div class="checkbox-grid">
{% for game in esport_games %}
{% set games_list = user.get_games_list() %}
<label class="checkbox-label">
<input type="checkbox" name="games" value="{{ game }}" {% if game in games_list %}checked{% endif %}>
<span>{{ game }}</span>
</label>
{% endfor %}
</div>
</div>
<!-- Gamertag inputs - will be shown when game is selected -->
<div id="gamertag-section" style="display: none;">
<hr class="section-divider">
<h4 class="section-title"><i class="fas fa-chart-line"></i> Gamertags for TRN</h4>
<p class="text-muted small">Enter gamertag for each selected game to link to Tracker Network.</p>
{% for game in esport_games %}
{% set gamertag_data = user_gamertags.get(game) %}
<div class="gamertag-input-row" data-game="{{ game }}" style="display: none;">
<h5>{{ game }}</h5>
<div class="form-row">
<div class="form-group col-6">
<label for="gamertag_{{ game }}">Gamertag</label>
<input type="text" id="gamertag_{{ game }}" name="gamertag_{{ game }}" value="{{ gamertag_data.gamertag if gamertag_data else '' }}" placeholder="Gamertag">
</div>
{% if game_platforms.get(game) %}
<div class="form-group col-6">
<label for="platform_{{ game }}">Platform</label>
<select id="platform_{{ game }}" name="platform_{{ game }}">
<option value="">Select Platform</option>
{% for platform in game_platforms[game] %}
<option value="{{ platform }}" {% if gamertag_data and gamertag_data.platform == platform %}selected{% endif %}>{{ platform }}</option>
{% endfor %}
</select>
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
<div class="form-row">
<div class="form-group col-4">
<label for="discord_username"><i class="fab fa-discord"></i> Discord Username</label>
<input type="text" id="discord_username" name="discord_username" value="{{ user.discord_username or '' }}" placeholder="e.g. Name#1234">
</div>
<div class="form-group col-4">
<label for="discord_user_id"><i class="fab fa-discord"></i> Discord User ID <small>(for DMs)</small></label>
<input type="text" id="discord_user_id" name="discord_user_id" value="{{ user.discord_user_id or '' }}" placeholder="Numeric ID (e.g. 123456789012345678)">
<small class="text-muted">Enable Developer Mode in Discord → Right-click profile → Copy ID</small>
</div>
<div class="form-group col-4">
<label for="league_os_profile"><i class="fas fa-link"></i> League OS Connection</label>
<input type="text" id="league_os_profile" name="league_os_profile" value="{{ user.league_os_profile or '' }}" placeholder="League OS profile link or ID">
</div>
</div>
<div class="form-group">
<label for="password">New Password <small>(leave blank to keep current)</small></label>
<input type="password" id="password" name="password" placeholder="Enter new password">
</div>
<div class="form-actions">
<a href="{{ url_for('users.list_users') }}" class="btn btn-secondary">Cancel</a>
<button type="submit" class="btn btn-primary">Update User</button>
</div>
</form>
</div>
</div>
<script>
// Show/hide gamertag inputs when games are checked
function toggleGamertagInputs() {
var selectedGames = [];
document.querySelectorAll('input[name="games"]:checked').forEach(function(cb) {
selectedGames.push(cb.value);
});
if (selectedGames.length > 0) {
document.getElementById('gamertag-section').style.display = 'block';
} else {
document.getElementById('gamertag-section').style.display = 'none';
}
document.querySelectorAll('.gamertag-input-row').forEach(function(row) {
if (selectedGames.includes(row.dataset.game)) {
row.style.display = 'block';
} else {
row.style.display = 'none';
}
});
}
// Initialize on page load
document.addEventListener('DOMContentLoaded', function() {
// Set up event listeners for game checkboxes
document.querySelectorAll('input[name="games"]').forEach(function(cb) {
cb.addEventListener('change', toggleGamertagInputs);
});
// Show gamertag inputs for already selected games
toggleGamertagInputs();
});
</script>
{% endblock %}
+184
View File
@@ -0,0 +1,184 @@
{% extends "layouts/base.html" %}
{% block title %}Evaluate {{ player.username }} - TryoutPro{% endblock %}
{% block page_title %}Evaluate {{ player.username }}{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}">{{ tryout.title }}</a> / Evaluate</span>{% endblock %}
{% block content %}
<div class="dashboard-grid">
<div class="card">
<div class="card-header">
<h3><i class="fas fa-clipboard-list"></i> Player Evaluation</h3>
<span class="badge badge-info">{{ tryout.title }} - {{ tryout.game }}</span>
</div>
<div class="card-body">
<div class="eval-player-info mb-4">
<div class="user-avatar avatar-lg">{{ player.username[:2] | upper }}</div>
<div>
<h3>{{ player.username }}</h3>
<p class="text-muted">{{ player.email }} | {{ player.phone or 'No phone' }}</p>
</div>
</div>
{% if existing_eval %}
<div class="alert alert-info">
<i class="fas fa-info-circle"></i> You have already evaluated this player. Your previous scores are shown below.
</div>
{% endif %}
<form method="POST" action="{{ url_for('evaluations.evaluate_player', tryout_id=tryout.id, player_id=player.id) }}" class="form">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div class="form-row">
<div class="form-group col-4">
<label for="mecanics_score">Mecanics (1-10)</label>
<div class="score-input">
<input type="range" id="mecanics_score" name="mecanics_score" min="1" max="10" value="{{ existing_eval.mecanics_score or 5 }}" oninput="this.nextElementSibling.textContent = this.value">
<span class="range-value">{{ existing_eval.mecanics_score or 5 }}</span>
</div>
</div>
<div class="form-group col-4">
<label for="cohesion_score">Cohesion (1-10)</label>
<div class="score-input">
<input type="range" id="cohesion_score" name="cohesion_score" min="1" max="10" value="{{ existing_eval.cohesion_score or 5 }}" oninput="this.nextElementSibling.textContent = this.value">
<span class="range-value">{{ existing_eval.cohesion_score or 5 }}</span>
</div>
</div>
<div class="form-group col-4">
<label for="communication_score">Communication (1-10)</label>
<div class="score-input">
<input type="range" id="communication_score" name="communication_score" min="1" max="10" value="{{ existing_eval.communication_score or 5 }}" oninput="this.nextElementSibling.textContent = this.value">
<span class="range-value">{{ existing_eval.communication_score or 5 }}</span>
</div>
</div>
</div>
<div class="form-row">
<div class="form-group col-4">
<label for="gamesense_score">Gamesense (1-10)</label>
<div class="score-input">
<input type="range" id="gamesense_score" name="gamesense_score" min="1" max="10" value="{{ existing_eval.gamesense_score or 5 }}" oninput="this.nextElementSibling.textContent = this.value">
<span class="range-value">{{ existing_eval.gamesense_score or 5 }}</span>
</div>
</div>
<div class="form-group col-4">
<label for="versatility_score">Versatility (1-10)</label>
<div class="score-input">
<input type="range" id="versatility_score" name="versatility_score" min="1" max="10" value="{{ existing_eval.versatility_score or 5 }}" oninput="this.nextElementSibling.textContent = this.value">
<span class="range-value">{{ existing_eval.versatility_score or 5 }}</span>
</div>
</div>
<div class="form-group col-4">
<label for="discipline_score">Discipline (1-10)</label>
<div class="score-input">
<input type="range" id="discipline_score" name="discipline_score" min="1" max="10" value="{{ existing_eval.discipline_score or 5 }}" oninput="this.nextElementSibling.textContent = this.value">
<span class="range-value">{{ existing_eval.discipline_score or 5 }}</span>
</div>
</div>
</div>
<div class="form-row">
<div class="form-group col-4">
<label for="analysis_score">Analysis (1-10)</label>
<div class="score-input">
<input type="range" id="analysis_score" name="analysis_score" min="1" max="10" value="{{ existing_eval.analysis_score or 5 }}" oninput="this.nextElementSibling.textContent = this.value">
<span class="range-value">{{ existing_eval.analysis_score or 5 }}</span>
</div>
</div>
<div class="form-group col-4">
<label for="sport_ethics_score">Sport Ethics (1-10)</label>
<div class="score-input">
<input type="range" id="sport_ethics_score" name="sport_ethics_score" min="1" max="10" value="{{ existing_eval.sport_ethics_score or 5 }}" oninput="this.nextElementSibling.textContent = this.value">
<span class="range-value">{{ existing_eval.sport_ethics_score or 5 }}</span>
</div>
</div>
<div class="form-group col-4">
<label for="mental_score">Mental (1-10)</label>
<div class="score-input">
<input type="range" id="mental_score" name="mental_score" min="1" max="10" value="{{ existing_eval.mental_score or 5 }}" oninput="this.nextElementSibling.textContent = this.value">
<span class="range-value">{{ existing_eval.mental_score or 5 }}</span>
</div>
</div>
</div>
{% set positions = game_positions.get(tryout.game, []) %}
<div class="form-row">
<div class="form-group col-12">
<label for="position_recommendation">Recommended Position</label>
{% if positions %}
<select id="position_recommendation" name="position_recommendation" class="form-select">
<option value="">-- Select Position --</option>
{% for pos in positions %}
<option value="{{ pos }}" {% if existing_eval.position_recommendation == pos %}selected{% endif %}>{{ pos }}</option>
{% endfor %}
</select>
{% else %}
<input type="text" id="position_recommendation" name="position_recommendation" value="{{ existing_eval.position_recommendation or '' }}" placeholder="Enter position (optional)">
{% endif %}
</div>
</div>
<div class="form-group">
<label for="comments">Comments</label>
<textarea id="comments" name="comments" rows="4" placeholder="Enter your evaluation notes...">{{ existing_eval.comments 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">
<i class="fas fa-save"></i> {% if existing_eval %}Update Evaluation{% else %}Submit Evaluation{% endif %}
</button>
<a href="{{ url_for('users.notes_dashboard') }}" class="btn btn-outline" title="Add note for this player">
<i class="fas fa-sticky-note"></i> Add Note
</a>
</div>
</form>
</div>
</div>
{% if evaluators %}
<div class="card">
<div class="card-header">
<h3><i class="fas fa-users"></i> All Evaluations for {{ player.username }}</h3>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th>Evaluator</th>
<th>Mecanics</th>
<th>Cohesion</th>
<th>Communication</th>
<th>Gamesense</th>
<th>Versatility</th>
<th>Discipline</th>
<th>Analysis</th>
<th>Sport Ethics</th>
<th>Mental</th>
<th>Overall</th>
<th>Position</th>
</tr>
</thead>
<tbody>
{% for entry in evaluators %}
<tr>
<td>{{ entry.evaluator.username }}</td>
<td>{{ entry.eval.mecanics_score or '-' }}</td>
<td>{{ entry.eval.cohesion_score or '-' }}</td>
<td>{{ entry.eval.communication_score or '-' }}</td>
<td>{{ entry.eval.gamesense_score or '-' }}</td>
<td>{{ entry.eval.versatility_score or '-' }}</td>
<td>{{ entry.eval.discipline_score or '-' }}</td>
<td>{{ entry.eval.analysis_score or '-' }}</td>
<td>{{ entry.eval.sport_ethics_score or '-' }}</td>
<td>{{ entry.eval.mental_score or '-' }}</td>
<td><span class="score">{{ entry.eval.overall_score or '-' }}</span></td>
<td>{{ entry.eval.position_recommendation or '-' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
</div>
{% endblock %}
+106
View File
@@ -0,0 +1,106 @@
{% extends "layouts/base.html" %}
{% block title %}Evaluations - TryoutPro{% endblock %}
{% block page_title %}Evaluations{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / Evaluations</span>{% endblock %}
{% block content %}
{% set sort_column = sort_column | default('created_at') %}
{% set sort_order = sort_order | default('desc') %}
{% macro sort_link(column, label) %}
<th>
{% set new_order = 'asc' if (sort_column == column and sort_order == 'desc') else 'desc' %}
<a href="{{ url_for('evaluations.list_evaluations', sort=column, order=new_order) }}" class="sort-link">
{{ label }}
{% if sort_column == column %}
<i class="fas fa-arrow-{{ 'up' if sort_order == 'asc' else 'down' }}"></i>
{% else %}
<i class="fas fa-arrows-alt-v sort-inactive"></i>
{% endif %}
</a>
</th>
{% endmacro %}
{% if current_user.role == 'admin' and player_scores %}
<div class="stats-grid mb-4">
{% for pid, data in player_scores.items() %}
<div class="stat-card stat-card-sm">
<div class="stat-icon bg-info">
<i class="fas fa-user"></i>
</div>
<div class="stat-info">
<h3>{{ data.player.username[:12] }}</h3>
<p>Avg: <strong>{{ data.avg }}</strong> / {{ data.count }} evals</p>
</div>
</div>
{% endfor %}
</div>
{% endif %}
<div class="card">
<div class="card-header">
<h3><i class="fas fa-clipboard-list"></i>
{% if current_user.role == 'player' %}
My Evaluations
{% else %}
All Evaluations
{% endif %}
</h3>
</div>
<div class="card-body">
<div class="table-container">
<table class="table">
<thead>
<tr>
{{ sort_link('tryout', 'Tryout') }}
{{ sort_link('player', 'Player') }}
{{ sort_link('evaluator', 'Evaluator') }}
{{ sort_link('mecanics_score', 'Mecanics') }}
{{ sort_link('cohesion_score', 'Cohesion') }}
{{ sort_link('communication_score', 'Communication') }}
{{ sort_link('gamesense_score', 'Gamesense') }}
{{ sort_link('versatility_score', 'Versatility') }}
{{ sort_link('discipline_score', 'Discipline') }}
{{ sort_link('analysis_score', 'Analysis') }}
{{ sort_link('sport_ethics_score', 'Sport Ethics') }}
{{ sort_link('mental_score', 'Mental') }}
{{ sort_link('overall_score', 'Overall') }}
{{ sort_link('position_recommendation', 'Position') }}
{{ sort_link('created_at', 'Date') }}
</tr>
</thead>
<tbody>
{% for eval in evaluations %}
<tr>
<td>{{ eval.tryout.title if eval.tryout else 'Deleted Tryout' }}</td>
<td>{{ eval.player.username if eval.player else 'Deleted Player' }}</td>
<td>{{ eval.evaluator.username if eval.evaluator else 'Deleted Evaluator' }}</td>
<td>{{ eval.mecanics_score or '-' }}</td>
<td>{{ eval.cohesion_score or '-' }}</td>
<td>{{ eval.communication_score or '-' }}</td>
<td>{{ eval.gamesense_score or '-' }}</td>
<td>{{ eval.versatility_score or '-' }}</td>
<td>{{ eval.discipline_score or '-' }}</td>
<td>{{ eval.analysis_score or '-' }}</td>
<td>{{ eval.sport_ethics_score or '-' }}</td>
<td>{{ eval.mental_score or '-' }}</td>
<td><span class="score">{{ eval.overall_score or '-' }}</span></td>
<td><span class="badge badge-info">{{ eval.position_recommendation or 'N/A' }}</span></td>
<td>{{ eval.created_at.strftime('%m/%d/%Y') }}</td>
</tr>
{% else %}
<tr>
<td colspan="15" class="text-center">
<div class="empty-state">
<i class="fas fa-clipboard"></i>
<h3>No evaluations yet</h3>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
+17
View File
@@ -0,0 +1,17 @@
{% extends "layouts/base.html" %}
{% block title %}Login - TryoutPro{% endblock %}
{% block auth_content %}
<form method="POST" action="{{ url_for('auth.login') }}" class="auth-form">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div class="form-group">
<label for="username"><i class="fas fa-user"></i> Username</label>
<input type="text" id="username" name="username" placeholder="Enter your username" required>
</div>
<div class="form-group">
<label for="password"><i class="fas fa-lock"></i> Password</label>
<input type="password" id="password" name="password" placeholder="Enter your password" required>
</div>
<button type="submit" class="btn btn-primary btn-block">Sign In</button>
<p class="auth-link">Don't have an account? <a href="{{ url_for('auth.register') }}">Register here</a></p>
</form>
{% endblock %}
File diff suppressed because it is too large Load Diff
+278
View File
@@ -0,0 +1,278 @@
{% extends "layouts/base.html" %}
{% block title %}My Team(s) - TryoutPro{% endblock %}
{% block page_title %}My Team(s){% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / My Team(s)</span>{% endblock %}
{% block content %}
{% if team_data %}
{% for item in team_data %}
{% set team = item.team %}
<div class="card mb-4">
<div class="card-header">
<h3><i class="fas fa-users"></i> {{ team.name }}</h3>
</div>
<!-- Staff bar -->
<div class="team-staff-bar">
<div class="staff-row">
<div class="staff-group">
<span class="staff-label"><i class="fas fa-chalkboard-teacher"></i> Coaches</span>
<div class="staff-items">
{% if item.coaches %}
{% for c in item.coaches %}
<span class="staff-tag">{{ c.username }}</span>
{% endfor %}
{% else %}
<span class="text-muted text-sm">None</span>
{% endif %}
</div>
</div>
<div class="staff-group">
<span class="staff-label"><i class="fas fa-user-tie"></i> Managers</span>
<div class="staff-items">
{% if item.managers %}
{% for m in item.managers %}
<span class="staff-tag manager-tag">{{ m.username }}</span>
{% endfor %}
{% else %}
<span class="text-muted text-sm">None</span>
{% endif %}
</div>
</div>
</div>
</div>
<div class="card-body">
<!-- Team Roster -->
<h4 class="mb-3"><i class="fas fa-users"></i> Team Roster</h4>
{% set roster = team.get_players_with_status() %}
{% if roster %}
<div class="table-container mb-4">
<table class="table">
<thead>
<tr>
<th>Player</th>
<th>Status</th>
<th>Position</th>
</tr>
</thead>
<tbody>
{% for entry in roster %}
<tr>
<td>
<div class="user-mini">
<div class="avatar-sm">{{ entry.player.username[:2] | upper }}</div>
<a href="{{ url_for('users.view_user', user_id=entry.player.id) }}">{{ entry.player.username }}</a>
</div>
</td>
<td>
<span class="badge {% if entry.status == 'starter' %}badge-success{% else %}badge-warning{% endif %}">
{{ entry.status | capitalize }}
</span>
</td>
<td>{{ entry.position or '—' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center py-3 text-muted">
<i class="fas fa-users-slash"></i> No players on this team.
</div>
{% endif %}
<!-- Team Matches -->
<h4 class="mb-3"><i class="fas fa-futbol"></i> Upcoming Matches</h4>
{% if item.matches %}
<div class="table-container">
<table class="table">
<thead>
<tr>
<th>Match</th>
<th>Opponent</th>
<th>Date</th>
<th>Time</th>
<th>Location</th>
<th>Presence</th>
<th>My Status</th>
</tr>
</thead>
<tbody>
{% for mdata in item.matches %}
{% set tm = mdata.match %}
<tr>
<td class="cell-title">{{ tm.title }}</td>
<td>
{% if tm.opponent %}
{{ tm.opponent }}
{% else %}
<span class="badge badge-info">Practice</span>
{% endif %}
</td>
<td>{{ tm.date.strftime('%m/%d/%Y') }}</td>
<td>
{% if tm.start_time and tm.end_time %}
{{ tm.start_time.strftime('%H:%M') }} - {{ tm.end_time.strftime('%H:%M') }}
{% else %}
TBD
{% endif %}
</td>
<td>{{ tm.location or '—' }}</td>
<td>
{% if mdata.total_count > 0 %}
<span title="{{ mdata.confirmed_count }} of {{ mdata.total_count }} confirmed">
{% if mdata.confirmed_count == mdata.total_count and mdata.total_count > 0 %}
✅ {{ mdata.confirmed_count }}/{{ mdata.total_count }}
{% elif mdata.confirmed_count > 0 %}
⏳ {{ mdata.confirmed_count }}/{{ mdata.total_count }}
{% else %}
❌ 0/{{ mdata.total_count }}
{% endif %}
</span>
{% else %}
<span class="text-muted"></span>
{% endif %}
</td>
<td>
{% if mdata.participant_id %}
<button class="btn btn-sm {% if mdata.is_confirmed %}btn-success{% else %}btn-outline{% endif %} presence-toggle-btn"
data-match-id="{{ tm.id }}"
data-participant-id="{{ mdata.participant_id }}"
onclick="togglePresence(this)">
{% if mdata.is_confirmed %}✅ Confirmed{% else %}Confirm{% endif %}
</button>
{% else %}
<span class="text-muted"></span>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center py-3 text-muted">
<i class="fas fa-calendar-alt"></i> No upcoming matches scheduled.
</div>
{% endif %}
</div>
</div>
{% endfor %}
{% else %}
<div class="card">
<div class="card-body text-center py-5">
<div class="empty-state">
<i class="fas fa-users fa-3x text-muted mb-3"></i>
<h3>No Teams</h3>
<p class="text-muted">You are not currently assigned to any team.</p>
</div>
</div>
</div>
{% endif %}
<script>
function togglePresence(btn) {
var matchId = btn.getAttribute('data-match-id');
var participantId = btn.getAttribute('data-participant-id');
fetch('/team-matches/' + matchId + '/toggle-presence/' + participantId, {
method: 'POST',
headers: {
'X-CSRFToken': '{{ csrf_token() }}',
'Content-Type': 'application/json'
}
})
.then(function(response) { return response.json(); })
.then(function(data) {
if (data.is_confirmed) {
btn.classList.add('btn-success');
btn.classList.remove('btn-outline');
btn.innerHTML = '✅ Confirmed';
} else {
btn.classList.remove('btn-success');
btn.classList.add('btn-outline');
btn.innerHTML = 'Confirm';
}
location.reload();
})
.catch(function(error) {
console.error('Error:', error);
});
}
</script>
<style>
.team-staff-bar {
padding: 10px 20px;
background: #f8fafc;
border-bottom: 1px solid #e2e8f0;
}
.staff-row {
display: flex;
align-items: center;
gap: 24px;
flex-wrap: wrap;
}
.staff-group {
display: flex;
align-items: center;
gap: 8px;
}
.staff-label {
font-size: 0.8rem;
color: #64748b;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
white-space: nowrap;
}
.staff-label i {
margin-right: 3px;
font-size: 0.75rem;
}
.staff-items {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.staff-tag {
display: inline-flex;
align-items: center;
gap: 5px;
background: #e0e7ff;
color: #3730a3;
padding: 3px 10px;
border-radius: 14px;
font-size: 0.82rem;
font-weight: 500;
line-height: 1.3;
}
.staff-tag.manager-tag {
background: #fef3c7;
color: #92400e;
}
.text-sm {
font-size: 0.82rem;
}
.presence-toggle-btn {
min-width: 100px;
}
/* Dark mode */
[data-theme="dark"] .team-staff-bar {
background: var(--bg-tertiary);
border-bottom-color: var(--border-color);
}
[data-theme="dark"] .staff-label {
color: var(--text-muted);
}
[data-theme="dark"] .staff-tag {
background: rgba(99, 102, 241, 0.2);
color: #a5b4fc;
}
[data-theme="dark"] .staff-tag.manager-tag {
background: rgba(229, 169, 57, 0.2);
color: #fcd34d;
}
</style>
{% endblock %}
+172
View File
@@ -0,0 +1,172 @@
{% extends "layouts/base.html" %}
{% block title %}Notes - TryoutPro{% endblock %}
{% block page_title %}Notes{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / Notes</span>{% endblock %}
{% block content %}
<div class="dashboard-grid">
<!-- Add Team Notes Section -->
{% if org_team %}
<div class="card">
<div class="card-header">
<h3><i class="fas fa-users"></i> Team Notes</h3>
<span class="badge badge-esport">{{ org_team.name }}</span>
</div>
<div class="card-body">
<form method="POST" action="{{ url_for('users.manage_team_notes') }}" class="form" id="teamNotesForm">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div class="form-group">
<label for="team_notes_content">Team Notes Content</label>
<textarea name="content" id="team_notes_content" class="form-textarea" rows="4" placeholder="Enter improvement suggestions and notes for your team...">{{ latest_team_note.content if latest_team_note else '' }}</textarea>
<p class="form-text">These notes will be visible to all players on your team.</p>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save"></i> {% if latest_team_note %}Update{% else %}Add{% endif %} Team Notes
</button>
</div>
</form>
</div>
</div>
{% endif %}
<!-- Add Personal Note Section -->
<div class="card">
<div class="card-header">
<h3><i class="fas fa-user-friends"></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" id="personalNoteForm">
<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.username }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label for="note_content">Note Content</label>
<textarea name="content" id="note_content" class="form-textarea" rows="3" 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="note_match_id">Match</label>
<select name="match_id" id="note_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="note_tryout_id">Tryout</label>
<select name="tryout_id" id="note_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="note_team_id">Team</label>
<select name="team_id" id="note_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>
</div>
</form>
</div>
</div>
</div>
<div class="dashboard-grid mt-4">
<!-- Team Notes History -->
{% if org_team and team_notes %}
<div class="card">
<div class="card-header">
<h3><i class="fas fa-history"></i> Team Notes History</h3>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th>Last Updated</th>
<th>Content Preview</th>
</tr>
</thead>
<tbody>
{% for note in team_notes %}
<tr>
<td>{{ note.updated_at.strftime('%B %d, %Y at %I:%M %p') if note.updated_at else 'Unknown date' }}</td>
<td>{{ note.content[:100] if note.content else '' }}{% if note.content and note.content|length > 100 %}...{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
<!-- Personal Notes History -->
{% if personal_notes %}
<div class="card">
<div class="card-header">
<h3><i class="fas fa-sticky-note"></i> Recent Personal Notes</h3>
</div>
<div class="card-body">
<div class="detail-grid">
{% for note in personal_notes %}
<div class="detail-item full-width mb-4">
<span class="detail-label">
<i class="fas fa-user"></i> {{ note.player.username if note.player else 'Unknown Player' }} -
<span class="text-muted">{{ note.created_at.strftime('%B %d, %Y') if note.created_at else 'Unknown date' }}</span>
</span>
<span class="detail-value">{{ note.content | nl2br if note.content else '' }}</span>
<span class="detail-label text-muted small">From: {{ note.coach.username if note.coach else 'Unknown Coach' }}</span>
{% if note.match_id or note.team_id or note.tryout_id %}
<div class="mt-2">
{% if note.match_id and note.match %}
<span class="badge badge-info" title="From match"><i class="fas fa-futbol"></i> {{ note.match.title }}</span>
{% endif %}
{% if note.team_id and note.team %}
<span class="badge badge-warning" title="From team"><i class="fas fa-users"></i> {{ note.team.name }}</span>
{% endif %}
{% if note.tryout_id and note.tryout %}
<span class="badge badge-success" title="From tryout"><i class="fas fa-calendar-alt"></i> {{ note.tryout.title }}</span>
{% endif %}
</div>
{% endif %}
</div>
{% endfor %}
</div>
</div>
</div>
{% endif %}
</div>
{% endblock %}
+250
View File
@@ -0,0 +1,250 @@
{% extends "layouts/base.html" %}
{% block title %}One on One - TryoutPro{% endblock %}
{% block page_title %}One on One{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / One on One</span>{% endblock %}
{% block content %}
<div class="dashboard-grid">
<!-- Team Notes Section -->
<div class="card">
<div class="card-header">
<h3><i class="fas fa-users"></i> Team Notes</h3>
{% if org_team %}
<span class="badge badge-esport">{{ org_team.name }}</span>
{% endif %}
</div>
<div class="card-body">
{% if team_notes %}
{% for note in team_notes %}
<div class="detail-grid mt-4">
<div class="detail-item full-width">
<span class="detail-label"><i class="fas fa-user"></i> Coach: {{ note.coach.username if note.coach else 'Unknown Coach' }}</span>
<span class="detail-value">{{ note.content | nl2br }}</span>
</div>
</div>
<p class="text-muted small mt-2">Updated: {{ note.updated_at.strftime('%B %d, %Y at %I:%M %p') }}</p>
{% endfor %}
{% else %}
<p class="text-muted">No team notes have been added yet. Your coach will post improvement suggestions here.</p>
{% endif %}
</div>
</div>
<!-- Personal Notes Section -->
<div class="card">
<div class="card-header">
<h3><i class="fas fa-user"></i> Personal Notes</h3>
{% if coach %}
<span class="badge badge-coach">From: {{ coach.username }}</span>
{% endif %}
</div>
<div class="card-body">
{% if personal_notes %}
{% for note in personal_notes %}
<div class="detail-grid mt-4">
<div class="detail-item full-width">
<span class="detail-label"><i class="fas fa-sticky-note"></i> Note from {{ note.coach.username if note.coach else 'Unknown Coach' }}</span>
<span class="detail-value">{{ note.content | nl2br }}</span>
</div>
</div>
<p class="text-muted small mt-2">Added: {{ note.created_at.strftime('%B %d, %Y at %I:%M %p') }}</p>
{% endfor %}
{% else %}
<p class="text-muted">No personal notes have been added yet. Your coach may provide individual feedback here.</p>
{% endif %}
</div>
</div>
<!-- One on One Request Section -->
<div class="card" style="grid-column: 1 / -1;">
<div class="card-header">
<h3><i class="fas fa-calendar-check"></i> Request One on One Session</h3>
{% if coach %}
<span class="badge badge-info">Coach: {{ coach.username }}</span>
{% endif %}
</div>
<div class="card-body">
{% if coach %}
<form method="POST" action="{{ url_for('users.one_on_one') }}" class="form" id="oneOnOneForm">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div class="form-row">
<div class="form-group">
<label for="date">Select Date</label>
<select name="date" id="date" class="form-select" onchange="updateTimeSlots()" required>
{% for d in dates %}
<option value="{{ d.value }}" data-day="{{ d.day_of_week }}">{{ d.display }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label for="start_time">Start Time</label>
<select name="start_time" id="start_time" class="form-select" onchange="updateEndTimeOptions()" required>
<option value="">-- Select Date First --</option>
</select>
</div>
<div class="form-group">
<label for="end_time">End Time</label>
<select name="end_time" id="end_time" class="form-select" required>
<option value="">-- Select Start Time First --</option>
</select>
</div>
</div>
<div class="form-group">
<label for="points">Discussion Points <span class="text-muted">(What would you like to discuss?)</span></label>
<textarea name="points" id="points" class="form-textarea" placeholder="Enter topics you'd like to cover in your One on One session..." rows="4"></textarea>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">
<i class="fas fa-paper-plane"></i> Send Request
</button>
</div>
</form>
{% else %}
<p class="text-muted">You need to be assigned to a team with a coach to request a One on One session.</p>
{% endif %}
</div>
</div>
</div>
{% if coach %}
<!-- Hidden data for JavaScript -->
<script id="coach-availability-data" type="application/json">
{{ coach_availability | tojson }}
</script>
{% endif %}
{% endblock %}
{% block scripts %}
<script>
// Time slots from 8:00 AM to 10:00 PM
const TIME_SLOTS = [];
for (let h = 8; h <= 22; h++) {
for (let m = 0; m < 60; m += 30) {
const displayHour = h > 12 ? h - 12 : h;
const displayAmpm = h >= 12 ? 'PM' : 'AM';
const timeStr = (h < 10 ? '0' : '') + h + ':' + (m < 10 ? '0' : '') + m;
const displayTime = displayHour + ':' + (m < 10 ? '0' : '') + m + ' ' + displayAmpm;
TIME_SLOTS.push({ time: timeStr, display: displayTime });
}
}
// Coach availability data
let coachAvailability = [];
// Initialize
document.addEventListener('DOMContentLoaded', function() {
loadCoachAvailability();
updateTimeSlots();
});
function loadCoachAvailability() {
const dataEl = document.getElementById('coach-availability-data');
if (!dataEl) return;
try {
coachAvailability = JSON.parse(dataEl.textContent);
} catch (e) {
coachAvailability = [];
}
}
function updateTimeSlots() {
const dateSelect = document.getElementById('date');
const startTimeSelect = document.getElementById('start_time');
const selectedOption = dateSelect.options[dateSelect.selectedIndex];
const dayOfWeek = parseInt(selectedOption.getAttribute('data-day'));
// Get available time slots for this day
const dayAvailability = coachAvailability.filter(av => av.day_of_week === dayOfWeek);
// Build available slots - collect all available minutes then sort
const availableSlots = [];
const availableMinutes = [];
dayAvailability.forEach(av => {
const startMinutes = av.start_time.split(':').reduce((acc, val, i) => acc + parseInt(val) * (i === 0 ? 60 : 1), 0);
const endMinutes = av.end_time.split(':').reduce((acc, val, i) => acc + parseInt(val) * (i === 0 ? 60 : 1), 0);
// Add 30-minute slots
for (let m = startMinutes; m < endMinutes; m += 30) {
availableMinutes.push(m);
}
});
// Sort minutes and convert to time strings
availableMinutes.sort((a, b) => a - b);
availableMinutes.forEach(m => {
const hour = Math.floor(m / 60);
const minute = m % 60;
const timeStr = (hour < 10 ? '0' : '') + hour + ':' + (minute < 10 ? '0' : '') + minute;
availableSlots.push(timeStr);
});
// Update start time options (sorted)
startTimeSelect.innerHTML = '<option value="">-- Select Start Time --</option>';
availableSlots.forEach(slot => {
const slotData = TIME_SLOTS.find(s => s.time === slot);
if (slotData) {
const option = document.createElement('option');
option.value = slotData.time;
option.textContent = slotData.display;
startTimeSelect.appendChild(option);
}
});
// Reset end time options
updateEndTimeOptions();
}
function updateEndTimeOptions() {
const dateSelect = document.getElementById('date');
const startTimeSelect = document.getElementById('start_time');
const endTimeSelect = document.getElementById('end_time');
const selectedOption = dateSelect.options[dateSelect.selectedIndex];
const dayOfWeek = parseInt(selectedOption.getAttribute('data-day'));
const selectedStart = startTimeSelect.value;
if (!selectedStart) {
endTimeSelect.innerHTML = '<option value="">-- Select Start Time First --</option>';
return;
}
// Get available minutes for this day
const dayAvailability = coachAvailability.filter(av => av.day_of_week === dayOfWeek);
const availableMinutes = [];
dayAvailability.forEach(av => {
const startMinutes = av.start_time.split(':').reduce((acc, val, i) => acc + parseInt(val) * (i === 0 ? 60 : 1), 0);
const endMinutes = av.end_time.split(':').reduce((acc, val, i) => acc + parseInt(val) * (i === 0 ? 60 : 1), 0);
for (let m = startMinutes; m < endMinutes; m += 30) {
availableMinutes.push(m);
}
});
// Convert selected start to minutes
const startMinutesVal = selectedStart.split(':').reduce((acc, val, i) => acc + parseInt(val) * (i === 0 ? 60 : 1), 0);
// Filter end times that are after start time
const validEndTimes = availableMinutes.filter(m => m > startMinutesVal);
validEndTimes.sort((a, b) => a - b);
// Update end time options
endTimeSelect.innerHTML = '<option value="">-- Select End Time --</option>';
validEndTimes.forEach(m => {
const hour = Math.floor(m / 60);
const minute = m % 60;
const timeStr = (hour < 10 ? '0' : '') + hour + ':' + (minute < 10 ? '0' : '') + minute;
const slotData = TIME_SLOTS.find(s => s.time === timeStr);
if (slotData) {
const option = document.createElement('option');
option.value = slotData.time;
option.textContent = slotData.display;
endTimeSelect.appendChild(option);
}
});
}
</script>
{% endblock %}
+64
View File
@@ -0,0 +1,64 @@
{% extends "layouts/base.html" %}
{% block title %}Personal Notes - TryoutPro{% endblock %}
{% block page_title %}Personal Notes{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / Personal Notes</span>{% endblock %}
{% block content %}
<div class="card">
<div class="card-header">
<h3><i class="fas fa-user-friends"></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.manage_personal_notes') }}" 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.username }}</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-actions">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save"></i> Add Note
</button>
</div>
</form>
</div>
</div>
{% if personal_notes %}
<div class="card mt-4">
<div class="card-header">
<h3><i class="fas fa-sticky-note"></i> Recent Notes</h3>
</div>
<div class="card-body">
<div class="detail-grid">
{% for note in personal_notes %}
<div class="detail-item full-width mb-4">
<span class="detail-label">
<i class="fas fa-user"></i> {{ note.player.username if note.player else 'Unknown Player' }} -
<span class="text-muted">{{ note.created_at.strftime('%B %d, %Y') if note.created_at else 'Unknown date' }}</span>
</span>
<span class="detail-value">{{ note.content | nl2br if note.content else '' }}</span>
<span class="detail-label text-muted small">From: {{ note.coach.username if note.coach else 'Unknown Coach' }}</span>
</div>
{% endfor %}
</div>
</div>
</div>
{% endif %}
{% endblock %}
@@ -0,0 +1,85 @@
{% extends "layouts/base.html" %}
{% block title %}My Notes - TryoutPro{% endblock %}
{% block page_title %}My Notes{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / One on One / My Notes</span>{% endblock %}
{% block content %}
<div class="dashboard-grid">
<!-- Personal Notes Section -->
<div class="card" style="grid-column: 1 / -1;">
<div class="card-header">
<h3><i class="fas fa-user"></i> Personal Notes</h3>
{% if org_team %}
<span class="badge badge-coach">Team: {{ org_team.name }}</span>
{% endif %}
</div>
<div class="card-body">
{% if personal_notes %}
{% for note in personal_notes %}
<div class="detail-grid mt-4">
<div class="detail-item full-width">
<span class="detail-label">
<i class="fas fa-sticky-note"></i> Note from {{ note.coach.username if note.coach else 'Unknown Coach' }}
</span>
<span class="detail-value">{{ note.content | nl2br }}</span>
<div class="mt-2">
{% if note.match_id and note.match %}
<span class="badge badge-info" title="From match">
<i class="fas fa-futbol"></i> Match: {{ note.match.title }}
</span>
{% endif %}
{% if note.team_id and note.team %}
<span class="badge badge-warning" title="From team">
<i class="fas fa-users"></i> Team: {{ note.team.name }}
</span>
{% endif %}
{% if note.tryout_id and note.tryout %}
<span class="badge badge-success" title="From tryout">
<i class="fas fa-calendar-alt"></i> Tryout: {{ note.tryout.title }}
</span>
{% endif %}
</div>
</div>
</div>
<p class="text-muted small mt-2">Added: {{ note.created_at.strftime('%B %d, %Y at %I:%M %p') }}</p>
{% endfor %}
{% else %}
<p class="text-muted">No personal notes have been added yet. Your coach may provide individual feedback here.</p>
{% endif %}
</div>
</div>
<!-- Team Notes Section -->
<div class="card" style="grid-column: 1 / -1;">
<div class="card-header">
<h3><i class="fas fa-users"></i> Team Notes</h3>
{% if org_team %}
<span class="badge badge-esport">{{ org_team.name }}</span>
{% endif %}
</div>
<div class="card-body">
{% if team_notes %}
{% for note in team_notes %}
<div class="detail-grid mt-4">
<div class="detail-item full-width">
<span class="detail-label"><i class="fas fa-user"></i> Coach: {{ note.coach.username if note.coach else 'Unknown Coach' }}</span>
<span class="detail-value">{{ note.content | nl2br }}</span>
</div>
</div>
<p class="text-muted small mt-2">Updated: {{ note.updated_at.strftime('%B %d, %Y at %I:%M %p') }}</p>
{% endfor %}
{% else %}
<p class="text-muted">No team notes have been added yet. Your coach will post improvement suggestions here.</p>
{% endif %}
</div>
</div>
</div>
<div class="card mt-4">
<div class="card-body text-center">
<a href="{{ url_for('users.one_on_one') }}" class="btn btn-primary">
<i class="fas fa-calendar-check"></i> Request One on One Session
</a>
</div>
</div>
{% endblock %}
@@ -0,0 +1,59 @@
{% extends "layouts/base.html" %}
{% block title %}Players to Evaluate - TryoutPro{% endblock %}
{% block page_title %}Players to Evaluate{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}">{{ tryout.title }}</a> / Evaluate</span>{% endblock %}
{% block content %}
<div class="card">
<div class="card-header">
<h3><i class="fas fa-users"></i> Players in {{ tryout.title }}</h3>
</div>
<div class="card-body">
<div class="table-container">
<table class="table">
<thead>
<tr>
<th>Player</th>
<th>Contact</th>
<th>Attendance</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for entry in players %}
<tr>
<td>
<div class="user-mini">
<div class="avatar-sm">{{ entry.player.username[:2] | upper }}</div>
<span>{{ entry.player.username }}</span>
</div>
</td>
<td>{{ entry.player.email }}</td>
<td>
<span class="badge badge-{{ entry.registration.status }}">{{ entry.registration.status }}</span>
</td>
<td>
{% if entry.evaluated %}
<span class="badge badge-success">Evaluated</span>
{% else %}
<span class="badge badge-warning">Not Evaluated</span>
{% endif %}
</td>
<td>
<a href="{{ url_for('evaluations.evaluate_player', tryout_id=tryout.id, player_id=entry.player.id) }}" class="btn btn-sm btn-primary">
<i class="fas fa-clipboard"></i> {% if entry.evaluated %}View/Edit{% else %}Evaluate{% endif %}
</a>
</td>
</tr>
{% else %}
<tr>
<td colspan="5" class="text-center">No players registered for this tryout.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
+184
View File
@@ -0,0 +1,184 @@
{% extends "layouts/base.html" %}
{% block title %}My Profile - TryoutPro{% endblock %}
{% block page_title %}My Profile{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / Profile</span>{% endblock %}
{% block header_actions %}
<div class="header-actions">
<a href="{{ url_for('users.list_contracts') }}" class="btn btn-sm btn-info">
<i class="fas fa-file-contract"></i> Contracts
</a>
<a href="{{ url_for('users.edit_profile') }}" class="btn btn-sm btn-primary">
<i class="fas fa-edit"></i> Edit Profile
</a>
</div>
{% endblock %}
{% block content %}
<div class="dashboard-grid">
<div class="card">
<div class="card-header">
<h3><i class="fas fa-user-circle"></i> Account Information</h3>
</div>
<div class="card-body">
<div class="profile-header">
<div class="user-avatar avatar-xl">{{ user.username[:2] | upper }}</div>
<div class="profile-info">
<h2>{{ user.username }}</h2>
<span class="text-muted" style="font-size: 1.1em;">{{ user.full_name }}</span>
<span class="badge badge-{{ user.role }} badge-lg">{{ user.role | capitalize }}</span>
<p class="text-muted">Member since {{ user.created_at.strftime('%B %Y') }}</p>
</div>
</div>
<div class="detail-grid mt-4">
<div class="detail-item">
<span class="detail-label">Username</span>
<span class="detail-value">{{ user.username }}</span>
</div>
<div class="detail-item">
<span class="detail-label">Email</span>
<span class="detail-value">{{ user.email }}</span>
</div>
<div class="detail-item">
<span class="detail-label">Phone</span>
<span class="detail-value">{{ user.phone or 'Not provided' }}</span>
</div>
<div class="detail-item">
<span class="detail-label">Account Status</span>
<span class="detail-value">
{% if user.is_active_account %}
<span class="badge badge-success">Active</span>
{% else %}
<span class="badge badge-danger">Inactive</span>
{% endif %}
</span>
</div>
</div>
</div>
</div>
<!-- E-Sports Profile Card -->
<div class="card">
<div class="card-header">
<h3><i class="fas fa-gamepad"></i> E-Sports Profile</h3>
</div>
<div class="card-body">
<div class="detail-grid">
<div class="detail-item">
<span class="detail-label"><i class="fas fa-gamepad"></i> Gamertags</span>
<span class="detail-value">
{% set games_list = user.get_games_list() %}
{% if games_list %}
<div style="display: flex; flex-direction: column; gap: 8px;">
{% for game in games_list %}
{% set gt = user.gamertags | selectattr('game', 'equalto', game) | first %}
<div>
{% if gt and gt.get_trn_url() %}
<a href="{{ gt.get_trn_url() }}" target="_blank" rel="noopener noreferrer" class="trn-link">
<span class="badge badge-esport" style="margin-right: 8px;">{{ game }}</span>
<i class="fas fa-external-link-alt"></i> {{ gt.gamertag }}
{% if gt.platform %}<small>({{ gt.platform }})</small>{% endif %}
</a>
{% else %}
<span class="badge badge-esport">{{ game }}</span>
{% endif %}
</div>
{% endfor %}
</div>
{% else %}
<span class="text-muted">Not specified</span>
{% endif %}
</span>
</div>
<div class="detail-item">
<span class="detail-label"><i class="fab fa-discord"></i> Discord</span>
<span class="detail-value">
{% if user.discord_username %}
{{ user.discord_username }}
{% else %}
<span class="text-muted">Not connected</span>
{% endif %}
</span>
</div>
<div class="detail-item">
<span class="detail-label"><i class="fas fa-link"></i> League OS</span>
<span class="detail-value">
{% if user.league_os_profile %}
<a href="{{ user.league_os_profile }}" target="_blank" rel="noopener noreferrer" class="trn-link">
<i class="fas fa-external-link-alt"></i> {{ user.league_os_profile }}
</a>
{% else %}
<span class="text-muted">Not connected</span>
{% endif %}
</span>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h3><i class="fas fa-chart-bar"></i> My Statistics</h3>
</div>
<div class="card-body">
{% if user.role == 'player' %}
<div class="profile-stats">
<div class="stat-item">
<h4>{{ user.tryout_registrations.count() }}</h4>
<p>Tryouts Registered</p>
</div>
<div class="stat-item">
<h4>{{ user.team_assignments.count() }}</h4>
<p>Team Assignments</p>
</div>
</div>
{% elif user.can_evaluate() %}
<div class="profile-stats">
<div class="stat-item">
<h4>{{ user.evaluations_given.count() }}</h4>
<p>Evaluations Given</p>
</div>
</div>
{% else %}
<p class="text-muted">No statistics available for this role.</p>
{% endif %}
</div>
</div>
<!-- Contracts Card -->
{% if user.role == 'player' %}
<div class="card">
<div class="card-header">
<h3><i class="fas fa-file-contract"></i> My Contracts</h3>
</div>
<div class="card-body">
{% if contracts %}
<div class="detail-grid">
<div class="detail-item">
<span class="detail-label">Contracts Pending</span>
<span class="detail-value">
{% set pending = contracts | selectattr('status', 'equalto', 'pending') | list %}
<span class="badge badge-{{ 'warning' if pending|length > 0 else 'success' }}">{{ pending | length }}</span>
</span>
</div>
<div class="detail-item">
<span class="detail-label">Contracts Signed</span>
<span class="detail-value">
{% set signed = contracts | selectattr('status', 'equalto', 'signed') | list %}
<span class="badge {% if signed %}badge-success{% else %}badge-secondary{% endif %}">{{ signed | length }}</span>
</span>
</div>
</div>
<div class="mt-3">
<a href="{{ url_for('users.list_contracts') }}" class="btn btn-sm btn-primary">
<i class="fas fa-folder-open"></i> View All Contracts
</a>
</div>
{% else %}
<p class="text-muted">No contracts have been uploaded for you yet.</p>
{% endif %}
</div>
</div>
{% endif %}
</div>
{% endblock %}
+66
View File
@@ -0,0 +1,66 @@
{% extends "layouts/base.html" %}
{% block title %}Register - TryoutPro{% endblock %}
{% block auth_content %}
<form method="POST" action="{{ url_for('auth.register') }}" class="auth-form">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div class="form-group">
<label for="full_name"><i class="fas fa-id-card"></i> Full Name</label>
<input type="text" id="full_name" name="full_name" placeholder="Enter your full name" required>
</div>
<div class="form-group">
<label for="username"><i class="fas fa-user"></i> Username</label>
<input type="text" id="username" name="username" placeholder="Choose a username" required>
</div>
<div class="form-group">
<label for="email"><i class="fas fa-envelope"></i> Email</label>
<input type="email" id="email" name="email" placeholder="Enter your email" required>
</div>
<div class="form-group">
<label for="phone"><i class="fas fa-phone"></i> Phone (Optional)</label>
<input type="tel" id="phone" name="phone" placeholder="Enter your phone number">
</div>
<hr class="section-divider">
<h4 class="section-title"><i class="fas fa-gamepad"></i> E-Sports Profile</h4>
<p class="text-muted small">Set up your competitive gaming profile for tryouts.</p>
<div class="form-group">
<label><i class="fas fa-headset"></i> Games You Play</label>
<div class="checkbox-grid">
{% for game in esport_games %}
<label class="checkbox-label">
<input type="checkbox" name="games" value="{{ game }}">
<span>{{ game }}</span>
</label>
{% endfor %}
</div>
<small class="form-text text-muted">Select all games you're signing in for.</small>
</div>
<div class="form-group">
<label for="trn_username"><i class="fas fa-chart-line"></i> TRN (Tracker Network) Username</label>
<input type="text" id="trn_username" name="trn_username" placeholder="e.g. YourTrackerGGUsername">
<small class="form-text text-muted">Your public Tracker Network profile name. Others can click it to view your stats.</small>
</div>
<div class="form-group">
<label for="discord_username"><i class="fab fa-discord"></i> Discord Username</label>
<input type="text" id="discord_username" name="discord_username" placeholder="e.g. YourName#1234">
</div>
<div class="form-group">
<label for="league_os_profile"><i class="fas fa-link"></i> League OS Connection</label>
<input type="text" id="league_os_profile" name="league_os_profile" placeholder="League OS profile link or ID">
<small class="form-text text-muted">Connect your League OS profile for organized play.</small>
</div>
<hr class="section-divider">
<div class="form-group">
<label for="password"><i class="fas fa-lock"></i> Password</label>
<input type="password" id="password" name="password" placeholder="Create a password" required minlength="6">
</div>
<div class="form-group">
<label for="confirm_password"><i class="fas fa-check-circle"></i> Confirm Password</label>
<input type="password" id="confirm_password" name="confirm_password" placeholder="Confirm your password" required>
</div>
<button type="submit" class="btn btn-primary btn-block">Create Account</button>
<p class="auth-link">Already have an account? <a href="{{ url_for('auth.login') }}">Sign in</a></p>
</form>
{% endblock %}
+149
View File
@@ -0,0 +1,149 @@
{% extends "layouts/base.html" %}
{% block title %}{% if match %}Edit{% else %}Schedule{% endif %} Team Match - TryoutPro{% endblock %}
{% block page_title %}{% if match %}Edit{% else %}Schedule{% endif %} Team Match{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('team_matches.list_matches') }}">Team Matches</a> / {% if match %}Edit{% else %}New{% endif %}</span>{% endblock %}
{% block content %}
<div class="card mb-4">
<div class="card-header">
<h3><i class="fas fa-futbol"></i> {% if match %}Edit Match{% else %}Schedule New Match{% endif %} — {{ team.name }}</h3>
</div>
<div class="card-body">
<form method="POST" action="{% if match %}{{ url_for('team_matches.edit_match', match_id=match.id) }}{% else %}{{ url_for('team_matches.create_match', team_id=team.id) }}{% endif %}" class="form">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div class="form-row">
<div class="form-group col-6">
<label for="title">Match Title</label>
<input type="text" id="title" name="title" class="form-input"
value="{{ match.title if match else 'Team Match — ' + team.name }}"
placeholder="e.g., Regular Season vs Opponent" required>
</div>
{% if not is_practice %}
<div class="form-group col-6">
<label for="opponent">Opponent (optional)</label>
<input type="text" id="opponent" name="opponent" class="form-input"
value="{{ match.opponent if match else '' }}"
placeholder="e.g., University of Toronto">
</div>
{% endif %}
</div>
<div class="form-row">
<div class="form-group col-4">
<label for="date">Date</label>
<input type="date" id="date" name="date" class="form-input"
value="{{ match.date.strftime('%Y-%m-%d') if match else '' }}"
required>
</div>
<div class="form-group col-4">
<label for="start_time">Start Time</label>
<input type="time" id="start_time" name="start_time" class="form-input"
value="{{ match.start_time.strftime('%H:%M') if match and match.start_time else '' }}">
</div>
<div class="form-group col-4">
<label for="end_time">End Time</label>
<input type="time" id="end_time" name="end_time" class="form-input"
value="{{ match.end_time.strftime('%H:%M') if match and match.end_time else '' }}">
<small class="text-muted">Auto-calculated if left empty (start + 30 min)</small>
</div>
</div>
<div class="form-row">
<div class="form-group col-6">
<label for="location">Location</label>
<input type="text" id="location" name="location" class="form-input"
value="{{ match.location if match else '' }}"
placeholder="e.g., Online, Gym A">
</div>
{% if match %}
<div class="form-group col-6">
<label for="status">Status</label>
<select id="status" name="status" class="form-select">
<option value="scheduled" {% if match.status == 'scheduled' %}selected{% endif %}>Scheduled</option>
<option value="completed" {% if match.status == 'completed' %}selected{% endif %}>Completed</option>
<option value="cancelled" {% if match.status == 'cancelled' %}selected{% endif %}>Cancelled</option>
</select>
</div>
{% endif %}
</div>
<div class="form-group">
<label for="description">Description (optional)</label>
<textarea id="description" name="description" class="form-input" rows="3"
placeholder="Additional details about the match...">{{ match.description if match else '' }}</textarea>
</div>
{% if not match %}
<!-- Player roster (pre-filled, read-only display) -->
<div class="card mt-4">
<div class="card-header">
<h4><i class="fas fa-users"></i> Team Roster (auto-included)</h4>
<span class="text-muted" style="font-size: 0.85rem;">All {{ team_players | length }} player(s) will be added automatically</span>
</div>
<div class="card-body">
{% if team_players %}
<div class="roster-list" style="display: flex; flex-wrap: wrap; gap: 8px;">
{% for tp in team_players %}
<span class="staff-tag">
{{ tp.player.username }}
{% if tp.status == 'substitute' %}
<span class="text-muted" style="font-size: 0.7rem;">(sub)</span>
{% endif %}
</span>
{% endfor %}
</div>
{% else %}
<p class="text-muted text-center py-3">
<i class="fas fa-users-slash"></i> No players on this team. Add players in the Teams page first.
</p>
{% endif %}
</div>
</div>
{% else %}
<!-- Edit mode: show participants with presence status -->
<div class="card mt-4">
<div class="card-header">
<h4><i class="fas fa-users"></i> Participants</h4>
</div>
<div class="card-body">
{% if match.participants %}
<table class="table">
<thead>
<tr>
<th>Player</th>
<th>Presence</th>
</tr>
</thead>
<tbody>
{% for p in match.participants.all() %}
<tr>
<td>{{ p.player.username if p.player else 'Unknown' }}</td>
<td>
{% if p.is_confirmed %}
<span class="badge badge-success">✅ Confirmed</span>
{% else %}
<span class="badge badge-warning">⏳ Pending</span>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="text-muted text-center">No participants recorded.</p>
{% endif %}
</div>
</div>
{% endif %}
<div class="form-actions mt-4">
<a href="{{ url_for('team_matches.list_matches') }}" class="btn btn-secondary">Cancel</a>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save"></i> {% if match %}Save Changes{% else %}Schedule Match{% endif %}
</button>
</div>
</form>
</div>
</div>
{% endblock %}
+230
View File
@@ -0,0 +1,230 @@
{% extends "layouts/base.html" %}
{% block title %}Team Matches - TryoutPro{% endblock %}
{% block page_title %}Team Matches{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / Team Matches</span>{% endblock %}
{% block header_actions %}
{% if teams %}
<div class="header-actions">
<select id="teamSelect" class="form-select" style="width:200px;" onchange="window.location.href='/team-matches/' + this.value + '/create'">
<option value="">+ Schedule Match</option>
{% for t in teams %}
<option value="{{ t.id }}">{{ t.name }}</option>
{% endfor %}
</select>
</div>
{% endif %}
{% endblock %}
{% block content %}
{% if match_data %}
<div class="table-container">
<table class="table">
<thead>
<tr>
<th>Match</th>
<th>Team</th>
<th>Opponent</th>
<th>Date</th>
<th>Time</th>
<th>Location</th>
<th>Presence</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for item in match_data %}
{% set m = item.match %}
<tr>
<td class="cell-title">{{ m.title }}</td>
<td>
<span class="badge badge-info">{{ m.org_team.name }}</span>
</td>
<td>
{% if m.opponent %}
{{ m.opponent }}
{% else %}
<span class="badge badge-info">Practice</span>
{% endif %}
</td>
<td>{{ m.date.strftime('%m/%d/%Y') }}</td>
<td>
{% if m.start_time and m.end_time %}
{{ m.start_time.strftime('%H:%M') }} - {{ m.end_time.strftime('%H:%M') }}
{% else %}
TBD
{% endif %}
</td>
<td>{{ m.location or '—' }}</td>
<td>
{% if item.total_count > 0 %}
<div class="presence-bar" title="{{ item.confirmed_count }} of {{ item.total_count }} confirmed">
<div class="presence-progress">
{% set pct = (item.confirmed_count / item.total_count * 100) | int %}
<div class="presence-fill" style="width: {{ pct }}%;"></div>
</div>
<span class="presence-text">
{% if item.confirmed_count == item.total_count and item.total_count > 0 %}
✅ {{ item.confirmed_count }}/{{ item.total_count }}
{% elif item.confirmed_count > 0 %}
⏳ {{ item.confirmed_count }}/{{ item.total_count }}
{% else %}
❌ 0/{{ item.total_count }}
{% endif %}
</span>
</div>
<!-- Player presence details -->
<div class="presence-players">
{% for p in item.participants %}
<span class="presence-player-tag {% if p.is_confirmed %}confirmed{% else %}pending{% endif %}"
title="{{ p.player.username }}{% if p.is_confirmed %} - Confirmed{% else %} - Pending{% endif %}">
{{ p.player.username[:2] | upper }} {{ p.player.username }}
{% if p.is_confirmed %}✅{% else %}⏳{% endif %}
</span>
{% endfor %}
</div>
{% else %}
<span class="text-muted"></span>
{% endif %}
</td>
<td>
<span class="badge badge-{{ m.status }}">{{ m.status }}</span>
</td>
<td class="eval-actions">
{% set can_manage_this = (current_user.role in ['admin', 'manager']) or (current_user.role == 'coach' and m.org_team.coaches.filter_by(id=current_user.id).first()) or (current_user.role == 'coach' and m.org_team.coach_id == current_user.id) %}
{% if can_manage_this %}
<a href="{{ url_for('team_matches.edit_match', match_id=m.id) }}" class="btn btn-sm btn-outline" title="Edit Match">
<i class="fas fa-edit"></i>
</a>
<form method="POST" action="{{ url_for('team_matches.delete_match', match_id=m.id) }}" class="inline-form" onsubmit="return confirm('Delete this match?')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<button type="submit" class="btn btn-sm btn-danger" title="Delete Match">
<i class="fas fa-trash"></i>
</button>
</form>
{% endif %}
<!-- Toggle presence for each participant if user is player -->
{% if current_user.role == 'player' %}
{% for p in item.participants %}
{% if p.player.id == current_user.id %}
<button class="btn btn-sm {% if p.is_confirmed %}btn-success{% else %}btn-outline{% endif %} presence-toggle-btn"
data-match-id="{{ m.id }}"
data-participant-id="{{ p.id }}"
onclick="togglePresence(this)">
{% if p.is_confirmed %}✅ Confirmed{% else %}Confirm{% endif %}
</button>
{% endif %}
{% endfor %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="card">
<div class="card-body text-center py-5">
<div class="empty-state">
<i class="fas fa-futbol fa-3x text-muted mb-3"></i>
<h3>No Team Matches</h3>
<p class="text-muted">Regular season matches have not been scheduled yet.</p>
{% if teams %}
<div class="mt-3">
<select id="teamSelectEmpty" class="form-select" style="width:220px; display:inline;" onchange="window.location.href='/team-matches/' + this.value + '/create'">
<option value="">-- Schedule a Match --</option>
{% for t in teams %}
<option value="{{ t.id }}">{{ t.name }}</option>
{% endfor %}
</select>
</div>
{% endif %}
</div>
</div>
</div>
{% endif %}
<script>
function togglePresence(btn) {
var matchId = btn.getAttribute('data-match-id');
var participantId = btn.getAttribute('data-participant-id');
fetch('/team-matches/' + matchId + '/toggle-presence/' + participantId, {
method: 'POST',
headers: {
'X-CSRFToken': '{{ csrf_token() }}',
'Content-Type': 'application/json'
}
})
.then(function(response) { return response.json(); })
.then(function(data) {
if (data.is_confirmed) {
btn.classList.add('btn-success');
btn.classList.remove('btn-outline');
btn.innerHTML = '✅ Confirmed';
} else {
btn.classList.remove('btn-success');
btn.classList.add('btn-outline');
btn.innerHTML = 'Confirm';
}
// Reload to update presence bar
location.reload();
})
.catch(function(error) {
console.error('Error:', error);
});
}
</script>
<style>
.presence-bar {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 4px;
}
.presence-progress {
width: 60px;
height: 6px;
background: #e5e7eb;
border-radius: 3px;
overflow: hidden;
}
.presence-fill {
height: 100%;
background: #10b981;
border-radius: 3px;
}
.presence-text {
font-size: 0.8rem;
font-weight: 600;
}
.presence-players {
display: flex;
flex-wrap: wrap;
gap: 3px;
margin-top: 3px;
}
.presence-player-tag {
display: inline-flex;
align-items: center;
gap: 2px;
padding: 1px 5px;
border-radius: 10px;
font-size: 0.7rem;
font-weight: 600;
}
.presence-player-tag.confirmed {
background: #d1fae5;
color: #065f46;
}
.presence-player-tag.pending {
background: #fef3c7;
color: #92400e;
}
.presence-toggle-btn {
margin-left: 4px;
}
</style>
{% endblock %}
+58
View File
@@ -0,0 +1,58 @@
{% extends "layouts/base.html" %}
{% block title %}Team Notes - TryoutPro{% endblock %}
{% block page_title %}Team Notes{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / Team Notes</span>{% endblock %}
{% block content %}
<div class="card">
<div class="card-header">
<h3><i class="fas fa-users"></i> Team Improvement Notes</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.manage_team_notes') }}" class="form">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div class="form-group">
<label for="content">Team Notes Content</label>
<textarea name="content" id="content" class="form-textarea" rows="6" placeholder="Enter improvement suggestions and notes for your team...">{{ team_notes[0].content if team_notes else '' }}</textarea>
<p class="form-text">These notes will be visible to all players on your team.</p>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">
<i class="fas fa-plus"></i> Add Team Notes
</button>
</div>
</form>
</div>
</div>
{% if team_notes %}
<div class="card mt-4">
<div class="card-header">
<h3><i class="fas fa-history"></i> Note History</h3>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th>Last Updated</th>
<th>Content Preview</th>
</tr>
</thead>
<tbody>
{% for note in team_notes %}
<tr>
<td>{{ note.updated_at.strftime('%B %d, %Y at %I:%M %p') if note.updated_at else 'Unknown date' }}</td>
<td>{{ note.content[:100] if note.content else '' }}{% if note.content and note.content|length > 100 %}...{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
{% endblock %}
+542
View File
@@ -0,0 +1,542 @@
{% extends "layouts/base.html" %}
{% block title %}Teams - TryoutPro{% endblock %}
{% block page_title %}Teams{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / Teams</span>{% endblock %}
{% block header_actions %}
{% if can_manage %}
<button class="btn btn-primary" onclick="showCreateForm()">
<i class="fas fa-plus"></i> New Team
</button>
{% endif %}
{% endblock %}
{% block content %}
{% if can_manage %}
<div id="createTeamForm" class="card mb-4 {% if not form_visible %}hidden{% endif %}">
<div class="card-header">
<h3>Create New Team</h3>
</div>
<div class="card-body">
<form method="POST" action="{{ url_for('teams.create_team') }}" class="form">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div class="form-row">
<div class="form-group col-4">
<label for="name">Team Name</label>
<input type="text" id="name" name="name" placeholder="e.g., Varsity, JV, U14" required>
</div>
<div class="form-group col-4">
<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 }}">{{ coach.username }}</option>
{% endfor %}
</select>
</div>
<div class="form-group col-4">
<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 }}">{{ manager.username }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-actions">
<button type="button" class="btn btn-secondary" onclick="hideCreateForm()">Cancel</button>
<button type="submit" class="btn btn-primary">Create Team</button>
</div>
</form>
</div>
</div>
{% endif %}
{% for team in teams %}
{% set can_manage_team = can_manage or (current_user.role == 'coach' and team.coach_id == current_user.id) %}
<div class="card mb-4">
<div class="card-header">
<h3><i class="fas fa-users-cog"></i> {{ team.name }}</h3>
<div class="card-actions">
{% if can_manage %}
<button class="btn btn-sm btn-outline edit-team-btn" data-team-id="{{ team.id }}" data-team-name="{{ team.name }}" data-coach-id="{{ team.coach_id or '' }}" data-manager-id="{{ team.manager_id or '' }}">
<i class="fas fa-edit"></i> Edit
</button>
<form method="POST" action="{{ url_for('teams.delete_team', team_id=team.id) }}" class="inline-form" onsubmit="return confirm('Delete team {{ team.name }}? This will unassign it from any linked tryouts.')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<button type="submit" class="btn btn-sm btn-danger">
<i class="fas fa-trash"></i> Delete
</button>
</form>
{% endif %}
{% if current_user.role == 'coach' and team.coach_id == current_user.id %}
<a href="{{ url_for('users.notes_dashboard') }}" class="btn btn-sm btn-primary" title="Add Notes">
<i class="fas fa-sticky-note"></i> Notes
</a>
{% endif %}
</div>
</div>
<!-- Full-width staff bar for coaches and managers -->
<div class="team-staff-bar">
<div class="staff-row">
<div class="staff-group">
<span class="staff-label"><i class="fas fa-chalkboard-teacher"></i> Coaches</span>
<div class="staff-items">
{% set team_coaches = team.get_coaches() %}
{% if team_coaches %}
{% for c in team_coaches %}
<span class="staff-tag">
{{ c.username }}
{% if can_manage %}
<form method="POST" action="{{ url_for('teams.remove_coach', team_id=team.id) }}" class="inline-form" onsubmit="return confirm('Remove coach {{ c.username }} from {{ team.name }}?')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<input type="hidden" name="coach_id" value="{{ c.id }}"/>
<button type="submit" class="btn-icon-sm" title="Remove Coach">&times;</button>
</form>
{% endif %}
</span>
{% endfor %}
{% else %}
<span class="text-muted text-sm">None assigned</span>
{% endif %}
</div>
</div>
<div class="staff-group">
<span class="staff-label"><i class="fas fa-user-tie"></i> Managers</span>
<div class="staff-items">
{% set team_managers = team.get_managers() %}
{% if team_managers %}
{% for m in team_managers %}
<span class="staff-tag manager-tag">
{{ m.username }}
{% if can_manage %}
<form method="POST" action="{{ url_for('teams.remove_manager', team_id=team.id) }}" class="inline-form" onsubmit="return confirm('Remove manager {{ m.username }} from {{ team.name }}?')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<input type="hidden" name="manager_id" value="{{ m.id }}"/>
<button type="submit" class="btn-icon-sm" title="Remove Manager">&times;</button>
</form>
{% endif %}
</span>
{% endfor %}
{% else %}
<span class="text-muted text-sm">None assigned</span>
{% endif %}
</div>
</div>
<a href="{{ url_for('team_matches.list_matches') }}?team_id={{ team.id }}" class="btn btn-sm btn-outline" title="View Team Matches">
<i class="fas fa-futbol"></i> Matches
</a>
{% if current_user.can_schedule_matches() and (current_user.role in ['admin', 'manager'] or (current_user.role == 'coach' and team.coaches.filter_by(id=current_user.id).first()) or (current_user.role == 'coach' and team.coach_id == current_user.id)) %}
<a href="{{ url_for('team_matches.create_match', team_id=team.id) }}" class="btn btn-sm btn-success" title="Schedule Team Match">
<i class="fas fa-plus"></i> Match
</a>
<a href="{{ url_for('team_matches.create_match', team_id=team.id, type='practice') }}" class="btn btn-sm btn-info" title="Schedule Practice">
<i class="fas fa-dumbbell"></i> Practice
</a>
{% endif %}
</div>
</div>
<div class="card-body">
<div class="table-container">
<table class="table">
<thead>
<tr>
<th>Player</th>
<th>Status</th>
<th>Position / Role</th>
<th>Email</th>
<th>Phone</th>
{% if can_manage_team %}
<th>Actions</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for entry in team.get_players_with_status() %}
<tr>
<td>
<div class="user-mini">
<div class="avatar-sm">{{ entry.player.username[:2] | upper }}</div>
<a href="{{ url_for('users.view_user', user_id=entry.player.id) }}">{{ entry.player.username }}</a>
</div>
</td>
<td>
{% if can_manage_team %}
<button class="btn btn-sm status-toggle-btn {% if entry.status == 'starter' %}btn-success{% else %}btn-warning{% endif %}"
data-team-id="{{ team.id }}"
data-player-id="{{ entry.player.id }}"
onclick="toggleStatus(this)">
{{ entry.status | capitalize }}
</button>
{% else %}
<span class="badge {% if entry.status == 'starter' %}badge-success{% else %}badge-warning{% endif %}">
{{ entry.status | capitalize }}
</span>
{% endif %}
</td>
<td>{{ entry.player.role | capitalize }}</td>
<td>{{ entry.player.email }}</td>
<td>{{ entry.player.phone or '-' }}</td>
{% if can_manage_team %}
<td>
<form method="POST" action="{{ url_for('teams.remove_player', team_id=team.id, player_id=entry.player.id) }}" class="inline-form" onsubmit="return confirm('Remove {{ entry.player.username }} from {{ team.name }}?')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<button type="submit" class="btn btn-sm btn-danger">
<i class="fas fa-user-minus"></i> Remove
</button>
</form>
</td>
{% endif %}
</tr>
{% else %}
<tr>
<td colspan="{% if can_manage_team %}6{% else %}5{% endif %}" class="text-center">
<div class="empty-state">
<i class="fas fa-users-slash"></i>
<h4>No players assigned</h4>
{% if can_manage_team %}
<p>Add players to this team using the form below.</p>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if can_manage_team %}
<hr class="my-3">
<div class="add-player-section">
<h5 class="mb-2"><i class="fas fa-user-plus"></i> Add Player to {{ team.name }}</h5>
<form method="POST" action="{{ url_for('teams.add_player', team_id=team.id) }}" class="form-inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<select name="player_id" class="form-select" required>
<option value="">-- Select a player --</option>
{% for p in all_players %}
{% if p.id not in team.players | map(attribute='id') %}
<option value="{{ p.id }}">{{ p.username }}</option>
{% endif %}
{% endfor %}
</select>
<select name="status" class="form-select ml-2">
<option value="starter">Starter</option>
<option value="substitute">Substitute</option>
</select>
<button type="submit" class="btn btn-sm btn-primary ml-2">
<i class="fas fa-plus"></i> Add to Team
</button>
</form>
</div>
{% endif %}
</div>
</div>
{% else %}
<div class="card">
<div class="card-body text-center">
<div class="empty-state">
<i class="fas fa-users-cog"></i>
<h3>No teams yet</h3>
{% if can_manage %}
<p>Create organization teams and assign coaches to manage tryouts.</p>
<button class="btn btn-primary" onclick="showCreateForm()">Create Team</button>
{% else %}
<p>There are no teams to display.</p>
{% endif %}
</div>
</div>
</div>
{% endfor %}
<!-- Edit Team Modal -->
<div id="editTeamModal" class="modal hidden">
<div class="modal-backdrop" onclick="hideEditForm()"></div>
<div class="modal-content">
<div class="modal-header">
<h3>Edit Team</h3>
<button class="modal-close" onclick="hideEditForm()">&times;</button>
</div>
<div class="modal-body">
<form id="editTeamForm" method="POST" action="" class="form">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<input type="hidden" name="sync_staff" value="1"/>
<div class="form-group">
<label for="edit_name">Team Name</label>
<input type="text" id="edit_name" name="name" required>
</div>
<hr class="section-divider">
<!-- Manage Coaches -->
<h5 class="mb-2"><i class="fas fa-chalkboard-teacher"></i> Coaches</h5>
<div class="form-group">
<select name="coach_ids" id="edit-coach-select" class="form-select" multiple style="min-height: 100px; width: 100%;">
{% for coach in coaches %}
<option value="{{ coach.id }}" class="coach-option">{{ coach.username }}</option>
{% endfor %}
</select>
<small class="form-text">Hold Ctrl/Cmd to select multiple. Only unassigned coaches shown.</small>
</div>
<!-- Manage Managers -->
<h5 class="mb-2"><i class="fas fa-user-tie"></i> Managers</h5>
<div class="form-group">
<select name="manager_ids" id="edit-manager-select" class="form-select" multiple style="min-height: 100px; width: 100%;">
{% for manager in managers %}
<option value="{{ manager.id }}" class="manager-option">{{ manager.username }}</option>
{% endfor %}
</select>
<small class="form-text">Hold Ctrl/Cmd to select multiple. Only unassigned managers shown.</small>
</div>
<div class="form-actions">
<button type="button" class="btn btn-secondary" onclick="hideEditForm()">Cancel</button>
<button type="submit" class="btn btn-primary">Save Changes</button>
</div>
</form>
</div>
</div>
</div>
<script>
function showCreateForm() {
document.getElementById('createTeamForm').classList.remove('hidden');
}
function hideCreateForm() {
document.getElementById('createTeamForm').classList.add('hidden');
}
function toggleStatus(btn) {
var teamId = btn.getAttribute('data-team-id');
var playerId = btn.getAttribute('data-player-id');
fetch('/teams/' + teamId + '/toggle_status/' + playerId, {
method: 'POST',
headers: {
'X-CSRFToken': '{{ csrf_token() }}',
'Content-Type': 'application/json'
}
})
.then(function(response) { return response.json(); })
.then(function(data) {
if (data.success) {
btn.textContent = data.new_status.charAt(0).toUpperCase() + data.new_status.slice(1);
if (data.new_status === 'starter') {
btn.classList.remove('btn-warning');
btn.classList.add('btn-success');
} else {
btn.classList.remove('btn-success');
btn.classList.add('btn-warning');
}
}
})
.catch(function(error) {
console.error('Error:', error);
});
}
var currentEditTeamId = null;
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('.edit-team-btn').forEach(function(btn) {
btn.addEventListener('click', function() {
currentEditTeamId = this.getAttribute('data-team-id');
var teamName = this.getAttribute('data-team-name');
document.getElementById('editTeamForm').action = '/teams/' + currentEditTeamId + '/edit';
document.getElementById('edit_name').value = teamName;
document.getElementById('editTeamModal').classList.remove('hidden');
populateEditSelects(currentEditTeamId);
});
});
});
function getCurrentStaffIds(teamId, type) {
// Get currently assigned coach/manager IDs from the staff bar pills
var ids = [];
var teamCard = document.querySelector('[data-team-id="' + teamId + '"]');
if (!teamCard) return ids;
var staffBar = teamCard.closest('.card').querySelector('.team-staff-bar');
if (!staffBar) return ids;
var groupIndex = type === 'coach' ? 0 : 1;
var group = staffBar.querySelectorAll('.staff-group')[groupIndex];
if (!group) return ids;
group.querySelectorAll('.staff-tag form input[name="coach_id"], .staff-tag form input[name="manager_id"]').forEach(function(input) {
ids.push(input.value);
});
return ids;
}
function populateEditSelects(teamId) {
var coachSelect = document.getElementById('edit-coach-select');
var managerSelect = document.getElementById('edit-manager-select');
var currentCoachIds = getCurrentStaffIds(teamId, 'coach');
var currentManagerIds = getCurrentStaffIds(teamId, 'manager');
// Show all coaches, but pre-select current ones and hide non-assigned
// Actually: show only currently-assigned options (pre-selected)
coachSelect.querySelectorAll('.coach-option').forEach(function(opt) {
var isAssigned = currentCoachIds.includes(opt.value);
opt.selected = isAssigned;
// Always show all options so user can add/remove
opt.style.display = '';
});
managerSelect.querySelectorAll('.manager-option').forEach(function(opt) {
var isAssigned = currentManagerIds.includes(opt.value);
opt.selected = isAssigned;
opt.style.display = '';
});
// Also update text to reflect current count
document.querySelector('#edit-coach-select + .form-text').textContent =
'Currently assigned: ' + currentCoachIds.length + '. Hold Ctrl/Cmd to select multiple.';
document.querySelector('#edit-manager-select + .form-text').textContent =
'Currently assigned: ' + currentManagerIds.length + '. Hold Ctrl/Cmd to select multiple.';
}
function hideEditForm() {
document.getElementById('editTeamModal').classList.add('hidden');
}
</script>
<style>
/* === Full-width staff bar layout === */
.team-staff-bar {
padding: 10px 20px;
background: #f8fafc;
border-bottom: 1px solid #e2e8f0;
}
.staff-row {
display: flex;
align-items: center;
gap: 24px;
flex-wrap: wrap;
}
.staff-group {
display: flex;
align-items: center;
gap: 8px;
}
.staff-label {
font-size: 0.8rem;
color: #64748b;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
white-space: nowrap;
}
.staff-label i {
margin-right: 3px;
font-size: 0.75rem;
}
.staff-items {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.staff-tag {
display: inline-flex;
align-items: center;
gap: 5px;
background: #e0e7ff;
color: #3730a3;
padding: 3px 10px;
border-radius: 14px;
font-size: 0.82rem;
font-weight: 500;
line-height: 1.3;
}
.staff-tag.manager-tag {
background: #fef3c7;
color: #92400e;
}
.staff-tag .btn-icon-sm {
background: none;
border: none;
color: #ef4444;
cursor: pointer;
padding: 0;
font-size: 0.85rem;
font-weight: 700;
line-height: 1;
margin-left: 1px;
}
.staff-tag .btn-icon-sm:hover {
color: #dc2626;
}
.staff-add-forms {
display: flex;
align-items: center;
gap: 8px;
margin-left: auto;
}
.staff-add-forms .form-select-sm {
font-size: 0.8rem;
padding: 4px 28px 4px 10px;
height: auto;
border-radius: 6px;
border: 1px dashed #cbd5e1;
background: #fff;
min-width: 150px;
}
.staff-add-forms .form-select-sm:focus {
border-color: #6366f1;
outline: none;
box-shadow: 0 0 0 2px rgba(99,102,241,0.15);
}
.text-sm {
font-size: 0.82rem;
}
.ml-2 {
margin-left: 8px;
}
.status-toggle-btn {
cursor: pointer;
min-width: 90px;
}
/* === Dark mode overrides for staff bar === */
[data-theme="dark"] .team-staff-bar {
background: var(--bg-tertiary);
border-bottom-color: var(--border-color);
}
[data-theme="dark"] .staff-label {
color: var(--text-muted);
}
[data-theme="dark"] .staff-tag {
background: rgba(99, 102, 241, 0.2);
color: #a5b4fc;
}
[data-theme="dark"] .staff-tag.manager-tag {
background: rgba(229, 169, 57, 0.2);
color: #fcd34d;
}
[data-theme="dark"] .staff-tag .btn-icon-sm {
color: var(--text-muted);
}
[data-theme="dark"] .staff-tag .btn-icon-sm:hover {
color: var(--danger);
}
[data-theme="dark"] .staff-add-forms .form-select-sm {
background: var(--input-bg);
border-color: var(--border-color);
color: var(--text-secondary);
}
[data-theme="dark"] .staff-add-forms .form-select-sm:focus {
border-color: var(--primary);
box-shadow: 0 0 0 2px rgba(0, 152, 76, 0.25);
}
[data-theme="dark"] .staff-add-forms .form-select-sm option {
background: var(--bg-secondary);
color: var(--text-primary);
}
</style>
{% endblock %}
+104
View File
@@ -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.username }}){% 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.username }}
</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.username }}
</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 %}
+98
View File
@@ -0,0 +1,98 @@
{% extends "layouts/base.html" %}
{% block title %}Tryouts - TryoutPro{% endblock %}
{% block page_title %}Tryouts{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / Tryouts</span>{% endblock %}
{% block header_actions %}
{% if current_user.can_manage_tryouts() %}
<a href="{{ url_for('tryouts.create_tryout') }}" class="btn btn-primary">
<i class="fas fa-plus"></i> New Tryout
</a>
{% endif %}
{% endblock %}
{% block content %}
<div class="tryouts-grid">
{% for tryout in tryouts %}
<div class="card tryout-card">
<div class="card-header">
<h3 class="tryout-title">{{ tryout.title }}</h3>
<span class="badge badge-{{ tryout.status }}">{{ tryout.status | replace('_', ' ') | title }}</span>
</div>
<div class="card-body">
<div class="tryout-info-grid">
<div class="info-item">
<i class="fas fa-gamepad info-icon"></i>
<span class="info-label">Game</span>
<span class="info-value">{{ tryout.game }}</span>
</div>
<div class="info-item">
<i class="fas fa-calendar info-icon"></i>
<span class="info-label">Date</span>
<span class="info-value">{{ tryout.date.strftime('%b %d, %Y') }}</span>
</div>
<div class="info-item">
<i class="fas fa-map-marker-alt info-icon"></i>
<span class="info-label">Location</span>
<span class="info-value">{{ tryout.location or 'TBD' }}</span>
</div>
<div class="info-item">
<i class="fas fa-users info-icon"></i>
<span class="info-label">Players</span>
<span class="info-value">{{ tryout.registrations.count() }}</span>
</div>
{% if current_user.can_evaluate() %}
<div class="info-item">
<i class="fas fa-clipboard-check info-icon"></i>
<span class="info-label">Evaluations</span>
<span class="info-value">{{ tryout.evaluations.count() }}</span>
</div>
{% endif %}
{% if tryout.target_org_team %}
<div class="info-item">
<i class="fas fa-flag info-icon"></i>
<span class="info-label">Target Team</span>
<span class="info-value">{{ tryout.target_org_team.name }}</span>
</div>
{% endif %}
{% if tryout.max_players %}
<div class="info-item">
<i class="fas fa-user-friends info-icon"></i>
<span class="info-label">Max Players</span>
<span class="info-value">{{ tryout.max_players }}</span>
</div>
{% endif %}
</div>
{% if tryout.description %}
<div class="tryout-description mt-3">
<p>{{ tryout.description | truncate(120) }}</p>
</div>
{% endif %}
</div>
<div class="card-footer">
<a href="{{ url_for('tryouts.view_tryout', tryout_id=tryout.id) }}" class="btn btn-sm btn-outline">
<i class="fas fa-eye"></i> View Details
</a>
{% if current_user.can_evaluate() %}
<a href="{{ url_for('evaluations.players_to_evaluate', tryout_id=tryout.id) }}" class="btn btn-sm btn-primary">
<i class="fas fa-clipboard-check"></i> Evaluate
</a>
{% endif %}
</div>
</div>
{% else %}
<div class="card" style="grid-column: 1 / -1;">
<div class="card-body text-center">
<div class="empty-state">
<i class="fas fa-calendar-times"></i>
<h3>No tryouts found</h3>
{% if current_user.can_manage_tryouts() %}
<p>Get started by creating a new tryout.</p>
<a href="{{ url_for('tryouts.create_tryout') }}" class="btn btn-primary">Create Tryout</a>
{% endif %}
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock %}
+45
View File
@@ -0,0 +1,45 @@
{% extends "layouts/base.html" %}
{% block title %}Upload Contract - TryoutPro{% endblock %}
{% block page_title %}Upload Contract{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('users.list_contracts') }}">Contracts</a> / Upload</span>{% endblock %}
{% block content %}
<div class="card">
<div class="card-header">
<h3><i class="fas fa-upload"></i> Upload Contract for Player</h3>
</div>
<div class="card-body">
<form method="POST" action="{{ url_for('users.upload_contract') }}" enctype="multipart/form-data" class="form">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div class="form-group">
<label for="player_id">Select Player</label>
<select id="player_id" name="player_id" class="form-select" required>
<option value="">-- Select a player --</option>
{% for player in players %}
<option value="{{ player.id }}">{{ player.username }}{% if player.org_team %} - {{ player.org_team.name }}{% endif %}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label for="contract_file">Contract File</label>
<input type="file" id="contract_file" name="contract_file" accept=".pdf,.doc,.docx,.jpg,.jpeg,.png" required>
<small class="form-text text-muted">Accepted formats: PDF, DOC, DOCX, JPG, PNG</small>
</div>
<div class="form-group">
<label for="notes">Notes (Optional)</label>
<textarea id="notes" name="notes" rows="3" placeholder="Add any notes about this contract..."></textarea>
</div>
<div class="form-actions">
<a href="{{ url_for('users.list_contracts') }}" class="btn btn-secondary">Cancel</a>
<button type="submit" class="btn btn-primary">
<i class="fas fa-upload"></i> Upload Contract
</button>
</div>
</form>
</div>
</div>
{% endblock %}
+68
View File
@@ -0,0 +1,68 @@
{% extends "layouts/base.html" %}
{% block title %}Manage Users - TryoutPro{% endblock %}
{% block page_title %}Manage Users{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / Users</span>{% endblock %}
{% block header_actions %}
<a href="{{ url_for('users.create_user') }}" class="btn btn-primary">
<i class="fas fa-user-plus"></i> Add User
</a>
{% endblock %}
{% block content %}
<div class="card">
<div class="card-body">
<div class="table-container">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Username</th>
<th>Email</th>
<th>Role</th>
<th>Status</th>
<th>Joined</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for u in users %}
<tr>
<td>
<div class="user-mini">
<div class="avatar-sm">{{ u.username[:2] | upper }}</div>
<span>{{ u.username }}</span>
</div>
</td>
<td>{{ u.username }}</td>
<td>{{ u.email }}</td>
<td><span class="badge badge-{{ u.role }}">{{ u.role | capitalize }}</span></td>
<td>
{% if u.is_active_account %}
<span class="badge badge-success">Active</span>
{% else %}
<span class="badge badge-danger">Inactive</span>
{% endif %}
</td>
<td>{{ u.created_at.strftime('%m/%d/%Y') }}</td>
<td>
<a href="{{ url_for('users.edit_user', user_id=u.id) }}" class="btn btn-sm btn-outline">
<i class="fas fa-edit"></i> Edit
</a>
{% if u.id != current_user.id %}
<form method="POST" action="{{ url_for('users.delete_user', user_id=u.id) }}" class="inline-form" onsubmit="return confirm('Delete {{ u.username }}? This cannot be undone.');">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<button type="submit" class="btn btn-sm btn-danger">
<i class="fas fa-trash"></i>
</button>
</form>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
+578
View File
@@ -0,0 +1,578 @@
{% extends "layouts/base.html" %}
{% block title %}{{ tryout.title }} - TryoutPro{% endblock %}
{% block page_title %}{{ tryout.title }}{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('tryouts.list_tryouts') }}">Tryouts</a> / {{ tryout.title }}</span>{% endblock %}
{% block content %}
<div class="tryout-detail">
<div class="card mb-4">
<div class="card-header">
<h3>Tryout Details</h3>
<div class="card-actions">
{% if can_edit %}
<a href="{{ url_for('matches.create_match', tryout_id=tryout.id) }}" class="btn btn-sm btn-success">
<i class="fas fa-futbol"></i> Schedule Match
</a>
{% endif %}
{% if can_edit %}
<form method="POST" action="{{ url_for('tryouts.update_status', tryout_id=tryout.id) }}" class="inline-form">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<select name="status" onchange="this.form.submit()" class="form-select">
<option value="upcoming" {% if tryout.status == 'upcoming' %}selected{% endif %}>Upcoming</option>
<option value="in_progress" {% if tryout.status == 'in_progress' %}selected{% endif %}>In Progress</option>
<option value="completed" {% if tryout.status == 'completed' %}selected{% endif %}>Completed</option>
</select>
</form>
{% endif %}
{% if can_edit %}
<a href="{{ url_for('tryouts.edit_tryout', tryout_id=tryout.id) }}" class="btn btn-sm btn-primary">
<i class="fas fa-edit"></i> Edit
</a>
{% endif %}
</div>
</div>
<div class="card-body">
<div class="detail-grid">
<div class="detail-item">
<span class="detail-label">Game</span>
<span class="detail-value">{{ tryout.game }}</span>
</div>
<div class="detail-item">
<span class="detail-label">Date</span>
<span class="detail-value">{{ tryout.date.strftime('%A, %B %d, %Y') }}</span>
</div>
<div class="detail-item">
<span class="detail-label">Location</span>
<span class="detail-value">{{ tryout.location or 'Not specified' }}</span>
</div>
<div class="detail-item">
<span class="detail-label">Status</span>
<span class="detail-value">
<span class="badge badge-{{ tryout.status }}">{{ tryout.status | replace('_', ' ') | title }}</span>
</span>
</div>
<div class="detail-item">
<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.username if tryout.manager else 'Not assigned' }}</span>
</div>
<div class="detail-item">
<span class="detail-label">Coach</span>
<span class="detail-value">{{ tryout.coach.username 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>
</div>
{% if tryout.description %}
<div class="detail-item full-width">
<span class="detail-label">Description</span>
<span class="detail-value">{{ tryout.description }}</span>
</div>
{% endif %}
</div>
</div>
</div>
{% if current_user.role == 'player' and not is_registered and tryout.status in ['upcoming', 'in_progress'] %}
<div class="card mb-4">
<div class="card-body text-center">
<form method="POST" action="{{ url_for('tryouts.register_for_tryout', tryout_id=tryout.id) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<button type="submit" class="btn btn-primary btn-lg">
<i class="fas fa-user-plus"></i> Register for this Tryout
</button>
</form>
</div>
</div>
{% endif %}
{% if can_edit %}
<div class="card mb-4">
<div class="card-header">
<h3><i class="fas fa-user-plus"></i> Add Player to Tryout</h3>
</div>
<div class="card-body">
<form method="POST" action="{{ url_for('tryouts.register_player', tryout_id=tryout.id) }}" class="form-inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<select name="player_id" class="form-select" required>
<option value="">-- Select a player --</option>
{% for p in all_players %}
{% set is_already = registrations | selectattr('player_id', 'equalto', p.id) | list | length > 0 %}
<option value="{{ p.id }}" {% if is_already %}disabled class="text-muted"{% endif %}>{{ p.username }}{% if is_already %} (already registered){% endif %}</option>
{% endfor %}
</select>
<button type="submit" class="btn btn-sm btn-primary ml-2">
<i class="fas fa-plus"></i> Register Player
</button>
</form>
</div>
</div>
{% endif %}
<div class="dashboard-grid">
<div class="card">
<div class="card-header">
<h3><i class="fas fa-users"></i> Registered Players ({{ registered_players | length }})</h3>
</div>
<div class="card-body">
<div class="table-container">
<table class="table">
<thead>
<tr>
<th>Player</th>
<th>Status</th>
{% if current_user.can_evaluate() %}
<th>Evaluation</th>
{% endif %}
{% if can_edit %}
<th>Actions</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for p in registered_players %}
{% set reg = registrations | selectattr('player_id', 'equalto', p.id) | first %}
<tr>
<td>
<div class="user-mini">
<div class="avatar-sm">{{ p.username[:2] | upper }}</div>
<a href="{{ url_for('users.view_user', user_id=p.id) }}">{{ p.username }}</a>
</div>
</td>
<td>
{% if can_edit %}
<form method="POST" action="{{ url_for('tryouts.update_registration_status', tryout_id=tryout.id, player_id=p.id) }}" class="inline-form">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<select name="status" onchange="this.form.submit()" class="form-select form-select-sm">
<option value="registered" {% if reg.status == 'registered' %}selected{% endif %}>Registered</option>
<option value="attended" {% if reg.status == 'attended' %}selected{% endif %}>Attended</option>
<option value="no_show" {% if reg.status == 'no_show' %}selected{% endif %}>No Show</option>
</select>
</form>
{% else %}
<span class="badge badge-{{ reg.status }}">{{ reg.status }}</span>
{% endif %}
</td>
{% if current_user.can_evaluate() %}
<td>
{% if player_eval_status.get(p.id) %}
<span class="badge badge-success">Evaluated</span>
{% else %}
<span class="badge badge-warning">Pending</span>
{% endif %}
</td>
{% endif %}
{% if can_edit %}
<td class="eval-actions">
{% if current_user.can_evaluate() %}
<a href="{{ url_for('evaluations.evaluate_player', tryout_id=tryout.id, player_id=p.id) }}" class="btn btn-sm btn-primary" title="Evaluate player">
<i class="fas fa-edit"></i> {% if player_eval_status.get(p.id) %}Edit{% else %}Evaluate{% endif %}
</a>
<a href="{{ url_for('users.add_note_from_tryout', tryout_id=tryout.id) }}?player_id={{ p.id }}" class="btn btn-sm btn-outline" title="Add note for {{ p.username }}">
<i class="fas fa-sticky-note"></i>
</a>
{% endif %}
<form method="POST" action="{{ url_for('tryouts.remove_player', tryout_id=tryout.id, player_id=p.id) }}" class="inline-form" onsubmit="return confirm('Remove {{ p.username }} from this tryout? This will also remove them from all teams and matches within this tryout.');">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<button type="submit" class="btn btn-sm btn-danger" title="Remove player from tryout">
<i class="fas fa-user-minus"></i> Remove
</button>
</form>
</td>
{% endif %}
</tr>
{% else %}
<tr>
<td colspan="{% if can_edit %}{% if current_user.can_evaluate() %}5{% else %}4{% endif %}{% else %}{% if current_user.can_evaluate() %}4{% else %}2{% endif %}{% endif %}" class="text-center">
No players registered yet.
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h3><i class="fas fa-users-cog"></i> Teams</h3>
{% if can_edit %}
<button class="btn btn-sm btn-primary" onclick="showCreateTeam()">
<i class="fas fa-plus"></i> New Team
</button>
{% endif %}
</div>
<div class="card-body">
{% if can_edit %}
<div id="createTeamForm" class="hidden mb-3">
<form method="POST" action="{{ url_for('tryouts.create_team', tryout_id=tryout.id) }}" class="form-inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<input type="text" name="team_name" placeholder="Team name" required class="form-input mr-2">
<button type="submit" class="btn btn-sm btn-success">Create</button>
<button type="button" class="btn btn-sm btn-secondary" onclick="hideCreateTeam()">Cancel</button>
</form>
</div>
{% endif %}
{% for team in team_data %}
<div class="team-card mb-3">
<h4 class="team-name">{{ team.team.name }}</h4>
<ul class="team-members">
{% for member in team.members %}
<li>
<div class="avatar-sm">{{ member.player.username[:2] | upper }}</div>
<span>{{ member.player.username }}</span>
{% if member.position %}
<span class="position-tag">{{ member.position }}</span>
{% endif %}
</li>
{% else %}
<li class="text-muted">No players assigned yet.</li>
{% endfor %}
</ul>
{% if can_edit and registered_players %}
{% set positions = game_positions.get(tryout.game, []) %}
<form method="POST" action="{{ url_for('tryouts.add_to_team', tryout_id=tryout.id, team_id=team.team.id) }}" class="form-inline mt-2">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<select name="player_id" class="form-select" required>
<option value="">Select player...</option>
{% for p in registered_players %}
{% if p.id not in team.members | map(attribute='player') | map(attribute='id') | list %}
<option value="{{ p.id }}">{{ p.username }}</option>
{% endif %}
{% endfor %}
</select>
{% if positions %}
<select name="position" class="form-select mx-2">
<option value="">-- Select Position --</option>
{% for pos in positions %}
<option value="{{ pos }}">{{ pos }}</option>
{% endfor %}
</select>
{% else %}
<input type="text" name="position" placeholder="Position" class="form-input mx-2">
{% endif %}
<button type="submit" class="btn btn-sm btn-primary">Add</button>
</form>
{% endif %}
</div>
{% else %}
<p class="text-muted text-center">No teams created yet.</p>
{% endfor %}
</div>
</div>
</div>
{% if can_view_calendar %}
<div class="card tryout-schedule-card mb-4">
<div class="card-header">
<h3><i class="fas fa-futbol"></i> Schedule</h3>
{% if can_edit %}
<div class="card-actions">
<a href="{{ url_for('matches.create_match', tryout_id=tryout.id) }}" class="btn btn-sm btn-success">
<i class="fas fa-plus"></i> Schedule Match
</a>
<a href="{{ url_for('users.add_note_from_tryout', tryout_id=tryout.id) }}" class="btn btn-sm btn-primary">
<i class="fas fa-sticky-note"></i> Add Note
</a>
</div>
{% endif %}
</div>
<div class="card-body">
{% if matches %}
<!-- Matches Table -->
<div class="table-container">
<table class="table">
<thead>
<tr>
<th>Match</th>
<th>Type</th>
<th>Date</th>
<th>Participants</th>
<th>Time</th>
<th>Presence</th>
<th>Status</th>
{% if can_edit %}
<th>Actions</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for item in match_data %}
{% set m = item.match %}
{% set participants = item.participants %}
<tr>
<td class="cell-title">{{ m.title }}</td>
<td>
<span class="badge badge-{{ 'success' if m.match_type in ['team_vs_team', 'player_vs_player'] else 'warning' }}">
{% if m.match_type == 'team_vs_team' %}
Team vs Team
{% elif m.match_type == 'player_vs_player' %}
Player vs Player
{% else %}
Player Scrim
{% endif %}
</span>
</td>
<td>{{ m.date.strftime('%m/%d/%Y') }}</td>
<td>
{% if m.start_time and m.end_time %}
{{ m.start_time.strftime('%H:%M') }} - {{ m.end_time.strftime('%H:%M') }}
{% else %}
TBD
{% endif %}
</td>
<td>
{% if item.total_count > 0 %}
<span class="presence-summary" title="{{ item.confirmed_count }} of {{ item.total_count }} confirmed">
{% if item.confirmed_count == item.total_count and item.total_count > 0 %}
<span class="badge badge-success">✅ {{ item.confirmed_count }}/{{ item.total_count }}</span>
{% elif item.confirmed_count > 0 %}
<span class="badge badge-warning">✅ {{ item.confirmed_count }}/{{ item.total_count }}</span>
{% else %}
<span class="badge badge-secondary">⏳ 0/{{ item.total_count }}</span>
{% endif %}
</span>
<!-- Per-player presence toggles -->
{% if can_edit and item.player_presence %}
<div class="presence-players" style="margin-top:6px;">
{% for pp in item.player_presence %}
<button class="btn btn-xs presence-toggle-btn {% if pp.attendance_confirmed %}presence-confirmed-btn{% else %}presence-pending-btn{% endif %}"
title="{{ pp.player_name }}"
onclick="toggleTryoutPresence({{ m.id }}, {{ pp.participant_id }}, this)">
{{ pp.player_name[:2] | upper }} {% if pp.attendance_confirmed %}✅{% else %}⏳{% endif %}
</button>
{% endfor %}
</div>
{% endif %}
{% else %}
<span class="text-muted"></span>
{% endif %}
</td>
<td>
{% if m.match_type == 'team_vs_team' %}
<div class="match-teams">
<div class="match-team">
<span class="team-name">{{ m.team1.name if m.team1 else 'Team 1' }}</span>
{% if participants.team1_players %}
<ul class="team-players-list">
{% for pl in participants.team1_players %}
<li>{{ pl.name }}{% if pl.position %} <span class="position-tag">{{ pl.position }}</span>{% endif %}</li>
{% endfor %}
</ul>
{% endif %}
</div>
<div class="match-vs">vs</div>
<div class="match-team">
<span class="team-name">{{ m.team2.name if m.team2 else 'Team 2' }}</span>
{% if participants.team2_players %}
<ul class="team-players-list">
{% for pl in participants.team2_players %}
<li>{{ pl.name }}{% if pl.position %} <span class="position-tag">{{ pl.position }}</span>{% endif %}</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
{% elif m.match_type == 'player_vs_player' %}
<div class="match-teams">
<div class="match-team">
<span class="team-name">Team 1</span>
{% if participants.team1_players %}
<ul class="team-players-list">
{% for pl in participants.team1_players %}
<li>{{ pl.name }}{% if pl.position %} <span class="position-tag">{{ pl.position }}</span>{% endif %}</li>
{% endfor %}
</ul>
{% endif %}
</div>
<div class="match-vs">vs</div>
<div class="match-team">
<span class="team-name">Team 2</span>
{% if participants.team2_players %}
<ul class="team-players-list">
{% for pl in participants.team2_players %}
<li>{{ pl.name }}{% if pl.position %} <span class="position-tag">{{ pl.position }}</span>{% endif %}</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
{% else %}
{{ participants | join(', ') }}
{% endif %}
</td>
<td>
<span class="badge badge-{{ m.status }}">{{ m.status }}</span>
</td>
{% if can_edit %}
<td>
<a href="{{ url_for('matches.edit_match', match_id=m.id) }}" class="btn btn-sm btn-outline">
<i class="fas fa-edit"></i> Edit
</a>
<a href="{{ url_for('users.add_note_from_match', match_id=m.id) }}" class="btn btn-sm btn-primary" title="Add Note for this Match">
<i class="fas fa-sticky-note"></i> Note
</a>
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
{% if matches %}
<!-- Mini Calendar -->
<div id="mini-calendar" style="min-height: 300px; margin-top: 20px;"></div>
{% else %}
<!-- No matches yet - show message -->
<div class="text-center py-4">
<i class="fas fa-calendar-alt fa-3x text-muted mb-3"></i>
<p class="text-muted">No matches scheduled yet. Check back later!</p>
</div>
{% endif %}
</div>
</div>
{% endif %}
{% if current_user.can_evaluate() %}
<div class="card">
<div class="card-header">
<h3><i class="fas fa-star"></i> Evaluation Summary</h3>
</div>
<div class="card-body">
<div class="table-container">
<table class="table">
<thead>
<tr>
<th>Player</th>
<th>Evaluator</th>
<th>Mecanics</th>
<th>Cohesion</th>
<th>Communication</th>
<th>Gamesense</th>
<th>Versatility</th>
<th>Discipline</th>
<th>Analysis</th>
<th>Sport Ethics</th>
<th>Mental</th>
<th>Overall</th>
<th>Recommendation</th>
</tr>
</thead>
<tbody>
{% for eval in evaluations %}
<tr>
<td>{{ eval.player.username }}</td>
<td>{{ eval.evaluator.username }}</td>
<td>{{ eval.mecanics_score or '-' }}</td>
<td>{{ eval.cohesion_score or '-' }}</td>
<td>{{ eval.communication_score or '-' }}</td>
<td>{{ eval.gamesense_score or '-' }}</td>
<td>{{ eval.versatility_score or '-' }}</td>
<td>{{ eval.discipline_score or '-' }}</td>
<td>{{ eval.analysis_score or '-' }}</td>
<td>{{ eval.sport_ethics_score or '-' }}</td>
<td>{{ eval.mental_score or '-' }}</td>
<td><span class="score">{{ eval.overall_score or '-' }}</span></td>
<td>{{ eval.position_recommendation or '-' }}</td>
</tr>
{% else %}
<tr>
<td colspan="13" class="text-center">No evaluations yet.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endif %}
</div>
<style>
.presence-toggle-btn {
padding: 2px 7px;
font-size: 0.7rem;
border-radius: 10px;
border: 1px solid #d1d5db;
cursor: pointer;
margin: 2px;
font-weight: 600;
transition: all 0.15s ease;
}
.presence-confirmed-btn {
background: #d1fae5;
color: #065f46;
border-color: #a7f3d0;
}
.presence-pending-btn {
background: #fef3c7;
color: #92400e;
border-color: #fde68a;
}
.presence-toggle-btn:hover {
transform: scale(1.08);
box-shadow: 0 1px 3px rgba(0,0,0,0.12);
}
</style>
<script>
function showCreateTeam() { document.getElementById('createTeamForm').classList.remove('hidden'); }
function hideCreateTeam() { document.getElementById('createTeamForm').classList.add('hidden'); }
function toggleTryoutPresence(matchId, participantId, btn) {
fetch('/matches/' + matchId + '/toggle-presence/' + participantId, {
method: 'POST',
headers: {
'X-CSRFToken': '{{ csrf_token() }}',
'Content-Type': 'application/json'
}
})
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.attendance_confirmed) {
btn.classList.add('presence-confirmed-btn');
btn.classList.remove('presence-pending-btn');
btn.innerHTML = btn.innerHTML.replace('⏳', '✅');
} else {
btn.classList.remove('presence-confirmed-btn');
btn.classList.add('presence-pending-btn');
btn.innerHTML = btn.innerHTML.replace('✅', '⏳');
}
location.reload();
})
.catch(function(err) {
console.error('Error toggling tryout presence:', err);
});
}
// Mini calendar for matches
document.addEventListener('DOMContentLoaded', function() {
var miniCalendarEl = document.getElementById('mini-calendar');
if (miniCalendarEl) {
var miniCalendar = new FullCalendar.Calendar(miniCalendarEl, {
initialView: 'dayGridMonth',
headerToolbar: {
left: 'prev,next',
center: 'title',
right: 'dayGridMonth,timeGridWeek'
},
events: '/matches/api/events/{{ tryout.id }}',
height: '300px',
eventClick: function(info) {
if (info.event.extendedProps.match_id) {
window.location.href = '/matches/' + info.event.extendedProps.match_id + '/edit';
}
}
});
miniCalendar.render();
}
});
</script>
{% endblock %}
+76
View File
@@ -0,0 +1,76 @@
{% extends "layouts/base.html" %}
{% block title %}{{ profile_user.username }} - TryoutPro{% endblock %}
{% block page_title %}{{ profile_user.username }}{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="#" onclick="history.back()">Back</a> / {{ profile_user.username }}</span>{% endblock %}
{% block content %}
<div class="card mb-4">
<div class="card-header">
<div class="user-mini" style="gap: 12px;">
<div class="avatar-sm" style="width: 48px; height: 48px; font-size: 1.2rem;">
{{ profile_user.username[:2] | upper }}
</div>
<div>
<h3 style="margin: 0;">{{ profile_user.username }}</h3>
<span class="badge badge-{{ 'info' if profile_user.role == 'player' else 'primary' }}">{{ profile_user.role | capitalize }}</span>
</div>
</div>
</div>
<div class="card-body">
<div class="detail-grid">
<div class="detail-item">
<span class="detail-label">Username</span>
<span class="detail-value">{{ profile_user.username }}</span>
</div>
<div class="detail-item">
<span class="detail-label">Role</span>
<span class="detail-value">
<span class="badge badge-{{ 'info' if profile_user.role == 'player' else 'primary' }}">{{ profile_user.role | capitalize }}</span>
</span>
</div>
{% if profile_user.discord_username %}
<div class="detail-item">
<span class="detail-label">Discord</span>
<span class="detail-value">{{ profile_user.discord_username }}</span>
</div>
{% endif %}
</div>
{% set gamertags = profile_user.gamertags %}
{% if gamertags %}
<hr class="my-4">
<h4 class="mb-3"><i class="fas fa-gamepad"></i> Gamertags</h4>
<div class="table-container">
<table class="table">
<thead>
<tr>
<th>Game</th>
<th>Gamertag</th>
<th>Platform</th>
<th>Profile</th>
</tr>
</thead>
<tbody>
{% for gt in gamertags %}
<tr>
<td>{{ gt.game }}</td>
<td>{{ gt.gamertag }}</td>
<td>{{ gt.platform or '-' }}</td>
<td>
{% if gt.get_trn_url() %}
<a href="{{ gt.get_trn_url() }}" target="_blank" class="btn btn-sm btn-outline trn-link">
<i class="fas fa-external-link-alt"></i> View Profile
</a>
{% else %}
<span class="text-muted"></span>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</div>
</div>
{% endblock %}