fix: js errors

This commit is contained in:
2025-08-12 06:41:39 +02:00
parent c132419213
commit 11bfc5c5cb
3 changed files with 1284 additions and 1 deletions

View File

@@ -395,6 +395,128 @@
</div>
{% endif %}
{# Section Compétences et Domaines #}
{% if summary.competence_domain_breakdown and (summary.competence_domain_breakdown.competences or summary.competence_domain_breakdown.domains) %}
<div class="competence-domain-section">
<h4 class="font-medium text-gray-700 mb-3 flex items-center">
<svg class="w-4 h-4 text-indigo-500 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
Progression par compétence et domaine
</h4>
{# Barres de compétences #}
{% if summary.competence_domain_breakdown.competences %}
<div class="mb-4">
<h5 class="text-sm font-medium text-purple-700 mb-3 flex items-center">
<svg class="w-3 h-3 text-purple-500 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/>
</svg>
Compétences
</h5>
<div class="space-y-2">
{% for competence in summary.competence_domain_breakdown.competences %}
<div class="competence-progress-bar">
<div class="flex items-center justify-between mb-1">
<span class="text-sm font-medium text-gray-700">{{ competence.name }}</span>
<span class="text-sm font-bold" style="color: {{ competence.color }}">
{{ competence.percentage }}% ({{ competence.earned_points }}/{{ competence.total_points }})
</span>
</div>
<div class="progress-bar-container segmented-progress"
data-competence-name="{{ competence.name }}"
data-assessments="{{ competence.assessments | tojson | e }}"
role="progressbar"
aria-valuenow="{{ competence.percentage }}"
aria-valuemin="0"
aria-valuemax="100"
aria-label="Progression de la compétence {{ competence.name }}: {{ competence.percentage }}%"
tabindex="0">
<div class="segmented-progress-bar" data-expanded="true">
<!-- Segments pour chaque évaluation -->
{% for assessment in competence.assessments %}
<div class="progress-segment"
style="width: {{ assessment.percentage_contribution }}%; background-color: {{ assessment.color }};"
data-assessment-id="{{ assessment.id }}"
data-assessment-title="{{ assessment.title }}"
data-assessment-performance="{{ assessment.performance }}"
data-earned-this="{{ assessment.earned_this }}"
data-max-this="{{ assessment.max_this }}"
data-earned-cumulative="{{ assessment.earned_cumulative }}"
data-max-cumulative="{{ assessment.max_cumulative }}"
data-contribution-percentage="{{ assessment.percentage_contribution }}"
title="{{ assessment.title }}: {{ assessment.earned_this }} pts ({{ assessment.performance }}%)"
role="button"
tabindex="0"
aria-label="Évaluation {{ assessment.title }}: {{ assessment.earned_this }} points sur {{ assessment.max_this }}">
<span class="segment-label">{{ assessment.title }}</span>
</div>
{% endfor %}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{# Barres de domaines #}
{% if summary.competence_domain_breakdown.domains %}
<div>
<h5 class="text-sm font-medium text-orange-700 mb-3 flex items-center">
<svg class="w-3 h-3 text-orange-500 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M3 4a1 1 0 011-1h4a1 1 0 010 2H6.414l2.293 2.293a1 1 0 01-1.414 1.414L5 6.414V8a1 1 0 01-2 0V4zm9 1a1 1 0 010-2h4a1 1 0 011 1v4a1 1 0 01-2 0V6.414l-2.293 2.293a1 1 0 11-1.414-1.414L13.586 5H12zm-9 7a1 1 0 012 0v1.586l2.293-2.293a1 1 0 111.414 1.414L6.414 15H8a1 1 0 010 2H4a1 1 0 01-1-1v-4zm13-1a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 010-2h1.586l-2.293-2.293a1 1 0 111.414-1.414L15 13.586V12a1 1 0 011-1z" clip-rule="evenodd"/>
</svg>
Domaines
</h5>
<div class="space-y-2">
{% for domain in summary.competence_domain_breakdown.domains %}
<div class="domain-progress-bar">
<div class="flex items-center justify-between mb-1">
<span class="text-sm font-medium text-gray-700">{{ domain.name }}</span>
<span class="text-sm font-bold" style="color: {{ domain.color }}">
{{ domain.percentage }}% ({{ domain.earned_points }}/{{ domain.total_points }})
</span>
</div>
<div class="progress-bar-container segmented-progress"
data-domain-name="{{ domain.name }}"
data-assessments="{{ domain.assessments | tojson | e }}"
role="progressbar"
aria-valuenow="{{ domain.percentage }}"
aria-valuemin="0"
aria-valuemax="100"
aria-label="Progression du domaine {{ domain.name }}: {{ domain.percentage }}%"
tabindex="0">
<div class="segmented-progress-bar" data-expanded="true">
<!-- Segments pour chaque évaluation -->
{% for assessment in domain.assessments %}
<div class="progress-segment"
style="width: {{ assessment.percentage_contribution }}%; background-color: {{ assessment.color }};"
data-assessment-id="{{ assessment.id }}"
data-assessment-title="{{ assessment.title }}"
data-assessment-performance="{{ assessment.performance }}"
data-earned-this="{{ assessment.earned_this }}"
data-max-this="{{ assessment.max_this }}"
data-earned-cumulative="{{ assessment.earned_cumulative }}"
data-max-cumulative="{{ assessment.max_cumulative }}"
data-contribution-percentage="{{ assessment.percentage_contribution }}"
title="{{ assessment.title }}: {{ assessment.earned_this }} pts ({{ assessment.performance }}%)"
role="button"
tabindex="0"
aria-label="Évaluation {{ assessment.title }}: {{ assessment.earned_this }} points sur {{ assessment.max_this }}">
<span class="segment-label">{{ assessment.title }}</span>
</div>
{% endfor %}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
{% endif %}
{# Zone d'appréciation #}
<div>
<label class="block font-medium text-gray-700 mb-3 flex items-center">
@@ -647,6 +769,462 @@ body.focus-mode {
margin-bottom: 0.5rem;
}
/* ========== BARRES DE PROGRESSION COMPÉTENCES/DOMAINES ========== */
/* ========== BARRES DE PROGRESSION SEGMENTÉES ========== */
/* Conteneur principal des barres de progression */
.competence-progress-bar,
.domain-progress-bar {
margin-bottom: 0.75rem;
}
.progress-bar-container {
position: relative;
cursor: pointer;
transition: all 0.3s ease;
}
/* Conteneur segmenté avec apparence moderne et claire */
.segmented-progress {
border-radius: 10px;
background: rgba(255, 255, 255, 0.8);
border: 1px solid rgba(0, 0, 0, 0.08);
padding: 6px;
min-height: 44px;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}
.segmented-progress:hover {
border-color: rgba(0, 0, 0, 0.12);
background: rgba(255, 255, 255, 0.95);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08), 0 1px 3px rgba(0, 0, 0, 0.1);
}
.segmented-progress:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
/* Barre segmentée - état expandé par défaut */
.segmented-progress-bar {
display: flex;
height: 36px;
gap: 3px;
padding: 3px;
border-radius: 6px;
overflow: visible;
position: relative;
background: transparent;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
/* État contracté (optionnel, si besoin futur) */
.segmented-progress-bar[data-expanded="false"] {
height: 16px;
gap: 0;
padding: 0;
background: #e5e7eb;
overflow: hidden;
}
/* Segments individuels - état expandé par défaut */
.progress-segment {
height: 30px;
border-radius: 6px;
position: relative;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
min-width: 8px; /* Minimum pour les petites contributions */
border: 2px solid rgba(255, 255, 255, 0.5);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
animation: segmentAppear 0.6s ease-out forwards;
overflow: hidden;
}
/* État contracté des segments (optionnel) */
.segmented-progress-bar[data-expanded="false"] .progress-segment {
height: 100%;
border-radius: 4px;
border: 1px solid rgba(255, 255, 255, 0.3);
box-shadow: none;
}
/* Séparateurs visuels entre segments (état contracté seulement) */
.segmented-progress-bar:not([data-expanded="true"]) .progress-segment:not(:last-child) {
border-right: 2px solid rgba(255, 255, 255, 0.8);
}
/* Hover effects pour les segments */
.progress-segment:hover {
filter: brightness(1.15);
transform: scaleY(1.05);
z-index: 10;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15);
}
.segmented-progress-bar[data-expanded="true"] .progress-segment:hover {
transform: translateY(-2px) scale(1.02);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
}
/* Focus pour accessibilité clavier */
.progress-segment:focus {
outline: none;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3);
z-index: 15;
}
/* Labels des segments - visibles par défaut */
.segment-label {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 0.7rem;
font-weight: 600;
color: rgba(255, 255, 255, 0.95);
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
opacity: 1;
transition: opacity 0.3s ease;
pointer-events: none;
white-space: nowrap;
max-width: 90%;
overflow: hidden;
text-overflow: ellipsis;
}
/* État contracté des labels (optionnel) */
.segmented-progress-bar[data-expanded="false"] .segment-label {
opacity: 0;
font-size: 0.65rem;
}
/* Tooltips améliorés avec fond clair */
.progress-segment:hover::after {
content: attr(data-assessment-title) ': ' attr(data-earned-this) ' pts (' attr(data-assessment-performance) '%)';
position: absolute;
bottom: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
background: rgba(255, 255, 255, 0.98);
color: #374151;
padding: 8px 12px;
border-radius: 8px;
font-size: 0.75rem;
font-weight: 600;
white-space: nowrap;
z-index: 1001;
pointer-events: none;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(0, 0, 0, 0.08);
animation: tooltipFadeIn 0.2s ease-out;
}
.progress-segment:hover::before {
content: '';
position: absolute;
bottom: calc(100% + 2px);
left: 50%;
transform: translateX(-50%);
border: 6px solid transparent;
border-top-color: rgba(255, 255, 255, 0.98);
z-index: 1001;
pointer-events: none;
}
/* Animation d'apparition des segments */
@keyframes segmentAppear {
0% {
opacity: 0;
transform: scaleX(0);
}
60% {
opacity: 0.9;
transform: scaleX(1.02);
}
100% {
opacity: 1;
transform: scaleX(1);
}
}
@keyframes tooltipFadeIn {
0% {
opacity: 0;
transform: translateX(-50%) translateY(4px);
}
100% {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
}
/* Animation séquentielle des segments */
.progress-segment:nth-child(1) { animation-delay: 0.1s; }
.progress-segment:nth-child(2) { animation-delay: 0.2s; }
.progress-segment:nth-child(3) { animation-delay: 0.3s; }
.progress-segment:nth-child(4) { animation-delay: 0.4s; }
.progress-segment:nth-child(5) { animation-delay: 0.5s; }
.progress-segment:nth-child(6) { animation-delay: 0.6s; }
/* ========== RESPONSIVE ET ACCESSIBILITÉ ========== */
/* Responsive pour mobile */
@media (max-width: 640px) {
.segmented-progress {
padding: 3px;
min-height: 24px;
}
.segmented-progress-bar {
height: 20px;
}
.segmented-progress-bar[data-expanded="true"] {
height: 44px; /* Touch target minimum 44px */
gap: 2px;
padding: 2px;
}
.segmented-progress-bar[data-expanded="true"] .progress-segment {
height: 40px;
min-width: 44px; /* Touch target minimum */
}
.segment-label {
font-size: 0.6rem;
}
.segmented-progress-bar[data-expanded="true"] .segment-label {
font-size: 0.65rem;
}
.progress-segment:hover::after {
font-size: 0.7rem;
padding: 6px 10px;
bottom: calc(100% + 12px);
}
}
/* Responsive pour très petits écrans */
@media (max-width: 480px) {
.competence-progress-bar .flex.items-center.justify-between,
.domain-progress-bar .flex.items-center.justify-between {
flex-direction: column;
align-items: flex-start;
gap: 0.5rem;
}
.progress-segment:hover::after {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
bottom: auto;
max-width: 280px;
text-align: center;
font-size: 0.75rem;
padding: 8px 12px;
}
}
/* Mode sombre - Réduction de mouvement pour accessibilité */
@media (prefers-reduced-motion: reduce) {
.progress-segment,
.segmented-progress,
.segmented-progress-bar,
.segment-label,
.progress-summary {
animation: none !important;
transition: none !important;
}
.progress-segment:hover {
transform: none;
}
}
/* Contraste élevé pour accessibilité */
@media (prefers-contrast: high) {
.segmented-progress {
border: 3px solid #000000;
}
.progress-segment {
border: 2px solid #ffffff;
filter: contrast(1.2);
}
.segment-label {
text-shadow: 1px 1px 0 #000000;
font-weight: 700;
}
}
/* Mode focus amélioré pour les barres segmentées */
.focus-mode-student .segmented-progress {
padding: 2px;
min-height: 16px;
}
.focus-mode-student .segmented-progress-bar {
height: 12px;
}
.focus-mode-student .segmented-progress-bar[data-expanded="true"] {
height: 28px;
}
.focus-mode-student .segmented-progress-bar[data-expanded="true"] .progress-segment {
height: 24px;
}
/* ========== CLASSES UTILITAIRES D'ACCESSIBILITÉ ========== */
/* Screen reader only content */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* Skip link pour navigation au clavier */
.skip-link {
position: absolute;
top: -40px;
left: 6px;
background: #000;
color: #fff;
padding: 8px;
text-decoration: none;
border-radius: 4px;
z-index: 9999;
transition: top 0.3s;
}
.skip-link:focus {
top: 6px;
}
/* Focus visible amélioré pour tous les éléments interactifs */
.progress-segment:focus-visible,
.segmented-progress:focus-visible {
outline: 3px solid #4f46e5;
outline-offset: 2px;
border-radius: 4px;
}
/* Indicateur de charge pour les animations */
.progress-segment[aria-busy="true"]::after {
content: '';
position: absolute;
top: 2px;
right: 2px;
width: 8px;
height: 8px;
border: 1px solid rgba(255,255,255,0.8);
border-top-color: transparent;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Mode sombre - support système */
@media (prefers-color-scheme: dark) {
.segmented-progress {
background: rgba(31, 41, 55, 0.8);
border-color: rgba(75, 85, 99, 0.4);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
.segmented-progress:hover {
background: rgba(31, 41, 55, 0.95);
border-color: rgba(75, 85, 99, 0.6);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4), 0 1px 3px rgba(0, 0, 0, 0.3);
}
.progress-segment {
border-color: rgba(255, 255, 255, 0.2);
}
.segment-label {
color: rgba(255, 255, 255, 0.9);
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
}
.progress-segment:hover::after {
background: rgba(17, 24, 39, 0.95);
color: #f9fafb;
border-color: rgba(255, 255, 255, 0.1);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.1);
}
.progress-segment:hover::before {
border-top-color: rgba(17, 24, 39, 0.95);
}
}
/* ========== CLASSES DE PERFORMANCE POUR LES NOUVEAUX SEGMENTS ========== */
/* Indicateurs visuels de performance pour les nouveaux segments */
.progress-segment.segment-excellent {
box-shadow: inset 0 0 0 2px rgba(34, 197, 94, 0.4);
}
.progress-segment.segment-good {
box-shadow: inset 0 0 0 2px rgba(59, 130, 246, 0.4);
}
.progress-segment.segment-average {
box-shadow: inset 0 0 0 2px rgba(245, 158, 11, 0.4);
}
.progress-segment.segment-struggling {
box-shadow: inset 0 0 0 2px rgba(239, 68, 68, 0.4);
}
/* Effet de pulsation pour les segments excellents */
.progress-segment.segment-excellent:hover {
animation: excellentPulse 0.6s ease-in-out;
}
@keyframes excellentPulse {
0%, 100% { filter: brightness(1.2); }
50% { filter: brightness(1.4); }
}
/* ========== ANIMATION D'APPARITION DE LA SECTION ========== */
/* Animation d'apparition pour toute la section */
.competence-domain-section {
animation: slideInUp 0.5s ease-out;
}
@keyframes slideInUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Boutons de navigation désactivés */
button:disabled {
opacity: 0.5;