Skip to content

fix(bookings): let a locker be chosen and booked from the locker form - #544

Merged
MrYuion merged 1 commit into
developfrom
fix/locker-select-modal-items
Oct 8, 2026
Merged

MrYuion merged 1 commit into
developfrom
fix/locker-select-modal-items

Conversation

@camreeves

Copy link
Copy Markdown
Contributor

What was broken

Two defects stopped a locker from being booked through the Workplace form.

  1. LockerListFieldComponent.changeResources handed the select modal its items signal instead of the signal's value. LockerSelectModalComponent spreads data.items into its selection, which throws on a function, so the dialog opened empty.
  2. loadLockersForScope put each locker into its bank's lockers list while every locker also referenced the bank, so the object graph was cyclic. Once a selected locker reached the booking model, the signal form walked that cycle and the app died with RangeError: Maximum call stack size exceeded. The modal closed but the field never showed the locker.

Both measured on develop at 23cd594 against a local stack with the e2e locker specs from #497 (LOCK-01, LOCK-03, LOCK-04).

Change

  • pass this.items() to the modal
  • lockers listed under a bank carry a copy of the bank without its own list

Unit tests added for both. LOCK-01, LOCK-03 and LOCK-04 pass against this change.

The locker field handed the select modal its `items` signal rather than
the signal's value. The modal spreads it into the selection, which throws
on a function, so the dialog opened empty.

Once a locker could be picked, the booking model received a cyclic object
graph: `loadLockersForScope` listed each locker under its bank while every
locker also referenced the bank. The signal form walks the model, so the
app died with "Maximum call stack size exceeded" and the field stayed
empty. Lockers listed under a bank now carry a copy of the bank without
its list.
@vercel

vercel Bot commented Oct 7, 2026

Copy link
Copy Markdown

Deployment failed for project frontend-templates with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/placeos?upgradeToPro=build-rate-limit

camreeves added a commit that referenced this pull request Oct 7, 2026
…e check

Thirteen `test.fixme` guards come off: ROOM-28 and CON-SURV-01 are fixed
on develop, DESK-21 passes as written, VIS-28 and VIS-28b hold on the
2.2609.6 images, and HOME-09, LOCK-01/03/04, ROOM-22, ROOM-24, CON-B2
and ROOM-21 pass once their app fixes are on develop (#544 to #548).

ROOM-21 now asserts what the app can promise: a standard user's booking
under `app.bookings.no_approval` is stored and pending rather than
refused, since staff-api only accepts `approved` from approvers.

`room-booking` asserted the booking's own title is "Room Booking";
develop keeps the meeting name on a native booking, so it asserts that.

The workplace locker seeder used the same asset names as the concierge
seeder on the same building zone, so each found the other's lockers.
Its assets are now `E2E Workplace Locker*`.

ROOM-22 accepts an order linked by `parent_id` as well as by
`extension_data.event_id`.
@MrYuion

MrYuion commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator

Both fixes are correct. I found no blocking issues. I found one latent copy of the same cycle and one small test gap.

Verification

  • nx test bookings passes at ea0d8e41b (448 passed, 3 todo).
  • With the two libs/bookings/src/lib files reverted to develop, the two new tests fail. Thus the tests cover the bugs.
  • The returned lockers, the bank lists, and the modal selection are now acyclic: locker -> bank -> lockers[] copy -> parent copy with lockers: []. No code reads locker.bank.lockers on the booking path, so the empty list on the parent copy is safe.

1. (Low, latent) The same cycle is still in two other loaders

  • libs/explore/src/lib/explore-lockers.service.ts:86-90 and apps/concierge/src/app/lockers/locker-state.service.ts:229-233 contain the old loop (.map((_) => ({ ..._ }))). The lockers that they return still reference a bank that lists them.
  • apps/concierge/src/app/lockers/locker-state.service.ts:726 (_addLocker) makes the same cycle for a new locker.
  • LockerStateService.editBooking (locker-state.service.ts:801-815) takes space from these lockers. LockerBookingModalComponent then writes it into model.resources (locker-booking-modal.component.ts:342). The signal form will then walk the cycle and stop with RangeError, as in this PR.
  • At this time, the only caller (locker-topbar.component.ts:321) gives no booking and no space. Thus users cannot get this error now. Explore only shows the lockers on the map and does not put them in a form.

Suggested fix: move the loop into one exported helper in booking.utilities.ts and use it in all three places. Then the next person to give a locker to a form does not find the bug again:

/** Attach lockers to their banks without a reference cycle. */
export function attachLockersToBanks(lockers: Locker[], banks: LockerBank[]) {
    for (const bank of banks) {
        const parent = { ...bank, lockers: [] as Locker[] };
        bank.lockers = lockers
            .filter((_) => _.bank_id === bank.id)
            .map((_) => ({ ..._, bank: parent }));
    }
    return lockers.filter((_) => _.bank);
}

You can also do this in a follow-up PR.

2. (Low) The new cycle test does not check types and does not check the returned lockers

  • libs/bookings/src/test/booking.utilities.spec.ts:246 casts the bank with as any. TypeScript then does not check bank.lockers[0].bank.lockers. Use { id: 'bank-1', name: 'Bank 1', lockers: [] } as Partial<LockerBank> as LockerBank or a typed factory.
  • The test runs JSON.stringify(bank), but the form gets the selected lockers. Also add expect(() => JSON.stringify(lockers)).not.toThrow(); so that the test checks the value that goes into the form.

Review by Claude Opus 5.5 in Claude Code (T3 Code).

@MrYuion
MrYuion merged commit 9bf867b into develop Oct 8, 2026
32 of 36 checks passed
@MrYuion
MrYuion deleted the fix/locker-select-modal-items branch October 8, 2026 00:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants