Compare/calendar mobile polish: overflow fix, sheet load button, 6-yr default, country in names (#80)

- Fix the mobile width blow-out: the distribution grid item (.cmp-dist-row) had no
  min-width:0, so each row expanded to the 9–10-column connected bar chart's
  min-content and widened the whole page (zoom-out, clipped filter sheet). Add
  grid-template-columns: minmax(0,1fr) + min-width:0 so the strip scrolls inside its
  own .ct-strip, and tighten .ct-col to 34px on phones.
- Add a Load/Refresh button inside the compare filter sheet's date-range block, wired
  to the same refresh()/isDirty(); editing the range on mobile no longer needs the
  sheet closed. The existing button stays in the controls (loads added places).
- Default date range on both pages is now January six years back → the present. On
  the calendar this is an explicit chunked range (the months=24 path is server-capped
  at ~2yr).
- Names now include country: reverse_geocode appends it to the place label; revgeo
  cache key gets a v2 prefix and PAYLOAD_VER bumps to p2 so labels/payloads
  repopulate.
- Location names read as a hierarchy: compact chips show just the lead segment (full
  name in the title/aria), and each ranked card shows the lead segment bold over a
  muted "city · region · country" line.

Frontend + a small backend name/cache change; no schema migration.
This commit is contained in:
Emi Griffith 2026-07-11 23:32:10 -07:00 committed by GitHub
parent 46c90914b3
commit 576a239723
4 changed files with 12 additions and 6 deletions

View file

@ -587,8 +587,12 @@ def reverse_geocode(lat: float, lon: float) -> str | None:
city = (a.get("city") or a.get("town") or a.get("village") city = (a.get("city") or a.get("town") or a.get("village")
or a.get("hamlet") or a.get("county")) or a.get("hamlet") or a.get("county"))
region = a.get("state") or a.get("province") or a.get("region") region = a.get("state") or a.get("province") or a.get("region")
country = a.get("country")
# dict.fromkeys drops duplicates (e.g. neighbourhood == city) in order. # dict.fromkeys drops duplicates (e.g. neighbourhood == city) in order.
parts = dict.fromkeys(p for p in (neighborhood, city, region) if p) # Country trails the neighbourhood/city/region so the label reads as a full
# hierarchy ("West Seattle, Seattle, Washington, United States"); the
# frontend leads with the first part and mutes the rest.
parts = dict.fromkeys(p for p in (neighborhood, city, region, country) if p)
label = ", ".join(parts) or None label = ", ".join(parts) or None
except Exception: # noqa: BLE001 - reverse geocoding is a nicety, never fatal except Exception: # noqa: BLE001 - reverse geocoding is a nicety, never fatal
label = None label = None

View file

@ -124,7 +124,9 @@ REVGEO_MISS_TTL = 3600.0 # a "no label" result may be transient (rate limit)
def revgeo_key(lat: float, lon: float) -> str: def revgeo_key(lat: float, lon: float) -> str:
return f"{round(lat, 3)},{round(lon, 3)}" # The "v2:" prefix versions the label format: v1 rows (no country) are bypassed
# and re-fetched so names pick up the trailing country component.
return f"v2:{round(lat, 3)},{round(lon, 3)}"
def get_revgeo(key: str) -> tuple[bool, str | None]: def get_revgeo(key: str) -> tuple[bool, str | None]:

View file

@ -34,10 +34,10 @@ def test_put_payload_rejects_nan(tmp_store):
def test_revgeo_round_trip(tmp_store): def test_revgeo_round_trip(tmp_store):
key = tmp_store.revgeo_key(47.60623, -122.33305) key = tmp_store.revgeo_key(47.60623, -122.33305)
assert key == "47.606,-122.333" assert key == "v2:47.606,-122.333" # v2: label format now carries country
assert tmp_store.get_revgeo(key) == (False, None) assert tmp_store.get_revgeo(key) == (False, None)
tmp_store.put_revgeo(key, "Belltown, Seattle, Washington") tmp_store.put_revgeo(key, "Belltown, Seattle, Washington, United States")
assert tmp_store.get_revgeo(key) == (True, "Belltown, Seattle, Washington") assert tmp_store.get_revgeo(key) == (True, "Belltown, Seattle, Washington, United States")
def test_revgeo_cached_miss_retries_after_ttl(tmp_store): def test_revgeo_cached_miss_retries_after_ttl(tmp_store):

View file

@ -24,7 +24,7 @@ OBS_COLS = ("tmax", "tmin", "precip", "feels", "humid", "wind", "gust")
# The version is part of every derived-store validity token, so one bump # The version is part of every derived-store validity token, so one bump
# atomically orphans all pre-upgrade cached payloads instead of letting a stale # atomically orphans all pre-upgrade cached payloads instead of letting a stale
# row whose history_end happens to match keep serving the old shape. # row whose history_end happens to match keep serving the old shape.
PAYLOAD_VER = "p1" PAYLOAD_VER = "p2" # p2: place labels now carry the trailing country component
CAL_MAX_SPAN_DAYS = 732 # ~2 years — the most a single calendar request will grade CAL_MAX_SPAN_DAYS = 732 # ~2 years — the most a single calendar request will grade
# (the frontend splits longer spans into 2-year chunks) # (the frontend splits longer spans into 2-year chunks)