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:
@@ -24,10 +24,10 @@ export const routes: Routes = [
|
|||||||
import('./features/sites/site-detail/site-detail').then((m) => m.SiteDetail),
|
import('./features/sites/site-detail/site-detail').then((m) => m.SiteDetail),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'monitoring/sensors',
|
path: 'monitoring/sensors',
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
data: { role: 'admin' },
|
data: { role: 'admin' },
|
||||||
loadComponent: () =>
|
loadComponent: () =>
|
||||||
import('./features/monitoring/sensor-status/sensor-status').then((m) => m.SensorStatusView),
|
import('./features/monitoring/sensor-status/sensor-status').then((m) => m.SensorStatusView),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<div class="dashboard__actions">
|
<div class="dashboard__actions">
|
||||||
@if (auth.principal()?.role === 'admin') {
|
@if (auth.principal()?.role === 'admin') {
|
||||||
<a routerLink="/monitoring/sensors" class="ev-link">Supervision des capteurs</a>
|
<a routerLink="/monitoring/sensors" class="ev-link">Supervision des capteurs</a>
|
||||||
}
|
}
|
||||||
<a routerLink="/sites" class="ev-link">Voir les sites</a>
|
<a routerLink="/sites" class="ev-link">Voir les sites</a>
|
||||||
<ev-button
|
<ev-button
|
||||||
class="logout-button"
|
class="logout-button"
|
||||||
|
|||||||
@@ -28,15 +28,17 @@
|
|||||||
|
|
||||||
<ul class="sensor-list">
|
<ul class="sensor-list">
|
||||||
@for (entry of sensorEntries; track entry[0]) {
|
@for (entry of sensorEntries; track entry[0]) {
|
||||||
|
@let diagnostic = sensorOf(site.sensors, entry[0]);
|
||||||
<li class="sensor-item">
|
<li class="sensor-item">
|
||||||
<span
|
<span class="sensor-dot" [class]="'sensor-dot--' + diagnostic.status"></span>
|
||||||
class="sensor-dot"
|
|
||||||
[class]="'sensor-dot--' + sensorOf(site.sensors, entry[0]).status"
|
|
||||||
></span>
|
|
||||||
<span class="sensor-item__label">{{ entry[1] }}</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">
|
<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>
|
</span>
|
||||||
}
|
}
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -79,21 +79,44 @@ describe('SensorStatusView', () => {
|
|||||||
expect(component.sensorOf(OK_SENSORS, 'temperature')).toEqual({ status: 'ok', since: null });
|
expect(component.sensorOf(OK_SENSORS, 'temperature')).toEqual({ status: 'ok', since: null });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('affiche la date depuis quand un capteur est en panne', () => {
|
it('affiche la date de la dernière lecture reçue pour un capteur en panne', () => {
|
||||||
const sensors: SiteSensors = {
|
const sensors: SiteSensors = {
|
||||||
...OK_SENSORS,
|
...OK_SENSORS,
|
||||||
temperature: { status: 'failing', since: '2026-09-18T08:00:00' },
|
temperature: { status: 'failing', since: '2026-09-18T08:00:00' },
|
||||||
};
|
};
|
||||||
sensorsMock.getStatus.mockReturnValue(
|
sensorsMock.getStatus.mockReturnValue(
|
||||||
of({
|
of({
|
||||||
timestamp: '2026-09-18T08:00:00',
|
timestamp: '2026-09-18T08:00:00',
|
||||||
sites: [{ site_id: 'SITE001', site_name: 'Bureau Test', overall: 'degraded', sensors }],
|
sites: [{ site_id: 'SITE001', site_name: 'Bureau Test', overall: 'degraded', sensors }],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const fixture = TestBed.createComponent(SensorStatusView);
|
const fixture = TestBed.createComponent(SensorStatusView);
|
||||||
fixture.detectChanges();
|
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');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user