feat: vue détail d'un site (#51)

Ajoute la page de détail d'un site (fiche, mesure instantanée, jauge de
consommation, historique) en remplacement du placeholder. Les champs
null sont affichés explicitement avec leur raison plutôt que masqués.

Ajoute l'endpoint GET /sites/{id}/current côté backend, qui retourne la
dernière mesure connue d'un site sans filtre temporel, conformément au
contrat de l'issue.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012i5NteMLRZgfTAKB5GD37X
This commit is contained in:
Johan LEROY
2026-09-17 16:43:15 +02:00
co-authored by Claude Sonnet 5
parent 56c134beb0
commit 5d921a9b1e
25 changed files with 1067 additions and 114 deletions
@@ -28,6 +28,11 @@ class FakeRepository:
def __init__(self, readings: list[Reading]) -> None:
self._readings = readings
self.appels: list[tuple[str | None, datetime, datetime, int, int]] = []
self.site_interroge: str | None = None
async def latest_for_site(self, site_id: str) -> Reading | None:
self.site_interroge = site_id
return self._readings[0] if self._readings else None
async def list_history(
self,
@@ -42,6 +47,25 @@ class FakeRepository:
return self._readings
async def test_get_latest_relays_the_repository_reading() -> None:
depot = FakeRepository([reading(1)])
service = ReadingService(readings=depot)
lecture = await service.get_latest("site-1")
assert lecture is not None
assert lecture.reading_id == 1
assert depot.site_interroge == "site-1"
async def test_get_latest_returns_none_when_the_site_has_no_reading() -> None:
service = ReadingService(readings=FakeRepository([]))
lecture = await service.get_latest("site-1")
assert lecture is None
async def test_list_history_returns_the_repository_readings() -> None:
service = ReadingService(readings=FakeRepository([reading(1), reading(2)]))