feat: Pagination et recherche des sections admin
Les listes admin (utilisateurs, classes, matières, affectations) chargeaient toutes les données d'un coup, ce qui dégradait l'expérience avec un volume croissant. La pagination côté serveur existait dans la config API Platform mais aucun Provider ne l'exploitait. Cette implémentation ajoute la pagination serveur (30 items/page, max 100) avec recherche textuelle sur toutes les sections, des composants frontend réutilisables (Pagination + SearchInput avec debounce), et la synchronisation URL pour le partage de liens filtrés. Les Query valident leurs paramètres (clamp page/limit, trim search) pour éviter les abus. Les affectations utilisent des lookup maps pour résoudre les noms sans N+1 queries. Les pages admin gèrent les race conditions via AbortController.
This commit is contained in:
@@ -86,7 +86,7 @@ test.describe('Student Management', () => {
|
||||
await page.locator('#email').fill(ADMIN_EMAIL);
|
||||
await page.locator('#password').fill(ADMIN_PASSWORD);
|
||||
await Promise.all([
|
||||
page.waitForURL(/\/dashboard/, { timeout: 10000 }),
|
||||
page.waitForURL(/\/dashboard/, { timeout: 30000 }),
|
||||
page.getByRole('button', { name: /se connecter/i }).click()
|
||||
]);
|
||||
}
|
||||
@@ -100,11 +100,11 @@ test.describe('Student Management', () => {
|
||||
* Waiting for one of these ensures the component is interactive.
|
||||
*/
|
||||
async function waitForGuardianSection(page: import('@playwright/test').Page) {
|
||||
await expect(page.locator('.guardian-section')).toBeVisible({ timeout: 10000 });
|
||||
await expect(page.locator('.guardian-section')).toBeVisible({ timeout: 15000 });
|
||||
await expect(
|
||||
page.getByText(/aucun parent\/tuteur lié/i)
|
||||
.or(page.locator('.guardian-list'))
|
||||
).toBeVisible({ timeout: 10000 });
|
||||
).toBeVisible({ timeout: 15000 });
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -151,8 +151,20 @@ test.describe('Student Management', () => {
|
||||
|
||||
await waitForGuardianSection(page);
|
||||
|
||||
// Clean up guardians if any exist (cross-browser interference: parallel
|
||||
// browser projects share the same DB, so another browser may have added
|
||||
// guardians between our beforeAll cleanup and this test).
|
||||
while (await page.locator('.guardian-item').count() > 0) {
|
||||
const item = page.locator('.guardian-item').first();
|
||||
await item.getByRole('button', { name: /retirer/i }).click();
|
||||
await expect(item.getByText(/confirmer/i)).toBeVisible({ timeout: 5000 });
|
||||
await item.getByRole('button', { name: /oui/i }).click();
|
||||
await expect(page.locator('.alert-success')).toBeVisible({ timeout: 10000 });
|
||||
await page.waitForTimeout(500);
|
||||
}
|
||||
|
||||
// Should show the empty state
|
||||
await expect(page.getByText(/aucun parent\/tuteur lié/i)).toBeVisible();
|
||||
await expect(page.getByText(/aucun parent\/tuteur lié/i)).toBeVisible({ timeout: 15000 });
|
||||
|
||||
// The "add guardian" button should be visible
|
||||
await expect(page.getByRole('button', { name: /ajouter un parent/i })).toBeVisible();
|
||||
@@ -342,6 +354,12 @@ test.describe('Student Management', () => {
|
||||
page.locator('.users-table, .empty-state')
|
||||
).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Search for the student user (pagination may hide them beyond page 1)
|
||||
const searchInput = page.locator('input[type="search"]');
|
||||
await searchInput.fill(STUDENT_EMAIL);
|
||||
await page.waitForTimeout(500);
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// The student email should appear in the users table
|
||||
await expect(page.locator('.users-table')).toBeVisible({ timeout: 10000 });
|
||||
const studentRow = page.locator('tr', { has: page.locator(`text=${STUDENT_EMAIL}`) });
|
||||
@@ -355,6 +373,12 @@ test.describe('Student Management', () => {
|
||||
// Wait for users table
|
||||
await expect(page.locator('.users-table')).toBeVisible({ timeout: 10000 });
|
||||
|
||||
// Search for the student user (pagination may hide them beyond page 1)
|
||||
const searchInput = page.locator('input[type="search"]');
|
||||
await searchInput.fill(STUDENT_EMAIL);
|
||||
await page.waitForTimeout(500);
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Find the student row and verify role
|
||||
const studentRow = page.locator('tr', { has: page.locator(`text=${STUDENT_EMAIL}`) });
|
||||
await expect(studentRow).toContainText(/élève/i);
|
||||
|
||||
Reference in New Issue
Block a user