Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ <h1>{{ selectedFoundation()?.name }} Overview</h1>
<!-- Middle Row - Two Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Pending Actions -->
<lfx-pending-actions class="h-full" [pendingActions]="boardMemberActions()" />
<lfx-pending-actions class="h-full" [pendingActions]="boardMemberActions()" (actionClick)="handleActionClick()" />

<!-- My Meetings -->
<lfx-my-meetings class="h-full" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import { SelectComponent } from '@components/select/select.component';
import { Account, PendingActionItem } from '@lfx-one/shared/interfaces';
import { AccountContextService } from '@services/account-context.service';
import { FeatureFlagService } from '@services/feature-flag.service';
import { HiddenActionsService } from '@services/hidden-actions.service';
import { ProjectContextService } from '@services/project-context.service';
import { ProjectService } from '@services/project.service';
import { catchError, of, switchMap } from 'rxjs';
import { BehaviorSubject, catchError, of, switchMap } from 'rxjs';

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

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

// Feature flags
Expand All @@ -54,27 +58,41 @@ export class BoardMemberDashboardComponent {
});

// Initialize board member actions with reactive pattern
this.boardMemberActions = this.initializeBoardMemberActions();
this.rawBoardMemberActions = this.initializeBoardMemberActions();

// Create filtered signal that removes hidden actions
this.boardMemberActions = computed(() => {
return this.rawBoardMemberActions().filter((item) => !this.hiddenActionsService.isActionHidden(item));
});
}

public handleActionClick(): void {
this.refresh$.next();
}

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

return toSignal(
project$.pipe(
switchMap((project) => {
// If no project/foundation selected, return empty array
if (!project?.slug) {
return of([]);
}
this.refresh$.pipe(
takeUntilDestroyed(),
switchMap(() => {
return project$.pipe(
switchMap((project) => {
// If no project/foundation selected, return empty array
if (!project?.slug) {
return of([]);
}

// Fetch survey actions from API
return this.projectService.getPendingActionSurveys(project.slug).pipe(
catchError((error) => {
console.error('Failed to fetch survey actions:', error);
// Return empty array on error
return of([]);
// Fetch survey actions from API
return this.projectService.getPendingActionSurveys(project.slug).pipe(
catchError((error) => {
console.error('Failed to fetch survey actions:', error);
// Return empty array on error
return of([]);
})
);
})
);
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ <h2 class="py-1">Pending Actions</h2>
'bg-purple-50': item.color === 'purple',
}"
[attr.data-testid]="'dashboard-pending-actions-item-' + item.type">
<!-- Header with Type and Badge -->
<!-- Header with Type -->
<div class="flex items-start justify-between mb-3">
<div class="flex items-center gap-2">
<div
Expand All @@ -44,11 +44,6 @@ <h2 class="py-1">Pending Actions</h2>
{{ item.type }}
</span>
</div>
<lfx-tag
[value]="item.badge"
severity="secondary"
styleClass="bg-white border border-gray-200"
data-testid="dashboard-pending-actions-badge"></lfx-tag>
</div>

<!-- Action Text -->
Expand All @@ -67,22 +62,28 @@ <h2 class="py-1">Pending Actions</h2>

<!-- Action Button -->
@if (item.buttonLink) {
<a
<lfx-button
size="small"
class="w-full"
styleClass="w-full"
severity="secondary"
rel="noopener noreferrer"
[link]="true"
[href]="item.buttonLink"
target="_blank"
rel="noopener noreferrer"
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"
data-testid="dashboard-pending-actions-button">
{{ item.buttonText }}
</a>
ariaLabel="Hide action for 24 hours"
(onClick)="handleActionClick(item)"
[label]="item.buttonText">
</lfx-button>
} @else {
<button
type="button"
(click)="handleActionClick(item)"
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"
data-testid="dashboard-pending-actions-button">
{{ item.buttonText }}
</button>
<lfx-button
size="small"
class="w-full"
styleClass="w-full text-sm"
severity="secondary"
(onClick)="handleActionClick(item)"
[label]="item.buttonText">
</lfx-button>
}
</div>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,32 @@
// SPDX-License-Identifier: MIT

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

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

@Component({
selector: 'lfx-pending-actions',
standalone: true,
imports: [CommonModule, TagComponent],
imports: [CommonModule, ButtonComponent],
templateUrl: './pending-actions.component.html',
styleUrl: './pending-actions.component.scss',
})
export class PendingActionsComponent {
/**
* Required input signal for pending action items
*/
private readonly hiddenActionsService = inject(HiddenActionsService);

public readonly pendingActions = input.required<PendingActionItem[]>();

public readonly actionClick = output<PendingActionItem>();

protected handleActionClick(item: PendingActionItem): void {
this.hiddenActionsService.hideAction(item);
this.actionClick.emit(item);
}

protected handleDismiss(item: PendingActionItem): void {
this.hiddenActionsService.hideAction(item);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import { Component, computed, inject, signal } from '@angular/core';
import { CORE_DEVELOPER_ACTION_ITEMS } from '@lfx-one/shared/constants';
import { HiddenActionsService } from '@services/hidden-actions.service';
import { ProjectContextService } from '@services/project-context.service';

import { MyMeetingsComponent } from '../components/my-meetings/my-meetings.component';
Expand All @@ -19,7 +20,11 @@ import { RecentProgressComponent } from '../components/recent-progress/recent-pr
})
export class CoreDeveloperDashboardComponent {
private readonly projectContextService = inject(ProjectContextService);
private readonly hiddenActionsService = inject(HiddenActionsService);

public readonly selectedFoundation = computed(() => this.projectContextService.selectedFoundation());
public readonly coreDevActions = signal(CORE_DEVELOPER_ACTION_ITEMS);
private readonly rawCoreDevActions = signal(CORE_DEVELOPER_ACTION_ITEMS);
public readonly coreDevActions = computed(() => {
return this.rawCoreDevActions().filter((item) => !this.hiddenActionsService.isActionHidden(item));
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import { Component, computed, inject, signal } from '@angular/core';
import { MAINTAINER_ACTION_ITEMS } from '@lfx-one/shared/constants';
import { HiddenActionsService } from '@services/hidden-actions.service';
import { ProjectContextService } from '@services/project-context.service';

import { MyMeetingsComponent } from '../components/my-meetings/my-meetings.component';
Expand All @@ -19,7 +20,11 @@ import { RecentProgressComponent } from '../components/recent-progress/recent-pr
})
export class MaintainerDashboardComponent {
private readonly projectContextService = inject(ProjectContextService);
private readonly hiddenActionsService = inject(HiddenActionsService);

public readonly selectedProject = computed(() => this.projectContextService.selectedFoundation() || this.projectContextService.selectedProject());
public readonly maintainerActions = signal(MAINTAINER_ACTION_ITEMS);
private readonly rawMaintainerActions = signal(MAINTAINER_ACTION_ITEMS);
public readonly maintainerActions = computed(() => {
return this.rawMaintainerActions().filter((item) => !this.hiddenActionsService.isActionHidden(item));
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,21 +61,31 @@
tooltip="Edit Meeting"
[routerLink]="['/meetings', meeting().uid, 'edit']"></lfx-button>

@if (!pastMeeting()) {
<lfx-button
[icon]="loading() ? 'fa-light fa-circle-notch fa-spin' : 'fa-light fa-ellipsis'"
[text]="true"
[rounded]="true"
size="small"
severity="secondary"
data-testid="meeting-actions-button"
(click)="actionMenu.toggle($event)"></lfx-button>
}
<lfx-button
icon="fa-light fa-people-group"
[text]="true"
[rounded]="true"
size="small"
severity="secondary"
data-testid="committees-button"
(click)="openCommitteeModal()"
[tooltip]="
meeting().committees && meeting().committees!.length > 0 ? 'Manage ' + committeeLabel.plural : 'Connect ' + committeeLabel.plural
"></lfx-button>
}
@if (meeting().organizer) {
<lfx-button
icon="fa-light fa-trash"
[text]="true"
[rounded]="true"
size="small"
severity="secondary"
data-testid="delete-meeting-button"
(click)="deleteMeeting()"
tooltip="Delete Meeting"></lfx-button>
}
</div>
</div>
<!-- Action Menu -->
<lfx-menu #actionMenu [model]="actionMenuItems()" [popup]="true" appendTo="body"></lfx-menu>

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