Skip to content

feat(board): a board can be edited and added on the boards page - #962

Merged
ExtraToast merged 1 commit into
mainfrom
feat/board-edited-on-the-page
Sep 2, 2026
Merged

feat(board): a board can be edited and added on the boards page#962
ExtraToast merged 1 commit into
mainfrom
feat/board-edited-on-the-page

Conversation

@ExtraToast

@ExtraToast ExtraToast commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

What this does

A board is written down and corrected on /board, the page it is read on. The plus at the end
of the timeline adds a board; a pencil on any stop opens the board it belongs to.

  • One dialog, domains/boards/island/BoardDialog.vue, in the island's dialog shell: number,
    name, cheer, colour, description, the two dates, and the board photo. Which it is — adding or
    correcting — follows from whether there is a board in it, so there is one form rather than two
    that drift.
  • The number of a new board is suggested. nextBoardNumber reads one past the highest number
    recorded, so ten boards make the eleventh, and a history with a gap in it still suggests a
    number nothing holds. Typed over freely: a board added out of order is a correction somebody
    may be making on purpose.
  • The photograph is uploaded from the dialog and shown before saving, so a wrong crop is
    visible while it can still be changed. ImagePicker is bound to the boards adapter's own
    storeBoardPhoto, which is what a shared control asks its caller for rather than importing a
    domain (ADR-001).
  • A board with no photograph offers one in the band itself. Boards I to IV have none and
    board X has none yet, so that is the normal case for half the history — the offer belongs in
    the band standing in for it, not behind a pencil elsewhere.
  • Cancelling changes nothing, picture included. Bytes go into storage on being chosen and
    onto the board only on Save, so a cancelled dialog leaves the board on the photograph it had
    and the bytes where they are. There is a spec that counts the writes and asserts none.
  • Removing asks first, through ConfirmDialog, naming how many seats are in the way — the
    page already read the board whole, so nothing is asked of the api to say it. Pressing through
    reports the api's own refusal, BoardHoldsSeats composed by refusals.ts, on the question
    that is standing. Board X holds nobody, so the empty path is real and removes.
  • A failed save says why. saveBoardOrReason joins dropBoard: the sdk hands a refusal back
    as a body rather than throwing, and a clashing number answers "Board 9 already exists", which
    is the one thing whoever typed it can act on. The form stands with what was typed.

The colour, and what reads on it

The accent is a fill rather than an ink: it washes a band, lights a stretch of the timeline
and backs the swatch. So the new rule is which ink reads on it, not how to make the colour
readable — domains/boards/accent.ts, inkOnAccent, whichever of the two inks the fill
contrasts with more. Read against the four colours #958 just seeded:

board accent under white under near-black ink
VI, Don't starve together #eaa4b6 1.85:1 8.51:1 dark
VII, Overcooked #b00b69 6.26:1 2.51:1 light
VIII, Wasted #9100d0 6.22:1 2.53:1 light
X, Rainbow road #65c6cd 1.84:1 8.53:1 dark
no colour, the association's blue #3387fa 3.24:1 4.86:1 dark

A blank field is answered against the blue, because that is the fill a board with no colour is
drawn in — as is a colour written in a notation the rule cannot read. The dialog's swatch is that
rule made visible: the field's colour painted as a block with the board's numeral on it in
whichever ink it takes, live as it is typed, and the stylesheet's blue showing through while the
field is empty.

Board X was the case to check first. It has no photograph, so its cyan fills the whole band
with the numeral on top — and the band does not paint the accent raw: BoardBand washes it 30%
into the theme's own ground, so the numeral in --color-chalk clears 8.19:1 in the dark half
and 10.98:1 in the light one. The same holds for all five accents (worst case 8.03:1), so
nothing there needed changing and the band's painting is untouched.

Reconciled with what was already on the page rather than replacing it. Board.vue's
--accent-ink answers a different question — the cheer is text in the accent, on the page's
ground — and it is kept: it mixes towards the ink of the theme, so one formula lifts a deep
colour off the dark half and drops a pale one onto the light half. Both directions now have a
spec that measures what is actually painted: board VI's pink reads 3.51:1 in light mode where
raw it would be 1.57:1, and board VII's magenta 3.38:1 in dark mode where raw it would be
2.51:1. Those two specs convert the rendered colour through a canvas, because Chromium serialises
a colour mixed in oklab as oklab(...) and reading the numbers out of that string measures
nothing — which is exactly how the first version of them passed while asserting garbage.

Who may edit

useMayEditBoards, beside the boards' other island pieces, asking the one question
useMayEditEsports asks: whether the viewer holds BOARD. The api rule is
hasPermission(..., 'Board', 'write'), which is BOARD and above, and the login response carries
inherited roles, so an admin arrives holding it. Asked per domain rather than shared, so a page
never reaches into another domain to find out who is reading. It decides what is offered and
nothing else — a visitor and a plain member get no pencil, no plus and no dialog mounted at all,
and the api still refuses what it refuses.

The candidate column

Nothing to add. saveBoard already passed a candidate through as undefined when it carries
none, and BoardUseCases.candidateFor fills the NOT NULL column with the board's name — or
with Board <number> where there is no name — on every create and every update. The dialog
therefore sends no candidate and never shows the field, and the adapter says in one comment why
it is passed through rather than composed: a second copy of that rule on this side would be a
second thing to keep in step.

Also

  • image, the asset file name the early history still points at, is carried through the dialog
    untouched. A save replaces every field, so leaving it out would quietly clear it.
  • The timeline stands for a line of one, or of none, to somebody who may edit — a board has to be
    reachable before the first one can be added. A visitor still sees it only from two boards up.
  • tests/e2e/mocks.ts gains POST /boards, DELETE /boards/{id} with the BoardHoldsSeats
    refusal, the two board picture ladders, and boards remembered across a test's own writes, so a
    page that reads again is answered the way the api would. New cases in a new spec file rather
    than changes to the ones other specs read.

Verification

yarn typecheck, yarn lint --max-warnings=0, yarn test:unit (1013 passed) and
yarn test:e2e --grep "board" (104 passed) all exit 0. The full yarn test:e2e came back 674
passed with three page.goto timeouts in three unrelated specs — boards.spec.ts,
esports-removals.spec.ts, esports-island.spec.ts — which pass together in isolation: eight
workers on four vCPUs, the starvation #424 describes.

Closes #932


Diff breakdown added removed, scaled to the largest row.

frontend                                         +1523    -28   13
  production         █████████████████████████░   +828    -21    8
  unit tests         ██░                           +97     -1    3
  e2e tests          █████████████████░           +598     -6    2

──────────────────────────────────────────────────────────────────
production                                        +828    -21
tests                                             +695     -7  0.84 test lines per prod line
total (hand-written)                             +1523    -28  13 files

@ExtraToast ExtraToast added the ready-for-agent Sized and specified for an agent to pick up label Sep 2, 2026
@ExtraToast ExtraToast changed the title feat(board): a board is corrected on the page it is read on feat(board): a board can be edited and added on the boards page Sep 2, 2026
@ExtraToast
ExtraToast merged commit 0a1799f into main Sep 2, 2026
27 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 board is edited on the page

1 participant