Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
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
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"PostgreSQL",
"PostgREST",
"primeng",
"sparkline",
"styleclass",
"supabase",
"timegrid",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!-- Copyright The Linux Foundation and each contributor to LFX. -->
<!-- SPDX-License-Identifier: MIT -->

<div class="container mx-auto px-4 sm:px-6 lg:px-8" data-testid="dashboard-container">
<!-- Organization Selector -->
<div class="mb-6 flex items-center gap-4" data-testid="organization-selector">
<label for="organization-select" class="text-sm font-semibold text-gray-700">Organization:</label>
<lfx-select
[form]="accountForm"
control="selectedAccountId"
[options]="availableAccounts()"
optionLabel="accountName"
optionValue="accountId"
[filter]="true"
filterPlaceholder="Search organizations..."
placeholder="Select an organization"
[showClear]="false"
styleClass="min-w-[300px]"
inputId="organization-select"
data-testid="organization-select"
(onChange)="handleAccountChange($event)" />
</div>

<!-- Dashboard Sections -->
<div class="flex flex-col gap-6" data-testid="dashboard-sections-grid">
<!-- Organization Involvement - Full Width -->
<lfx-organization-involvement />

<!-- Middle Row - Two Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- My Meetings -->
<lfx-my-meetings class="h-full" />

<!-- Pending Actions -->
<lfx-pending-actions class="h-full" />
</div>

<!-- Foundation Health - Full Width -->
<lfx-foundation-health />
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Copyright The Linux Foundation and each contributor to LFX.
// SPDX-License-Identifier: MIT
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright The Linux Foundation and each contributor to LFX.
// SPDX-License-Identifier: MIT

import { Component, computed, inject, Signal } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { Account } from '@lfx-one/shared/interfaces';
import { SelectComponent } from '../../../shared/components/select/select.component';
import { AccountContextService } from '../../../shared/services/account-context.service';
import { FoundationHealthComponent } from '../components/foundation-health/foundation-health.component';
import { MyMeetingsComponent } from '../components/my-meetings/my-meetings.component';
import { OrganizationInvolvementComponent } from '../components/organization-involvement/organization-involvement.component';
import { PendingActionsComponent } from '../components/pending-actions/pending-actions.component';

@Component({
selector: 'lfx-board-member-dashboard',
imports: [OrganizationInvolvementComponent, PendingActionsComponent, MyMeetingsComponent, FoundationHealthComponent, SelectComponent, ReactiveFormsModule],
templateUrl: './board-member-dashboard.component.html',
styleUrl: './board-member-dashboard.component.scss',
})
export class BoardMemberDashboardComponent {
private readonly accountContextService = inject(AccountContextService);

protected readonly accountForm = new FormGroup({
selectedAccountId: new FormControl<string>(this.accountContextService.selectedAccount().accountId),
});

protected readonly availableAccounts: Signal<Account[]> = computed(() => this.accountContextService.availableAccounts);
Comment thread
asithade marked this conversation as resolved.

/**
* Handle account selection change
*/
protected handleAccountChange(event: any): void {
const selectedAccountId = event.value as string;
const selectedAccount = this.accountContextService.availableAccounts.find((acc) => acc.accountId === selectedAccountId);
if (selectedAccount) {
this.accountContextService.setAccount(selectedAccount);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
<!-- Copyright The Linux Foundation and each contributor to LFX. -->
<!-- SPDX-License-Identifier: MIT -->

<section data-testid="dashboard-foundation-health-section">
<!-- Header with title and optional View All button -->
<div class="flex items-center justify-between mb-4">
<h2 class="font-['Roboto_Slab'] font-semibold text-[16px]">Foundation Health</h2>
@if (onViewAll()) {
<button
class="flex items-center gap-1 px-3 py-1.5 text-sm text-gray-700 hover:bg-gray-100 rounded transition-colors"
(click)="onViewAll()!()"
data-testid="foundation-health-view-all">
View All
<i class="fa-light fa-chevron-right w-4 h-4"></i>
</button>
}
</div>

<!-- Foundation Health Table -->
<div class="bg-white rounded-lg border border-slate-200">
<div class="overflow-x-auto">
<table class="w-full">
<thead>
<tr class="border-b border-border">
<th class="sticky left-0 z-10 bg-white text-left py-2 px-6 text-xs font-medium text-muted-foreground min-w-[200px] border-r-2 border-slate-200">
Foundation
</th>
<th class="text-left py-2 px-3 text-xs font-medium text-gray-500 min-w-[140px]">
<div class="flex items-center gap-1">
Health Score
<i class="fa-light fa-circle-question w-3 h-3 cursor-help" title="Overall health score of the foundation"></i>
</div>
</th>
<th class="text-left py-2 px-3 text-xs font-medium text-gray-500 min-w-[140px]">
<div class="flex items-center gap-1">
Software Value
<i class="fa-light fa-circle-question w-3 h-3 cursor-help" title="Estimated total value of software managed by the foundation"></i>
</div>
</th>
<th class="text-left py-2 px-3 text-xs font-medium text-gray-500 min-w-[140px]">
<div class="flex items-center gap-1">
Total Members
<i class="fa-light fa-circle-question w-3 h-3 cursor-help" title="Total number of member organizations in the foundation"></i>
</div>
</th>
<th class="text-left py-2 px-3 text-xs font-medium text-gray-500 min-w-[140px]">
<div class="flex items-center gap-1">
Active Contributors
<i class="fa-light fa-circle-question w-3 h-3 cursor-help" title="Average number of active contributors over the past year"></i>
</div>
</th>
<th class="text-left py-2 px-3 text-xs font-medium text-gray-500 min-w-[140px]">
<div class="flex items-center gap-1">
Maintainers
<i class="fa-light fa-circle-question w-3 h-3 cursor-help" title="Average number of project maintainers over the past year"></i>
</div>
</th>
<th class="text-left py-2 px-3 text-xs font-medium text-gray-500 min-w-[140px]">
<div class="flex items-center gap-1">
Events
<i class="fa-light fa-circle-question w-3 h-3 cursor-help" title="Total number of events hosted by the foundation this year"></i>
</div>
</th>
<th class="text-left py-2 px-3 text-xs font-medium text-gray-500 min-w-[180px]">
<div class="flex items-center gap-1">
Org Dependency Risk
<i
class="fa-light fa-circle-question w-3 h-3 cursor-help"
title="Risk level based on concentration of contributions from top organizations"></i>
</div>
</th>
</tr>
</thead>
<tbody>
@for (foundation of foundations(); track foundation.id) {
<tr class="border-b border-border last:border-b-0" [attr.data-testid]="'foundation-row-' + foundation.id">
<!-- Foundation Name Column (Sticky) -->
<td class="sticky left-0 z-10 bg-white py-3 px-6 border-r-2 border-slate-200">
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded-full overflow-hidden flex-shrink-0 bg-white p-1">
<img [src]="foundation.logo" [alt]="foundation.name + ' logo'" class="w-full h-full object-contain" />
</div>
<div class="min-w-0">
<div class="font-medium text-sm text-[#009aff] truncate">{{ foundation.name }}</div>
@if (foundation.projectBreakdown) {
<div class="text-xs text-gray-500">
<div>{{ foundation.projectBreakdown.sandbox }} sandbox</div>
<div>{{ foundation.projectBreakdown.incubating }} incubating</div>
<div>{{ foundation.projectBreakdown.graduated }} graduated</div>
</div>
} @else {
<div class="text-xs text-gray-500">{{ foundation.projectCount }} projects</div>
}
</div>
</div>
</td>

<!-- Health Score -->
<td class="py-3 px-3">
<lfx-health-score-tag [score]="foundation.healthScore"></lfx-health-score-tag>
</td>

<!-- Software Value -->
<td class="py-3 px-3">
<div class="text-sm font-medium whitespace-nowrap">
{{ foundation.softwareValueFormatted }}
</div>
</td>

<!-- Total Members -->
<td class="py-3 px-3">
<div class="text-sm font-medium whitespace-nowrap">
{{ foundation.totalMembersFormatted }}
</div>
</td>

<!-- Active Contributors with Sparkline -->
<td class="py-3 px-3">
<div class="flex items-center gap-2">
<div class="w-[60px] h-6 flex-shrink-0">
<lfx-chart type="line" [data]="foundation.activeContributorsChartData" [options]="sparklineOptions" height="100%"> </lfx-chart>
</div>
<div class="text-sm font-medium whitespace-nowrap">
{{ foundation.activeContributorsAvg }}
</div>
</div>
</td>

<!-- Maintainers with Sparkline -->
<td class="py-3 px-3">
<div class="flex items-center gap-2">
<div class="w-[60px] h-6 flex-shrink-0">
<lfx-chart type="line" [data]="foundation.maintainersChartData" [options]="sparklineOptions" height="100%"> </lfx-chart>
</div>
<div class="text-sm font-medium whitespace-nowrap">
{{ foundation.maintainersAvg }}
</div>
</div>
</td>

<!-- Events with Monthly Bar Chart -->
<td class="py-3 px-3">
<div class="flex items-center gap-2">
<div class="w-[80px] flex-shrink-0">
<div class="flex items-end gap-0.5 h-10">
@for (height of foundation.barHeights; track $index) {
<div class="flex-1 bg-[#0094FF] rounded-sm min-w-[3px]" [style.height.%]="height" [attr.data-testid]="'event-bar-' + $index"></div>
}
</div>
</div>
<div class="text-sm font-medium whitespace-nowrap">
{{ foundation.eventsTotal }}
</div>
</div>
</td>

<!-- Org Dependency Risk with Pie Chart -->
<td class="py-3 px-3">
<div class="flex items-center gap-3">
<div class="relative w-10 h-10 flex-shrink-0">
<svg class="w-10 h-10" viewBox="0 0 40 40">
<!-- Other orgs slice (light grey) -->
<path [attr.d]="foundation.pieChartPaths.otherPath" fill="#E5E7EB" />
<!-- Top orgs slice (risk color) -->
<path [attr.d]="foundation.pieChartPaths.topPath" [attr.fill]="foundation.orgDependencyColor" />
</svg>
</div>
<div class="flex flex-col gap-0.5 min-w-0">
<div class="text-sm font-medium" [ngClass]="foundation.orgDependencyTextColorClass">
{{ foundation.orgDependency.topOrgsCount }} orgs: {{ foundation.orgDependency.topOrgsPercentage }}%
</div>
<div class="text-xs text-gray-500">
{{ foundation.orgDependency.otherOrgsCount }} orgs: {{ foundation.orgDependency.otherOrgsPercentage }}%
</div>
</div>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</section>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright The Linux Foundation and each contributor to LFX.
// SPDX-License-Identifier: MIT

:host {
display: block;
}
Loading