refactor: html organisation
This commit is contained in:
@@ -1,81 +1,73 @@
|
||||
{% extends "base.html" %}
|
||||
{% from 'components/common/macros.html' import hero_section, filter_section %}
|
||||
{% from 'components/assessment/assessment_card.html' import assessment_card %}
|
||||
|
||||
{% block title %}Évaluations - Gestion Scolaire{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="space-y-8">
|
||||
<!-- Hero Section -->
|
||||
<div class="bg-gradient-to-r from-purple-600 to-blue-600 text-white rounded-xl p-8 shadow-lg">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 class="text-4xl font-bold mb-2">Mes Évaluations 📚</h1>
|
||||
<p class="text-xl opacity-90 mb-1">Gérez et organisez toutes vos évaluations</p>
|
||||
<div class="flex items-center space-x-6 text-sm opacity-75 mt-3">
|
||||
<span class="flex items-center">
|
||||
<svg class="w-4 h-4 mr-2" 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 102 0V3a2 2 0 012 2v6a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm2.5 2.5a.5.5 0 000 1h3a.5.5 0 000-1h-3z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
{{ assessments|length }} évaluations
|
||||
</span>
|
||||
<span class="flex items-center">
|
||||
<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>
|
||||
Année scolaire 2024-2025
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hidden md:block">
|
||||
<a href="{{ url_for('assessments.new') }}"
|
||||
class="bg-white/20 hover:bg-white/30 text-white px-6 py-3 rounded-xl transition-all duration-300 font-semibold shadow-lg hover:shadow-xl transform hover:scale-105 flex items-center">
|
||||
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
Nouvelle évaluation
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{# Hero Section avec composant réutilisable #}
|
||||
{% set meta_info = [
|
||||
{
|
||||
'icon': '<svg class="w-4 h-4 mr-2" 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 102 0V3a2 2 0 012 2v6a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm2.5 2.5a.5.5 0 000 1h3a.5.5 0 000-1h-3z" clip-rule="evenodd"/></svg>',
|
||||
'text': assessments|length ~ ' évaluations'
|
||||
},
|
||||
{
|
||||
'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': 'Année scolaire 2024-2025'
|
||||
}
|
||||
] %}
|
||||
{% set primary_action = {
|
||||
'url': url_for('assessments.new'),
|
||||
'text': 'Nouvelle évaluation',
|
||||
'icon': '<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" clip-rule="evenodd"/></svg>'
|
||||
} %}
|
||||
{{ hero_section(
|
||||
title="Mes Évaluations 📚",
|
||||
subtitle="Gérez et organisez toutes vos évaluations",
|
||||
meta_info=meta_info,
|
||||
primary_action=primary_action,
|
||||
gradient_class="from-purple-600 to-blue-600"
|
||||
) }}
|
||||
|
||||
<!-- Filtres et actions -->
|
||||
<div class="bg-white rounded-lg shadow p-6">
|
||||
<div class="flex flex-col lg:flex-row lg:items-center lg:justify-between space-y-4 lg:space-y-0">
|
||||
<div class="flex flex-col md:flex-row md:items-center space-y-3 md:space-y-0 md:space-x-4">
|
||||
<div class="flex items-center space-x-2">
|
||||
<label class="text-sm font-medium text-gray-700">Trimestre :</label>
|
||||
<select id="trimester-filter" class="border border-gray-300 rounded-md px-3 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent">
|
||||
<option value="">Tous</option>
|
||||
<option value="1" {% if current_trimester == '1' %}selected{% endif %}>Trimestre 1</option>
|
||||
<option value="2" {% if current_trimester == '2' %}selected{% endif %}>Trimestre 2</option>
|
||||
<option value="3" {% if current_trimester == '3' %}selected{% endif %}>Trimestre 3</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<label class="text-sm font-medium text-gray-700">Classe :</label>
|
||||
<select id="class-filter" class="border border-gray-300 rounded-md px-3 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent">
|
||||
<option value="">Toutes</option>
|
||||
{% for class_group in classes %}
|
||||
<option value="{{ class_group.id }}" {% if current_class == class_group.id|string %}selected{% endif %}>
|
||||
{{ class_group.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<label class="text-sm font-medium text-gray-700">Tri :</label>
|
||||
<select id="sort-filter" class="border border-gray-300 rounded-md px-3 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent">
|
||||
<option value="date_desc" {% if current_sort == 'date_desc' %}selected{% endif %}>Plus récent</option>
|
||||
<option value="date_asc" {% if current_sort == 'date_asc' %}selected{% endif %}>Plus ancien</option>
|
||||
<option value="title" {% if current_sort == 'title' %}selected{% endif %}>Titre A-Z</option>
|
||||
<option value="class" {% if current_sort == 'class' %}selected{% endif %}>Classe</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Compteur de résultats -->
|
||||
<div class="text-sm text-gray-500 font-medium">
|
||||
{{ assessments|length }} évaluation(s)
|
||||
</div>
|
||||
{# Filtres avec composant réutilisable #}
|
||||
{% set class_options = [{'value': '', 'label': 'Toutes'}] %}
|
||||
{% for class_group in classes %}
|
||||
{% set _ = class_options.append({'value': class_group.id|string, 'label': class_group.name}) %}
|
||||
{% endfor %}
|
||||
|
||||
{% set filters = [
|
||||
{
|
||||
'id': 'trimester-filter',
|
||||
'label': 'Trimestre',
|
||||
'options': [
|
||||
{'value': '', 'label': 'Tous'},
|
||||
{'value': '1', 'label': 'Trimestre 1'},
|
||||
{'value': '2', 'label': 'Trimestre 2'},
|
||||
{'value': '3', 'label': 'Trimestre 3'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'class-filter',
|
||||
'label': 'Classe',
|
||||
'options': class_options
|
||||
},
|
||||
{
|
||||
'id': 'sort-filter',
|
||||
'label': 'Tri',
|
||||
'options': [
|
||||
{'value': 'date_desc', 'label': 'Plus récent'},
|
||||
{'value': 'date_asc', 'label': 'Plus ancien'},
|
||||
{'value': 'title', 'label': 'Titre A-Z'},
|
||||
{'value': 'class', 'label': 'Classe'}
|
||||
]
|
||||
}
|
||||
] %}
|
||||
|
||||
{% call filter_section(filters, {'trimester-filter': current_trimester, 'class-filter': current_class, 'sort-filter': current_sort}) %}
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="text-sm text-gray-500 font-medium">
|
||||
{{ assessments|length }} évaluation(s)
|
||||
</div>
|
||||
<div class="md:hidden">
|
||||
<a href="{{ url_for('assessments.new') }}"
|
||||
@@ -87,144 +79,13 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endcall %}
|
||||
|
||||
{% if assessments %}
|
||||
<!-- Grille d'évaluations modernisée -->
|
||||
<!-- Grille d'évaluations avec composants -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{% for assessment in assessments %}
|
||||
{% set trimester_colors = {
|
||||
1: {'bg': 'from-blue-500 to-blue-600', 'accent': 'blue', 'icon_bg': 'bg-blue-100', 'icon_text': 'text-blue-600'},
|
||||
2: {'bg': 'from-green-500 to-green-600', 'accent': 'green', 'icon_bg': 'bg-green-100', 'icon_text': 'text-green-600'},
|
||||
3: {'bg': 'from-orange-500 to-orange-600', 'accent': 'orange', 'icon_bg': 'bg-orange-100', 'icon_text': 'text-orange-600'}
|
||||
} %}
|
||||
{% set colors = trimester_colors[assessment.trimester] %}
|
||||
|
||||
<div class="bg-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:scale-105 overflow-hidden">
|
||||
<!-- Header avec gradient thématique -->
|
||||
<div class="bg-gradient-to-r {{ colors.bg }} p-4 text-white">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="w-10 h-10 bg-white/20 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5" 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 102 0V3a2 2 0 012 2v6a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm2.5 2.5a.5.5 0 000 1h3a.5.5 0 000-1h-3z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-sm font-semibold">Trimestre {{ assessment.trimester }}</div>
|
||||
</div>
|
||||
<div class="text-xs opacity-75">{{ assessment.date.strftime('%d/%m/%Y') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contenu principal -->
|
||||
<div class="p-6">
|
||||
<h3 class="text-lg font-bold text-gray-900 mb-2 line-clamp-2">{{ assessment.title }}</h3>
|
||||
|
||||
<!-- Informations clés -->
|
||||
<div class="space-y-3 mb-4">
|
||||
<div class="flex items-center text-sm text-gray-600">
|
||||
<svg class="w-4 h-4 mr-2 {{ colors.icon_text }}" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
<span class="font-medium">{{ assessment.class_group.name }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center text-sm text-gray-600">
|
||||
<svg class="w-4 h-4 mr-2 {{ colors.icon_text }}" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M7 3a1 1 0 000 2h6a1 1 0 100-2H7zM4 7a1 1 0 011-1h10a1 1 0 110 2H5a1 1 0 01-1-1zM2 11a2 2 0 012-2h12a2 2 0 012 2v4a2 2 0 01-2 2H4a2 2 0 01-2-2v-4z"/>
|
||||
</svg>
|
||||
<span>{{ assessment.exercises|length }} exercice(s)</span>
|
||||
</div>
|
||||
|
||||
<div class="bg-{{ colors.accent }}-100 text-{{ colors.accent }}-800 text-xs px-2 py-1 rounded-full font-medium">
|
||||
Coeff. {{ assessment.coefficient }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Indicateur de progression des notes -->
|
||||
{% set progress = assessment.grading_progress %}
|
||||
<div class="flex items-center justify-between pt-2 border-t border-gray-100">
|
||||
<!-- Indicateur fusionné cliquable -->
|
||||
{% if progress.status == 'completed' %}
|
||||
<div class="inline-flex items-center px-3 py-2 rounded-lg text-sm font-medium bg-green-100 text-green-800 border border-green-200">
|
||||
<svg class="w-4 h-4 mr-2" 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>
|
||||
<span class="font-semibold">Correction 100% </span>
|
||||
</div>
|
||||
{% elif progress.status == 'in_progress' %}
|
||||
<a href="{{ url_for('grading.assessment_grading', assessment_id=assessment.id) }}"
|
||||
class="inline-flex items-center px-3 py-2 rounded-lg text-sm font-medium bg-orange-100 text-orange-800 border border-orange-200 hover:bg-orange-200 transition-colors cursor-pointer">
|
||||
<div class="relative w-4 h-4 mr-2">
|
||||
<svg class="w-4 h-4 transform -rotate-90" viewBox="0 0 16 16">
|
||||
<circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="2" fill="none" class="text-orange-300"/>
|
||||
<circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="2" fill="none"
|
||||
class="text-orange-600" stroke-dasharray="37.7"
|
||||
stroke-dashoffset="{{ 37.7 - (37.7 * progress.percentage / 100) }}"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="font-semibold">Correction {{ progress.percentage }}% </span>
|
||||
</a>
|
||||
{% elif progress.status == 'not_started' %}
|
||||
<a href="{{ url_for('grading.assessment_grading', assessment_id=assessment.id) }}"
|
||||
class="inline-flex items-center px-3 py-2 rounded-lg text-sm font-medium bg-red-100 text-red-800 border border-red-200 hover:bg-red-200 transition-colors cursor-pointer">
|
||||
<svg class="w-4 h-4 mr-2" 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.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span class="font-semibold">Correction 0%</span>
|
||||
</a>
|
||||
{% else %}
|
||||
<div class="inline-flex items-center px-3 py-2 rounded-lg text-sm font-medium bg-gray-100 text-gray-800 border border-gray-200">
|
||||
<svg class="w-4 h-4 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>
|
||||
<span class="font-semibold">Non définie</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Info détaillée -->
|
||||
<div class="text-xs text-gray-500 text-right">
|
||||
{{ progress.completed }}/{{ progress.total }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if assessment.description %}
|
||||
<p class="text-sm text-gray-600 mb-4 line-clamp-2">{{ assessment.description }}</p>
|
||||
{% endif %}
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="flex space-x-2">
|
||||
<a href="{{ url_for('assessments.detail', id=assessment.id) }}"
|
||||
class="flex-1 bg-gray-50 hover:bg-gray-100 text-gray-700 hover:text-gray-900 px-3 py-2 rounded-lg text-xs font-medium transition-colors flex items-center justify-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>
|
||||
Détails
|
||||
</a>
|
||||
|
||||
<a href="{{ url_for('grading.assessment_grading', assessment_id=assessment.id) }}"
|
||||
class="flex-1 bg-{{ colors.accent }}-50 hover:bg-{{ colors.accent }}-100 text-{{ colors.accent }}-700 hover:text-{{ colors.accent }}-900 px-3 py-2 rounded-lg text-xs font-medium transition-colors flex items-center justify-center">
|
||||
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z"/>
|
||||
</svg>
|
||||
Noter
|
||||
</a>
|
||||
|
||||
<a href="{{ url_for('assessments.edit', id=assessment.id) }}"
|
||||
class="flex-1 bg-gray-50 hover:bg-gray-100 text-gray-700 hover:text-gray-900 px-3 py-2 rounded-lg text-xs font-medium transition-colors flex items-center justify-center">
|
||||
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M17.414 2.586a2 2 0 00-2.828 0L7 10.172V13h2.828l7.586-7.586a2 2 0 000-2.828z"/>
|
||||
<path fill-rule="evenodd" d="M2 6a2 2 0 012-2h4a1 1 0 010 2H4v10h10v-4a1 1 0 112 0v4a2 2 0 01-2 2H4a2 2 0 01-2-2V6z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
Modifier
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ assessment_card(assessment) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
@@ -260,39 +121,6 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Gestion des filtres
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const trimesterFilter = document.getElementById('trimester-filter');
|
||||
const classFilter = document.getElementById('class-filter');
|
||||
const sortFilter = document.getElementById('sort-filter');
|
||||
|
||||
function applyFilters() {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (trimesterFilter.value) {
|
||||
params.set('trimester', trimesterFilter.value);
|
||||
}
|
||||
|
||||
if (classFilter.value) {
|
||||
params.set('class', classFilter.value);
|
||||
}
|
||||
|
||||
if (sortFilter.value) {
|
||||
params.set('sort', sortFilter.value);
|
||||
}
|
||||
|
||||
// Rediriger avec les nouveaux paramètres
|
||||
const url = new URL(window.location);
|
||||
url.search = params.toString();
|
||||
window.location.href = url.toString();
|
||||
}
|
||||
|
||||
// Écouter les changements sur tous les filtres
|
||||
[trimesterFilter, classFilter, sortFilter].forEach(filter => {
|
||||
filter.addEventListener('change', applyFilters);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{# JavaScript géré par le système centralisé #}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -5,17 +5,51 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Gestion Scolaire{% endblock %}</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/design-system.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/app.css') }}">
|
||||
<script>
|
||||
// Configuration Tailwind pour dark mode et focus
|
||||
// Configuration Tailwind étendue avec design tokens
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
50: '#eff6ff',
|
||||
100: '#dbeafe',
|
||||
500: '#3b82f6',
|
||||
600: '#2563eb',
|
||||
700: '#1d4ed8',
|
||||
800: '#1e40af',
|
||||
},
|
||||
success: {
|
||||
50: '#ecfdf5',
|
||||
100: '#d1fae5',
|
||||
500: '#10b981',
|
||||
600: '#059669',
|
||||
700: '#047857',
|
||||
},
|
||||
warning: {
|
||||
50: '#fffbeb',
|
||||
100: '#fef3c7',
|
||||
500: '#f59e0b',
|
||||
600: '#d97706',
|
||||
700: '#b45309',
|
||||
},
|
||||
danger: {
|
||||
50: '#fef2f2',
|
||||
100: '#fee2e2',
|
||||
500: '#ef4444',
|
||||
600: '#dc2626',
|
||||
700: '#b91c1c',
|
||||
}
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'system-ui', 'sans-serif'],
|
||||
},
|
||||
animation: {
|
||||
'fade-in-up': 'fadeInUp 0.5s ease-out',
|
||||
'slide-down': 'slideDown 0.3s ease-out',
|
||||
'pulse-slow': 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,5 +116,11 @@
|
||||
<p>© 2025 Gestion Scolaire - Application de gestion des évaluations</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- JavaScript Core -->
|
||||
<script src="{{ url_for('static', filename='js/notytex.js') }}"></script>
|
||||
|
||||
<!-- JavaScript spécifique aux pages -->
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
101
templates/components/assessment/assessment_card.html
Normal file
101
templates/components/assessment/assessment_card.html
Normal file
@@ -0,0 +1,101 @@
|
||||
{# Composant pour carte d'évaluation dans la liste #}
|
||||
{% from 'components/common/macros.html' import progress_indicator %}
|
||||
|
||||
{% macro assessment_card(assessment) %}
|
||||
{% set trimester_colors = {
|
||||
1: {'bg': 'from-blue-500 to-blue-600', 'accent': 'blue', 'icon_bg': 'bg-blue-100', 'icon_text': 'text-blue-600'},
|
||||
2: {'bg': 'from-green-500 to-green-600', 'accent': 'green', 'icon_bg': 'bg-green-100', 'icon_text': 'text-green-600'},
|
||||
3: {'bg': 'from-orange-500 to-orange-600', 'accent': 'orange', 'icon_bg': 'bg-orange-100', 'icon_text': 'text-orange-600'}
|
||||
} %}
|
||||
{% set colors = trimester_colors[assessment.trimester] %}
|
||||
|
||||
<div class="bg-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:scale-105 overflow-hidden">
|
||||
<!-- Header avec gradient thématique -->
|
||||
<div class="bg-gradient-to-r {{ colors.bg }} p-4 text-white">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="w-10 h-10 bg-white/20 rounded-full flex items-center justify-center">
|
||||
<svg class="w-5 h-5" 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 102 0V3a2 2 0 012 2v6a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm2.5 2.5a.5.5 0 000 1h3a.5.5 0 000-1h-3z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="text-sm font-semibold">Trimestre {{ assessment.trimester }}</div>
|
||||
</div>
|
||||
<div class="text-xs opacity-75">{{ assessment.date.strftime('%d/%m/%Y') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contenu principal -->
|
||||
<div class="p-6">
|
||||
<h3 class="text-lg font-bold text-gray-900 mb-2 line-clamp-2">{{ assessment.title }}</h3>
|
||||
|
||||
<!-- Informations clés -->
|
||||
<div class="space-y-3 mb-4">
|
||||
<div class="flex items-center text-sm text-gray-600">
|
||||
<svg class="w-4 h-4 mr-2 {{ colors.icon_text }}" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
<span class="font-medium">{{ assessment.class_group.name }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center text-sm text-gray-600">
|
||||
<svg class="w-4 h-4 mr-2 {{ colors.icon_text }}" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M7 3a1 1 0 000 2h6a1 1 0 100-2H7zM4 7a1 1 0 011-1h10a1 1 0 110 2H5a1 1 0 01-1-1zM2 11a2 2 0 012-2h12a2 2 0 012 2v4a2 2 0 01-2 2H4a2 2 0 01-2-2v-4z"/>
|
||||
</svg>
|
||||
<span>{{ assessment.exercises|length }} exercice(s)</span>
|
||||
</div>
|
||||
|
||||
<div class="bg-{{ colors.accent }}-100 text-{{ colors.accent }}-800 text-xs px-2 py-1 rounded-full font-medium">
|
||||
Coeff. {{ assessment.coefficient }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Indicateur de progression des notes -->
|
||||
{% set progress = assessment.grading_progress %}
|
||||
<div class="flex items-center justify-between pt-2 border-t border-gray-100">
|
||||
{{ progress_indicator(progress, True, assessment.id) }}
|
||||
|
||||
<!-- Info détaillée -->
|
||||
<div class="text-xs text-gray-500 text-right">
|
||||
{{ progress.completed }}/{{ progress.total }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if assessment.description %}
|
||||
<p class="text-sm text-gray-600 mb-4 line-clamp-2">{{ assessment.description }}</p>
|
||||
{% endif %}
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="flex space-x-2">
|
||||
<a href="{{ url_for('assessments.detail', id=assessment.id) }}"
|
||||
class="flex-1 bg-gray-50 hover:bg-gray-100 text-gray-700 hover:text-gray-900 px-3 py-2 rounded-lg text-xs font-medium transition-colors flex items-center justify-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>
|
||||
Détails
|
||||
</a>
|
||||
|
||||
<a href="{{ url_for('grading.assessment_grading', assessment_id=assessment.id) }}"
|
||||
class="flex-1 bg-{{ colors.accent }}-50 hover:bg-{{ colors.accent }}-100 text-{{ colors.accent }}-700 hover:text-{{ colors.accent }}-900 px-3 py-2 rounded-lg text-xs font-medium transition-colors flex items-center justify-center">
|
||||
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z"/>
|
||||
</svg>
|
||||
Noter
|
||||
</a>
|
||||
|
||||
<a href="{{ url_for('assessments.edit', id=assessment.id) }}"
|
||||
class="flex-1 bg-gray-50 hover:bg-gray-100 text-gray-700 hover:text-gray-900 px-3 py-2 rounded-lg text-xs font-medium transition-colors flex items-center justify-center">
|
||||
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M17.414 2.586a2 2 0 00-2.828 0L7 10.172V13h2.828l7.586-7.586a2 2 0 000-2.828z"/>
|
||||
<path fill-rule="evenodd" d="M2 6a2 2 0 012-2h4a1 1 0 010 2H4v10h10v-4a1 1 0 112 0v4a2 2 0 01-2 2H4a2 2 0 01-2-2V6z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
Modifier
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
213
templates/components/common/macros.html
Normal file
213
templates/components/common/macros.html
Normal file
@@ -0,0 +1,213 @@
|
||||
{# Macros pour composants réutilisables #}
|
||||
|
||||
{# Macro pour section hero avec gradient #}
|
||||
{% macro hero_section(title, subtitle=None, meta_info=[], primary_action=None, icon=None, gradient_class="from-purple-600 to-blue-600") %}
|
||||
<div class="bg-gradient-to-r {{ gradient_class }} text-white rounded-xl p-8 shadow-lg">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex-1">
|
||||
{% if caller %}
|
||||
{{ caller() }}
|
||||
{% endif %}
|
||||
<h1 class="text-4xl font-bold mb-2">{{ title }}</h1>
|
||||
{% if subtitle %}
|
||||
<p class="text-xl opacity-90 mb-1">{{ subtitle }}</p>
|
||||
{% endif %}
|
||||
{% if meta_info %}
|
||||
<div class="flex items-center space-x-6 text-sm opacity-75 mt-3">
|
||||
{% for info in meta_info %}
|
||||
<span class="flex items-center">
|
||||
{% if info.icon %}
|
||||
{{ info.icon|safe }}
|
||||
{% endif %}
|
||||
{{ info.text }}
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="hidden md:block">
|
||||
{% if primary_action %}
|
||||
<a href="{{ primary_action.url }}"
|
||||
class="bg-white/20 hover:bg-white/30 text-white px-6 py-3 rounded-xl transition-all duration-300 font-semibold shadow-lg hover:shadow-xl transform hover:scale-105 flex items-center">
|
||||
{% if primary_action.icon %}
|
||||
{{ primary_action.icon|safe }}
|
||||
{% endif %}
|
||||
{{ primary_action.text }}
|
||||
</a>
|
||||
{% elif icon %}
|
||||
<div class="w-24 h-24 bg-white/20 rounded-full flex items-center justify-center">
|
||||
{{ icon|safe }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{# Macro pour card standardisée #}
|
||||
{% macro card(title=None, actions=[], classes="", body_classes="p-6") %}
|
||||
<div class="bg-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 {{ classes }}">
|
||||
{% if title %}
|
||||
<div class="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
|
||||
<h2 class="text-lg font-semibold text-gray-900">{{ title }}</h2>
|
||||
{% if actions %}
|
||||
<div class="flex space-x-2">
|
||||
{% for action in actions %}
|
||||
<a href="{{ action.url }}" class="{{ action.classes|default('text-blue-600 hover:text-blue-800 text-sm font-medium') }}">
|
||||
{{ action.text }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="{{ body_classes }}">
|
||||
{{ caller() }}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{# Macro pour bouton avec gradient #}
|
||||
{% macro gradient_button(url, text, icon=None, gradient_class="from-blue-500 to-blue-600", hover_gradient="from-blue-600 to-blue-700", size="md", classes="") %}
|
||||
{% set size_classes = {
|
||||
'sm': 'px-4 py-2 text-sm',
|
||||
'md': 'px-6 py-3 text-base',
|
||||
'lg': 'px-8 py-4 text-lg'
|
||||
} %}
|
||||
<a href="{{ url }}"
|
||||
class="group bg-gradient-to-r {{ gradient_class }} hover:{{ hover_gradient }} text-white rounded-xl {{ size_classes[size] }} transition-all duration-300 transform hover:scale-105 shadow-lg hover:shadow-xl font-semibold flex items-center {{ classes }}">
|
||||
{% if icon %}
|
||||
<div class="w-5 h-5 mr-2 group-hover:scale-110 transition-transform">
|
||||
{{ icon|safe }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{{ text }}
|
||||
</a>
|
||||
{% endmacro %}
|
||||
|
||||
{# Macro pour action card (comme les cartes d'actions principales) #}
|
||||
{% macro action_card(url, title, subtitle, icon, gradient_class="from-green-500 to-green-600", hover_gradient="from-green-600 to-green-700", is_button=False) %}
|
||||
{% if is_button %}
|
||||
<button onclick="{{ url }}"
|
||||
{% else %}
|
||||
<a href="{{ url }}"
|
||||
{% endif %}
|
||||
class="group bg-gradient-to-r {{ gradient_class }} hover:{{ hover_gradient }} text-white rounded-xl p-6 transition-all duration-300 transform hover:scale-105 shadow-lg hover:shadow-xl">
|
||||
<div class="flex items-center">
|
||||
<div class="w-12 h-12 bg-white/20 rounded-xl flex items-center justify-center mr-4 group-hover:bg-white/30 transition-colors">
|
||||
{{ icon|safe }}
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-bold mb-1">{{ title }}</h3>
|
||||
<p class="text-sm opacity-90">{{ subtitle }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% if is_button %}
|
||||
</button>
|
||||
{% else %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{# Macro pour indicateur de progression #}
|
||||
{% macro progress_indicator(progress, clickable=True, assessment_id=None, compact=False) %}
|
||||
{% set base_classes = "inline-flex items-center rounded-full text-xs font-medium border" %}
|
||||
{% set size_classes = "px-3 py-2" if not compact else "px-2 py-1" %}
|
||||
|
||||
{% if progress.status == 'completed' %}
|
||||
<div class="{{ base_classes }} {{ size_classes }} bg-green-100 text-green-800 border-green-200">
|
||||
<svg class="w-4 h-4 mr-2" 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>
|
||||
<span class="font-semibold">Correction 100%</span>
|
||||
</div>
|
||||
{% elif progress.status == 'in_progress' %}
|
||||
{% if clickable and assessment_id %}
|
||||
<a href="{{ url_for('grading.assessment_grading', assessment_id=assessment_id) }}"
|
||||
{% else %}
|
||||
<div
|
||||
{% endif %}
|
||||
class="{{ base_classes }} {{ size_classes }} bg-orange-100 text-orange-800 border-orange-200 {% if clickable %}hover:bg-orange-200 transition-colors cursor-pointer{% endif %}">
|
||||
<div class="relative w-4 h-4 mr-2">
|
||||
<svg class="w-4 h-4 transform -rotate-90" viewBox="0 0 16 16">
|
||||
<circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="2" fill="none" class="text-orange-300"/>
|
||||
<circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="2" fill="none"
|
||||
class="text-orange-600" stroke-dasharray="37.7"
|
||||
stroke-dashoffset="{{ 37.7 - (37.7 * progress.percentage / 100) }}"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="font-semibold">Correction {{ progress.percentage }}%</span>
|
||||
{% if clickable and assessment_id %}</a>{% else %}</div>{% endif %}
|
||||
{% elif progress.status == 'not_started' %}
|
||||
{% if clickable and assessment_id %}
|
||||
<a href="{{ url_for('grading.assessment_grading', assessment_id=assessment_id) }}"
|
||||
{% else %}
|
||||
<div
|
||||
{% endif %}
|
||||
class="{{ base_classes }} {{ size_classes }} bg-red-100 text-red-800 border-red-200 {% if clickable %}hover:bg-red-200 transition-colors cursor-pointer{% endif %}">
|
||||
<svg class="w-4 h-4 mr-2" 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.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
<span class="font-semibold">Correction 0%</span>
|
||||
{% if clickable and assessment_id %}</a>{% else %}</div>{% endif %}
|
||||
{% else %}
|
||||
<div class="{{ base_classes }} {{ size_classes }} bg-gray-100 text-gray-800 border-gray-200">
|
||||
<svg class="w-4 h-4 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>
|
||||
<span class="font-semibold">Non définie</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{# Macro pour carte de statistique cliquable #}
|
||||
{% macro stat_card(title, value, url, icon, color="blue", description=None) %}
|
||||
<a href="{{ url }}" class="block bg-white rounded-lg shadow hover:shadow-lg transition-all duration-300 p-6 transform hover:scale-105 group">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="text-sm font-medium text-gray-500 group-hover:text-{{ color }}-600 transition-colors">{{ title }}</div>
|
||||
<div class="text-3xl font-bold text-gray-900 group-hover:text-{{ color }}-700 transition-colors">{{ value }}</div>
|
||||
{% if description %}
|
||||
<div class="text-xs text-{{ color }}-600 flex items-center mt-1">
|
||||
<svg class="w-3 h-3 mr-1" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M5.293 7.707a1 1 0 010-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 01-1.414 1.414L11 5.414V17a1 1 0 11-2 0V5.414L6.707 7.707a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
{{ description }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="w-12 h-12 bg-{{ color }}-100 group-hover:bg-{{ color }}-200 rounded-full flex items-center justify-center transition-colors">
|
||||
{{ icon|safe }}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{% endmacro %}
|
||||
|
||||
{# Macro pour filtres standardisés #}
|
||||
{% macro filter_section(filters, current_values={}) %}
|
||||
<div class="filter-section">
|
||||
<div class="flex flex-col lg:flex-row lg:items-center lg:justify-between space-y-4 lg:space-y-0">
|
||||
<div class="flex flex-col md:flex-row md:items-center space-y-3 md:space-y-0 md:space-x-4">
|
||||
{% for filter in filters %}
|
||||
<div class="flex items-center space-x-2">
|
||||
<label class="text-sm font-medium text-gray-700">{{ filter.label }} :</label>
|
||||
<select data-filter="{{ filter.id.replace('-filter', '') }}" class="filter-control">
|
||||
{% for option in filter.options %}
|
||||
<option value="{{ option.value }}" {% if current_values.get(filter.id) == option.value %}selected{% endif %}>
|
||||
{{ option.label }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Zone pour contenu additionnel -->
|
||||
{% if caller %}
|
||||
<div class="flex items-center space-x-4">
|
||||
{{ caller() }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
Reference in New Issue
Block a user