Skip to content

Conversation

@asithade
Copy link
Contributor

@asithade asithade commented Nov 26, 2025

Summary

  • Add health metrics daily, unique contributors daily, and events monthly API endpoints
  • Create reusable MetricCardComponent for unified dashboard metric display
  • Refactor foundation-health, recent-progress, and organization-involvement to use MetricCard
  • Add DashboardMetricCard interface for type-safe metric definitions
  • Implement chart tooltip callbacks for enhanced data visualization

Changes

Backend (API Endpoints)

  • GET /api/analytics/health-metrics-daily - Health metrics daily data from Snowflake
  • GET /api/analytics/unique-contributors-daily - Unique contributors daily data
  • GET /api/analytics/health-events-monthly - Health events monthly data

Frontend Components

  • MetricCardComponent - New reusable component for displaying dashboard metrics with charts
  • Foundation Health - Refactored to use MetricCard, added active contributors and events metrics
  • Recent Progress - Refactored to use MetricCard, added health score metric
  • Organization Involvement - Refactored to use MetricCard with enhanced tooltips

Shared Package

  • DashboardMetricCard Interface - Unified interface for all metric cards
  • Analytics Data Interfaces - New interfaces for health metrics, contributors, and events data
  • Constants Consolidation - Simplified progress metrics constants

JIRA

LFXV2-834

Generated with Claude Code

LFXV2-834

- Add health metrics daily, unique contributors daily, and events monthly API endpoints
- Create reusable MetricCardComponent for unified dashboard metric display
- Refactor foundation-health, recent-progress, and organization-involvement to use MetricCard
- Add DashboardMetricCard interface for type-safe metric definitions
- Implement chart tooltip callbacks for enhanced data visualization
- Consolidate progress metrics constants and analytics data interfaces

Signed-off-by: Asitha de Silva <[email protected]>
Copilot AI review requested due to automatic review settings November 26, 2025 21:58
@asithade asithade requested a review from jordane as a code owner November 26, 2025 21:58
@coderabbitai
Copy link

coderabbitai bot commented Nov 26, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Consolidates dashboard rendering around a new MetricCardComponent and a unified DashboardMetricCard type, migrates multiple dashboard components to use chartData/chartOptions and customContent templates, adds frontend/backend analytics endpoints and Snowflake-backed services for daily/monthly metrics, and updates shared interfaces/constants.

Changes

Cohort / File(s) Summary
MetricCardComponent
apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.ts, apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.html
New standalone component providing a unified metric card API (title/icon/testId, chartType/chartData/chartOptions, value/subtitle/valueTooltip, connected indicator) and support for custom content templates.
Dashboard components — foundation / involvement / progress / meeting / projects
apps/lfx-one/src/app/modules/dashboards/components/foundation-health/..., .../organization-involvement/..., .../recent-progress/..., .../dashboard-meeting-card/dashboard-meeting-card.component.ts, .../my-projects/my-projects.component.html
Replaced per-card markup with MetricCardComponent usage and migrated metric types to DashboardMetricCard. Built chartData/chartOptions inline, added new data pipelines (activeContributors, events, healthMetricsDaily), adjusted loading flags, and made small UI/text-color/layout tweaks; meeting card icon string now appends "mr-2".
Shared unified dashboard interfaces
packages/shared/src/interfaces/dashboard-metric.interface.ts, packages/shared/src/interfaces/index.ts
Adds DashboardMetricCard, MetricCategory, CustomContentType, foundation-health types (ProjectHealthDistribution, CompanyBusFactor, TopProjectDisplay/TopProjectByValue) and re-exports via interfaces index.
Removed legacy interfaces
packages/shared/src/interfaces/dashboard.interface.ts, packages/shared/src/interfaces/foundation-metrics.interface.ts, packages/shared/src/interfaces/foundation-metrics.interface.ts
Deletes legacy dashboard/foundation-metrics interface files and their exported types (PrimaryFoundationHealthMetric, FoundationMetricCard, OrganizationInvolvementMetricWithChart, etc.).
Shared constants — new unified metrics
packages/shared/src/constants/dashboard-metrics.constants.ts, packages/shared/src/constants/index.ts
Adds PRIMARY_FOUNDATION_HEALTH_METRICS, PRIMARY_INVOLVEMENT_METRICS, CORE_DEVELOPER_PROGRESS_METRICS, MAINTAINER_PROGRESS_METRICS, COMPANY_BUS_FACTOR; updates constants index to export unified dashboard metrics and drops several legacy constants exports.
Removed legacy constants
packages/shared/src/constants/foundation-health.constants.ts, packages/shared/src/constants/organization-involvement.constants.ts, packages/shared/src/constants/progress-metrics.constants.ts
Deletes old per-area constants and helper utilities (mock/sample data and chart helpers).
Chart options/constants
packages/shared/src/constants/chart-options.constants.ts
Adds EMPTY_CHART_DATA and NO_TOOLTIP_CHART_OPTIONS (sparkline/no-tooltip config) and expands ChartData/ChartType imports.
Analytics data interfaces
packages/shared/src/interfaces/analytics-data.interface.ts
Adds many Snowflake/API response row and wrapper interfaces for health metrics daily, unique contributors daily, health events monthly, certified employees, membership tier, maintainers/contributors, and training enrollments (note: duplicate blocks present in diff).
Frontend Analytics Service
apps/lfx-one/src/app/shared/services/analytics.service.ts
Renames and adds API client methods (e.g., getOrganizationMaintainers, getOrganizationContributors, getMembershipTier, getCertifiedEmployees, getTrainingEnrollments, getHealthMetricsDaily, getUniqueContributorsDaily, getHealthEventsMonthly) with adjusted endpoints, response types, and fallback payloads.
Server — analytics controller & routes
apps/lfx-one/src/server/controllers/analytics.controller.ts, apps/lfx-one/src/server/routes/analytics.route.ts
Adds controller methods and routes for new endpoints (certified-employees, membership-tier, organization-maintainers, organization-contributors, training-enrollments, health-metrics-daily, unique-contributors-daily, health-events-monthly) and renames an existing overview endpoint to event-centric naming.
Server — project & organization services
apps/lfx-one/src/server/services/project.service.ts, apps/lfx-one/src/server/services/organization.service.ts
ProjectService: adds getHealthMetricsDaily, getUniqueContributorsDaily, getHealthEventsMonthly (Snowflake queries and aggregations). OrganizationService: removes consolidated overview methods and adds granular getters (getCertifiedEmployees, getMembershipTier, getOrganizationMaintainers, getOrganizationContributors, getTrainingEnrollments) with updated query targets and return shapes.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant UI as Dashboard<br/>Component(s)
    participant MCC as MetricCardComponent
    participant FS as Frontend<br/>AnalyticsService
    participant API as AnalyticsController
    participant PS as ProjectService
    participant DB as Snowflake

    Note over UI,FS: Frontend requests daily/monthly analytics
    UI->>FS: getUniqueContributorsDaily(slug, type)
    FS->>API: GET /api/analytics/unique-contributors-daily
    API->>PS: getUniqueContributorsDaily(slug, type)
    PS->>DB: Run query
    DB-->>PS: Rows
    PS-->>API: UniqueContributorsDailyResponse
    API-->>FS: JSON response
    FS-->>UI: Observable<UniqueContributorsDailyResponse>

    Note over UI: UI transforms response into DashboardMetricCard
    UI->>UI: transformActiveContributors(data, metric)
    UI-->>MCC: Render with (title, icon, chartType, chartData, chartOptions, value)
    MCC->>MCC: Render header → chart/customContent → footer
    MCC-->>UI: Rendered metric card
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Areas needing extra attention:
    • Duplicate method/interface blocks in apps/lfx-one/src/app/shared/services/analytics.service.ts and packages/shared/src/interfaces/analytics-data.interface.ts — verify and deduplicate.
    • Type migration correctness: ensure all transformers supply required DashboardMetricCard fields (title, chartType) and updated optional fields are handled.
    • MetricCardComponent contract: confirm templates pass chartOptions/chartData/customContentTemplate and tooltip behavior.
    • Server-side Snowflake query logic and aggregation correctness in ProjectService.
    • Removed legacy constants/interfaces: confirm all imports updated to the unified constants/interfaces to avoid missing references.

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly summarizes the main changes: adding metrics API integration and a reusable metric card component for dashboards.
Description check ✅ Passed The description is directly related to the changeset, providing clear details about backend endpoints, frontend components, and shared package changes.
Linked Issues check ✅ Passed The PR fully implements all objectives from LFXV2-834: backend endpoints created, MetricCardComponent implemented, dashboard modules refactored, DashboardMetricCard interface added, and constants consolidated.
Out of Scope Changes check ✅ Passed All changes align with the linked issue LFXV2-834 objectives; no out-of-scope modifications detected beyond the stated metrics API integration and metric card refactoring scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/LFXV2-834

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/shared/src/constants/progress-metrics.constants.ts (2)

63-76: Non-deterministic data in constants causes inconsistent renders.

Using Math.random() in constant definitions generates new values on every module import/evaluation. This can cause:

  • Inconsistent snapshots in tests
  • Hydration mismatches in SSR
  • Unexpected re-renders

Since this is meant to be sample/mock data, consider using fixed values or moving this to a factory function if randomness is intentional.

     chartData: {
       labels: Array.from({ length: 30 }, (_, i) => `Day ${i + 1}`),
       datasets: [
         {
-          data: Array.from({ length: 30 }, () => Math.floor(Math.random() * 5)),
+          data: [2, 1, 3, 0, 2, 4, 1, 3, 2, 0, 1, 4, 2, 3, 1, 0, 2, 3, 4, 1, 2, 0, 3, 1, 2, 4, 1, 3, 2, 1],
           borderColor: lfxColors.blue[500],
           backgroundColor: hexToRgba(lfxColors.blue[500], 0.1),

125-141: Same random data issue in maintainer metrics.

The Critical Security Issues metric also uses Math.random() which will produce different values on each import. Apply the same fix as suggested above.

🧹 Nitpick comments (10)
packages/shared/src/interfaces/dashboard.interface.ts (1)

124-126: Consider using unknown instead of any for better type safety.

The Record<string, any> type is overly permissive and requires an ESLint disable comment. Consider using Record<string, unknown> or defining a more specific Chart.js options interface to maintain type safety.

Apply this diff:

-  /** Chart.js options configuration */
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  chartOptions?: Record<string, any>;
+  /** Chart.js options configuration */
+  chartOptions?: Record<string, unknown>;
apps/lfx-one/src/app/modules/dashboards/components/dashboard-meeting-card/dashboard-meeting-card.component.ts (1)

60-60: Move spacing to template for better separation of concerns.

Concatenating the mr-2 margin class directly to the icon identifier mixes presentation logic with data. Consider handling spacing in the template where the icon is rendered, keeping the icon value as a pure identifier.

For example, in the template:

<i [class]="meetingTypeInfo().icon + ' mr-2'"></i>

Or define the spacing as a separate property in the computed signal.

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

49-62: Consider sanitizing the testId to avoid spaces in data-testid attributes.

The testId is constructed using item.title directly, which may contain spaces (e.g., "Active Weeks Streak"). Spaces in data-testid attributes can cause issues with CSS selectors and test frameworks.

-            [testId]="'dashboard-recent-progress-item-' + item.title"
+            [testId]="'dashboard-recent-progress-item-' + item.title.toLowerCase().replace(/\s+/g, '-')"

Alternatively, add a dedicated testId field to DashboardMetricCard and set it in the data source.

apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.ts (1)

11-16: Consider adding ChangeDetectionStrategy.OnPush for zoneless change detection compatibility.

Per coding guidelines, Angular 19 components should use zoneless change detection with signals. While the component correctly uses input() signals, adding OnPush change detection would align with zoneless best practices.

-import { CommonModule } from '@angular/common';
-import { Component, ContentChild, input, TemplateRef } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { ChangeDetectionStrategy, Component, ContentChild, input, TemplateRef } from '@angular/core';
 import { ChartComponent } from '@components/chart/chart.component';
 import { TooltipModule } from 'primeng/tooltip';

 import type { ChartData, ChartOptions, ChartType } from 'chart.js';

 @Component({
   selector: 'lfx-metric-card',
   standalone: true,
   imports: [CommonModule, ChartComponent, TooltipModule],
   templateUrl: './metric-card.component.html',
+  changeDetection: ChangeDetectionStrategy.OnPush,
 })
packages/shared/src/constants/progress-metrics.constants.ts (1)

38-110: Missing testId attributes for E2E testing.

As per coding guidelines, new components should have data-testid attributes. Consider adding testId to each metric for reliable test targeting:

   {
     title: 'Code Commits',
     value: '0',
     trend: 'up',
     subtitle: 'Last 30 days',
     chartType: 'line',
     chartData: EMPTY_CHART_DATA,
     chartOptions: PROGRESS_BASE_CHART_OPTIONS,
+    testId: 'metric-code-commits',
   },
apps/lfx-one/src/server/services/project.service.ts (1)

1163-1214: Consider adding DEBUG logging for observability.

The other methods in this service use req.log.debug() for tracing, but these new analytics methods lack logging. While the methods don't receive a Request object, consider using serverLogger (already imported) for consistency with operational patterns. As per coding guidelines, DEBUG level is appropriate for internal operations.

   public async getUniqueContributorsDaily(slug: string, entityType: 'foundation' | 'project'): Promise<UniqueContributorsDailyResponse> {
+    serverLogger.debug({ slug, entityType }, 'Fetching unique contributors daily data');
     // Query switching based on entity type
     const query =
       entityType === 'foundation'
apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts (1)

49-149: Consider extracting repetitive tooltip callback patterns.

The chart option configurations follow a consistent pattern but are verbose. Consider creating a helper function to reduce duplication:

private createChartOptionsWithTooltip(
  baseOptions: ChartOptions<'bar' | 'line'>,
  labelFormatter: (count: number) => string
): ChartOptions<'bar' | 'line'> {
  return {
    ...baseOptions,
    plugins: {
      ...baseOptions.plugins,
      tooltip: {
        ...(baseOptions.plugins?.tooltip ?? {}),
        callbacks: {
          title: (context) => context[0].label,
          label: (context) => labelFormatter(context.parsed.y),
        },
      },
    },
  };
}

// Usage:
private readonly activeContributorsChartOptions = this.createChartOptionsWithTooltip(
  BASE_BAR_CHART_OPTIONS,
  (count) => `Active contributors: ${count}`
);
apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts (1)

139-166: String-based metric matching is fragile.

The initializeProgressItems method uses string comparisons on metric.title to determine which transform to apply. This is brittle and could break if titles are renamed.

Consider using a constant key or enum for matching:

// In constants file
export const METRIC_KEYS = {
  ACTIVE_WEEKS_STREAK: 'active-weeks-streak',
  HEALTH_SCORE: 'health-score',
  // ...
} as const;

// In metric definitions, add a key field
{ key: METRIC_KEYS.HEALTH_SCORE, title: 'Health Score', ... }

// In component
if (metric.key === METRIC_KEYS.HEALTH_SCORE) {
  return this.transformHealthMetricsDaily(healthMetricsData, metric);
}

This approach decouples display text from programmatic logic.

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

262-262: Track the TODO for Company Bus Factor API integration.

The TODO comment indicates that the Company Bus Factor metric is still using mock data. Please ensure this is tracked so the real API integration is not overlooked.

Do you want me to open a new issue to track the Company Bus Factor API endpoint integration?


521-557: Consider adding error handling for API failures.

The new data initialization methods (initializeActiveContributorsData and initializeEventsData) follow the established pattern but lack error handling. If the API calls fail, the loading state will be cleared, but the component will silently display zero values or "N/A" without indicating an error occurred to the user.

Consider adding error handling in the RxJS pipes, for example:

private initializeActiveContributorsData() {
  return toSignal(
    this.selectedFoundationSlug$.pipe(
      switchMap((foundationSlug) => {
        this.activeContributorsLoading.set(true);

        return this.analyticsService.getUniqueContributorsDaily(foundationSlug, 'foundation').pipe(
+         catchError((error) => {
+           console.error('Failed to fetch active contributors data:', error);
+           return of({
+             data: [],
+             avgContributors: 0,
+             totalDays: 0,
+           } as UniqueContributorsDailyResponse);
+         }),
          finalize(() => this.activeContributorsLoading.set(false))
        );
      })
    ),
    {
      initialValue: {
        data: [],
        avgContributors: 0,
        totalDays: 0,
      } as UniqueContributorsDailyResponse,
    }
  );
}

Note: If error handling exists at a higher level (e.g., HTTP interceptor with user notifications), this may not be necessary.

Please verify if error handling is implemented at the service or interceptor level. If not, consider adding it to provide better user feedback when API calls fail.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 63e8717 and da3ed3c.

📒 Files selected for processing (22)
  • apps/lfx-one/src/app/modules/dashboards/components/dashboard-meeting-card/dashboard-meeting-card.component.ts (1 hunks)
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.html (2 hunks)
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts (9 hunks)
  • apps/lfx-one/src/app/modules/dashboards/components/my-projects/my-projects.component.html (2 hunks)
  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.html (1 hunks)
  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts (5 hunks)
  • apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.html (1 hunks)
  • apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts (17 hunks)
  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.html (1 hunks)
  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.ts (1 hunks)
  • apps/lfx-one/src/app/shared/services/analytics.service.ts (2 hunks)
  • apps/lfx-one/src/server/controllers/analytics.controller.ts (1 hunks)
  • apps/lfx-one/src/server/routes/analytics.route.ts (1 hunks)
  • apps/lfx-one/src/server/services/project.service.ts (4 hunks)
  • packages/shared/src/constants/foundation-health.constants.ts (4 hunks)
  • packages/shared/src/constants/organization-involvement.constants.ts (1 hunks)
  • packages/shared/src/constants/progress-metrics.constants.ts (5 hunks)
  • packages/shared/src/interfaces/analytics-data.interface.ts (1 hunks)
  • packages/shared/src/interfaces/dashboard-metric.interface.ts (1 hunks)
  • packages/shared/src/interfaces/dashboard.interface.ts (1 hunks)
  • packages/shared/src/interfaces/foundation-metrics.interface.ts (1 hunks)
  • packages/shared/src/interfaces/index.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Always use direct imports for standalone components - no barrel exports
Use TypeScript interfaces instead of union types for better maintainability
Do not nest ternary expressions
Prefer interface for defining object shapes in TypeScript
Use path mappings and import aliases as configured in tsconfig.json for imports

Files:

  • apps/lfx-one/src/app/modules/dashboards/components/dashboard-meeting-card/dashboard-meeting-card.component.ts
  • packages/shared/src/constants/foundation-health.constants.ts
  • packages/shared/src/interfaces/dashboard.interface.ts
  • packages/shared/src/interfaces/index.ts
  • packages/shared/src/constants/organization-involvement.constants.ts
  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.ts
  • packages/shared/src/constants/progress-metrics.constants.ts
  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts
  • packages/shared/src/interfaces/analytics-data.interface.ts
  • apps/lfx-one/src/server/controllers/analytics.controller.ts
  • packages/shared/src/interfaces/foundation-metrics.interface.ts
  • apps/lfx-one/src/app/shared/services/analytics.service.ts
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts
  • apps/lfx-one/src/server/services/project.service.ts
  • packages/shared/src/interfaces/dashboard-metric.interface.ts
  • apps/lfx-one/src/server/routes/analytics.route.ts
  • apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts
**/*.{html,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always add data-testid attributes when creating new components for reliable test targeting

Files:

  • apps/lfx-one/src/app/modules/dashboards/components/dashboard-meeting-card/dashboard-meeting-card.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.html
  • packages/shared/src/constants/foundation-health.constants.ts
  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.html
  • apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.html
  • apps/lfx-one/src/app/modules/dashboards/components/my-projects/my-projects.component.html
  • packages/shared/src/interfaces/dashboard.interface.ts
  • packages/shared/src/interfaces/index.ts
  • packages/shared/src/constants/organization-involvement.constants.ts
  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.html
  • packages/shared/src/constants/progress-metrics.constants.ts
  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts
  • packages/shared/src/interfaces/analytics-data.interface.ts
  • apps/lfx-one/src/server/controllers/analytics.controller.ts
  • packages/shared/src/interfaces/foundation-metrics.interface.ts
  • apps/lfx-one/src/app/shared/services/analytics.service.ts
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts
  • apps/lfx-one/src/server/services/project.service.ts
  • packages/shared/src/interfaces/dashboard-metric.interface.ts
  • apps/lfx-one/src/server/routes/analytics.route.ts
  • apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: License headers are required on all source files
Prepend 'Generated with Claude Code (https://claude.ai/code)' if assisted with the code

Files:

  • apps/lfx-one/src/app/modules/dashboards/components/dashboard-meeting-card/dashboard-meeting-card.component.ts
  • packages/shared/src/constants/foundation-health.constants.ts
  • packages/shared/src/interfaces/dashboard.interface.ts
  • packages/shared/src/interfaces/index.ts
  • packages/shared/src/constants/organization-involvement.constants.ts
  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.ts
  • packages/shared/src/constants/progress-metrics.constants.ts
  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts
  • packages/shared/src/interfaces/analytics-data.interface.ts
  • apps/lfx-one/src/server/controllers/analytics.controller.ts
  • packages/shared/src/interfaces/foundation-metrics.interface.ts
  • apps/lfx-one/src/app/shared/services/analytics.service.ts
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts
  • apps/lfx-one/src/server/services/project.service.ts
  • packages/shared/src/interfaces/dashboard-metric.interface.ts
  • apps/lfx-one/src/server/routes/analytics.route.ts
  • apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts
**/*.component.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use Angular 19 zoneless change detection with signals for component state management

Files:

  • apps/lfx-one/src/app/modules/dashboards/components/dashboard-meeting-card/dashboard-meeting-card.component.ts
  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts
**/*.html

📄 CodeRabbit inference engine (CLAUDE.md)

Use data-testid naming convention - [section]-[component]-[element] for hierarchical structure

Files:

  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.html
  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.html
  • apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.html
  • apps/lfx-one/src/app/modules/dashboards/components/my-projects/my-projects.component.html
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.html
packages/shared/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

All shared types, interfaces, and constants are centralized in @lfx-one/shared package

Files:

  • packages/shared/src/constants/foundation-health.constants.ts
  • packages/shared/src/interfaces/dashboard.interface.ts
  • packages/shared/src/interfaces/index.ts
  • packages/shared/src/constants/organization-involvement.constants.ts
  • packages/shared/src/constants/progress-metrics.constants.ts
  • packages/shared/src/interfaces/analytics-data.interface.ts
  • packages/shared/src/interfaces/foundation-metrics.interface.ts
  • packages/shared/src/interfaces/dashboard-metric.interface.ts
apps/**/**/server/**/*.{ts,js}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/**/**/server/**/*.{ts,js}: Use Pino for structured JSON logs with sensitive data redaction in all backend services
Always use err field for errors to leverage Pino's error serializer - correct: req.log.error({ err: error, ...metadata }, 'message')
Log INFO level for business operation completions (created, updated, deleted) and successful data retrieval
Log WARN level for error conditions leading to exceptions and data quality issues
Log DEBUG level for internal operations, preparation steps, and intent statements
Log ERROR level for system failures, unhandled exceptions, and critical errors

Files:

  • apps/lfx-one/src/server/controllers/analytics.controller.ts
  • apps/lfx-one/src/server/services/project.service.ts
  • apps/lfx-one/src/server/routes/analytics.route.ts
apps/**/**/server/**/*controller*.{ts,js}

📄 CodeRabbit inference engine (CLAUDE.md)

All controller functions must use Logger helper methods: Logger.start(), Logger.success(), Logger.error(), Logger.warning(), Logger.validation()

Files:

  • apps/lfx-one/src/server/controllers/analytics.controller.ts
🧠 Learnings (5)
📚 Learning: 2025-10-21T21:19:13.599Z
Learnt from: andrest50
Repo: linuxfoundation/lfx-v2-ui PR: 125
File: apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts:345-350
Timestamp: 2025-10-21T21:19:13.599Z
Learning: In the Angular meeting card component (apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts), when selecting between `summary.summary_data.edited_content` and `summary.summary_data.content`, the logical OR operator (`||`) is intentionally used instead of nullish coalescing (`??`) because empty string edited_content should fall back to the original content rather than being displayed as empty.

Applied to files:

  • apps/lfx-one/src/app/modules/dashboards/components/dashboard-meeting-card/dashboard-meeting-card.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.html
  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.html
  • apps/lfx-one/src/app/modules/dashboards/components/my-projects/my-projects.component.html
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.html
📚 Learning: 2025-11-24T17:42:04.908Z
Learnt from: CR
Repo: linuxfoundation/lfx-v2-ui PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:42:04.908Z
Learning: Applies to packages/shared/src/**/*.{ts,tsx} : All shared types, interfaces, and constants are centralized in lfx-one/shared package

Applied to files:

  • packages/shared/src/interfaces/index.ts
  • packages/shared/src/interfaces/foundation-metrics.interface.ts
📚 Learning: 2025-11-24T17:42:04.908Z
Learnt from: CR
Repo: linuxfoundation/lfx-v2-ui PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:42:04.908Z
Learning: All PrimeNG components are wrapped in LFX components for UI library independence

Applied to files:

  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts
📚 Learning: 2025-11-24T17:42:04.908Z
Learnt from: CR
Repo: linuxfoundation/lfx-v2-ui PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:42:04.908Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript interfaces instead of union types for better maintainability

Applied to files:

  • packages/shared/src/interfaces/dashboard-metric.interface.ts
📚 Learning: 2025-11-24T17:42:04.908Z
Learnt from: CR
Repo: linuxfoundation/lfx-v2-ui PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:42:04.908Z
Learning: Applies to **/*.component.ts : Use Angular 19 zoneless change detection with signals for component state management

Applied to files:

  • apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts
🧬 Code graph analysis (7)
packages/shared/src/constants/foundation-health.constants.ts (2)
packages/shared/src/interfaces/dashboard-metric.interface.ts (1)
  • DashboardMetricCard (23-113)
packages/shared/src/constants/colors.constants.ts (1)
  • lfxColors (9-106)
apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.ts (3)
apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts (1)
  • Component (24-558)
apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts (1)
  • Component (20-429)
apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts (1)
  • Component (37-867)
packages/shared/src/constants/progress-metrics.constants.ts (1)
packages/shared/src/interfaces/dashboard-metric.interface.ts (1)
  • DashboardMetricCard (23-113)
apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts (2)
apps/lfx-one/src/app/shared/components/filter-pills/filter-pills.component.ts (2)
  • Component (12-26)
  • FilterOption (7-10)
packages/shared/src/constants/chart-options.constants.ts (2)
  • BASE_LINE_CHART_OPTIONS (69-82)
  • BASE_BAR_CHART_OPTIONS (88-109)
apps/lfx-one/src/app/shared/services/analytics.service.ts (1)
packages/shared/src/interfaces/analytics-data.interface.ts (3)
  • HealthMetricsDailyResponse (1224-1239)
  • UniqueContributorsDailyResponse (1327-1342)
  • HealthEventsMonthlyResponse (1420-1435)
apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts (3)
packages/shared/src/interfaces/dashboard-metric.interface.ts (1)
  • DashboardMetricCard (23-113)
packages/shared/src/constants/foundation-health.constants.ts (1)
  • AGGREGATE_FOUNDATION_METRICS (120-136)
packages/shared/src/interfaces/analytics-data.interface.ts (2)
  • UniqueContributorsDailyResponse (1327-1342)
  • HealthEventsMonthlyResponse (1420-1435)
apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts (7)
packages/shared/src/interfaces/dashboard-metric.interface.ts (1)
  • DashboardMetricCard (23-113)
packages/shared/src/constants/progress-metrics.constants.ts (2)
  • MAINTAINER_PROGRESS_METRICS (116-194)
  • CORE_DEVELOPER_PROGRESS_METRICS (38-110)
packages/shared/src/interfaces/analytics-data.interface.ts (5)
  • ActiveWeeksStreakResponse (25-40)
  • UserPullRequestsResponse (66-81)
  • UserCodeCommitsResponse (107-122)
  • FoundationContributorsMentoredResponse (612-632)
  • HealthMetricsDailyResponse (1224-1239)
packages/shared/src/constants/colors.constants.ts (1)
  • lfxColors (9-106)
packages/shared/src/utils/color.utils.ts (1)
  • hexToRgba (28-56)
packages/shared/src/constants/chart-options.constants.ts (1)
  • BASE_LINE_CHART_OPTIONS (69-82)
packages/shared/src/utils/date-time.utils.ts (1)
  • parseLocalDateString (52-65)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Agent
🔇 Additional comments (34)
apps/lfx-one/src/app/shared/services/analytics.service.ts (1)

400-470: LGTM! Well-structured analytics methods.

The three new methods (getHealthMetricsDaily, getUniqueContributorsDaily, getHealthEventsMonthly) follow the established service patterns consistently, with proper error handling, fallback data, and clear JSDoc documentation.

packages/shared/src/interfaces/analytics-data.interface.ts (1)

1093-1435: LGTM! Comprehensive interface definitions.

The new analytics interfaces are well-structured with:

  • Consistent Snowflake naming conventions (ALL_CAPS)
  • Clear JSDoc documentation for all fields
  • Proper separation of foundation-level and project-level data
  • Appropriate use of optional fields and union types in response interfaces
apps/lfx-one/src/app/modules/dashboards/components/my-projects/my-projects.component.html (1)

32-40: LGTM! Consistent styling improvements.

The text color changes to text-gray-500 for headers and secondary information improve visual hierarchy by making less prominent information more subdued.

Also applies to: 65-65

apps/lfx-one/src/server/routes/analytics.route.ts (1)

53-60: LGTM! Routes follow established patterns.

The three new analytics endpoints are properly registered and follow the same structure as existing routes in the file.

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

88-97: LGTM! Effective refactoring to reusable component.

Replacing inline markup with <lfx-metric-card> eliminates code duplication and standardizes the metric card presentation across dashboards, aligning with the PR's unified dashboard metric model.

packages/shared/src/interfaces/index.ts (1)

43-44: LGTM! Proper barrel export.

The new export for dashboard-metric.interface follows the established pattern and is logically placed with a clear descriptive comment.

apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.html (1)

4-50: LGTM! Well-structured reusable metric card template.

The component template is well-organized with clear sections for header, body (chart/custom content), and footer. The conditional rendering logic is correct, and the non-null assertions on line 28 are safe since they're guarded by @else if (chartData()).

apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.ts (1)

17-38: LGTM! Component inputs are well-structured using Angular 19 signals.

The use of input() and input.required() signals follows modern Angular patterns. The logical grouping of inputs (Header, Chart, Footer, Optional indicators) with comments improves readability.

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

65-77: LGTM! Clean switch-case pattern for rendering different metric card types.

The sparkline case correctly falls back to sparklineOptions when card.chartOptions is not provided, ensuring consistent chart rendering.


161-173: LGTM! Default fallback case handles standard metric cards appropriately.

The default case passes all relevant properties to the metric card, ensuring any card type not explicitly handled still renders correctly.


92-109: The template reference and ContentChild selector match correctly—no action needed.

Verification confirms the ng-template #customContent in foundation-health.component.html (line 95) perfectly matches the @ContentChild('customContent') selector in MetricCardComponent. The implementation is functioning correctly.

Likely an incorrect or invalid review comment.

apps/lfx-one/src/server/controllers/analytics.controller.ts (3)

628-668: LGTM! Endpoint follows established patterns with proper validation and logging.

The getHealthMetricsDaily endpoint correctly validates required parameters, uses Logger helper methods, and follows the same structure as existing endpoints.


675-715: LGTM! Consistent implementation with other analytics endpoints.

The getUniqueContributorsDaily endpoint maintains consistency with the codebase patterns for parameter validation, service calls, and structured logging.


722-762: LGTM! Well-structured endpoint implementation.

The getHealthEventsMonthly endpoint completes the trio of new analytics endpoints with proper validation and error handling consistent with the rest of the controller.

packages/shared/src/constants/organization-involvement.constants.ts (2)

10-13: LGTM! Empty chart data placeholder for live data metrics.

The EMPTY_CHART_DATA constant is a clean way to indicate that chart data will be populated at runtime from API responses.


20-69: LGTM! Migration to DashboardMetricCard[] type is correct.

The PRIMARY_INVOLVEMENT_METRICS array properly uses the unified DashboardMetricCard interface, with appropriate chartType assignments for each metric type.

packages/shared/src/constants/foundation-health.constants.ts (1)

143-213: LGTM! Migration to unified DashboardMetricCard[] type is well-structured.

The PRIMARY_FOUNDATION_HEALTH_METRICS array correctly uses the unified interface. The chartType assignments are logical: 'line' for trend-based metrics and 'bar' for categorical data (Events, Project Health Scores). The customContentType field is preserved to drive specialized rendering in the template.

packages/shared/src/interfaces/foundation-metrics.interface.ts (2)

4-7: LGTM! Type consolidation follows centralization best practices.

Re-exporting shared types from dashboard-metric.interface aligns with the coding guideline that all shared types should be centralized in the @lfx-one/shared package. This approach maintains backward compatibility for existing imports while consolidating type definitions.

Based on coding guidelines, this centralization approach is correct.


9-44: LGTM! AggregateFoundationMetrics interface properly uses imported types.

The interface correctly references the re-exported types (TopProjectByValue, CompanyBusFactor, ProjectHealthDistribution) from the unified dashboard metric interface.

packages/shared/src/constants/progress-metrics.constants.ts (1)

26-32: Good pattern for placeholder data.

The EMPTY_CHART_DATA placeholder is a clean approach for metrics that will be populated dynamically by transform functions. This avoids unnecessary initial data and makes it clear which metrics expect live API data.

apps/lfx-one/src/server/services/project.service.ts (2)

1102-1161: Looks good - production-ready with proper parameterization.

The getHealthMetricsDaily method is well-structured:

  • Uses parameterized queries to prevent SQL injection
  • Correctly switches between foundation and project queries
  • Properly calculates the current average health score from the most recent row

Note: This method uses production schema (ANALYTICS.PLATINUM_LFX_ONE) while the other two new methods use dev schema (ANALYTICS_DEV.DEV_MSALOMAO_PLATINUM_LFX_ONE).

Please verify that the schema references are intentional:

  • getHealthMetricsDaily: ANALYTICS.PLATINUM_LFX_ONE
  • getUniqueContributorsDaily: ANALYTICS_DEV.DEV_MSALOMAO_PLATINUM_LFX_ONE
  • getHealthEventsMonthly: ANALYTICS_DEV.DEV_MSALOMAO_PLATINUM_LFX_ONE

1216-1265: Implementation is consistent with the pattern.

The getHealthEventsMonthly method follows the established pattern. The aggregation logic correctly extracts totalEvents from the first row (which is the most recent due to ORDER BY MONTH_START_DATE DESC).

packages/shared/src/interfaces/dashboard-metric.interface.ts (3)

1-17: Well-structured type definitions.

The use of string literal union types for MetricCategory and CustomContentType is appropriate here - interfaces cannot represent finite sets of string values. The JSDoc documentation is helpful for maintainability.


22-113: Unified interface design looks good.

The DashboardMetricCard interface is well-organized with clear section groupings. Having title and chartType as required while other fields are optional provides flexibility across different dashboard use cases.

One consideration: the interface is quite large (20+ properties). If this grows further, consider splitting into composed interfaces (e.g., ChartConfig, MembershipConfig) to improve maintainability.


119-167: Supporting interfaces are well-defined.

The supporting interfaces (ProjectHealthDistribution, CompanyBusFactor, TopProjectDisplay, TopProjectByValue) are properly typed with clear JSDoc descriptions.

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

20-26: Component properly migrated to MetricCardComponent.

The standalone component correctly imports MetricCardComponent and removes the obsolete TooltipModule. The component uses signals for state management as per Angular 19 guidelines.


326-342: Transform methods are well-structured.

The transform methods follow a consistent pattern: spread the base metric, override specific properties, and apply chart options. This is clean and maintainable.

apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts (5)

37-43: Component properly configured with signals.

The standalone component correctly imports MetricCardComponent and uses Angular 19 zoneless change detection patterns with signals for state management.


53-62: Loading state correctly tracks new data source.

The healthMetricsDaily loading flag is properly added to the loading state object, ensuring the component's loading indicator accounts for the new data source.


585-636: Health metrics daily transform is well-implemented.

The transformHealthMetricsDaily method follows the established pattern:

  • Reverses data for chronological display
  • Uses appropriate color from the design system
  • Implements proper tooltip with date formatting
  • Sets trend based on health score threshold

779-802: Data initialization follows established patterns.

The initializeHealthMetricsDailyData method correctly:

  • Reacts to project slug changes
  • Handles missing slug gracefully
  • Updates loading state appropriately
  • Uses finalize for cleanup

427-437: Console.error usage in tooltip callbacks is acceptable.

The try/catch blocks with console.error in tooltip callbacks are appropriate for non-critical rendering errors that shouldn't crash the application. The fallback values ensure graceful degradation.

Also applies to: 549-552

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

44-45: LGTM! Loading signals properly added.

The new loading signals follow the established pattern and are correctly integrated into the isLoading computed signal.


273-318: Well-implemented chart data transformation.

The active contributors chart transformation correctly:

  • Reverses data for chronological rendering (oldest to newest)
  • Formats dates and contributor values for display
  • Provides custom tooltip callbacks with proper formatting
  • Uses proper color theming with alpha transparency for filled areas

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a comprehensive refactoring of dashboard metrics by adding new health metrics API endpoints, creating a reusable MetricCardComponent, and unifying the metric display interfaces across all dashboard sections.

Key Changes

  • Backend API Integration: Added three new analytics endpoints for health metrics daily, unique contributors daily, and health events monthly data from Snowflake
  • Unified Component Architecture: Created a reusable MetricCardComponent that consolidates metric display logic across foundation health, recent progress, and organization involvement dashboards
  • Type-Safe Interface: Introduced DashboardMetricCard interface to standardize metric definitions with Chart.js integration and custom content projection support

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
packages/shared/src/interfaces/dashboard-metric.interface.ts New unified interface for all dashboard metric cards with comprehensive JSDoc
packages/shared/src/interfaces/analytics-data.interface.ts Added interfaces for health metrics, contributors, and events data from Snowflake
packages/shared/src/constants/progress-metrics.constants.ts Refactored to use DashboardMetricCard and simplified empty data placeholders
packages/shared/src/constants/organization-involvement.constants.ts Updated to use unified interface with proper chart type definitions
packages/shared/src/constants/foundation-health.constants.ts Consolidated metric definitions with unified interface
apps/lfx-one/src/server/services/project.service.ts Added three new methods for fetching health metrics, contributors, and events data
apps/lfx-one/src/server/routes/analytics.route.ts Registered new analytics API routes
apps/lfx-one/src/server/controllers/analytics.controller.ts Implemented three new endpoint handlers with validation
apps/lfx-one/src/app/shared/services/analytics.service.ts Added frontend service methods with error handling
apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.ts New reusable component for metric display with chart and custom content support
apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts Refactored to use MetricCardComponent and added health score metric
apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts Refactored to use unified interface with enhanced chart tooltips
apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts Integrated live API data for active contributors and events metrics
apps/lfx-one/src/app/modules/dashboards/components/my-projects/my-projects.component.html Updated styling from text-muted-foreground to text-gray-500 for consistency
apps/lfx-one/src/app/modules/dashboards/components/dashboard-meeting-card/dashboard-meeting-card.component.ts Added margin to icon for proper spacing

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.

- Consolidate foundation-health, organization-involvement, and progress-metrics
  constants into single dashboard-metrics.constants.ts file
- Move EMPTY_CHART_DATA and NO_TOOLTIP_CHART_OPTIONS to chart-options.constants.ts
- Merge dashboard.interface.ts and foundation-metrics.interface.ts into
  dashboard-metric.interface.ts
- Remove sparkline customContentType and use default metric card
- Simplify organization-involvement component by using shared imports

Signed-off-by: Asitha de Silva <[email protected]>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (4)
apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.html (1)

26-29: Add chartType to the guard condition to prevent runtime errors.

The non-null assertion chartType()! on line 28 is unsafe because the guard only checks chartData(). If chartType() returns undefined, this will cause a runtime error.

Apply this diff to add chartType to the guard:

-    } @else if (chartData()) {
+    } @else if (chartData() && chartType()) {
       <div class="mt-3 w-full h-16">
-        <lfx-chart [type]="chartType()" [data]="chartData()!" [options]="chartOptions()" height="100%"></lfx-chart>
+        <lfx-chart [type]="chartType()" [data]="chartData()" [options]="chartOptions()" height="100%"></lfx-chart>
       </div>
     }
apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.ts (1)

17-18: Add JSDoc documentation for the component.

The MetricCardComponent lacks component-level JSDoc explaining its purpose, usage, and key features. This is important for a reusable component that will be used across multiple dashboard modules.

Add JSDoc like this:

+/**
+ * Reusable metric card component for dashboard visualizations
+ * @description Displays a metric with optional icon, chart, and custom content projection.
+ * Supports both chart-based rendering (line/bar charts via Chart.js) and custom content via ng-template.
+ * @example
+ * <lfx-metric-card
+ *   [title]="'Active Contributors'"
+ *   [icon]="'fa-light fa-code'"
+ *   [chartType]="'line'"
+ *   [chartData]="chartData"
+ *   [value]="'1,234'"
+ *   [subtitle]="'Last 30 days'">
+ * </lfx-metric-card>
+ */
 export class MetricCardComponent {
packages/shared/src/interfaces/dashboard-metric.interface.ts (1)

24-35: Update documentation to accurately reflect required properties.

The comment on line 27 states "All properties are optional to support different use cases," but title (line 35) and chartType (line 54) are not marked as optional. This documentation is misleading.

Apply this diff to clarify which properties are required:

 /**
  * Unified dashboard metric card interface
  * @description Single interface for all dashboard metric cards (progress, foundation health, organization involvement)
- * All properties are optional to support different use cases - components validate required fields
+ * Required properties: title, chartType. All other properties are optional to support different use cases.
  */
 export interface DashboardMetricCard {
apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts (1)

308-315: Hardcoded placeholder value should come from API.

The value changed from '156' to '0', but it remains a hardcoded placeholder. This should be populated from an API endpoint or from metric.value when the training enrollments endpoint is implemented.

Consider updating to use dynamic data:

  private transformTrainingEnrollments(metric: DashboardMetricCard): DashboardMetricCard {
+   // TODO: Integrate with training enrollments API endpoint
    return {
      ...metric,
-     value: '0',
+     value: metric.value ?? '0', // Use metric value when API is integrated
      subtitle: 'Employees enrolled in training',
      chartOptions: this.createLineChartOptions('Training enrollments'),
    };
  }
🧹 Nitpick comments (1)
apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts (1)

164-202: Consider extracting chart construction helpers to reduce duplication.

While the transform methods correctly implement the DashboardMetricCard pattern, there's significant duplication in chart construction logic. Multiple methods build chartData and chartOptions with nearly identical patterns (especially for line charts: transformTotalProjects, transformTotalMembers, transformActiveContributors, transformMaintainers).

Consider creating helper methods similar to the approach in organization-involvement.component.ts:

private createLineChartWithTooltip(
  labels: string[],
  data: number[],
  tooltipLabel: string,
  color: string = lfxColors.blue[500]
): { chartData: ChartData<'line'>; chartOptions: ChartOptions<'line'> } {
  return {
    chartData: {
      labels,
      datasets: [{
        data,
        borderColor: color,
        backgroundColor: hexToRgba(color, 0.1),
        fill: true,
        tension: 0.4,
        borderWidth: 2,
        pointRadius: 0,
      }],
    },
    chartOptions: {
      ...this.sparklineOptions,
      plugins: {
        ...this.sparklineOptions.plugins,
        tooltip: {
          ...(this.sparklineOptions.plugins?.tooltip ?? {}),
          callbacks: {
            title: (context: TooltipItem<'line'>[]) => context[0].label,
            label: (context: TooltipItem<'line'>) => `${tooltipLabel}: ${context.parsed.y.toLocaleString()}`,
          },
        },
      },
    },
  };
}

Then transform methods could use:

private transformTotalProjects(metric: DashboardMetricCard): DashboardMetricCard {
  const data = this.totalProjectsData();
  const { chartData, chartOptions } = this.createLineChartWithTooltip(
    data.monthlyLabels,
    data.monthlyData,
    'Total projects'
  );
  
  return {
    ...metric,
    value: data.totalProjects.toLocaleString(),
    subtitle: `Total ${this.projectContextService.selectedFoundation()?.name} projects`,
    chartData,
    chartOptions,
  };
}

Also applies to: 204-242, 265-311, 313-351, 353-396

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between da3ed3c and 3ca10e6.

📒 Files selected for processing (15)
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.html (2 hunks)
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts (9 hunks)
  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts (5 hunks)
  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.html (1 hunks)
  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.ts (1 hunks)
  • packages/shared/src/constants/chart-options.constants.ts (2 hunks)
  • packages/shared/src/constants/dashboard-metrics.constants.ts (1 hunks)
  • packages/shared/src/constants/foundation-health.constants.ts (0 hunks)
  • packages/shared/src/constants/index.ts (1 hunks)
  • packages/shared/src/constants/organization-involvement.constants.ts (0 hunks)
  • packages/shared/src/constants/progress-metrics.constants.ts (0 hunks)
  • packages/shared/src/interfaces/dashboard-metric.interface.ts (1 hunks)
  • packages/shared/src/interfaces/dashboard.interface.ts (0 hunks)
  • packages/shared/src/interfaces/foundation-metrics.interface.ts (0 hunks)
  • packages/shared/src/interfaces/index.ts (1 hunks)
💤 Files with no reviewable changes (5)
  • packages/shared/src/constants/progress-metrics.constants.ts
  • packages/shared/src/constants/organization-involvement.constants.ts
  • packages/shared/src/interfaces/dashboard.interface.ts
  • packages/shared/src/constants/foundation-health.constants.ts
  • packages/shared/src/interfaces/foundation-metrics.interface.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Always use direct imports for standalone components - no barrel exports
Use TypeScript interfaces instead of union types for better maintainability
Do not nest ternary expressions
Prefer interface for defining object shapes in TypeScript
Use path mappings and import aliases as configured in tsconfig.json for imports

Files:

  • packages/shared/src/constants/chart-options.constants.ts
  • packages/shared/src/constants/index.ts
  • packages/shared/src/constants/dashboard-metrics.constants.ts
  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts
  • packages/shared/src/interfaces/dashboard-metric.interface.ts
  • packages/shared/src/interfaces/index.ts
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts
**/*.{html,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always add data-testid attributes when creating new components for reliable test targeting

Files:

  • packages/shared/src/constants/chart-options.constants.ts
  • packages/shared/src/constants/index.ts
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.html
  • packages/shared/src/constants/dashboard-metrics.constants.ts
  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts
  • packages/shared/src/interfaces/dashboard-metric.interface.ts
  • packages/shared/src/interfaces/index.ts
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts
  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.html
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: License headers are required on all source files
Prepend 'Generated with Claude Code (https://claude.ai/code)' if assisted with the code

Files:

  • packages/shared/src/constants/chart-options.constants.ts
  • packages/shared/src/constants/index.ts
  • packages/shared/src/constants/dashboard-metrics.constants.ts
  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts
  • packages/shared/src/interfaces/dashboard-metric.interface.ts
  • packages/shared/src/interfaces/index.ts
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts
packages/shared/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

All shared types, interfaces, and constants are centralized in @lfx-one/shared package

Files:

  • packages/shared/src/constants/chart-options.constants.ts
  • packages/shared/src/constants/index.ts
  • packages/shared/src/constants/dashboard-metrics.constants.ts
  • packages/shared/src/interfaces/dashboard-metric.interface.ts
  • packages/shared/src/interfaces/index.ts
**/*.html

📄 CodeRabbit inference engine (CLAUDE.md)

Use data-testid naming convention - [section]-[component]-[element] for hierarchical structure

Files:

  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.html
  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.html
**/*.component.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use Angular 19 zoneless change detection with signals for component state management

Files:

  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts
🧠 Learnings (4)
📚 Learning: 2025-11-24T17:42:04.908Z
Learnt from: CR
Repo: linuxfoundation/lfx-v2-ui PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:42:04.908Z
Learning: Applies to packages/shared/src/**/*.{ts,tsx} : All shared types, interfaces, and constants are centralized in lfx-one/shared package

Applied to files:

  • packages/shared/src/constants/index.ts
  • packages/shared/src/interfaces/index.ts
📚 Learning: 2025-10-21T21:19:13.599Z
Learnt from: andrest50
Repo: linuxfoundation/lfx-v2-ui PR: 125
File: apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts:345-350
Timestamp: 2025-10-21T21:19:13.599Z
Learning: In the Angular meeting card component (apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts), when selecting between `summary.summary_data.edited_content` and `summary.summary_data.content`, the logical OR operator (`||`) is intentionally used instead of nullish coalescing (`??`) because empty string edited_content should fall back to the original content rather than being displayed as empty.

Applied to files:

  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.html
  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.html
📚 Learning: 2025-11-24T17:42:04.908Z
Learnt from: CR
Repo: linuxfoundation/lfx-v2-ui PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:42:04.908Z
Learning: All PrimeNG components are wrapped in LFX components for UI library independence

Applied to files:

  • apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts
📚 Learning: 2025-11-24T17:42:04.908Z
Learnt from: CR
Repo: linuxfoundation/lfx-v2-ui PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:42:04.908Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript interfaces instead of union types for better maintainability

Applied to files:

  • packages/shared/src/interfaces/dashboard-metric.interface.ts
🔇 Additional comments (23)
packages/shared/src/interfaces/index.ts (1)

40-41: LGTM! Appropriate consolidation of dashboard metric interfaces.

The replacement of separate dashboard and foundation metric exports with a unified dashboard-metric interface aligns with the PR objectives to standardize metric definitions across modules.

packages/shared/src/constants/chart-options.constants.ts (2)

4-14: LGTM! Well-documented placeholder constant.

The EMPTY_CHART_DATA constant provides a clean way to initialize chart structures before live data is available. The JSDoc clearly explains its purpose for metrics populated by APIs.


175-190: LGTM! Appropriate sparkline configuration.

The NO_TOOLTIP_CHART_OPTIONS constant is well-suited for simple sparkline visualizations where tooltips and axes are unnecessary. The JSDoc clearly documents its use case.

packages/shared/src/constants/index.ts (1)

20-25: LGTM! Export consolidation aligns with refactoring objectives.

The addition of dashboard-metrics.constants and cookie.constants exports properly consolidates the metric configuration surface, replacing the previous separate constant files.

apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.html (1)

4-4: LGTM! Proper data-testid implementation.

The component correctly uses the data-testid attribute for test targeting, following the coding guidelines.

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

65-178: LGTM! Well-structured switch-based metric card rendering.

The refactoring to use a unified lfx-metric-card component with switch-based custom content rendering is clean and maintainable. All cases properly bind chartType and other required properties.

apps/lfx-one/src/app/shared/components/metric-card/metric-card.component.ts (1)

1-3: Verify license header compliance with coding guidelines.

The coding guidelines state: "Prepend 'Generated with Claude Code (https://claude.ai/code)' if assisted with the code." Please confirm whether this code was AI-assisted and update the header accordingly.

As per coding guidelines, if this file was created or significantly assisted by AI, add the attribution:

 // Copyright The Linux Foundation and each contributor to LFX.
 // SPDX-License-Identifier: MIT
+// Generated with Claude Code (https://claude.ai/code)
packages/shared/src/constants/dashboard-metrics.constants.ts (3)

1-3: Verify license header compliance with coding guidelines.

The coding guidelines require prepending 'Generated with Claude Code (https://claude.ai/code)' if the code was AI-assisted. Please confirm and update if applicable.

As per coding guidelines:

 // Copyright The Linux Foundation and each contributor to LFX.
 // SPDX-License-Identifier: MIT
+// Generated with Claude Code (https://claude.ai/code)

30-92: LGTM! Well-documented metric configurations.

The PRIMARY_FOUNDATION_HEALTH_METRICS provides a clear, type-safe configuration template for foundation health dashboard cards. The JSDoc correctly notes this is UI configuration only with data coming from APIs.


156-228: Sample data with Math.random() is appropriate for placeholders.

The use of Math.random() to generate sample chart data for metrics like "Issues Resolved & Comments Added" and "Learning Hours" is acceptable for initial placeholder visualization. These will be replaced when API endpoints are available.

packages/shared/src/interfaces/dashboard-metric.interface.ts (2)

10-22: LGTM! Union types are appropriate for literal type definitions.

The use of union types for FoundationHealthScore, MetricCategory, and CustomContentType is correct. These are literal type enums, not object shapes. The coding guideline preference for interfaces over union types applies to object structures, not to enum-like literal types.


1-3: Verify license header compliance with coding guidelines.

The coding guidelines require prepending 'Generated with Claude Code (https://claude.ai/code)' if the code was AI-assisted. Please confirm and update if applicable.

As per coding guidelines:

 // Copyright The Linux Foundation and each contributor to LFX.
 // SPDX-License-Identifier: MIT
+// Generated with Claude Code (https://claude.ai/code)
apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts (5)

9-9: LGTM! Clean import updates align with MetricCard refactoring.

The addition of MetricCardComponent and DashboardMetricCard types properly supports the component's migration to the unified metric card pattern. Direct imports follow coding guidelines.

Also applies to: 17-19, 23-23


53-106: LGTM! primaryMetrics correctly implements DashboardMetricCard pattern.

The computed signal properly transforms metrics using the new DashboardMetricCard interface and applies filtering logic while preserving membership tier visibility across all filter states.


222-229: LGTM! Transform methods properly implement DashboardMetricCard pattern.

All transform methods correctly spread the incoming metric and add chartOptions using the helper methods. The consistent pattern improves maintainability and aligns with the unified MetricCard approach.

Also applies to: 231-238, 240-273, 275-285, 287-297, 299-306


317-324: LGTM! Clean default handler implementation.

The transformDefaultMetric method properly handles fallback cases with sensible defaults and conditional chart options selection based on chartType.


326-356: LGTM! Helper methods successfully address previous duplication concerns.

The createBarChartOptions and createLineChartOptions methods effectively reduce the code duplication that was flagged in previous reviews (six nearly identical chart option objects). While there's minor similarity between these two helpers, they operate on different chart types ('bar' vs 'line'), making complete consolidation impractical without sacrificing type safety and readability.

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

9-9: LGTM! Import updates align with MetricCard refactoring.

The addition of MetricCardComponent and relevant types (DashboardMetricCard, HealthEventsMonthlyResponse, UniqueContributorsDailyResponse) properly supports the component's migration to the unified metric card pattern.

Also applies to: 16-16, 21-21


38-39: LGTM! New data streams properly integrated.

The activeContributorsLoading and eventsLoading signals with their corresponding data signals are correctly initialized and integrated into the isLoading computed signal, ensuring proper loading state management for the new metrics.

Also applies to: 47-48, 49-58


73-82: LGTM! Metric cards properly implement DashboardMetricCard pattern.

The computed signals correctly transform metrics using the DashboardMetricCard interface and apply filtering logic appropriately.

Also applies to: 84-112


255-263: TODO comment tracked for Company Bus Factor API integration.

The transformCompanyBusFactor method includes a TODO for replacing hardcoded COMPANY_BUS_FACTOR data with real API data. This is properly documented and uses a constant for the placeholder data.

Ensure this TODO is tracked in the project's issue tracker so the API integration isn't forgotten.


513-530: LGTM! New data initialization methods follow established patterns.

The initializeActiveContributorsData and initializeEventsData methods properly implement the reactive data pipeline pattern used throughout the component. They correctly:

  • Use toSignal with switchMap for foundation slug changes
  • Manage loading states with finalize
  • Pass appropriate entity type ('foundation') to analytics service
  • Provide type-safe initial values

Also applies to: 532-549


268-274: LGTM! Clear documentation of data transformation.

The comments explaining why data is reversed ("show oldest to newest for chart rendering") provide valuable context for future maintainers and make the intent of the transformation clear.

Also applies to: 356-362

- Add individual API endpoints for organization metrics:
  - /certified-employees - certification data by foundation
  - /membership-tier - membership tier data by foundation
  - /organization-maintainers - maintainer counts
  - /organization-contributors - contributor counts
  - /training-enrollments - enrollment data with cumulative charts
- Add TypeScript interfaces for Snowflake data mapping
- Update organization-involvement component to use live API data
- Add dummy chart data for metrics without live chart data yet
- Training enrollments shows cumulative chart with formatted dates

LFXV2-834

Signed-off-by: Asitha de Silva <[email protected]>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated 9 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (8)
packages/shared/src/constants/dashboard-metrics.constants.ts (3)

103-222: Missing testId attributes for involvement metrics.

Per coding guidelines, new components should have data-testid attributes for reliable test targeting. The PRIMARY_INVOLVEMENT_METRICS array lacks testId properties, unlike PRIMARY_FOUNDATION_HEALTH_METRICS.

 export const PRIMARY_INVOLVEMENT_METRICS: DashboardMetricCard[] = [
   {
     title: 'Membership Tier',
     icon: 'fa-light fa-dollar-sign',
     chartType: 'line',
     isMembershipTier: true,
+    testId: 'involvement-card-membership-tier',
   },
   {
     title: 'Active Contributors',
     icon: 'fa-light fa-code',
     chartType: 'bar',
+    testId: 'involvement-card-active-contributors',
     chartData: {

Apply similar additions for the remaining metrics in this array.


232-304: Missing testId attributes for developer progress metrics.

The CORE_DEVELOPER_PROGRESS_METRICS array should include testId properties for E2E testing consistency.

 export const CORE_DEVELOPER_PROGRESS_METRICS: DashboardMetricCard[] = [
   {
     title: 'Code Commits',
     value: '0',
     trend: 'up',
     subtitle: 'Last 30 days',
     chartType: 'line',
+    testId: 'progress-card-code-commits',
     chartData: EMPTY_CHART_DATA,

310-388: Missing testId attributes for maintainer progress metrics.

The MAINTAINER_PROGRESS_METRICS array should include testId properties for E2E testing consistency, similar to PRIMARY_FOUNDATION_HEALTH_METRICS.

 export const MAINTAINER_PROGRESS_METRICS: DashboardMetricCard[] = [
   {
     title: 'Critical Security Issues',
     icon: 'fa-light fa-shield',
     value: '19',
     trend: 'down',
     subtitle: 'Open critical security vulnerabilities',
     chartType: 'line',
     category: 'projectHealth',
+    testId: 'maintainer-card-critical-security-issues',
     chartData: {
apps/lfx-one/src/server/services/organization.service.ts (1)

237-271: Inconsistent error handling pattern for empty results.

getTrainingEnrollments returns an empty response when no data is found, while other methods (getCertifiedEmployees, getMembershipTier, etc.) throw ResourceNotFoundError. This inconsistency could lead to unexpected behavior for API consumers.

Consider either:

  1. Throwing ResourceNotFoundError here too, or
  2. Documenting that empty data is a valid response for this endpoint
     const result = await this.snowflakeService.execute<TrainingEnrollmentDailyRow>(query, [projectSlug, accountId]);
 
-    // Calculate total from the last cumulative count or sum of daily counts
-    const totalEnrollments = result.rows.length > 0 ? result.rows[result.rows.length - 1].CUMULATIVE_COUNT : 0;
+    if (result.rows.length === 0) {
+      throw new ResourceNotFoundError('Training enrollments data', accountId, {
+        operation: 'get_training_enrollments',
+      });
+    }
+
+    // Calculate total from the last cumulative count
+    const totalEnrollments = result.rows[result.rows.length - 1].CUMULATIVE_COUNT;

Alternatively, if empty is valid, add a JSDoc note explaining the design decision.

apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts (2)

122-167: Consider using constants or enum for metric title matching.

The metric title matching using string literals (e.g., 'Active Weeks Streak', 'Health Score') is fragile and prone to errors if titles change. Consider using constants or an enum to map metric identifiers to their transform functions.

// Example approach using a mapping object
private readonly metricTransformers: Record<string, (data: any, metric: DashboardMetricCard) => DashboardMetricCard> = {
  [MetricIdentifier.ActiveWeeksStreak]: (data, metric) => this.transformActiveWeeksStreak(data, metric),
  // ... other mappings
};

241-244: Inconsistent error handling in tooltip callbacks.

Some tooltip callbacks wrap parseLocalDateString in try-catch (e.g., lines 427-437) while others don't (here and lines 285-288, 360-363, 622-625). For consistency and robustness, consider applying the same error handling pattern across all callbacks, or use a shared helper.

 callbacks: {
   title: (context: TooltipItem<'line'>[]) => {
+    try {
       const dateStr = context[0].label;
       const date = parseLocalDateString(dateStr);
       return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
+    } catch {
+      return context[0]?.label || '';
+    }
   },
apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts (1)

350-351: Consider validating date strings before parsing.

If data.startDate or data.endDate contain invalid or empty strings, new Date() will create an Invalid Date, and toLocaleDateString will return "Invalid Date". Consider adding validation or a fallback.

+  const parseDate = (dateStr: string): Date | null => {
+    const d = new Date(dateStr);
+    return isNaN(d.getTime()) ? null : d;
+  };
+
-  const startDate = new Date(data.startDate);
-  const endDate = new Date(data.endDate);
-  const tierSince = startDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
-  const nextDue = endDate.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' });
+  const startDate = parseDate(data.startDate);
+  const endDate = parseDate(data.endDate);
+  const tierSince = startDate?.toLocaleDateString('en-US', { month: 'long', year: 'numeric' }) ?? 'N/A';
+  const nextDue = endDate?.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' }) ?? 'N/A';
packages/shared/src/interfaces/analytics-data.interface.ts (1)

1224-1239: Consider discriminated union or generic for response type.

The data property uses a union type which requires type guards in consumer code to safely access entity-specific fields (e.g., FOUNDATION_ID vs PROJECT_ID). Consider using a discriminated union with a type field, or a generic interface:

// Option 1: Discriminated union
export interface HealthMetricsDailyResponse<T extends 'foundation' | 'project'> {
  data: T extends 'foundation' ? FoundationHealthMetricsDailyRow[] : ProjectHealthMetricsDailyRow[];
  // ...
}

// Option 2: Add entityType discriminator
export interface HealthMetricsDailyResponse {
  entityType: 'foundation' | 'project';
  data: FoundationHealthMetricsDailyRow[] | ProjectHealthMetricsDailyRow[];
  // ...
}

As per coding guidelines, interfaces are preferred over union types for maintainability.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3ca10e6 and 33c1bfb.

📒 Files selected for processing (8)
  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts (6 hunks)
  • apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts (19 hunks)
  • apps/lfx-one/src/app/shared/services/analytics.service.ts (4 hunks)
  • apps/lfx-one/src/server/controllers/analytics.controller.ts (2 hunks)
  • apps/lfx-one/src/server/routes/analytics.route.ts (2 hunks)
  • apps/lfx-one/src/server/services/organization.service.ts (2 hunks)
  • packages/shared/src/constants/dashboard-metrics.constants.ts (1 hunks)
  • packages/shared/src/interfaces/analytics-data.interface.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Always use direct imports for standalone components - no barrel exports
Use TypeScript interfaces instead of union types for better maintainability
Do not nest ternary expressions
Prefer interface for defining object shapes in TypeScript
Use path mappings and import aliases as configured in tsconfig.json for imports

Files:

  • packages/shared/src/constants/dashboard-metrics.constants.ts
  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts
  • apps/lfx-one/src/server/routes/analytics.route.ts
  • apps/lfx-one/src/server/controllers/analytics.controller.ts
  • apps/lfx-one/src/server/services/organization.service.ts
  • apps/lfx-one/src/app/shared/services/analytics.service.ts
  • packages/shared/src/interfaces/analytics-data.interface.ts
**/*.{html,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always add data-testid attributes when creating new components for reliable test targeting

Files:

  • packages/shared/src/constants/dashboard-metrics.constants.ts
  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts
  • apps/lfx-one/src/server/routes/analytics.route.ts
  • apps/lfx-one/src/server/controllers/analytics.controller.ts
  • apps/lfx-one/src/server/services/organization.service.ts
  • apps/lfx-one/src/app/shared/services/analytics.service.ts
  • packages/shared/src/interfaces/analytics-data.interface.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: License headers are required on all source files
Prepend 'Generated with Claude Code (https://claude.ai/code)' if assisted with the code

Files:

  • packages/shared/src/constants/dashboard-metrics.constants.ts
  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts
  • apps/lfx-one/src/server/routes/analytics.route.ts
  • apps/lfx-one/src/server/controllers/analytics.controller.ts
  • apps/lfx-one/src/server/services/organization.service.ts
  • apps/lfx-one/src/app/shared/services/analytics.service.ts
  • packages/shared/src/interfaces/analytics-data.interface.ts
packages/shared/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

All shared types, interfaces, and constants are centralized in @lfx-one/shared package

Files:

  • packages/shared/src/constants/dashboard-metrics.constants.ts
  • packages/shared/src/interfaces/analytics-data.interface.ts
**/*.component.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use Angular 19 zoneless change detection with signals for component state management

Files:

  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts
apps/**/**/server/**/*.{ts,js}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/**/**/server/**/*.{ts,js}: Use Pino for structured JSON logs with sensitive data redaction in all backend services
Always use err field for errors to leverage Pino's error serializer - correct: req.log.error({ err: error, ...metadata }, 'message')
Log INFO level for business operation completions (created, updated, deleted) and successful data retrieval
Log WARN level for error conditions leading to exceptions and data quality issues
Log DEBUG level for internal operations, preparation steps, and intent statements
Log ERROR level for system failures, unhandled exceptions, and critical errors

Files:

  • apps/lfx-one/src/server/routes/analytics.route.ts
  • apps/lfx-one/src/server/controllers/analytics.controller.ts
  • apps/lfx-one/src/server/services/organization.service.ts
apps/**/**/server/**/*controller*.{ts,js}

📄 CodeRabbit inference engine (CLAUDE.md)

All controller functions must use Logger helper methods: Logger.start(), Logger.success(), Logger.error(), Logger.warning(), Logger.validation()

Files:

  • apps/lfx-one/src/server/controllers/analytics.controller.ts
🧠 Learnings (2)
📚 Learning: 2025-10-21T21:19:13.599Z
Learnt from: andrest50
Repo: linuxfoundation/lfx-v2-ui PR: 125
File: apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts:345-350
Timestamp: 2025-10-21T21:19:13.599Z
Learning: In the Angular meeting card component (apps/lfx-one/src/app/modules/project/meetings/components/meeting-card/meeting-card.component.ts), when selecting between `summary.summary_data.edited_content` and `summary.summary_data.content`, the logical OR operator (`||`) is intentionally used instead of nullish coalescing (`??`) because empty string edited_content should fall back to the original content rather than being displayed as empty.

Applied to files:

  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts
📚 Learning: 2025-11-24T17:42:04.908Z
Learnt from: CR
Repo: linuxfoundation/lfx-v2-ui PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T17:42:04.908Z
Learning: Applies to **/*.component.ts : Use Angular 19 zoneless change detection with signals for component state management

Applied to files:

  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.ts
  • apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts
🧬 Code graph analysis (4)
packages/shared/src/constants/dashboard-metrics.constants.ts (4)
packages/shared/src/interfaces/dashboard-metric.interface.ts (2)
  • CompanyBusFactor (139-148)
  • DashboardMetricCard (29-116)
packages/shared/src/constants/colors.constants.ts (1)
  • lfxColors (9-106)
packages/shared/src/utils/color.utils.ts (1)
  • hexToRgba (28-56)
packages/shared/src/constants/chart-options.constants.ts (2)
  • EMPTY_CHART_DATA (11-14)
  • NO_TOOLTIP_CHART_OPTIONS (179-190)
apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts (6)
packages/shared/src/interfaces/dashboard-metric.interface.ts (1)
  • DashboardMetricCard (29-116)
packages/shared/src/constants/dashboard-metrics.constants.ts (2)
  • MAINTAINER_PROGRESS_METRICS (310-388)
  • CORE_DEVELOPER_PROGRESS_METRICS (232-304)
packages/shared/src/interfaces/analytics-data.interface.ts (5)
  • ActiveWeeksStreakResponse (25-40)
  • UserPullRequestsResponse (66-81)
  • UserCodeCommitsResponse (107-122)
  • FoundationContributorsMentoredResponse (612-632)
  • HealthMetricsDailyResponse (1224-1239)
packages/shared/src/constants/colors.constants.ts (1)
  • lfxColors (9-106)
packages/shared/src/utils/color.utils.ts (1)
  • hexToRgba (28-56)
packages/shared/src/utils/date-time.utils.ts (1)
  • parseLocalDateString (52-65)
apps/lfx-one/src/server/services/organization.service.ts (2)
packages/shared/src/interfaces/analytics-data.interface.ts (10)
  • CertifiedEmployeesResponse (1472-1497)
  • CertifiedEmployeesRow (1441-1466)
  • MembershipTierResponse (1569-1629)
  • MembershipTierRow (1503-1563)
  • OrganizationMaintainersResponse (1661-1681)
  • OrganizationMaintainersRow (1635-1655)
  • OrganizationContributorsResponse (1713-1733)
  • OrganizationContributorsRow (1687-1707)
  • TrainingEnrollmentsResponse (1821-1856)
  • TrainingEnrollmentDailyRow (1800-1815)
apps/lfx-one/src/server/errors/index.ts (1)
  • ResourceNotFoundError (7-7)
apps/lfx-one/src/app/shared/services/analytics.service.ts (1)
packages/shared/src/interfaces/analytics-data.interface.ts (8)
  • OrganizationMaintainersResponse (1661-1681)
  • OrganizationContributorsResponse (1713-1733)
  • MembershipTierResponse (1569-1629)
  • CertifiedEmployeesResponse (1472-1497)
  • TrainingEnrollmentsResponse (1821-1856)
  • HealthMetricsDailyResponse (1224-1239)
  • UniqueContributorsDailyResponse (1327-1342)
  • HealthEventsMonthlyResponse (1420-1435)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: CodeQL analysis (javascript-typescript)
  • GitHub Check: Agent
🔇 Additional comments (16)
apps/lfx-one/src/server/routes/analytics.route.ts (1)

18-74: LGTM!

The new analytics routes are well-organized with clear section comments and follow the existing routing pattern consistently. All routes properly delegate to the corresponding controller methods.

apps/lfx-one/src/server/services/organization.service.ts (1)

170-198: LGTM for organization maintainers method.

Clean implementation with proper parameterized query, null coalescing for optional fields, and consistent error handling.

apps/lfx-one/src/server/controllers/analytics.controller.ts (2)

219-257: LGTM!

The getMembershipTier controller method follows the established patterns with proper validation, structured logging, and error handling using the Logger helper methods.


728-868: LGTM!

The new health metrics endpoints (getHealthMetricsDaily, getUniqueContributorsDaily, getHealthEventsMonthly) are well-implemented with:

  • Consistent validation for slug and entityType parameters
  • Proper entityType validation restricting to 'foundation' | 'project'
  • Structured logging with relevant metrics
  • Standard error handling pattern
apps/lfx-one/src/app/shared/services/analytics.service.ts (2)

113-149: LGTM!

The renamed getOrganizationMaintainers and new getOrganizationContributors methods properly map to the backend endpoints with appropriate fallback responses that match the interface shapes.


452-523: LGTM!

The new daily/monthly health metrics methods (getHealthMetricsDaily, getUniqueContributorsDaily, getHealthEventsMonthly) are well-implemented with:

  • Proper parameter handling for slug and entityType
  • Consistent error handling with meaningful console logging
  • Fallback responses that match the expected interface shapes
apps/lfx-one/src/app/modules/dashboards/components/recent-progress/recent-progress.component.ts (3)

585-636: LGTM - Health Score metric transformation.

The transformHealthMetricsDaily implementation follows the established pattern and correctly integrates with the DashboardMetricCard interface. The trend threshold of 50 for health scores is reasonable.


816-839: LGTM - Health metrics data initialization.

The initializeHealthMetricsDailyData method follows the established pattern for data initialization, properly handles the empty slug case, and correctly manages loading state with finalize.


638-661: LGTM - Persona-based data fetching optimization.

The conditional data fetching based on persona (core-developer vs maintainer) is a good optimization that prevents unnecessary API calls and correctly manages loading state for skipped fetches.

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

398-422: Previous hardcoded value issue has been addressed.

The transformTrainingEnrollments method now correctly uses data.totalEnrollments.toString() from the API response instead of the previously hardcoded '156' value. The chart data is also properly populated from data.dailyData.


433-463: Good refactoring - Chart options helpers reduce duplication.

The createBarChartOptions and createLineChartOptions helpers effectively address the previous code duplication concern and provide a clean, reusable way to generate chart options with custom tooltip labels.


42-65: LGTM - Individual loading signals with aggregated state.

The use of individual loading signals per data source with an aggregated isLoading computed signal follows good practices for granular state management while providing a convenient overall loading indicator.


404-420: Verify date string format from API.

The date formatting in chart labels assumes row.date is a valid date string. If the API returns unexpected formats, this could display "Invalid Date". Consider using parseLocalDateString for consistency with other date handling in the codebase.

packages/shared/src/interfaces/analytics-data.interface.ts (3)

1817-1856: LGTM - TrainingEnrollmentsResponse structure.

The TrainingEnrollmentsResponse interface is well-structured with clear JSDoc documentation and appropriate property types for the cumulative enrollment chart data.


1565-1629: LGTM - MembershipTierResponse interface.

The interface is well-documented and follows consistent naming conventions. The note about cleaned tier names (line 1601-1602) provides helpful context for consumers.


1093-1157: LGTM - Well-structured health metrics interfaces.

The new FoundationHealthMetricsDailyRow interface is comprehensive, with proper JSDoc documentation for each field. The optional TOTAL_PROJECTS_COUNT and TOTAL_SUB_PROJECTS_COUNT fields (lines 1151-1156) appropriately handle the differences between foundation and project level data.

@asithade asithade merged commit 8285184 into main Nov 26, 2025
12 checks passed
@asithade asithade deleted the feat/LFXV2-834 branch November 26, 2025 23:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants