{% extends 'layout/backEndLayout.html.twig' %}
{% block stylesheets %}
{{ parent() }}
<style>
/* ── Tokens locaux ─────────────────────────────────────────── */
:root {
--ci-bg : #f5f7fa;
--ci-card : #ffffff;
--ci-border : rgba(0,0,0,.08);
--ci-border-hi: rgba(0,0,0,.16);
--ci-ink : #111827;
--ci-dim : #4b5563;
--ci-muted : #9ca3af;
--ci-blue : #1d4ed8;
--ci-blue-lt : rgba(29,78,216,.1);
--ci-teal : #0d9488;
--ci-teal-lt : rgba(13,148,136,.1);
--ci-amber : #b45309;
--ci-amber-lt : rgba(180,83,9,.1);
--ci-rose : #be123c;
--ci-rose-lt : rgba(190,18,60,.1);
--ci-r : 12px;
--ci-rs : 8px;
--ci-shadow : 0 1px 4px rgba(0,0,0,.06), 0 4px 16px rgba(0,0,0,.06);
}
/* ── Layout page ───────────────────────────────────────────── */
.ci-page { font-family:'DM Sans','Segoe UI',sans-serif; }
/* ── Barre de tête ─────────────────────────────────────────── */
.ci-topbar {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px;
margin-bottom: 24px;
}
.ci-topbar__title {
font-size: 20px;
font-weight: 700;
color: var(--ci-ink);
display: flex;
align-items: center;
gap: 10px;
}
.ci-topbar__title i { color: var(--ci-blue); font-size: 18px; }
.ci-topbar__sub { font-size: 13px; color: var(--ci-muted); margin-top: 2px; }
/* ── Statistiques globales ─────────────────────────────────── */
.ci-stats {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 12px;
margin-bottom: 24px;
}
.ci-stat {
background: var(--ci-card);
border: 1px solid var(--ci-border);
border-radius: var(--ci-r);
padding: 16px;
display: flex;
align-items: center;
gap: 12px;
transition: box-shadow .2s;
}
.ci-stat:hover { box-shadow: var(--ci-shadow); }
.ci-stat__icon {
width: 40px; height: 40px;
border-radius: var(--ci-rs);
display: flex; align-items: center; justify-content: center;
font-size: 16px; flex-shrink: 0;
}
.ic-blue { background: var(--ci-blue-lt); color: var(--ci-blue); }
.ic-teal { background: var(--ci-teal-lt); color: var(--ci-teal); }
.ic-amber { background: var(--ci-amber-lt); color: var(--ci-amber); }
.ic-rose { background: var(--ci-rose-lt); color: var(--ci-rose); }
.ci-stat__val { font-size: 22px; font-weight: 700; color: var(--ci-ink); line-height: 1; }
.ci-stat__lbl { font-size: 11.5px; color: var(--ci-muted); margin-top: 2px; }
/* ── Tableau ───────────────────────────────────────────────── */
.ci-table-card {
background: var(--ci-card);
border: 1px solid var(--ci-border);
border-radius: var(--ci-r);
overflow: hidden;
box-shadow: var(--ci-shadow);
}
.ci-table-head {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 10px;
padding: 16px 20px;
border-bottom: 1px solid var(--ci-border);
background: #fafbfc;
}
.ci-table-head__title {
font-size: 14px;
font-weight: 600;
color: var(--ci-ink);
display: flex;
align-items: center;
gap: 8px;
}
.ci-table-head__dot {
width: 8px; height: 8px;
border-radius: 50%;
background: var(--ci-blue);
box-shadow: 0 0 6px var(--ci-blue);
}
/* Bouton ajouter */
.btn-add {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 16px;
background: var(--ci-blue);
color: #fff;
border-radius: var(--ci-rs);
font-size: 13px;
font-weight: 600;
text-decoration: none;
border: none;
cursor: pointer;
transition: background .18s, transform .14s;
}
.btn-add:hover { background: #1e40af; color: #fff; transform: translateY(-1px); }
/* Table */
table.ci-tbl {
width: 100%;
border-collapse: collapse;
font-size: 13.5px;
}
table.ci-tbl thead th {
padding: 10px 16px;
color: var(--ci-muted);
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: .06em;
background: #fafbfc;
border-bottom: 1px solid var(--ci-border);
white-space: nowrap;
}
table.ci-tbl tbody td {
padding: 12px 16px;
border-bottom: 1px solid rgba(0,0,0,.04);
color: var(--ci-ink);
vertical-align: middle;
}
table.ci-tbl tbody tr:last-child td { border-bottom: none; }
table.ci-tbl tbody tr { transition: background .12s; }
table.ci-tbl tbody tr:hover td { background: rgba(29,78,216,.03); }
/* Nom de la classe */
.room-name {
font-weight: 700;
font-size: 14px;
color: var(--ci-ink);
}
.room-link {
display: inline-flex;
align-items: center;
gap: 6px;
color: var(--ci-ink);
text-decoration: none;
}
.room-link:hover { color: var(--ci-blue); }
.room-link:hover .room-name { text-decoration: underline; }
/* Niveau badge */
.level-badge {
display: inline-block;
padding: 3px 10px;
border-radius: 20px;
font-size: 11.5px;
font-weight: 600;
background: var(--ci-blue-lt);
color: var(--ci-blue);
border: 1px solid rgba(29,78,216,.2);
}
/* Séquence active */
.seq-badge {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 3px 10px;
border-radius: 20px;
font-size: 11px;
font-weight: 500;
background: var(--ci-teal-lt);
color: var(--ci-teal);
border: 1px solid rgba(13,148,136,.2);
}
.seq-dot {
width: 6px; height: 6px;
border-radius: 50%;
background: var(--ci-teal);
animation: pulse-dot 1.6s ease infinite;
}
@keyframes pulse-dot {
0%,100% { opacity:1; } 50% { opacity:.35; }
}
/* Prof titulaire */
.main-teacher {
display: flex;
align-items: center;
gap: 7px;
font-size: 13px;
}
.teacher-avatar {
width: 26px; height: 26px;
border-radius: 50%;
background: linear-gradient(135deg, #1d4ed8, #0d9488);
color: #fff;
font-size: 10px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.no-teacher {
font-size: 12px;
color: var(--ci-rose);
font-style: italic;
}
/* Boutons action ligne */
.ci-actions {
display: flex;
align-items: center;
gap: 4px;
justify-content: flex-end;
}
.ci-btn {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 5px 10px;
border-radius: 7px;
font-size: 11.5px;
font-weight: 500;
text-decoration: none;
border: 1px solid var(--ci-border);
cursor: pointer;
background: #fff;
color: var(--ci-dim);
transition: background .15s, border-color .15s, color .15s;
white-space: nowrap;
}
.ci-btn:hover { border-color: var(--ci-border-hi); color: var(--ci-ink); background: #f8f9fa; }
.ci-btn-primary { background: var(--ci-blue-lt); color: var(--ci-blue); border-color: rgba(29,78,216,.25); }
.ci-btn-primary:hover { background: #1d4ed8; color: #fff; }
.ci-btn-warning { background: var(--ci-amber-lt); color: var(--ci-amber); border-color: rgba(180,83,9,.25); }
.ci-btn-warning:hover { background: #b45309; color: #fff; }
.ci-btn-danger { background: var(--ci-rose-lt); color: var(--ci-rose); border-color: rgba(190,18,60,.25); }
.ci-btn-danger:hover { background: #be123c; color: #fff; }
/* ── Pagination ────────────────────────────────────────────── */
.ci-pagination {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 10px;
padding: 14px 20px;
border-top: 1px solid var(--ci-border);
background: #fafbfc;
}
.ci-pagination__info { font-size: 12.5px; color: var(--ci-muted); }
.ci-pager {
display: flex;
align-items: center;
gap: 4px;
list-style: none;
margin: 0; padding: 0;
}
.ci-pager li a,
.ci-pager li span {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 34px;
height: 34px;
padding: 0 8px;
border-radius: 8px;
border: 1px solid var(--ci-border);
font-size: 13px;
font-weight: 500;
color: var(--ci-dim);
text-decoration: none;
background: #fff;
transition: background .15s, border-color .15s, color .15s;
}
.ci-pager li a:hover { border-color: var(--ci-blue); color: var(--ci-blue); }
.ci-pager li.active span {
background: var(--ci-blue);
color: #fff;
border-color: var(--ci-blue);
font-weight: 700;
}
.ci-pager li.disabled span { opacity: .4; cursor: default; }
/* ── Vide ──────────────────────────────────────────────────── */
.ci-empty {
text-align: center;
padding: 64px 24px;
color: var(--ci-muted);
}
.ci-empty i { font-size: 40px; margin-bottom: 12px; display: block; }
.ci-empty strong { display: block; font-size: 15px; color: var(--ci-dim); margin-bottom: 4px; }
/* ── Animation ─────────────────────────────────────────────── */
@keyframes fadeUp {
from { opacity:0; transform:translateY(10px); }
to { opacity:1; transform:none; }
}
.ci-table-card { animation: fadeUp .4s ease both; }
</style>
{% endblock %}
{% block content %}
<div class="ci-page">
{# ── Barre de tête ──────────────────────────────────────────── #}
<div class="ci-topbar">
<div>
<div class="ci-topbar__title">
<i class="fa fa-th-large"></i>
Classes
</div>
<div class="ci-topbar__sub">
Gestion des classes — {% if year %}Année scolaire {{ year.code }}{% endif %}
{% if seq > 0 %} · Séquence active{% endif %}
</div>
</div>
{% if is_granted('ROLE_ADMIN') %}
<a href="{{ path('admin_classrooms_new') }}" class="btn-add">
<i class="fa fa-plus"></i> Nouvelle classe
</a>
{% endif %}
</div>
{# ── KPIs globaux ───────────────────────────────────────────── #}
<div class="ci-stats">
<div class="ci-stat">
<div class="ci-stat__icon ic-blue"><i class="fa fa-home"></i></div>
<div>
<div class="ci-stat__val">{{ classrooms|length }}</div>
<div class="ci-stat__lbl">Classes</div>
</div>
</div>
<div class="ci-stat">
<div class="ci-stat__icon ic-teal"><i class="fa fa-graduation-cap"></i></div>
<div>
<div class="ci-stat__val">{{ students_count() }}</div>
<div class="ci-stat__lbl">Élèves inscrits</div>
</div>
</div>
<div class="ci-stat">
<div class="ci-stat__icon ic-amber"><i class="fa fa-users"></i></div>
<div>
<div class="ci-stat__val">{{ teachers_count() }}</div>
<div class="ci-stat__lbl">Enseignants</div>
</div>
</div>
<div class="ci-stat">
<div class="ci-stat__icon ic-rose"><i class="fa fa-book"></i></div>
<div>
<div class="ci-stat__val">{{ rooms_count() }}</div>
<div class="ci-stat__lbl">Salles actives</div>
</div>
</div>
</div>
{# ── Tableau des classes ─────────────────────────────────────── #}
{% set items_per_page = 12 %}
{% set current_page = app.request.query.get('page', 1)|number_format %}
{% set total_pages = (classrooms|length / items_per_page)|round(0, 'ceil') %}
<div class="ci-table-card">
<div class="ci-table-head">
<div class="ci-table-head__title">
<span class="ci-table-head__dot"></span>
Liste des classes
<span style="font-size:11.5px;color:var(--ci-muted);font-weight:400;">({{ classrooms|length }} au total)</span>
</div>
<div style="display:flex;gap:8px;align-items:center;">
<span class="seq-badge">
<span class="seq-dot"></span>
{% if seq > 0 %}Séquence active{% else %}Aucune séquence active{% endif %}
</span>
</div>
</div>
{% if classrooms is empty %}
<div class="ci-empty">
<i class="fa fa-inbox"></i>
<strong>Aucune classe enregistrée</strong>
Commencez par créer une classe.
</div>
{% else %}
<div style="overflow-x:auto;">
<table class="ci-tbl">
<thead>
<tr>
<th style="width:4%">#</th>
<th style="width:18%">Classe</th>
<th style="width:12%">Niveau</th>
<th style="width:22%">Professeur titulaire</th>
<th style="width:10%" class="text-center">Modules</th>
<th style="width:10%" class="text-center">Matières</th>
<th style="width:24%" class="text-right">Actions</th>
</tr>
</thead>
<tbody>
{% for classroom in classrooms %}
{# ── Pagination côté Twig ── #}
{% if loop.index > (current_page - 1) * items_per_page and loop.index <= current_page * items_per_page %}
<tr>
<td style="color:var(--ci-muted);font-size:12px">{{ (current_page - 1) * items_per_page + loop.index - ((current_page - 1) * items_per_page) }}</td>
<td>
<a href="{{ path('admin_classrooms_show', {id: classroom.id}) }}" class="room-link">
<span class="room-name">{{ classroom.name }}</span>
</a>
</td>
<td>
<span class="level-badge">{{ classroom.level }}</span>
</td>
<td>
{% if mainTeachers[classroom.id] is defined %}
<div class="main-teacher">
<div class="teacher-avatar">
{{ mainTeachers[classroom.id].fullName|slice(0,1)|upper }}{{ mainTeachers[classroom.id].fullName|split(' ')|last|slice(0,1)|upper }}
</div>
{{ mainTeachers[classroom.id].fullName }}
</div>
{% else %}
<span class="no-teacher"><i class="fa fa-exclamation-triangle"></i> Non assigné</span>
{% endif %}
</td>
<td class="text-center">
<span style="font-weight:600;color:var(--ci-blue)">{{ classroom.modules|length }}</span>
</td>
<td class="text-center">
{% set total_courses = 0 %}
{% for module in classroom.modules %}
{% set total_courses = total_courses + module.courses|length %}
{% endfor %}
<span style="font-weight:600;color:var(--ci-teal)">{{ total_courses }}</span>
</td>
<td>
<div class="ci-actions">
<a class="ci-btn ci-btn-primary" href="{{ path('admin_classrooms_show', {id: classroom.id}) }}" title="Voir">
<i class="fa fa-eye"></i> Voir
</a>
{% if is_granted('ROLE_ADMIN') %}
<a class="ci-btn ci-btn-warning" href="{{ path('admin_classrooms_edit', {id: classroom.id}) }}" title="Modifier">
<i class="fa fa-pencil"></i>
</a>
{% if seq > 0 %}
<a class="ci-btn" href="{{ path('admin_classrooms_recapitulatif', {room: classroom.id, seq: seq}) }}" title="Récapitulatif PDF">
<i class="fa fa-file-pdf-o"></i>
</a>
{% endif %}
<form method="post" action="{{ path('admin_classrooms_delete', {id: classroom.id}) }}" onsubmit="return confirm('Supprimer cette classe ?')" style="display:inline">
<input type="hidden" name="_method" value="DELETE">
<button class="ci-btn ci-btn-danger" type="submit" title="Supprimer">
<i class="fa fa-trash"></i>
</button>
</form>
{% endif %}
</div>
</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
</div>
{# ── Pagination ────────────────────────────────────────────── #}
{% if total_pages > 1 %}
<div class="ci-pagination">
<div class="ci-pagination__info">
Page {{ current_page }} sur {{ total_pages }} — {{ classrooms|length }} classe{{ classrooms|length > 1 ? 's' : '' }}
</div>
<ul class="ci-pager">
<li class="{{ current_page <= 1 ? 'disabled' : '' }}">
{% if current_page > 1 %}
<a href="?page={{ current_page - 1 }}"><i class="fa fa-chevron-left"></i></a>
{% else %}
<span><i class="fa fa-chevron-left"></i></span>
{% endif %}
</li>
{% for p in 1..total_pages %}
{% if p == current_page %}
<li class="active"><span>{{ p }}</span></li>
{% elseif p == 1 or p == total_pages or (p >= current_page - 2 and p <= current_page + 2) %}
<li><a href="?page={{ p }}">{{ p }}</a></li>
{% elseif p == current_page - 3 or p == current_page + 3 %}
<li><span style="border:none;background:transparent">…</span></li>
{% endif %}
{% endfor %}
<li class="{{ current_page >= total_pages ? 'disabled' : '' }}">
{% if current_page < total_pages %}
<a href="?page={{ current_page + 1 }}"><i class="fa fa-chevron-right"></i></a>
{% else %}
<span><i class="fa fa-chevron-right"></i></span>
{% endif %}
</li>
</ul>
</div>
{% else %}
<div class="ci-pagination">
<div class="ci-pagination__info">{{ classrooms|length }} classe{{ classrooms|length > 1 ? 's' : '' }} au total</div>
</div>
{% endif %}
{% endif %}
</div>
</div>
{% endblock %}