From 6d0959cc5d0b5bb9e07a8edb236297ec04e8ccf3 Mon Sep 17 00:00:00 2001 From: Alex Sorafumo Date: Wed, 14 Oct 2026 12:04:15 +1100 Subject: [PATCH] fix(workplace): keep the prefilled value when a shortcut of the new home page opens the meeting form "Meeting with a colleague", "meeting with the selected colleagues" and "book a favourite room" of the new home page filled the meeting form on a 300 ms timer. When the form page loaded shortly after the timer, `loadForm` replaced the model before the form was saved, and the colleague or room was lost. 14d9a5a4da fixed this for the replaced home page only. The new home page is the default since #561 and still had the timers. The three shortcuts now use `EventFormService.newFormWith()`, which saves the values with the form at once. --- .../landing-colleagues-new.component.ts | 25 ++++++------- .../landing-favourites-new.component.ts | 10 ++--- .../landing-colleagues-new.component.spec.ts | 37 +++++++++++++++++++ .../landing-favourites-new.component.spec.ts | 22 +++++++++++ 4 files changed, 73 insertions(+), 21 deletions(-) diff --git a/apps/workplace/src/app/landing-new/landing-colleagues-new.component.ts b/apps/workplace/src/app/landing-new/landing-colleagues-new.component.ts index ba79b15dcaf..5ec37c75df7 100644 --- a/apps/workplace/src/app/landing-new/landing-colleagues-new.component.ts +++ b/apps/workplace/src/app/landing-new/landing-colleagues-new.component.ts @@ -8,7 +8,6 @@ import { Router } from '@angular/router'; import { BookingFormService } from '@placeos/bookings'; import { Booking, - CalendarEvent, i18n, notifySuccess, settingSignal, @@ -291,25 +290,23 @@ export class LandingColleaguesNewComponent { this.selected_users.set([]); } - public async bookMeeting(user: User) { + /** + * Open the meeting form with the colleague invited. The attendees are + * saved with the form at once, so the form page restores them at any + * load time. + */ + public bookMeeting(user: User) { + this._event_form.newFormWith({ attendees: [user] }); this._router.navigate(['/book', 'meeting', 'form']); - const event = new CalendarEvent({ - attendees: [user], - }); - setTimeout(() => this._event_form.newForm(event), 300); } - public async bookMeetingWithSelected() { + /** Open the meeting form with the selected colleagues invited */ + public bookMeetingWithSelected() { const attendees = this.selected_users(); if (attendees.length === 0) return; + this._event_form.newFormWith({ attendees }); + this.clearSelection(); this._router.navigate(['/book', 'meeting', 'form']); - const event = new CalendarEvent({ - attendees: attendees, - }); - setTimeout(() => { - this._event_form.newForm(event); - this.clearSelection(); - }, 300); } public async bookDeskWithSelected() { diff --git a/apps/workplace/src/app/landing-new/landing-favourites-new.component.ts b/apps/workplace/src/app/landing-new/landing-favourites-new.component.ts index 4e6dd3cbfb4..cc5ade74002 100644 --- a/apps/workplace/src/app/landing-new/landing-favourites-new.component.ts +++ b/apps/workplace/src/app/landing-new/landing-favourites-new.component.ts @@ -346,18 +346,14 @@ export class LandingFavouritesNewComponent { const space = this.all_spaces().find((s) => s.id === item.id); if (!space) return; - this._event_form.newForm(); + // Saved with the form at once, so the form page restores + // the room at any load time. + this._event_form.newFormWith({ resources: [space] }); if (use_new_features) { this._router.navigate(['/book', 'meeting']); } else { this._router.navigate(['/book', 'spaces']); } - setTimeout(() => { - this._event_form.model.update((m) => ({ - ...m, - resources: [space], - })); - }, 300); break; case 'desk': diff --git a/apps/workplace/src/tests/landing/landing-colleagues-new.component.spec.ts b/apps/workplace/src/tests/landing/landing-colleagues-new.component.spec.ts index 8ff9418cac3..4173e273dd3 100644 --- a/apps/workplace/src/tests/landing/landing-colleagues-new.component.spec.ts +++ b/apps/workplace/src/tests/landing/landing-colleagues-new.component.spec.ts @@ -39,6 +39,7 @@ describe('LandingColleaguesNewComponent', () => { }), MockProvider(EventFormService, { newForm: vi.fn(), + newFormWith: vi.fn(), }), MockProvider(BookingFormService, booking_form_service), MockProvider(Router, { navigate: vi.fn() }), @@ -130,6 +131,42 @@ describe('LandingColleaguesNewComponent', () => { ).not.toContain('COLLEAGUES_BOOK_MEETING'); }); + // No wait before the checks: a prefill on a timer is lost when the form + // page loads after the timer. + it('should save the colleague with the meeting form before it opens the form', () => { + const user = { name: 'Test User', email: 'test@example.com' }; + spectator = createComponent(); + const event_form = spectator.inject(EventFormService); + + spectator.component.bookMeeting(user as any); + + expect(event_form.newFormWith).toHaveBeenCalledWith({ + attendees: [user], + }); + expect(spectator.inject(Router).navigate).toHaveBeenCalledWith([ + '/book', + 'meeting', + 'form', + ]); + }); + + it('should save the selected colleagues with the meeting form and clear the selection', () => { + const attendees = [{ name: 'Test User', email: 'test@example.com' }]; + spectator = createComponent(); + const event_form = spectator.inject(EventFormService); + spectator.component.selected_users.set(attendees as any); + + spectator.component.bookMeetingWithSelected(); + + expect(event_form.newFormWith).toHaveBeenCalledWith({ attendees }); + expect(spectator.component.selected_users()).toEqual([]); + expect(spectator.inject(Router).navigate).toHaveBeenCalledWith([ + '/book', + 'meeting', + 'form', + ]); + }); + it('should initialise colleague desk bookings with desk booking type', async () => { const members = [{ name: 'Test User', email: 'test@example.com' }]; spectator = createComponent(); diff --git a/apps/workplace/src/tests/landing/landing-favourites-new.component.spec.ts b/apps/workplace/src/tests/landing/landing-favourites-new.component.spec.ts index f2264977512..75d4d477728 100644 --- a/apps/workplace/src/tests/landing/landing-favourites-new.component.spec.ts +++ b/apps/workplace/src/tests/landing/landing-favourites-new.component.spec.ts @@ -37,6 +37,7 @@ describe('LandingFavouritesNewComponent', () => { }), MockProvider(EventFormService, { newForm: vi.fn(), + newFormWith: vi.fn(), model: signal({}), } as any), MockProvider(BookingFormService, { @@ -112,6 +113,27 @@ describe('LandingFavouritesNewComponent', () => { expect(booking_model()).toEqual({}); }); + // No wait before the checks: a prefill on a timer is lost when the form + // page loads after the timer. + it('should save a favourite room with the meeting form before it opens the form', () => { + const router = spectator.inject(Router); + const settings = spectator.inject(SettingsService); + const event_form = spectator.inject(EventFormService); + (settings.get as any).mockReturnValue(true); + spectator.detectChanges(); + + spectator.component.bookResource({ + id: 'space-1', + name: 'Room 1', + type: 'room', + } as any); + + expect(event_form.newFormWith).toHaveBeenCalledWith({ + resources: [expect.objectContaining({ id: 'space-1' })], + }); + expect(router.navigate).toHaveBeenCalledWith(['/book', 'meeting']); + }); + it('should still preload non-desk favourites into the booking form', async () => { const router = spectator.inject(Router); const settings = spectator.inject(SettingsService);