Corrige la grille des besoins : postes manquants au-delà du premier
Chaque cellule de la grille (label de poste, en-têtes, créneaux, cases vides) recevait sa position via l'ordre d'émission des enfants, en comptant sur le fait que chaque ligne totalise exactement le nombre de tranches de 30 min d'un jour pour que l'auto-flow CSS reste aligné. Un poste dont les créneaux ne tombaient pas exactement sur ce total (deux créneaux adjacents mais non alignés sur 30 min, courant depuis la création de créneaux à la main) décalait tous les postes suivants — leur nom de poste n'apparaissait plus sur la bonne ligne. Chaque cellule reçoit maintenant sa position de grille explicitement (gridColumn/gridRow calculés), immunisé contre ce genre de décalage : l'anomalie d'un poste reste locale à sa propre ligne. Nouveau test de non-régression (scripts/smoke.tsx:checkGridLayout) : inspecte les gridRow réellement rendus plutôt que la simple absence de crash — un décalage CSS n'est pas détectable par un rendu SSR ordinaire. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
5f80f167c8
commit
8b9c0ac498
@@ -228,6 +228,22 @@ async function checkShiftCreation() {
|
||||
});
|
||||
const generated = await api.positions.generateShifts(e, position.id);
|
||||
|
||||
// Deuxième créneau adjacent au premier mais non aligné sur 30 min (fin à
|
||||
// 22:45, comme le premier) : arrondis à la tranche englobante, les deux
|
||||
// créneaux se chevauchent légèrement dans la grille (voir GrillePage.tsx,
|
||||
// `PositionRow`) — ne doit jamais planter le rendu ni décaler les postes
|
||||
// suivants (régression corrigée : la grille positionnait ses cellules par
|
||||
// ordre d'émission plutôt qu'explicitement, un poste dont les créneaux ne
|
||||
// totalisaient pas exactement le nombre de tranches d'un jour décalait
|
||||
// tous les postes suivants).
|
||||
await api.shifts.create(e, {
|
||||
position_id: position.id,
|
||||
starts_at: `${edition.start_date}T22:45:00`,
|
||||
ends_at: `${edition.start_date}T23:15:00`,
|
||||
min_staff: 1,
|
||||
ideal_staff: 1,
|
||||
});
|
||||
|
||||
const grid = buildTimeGrid([shift]);
|
||||
const day = grid[0];
|
||||
const placement = day ? placeInTimeGrid(grid, [shift]).get(day.date)?.[0] : undefined;
|
||||
@@ -260,6 +276,56 @@ async function checkShiftCreation() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Grille des besoins : chaque poste affiché doit occuper sa propre ligne de
|
||||
* grille CSS (`gridRow` explicite, voir `GrillePage.tsx:PositionRow`).
|
||||
* Régression testée : avant le passage à un positionnement explicite, un
|
||||
* poste dont les créneaux ne totalisaient pas exactement le nombre de
|
||||
* tranches de 30 min d'un jour (deux créneaux adjacents mais non alignés
|
||||
* sur 30 min, par ex. — cas courant depuis la création de créneaux à la
|
||||
* main) décalait, par un pur effet de bord CSS (auto-flow), tous les postes
|
||||
* suivants — leur nom de poste n'apparaissait alors plus sur sa propre
|
||||
* ligne. Purement visuel : un rendu SSR ordinaire ne l'aurait pas détecté,
|
||||
* d'où cette vérification directe des lignes de grille attribuées.
|
||||
*/
|
||||
async function checkGridLayout() {
|
||||
if (!coordinator) throw new Error('grille : pas de session coordination pour ce test');
|
||||
const positions = await api.positions.list(e);
|
||||
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
await prefetchCore(client);
|
||||
const html = renderToString(
|
||||
createElement(
|
||||
QueryClientProvider,
|
||||
{ client },
|
||||
createElement(
|
||||
AuthContext.Provider,
|
||||
{ value: authValue(coordinator) },
|
||||
createElement(MemoryRouter, null, createElement(GrillePage)),
|
||||
),
|
||||
),
|
||||
);
|
||||
const rowNumbers = [...html.matchAll(/grid-row:\s*(\d+)/g)].map((m) => Number(m[1]));
|
||||
const uniqueRows = [...new Set(rowNumbers)].sort((a, b) => a - b);
|
||||
// 2 lignes d'en-tête (jours, puis tranches) + une par poste affiché,
|
||||
// strictement consécutives (1, 2, 3, … sans trou ni doublon).
|
||||
const expected = Array.from({ length: positions.length + 2 }, (_, i) => i + 1);
|
||||
const sequential = uniqueRows.length === expected.length
|
||||
&& uniqueRows.every((row, i) => row === expected[i]);
|
||||
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
{ positionsCount: positions.length, gridRowsUsed: uniqueRows.length, sequential },
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
if (!sequential) {
|
||||
throw new Error(
|
||||
`grille des besoins : lignes de grille ${JSON.stringify(uniqueRows)}, attendu ${JSON.stringify(expected)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function prefetchCore(client: QueryClient) {
|
||||
await Promise.all([
|
||||
client.prefetchQuery({ queryKey: queryKeys.volunteers, queryFn: () => api.volunteers.list(e) }),
|
||||
@@ -330,6 +396,7 @@ await checkAuth().catch((err) => fail('checkAuth', err));
|
||||
await checkPublicForm().catch((err) => fail('checkPublicForm', err));
|
||||
await checkPositionSheets().catch((err) => fail('checkPositionSheets', err));
|
||||
await checkShiftCreation().catch((err) => fail('checkShiftCreation', err));
|
||||
await checkGridLayout().catch((err) => fail('checkGridLayout', err));
|
||||
|
||||
renderPublic('/connexion');
|
||||
renderPublic('/connexion/coordination');
|
||||
|
||||
Reference in New Issue
Block a user