Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<!-- Committee Members Section -->
<lfx-card>
<div class="flex justify-between gap-4 mb-8">
<h3 class="text-lg font-medium text-gray-900">Committee Members</h3>
<h3 class="text-lg font-medium text-gray-900">{{ committeeLabel.singular }} Members</h3>
<!-- Add Member Button -->
@if (committee()?.writer) {
<lfx-button label="Add Member" icon="fa-light fa-user-plus" size="small" severity="secondary" (onClick)="openAddMemberDialog()"> </lfx-button>
Expand Down Expand Up @@ -177,7 +177,7 @@ <h3 class="text-lg font-medium text-gray-900">Committee Members</h3>
<div class="text-center">
<i class="fa-light fa-users text-4xl text-gray-400 mb-4"></i>
<h3 class="text-lg font-medium text-gray-900 mb-2">No Members Yet</h3>
<p class="text-gray-600">This committee doesn't have any members yet.</p>
<p class="text-gray-600">This {{ committeeLabel.singular }} doesn't have any members yet.</p>
</div>
</td>
</tr>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { MenuComponent } from '@components/menu/menu.component';
import { SelectButtonComponent } from '@components/select-button/select-button.component';
import { SelectComponent } from '@components/select/select.component';
import { TableComponent } from '@components/table/table.component';
import { COMMITTEE_LABEL } from '@lfx-one/shared/constants';
import { Committee, CommitteeMember } from '@lfx-one/shared/interfaces';
import { CommitteeService } from '@services/committee.service';
import { AnimateOnScrollModule } from 'primeng/animateonscroll';
Expand Down Expand Up @@ -63,6 +64,7 @@ export class CommitteeMembersComponent implements OnInit {
public selectedMember: WritableSignal<CommitteeMember | null>;
public isDeleting: WritableSignal<boolean>;
public memberActionMenuItems: MenuItem[] = [];
public committeeLabel = COMMITTEE_LABEL;

// Filter-related variables
public filterForm: FormGroup;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<!-- SPDX-License-Identifier: MIT -->

<!-- Card wrapper with border and shadow -->
<div class="bg-white rounded-xl border shadow-md" data-testid="dashboard-meeting-card">
<div class="bg-white rounded-xl border shadow-md hover:border-blue-500 transition-all" data-testid="dashboard-meeting-card">
<div class="p-4 space-y-3">
<!-- Header with meeting type and project badges -->
<div class="flex items-center gap-2 flex-wrap">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<div class="flex gap-3 flex-wrap">
<h2 class="mb-0">{{ title() }}</h2>
<!-- Data Copilot Button -->
<lfx-data-copilot></lfx-data-copilot>
<lfx-data-copilot [includeOrganizationId]="false" [includeOrganizationName]="false"></lfx-data-copilot>
</div>

<div class="flex flex-wrap items-center gap-2">
Expand Down Expand Up @@ -114,11 +114,11 @@ <h2 class="mb-0">{{ title() }}</h2>
<div class="text-xs font-medium text-slate-700">
{{ card.busFactor.topCompaniesCount }} Companies ({{ card.busFactor.topCompaniesPercentage }}%)
</div>
<div class="h-4 rounded-l-sm bg-gray-300"></div>
<div class="h-4 rounded-l-sm bg-blue-500"></div>
</div>
<div [style.width.%]="card.busFactor.otherCompaniesPercentage" class="flex flex-col gap-1">
<div class="text-xs text-slate-600 text-right">{{ card.busFactor.otherCompaniesCount }} Other Companies</div>
<div class="h-4 bg-slate-200 rounded-r-sm"></div>
<div class="h-4 bg-blue-200 rounded-r-sm"></div>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@ import { toObservable, toSignal } from '@angular/core/rxjs-interop';
import { DataCopilotComponent } from '@app/shared/components/data-copilot/data-copilot.component';
import { FilterOption, FilterPillsComponent } from '@components/filter-pills/filter-pills.component';
import { MetricCardComponent } from '@components/metric-card/metric-card.component';
import { BASE_BAR_CHART_OPTIONS, BASE_LINE_CHART_OPTIONS, COMPANY_BUS_FACTOR, lfxColors, PRIMARY_FOUNDATION_HEALTH_METRICS } from '@lfx-one/shared/constants';
import { BASE_BAR_CHART_OPTIONS, BASE_LINE_CHART_OPTIONS, lfxColors, PRIMARY_FOUNDATION_HEALTH_METRICS } from '@lfx-one/shared/constants';
import { hexToRgba } from '@lfx-one/shared/utils';
import { AnalyticsService } from '@services/analytics.service';
import { ProjectContextService } from '@services/project-context.service';
import { catchError, map, of, switchMap, tap } from 'rxjs';

import type { DashboardMetricCard, HealthEventsMonthlyResponse, TopProjectDisplay, UniqueContributorsDailyResponse } from '@lfx-one/shared/interfaces';
import type {
CompanyBusFactor,
DashboardMetricCard,
FoundationCompanyBusFactorResponse,
HealthEventsMonthlyResponse,
TopProjectDisplay,
UniqueContributorsDailyResponse,
} from '@lfx-one/shared/interfaces';
import type { TooltipItem } from 'chart.js';

@Component({
Expand All @@ -35,6 +42,7 @@ export class FoundationHealthComponent {
private readonly totalProjectsLoading = signal(true);
private readonly totalMembersLoading = signal(true);
private readonly softwareValueLoading = signal(true);
private readonly companyBusFactorLoading = signal(true);
private readonly maintainersLoading = signal(true);
private readonly healthScoresLoading = signal(true);
private readonly activeContributorsLoading = signal(true);
Expand All @@ -47,6 +55,7 @@ export class FoundationHealthComponent {
private readonly totalProjectsData = this.initializeTotalProjectsData();
private readonly totalMembersData = this.initializeTotalMembersData();
private readonly softwareValueData = this.initializeSoftwareValueData();
private readonly companyBusFactorData = this.initializeCompanyBusFactorData();
private readonly maintainersData = this.initializeMaintainersData();
private readonly healthScoresData = this.initializeHealthScoresData();
private readonly activeContributorsData = this.initializeActiveContributorsData();
Expand Down Expand Up @@ -282,12 +291,21 @@ export class FoundationHealthComponent {
}

private transformCompanyBusFactor(metric: DashboardMetricCard): DashboardMetricCard {
// TODO: Replace with real API data when endpoint is available
const data = this.companyBusFactorData();

const busFactor: CompanyBusFactor = {
topCompaniesCount: data.topCompaniesCount,
topCompaniesPercentage: data.topCompaniesPercentage,
otherCompaniesCount: data.otherCompaniesCount,
otherCompaniesPercentage: data.otherCompaniesPercentage,
};

return {
...metric,
value: COMPANY_BUS_FACTOR.topCompaniesCount.toString(),
loading: this.companyBusFactorLoading(),
value: data.topCompaniesCount.toString(),
subtitle: 'Companies account for >50% code contributions',
busFactor: COMPANY_BUS_FACTOR,
busFactor,
};
}

Expand Down Expand Up @@ -510,6 +528,31 @@ export class FoundationHealthComponent {
);
}

private initializeCompanyBusFactorData() {
const defaultValue: FoundationCompanyBusFactorResponse = {
topCompaniesCount: 0,
topCompaniesPercentage: 0,
otherCompaniesCount: 0,
otherCompaniesPercentage: 0,
};

return toSignal(
this.selectedFoundationSlug$.pipe(
tap(() => this.companyBusFactorLoading.set(true)),
switchMap((foundationSlug) =>
this.analyticsService.getCompanyBusFactor(foundationSlug).pipe(
tap(() => this.companyBusFactorLoading.set(false)),
catchError(() => {
this.companyBusFactorLoading.set(false);
return of(defaultValue);
})
)
)
),
{ initialValue: defaultValue }
);
}

private initializeMaintainersData() {
const defaultValue = {
avgMaintainers: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ <h2 class="flex items-center gap-2">
<!-- TODAY Section - only show if there are meetings today -->
@if (todayMeetings().length > 0) {
<div>
<h4 class="text-xs font-medium text-gray-500 mb-3 uppercase tracking-wide">Today</h4>
<div class="flex flex-col gap-3">
@for (item of todayMeetings(); track item.meeting.uid) {
<lfx-dashboard-meeting-card
Expand All @@ -47,7 +46,6 @@ <h4 class="text-xs font-medium text-gray-500 mb-3 uppercase tracking-wide">Today
<!-- UPCOMING Section - only show if there are upcoming meetings -->
@if (upcomingMeetings().length > 0) {
<div>
<h4 class="text-xs font-medium text-gray-500 mb-3 uppercase tracking-wide">Upcoming</h4>
<div class="flex flex-col gap-3">
@for (item of upcomingMeetings(); track item.meeting.uid) {
<lfx-dashboard-meeting-card
Expand Down
Loading