Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Develop#234

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
ucswift merged 10 commits intomasterfromdevelop
Aug 12, 2025
Merged

Develop#234

ucswift merged 10 commits intomasterfromdevelop
Aug 12, 2025

Conversation

@ucswift
Copy link
Member

@ucswiftucswift commentedAug 12, 2025
edited by coderabbitaibot
Loading

Summary by CodeRabbit

  • New Features

    • New dispatch option to auto-set personnel status when placed on a unit.
    • Selectable analytics exporter (PostHog/Aptabase) with unified Analytics host/key and exposed Eventing URL.
    • Phone menu: repeatable barge‑in prompts and new voice actions to set status or staffing.
  • Improvements

    • “On Unit” status displayed across Big Board, reports and user status views.
    • Shift Settings localized and Department breadcrumbs added.
    • Alternate route for current staffing endpoint.
  • Bug Fixes

    • Safer call-file upload/download with validation and default content type.

@request-info
Copy link

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

@coderabbitai
Copy link
Contributor

coderabbitaibot commentedAug 12, 2025
edited
Loading

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between97dd5e9 and9afc325.

📒 Files selected for processing (8)
  • Core/Resgrid.Model/Services/IDepartmentSettingsService.cs (1 hunks)
  • Core/Resgrid.Services/DepartmentSettingsService.cs (3 hunks)
  • Providers/Resgrid.Providers.Bus.Rabbit/RabbitInboundQueueProvider.cs (2 hunks)
  • Web/Resgrid.Web.Services/Controllers/TwilioController.cs (9 hunks)
  • Web/Resgrid.Web.Services/Controllers/v4/PersonnelStaffingController.cs (4 hunks)
  • Web/Resgrid.Web/Areas/User/Controllers/DispatchController.cs (2 hunks)
  • Web/Resgrid.Web/Areas/User/Controllers/MessagesController.cs (1 hunks)
  • Web/Resgrid.Web/Areas/User/Views/Dispatch/CallExportEx.cshtml (3 hunks)
 _______________________________________________________< Coding ain't done 'til all the Tests run. 'Nuff said. > -------------------------------------------------------  \   \   (\__/)       (•ㅅ•)       /   づ

Walkthrough

Adds analytics exporter selection and config fields; introduces “On Unit” action logging with DB migrations and UI handling; adds a new department setting gating unit-linked personnel status updates; updates UnitStatus save flow to create linked action logs; enhances Twilio voice menus with new endpoints; adjusts RabbitMQ retry behavior; various UI/localization and validation updates.

Changes

Cohort / File(s)Summary of Changes
Telemetry & Config
Core/Resgrid.Config/TelemetryConfig.cs,Web/Resgrid.Web.Services/Models/v4/Configs/GetConfigResult.cs,Web/Resgrid.Web.Services/Controllers/v4/ConfigController.cs,Web/Resgrid.Web.Services/Resgrid.Web.Services.xml
Added TelemetryExporters enum and TelemetryConfig exporter selection/keys and GetAnalyticsKey(); renamed PostHog* → Analytics* and added EventingUrl; ConfigController uses TelemetryConfig to populate analytics host/key.
Action log model & flows
Core/Resgrid.Model/ActionLog.cs,Core/Resgrid.Model/ActionTypes.cs,Core/Resgrid.Model/Services/IActionLogsService.cs,Core/Resgrid.Services/ActionLogsService.cs,Providers/Resgrid.Providers.Migrations/Migrations/M0036_AddingUnitStateToActionLogs.cs,Providers/Resgrid.Providers.MigrationsPg/Migrations/M0036_AddingUnitStateToActionLogs.cs,Web/Resgrid.Web.Services/Models/v3/BigBoard/BigBoardModel.cs,Web/Resgrid.Web/Areas/User/Models/Home/BigBoardModel.cs,Web/Resgrid.Web/Areas/User/Controllers/ReportsController.cs,Web/Resgrid.Web/Areas/User/Views/Home/_UserStatusTableRowPartial.cshtml
Added ActionTypes.OnUnit and ActionLog.UnitStateId/UnitName; added IActionLogsService.CreateUnitLinkedStatus and implementation; DB migrations add columns; UI (big board, reports, partial) display handling for action type 7.
Department setting: Personnel-on-Unit
Core/Resgrid.Model/DepartmentSettingTypes.cs,Core/Resgrid.Model/Services/IDepartmentSettingsService.cs,Core/Resgrid.Services/DepartmentSettingsService.cs,Web/Resgrid.Web/Areas/User/Controllers/DepartmentController.cs,Web/Resgrid.Web/Areas/User/Models/Departments/DispatchSettingsView.cs,Web/Resgrid.Web/Areas/User/Views/Department/DispatchSettings.cshtml,Core/Resgrid.Localization/Areas/User/Department/Department.en.resx,Core/Resgrid.Localization/Areas/User/Department/Department.resx
New enum member and service API/impl to read PersonnelOnUnitSetUnitStatus; controller reads/saves flag; view model property and checkbox added; localization entries added/moved.
Unit status integration
Web/Resgrid.Web.Services/Controllers/v4/UnitStatusController.cs
Constructor injects IDepartmentSettingsService and IActionLogsService; SaveUnitStatus accepts CancellationToken; conditionally creates per-user unit-linked action logs when department flag is enabled.
Twilio voice menus & endpoints
Web/Resgrid.Web.Services/Controllers/TwilioController.cs
Reworked Gather flows to repeat prompts with BargeIn; dynamic status/staffing menus; added InboundVoiceActionStatus and InboundVoiceActionStaffing endpoints; added TwilioMessage type.
RabbitMQ retry behavior
Providers/Resgrid.Providers.Bus.Rabbit/RabbitInboundQueueProvider.cs
Replaced commented approach with RabbitConnection.CreateConnection usage; publish retry messages with TTL and headers (redelivery count, previous error); mandatory publish; drop after ≥3 redeliveries; changed retry handling semantics.
Call file validation
Web/Resgrid.Web.Services/Controllers/v4/CallFilesController.cs
Added attachment validation (FileName/Data), default content type fallback, and safe base64 decoding with BadRequest on failure; HEAD validates similarly.
Personnel staffing route alias
Web/Resgrid.Web.Services/Controllers/v4/PersonnelStaffingController.cs
Added second HttpGet route "GetCurrentStaffing" for existing GetCurrentStatffing action.
UI markup & localization
Web/Resgrid.Web/Areas/User/Views/Department/Api.cshtml,.../CallSettings.cshtml,.../MappingSettings.cshtml,.../ModuleSettings.cshtml,.../ShiftSettings.cshtml,Web/Resgrid.Web/Views/Account/LogOn.cshtml,Web/Resgrid.Web/Views/Account/Register.cshtml,Web/Resgrid.Web/Areas/User/Views/Department/DispatchSettings.cshtml
Added Department Settings breadcrumb to multiple views; localized ShiftSettings and added localizer injection; added PersonnelOnUnit checkbox in DispatchSettings view; footer layout adjusted in several account views.
Migrations placeholder
Providers/Resgrid.Providers.MigrationsPg/Migrations/M0035_UpdatingContactsTableAgain.cs
Added empty migration class [Migration(35)] with no Up/Down logic.
Vendor JS (date-fns/export bindings)
Web/Resgrid.Web/wwwroot/js/ng/vendor.js
Added getISOWeek export; swapped default export bindings between startOfISOWeek and startOfISOWeekYear; internal import alias refactors and module reordering.

Sequence Diagram(s)

sequenceDiagram  participant Client  participant UnitStatusController  participant UnitsService  participant DepartmentSettingsService  participant ActionLogsService  Client->>UnitStatusController: SaveUnitStatus(input, cancellationToken)  UnitStatusController->>UnitsService: Save unit state (+roles)  UnitsService-->>UnitStatusController: saved state + roles  UnitStatusController->>DepartmentSettingsService: GetPersonnelOnUnitSetUnitStatusAsync(deptId)  DepartmentSettingsService-->>UnitStatusController: flag  alt flag == true and roles exist    loop for each role user      UnitStatusController->>ActionLogsService: CreateUnitLinkedStatus(userId, deptId, unitStateId, unitName, ct)      ActionLogsService-->>UnitStatusController: ActionLog    end  end  UnitStatusController-->>Client: SaveUnitStatusResult
Loading
sequenceDiagram  participant Caller  participant Twilio  participant TwilioController  participant CustomStateService  participant ActionLogsService  participant UserStateService  Caller->>Twilio: Incoming Call  Twilio->>TwilioController: InboundVoice GET  TwilioController-->>Twilio: TwiML (repeated Gather prompts)  alt user selects status    Twilio->>TwilioController: InboundVoiceActionStatus (digit)    TwilioController->>ActionLogsService: Log status change    ActionLogsService-->>TwilioController: Result    TwilioController-->>Twilio: TwiML response  else user selects staffing    Twilio->>TwilioController: InboundVoiceActionStaffing (digit)    TwilioController->>UserStateService: Update staffing    UserStateService-->>TwilioController: Result    TwilioController-->>Twilio: TwiML response  end
Loading
sequenceDiagram  participant Client  participant ConfigController  participant TelemetryConfig  Client->>ConfigController: GetConfig()  ConfigController->>TelemetryConfig: GetAnalyticsKey()  TelemetryConfig-->>ConfigController: analytics key  ConfigController->>TelemetryConfig: ExporterType  TelemetryConfig-->>ConfigController: exporter enum  ConfigController-->>Client: AnalyticsApiKey, AnalyticsHost, EventingUrl
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

I hopped through code with a carrot grin,
Adding logs for when units roll in.
Twilio rings twice — press one, don't flee,
Analytics choose left or right of me.
Queues learn to nap after tries of three. 🥕🐇

Tip

You can disable sequence diagrams in the walkthrough.

Disable thereviews.sequence_diagrams setting in your project's settings in CodeRabbit to disable sequence diagrams in the walkthrough.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branchdevelop

🪧 Tips

Chat

There are 3 ways to chat withCodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag@coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag@coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on oursupport page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type@coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add@coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add@coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add@coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a.coderabbit.yaml file to the root of your repository.
  • Please see theconfiguration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation:# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit ourStatus Page to check the current availability of CodeRabbit.
  • Visit ourDocumentation for detailed information on how to use CodeRabbit.
  • Join ourDiscord Community to get help, request features, and share feedback.
  • Follow us onX/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitaicoderabbitaibot 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: 21

🔭 Outside diff range comments (4)
Web/Resgrid.Web/Views/Account/LogOn.cshtml (1)

97-97:Rename ‘SetLanugage’ to ‘SetLanguage’

The action and all its references are misspelled as “SetLanugage.” To prevent confusion and future bugs, rename the action method and update the view bindings:

• AccountController (Web/Resgrid.Web/Controllers/AccountController.cs, line 507)
– Change
public IActionResult SetLanugage(string culture, string returnUrl)
– To
public IActionResult SetLanguage(string culture, string returnUrl)

• LogOn.cshtml (Web/Resgrid.Web/Views/Account/LogOn.cshtml, line 97)
– Change
<form … asp-action="SetLanugage" …>
– To
<form … asp-action="SetLanguage" …>

• _TopNavbar.cshtml (Web/Resgrid.Web/Areas/User/Views/Shared/_TopNavbar.cshtml, line 53)
– Change
<form … asp-action="SetLanugage" …>
– To
<form … asp-action="SetLanguage" …>

Ensure any route tests or JavaScript calls referencing this endpoint are updated accordingly.

Web/Resgrid.Web.Services/Controllers/v4/PersonnelStaffingController.cs (2)

75-83:Bug: Using current user instead of requested user when loading state

GetLastUserStateByUserIdAsync is called withUserId (the caller) instead of theuserId parameter after it’s normalized. This returns the wrong person’s staffing for requests querying another user.

Fix the call to use theuserId variable:

- var userState = await _userStateService.GetLastUserStateByUserIdAsync(UserId);+ var userState = await _userStateService.GetLastUserStateByUserIdAsync(userId);

187-191:Harden parsing of input.Type before looping over UserIds

To prevent aFormatException (e.g. wheninput.Type is whitespace), parse once up‐front withint.TryParse and return a 400 if invalid. Then use the parsed value in bothCreateUserState calls:

• File: Web/Resgrid.Web.Services/Controllers/v4/PersonnelStaffingController.cs
Lines: around 187–191

Suggested diff:

-            // inside SavePersonsStaffings, before looping-            List<string> logIds = new List<string>();+            // Validate and parse Type once+            if (!int.TryParse(input.Type, out var typeVal))+                return BadRequest("`Type` must be a valid integer.");+            var logIds = new List<string>();             foreach (var userId in input.UserIds)             {@@-                if (String.IsNullOrWhiteSpace(input.Note))-                    savedState = await _userStateService.CreateUserState(userToSetStatusFor.UserId, DepartmentId, int.Parse(input.Type), cancellationToken);-                else-                    savedState = await _userStateService.CreateUserState(userToSetStatusFor.UserId, DepartmentId, int.Parse(input.Type), input.Note, cancellationToken);+                if (string.IsNullOrWhiteSpace(input.Note))+                    savedState = await _userStateService.CreateUserState(userToSetStatusFor.UserId, DepartmentId, typeVal, cancellationToken);+                else+                    savedState = await _userStateService.CreateUserState(userToSetStatusFor.UserId, DepartmentId, typeVal, input.Note, cancellationToken);
Providers/Resgrid.Providers.Bus.Rabbit/RabbitInboundQueueProvider.cs (1)

579-582:Header parsing is brittle; Rabbit header values are often byte[] or long

ea.BasicProperties.Headers values are AMQP field tables and commonly arrive as byte[] or integer types. To avoid parse errors (e.g., "System.Byte[]" string), handle multiple types.

Apply this diff:

-int currentDeliveryCount = 0;--if (ea.BasicProperties != null && ea.BasicProperties.Headers != null && ea.BasicProperties.Headers.Count > 0 &&-  ea.BasicProperties.Headers.ContainsKey("x-redelivered-count"))-  currentDeliveryCount = int.Parse(ea.BasicProperties.Headers["x-redelivered-count"].ToString());+int currentDeliveryCount = 0;+if (ea.BasicProperties?.Headers != null &&+    ea.BasicProperties.Headers.TryGetValue("x-redelivered-count", out var hdrVal))+{+  switch (hdrVal)+  {+    case byte b: currentDeliveryCount = b; break;+    case sbyte sb: currentDeliveryCount = sb; break;+    case short s: currentDeliveryCount = s; break;+    case int i: currentDeliveryCount = i; break;+    case long l: currentDeliveryCount = (int)l; break;+    case byte[] bytes:+      if (int.TryParse(Encoding.UTF8.GetString(bytes), out var parsedBytes))+        currentDeliveryCount = parsedBytes;+      break;+    default:+      int.TryParse(hdrVal?.ToString(), out currentDeliveryCount);+      break;+  }+}
🧹 Nitpick comments (19)
Web/Resgrid.Web/wwwroot/js/ng/vendor.js (1)

124644-124647:No-op async handler registered on Reconnected

This is an async no-op: function* () {}. Consider omitting the handler to reduce noise, unless used as a future hook.

Web/Resgrid.Web/Views/Account/Register.cshtml (2)

120-120:Possible typo in localization key: "Resgister".

The button uses@localizer["Resgister"]. If this is a misspelling, update to@localizer["Register"] and the corresponding resource keys. If “Resgister” is the intentional key in your resource files, ignore this.


94-94:Invalid CSS value: vertical-align: central;

“central” isn’t a valid value for vertical-align, and vertical-align doesn’t apply to block-level elements in this context. Consider removing it or using a flexbox layout for the label/select alignment.

Core/Resgrid.Model/DepartmentSettingTypes.cs (1)

39-39:Enum value added correctly; consider documenting the setting intent

PersonnelOnUnitSetUnitStatus = 35 is consistent with sequencing. Consider adding an XML summary to capture the behavior (gates creating unit-linked action logs and optionally personnel status updates).

Web/Resgrid.Web.Services/Controllers/v4/PersonnelStaffingController.cs (1)

71-71:Rename the action method to the correct spelling

The action name is still misspelled (GetCurrentStatffing). Rename the method to reduce confusion and improve API docs, while keeping both route attributes for compatibility.

- public async Task<ActionResult<GetCurrentStaffingResult>> GetCurrentStatffing(string userId)+ public async Task<ActionResult<GetCurrentStaffingResult>> GetCurrentStaffing(string userId)
Core/Resgrid.Model/ActionLog.cs (1)

54-59:Persisting Unit linkage: consider constraining UnitName length and indexing strategy

Adding UnitStateId and UnitName looks good. Recommend:

  • Add a [MaxLength] constraint on UnitName to match expected lengths and avoid unbounded strings in SQL (e.g., 128–256 chars).
  • If UnitName will be searched/filtered, consider adding an index at the DB level in the migration.

Example annotation:

 [ProtoMember(13)]-public string UnitName { get; set; }+[MaxLength(256)]+public string UnitName { get; set; }

If you’d prefer a different max length, I can update the migration(s) accordingly.

Providers/Resgrid.Providers.MigrationsPg/Migrations/M0035_UpdatingContactsTableAgain.cs (1)

9-17:Empty migration body with non-empty name/number

This no-op migration upgrades the version but does nothing. If intentional (to reconcile numbering across providers), add a brief comment explaining why to avoid confusion; otherwise, either implement the intended change or remove this migration.

Example:

// Intentionally left blank to align migration numbering across providers.
Providers/Resgrid.Providers.Bus.Rabbit/RabbitInboundQueueProvider.cs (1)

597-598:Consider excluding raw exception messages from headers

"x-previous-error" may leak sensitive info to consumers/logs. Prefer an error code or truncated/sanitized message.

Apply this diff to reduce exposure:

-props.Headers.Add("x-previous-error", mex.Message);+props.Headers.Add("x-previous-error", string.IsNullOrWhiteSpace(mex.Message) ? "UnhandledError" : mex.Message.Substring(0, Math.Min(256, mex.Message.Length)));

Or use a known error code map.

Providers/Resgrid.Providers.MigrationsPg/Migrations/M0036_AddingUnitStateToActionLogs.cs (1)

11-12:Nit: avoid repeated ToLower() calls on identical literals.

Minor readability refactor: store the normalized identifiers in locals to avoid repetition.

-Alter.Table("ActionLogs".ToLower()).AddColumn("UnitStateId".ToLower()).AsInt32().Nullable();-Alter.Table("ActionLogs".ToLower()).AddColumn("UnitName".ToLower()).AsCustom("citext").Nullable();+var table = "actionlogs";+Alter.Table(table).AddColumn("unitstateid").AsInt32().Nullable();+Alter.Table(table).AddColumn("unitname").AsCustom("citext").Nullable();
Web/Resgrid.Web.Services/Resgrid.Web.Services.xml (1)

3978-3984:Fix method XML docs: wrong summary and missing parameter.

The summary refers to “current user”, but this is UnitStatusController.SaveUnitStatus; it should describe saving a unit’s status. Also, the new CancellationToken parameter lacks documentation, and the param name/type reference says “StatusInput” instead of “UnitStatusInput”.

Proposed doc correction:

-        <member name="M:Resgrid.Web.Services.Controllers.v4.UnitStatusController.SaveUnitStatus(Resgrid.Web.Services.Models.v4.UnitStatus.UnitStatusInput,System.Threading.CancellationToken)">-            <summary>-            Sets the status/action for the current user.-            </summary>-            <param name="statusInput">StatusInput object with the Status/Action to set.</param>-            <returns>Returns HttpStatusCode Created if successful, BadRequest otherwise.</returns>-        </member>+        <member name="M:Resgrid.Web.Services.Controllers.v4.UnitStatusController.SaveUnitStatus(Resgrid.Web.Services.Models.v4.UnitStatus.UnitStatusInput,System.Threading.CancellationToken)">+            <summary>+            Sets the status/state for the specified unit.+            </summary>+            <param name="statusInput">UnitStatusInput object with the state to set.</param>+            <param name="cancellationToken">The cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>+            <returns>Returns HttpStatusCode Created if successful, BadRequest otherwise.</returns>+        </member>
Core/Resgrid.Config/TelemetryConfig.cs (1)

19-26:Clarify multi-client analytics key selection and avoid dual source of truth.

  • GetAnalyticsKey() always returns AptabaseWebApiKey for Aptabase. You added multiple Aptabase keys (Services/Responder/Unit/BigBoard/Dispatch), but they’re unused here. If GetConfig serves multiple client apps, this may leak the wrong key. Consider a keyed selector, e.g., GetAnalyticsKeyFor(string client).
  • Exporter (string) and ExporterType (enum) can drift. Provide a single setter to keep them in sync.

Suggested additions:

 public static string GetAnalyticsKey() { if (ExporterType == TelemetryExporters.PostHog) return PostHogApiKey; else if (ExporterType == TelemetryExporters.Aptabase) return AptabaseWebApiKey; return string.Empty; }++// Optional: centralize host selection alongside key selection+public static string GetAnalyticsHost() =>+ExporterType == TelemetryExporters.PostHog ? PostHogUrl :+ExporterType == TelemetryExporters.Aptabase ? AptabaseUrl : string.Empty;++// Optional: keep Exporter and ExporterType in sync+public static void SetExporter(string exporter)+{+Exporter = exporter ?? string.Empty;+switch (exporter?.Trim().ToLowerInvariant())+{+case "posthog": ExporterType = TelemetryExporters.PostHog; break;+case "aptabase": ExporterType = TelemetryExporters.Aptabase; break;+default: ExporterType = TelemetryExporters.None; break;+}+}

Alternatively, extend GetAnalyticsKey(client) to map Aptabase keys per client (web/services/responder/unit/bigboard/dispatch).

Core/Resgrid.Services/DepartmentSettingsService.cs (1)

718-726:Consider caching this hot-path setting and invalidate on updates

This flag will be checked on SaveUnitStatus requests; adding caching will reduce DB load. Also ensure cache invalidation when the setting changes.

Apply this diff to introduce caching for the getter:

-public async Task<bool> GetPersonnelOnUnitSetUnitStatusAsync(int departmentId)-{-var settingValue = await GetSettingByDepartmentIdType(departmentId, DepartmentSettingTypes.PersonnelOnUnitSetUnitStatus);--if (settingValue != null)-return bool.Parse(settingValue.Setting);--return false;-}+public async Task<bool> GetPersonnelOnUnitSetUnitStatusAsync(int departmentId)+{+async Task<string> getSetting()+{+var s = await GetSettingByDepartmentIdType(departmentId, DepartmentSettingTypes.PersonnelOnUnitSetUnitStatus);+return s?.Setting ?? "false";+}++if (Config.SystemBehaviorConfig.CacheEnabled)+{+var cachedValue = await _cacheProvider.RetrieveAsync<string>(+string.Format(PersonnelOnUnitSetUnitStatusCacheKey, departmentId),+getSetting,+LongCacheLength);+return bool.Parse(cachedValue);+}++return bool.Parse(await getSetting());+}

Add these outside the selected range:

// Near other cache keys at the topprivatestaticstringPersonnelOnUnitSetUnitStatusCacheKey="DSetOnUnitSetUnitStatus_{0}";

And invalidate on update in SaveOrUpdateSettingAsync’s switch:

caseDepartmentSettingTypes.PersonnelOnUnitSetUnitStatus:_cacheProvider.Remove(string.Format(PersonnelOnUnitSetUnitStatusCacheKey,departmentId));break;
Web/Resgrid.Web.Services/Controllers/v4/CallFilesController.cs (2)

128-130:Good defensive validation, but consider logging or monitoring.

The validation correctly prevents processing when attachment data is invalid. However, since this is a change in behavior (previously it might have thrown an exception downstream), consider adding logging to track when these conditions occur.

 if (String.IsNullOrWhiteSpace(attachment.FileName) || attachment.Data == null || attachment.Data.Length == 0)+{+    Logging.LogWarning($"GetFile: Invalid attachment data for attachment ID {id} - FileName: {!String.IsNullOrWhiteSpace(attachment.FileName)}, DataLength: {attachment.Data?.Length ?? 0}");     return NotFound();+}

171-173:Inconsistent validation pattern with GetFile method.

This method has the same validation asGetFile, which is good. However, the validation logic is duplicated. Consider extracting this to a helper method to maintain DRY principles.

Add a private helper method to reduce duplication:

privateboolIsValidAttachment(CallAttachmentattachment){return!String.IsNullOrWhiteSpace(attachment?.FileName)&&attachment.Data!=null&&attachment.Data.Length>0;}

Then use it in both methods:

-if (String.IsNullOrWhiteSpace(attachment.FileName) || attachment.Data == null || attachment.Data.Length == 0)+if (!IsValidAttachment(attachment))     return NotFound();
Web/Resgrid.Web.Services/Controllers/TwilioController.cs (5)

478-488:Duplicate Gather blocks may confuse callers

Creating two identical Gather blocks in sequence could lead to poor user experience. If the user doesn't respond to the first prompt, they'll hear the exact same prompt again, which may be confusing. Consider adding a variation or escalation in the second prompt.

 for (int repeat = 0; repeat < 2; repeat++) {     var gatherResponse1 = new Gather(numDigits: 1, action: new Uri(string.Format("{0}/api/Twilio/VoiceCallAction?userId={1}&callId={2}", Config.SystemBehaviorConfig.ResgridApiBaseUrl, userId, callId)), method: "GET")     {         BargeIn = true     };-    gatherResponse1.Append(new Say(sb1.ToString()));+    if (repeat == 0)+        gatherResponse1.Append(new Say(sb1.ToString()));+    else+        gatherResponse1.Append(new Say("I didn't catch that. " + sb1.ToString()));     gatherResponse1.Append(playResponse);     response.Append(gatherResponse1); }

537-545:Duplicate Gather blocks may confuse callers

Similar to the previous comment, having two identical prompts in sequence without variation could confuse users who don't respond to the first prompt.

 for (int repeat = 0; repeat < 2; repeat++) {     var gatherResponse = new Gather(numDigits: 1, action: new Uri(string.Format("{0}/api/Twilio/VoiceCallAction?userId={1}&callId={2}", Config.SystemBehaviorConfig.ResgridApiBaseUrl, userId, callId)), method: "GET")     {         BargeIn = true     };-    gatherResponse.Append(new Say(sb.ToString()));+    if (repeat == 0)+        gatherResponse.Append(new Say(sb.ToString()));+    else+        gatherResponse.Append(new Say("I didn't catch that. " + sb.ToString()));     response.Append(gatherResponse); }

638-646:Duplicate Gather blocks may confuse callers

The main menu also uses duplicate identical prompts. Consider adding variation for better user experience.

 for (int repeat = 0; repeat < 2; repeat++) {     var gatherResponse = new Gather(numDigits: 1, action: new Uri(string.Format("{0}/api/Twilio/InboundVoiceAction?userId={1}", Config.SystemBehaviorConfig.ResgridApiBaseUrl, profile.UserId)), method: "GET")     {         BargeIn = true     };-    gatherResponse.Append(new Say(sb.ToString()));+    if (repeat == 0)+        gatherResponse.Append(new Say(sb.ToString()));+    else+        gatherResponse.Append(new Say("I didn't catch that. " + sb.ToString()));     response.Append(gatherResponse); }

832-836:Duplicate Gather blocks in dynamic menu generation

The pattern of duplicate identical Gather blocks continues here. Consider adding variation for consistency with other suggested improvements.

 for (int repeat = 0; repeat < 2; repeat++) {-    gatherResponse.Append(new Say(sb.ToString()));+    if (repeat == 0)+        gatherResponse.Append(new Say(sb.ToString()));+    else+        gatherResponse.Append(new Say("I didn't catch that. " + sb.ToString()));     response.Append(gatherResponse); }

781-822:Consider extracting option building logic

The status and staffing option building logic is very similar. Consider extracting this into a helper method to reduce duplication and improve maintainability.

Would you like me to generate a helper method that consolidates the option building logic for both status and staffing selections?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between2d4a780 ande4dca46.

📒 Files selected for processing (37)
  • Core/Resgrid.Config/TelemetryConfig.cs (1 hunks)
  • Core/Resgrid.Localization/Areas/User/Department/Department.en.resx (1 hunks)
  • Core/Resgrid.Localization/Areas/User/Department/Department.resx (1 hunks)
  • Core/Resgrid.Model/ActionLog.cs (4 hunks)
  • Core/Resgrid.Model/ActionTypes.cs (1 hunks)
  • Core/Resgrid.Model/DepartmentSettingTypes.cs (1 hunks)
  • Core/Resgrid.Model/Services/IActionLogsService.cs (1 hunks)
  • Core/Resgrid.Model/Services/IDepartmentSettingsService.cs (1 hunks)
  • Core/Resgrid.Services/ActionLogsService.cs (2 hunks)
  • Core/Resgrid.Services/CustomStateService.cs (2 hunks)
  • Core/Resgrid.Services/DepartmentSettingsService.cs (1 hunks)
  • Providers/Resgrid.Providers.Bus.Rabbit/RabbitInboundQueueProvider.cs (2 hunks)
  • Providers/Resgrid.Providers.Migrations/Migrations/M0036_AddingUnitStateToActionLogs.cs (1 hunks)
  • Providers/Resgrid.Providers.MigrationsPg/Migrations/M0035_UpdatingContactsTableAgain.cs (1 hunks)
  • Providers/Resgrid.Providers.MigrationsPg/Migrations/M0036_AddingUnitStateToActionLogs.cs (1 hunks)
  • Web/Resgrid.Web.Services/Controllers/TwilioController.cs (5 hunks)
  • Web/Resgrid.Web.Services/Controllers/v4/CallFilesController.cs (4 hunks)
  • Web/Resgrid.Web.Services/Controllers/v4/ConfigController.cs (3 hunks)
  • Web/Resgrid.Web.Services/Controllers/v4/PersonnelStaffingController.cs (1 hunks)
  • Web/Resgrid.Web.Services/Controllers/v4/UnitStatusController.cs (5 hunks)
  • Web/Resgrid.Web.Services/Models/v3/BigBoard/BigBoardModel.cs (1 hunks)
  • Web/Resgrid.Web.Services/Models/v4/Configs/GetConfigResult.cs (2 hunks)
  • Web/Resgrid.Web.Services/Resgrid.Web.Services.xml (3 hunks)
  • Web/Resgrid.Web/Areas/User/Controllers/DepartmentController.cs (2 hunks)
  • Web/Resgrid.Web/Areas/User/Controllers/ReportsController.cs (1 hunks)
  • Web/Resgrid.Web/Areas/User/Models/Departments/DispatchSettingsView.cs (1 hunks)
  • Web/Resgrid.Web/Areas/User/Models/Home/BigBoardModel.cs (1 hunks)
  • Web/Resgrid.Web/Areas/User/Views/Department/Api.cshtml (1 hunks)
  • Web/Resgrid.Web/Areas/User/Views/Department/CallSettings.cshtml (1 hunks)
  • Web/Resgrid.Web/Areas/User/Views/Department/DispatchSettings.cshtml (2 hunks)
  • Web/Resgrid.Web/Areas/User/Views/Department/MappingSettings.cshtml (1 hunks)
  • Web/Resgrid.Web/Areas/User/Views/Department/ModuleSettings.cshtml (1 hunks)
  • Web/Resgrid.Web/Areas/User/Views/Department/ShiftSettings.cshtml (3 hunks)
  • Web/Resgrid.Web/Areas/User/Views/Home/_UserStatusTableRowPartial.cshtml (1 hunks)
  • Web/Resgrid.Web/Views/Account/LogOn.cshtml (1 hunks)
  • Web/Resgrid.Web/Views/Account/Register.cshtml (1 hunks)
  • Web/Resgrid.Web/wwwroot/js/ng/vendor.js (33 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (10)
Web/Resgrid.Web/Areas/User/Models/Home/BigBoardModel.cs (1)
Core/Resgrid.Model/ActionLog.cs (1)
  • GetActionText (94-120)
Web/Resgrid.Web/Areas/User/Controllers/ReportsController.cs (1)
Core/Resgrid.Model/ActionLog.cs (1)
  • GetActionText (94-120)
Providers/Resgrid.Providers.Migrations/Migrations/M0036_AddingUnitStateToActionLogs.cs (1)
Providers/Resgrid.Providers.MigrationsPg/Migrations/M0036_AddingUnitStateToActionLogs.cs (3)
  • Migration (6-19)
  • Up (9-13)
  • Down (15-18)
Providers/Resgrid.Providers.MigrationsPg/Migrations/M0035_UpdatingContactsTableAgain.cs (2)
Providers/Resgrid.Providers.Migrations/Migrations/M0036_AddingUnitStateToActionLogs.cs (3)
  • Migration (6-19)
  • Up (9-13)
  • Down (15-18)
Providers/Resgrid.Providers.MigrationsPg/Migrations/M0036_AddingUnitStateToActionLogs.cs (3)
  • Migration (6-19)
  • Up (9-13)
  • Down (15-18)
Web/Resgrid.Web.Services/Models/v3/BigBoard/BigBoardModel.cs (1)
Core/Resgrid.Model/ActionLog.cs (1)
  • GetActionText (94-120)
Web/Resgrid.Web.Services/Controllers/v4/PersonnelStaffingController.cs (1)
Web/Resgrid.Web.Services/Controllers/v4/CallFilesController.cs (2)
  • HttpGet (49-91)
  • HttpGet (98-138)
Providers/Resgrid.Providers.MigrationsPg/Migrations/M0036_AddingUnitStateToActionLogs.cs (1)
Providers/Resgrid.Providers.Migrations/Migrations/M0036_AddingUnitStateToActionLogs.cs (3)
  • Migration (6-19)
  • Up (9-13)
  • Down (15-18)
Web/Resgrid.Web.Services/Controllers/v4/ConfigController.cs (2)
Core/Resgrid.Config/SystemBehaviorConfig.cs (1)
  • SystemBehaviorConfig (11-204)
Core/Resgrid.Config/TelemetryConfig.cs (2)
  • TelemetryConfig (3-27)
  • GetAnalyticsKey (19-26)
Web/Resgrid.Web.Services/Controllers/v4/UnitStatusController.cs (3)
Web/Resgrid.Web.Services/Models/v3/BigBoard/BigBoardModel.cs (1)
  • Task (78-318)
Web/Resgrid.Web/Areas/User/Models/Home/BigBoardModel.cs (1)
  • Task (102-342)
Core/Resgrid.Model/Services/IDepartmentSettingsService.cs (1)
  • Task (18-18)
Web/Resgrid.Web.Services/Controllers/TwilioController.cs (2)
Core/Resgrid.Config/SystemBehaviorConfig.cs (1)
  • SystemBehaviorConfig (11-204)
Core/Resgrid.Model/Call.cs (1)
  • GetPriorityText (251-276)
🔇 Additional comments (67)
Web/Resgrid.Web/wwwroot/js/ng/vendor.js (34)

15393-15402:Check getISOWeek DST behavior (rounding by milliseconds)

The implementation uses Math.round(diff / 604800000). This is sensitive to DST transitions; behavior differs from difference-in-calendar-weeks approaches. If this is intentional, add tests around DST boundaries to confirm expected outcomes.


16034-16080:startOfISOWeekYear implementation looks correct

This matches the canonical algorithm: compute Monday of the week containing Jan 4 of the ISO week-year.


16084-16124:startOfISOWeek implementation looks correct

Delegating to startOfWeek with weekStartsOn: 1 is correct for ISO weeks.


16043-16044:AI summary appears inconsistent with the code (no export swap in these modules)

The summary mentions swapped default exports between startOfISOWeekYear and startOfISOWeek. The code here shows startOfISOWeekYear default-exported from startOfISOWeekYear/index.js and startOfISOWeek default-exported from startOfISOWeek/index.js, which is correct.

Also applies to: 16093-16094


72374-72377:Non-functional import alias change (Babel helper)

Renaming the asyncToGenerator helper import alias has no functional impact. Ensure these changes originate from a consistent build rather than a manual edit in vendor.js.


73897-73900:Async wrapper import usage looks fine

This is a standard asyncToGenerator wrapping. No logic change beyond build-time transformation.


73951-73954:Async wrapper inside runOutsideAngular is fine

No functional change; just the compiled async generator wrapper.


75987-75990:Non-functional alias import change (Babel helper)

Same pattern as above; no functional impact.


123077-123080:Non-functional alias import change

No logic change; ensure this comes from a reproducible build.


123891-123894:Async wrapper for storage read is fine

No functional change beyond async compilation.


123907-123910:Async wrapper for storage write is fine

No functional change.


123916-123919:Async wrapper for storage remove is fine

No functional change.


123925-123928:Async wrapper for storage clear is fine

No functional change.


123997-124000:Async wrapper for logout looks fine

No functional change.


124031-124034:Token storage flow looks unchanged

Wrapper only; no functional change.


124042-124045:Token retrieval wrapper is fine

No functional change.


124052-124054:Token removal wrapper is fine

No functional change.


124200-124203:Auth header interception wrapper is fine

No functional change here.


124264-124267:Cache provider initialize wrapper is fine

No functional change.


124277-124280:Cache put wrapper is fine

No functional change.


124288-124291:Cache get wrapper is fine

No functional change.


124320-124323:Cache delete/HTTP response put wrappers are fine

No functional change.

Also applies to: 124328-124331


124338-124341:HTTP response get wrapper is fine

No functional change.


124351-124354:HTTP response delete wrapper is fine

No functional change.


124597-124600:LiveKit connect wrapper is fine

No functional change.


124634-124637:LiveKit prepareConnection wrapper is fine

No functional change.


124683-124686:SignalConnected handler wrapper is fine

No functional change; comment lines clarify intent.


124957-124960:toggleAudio wrapper is fine

Assuming isMicrophoneEnabled is a boolean accessor, this is OK.


124965-124968:toggleVideo wrapper is fine

No functional change.


124975-124978:disconnect wrapper is fine

No functional change.


124999-125004:Device change handler wrapper is fine

No functional change.


125110-125113:toggleTransmitting wrapper is fine

No functional change.


125120-125121:start/stop transmitting and channel change wrappers are fine

No functional changes.

Also applies to: 125126-125127, 125133-125136


128200-128203:SignalR Channel start wrapper is fine

No functional changes.

Core/Resgrid.Model/ActionTypes.cs (1)

50-56:New ActionType 'OnUnit' is well-introduced and consistent with existing enum usage

  • Display attribute and XML summary are aligned with existing entries.
  • Value 7 fits the contiguous range and matches downstream handling referenced in related files (UI styling, text, weighting).

No issues from a serialization or persistence standpoint given the enum is additive.

Web/Resgrid.Web/Areas/User/Models/Departments/DispatchSettingsView.cs (1)

18-19:Property addition looks good; ensure UI binding and defaults are intentional

AddingPersonnelOnUnitSetUnitStatus as a bool is consistent with other toggles and will default to false when not posted. Verify the view uses the typical checkbox + hidden input pattern so false binds correctly when unchecked.

Core/Resgrid.Model/Services/IDepartmentSettingsService.cs (1)

275-276:Interface addition is fine; ensure implementation handles invalid persisted values safely

AddingGetPersonnelOnUnitSetUnitStatusAsync(int departmentId) is consistent with other getters. In the implementation, avoidbool.Parse on persisted values (throws on invalid); preferbool.TryParse(setting.Value, out var enabled) with a false fallback.

Web/Resgrid.Web.Services/Controllers/v4/PersonnelStaffingController.cs (1)

68-69:Good: Added corrected route alias while preserving the legacy misspelling

Keeping both[HttpGet("GetCurrentStatffing")] and[HttpGet("GetCurrentStaffing")] preserves backward compatibility and fixes discoverability.

Core/Resgrid.Localization/Areas/User/Department/Department.en.resx (1)

492-506:English localizations added correctly and match UI usage

The added strings look consistent with the new setting and shift UI. Ensure the neutral resx provides sane defaults (see comment in Department.resx) to avoid blank labels for non-en cultures.

Core/Resgrid.Model/ActionLog.cs (3)

112-116:Action text for OnUnit is sensible; keep an eye on localization

The new OnUnit text logic is fine and aligns with other cases. If/when we localize ActionLog strings, we’ll want to route this through localization like the UI does.

Would you like this to be localized now or tracked for later?


140-141:Consistent styling for OnUnit

Returning "label-success" for OnUnit aligns with Responding and related states. Looks good.


193-194:Weight mapping for OnUnit

Weight 2 for OnUnit makes sense alongside OnScene. Good addition.

Web/Resgrid.Web/Areas/User/Controllers/ReportsController.cs (1)

179-182:OnUnit handling: reuse ActionLog text helper

Using l.GetActionText() for ActionTypeId == 7 is correct and keeps logic centralized.

Web/Resgrid.Web/Areas/User/Views/Home/_UserStatusTableRowPartial.cshtml (1)

149-152:OnUnit rendering is consistent with model helpers

Using al.GetActionCss() and al.GetActionText() is the right approach; keeps UI logic DRY and aligned.

Web/Resgrid.Web.Services/Models/v4/Configs/GetConfigResult.cs (1)

34-38:New EventingUrl field

Adding EventingUrl is straightforward; confirm the controller sets it for all code paths.

If not already done, ensure ConfigController populates EventingUrl for all environments.

Providers/Resgrid.Providers.Bus.Rabbit/RabbitInboundQueueProvider.cs (2)

599-603:mandatory publish without BasicReturn handler may drop messages silently

With mandatory: true, undeliverable messages are returned via BasicReturn. No handler is attached here, so returns are ignored.

Options:

  • Handle BasicReturn on the channel to log and decide on next steps, or
  • Set mandatory: false if you expect the same binding to re-route reliably.

Do you want me to wire a BasicReturn handler?


586-606:Connection/channel lifecycle and call-site semantics

  • A new channel is opened per retry and disposed. That’s OK if RabbitConnection pools the connection, but verify CreateConnection(_clientName) doesn’t open a new connection each time (or dispose it later) to avoid leaks.

  • RetryQueueItem returns true to indicate “don’t retry the original”; however, call sites handle this inconsistently:

    • CallQueueReceived: Nack(requeue: false)
    • Others: Ack()

If queues are DLX-enabled, Nack(false) may route to a dead-letter queue while Ack() simply drops it. Suggest standardizing behavior depending on your retry/DLX strategy.

I can normalize the call sites to all Ack() on successful republish. Would you like a follow-up patch?

Web/Resgrid.Web/Areas/User/Views/Department/ShiftSettings.cshtml (1)

12-12:commonLocalizer injection and resource keys verified

  • The@inject IStringLocalizer<Resgrid.Localization.Common> commonLocalizer is declared in Web/Resgrid.Web/Areas/User/Views/_ViewImports.cshtml (line 14).
  • All view-specific keys (ShiftSettingsHeader, DepartmentSettingsHeader, SaveShiftSettingsText, ShiftSignupSettingsHeader, ShiftSignupsMultipleGroupsHeader) exist in Core/Resgrid.Localization/Areas/User/Department/Department*.resx.
  • Common keys (HomeModule, Cancel, Save) are defined in Core/Resgrid.Localization/Common.{en,es}.resx.

No changes required.

Web/Resgrid.Web.Services/Resgrid.Web.Services.xml (2)

5453-5457:EventingUrl doc addition looks good.

The new EventingUrl field is documented with a clear description and aligns with the config controller behavior.


5533-5542:AnalyticsApiKey/AnalyticsHost renaming doc looks correct.

Docs reflect the telemetry exporter change and new field names. Ensure any old PostHog-specific doc entries (e.g., PostHogHost/PostHogApiKey) have been removed to avoid confusion.

Web/Resgrid.Web.Services/Controllers/v4/ConfigController.cs (1)

71-71:EventingUrl surfaced in API response — LGTM

Adding EventingUrl mapped from SystemBehaviorConfig aligns the config shape with eventing needs.

Web/Resgrid.Web/Areas/User/Views/Department/CallSettings.cshtml (1)

18-20:Added Department Settings breadcrumb — LGTM

Breadcrumb structure and localization usage are consistent with parallel views.

Web/Resgrid.Web/Areas/User/Views/Department/ModuleSettings.cshtml (1)

15-15:Switch to DepartmentSettingsHeader in breadcrumb — LGTM

Localization source and key are appropriate and consistent across the Department views.

Web/Resgrid.Web/Areas/User/Views/Department/Api.cshtml (1)

14-16:Added Department Settings breadcrumb — LGTM

Consistent with other pages and correctly routes to Department/Settings.

Core/Resgrid.Services/CustomStateService.cs (1)

351-351:Default personnel status “On Unit” added — LGTM

Matches ActionTypes.OnUnit addition and the new ActionLog behavior. Defaults and styling align with existing statuses.

Web/Resgrid.Web/Areas/User/Views/Department/MappingSettings.cshtml (1)

14-16:Added Department Settings breadcrumb — LGTM

Navigation and localization are in line with the broader UI updates.

Web/Resgrid.Web/Areas/User/Views/Department/DispatchSettings.cshtml (1)

14-16:Added Department Settings breadcrumb — LGTM

Matches the breadcrumb pattern in related settings views.

Core/Resgrid.Model/Services/IActionLogsService.cs (1)

228-229:LGTM! Clean interface addition for unit-linked status tracking.

The newCreateUnitLinkedStatus method follows the established async pattern and naming conventions, properly positioned after the related methods and maintaining backward compatibility with the optionalCancellationToken parameter.

Core/Resgrid.Services/ActionLogsService.cs (2)

411-422:LGTM! Well-implemented unit-linked status creation method.

The method correctly creates anActionLog with theOnUnit action type and properly associates it with the unit state information.


1-12:Clean up duplicate and unused imports.

The imports section contains duplicates and an unused import:

  • Amazon.SimpleEmail.Model is imported but not used
  • Multiple System.* imports appear to be duplicated
-using Amazon.SimpleEmail.Model; using Resgrid.Model; using Resgrid.Model.Events; using Resgrid.Model.Providers; using Resgrid.Model.Repositories; using Resgrid.Model.Services; using Resgrid.Providers.Bus; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks;

Likely an incorrect or invalid review comment.

Web/Resgrid.Web.Services/Controllers/v4/CallFilesController.cs (1)

214-216:Good defensive programming with explicit base64 validation.

The addition of input validation and try-catch for base64 decoding improves robustness. The error handling properly returns BadRequest for invalid input.

Also applies to: 229-236

Web/Resgrid.Web/Areas/User/Controllers/DepartmentController.cs (1)

1645-1645:LGTM! Clean integration of the new PersonnelOnUnitSetUnitStatus setting.

The implementation correctly follows the existing pattern for department settings, properly retrieving and persisting the new setting.

Also applies to: 1687-1689

Web/Resgrid.Web.Services/Controllers/v4/UnitStatusController.cs (4)

39-40:LGTM! Dependencies properly injected following established patterns.

The new service dependencies are correctly added and follow the existing naming conventions.

Also applies to: 47-48, 55-56


191-191:Good addition of CancellationToken support.

The method signature properly supports cancellation with a default parameter value, maintaining backward compatibility while enabling proper async cancellation support.

Also applies to: 196-196, 199-199


273-273:Fixed extraneous semicolon.

Good catch on removing the duplicate semicolon from the property assignment.


298-308:VerifyUnit.Name nullability and error-handling in unit-linked status creation

We weren’t able to find any[Required] attribute or EF configuration enforcing non-null on theName property inCore/Resgrid.Model/Unit, so please confirm:

  • Canunit.Name ever benull at runtime?
    If it can, consider coalescing or guarding before calling:
    - await _actionLogsService.CreateUnitLinkedStatus(role.UserId, DepartmentId, savedState.UnitStateId, unit.Name, cancellationToken);+ await _actionLogsService.CreateUnitLinkedStatus(+     role.UserId,+     DepartmentId,+     savedState.UnitStateId,+     unit.Name ?? string.Empty,+     cancellationToken+ );
  • What should happen if one of theseCreateUnitLinkedStatus calls fails?
    Should the loop abort and roll back all previous entries (e.g., via a transaction), or continue/ retry on error?

File: Web/Resgrid.Web.Services/Controllers/v4/UnitStatusController.cs (lines 298–308)

Web/Resgrid.Web.Services/Controllers/TwilioController.cs (1)

689-690:GatherResponse initialization is safe due to fallback assignment
Theif (gatherResponse == null) block (lines 824–830) guarantees a non-nullGather instance before it’s ever used in the loop, covering all cases whereDigits isn’t “0” or “7.” No null-reference risk remains—no changes required.

Likely an incorrect or invalid review comment.

Comment on lines 177 to 192
<dataname="ShiftSettingsHeader"xml:space="preserve">
<value />
</data>
<dataname="SaveShiftSettingsText"xml:space="preserve">
<value />
</data>
<dataname="ShiftSignupSettingsHeader"xml:space="preserve">
<value />
</data>
<dataname="ShiftSignupsMultipleGroupsHeader"xml:space="preserve">
<value />
</data>
<dataname="PersonnelOnUnitSetUnitStatusLabel"xml:space="preserve">
<value />
</data>
</root>
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Empty neutral-resx entries may render blank UI for non-English cultures

All five new keys have empty values in the neutral resource. Unless you’re guaranteeing culture fallback to Department.en.resx for all deployments, these may appear blank. Prefer providing default English strings here to ensure sane fallback.

Apply this diff to populate values:

-  <data name="ShiftSettingsHeader" xml:space="preserve">-    <value />-  </data>+  <data name="ShiftSettingsHeader" xml:space="preserve">+    <value>Shift Settings</value>+  </data>-  <data name="SaveShiftSettingsText" xml:space="preserve">-    <value />-  </data>+  <data name="SaveShiftSettingsText" xml:space="preserve">+    <value>Successfully saved Shift Settings.</value>+  </data>-  <data name="ShiftSignupSettingsHeader" xml:space="preserve">-    <value />-  </data>+  <data name="ShiftSignupSettingsHeader" xml:space="preserve">+    <value>Shift Signup Settings</value>+  </data>-  <data name="ShiftSignupsMultipleGroupsHeader" xml:space="preserve">-    <value />-  </data>+  <data name="ShiftSignupsMultipleGroupsHeader" xml:space="preserve">+    <value>Allow Signups For Multiple Groups</value>+  </data>-  <data name="PersonnelOnUnitSetUnitStatusLabel" xml:space="preserve">-    <value />-  </data>+  <data name="PersonnelOnUnitSetUnitStatusLabel" xml:space="preserve">+    <value>Personnel on Unit Set Unit Status</value>+  </data>

🏁 Script executed:

#!/bin/bash# Locate the neutral and English resources for Departmentecho"Searching for Department.resx and Department.en.resx..."find Core/Resgrid.Localization/Areas/User/Department -type f\( -name"Department.resx" -o -name"Department.en.resx"\)echo -e"\nChecking English-specific entries in Department.en.resx if present..."EN_FILE="Core/Resgrid.Localization/Areas/User/Department/Department.en.resx"if [-f"$EN_FILE" ];thenforkeyin ShiftSettingsHeader SaveShiftSettingsText ShiftSignupSettingsHeader ShiftSignupsMultipleGroupsHeader PersonnelOnUnitSetUnitStatusLabel;doecho"Key:$key"    rg -n"<data name=\"$key\"" -A1"$EN_FILE"||echo"  → Not found"doneelseecho"Department.en.resx not found."fi

Length of output: 2989


🏁 Script executed:

#!/bin/bash# Search for NeutralResourcesLanguage attribute in the codebaserg -n"NeutralResourcesLanguage" -H.

Length of output: 37


Populate default English values in the neutral resource or specify a fallback culture

The neutralDepartment.resx currently has empty<value/> entries for these five keys. Without an assembly-level[NeutralResourcesLanguage("en")] attribute, non-English UI cultures will fall back to this empty file—resulting in blank labels. To resolve:

• Option A: Populate the English defaults in Core/Resgrid.Localization/Areas/User/Department/Department.resx
• Option B: Add[assembly: NeutralResourcesLanguage("en")] (e.g. in your Localization project’s AssemblyInfo.cs) to force fallback to Department.en.resx

Suggested diff for Option A:

--- a/Core/Resgrid.Localization/Areas/User/Department/Department.resx+++ b/Core/Resgrid.Localization/Areas/User/Department/Department.resx@@ -177,7 +177,7 @@   <data name="ShiftSettingsHeader" xml:space="preserve">-    <value />+    <value>Shift Settings</value>   </data>@@ -180,7 +180,7 @@   <data name="SaveShiftSettingsText" xml:space="preserve">-    <value />+    <value>Successfully saved Shift Settings.</value>   </data>@@ -183,7 +183,7 @@   <data name="ShiftSignupSettingsHeader" xml:space="preserve">-    <value />+    <value>Shift Signup Settings</value>   </data>@@ -186,7 +186,7 @@   <data name="ShiftSignupsMultipleGroupsHeader" xml:space="preserve">-    <value />+    <value>Allow Signups For Multiple Groups</value>   </data>@@ -189,7 +189,7 @@   <data name="PersonnelOnUnitSetUnitStatusLabel" xml:space="preserve">-    <value />+    <value>Personnel on Unit Set Unit Status</value>   </data>
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<dataname="ShiftSettingsHeader"xml:space="preserve">
<value />
</data>
<dataname="SaveShiftSettingsText"xml:space="preserve">
<value />
</data>
<dataname="ShiftSignupSettingsHeader"xml:space="preserve">
<value />
</data>
<dataname="ShiftSignupsMultipleGroupsHeader"xml:space="preserve">
<value />
</data>
<dataname="PersonnelOnUnitSetUnitStatusLabel"xml:space="preserve">
<value />
</data>
</root>
<dataname="ShiftSettingsHeader"xml:space="preserve">
<value>Shift Settings</value>
</data>
<dataname="SaveShiftSettingsText"xml:space="preserve">
<value>Successfully saved Shift Settings.</value>
</data>
<dataname="ShiftSignupSettingsHeader"xml:space="preserve">
<value>Shift Signup Settings</value>
</data>
<dataname="ShiftSignupsMultipleGroupsHeader"xml:space="preserve">
<value>Allow Signups For Multiple Groups</value>
</data>
<dataname="PersonnelOnUnitSetUnitStatusLabel"xml:space="preserve">
<value>Personnel on Unit Set Unit Status</value>
</data>
</root>
🤖 Prompt for AI Agents
In Core/Resgrid.Localization/Areas/User/Department/Department.resx around lines177 to 192, five resource entries (ShiftSettingsHeader, SaveShiftSettingsText,ShiftSignupSettingsHeader, ShiftSignupsMultipleGroupsHeader,PersonnelOnUnitSetUnitStatusLabel) have empty <value/> elements; populate thesewith the English default text in this neutral .resx (e.g., "Shift Settings","Save Shift Settings", "Shift Signup Settings", "Multiple Group Signups", "SetUnit Status for Personnel") OR, if you prefer to keep English inDepartment.en.resx, add an assembly-level NeutralResourcesLanguage("en")attribute (e.g., in the Localization project’s AssemblyInfo.cs) so non-Englishcultures fall back to Department.en.resx; choose one approach and apply itconsistently.

Comment on lines +11 to +16
Alter.Table("ActionLogs").AddColumn("UnitStateId").AsInt32().Nullable();
Alter.Table("ActionLogs").AddColumn("UnitName").AsString().Nullable();
}

publicoverridevoidDown()
{
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Implement Down() and consider explicit length for UnitName for SQL Server parity

  • Provide a reversible migration so rollbacks don’t get stuck.
  • For SQL Server,AsString() defaults may vary; set an explicit reasonable length (e.g., 256) to align expectations and avoid unintended nvarchar(max)/length mismatches with PG’s citext usage.

Apply this diff:

- Alter.Table("ActionLogs").AddColumn("UnitStateId").AsInt32().Nullable();- Alter.Table("ActionLogs").AddColumn("UnitName").AsString().Nullable();+ Alter.Table("ActionLogs").AddColumn("UnitStateId").AsInt32().Nullable();+ Alter.Table("ActionLogs").AddColumn("UnitName").AsString(256).Nullable();

And implement Down():

 public override void Down() {-+  Delete.Column("UnitName").FromTable("ActionLogs");+  Delete.Column("UnitStateId").FromTable("ActionLogs"); }

🏁 Script executed:

#!/bin/bashset -e# Locate the migration file for M0036echo"=== Migration file path ==="rg -l"M0036_AddingUnitStateToActionLogs" -t cs# Show the Up() methodecho -e"\n=== Up() method content ==="rg -n"public override void Up" -A5 -B2 Providers/Resgrid.Providers.Migrations/Migrations/M0036_AddingUnitStateToActionLogs.cs# Show the Down() methodecho -e"\n=== Down() method content ==="rg -n"public override void Down" -A3 -B2 Providers/Resgrid.Providers.Migrations/Migrations/M0036_AddingUnitStateToActionLogs.cs# List other migrations using AsString() without an explicit lengthecho -e"\n=== Other AsString() usages without length ==="rg -n"AsString\\(\\)" -A1 -t cs Providers/Resgrid.Providers.Migrations/Migrations

Length of output: 19647


🏁 Script executed:

#!/bin/bashset -e# Show the full content of the PG version of the migrationecho"=== PG Migration file content ==="sed -n'1,200p' Providers/Resgrid.Providers.MigrationsPg/Migrations/M0036_AddingUnitStateToActionLogs.cs

Length of output: 641


Add reversible Down() methods and specify SQL Server string length

  • Implement theDown() override in both SQL and PG migrations so rollbacks remove the new columns.
  • In the SQL-Server migration (Providers/Resgrid.Providers.Migrations/Migrations/M0036_AddingUnitStateToActionLogs.cs), set an explicit length onUnitName (e.g. 256) to avoid default NVARCHAR(MAX) and stay in line with PG’s citext expectations.
  • The PG migration (Providers/Resgrid.Providers.MigrationsPg/Migrations/M0036_AddingUnitStateToActionLogs.cs) correctly usescitext; just add the correspondingDown() cleanup for the lowercase names.

SQL Server migration diff:

 public override void Up() {-   Alter.Table("ActionLogs").AddColumn("UnitStateId").AsInt32().Nullable();-   Alter.Table("ActionLogs").AddColumn("UnitName").AsString().Nullable();+   Alter.Table("ActionLogs").AddColumn("UnitStateId").AsInt32().Nullable();+   Alter.Table("ActionLogs").AddColumn("UnitName").AsString(256).Nullable(); } public override void Down()-{-}+{+   Delete.Column("UnitName").FromTable("ActionLogs");+   Delete.Column("UnitStateId").FromTable("ActionLogs");+}

PG migration diff:

 public override void Down()-{-}+{+   Delete.Column("unitname").FromTable("actionlogs");+   Delete.Column("unitstateid").FromTable("actionlogs");+}
🤖 Prompt for AI Agents
InProviders/Resgrid.Providers.Migrations/Migrations/M0036_AddingUnitStateToActionLogs.csaround lines 11-16, implement the Down() override to remove the two columnsadded in Up() and change the Up() call for UnitName to use AsString(256) so SQLServer creates NVARCHAR(256) instead of NVARCHAR(MAX); inProviders/Resgrid.Providers.MigrationsPg/Migrations/M0036_AddingUnitStateToActionLogs.csadd a Down() override that drops the citext column and the unit state int columnusing the lowercased column names used in the PG Up(), ensuring rollbacks removethe same columns each migration added.

Comment on lines +11 to +13
Alter.Table("ActionLogs".ToLower()).AddColumn("UnitStateId".ToLower()).AsInt32().Nullable();
Alter.Table("ActionLogs".ToLower()).AddColumn("UnitName".ToLower()).AsCustom("citext").Nullable();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure citext extension exists before adding a citext column.

PostgreSQL requires the citext extension to be installed. Without it, this migration will fail on environments where the extension hasn’t been enabled.

Consider executing CREATE EXTENSION IF NOT EXISTS citext prior to altering the table:

 public override void Up() {+// Ensure citext extension is available (idempotent)+Execute.Sql("CREATE EXTENSION IF NOT EXISTS citext;"); Alter.Table("ActionLogs".ToLower()).AddColumn("UnitStateId".ToLower()).AsInt32().Nullable(); Alter.Table("ActionLogs".ToLower()).AddColumn("UnitName".ToLower()).AsCustom("citext").Nullable(); }
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Alter.Table("ActionLogs".ToLower()).AddColumn("UnitStateId".ToLower()).AsInt32().Nullable();
Alter.Table("ActionLogs".ToLower()).AddColumn("UnitName".ToLower()).AsCustom("citext").Nullable();
}
publicoverridevoidUp()
{
// Ensure citext extension is available (idempotent)
Execute.Sql("CREATE EXTENSION IF NOT EXISTS citext;");
Alter.Table("ActionLogs".ToLower())
.AddColumn("UnitStateId".ToLower()).AsInt32().Nullable();
Alter.Table("ActionLogs".ToLower())
.AddColumn("UnitName".ToLower()).AsCustom("citext").Nullable();
}
🤖 Prompt for AI Agents
InProviders/Resgrid.Providers.MigrationsPg/Migrations/M0036_AddingUnitStateToActionLogs.csaround lines 11 to 13 you add a citext column but don’t ensure the citextextension exists; add a statement at the start of the Up() migration to execute"CREATE EXTENSION IF NOT EXISTS citext" (or the FluentMigrator equivalent)before calling Alter.Table(...). Ensure this extension-creation runs prior toadding the citext column so the migration succeeds on databases where citext isnot yet enabled.

Comment on lines +15 to +18
publicoverridevoidDown()
{

}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Implement Down() to support rollback.

Down() is empty; rollbacks should drop the added columns to keep migrations reversible.

 public override void Down() {-+Delete.Column("unitname").FromTable("actionlogs");+Delete.Column("unitstateid").FromTable("actionlogs"); }
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
publicoverridevoidDown()
{
}
publicoverridevoidDown()
{
Delete.Column("unitname").FromTable("actionlogs");
Delete.Column("unitstateid").FromTable("actionlogs");
}
🤖 Prompt for AI Agents
InProviders/Resgrid.Providers.MigrationsPg/Migrations/M0036_AddingUnitStateToActionLogs.csaround lines 15-18, the Down() method is empty so the migration is notreversible; implement Down() to revert the Up() changes by removing thecolumn(s) and any associated index/constraint added for UnitState on theActionLogs table (e.g., drop the UnitState column and any FK/index created)using the same migration API your project uses so rollbacks restore the schemato its previous state.

Comment on lines 118 to 125
<divclass="col-md-3">
Resgrid, LLC.
</div>
<divclass="col-md-6 text-right">
<divclass="col-md-3">
Made with <iclass="fa fa-heart text-danger"></i> in Lake Tahoe
</div>
<divclass="col-md-3 text-right">
<small@DateTime.Now.Year</small>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Mirror fixes: localize the new footer string, add a11y, and consider 12-column layout.

  • The “Made with … in Lake Tahoe” string should be localized to match the rest of the page.
  • Add aria-hidden to the icon and provide a visually hidden label for screen readers.
  • Three col-md-3 columns only use 9/12 columns. Consider col-md-4 across all three for balanced layout (if 9/12 isn’t intentional).

Apply this diff:

-            <div>-                Resgrid, LLC.-            </div>-            <div>-                Made with <i></i> in Lake Tahoe-            </div>-            <div>+            <div>+                Resgrid, LLC.+            </div>+            <div>+                @localizer["MadeWith"] <span>@localizer["Love"]</span><i aria-hidden="true"></i> @localizer["InLakeTahoe"]+            </div>+            <div>                 <small>© @DateTime.Now.Year</small>             </div>
  • If you implement a shared footer partial in Register.cshtml, reuse it here for consistency.
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<divclass="col-md-3">
Resgrid, LLC.
</div>
<divclass="col-md-6 text-right">
<divclass="col-md-3">
Made with <iclass="fa fa-heart text-danger"></i> in Lake Tahoe
</div>
<divclass="col-md-3 text-right">
<small@DateTime.Now.Year</small>
<divclass="col-md-4">
Resgrid, LLC.
</div>
<divclass="col-md-4">
@localizer["MadeWith"] <spanclass="sr-only">@localizer["Love"]</span><iclass="fa fa-heart text-danger"aria-hidden="true"></i>@localizer["InLakeTahoe"]
</div>
<divclass="col-md-4 text-right">
<small@DateTime.Now.Year</small>
</div>
🤖 Prompt for AI Agents
In Web/Resgrid.Web/Views/Account/LogOn.cshtml around lines 118-125, the footerneeds three fixes: localize the "Made with … in Lake Tahoe" string using theexisting localization mechanism (e.g., resource lookup), add accessibility bymarking the heart icon aria-hidden="true" and include a visually-hidden textlabel for screen readers (e.g., span with sr-only or aria-label) describing thephrase or heart, and adjust layout from three col-md-3 to three col-md-4 forbalanced 12-column layout (unless the 9/12 width is intentional). If a sharedfooter partial was implemented in Register.cshtml, replace this block with thatpartial to reuse the same footer.

Comment on lines 132 to 139
<divclass="col-md-3">
Resgrid, LLC.
</div>
<divclass="col-md-6 text-right">
<divclass="col-md-3">
Made with <iclass="fa fa-heart text-danger"></i> in Lake Tahoe
</div>
<divclass="col-md-3 text-right">
<small@DateTime.Now.Year</small>
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Localize the new footer text, add a11y for the icon, and consider using full 12-column layout.

  • The new “Made with … in Lake Tahoe” string is hard-coded and not localized, unlike the rest of the page. This regresses i18n. Please move it to the localization resources.
  • Add aria-hidden to the heart icon and include a visually hidden “love” for screen readers.
  • Three col-md-3 columns only consume 9/12 grid columns. Consider making them col-md-4 to avoid ragged spacing on md+ breakpoints (unless the 9/12 is intentional).

Apply this diff:

-            <div>-                Resgrid, LLC.-            </div>-            <div>-                Made with <i></i> in Lake Tahoe-            </div>-            <div>+            <div>+                Resgrid, LLC.+            </div>+            <div>+                @localizer["MadeWith"] <span>@localizer["Love"]</span><i aria-hidden="true"></i> @localizer["InLakeTahoe"]+            </div>+            <div>                 <small>© @DateTime.Now.Year</small>             </div>
  • Alternatively, use a single key like@localizer["MadeWithLoveInLakeTahoe"] and place the heart icon as purely decorative after the text.
  • Since the same footer appears in LogOn.cshtml, consider extracting this into a shared partial (e.g., _AccountFooter.cshtml) to keep things DRY and consistent across pages.

To confirm Font Awesome is available on these pages (for the heart icon), run:


🏁 Script executed:

#!/bin/bash# Locate style partial(s) and check for Font Awesome referencesfd -a _StylePartial.cshtmlrg -n"font-awesome|fontawesome|fa-" -S

Length of output: 309423


Localize Footer Text, Improve A11y, Use 12-Column Layout & Extract to Partial

  • Move the hard-coded “Made with … in Lake Tahoe” string into your localization resources (e.g. Resources.Account.Footer):
    • @localizer["MadeWithLoveInLakeTahoe"] (or two keys: “MadeWith” and “InLakeTahoe” plus a “Love” screen-reader key)
  • Addaria-hidden="true" to the<i> and include a visually hidden label:
    • <span>@localizer["Love"]</span>
  • Change the threecol-md-3 tocol-md-4 so they fill the full 12-column grid:
  •        <div>
  •            Resgrid, LLC.
  •        </div>
  •        <div>
  •            Made with <i></i> in Lake Tahoe
  •        </div>
  •        <div>
  •        <div>
  •            Resgrid, LLC.
  •        </div>
  •        <div>
  •            @localizer["MadeWith"] <span>@localizer["Love"]</span><i aria-hidden="true"></i> @localizer["InLakeTahoe"]
  •        </div>
  •        <div>           <small>© @DateTime.Now.Year</small>       </div>
  • Extract the footer into a shared partial (e.g._AccountFooter.cshtml) and include it in both Register.cshtml and LogOn.cshtml to keep layouts DRY.
  • confirmed: Font Awesome is available via libman underwwwroot/lib/font-awesome/
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<divclass="col-md-3">
Resgrid, LLC.
</div>
<divclass="col-md-6 text-right">
<divclass="col-md-3">
Made with <iclass="fa fa-heart text-danger"></i> in Lake Tahoe
</div>
<divclass="col-md-3 text-right">
<small@DateTime.Now.Year</small>
<divclass="col-md-4">
Resgrid, LLC.
</div>
<divclass="col-md-4">
@localizer["MadeWith"] <spanclass="sr-only">@localizer["Love"]</span><iclass="fa fa-heart text-danger"aria-hidden="true"></i>@localizer["InLakeTahoe"]
</div>
<divclass="col-md-4 text-right">
<small@DateTime.Now.Year</small>
</div>
🤖 Prompt for AI Agents
In Web/Resgrid.Web/Views/Account/Register.cshtml around lines 132–139 the footercontains hard-coded text, incorrect column widths, missing a11y markup, andshould be shared across pages; move the “Made with … in Lake Tahoe” string intolocalization (e.g. use @localizer["MadeWithLoveInLakeTahoe"] or separate keyslike "MadeWith", "Love", "InLakeTahoe"), add aria-hidden="true" to the <iclass="fa fa-heart"> icon and include a visually-hidden label for screen readers(e.g. a sr-only span with @localizer["Love"]), change the three col-md-3 classesto col-md-4 so they total 12 columns, and extract this block into a sharedpartial (e.g. Views/Shared/_AccountFooter.cshtml) then include that partial inboth Register.cshtml and LogOn.cshtml so the footer is localized, accessible,and DRY.

Comment on lines +15353 to +15402
/***/ 8355:
/*!*******************************************************!*\
!*** ./node_modules/date-fns/esm/getISOWeek/index.js ***!
\*******************************************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (/* binding */ getISOWeek)
/* harmony export */ });
/* harmony import */ var _toDate_index_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../toDate/index.js */ 9103);
/* harmony import */ var _startOfISOWeek_index_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../startOfISOWeek/index.js */ 5556);
/* harmony import */ var _startOfISOWeekYear_index_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../startOfISOWeekYear/index.js */ 2781);
/* harmony import */ var _lib_requiredArgs_index_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../_lib/requiredArgs/index.js */ 4507);




var MILLISECONDS_IN_WEEK = 604800000;

/**
* @name getISOWeek
* @category ISO Week Helpers
* @summary Get the ISO week of the given date.
*
* @description
* Get the ISO week of the given date.
*
* ISO week-numbering year: http://en.wikipedia.org/wiki/ISO_week_date
*
* @param {Date|Number} date - the given date
* @returns {Number} the ISO week
* @throws {TypeError} 1 argument required
*
* @example
* // Which week of the ISO-week numbering year is 2 January 2005?
* const result = getISOWeek(new Date(2005, 0, 2))
* //=> 53
*/
function getISOWeek(dirtyDate) {
(0,_lib_requiredArgs_index_js__WEBPACK_IMPORTED_MODULE_0__["default"])(1, arguments);
var date = (0,_toDate_index_js__WEBPACK_IMPORTED_MODULE_1__["default"])(dirtyDate);
var diff = (0,_startOfISOWeek_index_js__WEBPACK_IMPORTED_MODULE_2__["default"])(date).getTime() - (0,_startOfISOWeekYear_index_js__WEBPACK_IMPORTED_MODULE_3__["default"])(date).getTime();

// Round the number of days to the nearest integer
// because the number of milliseconds in a week is not constant
// (e.g. it's different in the week of the daylight saving time clock shift)
return Math.round(diff / MILLISECONDS_IN_WEEK) + 1;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Do not hand-edit bundled vendor.js; update dependencies and rebuild instead

This file appears to have been manually modified to add/alter date-fns modules (e.g., getISOWeek). Compiled vendor assets should be generated by the build toolchain (webpack) from package sources to ensure reproducibility, correct sourcemaps, and avoid future drift. Please revert direct edits to vendor.js, adjust the dependency (e.g., update date-fns), and rebuild.

If the intent was to introduce getISOWeek, add/upgrade date-fns in package.json and re-run the build.

I can propose the package.json changes and expected build steps if you confirm your target date-fns version. Do you want me to open an issue to track removing manual edits from vendor assets?

🤖 Prompt for AI Agents
In Web/Resgrid.Web/wwwroot/js/ng/vendor.js around lines 15353–15402, the bundledvendor file was manually edited to add/alter date-fns modules (e.g.,getISOWeek); revert these manual edits and restore vendor.js from the last cleanbuild artifact or from source control, then update package.json to the desireddate-fns version that includes getISOWeek (or add the correct package), run npminstall to update package-lock.json/yarn.lock, rebuild the frontend bundle usingthe project build script (e.g., npm run build or the project's webpack task),verify the generated vendor.js contains the expected date-fns exports, andcommit only the dependency changes and regenerated artifacts (not hand edits) sofuture builds remain reproducible.

Comment on lines +109667 to 109670
return (0,G_Resgrid_Resgrid_Web_Resgrid_Web_Areas_User_Apps_node_modules_babel_runtime_helpers_esm_asyncToGenerator_js__WEBPACK_IMPORTED_MODULE_0__["default"])(function* () {
const timeout = new Promise(resolve => setTimeout(resolve));
const rAF = typeof _global['requestAnimationFrame'] === 'function' ? new Promise(resolve => requestAnimationFrame(() => resolve())) : null;
yield Promise.race([timeout, rAF]);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Bug: Promise.race includes a non-promise (null) resulting in immediate resolution

When requestAnimationFrame is unavailable, rAF is null, and Promise.race([timeout, rAF]) resolves immediately (non-thenables are wrapped by Promise.resolve). This defeats the intended wait behavior.

Apply this diff to safely handle the absence of rAF:

-      const rAF = typeof _global['requestAnimationFrame'] === 'function' ? new Promise(resolve => requestAnimationFrame(() => resolve())) : null;-      yield Promise.race([timeout, rAF]);+      const rAF = typeof _global['requestAnimationFrame'] === 'function'+        ? new Promise(resolve => requestAnimationFrame(() => resolve()))+        : undefined;+      yield Promise.race([timeout, rAF].filter(Boolean));

Note: Please make the change in the source TS/JS before bundling, not directly in vendor.js.

📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return(0,G_Resgrid_Resgrid_Web_Resgrid_Web_Areas_User_Apps_node_modules_babel_runtime_helpers_esm_asyncToGenerator_js__WEBPACK_IMPORTED_MODULE_0__["default"])(function*(){
consttimeout=newPromise(resolve=>setTimeout(resolve));
constrAF=typeof_global['requestAnimationFrame']==='function' ?newPromise(resolve=>requestAnimationFrame(()=>resolve())) :null;
yieldPromise.race([timeout,rAF]);
constrAF=typeof_global['requestAnimationFrame']==='function'
?newPromise(resolve=>requestAnimationFrame(()=>resolve()))
:undefined;
yieldPromise.race([timeout,rAF].filter(Boolean));
🤖 Prompt for AI Agents
In Web/Resgrid.Web/wwwroot/js/ng/vendor.js around lines 109667 to 109670 thePromise.race call includes rAF which can be null when requestAnimationFrame isunavailable, causing Promise.race to immediately resolve; update the originalsource TS/JS (not vendor bundle) to only include a thenable: either push rAFinto the race array only if it's truthy, or replace a missing rAF with anon-resolving Promise (e.g. new Promise(()=>{})) so the timeout remainseffective; make the change in the source file and rebuild the bundle.

ucswiftand others added2 commitsAugust 11, 2025 21:59
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitaicoderabbitaibot 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: 7

🔭 Outside diff range comments (3)
Web/Resgrid.Web/Views/Account/CompleteInvite.cshtml (3)

41-43:Add rel="noopener noreferrer" to external links opened in a new tab

Prevents window.opener vulnerabilities and improves performance when using.

-                    @localizer["SignUpTerms1"] Resgrid's <a asp-controller="Public" asp-action="Terms">@localizer["TermsOfUse"]</a> &-                    <a asp-controller="Public" asp-action="Privacy">@localizer["PrivacyPolicy"]</a>+                    @localizer["SignUpTerms1"] Resgrid's <a asp-controller="Public" asp-action="Terms" rel="noopener noreferrer">@localizer["TermsOfUse"]</a> &+                    <a asp-controller="Public" asp-action="Privacy" rel="noopener noreferrer">@localizer["PrivacyPolicy"]</a>

26-26:Add alt text to the logo for accessibility

Images used as branding should have an alt description so screen readers convey context.

-                    <a href="@Resgrid.Config.UrlsConfig.HomeUrl"><img src="~/images/Resgrid_JustText.png" /></a>+                    <a href="@Resgrid.Config.UrlsConfig.HomeUrl"><img src="~/images/Resgrid_JustText.png" alt="Resgrid" /></a>

80-80:Fix typo “Resgister” → “Register” in views and resource files

The key “Resgister” is consistently used in two Razor views and two.resx files—this will render the typo at runtime. Please update the key and its value as follows:

• Web/Resgrid.Web/Views/Account/CompleteInvite.cshtml (line 80)
Change
@localizer["Resgister"]
to
@localizer["Register"]

• Web/Resgrid.Web/Views/Account/Register.cshtml (line 120)
Change
@localizer["Resgister"]
to
@localizer["Register"]

• Core/Resgrid.Localization/Account/Login.en.resx (≈lines 243–244)
– Rename
<data name="Resgister" …><data name="Register" …>
– Update
<value>Resgister</value><value>Register</value>

• Core/Resgrid.Localization/Account/Login.es.resx (≈line 243)
– Rename key as above
– Update the<value> to the correct Spanish translation (e.g. “Registrarse”) per your localization guidelines

Suggested diff:

--- before                                       --- after- @localizer["Resgister"]                       + @localizer["Register"]- <data name="Resgister" xml:space="preserve">  + <data name="Register" xml:space="preserve">-   <value>Resgister</value>                    +   <value>Register</value>  (or proper Spanish text)
🧹 Nitpick comments (5)
Web/Resgrid.Web/Views/Account/CompleteInvite.cshtml (3)

94-96:Optional: match typography with the year using

To visually balance the left/right columns, wrap the company name in small text like the year column.

-            <div>-                Resgrid, LLC.-            </div>+            <div>+                <small>Resgrid, LLC.</small>+            </div>

101-101:Use HTML entity for the copyright symbol

Using © avoids encoding surprises across locales and editors.

-                <small>© @DateTime.Now.Year</small>+                <small>&copy; @DateTime.Now.Year</small>

51-52:Form/JS mismatch: IDs referenced in JS don’t exist; reCAPTCHA line should be guarded

The submit handler targets #registrationForm and #registerButton, but the markup lacks these IDs. Also, reCAPTCHA fields may not exist on this page; guard the code to avoid no-op confusion.

Apply these diffs:

Add IDs to form and button so the JS can bind and disable the button on submit:

-                    <form role="form" asp-controller="Account" asp-action="CompleteInvite" method="post">+                    <form role="form" asp-controller="Account" asp-action="CompleteInvite" method="post">...-                        <button type="submit">@localizer["Resgister"]</button>+                        <button type="submit">@localizer["Resgister"]</button>

Guard the reCAPTCHA assignment so it only runs if fields exist:

-              $(document).on('submit', '#registrationForm', function () {-                var crep = $('#g-recaptcha-response').val();-                $('#GoogleReCaptchaResponse').val(crep);+              $(document).on('submit', '#registrationForm', function () {+                var $crep = $('#g-recaptcha-response');+                if ($crep.length) {+                  $('#GoogleReCaptchaResponse').val($crep.val());+                }

If this page doesn’t use reCAPTCHA, consider removing those lines entirely.

Also applies to: 80-80, 117-120

Web/Resgrid.Web.Services/Controllers/TwilioController.cs (2)

630-637:Polish the IVR prompt phrasing and punctuation

Minor grammatical nit: standardize casing and insert commas to aid TTS clarity.

Apply this diff to refine the wording:

- sb.Append($@"Hello {profile.FirstName}, this is the Resgrid Automated Voice System for {department.Name}. Please select from the following options.-To list current active calls press 1,-To list current user statuses press 2,-To list current unit statuses press 3,-To list upcoming Calendar events press 4,-To list upcoming Shifts press 5,-To Set your current status press 6,-To set your current staffing level press 7");+ sb.Append($@"Hello {profile.FirstName}, this is the Resgrid automated voice system for {department.Name}. Please select from the following options.+To list current active calls, press 1.+To list current user statuses, press 2.+To list current unit statuses, press 3.+To list upcoming calendar events, press 4.+To list upcoming shifts, press 5.+To set your current status, press 6.+To set your current staffing level, press 7.");

709-715:Risk of overly long TTS prompts; consider limiting or chunking results

Reading all calls, all users with status/staffing, and all units can produce extremely long prompts, hurting UX and risking Twilio timeouts.

Consider:

  • Limit items (e.g., top 10) and end with “For more, use the mobile app or website.”
  • Or paginate/chunk and redirect to a follow-up menu.
  • For calls/units, summarize counts and only read brief highlights (e.g., counts by priority/status) to reduce verbosity.

Also applies to: 727-737, 748-756

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and betweene4dca46 andc5eb24c.

📒 Files selected for processing (11)
  • Core/Resgrid.Localization/Areas/User/Department/Department.en.resx (2 hunks)
  • Core/Resgrid.Localization/Areas/User/Department/Department.es.resx (2 hunks)
  • Core/Resgrid.Localization/Areas/User/Department/Department.resx (1 hunks)
  • Providers/Resgrid.Providers.Bus.Rabbit/RabbitInboundQueueProvider.cs (2 hunks)
  • Web/Resgrid.Web.Services/Controllers/TwilioController.cs (5 hunks)
  • Web/Resgrid.Web.Services/Models/v3/BigBoard/BigBoardModel.cs (1 hunks)
  • Web/Resgrid.Web/Areas/User/Models/Home/BigBoardModel.cs (1 hunks)
  • Web/Resgrid.Web/Areas/User/Views/Department/DispatchSettings.cshtml (3 hunks)
  • Web/Resgrid.Web/Views/Account/CompleteInvite.cshtml (1 hunks)
  • Web/Resgrid.Web/Views/Account/LogOn.cshtml (1 hunks)
  • Web/Resgrid.Web/Views/Account/Register.cshtml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • Web/Resgrid.Web/Areas/User/Models/Home/BigBoardModel.cs
  • Web/Resgrid.Web/Areas/User/Views/Department/DispatchSettings.cshtml
  • Core/Resgrid.Localization/Areas/User/Department/Department.en.resx
  • Web/Resgrid.Web/Views/Account/Register.cshtml
  • Core/Resgrid.Localization/Areas/User/Department/Department.resx
  • Web/Resgrid.Web.Services/Models/v3/BigBoard/BigBoardModel.cs
  • Web/Resgrid.Web/Views/Account/LogOn.cshtml
  • Providers/Resgrid.Providers.Bus.Rabbit/RabbitInboundQueueProvider.cs
🧰 Additional context used
🧬 Code Graph Analysis (1)
Web/Resgrid.Web.Services/Controllers/TwilioController.cs (2)
Core/Resgrid.Config/SystemBehaviorConfig.cs (1)
  • SystemBehaviorConfig (11-204)
Core/Resgrid.Model/Call.cs (1)
  • GetPriorityText (251-276)
🔇 Additional comments (4)
Core/Resgrid.Localization/Areas/User/Department/Department.es.resx (1)

123-134:Don’t remove these dispatch localization keys

The grep results show that all four keys are still referenced in
Web/Resgrid.Web/Areas/User/Views/Department/DispatchSettings.cshtml
and are present in both the neutral (Department.resx) and English (Department.en.resx) resource files. Removing them fromDepartment.es.resx would break the localization calls.

Likely an incorrect or invalid review comment.

Web/Resgrid.Web.Services/Controllers/TwilioController.cs (3)

478-490:Good use of Gather with barge-in and audio playback for call dispatch

Creating two Gather prompts with BargeIn and embedding Play + Say is appropriate for improving input responsiveness during audio playback.


537-548:LGTM: Consistent Gather loop for non-audio branch

The repeated Gather prompts with BargeIn and explicit Hangup look correct and consistent with the audio branch.


638-647:LGTM: Main menu gathers properly configured

Two Gather prompts with BargeIn and an explicit Hangup afterwards are appropriate for the main menu.

Comment on lines +393 to 395
<dataname="PersonnelOnUnitSetUnitStatusHelp"xml:space="preserve">
<value />
</data>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add the missing label key for the new PersonnelOnUnitSetUnitStatus setting

UI relies on both the Label and Help resources; es.resx currently contains only the Help key. Add the Label key to keep parity and avoid fallback to the default language.

Apply this diff to add the missing label adjacent to the help entry:

   <data name="DeleteDepartmentSettingsHeader" xml:space="preserve">     <value />   </data>+  <data name="PersonnelOnUnitSetUnitStatusLabel" xml:space="preserve">+    <value />+  </data>   <data name="PersonnelOnUnitSetUnitStatusHelp" xml:space="preserve">     <value />   </data>

If you want, I can propose Spanish copy for both Label/Help once the final English strings are confirmed.

📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<dataname="PersonnelOnUnitSetUnitStatusHelp"xml:space="preserve">
<value />
</data>
<dataname="DeleteDepartmentSettingsHeader"xml:space="preserve">
<value />
</data>
<dataname="PersonnelOnUnitSetUnitStatusLabel"xml:space="preserve">
<value />
</data>
<dataname="PersonnelOnUnitSetUnitStatusHelp"xml:space="preserve">
<value />
</data>
🤖 Prompt for AI Agents
In Core/Resgrid.Localization/Areas/User/Department/Department.es.resx aroundlines 393 to 395, the resource file contains only the Help entry forPersonnelOnUnitSetUnitStatus; add a matching Label entry named"PersonnelOnUnitSetUnitStatus" immediately adjacent to the existing Help <data>block so the UI has both Label and Help keys (use an empty <value /> for now oradd the Spanish label text once confirmed).

Comment on lines +97 to +99
<divclass="col-md-6 text-center">
Made with <iclass="fa fa-heart text-danger"></i> in Lake Tahoe
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Localize the new footer sentence and make the heart icon accessible

The hardcoded string isn’t localized, and the icon lacks accessible text. Use the localizer and provide screen-reader text for “love,” while hiding the icon from AT.

Apply this diff:

-            <div>-                Made with <i></i> in Lake Tahoe-            </div>+            <div>+                @localizer["MadeWith"] <span>@localizer["Love"]</span><i aria-hidden="true"></i> @localizer["In"] Lake Tahoe+            </div>

Follow-up:

  • Add the resource keys “MadeWith”, “Love”, and “In” to the localization resource used by this view.
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<divclass="col-md-6 text-center">
Made with <iclass="fa fa-heart text-danger"></i>in Lake Tahoe
</div>
<divclass="col-md-6 text-center">
@localizer["MadeWith"] <spanclass="sr-only">@localizer["Love"]</span><iclass="fa fa-heart text-danger"aria-hidden="true"></i>@localizer["In"] Lake Tahoe
</div>
🤖 Prompt for AI Agents
In Web/Resgrid.Web/Views/Account/CompleteInvite.cshtml around lines 97-99, thefooter string is hardcoded and the heart icon lacks accessible text; replace theliteral sentence with localized fragments using the view's IViewLocalizer (e.g.,@Localizer["MadeWith"], @Localizer["In"]) and render the heart icon asdecorative (aria-hidden="true") with a screen-reader-only span containing thelocalized "Love" text (e.g., <span>@Localizer["Love"]</span>),and add the resource keys "MadeWith", "Love", and "In" to the localizationresource used by this view.

Copy link
Contributor

@coderabbitaicoderabbitaibot 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

🔭 Outside diff range comments (3)
Web/Resgrid.Web.Services/Models/v3/BigBoard/BigBoardModel.cs (1)

291-297:Guard against null roles to avoid NRE

roles can be null; wrap the loop to prevent a runtime exception.

Apply this diff:

-foreach (var role in roles)-{-if (newRoles.Length > 0)-newRoles.Append(", " + role.Name);-else-newRoles.Append(role.Name);-}+if (roles != null)+{+foreach (var role in roles)+{+if (newRoles.Length > 0)+newRoles.Append(", " + role.Name);+else+newRoles.Append(role.Name);+}+}
Web/Resgrid.Web/Views/Account/CompleteInvite.cshtml (2)

106-128:JavaScript selectors reference non-existent element IDs (form/button)

The script binds to#registrationForm and disables#registerButton on submit, but:

  • The<form> at Line 51 has no id.
  • The submit<button> at Line 80 has no id.

Result: no handler is attached; the disable-on-submit safeguard never runs.

Add stable IDs to match the selectors (or update the selectors). Suggested fix:

@@-                    <form role="form" asp-controller="Account" asp-action="CompleteInvite" method="post">+                    <form role="form" asp-controller="Account" asp-action="CompleteInvite" method="post">@@-                        <button type="submit">@localizer["Resgister"]</button>+                        <button type="submit">@localizer["Resgister"]</button>

If you adopt the typo fix above, also update the button label accordingly.


80-80:Fix typo in localization key “Resgister” → “Register”

Update resource entries and cshtml references:

  • Core/Resgrid.Localization/Account/Login.en.resx

    –  <data name="Resgister" xml:space="preserve">–    <value>Resgister</value>+  <data name="Register" xml:space="preserve">+    <value>Register</value>
  • Core/Resgrid.Localization/Account/Login.es.resx

    –  <data name="Resgister" xml:space="preserve">–    <value>Registrarse</value>+  <data name="Register" xml:space="preserve">+    <value>Registrarse</value>
  • Web/Resgrid.Web/Views/Account/Register.cshtml

    –<button type="submit">@localizer["Resgister"]</button>+<button type="submit">@localizer["Register"]</button>
  • Web/Resgrid.Web/Views/Account/CompleteInvite.cshtml

    –<button type="submit">@localizer["Resgister"]</button>+<button type="submit">@localizer["Register"]</button>
♻️ Duplicate comments (1)
Web/Resgrid.Web.Services/Models/v3/BigBoard/BigBoardModel.cs (1)

484-497:Add OnUnit to ActionTypes.ToCss() for consistency with new helper/state

Extend the local mapping so callers using CssExtensions also get the correct class for OnUnit.

Apply this diff:

 switch (type) { case ActionTypes.StandingBy: return ""; case ActionTypes.NotResponding: return "label-important"; case ActionTypes.Responding: return "label-success"; case ActionTypes.OnScene: return "label-inverse"; case ActionTypes.AvailableStation: return ""; case ActionTypes.RespondingToStation: return "label-success"; case ActionTypes.RespondingToScene: return "label-success";+case ActionTypes.OnUnit: return "label-success"; default: return ""; }
🧹 Nitpick comments (6)
Web/Resgrid.Web.Services/Models/v3/BigBoard/BigBoardModel.cs (2)

249-253:Consider capturing coordinates for OnUnit (if present)

Other “responding” states persist GeoLocationData to map pins; if OnUnit actions can include coordinates, mirror that behavior for parity.

Apply this diff if OnUnit may include GeoLocationData:

-            statusCss = actionLog.GetActionCss();-            status = actionLog.GetActionText();+            statusCss = actionLog.GetActionCss();+            status = actionLog.GetActionText();+            if (!String.IsNullOrWhiteSpace(actionLog.GeoLocationData))+            {+              var cords = actionLog.GetCoordinates();+              latitude = cords.Latitude;+              longitude = cords.Longitude;+            }

299-317:Set CallNumber on the returned view model (currently unused property)

CallNumber is a property but isn’t populated. If intended for UI, assign it during initialization.

Apply this diff:

-DestinationType = destinationType+DestinationType = destinationType,+CallNumber = callNumber
Web/Resgrid.Web.Services/Controllers/TwilioController.cs (4)

689-691:Unnecessary variable initialization

ThegatherResponse variable is initialized tonull on line 690 but is conditionally assigned later. Consider initializing it only where needed to improve clarity.

 StringBuilder sb = new StringBuilder();-Gather gatherResponse = null;

871-879:Consider consolidating error handling logic

The error handling for invalid digits and empty input could be consolidated since they both redirect to the same endpoint with similar messages.

-else-{-    response.Say("Invalid status selection, goodbye.").Redirect(new Uri($"{Config.SystemBehaviorConfig.ResgridApiBaseUrl}/api/Twilio/InboundVoice"), "GET");-}} else {-    response.Say("No status selection made, goodbye.").Redirect(new Uri($"{Config.SystemBehaviorConfig.ResgridApiBaseUrl}/api/Twilio/InboundVoice"), "GET");+    var message = String.IsNullOrWhiteSpace(twilioRequest.Digits)+        ? "No status selection made, goodbye."+        : "Invalid status selection, goodbye.";+    response.Say(message).Redirect(new Uri($"{Config.SystemBehaviorConfig.ResgridApiBaseUrl}/api/Twilio/InboundVoice"), "GET"); }

911-919:Apply same error handling consolidation

Similar to the status endpoint, the error handling here could be consolidated.

-else-{-    response.Say("Invalid staffing selection, goodbye.").Redirect(new Uri($"{Config.SystemBehaviorConfig.ResgridApiBaseUrl}/api/Twilio/InboundVoice"), "GET");-}} else {-    response.Say("No staffing selection made, goodbye.").Redirect(new Uri($"{Config.SystemBehaviorConfig.ResgridApiBaseUrl}/api/Twilio/InboundVoice"), "GET");+    var message = String.IsNullOrWhiteSpace(twilioRequest.Digits)+        ? "No staffing selection made, goodbye."+        : "Invalid staffing selection, goodbye.";+    response.Say(message).Redirect(new Uri($"{Config.SystemBehaviorConfig.ResgridApiBaseUrl}/api/Twilio/InboundVoice"), "GET"); }

930-936:Consider proper namespace for TwilioMessage class

TheTwilioMessage class is defined at the bottom of the controller file. Consider moving it to a separate file in the Models namespace for better organization and maintainability.

Would you like me to help create a separate model file for theTwilioMessage class to improve code organization?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and betweene4dca46 and97dd5e9.

📒 Files selected for processing (12)
  • Core/Resgrid.Localization/Areas/User/Department/Department.en.resx (2 hunks)
  • Core/Resgrid.Localization/Areas/User/Department/Department.es.resx (2 hunks)
  • Core/Resgrid.Localization/Areas/User/Department/Department.resx (1 hunks)
  • Providers/Resgrid.Providers.Bus.Rabbit/RabbitInboundQueueProvider.cs (2 hunks)
  • Web/Resgrid.Web.Services/Controllers/TwilioController.cs (5 hunks)
  • Web/Resgrid.Web.Services/Models/v3/BigBoard/BigBoardModel.cs (1 hunks)
  • Web/Resgrid.Web/Areas/User/Models/Home/BigBoardModel.cs (1 hunks)
  • Web/Resgrid.Web/Areas/User/Views/Department/DispatchSettings.cshtml (3 hunks)
  • Web/Resgrid.Web/Areas/User/Views/Department/ShiftSettings.cshtml (3 hunks)
  • Web/Resgrid.Web/Views/Account/CompleteInvite.cshtml (1 hunks)
  • Web/Resgrid.Web/Views/Account/LogOn.cshtml (1 hunks)
  • Web/Resgrid.Web/Views/Account/Register.cshtml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • Web/Resgrid.Web/Areas/User/Views/Department/ShiftSettings.cshtml
  • Web/Resgrid.Web/Areas/User/Models/Home/BigBoardModel.cs
  • Web/Resgrid.Web/Views/Account/LogOn.cshtml
  • Core/Resgrid.Localization/Areas/User/Department/Department.resx
  • Web/Resgrid.Web/Views/Account/Register.cshtml
  • Providers/Resgrid.Providers.Bus.Rabbit/RabbitInboundQueueProvider.cs
  • Web/Resgrid.Web/Areas/User/Views/Department/DispatchSettings.cshtml
  • Core/Resgrid.Localization/Areas/User/Department/Department.en.resx
🧰 Additional context used
🧬 Code Graph Analysis (2)
Web/Resgrid.Web.Services/Models/v3/BigBoard/BigBoardModel.cs (1)
Core/Resgrid.Model/ActionLog.cs (2)
  • GetActionCss (122-145)
  • GetActionText (94-120)
Web/Resgrid.Web.Services/Controllers/TwilioController.cs (1)
Core/Resgrid.Config/SystemBehaviorConfig.cs (1)
  • SystemBehaviorConfig (11-204)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (8)
Web/Resgrid.Web.Services/Models/v3/BigBoard/BigBoardModel.cs (1)

249-253:Good fix: OnUnit now uses centralized helpers

Using actionLog.GetActionCss() and GetActionText() keeps styling/text consistent across the app and aligns with recent model updates.

Web/Resgrid.Web/Views/Account/CompleteInvite.cshtml (1)

94-103:Three-column footer restructure reads well and improves balance

The new 3-column layout with left brand, centered message, and right © year is a clean improvement.

Web/Resgrid.Web.Services/Controllers/TwilioController.cs (6)

478-488:Good improvement to the Gather flow

The refactored approach with the loop creating two Gather blocks, each containing both the Say and Play elements, provides better user experience with BargeIn support and repeated prompts.


537-547:Consistent implementation of dual Gather prompts

The same pattern is correctly applied here for voice calls without attachments, maintaining consistency across the voice call flows.


638-648:Well-structured main menu implementation

The dual Gather implementation for the main menu with BargeIn enabled provides a better interactive experience, allowing users to interrupt and make selections immediately.


781-822:Status and staffing menu generation looks good

The dynamic menu generation for status (digit 6) and staffing (digit 7) options is well-implemented, correctly routing to the appropriate action endpoints.


849-887:Good error handling in status endpoint

The newInboundVoiceActionStatus endpoint properly validates input and provides appropriate feedback for invalid selections. The implementation correctly handles edge cases.


889-927:Consistent implementation for staffing endpoint

TheInboundVoiceActionStaffing endpoint follows the same pattern as the status endpoint with proper validation and error handling.

Comment on lines +123 to +134
<dataname="GroupDispatchSettingsHeader"xml:space="preserve">
<value />
</data>
<dataname="UnitDispatchSettingsHeader"xml:space="preserve">
<value />
</data>
<dataname="UnitDispatchAlsoDispatchToAssignedPersonnelLabel"xml:space="preserve">
<value />
</data>
<dataname="UnitDispatchAlsoDispatchToGroupLabel"xml:space="preserve">
<value />
</data>
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Empty Spanish values will render blank UI; remove to enable fallback or populate with text

Including keys with<value /> in a localized .resx overrides fallback to the neutral/English resource. These four entries will show as empty strings in the Spanish UI. Either remove them (so ResourceManager falls back to English) or populate with text (Spanish or English placeholders).

Also, the PR summary claims these entries were later removed in this file, but they are still present here. Please reconcile to avoid drift.

Option A — remove to enable fallback:

-  <data name="GroupDispatchSettingsHeader" xml:space="preserve">-    <value />-  </data>-  <data name="UnitDispatchSettingsHeader" xml:space="preserve">-    <value />-  </data>-  <data name="UnitDispatchAlsoDispatchToAssignedPersonnelLabel" xml:space="preserve">-    <value />-  </data>-  <data name="UnitDispatchAlsoDispatchToGroupLabel" xml:space="preserve">-    <value />-  </data>

Option B — populate with English placeholders to avoid blanks (until Spanish translations are ready):

   <data name="GroupDispatchSettingsHeader" xml:space="preserve">-    <value />+    <value>Group Dispatch Settings</value>   </data>   <data name="UnitDispatchSettingsHeader" xml:space="preserve">-    <value />+    <value>Unit Dispatch Settings</value>   </data>   <data name="UnitDispatchAlsoDispatchToAssignedPersonnelLabel" xml:space="preserve">-    <value />+    <value>Also dispatch to assigned personnel</value>   </data>   <data name="UnitDispatchAlsoDispatchToGroupLabel" xml:space="preserve">-    <value />+    <value>Also dispatch to group</value>   </data>

🏁 Script executed:

#!/bin/bashgrep -n -E"GroupDispatchSettingsHeader|UnitDispatchSettingsHeader|UnitDispatchAlsoDispatchToAssignedPersonnelLabel|UnitDispatchAlsoDispatchToGroupLabel" Core/Resgrid.Localization/Areas/User/Department/Department.es.resx||echo"No matches found"

Length of output: 527


Empty Spanish entries in Department.es.resx will render blank UI—please remove or populate

The following keys inCore/Resgrid.Localization/Areas/User/Department/Department.es.resx still have empty<value/>, which overrides fallback to English and leads to blank labels in the Spanish UI. Additionally, the PR summary states these were removed, but they remain in this file. Please reconcile and then either:

• Remove them so ResourceManager falls back to English
• Populate them with placeholder text (Spanish or English) until translations are available

Affected locations:

  • Department.es.resx: lines 123, 126, 129, 132

Option A — remove entries to enable fallback:

-  <data name="GroupDispatchSettingsHeader" xml:space="preserve">-    <value />-  </data>-  <data name="UnitDispatchSettingsHeader" xml:space="preserve">-    <value />-  </data>-  <data name="UnitDispatchAlsoDispatchToAssignedPersonnelLabel" xml:space="preserve">-    <value />-  </data>-  <data name="UnitDispatchAlsoDispatchToGroupLabel" xml:space="preserve">-    <value />-  </data>

Option B — add English placeholders until Spanish copy is ready:

   <data name="GroupDispatchSettingsHeader" xml:space="preserve">-    <value />+    <value>Group Dispatch Settings</value>   </data>   <data name="UnitDispatchSettingsHeader" xml:space="preserve">-    <value />+    <value>Unit Dispatch Settings</value>   </data>   <data name="UnitDispatchAlsoDispatchToAssignedPersonnelLabel" xml:space="preserve">-    <value />+    <value>Also dispatch to assigned personnel</value>   </data>   <data name="UnitDispatchAlsoDispatchToGroupLabel" xml:space="preserve">-    <value />+    <value>Also dispatch to group</value>   </data>
📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<dataname="GroupDispatchSettingsHeader"xml:space="preserve">
<value />
</data>
<dataname="UnitDispatchSettingsHeader"xml:space="preserve">
<value />
</data>
<dataname="UnitDispatchAlsoDispatchToAssignedPersonnelLabel"xml:space="preserve">
<value />
</data>
<dataname="UnitDispatchAlsoDispatchToGroupLabel"xml:space="preserve">
<value />
</data>
<dataname="GroupDispatchSettingsHeader"xml:space="preserve">
<value>Group Dispatch Settings</value>
</data>
<dataname="UnitDispatchSettingsHeader"xml:space="preserve">
<value>Unit Dispatch Settings</value>
</data>
<dataname="UnitDispatchAlsoDispatchToAssignedPersonnelLabel"xml:space="preserve">
<value>Also dispatch to assigned personnel</value>
</data>
<dataname="UnitDispatchAlsoDispatchToGroupLabel"xml:space="preserve">
<value>Also dispatch to group</value>
</data>
🤖 Prompt for AI Agents
In Core/Resgrid.Localization/Areas/User/Department/Department.es.resx aroundlines 123-134 the keys GroupDispatchSettingsHeader, UnitDispatchSettingsHeader,UnitDispatchAlsoDispatchToAssignedPersonnelLabel andUnitDispatchAlsoDispatchToGroupLabel have empty <value/> entries which overridefallback and produce blank UI; either remove these <data> entries so theResourceManager falls back to the default English resources, or populate each<value> with a temporary placeholder (e.g., English text) until proper Spanishtranslations are available, and then commit the file reflecting the chosenoption.

Comment on lines +393 to 395
<dataname="PersonnelOnUnitSetUnitStatusHelp"xml:space="preserve">
<value />
</data>
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

“PersonnelOnUnitSetUnitStatusHelp” is empty; will display as blank in Spanish

Same issue: an empty localized value prevents fallback to neutral/English. Either remove this entry to inherit the English string or populate it.

Option A — remove to enable fallback:

-  <data name="PersonnelOnUnitSetUnitStatusHelp" xml:space="preserve">-    <value />-  </data>

Option B — populate with the English help text (from en.resx) for now:

   <data name="PersonnelOnUnitSetUnitStatusHelp" xml:space="preserve">-    <value />+    <value>When a unit submits a status and users are setup on roles, the users status will be set to On Unit.</value>   </data>

If you prefer, I can also add the Spanish translation in a follow-up, or add a<comment> marker to flag it for translators.


🏁 Script executed:

#!/bin/bash# Search for the key across all .resx files to verify English fallback valuerg -n'PersonnelOnUnitSetUnitStatusHelp' -n --glob'*.resx'

Length of output: 479


🏁 Script executed:

#!/bin/bash# Extract the English help text for the key from Department.en.resxrg -A2 -n'PersonnelOnUnitSetUnitStatusHelp' --glob'*.resx' Core/Resgrid.Localization/Areas/User/Department/Department.en.resx

Length of output: 338


Empty Spanish localization entry prevents fallback
The<value /> in Department.es.resx will return an empty string instead of falling back to the default (English) text. Please either remove the Spanish entry or supply at least a placeholder so users don’t see a blank field.

• File: Core/Resgrid.Localization/Areas/User/Department/Department.es.resx (lines 393–395)
• Reference English text in Core/Resgrid.Localization/Areas/User/Department/Department.en.resx (line 508)

Option A — remove the empty entry to enable fallback:

-  <data name="PersonnelOnUnitSetUnitStatusHelp" xml:space="preserve">-    <value />-  </data>

Option B — add the English text as a temporary placeholder:

   <data name="PersonnelOnUnitSetUnitStatusHelp" xml:space="preserve">-    <value />+    <value>When a unit submits a status and users are setup on roles, the users status will be set to On Unit.</value>   </data>

If you prefer, add a<comment> marker or follow up with the actual Spanish translation later.

📝 Committable suggestion

‼️IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<dataname="PersonnelOnUnitSetUnitStatusHelp"xml:space="preserve">
<value />
</data>
Suggested change
<dataname="PersonnelOnUnitSetUnitStatusHelp"xml:space="preserve">
<value />
</data>
<dataname="PersonnelOnUnitSetUnitStatusHelp"xml:space="preserve">
<value>When a unit submits a status and users are setup on roles, the users status will be set to On Unit.</value>
</data>
🤖 Prompt for AI Agents
In Core/Resgrid.Localization/Areas/User/Department/Department.es.resx aroundlines 393–395 the PersonnelOnUnitSetUnitStatusHelp entry has an empty <value/>,which returns an empty string instead of falling back to English; fix by eitherremoving this Spanish <data> element so the resource system falls back toCore/Resgrid.Localization/Areas/User/Department/Department.en.resx (see line 508for the English text), or populate the <value> with a placeholder (e.g., theEnglish text or a <comment> marker) until a proper Spanish translation isavailable.

Comment on lines +97 to +99
<divclass="col-md-6 text-center">
Made with <iclass="fa fa-heart text-danger"></i> in Lake Tahoe
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Localize and make the “Made with ♥” message accessible

  • The phrase is hardcoded and not localized, unlike the rest of the page.
  • Screen readers won’t announce “love” because the heart is an icon; they’ll read “Made with in Lake Tahoe”.

Consider localizing the text and ensuring SR users hear a full sentence. Minimal change below keeps the icon decorative and adds SR-only “love”:

-            <div>-                Made with <i></i> in Lake Tahoe-            </div>+            <div>+                @localizer["MadeWith"] <i aria-hidden="true"></i><span>@localizer["Love"]</span> @localizer["In"] Lake Tahoe+            </div>

Alternatively, prefer a single localized string so translators control word order: e.g.,@localizer["MadeWithLoveIn", "Lake Tahoe"] and keep the icon purely decorative after/before the text.

To verify whether the localization keys already exist (or where to add them), run:


🏁 Script executed:

#!/bin/bash# Search for existing localization keys or similar phrases across the reporg -n --hidden --glob'!**/bin/**' --glob'!**/obj/**'$'MadeWith|Love|In|MadeWithLoveIn|Made with|Lake Tahoe'# Inspect localization resources (resx/json)fd -a -e resx -e json| xargs -I{} sh -c'echo "### {}"; rg -n $'\''MadeWith|Love|In|MadeWithLoveIn|Lake Tahoe'\'' "{}" || true'

Length of output: 281011


Add new localization entries and update for accessibility

No existing resource keys (MadeWith, Love, In, MadeWithLoveIn, “Lake Tahoe”) were found in any .resx/.json files, so you’ll need to add them. Then update the view to use localized text and expose “love” to screen readers:

• Create localization entries (CommonResources.resx or similar):
– Key=”MadeWith” Value=”Made with”
– Key=”Love” Value=”love”
– Key=”In” Value=”in”
– (Or a combined key) Key=”MadeWithLoveIn” Value=”Made with love in {0}”

• Update Web/Resgrid.Web/Views/Account/CompleteInvite.cshtml (around line 97):

-            <div>-                Made with <i></i> in Lake Tahoe-            </div>+            <div>+                @localizer["MadeWith"] <i aria-hidden="true"></i><span>@localizer["Love"]</span> @localizer["In"] Lake Tahoe+            </div>

• Alternatively, if you add a single key (MadeWithLoveIn), you can write:

@localizer["MadeWithLoveIn","Lake Tahoe"]<i class="fa fa-heart text-danger"aria-hidden="true"></i>
🤖 Prompt for AI Agents
In Web/Resgrid.Web/Views/Account/CompleteInvite.cshtml around lines 97-99, thestatic footer text "Made with <heart> in Lake Tahoe" lacks localization andaccessibility for the heart icon; add resource entries (e.g.,CommonResources.resx or equivalent) for MadeWith ("Made with"), Love ("love"),In ("in") or a single composite key MadeWithLoveIn ("Made with love in {0}") andupdate the view to use the localizer to render the localized string with "LakeTahoe" as the parameter, render the heart icon with aria-hidden="true" andinclude the word "love" for screen readers (e.g., a visually hidden span oraria-label) so the phrase is localized and accessible.

@ucswift
Copy link
MemberAuthor

Approve

Copy link

@github-actionsgithub-actionsbot 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.

@ucswiftucswift merged commit3fac2b4 intomasterAug 12, 2025
4 of 5 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@coderabbitaicoderabbitai[bot]coderabbitai[bot] left review comments

@github-actionsgithub-actions[bot]github-actions[bot] approved these changes

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

2 participants

@ucswift

[8]ページ先頭

©2009-2025 Movatter.jp