demander IA de faire tous les modifications pour que le webapp soit pret au déploiement.
Force connection HTTPS, proxy-inversé, WSGI de production, reset cookie de conncection à chaque reconnection, limite sur les mdp, fichiers et One on One par minute, verification d'injection de SQL dans les champs d'entrées. renommage des fichiers lors du téléchargement, fichier de backup quotidien pour la bd et j'ai oublié quelque chose :(
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}400 Bad Request - TryoutPro{% endblock %}
|
||||
{% block page_title %}Bad Request{% endblock %}
|
||||
{% block content %}
|
||||
<div class="error-container">
|
||||
<div class="error-icon">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
</div>
|
||||
<h2>400 — Bad Request</h2>
|
||||
<p>The request could not be understood by the server. Please check your input and try again.</p>
|
||||
<a href="{{ url_for('main.dashboard') if current_user.is_authenticated else url_for('auth.login') }}" class="btn btn-primary">
|
||||
<i class="fas fa-arrow-left"></i> Go Back
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,15 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}403 Forbidden - TryoutPro{% endblock %}
|
||||
{% block page_title %}Access Denied{% endblock %}
|
||||
{% block content %}
|
||||
<div class="error-container">
|
||||
<div class="error-icon">
|
||||
<i class="fas fa-lock"></i>
|
||||
</div>
|
||||
<h2>403 — Forbidden</h2>
|
||||
<p>You do not have permission to access this resource. If you believe this is an error, please contact an administrator.</p>
|
||||
<a href="{{ url_for('main.dashboard') if current_user.is_authenticated else url_for('auth.login') }}" class="btn btn-primary">
|
||||
<i class="fas fa-arrow-left"></i> Go Back
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,15 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}404 Not Found - TryoutPro{% endblock %}
|
||||
{% block page_title %}Page Not Found{% endblock %}
|
||||
{% block content %}
|
||||
<div class="error-container">
|
||||
<div class="error-icon">
|
||||
<i class="fas fa-search"></i>
|
||||
</div>
|
||||
<h2>404 — Not Found</h2>
|
||||
<p>The page you are looking for does not exist. It may have been moved or deleted.</p>
|
||||
<a href="{{ url_for('main.dashboard') if current_user.is_authenticated else url_for('auth.login') }}" class="btn btn-primary">
|
||||
<i class="fas fa-home"></i> Return Home
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,15 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}429 Too Many Requests - TryoutPro{% endblock %}
|
||||
{% block page_title %}Rate Limit Exceeded{% endblock %}
|
||||
{% block content %}
|
||||
<div class="error-container">
|
||||
<div class="error-icon">
|
||||
<i class="fas fa-hourglass-half"></i>
|
||||
</div>
|
||||
<h2>429 — Too Many Requests</h2>
|
||||
<p>You have sent too many requests in a short period. Please wait a moment and try again.</p>
|
||||
<a href="{{ url_for('main.dashboard') if current_user.is_authenticated else url_for('auth.login') }}" class="btn btn-primary">
|
||||
<i class="fas fa-arrow-left"></i> Go Back
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,15 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% block title %}500 Server Error - TryoutPro{% endblock %}
|
||||
{% block page_title %}Internal Server Error{% endblock %}
|
||||
{% block content %}
|
||||
<div class="error-container">
|
||||
<div class="error-icon">
|
||||
<i class="fas fa-cogs"></i>
|
||||
</div>
|
||||
<h2>500 — Internal Server Error</h2>
|
||||
<p>Something went wrong on our end. The error has been logged and will be investigated. Please try again later.</p>
|
||||
<a href="{{ url_for('main.dashboard') if current_user.is_authenticated else url_for('auth.login') }}" class="btn btn-primary">
|
||||
<i class="fas fa-redo-alt"></i> Try Again
|
||||
</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -22,39 +22,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h3><i class="fas fa-list"></i> Current Availability</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if existing_availability %}
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Day</th>
|
||||
<th>Time</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for av in existing_availability %}
|
||||
<tr>
|
||||
<td>{{ ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'][av.day_of_week] }}</td>
|
||||
<td>{{ av.start_time.strftime('%I:%M %p') }} - {{ av.end_time.strftime('%I:%M %p') }}</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm btn-danger" onclick="deleteAvailability({{ av.id }})">
|
||||
<i class="fas fa-trash"></i> Remove
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="text-muted">No availability slots set. Use the grid above to add your available times.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
@@ -207,9 +174,12 @@ function saveAvailability() {
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
flash('Availability saved successfully!', 'success');
|
||||
setTimeout(() => location.reload(), 1000);
|
||||
flash('Availability saved!', 'success');
|
||||
}
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error('Save error:', error);
|
||||
flash('Error saving availability.', 'danger');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -229,17 +199,6 @@ function clearAllAvailability() {
|
||||
});
|
||||
}
|
||||
|
||||
function deleteAvailability(availabilityId) {
|
||||
fetch('{{ url_for("users.delete_coach_availability", availability_id=0) }}'.replace('/0', '/' + availabilityId), { method: 'POST' })
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
flash('Availability slot removed!', 'success');
|
||||
setTimeout(() => location.reload(), 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function flash(message, type) {
|
||||
const flashContainer = document.querySelector('.flash-messages');
|
||||
const alert = document.createElement('div');
|
||||
|
||||
@@ -70,12 +70,6 @@
|
||||
<h4 class="section-title"><i class="fas fa-clock"></i> Select Match Time</h4>
|
||||
<p class="text-muted small">Click time slots consecutively to set match duration. Players available in all selected time blocks are shown below.</p>
|
||||
|
||||
<div id="merged-disponibility-selected" class="merged-disponibility-selected-display hidden">
|
||||
<span>Selected time:</span>
|
||||
<span class="selected-time-badge" id="selected-time-badge"></span>
|
||||
<button type="button" class="btn btn-sm btn-outline" onclick="clearTimeSelection()">Change</button>
|
||||
</div>
|
||||
|
||||
<div id="merged-disponibility-grid" class="merged-disponibility-grid">
|
||||
<p class="text-muted">Loading...</p>
|
||||
</div>
|
||||
@@ -539,9 +533,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('merged-disponibility-selected').classList.remove('hidden');
|
||||
document.getElementById('selected-time-badge').textContent =
|
||||
formatTimeDisplay(selectedStartTime) + ' - ' + formatTimeDisplay(selectedEndTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -621,26 +612,38 @@ function renderMergedDisponibilityGrid() {
|
||||
dayRow += '<div class="merged-disponibility-day-label">' + day.name + '</div>';
|
||||
dayRow += '<div class="merged-disponibility-time-blocks">';
|
||||
|
||||
// First pass: collect all counts to determine max
|
||||
var slotCounts = [];
|
||||
TIME_SLOTS.forEach(function(slot) {
|
||||
var count = getAvailabilityCount(day.value, slot.time);
|
||||
var percentage = totalPlayers > 0 ? count / totalPlayers : 0;
|
||||
slotCounts.push({ time: slot.time, display: slot.display, count: count });
|
||||
});
|
||||
|
||||
// Find max count to use as reference
|
||||
var maxCount = 0;
|
||||
slotCounts.forEach(function(s) { if (s.count > maxCount) maxCount = s.count; });
|
||||
|
||||
slotCounts.forEach(function(slotData) {
|
||||
var count = slotData.count;
|
||||
var cssClass = 'merged-disponibility-time-block';
|
||||
|
||||
if (percentage >= 0.75) {
|
||||
cssClass += ' high-availability';
|
||||
} else if (percentage >= 0.5) {
|
||||
cssClass += ' medium-availability';
|
||||
} else {
|
||||
cssClass += ' low-availability';
|
||||
// Only color slots with 2+ available players. Green = top count, yellow = medium, no class for low
|
||||
if (count >= 2) {
|
||||
if (maxCount > 0 && count === maxCount) {
|
||||
cssClass += ' high-availability';
|
||||
} else if (count >= Math.ceil(maxCount * 0.5)) {
|
||||
cssClass += ' medium-availability';
|
||||
}
|
||||
}
|
||||
// Slots with 0-1 players get no special color class (neutral)
|
||||
|
||||
if (selectedSlots.includes(slot.time)) {
|
||||
if (selectedSlots.includes(slotData.time)) {
|
||||
cssClass += ' selected';
|
||||
}
|
||||
|
||||
dayRow += '<div class="' + cssClass + '" data-day="' + day.value + '" data-time="' + slot.time + '" ' +
|
||||
'onclick="toggleTimeSlot(' + day.value + ', \'' + slot.time + '\', this)">' +
|
||||
slot.display +
|
||||
dayRow += '<div class="' + cssClass + '" data-day="' + day.value + '" data-time="' + slotData.time + '" ' +
|
||||
'onclick="toggleTimeSlot(' + day.value + ', \'' + slotData.time + '\', this)">' +
|
||||
slotData.display +
|
||||
'<span class="merged-disponibility-count">' + count + '</span>' +
|
||||
'</div>';
|
||||
});
|
||||
@@ -658,6 +661,9 @@ function getAvailabilityCount(dayOfWeek, timeStr) {
|
||||
var minutes = parseInt(timeParts[0]) * 60 + parseInt(timeParts[1]);
|
||||
|
||||
for (var playerId in allDisponibilities) {
|
||||
// Only count players registered for THIS tryout
|
||||
if (!allRegisteredPlayers.includes(parseInt(playerId))) continue;
|
||||
|
||||
var playerData = allDisponibilities[playerId];
|
||||
if (playerData && playerData.disponibilities) {
|
||||
var isAvailable = playerData.disponibilities.some(function(d) {
|
||||
@@ -746,14 +752,6 @@ function toggleTimeSlot(dayOfWeek, timeStr, element) {
|
||||
document.getElementById('start_time').value = startTime;
|
||||
document.getElementById('end_time').value = endTime;
|
||||
|
||||
if (selectedSlots.length > 0) {
|
||||
document.getElementById('merged-disponibility-selected').classList.remove('hidden');
|
||||
document.getElementById('selected-time-badge').textContent =
|
||||
formatTimeDisplay(selectedSlots[0]) + ' - ' + formatTimeDisplay(endTime);
|
||||
} else {
|
||||
document.getElementById('merged-disponibility-selected').classList.add('hidden');
|
||||
}
|
||||
|
||||
document.querySelectorAll('.merged-disponibility-time-block').forEach(function(el) {
|
||||
el.classList.remove('selected');
|
||||
});
|
||||
@@ -868,12 +866,11 @@ function updatePlayerPool() {
|
||||
var team2Ids = getSelectedTeamIds(2);
|
||||
var assignedIds = [...team1Ids, ...team2Ids];
|
||||
|
||||
// When time slots are selected, show only players available in all slots who aren't assigned yet
|
||||
// When no time slots selected, show all registered players
|
||||
var playersToShow = selectedSlots.length > 0 ? availablePlayersForSlots : allRegisteredPlayers;
|
||||
// Always show ALL registered players, not just available ones.
|
||||
// Availability indicators are shown per-player, but coaches can still select any player.
|
||||
|
||||
if (playersToShow.length === 0) {
|
||||
pool.innerHTML = '<p class="text-muted small">No players available</p>';
|
||||
if (allRegisteredPlayers.length === 0) {
|
||||
pool.innerHTML = '<p class="text-muted small">No players registered</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -883,7 +880,7 @@ function updatePlayerPool() {
|
||||
var dayOfWeek = jsDay === 0 ? 6 : jsDay - 1;
|
||||
|
||||
var html = '';
|
||||
playersToShow.forEach(function(pid) {
|
||||
allRegisteredPlayers.forEach(function(pid) {
|
||||
if (assignedIds.includes(pid)) return;
|
||||
|
||||
var playerName = playerDataById.player_data[pid];
|
||||
@@ -896,6 +893,9 @@ function updatePlayerPool() {
|
||||
isAvailable = selectedSlots.every(function(slot) {
|
||||
return playerSlots.includes(slot);
|
||||
});
|
||||
} else if (selectedSlots.length === 0) {
|
||||
// No time slots selected yet: all players count as "available" (no filter active)
|
||||
isAvailable = true;
|
||||
}
|
||||
|
||||
var availabilityClass = isAvailable ? 'available' : 'unavailable';
|
||||
@@ -961,7 +961,6 @@ function clearTimeSelection() {
|
||||
selectedSlots = [];
|
||||
document.getElementById('start_time').value = '';
|
||||
document.getElementById('end_time').value = '';
|
||||
document.getElementById('merged-disponibility-selected').classList.add('hidden');
|
||||
document.querySelectorAll('.merged-disponibility-time-block').forEach(function(el) {
|
||||
el.classList.remove('selected');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user