Skip to content

feat(board): a seat is filled in on the page it is read on - #968

Merged
ExtraToast merged 1 commit into
mainfrom
feat/board-seat-dialog
Sep 2, 2026
Merged

feat(board): a seat is filled in on the page it is read on#968
ExtraToast merged 1 commit into
mainfrom
feat/board-seat-dialog

Conversation

@ExtraToast

@ExtraToast ExtraToast commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

What this does

A board member fills in a seat from the page a visitor reads it on. A pencil on a row opens
that seat; a plus at the end of the stack adds one.

One dialog per seat carries everything a seat is: the name it stands under, the nickname the
history knows it by, the role in the board's own words, the portrait uploaded, the account it
belongs to, the stretch it was served and the blurb. Plus Remove, which asks first.

The parts that are about the real data

  • A seat is not a person. Most of the forty-six seats never had an account here, so the
    name is the seat's own and an account is something a seat may additionally have. Detaching
    leaves the seat standing under its own name, and the spec asserts exactly that.
  • The account link and the serving dates are load-bearing. The cohort module reads them to
    answer "was on the board that year", so the dates are pre-filled from the board and then
    editable: a mid-year handover is recorded truthfully rather than credited as a full year.
  • A picture is stored on choosing and put on the seat on saving, the house pattern.
    Cancelling leaves the seat exactly as it was, portrait included — asserted by comparing the
    plate's src across a cancelled dialog that had already uploaded a replacement.
  • A failed save says why. The generated sdk returns {error} rather than throwing, so the
    adapter grew addSeatOrReason, saveSeatOrReason, linkSeatMemberOrReason and
    dropSeatOrReason beside the existing four — the same shape feat(board): a board can be edited and added on the boards page #962 gave saveBoardOrReason.
    The old signatures stay as thin wrappers, so BoardManager is untouched until The board editor leaves the management area #934 removes it.
  • The account is chosen with the island's own IslandPicker, not Vuetify's, and the portrait
    with ImagePicker bound to storeSeatPortrait.

How the pencil reaches the page without a cross-domain import

src/components/island/SeatRows.vue may not know about boards (frontend ADR-001), so it does
not. It takes a mayEdit boolean and an addLabel, and emits edit(id) and add() — the same
contract Timeline and BannerSlices already have. Board.vue owns the dialog, decides who may
see a pencil, and turns a row id back into the seat it read. The pencil is positioned over the
row rather than nested inside it, because the row head is itself a button wherever a blurb was
written and a button inside a button is not markup — which also kept the diff off the existing
markup's indentation.

It is hidden until the row is approached (and simply stands where there is no pointer to hover
with), following the strip's own rule. Nothing here is a guard: a visitor is offered no pencil at
all, and a refused request is still refused.

On #962

useMayEditBoards is #962's file and this imports it rather than carrying a second copy. #962
merged before this was pushed, so the rebase absorbed it and the file does not appear in this
diff at all.

The e2e board mocks are now stateful on both sides: #962 made a board's own fields stateful, and
this lands a seat write on the board's members, so a page that reads again is answered the way
the api would. boardHolding deliberately looks at the raw fixtures and boardsMade rather than
at boardsNow(), which composes a copy per board — a seat pushed onto a copy's list would reach
the board, and a seat removed from one would not.

Verification

All from services/frontend, run against the rebased tree, exit codes checked rather than
grepped summaries.

step exit
vue-tsc --noEmit -p tsconfig.typecheck.json 0
eslint --fix --max-warnings 0 src tests scripts … 0
vitest run --coverage — 138 files, 1017 tests 0
vite build 0
playwright test --grep "board" — 130 passed 0
playwright test (full) 1, see below

New coverage: 5 unit tests on SeatRows (a visitor is offered nothing; a pencil per row named
for its seat; which seat a press reports; the one way in at the end) and 13 e2e tests in
board-seat-edit.spec.ts — adding a seat, each field round-tripping, the nickname recorded apart
from the name, a portrait uploaded and previewed before saving, an account linked and detached,
the dates pre-filled and changed, removal asking first and naming the blurb it takes,
cancellation changing nothing, a refusal reported, and a visitor seeing none of it.

The full run had one failure — email-manager.spec.ts "stats panel shows delivery rate
percentage", a lone page.goto 5000ms timeout. That is the documented runner-starvation
signature (8 workers on 4 vCPUs), in a spec this branch does not touch; the whole file re-ran in
isolation 44/44 green, exit 0.

The e2e runs were driven by starting vite preview (4173) and vite dev (4174) by hand and
letting Playwright reuse them, so the wrapper's coverage-conversion step was skipped. No
yarn install was run and package.json, yarn.lock and .yarn are untouched.

The OpenAPI client was not regenerated: every field this needs — nickname, portrait,
displayName, role, startDate, endDate, description and the link endpoint — already
exists.

Closes #933


Diff breakdown added removed, scaled to the largest row.

frontend                                         +1364    -36    7
  production         █████████████████████████░   +854    -33    4
  unit tests         █░                            +39     -1    1
  e2e tests          █████████████░               +471     -2    2

──────────────────────────────────────────────────────────────────
production                                        +854    -33
tests                                             +510     -3  0.60 test lines per prod line
total (hand-written)                             +1364    -36  7 files

@ExtraToast ExtraToast added the ready-for-agent Sized and specified for an agent to pick up label Sep 2, 2026
@ExtraToast
ExtraToast merged commit 43b7a2a into main Sep 2, 2026
25 checks passed
@github-actions github-actions Bot mentioned this pull request Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-agent Sized and specified for an agent to pick up

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A seat is edited on the page

1 participant