Skip to content

RG-T151 Custom Modern Dashboard - #573

Merged
ucswift merged 2 commits into
masterfrom
develop
Nov 1, 2026
Merged

ucswift merged 2 commits into
masterfrom
develop

Conversation

@ucswift

@ucswift ucswift commented Nov 1, 2026 •

Copy link
Copy Markdown
Member

Summary

Implements RG-T151, introducing a feature-flagged modern home dashboard while preserving the existing classic dashboard experience.

Modern dashboard

  • Adds the Dashboard.Modern feature flag, seeded off by default and evaluated per department.
  • Replaces the home page with a customizable widget board when enabled.
  • Supports:
    • Drag-and-drop and keyboard widget reordering.
    • Widget resizing from XS through Full.
    • Widget-specific settings and department-scoped option lists.
    • Up to five boards per member, with the Home board protected from deletion.
    • Server-side persistence per member and department.
    • Optimistic concurrency handling with conflict resolution.
    • Compact and Comfortable density modes.
    • Wall-display mode.
    • Live updates where supported, with polling fallbacks.
    • Classic-dashboard fallback when JavaScript is unavailable or the modern dashboard cannot load.

Dashboard templates

  • Adds department dashboard templates for department administrators.
  • Templates can target:
    • Everyone.
    • A personnel role.
    • A group.
  • Adds priority-based template resolution and a department-default template.
  • Preserves customized member boards when templates change.
  • Adds limits of 25 templates per department and 40 widgets per board.
  • Records template saves and deletions as DashboardTemplateChanged system audit events.

Widget catalog and module coverage

Adds a catalog and provider model for dashboard widgets across Operations, Personnel, Map & Weather, Communications, Planning, Readiness, Records, Workforce & Business, Incident Back Office, Intelligence, and Administration.

Widgets expose module-consistent data and access behavior for areas including:

  • Active, pending, scheduled, and awaiting-resource calls.
  • Unit status, alerts, summaries, routes, and incident command.
  • Personnel status, staffing, and status boards.
  • Calendar, shifts, trainings, documents, notes, links, and contacts.
  • Checklists, work orders, inventory, and certifications.
  • Records queues, analytics, prevention, hydrants, investigations, legal holds, and NERIS.
  • Deployments, time reports, invoicing, bids, contracts, cost recovery, and workforce reporting.
  • Chat, messaging, communication tests, Push-To-Talk, weather, maps, AI dispatch triage, and administrative health information.

Security and entitlement handling

  • Reuses module policies, permissions, feature flags, add-on entitlements, visibility rules, and module switches when determining widget availability.
  • Adds locked gallery cards for department administrators when an offered paid add-on is not held.
  • Keeps add-on widgets read-only or hidden according to the associated module behavior when an entitlement lapses.
  • Adds Advanced Data Protection handling for protected dashboard values:
    • Protected values are hidden by default.
    • Reveals use the existing step-up verification flow.
    • Reveals are audited.
    • Wall display never reveals protected data.
    • Protected values are represented using dashboard-specific protected-value DTOs rather than exposing redaction markers or encrypted envelopes.

Supporting services and persistence

  • Adds UserDashboards and DepartmentDashboardTemplates persistence models, repositories, and SQL Server/PostgreSQL migrations.
  • Adds dashboard layout validation, schema handling, size validation, setting allow-lists, limits, and unknown-widget preservation.
  • Adds dashboard entitlement, board resolution, option-source, widget data, and provider contracts.
  • Adds supporting aggregate reads for calls, chat, workflows, inventory, documents, deployments, time reports, imports, hydrants, security posture, and other widget data.
  • Extracts personnel status-board construction into a shared service used by both the classic dashboard and the modern personnel board widget.
  • Extends SignalR/eventing support for dashboard-relevant staffing, checklist, unit alert, outstanding-resource, and incident-command updates.
  • Adds localized dashboard chrome and widget resources for the supported languages.

Additional fix

  • Changes route lookup caching so successful routes remain cached for 30 days, while failed lookups are retried after a short failure window instead of retaining the failure for the full cache duration.

Testing

Adds coverage for dashboard catalog validation, entitlement and add-on gating, layout normalization, board/template resolution, concurrency, persistence, widget providers, protected data behavior, localization completeness, controller security attributes, and route lookup caching.

@request-info

request-info Bot commented Nov 1, 2026

Copy link
Copy Markdown

Thanks for opening this, but we'd appreciate a little more information. Could you update it with more details?

@coderabbitai

coderabbitai Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

  • Run on-demand review

This review includes 294 billable files and costs up to $73.50.

View limit details

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between cc1a058 and 1913493.

Files ignored due to path filters: 224
  • Core/Resgrid.AdminAssist/Catalog/apps.yaml is excluded by !**/*.yaml
  • Core/Resgrid.Localization/Areas/User/AdminAssist/AdminAssist.ar.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/AdminAssist/AdminAssist.de.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/AdminAssist/AdminAssist.el.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/AdminAssist/AdminAssist.en.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/AdminAssist/AdminAssist.es.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/AdminAssist/AdminAssist.fr.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/AdminAssist/AdminAssist.it.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/AdminAssist/AdminAssist.pl.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/AdminAssist/AdminAssist.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/AdminAssist/AdminAssist.sv.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/AdminAssist/AdminAssist.uk.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/DashboardWidgets.ar.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/DashboardWidgets.de.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/DashboardWidgets.el.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/DashboardWidgets.en.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/DashboardWidgets.es.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/DashboardWidgets.fr.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/DashboardWidgets.it.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/DashboardWidgets.pl.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/DashboardWidgets.sv.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/DashboardWidgets.uk.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/HomeDashboard.ar.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/HomeDashboard.de.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/HomeDashboard.el.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/HomeDashboard.en.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/HomeDashboard.es.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/HomeDashboard.fr.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/HomeDashboard.it.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/HomeDashboard.pl.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/HomeDashboard.sv.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Home/HomeDashboard.uk.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/gloss_idx.json is excluded by !**/*.json
  • Core/Resgrid.Model/AiDispatch/AiDispatchEnrichment.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/AiDispatch/AiDispatchSettings.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/AiDispatch/AiDispatchTriage.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/CallDispatchCounts.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Chat/ChatReadState.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Checklists/ChecklistScheduling.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/DashboardLayout.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/DashboardServiceContracts.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/DashboardWidgetContext.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/DashboardWidgetDefinition.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/DepartmentDashboardTemplate.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/UserDashboard.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Administration/AdminDataProtectionWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Administration/AdminImportWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Administration/AdminPlanWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Administration/AdminSecurityWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Administration/AdminSetupWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Administration/AdminWorkflowsWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Administration/DepartmentInfoWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Awareness/ClockWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Awareness/MapOperationsWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Awareness/MapVisualizationWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Awareness/WeatherAlertsWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Awareness/WeatherCurrentWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Business/BidsPipelineWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Business/BusinessWidgetCommon.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Business/ContractsExpiringWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Business/CostRecoveryQueueWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Business/DeploymentTimeReportsWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Business/DeploymentsActiveWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Business/InvoicingOnlinePaymentsWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Business/InvoicingReceivablesWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Business/WorkforceFieldCostingWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Business/WorkforcePayDataWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Communications/ChatModerationWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Communications/ChatUnreadWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Communications/CommunicationTestsWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Communications/MessagesInboxWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Communications/VoicePttWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/IncidentBackOffice/IncidentActivationsWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/IncidentBackOffice/IncidentCheckInWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/IncidentBackOffice/IncidentCoordinationWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/IncidentBackOffice/IncidentDeskWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/IncidentBackOffice/IncidentFinanceWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/IncidentBackOffice/IncidentResourcesWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/IncidentBackOffice/IncidentWidgetDataBase.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Intelligence/AiDispatchTriageWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Intelligence/AssistantWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Operations/CallsActiveWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Operations/CallsAwaitingResourcesWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Operations/CallsKpisWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Operations/CallsMyCheckInsWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Operations/CallsPendingWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Operations/CallsVolumeWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Operations/DashboardStatusChip.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Operations/IncidentCommandActiveWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Operations/UnitsAlertsWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Operations/UnitsRoutesWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Operations/UnitsStatusWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Operations/UnitsSummaryWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Personnel/PersonnelBoardWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Personnel/PersonnelMyStatusWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Personnel/PersonnelStaffingSummaryWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Personnel/PersonnelStatusSummaryWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Planning/CalendarAgendaWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Planning/ContactsPreplansWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Planning/DocumentsRecentWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Planning/NotesBoardWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Planning/QuickLinksWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Planning/ShiftApprovalsWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Planning/ShiftCoverageWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Planning/ShiftsMineWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Planning/ShiftsTodayWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Planning/StickyNoteWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Planning/TrainingsMineWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Readiness/CertificationsExpiryWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Readiness/CertificationsMineWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Readiness/ChecklistsComplianceWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Readiness/ChecklistsDueWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Readiness/InventoryAlertsWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Readiness/InventoryValuationWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Readiness/WorkOrdersMineWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Readiness/WorkOrdersStatsWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Records/LogsRecentWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Records/RecordsAnalyticsWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Records/RecordsCrrWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Records/RecordsDeploymentsWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Records/RecordsHydrantsWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Records/RecordsInvestigationsWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Records/RecordsLegalHoldsWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Records/RecordsMyWorkWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Records/RecordsNerisWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Records/RecordsOverviewWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Records/RecordsPreventionWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Dashboard/Widgets/Records/RecordsQualityReviewWidgetData.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/DepartmentSecurityPosture.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/FeatureFlagKeys.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/IncidentLogistics/IncidentCoordinationSummary.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Inventories/InventoryAlertSummary.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Invoicing/BusinessDashboardSummaries.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Onboarding/DepartmentImportSummary.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/PersonnelStatusBoard.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Repositories/ICallsRepository.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Repositories/IContractorRepositories.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Repositories/IDashboardRepositories.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Repositories/IDeploymentRepositories.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Repositories/IDocumentRepository.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Repositories/IInventoryStore.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Repositories/ILogsRepository.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Repositories/IRmsPreventionRepositories.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Repositories/IRouteInstancesRepository.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Repositories/IUserSessionsRepository.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Repositories/IWorkflowRunRepository.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Services/IBidsService.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Services/ICallsService.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Services/IChatServices.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Services/IDashboardServices.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Services/IDepartmentImportServices.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Services/IDocumentsService.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Services/IIncidentFoundationServices.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Services/IInventoryOperationsService.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Services/IPersonnelStatusBoardService.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Services/IRecordsPreventionServices.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Services/IRouteService.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Services/ITimeTrackingService.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Services/ITrainingService.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Services/IUserSessionService.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Services/IWorkLogsService.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/Services/IWorkflowService.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/SystemAuditTypes.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/TrainingAssignmentStatus.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/WorkLogSummary.cs is excluded by !**/Core/Resgrid.Model/**
  • Core/Resgrid.Model/WorkflowRunStatusCount.cs is excluded by !**/Core/Resgrid.Model/**
  • Tests/Resgrid.Tests/Dashboard/DashboardCatalogGuardTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/DashboardEntitlementServiceTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/DashboardLayoutValidatorTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/DashboardServiceTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/AdministrationWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/AgentDWidgetTestSupport.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/BusinessWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/CalendarAgendaWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/CallsWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/CertificationsWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/ChatModerationWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/ChatUnreadWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/ChecklistsWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/ClockWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/CommsPlanningWidgetContexts.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/CommunicationTestsWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/ContactsPreplansWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/DocumentsRecentWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/IncidentBackOfficeWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/IntelligenceWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/InventoryWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/MapOperationsWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/MapVisualizationWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/MessagesInboxWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/NotesBoardWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/OperationsWidgetTestSupport.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/PersonnelStatusBoardServiceTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/PersonnelWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/QuickLinksWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/ReadinessRecordsSummaryMethodTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/ReadinessRecordsWidgetGateTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/ReadinessRecordsWidgetTestSupport.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/RecordsWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/ShiftApprovalsWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/ShiftCoverageWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/ShiftsMineWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/ShiftsTodayWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/StickyNoteWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/TrainingsMineWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/UnitsWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/VoicePttWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/WeatherAlertsWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/WeatherCurrentWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Dashboard/Widgets/WorkOrdersWidgetProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Localization/TranslationCompletenessTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Providers/RouteLookupCacheTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Repositories/DashboardRepositoriesDatabaseTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Rms/RmsPreventionFakes.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Security/InMemoryUserSessionsRepository.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Services/InventoryModernizationTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Web/User/DashboardControllerSurfaceTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Web/User/EnableMemberPersonnelLimitTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Web/User/StatusDestinationScopeTests.cs is excluded by !**/Tests/**
  • Web/Resgrid.Web/Areas/User/Apps/package-lock.json is excluded by !**/package-lock.json, !**/*.json
  • Web/Resgrid.Web/Areas/User/Apps/package.json is excluded by !**/*.json
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/chromeStrings.en.json is excluded by !**/*.json
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/primitives/README.md is excluded by !**/*.md
  • docs/admin-assist/settings-reference.md is excluded by !**/*.md
Files selected for processing: 294
  • Core/Resgrid.Localization/Areas/User/Home/DashboardWidgets.cs
  • Core/Resgrid.Services/AiDispatch/AiDispatchAdminService.cs
  • Core/Resgrid.Services/CallsService.cs
  • Core/Resgrid.Services/ChatChannelService.cs
  • Core/Resgrid.Services/ChecklistsScheduling.cs
  • Core/Resgrid.Services/Dashboard/DashboardBuiltInLayouts.cs
  • Core/Resgrid.Services/Dashboard/DashboardEntitlementService.cs
  • Core/Resgrid.Services/Dashboard/DashboardLayoutValidator.cs
  • Core/Resgrid.Services/Dashboard/DashboardOptionSources.cs
  • Core/Resgrid.Services/Dashboard/DashboardService.cs
  • Core/Resgrid.Services/Dashboard/DashboardWidgetCatalog.Administration.cs
  • Core/Resgrid.Services/Dashboard/DashboardWidgetCatalog.Awareness.cs
  • Core/Resgrid.Services/Dashboard/DashboardWidgetCatalog.Business.cs
  • Core/Resgrid.Services/Dashboard/DashboardWidgetCatalog.Communications.cs
  • Core/Resgrid.Services/Dashboard/DashboardWidgetCatalog.IncidentBackOffice.cs
  • Core/Resgrid.Services/Dashboard/DashboardWidgetCatalog.Intelligence.cs
  • Core/Resgrid.Services/Dashboard/DashboardWidgetCatalog.Operations.cs
  • Core/Resgrid.Services/Dashboard/DashboardWidgetCatalog.Personnel.cs
  • Core/Resgrid.Services/Dashboard/DashboardWidgetCatalog.Planning.cs
  • Core/Resgrid.Services/Dashboard/DashboardWidgetCatalog.Readiness.cs
  • Core/Resgrid.Services/Dashboard/DashboardWidgetCatalog.Records.cs
  • Core/Resgrid.Services/Dashboard/DashboardWidgetCatalog.cs
  • Core/Resgrid.Services/Dashboard/DashboardWidgetDataService.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Administration/AdminDataProtectionWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Administration/AdminImportWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Administration/AdminPlanWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Administration/AdminSecurityWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Administration/AdminSetupWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Administration/AdminWorkflowsWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Administration/DepartmentInfoWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Awareness/ClockWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Awareness/DashboardDepartmentCenter.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Awareness/MapOperationsWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Awareness/MapVisualizationWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Awareness/WeatherAlertsWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Awareness/WeatherCurrentWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Business/BidsPipelineWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Business/ContractsExpiringWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Business/CostRecoveryQueueWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Business/DashboardDepartmentClock.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Business/DashboardModulePermissions.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Business/DeploymentTimeReportsWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Business/DeploymentsActiveWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Business/InvoicingOnlinePaymentsWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Business/InvoicingReceivablesWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Business/WorkforceFieldCostingWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Business/WorkforcePayDataWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Communications/ChatModerationWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Communications/ChatUnreadWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Communications/CommunicationTestsWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Communications/MessagesInboxWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Communications/VoicePttWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Communications/WidgetProtectedText.cs
  • Core/Resgrid.Services/Dashboard/Widgets/IncidentBackOffice/IncidentActivationsOptionSource.cs
  • Core/Resgrid.Services/Dashboard/Widgets/IncidentBackOffice/IncidentActivationsWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/IncidentBackOffice/IncidentCheckInWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/IncidentBackOffice/IncidentCoordinationWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/IncidentBackOffice/IncidentDeskWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/IncidentBackOffice/IncidentFinanceWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/IncidentBackOffice/IncidentResourcesWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/IncidentBackOffice/IncidentWidgetSupport.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Intelligence/AiDispatchTriageWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Intelligence/AssistantWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Operations/CallsActiveWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Operations/CallsAwaitingResourcesWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Operations/CallsKpisWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Operations/CallsMyCheckInsWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Operations/CallsPendingWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Operations/CallsVolumeWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Operations/DashboardCallReads.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Operations/DashboardStatusPalette.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Operations/DashboardUnitReads.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Operations/IncidentCommandActiveWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Operations/UnitsAlertsWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Operations/UnitsRoutesWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Operations/UnitsStatusWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Operations/UnitsSummaryWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Personnel/DashboardPersonnelReads.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Personnel/PersonnelBoardWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Personnel/PersonnelMyStatusWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Personnel/PersonnelStaffingSummaryWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Personnel/PersonnelStatusSummaryWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Planning/CalendarAgendaWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Planning/ContactsPreplansWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Planning/DocumentsRecentWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Planning/NotesBoardWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Planning/PlanningOptionSources.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Planning/QuickLinksWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Planning/ShiftApprovalsWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Planning/ShiftCoverageWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Planning/ShiftWidgetHelpers.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Planning/ShiftsMineWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Planning/ShiftsTodayWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Planning/StickyNoteWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Planning/TrainingsMineWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Readiness/CertificationsExpiryWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Readiness/CertificationsMineWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Readiness/ChecklistsComplianceWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Readiness/ChecklistsDueWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Readiness/InventoryAlertsWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Readiness/InventoryValuationWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Readiness/ReadinessWidgetSupport.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Readiness/WorkOrdersMineWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Readiness/WorkOrdersStatsWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Records/LogsRecentWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Records/RecordsAnalyticsWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Records/RecordsCrrWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Records/RecordsDeploymentsWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Records/RecordsHydrantsWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Records/RecordsInvestigationsWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Records/RecordsLegalHoldsWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Records/RecordsMyWorkWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Records/RecordsNerisWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Records/RecordsOverviewWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Records/RecordsPreventionWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Records/RecordsQualityReviewWidgetProvider.cs
  • Core/Resgrid.Services/Dashboard/Widgets/Records/RecordsWidgetSupport.cs
  • Core/Resgrid.Services/DocumentsService.cs
  • Core/Resgrid.Services/IncidentLogistics/IncidentIntelligenceServices.cs
  • Core/Resgrid.Services/InventoryAlertSummaries.cs
  • Core/Resgrid.Services/Invoicing/BidsService.cs
  • Core/Resgrid.Services/Invoicing/TimeTrackingService.cs
  • Core/Resgrid.Services/Onboarding/DepartmentImportService.cs
  • Core/Resgrid.Services/PersonnelStatusBoardService.cs
  • Core/Resgrid.Services/Records/RecordsHydrantsService.cs
  • Core/Resgrid.Services/RouteService.cs
  • Core/Resgrid.Services/ServicesModule.cs
  • Core/Resgrid.Services/TrainingService.cs
  • Core/Resgrid.Services/UserSessionService.cs
  • Core/Resgrid.Services/WorkLogsService.cs
  • Core/Resgrid.Services/WorkflowService.cs
  • Providers/Resgrid.Providers.Geo/GeoLocationProvider.cs
  • Providers/Resgrid.Providers.Geo/RouteLookupCache.cs
  • Providers/Resgrid.Providers.Migrations/Migrations/M0307_AddHomeDashboards.cs
  • Providers/Resgrid.Providers.MigrationsPg/Migrations/M0307_AddHomeDashboardsPg.cs
  • Repositories/Resgrid.Repositories.DataRepository/AiDispatchAuditRepository.cs
  • Repositories/Resgrid.Repositories.DataRepository/CallsRepository.Dashboard.cs
  • Repositories/Resgrid.Repositories.DataRepository/ContractorRepositories.cs
  • Repositories/Resgrid.Repositories.DataRepository/DashboardRepositories.cs
  • Repositories/Resgrid.Repositories.DataRepository/DeploymentRepositories.cs
  • Repositories/Resgrid.Repositories.DataRepository/DocumentRepository.cs
  • Repositories/Resgrid.Repositories.DataRepository/InventoryStore.cs
  • Repositories/Resgrid.Repositories.DataRepository/LogsRepository.cs
  • Repositories/Resgrid.Repositories.DataRepository/Modules/DataModule.cs
  • Repositories/Resgrid.Repositories.DataRepository/Modules/TestingDataModule.cs
  • Repositories/Resgrid.Repositories.DataRepository/RmsPreventionRepositories.cs
  • Repositories/Resgrid.Repositories.DataRepository/RouteInstancesRepository.cs
  • Repositories/Resgrid.Repositories.DataRepository/UnlinkedDepartmentCleanup.cs
  • Repositories/Resgrid.Repositories.DataRepository/UserSessionsRepository.cs
  • Repositories/Resgrid.Repositories.DataRepository/WorkflowRunRepository.cs
  • Web/Resgrid.Web.Services/Controllers/v4/ChatController.cs
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/DashboardElement.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/api.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/chrome/Bars.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/chrome/HeaderBand.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/chrome/TemplatePanel.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/chrome/WidgetFrame.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/chrome/WidgetGallery.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/chrome/WidgetSettingsDialog.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/chrome/overlays.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/dashboard.css
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/data/dashboardBus.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/data/scheduler.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/data/useWidgetData.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/grid/DashboardGrid.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/grid/sizes.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/i18n.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/layoutUtils.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/primitives/MiniMap.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/primitives/MiniMapView.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/primitives/basic.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/primitives/index.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/primitives/lists.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/primitives/numbers.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/runtime.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/types.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/administration/AdminDataProtectionWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/administration/AdminImportWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/administration/AdminPlanWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/administration/AdminSecurityWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/administration/AdminSetupWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/administration/AdminWorkflowsWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/administration/DepartmentInfoWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/administration/index.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/awareness/ClockWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/awareness/MapOperationsWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/awareness/MapVisualizationWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/awareness/WeatherAlertsWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/awareness/WeatherCurrentWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/awareness/awarenessWidgets.css
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/awareness/index.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/business/BidsPipelineWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/business/ContractsExpiringWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/business/CostRecoveryQueueWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/business/DeploymentTimeReportsWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/business/DeploymentsActiveWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/business/InvoicingOnlinePaymentsWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/business/InvoicingReceivablesWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/business/WorkforceFieldCostingWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/business/WorkforcePayDataWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/business/businessShared.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/business/index.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/communications/ChatModerationWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/communications/ChatUnreadWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/communications/CommunicationTestsWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/communications/MessagesInboxWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/communications/VoicePttWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/communications/index.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/hooks.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/incidentBackOffice/IncidentActivationsWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/incidentBackOffice/IncidentCheckInWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/incidentBackOffice/IncidentCoordinationWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/incidentBackOffice/IncidentDeskWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/incidentBackOffice/IncidentFinanceWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/incidentBackOffice/IncidentResourcesWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/incidentBackOffice/ibo.css
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/incidentBackOffice/iboShared.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/incidentBackOffice/index.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/intelligence/AiDispatchTriageWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/intelligence/AssistantWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/intelligence/index.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/intelligence/intelligence.css
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/operations/CallsActiveWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/operations/CallsAwaitingResourcesWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/operations/CallsKpisWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/operations/CallsMyCheckInsWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/operations/CallsPendingWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/operations/CallsVolumeWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/operations/IncidentCommandActiveWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/operations/UnitsAlertsWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/operations/UnitsRoutesWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/operations/UnitsStatusWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/operations/UnitsSummaryWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/operations/index.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/operations/opsShared.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/operations/opsWidgets.css
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/personnel/PersonnelBoardWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/personnel/PersonnelMyStatusWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/personnel/PersonnelStaffingSummaryWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/personnel/PersonnelStatusSummaryWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/personnel/index.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/personnel/personnelWidgets.css
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/planning/CalendarAgendaWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/planning/ContactsPreplansWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/planning/DocumentsRecentWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/planning/NotesBoardWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/planning/QuickLinksWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/planning/ShiftApprovalsWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/planning/ShiftCoverageWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/planning/ShiftsMineWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/planning/ShiftsTodayWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/planning/StickyNoteWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/planning/TrainingsMineWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/planning/index.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/planning/planningTime.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/planning/planningWidgets.css
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/readiness/CertificationsExpiryWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/readiness/CertificationsMineWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/readiness/ChecklistsComplianceWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/readiness/ChecklistsDueWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/readiness/InventoryAlertsWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/readiness/InventoryValuationWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/readiness/WorkOrdersMineWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/readiness/WorkOrdersStatsWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/readiness/index.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/readiness/readinessShared.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/records/LogsRecentWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/records/RecordsAnalyticsWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/records/RecordsCrrWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/records/RecordsDeploymentsWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/records/RecordsHydrantsWidget.css
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/records/RecordsHydrantsWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/records/RecordsInvestigationsWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/records/RecordsLegalHoldsWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/records/RecordsMyWorkWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/records/RecordsNerisWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/records/RecordsOverviewWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/records/RecordsPreventionWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/records/RecordsQualityReviewWidget.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/records/index.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/registry.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/types.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/elements.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/runtime/signalr.ts
  • Web/Resgrid.Web/Areas/User/Controllers/DashboardController.cs
  • Web/Resgrid.Web/Areas/User/Controllers/HomeController.cs
  • Web/Resgrid.Web/Areas/User/Models/Dashboard/DashboardModels.cs
  • Web/Resgrid.Web/Areas/User/Models/DashboardModel.cs
  • Web/Resgrid.Web/Areas/User/Views/Dashboard/Templates.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Home/Dashboard.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Home/DashboardModern.cshtml
  • Web/Resgrid.Web/Controllers/WebApiBffController.cs
  • Web/Resgrid.Web/wwwroot/js/app/internal/home/resgrid.dashboard.templates.js
  • Web/Resgrid.Web/wwwroot/js/app/internal/home/resgrid.home.dashboard.js
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

@Resgrid-Bot

This comment has been minimized.

return value switch
{
int i => i,
long l => (int)l,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

DashboardWidgetContext.cs and DashboardRepositories.cs cast long values directly to int, which can overflow or truncate values outside the Int32 range. Check that each value is between int.MinValue and int.MaxValue before casting, or preserve it as a long.

Kody rule violation: Prevent Numeric Overflow in Calculations

long l when l >= int.MinValue && l <= int.MaxValue => (int)l,
Prompt for LLM

File Core/Resgrid.Model/Dashboard/DashboardWidgetContext.cs:

Line 105:

DashboardWidgetContext.cs and DashboardRepositories.cs cast long values directly to int, which can overflow or truncate values outside the Int32 range. Check that each value is between int.MinValue and int.MaxValue before casting, or preserve it as a long.

Suggested Code:

long l when l >= int.MinValue && l <= int.MaxValue => (int)l,

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

await RevealAsync(actor, schedule, display: true);
var metadata = JsonConvert.DeserializeObject<ChecklistOccurrence>(JsonConvert.SerializeObject(occurrence)); metadata.Content = null;
views.Add(new ChecklistOccurrenceView { Occurrence = metadata, Name = Decode<ChecklistScheduleContent>(schedule.Content).Name, Target = target,
views.Add(new ChecklistOccurrenceView { Occurrence = metadata, Name = Decode<ChecklistScheduleContent>(schedule.Content).Name, Target = target, Assigned = assigned,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

ChecklistsScheduling.cs accesses Name on the result of Decode(schedule.Content) without handling a null decoded value, which can cause a NullReferenceException. Use null propagation and default Name to string.Empty.

Kody rule violation: Add null checks to prevent NullReferenceException

Name = Decode<ChecklistScheduleContent>(schedule.Content)?.Name ?? string.Empty
Prompt for LLM

File Core/Resgrid.Services/ChecklistsScheduling.cs:

Line 93:

ChecklistsScheduling.cs accesses Name on the result of Decode<ChecklistScheduleContent>(schedule.Content) without handling a null decoded value, which can cause a NullReferenceException. Use null propagation and default Name to string.Empty.

Suggested Code:

Name = Decode<ChecklistScheduleContent>(schedule.Content)?.Name ?? string.Empty

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

Comment on lines +229 to +234
row.LayoutJson = DashboardLayoutValidator.Serialize(resolved.Layout);
row.TemplateId = resolved.TemplateId;
row.UpdatedOn = DateTime.UtcNow;
await _userDashboardsRepository.UpdateWithRowVersionAsync(row, row.RowVersion, CancellationToken.None);
await InvalidateUserAsync(departmentId, userId);
return ToBoard(row);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Bug high

ResetBoardAsync returns the old RowVersion after UpdateWithRowVersionAsync increments the stored version, causing the client’s next save to fail with a concurrency conflict despite no changes from another browser. Reload the row after the update or use the incremented RowVersion returned by the repository before constructing the result.

var updated = await _userDashboardsRepository.UpdateWithRowVersionAsync(row, row.RowVersion, CancellationToken.None);
if (!updated)
    return await GetBoardAsync(departmentId, userId, viewer, boardId);
row = await _userDashboardsRepository.GetByIdForUserAsync(row.UserDashboardId, departmentId, userId);
await InvalidateUserAsync(departmentId, userId);
return ToBoard(row);
Prompt for LLM

File Core/Resgrid.Services/Dashboard/DashboardService.cs:

Line 229 to 234:

ResetBoardAsync returns the old RowVersion after UpdateWithRowVersionAsync increments the stored version, causing the client’s next save to fail with a concurrency conflict despite no changes from another browser. Reload the row after the update or use the incremented RowVersion returned by the repository before constructing the result.

Suggested Code:

var updated = await _userDashboardsRepository.UpdateWithRowVersionAsync(row, row.RowVersion, CancellationToken.None);
if (!updated)
    return await GetBoardAsync(departmentId, userId, viewer, boardId);
row = await _userDashboardsRepository.GetByIdForUserAsync(row.UserDashboardId, departmentId, userId);
await InvalidateUserAsync(departmentId, userId);
return ToBoard(row);

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

Comment on lines +26 to +30
Settings = new[]
{
Number("days", 7, 1, 14),
MultiFrom("types", PlanningOptionSources.CalendarTypes),
Number("maxItems", 10, 1, 25)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Bug high

Planning widget settings reference DashboardOptionSources constants without registered IDashboardOptionSource implementations, leaving the Calendar type, document category, note category, and incident activation selectors without options and allowing posted values outside the advertised bootstrap choices. Add keyed providers for every referenced source, or remove those sources from DashboardWidgetCatalog.Planning.cs until providers exist.

// Register keyed IDashboardOptionSource implementations for CalendarTypes,
// DocumentCategories, NoteCategories, and IncidentActivations before exposing
// these settings in the catalog.
Prompt for LLM

File Core/Resgrid.Services/Dashboard/DashboardWidgetCatalog.Planning.cs:

Line 26 to 30:

Planning widget settings reference DashboardOptionSources constants without registered IDashboardOptionSource implementations, leaving the Calendar type, document category, note category, and incident activation selectors without options and allowing posted values outside the advertised bootstrap choices. Add keyed providers for every referenced source, or remove those sources from DashboardWidgetCatalog.Planning.cs until providers exist.

Suggested Code:

// Register keyed IDashboardOptionSource implementations for CalendarTypes,
// DocumentCategories, NoteCategories, and IncidentActivations before exposing
// these settings in the catalog.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

if (context.Viewer?.IsDepartmentAdmin != true)
return data;

var latest = runs.FirstOrDefault(r => r.Status != (int)CommunicationTestRunStatus.Pending) ?? runs[0];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

CommunicationTestsWidgetProvider.cs has already established that runs is non-empty, so FirstOrDefault followed by runs[0] obscures the invariant and preserves an unnecessary fallback. Use First with the non-pending predicate, or document and explicitly retain the non-empty fallback semantics.

Kody rule violation: Use `First`/`Single` Instead of `FirstOrDefault`/`SingleOrDefault` for Non-Empty Collections

CommunicationTestRun latest = runs.First(r => r.Status != (int)CommunicationTestRunStatus.Pending);
Prompt for LLM

File Core/Resgrid.Services/Dashboard/Widgets/Communications/CommunicationTestsWidgetProvider.cs:

Line 70:

CommunicationTestsWidgetProvider.cs has already established that runs is non-empty, so FirstOrDefault followed by runs[0] obscures the invariant and preserves an unnecessary fallback. Use First with the non-pending predicate, or document and explicitly retain the non-empty fallback semantics.

Suggested Code:

CommunicationTestRun latest = runs.First(r => r.Status != (int)CommunicationTestRunStatus.Pending);

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

var visible = new List<UnitState>();
foreach (var state in states)
{
if (await authorizationService.CanUserViewUnitViaMatrixAsync(state.UnitId, userId, departmentId))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

DashboardUnitReads.cs calls CanUserViewUnitViaMatrixAsync once per state inside the loop, creating one authorization request per unit. Use a batch authorization API or retrieve the permitted unit IDs once and filter locally.

Kody rule violation: Detect N+1 style queries and suggest batching

Prompt for LLM

File Core/Resgrid.Services/Dashboard/Widgets/Operations/DashboardUnitReads.cs:

Line 32:

DashboardUnitReads.cs calls CanUserViewUnitViaMatrixAsync once per state inside the loop, creating one authorization request per unit. Use a batch authorization API or retrieve the permitted unit IDs once and filter locally.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

public const int MaxLabelLength = 100;

/// <summary>"[label |] http(s)://…" — the URL is the line's last token, so a label may itself contain "|".</summary>
private static readonly Regex LinePattern = new Regex(@"^(?:(?<label>.*?)\s*\|\s*)?(?<url>https?://\S+)$",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

QuickLinksWidgetProvider.cs and DashboardCatalogGuardTests.cs construct Regex instances without a timeout, allowing malicious input to consume excessive processing time. Define an explicit Regex timeout for every affected pattern.

Kody rule violation: Specify Timeout for Regular Expressions

Prompt for LLM

File Core/Resgrid.Services/Dashboard/Widgets/Planning/QuickLinksWidgetProvider.cs:

Line 29:

QuickLinksWidgetProvider.cs and DashboardCatalogGuardTests.cs construct Regex instances without a timeout, allowing malicious input to consume excessive processing time. Define an explicit Regex timeout for every affected pattern.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

Comment on lines +36 to +47
OutOfServiceHydrants = (summary.OutOfServiceHydrants ?? new List<RmsHydrant>())
.Where(h => h != null && h.DepartmentId == context.DepartmentId && h.DeletedOn == null && !h.InService)
.Take(maxRows)
.Select(h => new RecordsHydrantItem
{
HydrantId = h.RmsHydrantId,
Number = RecordsWidgetSupport.SafeText(h.HydrantNumber),
Type = h.Type,
Latitude = h.Latitude,
Longitude = h.Longitude,
OutOfServiceSince = h.OutOfServiceSince
}).ToList()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

RecordsHydrantsWidgetProvider.cs and the listed dashboard providers chain filtering, limiting, projection, and materialization in a single LINQ expression, making each stage harder to inspect and maintain. Store the source sequence, filtered and limited sequence, and projected materialized results in named intermediate variables.

Kody rule violation: Limit Lengthy LINQ Chains

var hydrants = summary.OutOfServiceHydrants ?? new List<RmsHydrant>();
var outOfServiceHydrants = hydrants.Where(h => h != null && h.DepartmentId == context.DepartmentId && h.DeletedOn == null && !h.InService).Take(maxRows);
var hydrantItems = outOfServiceHydrants.Select(h => new RecordsHydrantItem
{
	HydrantId = h.RmsHydrantId,
	Number = RecordsWidgetSupport.SafeText(h.HydrantNumber),
	Type = h.Type,
	Latitude = h.Latitude,
	Longitude = h.Longitude,
	OutOfServiceSince = h.OutOfServiceSince
}).ToList();
Prompt for LLM

File Core/Resgrid.Services/Dashboard/Widgets/Records/RecordsHydrantsWidgetProvider.cs:

Line 36 to 47:

RecordsHydrantsWidgetProvider.cs and the listed dashboard providers chain filtering, limiting, projection, and materialization in a single LINQ expression, making each stage harder to inspect and maintain. Store the source sequence, filtered and limited sequence, and projected materialized results in named intermediate variables.

Suggested Code:

var hydrants = summary.OutOfServiceHydrants ?? new List<RmsHydrant>();
var outOfServiceHydrants = hydrants.Where(h => h != null && h.DepartmentId == context.DepartmentId && h.DeletedOn == null && !h.InService).Take(maxRows);
var hydrantItems = outOfServiceHydrants.Select(h => new RecordsHydrantItem
{
	HydrantId = h.RmsHydrantId,
	Number = RecordsWidgetSupport.SafeText(h.HydrantNumber),
	Type = h.Type,
	Latitude = h.Latitude,
	Longitude = h.Longitude,
	OutOfServiceSince = h.OutOfServiceSince
}).ToList();

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

builder.RegisterAssemblyTypes(assembly)
.Where(t => typeof(IDashboardWidgetDataProvider).IsAssignableFrom(t) && !t.IsAbstract &&
System.Reflection.CustomAttributeExtensions.GetCustomAttribute<Resgrid.Model.Dashboard.DashboardWidgetAttribute>(t) != null)
.Keyed<IDashboardWidgetDataProvider>(t => System.Reflection.CustomAttributeExtensions.GetCustomAttribute<Resgrid.Model.Dashboard.DashboardWidgetAttribute>(t).Key)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

ServicesModule.cs accesses DashboardWidgetAttribute.Key through a separate reflection lookup that can return null despite the earlier predicate. Use null propagation with an explicit InvalidOperationException fallback when the DashboardWidgetAttribute is absent.

Kody rule violation: Add null checks before accessing properties

.Keyed<IDashboardWidgetDataProvider>(t => System.Reflection.CustomAttributeExtensions.GetCustomAttribute<Resgrid.Model.Dashboard.DashboardWidgetAttribute>(t)?.Key ?? throw new InvalidOperationException("Dashboard widget attribute is required."))
Prompt for LLM

File Core/Resgrid.Services/ServicesModule.cs:

Line 601:

ServicesModule.cs accesses DashboardWidgetAttribute.Key through a separate reflection lookup that can return null despite the earlier predicate. Use null propagation with an explicit InvalidOperationException fallback when the DashboardWidgetAttribute is absent.

Suggested Code:

.Keyed<IDashboardWidgetDataProvider>(t => System.Reflection.CustomAttributeExtensions.GetCustomAttribute<Resgrid.Model.Dashboard.DashboardWidgetAttribute>(t)?.Key ?? throw new InvalidOperationException("Dashboard widget attribute is required."))

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

// minimum; the questions are loaded for that case alone.
if (assignment.Score < training.MinimumScore)
{
var questions = await _trainingQuestionRepository.GetTrainingQuestionsByTrainingIdAsync(training.TrainingId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

TrainingService.cs queries _trainingQuestionRepository once per training inside the loop, causing an avoidable N+1 query pattern. Batch-load questions with GetTrainingQuestionsByTrainingIdsAsync for the relevant training IDs or eager-load them with the initial query.

Kody rule violation: Optimize database queries with JOINs

var trainingIds = trainings.Where(t => t != null && t.Score < t.MinimumScore).Select(t => t.TrainingId).ToList();
var questionsByTrainingId = await _trainingQuestionRepository.GetTrainingQuestionsByTrainingIdsAsync(trainingIds);
Prompt for LLM

File Core/Resgrid.Services/TrainingService.cs:

Line 374:

TrainingService.cs queries _trainingQuestionRepository once per training inside the loop, causing an avoidable N+1 query pattern. Batch-load questions with GetTrainingQuestionsByTrainingIdsAsync for the relevant training IDs or eager-load them with the initial query.

Suggested Code:

var trainingIds = trainings.Where(t => t != null && t.Score < t.MinimumScore).Select(t => t.TrainingId).ToList();
var questionsByTrainingId = await _trainingQuestionRepository.GetTrainingQuestionsByTrainingIdsAsync(trainingIds);

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

// Seeded OFF: every member keeps the classic dashboard until the flag is enabled globally or per department.
Execute.Sql(
"IF NOT EXISTS (SELECT 1 FROM [FeatureFlags] WHERE [FlagKey] = '" + FlagKey + "') " +
"INSERT INTO [FeatureFlags] ([FlagKey], [Name], [Description], [Category], [IsEnabledGlobally]) " +

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules critical

The SQL statements in M0307_AddHomeDashboards.cs, M0307_AddHomeDashboardsPg.cs, CallsRepository.Dashboard.cs, DocumentRepository.cs, RouteInstancesRepository.cs, and the referenced tests must not interpolate unsanitized input into queries. Use parameterized queries for values supplied by users or external input.

Kody rule violation: Prevent SQL Injection in Queries

Prompt for LLM

File Providers/Resgrid.Providers.Migrations/Migrations/M0307_AddHomeDashboards.cs:

Line 60:

The SQL statements in M0307_AddHomeDashboards.cs, M0307_AddHomeDashboardsPg.cs, CallsRepository.Dashboard.cs, DocumentRepository.cs, RouteInstancesRepository.cs, and the referenced tests must not interpolate unsanitized input into queries. Use parameterized queries for values supplied by users or external input.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

.WithColumn("UpdatedByUserId").AsString(128).Nullable()
.WithColumn("UpdatedOn").AsDateTime2().NotNullable();
Execute.Sql("CREATE NONCLUSTERED INDEX IX_DepartmentDashboardTemplates_Department ON DepartmentDashboardTemplates (DepartmentId);");
Execute.Sql("CREATE UNIQUE NONCLUSTERED INDEX IX_DepartmentDashboardTemplates_Default ON DepartmentDashboardTemplates (DepartmentId) WHERE IsDefault = 1;");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules critical

The filtered unique index in M0307_AddHomeDashboards.cs can acquire significant locks during migration, and the target database may not support online creation for filtered indexes. Use an online, low-locking strategy where supported, verify compatibility, and document the rollback plan.

Kody rule violation: Block risky database migrations (locking ops, downtime risk)

Execute.Sql("CREATE UNIQUE NONCLUSTERED INDEX IX_DepartmentDashboardTemplates_Default ON DepartmentDashboardTemplates (DepartmentId) WHERE IsDefault = 1 WITH (ONLINE = ON);");
Prompt for LLM

File Providers/Resgrid.Providers.Migrations/Migrations/M0307_AddHomeDashboards.cs:

Line 54:

The filtered unique index in M0307_AddHomeDashboards.cs can acquire significant locks during migration, and the target database may not support online creation for filtered indexes. Use an online, low-locking strategy where supported, verify compatibility, and document the rollback plan.

Suggested Code:

Execute.Sql("CREATE UNIQUE NONCLUSTERED INDEX IX_DepartmentDashboardTemplates_Default ON DepartmentDashboardTemplates (DepartmentId) WHERE IsDefault = 1 WITH (ONLINE = ON);");

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

{
throw;
}
catch (Exception ex)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

CallsRepository.Dashboard.cs and the listed repositories catch every Exception uniformly, preventing safe retries for transient database failures and losing specific handling for unrecoverable errors. Catch DbException selectively with IsTransient for safe, idempotent retries, and map non-transient failures with operation context.

Kody rule violation: Implement proper database error checking

catch (DbException ex) when (IsTransient(ex))
{
	// Retry only safe, idempotent reads.
}
catch (DbException ex)
{
	// Map non-transient database failures with operation context.
}
Prompt for LLM

File Repositories/Resgrid.Repositories.DataRepository/CallsRepository.Dashboard.cs:

Line 51:

CallsRepository.Dashboard.cs and the listed repositories catch every Exception uniformly, preventing safe retries for transient database failures and losing specific handling for unrecoverable errors. Catch DbException selectively with IsTransient for safe, idempotent retries, and map non-transient failures with operation context.

Suggested Code:

catch (DbException ex) when (IsTransient(ex))
{
	// Retry only safe, idempotent reads.
}
catch (DbException ex)
{
	// Map non-transient database failures with operation context.
}

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

var p = RmsPreventionSql.Paged(departmentId, 0, take, 200);
p.Add("Off", false);
// NULL ordering differs by dialect (SQL Server first, PostgreSQL last), so an unknown start date is placed explicitly.
return QueryAsync<RmsHydrant>($"SELECT * FROM {Tbl("RmsHydrants")} WHERE {Col("DepartmentId")} = {P}DepartmentId AND {Col("DeletedOn")} IS NULL AND {Col("InService")} = {P}Off ORDER BY CASE WHEN {Col("OutOfServiceSince")} IS NULL THEN 1 ELSE 0 END, {Col("OutOfServiceSince")}, {Col("HydrantNumber")}, {Col("RmsHydrantId")} {Paging()}", p);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

RmsPreventionRepositories.cs and the listed repository call sites allow asynchronous database failures to escape without operation or department context. Make the method async, await QueryAsync, catch the database failure, and rethrow an application-level exception such as DataException with the department context while passing parameters rather than the existing p variable.

Kody rule violation: Add try-catch blocks for external calls

try
{
	return await QueryAsync<RmsHydrant>($"SELECT * FROM {Tbl("RmsHydrants")} WHERE {Col("DepartmentId")} = {P}DepartmentId AND {Col("DeletedOn")} IS NULL AND {Col("InService")} = {P}Off ORDER BY CASE WHEN {Col("OutOfServiceSince")} IS NULL THEN 1 ELSE 0 END, {Col("OutOfServiceSince")}, {Col("HydrantNumber")}, {Col("RmsHydrantId")} {Paging()}", parameters);
}
catch (Exception ex)
{
	throw new DataException($"Failed to retrieve out-of-service hydrants for department {departmentId}.", ex);
}
Prompt for LLM

File Repositories/Resgrid.Repositories.DataRepository/RmsPreventionRepositories.cs:

Line 444:

RmsPreventionRepositories.cs and the listed repository call sites allow asynchronous database failures to escape without operation or department context. Make the method async, await QueryAsync, catch the database failure, and rethrow an application-level exception such as DataException with the department context while passing parameters rather than the existing p variable.

Suggested Code:

			try
			{
				return await QueryAsync<RmsHydrant>($"SELECT * FROM {Tbl("RmsHydrants")} WHERE {Col("DepartmentId")} = {P}DepartmentId AND {Col("DeletedOn")} IS NULL AND {Col("InService")} = {P}Off ORDER BY CASE WHEN {Col("OutOfServiceSince")} IS NULL THEN 1 ELSE 0 END, {Col("OutOfServiceSince")}, {Col("HydrantNumber")}, {Col("RmsHydrantId")} {Paging()}", parameters);
			}
			catch (Exception ex)
			{
				throw new DataException($"Failed to retrieve out-of-service hydrants for department {departmentId}.", ex);
			}

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

// stylesheet, and require every cell above the last card's bottom edge to be filled.
var css = System.IO.File.ReadAllText(Path.Combine(RepositoryRoot(), "Web", "Resgrid.Web", "Areas", "User", "Apps", "src", "components", "dashboard", "dashboard.css"));
var spans = Regex.Matches(css, @"\.rg-w\[data-size='(\w+)'\]\s*\{\s*grid-column:\s*span (\d+);\s*grid-row:\s*span (\d+);")
.GroupBy(m => m.Groups[1].Value).ToDictionary(g => g.Key, g => (Columns: int.Parse(g.First().Groups[2].Value), Rows: int.Parse(g.First().Groups[3].Value)));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

DashboardCatalogGuardTests.cs uses int.Parse on regex-captured strings, so malformed or unexpected input can throw a FormatException. Use an int.TryParse-style conversion with explicit culture and format validation before constructing the Columns and Rows values.

Kody rule violation: Use TryParse for string conversions

Prompt for LLM

File Tests/Resgrid.Tests/Dashboard/DashboardCatalogGuardTests.cs:

Line 157:

DashboardCatalogGuardTests.cs uses int.Parse on regex-captured strings, so malformed or unexpected input can throw a FormatException. Use an int.TryParse-style conversion with explicit culture and format validation before constructing the Columns and Rows values.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

var data = (MapVisualizationWidgetData)await provider.GetDataAsync(For("map.visualization"), CancellationToken.None);

data.Configured.Should().BeFalse();
data.Result.Should().BeNull();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules critical

MapVisualizationWidgetProviderTests.cs blocks on asynchronous work through data.Result, and the related MapVisualizationWidget.tsx paths contain the same blocking pattern. Replace .Result and .Wait() usage with await so asynchronous execution cannot deadlock.

Kody rule violation: Avoid Blocking Calls to Async Methods

Prompt for LLM

File Tests/Resgrid.Tests/Dashboard/Widgets/MapVisualizationWidgetProviderTests.cs:

Line 27:

MapVisualizationWidgetProviderTests.cs blocks on asynchronous work through data.Result, and the related MapVisualizationWidget.tsx paths contain the same blocking pattern. Replace .Result and .Wait() usage with await so asynchronous execution cannot deadlock.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

var data = (MapVisualizationWidgetData)await provider.GetDataAsync(For("map.visualization"), CancellationToken.None);

data.Configured.Should().BeFalse();
data.Result.Should().BeNull();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

MapVisualizationWidgetProviderTests.cs blocks on the asynchronous result through data.Result, and the related MapVisualizationWidget.tsx paths have the same synchronous access pattern. Await the underlying Tasks end to end and use configure awaits appropriately instead of blocking.

Kody rule violation: Await async operations properly

Prompt for LLM

File Tests/Resgrid.Tests/Dashboard/Widgets/MapVisualizationWidgetProviderTests.cs:

Line 27:

MapVisualizationWidgetProviderTests.cs blocks on the asynchronous result through data.Result, and the related MapVisualizationWidget.tsx paths have the same synchronous access pattern. Await the underlying Tasks end to end and use configure awaits appropriately instead of blocking.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

departments.Setup(x => x.GetDepartmentByIdAsync(DepartmentId, It.IsAny<bool>())).ReturnsAsync(new Department { DepartmentId = DepartmentId });
var geo = new Mock<IGeoLocationProvider>(MockBehavior.Strict);

var provider = new WeatherCurrentWidgetProvider(new HttpClient(handler), cache.Object, settings.Object, departments.Object, geo.Object);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

WeatherCurrentWidgetProviderTests.cs creates an HttpClient without clearly disposing its disposable resource, which can leak handlers across tests. Dispose the HttpClient deterministically with using var or make its ownership explicit.

Kody rule violation: Use using statements for disposable resources

using var client = new HttpClient(handler);
var provider = new WeatherCurrentWidgetProvider(client, cache.Object, settings.Object, departments.Object, geo.Object);
Prompt for LLM

File Tests/Resgrid.Tests/Dashboard/Widgets/WeatherCurrentWidgetProviderTests.cs:

Line 96:

WeatherCurrentWidgetProviderTests.cs creates an HttpClient without clearly disposing its disposable resource, which can leak handlers across tests. Dispose the HttpClient deterministically with using var or make its ownership explicit.

Suggested Code:

using var client = new HttpClient(handler);
var provider = new WeatherCurrentWidgetProvider(client, cache.Object, settings.Object, departments.Object, geo.Object);

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

if (type == DatabaseTypes.Postgres) r.AddPostgres(); else r.AddSqlServer();
r.WithGlobalConnectionString(_connection);
}).AddSingleton(source.Object).BuildServiceProvider();
_runner.GetRequiredService<IMigrationRunner>().MigrateUp();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

DashboardRepositoriesDatabaseTests.cs blocks the async migration setup by calling MigrateUp synchronously. Use the migration runner’s awaitable MigrateUpAsync API inside the async setup method.

Kody rule violation: Use Awaitable Methods in Async Code

await _runner.GetRequiredService<IMigrationRunner>().MigrateUpAsync();
Prompt for LLM

File Tests/Resgrid.Tests/Repositories/DashboardRepositoriesDatabaseTests.cs:

Line 89:

DashboardRepositoriesDatabaseTests.cs blocks the async migration setup by calling MigrateUp synchronously. Use the migration runner’s awaitable MigrateUpAsync API inside the async setup method.

Suggested Code:

await _runner.GetRequiredService<IMigrationRunner>().MigrateUpAsync();

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

<div className="rg-dash" aria-busy="true">
<div className="rg-grid rg-grid-loading">
{['s', 's', 's', 's', 'l', 'l', 'full'].map((size, index) => (
<div key={index} className="rg-w rg-w-placeholder" data-size={size}>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

DashboardElement.tsx and numbers.tsx use array indexes as React keys, which can associate state with the wrong element when dashboard items reorder or change. Use stable, unique identifiers for each list item.

Kody rule violation: Avoid array indexes as keys in React lists

Prompt for LLM

File Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/DashboardElement.tsx:

Line 946:

DashboardElement.tsx and numbers.tsx use array indexes as React keys, which can associate state with the wrong element when dashboard items reorder or change. Use stable, unique identifiers for each list item.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

<i className="fa fa-th-large" aria-hidden="true" />
<b>{editing ? t('Dash.EmptyBoardEditing') : t('Dash.EmptyBoard')}</b>
{editing ? (
<button type="button" className="btn btn-sm btn-primary" onClick={() => setGalleryOpen(true)}>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

DashboardElement.tsx and the listed dashboard components create inline arrow functions or use .bind() in JSX props, allocating new callbacks on every render and potentially increasing child re-renders. Define stable handlers outside JSX, using memoization where parameters are required.

Kody rule violation: Avoid using .bind() or arrow functions in JSX props

Prompt for LLM

File Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/DashboardElement.tsx:

Line 969:

DashboardElement.tsx and the listed dashboard components create inline arrow functions or use .bind() in JSX props, allocating new callbacks on every render and potentially increasing child re-renders. Define stable handlers outside JSX, using memoization where parameters are required.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

const loadInitial = useCallback(() => {
setBootError(false);
loadBootstrap(null, 'initial').catch((error: unknown) => {
console.error('Dashboard: the bootstrap failed to load.', error);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

DashboardElement.tsx logs bootstrap failures with console.error without structured operation context, making failures difficult to correlate across dashboard instances. Replace it with a structured error log such as logger.error('dashboard bootstrap failed', { operation: 'bootstrap', boardId: null, templateId: props.templateId, error }), and apply the same approach to the listed call sites.

Kody rule violation: Include error context in structured logs

Prompt for LLM

File Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/DashboardElement.tsx:

Line 236:

DashboardElement.tsx logs bootstrap failures with console.error without structured operation context, making failures difficult to correlate across dashboard instances. Replace it with a structured error log such as logger.error('dashboard bootstrap failed', { operation: 'bootstrap', boardId: null, templateId: props.templateId, error }), and apply the same approach to the listed call sites.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

'incidentCommand',
] as const;

const RETRY_DELAYS_MS = [2000, 5000, 10000, 30000, 60000];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

dashboardBus.ts exposes RETRY_DELAYS_MS as a mutable array, allowing retry timing to change at runtime. Mark the collection as immutable with as const.

Kody rule violation: Use `readonly` or `const` for Immutable Data

const RETRY_DELAYS_MS = [2000, 5000, 10000, 30000, 60000] as const;
Prompt for LLM

File Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/data/dashboardBus.ts:

Line 26:

dashboardBus.ts exposes RETRY_DELAYS_MS as a mutable array, allowing retry timing to change at runtime. Mark the collection as immutable with as const.

Suggested Code:

const RETRY_DELAYS_MS = [2000, 5000, 10000, 30000, 60000] as const;

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

}
pumpScheduled = true;
// A macrotask, not requestAnimationFrame: a hidden tab or preview pane stalls rAF.
setTimeout(pump, 0);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

scheduler.ts schedules pump with setTimeout without retaining the timeout handle, so scheduled work can survive scheduler teardown. Store the handle and call clearTimeout(timeoutId) during cleanup.

Kody rule violation: Clear timers on teardown/unmount

const timeoutId = setTimeout(pump, 0);
// Expose or invoke clearTimeout(timeoutId) during scheduler teardown.
Prompt for LLM

File Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/data/scheduler.ts:

Line 56:

scheduler.ts schedules pump with setTimeout without retaining the timeout handle, so scheduled work can survive scheduler teardown. Store the handle and call clearTimeout(timeoutId) during cleanup.

Suggested Code:

  const timeoutId = setTimeout(pump, 0);
  // Expose or invoke clearTimeout(timeoutId) during scheduler teardown.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

Comment on lines +260 to +266
if (shownMarkers.length > 0) {
if (fit) {
key = `m:${markers
.map((marker) => marker.id)
.sort()
.join('|')}`;
target = { bounds: L.latLngBounds(shownMarkers.map((marker) => [marker.lat, marker.lng] as [number, number])).pad(0.15) };

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Bug medium

MiniMapView.tsx derives the auto-fit guard key from all markers while deriving bounds from shownMarkers, so layer toggles can leave the key unchanged and prevent applyFit from updating the viewport. Build the key from shownMarkers, including the visible marker IDs used for the bounds.

key = `m:${shownMarkers
          .map((marker) => marker.id)
          .sort()
          .join('|')}`;
        target = { bounds: L.latLngBounds(shownMarkers.map((marker) => [marker.lat, marker.lng] as [number, number])).pad(0.15) };
Prompt for LLM

File Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/primitives/MiniMapView.tsx:

Line 260 to 266:

MiniMapView.tsx derives the auto-fit guard key from all markers while deriving bounds from shownMarkers, so layer toggles can leave the key unchanged and prevent applyFit from updating the viewport. Build the key from shownMarkers, including the visible marker IDs used for the bounds.

Suggested Code:

key = `m:${shownMarkers
          .map((marker) => marker.id)
          .sort()
          .join('|')}`;
        target = { bounds: L.latLngBounds(shownMarkers.map((marker) => [marker.lat, marker.lng] as [number, number])).pad(0.15) };

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

import type { Tone } from '../../primitives';
import type { WidgetProps } from '../types';
import { ActivationHeader, deskUrl, type IncidentArrivalWindow, type IncidentWidgetDataBase, NoActivation, ReadOnlyNote, compactCurrency, wholeCurrency } from './iboShared';
import './ibo.css';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

IncidentDeskWidget.tsx and the listed non-layout dashboard components import global styles such as ibo.css, allowing selectors to leak into unrelated components. Use a component-scoped stylesheet such as ibo.module.css with CSS Modules or equivalent scoped/BEM classes.

Kody rule violation: Use component-scoped styling

import styles from './ibo.module.css';
Prompt for LLM

File Web/Resgrid.Web/Areas/User/Apps/src/components/dashboard/widgets/incidentBackOffice/IncidentDeskWidget.tsx:

Line 5:

IncidentDeskWidget.tsx and the listed non-layout dashboard components import global styles such as ibo.css, allowing selectors to leak into unrelated components. Use a component-scoped stylesheet such as ibo.module.css with CSS Modules or equivalent scoped/BEM classes.

Suggested Code:

import styles from './ibo.module.css';

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

@@ -110,6 +120,23 @@ export async function connectEventingHub(departmentId: number, handlers: Eventin
connection.on('callAdded', (id: unknown) => handlers.onCallsChanged?.(asId(id)));
connection.on('callClosed', (id: unknown) => handlers.onCallsChanged?.(asId(id)));

// Registered only when asked for, so elements that predate these events see no change in hub traffic handling.
if (handlers.onPersonnelStaffingUpdated) {
connection.on('personnelStaffingUpdated', (id: unknown) => handlers.onPersonnelStaffingUpdated?.(asId(id)));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

signalr.ts registers the personnelStaffingUpdated listener without an error-handling path or deterministic cleanup, so the callback can remain attached after the connection is torn down. Store the handler, remove it with connection.off during cleanup, and register connection.onclose(onError).

Kody rule violation: Provide error handlers to subscription/listener APIs

const onPersonnelStaffingUpdated = (id: unknown) => handlers.onPersonnelStaffingUpdated?.(asId(id));
connection.on('personnelStaffingUpdated', onPersonnelStaffingUpdated);
cleanup.push(() => connection.off('personnelStaffingUpdated', onPersonnelStaffingUpdated));
connection.onclose(onError);
Prompt for LLM

File Web/Resgrid.Web/Areas/User/Apps/src/runtime/signalr.ts:

Line 125:

signalr.ts registers the personnelStaffingUpdated listener without an error-handling path or deterministic cleanup, so the callback can remain attached after the connection is torn down. Store the handler, remove it with connection.off during cleanup, and register connection.onclose(onError).

Suggested Code:

const onPersonnelStaffingUpdated = (id: unknown) => handlers.onPersonnelStaffingUpdated?.(asId(id));
connection.on('personnelStaffingUpdated', onPersonnelStaffingUpdated);
cleanup.push(() => connection.off('personnelStaffingUpdated', onPersonnelStaffingUpdated));
connection.onclose(onError);

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

{
// The widget-based dashboard (home-dashboard-redesign-plan.md D10): behind Dashboard.Modern, unless the member chose
// the classic page (or asked for it once with ?classic=true).
var modernEnabled = await _featureToggleService.IsEnabledAsync(FeatureFlagKeys.DashboardModern, DepartmentId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

HomeController.cs and the listed feature-toggle call sites allow IsEnabledAsync and other awaited operations to propagate service failures without logging their operation context or applying a fallback. Wrap the awaited operation in try/catch, log the relevant context, and apply an appropriate fallback or application-level error.

Kody rule violation: Handle async operations with proper error handling

bool modernEnabled;
try
{
	modernEnabled = await _featureToggleService.IsEnabledAsync(FeatureFlagKeys.DashboardModern, DepartmentId);
}
catch (Exception ex)
{
	// Log and apply the appropriate fallback.
	throw;
}
Prompt for LLM

File Web/Resgrid.Web/Areas/User/Controllers/HomeController.cs:

Line 182:

HomeController.cs and the listed feature-toggle call sites allow IsEnabledAsync and other awaited operations to propagate service failures without logging their operation context or applying a fallback. Wrap the awaited operation in try/catch, log the relevant context, and apply an appropriate fallback or application-level error.

Suggested Code:

bool modernEnabled;
try
{
	modernEnabled = await _featureToggleService.IsEnabledAsync(FeatureFlagKeys.DashboardModern, DepartmentId);
}
catch (Exception ex)
{
	// Log and apply the appropriate fallback.
	throw;
}

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

data: JSON.stringify({ UseClassic: false }),
headers: { 'RequestVerificationToken': getAntiForgeryToken() }
}).done(function (result) {
window.location.href = (result && result.Redirect) ? result.Redirect : '/User/Home/Dashboard';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules critical

resgrid.home.dashboard.js and DashboardElement.tsx assign the server-provided Redirect directly to window.location.href, allowing an open redirect if the value targets an external origin. Validate the redirect against an allowlist or same-origin path before assigning it, and fall back to /User/Home/Dashboard.

Kody rule violation: Avoid unprotected HTTP request redirections

const redirect = result?.Redirect;
const target = isAllowedSameOriginPath(redirect) ? redirect : '/User/Home/Dashboard';
window.location.href = target;
Prompt for LLM

File Web/Resgrid.Web/wwwroot/js/app/internal/home/resgrid.home.dashboard.js:

Line 62:

resgrid.home.dashboard.js and DashboardElement.tsx assign the server-provided Redirect directly to window.location.href, allowing an open redirect if the value targets an external origin. Validate the redirect against an allowlist or same-origin path before assigning it, and fall back to /User/Home/Dashboard.

Suggested Code:

const redirect = result?.Redirect;
const target = isAllowedSameOriginPath(redirect) ? redirect : '/User/Home/Dashboard';
window.location.href = target;

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

​

​

@Resgrid-Bot

Resgrid-Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Kody Review Complete

Great news! 🎉
No issues were found that match your current review configurations.

Keep up the excellent work! 🚀

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the @kody start-review command at the root of your PR.

  • Validate Business Logic: Ask Kody to validate your code against business rules by adding a comment with the @kody -v business-logic command.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug ✅
Performance ❌
Security ✅
Business Logic ❌

Access your configuration settings here.

​

@ucswift

ucswift commented Nov 1, 2026

Copy link
Copy Markdown
Member Author

Approve

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This PR is approved.

@ucswift
ucswift merged commit 5aa34aa into master Nov 1, 2026
17 of 19 checks passed
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.

2 participants