Checklist and queue skeletons — same tri-state gap as useThread had
- Dominant language
- TypeScript
- Stars
- 1
- Forks
- 0
- Avg merge
- 1h 21m
- Merged PRs (30d)
- 41
Description
## Context
#80 gave `useRoster`, `usePhoneLanding`, `useThread` and `useDecision` a third state (`null`/`loading` distinct from "settled and empty"), and a skeleton for each cold load. Two more hooks have the identical shape and were left out of that ticket on purpose (named in its prose but not its checkboxed acceptance criteria):
- **`useSessionPlan.ts`** — returns `PlanStep[] | null`. `null` means both "still fetching" and "no plan at all", the same conflation `useThread`'s `entries: []` used to have before #80.
- **`useQueue.ts`** — `items: Waiting[]`, starts `[]`. No way to tell "queue is open and genuinely has nothing in it" from "queue just opened and hasn't fetched yet".
## What to build
Same pattern as #80:
1. Give each hook a third state (a `loading`/`settled` flag alongside its existing return value).
2. A skeleton for the loading case, reusing the `Skeleton` primitive from #80 (`src/client/components/Skeleton.tsx`) rather than hand-rolling a new one.
3. Keep the existing wording/behavior for the genuinely-empty case unchanged.
## Acceptance criteria
- [ ] `useSessionPlan` distinguishes "no plan yet, still fetching" from "no plan, and none is coming" (e.g. a specialist not currently working).
- [ ] The checklist (wherever `useSessionPlan`'s `null` currently renders as nothing) shows a skeleton for the loading case only.
- [ ] `useQueue` distinguishes "queue open, not yet fetched" from "queue open, fetched, empty".
- [ ] `Queue.tsx` shows a skeleton for the loading case only; the empty-queue wording is unchanged.
- [ ] Skeletons occupy the same box the real content will (no layout shift on arrival).
- [ ] Shimmer disabled under `prefers-reduced-motion: reduce`.
- [ ] `pnpm typecheck` and `pnpm test` pass, with no new failures beyond #78's existing four.
## Out of scope
- Any other #80 follow-on work.
- Caching (already called out of scope in #80).
## Related
- #80 — the pattern this repeats, and the `Skeleton` primitive to reuse.
## Verification
```
pnpm typecheck
pnpm test
```
`pnpm test` must show exactly #78's four known failures and no fifth.
Neither proves the important half. Manual, and required: render the cockpit at a **measured** 390px viewport (headless Chrome's `--window-size` does not give you one — see #84) and confirm the checklist and queue skeletons occupy the same box the real content lands in, with no shift on arrival.
## Related
- #82 — arrival animations. A skeleton that is replaced by content must not make that content replay its arrival animation. Check this; #82's whole point was that content already on screen does not re-animate.
Contributor guide
Research direction
Start with useSessionPlan.ts, useQueue.ts, Queue.tsx, and the checklist render location, then read #80 and reuse the Skeleton primitive in src/client/components/Skeleton.tsx. Verify distinct loading and settled-empty states, stable content-sized boxes, reduced-motion behavior, and no replayed arrival animation. Run pnpm typecheck and pnpm test, then manually verify both transitions at a measured 390px viewport.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100