feat: Réorganiser la navigation admin en catégories pour améliorer l'UX mobile-first

Le menu d'administration contenait 13 liens à plat dans le header, ce qui
débordait sur desktop et rendait le drawer mobile trop long à scanner.

Les liens sont maintenant regroupés en 4 catégories (Personnes, Organisation,
Année scolaire, Paramètres) avec des dropdowns au survol sur desktop et des
accordéons repliables dans le drawer mobile. Le nombre d'éléments visibles
passe de 13 à 5 (1 lien direct + 4 catégories), la catégorie active
s'auto-déplie dans le menu mobile.
This commit is contained in:
2026-02-28 00:09:20 +01:00
parent be1b0b60a6
commit ce05207c64
9 changed files with 418 additions and 73 deletions

View File

@@ -119,6 +119,7 @@ test.describe('Admin Responsive Navigation', () => {
const drawer = page.locator('[role="dialog"][aria-modal="true"]');
await expect(drawer).toBeVisible();
// Active category "Personnes" should be auto-expanded
const activeLink = drawer.locator('.mobile-nav-link.active');
await expect(activeLink).toHaveText('Utilisateurs');
});
@@ -128,11 +129,13 @@ test.describe('Admin Responsive Navigation', () => {
await page.goto(`${ALPHA_URL}/admin/users`);
await page.waitForLoadState('networkidle');
// Open menu and click Classes
// Open menu and expand "Organisation" section to find "Classes"
await page.getByRole('button', { name: /ouvrir le menu/i }).click();
const drawer = page.locator('[role="dialog"][aria-modal="true"]');
await expect(drawer).toBeVisible();
// Expand "Organisation" accordion
await drawer.getByRole('button', { name: 'Organisation' }).click();
await drawer.getByRole('link', { name: 'Classes' }).click();
// Menu should close and page should navigate
@@ -143,6 +146,30 @@ test.describe('Admin Responsive Navigation', () => {
const label = page.locator('.mobile-section-label');
await expect(label).toHaveText('Classes');
});
test('accordion sections expand and collapse', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`${ALPHA_URL}/admin/users`);
await page.waitForLoadState('networkidle');
await page.getByRole('button', { name: /ouvrir le menu/i }).click();
const drawer = page.locator('[role="dialog"][aria-modal="true"]');
await expect(drawer).toBeVisible();
// "Personnes" should be auto-expanded (active category)
await expect(drawer.getByRole('link', { name: 'Utilisateurs' })).toBeVisible();
// "Organisation" should be collapsed initially
await expect(drawer.getByRole('link', { name: 'Classes' })).not.toBeVisible();
// Expand "Organisation"
await drawer.getByRole('button', { name: 'Organisation' }).click();
await expect(drawer.getByRole('link', { name: 'Classes' })).toBeVisible();
// Collapse "Organisation"
await drawer.getByRole('button', { name: 'Organisation' }).click();
await expect(drawer.getByRole('link', { name: 'Classes' })).not.toBeVisible();
});
});
// =========================================================================
@@ -163,7 +190,7 @@ test.describe('Admin Responsive Navigation', () => {
await expect(desktopNav).not.toBeVisible();
});
test('drawer opens and works', async ({ page }) => {
test('drawer opens and shows grouped nav', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`${ALPHA_URL}/admin/users`);
await page.waitForLoadState('networkidle');
@@ -172,8 +199,11 @@ test.describe('Admin Responsive Navigation', () => {
const drawer = page.locator('[role="dialog"][aria-modal="true"]');
await expect(drawer).toBeVisible();
// All nav links should be visible in drawer
// "Personnes" auto-expanded (contains active link Utilisateurs)
await expect(drawer.getByRole('link', { name: 'Utilisateurs' })).toBeVisible();
// Expand "Organisation" to see its links
await drawer.getByRole('button', { name: 'Organisation' }).click();
await expect(drawer.getByRole('link', { name: 'Classes' })).toBeVisible();
await expect(drawer.getByRole('link', { name: 'Matières' })).toBeVisible();
});
@@ -197,18 +227,42 @@ test.describe('Admin Responsive Navigation', () => {
await expect(desktopNav).toBeVisible();
});
test('desktop nav shows all navigation links', async ({ page }) => {
test('desktop nav shows category dropdowns', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`${ALPHA_URL}/admin/users`);
await page.waitForLoadState('networkidle');
const nav = page.locator('.desktop-nav');
await expect(nav.getByRole('link', { name: 'Utilisateurs' })).toBeVisible();
await expect(nav.getByRole('link', { name: 'Classes' })).toBeVisible();
await expect(nav.getByRole('link', { name: 'Matières' })).toBeVisible();
await expect(nav.getByRole('link', { name: 'Affectations' })).toBeVisible();
await expect(nav.getByRole('link', { name: 'Périodes' })).toBeVisible();
await expect(nav.getByRole('link', { name: 'Pédagogie' })).toBeVisible();
// Category triggers should be visible
await expect(nav.getByRole('button', { name: /personnes/i })).toBeVisible();
await expect(nav.getByRole('button', { name: /organisation/i })).toBeVisible();
await expect(nav.getByRole('button', { name: /année scolaire/i })).toBeVisible();
await expect(nav.getByRole('button', { name: /paramètres/i })).toBeVisible();
// Hover "Personnes" to reveal dropdown
await nav.getByRole('button', { name: /personnes/i }).hover();
const dropdown = nav.locator('.dropdown-panel').first();
await expect(dropdown).toBeVisible();
await expect(dropdown.getByRole('menuitem', { name: 'Utilisateurs' })).toBeVisible();
await expect(dropdown.getByRole('menuitem', { name: 'Élèves' })).toBeVisible();
// Hover "Organisation"
await nav.getByRole('button', { name: /organisation/i }).hover();
const orgDropdown = nav.locator('.dropdown-panel').first();
await expect(orgDropdown.getByRole('menuitem', { name: 'Classes' })).toBeVisible();
await expect(orgDropdown.getByRole('menuitem', { name: 'Matières' })).toBeVisible();
await expect(orgDropdown.getByRole('menuitem', { name: 'Affectations' })).toBeVisible();
});
test('active category trigger is highlighted', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`${ALPHA_URL}/admin/users`);
await page.waitForLoadState('networkidle');
const nav = page.locator('.desktop-nav');
const personnesTrigger = nav.getByRole('button', { name: /personnes/i });
await expect(personnesTrigger).toHaveClass(/active/);
});
test('hides mobile section label', async ({ page }) => {