feat: put the viewer's own tech column first (and fix the case mismatch that would have emptied it) #6
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/own-column-first"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements the requested behaviour: everyone keeps seeing every tech's column, and the viewer's own column is simply first.
What was actually there
Worth stating, because the README is misleading. It claims "field techs see only their own column" — the web board has never filtered. It renders every column, sorted alphabetically, and auto-scrolls to yours. The mobile board already moved your column to the front. So the web board was the only place that needed the ordering change.
The latent bug this uncovered
The mobile board's
myTech-first logic compared two differently-cased strings:myTechNamecomes fromreverseTechMap(), whose keysparsedTechMap()lower-casesa.Techcarries the billing system's own casing —STOUTD,DIAZG,SkinnR,morrictechSet[myTechName] = truetherefore inserted a lower-cased duplicate,t != myTechNamesorted that duplicate to the front as "mine", and the client filtera.tech === techmatched zero appointments against it.Net effect: the tech opens the mobile board on an empty column, with their real appointments sitting elsewhere in the swipe order. It affects 4 of the 5 techs — every one whose billing name is not already lower-case. Only
morricescapes, by luck.This has been dormant only because the TechMap has never been populated, so
myTechNamewas always empty. It would have surfaced immediately on populating it.Also fixed: one tech, two columns
Stored appointments contain both
SkinnR(5 appointments) andskinnr(1). Both boards keyed columns on the raw string, so that renders as two columns for the same person. Both now group case-insensitively, keeping the first-seen spelling for display.Changes
server/mobile.goMY_TECHcompares equal to entries inTECHSstrings.EqualFoldisMinenow compare case-insensitivelyMY_TECHis emitted as the canonical spelling (marshal moved after resolution)webapp/src/components/DispatchBoard.tsxgroupByTechgroups case-insensitively and takes the viewer's tech, moving that column to the frontVerifying after deploy
Requires the TechMap to be populated — it is currently empty, so
tech_nameresolves to""for everyone and neither board has anything to order by. Once set, checkGET /api/v1/mereturns the expectedtech_name, then confirm on both boards that the viewer's column is first and that all other columns are still present.Added the two staleness fixes discussed, on the same branch.
Mobile: the board never updated itself
It is server-rendered with its data embedded in the page — no websocket, no polling, no refresh-on-focus. The only update path was the manual
↻ Refreshbutton, so a cancelled or rescheduled job stayed on a tech's screen indefinitely. That is how someone ends up driving to a job that was called off hours ago.Now reloads on two triggers:
visibilitychange) — the common case: phone goes in the pocket, comes back outNever within 20 seconds of a touch, scroll, click or keypress. A full page reload mid-read is worse than slightly stale data, and this board is read while standing at someone's door.
The open column is saved to
sessionStorageand restored after reload, so an auto-refresh does not bounce a dispatcher reviewing another tech's day back to their own column.Web: refetch on websocket reconnect
The board fetched once on mount and then relied entirely on websocket events, with no polling fallback. Any event fired while the socket was down — laptop asleep, network blip, or one of tonight's server restarts — was lost permanently, and the board went quietly stale with nothing on screen to say so.
registerReconnectHandleris now wired through from the plugin registry (optional-chained, so an older server without it simply keeps the previous behaviour). On reconnect it refetches both/meand/appointments.Refetching
/metoo is deliberate: it means a TechMap change lands without a page reload — relevant immediately, since the map is about to be populated for the first time and anyone with the board open would otherwise keep the empty-map ordering until they reloaded.Pre-merge verification
This repo builds only on push to
main, so nothing verifies a PR. Built the branch by hand on the CI host instead:Confirmed the new logic is actually in the compiled artifacts, not just the source:
dispatch:selectedTechvisibilitychangeREFRESH_MAX_AGE_MS30-minute reminderregisterReconnectHandlerunregisterReconnectHandlerThe Go markers matter because the mobile JS lives inside a Go raw string literal — a stray backtick would have silently truncated the template rather than failing the build.
Note:
gofmt -lflagsapi.go,main.go,mobile.go,plugin.go,store.go. That is pre-existing — identical output onmainbefore this branch (several files use spaces rather than tabs). Not introduced here, and not fixed here to keep the diff readable, but worth a separate cleanup plus a CIgofmtcheck.