-
Notifications
You must be signed in to change notification settings - Fork 0
feat(analytics): add foundation health metrics integration #181
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
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>
|
Note Other AI code review bot(s) detectedCodeRabbit 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. WalkthroughAdds 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes
Possibly related PRs
Suggested labels
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this 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.
...one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.html
Show resolved
Hide resolved
...x-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.ts
Outdated
Show resolved
Hide resolved
...one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.html
Show resolved
Hide resolved
...dules/dashboards/components/organization-involvement/organization-involvement.component.html
Show resolved
Hide resolved
...one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.html
Show resolved
Hide resolved
There was a problem hiding this 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, sotoLocaleString()on it won't add thousand separators. For a value like1234.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:
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}").Metric card naming (lines 63, 89): The dynamic
'dashboard-involvement-metric-' + metric.titleselector may include spaces (e.g., "Active Contributors"), which could complicate test selectors. Consider:
- Deriving test IDs from a stable, hyphenated
metric.idfield instead ofmetric.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 forchartOptions.Using
anyforchartOptionsloses 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:categoryshould useMetricCategory.
PrimaryFoundationHealthMetric.categoryis typed asstring, butFoundationMetricCard.categoryusesMetricCategory. 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
ifstatements checkingmetric.titlecould 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) usefinalizefor loading state but lackcatchErrorhandling. 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
ofimport fromrxjsand repeat for other initialization methods.
306-320: TODO: Replace mock data with real API data.The
transformCompanyBusFactor,transformActiveContributors, andtransformEventsmethods still useAGGREGATE_FOUNDATION_METRICSmock 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 constwas changed to plain'amber'. This is acceptable if thePendingActionItem.colortype acceptsstring; however, if it's a union type literal, consider keepingas constfor 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
ifstatements 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.
📒 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.tspackages/shared/src/constants/foundation-health.constants.tsapps/lfx-one/src/server/routes/analytics.route.tspackages/shared/src/constants/organization-involvement.constants.tsapps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.tspackages/shared/src/interfaces/foundation-metrics.interface.tsapps/lfx-one/src/server/services/project.service.tsapps/lfx-one/src/app/shared/services/analytics.service.tsapps/lfx-one/src/server/controllers/analytics.controller.tspackages/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.tsapps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.htmlpackages/shared/src/constants/foundation-health.constants.tsapps/lfx-one/src/server/routes/analytics.route.tspackages/shared/src/constants/organization-involvement.constants.tsapps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.tspackages/shared/src/interfaces/foundation-metrics.interface.tsapps/lfx-one/src/server/services/project.service.tsapps/lfx-one/src/app/modules/dashboards/components/organization-involvement/organization-involvement.component.htmlapps/lfx-one/src/app/shared/services/analytics.service.tsapps/lfx-one/src/server/controllers/analytics.controller.tspackages/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.tspackages/shared/src/constants/foundation-health.constants.tsapps/lfx-one/src/server/routes/analytics.route.tspackages/shared/src/constants/organization-involvement.constants.tsapps/lfx-one/src/app/modules/dashboards/components/foundation-health/foundation-health.component.tspackages/shared/src/interfaces/foundation-metrics.interface.tsapps/lfx-one/src/server/services/project.service.tsapps/lfx-one/src/app/shared/services/analytics.service.tsapps/lfx-one/src/server/controllers/analytics.controller.tspackages/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.htmlapps/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.tspackages/shared/src/constants/organization-involvement.constants.tspackages/shared/src/interfaces/foundation-metrics.interface.tspackages/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.tsapps/lfx-one/src/server/services/project.service.tsapps/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.htmlapps/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_METRICSarray provides consistent UI configuration for all 8 foundation health metrics. ThetestIdvalues follow thefoundation-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
sparklineColorvalues (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 || sparklineOptionsfor the sparkline fallback, and each card properly receives itsdata-testidfrom the metric configuration. The@switchblock 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, andtoSignal/toObservablefor 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 untilinitializeFromAuth()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
foundationSlugquery parameter in the controller layer (lines 469, 501, 533, 565, and 597 respectively), throwingServiceValidationErrorif 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
getFoundationTotalProjectsmethod correctly validates the requiredfoundationSlugparameter, delegates to the service layer, logs appropriate metrics, and handles errors consistently with other endpoints in this controller.
492-522: LGTM! Consistent implementation.The
getFoundationTotalMembersmethod follows the same well-established pattern as other foundation analytics endpoints.
524-554: LGTM! Proper logging of business metrics.The
getFoundationSoftwareValuemethod correctly logs thetotal_value_millionsandtop_projects_countfor observability.
556-586: LGTM! Consistent with other foundation endpoints.The
getFoundationMaintainersmethod properly logs trend data points count for monitoring purposes.
588-624: LGTM! Good derived metric logging.The
getFoundationHealthScoreDistributionmethod correctly computes and logstotalProjectsas 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
getFoundationTotalProjectsmethod uses a CTE with window function (SUM OVER) for proper cumulative counting. The transformation tomonthlyData/monthlyLabelsarrays and extractingtotalProjectsfrom the last row is correct.
922-962: LGTM! Consistent implementation with getFoundationTotalProjects.The
getFoundationTotalMembersmethod 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
getFoundationMaintainersmethod correctly extracts theavgMaintainersfrom 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
getFoundationTotalProjectsmethod 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
getFoundationTotalMembersmethod correctly returns empty arrays for monthly data on error.
226-241: LGTM! Proper fallback structure.The
getFoundationSoftwareValuemethod correctly returnstotalValue: 0and emptytopProjectsarray on error.
243-259: LGTM! Consistent implementation.The
getFoundationMaintainersmethod follows the same pattern with appropriate fallback values.
261-279: LGTM! Complete fallback for distribution response.The
getFoundationHealthScoreDistributionmethod 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
MonthlyProjectCountRowinterface follows the established Snowflake naming conventions with proper documentation.
725-774: LGTM! Comprehensive raw row interface.The
FoundationTotalProjectsRowinterface properly captures all Snowflake columns with clear JSDoc documentation for each field.
776-842: LGTM! Well-structured aggregation interfaces.Both
MonthlyProjectCountWithFoundationandMonthlyMemberCountWithFoundationcorrectly model the CTE query results used in the service layer.
844-890: LGTM! Clean API response shapes.The
FoundationTotalProjectsResponseandFoundationTotalMembersResponseinterfaces provide optimized response structures for frontend consumption with clear array-based trend data.
892-956: LGTM! Proper software value interfaces.The
FoundationTopProjectBySoftwareValueRowcaptures the raw Snowflake data, andFoundationSoftwareValueResponseprovides a clean API contract with thetopProjectsarray structure.
1036-1091: LGTM! Complete health score distribution interfaces.The
FoundationHealthScoreDistributionRowandFoundationHealthScoreDistributionResponseinterfaces properly model the five health categories with clear documentation.
958-1034: WeeklyMaintainersRow is unused—verified and appropriate for future features.Verification confirms that
WeeklyMaintainersRowhas 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>
There was a problem hiding this 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.
📒 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.
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 dataGET /api/analytics/foundation-total-members- Total members with cumulative monthly dataGET /api/analytics/foundation-software-value- Software value with top projectsGET /api/analytics/foundation-maintainers- Maintainers with daily trend dataGET /api/analytics/foundation-health-score-distribution- Health score distributionChanges
Frontend Integration (LFXV2-817)
Component Updates
Service Methods
Data Flow
Shared Types (LFXV2-818)
New Interfaces
FoundationTotalProjectsResponse- Projects with monthly dataFoundationTotalMembersResponse- Members with monthly dataFoundationSoftwareValueResponse- Software value with top projectsFoundationMaintainersResponse- Maintainers with trend dataFoundationHealthScoreDistributionResponse- Health score distributionConstants
PRIMARY_FOUNDATION_HEALTH_METRICS- Metric configuration templatesFiles 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
Generated with Claude Code