added print, download as pdf. no puppeteer, we'll see

This commit is contained in:
2026-09-22 22:06:06 +03:00
parent 122480cd46
commit 6f614270e7
37 changed files with 3955 additions and 473 deletions
@@ -0,0 +1,2 @@
- [SSE operation_map fallback](sse-operation-map-fallback.md) — unmapped `sse_broadcast_update` action strings pass through to the frontend verbatim as `operation`.
- [NomenclatureContext SSE dispatch structure](nomenclature-context-sse-dispatch.md) — how `handleSSEUpdate` routes object type to array key and operation to array mutation.
@@ -0,0 +1,33 @@
---
name: nomenclature-context-sse-dispatch
description: How handleSSEUpdate in NomenclatureContext.jsx routes an SSE message's object/operation to state mutations
metadata:
type: project
---
`frontend/src/contexts/NomenclatureContext.jsx`'s `handleSSEUpdate(message)`:
1. Special-cases `object === 'nomenclature' | 'nomenclature_entry' | 'extra_combination'`
and `object === 'invoice' | 'notice'` — these just fan out to `subscribersRef`
callbacks and return early, bypassing the nomenclature-array logic entirely.
2. Otherwise maps `message.object` (the SSE `sseObjectType`, e.g. `'vehicle'`) to a
`nomenclatureKey` (e.g. `'vehicles'`) via `getNomenclatureKeyFromSSE`, which just
looks up `NOMENCLATURES_CONFIG` by `sseObjectType` — this mapping is
operation-agnostic, so it already works correctly for any new operation string
on an existing object type (no changes needed there when adding new operations).
3. Inside `setNomenclatures(prev => {...})`, an `if/else if` chain on `operation`
decides how to mutate `prev[nomenclatureKey]`:
- `'insert'`: skip if `id` already present, else `insertSorted`.
- `'update'`: `updateSorted` (removes old, re-inserts at sorted position, or
falls back to `insertSorted` if the item wasn't present at all).
- Falls through to `return prev` (no-op) for anything unhandled — this was true
even for the backend's own long-standing `'deleted'` action before this
session; there was no delete/removal handling of any kind until Phase B of
`.claude/plans/finalize-history-void.md` added `'archived'` (filter out by id)
and `'voided'` (update-in-place if present, safe no-op if not) as new
`else if` branches alongside `insert`/`update`.
`insertSorted`/`updateSorted` (same file, ~line 62 and ~90) are the two shared
mutation helpers — always spread-based (`[...array]`, `.filter`, `.map`), never
in-place `.push`/`.splice` on the original array. Any new operation case should
reuse these two helpers rather than writing new sort/insert logic.
@@ -0,0 +1,37 @@
---
name: sse-operation-map-fallback
description: sse_broadcast_update's operation_map only maps created/updated/deleted; other action strings pass through unchanged to the frontend's `operation` field
metadata:
type: project
---
`backend/scalesapp/sse.py`'s `sse_broadcast_update(object_name, action, data)` maps
`action` through a small dict:
```python
operation_map = {
'created': 'insert',
'updated': 'update',
'deleted': 'delete'
}
...
'operation': operation_map.get(action, action),
```
Any action string NOT in that dict (e.g. `'archived'`, `'voided'`, added for the
vehicle finalize/void feature) is **not translated** — it arrives client-side in
`message.operation` exactly as the backend passed it, verbatim.
**Why:** confirmed by reading the actual source during Phase B of
`.claude/plans/finalize-history-void.md` (finalize/history/void feature), rather
than assuming the plan's own hedge ("mapped through operation_map, or handled as
raw fallback — check which is simpler") was already resolved. It wasn't resolved
in the plan text; the code settles it.
**How to apply:** when adding a new `handleSSEUpdate` case in
`frontend/src/contexts/NomenclatureContext.jsx` for a new backend action, do NOT
assume the operation string will be renamed/normalized — check `operation_map` in
`backend/scalesapp/sse.py` first, and if the action isn't in the map, write the
frontend case using the exact same string the backend passes as `action`. Do not
add entries to `operation_map` yourself for a frontend-only task — that's a
backend change and out of scope unless explicitly asked.