feat: Appliquer le pattern optimistic update sur les pages admin
Chaque action inline (block, unblock, renvoi d'invitation, modification de période, fin de remplacement, ajout de journée pédagogique) déclenchait un rechargement complet de la liste via loadXxx(). Cela causait un reset du scroll, un temps d'attente visible et des requêtes réseau inutiles. Le pattern de mise à jour locale introduit dans la story Droit à l'image est généralisé aux 4 pages admin restantes, en extrayant explicitement les champs utiles de la réponse API pour éviter la pollution JSON-LD.
This commit is contained in:
@@ -189,7 +189,29 @@
|
||||
);
|
||||
}
|
||||
|
||||
await loadPeriods();
|
||||
const updated = await response.json();
|
||||
if (config) {
|
||||
const updatedPeriod: Period = {
|
||||
sequence: editingPeriod!.sequence,
|
||||
label: updated.label ?? editingPeriod!.label,
|
||||
startDate: updated.startDate ?? editStartDate,
|
||||
endDate: updated.endDate ?? editEndDate,
|
||||
isCurrent: updated.isCurrent ?? editingPeriod!.isCurrent,
|
||||
daysRemaining: updated.daysRemaining ?? editingPeriod!.daysRemaining,
|
||||
isPast: updated.isPast ?? editingPeriod!.isPast
|
||||
};
|
||||
config = {
|
||||
...config,
|
||||
periods: config.periods.map((p) =>
|
||||
p.sequence === editingPeriod!.sequence ? updatedPeriod : p
|
||||
),
|
||||
currentPeriod: updatedPeriod.isCurrent
|
||||
? updatedPeriod
|
||||
: config.currentPeriod?.sequence === editingPeriod!.sequence
|
||||
? null
|
||||
: config.currentPeriod
|
||||
};
|
||||
}
|
||||
closeEditModal();
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : 'Erreur inconnue';
|
||||
|
||||
@@ -190,7 +190,27 @@
|
||||
);
|
||||
}
|
||||
|
||||
await loadCalendar();
|
||||
const data = await response.json();
|
||||
const newEntry: CalendarEntry = {
|
||||
id: data.id ?? data['@id'] ?? '',
|
||||
type: data.type ?? 'pedagogical',
|
||||
startDate: data.startDate ?? data.date ?? pedDate,
|
||||
endDate: data.endDate ?? data.date ?? pedDate,
|
||||
label: data.label ?? pedLabel,
|
||||
description: data.description ?? null
|
||||
};
|
||||
if (calendar) {
|
||||
const entries = [...calendar.entries, newEntry].sort((a, b) =>
|
||||
a.startDate.localeCompare(b.startDate)
|
||||
);
|
||||
calendar = { ...calendar, entries };
|
||||
} else {
|
||||
calendar = {
|
||||
academicYearId,
|
||||
zone: null,
|
||||
entries: [newEntry]
|
||||
};
|
||||
}
|
||||
showPedagogicalModal = false;
|
||||
pedDate = '';
|
||||
pedLabel = '';
|
||||
|
||||
@@ -330,9 +330,15 @@
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
replacements = replacements.filter((r) => r.id !== replacementToEnd!.id);
|
||||
totalItems = Math.max(0, totalItems - 1);
|
||||
if (replacements.length === 0 && currentPage > 1) {
|
||||
currentPage -= 1;
|
||||
updateUrl();
|
||||
reloadReplacements();
|
||||
}
|
||||
successMessage = 'Remplacement terminé avec succès';
|
||||
closeEndModal();
|
||||
await reloadReplacements();
|
||||
globalThis.setTimeout(() => {
|
||||
successMessage = null;
|
||||
}, 3000);
|
||||
|
||||
@@ -246,8 +246,17 @@
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
const updated = await response.json().catch(() => null);
|
||||
users = users.map((u) =>
|
||||
u.id === userId
|
||||
? {
|
||||
...u,
|
||||
invitedAt: updated?.invitedAt ?? new Date().toISOString(),
|
||||
invitationExpiree: false
|
||||
}
|
||||
: u
|
||||
);
|
||||
successMessage = 'L\'invitation a été renvoyée avec succès.';
|
||||
await loadUsers();
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : 'Erreur lors du renvoi';
|
||||
} finally {
|
||||
@@ -361,9 +370,19 @@
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
const updated = await response.json();
|
||||
users = users.map((u) =>
|
||||
u.id === blockTargetUser!.id
|
||||
? {
|
||||
...u,
|
||||
statut: updated.statut ?? 'suspended',
|
||||
blockedAt: updated.blockedAt ?? new Date().toISOString(),
|
||||
blockedReason: updated.blockedReason ?? blockReason.trim()
|
||||
}
|
||||
: u
|
||||
);
|
||||
successMessage = `L'utilisateur ${blockTargetUser.firstName} ${blockTargetUser.lastName} a été bloqué.`;
|
||||
closeBlockModal();
|
||||
await loadUsers();
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : 'Erreur lors du blocage';
|
||||
} finally {
|
||||
@@ -401,8 +420,18 @@
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
const updated = await response.json();
|
||||
users = users.map((u) =>
|
||||
u.id === user.id
|
||||
? {
|
||||
...u,
|
||||
statut: updated.statut ?? 'active',
|
||||
blockedAt: null,
|
||||
blockedReason: null
|
||||
}
|
||||
: u
|
||||
);
|
||||
successMessage = `L'utilisateur ${user.firstName} ${user.lastName} a été débloqué.`;
|
||||
await loadUsers();
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : 'Erreur lors du déblocage';
|
||||
} finally {
|
||||
@@ -440,9 +469,12 @@
|
||||
isSavingRoles = true;
|
||||
error = null;
|
||||
await updateUserRoles(rolesTargetUser.id, selectedRoles);
|
||||
const updatedRoles = [...selectedRoles];
|
||||
users = users.map((u) =>
|
||||
u.id === rolesTargetUser!.id ? { ...u, roles: updatedRoles } : u
|
||||
);
|
||||
successMessage = `Les rôles de ${rolesTargetUser.firstName} ${rolesTargetUser.lastName} ont été mis à jour.`;
|
||||
closeRolesModal();
|
||||
await loadUsers();
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : 'Erreur lors de la mise à jour des rôles';
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user