|
1 | | -# Worklog — v2.17.1 (in progress) |
| 1 | +# Worklog — v2.17.2 (in progress) |
2 | 2 |
|
3 | 3 | ## Added |
| 4 | +- **Product card: HEX colour, quantity, and a raw-JSON block in debug mode.** DÉTAILS gained a **Color** row listing every slot as `#RRGGBB`, one per line, alpha normalised to `RRGGBBAA` (a dual/tri product shows all of them, not just the first) and a **Quantity** row showing the product's own `measure` with its unit — `2 kg`, not the `2000 g` the gauge uses. With debug mode on, the card now ends with the same collapsible **raw JSON** block (and copy button) the material side-card has, so a catalogue product's seed can be inspected without leaving the card — `renderer/inventory.js`. |
4 | 5 |
|
5 | 6 | ## Changed |
6 | | -- **`productKey` changed meaning in v2.17.0 and no data-model doc said so.** It is the join key third parties, the Hub and the backend statistics use, and since the tier left the key a `TigerData+` document now hashes to the same value as the `TigerTag+` of its `id_product`. Documented in `docs/firestore-schema.md` and propagated to the public integration reference (the backend repo's README): which documents change value (only `TigerData+`), that every other tier keeps its hash, that the mirror rewrites them lazily so BOTH values are in the wild during the rollout — the same caveat `protocol` already carries — and what a reader should do about it — `docs/firestore-schema.md`. |
7 | | - |
8 | | -- **Signing in with Google is near-instant when a browser session is already open.** The loopback flow forced `prompt=select_account` on every sign-in, which made Google re-authenticate from scratch each time — account chooser plus a full identity challenge (passkey / security key / password) even with a live session in the browser. Dropped the parameter: an existing session now completes the round-trip with little or no interaction. Multi-account users are unaffected in practice — Google shows the chooser on its own whenever the browser holds more than one session — `main.js`. |
9 | 7 |
|
10 | 8 | ## Fixed |
11 | | -- **Google sign-in works again — regression live since v1.10.0.** Commit `a94b4aa` (2026-05-20) moved the Google Desktop OAuth credentials to `process.env.…` with no fallback. `main.js` reads them at **runtime**, on the user machine, where they never exist: CI only sets them at **build** time and, with no bundler and no `afterPack` hook, nothing inlined them — so **every shipped build** ran with empty credentials. The loopback flow (RFC 8252 + PKCE, system browser) then reported "not configured" and the renderer silently fell back to `signInWithPopup`, which Google refuses from an embedded Electron webview. Credentials are now written by **`scripts/gen-oauth-config.mjs`** into a gitignored `oauth-config.json` that electron-builder packs into the app, and `main.js` resolves them **env → generated file → hardcoded public client id**. The client id keeps a hardcoded fallback (public by design, already in the repo history); the **client secret has none** — it must never enter the public repository, even though it necessarily ships inside the binary (RFC 8252 §8.5: an installed app cannot keep a secret; PKCE is what protects the exchange). Google **requires** that secret for this Desktop client — verified against the live endpoint, which answers `invalid_request / client_secret is missing.` without it — so the `client_secret` field stays in the token exchange, with a comment saying why. The "not configured" guard now checks **both** credentials and names the missing one, so a misconfiguration fails fast in the log instead of surfacing as a mysterious popup — `main.js`, `scripts/gen-oauth-config.mjs`, `package.json`, `.gitignore`. |
| 9 | +- **The Refill / Recycled / Filled badges were never displayed anywhere.** The spool detail card computed `infoBadges` and built `infoHtml2` from them, then never inserted it into its template — dead code, which is why a "PLA Basic Refill" spool showed no badge at all (eslint had been flagging the unused variable). Inserted it into the Details body, and added the same three chips to the **product card** (same `.aspect-chip` markup and the same existing `badgeRefill`/`badgeRecycled`/`badgeFilled` keys, already translated in all 9 locales — no new key, no new CSS). They read `info1`/`info2`/`info3` through `normalizeRow` (`isRefill`/`isRecycled`/`isFilled`) — `renderer/inventory.js`. |
| 10 | +- **Side-card detail rows: a long value ran off the edge, and a medium one wrapped with half the row empty.** `.panel-row .pv` was capped at `max-width: 60%`, so a SKU broke onto two lines even when the label beside it was short and the space was free; and nothing could break an UNBREAKABLE string (a SKU or barcode has no spaces), so a really long one overflowed past the right edge. The label is now `flex: 0 0 auto` and the value `flex: 1 1 auto; min-width: 0` — it takes all the free space and only wraps when it genuinely has to — plus `overflow-wrap: anywhere` for the no-space case. Applies to every panel row, so the material card benefits too — `renderer/css/70-detail-misc.css`. |
| 11 | +- **Catalogue seed: `measure_value` and `grams` are two different quantities and were read into one variable.** `_catalogDocFromApi` did `Number(fil.measure_value ?? fil.grams)` and then ran the result through the unit conversion — but `measure_value` is unitless and only means something with `measure_unit` (500 + "g", or 2 + "kg", or a volume), while `grams` is ALREADY normalised by the API. A missing `measure_value` on a kg product would therefore have yielded 2000 x 1000 g. `grams` is now the authority for `measure_gr`, and the raw `measure_value` (paired with `id_unit`) is kept for the chip payload as the `.ttag` contract requires; conversion only runs when the API omits `grams`. Verified on four live products, including the failing path — `renderer/inventory.js`. |
| 12 | +- **The product card always showed a 1 kg spool, whatever the real size.** `_renderProductCard` hardcoded `const cap = 1000, curW = 1000`, so a 500 g, 750 g or 2 kg product all read "1000 g / 1 kg total" with a full bar — while the grid card right next to it showed the correct `500 g`. The capacity was never missing: the catalogue seed already carries it (`_catalogDocFromApi` sets `measure`/`measure_gr` from the API `measure_value`+`measure_unit`, falling back to `grams`), and `normalizeRow` exposes it as `r.capacity`. The card now uses it, falling back to parsing the catalogue item's display string (`"500 g"` / `"2 kg"`, new `_catMeasureToGrams`) for a preview opened before the API detail lands, then to 1 kg. The spool still renders FULL — a product identity has no per-spool weight — only its capacity is now real. **One render function serves all six product-card entry points** (catalogue, favourites, lists, cart, friend view, groups), so every card in the app was affected and is fixed by this. Verified against the live API: 500 g -> 500, 2 kg -> 2000, 750 g -> 750 — `renderer/inventory.js`. |
| 13 | + |
| 14 | +## Removed |
| 15 | + |
| 16 | +## i18n |
0 commit comments