Correction des présences et correction de plusieurs erreurs mineur de déplacement/parcours de l'utilisateur, harmonisation des processus
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
{% 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 ['president', 'manager', 'coach'] %}
|
||||
{% 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>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="dashboard">
|
||||
{% if user.role == 'president' %}
|
||||
{% if user.role == 'admin' %}
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon bg-primary">
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</th>
|
||||
{% endmacro %}
|
||||
|
||||
{% if current_user.role == 'president' and player_scores %}
|
||||
{% 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">
|
||||
|
||||
@@ -75,7 +75,12 @@
|
||||
{% if current_user.can_manage_teams() or current_user.can_schedule_matches() %}
|
||||
<hr class="section-divider">
|
||||
<h4 class="section-title"><i class="fas fa-clock"></i> Select Match Time</h4>
|
||||
<p class="text-muted small">Click time slots consecutively to set match duration. Players available in all selected time blocks are shown below.</p>
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:8px;">
|
||||
<p class="text-muted small" style="margin:0;">Click time slots consecutively to set match duration. Players available in all selected time blocks are shown below.</p>
|
||||
<button type="button" class="btn btn-sm btn-outline" onclick="clearTimeSelection()" title="Reset time selection">
|
||||
<i class="fas fa-undo"></i> Reset Time
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="merged-disponibility-grid" class="merged-disponibility-grid">
|
||||
<p class="text-muted">Loading...</p>
|
||||
|
||||
@@ -77,12 +77,11 @@
|
||||
<!-- Player presence details -->
|
||||
<div class="presence-players">
|
||||
{% for p in item.participants %}
|
||||
<a href="{{ url_for('users.view_user', user_id=p.player.id) }}" class="presence-player-tag {% if p.is_confirmed %}confirmed{% else %}pending{% endif %}"
|
||||
title="{{ p.player.username }}{% if p.is_confirmed %} - Confirmed{% else %} - Pending{% endif %}"
|
||||
style="text-decoration: none;">
|
||||
<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 %}
|
||||
</a>
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
@@ -93,7 +92,7 @@
|
||||
<span class="badge badge-{{ m.status }}">{{ m.status }}</span>
|
||||
</td>
|
||||
<td class="eval-actions">
|
||||
{% set can_manage_this = (current_user.role in ['president', '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) %}
|
||||
{% 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>
|
||||
|
||||
+70
-47
@@ -124,36 +124,10 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% if can_manage %}
|
||||
<div class="staff-add-forms">
|
||||
<form method="POST" action="{{ url_for('teams.add_coach', team_id=team.id) }}" class="inline-form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<select name="coach_id" class="form-select form-select-sm" onchange="if(this.value) this.form.submit()">
|
||||
<option value="">+ Add Coach</option>
|
||||
{% for c in coaches %}
|
||||
{% if c.id not in (team_coaches | map(attribute='id') | list) %}
|
||||
<option value="{{ c.id }}">{{ c.username }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
</form>
|
||||
<form method="POST" action="{{ url_for('teams.add_manager', team_id=team.id) }}" class="inline-form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||
<select name="manager_id" class="form-select form-select-sm" onchange="if(this.value) this.form.submit()">
|
||||
<option value="">+ Add Manager</option>
|
||||
{% for m in managers %}
|
||||
{% if m.id not in (team_managers | map(attribute='id') | list) %}
|
||||
<option value="{{ m.id }}">{{ m.username }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
<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 ['president', '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)) %}
|
||||
{% 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>
|
||||
@@ -286,28 +260,37 @@
|
||||
<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">
|
||||
<label for="edit_coach_id">Assigned Coach</label>
|
||||
<select id="edit_coach_id" name="coach_id" class="form-select">
|
||||
<option value="">-- No coach assigned --</option>
|
||||
<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 }}">{{ coach.username }}</option>
|
||||
<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">
|
||||
<label for="edit_manager_id">Assigned Manager</label>
|
||||
<select id="edit_manager_id" name="manager_id" class="form-select">
|
||||
<option value="">-- No manager assigned --</option>
|
||||
<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 }}">{{ manager.username }}</option>
|
||||
<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>
|
||||
@@ -355,28 +338,68 @@ function toggleStatus(btn) {
|
||||
});
|
||||
}
|
||||
|
||||
var currentEditTeamId = null;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.querySelectorAll('.edit-team-btn').forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
var teamId = this.getAttribute('data-team-id');
|
||||
currentEditTeamId = this.getAttribute('data-team-id');
|
||||
var teamName = this.getAttribute('data-team-name');
|
||||
var coachId = this.getAttribute('data-coach-id');
|
||||
var managerId = this.getAttribute('data-manager-id');
|
||||
document.getElementById('editTeamForm').action = '/teams/' + teamId + '/edit';
|
||||
document.getElementById('editTeamForm').action = '/teams/' + currentEditTeamId + '/edit';
|
||||
document.getElementById('edit_name').value = teamName;
|
||||
document.getElementById('edit_coach_id').value = coachId || '';
|
||||
document.getElementById('edit_manager_id').value = managerId || '';
|
||||
document.getElementById('editTeamModal').classList.remove('hidden');
|
||||
populateEditSelects(currentEditTeamId);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function showEditForm(teamId, teamName, coachId, managerId) {
|
||||
document.getElementById('editTeamForm').action = '/teams/' + teamId + '/edit';
|
||||
document.getElementById('edit_name').value = teamName;
|
||||
document.getElementById('edit_coach_id').value = coachId || '';
|
||||
document.getElementById('edit_manager_id').value = managerId || '';
|
||||
document.getElementById('editTeamModal').classList.remove('hidden');
|
||||
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() {
|
||||
|
||||
@@ -338,6 +338,18 @@
|
||||
<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 %}
|
||||
@@ -484,10 +496,62 @@
|
||||
{% 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');
|
||||
|
||||
Reference in New Issue
Block a user