ajout de match régulier pour les équipes et de pratiques
Ajout d'un profil public cliquable pour les utilisateurs déplacement du profil
This commit is contained in:
@@ -0,0 +1,231 @@
|
||||
{% 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 %}
|
||||
<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;">
|
||||
{{ p.player.username[:2] | upper }} {{ p.player.username }}
|
||||
{% if p.is_confirmed %}✅{% else %}⏳{% endif %}
|
||||
</a>
|
||||
{% 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 ['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) %}
|
||||
{% 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 %}
|
||||
Reference in New Issue
Block a user