Author SHA1 Message Date
cedrick2711 2b943c5c22 régler problème avec le bouton pour sauvegarder les dispo 2026-08-14 12:03:39 -04:00
5 changed files with 48 additions and 157 deletions
+22 -17
View File
@@ -407,9 +407,18 @@ def add_disponibility():
@users_bp.route('/disponibilities/add_bulk', methods=['POST'])
@login_required
def add_disponibilities_bulk():
"""Add multiple disponibility blocks at once."""
"""Replace the player's disponibilities with the submitted slots.
Performs a full replace (delete existing + insert submitted) so that
deselected slots are correctly removed, mirroring the coach availability
flow. This keeps the auto-save idempotent and correct.
"""
data = request.get_json()
slots = data.get('slots', [])
slots = data.get('slots', []) if data else []
# Clear existing disponibilities for the current player.
PlayerDisponibility.query.filter_by(player_id=current_user.id).delete()
created = []
for slot in slots:
day_of_week = slot.get('day_of_week')
@@ -422,21 +431,17 @@ def add_disponibilities_bulk():
continue
end_time = add_30_minutes(start_time)
existing = PlayerDisponibility.query.filter_by(
player_id=current_user.id, day_of_week=day_of_week, start_time=start_time,
).first()
if not existing:
disponibility = PlayerDisponibility(
player_id=current_user.id, day_of_week=day_of_week,
start_time=start_time, end_time=end_time,
)
db.session.add(disponibility)
db.session.flush()
created.append({
'id': disponibility.id, 'day_of_week': disponibility.day_of_week,
'day_name': DAY_NAMES[disponibility.day_of_week],
'start_time': disponibility.start_time.strftime('%H:%M'),
})
disponibility = PlayerDisponibility(
player_id=current_user.id, day_of_week=day_of_week,
start_time=start_time, end_time=end_time,
)
db.session.add(disponibility)
db.session.flush()
created.append({
'id': disponibility.id, 'day_of_week': disponibility.day_of_week,
'day_name': DAY_NAMES[disponibility.day_of_week],
'start_time': disponibility.start_time.strftime('%H:%M'),
})
db.session.commit()
return jsonify({'success': True, 'created': created})
+22 -94
View File
@@ -1,8 +1,8 @@
:root {
/* New Color Palette (cozy / e-sporty) */
--primary: #6C4CFF;
--primary-dark: #5530D9;
--primary-light: #8B6CFF;
/* New Color Palette */
--primary: #00984C;
--primary-dark: #003E21;
--primary-light: #E5A939;
--success: #00984C;
--success-light: #F0F2F2;
--warning: #E5A939;
@@ -13,7 +13,7 @@
--info-light: #eff6ff;
--secondary: #6b7280;
--secondary-light: #F0F2F2;
--dark: #0D0D12;
--dark: #12130F;
--gray-50: #F0F2F2;
--gray-100: #E8EAEB;
--gray-200: #D1D5DB;
@@ -22,8 +22,8 @@
--gray-500: #4B5563;
--gray-600: #374151;
--gray-700: #1F2937;
--gray-800: #16161D;
--gray-900: #0D0D12;
--gray-800: #12130F;
--gray-900: #0A0B0A;
--sidebar-width: 260px;
--sidebar-collapsed: 0px;
--radius: 12px;
@@ -34,66 +34,21 @@
--transition: all 0.2s ease;
}
/* Local-first Sora font-face (local installed fonts preferred, then /static/fonts fallback). */
/* To use local files, place these filenames in app/static/fonts: Sora-300.woff2, Sora-400.woff2, Sora-500.woff2, Sora-600.woff2, Sora-700.woff2, Sora-800.woff2 */
@font-face {
font-family: 'Sora';
src: local('Sora'), local('Sora-Regular'), url('/static/fonts/Sora-400.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Sora';
src: local('Sora Medium'), local('Sora-Medium'), url('/static/fonts/Sora-500.woff2') format('woff2');
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Sora';
src: local('Sora SemiBold'), local('Sora-SemiBold'), url('/static/fonts/Sora-600.woff2') format('woff2');
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Sora';
src: local('Sora Bold'), local('Sora-Bold'), url('/static/fonts/Sora-700.woff2') format('woff2');
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Sora';
src: local('Sora ExtraBold'), local('Sora-ExtraBold'), url('/static/fonts/Sora-800.woff2') format('woff2');
font-weight: 800;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Sora';
src: local('Sora Light'), local('Sora-Light'), url('/static/fonts/Sora-300.woff2') format('woff2');
font-weight: 300;
font-style: normal;
font-display: swap;
}
/* Dark Mode Variables */
[data-theme="dark"] {
--bg-primary: #0D0D12;
--bg-secondary: #16161D;
--bg-tertiary: #23232E;
--text-primary: #FFFFFF;
--bg-primary: #12130F;
--bg-secondary: #1A1D17;
--bg-tertiary: #232820;
--text-primary: #F0F2F2;
--text-secondary: #D1D5DB;
--text-muted: #9CA3AF;
--border-color: #2D2A3A;
--card-bg: #16161D;
--sidebar-bg: #0D0D12;
--border-color: #2D342A;
--card-bg: #1A1D17;
--sidebar-bg: #0A0B0A;
--sidebar-text: #F0F2F2;
--sidebar-hover: rgba(108, 76, 255, 0.08);
--input-bg: #23232E;
--input-border: #2D2A3A;
--sidebar-hover: rgba(240, 242, 242, 0.08);
--input-bg: #232820;
--input-border: #2D342A;
--shadow: 0 1px 3px rgba(0,0,0,0.3), 0 1px 2px rgba(0,0,0,0.2);
--shadow-md: 0 4px 6px rgba(0,0,0,0.25), 0 2px 4px rgba(0,0,0,0.2);
--shadow-lg: 0 10px 15px rgba(0,0,0,0.3), 0 4px 6px rgba(0,0,0,0.2);
@@ -102,40 +57,13 @@
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Sora', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: var(--gray-50);
color: var(--gray-800);
line-height: 1.6;
min-height: 100vh;
}
/* Headings use Sora Bold for stronger, e-sporty look */
h1, h2, h3, h4, h5, h6, .page-header h1, .card-header h3 {
font-family: 'Sora', sans-serif;
font-weight: 700;
letter-spacing: -0.01em;
color: var(--text-primary, var(--gray-900));
}
/* Tweak letter-spacing when local Sora is available vs fallback */
html.font-sora-available h1, html.font-sora-available h2, html.font-sora-available h3,
html.font-sora-available h4, html.font-sora-available h5, html.font-sora-available h6,
html.font-sora-available .page-header h1, html.font-sora-available .card-header h3 {
letter-spacing: -0.02em; /* tighter when true Sora is present */
}
html.font-sora-fallback h1, html.font-sora-fallback h2, html.font-sora-fallback h3,
html.font-sora-fallback h4, html.font-sora-fallback h5, html.font-sora-fallback h6,
html.font-sora-fallback .page-header h1, html.font-sora-fallback .card-header h3 {
letter-spacing: -0.01em; /* keep the default for fallback fonts */
}
/* Logo text use extra-bold feel */
.logo span, .auth-header h2 {
font-family: 'Sora', sans-serif;
font-weight: 800;
letter-spacing: 0.02em;
}
a { color: var(--primary); text-decoration: none; }
a:hover { color: var(--primary-dark); }
@@ -574,7 +502,7 @@ a:hover { color: var(--primary-dark); }
.form-group textarea:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(108, 76, 255, 0.12);
box-shadow: 0 0 0 3px rgba(0, 152, 76, 0.1);
}
.form-group textarea { resize: vertical; min-height: 80px; }
@@ -645,12 +573,12 @@ a:hover { color: var(--primary-dark); }
flex-direction: column;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #6C4CFF 0%, #16161D 100%);
background: linear-gradient(135deg, #00984C 0%, #003E21 100%);
padding: 20px;
}
[data-theme="dark"] .auth-wrapper {
background: linear-gradient(135deg, #5530D9 0%, #0D0D12 100%);
background: linear-gradient(135deg, #003E21 0%, #12130F 100%);
}
.auth-wrapper .flash-messages {
@@ -728,7 +656,7 @@ a:hover { color: var(--primary-dark); }
.auth-form .form-group input:focus {
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(108, 76, 255, 0.12);
box-shadow: 0 0 0 3px rgba(0, 152, 76, 0.1);
outline: none;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

+2 -41
View File
@@ -5,46 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}UdeS team manager{% endblock %}</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<link href="https://fonts.googleapis.com/css2?family=Sora:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<script>
// Font detection: prefer local Sora, add html.font-sora-available or html.font-sora-fallback
(function(){
var fontName = 'Sora';
function mark(available){
try { document.documentElement.classList.add(available ? 'font-sora-available' : 'font-sora-fallback'); } catch(e){}
}
if (document.fonts && document.fonts.check) {
// Quick check for regular weight first
try {
if (document.fonts.check('1em "' + fontName + '"')) return mark(true);
} catch(e){}
// Wait briefly for the font to load (up to 1500ms)
var settled = false;
var timeout = setTimeout(function(){ if (!settled) { settled = true; mark(false); } }, 1500);
document.fonts.load('1em "' + fontName + '"').then(function(loaded){
if (settled) return;
settled = true;
clearTimeout(timeout);
// document.fonts.load resolves when font is available; double-check with check()
var ok = document.fonts.check('1em "' + fontName + '"');
mark(!!ok);
}).catch(function(){ if (!settled){ settled = true; clearTimeout(timeout); mark(false); } });
} else {
// Fallback: inject hidden element and compare computed family
var span = document.createElement('span');
span.style.fontFamily = fontName + ', monospace';
span.style.position = 'absolute';
span.style.left = '-9999px';
span.style.visibility = 'hidden';
span.textContent = 'Axm4';
document.head.appendChild(span);
var computed = window.getComputedStyle(span).fontFamily || '';
document.head.removeChild(span);
mark(computed.toLowerCase().indexOf(fontName.toLowerCase()) !== -1);
}
})();
</script>
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🏆</text></svg>">
</head>
<body>
@@ -52,7 +13,7 @@
<nav class="sidebar" id="sidebar">
<div class="sidebar-header">
<div class="logo">
<img src="{{ url_for('static', filename='images/Lockin_logo.png') }}" alt="Lockin Logo" class="logo-img">
<img src="{{ url_for('static', filename='images/UdeS_logo.png') }}" alt="UdeS Logo" class="logo-img">
<span>UdeS team manager</span>
</div>
<div class="user-badge">
@@ -199,7 +160,7 @@
</div>
<div class="auth-container">
<div class="auth-header">
<img src="{{ url_for('static', filename='images/Lockin_logo.png') }}" alt="Lockin Logo" class="auth-logo-img">
<img src="{{ url_for('static', filename='images/UdeS_logo.png') }}" alt="UdeS Logo" class="auth-logo-img">
<h2>UdeS team manager</h2>
<p>UdeS team manager</p>
</div>
+2 -5
View File
@@ -193,9 +193,6 @@
<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>
@@ -369,7 +366,7 @@ function saveCoachAvailability() {
const msg = document.createElement('div');
msg.className = 'alert alert-success';
msg.style.marginTop = '10px';
msg.innerHTML = '<i class="fas fa-check"></i> Availability saved!';
msg.innerHTML = '<span><i class="fas fa-check"></i> Availability saved!</span>';
document.getElementById('availability-grid').appendChild(msg);
setTimeout(() => msg.remove(), 3000);
}
@@ -571,7 +568,7 @@ function saveDisponibilities() {
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!';
msg.innerHTML = '<span><i class="fas fa-check"></i> Disponibilities saved successfully!</span>';
document.getElementById('disponibilities-grid').appendChild(msg);
setTimeout(function() { msg.remove(); }, 3000);
}