Skip to content

Commit cc3ae19

Browse files
authored
feat(dashboards): add hidden actions and meeting ui improvements (#180)
* feat(dashboards): add hidden actions and meeting UI improvements - Add HiddenActionsService for 24-hour cookie-based action dismissal - Update pending-actions component with dismiss functionality - Integrate hidden actions filtering in all dashboard variants - Simplify meeting card UI with inline action buttons - Add dynamic committee label support using COMMITTEE_LABEL constant - Remove action menu dropdown from meeting cards - Remove badge display from pending actions LFXV2-814, LFXV2-815 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <[email protected]> * refactor(dashboards): improve hidden actions SSR compatibility LFXV2-814 - Replace manual cookie operations with ngx-cookie-service-ssr for SSR compatibility - Add event handling for hidden actions with refresh capability - Improve action identifier uniqueness by including text - Fix button click handling for link components - Remove unused handleDismiss method and ariaLabel attribute Signed-off-by: Asitha de Silva <[email protected]> --------- Signed-off-by: Asitha de Silva <[email protected]>
1 parent ffb3e79 commit cc3ae19

15 files changed

+254
-179
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ <h1>{{ selectedFoundation()?.name }} Overview</h1>
3737
<!-- Middle Row - Two Cards -->
3838
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
3939
<!-- Pending Actions -->
40-
<lfx-pending-actions class="h-full" [pendingActions]="boardMemberActions()" />
40+
<lfx-pending-actions class="h-full" [pendingActions]="boardMemberActions()" (actionClick)="handleActionClick()" />
4141

4242
<!-- My Meetings -->
4343
<lfx-my-meetings class="h-full" />

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

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import { SelectComponent } from '@components/select/select.component';
88
import { Account, PendingActionItem } from '@lfx-one/shared/interfaces';
99
import { AccountContextService } from '@services/account-context.service';
1010
import { FeatureFlagService } from '@services/feature-flag.service';
11+
import { HiddenActionsService } from '@services/hidden-actions.service';
1112
import { ProjectContextService } from '@services/project-context.service';
1213
import { ProjectService } from '@services/project.service';
13-
import { catchError, of, switchMap } from 'rxjs';
14+
import { BehaviorSubject, catchError, of, switchMap } from 'rxjs';
1415

1516
import { FoundationHealthComponent } from '../components/foundation-health/foundation-health.component';
1617
import { MyMeetingsComponent } from '../components/my-meetings/my-meetings.component';
@@ -28,6 +29,7 @@ export class BoardMemberDashboardComponent {
2829
private readonly projectContextService = inject(ProjectContextService);
2930
private readonly projectService = inject(ProjectService);
3031
private readonly featureFlagService = inject(FeatureFlagService);
32+
private readonly hiddenActionsService = inject(HiddenActionsService);
3133

3234
public readonly form = new FormGroup({
3335
selectedAccountId: new FormControl<string>(this.accountContextService.selectedAccount().accountId),
@@ -36,6 +38,8 @@ export class BoardMemberDashboardComponent {
3638
public readonly availableAccounts: Signal<Account[]> = computed(() => this.accountContextService.availableAccounts);
3739
public readonly selectedFoundation = computed(() => this.projectContextService.selectedFoundation());
3840
public readonly selectedProject = computed(() => this.projectContextService.selectedProject() || this.projectContextService.selectedFoundation());
41+
public readonly refresh$: BehaviorSubject<void> = new BehaviorSubject<void>(undefined);
42+
private readonly rawBoardMemberActions: Signal<PendingActionItem[]>;
3943
public readonly boardMemberActions: Signal<PendingActionItem[]>;
4044

4145
// Feature flags
@@ -54,27 +58,41 @@ export class BoardMemberDashboardComponent {
5458
});
5559

5660
// Initialize board member actions with reactive pattern
57-
this.boardMemberActions = this.initializeBoardMemberActions();
61+
this.rawBoardMemberActions = this.initializeBoardMemberActions();
62+
63+
// Create filtered signal that removes hidden actions
64+
this.boardMemberActions = computed(() => {
65+
return this.rawBoardMemberActions().filter((item) => !this.hiddenActionsService.isActionHidden(item));
66+
});
67+
}
68+
69+
public handleActionClick(): void {
70+
this.refresh$.next();
5871
}
5972

6073
private initializeBoardMemberActions(): Signal<PendingActionItem[]> {
6174
// Convert project signal to observable to react to changes (handles both project and foundation)
6275
const project$ = toObservable(this.selectedProject);
6376

6477
return toSignal(
65-
project$.pipe(
66-
switchMap((project) => {
67-
// If no project/foundation selected, return empty array
68-
if (!project?.slug) {
69-
return of([]);
70-
}
78+
this.refresh$.pipe(
79+
takeUntilDestroyed(),
80+
switchMap(() => {
81+
return project$.pipe(
82+
switchMap((project) => {
83+
// If no project/foundation selected, return empty array
84+
if (!project?.slug) {
85+
return of([]);
86+
}
7187

72-
// Fetch survey actions from API
73-
return this.projectService.getPendingActionSurveys(project.slug).pipe(
74-
catchError((error) => {
75-
console.error('Failed to fetch survey actions:', error);
76-
// Return empty array on error
77-
return of([]);
88+
// Fetch survey actions from API
89+
return this.projectService.getPendingActionSurveys(project.slug).pipe(
90+
catchError((error) => {
91+
console.error('Failed to fetch survey actions:', error);
92+
// Return empty array on error
93+
return of([]);
94+
})
95+
);
7896
})
7997
);
8098
})

apps/lfx-one/src/app/modules/dashboards/components/pending-actions/pending-actions.component.html

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ <h2 class="py-1">Pending Actions</h2>
2121
'bg-purple-50': item.color === 'purple',
2222
}"
2323
[attr.data-testid]="'dashboard-pending-actions-item-' + item.type">
24-
<!-- Header with Type and Badge -->
24+
<!-- Header with Type -->
2525
<div class="flex items-start justify-between mb-3">
2626
<div class="flex items-center gap-2">
2727
<div
@@ -44,11 +44,6 @@ <h2 class="py-1">Pending Actions</h2>
4444
{{ item.type }}
4545
</span>
4646
</div>
47-
<lfx-tag
48-
[value]="item.badge"
49-
severity="secondary"
50-
styleClass="bg-white border border-gray-200"
51-
data-testid="dashboard-pending-actions-badge"></lfx-tag>
5247
</div>
5348

5449
<!-- Action Text -->
@@ -67,22 +62,27 @@ <h2 class="py-1">Pending Actions</h2>
6762

6863
<!-- Action Button -->
6964
@if (item.buttonLink) {
70-
<a
65+
<lfx-button
66+
size="small"
67+
class="w-full"
68+
styleClass="w-full"
69+
severity="secondary"
70+
rel="noopener noreferrer"
71+
[link]="true"
7172
[href]="item.buttonLink"
7273
target="_blank"
73-
rel="noopener noreferrer"
74-
class="w-full h-8 rounded-md px-3 text-sm font-medium border border-gray-300 bg-white text-gray-700 hover:bg-slate-100 transition-colors flex items-center justify-center"
75-
data-testid="dashboard-pending-actions-button">
76-
{{ item.buttonText }}
77-
</a>
74+
(onClick)="handleActionClick(item)"
75+
[label]="item.buttonText">
76+
</lfx-button>
7877
} @else {
79-
<button
80-
type="button"
81-
(click)="handleActionClick(item)"
82-
class="w-full h-8 rounded-md px-3 text-sm font-medium border border-gray-300 bg-white text-gray-700 hover:bg-slate-100 transition-colors"
83-
data-testid="dashboard-pending-actions-button">
84-
{{ item.buttonText }}
85-
</button>
78+
<lfx-button
79+
size="small"
80+
class="w-full"
81+
styleClass="w-full text-sm"
82+
severity="secondary"
83+
(onClick)="handleActionClick(item)"
84+
[label]="item.buttonText">
85+
</lfx-button>
8686
}
8787
</div>
8888
}

apps/lfx-one/src/app/modules/dashboards/components/pending-actions/pending-actions.component.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,28 @@
22
// SPDX-License-Identifier: MIT
33

44
import { CommonModule } from '@angular/common';
5-
import { Component, input, output } from '@angular/core';
6-
import { TagComponent } from '@components/tag/tag.component';
5+
import { Component, inject, input, output } from '@angular/core';
6+
import { ButtonComponent } from '@components/button/button.component';
7+
import { HiddenActionsService } from '@shared/services/hidden-actions.service';
78

89
import type { PendingActionItem } from '@lfx-one/shared/interfaces';
910

1011
@Component({
1112
selector: 'lfx-pending-actions',
1213
standalone: true,
13-
imports: [CommonModule, TagComponent],
14+
imports: [CommonModule, ButtonComponent],
1415
templateUrl: './pending-actions.component.html',
1516
styleUrl: './pending-actions.component.scss',
1617
})
1718
export class PendingActionsComponent {
18-
/**
19-
* Required input signal for pending action items
20-
*/
19+
private readonly hiddenActionsService = inject(HiddenActionsService);
20+
2121
public readonly pendingActions = input.required<PendingActionItem[]>();
2222

2323
public readonly actionClick = output<PendingActionItem>();
2424

2525
protected handleActionClick(item: PendingActionItem): void {
26+
this.hiddenActionsService.hideAction(item);
2627
this.actionClick.emit(item);
2728
}
2829
}

apps/lfx-one/src/app/modules/dashboards/core-developer/core-developer-dashboard.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ <h1>{{ selectedFoundation()?.name }} Overview</h1>
1717
<!-- Middle Row - Two Cards -->
1818
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
1919
<!-- Pending Actions -->
20-
<lfx-pending-actions class="h-full" [pendingActions]="coreDevActions()" />
20+
<lfx-pending-actions class="h-full" [pendingActions]="coreDevActions()" (actionClick)="handleActionClick()" />
2121

2222
<!-- My Meetings -->
2323
<lfx-my-meetings class="h-full" />

apps/lfx-one/src/app/modules/dashboards/core-developer/core-developer-dashboard.component.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

44
import { Component, computed, inject, signal } from '@angular/core';
55
import { CORE_DEVELOPER_ACTION_ITEMS } from '@lfx-one/shared/constants';
6+
import { HiddenActionsService } from '@services/hidden-actions.service';
67
import { ProjectContextService } from '@services/project-context.service';
8+
import { BehaviorSubject } from 'rxjs';
79

810
import { MyMeetingsComponent } from '../components/my-meetings/my-meetings.component';
911
import { MyProjectsComponent } from '../components/my-projects/my-projects.component';
@@ -19,7 +21,16 @@ import { RecentProgressComponent } from '../components/recent-progress/recent-pr
1921
})
2022
export class CoreDeveloperDashboardComponent {
2123
private readonly projectContextService = inject(ProjectContextService);
24+
private readonly hiddenActionsService = inject(HiddenActionsService);
2225

2326
public readonly selectedFoundation = computed(() => this.projectContextService.selectedFoundation());
24-
public readonly coreDevActions = signal(CORE_DEVELOPER_ACTION_ITEMS);
27+
public readonly refresh$: BehaviorSubject<void> = new BehaviorSubject<void>(undefined);
28+
private readonly rawCoreDevActions = signal(CORE_DEVELOPER_ACTION_ITEMS);
29+
public readonly coreDevActions = computed(() => {
30+
return this.rawCoreDevActions().filter((item) => !this.hiddenActionsService.isActionHidden(item));
31+
});
32+
33+
public handleActionClick(): void {
34+
this.refresh$.next();
35+
}
2536
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ <h1>{{ selectedProject()?.name }} Overview</h1>
1717
<!-- Middle Row - Two Cards -->
1818
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
1919
<!-- Pending Actions -->
20-
<lfx-pending-actions class="h-full" [pendingActions]="maintainerActions()" />
20+
<lfx-pending-actions class="h-full" [pendingActions]="maintainerActions()" (actionClick)="handleActionClick()" />
2121

2222
<!-- My Meetings -->
2323
<lfx-my-meetings class="h-full" />

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

44
import { Component, computed, inject, signal } from '@angular/core';
55
import { MAINTAINER_ACTION_ITEMS } from '@lfx-one/shared/constants';
6+
import { HiddenActionsService } from '@services/hidden-actions.service';
67
import { ProjectContextService } from '@services/project-context.service';
8+
import { BehaviorSubject } from 'rxjs';
79

810
import { MyMeetingsComponent } from '../components/my-meetings/my-meetings.component';
911
import { MyProjectsComponent } from '../components/my-projects/my-projects.component';
@@ -19,7 +21,16 @@ import { RecentProgressComponent } from '../components/recent-progress/recent-pr
1921
})
2022
export class MaintainerDashboardComponent {
2123
private readonly projectContextService = inject(ProjectContextService);
24+
private readonly hiddenActionsService = inject(HiddenActionsService);
2225

2326
public readonly selectedProject = computed(() => this.projectContextService.selectedFoundation() || this.projectContextService.selectedProject());
24-
public readonly maintainerActions = signal(MAINTAINER_ACTION_ITEMS);
27+
public readonly refresh$: BehaviorSubject<void> = new BehaviorSubject<void>(undefined);
28+
private readonly rawMaintainerActions = signal(MAINTAINER_ACTION_ITEMS);
29+
public readonly maintainerActions = computed(() => {
30+
return this.rawMaintainerActions().filter((item) => !this.hiddenActionsService.isActionHidden(item));
31+
});
32+
33+
public handleActionClick(): void {
34+
this.refresh$.next();
35+
}
2536
}

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

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,21 +61,31 @@
6161
tooltip="Edit Meeting"
6262
[routerLink]="['/meetings', meeting().uid, 'edit']"></lfx-button>
6363

64-
@if (!pastMeeting()) {
65-
<lfx-button
66-
[icon]="loading() ? 'fa-light fa-circle-notch fa-spin' : 'fa-light fa-ellipsis'"
67-
[text]="true"
68-
[rounded]="true"
69-
size="small"
70-
severity="secondary"
71-
data-testid="meeting-actions-button"
72-
(click)="actionMenu.toggle($event)"></lfx-button>
73-
}
64+
<lfx-button
65+
icon="fa-light fa-people-group"
66+
[text]="true"
67+
[rounded]="true"
68+
size="small"
69+
severity="secondary"
70+
data-testid="committees-button"
71+
(click)="openCommitteeModal()"
72+
[tooltip]="
73+
meeting().committees && meeting().committees!.length > 0 ? 'Manage ' + committeeLabel.plural : 'Connect ' + committeeLabel.plural
74+
"></lfx-button>
75+
}
76+
@if (meeting().organizer) {
77+
<lfx-button
78+
icon="fa-light fa-trash"
79+
[text]="true"
80+
[rounded]="true"
81+
size="small"
82+
severity="secondary"
83+
data-testid="delete-meeting-button"
84+
(click)="deleteMeeting()"
85+
tooltip="Delete Meeting"></lfx-button>
7486
}
7587
</div>
7688
</div>
77-
<!-- Action Menu -->
78-
<lfx-menu #actionMenu [model]="actionMenuItems()" [popup]="true" appendTo="body"></lfx-menu>
7989

8090
<!-- Meeting Title -->
8191
<div class="flex items-center gap-2 mb-2" data-testid="meeting-title-section">

0 commit comments

Comments
 (0)