feat(jobs): rebuild the job card, five states on one silhouette #214

Merged
nalum merged 1 commit from feat/job-card into main 2026-08-26 18:34:04 +00:00
Owner

Closes #174.

One card shape at min-height: 280px, growing only with the reader's
text-scale setting so a board stays level instead of reflowing. Five
states — pips, bar, waiting on others, plain, and up for grabs — sharing
the sheet's own progress block, so opening a card feels like it grew
rather than navigated.

Late is a modifier on any of the five, not a sixth state. It changes
one line: the merged due-and-points line goes amber with a glyph and the
word, and the accent rail keeps the owner's colour. Recolouring the rail
would give one element two meanings, and the up-for-grabs card's missing
rail is what makes it readable. Amber rather than red because red means
destruction here, and a late job is still a job you can finish in ten
seconds. Done ✓ is untouched and still celebrates in full — completing
late claims the points in full, so nothing may imply a docking.

The 2 steps left button is gone, not restyled. It was a status in the
shape of an action: it looked pressable, could not do what it said, and
repeated a count sitting two lines above — rendered as an aria-disabled
span, which law 5 bans outright. The count belongs to the progress block.

Three things found while building it, none of them in the brief:

  • Android's JobProgress truncated instead of rounding, so 2 of 3 read 66%
    where web reads 67%. No fixture had ever used a non-exact fraction.
  • Today.tsx's nameOf returned the reader's own name for every uid.
    Harmless while only your own mark showed; wrong the moment the waiting
    panel began naming other people.
  • Android's uppercase eyebrow changes the real accessible string where
    web's text-transform does not, so those four strings sit in the
    per-surface expectation lists rather than a false shared one.

New golden cases ride here; the re-recorded ones follow in their own
commit.

🤖 Generated with Claude Code

Closes #174. One card shape at `min-height: 280px`, growing only with the reader's text-scale setting so a board stays level instead of reflowing. Five states — pips, bar, waiting on others, plain, and up for grabs — sharing the sheet's own progress block, so opening a card feels like it grew rather than navigated. **Late is a modifier on any of the five, not a sixth state.** It changes one line: the merged due-and-points line goes amber with a glyph and the word, and the accent rail keeps the owner's colour. Recolouring the rail would give one element two meanings, and the up-for-grabs card's *missing* rail is what makes it readable. Amber rather than red because red means destruction here, and a late job is still a job you can finish in ten seconds. `Done ✓` is untouched and still celebrates in full — completing late claims the points in full, so nothing may imply a docking. **The `2 steps left` button is gone**, not restyled. It was a status in the shape of an action: it looked pressable, could not do what it said, and repeated a count sitting two lines above — rendered as an `aria-disabled` span, which law 5 bans outright. The count belongs to the progress block. Three things found while building it, none of them in the brief: - Android's `JobProgress` truncated instead of rounding, so 2 of 3 read 66% where web reads 67%. No fixture had ever used a non-exact fraction. - `Today.tsx`'s `nameOf` returned the reader's own name for every uid. Harmless while only your own mark showed; wrong the moment the waiting panel began naming other people. - Android's uppercase eyebrow changes the real accessible string where web's `text-transform` does not, so those four strings sit in the per-surface expectation lists rather than a false shared one. New golden cases ride here; the re-recorded ones follow in their own commit. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(jobs): rebuild the job card, five states on one silhouette
Some checks failed
check / commits (pull_request) Successful in 37s
check / go (pull_request) Successful in 4m30s
check / web (pull_request) Failing after 7m9s
android / build (pull_request) Failing after 15m44s
check / report (pull_request) Successful in 4s
android / report (pull_request) Successful in 4s
25bbc089c5
The shipped card was one shape wearing conditional bits: an overdue pill
next to a due chip next to a worth chip next to a steps chip, and a fake
`N steps left` button that looked pressable and could not act. #174
(docs/design_handoff_jobs_flow §4) replaces it with one silhouette that
grows only with the text-scale setting and five states instead:

  - broken up (pips <=12 / a bar past that) reuses JobSheet's JobProgress,
    so opening a card feels like it grew rather than navigated;
  - waiting on others — the owner's own sub-jobs are done but the job
    isn't — replaces the progress block with a capped avatar stack and
    "N to do with <names>", count first because the job has one owner
    and names-first would read as co-ownership the data can't express;
  - plain (no sub-jobs) falls back description -> owner -> repeat rule
    under an uppercase eyebrow, so a board with no sub-jobs invents no
    metric;
  - up for grabs reuses the plain silhouette with the accent rail
    dropped, a dashed "?" for the owner mark, and the eyebrow fixed to
    "Nobody's taken it".

Late (§4f) is a modifier on any of the above, not a sixth state: it only
swaps the due/worth line to the amber `warning` role (glyph + colour +
word, never the rail — recolouring that would give one element two
meanings) and retires the shipped red Overdue pill on this card. The due
and worth lines merge into one everywhere, late or not, closing the gap
between the two separate elements the shipped card drew.

The fake steps-left button is gone outright (law 5: absent, not
disabled) — the count already lives in the progress block, and `Done ✓`
is the only thing the primary button ever says. Android's own
steps-left box (accent at 70% opacity, same fake-button shape) goes the
same way, and its JobProgress rounded 2/3 down to 66% where the web
rounds to 67% — a latent cross-surface drift #174 is the first card to
actually put a non-exact fraction through both surfaces at once, fixed
to match.

Both surfaces read the SAME per-sub-job owner list now (JobCard.tsx's
`steps: Item[]`, Android's `steps: List<ItemRow>` fetched alongside the
card) rather than a stepsDone/stepsTotal count, since the waiting state
needs to know who holds each open one. Today.tsx's JobCard call passed
a nameOf that always answered "Me" regardless of uid — harmless while
the card only ever showed the reader's own mark, but wrong the moment a
waiting panel needs a held sub-job's actual owner name; fixed to the
page's real per-uid lookup.

conformance/render-job.json gains three new states (waiting, late with
points, up-for-grabs-with-a-description) and drops the old steps-left
and N/M-steps msgids; the eyebrow strings ("Waiting on", "What to
do", "Nobody's taken it") ride in expectWeb/expectAndroid rather than
the shared list because Android's uppercase display, unlike the web's
CSS text-transform, changes the actual accessible string. New cases
ride with this commit's goldens; re-recording the pre-existing cases
(the redesign changed their pixels too) is its own commit next.

Test report

Suite Tests Result Skipped
Unit 1434 ✅ pass 1
Integration 130 ✅ pass —

Coverage: 27.0%

Updated by the check workflow · commit cd6b793436

<!-- ci-test-report --> ## Test report | Suite | Tests | Result | Skipped | | --- | --: | --- | --: | | Unit | 1434 | ✅ pass | 1 | | Integration | 130 | ✅ pass | — | **Coverage:** 27.0% <sub>Updated by the check workflow · commit cd6b793436b4918b40b54f07f0665c24e2cc3a45</sub>

Android test report

Suite Tests Result Skipped
Unit (debug) ❌ 1 failed

Coverage:

Updated by the android workflow · commit cd6b793436

<!-- android-test-report --> ## Android test report | Suite | Tests | Result | Skipped | | --- | --: | --- | --: | | Unit (debug) | | ❌ 1 failed | | **Coverage:** <sub>Updated by the android workflow · commit cd6b793436b4918b40b54f07f0665c24e2cc3a45</sub>
nalum force-pushed feat/job-card from 25bbc089c5
Some checks failed
check / commits (pull_request) Successful in 37s
check / go (pull_request) Successful in 4m30s
check / web (pull_request) Failing after 7m9s
android / build (pull_request) Failing after 15m44s
check / report (pull_request) Successful in 4s
android / report (pull_request) Successful in 4s
to cd6b793436
Some checks failed
check / commits (pull_request) Successful in 16s
android / build (pull_request) Failing after 4m56s
check / go (pull_request) Successful in 2m49s
check / web (pull_request) Failing after 2m37s
android / report (pull_request) Successful in 4s
check / report (pull_request) Successful in 4s
android / report (push) Has been cancelled
android / build (push) Has been cancelled
check / commits (push) Has been cancelled
check / go (push) Has been cancelled
check / report (push) Has been cancelled
check / web (push) Has been cancelled
tag / tag (push) Has been cancelled
2026-08-26 07:03:34 +00:00
Compare
nalum changed target branch from feat/job-detail-sheet to main 2026-08-26 18:34:01 +00:00
nalum merged commit cd6b793436 into main 2026-08-26 18:34:04 +00:00
nalum deleted branch feat/job-card 2026-08-26 18:34:07 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
eagraiclainne/app!214
No description provided.