297 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			297 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| {# 
 | |
|   Macro : class_stats_card(type, data, expanded=False, trimester_colors={})
 | |
|   
 | |
|   TYPES : 'quantity', 'domains', 'competences', 'results'
 | |
|   
 | |
|   DATA STRUCTURE attendue :
 | |
|   - quantity: {"total": 8, "completed": 6, "in_progress": 2, "not_started": 0}
 | |
|   - domains: [{"name": "Calcul", "color": "#3B82F6", "total_points": 45.0, "elements_count": 12}]
 | |
|   - competences: [{"name": "Calculer", "color": "#8B5CF6", "total_points": 25.0, "elements_count": 6}]
 | |
|   - results: {"mean": 14.2, "median": 15.0, "distribution": [0,1,3,8,12,6,2,0], "min": 8.0, "max": 20.0}
 | |
| #}
 | |
| 
 | |
| {% macro class_stats_card(type, data, expanded=False, trimester_colors={}) %}
 | |
| {# Configuration des icônes par type #}
 | |
| {% set icons = {
 | |
|     'quantity': '<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path d="M9 2a1 1 0 000 2h2a1 1 0 100-2H9z"/><path fill-rule="evenodd" d="M4 5a2 2 0 012-2v1a1 1 0 001 1h6a1 1 0 001-1V3a2 2 0 012 2v6a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm3 4a1 1 0 000 2h2a1 1 0 100-2H7z" clip-rule="evenodd"/></svg>',
 | |
|     'domains': '<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path d="M3 4a1 1 0 011-1h12a1 1 0 011 1v2a1 1 0 01-1 1H4a1 1 0 01-1-1V4zM3 10a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H4a1 1 0 01-1-1v-6zM14 9a1 1 0 00-1 1v6a1 1 0 001 1h2a1 1 0 001-1v-6a1 1 0 00-1-1h-2z"/></svg>',
 | |
|     'competences': '<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M6 6V5a3 3 0 013-3h2a3 3 0 013 3v1h2a2 2 0 012 2v6a2 2 0 01-2 2H4a2 2 0 01-2-2V8a2 2 0 012-2h2zm-1 5a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zm2-3a1 1 0 000 2h6a1 1 0 100-2H7z" clip-rule="evenodd"/></svg>',
 | |
|     'results': '<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path d="M2 10a8 8 0 018-8v8h8a8 8 0 11-16 0z"/><path d="M12 2.252A8.014 8.014 0 0117.748 8H12V2.252z"/></svg>'
 | |
| } %}
 | |
| 
 | |
| {# Configuration des titres et couleurs par type #}
 | |
| {% set config = {
 | |
|     'quantity': {
 | |
|         'title': 'Évaluations',
 | |
|         'color_class': 'blue',
 | |
|         'icon_color': 'text-blue-600',
 | |
|         'bg_color': 'bg-blue-50',
 | |
|         'border_color': 'border-blue-200'
 | |
|     },
 | |
|     'domains': {
 | |
|         'title': 'Domaines',
 | |
|         'color_class': 'purple',
 | |
|         'icon_color': 'text-purple-600',
 | |
|         'bg_color': 'bg-purple-50',
 | |
|         'border_color': 'border-purple-200'
 | |
|     },
 | |
|     'competences': {
 | |
|         'title': 'Compétences',
 | |
|         'color_class': 'indigo',
 | |
|         'icon_color': 'text-indigo-600',
 | |
|         'bg_color': 'bg-indigo-50',
 | |
|         'border_color': 'border-indigo-200'
 | |
|     },
 | |
|     'results': {
 | |
|         'title': 'Résultats',
 | |
|         'color_class': 'green',
 | |
|         'icon_color': 'text-green-600',
 | |
|         'bg_color': 'bg-green-50',
 | |
|         'border_color': 'border-green-200'
 | |
|     }
 | |
| } %}
 | |
| 
 | |
| {% set card_config = config[type] %}
 | |
| 
 | |
| <div class="stats-card bg-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 border {{ card_config.border_color }}" 
 | |
|      data-type="{{ type }}" 
 | |
|      data-expanded="{{ 'true' if expanded else 'false' }}">
 | |
|     
 | |
|     <!-- Header - Toujours visible -->
 | |
|     <div class="p-6">
 | |
|         <div class="flex items-center justify-between">
 | |
|             <div class="flex items-center">
 | |
|                 <div class="w-12 h-12 {{ card_config.bg_color }} rounded-xl flex items-center justify-center mr-4 {{ card_config.icon_color }}">
 | |
|                     {{ icons[type]|safe }}
 | |
|                 </div>
 | |
|                 <div>
 | |
|                     <h3 class="text-lg font-semibold text-gray-900 mb-1">{{ card_config.title }}</h3>
 | |
|                     {% if type == 'quantity' %}
 | |
|                         <p class="text-2xl font-bold {{ card_config.icon_color }}">{{ data.total }} évaluations</p>
 | |
|                         <p class="text-sm text-gray-500">{{ data.completed }} terminées</p>
 | |
|                     {% elif type == 'domains' %}
 | |
|                         <p class="text-2xl font-bold {{ card_config.icon_color }}">{{ data|length }} domaines</p>
 | |
|                         <p class="text-sm text-gray-500">{{ data|sum(attribute='total_points')|round(1) }} points total</p>
 | |
|                     {% elif type == 'competences' %}
 | |
|                         <p class="text-2xl font-bold {{ card_config.icon_color }}">{{ data|length }} compétences</p>
 | |
|                         <p class="text-sm text-gray-500">{{ data|sum(attribute='elements_count') }} éléments</p>
 | |
|                     {% elif type == 'results' %}
 | |
|                         <p class="text-2xl font-bold {{ card_config.icon_color }}">{{ data.mean|round(1) }}/20</p>
 | |
|                         <p class="text-sm text-gray-500">Moyenne générale</p>
 | |
|                     {% endif %}
 | |
|                 </div>
 | |
|             </div>
 | |
|             
 | |
|             <!-- Bouton d'expansion -->
 | |
|             <button type="button" 
 | |
|                     class="expand-btn text-gray-400 hover:text-gray-600 transition-colors p-2 rounded-lg hover:bg-gray-50"
 | |
|                     onclick="toggleStatsCard(this)">
 | |
|                 <svg class="w-5 h-5 transform transition-transform {{ 'rotate-180' if expanded else '' }}" 
 | |
|                      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>
 | |
|             </button>
 | |
|         </div>
 | |
|     </div>
 | |
|     
 | |
|     <!-- Contenu étendu -->
 | |
|     <div class="expanded-content {{ 'hidden' if not expanded else '' }} border-t {{ card_config.border_color }}">
 | |
|         <div class="p-6">
 | |
|             {% if type == 'quantity' %}
 | |
|                 <!-- Détails des évaluations par statut -->
 | |
|                 <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
 | |
|                     <div class="bg-green-50 rounded-lg p-4 border border-green-200">
 | |
|                         <div class="flex items-center justify-between">
 | |
|                             <div>
 | |
|                                 <p class="text-sm font-medium text-green-800">Terminées</p>
 | |
|                                 <p class="text-xl font-bold text-green-900">{{ data.completed }}</p>
 | |
|                             </div>
 | |
|                             <svg class="w-8 h-8 text-green-600" fill="currentColor" viewBox="0 0 20 20">
 | |
|                                 <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 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>
 | |
|                         </div>
 | |
|                     </div>
 | |
|                     
 | |
|                     <div class="bg-orange-50 rounded-lg p-4 border border-orange-200">
 | |
|                         <div class="flex items-center justify-between">
 | |
|                             <div>
 | |
|                                 <p class="text-sm font-medium text-orange-800">En cours</p>
 | |
|                                 <p class="text-xl font-bold text-orange-900">{{ data.in_progress }}</p>
 | |
|                             </div>
 | |
|                             <svg class="w-8 h-8 text-orange-600" fill="currentColor" viewBox="0 0 20 20">
 | |
|                                 <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM7 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H7z" clip-rule="evenodd"/>
 | |
|                             </svg>
 | |
|                         </div>
 | |
|                     </div>
 | |
|                     
 | |
|                     <div class="bg-red-50 rounded-lg p-4 border border-red-200">
 | |
|                         <div class="flex items-center justify-between">
 | |
|                             <div>
 | |
|                                 <p class="text-sm font-medium text-red-800">Non commencées</p>
 | |
|                                 <p class="text-xl font-bold text-red-900">{{ data.not_started }}</p>
 | |
|                             </div>
 | |
|                             <svg class="w-8 h-8 text-red-600" fill="currentColor" viewBox="0 0 20 20">
 | |
|                                 <path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92z" clip-rule="evenodd"/>
 | |
|                             </svg>
 | |
|                         </div>
 | |
|                     </div>
 | |
|                 </div>
 | |
|                 
 | |
|                 <!-- Actions contextuelles -->
 | |
|                 <div class="flex flex-wrap gap-2 mt-4 pt-4 border-t border-gray-200">
 | |
|                     <a href="#" class="text-sm text-blue-600 hover:text-blue-800 font-medium flex items-center">
 | |
|                         <svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
 | |
|                             <path d="M10 12a2 2 0 100-4 2 2 0 000 4z"/>
 | |
|                             <path fill-rule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clip-rule="evenodd"/>
 | |
|                         </svg>
 | |
|                         Voir toutes les évaluations
 | |
|                     </a>
 | |
|                     <a href="#" class="text-sm text-green-600 hover:text-green-800 font-medium flex items-center">
 | |
|                         <svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
 | |
|                             <path fill-rule="evenodd" d="M10 5a1 1 0 011 1v3h3a1 1 0 110 2h-3v3a1 1 0 11-2 0v-3H6a1 1 0 110-2h3V6a1 1 0 011-1z" clip-rule="evenodd"/>
 | |
|                         </svg>
 | |
|                         Créer une évaluation
 | |
|                     </a>
 | |
|                 </div>
 | |
|                 
 | |
|             {% elif type == 'domains' %}
 | |
|                 <!-- Liste des domaines avec codes couleurs -->
 | |
|                 <div class="space-y-3">
 | |
|                     {% for domain in data %}
 | |
|                     <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
 | |
|                         <div class="flex items-center">
 | |
|                             <div class="w-4 h-4 rounded-full mr-3" style="background-color: {{ domain.color }}"></div>
 | |
|                             <div>
 | |
|                                 <p class="font-medium text-gray-900">{{ domain.name }}</p>
 | |
|                                 <p class="text-sm text-gray-500">{{ domain.elements_count }} éléments</p>
 | |
|                             </div>
 | |
|                         </div>
 | |
|                         <div class="text-right">
 | |
|                             <p class="font-semibold text-gray-900">{{ domain.total_points|round(1) }} pts</p>
 | |
|                         </div>
 | |
|                     </div>
 | |
|                     {% endfor %}
 | |
|                 </div>
 | |
|                 
 | |
|                 <!-- Actions contextuelles -->
 | |
|                 <div class="flex flex-wrap gap-2 mt-4 pt-4 border-t border-gray-200">
 | |
|                     <a href="#" class="text-sm text-purple-600 hover:text-purple-800 font-medium flex items-center">
 | |
|                         <svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
 | |
|                             <path d="M2 10a8 8 0 018-8v8h8a8 8 0 11-16 0z"/>
 | |
|                         </svg>
 | |
|                         Analyse par domaines
 | |
|                     </a>
 | |
|                 </div>
 | |
|                 
 | |
|             {% elif type == 'competences' %}
 | |
|                 <!-- Liste des compétences avec badges -->
 | |
|                 <div class="space-y-3">
 | |
|                     {% for competence in data %}
 | |
|                     <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
 | |
|                         <div class="flex items-center">
 | |
|                             <div class="px-2 py-1 text-xs font-medium rounded-full text-white mr-3" 
 | |
|                                  style="background-color: {{ competence.color }}">
 | |
|                                 {{ competence.name[:2]|upper }}
 | |
|                             </div>
 | |
|                             <div>
 | |
|                                 <p class="font-medium text-gray-900">{{ competence.name }}</p>
 | |
|                                 <p class="text-sm text-gray-500">{{ competence.elements_count }} éléments</p>
 | |
|                             </div>
 | |
|                         </div>
 | |
|                         <div class="text-right">
 | |
|                             <p class="font-semibold text-gray-900">{{ competence.total_points|round(1) }} pts</p>
 | |
|                         </div>
 | |
|                     </div>
 | |
|                     {% endfor %}
 | |
|                 </div>
 | |
|                 
 | |
|                 <!-- Actions contextuelles -->
 | |
|                 <div class="flex flex-wrap gap-2 mt-4 pt-4 border-t border-gray-200">
 | |
|                     <a href="#" class="text-sm text-indigo-600 hover:text-indigo-800 font-medium flex items-center">
 | |
|                         <svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
 | |
|                             <path fill-rule="evenodd" d="M3 3a1 1 0 000 2v8a2 2 0 002 2h2.586l-1.293 1.293a1 1 0 101.414 1.414L10 15.414l2.293 2.293a1 1 0 001.414-1.414L12.414 15H15a2 2 0 002-2V5a1 1 0 100-2H3z" clip-rule="evenodd"/>
 | |
|                         </svg>
 | |
|                         Évaluation par compétences
 | |
|                     </a>
 | |
|                 </div>
 | |
|                 
 | |
|             {% elif type == 'results' %}
 | |
|                 <!-- Statistiques détaillées -->
 | |
|                 <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-4">
 | |
|                     <div class="text-center p-3 bg-blue-50 rounded-lg">
 | |
|                         <p class="text-sm font-medium text-blue-800">Moyenne</p>
 | |
|                         <p class="text-xl font-bold text-blue-900">{{ data.mean|round(1) }}</p>
 | |
|                     </div>
 | |
|                     <div class="text-center p-3 bg-green-50 rounded-lg">
 | |
|                         <p class="text-sm font-medium text-green-800">Médiane</p>
 | |
|                         <p class="text-xl font-bold text-green-900">{{ data.median|round(1) }}</p>
 | |
|                     </div>
 | |
|                     <div class="text-center p-3 bg-red-50 rounded-lg">
 | |
|                         <p class="text-sm font-medium text-red-800">Minimum</p>
 | |
|                         <p class="text-xl font-bold text-red-900">{{ data.min|round(1) }}</p>
 | |
|                     </div>
 | |
|                     <div class="text-center p-3 bg-purple-50 rounded-lg">
 | |
|                         <p class="text-sm font-medium text-purple-800">Maximum</p>
 | |
|                         <p class="text-xl font-bold text-purple-900">{{ data.max|round(1) }}</p>
 | |
|                     </div>
 | |
|                 </div>
 | |
|                 
 | |
|                 <!-- Mini distribution des notes -->
 | |
|                 <div class="mb-4">
 | |
|                     <p class="text-sm font-medium text-gray-700 mb-2">Distribution des notes :</p>
 | |
|                     <div class="flex items-end space-x-1 h-12">
 | |
|                         {% for count in data.distribution %}
 | |
|                         <div class="flex-1 bg-blue-200 rounded-t" style="height: {{ (count / (data.distribution|max or 1) * 100)|round }}%">
 | |
|                             {% if count > 0 %}
 | |
|                             <div class="text-xs text-center text-blue-800 pt-1">{{ count }}</div>
 | |
|                             {% endif %}
 | |
|                         </div>
 | |
|                         {% endfor %}
 | |
|                     </div>
 | |
|                     <div class="flex justify-between text-xs text-gray-500 mt-1">
 | |
|                         {% for i in range(data.distribution|length) %}
 | |
|                         <span>{{ i }}-{{ i+1 }}</span>
 | |
|                         {% endfor %}
 | |
|                     </div>
 | |
|                 </div>
 | |
|                 
 | |
|                 <!-- Actions contextuelles -->
 | |
|                 <div class="flex flex-wrap gap-2 pt-4 border-t border-gray-200">
 | |
|                     <a href="#" class="text-sm text-green-600 hover:text-green-800 font-medium flex items-center">
 | |
|                         <svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
 | |
|                             <path d="M2 10a8 8 0 018-8v8h8a8 8 0 11-16 0z"/>
 | |
|                         </svg>
 | |
|                         Analyse détaillée
 | |
|                     </a>
 | |
|                     <a href="#" class="text-sm text-blue-600 hover:text-blue-800 font-medium flex items-center">
 | |
|                         <svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
 | |
|                             <path d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V5a2 2 0 00-2-2H4zm12 12H4l4-8 3 6 2-4 3 6z"/>
 | |
|                         </svg>
 | |
|                         Voir les graphiques
 | |
|                     </a>
 | |
|                 </div>
 | |
|             {% endif %}
 | |
|         </div>
 | |
|     </div>
 | |
| </div>
 | |
| {% endmacro %}
 | |
| 
 | |
| {# JavaScript pour l'interaction d'expansion - À inclure dans la page qui utilise le composant #}
 | |
| <script>
 | |
| function toggleStatsCard(button) {
 | |
|     const card = button.closest('.stats-card');
 | |
|     const expandedContent = card.querySelector('.expanded-content');
 | |
|     const chevron = button.querySelector('svg');
 | |
|     const isExpanded = card.dataset.expanded === 'true';
 | |
|     
 | |
|     if (isExpanded) {
 | |
|         expandedContent.classList.add('hidden');
 | |
|         chevron.classList.remove('rotate-180');
 | |
|         card.dataset.expanded = 'false';
 | |
|     } else {
 | |
|         expandedContent.classList.remove('hidden');
 | |
|         chevron.classList.add('rotate-180');
 | |
|         card.dataset.expanded = 'true';
 | |
|     }
 | |
| }
 | |
| </script> |