Skip to content

Conversation

@asithade
Copy link
Contributor

@asithade asithade commented Nov 25, 2025

Summary

Implements comprehensive foundation health analytics system with real-time Snowflake data integration across backend, frontend, and shared types.

Related JIRA Tickets

Backend Implementation (LFXV2-816)

New Endpoints

  • GET /api/analytics/foundation-total-projects - Total projects with cumulative monthly data
  • GET /api/analytics/foundation-total-members - Total members with cumulative monthly data
  • GET /api/analytics/foundation-software-value - Software value with top projects
  • GET /api/analytics/foundation-maintainers - Maintainers with daily trend data
  • GET /api/analytics/foundation-health-score-distribution - Health score distribution

Changes

  • Analytics controller: 5 new endpoint handlers with proper logging
  • Analytics routes: New route definitions with validation
  • Project service: 5 new Snowflake query methods with optimized aggregations
  • Error handling with ServiceValidationError for missing parameters

Frontend Integration (LFXV2-817)

Component Updates

  • Refactored Foundation Health component with Angular 19 signals
  • Reactive data loading with proper state management
  • Loading states per metric with skeleton loaders
  • Error handling with empty state fallbacks

Service Methods

  • 5 new analytics service methods with RxJS operators
  • Proper error handling with catchError
  • Type-safe API integration with HttpClient

Data Flow

  • Signal-based reactive data management
  • toObservable for foundation selection changes
  • switchMap for dependent API calls
  • finalize for loading state cleanup

Shared Types (LFXV2-818)

New Interfaces

  • FoundationTotalProjectsResponse - Projects with monthly data
  • FoundationTotalMembersResponse - Members with monthly data
  • FoundationSoftwareValueResponse - Software value with top projects
  • FoundationMaintainersResponse - Maintainers with trend data
  • FoundationHealthScoreDistributionResponse - Health score distribution
  • Snowflake row types with proper naming conventions (ALL_CAPS)

Constants

  • PRIMARY_FOUNDATION_HEALTH_METRICS - Metric configuration templates
  • Updated foundation health constants structure

Files Changed

Backend (3 files)

  • apps/lfx-one/src/server/controllers/analytics.controller.ts (+166 lines)
  • apps/lfx-one/src/server/routes/analytics.route.ts (+15 lines)
  • apps/lfx-one/src/server/services/project.service.ts (+226 lines)

Frontend (3 files)

  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts (+491 lines)
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.html (199 lines modified)
  • apps/lfx-one/src/app/shared/services/analytics.service.ts (+96 lines)

Shared Package (4 files)

  • packages/shared/src/interfaces/analytics-data.interface.ts (+384 lines)
  • packages/shared/src/interfaces/foundation-metrics.interface.ts (+24 lines)
  • packages/shared/src/constants/foundation-health.constants.ts (+73 lines)
  • packages/shared/src/constants/organization-involvement.constants.ts (6 lines modified)

Technical Details

  • Uses Angular 19 signals for reactive state management
  • Snowflake queries with optimized monthly aggregations
  • Proper error handling with Logger helper methods
  • Type-safe interfaces for Snowflake responses
  • Loading states with skeleton UI patterns

Generated with Claude Code

Implements comprehensive foundation health analytics system with real-time Snowflake data:

Backend Implementation (LFXV2-816):
- 5 new Snowflake analytics endpoints for foundation metrics
- Total projects with cumulative monthly aggregation
- Total members with cumulative monthly data
- Software value with top projects breakdown
- Maintainers with daily trend analysis
- Health score distribution across projects
- Server-side controllers with proper error handling
- Analytics routes configuration and validation

Frontend Integration (LFXV2-817):
- Refactored Foundation Health component with Angular signals
- Reactive data loading with proper state management
- 5 new analytics service methods with RxJS operators
- Loading states per metric with fallback handling
- Dynamic data visualization with sparklines and charts
- Error handling with empty state fallbacks

Shared Types (LFXV2-818):
- Comprehensive TypeScript interfaces for Snowflake responses
- Foundation health metric configurations
- Monthly aggregation data structures
- Top projects and health score distribution types
- Primary foundation health metrics constants

Generated with [Claude Code](https://claude.ai/code)

Signed-off-by: Asitha de Silva <asithade@gmail.com>
@asithade asithade requested a review from jordane as a code owner November 25, 2025 02:58
Copilot AI review requested due to automatic review settings November 25, 2025 02:58
@coderabbitai
Copy link

coderabbitai bot commented Nov 25, 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

Adds foundation-level analytics end-to-end: new backend endpoints and ProjectService Snowflake queries, new frontend AnalyticsService methods, extended shared interfaces/constants for foundation metrics, and UI changes to foundation-health and organization-involvement components (state-driven rendering and metric transformers). Also minor config and persona default changes.

Changes

Cohort / File(s) Change Summary
Configuration
​.vscode/settings.json
Added two words ("ADESILVA", "JEVANS") to cSpell dictionary.
Foundation health constants
packages/shared/src/constants/foundation-health.constants.ts
Added PRIMARY_FOUNDATION_HEALTH_METRICS export defining eight foundation metric card configurations.
Organization involvement constants
packages/shared/src/constants/organization-involvement.constants.ts
Updated sparklineColor values for three PRIMARY_INVOLVEMENT_METRICS entries.
Shared analytics interfaces
packages/shared/src/interfaces/analytics-data.interface.ts
Added multiple Snowflake row and API response interfaces for foundation metrics (total projects, members, software value, maintainers, health-score distribution, top projects, and related monthly/trend shapes).
Foundation metrics interfaces
packages/shared/src/interfaces/foundation-metrics.interface.ts
Added PrimaryFoundationHealthMetric interface and extended FoundationMetricCard with optional chartOptions.
Frontend — Foundation Health component
apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts, .../foundation-health.component.html
Converted to state-driven rendering (no-foundation / loading / carousel); added loading signals, data initialization pipelines, metric transformer functions mapping PRIMARY_FOUNDATION_HEALTH_METRICS to FoundationMetricCard instances, chart helpers and formatting logic.
Frontend — Organization Involvement component
apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.html
Reworked conditional rendering: loading guard now requires isLoading() && !primaryMetrics(), moved membership tier card into else branch, expanded membership info fields and adjusted markup around cards.
Frontend — Analytics service
apps/lfx-one/src/app/shared/services/analytics.service.ts
Added five new public methods to fetch foundation-scoped metrics with error fallbacks: getFoundationTotalProjects, getFoundationTotalMembers, getFoundationSoftwareValue, getFoundationMaintainers, getFoundationHealthScoreDistribution.
Frontend — Persona default
apps/lfx-one/src/app/shared/services/persona.service.ts
Changed default currentPersona value from 'maintainer' to 'board-member'.
Backend — Analytics controller & routes
apps/lfx-one/src/server/controllers/analytics.controller.ts, apps/lfx-one/src/server/routes/analytics.route.ts
Added new GET routes and controller handlers for foundation-total-projects, foundation-total-members, foundation-software-value, foundation-maintainers, foundation-health-score-distribution. Note: controller file contains duplicated implementations of the same five methods.
Backend — Project service
apps/lfx-one/src/server/services/project.service.ts
Implemented Snowflake-backed methods returning foundation analytics (total projects, members, software value, maintainers, health-score distribution) and constructed monthly/trend arrays; minor change: PendingActionItem color literal removed as const.

Sequence Diagram(s)

sequenceDiagram
    participant UI as Foundation Health Component
    participant FE as AnalyticsService (frontend)
    participant Ctrl as AnalyticsController (server)
    participant PS as ProjectService (server)
    participant DB as Snowflake

    UI->>FE: request foundation metrics (multiple endpoints)
    par Parallel requests
      FE->>Ctrl: GET /analytics/foundation-total-projects?slug=...
      FE->>Ctrl: GET /analytics/foundation-total-members?slug=...
      FE->>Ctrl: GET /analytics/foundation-software-value?slug=...
      FE->>Ctrl: GET /analytics/foundation-maintainers?slug=...
      FE->>Ctrl: GET /analytics/foundation-health-score-distribution?slug=...
    end

    par Controller -> Service
      Ctrl->>PS: getFoundationTotalProjects(slug)
      Ctrl->>PS: getFoundationTotalMembers(slug)
      Ctrl->>PS: getFoundationSoftwareValue(slug)
      Ctrl->>PS: getFoundationMaintainers(slug)
      Ctrl->>PS: getFoundationHealthScoreDistribution(slug)
    end

    par ProjectService -> Snowflake
      PS->>DB: execute queries for counts, trends, top projects, distributions
    end

    DB-->>PS: rows
    PS-->>Ctrl: aggregated responses
    Ctrl-->>FE: API responses (with fallbacks on error)
    FE-->>UI: observables/streams
    UI->>UI: map responses via transformers -> render no-data/loading/carousel states
    rect rgb(230,245,255)
      note right of UI: UI shows no-foundation OR loading OR carousel with metric cards
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

  • Areas needing extra attention:
    • apps/lfx-one/src/server/controllers/analytics.controller.ts — duplicated method implementations must be resolved.
    • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts — many new signals, transformers and async flows to validate correctness and error handling.
    • Cross-file consistency between new interfaces, ProjectService query outputs, AnalyticsService fallbacks and UI transformers.

Possibly related PRs

Suggested labels

deploy-preview

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive The VSCode spell-check entries, organization-involvement color updates, and persona service default change are minor adjustments; the duplicate methods in analytics.controller.ts appear to be unintended and warrant review. Review and remove the duplicate method declarations in analytics.controller.ts to ensure the controller has clean, non-redundant public API definitions.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(analytics): add foundation health metrics integration' clearly and concisely summarizes the main change—implementing comprehensive foundation health analytics with Snowflake data integration.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, covering backend, frontend, and shared type implementations with linked JIRA tickets and detailed file summaries.
Linked Issues check ✅ Passed All five new backend endpoints are implemented LFXV2-816, frontend component is refactored with signals and reactive loading [LFXV2-817], and shared interfaces and constants are added [LFXV2-818].
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-816

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

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 implements a comprehensive foundation health analytics system that integrates real-time Snowflake data across the full stack. The implementation connects 5 new backend API endpoints with Angular 19 signal-based reactive components to provide foundation health metrics including projects, members, software value, maintainers, and health score distribution.

Key Changes

  • 5 new Snowflake-backed API endpoints with monthly/daily aggregations for foundation metrics
  • Reactive Angular 19 signals pattern using toSignal + switchMap for automatic foundation-based data loading
  • Type-safe interfaces for raw Snowflake responses (ALL_CAPS) and transformed API responses (camelCase)

Reviewed changes

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

Show a summary per file
File Description
packages/shared/src/interfaces/analytics-data.interface.ts Added 384 lines of Snowflake row and API response interfaces with comprehensive documentation
packages/shared/src/interfaces/foundation-metrics.interface.ts Added PrimaryFoundationHealthMetric interface with custom content types and optional chart options
packages/shared/src/constants/foundation-health.constants.ts Added PRIMARY_FOUNDATION_HEALTH_METRICS configuration array for 8 metric cards
packages/shared/src/constants/organization-involvement.constants.ts Updated sparkline colors for involvement metrics
apps/lfx-one/src/server/services/project.service.ts Added 5 new Snowflake query methods with CTE-based cumulative aggregations
apps/lfx-one/src/server/routes/analytics.route.ts Added 5 new route definitions for foundation health endpoints
apps/lfx-one/src/server/controllers/analytics.controller.ts Added 5 controller methods with validation, logging, and error handling
apps/lfx-one/src/app/shared/services/analytics.service.ts Added 5 service methods with catchError fallbacks for graceful degradation
apps/lfx-one/src/app/shared/services/persona.service.ts Changed default persona from 'maintainer' to 'board-member'
apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts Refactored to reactive signals pattern with separate loading states per metric
apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.html Added foundation selection check and loading states
apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.html Improved loading state condition logic
.vscode/settings.json Added ADESILVA and JEVANS to spell check dictionary

💡 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: 0

Caution

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

⚠️ Outside diff range comments (1)
apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts (1)

143-149: Bug: toLocaleString() called on string has no effect.

toFixed(1) returns a string, so toLocaleString() on it won't add thousand separators. For a value like 1234.5, this returns "1234.5M" instead of "1,234.5M".

 private formatSoftwareValue(valueInMillions: number): string {
   if (valueInMillions >= 1000) {
     const billions = valueInMillions / 1000;
     return `${billions.toFixed(1)}B`;
   }
-  return `${valueInMillions.toFixed(1).toLocaleString()}M`;
+  return `${valueInMillions.toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 })}M`;
 }
🧹 Nitpick comments (10)
apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.html (1)

57-57: Consider enhancing data-testid coverage and naming clarity.

Current data-testid attributes follow the [section]-[component]-[element] convention. However, two refinements would improve test maintainability:

  1. Chart element (line 96): The chart wrapper currently lacks a data-testid. Per coding guidelines, new components should have data-testid attributes. Consider adding one (e.g., data-testid="dashboard-involvement-metric-chart-{metricType}").

  2. Metric card naming (lines 63, 89): The dynamic 'dashboard-involvement-metric-' + metric.title selector may include spaces (e.g., "Active Contributors"), which could complicate test selectors. Consider:

    • Deriving test IDs from a stable, hyphenated metric.id field instead of metric.title, or
    • Appending a card type indicator to distinguish membership from regular cards (e.g., 'dashboard-involvement-metric-' + metric.id + '-membership').

Also applies to: 63-63, 89-89, 96-96

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

64-65: Consider stronger typing for chartOptions.

Using any for chartOptions loses type safety. Consider importing Chart.js types for better IDE support and compile-time checks.

+import type { ChartOptions } from 'chart.js';
+
 export interface FoundationMetricCard {
   // ...
-  chartOptions?: any;
+  chartOptions?: ChartOptions;
 }

20-22: Type inconsistency: category should use MetricCategory.

PrimaryFoundationHealthMetric.category is typed as string, but FoundationMetricCard.category uses MetricCategory. This forces unnecessary type casting in the component (e.g., metric.category as MetricCategory).

 export interface PrimaryFoundationHealthMetric {
   // ...
-  /** Category for filtering */
-  category: string;
+  /** Category for filtering */
+  category: MetricCategory;
   // ...
 }
apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts (4)

1-2: Missing 'Generated with Claude Code' attribution.

Per coding guidelines, files assisted with Claude Code should include the attribution comment.

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

78-106: Consider using a Map or switch for metric transformers.

The chain of if statements checking metric.title could be refactored to a Map-based lookup or switch statement for better maintainability.

private readonly metricTransformers = new Map<string, (metric: PrimaryFoundationHealthMetric) => FoundationMetricCard>([
  ['Total Projects', (m) => this.transformTotalProjects(m)],
  ['Total Members', (m) => this.transformTotalMembers(m)],
  // ... other mappings
]);

private readonly allMetricCards = computed<FoundationMetricCard[]>(() => {
  return PRIMARY_FOUNDATION_HEALTH_METRICS.map((metric) => {
    const transformer = this.metricTransformers.get(metric.title);
    return transformer ? transformer(metric) : this.transformDefault(metric);
  });
});

431-448: Missing error handling in data initialization pipelines.

The data initialization methods (e.g., initializeTotalProjectsData) use finalize for loading state but lack catchError handling. API failures will log errors to the console, but users won't receive feedback, and the UI will show empty/zero data without indication of failure.

Consider adding error handling with fallback to initial values:

 return this.analyticsService.getFoundationTotalProjects(foundationSlug).pipe(
+  catchError((error) => {
+    console.error('Failed to load total projects:', error);
+    return of({ totalProjects: 0, monthlyData: [], monthlyLabels: [] });
+  }),
   finalize(() => this.totalProjectsLoading.set(false))
 );

Add the of import from rxjs and repeat for other initialization methods.


306-320: TODO: Replace mock data with real API data.

The transformCompanyBusFactor, transformActiveContributors, and transformEvents methods still use AGGREGATE_FOUNDATION_METRICS mock data as noted in the TODO comments. This aligns with the PR scope focusing on the 5 implemented endpoints.

Would you like me to open issues to track the remaining API integrations for Company Bus Factor, Active Contributors, and Events metrics?

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

857-857: Minor: Const assertion removed.

The 'amber' as const was changed to plain 'amber'. This is acceptable if the PendingActionItem.color type accepts string; however, if it's a union type literal, consider keeping as const for type safety.


970-998: Consider precision handling for currency values.

The software value is divided by 1,000,000 to convert to millions. Using floating-point division on potentially large financial values may introduce precision issues.

Consider using a more precise approach for financial calculations:

-    const totalValue = result.rows.reduce((sum, row) => sum + row.SOFTWARE_VALUE, 0) / 1000000;
+    const totalValueRaw = result.rows.reduce((sum, row) => sum + row.SOFTWARE_VALUE, 0);
+    const totalValue = Math.round(totalValueRaw / 10000) / 100; // Round to 2 decimal places in millions

     // Get top 3 projects and convert values to millions
     const topProjects = result.rows.slice(0, 3).map((row) => ({
       name: row.PROJECT_NAME,
-      value: row.SOFTWARE_VALUE / 1000000,
+      value: Math.round(row.SOFTWARE_VALUE / 10000) / 100,
     }));

1044-1074: Consider using a Map or object lookup for category mapping.

The current implementation uses multiple if statements for category mapping. A more maintainable approach would use a lookup pattern.

     // Map categories to response structure (case-insensitive)
-    const distribution = {
+    const distribution: FoundationHealthScoreDistributionResponse = {
       excellent: 0,
       healthy: 0,
       stable: 0,
       unsteady: 0,
       critical: 0,
     };

+    const categoryMap: Record<string, keyof FoundationHealthScoreDistributionResponse> = {
+      excellent: 'excellent',
+      healthy: 'healthy',
+      stable: 'stable',
+      unsteady: 'unsteady',
+      critical: 'critical',
+    };
+
     result.rows.forEach((row) => {
       const category = row.HEALTH_SCORE_CATEGORY.toLowerCase();
-      if (category === 'excellent') distribution.excellent = row.PROJECT_COUNT;
-      if (category === 'healthy') distribution.healthy = row.PROJECT_COUNT;
-      if (category === 'stable') distribution.stable = row.PROJECT_COUNT;
-      if (category === 'unsteady') distribution.unsteady = row.PROJECT_COUNT;
-      if (category === 'critical') distribution.critical = row.PROJECT_COUNT;
+      const key = categoryMap[category];
+      if (key) {
+        distribution[key] = row.PROJECT_COUNT;
+      }
     });
📜 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 cc3ae19 and 2917ccc.

📒 Files selected for processing (13)
  • .vscode/settings.json (2 hunks)
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.html (1 hunks)
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts (5 hunks)
  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.html (1 hunks)
  • apps/lfx-one/src/app/shared/services/analytics.service.ts (2 hunks)
  • apps/lfx-one/src/app/shared/services/persona.service.ts (1 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 (2 hunks)
  • packages/shared/src/constants/foundation-health.constants.ts (2 hunks)
  • packages/shared/src/constants/organization-involvement.constants.ts (1 hunks)
  • packages/shared/src/interfaces/analytics-data.interface.ts (1 hunks)
  • packages/shared/src/interfaces/foundation-metrics.interface.ts (2 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/shared/services/persona.service.ts
  • packages/shared/src/constants/foundation-health.constants.ts
  • apps/lfx-one/src/server/routes/analytics.route.ts
  • packages/shared/src/constants/organization-involvement.constants.ts
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts
  • packages/shared/src/interfaces/foundation-metrics.interface.ts
  • apps/lfx-one/src/server/services/project.service.ts
  • apps/lfx-one/src/app/shared/services/analytics.service.ts
  • apps/lfx-one/src/server/controllers/analytics.controller.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:

  • apps/lfx-one/src/app/shared/services/persona.service.ts
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.html
  • packages/shared/src/constants/foundation-health.constants.ts
  • apps/lfx-one/src/server/routes/analytics.route.ts
  • packages/shared/src/constants/organization-involvement.constants.ts
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts
  • packages/shared/src/interfaces/foundation-metrics.interface.ts
  • apps/lfx-one/src/server/services/project.service.ts
  • apps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.html
  • apps/lfx-one/src/app/shared/services/analytics.service.ts
  • apps/lfx-one/src/server/controllers/analytics.controller.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:

  • apps/lfx-one/src/app/shared/services/persona.service.ts
  • packages/shared/src/constants/foundation-health.constants.ts
  • apps/lfx-one/src/server/routes/analytics.route.ts
  • packages/shared/src/constants/organization-involvement.constants.ts
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts
  • packages/shared/src/interfaces/foundation-metrics.interface.ts
  • apps/lfx-one/src/server/services/project.service.ts
  • apps/lfx-one/src/app/shared/services/analytics.service.ts
  • apps/lfx-one/src/server/controllers/analytics.controller.ts
  • packages/shared/src/interfaces/analytics-data.interface.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/modules/dashboards/components/organization-involvement/organization-involvement.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/constants/organization-involvement.constants.ts
  • packages/shared/src/interfaces/foundation-metrics.interface.ts
  • packages/shared/src/interfaces/analytics-data.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/routes/analytics.route.ts
  • apps/lfx-one/src/server/services/project.service.ts
  • apps/lfx-one/src/server/controllers/analytics.controller.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/foundation-health/foundation-health.component.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 (3)
📚 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/modules/dashboards/components/organization-involvement/organization-involvement.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 **/*.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/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: All PrimeNG components are wrapped in LFX components for UI library independence

Applied to files:

  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts
🧬 Code graph analysis (5)
apps/lfx-one/src/app/shared/services/persona.service.ts (1)
packages/shared/src/interfaces/persona.interface.ts (1)
  • PersonaType (8-8)
packages/shared/src/constants/foundation-health.constants.ts (1)
packages/shared/src/interfaces/foundation-metrics.interface.ts (1)
  • PrimaryFoundationHealthMetric (15-30)
apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts (3)
packages/shared/src/interfaces/foundation-metrics.interface.ts (3)
  • FoundationMetricCard (36-72)
  • PrimaryFoundationHealthMetric (15-30)
  • MetricCategory (8-8)
packages/shared/src/constants/foundation-health.constants.ts (2)
  • PRIMARY_FOUNDATION_HEALTH_METRICS (178-240)
  • AGGREGATE_FOUNDATION_METRICS (155-171)
packages/shared/src/utils/color.utils.ts (1)
  • hexToRgba (28-56)
apps/lfx-one/src/server/services/project.service.ts (1)
packages/shared/src/interfaces/analytics-data.interface.ts (10)
  • FoundationTotalProjectsResponse (849-866)
  • MonthlyProjectCountWithFoundation (780-810)
  • FoundationTotalMembersResponse (873-890)
  • MonthlyMemberCountWithFoundation (817-842)
  • FoundationSoftwareValueResponse (942-956)
  • FoundationTopProjectBySoftwareValueRow (896-936)
  • FoundationMaintainersResponse (1018-1034)
  • FoundationMaintainersDailyRow (962-992)
  • FoundationHealthScoreDistributionResponse (1066-1091)
  • FoundationHealthScoreDistributionRow (1040-1060)
apps/lfx-one/src/app/shared/services/analytics.service.ts (1)
packages/shared/src/interfaces/analytics-data.interface.ts (5)
  • FoundationTotalProjectsResponse (849-866)
  • FoundationTotalMembersResponse (873-890)
  • FoundationSoftwareValueResponse (942-956)
  • FoundationMaintainersResponse (1018-1034)
  • FoundationHealthScoreDistributionResponse (1066-1091)
⏰ 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 (32)
.vscode/settings.json (1)

3-3: Verify necessity of spell-check dictionary additions.

Two proper nouns ("ADESILVA" and "JEVANS") are being added to the cSpell dictionary. While this is a low-risk change, please confirm these names are actually referenced within this PR's code changes (e.g., in data, comments, or configuration) and are necessary for this submission. If they're unrelated to the foundation health analytics implementation, consider deferring this change to a separate housekeeping PR to keep the commit focused.

Also applies to: 20-20

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

47-116: Structural refactor aligns with state-driven rendering; loading UX improved.

The change to isLoading() && !primaryMetrics() enables progressive rendering, and the else-block consolidation cleanly separates loading/no-data from content branches. Membership tier and regular card logic are well-organized.

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

78-163: Well-structured interfaces for foundation metrics data.

The interfaces (ProjectHealthDistribution, CompanyBusFactor, TopProjectByValue, TopProjectDisplay, AggregateFoundationMetrics) are well-documented with JSDoc comments and provide appropriate type safety for the analytics data structures.

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

173-240: Well-structured metric configurations with proper test IDs.

The PRIMARY_FOUNDATION_HEALTH_METRICS array provides consistent UI configuration for all 8 foundation health metrics. The testId values follow the foundation-health-card-[metric] naming convention as per coding guidelines. Colors and custom content types are appropriately assigned per metric type.

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

84-100: Color differentiation improves metric visual distinction.

The updated sparklineColor values (purple, green, amber) for Event Attendees, Event Speakers, and Certified Employees provide better visual distinction between metrics compared to the previous uniform blue (#0094FF).

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

43-59: State-driven rendering with proper loading/empty states.

The three-branch conditional (!hasFoundationSelected(), isLoading() && !metricCards(), else) handles the UI states appropriately. The loading and empty states include helpful icons and messages for user feedback.


60-171: Carousel implementation with proper chart options fallback.

The card rendering correctly uses card.chartOptions || sparklineOptions for the sparkline fallback, and each card properly receives its data-testid from the metric configuration. The @switch block handles all five custom content types appropriately.

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

33-52: Angular 19 signal-based architecture is well implemented.

The component correctly uses inject() for DI, signal() for loading states, computed() for derived values, and toSignal/toObservable for RxJS interop. This follows Angular 19 zoneless change detection patterns as per coding guidelines.

apps/lfx-one/src/app/shared/services/persona.service.ts (1)

20-23: Default persona change is safe—no breaking changes.

The change to 'board-member' as the fallback default is appropriate. This value only persists until initializeFromAuth() overrides it with the backend-provided persona based on committee membership. Existing users (maintainers, core-developers) will continue to receive their correct persona from the backend, and all persona-dependent UI logic remains intact. The fallback to 'board-member' for unauthenticated users is the most restrictive default and aligns with the PR's foundation health feature.

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

38-52: No changes required — validation is properly implemented in the controller.

Verification confirms all five new endpoints properly validate the required foundationSlug query parameter in the controller layer (lines 469, 501, 533, 565, and 597 respectively), throwing ServiceValidationError if missing. Each endpoint has comprehensive JSDoc documentation and follows the established logging patterns. The routes correctly delegate to these validated controller methods.

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

459-490: LGTM! Well-structured endpoint following established patterns.

The getFoundationTotalProjects method correctly validates the required foundationSlug parameter, delegates to the service layer, logs appropriate metrics, and handles errors consistently with other endpoints in this controller.


492-522: LGTM! Consistent implementation.

The getFoundationTotalMembers method follows the same well-established pattern as other foundation analytics endpoints.


524-554: LGTM! Proper logging of business metrics.

The getFoundationSoftwareValue method correctly logs the total_value_millions and top_projects_count for observability.


556-586: LGTM! Consistent with other foundation endpoints.

The getFoundationMaintainers method properly logs trend data points count for monitoring purposes.


588-624: LGTM! Good derived metric logging.

The getFoundationHealthScoreDistribution method correctly computes and logs totalProjects as a derived aggregate, along with individual category counts for comprehensive observability.

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

9-18: LGTM! Properly organized imports.

The new Foundation-related type imports are correctly grouped and imported from the shared interfaces package.


871-913: LGTM! Well-structured cumulative aggregation query.

The getFoundationTotalProjects method uses a CTE with window function (SUM OVER) for proper cumulative counting. The transformation to monthlyData/monthlyLabels arrays and extracting totalProjects from the last row is correct.


922-962: LGTM! Consistent implementation with getFoundationTotalProjects.

The getFoundationTotalMembers method correctly mirrors the pattern used for total projects, querying a different table and returning member counts.


1007-1035: LGTM! Clear handling of daily trend data.

The getFoundationMaintainers method correctly extracts the avgMaintainers from the first row (since it's consistent across all rows) and maps daily data to trend arrays.

apps/lfx-one/src/app/shared/services/analytics.service.ts (6)

10-14: LGTM! Properly imported Foundation response types.

The new imports are correctly added and alphabetically ordered within the existing import block.


190-206: LGTM! Consistent error handling pattern.

The getFoundationTotalProjects method follows the established pattern with proper type-safe HTTP call and fallback response matching the interface.


208-224: LGTM! Consistent with other foundation methods.

The getFoundationTotalMembers method correctly returns empty arrays for monthly data on error.


226-241: LGTM! Proper fallback structure.

The getFoundationSoftwareValue method correctly returns totalValue: 0 and empty topProjects array on error.


243-259: LGTM! Consistent implementation.

The getFoundationMaintainers method follows the same pattern with appropriate fallback values.


261-279: LGTM! Complete fallback for distribution response.

The getFoundationHealthScoreDistribution method correctly returns zero counts for all five health categories on error.

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

708-723: LGTM! Clear interface for monthly aggregation.

The MonthlyProjectCountRow interface follows the established Snowflake naming conventions with proper documentation.


725-774: LGTM! Comprehensive raw row interface.

The FoundationTotalProjectsRow interface properly captures all Snowflake columns with clear JSDoc documentation for each field.


776-842: LGTM! Well-structured aggregation interfaces.

Both MonthlyProjectCountWithFoundation and MonthlyMemberCountWithFoundation correctly model the CTE query results used in the service layer.


844-890: LGTM! Clean API response shapes.

The FoundationTotalProjectsResponse and FoundationTotalMembersResponse interfaces provide optimized response structures for frontend consumption with clear array-based trend data.


892-956: LGTM! Proper software value interfaces.

The FoundationTopProjectBySoftwareValueRow captures the raw Snowflake data, and FoundationSoftwareValueResponse provides a clean API contract with the topProjects array structure.


1036-1091: LGTM! Complete health score distribution interfaces.

The FoundationHealthScoreDistributionRow and FoundationHealthScoreDistributionResponse interfaces properly model the five health categories with clear documentation.


958-1034: WeeklyMaintainersRow is unused—verified and appropriate for future features.

Verification confirms that WeeklyMaintainersRow has no imports or references anywhere in the codebase. The interface is defined and exported but not utilized, which aligns with the original observation. For a shared package, maintaining forward-looking interface definitions for planned aggregation features is acceptable practice.

Changed from toFixed().toLocaleString() to toLocaleString() with decimal options.
toFixed() returns a string, so calling toLocaleString() on it had no effect.

Now using toLocaleString('en-US', { minimumFractionDigits: 1, maximumFractionDigits: 1 })
which properly formats numbers with thousand separators and one decimal place.

LFXV2-817

Generated with [Claude Code](https://claude.ai/code)

Signed-off-by: Asitha de Silva <asithade@gmail.com>
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 (1)
apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts (1)

1-3: Add Claude Code attribution per coding guidelines.

The PR summary indicates this code was "Generated with Claude Code," but the required attribution is missing from the file header.

Add the attribution after the license header:

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

Based on coding guidelines requiring: "Prepend 'Generated with Claude Code (https://claude.ai/code)' if assisted with the code"

📜 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 2917ccc and c32a41a.

📒 Files selected for processing (2)
  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts (5 hunks)
  • packages/shared/src/interfaces/foundation-metrics.interface.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/shared/src/interfaces/foundation-metrics.interface.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{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/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:

  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.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/foundation-health/foundation-health.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/foundation-health/foundation-health.component.ts
🧠 Learnings (2)
📚 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/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: All PrimeNG components are wrapped in LFX components for UI library independence

Applied to files:

  • apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts
🔇 Additional comments (1)
apps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts (1)

143-149: LGTM! Locale formatting issue resolved.

The method now correctly applies toLocaleString() directly on numeric values with fraction digit options, addressing the previous review concern about formatting a string.

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