M2: frontend walkthrough fixes + explicit dev compose stack
frontend / frontend (push) Successful in 2m0s
pytest / test (push) Successful in 1m32s

Post-M2 self-walkthrough polish, batched into one commit.

Map / heat:
- fix heat-layer white-screen crash after login (add layer to map before
  setLatLngs; an off-map leaflet.heat layer has a null _map and throws)
- normalize each heat layer to the densest pixel cell visible in the CURRENT
  viewport (maxZoom:0 so intensity factor f=1) and recompute on moveend/zoomend,
  so sparse poo data reaches red and stays normalized at any zoom level
- dark CARTO basemap tiles when the color scheme is dark

UI:
- dark-mode toggle in the top-right, beside the settings gear
- switch top-right nav (records / theme / settings / logout) to Feather icons
  with hover tooltips
- home: Grafana-style quick time-range presets + back/forward shift buttons,
  placed between the From/To pickers and Apply; fix Select/tooltip z-index
  (Leaflet stacking) and the shift-button height alignment

API client:
- stop flooding GET /api/session with 401s: the session probe and the login
  endpoint own their 401s (no global redirect), which fixes the logout hang and
  the spinning login page

Compose:
- rename docker-compose.override.yml -> docker-compose.dev.yml as an explicit,
  non-auto-layered dev stack (8001, -dev container names, prod-copy ./data DB);
  update tests/test_deployment.py (read dev.yml, tolerate the !override tag) and
  the README "Docker Compose" section

Tests:
- pixel-grid peak counter, time-range presets, heat-layer ordering regression,
  and 401-redirect regression
This commit is contained in:
2026-06-13 15:20:35 +02:00
parent bd09523e94
commit da236643f2
16 changed files with 722 additions and 66 deletions
+23 -4
View File
@@ -14,8 +14,25 @@ from scripts.run_migrations import run_all_migrations
PROJECT_ROOT = Path(__file__).resolve().parents[1]
class _ComposeLoader(yaml.SafeLoader):
"""SafeLoader that tolerates docker-compose merge tags (e.g. ``!override``,
``!reset``), which appear in docker-compose.dev.yml's ``ports`` and which
plain ``safe_load`` rejects as unknown constructors."""
def _construct_compose_tag(loader: yaml.Loader, _suffix: str, node: yaml.Node):
if isinstance(node, yaml.MappingNode):
return loader.construct_mapping(node, deep=True)
if isinstance(node, yaml.SequenceNode):
return loader.construct_sequence(node, deep=True)
return loader.construct_scalar(node)
_ComposeLoader.add_multi_constructor("!", _construct_compose_tag)
def _read_yaml(path: str) -> dict:
return yaml.safe_load((PROJECT_ROOT / path).read_text())
return yaml.load((PROJECT_ROOT / path).read_text(), Loader=_ComposeLoader)
async def _run_lifespan(app) -> None:
@@ -41,7 +58,9 @@ def _configure_database_env(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) ->
def test_compose_uses_migration_job_before_app() -> None:
compose = _read_yaml("docker-compose.yml")
override = _read_yaml("docker-compose.override.yml")
# Local dev overrides live in docker-compose.dev.yml (explicitly layered;
# see README "Docker Compose"). It supplies build: . for local-source builds.
dev = _read_yaml("docker-compose.dev.yml")
migration_service = compose["services"]["migration"]
app_service = compose["services"]["app"]
@@ -49,8 +68,8 @@ def test_compose_uses_migration_job_before_app() -> None:
assert migration_service["command"] == ["python", "-m", "scripts.run_migrations"]
assert migration_service["restart"] == "no"
assert app_service["depends_on"]["migration"]["condition"] == "service_completed_successfully"
assert override["services"]["migration"]["build"] == "."
assert override["services"]["app"]["build"] == "."
assert dev["services"]["migration"]["build"] == "."
assert dev["services"]["app"]["build"] == "."
def test_image_defaults_to_uvicorn_only() -> None: