changement visualistation des evalutations
CI - Security, Lint & Tests / validate (push) Canceled after 0s

This commit is contained in:
cedrick2711
2026-09-09 12:15:25 -04:00
parent d994d88c12
commit 30069d782f
4 changed files with 700 additions and 122 deletions
+105 -54
View File
@@ -5,7 +5,7 @@
{% block breadcrumb %}<span class="breadcrumb">Home / Evaluations</span>{% endblock %}
{% block content %}
{% set sort_column = sort_column | default('created_at') %}
{% set sort_column = sort_column | default('overall_score') %}
{% set sort_order = sort_order | default('desc') %}
{% macro sort_link(column, label) %}
@@ -22,76 +22,93 @@
</th>
{% endmacro %}
{% 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">
<div class="stat-icon bg-info">
<i class="fas fa-user"></i>
</div>
<div class="stat-info">
<h3>{{ data.player.username[:12] }}</h3>
<p>Avg: <strong>{{ data.avg }}</strong> / {{ data.count }} evals</p>
</div>
</div>
{% endfor %}
<div class="view-toggle mb-3">
<a href="{{ url_for('evaluations.list_evaluations') }}" class="btn btn-sm btn-primary">
<i class="fas fa-table"></i> {{ _('Table') }}
</a>
{% if first_player_id %}
<a href="{{ url_for('evaluations.player_evaluations', player_id=first_player_id) }}" class="btn btn-sm btn-outline">
<i class="fas fa-user"></i> {{ _('By player') }}
</a>
{% endif %}
</div>
{% endif %}
<div class="card">
<div class="card-header">
<h3><i class="fas fa-clipboard-list"></i>
{% if current_user.role == 'player' %}
My Evaluations
{% else %}
All Evaluations
{% endif %}
</h3>
<h3><i class="fas fa-clipboard-list"></i> {{ _('Player Evaluations') }}</h3>
</div>
<div class="card-body">
<div class="table-container">
<table class="table">
<thead>
<tr>
{{ sort_link('tryout', 'Tryout') }}
<th></th>
{{ sort_link('player', 'Player') }}
{{ sort_link('evaluator', 'Evaluator') }}
{{ sort_link('mecanics_score', 'Mecanics') }}
{{ sort_link('cohesion_score', 'Cohesion') }}
{{ sort_link('communication_score', 'Communication') }}
{{ sort_link('gamesense_score', 'Gamesense') }}
{{ sort_link('versatility_score', 'Versatility') }}
{{ sort_link('discipline_score', 'Discipline') }}
{{ sort_link('analysis_score', 'Analysis') }}
{{ sort_link('sport_ethics_score', 'Sport Ethics') }}
{{ sort_link('mental_score', 'Mental') }}
{{ sort_link('overall_score', 'Overall') }}
{{ sort_link('position_recommendation', 'Position') }}
{{ sort_link('created_at', 'Date') }}
{{ sort_link('overall_score', 'Average') }}
{{ sort_link('count', 'Evaluations') }}
<th>{{ _('Open') }}</th>
</tr>
</thead>
<tbody>
{% for eval in evaluations %}
<tr>
<td>{{ eval.tryout.title if eval.tryout else 'Deleted Tryout' }}</td>
<td>{{ eval.player.username if eval.player else 'Deleted Player' }}</td>
<td>{{ eval.evaluator.username if eval.evaluator else 'Deleted Evaluator' }}</td>
<td>{{ eval.mecanics_score or '-' }}</td>
<td>{{ eval.cohesion_score or '-' }}</td>
<td>{{ eval.communication_score or '-' }}</td>
<td>{{ eval.gamesense_score or '-' }}</td>
<td>{{ eval.versatility_score or '-' }}</td>
<td>{{ eval.discipline_score or '-' }}</td>
<td>{{ eval.analysis_score or '-' }}</td>
<td>{{ eval.sport_ethics_score or '-' }}</td>
<td>{{ eval.mental_score or '-' }}</td>
<td><span class="score">{{ eval.overall_score or '-' }}</span></td>
<td><span class="badge badge-info">{{ eval.position_recommendation or 'N/A' }}</span></td>
<td>{{ eval.created_at.strftime('%m/%d/%Y') }}</td>
{% for entry in players %}
<tr class="eval-row" data-action="toggle-player-evals" data-target="evals-{{ entry.player.id }}">
<td><i class="fas fa-chevron-right eval-chevron"></i></td>
<td>
<a href="{{ url_for('evaluations.player_evaluations', player_id=entry.player.id) }}" class="player-link">
{{ entry.player.username }}
</a>
</td>
<td><span class="score">{{ entry.avg if entry.avg is not none else '-' }}</span></td>
<td>{{ entry.count }}</td>
<td>
<a href="{{ url_for('evaluations.player_evaluations', player_id=entry.player.id) }}" class="btn btn-sm btn-outline" title="{{ _('Open player view') }}">
<i class="fas fa-external-link-alt"></i>
</a>
</td>
</tr>
<tr class="eval-detail hidden" id="evals-{{ entry.player.id }}">
<td colspan="5">
<div class="eval-detail-inner">
<table class="table table-sm">
<thead>
<tr>
<th>{{ _('Evaluator') }}</th>
<th>{{ _('Tryout') }}</th>
{% for field, label in evaluation_criteria %}
<th>{{ label }}</th>
{% endfor %}
<th>{{ _('Overall') }}</th>
<th>{{ _('Position') }}</th>
<th>{{ _('Comment') }}</th>
<th>{{ _('Date') }}</th>
</tr>
</thead>
<tbody>
{% for ev in entry.evaluations %}
<tr>
<td>{{ ev.evaluator.username if ev.evaluator else _('Deleted evaluator') }}</td>
<td>{{ ev.tryout.title if ev.tryout else _('Deleted tryout') }}</td>
{% for score in ev.scores %}
<td>{{ score if score is not none else '-' }}</td>
{% endfor %}
<td><span class="score">{{ ev.overall if ev.overall is not none else '-' }}</span></td>
<td>{{ ev.position or '-' }}</td>
<td class="eval-comment">{{ ev.comment or '-' }}</td>
<td>{{ ev.date.strftime('%m/%d/%Y') if ev.date else '-' }}</td>
</tr>
{% else %}
<tr>
<td colspan="{{ evaluation_criteria | length + 6 }}" class="text-center">{{ _('No evaluations for this player.') }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</td>
</tr>
{% else %}
<tr>
<td colspan="15" class="text-center">
<td colspan="5" class="text-center">
<div class="empty-state">
<i class="fas fa-clipboard"></i>
<h3>{{ _('No evaluations yet') }}</h3>
@@ -106,3 +123,37 @@
</div>
</div>
{% endblock %}
{% block scripts %}
<style>
.view-toggle { display: flex; gap: 8px; }
.eval-row { cursor: pointer; }
.eval-chevron { transition: transform 0.15s ease; font-size: 0.75rem; }
.eval-detail-inner { padding: 8px 4px; }
.eval-detail-inner .table { margin-bottom: 0; }
.eval-comment { max-width: 320px; white-space: pre-wrap; }
.player-nav { display: flex; gap: 8px; align-items: center; }
.is-disabled { opacity: 0.5; pointer-events: none; }
</style>
<script nonce="{{ csp_nonce }}">
function togglePlayerEvals(element, event) {
if (event.target.closest('a')) {
return;
}
var detail = document.getElementById(element.getAttribute('data-target'));
if (!detail) {
return;
}
var chevron = element.querySelector('.eval-chevron');
var isHidden = detail.classList.contains('hidden');
if (isHidden) {
detail.classList.remove('hidden');
if (chevron) chevron.classList.add('fa-rotate-90');
} else {
detail.classList.add('hidden');
if (chevron) chevron.classList.remove('fa-rotate-90');
}
}
registerActions({ 'toggle-player-evals': togglePlayerEvals });
</script>
{% endblock %}
+108
View File
@@ -0,0 +1,108 @@
{% extends "layouts/base.html" %}
{% block title %}{{ player.username }} - {{ _('Evaluations') }} - UdeS team manager{% endblock %}
{% block page_title %}{{ _('Player Evaluations') }}{% endblock %}
{% block breadcrumb %}<span class="breadcrumb">Home / <a href="{{ url_for('evaluations.list_evaluations') }}">{{ _('Evaluations') }}</a> / {{ player.username }}</span>{% endblock %}
{% block content %}
<div class="view-toggle mb-3">
<a href="{{ url_for('evaluations.list_evaluations') }}" class="btn btn-sm btn-outline">
<i class="fas fa-table"></i> {{ _('Table') }}
</a>
<a href="{{ url_for('evaluations.player_evaluations', player_id=player.id) }}" class="btn btn-sm btn-primary">
<i class="fas fa-user"></i> {{ _('By player') }}
</a>
</div>
<div class="card mb-4">
<div class="card-header">
<h3><i class="fas fa-user"></i> {{ player.username }}</h3>
<div class="player-nav">
{% if prev_player %}
<a href="{{ url_for('evaluations.player_evaluations', player_id=prev_player.id) }}" class="btn btn-sm btn-secondary" title="{{ prev_player.username }}">
<i class="fas fa-chevron-left"></i> {{ prev_player.username }}
</a>
{% else %}
<span class="btn btn-sm btn-secondary is-disabled" aria-disabled="true"><i class="fas fa-chevron-left"></i></span>
{% endif %}
{% if next_player %}
<a href="{{ url_for('evaluations.player_evaluations', player_id=next_player.id) }}" class="btn btn-sm btn-secondary" title="{{ next_player.username }}">
{{ next_player.username }} <i class="fas fa-chevron-right"></i>
</a>
{% else %}
<span class="btn btn-sm btn-secondary is-disabled" aria-disabled="true"><i class="fas fa-chevron-right"></i></span>
{% endif %}
</div>
</div>
<div class="card-body">
<div class="stats-grid mb-4">
<div class="stat-card">
<div class="stat-icon bg-info"><i class="fas fa-star"></i></div>
<div class="stat-info">
<h3>{{ combined_average if combined_average is not none else '-' }}</h3>
<p>{{ _('Combined average') }} &middot; {{ evaluation_count }} {{ _('evaluations') }}</p>
</div>
</div>
{% for label, avg in criterion_averages %}
<div class="stat-card stat-card-sm">
<div class="stat-icon bg-info"><i class="fas fa-chart-bar"></i></div>
<div class="stat-info">
<h3>{{ avg if avg is not none else '-' }}</h3>
<p>{{ label }}</p>
</div>
</div>
{% endfor %}
</div>
<div class="table-container">
<table class="table">
<thead>
<tr>
<th>{{ _('Evaluator') }}</th>
<th>{{ _('Tryout') }}</th>
{% for field, label in evaluation_criteria %}
<th>{{ label }}</th>
{% endfor %}
<th>{{ _('Overall') }}</th>
<th>{{ _('Position') }}</th>
<th>{{ _('Comment') }}</th>
<th>{{ _('Date') }}</th>
</tr>
</thead>
<tbody>
{% for ev in evaluations %}
<tr>
<td>{{ ev.evaluator.username if ev.evaluator else _('Deleted evaluator') }}</td>
<td>{{ ev.tryout.title if ev.tryout else _('Deleted tryout') }}</td>
{% for score in ev.scores %}
<td>{{ score if score is not none else '-' }}</td>
{% endfor %}
<td><span class="score">{{ ev.overall if ev.overall is not none else '-' }}</span></td>
<td>{{ ev.position or '-' }}</td>
<td class="eval-comment">{{ ev.comment or '-' }}</td>
<td>{{ ev.date.strftime('%m/%d/%Y') if ev.date else '-' }}</td>
</tr>
{% else %}
<tr>
<td colspan="{{ evaluation_criteria | length + 6 }}" class="text-center">
<div class="empty-state">
<i class="fas fa-clipboard"></i>
<h3>{{ _('No evaluations for this player yet') }}</h3>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<style>
.view-toggle { display: flex; gap: 8px; }
.player-nav { display: flex; gap: 8px; align-items: center; }
.is-disabled { opacity: 0.5; pointer-events: none; }
.eval-comment { max-width: 320px; white-space: pre-wrap; }
</style>
{% endblock %}