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();
|
closeEditModal();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e instanceof Error ? e.message : 'Erreur inconnue';
|
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;
|
showPedagogicalModal = false;
|
||||||
pedDate = '';
|
pedDate = '';
|
||||||
pedLabel = '';
|
pedLabel = '';
|
||||||
|
|||||||
@@ -330,9 +330,15 @@
|
|||||||
throw new Error(message);
|
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';
|
successMessage = 'Remplacement terminé avec succès';
|
||||||
closeEndModal();
|
closeEndModal();
|
||||||
await reloadReplacements();
|
|
||||||
globalThis.setTimeout(() => {
|
globalThis.setTimeout(() => {
|
||||||
successMessage = null;
|
successMessage = null;
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|||||||
@@ -246,8 +246,17 @@
|
|||||||
throw new Error(errorMessage);
|
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.';
|
successMessage = 'L\'invitation a été renvoyée avec succès.';
|
||||||
await loadUsers();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e instanceof Error ? e.message : 'Erreur lors du renvoi';
|
error = e instanceof Error ? e.message : 'Erreur lors du renvoi';
|
||||||
} finally {
|
} finally {
|
||||||
@@ -361,9 +370,19 @@
|
|||||||
throw new Error(errorMessage);
|
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é.`;
|
successMessage = `L'utilisateur ${blockTargetUser.firstName} ${blockTargetUser.lastName} a été bloqué.`;
|
||||||
closeBlockModal();
|
closeBlockModal();
|
||||||
await loadUsers();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e instanceof Error ? e.message : 'Erreur lors du blocage';
|
error = e instanceof Error ? e.message : 'Erreur lors du blocage';
|
||||||
} finally {
|
} finally {
|
||||||
@@ -401,8 +420,18 @@
|
|||||||
throw new Error(errorMessage);
|
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é.`;
|
successMessage = `L'utilisateur ${user.firstName} ${user.lastName} a été débloqué.`;
|
||||||
await loadUsers();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e instanceof Error ? e.message : 'Erreur lors du déblocage';
|
error = e instanceof Error ? e.message : 'Erreur lors du déblocage';
|
||||||
} finally {
|
} finally {
|
||||||
@@ -440,9 +469,12 @@
|
|||||||
isSavingRoles = true;
|
isSavingRoles = true;
|
||||||
error = null;
|
error = null;
|
||||||
await updateUserRoles(rolesTargetUser.id, selectedRoles);
|
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.`;
|
successMessage = `Les rôles de ${rolesTargetUser.firstName} ${rolesTargetUser.lastName} ont été mis à jour.`;
|
||||||
closeRolesModal();
|
closeRolesModal();
|
||||||
await loadUsers();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e instanceof Error ? e.message : 'Erreur lors de la mise à jour des rôles';
|
error = e instanceof Error ? e.message : 'Erreur lors de la mise à jour des rôles';
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Reference in New Issue
Block a user