feat: Regrouper les cartes du dashboard admin par section thématique
Le dashboard admin affichait 16 cartes dans une grille unique sans organisation logique, obligeant l'administrateur à scanner visuellement toutes les cartes pour trouver la fonctionnalité souhaitée. Les cartes sont désormais regroupées en 5 sections cohérentes (Personnes, Organisation, Année scolaire, Paramètres, Imports) qui reflètent la structure du menu de navigation latéral.
This commit is contained in:
@@ -322,6 +322,71 @@ test.describe('Dashboard', () => {
|
|||||||
await expect(page.getByRole('heading', { name: 'Configuration', exact: true })).toBeVisible();
|
await expect(page.getByRole('heading', { name: 'Configuration', exact: true })).toBeVisible();
|
||||||
await expect(page.getByRole('heading', { name: /activité récente/i })).toBeVisible();
|
await expect(page.getByRole('heading', { name: /activité récente/i })).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('shows section titles grouping action cards', async ({ page }) => {
|
||||||
|
await goToDashboard(page);
|
||||||
|
await switchToDemoRole(page, 'Admin');
|
||||||
|
|
||||||
|
await expect(page.getByRole('heading', { name: 'Personnes', exact: true })).toBeVisible();
|
||||||
|
await expect(page.getByRole('heading', { name: 'Organisation', exact: true })).toBeVisible();
|
||||||
|
await expect(page.getByRole('heading', { name: 'Année scolaire', exact: true })).toBeVisible();
|
||||||
|
await expect(page.getByRole('heading', { name: 'Paramètres', exact: true })).toBeVisible();
|
||||||
|
await expect(page.getByRole('heading', { name: 'Imports', exact: true })).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('groups correct cards within each section', async ({ page }) => {
|
||||||
|
await goToDashboard(page);
|
||||||
|
await switchToDemoRole(page, 'Admin');
|
||||||
|
|
||||||
|
// Personnes: Utilisateurs, Invitations parents, Élèves
|
||||||
|
const personnes = page.locator('section[aria-labelledby="section-personnes"]');
|
||||||
|
await expect(personnes.locator('.action-card')).toHaveCount(3);
|
||||||
|
await expect(personnes.locator('.action-card[href="/admin/users"]')).toBeVisible();
|
||||||
|
await expect(personnes.locator('.action-card[href="/admin/parent-invitations"]')).toBeVisible();
|
||||||
|
await expect(personnes.locator('.action-card[href="/admin/students"]')).toBeVisible();
|
||||||
|
|
||||||
|
// Organisation: Classes, Matières, Affectations, Remplacements, Emploi du temps
|
||||||
|
const organisation = page.locator('section[aria-labelledby="section-organisation"]');
|
||||||
|
await expect(organisation.locator('.action-card')).toHaveCount(5);
|
||||||
|
await expect(organisation.locator('.action-card[href="/admin/classes"]')).toBeVisible();
|
||||||
|
await expect(organisation.locator('.action-card[href="/admin/subjects"]')).toBeVisible();
|
||||||
|
await expect(organisation.locator('.action-card[href="/admin/assignments"]')).toBeVisible();
|
||||||
|
await expect(organisation.locator('.action-card[href="/admin/replacements"]')).toBeVisible();
|
||||||
|
await expect(organisation.locator('.action-card[href="/admin/schedule"]')).toBeVisible();
|
||||||
|
|
||||||
|
// Année scolaire: Périodes scolaires, Calendrier
|
||||||
|
const anneeScolaire = page.locator('section[aria-labelledby="section-annee-scolaire"]');
|
||||||
|
await expect(anneeScolaire.locator('.action-card')).toHaveCount(2);
|
||||||
|
await expect(anneeScolaire.locator('.action-card[href="/admin/academic-year/periods"]')).toBeVisible();
|
||||||
|
await expect(anneeScolaire.locator('.action-card[href="/admin/calendar"]')).toBeVisible();
|
||||||
|
|
||||||
|
// Paramètres: Droit à l'image, Pédagogie, Identité visuelle, Règles de devoirs
|
||||||
|
const parametres = page.locator('section[aria-labelledby="section-parametres"]');
|
||||||
|
await expect(parametres.locator('.action-card')).toHaveCount(4);
|
||||||
|
await expect(parametres.locator('.action-card[href="/admin/image-rights"]')).toBeVisible();
|
||||||
|
await expect(parametres.locator('.action-card[href="/admin/pedagogy"]')).toBeVisible();
|
||||||
|
await expect(parametres.locator('.action-card[href="/admin/branding"]')).toBeVisible();
|
||||||
|
await expect(parametres.locator('.action-card[href="/admin/homework-rules"]')).toBeVisible();
|
||||||
|
|
||||||
|
// Imports: Importer des élèves, Importer des enseignants
|
||||||
|
const imports = page.locator('section[aria-labelledby="section-imports"]');
|
||||||
|
await expect(imports.locator('.action-card')).toHaveCount(2);
|
||||||
|
await expect(imports.locator('.action-card[href="/admin/import/students"]')).toBeVisible();
|
||||||
|
await expect(imports.locator('.action-card[href="/admin/import/teachers"]')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('sections are displayed in the expected order', async ({ page }) => {
|
||||||
|
await goToDashboard(page);
|
||||||
|
await switchToDemoRole(page, 'Admin');
|
||||||
|
|
||||||
|
const sectionTitles = page.locator('.dashboard-section .section-title');
|
||||||
|
await expect(sectionTitles).toHaveCount(5);
|
||||||
|
await expect(sectionTitles.nth(0)).toHaveText('Personnes');
|
||||||
|
await expect(sectionTitles.nth(1)).toHaveText('Organisation');
|
||||||
|
await expect(sectionTitles.nth(2)).toHaveText('Année scolaire');
|
||||||
|
await expect(sectionTitles.nth(3)).toHaveText('Paramètres');
|
||||||
|
await expect(sectionTitles.nth(4)).toHaveText('Imports');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -11,6 +11,58 @@
|
|||||||
hasRealData?: boolean;
|
hasRealData?: boolean;
|
||||||
establishmentName?: string;
|
establishmentName?: string;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
|
type ActionCard = { href: string; icon: string; label: string; hint: string };
|
||||||
|
type DashboardSectionDef = { id: string; title: string; cards: ActionCard[] };
|
||||||
|
|
||||||
|
const dashboardSections: DashboardSectionDef[] = [
|
||||||
|
{
|
||||||
|
id: 'personnes',
|
||||||
|
title: 'Personnes',
|
||||||
|
cards: [
|
||||||
|
{ href: '/admin/users', icon: '👥', label: 'Gérer les utilisateurs', hint: 'Inviter et gérer' },
|
||||||
|
{ href: '/admin/parent-invitations', icon: '✉️', label: 'Invitations parents', hint: "Codes d'invitation" },
|
||||||
|
{ href: '/admin/students', icon: '🎒', label: 'Gérer les élèves', hint: 'Inscrire et affecter' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'organisation',
|
||||||
|
title: 'Organisation',
|
||||||
|
cards: [
|
||||||
|
{ href: '/admin/classes', icon: '🏫', label: 'Configurer les classes', hint: 'Créer et gérer' },
|
||||||
|
{ href: '/admin/subjects', icon: '📚', label: 'Gérer les matières', hint: 'Créer et gérer' },
|
||||||
|
{ href: '/admin/assignments', icon: '📋', label: 'Affectations', hint: 'Enseignants et classes' },
|
||||||
|
{ href: '/admin/replacements', icon: '🔄', label: 'Remplacements', hint: 'Enseignants absents' },
|
||||||
|
{ href: '/admin/schedule', icon: '🕐', label: 'Emploi du temps', hint: 'Cours et créneaux' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'annee-scolaire',
|
||||||
|
title: 'Année scolaire',
|
||||||
|
cards: [
|
||||||
|
{ href: '/admin/academic-year/periods', icon: '📅', label: 'Périodes scolaires', hint: 'Trimestres et semestres' },
|
||||||
|
{ href: '/admin/calendar', icon: '🗓️', label: 'Calendrier scolaire', hint: 'Fériés et vacances' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'parametres',
|
||||||
|
title: 'Paramètres',
|
||||||
|
cards: [
|
||||||
|
{ href: '/admin/image-rights', icon: '📷', label: "Droit à l'image", hint: 'Autorisations élèves' },
|
||||||
|
{ href: '/admin/pedagogy', icon: '🎓', label: 'Pédagogie', hint: 'Mode de notation' },
|
||||||
|
{ href: '/admin/branding', icon: '🎨', label: 'Identité visuelle', hint: 'Logo et couleurs' },
|
||||||
|
{ href: '/admin/homework-rules', icon: '📏', label: 'Règles de devoirs', hint: 'Timing et contraintes' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'imports',
|
||||||
|
title: 'Imports',
|
||||||
|
cards: [
|
||||||
|
{ href: '/admin/import/students', icon: '📤', label: 'Importer des élèves', hint: 'CSV ou XLSX' },
|
||||||
|
{ href: '/admin/import/teachers', icon: '📤', label: 'Importer des enseignants', hint: 'CSV ou XLSX' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="dashboard-admin">
|
<div class="dashboard-admin">
|
||||||
@@ -23,91 +75,20 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="quick-actions">
|
{#each dashboardSections as section (section.id)}
|
||||||
<h2 class="sr-only">Actions de configuration</h2>
|
<section class="dashboard-section" aria-labelledby="section-{section.id}">
|
||||||
<div class="action-cards">
|
<h2 id="section-{section.id}" class="section-title">{section.title}</h2>
|
||||||
<a class="action-card" href="/admin/users">
|
<div class="action-cards">
|
||||||
<span class="action-icon">👥</span>
|
{#each section.cards as card (card.href)}
|
||||||
<span class="action-label">Gérer les utilisateurs</span>
|
<a class="action-card" href={card.href}>
|
||||||
<span class="action-hint">Inviter et gérer</span>
|
<span class="action-icon">{card.icon}</span>
|
||||||
</a>
|
<span class="action-label">{card.label}</span>
|
||||||
<a class="action-card" href="/admin/parent-invitations">
|
<span class="action-hint">{card.hint}</span>
|
||||||
<span class="action-icon">✉️</span>
|
</a>
|
||||||
<span class="action-label">Invitations parents</span>
|
{/each}
|
||||||
<span class="action-hint">Codes d'invitation</span>
|
</div>
|
||||||
</a>
|
</section>
|
||||||
<a class="action-card" href="/admin/classes">
|
{/each}
|
||||||
<span class="action-icon">🏫</span>
|
|
||||||
<span class="action-label">Configurer les classes</span>
|
|
||||||
<span class="action-hint">Créer et gérer</span>
|
|
||||||
</a>
|
|
||||||
<a class="action-card" href="/admin/students">
|
|
||||||
<span class="action-icon">🎒</span>
|
|
||||||
<span class="action-label">Gérer les élèves</span>
|
|
||||||
<span class="action-hint">Inscrire et affecter</span>
|
|
||||||
</a>
|
|
||||||
<a class="action-card" href="/admin/subjects">
|
|
||||||
<span class="action-icon">📚</span>
|
|
||||||
<span class="action-label">Gérer les matières</span>
|
|
||||||
<span class="action-hint">Créer et gérer</span>
|
|
||||||
</a>
|
|
||||||
<a class="action-card" href="/admin/assignments">
|
|
||||||
<span class="action-icon">📋</span>
|
|
||||||
<span class="action-label">Affectations</span>
|
|
||||||
<span class="action-hint">Enseignants et classes</span>
|
|
||||||
</a>
|
|
||||||
<a class="action-card" href="/admin/replacements">
|
|
||||||
<span class="action-icon">🔄</span>
|
|
||||||
<span class="action-label">Remplacements</span>
|
|
||||||
<span class="action-hint">Enseignants absents</span>
|
|
||||||
</a>
|
|
||||||
<a class="action-card" href="/admin/schedule">
|
|
||||||
<span class="action-icon">🕐</span>
|
|
||||||
<span class="action-label">Emploi du temps</span>
|
|
||||||
<span class="action-hint">Cours et créneaux</span>
|
|
||||||
</a>
|
|
||||||
<a class="action-card" href="/admin/academic-year/periods">
|
|
||||||
<span class="action-icon">📅</span>
|
|
||||||
<span class="action-label">Périodes scolaires</span>
|
|
||||||
<span class="action-hint">Trimestres et semestres</span>
|
|
||||||
</a>
|
|
||||||
<a class="action-card" href="/admin/calendar">
|
|
||||||
<span class="action-icon">🗓️</span>
|
|
||||||
<span class="action-label">Calendrier scolaire</span>
|
|
||||||
<span class="action-hint">Fériés et vacances</span>
|
|
||||||
</a>
|
|
||||||
<a class="action-card" href="/admin/image-rights">
|
|
||||||
<span class="action-icon">📷</span>
|
|
||||||
<span class="action-label">Droit à l'image</span>
|
|
||||||
<span class="action-hint">Autorisations élèves</span>
|
|
||||||
</a>
|
|
||||||
<a class="action-card" href="/admin/pedagogy">
|
|
||||||
<span class="action-icon">🎓</span>
|
|
||||||
<span class="action-label">Pédagogie</span>
|
|
||||||
<span class="action-hint">Mode de notation</span>
|
|
||||||
</a>
|
|
||||||
<a class="action-card" href="/admin/branding">
|
|
||||||
<span class="action-icon">🎨</span>
|
|
||||||
<span class="action-label">Identité visuelle</span>
|
|
||||||
<span class="action-hint">Logo et couleurs</span>
|
|
||||||
</a>
|
|
||||||
<a class="action-card" href="/admin/homework-rules">
|
|
||||||
<span class="action-icon">📏</span>
|
|
||||||
<span class="action-label">Règles de devoirs</span>
|
|
||||||
<span class="action-hint">Timing et contraintes</span>
|
|
||||||
</a>
|
|
||||||
<a class="action-card" href="/admin/import/students">
|
|
||||||
<span class="action-icon">📤</span>
|
|
||||||
<span class="action-label">Importer des élèves</span>
|
|
||||||
<span class="action-hint">CSV ou XLSX</span>
|
|
||||||
</a>
|
|
||||||
<a class="action-card" href="/admin/import/teachers">
|
|
||||||
<span class="action-icon">📤</span>
|
|
||||||
<span class="action-label">Importer des enseignants</span>
|
|
||||||
<span class="action-hint">CSV ou XLSX</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="dashboard-grid">
|
<div class="dashboard-grid">
|
||||||
<DashboardSection
|
<DashboardSection
|
||||||
@@ -182,20 +163,19 @@
|
|||||||
color: #6b7280;
|
color: #6b7280;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sr-only {
|
.dashboard-section {
|
||||||
position: absolute;
|
display: flex;
|
||||||
width: 1px;
|
flex-direction: column;
|
||||||
height: 1px;
|
gap: 0.75rem;
|
||||||
padding: 0;
|
|
||||||
margin: -1px;
|
|
||||||
overflow: hidden;
|
|
||||||
clip: rect(0, 0, 0, 0);
|
|
||||||
white-space: nowrap;
|
|
||||||
border: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.quick-actions {
|
.section-title {
|
||||||
margin-bottom: 0.5rem;
|
margin: 0;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #374151;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-cards {
|
.action-cards {
|
||||||
|
|||||||
@@ -207,14 +207,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ruleTypeLabel(type: string): string {
|
|
||||||
return type === 'minimum_delay'
|
|
||||||
? 'Délai minimum'
|
|
||||||
: type === 'no_monday_after'
|
|
||||||
? 'Pas de devoir pour lundi'
|
|
||||||
: type;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDate(dateStr: string): string {
|
function formatDate(dateStr: string): string {
|
||||||
const d = new Date(dateStr);
|
const d = new Date(dateStr);
|
||||||
return d.toLocaleDateString('fr-FR', {
|
return d.toLocaleDateString('fr-FR', {
|
||||||
|
|||||||
Reference in New Issue
Block a user