Files
notytex/templates/class_council_preparation.html

1655 lines
74 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "base.html" %}
{% from 'components/common/macros.html' import hero_section %}
{% block title %}Préparation Conseil de Classe - {{ class_group.name }} - T{{ trimester }}{% endblock %}
{# Override le style du main container pour éviter le clipping des hover effects #}
{% block main_class %}w-full px-8 py-8 bg-gray-100{% endblock %}
{% block content %}
<div class="council-preparation" data-council-preparation data-class-id="{{ class_group.id }}" data-trimester="{{ trimester }}">
<!-- Loading overlay -->
<div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50" data-loading-overlay>
<div class="bg-white rounded-xl p-6 flex items-center space-x-3">
<svg class="animate-spin w-6 h-6 text-purple-600" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<span class="text-gray-700 font-medium">Chargement...</span>
</div>
</div>
<div class="max-w-7xl mx-auto">
<div class="space-y-8" style="overflow: visible; padding: 8px; margin: -8px;">
{# 1. Hero Section #}
{% set meta_info = [
{
'icon': '<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20"><path d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"/></svg>',
'text': council_data.total_students ~ ' élèves'
},
{
'icon': '<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z" clip-rule="evenodd"/></svg>',
'text': 'Trimestre ' ~ trimester
},
{
'icon': '<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20"><path d="M2.93 17.07A10 10 0 1117.07 2.93 10 10 0 012.93 17.07zM11.4 10.6L9 8.2V4.5a.5.5 0 00-1 0v4a.5.5 0 00.15.35l2.7 2.7a.5.5 0 00.7-.7z"/></svg>',
'text': council_data.completed_appreciations ~ '/' ~ council_data.total_students ~ ' appréciations rédigées'
}
] %}
<div class="list-mode-hero">
{{ hero_section(
title="📊 Préparation Conseil de Classe",
subtitle="Rédaction des appréciations • " + class_group.name,
meta_info=meta_info,
gradient_class="from-purple-600 to-orange-500"
) }}
</div>
{# Breadcrumb de retour et sélecteur de trimestre #}
<div class="list-mode-breadcrumb flex flex-col sm:flex-row sm:items-center justify-between space-y-3 sm:space-y-0">
<div class="flex items-center text-sm text-gray-600">
<a href="{{ url_for('classes.dashboard', id=class_group.id) }}" class="hover:text-blue-600 transition-colors flex items-center">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
Retour au dashboard de classe
</a>
</div>
{# Sélecteur de trimestre #}
<div class="flex items-center space-x-3">
<label class="text-sm font-medium text-gray-700">Trimestre :</label>
<select id="trimester-selector"
data-trimester-selector
class="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-purple-500 focus:border-purple-500 bg-white shadow-sm">
<option value="1" {% if trimester == 1 %}selected{% endif %}>Trimestre 1</option>
<option value="2" {% if trimester == 2 %}selected{% endif %}>Trimestre 2</option>
<option value="3" {% if trimester == 3 %}selected{% endif %}>Trimestre 3</option>
</select>
</div>
</div>
{# 2. Filtres et Actions Principales #}
<div class="list-mode-filters-section bg-white rounded-xl shadow-sm border border-gray-200 p-6">
<div class="flex flex-col lg:flex-row lg:items-center justify-between space-y-4 lg:space-y-0">
{# Recherche et filtres - masqués en mode focus #}
<div class="list-mode-filters flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-4">
<div class="relative">
<input type="text"
data-search-students
placeholder="Rechercher un élève..."
class="pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 text-sm w-64">
<svg class="w-4 h-4 text-gray-400 absolute left-3 top-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clip-rule="evenodd"/>
</svg>
</div>
<select data-sort-students class="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-purple-500 focus:border-purple-500">
<option value="alphabetical">Trier par nom</option>
<option value="average">Trier par moyenne</option>
<option value="status">Trier par statut</option>
</select>
<select data-filter-status class="border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-purple-500 focus:border-purple-500">
<option value="all">Tous les élèves</option>
<option value="completed">Appréciations rédigées</option>
<option value="pending">À rédiger</option>
<option value="struggling">Élèves en difficulté</option>
</select>
</div>
{# Actions globales #}
<div class="flex space-x-3">
{# Toujours visible : bouton mode focus #}
<button data-toggle-focus-mode
class="inline-flex items-center px-4 py-2 bg-gradient-to-r from-purple-500 to-purple-600 text-white rounded-lg hover:from-purple-600 hover:to-purple-700 transition-all duration-300 text-sm font-medium transform hover:scale-[1.02] shadow-lg hover:shadow-xl">
<svg class="w-4 h-4 mr-2" 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>
<span data-focus-mode-text>Mode Focus</span>
</button>
{# Actions masquées en mode focus #}
<div class="list-mode-actions space-x-3 flex">
<button data-export-pdf
class="inline-flex items-center px-4 py-2 bg-gradient-to-r from-green-500 to-green-600 text-white rounded-lg hover:from-green-600 hover:to-green-700 transition-all duration-300 text-sm font-medium transform hover:scale-[1.02] shadow-lg hover:shadow-xl">
<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z" clip-rule="evenodd"/>
</svg>
Exporter PDF
</button>
<button data-class-synthesis
class="inline-flex items-center px-4 py-2 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-lg hover:from-blue-600 hover:to-blue-700 transition-all duration-300 text-sm font-medium transform hover:scale-[1.02] shadow-lg hover:shadow-xl">
<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path d="M2 11a1 1 0 011-1h2a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1v-5zM8 7a1 1 0 011-1h2a1 1 0 011 1v9a1 1 0 01-1 1H9a1 1 0 01-1-1V7zM14 4a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1h-2a1 1 0 01-1-1V4z"/>
</svg>
Synthèse classe
</button>
</div>
</div>
</div>
{# Compteur de résultats et contrôles mode focus #}
<div class="mt-4 pt-4 border-t border-gray-200">
{# Mode liste - compteur normal #}
<div class="list-mode-controls">
<p class="text-sm text-gray-600" data-results-counter>
{{ student_summaries|length }} élèves affichés
</p>
</div>
{# Mode focus - contrôles de navigation #}
<div class="focus-mode-controls hidden">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-4">
<button data-focus-prev
class="inline-flex items-center px-3 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors text-sm font-medium disabled:opacity-50 disabled:cursor-not-allowed">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
Précédent
</button>
<div class="text-sm text-gray-600 bg-gray-50 px-3 py-2 rounded-lg">
Élève <span data-focus-current>1</span> sur <span data-focus-total>{{ student_summaries|length }}</span>
</div>
<button data-focus-next
class="inline-flex items-center px-3 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors text-sm font-medium disabled:opacity-50 disabled:cursor-not-allowed">
Suivant
<svg class="w-4 h-4 ml-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
</button>
</div>
<div class="flex items-center space-x-2 text-xs text-gray-500">
<span>Navigation: ← → | Échap pour quitter</span>
</div>
</div>
</div>
</div>
</div>
{# Header compact mode focus - visible uniquement en mode focus #}
<div class="focus-mode-header hidden bg-white rounded-lg shadow-sm border border-gray-200 p-3 mb-4">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-3">
<button data-toggle-focus-mode
class="inline-flex items-center px-3 py-1.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-md transition-colors text-sm font-medium">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M9.707 16.707a1 1 0 01-1.414 0l-6-6a1 1 0 010-1.414l6-6a1 1 0 011.414 1.414L5.414 9H17a1 1 0 110 2H5.414l4.293 4.293a1 1 0 010 1.414z" clip-rule="evenodd"/>
</svg>
Mode Liste
</button>
<div class="text-sm text-gray-600">
<strong>{{ class_group.name }}</strong> • Trimestre {{ trimester }}
</div>
</div>
<div class="flex items-center space-x-3">
<button data-focus-prev
class="inline-flex items-center px-2 py-1 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded transition-colors text-sm disabled:opacity-50 disabled:cursor-not-allowed">
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
</button>
<div class="text-sm text-gray-600 bg-gray-50 px-2 py-1 rounded">
<span data-focus-current>1</span>/<span data-focus-total>{{ student_summaries|length }}</span>
</div>
<button data-focus-next
class="inline-flex items-center px-2 py-1 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded transition-colors text-sm disabled:opacity-50 disabled:cursor-not-allowed">
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
</button>
<div class="text-xs text-gray-500">← → Échap</div>
</div>
</div>
</div>
{# 3. Liste des Élèves (Cards Expandables) #}
<div class="students-display">
{# Mode liste - tous les élèves visibles #}
<div class="list-mode-display space-y-4" data-students-container>
{% for summary in student_summaries %}
<div class="bg-white rounded-xl shadow-sm border border-gray-200 hover:shadow-md transition-all duration-300
{% if summary.performance_status == 'struggling' %}border-l-4 border-l-red-400{% endif %}
{% if summary.performance_status == 'excellent' %}border-l-4 border-l-green-400{% endif %}
{% if summary.performance_status == 'good' %}border-l-4 border-l-blue-400{% endif %}
{% if summary.performance_status == 'no_data' %}border-l-4 border-l-gray-400{% endif %}"
data-student-card="{{ summary.student.id }}"
data-student-name="{{ summary.student.last_name }} {{ summary.student.first_name }}"
data-student-average="{{ summary.overall_average or 0 }}"
data-performance-status="{{ summary.performance_status }}"
data-has-appreciation="{{ 'true' if summary.has_appreciation else 'false' }}">
{# Header avec layout en deux colonnes principales #}
<div class="px-6 py-4">
{# Layout principal : Informations élève + Appréciation + Moyenne #}
<div class="flex items-start space-x-6">
{# Colonne 1: Informations élève avec avatar #}
<div class="flex items-center space-x-4 cursor-pointer" data-toggle-student="{{ summary.student.id }}">
{# Avatar avec initiales #}
<div class="w-12 h-12 rounded-full flex items-center justify-center text-white font-bold text-sm
{% if summary.performance_status == 'excellent' %}bg-gradient-to-r from-green-500 to-green-600{% endif %}
{% if summary.performance_status == 'good' %}bg-gradient-to-r from-blue-500 to-blue-600{% endif %}
{% if summary.performance_status == 'average' %}bg-gradient-to-r from-yellow-500 to-yellow-600{% endif %}
{% if summary.performance_status == 'struggling' %}bg-gradient-to-r from-red-500 to-red-600{% endif %}
{% if summary.performance_status == 'no_data' %}bg-gradient-to-r from-gray-500 to-gray-600{% endif %}">
{{ summary.student.first_name[0] }}{{ summary.student.last_name[0] }}
</div>
{# Informations de base élève #}
<div class="min-w-0">
<h3 class="font-semibold text-gray-900 text-lg">{{ summary.student.last_name }}, {{ summary.student.first_name }}</h3>
{# Info de base avec chevron #}
<div class="flex items-center space-x-4 text-sm text-gray-600">
<span class="flex items-center">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path d="M2 11a1 1 0 011-1h2a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1v-5zM8 7a1 1 0 011-1h2a1 1 0 011 1v9a1 1 0 01-1 1H9a1 1 0 01-1-1V7zM14 4a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1h-2a1 1 0 01-1-1V4z"/>
</svg>
{{ summary.assessment_count }} évaluation(s)
</span>
{% if summary.performance_status == 'struggling' %}
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs bg-red-100 text-red-800 font-medium">
⚠️ Attention requise
</span>
{% endif %}
{# Chevron d'expansion #}
<svg class="w-4 h-4 text-gray-400 transform transition-transform duration-300"
data-toggle-icon fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/>
</svg>
</div>
{# Résumé compact des valeurs spéciales #}
{% if summary.special_values_summary and summary.special_values_summary.has_special_values %}
<div class="mt-2 flex items-center space-x-1 text-xs">
<span class="text-gray-500">Valeurs spéciales:</span>
{% for special_value, data in summary.special_values_summary.global.items() %}
{% if data.count > 0 %}
{% set tooltip_content = [] %}
{% for detail in data.details %}
{% if detail.comment %}
{% set _ = tooltip_content.append(detail.element_name + ': ' + detail.comment) %}
{% else %}
{% set _ = tooltip_content.append(detail.element_name) %}
{% endif %}
{% endfor %}
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium cursor-help"
style="background-color: {{ data.color }}20; color: {{ data.color }};"
title="{{ data.label }} ({{ data.count }}){% if tooltip_content|length > 0 %}&#10;{{ tooltip_content|join('&#10;') }}{% endif %}">
{{ special_value }} {{ data.count }}
</span>
{% endif %}
{% endfor %}
</div>
{% endif %}
</div>
</div>
{# Colonne 2: Zone d'appréciation intégrée #}
<div class="flex-1 min-w-0">
<label class="block text-sm font-medium text-gray-700 mb-2 flex items-center">
<svg class="w-4 h-4 text-purple-500 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/>
</svg>
Appréciation du conseil de classe
</label>
<textarea
data-appreciation-textarea
data-student-id="{{ summary.student.id }}"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-purple-500 focus:border-purple-500 resize-none transition-colors"
rows="2"
placeholder="Saisir l'appréciation générale pour le bulletin...">{% if summary.appreciation and summary.appreciation.general_appreciation %}{{ summary.appreciation.general_appreciation }}{% endif %}</textarea>
<div class="mt-1 flex justify-between items-center text-xs text-gray-500">
<div class="flex items-center space-x-3">
<span>Sauvegarde automatique</span>
<div class="hidden" data-save-indicator="{{ summary.student.id }}"></div>
{% if summary.appreciation %}
<span class="text-gray-400"></span>
<span data-last-modified="{{ summary.student.id }}">{{ summary.appreciation.last_modified.strftime('%d/%m à %H:%M') }}</span>
{% endif %}
</div>
<span data-char-counter>0 caractères</span>
</div>
</div>
{# Colonne 3: Moyenne et statuts #}
<div class="flex flex-col items-end space-y-2 text-right min-w-max">
{# Moyenne principale #}
<div class="flex items-center space-x-2">
{% if summary.overall_average %}
<span class="text-xl font-bold px-4 py-2 rounded-lg
{% if summary.performance_status == 'excellent' %}bg-green-100 text-green-800{% endif %}
{% if summary.performance_status == 'good' %}bg-blue-100 text-blue-800{% endif %}
{% if summary.performance_status == 'average' %}bg-yellow-100 text-yellow-800{% endif %}
{% if summary.performance_status == 'struggling' %}bg-red-100 text-red-800{% endif %}">
{{ "%.1f"|format(summary.overall_average) }}/20
</span>
{% else %}
<span class="text-sm text-gray-500 px-3 py-1 bg-gray-100 rounded-lg">
Pas de données
</span>
{% endif %}
</div>
{# Indicateurs de statut compacts #}
<div class="flex items-center space-x-2">
{# Indicateur d'appréciation #}
{% if summary.has_appreciation %}
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs bg-green-100 text-green-800">
<span class="w-2 h-2 bg-green-400 rounded-full mr-1"></span>
Rédigée
</span>
{% else %}
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs bg-orange-100 text-orange-800">
<span class="w-2 h-2 bg-orange-400 rounded-full mr-1"></span>
À rédiger
</span>
{% endif %}
{# Indicateur de tendance condensé #}
{% if summary.overall_average and summary.grades_by_assessment | length > 1 %}
{% set recent_assessments = summary.grades_by_assessment.values() | list | sort(attribute='date') %}
{% set trend_recent = (recent_assessments[-1].score / recent_assessments[-1].max * 20) if recent_assessments | length > 0 else 0 %}
{% set trend_previous = (recent_assessments[-2].score / recent_assessments[-2].max * 20) if recent_assessments | length > 1 else trend_recent %}
{% if trend_recent > trend_previous + 1 %}
<span class="inline-flex items-center text-xs text-green-600" title="Tendance positive: +{{ '%.1f'|format(trend_recent - trend_previous) }}">
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M3.293 9.707a1 1 0 010-1.414l6-6a1 1 0 011.414 0l6 6a1 1 0 01-1.414 1.414L10 4.414 4.707 9.707a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
</span>
{% elif trend_recent < trend_previous - 1 %}
<span class="inline-flex items-center text-xs text-red-600" title="Tendance négative: {{ '%.1f'|format(trend_recent - trend_previous) }}">
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 10.293a1 1 0 010 1.414l-6 6a1 1 0 01-1.414 0l-6-6a1 1 0 111.414-1.414L10 15.586l5.293-5.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
</span>
{% endif %}
{% endif %}
</div>
</div>
</div>
</div>
{# Contenu expandable #}
<div class="hidden border-t border-gray-200" data-student-details="{{ summary.student.id }}">
<div class="px-6 py-6 space-y-6">
{# Section Compétences et Domaines - Layout horizontal #}
{% if summary.competence_domain_breakdown and (summary.competence_domain_breakdown.competences or summary.competence_domain_breakdown.domains) %}
<div class="competence-domain-section">
{# Layout horizontal: Résultats + Barres #}
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
{# Colonne gauche: Résultats par évaluation (si disponibles) #}
{% if summary.grades_by_assessment %}
<div class="evaluation-results">
<h5 class="text-sm font-medium text-blue-700 mb-3 flex items-center">
<svg class="w-4 h-4 text-blue-500 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path d="M2 11a1 1 0 011-1h2a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1v-5zM8 7a1 1 0 011-1h2a1 1 0 011 1v9a1 1 0 01-1 1H9a1 1 0 01-1-1V7zM14 4a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1h-2a1 1 0 01-1-1V4z"/>
</svg>
Résultats par évaluation
</h5>
<div class="space-y-1.5">
{% for assessment_id, assessment_data in summary.grades_by_assessment.items() %}
<div class="flex items-center justify-between p-2 bg-blue-50 rounded-lg border border-blue-100">
<span class="text-sm font-medium text-blue-900 truncate">{{ assessment_data.title }}</span>
<div class="flex items-center space-x-2">
<span class="text-sm font-bold text-blue-700">{{ "%.1f"|format(assessment_data.score) }}/{{ assessment_data.max }}</span>
{% if assessment_data.coefficient != 1 %}
<span class="text-xs text-blue-600 bg-blue-200 px-1 rounded">×{{ assessment_data.coefficient }}</span>
{% endif %}
</div>
</div>
{% endfor %}
</div>
{# Section Valeurs spéciales par évaluation #}
{% if summary.special_values_summary and summary.special_values_summary.by_assessment %}
<div class="mt-4">
<h6 class="text-xs font-medium text-gray-600 mb-2 flex items-center">
<svg class="w-3 h-3 text-gray-500 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd"/>
</svg>
Valeurs spéciales
</h6>
<div class="space-y-1">
{% for assessment_id, assessment_data in summary.special_values_summary.by_assessment.items() %}
<div class="flex items-center justify-between p-1.5 bg-gray-50 rounded border border-gray-100">
<span class="text-xs text-gray-700 truncate">{{ assessment_data.title }}</span>
<div class="flex items-center space-x-1">
{% for special_value, data in assessment_data.special_values.items() %}
{% if data.count > 0 %}
{% set detail_tooltip = [] %}
{% for detail in data.details %}
{% if detail.comment %}
{% set _ = detail_tooltip.append(detail.element_name + ': ' + detail.comment) %}
{% else %}
{% set _ = detail_tooltip.append(detail.element_name) %}
{% endif %}
{% endfor %}
<span class="inline-flex items-center px-1 py-0.5 rounded text-xs font-medium cursor-help"
style="background-color: {{ data.color }}15; color: {{ data.color }}; border: 1px solid {{ data.color }}30;"
title="{{ data.label }} ({{ data.count }}){% if detail_tooltip|length > 0 %}&#10;{{ detail_tooltip|join('&#10;') }}{% endif %}">
{{ special_value }} {{ data.count }}
</span>
{% endif %}
{% endfor %}
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{# Section Commentaires par évaluation #}
{% if summary.special_values_summary and summary.special_values_summary.comments_by_assessments and summary.special_values_summary.comments_by_assessments.has_comments %}
<div class="mt-4">
<h6 class="text-xs font-medium text-gray-600 mb-2 flex items-center">
<svg class="w-3 h-3 text-gray-500 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/>
</svg>
Commentaires ({{ summary.special_values_summary.comments_by_assessments.total_comments }})
</h6>
<div class="space-y-2">
{% for assessment in summary.special_values_summary.comments_by_assessments.assessments %}
<div class="bg-amber-50 border border-amber-200 rounded p-2">
<div class="flex items-center justify-between mb-1">
<span class="text-xs font-medium text-amber-800">{{ assessment.title }}</span>
<span class="text-xs text-amber-600">{{ assessment.comments|length }} commentaire(s)</span>
</div>
<div class="space-y-1">
{% for comment in assessment.comments %}
<div class="bg-white border border-amber-100 rounded p-1.5">
<div class="flex-1 min-w-0">
{# Ligne 1: Label et description #}
<div class="text-xs font-medium text-gray-800">
{{ comment.element_label }}{% if comment.element_description %} • {{ comment.element_description }}{% endif %}
</div>
{# Ligne 2: Valeur et commentaire #}
<div class="text-xs text-amber-700 mt-0.5 flex items-center">
{% if comment.value %}
<span class="inline-flex items-center px-1 py-0.5 rounded text-xs font-medium mr-2 bg-orange-100 text-orange-800">
{{ comment.value }}
</span>
{% endif %}
<span class="flex-1">{{ comment.comment }}</span>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
{% endif %}
{# Colonne droite: Barres de progression #}
<div class="progress-bars">
{# 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-3">
{% for competence in summary.competence_domain_breakdown.competences %}
<div class="competence-progress-horizontal">
{# Nom et score à côté de la barre #}
<div class="flex items-center justify-between mb-1">
<span class="text-xs font-medium text-gray-700 truncate flex-1 mr-2">{{ competence.name }}</span>
<span class="text-xs font-bold whitespace-nowrap" style="color: {{ competence.color }}">
{{ competence.percentage }}%
</span>
</div>
{# Barre de progression compacte #}
<div class="progress-bar-container segmented-progress compact"
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 compact" data-expanded="true">
{% 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|truncate(6, true) }}</span>
</div>
{% endfor %}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{# 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-3">
{% for domain in summary.competence_domain_breakdown.domains %}
<div class="domain-progress-horizontal">
{# Nom et score à côté de la barre #}
<div class="flex items-center justify-between mb-1">
<span class="text-xs font-medium text-gray-700 truncate flex-1 mr-2">{{ domain.name }}</span>
<span class="text-xs font-bold whitespace-nowrap" style="color: {{ domain.color }}">
{{ domain.percentage }}%
</span>
</div>
{# Barre de progression compacte #}
<div class="progress-bar-container segmented-progress compact"
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 compact" data-expanded="true">
{% 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|truncate(6, true) }}</span>
</div>
{% endfor %}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
</div>
</div>
{% endif %}
</div>
</div>
</div>
{% endfor %}
</div>
{# Mode focus - un seul élève visible #}
<div class="focus-mode-display hidden" data-focus-container>
{# Le contenu sera géré dynamiquement par JavaScript #}
{# L'élève affiché sera cloné depuis la liste et affiché ici #}
</div>
</div>
{# Message si aucun élève #}
{% if not student_summaries %}
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-12 text-center">
<div class="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-4">
<svg class="w-8 h-8 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"/>
</svg>
</div>
<h3 class="text-lg font-medium text-gray-900 mb-2">Aucun élève trouvé</h3>
<p class="text-gray-500">Aucune donnée disponible pour ce trimestre ou cette classe.</p>
</div>
{% endif %}
{# 4. Statistiques de classe (sidebar ou bottom) - masqué en mode focus #}
{% if class_statistics and class_statistics.mean %}
<div class="list-mode-stats bg-white rounded-xl shadow-sm border border-gray-200 p-6">
<h3 class="text-lg font-semibold text-gray-900 mb-4 flex items-center">
<svg class="w-5 h-5 text-orange-500 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path d="M2 11a1 1 0 011-1h2a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1v-5zM8 7a1 1 0 011-1h2a1 1 0 011 1v9a1 1 0 01-1 1H9a1 1 0 01-1-1V7zM14 4a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1h-2a1 1 0 01-1-1V4z"/>
</svg>
Statistiques de la classe
</h3>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<div class="text-center">
<div class="text-2xl font-bold text-orange-900">{{ "%.1f"|format(class_statistics.mean) }}</div>
<div class="text-sm text-orange-700">Moyenne générale</div>
</div>
<div class="text-center">
<div class="text-lg font-semibold text-gray-700">{{ "%.1f"|format(class_statistics.min) }}</div>
<div class="text-xs text-gray-500">Minimum</div>
</div>
<div class="text-center">
<div class="text-lg font-semibold text-gray-700">{{ "%.1f"|format(class_statistics.max) }}</div>
<div class="text-xs text-gray-500">Maximum</div>
</div>
<div class="text-center">
<div class="text-lg font-semibold text-gray-700">{{ "%.1f"|format(class_statistics.median) }}</div>
<div class="text-xs text-gray-500">Médiane</div>
</div>
</div>
{# Distribution des performances #}
<div class="mt-6">
<h4 class="text-sm font-medium text-gray-700 mb-3">Répartition des performances</h4>
<div class="grid grid-cols-2 md:grid-cols-4 gap-2">
<div class="bg-green-50 p-2 rounded text-center">
<div class="text-lg font-bold text-green-800">{{ class_statistics.performance_distribution.excellent }}</div>
<div class="text-xs text-green-600">Excellent (≥16)</div>
</div>
<div class="bg-blue-50 p-2 rounded text-center">
<div class="text-lg font-bold text-blue-800">{{ class_statistics.performance_distribution.good }}</div>
<div class="text-xs text-blue-600">Bien (14-16)</div>
</div>
<div class="bg-yellow-50 p-2 rounded text-center">
<div class="text-lg font-bold text-yellow-800">{{ class_statistics.performance_distribution.average }}</div>
<div class="text-xs text-yellow-600">Moyen (10-14)</div>
</div>
<div class="bg-red-50 p-2 rounded text-center">
<div class="text-lg font-bold text-red-800">{{ class_statistics.performance_distribution.struggling }}</div>
<div class="text-xs text-red-600">Difficulté (<10)</div>
</div>
</div>
</div>
</div>
{% endif %}
</div>
</div> <!-- Fermeture max-w-7xl -->
</div> <!-- Fermeture council-preparation -->
{% endblock %}
{% block head %}
<script src="{{ url_for('static', filename='js/CouncilPreparation.js') }}"></script>
<style>
/* Styles spécifiques pour la page conseil */
.council-preparation {
overflow: visible !important;
}
.council-preparation .grid {
overflow: visible !important;
}
.council-preparation [class*="transform"][class*="hover:scale"] {
transform-origin: center;
}
/* Assurer que les conteneurs parents permettent l'overflow */
main {
overflow: visible !important;
}
/* Styles pour le mode focus */
.focus-mode-student {
animation: focusFadeIn 0.3s ease-out;
transform: scale(1);
}
@keyframes focusFadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Mode focus - Carte élève élargie */
.focus-mode-display .focus-mode-student {
max-width: none !important;
width: 100%;
margin: 0 auto;
}
/* Mode focus - Section appréciation toujours visible */
.focus-mode-student [data-student-details] {
display: block !important;
height: auto !important;
opacity: 1 !important;
}
/* Mode focus - Interface ultra-compacte */
body.focus-mode {
overflow: hidden; /* Empêcher le scroll global */
}
.focus-mode-header {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 50;
background: white;
border-bottom: 1px solid #e5e7eb;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.focus-mode-display {
position: fixed;
top: 60px; /* Hauteur du header compact */
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
background: #f9fafb;
padding: 1rem;
display: flex;
flex-direction: column;
}
/* Mode focus - Utilisation complète de la hauteur */
.focus-mode-student {
height: 100%;
min-height: 100%;
max-height: none;
overflow: hidden;
background: white;
border-radius: 0.5rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
display: flex;
flex-direction: column;
}
/* Mode focus - Améliorer la lisibilité */
.focus-mode-student textarea {
min-height: 150px;
max-height: 50vh;
font-size: 14px;
line-height: 1.5;
resize: vertical;
flex: 1;
}
/* Mode focus - Conteneur de la carte élève s'étend sur toute la hauteur */
.focus-mode-student .px-6 {
padding-left: 1rem;
padding-right: 1rem;
/* Retirer flex: 1 car ce n'est que le header */
flex-shrink: 0;
}
/* Mode focus - Section détails s'étend pour utiliser l'espace disponible */
.focus-mode-student [data-student-details] {
flex: 1;
display: flex;
flex-direction: column;
}
/* Mode focus - Zone d'appréciation garde sa structure */
.focus-mode-student .flex-1.min-w-0 {
/* Garder la structure originale */
flex: 1;
min-width: 0;
}
/* Mode focus - Garder le layout horizontal du header */
.focus-mode-student .flex.items-start.space-x-6 {
/* Conserver le layout horizontal original */
flex-direction: row;
gap: 1.5rem;
align-items: flex-start;
flex-shrink: 0;
}
/* Mode focus - Appréciation garde sa taille originale */
.focus-mode-student .flex-1.min-w-0 textarea {
/* Garder les propriétés originales, juste ajuster légèrement */
min-height: 80px;
max-height: 200px;
height: auto;
}
/* Mode focus - Assurer que les sections s'étendent correctement */
.focus-mode-student .space-y-6 {
display: flex;
flex-direction: column;
flex: 1;
gap: 1.5rem;
}
/* Mode focus - Section détails visible et prend tout l'espace restant */
.focus-mode-student [data-student-details] {
display: block !important;
opacity: 1 !important;
flex: 1 1 0;
overflow-y: auto;
min-height: 0;
height: 0; /* Force flexbox à utiliser flex: 1 */
}
/* Mode focus - Organisation du contenu des détails */
.focus-mode-student [data-student-details] > .px-6.py-6.space-y-6 {
display: flex;
flex-direction: column;
height: 100%;
gap: 1.5rem;
}
/* Mode focus - Section compétences/domaines compacte mais visible */
.focus-mode-student .competence-domain-section {
flex-shrink: 0;
min-height: 200px;
}
/* Mode focus - Header garde son padding normal */
.focus-mode-student .px-6.py-4 {
padding: 1rem;
flex-shrink: 0;
}
/* Mode focus - Réduction des espacements */
.focus-mode-student .space-y-4 > * + * {
margin-top: 0.75rem;
}
.focus-mode-student .mb-2 {
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;
cursor: not-allowed;
}
/* Transitions fluides pour les changements de mode */
.list-mode-display,
.focus-mode-display {
transition: all 0.3s ease-out;
}
.hidden {
display: none !important;
}
/* Animations fluides pour l'expansion des cards */
[data-student-details] {
overflow: hidden;
transition: all 0.3s ease-out;
}
/* États des textareas */
[data-appreciation-textarea]:focus {
box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1);
}
/* Indicateurs de sauvegarde */
[data-save-indicator] {
transition: all 0.2s ease-in-out;
}
/* Hover effects pour les cards */
[data-student-card]:hover {
transform: translateY(-1px);
}
/* Styles pour l'aperçu des évaluations */
.assessment-preview-pills {
transition: all 0.2s ease-in-out;
}
.assessment-preview-pills:hover {
transform: scale(1.05);
}
/* Responsive: masquer l'aperçu sur très petits écrans */
@media (max-width: 640px) {
.assessment-preview-mobile-hide {
display: none !important;
}
}
/* Animation pour les indicateurs de tendance */
[title*="Tendance"] svg {
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
/* Amélioration de la lisibilité sur mobile */
@media (max-width: 768px) {
[data-student-card] .flex.items-center.justify-between {
flex-direction: column;
align-items: stretch;
gap: 0.5rem;
}
[data-student-card] .flex.flex-col.items-end {
align-items: stretch;
text-align: center;
}
}
/* ========== STYLES POUR LE NOUVEAU LAYOUT HORIZONTAL ========== */
/* Header à trois colonnes */
[data-student-card] .flex.items-start.space-x-6 {
gap: 1.5rem;
}
/* Responsive du header : passage en vertical sur tablette */
@media (max-width: 1024px) {
[data-student-card] .flex.items-start.space-x-6 {
flex-direction: column;
gap: 1rem;
align-items: stretch;
}
/* Appréciation en pleine largeur sur tablette */
[data-student-card] .flex-1.min-w-0 textarea {
min-height: 60px;
}
/* Moyenne et statuts alignés à droite même en vertical */
[data-student-card] .flex.flex-col.items-end.space-y-2.text-right {
flex-direction: row;
justify-content: space-between;
align-items: center;
text-align: left;
}
}
/* Mobile : layout complètement vertical */
@media (max-width: 640px) {
[data-student-card] .flex.items-start.space-x-6 {
gap: 0.75rem;
}
/* Info élève en ligne sur mobile */
[data-student-card] .flex.items-center.space-x-4[data-toggle-student] {
flex-direction: row;
align-items: center;
}
/* Appréciation compacte sur mobile */
[data-student-card] .flex-1.min-w-0 textarea {
rows: 2;
font-size: 0.875rem;
}
/* Statuts empilés sur mobile */
[data-student-card] .flex.flex-col.items-end.space-y-2.text-right {
flex-direction: column;
align-items: stretch;
gap: 0.5rem;
}
}
/* ========== STYLES POUR LES BARRES DE PROGRESSION COMPACTES ========== */
/* Version compacte des barres de progression */
.segmented-progress.compact {
padding: 3px;
min-height: 24px;
border-radius: 6px;
}
.segmented-progress-bar.compact {
height: 18px;
gap: 1px;
padding: 1px;
}
.segmented-progress.compact .progress-segment {
height: 16px;
border-radius: 3px;
border: 1px solid rgba(255, 255, 255, 0.3);
min-width: 4px;
}
/* Labels plus petits en mode compact */
.segmented-progress.compact .segment-label {
font-size: 0.6rem;
font-weight: 500;
}
/* Hover effects compacts */
.segmented-progress.compact .progress-segment:hover {
transform: translateY(-1px);
filter: brightness(1.1);
}
/* ========== SECTION RÉSULTATS PAR ÉVALUATION ========== */
.evaluation-results {
background: rgba(239, 246, 255, 0.3);
border-radius: 8px;
padding: 1rem;
border: 1px solid rgba(59, 130, 246, 0.1);
}
.evaluation-results .space-y-1\.5 > * + * {
margin-top: 0.375rem;
}
/* Cards d'évaluation avec couleurs des évaluations */
.evaluation-results .flex.items-center.justify-between {
transition: all 0.2s ease;
}
.evaluation-results .flex.items-center.justify-between:hover {
transform: translateX(2px);
box-shadow: 0 2px 4px rgba(59, 130, 246, 0.1);
}
/* ========== BARRES DE PROGRESSION HORIZONTALES ========== */
.competence-progress-horizontal,
.domain-progress-horizontal {
background: rgba(255, 255, 255, 0.5);
border-radius: 6px;
padding: 0.5rem;
border: 1px solid rgba(0, 0, 0, 0.05);
transition: all 0.2s ease;
}
.competence-progress-horizontal:hover,
.domain-progress-horizontal:hover {
background: rgba(255, 255, 255, 0.8);
border-color: rgba(0, 0, 0, 0.1);
transform: translateY(-1px);
}
/* ========== RESPONSIVE POUR LE NOUVEAU LAYOUT ========== */
/* Sur grands écrans : layout côte à côte */
@media (min-width: 1024px) {
.grid.grid-cols-1.lg\\:grid-cols-2.gap-6 {
grid-template-columns: 1fr 1fr;
}
}
/* Sur moyens écrans : layout adaptatif */
@media (max-width: 1023px) {
.grid.grid-cols-1.lg\\:grid-cols-2.gap-6 {
grid-template-columns: 1fr;
gap: 1rem;
}
/* Barres plus visibles sur tablette */
.segmented-progress.compact {
min-height: 28px;
padding: 4px;
}
.segmented-progress-bar.compact {
height: 20px;
}
.segmented-progress.compact .progress-segment {
height: 18px;
min-width: 6px;
}
}
/* Sur petits écrans : layout ultra-compact */
@media (max-width: 640px) {
.competence-domain-section .grid.grid-cols-1.lg\\:grid-cols-2.gap-6 {
gap: 0.75rem;
}
/* Textes plus petits */
.evaluation-results h5,
.progress-bars h5 {
font-size: 0.8rem;
}
/* Espacement réduit */
.competence-progress-horizontal,
.domain-progress-horizontal {
padding: 0.375rem;
margin-bottom: 0.5rem;
}
/* Noms tronqués plus agressivement */
.competence-progress-horizontal .text-xs.font-medium.text-gray-700,
.domain-progress-horizontal .text-xs.font-medium.text-gray-700 {
max-width: 100px;
}
}
/* ========== MODE FOCUS ADAPTATIONS ========== */
/* Mode focus : maintenir le layout 2 colonnes */
.focus-mode-student .competence-domain-section .grid.grid-cols-1.lg\\:grid-cols-2 {
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
/* Mode focus sur tablette : revenir à une colonne */
@media (max-width: 1023px) {
.focus-mode-student .competence-domain-section .grid.grid-cols-1.lg\\:grid-cols-2 {
grid-template-columns: 1fr;
gap: 0.75rem;
}
}
.focus-mode-student .evaluation-results,
.focus-mode-student .progress-bars {
padding: 0.75rem;
}
.focus-mode-student .segmented-progress.compact {
min-height: 20px;
padding: 2px;
}
.focus-mode-student .segmented-progress-bar.compact {
height: 16px;
}
.focus-mode-student .segmented-progress.compact .progress-segment {
height: 14px;
}
</style>
{% endblock %}