Skip to content

Develop#332

Merged
ucswift merged 3 commits intomasterfrom
develop
Apr 20, 2026
Merged

Develop#332
ucswift merged 3 commits intomasterfrom
develop

Conversation

@ucswift
Copy link
Copy Markdown
Member

@ucswift ucswift commented Apr 20, 2026

Summary by CodeRabbit

  • New Features

    • Mapbox map provider support with department-level configuration overrides.
    • Personnel check-in timers for active calls, tracking status and overdue notifications.
    • Voice call-based contact verification for home numbers.
    • Omnibar search and filterable item selection component.
    • React-based dashboard components (map, shifts calendar).
  • Improvements

    • Enhanced file extension handling with shared utility.
    • Fixed file upload form encoding.

@request-info
Copy link
Copy Markdown

request-info Bot commented Apr 20, 2026

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 20, 2026

Warning

Rate limit exceeded

@ucswift has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 1 minutes and 14 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 1 minutes and 14 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 56120108-2f6e-491b-8475-7854de872129

📥 Commits

Reviewing files that changed from the base of the PR and between 76fcd11 and f70c267.

📒 Files selected for processing (36)
  • Core/Resgrid.Config/MappingConfig.cs
  • Core/Resgrid.Model/UserProfile.cs
  • Core/Resgrid.Services/ContactVerificationService.cs
  • Core/Resgrid.Services/UserProfileService.cs
  • Providers/Resgrid.Providers.Migrations/Migrations/M0048_AddingVoiceVerificationConsumptionFlags.cs
  • Providers/Resgrid.Providers.MigrationsPg/Migrations/M0048_AddingVoiceVerificationConsumptionFlagsPg.cs
  • Tests/Resgrid.Tests/Resgrid.Tests.csproj
  • Tests/Resgrid.Tests/Services/ContactVerificationServiceTests.cs
  • Tests/Resgrid.Tests/Services/DepartmentSettingsServiceMapConfigTests.cs
  • Tests/Resgrid.Tests/Web/Services/TwilioControllerVoiceVerificationTests.cs
  • Web/Resgrid.Web.Services/Controllers/TwilioController.cs
  • Web/Resgrid.Web.Services/Resgrid.Web.Services.csproj
  • Web/Resgrid.Web.Services/Startup.cs
  • Web/Resgrid.Web/Areas/User/Views/CustomMaps/Edit.cshtml
  • Web/Resgrid.Web/Areas/User/Views/CustomMaps/New.cshtml
  • Web/Resgrid.Web/Areas/User/Views/CustomMaps/RegionEditor.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Dispatch/AddArchivedCall.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Dispatch/CallExportEx.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Dispatch/NewCall.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Dispatch/UpdateCall.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Dispatch/ViewCall.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Groups/Geofence.cshtml
  • Web/Resgrid.Web/Areas/User/Views/IndoorMaps/Edit.cshtml
  • Web/Resgrid.Web/Areas/User/Views/IndoorMaps/New.cshtml
  • Web/Resgrid.Web/Areas/User/Views/IndoorMaps/ZoneEditor.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Mapping/LiveRouting.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Mapping/StationRouting.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Mapping/ViewType.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Personnel/ViewEvents.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Routes/ArchivedView.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Routes/Directions.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Routes/Edit.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Routes/InstanceDetail.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Routes/New.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Routes/View.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Units/ViewEvents.cshtml
📝 Walkthrough

Walkthrough

This PR introduces map configuration with per-department Mapbox overrides, adds personnel check-in timer functionality for dispatched calls, implements Twilio voice verification for contact verification, and migrates client-side apps from Angular to React using Vite, plus updates numerous Razor views to use department-specific map configuration.

Changes

Cohort / File(s) Summary
Map Configuration Infrastructure
Core/Resgrid.Config/MappingConfig.cs, Core/Resgrid.Config/ResolvedMapConfig.cs
Refactored mapping config to support per-surface provider selection (Leaflet/Mapbox) via new GetMapConfig(key) API and ResolvedMapConfig container class; added Mapbox credential fields and helper methods for style URL normalization and tile URL generation.
Check-In Timer Domain Models
Core/Resgrid.Model/DepartmentSettingTypes.cs, Core/Resgrid.Model/PersonnelCallCheckInStatus.cs, Core/Resgrid.Model/UserCallCheckInSummary.cs
Added three new enum values for Mapbox settings and two new model classes representing per-person and per-call check-in timer state with timing/status fields.
Check-In Timer Services & Data Access
Core/Resgrid.Model/Services/ICheckInTimerService.cs, Core/Resgrid.Services/CheckInTimerService.cs, Core/Resgrid.Model/Repositories/ICallsRepository.cs, Repositories/Resgrid.Repositories.DataRepository/CallsRepository.cs, Repositories/Resgrid.Repositories.DataRepository/Queries/Calls/SelectActiveCallsWithCheckInTimersForUserQuery.cs, Repositories/Resgrid.Repositories.DataRepository/Servers/*/SqlServerConfiguration.cs, Repositories/Resgrid.Repositories.DataRepository/Servers/*/PostgreSqlConfiguration.cs
Implemented check-in timer query/retrieval across service, repository, and SQL layers; added methods to fetch active calls with timers for a user and per-person status for a call, with helper methods resolving timing states.
Call Service & Verification Updates
Core/Resgrid.Services/CallsService.cs, Core/Resgrid.Model/Services/ICallsService.cs, Core/Resgrid.Model/Providers/IOutboundVoiceProvider.cs, Providers/Resgrid.Providers.Number/OutboundVoiceProvider.cs, Core/Resgrid.Services/ContactVerificationService.cs, Core/Resgrid.Model/Services/IContactVerificationService.cs
Added call service delegation method for check-in timers; added Twilio voice verification call method replacing SMS for home verification; updated ContactVerificationService to use voice provider.
Department Settings for Mapbox Overrides
Core/Resgrid.Model/Services/IDepartmentSettingsService.cs, Core/Resgrid.Services/DepartmentSettingsService.cs, Web/Resgrid.Web/Areas/User/Models/Departments/MappingSettingsView.cs
Added department-level methods to retrieve Mapbox override settings and resolve per-department map config with fallback to system settings; added view model properties for UI configuration.
API Endpoints & Response Models
Web/Resgrid.Web.Services/Controllers/v4/CheckInTimersController.cs, Web/Resgrid.Web.Services/Controllers/v4/ConfigController.cs, Web/Resgrid.Web.Services/Controllers/TwilioController.cs, Web/Resgrid.Web.Services/Models/v4/CheckInTimers/CheckInTimerModels.cs, Web/Resgrid.Web.Services/Models/v4/Configs/GetConfigResult.cs
Added two new check-in timer endpoints (user summaries and per-call personnel statuses); added Twilio voice verification endpoint with TwiML output; updated config controller to include department-specific map config; extended response models with check-in and map provider fields.
Razor Views - Map Configuration Integration
Web/Resgrid.Web/Areas/User/Views/*/*.cshtml
Updated 30+ views across Mapping, Dispatch, Routes, Groups, CustomMaps, IndoorMaps, Personnel, Units, and shared layout to use SettingsHelper.GetDepartmentMapConfig() instead of direct MappingConfig calls, including new map provider/style/token attributes on <rg-map> component.
Client-Side Framework Migration
Web/Resgrid.Web/Areas/User/Apps/package.json, Web/Resgrid.Web/Areas/User/Apps/tsconfig.json, Web/Resgrid.Web/Areas/User/Apps/vite.config.ts, Web/Resgrid.Web/Areas/User/Apps/src/**/*
Migrated from Angular to React with Vite; replaced Angular CLI scripts with Vite/npm equivalents; updated TypeScript config for ES2022/Vite; added React components (LeafletMapView, MapboxMapView, MapElement, OmnibarElement, ShiftsCalendarElement, LoadingIndicator); added runtime utilities (API, auth, SignalR, custom element framework); removed Angular dependencies, added React/Mapbox GL/Leaflet; created custom element registration system.
Supporting Infrastructure
Core/Resgrid.Framework/FileHelper.cs, Web/Resgrid.Web/Helpers/SettingsHelper.cs, Tests/Resgrid.Tests/Framework/FileHelperTests.cs, Tests/Resgrid.Tests/Services/ContactVerificationServiceTests.cs, Tests/Resgrid.Tests/Services/DepartmentSettingsServiceMapConfigTests.cs, Web/Resgrid.Web.Services/Resgrid.Web.Services.xml
Added file extension helper method; implemented department map config caching in SettingsHelper; added comprehensive test coverage for file helper and new service methods; updated XML documentation for API metadata.
Configuration & Build
.claude/settings.local.json, .gitignore, Web/Resgrid.Web/Resgrid.Web.csproj, Web/Resgrid.Web/Areas/User/Apps/README.md
Updated MCP allowlist for mapping-related scans; added gitignore for React build chunks; updated MSBuild targets for Vite build output; replaced Angular README with React/Vite documentation.
Asset Cleanup
Web/Resgrid.Web/wwwroot/js/ng/*
Removed all Angular-era assets: main.js, runtime chunks, polyfills, styles bundles, index.html, third-party licenses; added react-elements.css and react-elements.js.
View Support Files
Web/Resgrid.Web/Areas/User/Controllers/*, Web/Resgrid.Web/Areas/User/Views/Files/Upload.cshtml, Web/Resgrid.Web/Areas/User/Views/Home/EditUserProfile.cshtml, Web/Resgrid.Web/Areas/User/Views/Shared/_UserLayout.cshtml, Web/Resgrid.Web/wwwroot/js/app/internal/home/resgrid.home.edituserprofile.js
Updated controllers to use FileHelper.GetFileExtensionWithoutDot() for consistent extension parsing; fixed file upload form encoding; added voice verification label to user profile; updated layout to load React bundle instead of Angular chunks.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~70 minutes

The PR spans multiple architectural layers (config, domain, services, repositories, controllers, views, client-side) with heterogeneous changes including new feature implementation (check-in timers, voice verification, map overrides), a complete client-side framework migration (Angular → React), and extensive view updates. While individual components follow established patterns, the breadth, depth, and diversity of changes across the codebase require careful multi-faceted review.

Possibly related PRs

Suggested reviewers

  • github-actions
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

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

⚠️ Outside diff range comments (8)
Web/Resgrid.Web/Areas/User/Controllers/DispatchController.cs (1)

2488-2490: ⚠️ Potential issue | 🟠 Major

Missing authorization on AttachCallFile.

Unlike the sibling flag/update endpoints, this POST has no [Authorize(Policy = ResgridResources.Call_Update)] and no _authorizationService.CanUserEditCallAsync(UserId, model.CallId) check before writing the attachment. Any authenticated user can attach a file to any call whose id they know (and DepartmentId isn't validated either). Pre-existing, but worth fixing while in the area.

🛡️ Suggested guard
 [HttpPost]
+[Authorize(Policy = ResgridResources.Call_Update)]
 public async Task<IActionResult> AttachCallFile(FileAttachInput model, IFormFile fileToUpload, CancellationToken cancellationToken)
 {
+	if (!await _authorizationService.CanUserEditCallAsync(UserId, model.CallId))
+		return Unauthorized();
+
 	if (fileToUpload == null || fileToUpload.Length <= 0)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Areas/User/Controllers/DispatchController.cs` around lines
2488 - 2490, The POST action AttachCallFile lacks authorization checks and
allows any authenticated user to attach files to arbitrary calls; add the
[Authorize(Policy = ResgridResources.Call_Update)] attribute to AttachCallFile
and, inside the method, call _authorizationService.CanUserEditCallAsync(UserId,
model.CallId) (or equivalent) and return Forbid/Unauthorized if it fails, and
additionally validate that model.DepartmentId matches the call's DepartmentId
before proceeding to save the attachment to ensure the caller is permitted to
modify that department's call.
Web/Resgrid.Web/Areas/User/Controllers/ProfileController.cs (3)

782-782: ⚠️ Potential issue | 🟡 Minor

Fix grammar in user-facing error message.

The message reads "must be smaller then 10MB" but should use "than" instead of "then".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Areas/User/Controllers/ProfileController.cs` at line 782,
Update the user-facing error message passed to ModelState.AddModelError for
"fileToUpload" to correct the grammar from "must be smaller then 10MB." to "must
be smaller than 10MB." — locate the call to
ModelState.AddModelError("fileToUpload", ...) in ProfileController (around the
file upload/validation logic) and replace "then" with "than".

851-862: 🛠️ Refactor suggestion | 🟠 Major

Extract duplicated file validation logic into a shared method.

The file upload validation logic (lines 851-862) is duplicated from AddCertification (lines 772-783). This violates the DRY principle and makes maintenance more difficult. Changes to validation rules must be synchronized across both methods.

♻️ Proposed refactor to eliminate duplication

Extract the validation into a private method:

private void ValidateCertificationFile(IFormFile file, ModelStateDictionary modelState)
{
    if (file == null || file.Length == 0)
        return;

    var extension = FileHelper.GetFileExtensionWithoutDot(file.FileName);
    
    var allowedExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
    {
        "jpg", "jpeg", "png", "gif", "pdf", "doc", "docx",
        "ppt", "pptx", "pps", "ppsx", "odt", "xls", "xlsx", "txt"
    };

    if (!allowedExtensions.Contains(extension))
        modelState.AddModelError("fileToUpload", $"File type ({extension}) is not importable.");

    if (file.Length > 10_000_000)
        modelState.AddModelError("fileToUpload", "Document is too large, must be smaller than 10MB.");
}

Then in both methods:

-			if (fileToUpload != null && fileToUpload.Length > 0)
-			{
-				var extenion = FileHelper.GetFileExtensionWithoutDot(fileToUpload.FileName);
-
-				if (extenion != "jpg" && extenion != "jpeg" && extenion != "png" && extenion != "gif" && extenion != "gif" && extenion != "pdf" && extenion != "doc"
-					&& extenion != "docx" && extenion != "ppt" && extenion != "pptx" && extenion != "pps" && extenion != "ppsx" && extenion != "odt"
-					&& extenion != "xls" && extenion != "xlsx" && extenion != "txt")
-					ModelState.AddModelError("fileToUpload", string.Format("File type ({0}) is not importable.", extenion));
-
-				if (fileToUpload.Length > 10000000)
-					ModelState.AddModelError("fileToUpload", "Document is too large, must be smaller then 10MB.");
-			}
+			ValidateCertificationFile(fileToUpload, ModelState);

Note: This also addresses the typo (extenion), duplicate "gif" check, and grammar error ("then" → "than") mentioned in the AddCertification review.

As per coding guidelines: "Design for testability; avoid hidden dependencies inside methods and prefer explicit, pure functions" and the principle of not repeating yourself (DRY).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Areas/User/Controllers/ProfileController.cs` around lines 851
- 862, The file upload validation block in ProfileController (duplicated between
the current method and AddCertification) should be extracted to a single private
helper to follow DRY and fix typos; create a private method (e.g.,
ValidateCertificationFile(IFormFile file, ModelStateDictionary modelState)) that
returns early for null/zero-length, checks extension against a case-insensitive
set of allowed extensions (remove duplicate "gif" and correct "extenion" to
"extension"), and enforces the 10MB size limit with the corrected message
("smaller than 10MB"); then call ValidateCertificationFile(...) from both
AddCertification and the current controller action, keeping ModelState error
keys consistent ("fileToUpload").

774-779: ⚠️ Potential issue | 🟠 Major

Add explicit guard against null or empty file extensions.

FileHelper.GetFileExtensionWithoutDot returns string.Empty for files without extensions but will throw ArgumentNullException if fileName is null. Add an explicit null/empty check on the returned extension before the validation block:

var extenion = FileHelper.GetFileExtensionWithoutDot(fileToUpload.FileName);

if (string.IsNullOrEmpty(extenion) || 
    (extenion != "jpg" && extenion != "jpeg" && extenion != "png" && extenion != "gif" && 
     extenion != "pdf" && extenion != "doc" && extenion != "docx" && extenion != "ppt" && 
     extenion != "pptx" && extenion != "pps" && extenion != "ppsx" && extenion != "odt" && 
     extenion != "xls" && extenion != "xlsx" && extenion != "txt"))
    ModelState.AddModelError("fileToUpload", string.Format("File type ({0}) is not importable.", extenion ?? "unknown"));

Additionally, this validation pattern is duplicated across multiple controllers (ProfileController, FilesController, DocumentsController, DispatchController, etc.) with varying allowed extensions. Consider extracting this into a reusable validation method or use a collection-based approach (e.g., !allowedExtensions.Contains(extension)) to reduce repetition and maintenance burden.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Areas/User/Controllers/ProfileController.cs` around lines 774
- 779, The file-extension validation in ProfileController uses
FileHelper.GetFileExtensionWithoutDot (assigned to extenion) but lacks an
explicit null/empty guard; update the validation so it first checks
string.IsNullOrEmpty(extenion) and treats that as invalid (calling
ModelState.AddModelError with a safe message, e.g., showing "unknown" if
extension is null/empty), and replace the long chained != checks with a
collection-based check like !allowedExtensions.Contains(extenion) for clarity;
also extract this logic into a reusable helper/validator used by
ProfileController, FilesController, DocumentsController, DispatchController,
etc., to avoid duplication and ensure consistent allowed-extension lists.
Core/Resgrid.Model/Services/IContactVerificationService.cs (1)

6-10: ⚠️ Potential issue | 🟡 Minor

Stale interface-level XML doc — home is now voice, not SMS.

The summary still reads "email, mobile SMS, home SMS" even though this PR switches home verification to a Twilio voice call. Consider updating to match the per-method docs on line 28-31.

✏️ Proposed doc update
 	/// <summary>
 	/// Service responsible for generating, sending, and confirming contact-method
-	/// verification codes (email, mobile SMS, home SMS). Enforces configurable
-	/// expiry, daily attempt caps, and hourly send rate limits.
+	/// verification codes (email, mobile SMS, home voice call). Enforces configurable
+	/// expiry, daily attempt caps, and hourly send rate limits.
 	/// </summary>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Core/Resgrid.Model/Services/IContactVerificationService.cs` around lines 6 -
10, Update the stale XML summary on the IContactVerificationService interface to
reflect that verification supports email, mobile SMS, and home voice calls
(Twilio) instead of "home SMS"; edit the interface-level <summary> text in
IContactVerificationService to match the per-method descriptions (email, mobile
SMS, home voice) so documentation is accurate and consistent with the voice-call
implementation.
Core/Resgrid.Services/ContactVerificationService.cs (1)

108-120: ⚠️ Potential issue | 🔴 Critical

Add [ValidateTwilioRequest] to VoiceVerification endpoint.

The VoiceVerification endpoint correctly retrieves and decrypts the stored code from the profile, so the design is sound. However, the endpoint has no Twilio signature validation. Line 630 shows only [HttpGet("VoiceVerification")] with no [ValidateTwilioRequest] attribute, despite Twilio.AspNet.Common being imported and available. This allows any unauthenticated HTTP request to call the endpoint with arbitrary userId and contactType parameters, retrieve the encrypted code from the database, and expose it in the TwiML response. Apply the [ValidateTwilioRequest] filter to cryptographically validate that requests originate from Twilio, mirroring the security used for other Twilio callbacks.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Core/Resgrid.Services/ContactVerificationService.cs` around lines 108 - 120,
The VoiceVerification endpoint lacks Twilio signature validation—add the
[ValidateTwilioRequest] attribute to the VoiceVerification action (the method
currently annotated with [HttpGet("VoiceVerification")]) so incoming callbacks
are cryptographically validated as originating from Twilio; ensure the project
imports/uses Twilio.AspNet.Common and apply the attribute to the same method
that reads/decrypts the profile verification code so it mirrors other Twilio
callbacks' protection.
Web/Resgrid.Web/Areas/User/Views/Mapping/NewLayer.cshtml (1)

168-177: ⚠️ Potential issue | 🟠 Major

HTML-encoded tile URL / attribution in JS literals, plus duplicate helper calls.

Two issues here:

  1. Encoding bug. @SettingsHelper.GetDepartmentMapConfig(...).TileUrl and .Attribution are HTML-encoded by Razor before being placed inside single-quoted JS strings. Tile URLs with & become &amp; (breaks tile fetches for Mapbox-style URLs), and the typical Leaflet attribution HTML (© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>) will be rendered as literal escaped markup in the attribution control instead of a working link. A value containing ' would also break the script.
  2. Duplicate resolution. GetDepartmentMapConfig is invoked twice on this page (and twice more in EditLayer.cshtml). Other views in this PR cache the result into a mapConfig Razor local — please follow the same pattern here for consistency and to avoid doing the lookup work twice per render.
🔧 Suggested fix
 @{
 	ViewBag.Title = "Resgrid | " + localizer["NewLayerPageTitle"];
+	var mapConfig = SettingsHelper.GetDepartmentMapConfig(Resgrid.Config.InfoConfig.WebsiteKey);
 }
@@
 			const tiles1 = L.tileLayer(
-				'@SettingsHelper.GetDepartmentMapConfig(Resgrid.Config.InfoConfig.WebsiteKey).TileUrl',
+				`@Html.Raw`(Json.Serialize(mapConfig.TileUrl)),
 				{
 					maxZoom: 19,
 					attribution:
-						'@SettingsHelper.GetDepartmentMapConfig(Resgrid.Config.InfoConfig.WebsiteKey).Attribution',
+						`@Html.Raw`(Json.Serialize(mapConfig.Attribution)),
 				}
 			);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Areas/User/Views/Mapping/NewLayer.cshtml` around lines 168 -
177, Call SettingsHelper.GetDepartmentMapConfig once and store it in a Razor
local (e.g., mapConfig) instead of calling it twice; then render the tile URL
into the JS safely by serializing it into a JS string (use
Json.Encode(mapConfig.TileUrl) so quotes and ampersands are escaped correctly)
and render the attribution as unencoded HTML for Leaflet (use
Html.Raw(mapConfig.Attribution)) so links are not HTML-escaped; update the
L.tileLayer/tiles1 and map initialization (tiles1, mapContainer) to use these
two prepared values.
Web/Resgrid.Web/Areas/User/Controllers/DepartmentController.cs (1)

1958-1994: ⚠️ Potential issue | 🟠 Major

Validate token shape and bound input sizes.

Two small hardening gaps in the Mapbox override path:

  1. The access token is accepted as-is. Mapbox distinguishes public (pk.*) vs secret (sk.*) tokens; saving a secret token here would ship it to every client rendering a map. Since the UI labels this field "Mapbox Public Access Token", explicitly reject non-pk. values.
  2. No max-length check on MapboxStyleUrl / MapboxAccessToken — consider adding [StringLength] on the view-model or bounding here before persisting to avoid oversized settings.
🛡️ Suggested validation
                 if (String.IsNullOrWhiteSpace(model.MapboxAccessToken))
                     ModelState.AddModelError(nameof(model.MapboxAccessToken), "A Mapbox public access token is required when the department override is enabled for website and mobile map rendering.");
+
+                if (!String.IsNullOrWhiteSpace(model.MapboxAccessToken)
+                    && !model.MapboxAccessToken.Trim().StartsWith("pk.", StringComparison.Ordinal))
+                    ModelState.AddModelError(nameof(model.MapboxAccessToken), "Only Mapbox public access tokens (pk.*) are allowed; secret tokens (sk.*) must not be used.");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Areas/User/Controllers/DepartmentController.cs` around lines
1958 - 1994, When handling model.UseMapboxOverride, validate MapboxAccessToken
and MapboxStyleUrl more strictly: ensure MapboxAccessToken
(model.MapboxAccessToken) is non-empty and begins with the public prefix "pk."
(reject values starting with "sk." or anything not prefixed with "pk.") and add
max-length checks for both MapboxAccessToken and MapboxStyleUrl (e.g., enforce a
reasonable limit such as 255 chars) before calling
_departmentSettingsService.SaveOrUpdateSettingAsync; if validations fail, call
ModelState.AddModelError for the relevant field names so code does not reach
SaveOrUpdateSettingAsync/DeleteSettingAsync for MappingMapboxStyleUrl or
MappingMapboxAccessToken or persist oversized/secret tokens.
♻️ Duplicate comments (4)
Web/Resgrid.Web/Areas/User/Views/Mapping/LiveRouting.cshtml (1)

63-64: ⚠️ Potential issue | 🟠 Major

Same HTML-encoding hazard; corrupts L.tileLayer URL and attribution.

These values are consumed directly by L.tileLayer(osmTileUrl, { ..., attribution: osmTileAttribution }) below, so any & in the tile URL or HTML in the attribution will be broken by Razor's HTML encoding. Same fix as the other map views:

🔧 Suggested fix
-        var osmTileUrl = '@mapConfig.TileUrl';
-        var osmTileAttribution = '@mapConfig.Attribution';
+        var osmTileUrl = `@Html.Raw`(Json.Serialize(mapConfig.TileUrl));
+        var osmTileAttribution = `@Html.Raw`(Json.Serialize(mapConfig.Attribution));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Areas/User/Views/Mapping/LiveRouting.cshtml` around lines 63
- 64, The osmTileUrl and osmTileAttribution Razor interpolations are being
HTML-encoded which corrupts values passed into L.tileLayer; replace the plain
'@mapConfig.TileUrl' and '@mapConfig.Attribution' injections with properly
JSON-encoded/raw output so JavaScript receives correct strings (e.g. emit
mapConfig.TileUrl and mapConfig.Attribution via Json.Encode and Html.Raw) and
then pass those variables into L.tileLayer(osmTileUrl, { attribution:
osmTileAttribution }) to avoid HTML-encoding issues.
Web/Resgrid.Web/Areas/User/Views/Mapping/EditLayer.cshtml (1)

170-177: ⚠️ Potential issue | 🟠 Major

HTML-encoded tile URL / attribution plus duplicate helper calls (see NewLayer.cshtml comment).

Razor @ HTML-encodes the interpolated values: & in the tile URL becomes &amp; (breaks Mapbox-style tile fetches), and the attribution's <a> markup ends up rendered as literal escaped text in Leaflet's attribution control. Additionally, GetDepartmentMapConfig is invoked twice — the other updated views in this PR cache it into a mapConfig local; please mirror that.

🔧 Suggested fix
 @{
 	ViewBag.Title = "Resgrid | " + localizer["EditLayerPageTitle"];
+	var mapConfig = SettingsHelper.GetDepartmentMapConfig(Resgrid.Config.InfoConfig.WebsiteKey);
 }
@@
 			const tiles1 = L.tileLayer(
-				'@SettingsHelper.GetDepartmentMapConfig(Resgrid.Config.InfoConfig.WebsiteKey).TileUrl',
+				`@Html.Raw`(Json.Serialize(mapConfig.TileUrl)),
 				{
 					maxZoom: 19,
 					attribution:
-						'@SettingsHelper.GetDepartmentMapConfig(Resgrid.Config.InfoConfig.WebsiteKey).Attribution',
+						`@Html.Raw`(Json.Serialize(mapConfig.Attribution)),
 				}
 			);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Areas/User/Views/Mapping/EditLayer.cshtml` around lines 170 -
177, The tile URL and attribution are being HTML-encoded and
GetDepartmentMapConfig is called twice; fix by caching
SettingsHelper.GetDepartmentMapConfig(Resgrid.Config.InfoConfig.WebsiteKey) into
a local mapConfig variable (as done in NewLayer.cshtml), then pass the
raw/unencoded strings to Leaflet by using the unencoded values for L.tileLayer
(e.g., use the mapConfig.TileUrl without Razor escaping) and for attribution use
the unescaped HTML (e.g., mapConfig.Attribution rendered without HTML-encoding)
so tiles1/L.tileLayer and its attribution receive the correct, unescaped
strings.
Web/Resgrid.Web/Areas/User/Views/Mapping/ViewType.cshtml (1)

125-126: ⚠️ Potential issue | 🟠 Major

Same HTML-encoding hazard for tile URL and attribution.

See the consolidated note on the other map views — Razor HTML-encodes these values, which breaks URLs containing & and corrupts attribution HTML. Use @Html.Raw(Json.Serialize(...)) (without surrounding quotes) instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Areas/User/Views/Mapping/ViewType.cshtml` around lines 125 -
126, Replace the Razor-encoded string assignments for osmTileUrl and
osmTileAttribution so they are emitted as raw JSON (not HTML-encoded) by using
Html.Raw(Json.Serialize(...)) instead of embedding '@mapConfig.TileUrl' and
'@mapConfig.Attribution' in quotes; update the assignments that set osmTileUrl
and osmTileAttribution to use Html.Raw(Json.Serialize(mapConfig.TileUrl)) and
Html.Raw(Json.Serialize(mapConfig.Attribution)) respectively (without
surrounding quotes) to avoid &-encoding and broken attribution HTML.
Web/Resgrid.Web/Areas/User/Views/Dispatch/AddArchivedCall.cshtml (1)

411-412: ⚠️ Potential issue | 🟠 Major

Same HTML-encoding hazard as other map views.

@mapConfig.TileUrl / @mapConfig.Attribution are HTML-encoded by Razor before being placed into single-quoted JS strings, corrupting &-containing tile URLs and attribution HTML. Suggested fix:

🔧 Suggested fix
-        var osmTileUrl = '@mapConfig.TileUrl';
-        var osmTileAttribution = '@mapConfig.Attribution';
+        var osmTileUrl = `@Html.Raw`(Json.Serialize(mapConfig.TileUrl));
+        var osmTileAttribution = `@Html.Raw`(Json.Serialize(mapConfig.Attribution));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Areas/User/Views/Dispatch/AddArchivedCall.cshtml` around
lines 411 - 412, The Razor view is HTML-encoding mapConfig.TileUrl and
mapConfig.Attribution before placing them into JS strings (osmTileUrl,
osmTileAttribution), corrupting values with & and HTML; fix by emitting safe
JSON/string literals instead of raw Razor-encoded text—replace the single-quoted
'@mapConfig.TileUrl' and '@mapConfig.Attribution' usages with JSON-encoded/raw
output (e.g., use Json.Encode wrapped with Html.Raw) so var osmTileUrl and var
osmTileAttribution receive properly escaped JS string literals.
🟡 Minor comments (8)
.claude/settings.local.json-9-15 (1)

9-15: ⚠️ Potential issue | 🟡 Minor

Malformed allowlist entries likely won't match intended commands.

A few of these Bash allowlist patterns look broken and will never match the commands you actually run:

  • Line 9: \\\\\\(-name *mobile* — missing a space between \( and -name. The shell tokenizer will see \(-name as a single token, so this pattern won't match a normal find … \( -name *mobile* -o … \) invocation.
  • Line 12: find G:ResgridResgridWebResgrid.WebAreasUserViews -type f -name *.cshtml — the path has no separators (/ or \) between G:, Resgrid, Resgrid, Web, Resgrid.Web, Areas, User, Views. This is not a valid filesystem path and almost certainly a copy/paste artifact.
  • Line 15: find /g/Resgrid/Resgrid -type f -name *.swift -o -name *.kt -o -name *.java — due to -o precedence vs. the implicit -print, only *.java files will actually be printed. If the intent is to allow a command that finds all three extensions, it needs grouping: \( -name *.swift -o -name *.kt -o -name *.java \).

Also worth considering: .claude/settings.local.json is typically a per-developer local file and probably shouldn't be committed to the repo (consider .gitignore-ing it and keeping a shared settings.json with only team-wide entries).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/settings.local.json around lines 9 - 15, Fix the malformed Bash
allowlist entries in .claude/settings.local.json: add the missing space after
the escaped paren in the find pattern (change "\\\\\\(-name" to "\\\\( -name" so
the token becomes "\( -name"), correct the garbled Windows path token
"G:ResgridResgridWebResgrid.WebAreasUserViews" to a proper path with separators
like "G:/Resgrid/ResgridWeb/Resgrid.Web/Areas/User/Views" (or use backslashes),
and group the multiple -name tests in the last find entry so all extensions
match (wrap the name tests in an escaped group like "\\( -name *.swift -o -name
*.kt -o -name *.java \\)" rather than relying on -o precedence); optionally
remove or add .claude/settings.local.json to .gitignore if this is meant to be
local-only.
Web/Resgrid.Web/Areas/User/Controllers/ProfileController.cs-776-776 (1)

776-776: ⚠️ Potential issue | 🟡 Minor

Remove duplicate "gif" check.

The extension "gif" is checked twice in the same condition on Line 776, which is redundant.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Areas/User/Controllers/ProfileController.cs` at line 776, The
conditional in ProfileController.cs that checks the file extension (variable
extenion) redundantly tests "gif" twice; remove the duplicate "gif" comparison
from the if statement (the check that compares extenion against
"jpg","jpeg","png","gif","gif","pdf","doc") so each extension appears only once
(or replace the chain with a single collection membership test) to eliminate the
redundant condition.
Web/Resgrid.Web/Areas/User/Controllers/ProfileController.cs-774-774 (1)

774-774: ⚠️ Potential issue | 🟡 Minor

Fix the variable name typo.

The variable is misspelled as extenion instead of extension. While this doesn't affect functionality, it reduces code clarity and could confuse future maintainers.

📝 Proposed fix
-				var extenion = FileHelper.GetFileExtensionWithoutDot(fileToUpload.FileName);
+				var extension = FileHelper.GetFileExtensionWithoutDot(fileToUpload.FileName);

-				if (extenion != "jpg" && extenion != "jpeg" && extenion != "png" && extenion != "gif" && extenion != "gif" && extenion != "pdf" && extenion != "doc"
-					&& extenion != "docx" && extenion != "ppt" && extenion != "pptx" && extenion != "pps" && extenion != "ppsx" && extenion != "odt"
-					&& extenion != "xls" && extenion != "xlsx" && extenion != "txt")
-					ModelState.AddModelError("fileToUpload", string.Format("File type ({0}) is not importable.", extenion));
+				if (extension != "jpg" && extension != "jpeg" && extension != "png" && extension != "gif" && extension != "pdf" && extension != "doc"
+					&& extension != "docx" && extension != "ppt" && extension != "pptx" && extension != "pps" && extension != "ppsx" && extension != "odt"
+					&& extension != "xls" && extension != "xlsx" && extension != "txt")
+					ModelState.AddModelError("fileToUpload", string.Format("File type ({0}) is not importable.", extension));

As per coding guidelines: "Use meaningful, descriptive names for types, methods, and parameters; avoid unclear abbreviations".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Areas/User/Controllers/ProfileController.cs` at line 774, In
ProfileController, rename the local variable extenion to extension for clarity
wherever it is assigned from
FileHelper.GetFileExtensionWithoutDot(fileToUpload.FileName) and update any
subsequent references within the method to use extension instead of extenion so
the code reads correctly and matches naming conventions.
Web/Resgrid.Web/Areas/User/Views/Department/MappingSettings.cshtml-89-114 (1)

89-114: ⚠️ Potential issue | 🟡 Minor

Localize the new Mapbox settings text.

This page is localized elsewhere, but the new header, labels, placeholders, and help text are hard-coded English strings.

🌐 Suggested direction
-							<h3>Department Mapbox Override</h3>
+							<h3>@localizer["DepartmentMapboxOverrideHeader"]</h3>
...
-								<label class="col-sm-3 control-label">Use Department Mapbox Map</label>
+								<label class="col-sm-3 control-label">@localizer["UseDepartmentMapboxMapLabel"]</label>
...
-									<span class="help-block m-b-none">Use a public Mapbox token intended for website and mobile client-side map rendering for this department.</span>
+									<span class="help-block m-b-none">@localizer["MapboxPublicAccessTokenHelp"]</span>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Areas/User/Views/Department/MappingSettings.cshtml` around
lines 89 - 114, Replace all hard-coded English strings in MappingSettings.cshtml
with localized resources: inject IViewLocalizer (e.g., `@inject` IViewLocalizer
Localizer) at the top of the view and change the header, labels, placeholders
and help-block texts to use Localizer[...] calls (use keys like
"DepartmentMapboxOverrideHeader", "UseDepartmentMapboxMap",
"MapboxStyleUrlLabel", "MapboxStylePlaceholder", "MapboxStyleHelp",
"MapboxAccessTokenLabel", "MapboxAccessTokenPlaceholder",
"MapboxAccessTokenHelp"). Update the markup surrounding the inputs bound to
UseMapboxOverride, MapboxStyleUrl and MapboxAccessToken to use those Localizer
keys for text and placeholder attributes, and add the corresponding entries to
the view/resource .resx files for translations.
Web/Resgrid.Web/Areas/User/Views/Dispatch/ViewCall.cshtml-951-952 (1)

951-952: ⚠️ Potential issue | 🟡 Minor

Use Html.Raw() to prevent Razor HTML encoding in JavaScript string context.

Razor's default behavior HTML-encodes output, converting & to &amp;. While current tile URLs only have single query parameters (not affected), if configuration ever adds multiple parameters with ampersands, the URL would break. Use Html.Raw() to preserve the values as-is:

-        var osmTileUrl = '@mapConfig.TileUrl';
-        var osmTileAttribution = '@mapConfig.Attribution';
+        var osmTileUrl = '@Html.Raw(mapConfig.TileUrl)';
+        var osmTileAttribution = '@Html.Raw(mapConfig.Attribution)';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web/Areas/User/Views/Dispatch/ViewCall.cshtml` around lines 951 -
952, Razor is HTML-encoding mapConfig values causing '&' to become '&amp;' in
JavaScript strings; update the JavaScript assignments for osmTileUrl and
osmTileAttribution to output the raw, unencoded values by using Html.Raw when
rendering mapConfig.TileUrl and mapConfig.Attribution so the URL and attribution
are preserved exactly in the osmTileUrl and osmTileAttribution variables.
Tests/Resgrid.Tests/Services/DepartmentSettingsServiceMapConfigTests.cs-37-43 (1)

37-43: ⚠️ Potential issue | 🟡 Minor

Restore MappingConfig after tests mutate it.

These static assignments can leak into unrelated tests or race with parallel fixtures. Capture the original values and restore them in [TearDown]/[OneTimeTearDown]; consider marking the fixture [NonParallelizable] if shared config cannot be isolated.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Tests/Resgrid.Tests/Services/DepartmentSettingsServiceMapConfigTests.cs`
around lines 37 - 43, The test mutates the global MappingConfig static
properties (e.g., MappingConfig.MapBoxStyleUrl, MapBoxTileUrl, WebsiteOSMKey,
WebsiteMapboxKey, WebsiteMapboxAccessToken, WebsiteMapMode, LeafletTileUrl)
which can leak into other tests; update the test class to capture the original
values before modification and restore them in a teardown method (use [TearDown]
or [OneTimeTearDown] that resets those MappingConfig properties to the saved
originals), and if isolation cannot be ensured add the NUnit [NonParallelizable]
attribute to the fixture to prevent parallel test interference.
Web/Resgrid.Web.Services/Resgrid.Web.Services.xml-427-438 (1)

427-438: ⚠️ Potential issue | 🟡 Minor

Align the endpoint summary with the NoTimer result contract.

The endpoint summary states that only active calls "with personnel check-in timers enabled" are returned, but the response model explicitly documents HasPersonnelTimer=false, zero timer durations, and Status="NoTimer" as valid values. The source code directly maps HasPersonnelTimer without filtering, confirming the endpoint returns calls both with and without active timers.

📝 Proposed doc fix
-            Returns a check-in timer status summary for every active call (with
-            personnel check-in timers enabled) that <paramref name="userId"/> has been
-            dispatched on.  If the caller is querying a different user they must hold
+            Returns a check-in timer status summary for every active call that
+            <paramref name="userId"/> has been dispatched on, including calls without
+            an active personnel timer reported as <c>NoTimer</c>. If the caller is querying a different user they must hold
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web.Services/Resgrid.Web.Services.xml` around lines 427 - 438,
Update the XML doc summary for member
M:Resgrid.Web.Services.Controllers.v4.CheckInTimersController.GetUserCallCheckInStatuses(System.String)
to reflect that the endpoint returns dispatched calls both with and without
personnel check-in timers enabled (i.e., it may return items with
HasPersonnelTimer=false, zero durations, and Status="NoTimer"), rather than
claiming it only returns calls "with personnel check-in timers enabled";
reference the response contract semantics (HasPersonnelTimer, timer durations,
and Status="NoTimer") and ensure the permission note about Personnel_View vs
Call_View remains unchanged.
Web/Resgrid.Web.Services/Resgrid.Web.Services.xml-5803-5852 (1)

5803-5852: ⚠️ Potential issue | 🟡 Minor

Fix the unresolved XML documentation cross-references.

Lines 5805 and 5851 have cref="!:..." which indicates unresolved links. Generated API docs will render these as broken references. Since this appears to be generated documentation, apply the fix in the source XML comments.

Proposed fix
-            Response wrapper for <see cref="!:GetUserCallCheckInStatuses"/>.
+            Response wrapper for <see cref="M:Resgrid.Web.Services.Controllers.v4.CheckInTimersController.GetUserCallCheckInStatuses(System.String)"/>.
@@
-            Response wrapper for <see cref="!:GetCallPersonnelCheckInStatuses"/>.
+            Response wrapper for <see cref="M:Resgrid.Web.Services.Controllers.v4.CheckInTimersController.GetCallPersonnelCheckInStatuses(System.Int32)"/>.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Web/Resgrid.Web.Services/Resgrid.Web.Services.xml` around lines 5803 - 5852,
The XML doc has unresolved cross-refs: change the invalid cref attributes
(cref="!:GetUserCallCheckInStatuses" and
cref="!:GetCallPersonnelCheckInStatuses") in the XML comments for the types
Resgrid.Web.Services.Models.v4.CheckInTimers.UserCallCheckInStatusResult and
CallPersonnelCheckInStatusResult to valid references — either replace "!:" with
the correct member-kind prefix and fully-qualified member name (e.g.,
"M:Namespace.ControllerClass.GetUserCallCheckInStatuses" and
"M:Namespace.ControllerClass.GetCallPersonnelCheckInStatuses") matching the
actual controller/method symbols, or replace the cref with plain text summaries
if you cannot determine the exact fully-qualified member signature; update the
source XML comments (where these types are documented) so the generated docs no
longer contain broken cref links.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7789b963-ca0a-4acc-bf01-34326731f306

📥 Commits

Reviewing files that changed from the base of the PR and between 11b789e and 76fcd11.

⛔ Files ignored due to path filters (3)
  • Core/Resgrid.Localization/Areas/User/Home/EditProfile.en.resx is excluded by !**/*.resx
  • Web/Resgrid.Web/Areas/User/Apps/package-lock.json is excluded by !**/package-lock.json
  • Web/Resgrid.Web/wwwroot/js/ng/favicon.ico is excluded by !**/*.ico
📒 Files selected for processing (116)
  • .claude/settings.local.json
  • .gitignore
  • Core/Resgrid.Config/MappingConfig.cs
  • Core/Resgrid.Config/NumberProviderConfig.cs
  • Core/Resgrid.Config/ResolvedMapConfig.cs
  • Core/Resgrid.Framework/FileHelper.cs
  • Core/Resgrid.Model/DepartmentSettingTypes.cs
  • Core/Resgrid.Model/PersonnelCallCheckInStatus.cs
  • Core/Resgrid.Model/Providers/IOutboundVoiceProvider.cs
  • Core/Resgrid.Model/Repositories/ICallsRepository.cs
  • Core/Resgrid.Model/Services/ICallsService.cs
  • Core/Resgrid.Model/Services/ICheckInTimerService.cs
  • Core/Resgrid.Model/Services/IContactVerificationService.cs
  • Core/Resgrid.Model/Services/IDepartmentSettingsService.cs
  • Core/Resgrid.Model/UserCallCheckInSummary.cs
  • Core/Resgrid.Services/CallsService.cs
  • Core/Resgrid.Services/CheckInTimerService.cs
  • Core/Resgrid.Services/ContactVerificationService.cs
  • Core/Resgrid.Services/DepartmentSettingsService.cs
  • Providers/Resgrid.Providers.Number/OutboundVoiceProvider.cs
  • Repositories/Resgrid.Repositories.DataRepository/CallsRepository.cs
  • Repositories/Resgrid.Repositories.DataRepository/Configs/SqlConfiguration.cs
  • Repositories/Resgrid.Repositories.DataRepository/Queries/Calls/SelectActiveCallsWithCheckInTimersForUserQuery.cs
  • Repositories/Resgrid.Repositories.DataRepository/Servers/PostgreSql/PostgreSqlConfiguration.cs
  • Repositories/Resgrid.Repositories.DataRepository/Servers/SqlServer/SqlServerConfiguration.cs
  • Tests/Resgrid.Tests/Framework/FileHelperTests.cs
  • Tests/Resgrid.Tests/Services/ContactVerificationServiceTests.cs
  • Tests/Resgrid.Tests/Services/DepartmentSettingsServiceMapConfigTests.cs
  • Web/Resgrid.Web.Services/Controllers/TwilioController.cs
  • Web/Resgrid.Web.Services/Controllers/v4/CheckInTimersController.cs
  • Web/Resgrid.Web.Services/Controllers/v4/ConfigController.cs
  • Web/Resgrid.Web.Services/Models/v4/CheckInTimers/CheckInTimerModels.cs
  • Web/Resgrid.Web.Services/Models/v4/Configs/GetConfigResult.cs
  • Web/Resgrid.Web.Services/Resgrid.Web.Services.xml
  • Web/Resgrid.Web/Areas/User/Apps/README.md
  • Web/Resgrid.Web/Areas/User/Apps/package.json
  • Web/Resgrid.Web/Areas/User/Apps/src/components/map/LeafletMapView.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/map/MapElement.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/map/MapboxMapView.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/map/map.css
  • Web/Resgrid.Web/Areas/User/Apps/src/components/map/mapTypes.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/components/omnibar/OmnibarElement.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/omnibar/omnibar.css
  • Web/Resgrid.Web/Areas/User/Apps/src/components/shared/LoadingIndicator.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/shifts/ShiftsCalendarElement.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/shifts/shifts.css
  • Web/Resgrid.Web/Areas/User/Apps/src/elements.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/runtime/api.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/runtime/auth.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/runtime/browserConfig.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/runtime/customElement.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/runtime/events.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/runtime/signalr.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/styles/base.css
  • Web/Resgrid.Web/Areas/User/Apps/src/vite-env.d.ts
  • Web/Resgrid.Web/Areas/User/Apps/tsconfig.json
  • Web/Resgrid.Web/Areas/User/Apps/vite.config.ts
  • Web/Resgrid.Web/Areas/User/Controllers/DepartmentController.cs
  • Web/Resgrid.Web/Areas/User/Controllers/DispatchController.cs
  • Web/Resgrid.Web/Areas/User/Controllers/DocumentsController.cs
  • Web/Resgrid.Web/Areas/User/Controllers/FilesController.cs
  • Web/Resgrid.Web/Areas/User/Controllers/ProfileController.cs
  • Web/Resgrid.Web/Areas/User/Controllers/ProtocolsController.cs
  • Web/Resgrid.Web/Areas/User/Controllers/TrainingsController.cs
  • Web/Resgrid.Web/Areas/User/Models/Departments/MappingSettingsView.cs
  • Web/Resgrid.Web/Areas/User/Views/CustomMaps/Edit.cshtml
  • Web/Resgrid.Web/Areas/User/Views/CustomMaps/New.cshtml
  • Web/Resgrid.Web/Areas/User/Views/CustomMaps/RegionEditor.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Department/MappingSettings.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Dispatch/AddArchivedCall.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Dispatch/CallExportEx.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Dispatch/Dashboard.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Dispatch/NewCall.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Dispatch/UpdateCall.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Dispatch/ViewCall.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Files/Upload.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Groups/Geofence.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Home/EditUserProfile.cshtml
  • Web/Resgrid.Web/Areas/User/Views/IndoorMaps/Edit.cshtml
  • Web/Resgrid.Web/Areas/User/Views/IndoorMaps/New.cshtml
  • Web/Resgrid.Web/Areas/User/Views/IndoorMaps/ZoneEditor.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Mapping/EditLayer.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Mapping/Index.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Mapping/LiveRouting.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Mapping/NewLayer.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Mapping/StationRouting.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Mapping/ViewType.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Personnel/ViewEvents.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Routes/ArchivedView.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Routes/Directions.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Routes/Edit.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Routes/InstanceDetail.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Routes/New.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Routes/View.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Shared/_UserLayout.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Units/ViewEvents.cshtml
  • Web/Resgrid.Web/Helpers/SettingsHelper.cs
  • Web/Resgrid.Web/Resgrid.Web.csproj
  • Web/Resgrid.Web/wwwroot/js/app/internal/home/resgrid.home.edituserprofile.js
  • Web/Resgrid.Web/wwwroot/js/ng/3rdpartylicenses.txt
  • Web/Resgrid.Web/wwwroot/js/ng/index.html
  • Web/Resgrid.Web/wwwroot/js/ng/main.226d4053d1364245.js
  • Web/Resgrid.Web/wwwroot/js/ng/main.41cf6e57a0ec9523.js
  • Web/Resgrid.Web/wwwroot/js/ng/main.de531c90083e84ba.js
  • Web/Resgrid.Web/wwwroot/js/ng/main.js
  • Web/Resgrid.Web/wwwroot/js/ng/polyfills.23572fc657073556.js
  • Web/Resgrid.Web/wwwroot/js/ng/polyfills.js
  • Web/Resgrid.Web/wwwroot/js/ng/react-elements.css
  • Web/Resgrid.Web/wwwroot/js/ng/react-elements.js
  • Web/Resgrid.Web/wwwroot/js/ng/runtime.1a4e2ecd996ea63f.js
  • Web/Resgrid.Web/wwwroot/js/ng/runtime.6b3b18976611456e.js
  • Web/Resgrid.Web/wwwroot/js/ng/runtime.js
  • Web/Resgrid.Web/wwwroot/js/ng/styles.6e462343e310d4b8.css
  • Web/Resgrid.Web/wwwroot/js/ng/styles.css
  • Web/Resgrid.Web/wwwroot/js/ng/styles.f0de54956caa9d30.css
  • Web/Resgrid.Web/wwwroot/js/ng/vendor.js
💤 Files with no reviewable changes (10)
  • Web/Resgrid.Web/wwwroot/js/ng/index.html
  • Web/Resgrid.Web/wwwroot/js/ng/styles.f0de54956caa9d30.css
  • Web/Resgrid.Web/wwwroot/js/ng/styles.6e462343e310d4b8.css
  • Web/Resgrid.Web/wwwroot/js/ng/runtime.6b3b18976611456e.js
  • Web/Resgrid.Web/wwwroot/js/ng/runtime.js
  • Web/Resgrid.Web/wwwroot/js/ng/runtime.1a4e2ecd996ea63f.js
  • Web/Resgrid.Web/wwwroot/js/ng/polyfills.23572fc657073556.js
  • Web/Resgrid.Web/wwwroot/js/ng/styles.css
  • Web/Resgrid.Web/wwwroot/js/ng/main.js
  • Web/Resgrid.Web/wwwroot/js/ng/3rdpartylicenses.txt

Comment thread .gitignore
.dual-graph/
.claude/settings.local.json
.claude/settings.local.json
/Web/Resgrid.Web/wwwroot/js/ng/chunks
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check whether generated entries reference chunk paths.
rg -n --glob '!**/node_modules/**' 'chunks/|js/ng/chunks|react-elements\.js' Web/Resgrid.Web Web/Resgrid.Web/Areas/User/Apps 2>/dev/null || true

# Confirm git ignores the new chunk directory.
git check-ignore -v Web/Resgrid.Web/wwwroot/js/ng/chunks/example.js || true

# Inspect build/publish config for copying Vite output into wwwroot/js/ng.
fd -i '^(vite\.config\.(ts|js)|package\.json|.*\.csproj|.*\.targets|.*\.props|Directory\.Build\.(props|targets))$' \
  -x sh -c '
    printf "\n--- %s ---\n" "$1"
    rg -n "Areas/User/Apps|wwwroot/js/ng|react-elements|vite|chunks|outDir" "$1" || true
  ' sh {}

Repository: Resgrid/Core

Length of output: 50370


🏁 Script executed:

#!/bin/bash
# Read the vite.config.ts to see output configuration
echo "=== vite.config.ts ===" 
cat Web/Resgrid.Web/Areas/User/Apps/vite.config.ts

echo ""
echo "=== Contents of wwwroot/js/ng/ ==="
ls -la Web/Resgrid.Web/wwwroot/js/ng/ 2>/dev/null || echo "Directory not found"

echo ""
echo "=== Contents of wwwroot/js/ng/chunks/ ==="
ls -la Web/Resgrid.Web/wwwroot/js/ng/chunks/ 2>/dev/null || echo "Chunks directory not found or empty"

echo ""
echo "=== Check if chunks directory is listed in .gitignore ==="
grep -n "chunks" .gitignore || echo "No explicit chunks entry in .gitignore"

Repository: Resgrid/Core

Length of output: 1810


🏁 Script executed:

#!/bin/bash
# Search for build/publish configuration that copies dist/core to wwwroot
echo "=== Searching for build scripts/targets ==="
fd -i '\.(csproj|targets|props|json)$' Web/Resgrid.Web Web/Resgrid.Web/Areas/User/Apps \
  | head -20 | xargs rg -l 'dist/core|wwwroot|copy|build.*chunk' 2>/dev/null

echo ""
echo "=== Check .csproj for publish/build configuration ==="
find Web/Resgrid.Web -name "*.csproj" -type f | head -1 | xargs rg -A5 -B5 'dist|www|publish|copy' 2>/dev/null | head -50

echo ""
echo "=== Check package.json for build/publish scripts ==="
rg -A20 '"scripts"' Web/Resgrid.Web/Areas/User/Apps/package.json 2>/dev/null || echo "Not found"

echo ""
echo "=== Verify if dist/core exists and has chunks ==="
ls -la Web/Resgrid.Web/Areas/User/Apps/dist/ 2>/dev/null || echo "dist/ not found"
ls -la Web/Resgrid.Web/Areas/User/Apps/dist/core/ 2>/dev/null || echo "dist/core/ not found"
ls -la Web/Resgrid.Web/Areas/User/Apps/dist/core/chunks/ 2>/dev/null || echo "dist/core/chunks/ not found"

Repository: Resgrid/Core

Length of output: 3097


Confirm the build/publish pipeline copies chunks from dist/core/ to wwwroot/js/ng/.

The vite config outputs chunks to dist/core/chunks/, but the entry point react-elements.js in wwwroot/js/ng/ imports them with relative paths (import "./chunks/elements-BnCXm89z.js"). The chunks directory is not present in wwwroot and is correctly ignored by git as a generated artifact. However, no build step in the .csproj or npm scripts explicitly copies the compiled chunks to the deployed location. Without this copy step, the entry file will fail at runtime with 404 errors when browsers attempt to load the chunk files.

Ensure the ASP.NET publish or build process includes a step that copies dist/core/chunks/ to wwwroot/js/ng/chunks/ (or verify this happens in your CI/CD pipeline).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.gitignore at line 277, The Vite-generated chunk files in dist/core/chunks
are not being copied to the deployed wwwroot/js/ng/chunks so react-elements.js
imports will 404; fix by adding an explicit copy step either in the project
build or npm pipeline: update the .csproj to include an MSBuild Target (e.g.,
AfterPublish or BeforePublish) that copies dist/core/chunks/** to
wwwroot/js/ng/chunks/**, or add a npm script (postbuild) that copies
dist/core/chunks to wwwroot/js/ng/chunks before publish; reference the artifact
paths (dist/core/chunks, wwwroot/js/ng/chunks) and the entry file
react-elements.js so the copy happens as part of the build/publish for CI/CD.

Comment thread Web/Resgrid.Web.Services/Controllers/TwilioController.cs
Comment thread Web/Resgrid.Web/Areas/User/Views/CustomMaps/New.cshtml Outdated
Comment thread Web/Resgrid.Web/Areas/User/Views/Dispatch/Dashboard.cshtml
@ucswift
Copy link
Copy Markdown
Member Author

ucswift commented Apr 20, 2026

Approve

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

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 8207b3f into master Apr 20, 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.

1 participant