Files
notytex/templates/class_council_preparation.html

727 lines
40 KiB
HTML

{% 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 cliquable #}
<div class="px-6 py-4 cursor-pointer flex items-center justify-between"
data-toggle-student="{{ summary.student.id }}">
<div class="flex items-center space-x-4">
{# 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 élève #}
<div class="flex-1 min-w-0">
<h3 class="font-semibold text-gray-900 text-lg">{{ summary.student.last_name }}, {{ summary.student.first_name }}</h3>
{# Ligne 1: Info de base #}
<div class="flex items-center space-x-4 text-sm text-gray-600 mb-2">
<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 %}
</div>
{# NOUVEAU - Ligne 2: Aperçu rapide des dernières évaluations #}
{% if summary.grades_by_assessment %}
<div class="assessment-preview-mobile-hide">
<div class="flex items-center space-x-1 text-xs text-gray-500 mb-1">Dernières évaluations :</div>
<div class="flex items-center space-x-2">
{% set recent_assessments = summary.grades_by_assessment.items() | list | sort(attribute='1.date', reverse=True) %}
{% for assessment_id, assessment_data in recent_assessments[:4] %}
<div class="assessment-preview-pills flex items-center space-x-1 bg-gray-50 px-2 py-1 rounded text-xs cursor-help
{% if assessment_data.score / assessment_data.max >= 0.8 %}text-green-700 bg-green-50{% endif %}
{% if assessment_data.score / assessment_data.max < 0.5 %}text-red-700 bg-red-50{% endif %}"
title="{{ assessment_data.title }} - {{ assessment_data.date.strftime('%d/%m') if assessment_data.date else 'Date inconnue' }}">
<span class="font-medium">{{ "%.1f"|format(assessment_data.score) }}</span>
<span class="text-gray-400">/</span>
<span>{{ assessment_data.max }}</span>
</div>
{% endfor %}
{% if summary.grades_by_assessment | length > 4 %}
<span class="text-xs text-gray-400 cursor-help" title="Cliquez pour voir toutes les évaluations">+{{ summary.grades_by_assessment | length - 4 }}</span>
{% endif %}
</div>
</div>
{% endif %}
</div>
</div>
{# Moyenne et statut - REORGANISÉ pour plus de clarté #}
<div class="flex flex-col items-end space-y-2 text-right">
{# Ligne 1: Moyenne principale #}
<div class="flex items-center space-x-3">
{% 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 %}
{# Chevron d'expansion #}
<svg class="w-5 h-5 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>
{# Ligne 2: Indicateurs de statut #}
<div class="flex items-center space-x-2">
{# NOUVEAU - Indicateur de tendance #}
{% 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">
<svg class="w-3 h-3 mr-1" 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>
+{{ "%.1f"|format(trend_recent - trend_previous) }}
</span>
{% elif trend_recent < trend_previous - 1 %}
<span class="inline-flex items-center text-xs text-red-600" title="Tendance négative">
<svg class="w-3 h-3 mr-1" 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>
{{ "%.1f"|format(trend_recent - trend_previous) }}
</span>
{% else %}
<span class="inline-flex items-center text-xs text-gray-500" title="Stable">
<svg class="w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M5 10a1 1 0 011-1h8a1 1 0 110 2H6a1 1 0 01-1-1z" clip-rule="evenodd"/>
</svg>
Stable
</span>
{% endif %}
{% endif %}
{# Indicateur d'appréciation #}
<div class="flex items-center">
{% 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 %}
</div>
{# Indicateur de sauvegarde #}
<div class="hidden" data-save-indicator="{{ summary.student.id }}"></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">
{# Détail des évaluations #}
{% if summary.grades_by_assessment %}
<div>
<h4 class="font-medium text-gray-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
</h4>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
{% for assessment_id, assessment_data in summary.grades_by_assessment.items() %}
<div class="bg-blue-50 px-4 py-3 rounded-lg border border-blue-100">
<div class="font-medium text-blue-900 text-sm mb-1">{{ assessment_data.title }}</div>
<div class="flex items-center justify-between">
<span class="text-blue-700 font-bold">{{ "%.1f"|format(assessment_data.score) }}/{{ assessment_data.max }}</span>
<span class="text-xs text-blue-600">Coeff. {{ assessment_data.coefficient }}</span>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{# Zone d'appréciation #}
<div>
<label class="block font-medium text-gray-700 mb-3 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-4 py-3 text-sm focus:ring-2 focus:ring-purple-500 focus:border-purple-500 resize-none transition-colors"
rows="4"
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-2 flex justify-between items-center text-xs text-gray-500">
<span>L'appréciation est sauvegardée automatiquement</span>
<span data-char-counter>0 caractères</span>
</div>
</div>
{# Actions #}
<div class="flex items-center justify-between pt-4 border-t border-gray-200">
<div class="flex items-center space-x-3">
<button data-save-manual="{{ summary.student.id }}"
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="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
Sauvegarder
</button>
<button data-finalize="{{ summary.student.id }}"
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="M6.267 3.455a3.066 3.066 0 001.745-.723 3.066 3.066 0 013.976 0 3.066 3.066 0 001.745.723 3.066 3.066 0 012.812 2.812c.051.643.304 1.254.723 1.745a3.066 3.066 0 010 3.976 3.066 3.066 0 00-.723 1.745 3.066 3.066 0 01-2.812 2.812 3.066 3.066 0 00-1.745.723 3.066 3.066 0 01-3.976 0 3.066 3.066 0 00-1.745-.723 3.066 3.066 0 01-2.812-2.812 3.066 3.066 0 00-.723-1.745 3.066 3.066 0 010-3.976 3.066 3.066 0 00.723-1.745 3.066 3.066 0 012.812-2.812zm7.44 5.252a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
Finaliser
</button>
</div>
<div class="text-xs text-gray-500">
{% if summary.appreciation %}
Dernière modification : <span data-last-modified="{{ summary.student.id }}">{{ summary.appreciation.last_modified.strftime('%d/%m à %H:%M') }}</span>
{% else %}
Pas encore d'appréciation
{% endif %}
</div>
</div>
</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-y: auto;
background: #f9fafb;
padding: 1rem;
}
/* Mode focus - Optimisation pour éviter le scroll */
.focus-mode-student {
max-height: calc(100vh - 80px); /* Header compact + padding */
overflow-y: auto;
background: white;
border-radius: 0.5rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
/* Mode focus - Améliorer la lisibilité */
.focus-mode-student textarea {
min-height: 100px;
max-height: 200px;
font-size: 14px;
line-height: 1.5;
resize: vertical;
}
/* Mode focus - Compactage de la carte */
.focus-mode-student .px-6 {
padding-left: 1rem;
padding-right: 1rem;
}
.focus-mode-student .py-4 {
padding-top: 1rem;
padding-bottom: 1rem;
}
/* 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;
}
/* 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;
}
}
</style>
{% endblock %}