Skip to content

Commit 529a49e

Browse files
committed
feat(dashboards): add board member persona with snowflake integration
Implement Board Member persona dashboard with three key components: - Organization Involvement with Snowflake integration - Foundation Health metrics - Pending Actions (persona-aware) Backend changes: - Add organization maintainers endpoint - Add membership tier endpoint - Integrate Snowflake queries with proper error handling Frontend changes: - Create board-member-dashboard component - Create organization-involvement component with loading state - Create foundation-health component - Create health-score-tag component - Add persona routing and navigation Shared package updates: - Add OrganizationMaintainersResponse interface - Add MembershipTierResponse interface - Add organization involvement constants - Add foundation health constants - Update persona configuration LFXV2-687 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <asithade@gmail.com>
1 parent 42e9854 commit 529a49e

27 files changed

+1540
-4
lines changed

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"PostgreSQL",
2626
"PostgREST",
2727
"primeng",
28+
"sparkline",
2829
"styleclass",
2930
"supabase",
3031
"timegrid",
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<!-- Copyright The Linux Foundation and each contributor to LFX. -->
2+
<!-- SPDX-License-Identifier: MIT -->
3+
4+
<div class="container mx-auto px-4 sm:px-6 lg:px-8" data-testid="dashboard-container">
5+
<!-- Dashboard Sections -->
6+
<div class="flex flex-col gap-6" data-testid="dashboard-sections-grid">
7+
<!-- Organization Involvement - Full Width -->
8+
<lfx-organization-involvement />
9+
10+
<!-- Middle Row - Two Cards -->
11+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
12+
<!-- My Meetings -->
13+
<lfx-my-meetings class="h-full" />
14+
15+
<!-- Pending Actions -->
16+
<lfx-pending-actions class="h-full" />
17+
</div>
18+
19+
<!-- Foundation Health - Full Width -->
20+
<lfx-foundation-health />
21+
</div>
22+
</div>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Copyright The Linux Foundation and each contributor to LFX.
2+
// SPDX-License-Identifier: MIT
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright The Linux Foundation and each contributor to LFX.
2+
// SPDX-License-Identifier: MIT
3+
4+
import { Component } from '@angular/core';
5+
import { FoundationHealthComponent } from '../components/foundation-health/foundation-health.component';
6+
import { MyMeetingsComponent } from '../components/my-meetings/my-meetings.component';
7+
import { OrganizationInvolvementComponent } from '../components/organization-involvement/organization-involvement.component';
8+
import { PendingActionsComponent } from '../components/pending-actions/pending-actions.component';
9+
10+
@Component({
11+
selector: 'lfx-board-member-dashboard',
12+
imports: [OrganizationInvolvementComponent, PendingActionsComponent, MyMeetingsComponent, FoundationHealthComponent],
13+
templateUrl: './board-member-dashboard.component.html',
14+
styleUrl: './board-member-dashboard.component.scss',
15+
})
16+
export class BoardMemberDashboardComponent {}
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
<!-- Copyright The Linux Foundation and each contributor to LFX. -->
2+
<!-- SPDX-License-Identifier: MIT -->
3+
4+
<section data-testid="dashboard-foundation-health-section">
5+
<!-- Header with title and optional View All button -->
6+
<div class="flex items-center justify-between mb-4">
7+
<h2 class="font-['Roboto_Slab'] font-semibold text-[16px]">Foundation Health</h2>
8+
@if (onViewAll()) {
9+
<button
10+
class="flex items-center gap-1 px-3 py-1.5 text-sm text-gray-700 hover:bg-gray-100 rounded transition-colors"
11+
(click)="onViewAll()!()"
12+
data-testid="foundation-health-view-all">
13+
View All
14+
<i class="fa-light fa-chevron-right w-4 h-4"></i>
15+
</button>
16+
}
17+
</div>
18+
19+
<!-- Foundation Health Table -->
20+
<div class="bg-white rounded-lg border border-slate-200">
21+
<div class="overflow-x-auto">
22+
<table class="w-full">
23+
<thead>
24+
<tr class="border-b border-border">
25+
<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">
26+
Foundation
27+
</th>
28+
<th class="text-left py-2 px-3 text-xs font-medium text-gray-500 min-w-[140px]">
29+
<div class="flex items-center gap-1">
30+
Health Score
31+
<i class="fa-light fa-circle-question w-3 h-3 cursor-help" title="Overall health score of the foundation"></i>
32+
</div>
33+
</th>
34+
<th class="text-left py-2 px-3 text-xs font-medium text-gray-500 min-w-[140px]">
35+
<div class="flex items-center gap-1">
36+
Software Value
37+
<i class="fa-light fa-circle-question w-3 h-3 cursor-help" title="Estimated total value of software managed by the foundation"></i>
38+
</div>
39+
</th>
40+
<th class="text-left py-2 px-3 text-xs font-medium text-gray-500 min-w-[140px]">
41+
<div class="flex items-center gap-1">
42+
Total Members
43+
<i class="fa-light fa-circle-question w-3 h-3 cursor-help" title="Total number of member organizations in the foundation"></i>
44+
</div>
45+
</th>
46+
<th class="text-left py-2 px-3 text-xs font-medium text-gray-500 min-w-[140px]">
47+
<div class="flex items-center gap-1">
48+
Active Contributors
49+
<i class="fa-light fa-circle-question w-3 h-3 cursor-help" title="Average number of active contributors over the past year"></i>
50+
</div>
51+
</th>
52+
<th class="text-left py-2 px-3 text-xs font-medium text-gray-500 min-w-[140px]">
53+
<div class="flex items-center gap-1">
54+
Maintainers
55+
<i class="fa-light fa-circle-question w-3 h-3 cursor-help" title="Average number of project maintainers over the past year"></i>
56+
</div>
57+
</th>
58+
<th class="text-left py-2 px-3 text-xs font-medium text-gray-500 min-w-[140px]">
59+
<div class="flex items-center gap-1">
60+
Events
61+
<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>
62+
</div>
63+
</th>
64+
<th class="text-left py-2 px-3 text-xs font-medium text-gray-500 min-w-[180px]">
65+
<div class="flex items-center gap-1">
66+
Org Dependency Risk
67+
<i
68+
class="fa-light fa-circle-question w-3 h-3 cursor-help"
69+
title="Risk level based on concentration of contributions from top organizations"></i>
70+
</div>
71+
</th>
72+
</tr>
73+
</thead>
74+
<tbody>
75+
@for (foundation of foundations(); track foundation.id) {
76+
<tr class="border-b border-border last:border-b-0" [attr.data-testid]="'foundation-row-' + foundation.id">
77+
<!-- Foundation Name Column (Sticky) -->
78+
<td class="sticky left-0 z-10 bg-white py-3 px-6 border-r-2 border-slate-200">
79+
<div class="flex items-center gap-3">
80+
<div class="w-8 h-8 rounded-full overflow-hidden flex-shrink-0 bg-white p-1">
81+
<img [src]="foundation.logo" [alt]="foundation.name + ' logo'" class="w-full h-full object-contain" />
82+
</div>
83+
<div class="min-w-0">
84+
<div class="font-medium text-sm text-[#009aff] truncate">{{ foundation.name }}</div>
85+
@if (foundation.projectBreakdown) {
86+
<div class="text-xs text-gray-500">
87+
<div>{{ foundation.projectBreakdown.sandbox }} sandbox</div>
88+
<div>{{ foundation.projectBreakdown.incubating }} incubating</div>
89+
<div>{{ foundation.projectBreakdown.graduated }} graduated</div>
90+
</div>
91+
} @else {
92+
<div class="text-xs text-gray-500">{{ foundation.projectCount }} projects</div>
93+
}
94+
</div>
95+
</div>
96+
</td>
97+
98+
<!-- Health Score -->
99+
<td class="py-3 px-3">
100+
<lfx-health-score-tag [score]="foundation.healthScore"></lfx-health-score-tag>
101+
</td>
102+
103+
<!-- Software Value -->
104+
<td class="py-3 px-3">
105+
<div class="text-sm font-medium whitespace-nowrap">
106+
{{ foundation.softwareValueFormatted }}
107+
</div>
108+
</td>
109+
110+
<!-- Total Members -->
111+
<td class="py-3 px-3">
112+
<div class="text-sm font-medium whitespace-nowrap">
113+
{{ foundation.totalMembersFormatted }}
114+
</div>
115+
</td>
116+
117+
<!-- Active Contributors with Sparkline -->
118+
<td class="py-3 px-3">
119+
<div class="flex items-center gap-2">
120+
<div class="w-[60px] h-6 flex-shrink-0">
121+
<lfx-chart type="line" [data]="foundation.activeContributorsChartData" [options]="sparklineOptions" height="100%"> </lfx-chart>
122+
</div>
123+
<div class="text-sm font-medium whitespace-nowrap">
124+
{{ foundation.activeContributorsAvg }}
125+
</div>
126+
</div>
127+
</td>
128+
129+
<!-- Maintainers with Sparkline -->
130+
<td class="py-3 px-3">
131+
<div class="flex items-center gap-2">
132+
<div class="w-[60px] h-6 flex-shrink-0">
133+
<lfx-chart type="line" [data]="foundation.maintainersChartData" [options]="sparklineOptions" height="100%"> </lfx-chart>
134+
</div>
135+
<div class="text-sm font-medium whitespace-nowrap">
136+
{{ foundation.maintainersAvg }}
137+
</div>
138+
</div>
139+
</td>
140+
141+
<!-- Events with Monthly Bar Chart -->
142+
<td class="py-3 px-3">
143+
<div class="flex items-center gap-2">
144+
<div class="w-[80px] flex-shrink-0">
145+
<div class="flex items-end gap-0.5 h-10">
146+
@for (height of foundation.barHeights; track $index) {
147+
<div class="flex-1 bg-[#0094FF] rounded-sm min-w-[3px]" [style.height.%]="height" [attr.data-testid]="'event-bar-' + $index"></div>
148+
}
149+
</div>
150+
</div>
151+
<div class="text-sm font-medium whitespace-nowrap">
152+
{{ foundation.eventsTotal }}
153+
</div>
154+
</div>
155+
</td>
156+
157+
<!-- Org Dependency Risk with Pie Chart -->
158+
<td class="py-3 px-3">
159+
<div class="flex items-center gap-3">
160+
<div class="relative w-10 h-10 flex-shrink-0">
161+
<svg class="w-10 h-10" viewBox="0 0 40 40">
162+
<!-- Other orgs slice (light grey) -->
163+
<path [attr.d]="foundation.pieChartPaths.otherPath" fill="#E5E7EB" />
164+
<!-- Top orgs slice (risk color) -->
165+
<path [attr.d]="foundation.pieChartPaths.topPath" [attr.fill]="foundation.orgDependencyColor" />
166+
</svg>
167+
</div>
168+
<div class="flex flex-col gap-0.5 min-w-0">
169+
<div class="text-sm font-medium" [ngClass]="foundation.orgDependencyTextColorClass">
170+
{{ foundation.orgDependency.topOrgsCount }} orgs: {{ foundation.orgDependency.topOrgsPercentage }}%
171+
</div>
172+
<div class="text-xs text-gray-500">
173+
{{ foundation.orgDependency.otherOrgsCount }} orgs: {{ foundation.orgDependency.otherOrgsPercentage }}%
174+
</div>
175+
</div>
176+
</div>
177+
</td>
178+
</tr>
179+
}
180+
</tbody>
181+
</table>
182+
</div>
183+
</div>
184+
</section>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright The Linux Foundation and each contributor to LFX.
2+
// SPDX-License-Identifier: MIT
3+
4+
:host {
5+
display: block;
6+
}

0 commit comments

Comments
 (0)