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:
cedrick2711
2026-07-25 18:47:27 -04:00
parent afdf6ded0e
commit 666673fa8f
24 changed files with 2513 additions and 171 deletions
+15
View File
@@ -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 %}
+15
View File
@@ -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 %}
+15
View File
@@ -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 %}
+15
View File
@@ -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 %}
+15
View File
@@ -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 %}
+5 -46
View File
@@ -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');
+34 -35
View File
@@ -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');
});