fix(frontend): affiche since comme la dernière lecture reçue, pas comme un début de panne

Le schéma backend dit que since est l'horodatage de la dernière lecture du
site, identique pour tous ses capteurs en panne et sans rapport avec le début
de la panne. Le template annonçait « depuis <date> », ce que l'exploitant lit
comme une date de début de panne.

Un site sans aucune lecture renvoie ses cinq capteurs en échec avec since à
null : le template affichait « depuis » suivi d'une chaîne vide. Ce cas dit
maintenant « aucune lecture reçue ».

Format de date explicite plutôt que 'short' : aucune locale n'est enregistrée
dans app.config.ts, donc 'short' rendait la date au format en-US.
This commit is contained in:
Johan LEROY
2026-09-21 11:38:52 +02:00
parent f9c2a4610c
commit 777cd0ac64
4 changed files with 52 additions and 27 deletions
+6 -6
View File
@@ -24,10 +24,10 @@ export const routes: Routes = [
import('./features/sites/site-detail/site-detail').then((m) => m.SiteDetail),
},
{
path: 'monitoring/sensors',
canActivate: [authGuard],
data: { role: 'admin' },
loadComponent: () =>
import('./features/monitoring/sensor-status/sensor-status').then((m) => m.SensorStatusView),
},
path: 'monitoring/sensors',
canActivate: [authGuard],
data: { role: 'admin' },
loadComponent: () =>
import('./features/monitoring/sensor-status/sensor-status').then((m) => m.SensorStatusView),
},
];
@@ -12,7 +12,7 @@
<div class="dashboard__actions">
@if (auth.principal()?.role === 'admin') {
<a routerLink="/monitoring/sensors" class="ev-link">Supervision des capteurs</a>
}
}
<a routerLink="/sites" class="ev-link">Voir les sites</a>
<ev-button
class="logout-button"
@@ -28,15 +28,17 @@
<ul class="sensor-list">
@for (entry of sensorEntries; track entry[0]) {
@let diagnostic = sensorOf(site.sensors, entry[0]);
<li class="sensor-item">
<span
class="sensor-dot"
[class]="'sensor-dot--' + sensorOf(site.sensors, entry[0]).status"
></span>
<span class="sensor-dot" [class]="'sensor-dot--' + diagnostic.status"></span>
<span class="sensor-item__label">{{ entry[1] }}</span>
@if (sensorOf(site.sensors, entry[0]).status === 'failing') {
@if (diagnostic.status === 'failing') {
<span class="sensor-item__since">
depuis {{ sensorOf(site.sensors, entry[0]).since | date: 'short' }}
@if (diagnostic.since; as since) {
dernière lecture le {{ since | date: 'dd/MM/yyyy HH:mm' }}
} @else {
aucune lecture reçue
}
</span>
}
</li>
@@ -79,21 +79,44 @@ describe('SensorStatusView', () => {
expect(component.sensorOf(OK_SENSORS, 'temperature')).toEqual({ status: 'ok', since: null });
});
it('affiche la date depuis quand un capteur est en panne', () => {
const sensors: SiteSensors = {
...OK_SENSORS,
temperature: { status: 'failing', since: '2026-09-18T08:00:00' },
};
sensorsMock.getStatus.mockReturnValue(
of({
timestamp: '2026-09-18T08:00:00',
sites: [{ site_id: 'SITE001', site_name: 'Bureau Test', overall: 'degraded', sensors }],
})
);
it('affiche la date de la dernière lecture reçue pour un capteur en panne', () => {
const sensors: SiteSensors = {
...OK_SENSORS,
temperature: { status: 'failing', since: '2026-09-18T08:00:00' },
};
sensorsMock.getStatus.mockReturnValue(
of({
timestamp: '2026-09-18T08:00:00',
sites: [{ site_id: 'SITE001', site_name: 'Bureau Test', overall: 'degraded', sensors }],
})
);
const fixture = TestBed.createComponent(SensorStatusView);
fixture.detectChanges();
const fixture = TestBed.createComponent(SensorStatusView);
fixture.detectChanges();
expect(fixture.nativeElement.textContent).toContain('depuis');
expect(fixture.nativeElement.textContent).toContain('dernière lecture le');
expect(fixture.nativeElement.textContent).toContain('18/09/2026 08:00');
});
it("annonce l'absence de lecture quand un site n'en a jamais reçu", () => {
const sensors: SiteSensors = {
consumption: { status: 'failing', since: null },
electrical: { status: 'failing', since: null },
temperature: { status: 'failing', since: null },
humidity: { status: 'failing', since: null },
network: { status: 'failing', since: null },
};
sensorsMock.getStatus.mockReturnValue(
of({
timestamp: '2026-09-18T08:00:00',
sites: [{ site_id: 'SITE001', site_name: 'Bureau Test', overall: 'critical', sensors }],
})
);
const fixture = TestBed.createComponent(SensorStatusView);
fixture.detectChanges();
expect(fixture.nativeElement.textContent).toContain('aucune lecture reçue');
expect(fixture.nativeElement.textContent).not.toContain('dernière lecture le');
});
});