Phase B.3.1: Operational decision dashboard core - #60
Conversation
Add scoped dashboard query service, four API endpoints, permissions, RTL /dashboard UI, drill-down list parity, tests, and phase documentation. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
There was a problem hiding this comment.
Sorry @henter36, your pull request is larger than the review limit of 150000 diff characters
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughAdds the Phase B.3.1 operational dashboard with scoped backend aggregation, permissions, API endpoints, an RTL frontend page, drill-down filters, URL-synchronized list state, documentation, and automated tests. ChangesOperational dashboard
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant DashboardPage
participant DashboardApi
participant DashboardEndpoints
participant DashboardQueryService
participant Database
DashboardPage->>DashboardApi: request dashboard data
DashboardApi->>DashboardEndpoints: GET dashboard operation endpoint
DashboardEndpoints->>DashboardQueryService: execute filtered query
DashboardQueryService->>Database: aggregate scoped notes and corrective actions
Database-->>DashboardQueryService: return aggregates
DashboardQueryService-->>DashboardEndpoints: return dashboard DTO
DashboardEndpoints-->>DashboardApi: return HTTP response
DashboardApi-->>DashboardPage: render dashboard
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request implements Phase B.3.1, introducing the Operational Decision Dashboard Core. It adds backend services, API endpoints, permissions, and a React-based RTL frontend page at /dashboard with KPI summaries, trends, breakdowns, and priority queues, as well as drill-down parity filters on the notes list. The reviewer feedback highlights critical performance improvements, specifically addressing an N+1 query bottleneck in the trends calculation, recommending the consolidation of multiple database count queries in the summary endpoint into a single projection, and suggesting centralized exception handling to eliminate duplicated try-catch blocks across the new endpoints.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (2)
src/backend/Baseera.Application/Dashboard/OperationalDashboardQueryService.cs (1)
147-206: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy liftTrend aggregation issues 6 DB round trips per bucket.
The per-bucket loop runs
notesCreated,notesCompleted,notesBecameOverdue,caCompleted,routingSuccess, androutingFailureas separateCountAsynccalls. For daily granularity (up to ~59 buckets) this is ~350+ sequential queries per trends request, plus the summary path repeatedly re-materializes scoped note IDs. Consider a single grouped/bucketed aggregation per metric (e.g.,GroupByon a computed bucket key) to collapse this into a handful of queries.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/backend/Baseera.Application/Dashboard/OperationalDashboardQueryService.cs` around lines 147 - 206, Refactor the trend aggregation around the per-bucket loop so the six sequential CountAsync calls are replaced by grouped, bucketed queries that aggregate each metric across all buckets in a handful of database round trips. Reuse the scoped note and corrective-action queryables, materialize each grouped result once, and populate points from lookup results while preserving the existing bucket boundaries, filters, and zero values for missing buckets.src/frontend/src/pages/notes/NotesListPage.tsx (1)
97-117: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReduce cognitive complexity of the URL-sync effect (SonarCloud gate).
SonarCloud reports this effect at cognitive complexity 17 (limit 15), which is failing the check. Extract the param-building into a small pure helper (e.g.
buildNotesSearchParams(state)) and callsetSearchParamswith its result to bring it under the threshold without behavior change.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/frontend/src/pages/notes/NotesListPage.tsx` around lines 97 - 117, The URL synchronization effect in NotesListPage currently builds parameters with excessive branching. Extract that logic into a pure helper such as buildNotesSearchParams, passing the relevant filter, pagination, and sorting state, then have the effect call setSearchParams with the helper’s result while preserving all existing parameter inclusion and replacement behavior.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/phase-b31-dashboard-baseline.md`:
- Around line 4-10: Reconcile the unit-test count in the pre-B.3.1 baseline for
base SHA afb796b2bd85a1ae26b91b8f6e67b7646d024f06 with the 287-test B.2.3.2
completion report. Update the baseline to the matching count, or document the
differing test commands or scope that account for 298 versus 287, keeping the
phase comparison auditable.
In
`@src/backend/Baseera.Application/Dashboard/OperationalDashboardQueryService.cs`:
- Around line 775-792: Update the facility name resolution in the
OperationalDashboardOverdueLocationQueueItemDto projection to use
facilities.TryGetValue instead of the direct facilities indexer, returning a
safe null or established fallback when the facility is absent while preserving
the existing region resolution and queue item mapping.
- Around line 594-641: The severity and status breakdown builders return a
hardcoded corrective-action overdue value of zero despite receiving actions. In
BuildSeverityBreakdownAsync and the sibling builder at
src/backend/Baseera.Application/Dashboard/OperationalDashboardQueryService.cs
lines 643-690, join or otherwise aggregate the matching actions to populate
caOverdue consistently with the region, facility, and note-type breakdowns; if
corrective-action reporting is intentionally excluded, remove the unused actions
parameter and ensure both sites reflect that decision.
In `@src/frontend/src/index.css`:
- Line 327: Update the metric card border declaration using the visible border
property to reference the defined --line CSS variable instead of --border,
preserving the existing 1px solid styling.
In `@src/frontend/src/pages/dashboard/dashboardDrillDown.ts`:
- Line 23: Update the sortDesc serialization near the dashboard drill-down
parameter construction to set the URL parameter whenever filters.sortDesc is
defined, emitting its boolean value as a string so false is preserved. Keep
omission only for an unset value, allowing NotesListPage to distinguish explicit
ascending order from the default descending order.
---
Nitpick comments:
In
`@src/backend/Baseera.Application/Dashboard/OperationalDashboardQueryService.cs`:
- Around line 147-206: Refactor the trend aggregation around the per-bucket loop
so the six sequential CountAsync calls are replaced by grouped, bucketed queries
that aggregate each metric across all buckets in a handful of database round
trips. Reuse the scoped note and corrective-action queryables, materialize each
grouped result once, and populate points from lookup results while preserving
the existing bucket boundaries, filters, and zero values for missing buckets.
In `@src/frontend/src/pages/notes/NotesListPage.tsx`:
- Around line 97-117: The URL synchronization effect in NotesListPage currently
builds parameters with excessive branching. Extract that logic into a pure
helper such as buildNotesSearchParams, passing the relevant filter, pagination,
and sorting state, then have the effect call setSearchParams with the helper’s
result while preserving all existing parameter inclusion and replacement
behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 20343b57-ef69-4b15-9128-63ec13437fbb
📒 Files selected for processing (36)
README.mddocs/implementation-plan.mddocs/permissions-matrix.mddocs/phase-b232-note-routing-completion-report.mddocs/phase-b31-dashboard-api-contract.mddocs/phase-b31-dashboard-baseline.mddocs/phase-b31-dashboard-completion-report.mddocs/phase-b31-dashboard-scope.mddocs/phase-b31-dashboard-security.mddocs/phase-b31-dashboard-test-matrix.mdsrc/backend/Baseera.Api/Authorization/AuthorizationExtensions.cssrc/backend/Baseera.Api/Endpoints/ApiEndpoints.cssrc/backend/Baseera.Api/Endpoints/NoteListQueryParams.cssrc/backend/Baseera.Api/Endpoints/OperationalDashboardQueryParams.cssrc/backend/Baseera.Application/Dashboard/IOperationalDashboardQueryService.cssrc/backend/Baseera.Application/Dashboard/OperationalDashboardDtos.cssrc/backend/Baseera.Application/Dashboard/OperationalDashboardFilterBuilder.cssrc/backend/Baseera.Application/Dashboard/OperationalDashboardKpiDefinitions.cssrc/backend/Baseera.Application/Dashboard/OperationalDashboardQueryService.cssrc/backend/Baseera.Application/DependencyInjection/ApplicationServiceCollectionExtensions.cssrc/backend/Baseera.Application/Notes/NoteDtos.cssrc/backend/Baseera.Application/Notes/NoteQueryService.cssrc/backend/Baseera.Domain/Identity/IdentityEntities.cssrc/backend/Baseera.Infrastructure/Persistence/DatabaseInitializer.cssrc/backend/tests/Baseera.IntegrationTests/OperationalDashboardIntegrationTests.cssrc/backend/tests/Baseera.UnitTests/Dashboard/OperationalDashboardQueryServiceTests.cssrc/frontend/src/App.tsxsrc/frontend/src/api/client.tssrc/frontend/src/index.csssrc/frontend/src/pages/corrective-actions/CorrectiveActionsListPage.tsxsrc/frontend/src/pages/dashboard/OperationalDashboardPage.permission.test.tsxsrc/frontend/src/pages/dashboard/OperationalDashboardPage.test.tsxsrc/frontend/src/pages/dashboard/OperationalDashboardPage.tsxsrc/frontend/src/pages/dashboard/dashboardDrillDown.test.tssrc/frontend/src/pages/dashboard/dashboardDrillDown.tssrc/frontend/src/pages/notes/NotesListPage.tsx
Consolidate summary KPIs into SQL-side aggregates, replace trends per-bucket queries with fixed daily routing queries merged in memory, deduplicate breakdown builders, and fix severity/status corrective-action overdue counts with query-count tests. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/frontend/src/pages/notes/notesListSearchParams.ts (1)
3-23: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReduce cognitive complexity to clear the SonarCloud gate. The linear
if-chain reports cognitive complexity 19 (limit 15) and is failing the SonarCloud check. Extracting the simple string-field assignments into a small table keeps the special cases (page > 1,sortBy !== 'createdAtUtc',sortDesc === false) explicit while dropping the count below threshold.♻️ Example table-driven approach
export function buildNotesListSearchParams(filters: NoteListFilters): URLSearchParams { const params = new URLSearchParams() const stringFields: [keyof NoteListFilters, string][] = [ ['search', 'search'], ['noteTypeId', 'noteTypeId'], ['regionId', 'regionId'], ['facilityId', 'facilityId'], ['facilityUnitId', 'facilityUnitId'], ['ownerDepartmentId', 'ownerDepartmentId'], ] for (const [key, param] of stringFields) { const value = filters[key] if (value) params.set(param, String(value)) } const numericFields: [keyof NoteListFilters, string][] = [ ['status', 'status'], ['severity', 'severity'], ['classification', 'classification'], ['dueSoonDays', 'dueSoonDays'], ] for (const [key, param] of numericFields) { const value = filters[key] if (value != null) params.set(param, String(value)) } const boolFlags: [keyof NoteListFilters, string][] = [ ['overdueOnly', 'overdueOnly'], ['unassignedOnly', 'unassignedOnly'], ['requiresMyAction', 'requiresMyAction'], ['requiresRouting', 'requiresRouting'], ] for (const [key, param] of boolFlags) { if (filters[key]) params.set(param, 'true') } if (filters.page != null && filters.page > 1) params.set('page', String(filters.page)) if (filters.sortBy && filters.sortBy !== 'createdAtUtc') params.set('sortBy', filters.sortBy) if (filters.sortDesc === false) params.set('sortDesc', 'false') return params }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/frontend/src/pages/notes/notesListSearchParams.ts` around lines 3 - 23, Reduce cognitive complexity in buildNotesListSearchParams by grouping the straightforward string, nullable numeric, and boolean fields into table-driven loops that set their corresponding parameters. Keep the special-case handling for page > 1, non-default sortBy, and sortDesc === false explicit and preserve all existing truthiness and null checks.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/frontend/src/pages/notes/notesListSearchParams.ts`:
- Around line 3-23: Reduce cognitive complexity in buildNotesListSearchParams by
grouping the straightforward string, nullable numeric, and boolean fields into
table-driven loops that set their corresponding parameters. Keep the
special-case handling for page > 1, non-default sortBy, and sortDesc === false
explicit and preserve all existing truthiness and null checks.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 19a6ff19-81cd-43e2-a3c8-b5bd0844dab0
📒 Files selected for processing (11)
src/backend/Baseera.Application/Dashboard/OperationalDashboardAggregationHelpers.cssrc/backend/Baseera.Application/Dashboard/OperationalDashboardFilterBuilder.cssrc/backend/Baseera.Application/Dashboard/OperationalDashboardKpiDefinitions.cssrc/backend/Baseera.Application/Dashboard/OperationalDashboardQueryService.cssrc/backend/tests/Baseera.IntegrationTests/OperationalDashboardQueryCountIntegrationTests.cssrc/backend/tests/Baseera.UnitTests/Dashboard/OperationalDashboardQueryServiceTests.cssrc/frontend/src/pages/dashboard/OperationalDashboardPage.test.tsxsrc/frontend/src/pages/dashboard/OperationalDashboardPage.tsxsrc/frontend/src/pages/notes/NotesListPage.tsxsrc/frontend/src/pages/notes/notesListSearchParams.test.tssrc/frontend/src/pages/notes/notesListSearchParams.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- src/frontend/src/pages/dashboard/OperationalDashboardPage.test.tsx
- src/frontend/src/pages/notes/NotesListPage.tsx
- src/backend/Baseera.Application/Dashboard/OperationalDashboardFilterBuilder.cs
- src/frontend/src/pages/dashboard/OperationalDashboardPage.tsx
|



Summary
summary,trends,breakdowns,priority-queues) and RTL/dashboardUI with drill-down linksDashboard.*permissions, extend notes/CA list URL filters for parity, and add unit/integration/frontend test coverage plus phase docsTest plan
dotnet build src/backend/Baseera.slnx -c ReleaseBASEERA_TEST_CONNECTION)Made with Cursor
Summary by CodeRabbit
/dashboard(summary, trends, breakdowns, and priority queues) with drill-down links and permission-based section visibility.due-soonandunassignedfiltering, synced to URL state.