Commit Graph
3 Commits
Author SHA1 Message Date
hermes 27b2611a20 fix(ui): keep the table columns readable on a phone
The tables were squeezed to fit the screen, and with seven to nine
columns on a 390px phone every heading and every value was truncated to
a few characters - a gravity of "1.0..." and a date of "202..." are
worse than useless, and the beer name was painted over the gravity
reading because the cell was allowed to overflow.

The tables keep their natural width and scroll sideways inside their
card, which is what a table does.  The advice is a small button so the
column that matters is narrow, and the headings have a short form for
a phone ("Brewing advice" -> "Advice", "Attenuation" -> "Att.").

The secondary columns - the drop, the legacy ABV and the days - go away
on a phone; the details are still on the run's own page.

A name and a comment get a sensible minimum width and wrap on a word
boundary rather than one word per line.

The desktop is untouched: the same full headings, the same 806px table,
and the pop-out still opens on hover.
2026-11-09 21:48:07 +10:00
hermes 7dd39190f1 fix(ui): mobile pop-out for the advice, and tables that fit a phone
The brewing advice is several hundred characters.  In a table cell it
pushed the table past the width of a phone, so the column was clipped
off the right edge and the advice could not be read without sideways
scrolling.

The advice is now a compact trigger in the cell and the text is in a
floating panel that opens on hover (desktop) or on tap (phone).

The panel is position:fixed and anchored by the script.  Absolutely
positioned inside the cell it was clipped by the table's own scroll
container, and the advice was sliced in half by the card's edge.  It
flips above the trigger when there is no room below.

The tables now reflow into stacked cards on a narrow screen, with each
value labelled by its column name.  A seven column table does not fit a
phone, and it used to scroll sideways inside its wrapper, so the
columns on the right were off the screen.

Also: the header wraps onto two rows so the nav tabs are not clipped,
the point scale says 'points' rather than 'pts', and the reading count
on the beer card does not orphan its word.
2026-11-09 20:57:34 +10:00
hermes 24cdd452c5 feat(ui): rebuild the interface with a proper design system
Build and Push Image / Build and push image (push) Canceled after 0s
Build and Push Image / Unit tests (push) Canceled after 17m41s
The old UI was two pages of raw pandas tables with no navigation, no styling
and no way to see a run's comments. This replaces it with a small,
testable application and a beer-themed design.

What changed
------------

Restructure
- The domain logic now lives in `brewing.py` as pure functions over plain
  data, with no Flask or Mongo dependency. The gravity maths, the ABV
  formula and the run-aggregation rules are unit-testable on their own.
- `app_factory.py` replaces the module-level app, and the routes move into
  blueprints under `views/`. A request-scoped Mongo repository is created per
  request and closed at teardown.
- `mongo/repository.py` replaces `build_db.py` / `query_db.py` / `get_conn.py`.
  Reading a document no longer requires a second query to fetch the fields.

Interface
- A dark, beer-themed design system: warm amber `#be7f39` on near-black, a
  condensed display face (Anton) for headings and Inter for body text. Both
  fonts and the chart library are vendored into `static/`, so the container
  needs no outbound network at runtime — the old templates pulled Bootstrap
  from a CDN.
- A real navigation bar, a login page, a brew-process page with a proper
  form, and a run-detail page listing every reading with its comment.
- The run summary tables now show the beer name and the latest comment, which
  the old pandas tables did not.
- Native form controls (checkbox, number spinners, date icon, scrollbars)
  are restyled for the dark theme.

Correctness
- The alcohol calculation now uses the standard homebrew formula,
  `(OG - FG) x 131.25`. The old code used `(max - min) / 7.36 + 0.5`, which is
  not a recognised brewing formula and overstated every result.
- Gravity points and decimal readings now convert correctly in both
  directions (`1039` <-> `1.039`).
- A run's display name resolves to the most-used label across its readings,
  because `beer_type` is stored per-reading and drifts across a run.
- Re-submitting a reading for the same run and day updates it in place
  rather than creating a duplicate.

Security / production
- The container ran the Werkzeug debugger with `FLASK_DEBUG=1`, exposing an
  interactive console on any unhandled error. It now runs waitress with
  debugging off.
- `SECRET_KEY` comes from the environment.
- The app secret is no longer a hard-coded string in source.

Housekeeping
- Removes the bokeh/duckdb/pandas dependency chain and the static file it
  generated at import time; removes the dead `add_user.py` and `test.py`.
- Splits `requirements.txt` (runtime) from `requirements-dev.txt` (tests).
- Adds a pytest suite (64 tests) and a `test` job to the CI workflow so the
  image is only built when the tests pass.
- Adds a README describing the data model and a RUNBOOK for the cluster side.

Data compatibility
------------------
The existing `beer_db.beer_data` documents are read as they are and are not
migrated or rewritten. No collection is dropped, and no document is modified
by this change other than through the explicit "record a reading" form, which
keeps the original `(date, beer_run_id)` uniqueness rule.
2026-11-09 10:21:44 +10:00