Skip to content

Commit 2a77047

Browse files
committed
fix(meetings): load past and upcoming meetings separately (LFXV2-757)
Fixed recurring meetings incorrectly showing as past by refactoring to use separate API endpoints for upcoming and past meetings: - Load upcoming meetings via getMeetingsByProject() - Load past meetings via getPastMeetingsByProject() - React to timeFilter changes to call appropriate endpoint - Hide RSVP button for non-organizers on past meetings - Removed client-side time filtering (handled by API) This ensures proper meeting categorization and enables accurate counts for both upcoming and past meetings. Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <asithade@gmail.com>
1 parent 154cb09 commit 2a77047

File tree

3 files changed

+53
-30
lines changed

3 files changed

+53
-30
lines changed

apps/lfx-one/src/app/modules/meetings/meetings-dashboard/meetings-dashboard.component.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,21 +45,21 @@
4545
</div>
4646

4747
<div class="flex-1">
48-
@if (meetings()) {
48+
@if (upcomingMeetings() || pastMeetings()) {
4949
<lfx-meetings-top-bar
5050
[(searchQuery)]="searchQuery"
5151
[(timeFilter)]="timeFilter"
5252
[(visibilityFilter)]="topBarVisibilityFilter"
5353
[viewMode]="currentView()"
54-
[meetings]="meetings()" />
54+
[meetings]="timeFilter() === 'past' ? pastMeetings() : upcomingMeetings()" />
5555
}
5656
</div>
5757
</div>
5858
</div>
5959
</div>
6060

6161
<div class="min-h-[400px]">
62-
@if (meetingsLoading()) {
62+
@if ((timeFilter() === 'upcoming' && meetingsLoading()) || (timeFilter() === 'past' && pastMeetingsLoading())) {
6363
<div class="p-8">
6464
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
6565
@for (item of [0, 1, 2, 3, 4, 5]; track item) {

apps/lfx-one/src/app/modules/meetings/meetings-dashboard/meetings-dashboard.component.ts

Lines changed: 48 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { toObservable, toSignal } from '@angular/core/rxjs-interop';
77
import { MeetingCardComponent } from '@app/shared/components/meeting-card/meeting-card.component';
88
import { ProjectContextService } from '@app/shared/services/project-context.service';
99
import { ButtonComponent } from '@components/button/button.component';
10-
import { Meeting, ProjectContext } from '@lfx-one/shared/interfaces';
10+
import { Meeting, PastMeeting, ProjectContext } from '@lfx-one/shared/interfaces';
1111
import { getCurrentOrNextOccurrence } from '@lfx-one/shared/utils';
1212
import { MeetingService } from '@services/meeting.service';
1313
import { PersonaService } from '@services/persona.service';
@@ -28,9 +28,11 @@ export class MeetingsDashboardComponent {
2828
private readonly personaService = inject(PersonaService);
2929

3030
public meetingsLoading: WritableSignal<boolean>;
31-
public meetings: Signal<Meeting[]> = signal([]);
31+
public pastMeetingsLoading: WritableSignal<boolean>;
32+
public upcomingMeetings: Signal<Meeting[]>;
33+
public pastMeetings: Signal<PastMeeting[]>;
3234
public currentView: WritableSignal<'list' | 'calendar'>;
33-
public filteredMeetings: Signal<Meeting[]>;
35+
public filteredMeetings: Signal<(Meeting | PastMeeting)[]>;
3436
public refresh$: BehaviorSubject<void>;
3537
public searchQuery: WritableSignal<string>;
3638
public timeFilter: WritableSignal<'upcoming' | 'past'>;
@@ -49,14 +51,16 @@ export class MeetingsDashboardComponent {
4951

5052
// Initialize state
5153
this.meetingsLoading = signal<boolean>(true);
54+
this.pastMeetingsLoading = signal<boolean>(true);
5255
this.refresh$ = new BehaviorSubject<void>(undefined);
5356
this.currentView = signal<'list' | 'calendar'>('list');
5457
this.searchQuery = signal<string>('');
5558
this.timeFilter = signal<'upcoming' | 'past'>('upcoming');
5659
this.topBarVisibilityFilter = signal<'mine' | 'public'>('mine');
5760

5861
// Initialize data with reactive pattern
59-
this.meetings = this.initializeMeetings();
62+
this.upcomingMeetings = this.initializeUpcomingMeetings();
63+
this.pastMeetings = this.initializePastMeetings();
6064
this.filteredMeetings = this.initializeFilteredMeetings();
6165
}
6266

@@ -66,10 +70,11 @@ export class MeetingsDashboardComponent {
6670

6771
public refreshMeetings(): void {
6872
this.meetingsLoading.set(true);
73+
this.pastMeetingsLoading.set(true);
6974
this.refresh$.next();
7075
}
7176

72-
private initializeMeetings(): Signal<Meeting[]> {
77+
private initializeUpcomingMeetings(): Signal<Meeting[]> {
7378
// Convert project signal to observable to react to project changes
7479
const project$ = toObservable(this.project);
7580

@@ -86,7 +91,7 @@ export class MeetingsDashboardComponent {
8691
return of([]);
8792
}
8893

89-
return this.meetingService.getMeetings().pipe(
94+
return this.meetingService.getMeetingsByProject(project.projectId, 100).pipe(
9095
map((meetings) => {
9196
// Sort meetings by current or next occurrence start time (earliest first)
9297
return meetings.sort((a, b) => {
@@ -107,7 +112,7 @@ export class MeetingsDashboardComponent {
107112
});
108113
}),
109114
catchError((error) => {
110-
console.error('Failed to load meetings:', error);
115+
console.error('Failed to load upcoming meetings:', error);
111116
this.meetingsLoading.set(false);
112117
return of([]);
113118
}),
@@ -119,11 +124,44 @@ export class MeetingsDashboardComponent {
119124
);
120125
}
121126

122-
private initializeFilteredMeetings(): Signal<Meeting[]> {
127+
private initializePastMeetings(): Signal<PastMeeting[]> {
128+
// Convert project signal to observable to react to project changes
129+
const project$ = toObservable(this.project);
130+
131+
return toSignal(
132+
merge(
133+
project$, // Triggers on project context changes
134+
this.refresh$ // Triggers on manual refresh
135+
).pipe(
136+
tap(() => this.pastMeetingsLoading.set(true)),
137+
switchMap(() => {
138+
const project = this.project();
139+
if (!project?.projectId) {
140+
this.pastMeetingsLoading.set(false);
141+
return of([]);
142+
}
143+
144+
return this.meetingService.getPastMeetingsByProject(project.projectId, 100).pipe(
145+
catchError((error) => {
146+
console.error('Failed to load past meetings:', error);
147+
this.pastMeetingsLoading.set(false);
148+
return of([]);
149+
}),
150+
tap(() => this.pastMeetingsLoading.set(false))
151+
);
152+
})
153+
),
154+
{ initialValue: [] }
155+
);
156+
}
157+
158+
private initializeFilteredMeetings(): Signal<(Meeting | PastMeeting)[]> {
123159
return computed(() => {
124-
let filtered = this.meetings();
125-
const now = new Date();
160+
// Get appropriate meetings based on time filter
161+
const timeFilter = this.timeFilter();
162+
let filtered: (Meeting | PastMeeting)[] = timeFilter === 'past' ? this.pastMeetings() : this.upcomingMeetings();
126163

164+
// Apply search filter only - time filtering is handled by API
127165
const search = this.searchQuery()?.toLowerCase() || '';
128166
if (search) {
129167
filtered = filtered.filter(
@@ -135,21 +173,6 @@ export class MeetingsDashboardComponent {
135173
);
136174
}
137175

138-
const timeFilterValue = this.timeFilter();
139-
if (timeFilterValue === 'upcoming') {
140-
filtered = filtered.filter((meeting) => {
141-
const meetingEndTime = new Date(meeting.start_time);
142-
meetingEndTime.setMinutes(meetingEndTime.getMinutes() + meeting.duration + 40);
143-
return meetingEndTime >= now;
144-
});
145-
} else {
146-
filtered = filtered.filter((meeting) => {
147-
const meetingEndTime = new Date(meeting.start_time);
148-
meetingEndTime.setMinutes(meetingEndTime.getMinutes() + meeting.duration + 40);
149-
return meetingEndTime < now;
150-
});
151-
}
152-
153176
return filtered;
154177
});
155178
}

apps/lfx-one/src/app/shared/components/meeting-card/meeting-card.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,8 +277,8 @@ <h3 class="text-base font-medium text-gray-900 leading-tight tracking-tight" dat
277277
[additionalRegistrantsCount]="additionalRegistrantsCount()"
278278
(addParticipant)="openAddRegistrantModal()">
279279
</lfx-meeting-rsvp-details>
280-
} @else {
281-
<!-- Show RSVP Selection for authenticated non-organizers -->
280+
} @else if (!pastMeeting()) {
281+
<!-- Show RSVP Selection for authenticated non-organizers (upcoming meetings only) -->
282282
<lfx-rsvp-button-group [meeting]="meeting()" [occurrenceId]="occurrence()?.occurrence_id"> </lfx-rsvp-button-group>
283283
}
284284

0 commit comments

Comments
 (0)