fix(review) : resout findings 3e passe review (HIGH frontend + MEDIUMs backend/frontend/E2E)
Backend :
- AuditLogWriter::stripSensitive rendu reellement recursif (matche doc).
- Tests GET /api/permissions/{id} non-admin pour chaque branche OR (gap Codex).
- Gardes non-regression UserRbacProcessor : PATCH /rbac sans clef sites ne
doit ni auto-selectionner currentSite ni exiger sites.manage.
Frontend :
- useAuditLog : renomme export trompeur fetchLogs -> fetchLogsCached, le
nom reflete desormais le comportement (cache pollue sinon).
- RoleDrawer / UserRbacDrawer : catch explicite + message d'erreur +
bouton save disabled si le chargement des referentiels a echoue (evite
un ecrasement silencieux des droits).
- AuditTimeline / AuditLogDetail : `oui`/`non` passent par common.yes/no.
- AuditTimeline : Intl.RelativeTimeFormat et toLocaleString suivent la
locale i18n courante (plus de hardcode 'fr').
E2E :
- sidebar-visibility.spec : remplace waitForLoadState('networkidle')
fragile par attente semantique sur accountDashboardLink (stable en CI).
Tests : 237/237 green, eslint clean, php-cs-fixer clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -91,7 +91,9 @@ const collectionDiff = computed<Record<string, { added: unknown[]; removed: unkn
|
||||
|
||||
function formatValue(value: unknown): string {
|
||||
if (value === null || value === undefined) return '∅'
|
||||
if (typeof value === 'boolean') return value ? 'oui' : 'non'
|
||||
// Passe par i18n plutot qu'un hardcode FR : si une autre locale est
|
||||
// ajoutee, le rendu s'adapte sans nouvelle passe sur ce composant.
|
||||
if (typeof value === 'boolean') return value ? t('common.yes') : t('common.no')
|
||||
if (typeof value === 'object') return JSON.stringify(value)
|
||||
return String(value)
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ const props = defineProps<{
|
||||
|
||||
const { entityType, entityId } = toRefs(props)
|
||||
|
||||
const { t } = useI18n()
|
||||
const { t, locale } = useI18n()
|
||||
const { can } = usePermissions()
|
||||
const { fetchEntityLogs } = useAuditLog()
|
||||
|
||||
@@ -162,24 +162,28 @@ function dotClass(action: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
// Relativise une date en francais via Intl.RelativeTimeFormat. On selectionne
|
||||
// l'unite la plus grossiere possible (minutes < heures < jours < semaines).
|
||||
const rtf = new Intl.RelativeTimeFormat('fr', { numeric: 'auto' })
|
||||
// Relativise une date via Intl.RelativeTimeFormat. On selectionne l'unite
|
||||
// la plus grossiere possible (minutes < heures < jours < semaines). La
|
||||
// locale suit dynamiquement celle de l'app pour qu'un switch de langue
|
||||
// prenne effet sans nouveau mount (recomputed = cache par-locale).
|
||||
const rtf = computed(() => new Intl.RelativeTimeFormat(locale.value, { numeric: 'auto' }))
|
||||
|
||||
function relativeDate(iso: string): string {
|
||||
const diffMs = Date.now() - new Date(iso).getTime()
|
||||
const diffSec = Math.round(diffMs / 1000)
|
||||
const absSec = Math.abs(diffSec)
|
||||
const fmt = rtf.value
|
||||
|
||||
if (absSec < 60) return rtf.format(-Math.sign(diffSec) * Math.abs(diffSec), 'second')
|
||||
if (absSec < 3600) return rtf.format(-Math.sign(diffSec) * Math.round(absSec / 60), 'minute')
|
||||
if (absSec < 86400) return rtf.format(-Math.sign(diffSec) * Math.round(absSec / 3600), 'hour')
|
||||
if (absSec < 604800) return rtf.format(-Math.sign(diffSec) * Math.round(absSec / 86400), 'day')
|
||||
return rtf.format(-Math.sign(diffSec) * Math.round(absSec / 604800), 'week')
|
||||
if (absSec < 60) return fmt.format(-Math.sign(diffSec) * Math.abs(diffSec), 'second')
|
||||
if (absSec < 3600) return fmt.format(-Math.sign(diffSec) * Math.round(absSec / 60), 'minute')
|
||||
if (absSec < 86400) return fmt.format(-Math.sign(diffSec) * Math.round(absSec / 3600), 'hour')
|
||||
if (absSec < 604800) return fmt.format(-Math.sign(diffSec) * Math.round(absSec / 86400), 'day')
|
||||
return fmt.format(-Math.sign(diffSec) * Math.round(absSec / 604800), 'week')
|
||||
}
|
||||
|
||||
function absoluteDate(iso: string): string {
|
||||
return new Date(iso).toLocaleString('fr-FR', {
|
||||
// Meme logique : la locale de formatage suit celle de l'app.
|
||||
return new Date(iso).toLocaleString(locale.value, {
|
||||
dateStyle: 'medium',
|
||||
timeStyle: 'short',
|
||||
})
|
||||
@@ -223,7 +227,9 @@ function snapshotSummary(entry: AuditLogEntry): string {
|
||||
|
||||
function formatValue(value: unknown): string {
|
||||
if (value === null || value === undefined) return '∅'
|
||||
if (typeof value === 'boolean') return value ? 'oui' : 'non'
|
||||
// Passe par i18n plutot qu'un hardcode FR : si une autre locale est
|
||||
// ajoutee, le rendu s'adapte sans nouvelle passe sur ce composant.
|
||||
if (typeof value === 'boolean') return value ? t('common.yes') : t('common.no')
|
||||
if (typeof value === 'object') return JSON.stringify(value)
|
||||
return String(value)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user