fix(audit) : eviter les echecs silencieux du journal d'audit
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 1m38s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 10m14s

- log + toast d'erreur dans le catch de loadEntries : evite qu'une RangeError
  de toIso (date invalide) ou une 500 API laisse l'utilisateur devant une
  table vide indistinguable d'un filtre a zero resultat
- onMounted : Promise.all pour charger entity types et liste en parallele
  (TTFD divise par 2 sur backend lent), comportement promis par le commentaire
- filtre dates : :max sur Du et :min sur Au empechent une plage inversee a la
  saisie (le backend renvoyait 0 ligne sans diag)
- i18n : audit.error.title / message
This commit is contained in:
2026-05-28 14:16:53 +02:00
parent 5c4bc32827
commit e611f4c65a
2 changed files with 35 additions and 16 deletions
+4
View File
@@ -85,6 +85,10 @@
},
"empty": "Aucune activité enregistrée",
"no_results": "Aucun résultat pour ces filtres",
"error": {
"title": "Erreur",
"message": "Impossible de charger le journal d'audit. Vérifiez les filtres ou réessayez."
},
"timeline": {
"empty": "Aucun historique",
"load_more": "Voir plus"
+31 -16
View File
@@ -68,9 +68,17 @@
<MalioAccordionItem :title="t('audit.filters.date_range')" value="dates">
<div class="grid grid-cols-[auto_1fr] items-center gap-x-3 gap-y-4">
<span>{{ t('audit.filters.date_from') }}</span>
<MalioDateTime v-model="draftDateFrom" />
<!-- Borne le picker "Du" par la valeur "Au" pour interdire une plage
inversee a la saisie (le backend renverrait silencieusement 0 ligne). -->
<MalioDateTime
v-model="draftDateFrom"
:max="draftDateTo ?? undefined"
/>
<span>{{ t('audit.filters.date_to') }}</span>
<MalioDateTime v-model="draftDateTo" />
<MalioDateTime
v-model="draftDateTo"
:min="draftDateFrom ?? undefined"
/>
</div>
</MalioAccordionItem>
@@ -161,6 +169,7 @@ import type { AuditLogEntry, AuditLogFilters } from '~/shared/types'
const { t, te } = useI18n()
const { can } = usePermissions()
const { fetchLogsCached, fetchEntityTypes } = useAuditLog()
const toast = useToast()
// Traduit un identifiant `module.Entity` (ex: `core.User`, `sites.Site`) en
// libelle lisible via la cle i18n `audit.entity.<module>_<entity>`. Si aucune
@@ -333,13 +342,19 @@ async function loadEntries(): Promise<void> {
if (token !== requestToken) return
entries.value = data.member ?? []
totalItems.value = data.totalItems ?? 0
} catch {
// En cas d'echec (reseau, 403, 500...), on reset l'etat pour ne pas
// laisser l'utilisateur croire que les donnees affichees sont a jour.
// Le toast d'erreur est deja emis par `useApi()` via useAuditLog.
} catch (err) {
// useAuditLog appelle useApi avec { toast: false } pour ne pas multiplier
// les toasts, donc c'est ici qu'on fait remonter l'erreur. Sans ce log+toast,
// une RangeError de `toIso` (date invalide) ou une 500 API laissait l'utilisateur
// devant une table vide indistinguable d'un filtre a zero resultat.
if (token === requestToken) {
entries.value = []
totalItems.value = 0
console.error('[audit-log] loadEntries failed', err)
toast.error({
title: t('audit.error.title'),
message: t('audit.error.message'),
})
}
} finally {
if (token === requestToken) {
@@ -397,15 +412,15 @@ function onPerPageChange(value: number): void {
}
onMounted(async () => {
// Charge les entity types en parallele de la liste principale : un
// echec du premier endpoint (ex: reseau flaky) ne doit pas empecher
// le tableau d'audit de s'afficher. En cas d'erreur, on laisse le
// filtre vide — l'utilisateur pourra quand meme consulter le journal.
try {
entityTypes.value = await fetchEntityTypes()
} catch {
entityTypes.value = []
}
await loadEntries()
// Charge les entity types ET la liste principale en parallele (TTFD divise
// par 2 sur un backend lent). Le `.catch` du premier garantit qu'un echec
// de /audit-log-entity-types ne bloque pas l'affichage du tableau —
// l'utilisateur perd juste le filtre, pas la page entiere.
await Promise.all([
fetchEntityTypes()
.then(types => { entityTypes.value = types })
.catch(() => { entityTypes.value = [] }),
loadEntries(),
])
})
</script>