Skip to content

Commit a8917d8

Browse files
Merge branch 'main' into feature/lfxv2-502-email-linking-from-auth-service
Jira Ticket: https://linuxfoundation.atlassian.net/browse/LFXV2-502 Signed-off-by: Mauricio Zanetti Salomao <msalomao@contractor.linuxfoundation.org>
2 parents 12134c9 + 5147707 commit a8917d8

File tree

67 files changed

+3601
-1638
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+3601
-1638
lines changed

.github/workflows/config/reviewers.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ addAssignees: author
99

1010
# A list of reviewers to be added to pull requests (GitHub user name)
1111
reviewers:
12-
- audigregorie
1312
- asithade
1413
- andrest50
1514
- mauriciozanettisalomao
1615
- dealako
16+
- jordane
1717

1818
# A number of reviewers added to the pull request
1919
# Set 0 to add all the reviewers (default: 0)

apps/lfx-one/src/app/app.routes.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,19 @@ export const routes: Routes = [
1919
path: 'projects',
2020
loadComponent: () => import('./modules/pages/home/home.component').then((m) => m.HomeComponent),
2121
},
22+
{
23+
path: 'meetings',
24+
loadChildren: () => import('./modules/meetings/meetings.routes').then((m) => m.MEETING_ROUTES),
25+
},
2226
],
2327
},
2428
{
25-
path: 'meetings',
26-
loadChildren: () => import('./modules/meeting/meeting.routes').then((m) => m.MEETING_ROUTES),
29+
path: 'meetings/not-found',
30+
loadComponent: () => import('./modules/meetings/meeting-not-found/meeting-not-found.component').then((m) => m.MeetingNotFoundComponent),
31+
},
32+
{
33+
path: 'meetings/:id',
34+
loadComponent: () => import('./modules/meetings/meeting-join/meeting-join.component').then((m) => m.MeetingJoinComponent),
2735
},
2836
{
2937
path: 'project/:slug',

apps/lfx-one/src/app/layouts/main-layout/main-layout.component.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,9 @@ export class MainLayoutComponent {
3333
routerLink: '/',
3434
},
3535
{
36-
label: 'My Meetings',
36+
label: 'Meetings',
3737
icon: 'fa-light fa-video',
38-
routerLink: '/my-meetings',
39-
disabled: true,
38+
routerLink: '/meetings',
4039
},
4140
{
4241
label: 'Project Health',

apps/lfx-one/src/app/modules/dashboards/board-member/board-member-dashboard.component.html

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<div class="mb-6 flex items-center gap-4" data-testid="organization-selector">
77
<label for="organization-select" class="text-sm font-semibold text-gray-700">Organization:</label>
88
<lfx-select
9-
[form]="accountForm"
9+
[form]="form"
1010
control="selectedAccountId"
1111
[options]="availableAccounts()"
1212
optionLabel="accountName"
@@ -17,8 +17,7 @@
1717
[showClear]="false"
1818
styleClass="min-w-[300px]"
1919
inputId="organization-select"
20-
data-testid="organization-select"
21-
(onChange)="handleAccountChange($event)" />
20+
data-testid="organization-select" />
2221
</div>
2322

2423
<!-- Dashboard Sections -->

apps/lfx-one/src/app/modules/dashboards/board-member/board-member-dashboard.component.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
// SPDX-License-Identifier: MIT
33

44
import { Component, computed, inject, Signal } from '@angular/core';
5+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
56
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
67
import { Account } from '@lfx-one/shared/interfaces';
8+
79
import { SelectComponent } from '../../../shared/components/select/select.component';
810
import { AccountContextService } from '../../../shared/services/account-context.service';
911
import { FoundationHealthComponent } from '../components/foundation-health/foundation-health.component';
@@ -20,20 +22,21 @@ import { PendingActionsComponent } from '../components/pending-actions/pending-a
2022
export class BoardMemberDashboardComponent {
2123
private readonly accountContextService = inject(AccountContextService);
2224

23-
protected readonly accountForm = new FormGroup({
25+
public readonly form = new FormGroup({
2426
selectedAccountId: new FormControl<string>(this.accountContextService.selectedAccount().accountId),
2527
});
2628

27-
protected readonly availableAccounts: Signal<Account[]> = computed(() => this.accountContextService.availableAccounts);
29+
public readonly availableAccounts: Signal<Account[]> = computed(() => this.accountContextService.availableAccounts);
2830

29-
/**
30-
* Handle account selection change
31-
*/
32-
protected handleAccountChange(event: any): void {
33-
const selectedAccountId = event.value as string;
34-
const selectedAccount = this.accountContextService.availableAccounts.find((acc) => acc.accountId === selectedAccountId);
35-
if (selectedAccount) {
36-
this.accountContextService.setAccount(selectedAccount);
37-
}
31+
public constructor() {
32+
this.form
33+
.get('selectedAccountId')
34+
?.valueChanges.pipe(takeUntilDestroyed())
35+
.subscribe((value) => {
36+
const selectedAccount = this.accountContextService.availableAccounts.find((acc) => acc.accountId === value);
37+
if (selectedAccount) {
38+
this.accountContextService.setAccount(selectedAccount as Account);
39+
}
40+
});
3841
}
3942
}

apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.html

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ <h2 class="font-display font-semibold text-gray-900">My Meetings</h2>
99
label="View All"
1010
icon="fa-light fa-chevron-right"
1111
iconPos="right"
12-
(onClick)="handleViewAll()"
12+
[routerLink]="['/meetings']"
1313
styleClass="!text-sm !font-normal"
1414
[text]="true"
1515
size="small"
@@ -18,45 +18,50 @@ <h2 class="font-display font-semibold text-gray-900">My Meetings</h2>
1818

1919
<!-- Scrollable Content -->
2020
<div class="flex flex-col flex-1">
21-
<div class="flex flex-col gap-6 overflow-scroll max-h-[30rem]" data-testid="dashboard-my-meetings-list">
22-
@if (todayMeetings().length > 0 || upcomingMeetings().length > 0) {
23-
<!-- TODAY Section - only show if there are meetings today -->
24-
@if (todayMeetings().length > 0) {
25-
<div>
26-
<h4 class="text-xs font-medium text-gray-500 mb-3 uppercase tracking-wide">Today</h4>
27-
<div class="flex flex-col gap-3">
28-
@for (item of todayMeetings(); track item.meeting.uid) {
29-
<lfx-dashboard-meeting-card
30-
[meeting]="item.meeting"
31-
[occurrence]="item.occurrence"
32-
(onSeeMeeting)="handleSeeMeeting($event)"
33-
[attr.data-testid]="'dashboard-my-meetings-today-item-' + item.meeting.uid" />
34-
}
21+
@if (loading()) {
22+
<div class="flex flex-col gap-3" data-testid="dashboard-my-meetings-loading">
23+
<p-skeleton width="100%" height="140px"></p-skeleton>
24+
<p-skeleton width="100%" height="140px"></p-skeleton>
25+
</div>
26+
} @else {
27+
<div class="flex flex-col gap-6 overflow-scroll max-h-[30rem]" data-testid="dashboard-my-meetings-list">
28+
@if (todayMeetings().length > 0 || upcomingMeetings().length > 0) {
29+
<!-- TODAY Section - only show if there are meetings today -->
30+
@if (todayMeetings().length > 0) {
31+
<div>
32+
<h4 class="text-xs font-medium text-gray-500 mb-3 uppercase tracking-wide">Today</h4>
33+
<div class="flex flex-col gap-3">
34+
@for (item of todayMeetings(); track item.meeting.uid) {
35+
<lfx-dashboard-meeting-card
36+
[meeting]="item.meeting"
37+
[occurrence]="item.occurrence"
38+
[attr.data-testid]="'dashboard-my-meetings-today-item-' + item.meeting.uid" />
39+
}
40+
</div>
3541
</div>
36-
</div>
37-
}
42+
}
3843

39-
<!-- UPCOMING Section - only show if there are upcoming meetings -->
40-
@if (upcomingMeetings().length > 0) {
41-
<div>
42-
<h4 class="text-xs font-medium text-gray-500 mb-3 uppercase tracking-wide">Upcoming</h4>
43-
<div class="flex flex-col gap-3">
44-
@for (item of upcomingMeetings(); track item.meeting.uid) {
45-
<lfx-dashboard-meeting-card
46-
[meeting]="item.meeting"
47-
[occurrence]="item.occurrence"
48-
(onSeeMeeting)="handleSeeMeeting($event)"
49-
[attr.data-testid]="'dashboard-my-meetings-upcoming-item-' + item.meeting.uid" />
50-
}
44+
<!-- UPCOMING Section - only show if there are upcoming meetings -->
45+
@if (upcomingMeetings().length > 0) {
46+
<div>
47+
<h4 class="text-xs font-medium text-gray-500 mb-3 uppercase tracking-wide">Upcoming</h4>
48+
<div class="flex flex-col gap-3">
49+
@for (item of upcomingMeetings(); track item.meeting.uid) {
50+
<lfx-dashboard-meeting-card
51+
[meeting]="item.meeting"
52+
[occurrence]="item.occurrence"
53+
[attr.data-testid]="'dashboard-my-meetings-upcoming-item-' + item.meeting.uid" />
54+
}
55+
</div>
5156
</div>
57+
}
58+
} @else {
59+
<!-- Global empty state - only shows when no meetings at all -->
60+
<div class="text-xs text-gray-500 py-8 text-center border-2 border-dashed border-gray-300 rounded-lg" data-testid="dashboard-my-meetings-empty">
61+
No meetings scheduled
5262
</div>
5363
}
54-
} @else {
55-
<!-- Global empty state - only shows when no meetings at all -->
56-
<div class="text-xs text-gray-500 py-8 text-center border-2 border-dashed border-gray-300 rounded-lg" data-testid="dashboard-my-meetings-empty">
57-
No meetings scheduled
58-
</div>
59-
}
60-
</div>
64+
</div>
65+
}
6166
</div>
6267
</section>

apps/lfx-one/src/app/modules/dashboards/components/my-meetings/my-meetings.component.ts

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,30 @@
22
// SPDX-License-Identifier: MIT
33

44
import { CommonModule } from '@angular/common';
5-
import { Component, computed, inject } from '@angular/core';
5+
import { Component, computed, inject, signal } from '@angular/core';
66
import { toSignal } from '@angular/core/rxjs-interop';
77
import { Router } from '@angular/router';
88
import { MeetingService } from '@app/shared/services/meeting.service';
99
import { ButtonComponent } from '@components/button/button.component';
1010
import { DashboardMeetingCardComponent } from '@components/dashboard-meeting-card/dashboard-meeting-card.component';
11+
import { getActiveOccurrences } from '@lfx-one/shared';
12+
import { SkeletonModule } from 'primeng/skeleton';
13+
import { finalize } from 'rxjs';
1114

12-
import type { Meeting, MeetingOccurrence } from '@lfx-one/shared/interfaces';
13-
14-
interface MeetingWithOccurrence {
15-
meeting: Meeting;
16-
occurrence: MeetingOccurrence;
17-
sortTime: number;
18-
}
15+
import type { MeetingWithOccurrence } from '@lfx-one/shared/interfaces';
1916

2017
@Component({
2118
selector: 'lfx-my-meetings',
2219
standalone: true,
23-
imports: [CommonModule, DashboardMeetingCardComponent, ButtonComponent],
20+
imports: [CommonModule, DashboardMeetingCardComponent, ButtonComponent, SkeletonModule],
2421
templateUrl: './my-meetings.component.html',
2522
styleUrl: './my-meetings.component.scss',
2623
})
2724
export class MyMeetingsComponent {
2825
private readonly meetingService = inject(MeetingService);
2926
private readonly router = inject(Router);
30-
private readonly allMeetings = toSignal(this.meetingService.getMeetings(), { initialValue: [] });
27+
protected readonly loading = signal(true);
28+
private readonly allMeetings = toSignal(this.meetingService.getMeetings().pipe(finalize(() => this.loading.set(false))), { initialValue: [] });
3129

3230
protected readonly todayMeetings = computed<MeetingWithOccurrence[]>(() => {
3331
const now = new Date();
@@ -41,7 +39,10 @@ export class MyMeetingsComponent {
4139
for (const meeting of this.allMeetings()) {
4240
// Process occurrences if they exist
4341
if (meeting.occurrences && meeting.occurrences.length > 0) {
44-
for (const occurrence of meeting.occurrences) {
42+
// Get only active (non-cancelled) occurrences
43+
const activeOccurrences = getActiveOccurrences(meeting.occurrences);
44+
45+
for (const occurrence of activeOccurrences) {
4546
const startTime = new Date(occurrence.start_time);
4647
const startTimeMs = startTime.getTime();
4748
const endTime = startTimeMs + occurrence.duration * 60 * 1000 + buffer;
@@ -92,7 +93,10 @@ export class MyMeetingsComponent {
9293
for (const meeting of this.allMeetings()) {
9394
// Process occurrences if they exist
9495
if (meeting.occurrences && meeting.occurrences.length > 0) {
95-
for (const occurrence of meeting.occurrences) {
96+
// Get only active (non-cancelled) occurrences
97+
const activeOccurrences = getActiveOccurrences(meeting.occurrences);
98+
99+
for (const occurrence of activeOccurrences) {
96100
const startTime = new Date(occurrence.start_time);
97101
const startTimeMs = startTime.getTime();
98102

@@ -130,12 +134,4 @@ export class MyMeetingsComponent {
130134
// Sort by earliest time first and limit to 5
131135
return meetings.sort((a, b) => a.sortTime - b.sortTime).slice(0, 5);
132136
});
133-
134-
public handleSeeMeeting(meetingId: string): void {
135-
this.router.navigate(['/meetings', meetingId]);
136-
}
137-
138-
public handleViewAll(): void {
139-
this.router.navigate(['/meetings']);
140-
}
141137
}

apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.html

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,19 +40,16 @@ <h3 class="text-sm font-medium">{{ metric.title }}</h3>
4040
<div class="space-y-2 pt-1">
4141
<div class="flex items-center justify-between">
4242
<span class="text-sm text-gray-500">Tier</span>
43-
<div class="flex items-center gap-2">
44-
<span class="px-2 py-0.5 text-xs font-medium rounded bg-gradient-to-r from-gray-400 to-gray-300 text-white border border-gray-300">
45-
{{ metric.tier }}
46-
</span>
47-
<span class="text-xs text-gray-500">since {{ metric.tierSince }}</span>
48-
</div>
43+
<span class="px-2 py-0.5 text-xs font-medium rounded bg-gradient-to-r from-gray-400 to-gray-300 text-white border border-gray-300">
44+
{{ metric.tier }}
45+
</span>
4946
</div>
5047
<div class="flex items-center justify-between">
51-
<span class="text-sm text-gray-500">Annual Fee</span>
52-
<span class="text-sm font-medium">{{ metric.annualFee }}</span>
48+
<span class="text-sm text-gray-500">Member Since</span>
49+
<span class="text-sm font-medium">{{ metric.tierSince }}</span>
5350
</div>
5451
<div class="flex items-center justify-between">
55-
<span class="text-sm text-gray-500">Next Due</span>
52+
<span class="text-sm text-gray-500">Renewal Date</span>
5653
<span class="text-sm font-medium">{{ metric.nextDue }}</span>
5754
</div>
5855
</div>
@@ -87,7 +84,7 @@ <h3 class="text-sm font-medium">{{ metric.title }}</h3>
8784
}
8885
}
8986
<div class="space-y-0.5">
90-
<div class="text-xl font-medium">{{ metric.value | number }}</div>
87+
<div class="text-xl font-medium">{{ metric.value }}</div>
9188
@if (metric.subtitle) {
9289
<div class="text-xs text-gray-500">{{ metric.subtitle }}</div>
9390
}

0 commit comments

Comments
 (0)