Repository navigation
Conversation
updateSlotCapacities takes a list of slots of one signup, each with a new capacity (a positive whole number, or null for no limit), and sets them in one transaction. It is the service behind "People for each slot" on the draft review page (issue #504); the page and its Undo come in a later change. It locks the signup, then its slots, as reorderSlots does. A slot that is not one of the signup's is refused as invalid_input, naming it, with a suggestion to reload. It is not limited to drafts, but it is safe on a published signup: the places each slot has taken (confirmed and tentative, counted by quantity, as updateSlot counts them) are read in one grouped query under the slot locks, and a capacity below them is refused with updateSlot's conflict, naming the slot. Either refusal changes nothing. Slots already at the capacity sent are skipped. When none changes, nothing is written and no activity is recorded. Otherwise one UPDATE sets every changed slot, with the new capacities passed as one JSON parameter, and one slot.updated row records { bulk: true, slotIds, changed: ['capacity'] }, stamped with the connected app when there is one. It returns the slots sent as they now stand, and each changed slot's previous capacity, which sent back puts them back. The DB tests cover several slots at once, no limit, skipped and no-op calls, a foreign slot, a capacity below a booking, a sign-up in flight, a viewer, a deleted signup, the round trip through previous, and two calls at once. docs/telemetry.md describes the bulk payload. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BTvMvHZ4i4FWQFAsxcr5Zm
"People for each slot" sits beside the slots card's headline on the draft review page (issue #504), on a draft with two to 500 slots: a number box, filled in with the number every slot takes when they all take the same one, and Set. It gives every slot that many people through updateSlotCapacities, and the card's one line for its changes says "Set every slot to 4 people." ("... to 1 person.") with Undo, shared with the notices' fixes and Remove day. When every slot already takes that number it says "Every slot is already for 4 people." with no Undo. An open slot with unsaved changes is saved first, as for the other changes. The page sends the number and a fingerprint of every slot, not their ids, to a new server action, setCapacityAction. setDraftCapacity in src/services/draft-fixes.ts works the fingerprint out again from the draft as it is now and sets nothing unless it matches, so a slot added or deleted since is refused as stale. Undo goes through the shared undo path and refuses unless every slot it changed is still there and still takes the number set, so a number changed since is never overwritten and a second Undo does nothing. A refusal shows under the box and takes focus, with "Type a whole number of people, 1 or more." for a number the server won't take; the refusal block Remove day used is now shared as OwnRefusal. The provider takes a header, so the headline and the control sit inside it above the line. It writes review.slots_edited with kind set_capacity, and its Undo review.fix_undone with the same kind. MAX_CAPACITY, the top of the capacity column, moves to src/schemas/slots.ts, shared with the merge service. The telemetry doc, privacy page, changelog and the help page "Create and publish your first signup" say so, and its walkthrough sets three slots to 4 people and undoes it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BTvMvHZ4i4FWQFAsxcr5Zm
Re-taken with the pointer moved off the slot rows, which highlight under it; the article's size and alt text follow the new picture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BTvMvHZ4i4FWQFAsxcr5Zm
…en it does nothing Code-review fixes for People for each slot on the draft review page. updateSlotCapacities takes three optional guards, checked under the signup's and its slots' locks: onlyDraft refuses a signup that is no longer a draft (conflict on status), wholeSignup refuses a list that isn't every slot of the signup (conflict on slots), and a slot's expected capacity refuses one that has changed since (conflict on slots). Set uses onlyDraft and wholeSignup, so a publish or a slot added between the page's read and the write is refused, rather than a live signup changed or a slot left out while the page says every slot was set. Undo uses onlyDraft and expects the number Set gave each slot, so a capacity changed after Undo read the draft is never overwritten. The page words them as before: not a draft any more, changed since, or changed again. The shared CapacitySchema now stops at MAX_CAPACITY, the top of the column, so a bigger capacity is a structured invalid_input rather than a database error; the undo's previous capacities use it too. A number too big for a slot now reads "That’s more people than one slot can take.", told apart from the zod issue, rather than asking for a whole number. When every slot already takes the number, the card's line and the last change's Undo stay as they were, and "Every slot is already for 3 people." shows under the box as a polite note that takes focus, not as an error. The box no longer starts again when the page comes back, so a number typed before a refusal stays. Its words for when no answer comes back are passed in from the server, from the same list fixRefusal uses, rather than typed into the page's script. DB tests cover each guard, a publish and a slot added while the call waits behind the signup lock, a capacity changed while an Undo waits, and a too-big capacity; the component test checks the note keeps Remove day's Undo. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BTvMvHZ4i4FWQFAsxcr5Zm
Owner
Author
|
Tophat: pass in the running app with made-up data (
Code review on this PR, fixed in 960aa8b:
Left as is: the extra signup read and policy check that Checks on 960aa8b: lint, typecheck and knip pass; unit (1573), Generated by Claude Code |
This was referenced Oct 21, 2026
Owner
Author
|
Closing without merging, as part of the redesign in #498. The branch stays: Generated by Claude Code |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #504 (part 3 of 3; parts 1 and 2 are #535 and #536). Part of #498. Stacked on #536; the diff here is only "People for each slot".
What changes
On the review page, next to the slots' headline ("4 slots · 2 days · 1 to 3 people each"), there's now People for each slot: a number box and Set. It appears when the draft has at least two slots.
How it works
A new service,
updateSlotCapacitiesinsrc/services/slots.ts. The issue suggested theslots/bulkroute, but that route only adds slots. The service:updateSlot, so it's safe on a published signup too;slot.updatedrow{ bulk: true, slotIds, changed: ['capacity'] }.It returns each changed slot's previous capacity.
The review page calls it through
setCapacityAction→setDraftCapacityinsrc/services/draft-fixes.ts. That sharesloadDraft, so it works on drafts only and refuses viewers. A fingerprint of all the draft's slots, as the page showed them, must match the slots as they are now. Otherwise: "That changed since you opened this page, so nothing was done."Undo with a staleness guard. Undo goes through the shared
undoDraftFixAction. It's refused ("That changed again since…") unless every slot still exists and still has the number Set gave it. So a number changed since, in the slot editor, the builder or by an assistant, is never overwritten, and a second Undo is refused.One line, one Undo. It shares the card's status line and last-action Undo with the one-tap fixes and Remove day (Remove a whole day from the draft review page, with Undo #536). An open slot editor saves first. A refusal shows by the control and takes focus.
Telemetry:
review.slots_edited { kind: 'set_capacity' }after Set, andreview.fix_undone { kind: 'set_capacity' }after Undo.docs/telemetry.mdand the privacy page say so.Budget
/app/signups/[id]: 114.9 / 115 kB, so no change to the budget.Help
"Create and publish your first signup": "To give every slot the same number of spots, type the number in People for each slot, above your slots. Then choose Set. Right after, Undo puts back what each slot had." A new walkthrough test sets three slots at 1, 2 and 3 to 4, checks the rows and the headline, then undoes and reloads. The review page's picture (
check-your-signup.png) is re-taken to show the control, with the pointer moved off the rows first.Checks
pnpm lint && pnpm typecheck && pnpm knip && pnpm test: pass (1571)pnpm test:db: pass (634), including:pnpm build && pnpm budgets: passTophat
In the running app with made-up data (4 slots at 5, 1, 2 and 3 spots):
slot.updatedrow covers all 4, andreview.slots_editedis written.review.fix_undoneis written.Generated by Claude Code