Skip to content

Commit 198e084

Browse files
authored
feat(dashboards): chart consolidation and foundation support (#182)
- Create centralized chart-options.constants.ts with reusable configurations - Replace hardcoded hex colors with lfxColors theme tokens across dashboards - Add foundation context fallback for meetings module components - Implement feature flag for foundation-level meeting creation - Add array query param support and pagination to API services - Fix filter-pills and project-selector theme color bugs - Update sidebar to load TLF projects with children - Default persona service to maintainer when no auto-detection - Fix project-context service cookie deletion order LFXV2-821 LFXV2-822 LFXV2-823 LFXV2-824 Generated with [Claude Code](https://claude.ai/code) Signed-off-by: Asitha de Silva <asithade@gmail.com>
1 parent d986bbb commit 198e084

30 files changed

+390
-426
lines changed

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { SidebarMenuItem } from '@lfx-one/shared/interfaces';
1212
import { AppService } from '@services/app.service';
1313
import { FeatureFlagService } from '@services/feature-flag.service';
1414
import { PersonaService } from '@services/persona.service';
15-
import { ProjectContextService } from '@services/project-context.service';
1615
import { DrawerModule } from 'primeng/drawer';
1716
import { filter } from 'rxjs';
1817

@@ -29,10 +28,8 @@ export class MainLayoutComponent {
2928
private readonly appService = inject(AppService);
3029
private readonly featureFlagService = inject(FeatureFlagService);
3130
private readonly personaService = inject(PersonaService);
32-
private readonly projectContextService = inject(ProjectContextService);
3331

3432
// Expose mobile sidebar state from service (writable for two-way binding with p-drawer)
35-
protected readonly selectedProject = this.projectContextService.selectedProject;
3633
protected readonly showMobileSidebar = this.appService.showMobileSidebar;
3734

3835
// Feature flags

apps/lfx-one/src/app/modules/committees/committee-manage/committee-manage.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export class CommitteeManageComponent {
3636

3737
// Initialize committee data
3838
public committee = this.initializeCommittee();
39+
public project = computed(() => this.projectContextService.selectedProject() || this.projectContextService.selectedFoundation());
3940

4041
// Form state
4142
public form: FormGroup = this.createCommitteeFormGroup();
@@ -87,7 +88,7 @@ export class CommitteeManageComponent {
8788
},
8889
display_name: this.form.value.display_name || this.form.value.name,
8990
website: this.form.value.website || null,
90-
project_uid: this.projectContextService.getProjectUid() || this.projectContextService.getFoundationId() || null,
91+
project_uid: this.project()?.uid || null,
9192
};
9293

9394
const committeeData = this.cleanFormData(formValue);

apps/lfx-one/src/app/modules/committees/components/committee-form/committee-form.component.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ <h2>Basic Information</h2>
1717
control="name"
1818
id="committeeName"
1919
[placeholder]="'Enter ' + committeeLabel.toLowerCase() + ' name'"
20-
styleClass="w-full border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#0094FF] focus:border-transparent"></lfx-input-text>
20+
styleClass="w-full border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"></lfx-input-text>
2121
@if (form().get('name')?.errors?.['required'] && form().get('name')?.touched) {
2222
<p class="text-sm text-red-600">{{ committeeLabel }} name is required</p>
2323
}
@@ -66,7 +66,7 @@ <h2>Basic Information</h2>
6666
id="description"
6767
[placeholder]="'Enter ' + committeeLabel.toLowerCase() + ' description'"
6868
[rows]="3"
69-
styleClass="w-full border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#0094FF] focus:border-transparent resize-none"></lfx-textarea>
69+
styleClass="w-full border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none"></lfx-textarea>
7070
</div>
7171

7272
<!-- Committee Website -->
@@ -79,7 +79,7 @@ <h2>Basic Information</h2>
7979
id="website"
8080
type="url"
8181
placeholder="https://example.com"
82-
styleClass="w-full border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#0094FF] focus:border-transparent"></lfx-input-text>
82+
styleClass="w-full border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"></lfx-input-text>
8383
@if (form().get('website')?.errors?.['pattern'] && form().get('website')?.touched) {
8484
<p class="text-sm text-red-600">Please enter a valid URL</p>
8585
}
@@ -151,7 +151,7 @@ <h2 class="mb-4">Settings</h2>
151151
control="display_name"
152152
id="displayName"
153153
placeholder="Enter public display name"
154-
styleClass="w-full border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#0094FF] focus:border-transparent"></lfx-input-text>
154+
styleClass="w-full border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"></lfx-input-text>
155155
</div>
156156
}
157157

@@ -173,7 +173,7 @@ <h2 class="mb-4">Settings</h2>
173173
control="sso_group_name"
174174
id="ssoGroupName"
175175
placeholder="Enter SSO group name"
176-
styleClass="w-full border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#0094FF] focus:border-transparent"></lfx-input-text>
176+
styleClass="w-full border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"></lfx-input-text>
177177
</div>
178178
}
179179
</div>

apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ <h2>{{ title() }}</h2>
5353
<!-- Loading State -->
5454
<div class="flex items-center justify-center p-12">
5555
<div class="text-center space-y-3">
56-
<i class="fa-light fa-spinner-third fa-spin text-4xl text-[#0094FF]"></i>
56+
<i class="fa-light fa-spinner-third fa-spin text-4xl text-blue-500"></i>
5757
<p class="text-sm text-gray-500">Loading foundation health data...</p>
5858
</div>
5959
</div>

apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts

Lines changed: 21 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import { ChartComponent } from '@components/chart/chart.component';
99
import { FilterOption, FilterPillsComponent } from '@components/filter-pills/filter-pills.component';
1010
import {
1111
AGGREGATE_FOUNDATION_METRICS,
12-
FOUNDATION_BAR_CHART_OPTIONS,
13-
FOUNDATION_SPARKLINE_CHART_OPTIONS,
12+
BASE_BAR_CHART_OPTIONS,
13+
BASE_LINE_CHART_OPTIONS,
14+
lfxColors,
1415
PRIMARY_FOUNDATION_HEALTH_METRICS,
1516
} from '@lfx-one/shared/constants';
1617
import { FoundationMetricCard, MetricCategory, PrimaryFoundationHealthMetric, TopProjectDisplay } from '@lfx-one/shared/interfaces';
@@ -60,9 +61,9 @@ export class FoundationHealthComponent {
6061
{ id: 'events', label: 'Event' },
6162
];
6263

63-
public readonly sparklineOptions = FOUNDATION_SPARKLINE_CHART_OPTIONS;
64+
public readonly sparklineOptions = BASE_LINE_CHART_OPTIONS;
6465

65-
public readonly barChartOptions = FOUNDATION_BAR_CHART_OPTIONS;
66+
public readonly barChartOptions = BASE_BAR_CHART_OPTIONS;
6667

6768
public readonly metricCards = computed<FoundationMetricCard[]>(() => {
6869
const filter = this.selectedFilter();
@@ -109,11 +110,11 @@ export class FoundationHealthComponent {
109110
const distribution = this.healthScoresData();
110111

111112
const data = [
112-
{ category: 'Critical', count: distribution.critical, color: '#EF4444' },
113-
{ category: 'Unsteady', count: distribution.unsteady, color: '#FB923C' },
114-
{ category: 'Stable', count: distribution.stable, color: '#F59E0B' },
115-
{ category: 'Healthy', count: distribution.healthy, color: '#0094FF' },
116-
{ category: 'Excellent', count: distribution.excellent, color: '#10bc8a' },
113+
{ category: 'Critical', count: distribution.critical, color: lfxColors.negative[500] },
114+
{ category: 'Unsteady', count: distribution.unsteady, color: lfxColors.warning[400] },
115+
{ category: 'Stable', count: distribution.stable, color: lfxColors.warning[500] },
116+
{ category: 'Healthy', count: distribution.healthy, color: lfxColors.brand[500] },
117+
{ category: 'Excellent', count: distribution.excellent, color: lfxColors.positive[500] },
117118
];
118119

119120
const maxCount = Math.max(...data.map((d) => d.count));
@@ -204,8 +205,8 @@ export class FoundationHealthComponent {
204205
datasets: [
205206
{
206207
data: data.monthlyData,
207-
borderColor: metric.sparklineColor || '#0094FF',
208-
backgroundColor: hexToRgba(metric.sparklineColor || '#0094FF', 0.1),
208+
borderColor: metric.sparklineColor || lfxColors.brand[500],
209+
backgroundColor: hexToRgba(metric.sparklineColor || lfxColors.brand[500], 0.1),
209210
fill: true,
210211
tension: 0.4,
211212
borderWidth: 2,
@@ -215,16 +216,10 @@ export class FoundationHealthComponent {
215216
},
216217
chartOptions: {
217218
...this.sparklineOptions,
218-
interaction: {
219-
mode: 'index',
220-
intersect: false,
221-
},
222219
plugins: {
223220
...this.sparklineOptions.plugins,
224221
tooltip: {
225-
enabled: true,
226-
mode: 'index',
227-
intersect: false,
222+
...(this.sparklineOptions.plugins?.tooltip ?? {}),
228223
callbacks: {
229224
title: (context: TooltipItem<'line'>[]) => context[0].label,
230225
label: (context: TooltipItem<'line'>) => {
@@ -254,8 +249,8 @@ export class FoundationHealthComponent {
254249
datasets: [
255250
{
256251
data: data.monthlyData,
257-
borderColor: metric.sparklineColor || '#0094FF',
258-
backgroundColor: hexToRgba(metric.sparklineColor || '#0094FF', 0.1),
252+
borderColor: metric.sparklineColor || lfxColors.brand[500],
253+
backgroundColor: hexToRgba(metric.sparklineColor || lfxColors.brand[500], 0.1),
259254
fill: true,
260255
tension: 0.4,
261256
borderWidth: 2,
@@ -265,16 +260,10 @@ export class FoundationHealthComponent {
265260
},
266261
chartOptions: {
267262
...this.sparklineOptions,
268-
interaction: {
269-
mode: 'index',
270-
intersect: false,
271-
},
272263
plugins: {
273264
...this.sparklineOptions.plugins,
274265
tooltip: {
275-
enabled: true,
276-
mode: 'index',
277-
intersect: false,
266+
...(this.sparklineOptions.plugins?.tooltip ?? {}),
278267
callbacks: {
279268
title: (context: TooltipItem<'line'>[]) => context[0].label,
280269
label: (context: TooltipItem<'line'>) => {
@@ -331,7 +320,7 @@ export class FoundationHealthComponent {
331320
category: metric.category as MetricCategory,
332321
testId: metric.testId,
333322
customContentType: metric.customContentType,
334-
chartData: this.createSparklineData(metrics.activeContributorsData, metric.sparklineColor || '#0094FF'),
323+
chartData: this.createSparklineData(metrics.activeContributorsData, metric.sparklineColor || lfxColors.brand[500]),
335324
};
336325
}
337326

@@ -351,8 +340,8 @@ export class FoundationHealthComponent {
351340
datasets: [
352341
{
353342
data: data.trendData,
354-
borderColor: metric.sparklineColor || '#0094FF',
355-
backgroundColor: hexToRgba(metric.sparklineColor || '#0094FF', 0.1),
343+
borderColor: metric.sparklineColor || lfxColors.brand[500],
344+
backgroundColor: hexToRgba(metric.sparklineColor || lfxColors.brand[500], 0.1),
356345
fill: true,
357346
tension: 0.4,
358347
borderWidth: 2,
@@ -362,16 +351,10 @@ export class FoundationHealthComponent {
362351
},
363352
chartOptions: {
364353
...this.sparklineOptions,
365-
interaction: {
366-
mode: 'index',
367-
intersect: false,
368-
},
369354
plugins: {
370355
...this.sparklineOptions.plugins,
371356
tooltip: {
372-
enabled: true,
373-
mode: 'index',
374-
intersect: false,
357+
...(this.sparklineOptions.plugins?.tooltip ?? {}),
375358
callbacks: {
376359
title: (context: TooltipItem<'line'>[]) => context[0].label,
377360
label: (context: TooltipItem<'line'>) => {
@@ -397,7 +380,7 @@ export class FoundationHealthComponent {
397380
category: metric.category as MetricCategory,
398381
testId: metric.testId,
399382
customContentType: metric.customContentType,
400-
chartData: this.createBarChartData(metrics.eventsMonthlyData, metric.chartColor || '#0094FF'),
383+
chartData: this.createBarChartData(metrics.eventsMonthlyData, metric.chartColor || lfxColors.brand[500]),
401384
};
402385
}
403386

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ <h2>{{ accountName() }}'s Involvement</h2>
4848
<!-- Loading State -->
4949
<div class="flex items-center justify-center p-12">
5050
<div class="text-center space-y-3">
51-
<i class="fa-light fa-spinner-third fa-spin text-4xl text-[#0094FF]"></i>
51+
<i class="fa-light fa-spinner-third fa-spin text-4xl text-blue-500"></i>
5252
<p class="text-sm text-gray-500">Loading organization data...</p>
5353
</div>
5454
</div>

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { DataCopilotComponent } from '@app/shared/components/data-copilot/data-c
88
import { ChartComponent } from '@components/chart/chart.component';
99
import { FilterOption, FilterPillsComponent } from '@components/filter-pills/filter-pills.component';
1010
import { TagComponent } from '@components/tag/tag.component';
11-
import { BAR_CHART_OPTIONS, PRIMARY_INVOLVEMENT_METRICS, SPARKLINE_CHART_OPTIONS } from '@lfx-one/shared/constants';
11+
import { BASE_BAR_CHART_OPTIONS, BASE_LINE_CHART_OPTIONS, PRIMARY_INVOLVEMENT_METRICS } from '@lfx-one/shared/constants';
1212
import { OrganizationInvolvementMetricWithChart, PrimaryInvolvementMetric } from '@lfx-one/shared/interfaces';
1313
import { hexToRgba } from '@lfx-one/shared/utils';
1414
import { AccountContextService } from '@services/account-context.service';
@@ -43,8 +43,8 @@ export class OrganizationInvolvementComponent {
4343
public readonly isLoading = computed<boolean>(() => this.contributionsLoading() || this.dashboardLoading() || this.eventsLoading());
4444
public readonly selectedFilter = signal<string>('all');
4545
public readonly accountName = computed<string>(() => this.accountContextService.selectedAccount().accountName || 'Organization');
46-
public readonly sparklineChartOptions = SPARKLINE_CHART_OPTIONS;
47-
public readonly barChartOptions = BAR_CHART_OPTIONS;
46+
public readonly sparklineChartOptions = BASE_LINE_CHART_OPTIONS;
47+
public readonly barChartOptions = BASE_BAR_CHART_OPTIONS;
4848
public readonly filterOptions: FilterOption[] = [
4949
{ id: 'all', label: 'All' },
5050
{ id: 'contributions', label: 'Contribution' },
@@ -231,7 +231,7 @@ export class OrganizationInvolvementComponent {
231231
subtitle: 'Contributors from our organization',
232232
icon: metric.icon ?? '',
233233
chartData: {
234-
labels: Array.from({ length: metric.sparklineData?.length ?? 0 }, (_, i) => `Point ${i + 1}`),
234+
labels: Array.from({ length: metric.sparklineData?.length ?? 0 }, (_, i) => `Month ${i + 1}`),
235235
datasets: [
236236
{
237237
data: metric.sparklineData ?? [],
@@ -254,7 +254,7 @@ export class OrganizationInvolvementComponent {
254254
subtitle: `Across ${data.projects} projects`,
255255
icon: metric.icon ?? '',
256256
chartData: {
257-
labels: Array.from({ length: metric.sparklineData?.length ?? 0 }, (_, i) => `Point ${i + 1}`),
257+
labels: Array.from({ length: metric.sparklineData?.length ?? 0 }, (_, i) => `Month ${i + 1}`),
258258
datasets: [
259259
{
260260
data: metric.sparklineData ?? [],
@@ -319,7 +319,7 @@ export class OrganizationInvolvementComponent {
319319
subtitle: 'Employees at foundation events',
320320
icon: metric.icon ?? '',
321321
chartData: {
322-
labels: Array.from({ length: metric.sparklineData?.length ?? 0 }, (_, i) => `Point ${i + 1}`),
322+
labels: Array.from({ length: metric.sparklineData?.length ?? 0 }, (_, i) => `Day ${i + 1}`),
323323
datasets: [
324324
{
325325
data: metric.sparklineData ?? [],
@@ -345,7 +345,7 @@ export class OrganizationInvolvementComponent {
345345
subtitle: 'Employee speakers at events',
346346
icon: metric.icon ?? '',
347347
chartData: {
348-
labels: Array.from({ length: metric.sparklineData?.length ?? 0 }, (_, i) => `Point ${i + 1}`),
348+
labels: Array.from({ length: metric.sparklineData?.length ?? 0 }, (_, i) => `Day ${i + 1}`),
349349
datasets: [
350350
{
351351
data: metric.sparklineData ?? [],
@@ -371,7 +371,7 @@ export class OrganizationInvolvementComponent {
371371
subtitle: `${data.certifications} total certifications`,
372372
icon: metric.icon ?? '',
373373
chartData: {
374-
labels: Array.from({ length: metric.sparklineData?.length ?? 0 }, (_, i) => `Point ${i + 1}`),
374+
labels: Array.from({ length: metric.sparklineData?.length ?? 0 }, (_, i) => `Day ${i + 1}`),
375375
datasets: [
376376
{
377377
data: metric.sparklineData ?? [],
@@ -394,7 +394,7 @@ export class OrganizationInvolvementComponent {
394394
subtitle: 'Employees enrolled in training',
395395
icon: metric.icon ?? '',
396396
chartData: {
397-
labels: Array.from({ length: metric.sparklineData?.length ?? 0 }, (_, i) => `Point ${i + 1}`),
397+
labels: Array.from({ length: metric.sparklineData?.length ?? 0 }, (_, i) => `Day ${i + 1}`),
398398
datasets: [
399399
{
400400
data: metric.sparklineData ?? [],

apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ <h2>📈 <span class="ml-1">Recent Progress</span></h2>
3939
<!-- Loading State -->
4040
<div class="flex items-center justify-center p-12">
4141
<div class="text-center space-y-3">
42-
<i class="fa-light fa-spinner-third fa-spin text-4xl text-[#0094FF]"></i>
42+
<i class="fa-light fa-spinner-third fa-spin text-4xl text-blue-500"></i>
4343
<p class="text-sm text-gray-500">Loading project data...</p>
4444
</div>
4545
</div>

0 commit comments

Comments
 (0)