From 737a29c0533b9376730d084320e6119b9b802abd Mon Sep 17 00:00:00 2001 From: Cameron Reeves Date: Thu, 8 Oct 2026 10:42:25 +1100 Subject: [PATCH] fix(concierge): name the locker on the locker bookings listing A booking stores the locker's id only (the Booking model has no asset name column; the name sent on create never comes back), so the Locker column showed `asset-...`. The listing now resolves the name from the lockers the state has already loaded for the building, the way the desk listing resolves desk names, and falls back to the id for a locker it does not know. --- .../app/lockers/locker-bookings.component.ts | 20 ++++++++++++++++++- .../lockers/locker-bookings.component.spec.ts | 12 +++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/apps/concierge/src/app/lockers/locker-bookings.component.ts b/apps/concierge/src/app/lockers/locker-bookings.component.ts index 230c43dd85e..7af07ebff92 100644 --- a/apps/concierge/src/app/lockers/locker-bookings.component.ts +++ b/apps/concierge/src/app/lockers/locker-bookings.component.ts @@ -128,7 +128,7 @@ import { LockerStateService } from './locker-state.service';
- {{ row.asset_name || row.asset_id }} + {{ lockerName(row) }}
@@ -305,6 +305,24 @@ export class LockerBookingsComponent { private _state = inject(LockerStateService); public readonly load_error = this._state.load_error; public readonly retryLoad = () => this._state.refresh(); + /** Locker names by asset id, from the lockers loaded for the building. */ + private readonly _locker_names = computed(() => { + const names: Record = {}; + for (const locker of this._state.lockers()) { + names[locker.id] = locker.name; + } + return names; + }); + + /** + * A booking stores only the locker's id, so the name comes from the + * lockers the state has loaded; the id is shown when it is not among them. + */ + public lockerName(row: Booking): string { + return ( + this._locker_names()[row.asset_id] || row.asset_name || row.asset_id + ); + } public readonly rowKey = bookingRowKey; public readonly canSelect = ( row: BookingRowKeyFields & Pick, diff --git a/apps/concierge/src/tests/lockers/locker-bookings.component.spec.ts b/apps/concierge/src/tests/lockers/locker-bookings.component.spec.ts index 1ddf1a2728c..471708b2e26 100644 --- a/apps/concierge/src/tests/lockers/locker-bookings.component.spec.ts +++ b/apps/concierge/src/tests/lockers/locker-bookings.component.spec.ts @@ -30,6 +30,7 @@ describe('LockerBookingsComponent', () => { search: signal(''), has_more_pages: signal(false), load_error: signal(false), + lockers: signal([{ id: 'locker-1', name: 'Locker 1' }]), filtered_bookings: signal([ { id: 'bk-1', date: 1_000, duration: 30 }, ]), @@ -49,6 +50,17 @@ describe('LockerBookingsComponent', () => { ]); }); + it('should name a locker from the loaded lockers, falling back to the id', () => { + const name = (row: any) => spectator.component.lockerName(row); + expect(name({ asset_id: 'locker-1', asset_name: 'locker-1' })).toBe( + 'Locker 1', + ); + expect(name({ asset_id: 'locker-9', asset_name: 'Named' })).toBe( + 'Named', + ); + expect(name({ asset_id: 'locker-9', asset_name: '' })).toBe('locker-9'); + }); + it('should request the next page when loading more', () => { spectator.component.loadMore(); expect(state.nextPage).toHaveBeenCalledTimes(1);