Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Core/Resgrid.Model/CommandBoards/CommandBoardTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ public CommandDefinition CreateDefinition(IEnumerable<UnitType> unitTypes, IEnum
MaxUnitPersonnel = lane.MaxUnitPersonnel,
MinTimeInRole = lane.MinTimeInRole,
MaxTimeInRole = lane.MaxTimeInRole,
WorkTimeAmberMinutes = lane.WorkTimeAmberMinutes,
WorkTimeRedMinutes = lane.WorkTimeRedMinutes,
RequiredUnitTypes = matchedUnitTypes,
RequiredRoles = matchedPersonnelRoles,
ForceRequirements = lane.ForceRequirements && (matchedUnitTypes.Count > 0 || matchedPersonnelRoles.Count > 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public static IReadOnlyList<CommandBoardTemplate> Search(string query)

private static CommandBoardTemplateLane L(string name, CommandNodeType laneType, string description,
string[] unitTypes = null, string[] personnelRoles = null, bool forceRequirements = false,
int minUnits = 0, int maxUnits = 0, int minUnitPersonnel = 0, int maxUnitPersonnel = 0, int minTimeInRole = 0, int maxTimeInRole = 0)
int minUnits = 0, int maxUnits = 0, int minUnitPersonnel = 0, int maxUnitPersonnel = 0, int minTimeInRole = 0, int maxTimeInRole = 0,
int workTimeAmberMinutes = 20, int workTimeRedMinutes = 40)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

Uninitialized property WorkTimeAmberMinutes in CommandModels.cs lacks an explicit default initializer, leaving the object in an ambiguous state per Rule [32]. Add an explicit initializer such as = 0 or set it in the constructor.

Kody rule violation: Replace magic numbers with named constants

Prompt for LLM

File Core/Resgrid.Model/CommandBoards/CommandBoardTemplateCatalog.cs:

Line 45:

Uninitialized property WorkTimeAmberMinutes in CommandModels.cs lacks an explicit default initializer, leaving the object in an ambiguous state per Rule [32]. Add an explicit initializer such as = 0 or set it in the constructor.

Talk to Kody by mentioning @kody

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

{
return new CommandBoardTemplateLane
{
Expand All @@ -56,7 +57,9 @@ private static CommandBoardTemplateLane L(string name, CommandNodeType laneType,
MinUnitPersonnel = minUnitPersonnel,
MaxUnitPersonnel = maxUnitPersonnel,
MinTimeInRole = minTimeInRole,
MaxTimeInRole = maxTimeInRole
MaxTimeInRole = maxTimeInRole,
WorkTimeAmberMinutes = workTimeAmberMinutes,
WorkTimeRedMinutes = workTimeRedMinutes
};
}

Expand Down
6 changes: 6 additions & 0 deletions Core/Resgrid.Model/CommandBoards/CommandBoardTemplateLane.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,11 @@ public class CommandBoardTemplateLane

/// <summary>Minutes before an assigned resource shows rotation-due (0 = none).</summary>
public int MaxTimeInRole { get; set; }

/// <summary>Minutes before the lane work-time indicator turns amber (0 = disabled; default 20).</summary>
public int WorkTimeAmberMinutes { get; set; } = 20;

/// <summary>Minutes before the lane work-time indicator turns red (0 = disabled; default 40).</summary>
public int WorkTimeRedMinutes { get; set; } = 40;
}
}
6 changes: 6 additions & 0 deletions Core/Resgrid.Model/CommandDefinitionRole.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ public class CommandDefinitionRole : IEntity

public int MaxTimeInRole { get; set; }

/// <summary>Minutes on the lane before the work-time indicator turns amber (0 = disabled; seeded onto runtime nodes).</summary>
public int WorkTimeAmberMinutes { get; set; }

/// <summary>Minutes on the lane before the work-time indicator turns red (0 = disabled; seeded onto runtime nodes).</summary>
public int WorkTimeRedMinutes { get; set; }

public bool ForceRequirements { get; set; }

/// <summary>
Expand Down
6 changes: 6 additions & 0 deletions Core/Resgrid.Model/IncidentCommand/CommandStructureNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ public class CommandStructureNode : IEntity, IChangeTracked
/// <summary>Maximum minutes a resource should work this lane before rotation (0 = none; surfaced as rotation-due).</summary>
public int MaxTimeInRole { get; set; }

/// <summary>Minutes on this lane before the work-time indicator turns amber (0 = disabled; client defaults to 20).</summary>
public int WorkTimeAmberMinutes { get; set; }

/// <summary>Minutes on this lane before the work-time indicator turns red (0 = disabled; client defaults to 40).</summary>
public int WorkTimeRedMinutes { get; set; }

/// <summary>When true, unmet lane requirements block assignment instead of warning. Seeded from the template role.</summary>
public bool ForceRequirements { get; set; }

Expand Down
2 changes: 2 additions & 0 deletions Core/Resgrid.Services/IncidentCommandService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ public IncidentCommandService(
MaxUnits = role.MaxUnits,
MinTimeInRole = role.MinTimeInRole,
MaxTimeInRole = role.MaxTimeInRole,
WorkTimeAmberMinutes = role.WorkTimeAmberMinutes,
WorkTimeRedMinutes = role.WorkTimeRedMinutes,
ForceRequirements = role.ForceRequirements
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using FluentMigrator;

namespace Resgrid.Providers.Migrations.Migrations
{
/// <summary>
/// Per-lane work-time (crew fatigue) indicator thresholds: minutes before the lane work light
/// turns amber/red (0 = disabled; clients fall back to their own defaults). Denormalized onto
/// runtime nodes from the template role at seeding, like the other lane limits.
/// </summary>
[Migration(103)]
public class M0103_AddLaneWorkTimeThresholds : Migration
{
public override void Up()
{
if (Schema.Table("CommandStructureNodes").Exists())
{
foreach (var column in new[] { "WorkTimeAmberMinutes", "WorkTimeRedMinutes" })

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

WHAT: The column-name string literals are defined inline and will be repeated four times in this file. WHY: Repeating raw string literals for database identifiers risks typos and makes future renames error-prone. HOW: Extract a private static readonly string[] WorkTimeColumns = { "WorkTimeAmberMinutes", "WorkTimeRedMinutes" }; at class level and reference it in every loop.

Also found in:

  • Providers/Resgrid.Providers.Migrations/Migrations/M0103_AddLaneWorkTimeThresholds.cs:29-29
  • Providers/Resgrid.Providers.Migrations/Migrations/M0103_AddLaneWorkTimeThresholds.cs:44-44
  • Providers/Resgrid.Providers.Migrations/Migrations/M0103_AddLaneWorkTimeThresholds.cs:53-53

Kody rule violation: Centralize string constants

Prompt for LLM

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

Line 17:

WHAT: The column-name string literals are defined inline and will be repeated four times in this file. WHY: Repeating raw string literals for database identifiers risks typos and makes future renames error-prone. HOW: Extract a private static readonly string[] WorkTimeColumns = { "WorkTimeAmberMinutes", "WorkTimeRedMinutes" }; at class level and reference it in every loop.

Talk to Kody by mentioning @kody

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

{
if (!Schema.Table("CommandStructureNodes").Column(column).Exists())
{
Alter.Table("CommandStructureNodes")
.AddColumn(column).AsInt32().NotNullable().WithDefaultValue(0);
}
}
}

if (Schema.Table("CommandDefinitionRoles").Exists())
{
foreach (var column in new[] { "WorkTimeAmberMinutes", "WorkTimeRedMinutes" })
{
if (!Schema.Table("CommandDefinitionRoles").Column(column).Exists())
{
Alter.Table("CommandDefinitionRoles")
.AddColumn(column).AsInt32().NotNullable().WithDefaultValue(0);
}
}
}
}

public override void Down()
{
if (Schema.Table("CommandStructureNodes").Exists())
{
foreach (var column in new[] { "WorkTimeAmberMinutes", "WorkTimeRedMinutes" })
{
if (Schema.Table("CommandStructureNodes").Column(column).Exists())
Delete.Column(column).FromTable("CommandStructureNodes");
}
}

if (Schema.Table("CommandDefinitionRoles").Exists())
{
foreach (var column in new[] { "WorkTimeAmberMinutes", "WorkTimeRedMinutes" })
{
if (Schema.Table("CommandDefinitionRoles").Column(column).Exists())
Delete.Column(column).FromTable("CommandDefinitionRoles");
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using FluentMigrator;

namespace Resgrid.Providers.MigrationsPg.Migrations
{
/// <summary>
/// Per-lane work-time (crew fatigue) indicator thresholds: minutes before the lane work light
/// turns amber/red (0 = disabled; clients fall back to their own defaults). Denormalized onto
/// runtime nodes from the template role at seeding, like the other lane limits.
/// </summary>
[Migration(103)]
public class M0103_AddLaneWorkTimeThresholdsPg : Migration
{
public override void Up()
{
if (Schema.Table("commandstructurenodes").Exists())
{
foreach (var column in new[] { "worktimeamberminutes", "worktimeredminutes" })

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

WHAT: The column-name array literal new[] { "worktimeamberminutes", "worktimeredminutes" } is repeated on lines 17, 29, 44, and 53. WHY: Duplicated literals risk divergence if a column name is renamed or added. HOW: Declare a private static readonly string[] WorkTimeColumns at class level and reference it in all four loops; additionally, factor the repeated Alter/Table-check and Delete/Table-check blocks into helper methods.

Also found in:

  • Providers/Resgrid.Providers.Migrations/Migrations/M0103_AddLaneWorkTimeThresholds.cs:29-29
  • Providers/Resgrid.Providers.Migrations/Migrations/M0103_AddLaneWorkTimeThresholds.cs:53-53
  • Providers/Resgrid.Providers.MigrationsPg/Migrations/M0103_AddLaneWorkTimeThresholdsPg.cs:29-29
  • Providers/Resgrid.Providers.MigrationsPg/Migrations/M0103_AddLaneWorkTimeThresholdsPg.cs:53-53
  • Providers/Resgrid.Providers.MigrationsPg/Migrations/M0103_AddLaneWorkTimeThresholdsPg.cs:44-44

Kody rule violation: Extract duplicated logic into functions

Prompt for LLM

File Providers/Resgrid.Providers.MigrationsPg/Migrations/M0103_AddLaneWorkTimeThresholdsPg.cs:

Line 17:

WHAT: The column-name array literal new[] { "worktimeamberminutes", "worktimeredminutes" } is repeated on lines 17, 29, 44, and 53. WHY: Duplicated literals risk divergence if a column name is renamed or added. HOW: Declare a private static readonly string[] WorkTimeColumns at class level and reference it in all four loops; additionally, factor the repeated Alter/Table-check and Delete/Table-check blocks into helper methods.

Talk to Kody by mentioning @kody

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

{
if (!Schema.Table("commandstructurenodes").Column(column).Exists())
{
Alter.Table("commandstructurenodes")
.AddColumn(column).AsInt32().NotNullable().WithDefaultValue(0);
}
}
}

if (Schema.Table("commanddefinitionroles").Exists())
{
foreach (var column in new[] { "worktimeamberminutes", "worktimeredminutes" })
{
if (!Schema.Table("commanddefinitionroles").Column(column).Exists())
{
Alter.Table("commanddefinitionroles")
.AddColumn(column).AsInt32().NotNullable().WithDefaultValue(0);
}
}
}
}

public override void Down()
{
if (Schema.Table("commandstructurenodes").Exists())
{
foreach (var column in new[] { "worktimeamberminutes", "worktimeredminutes" })
{
if (Schema.Table("commandstructurenodes").Column(column).Exists())
Delete.Column(column).FromTable("commandstructurenodes");
}
}

if (Schema.Table("commanddefinitionroles").Exists())
{
foreach (var column in new[] { "worktimeamberminutes", "worktimeredminutes" })
{
if (Schema.Table("commanddefinitionroles").Column(column).Exists())
Delete.Column(column).FromTable("commanddefinitionroles");
}
}
}
}
}
4 changes: 4 additions & 0 deletions Web/Resgrid.Web.Services/Controllers/v4/CommandsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ public async Task<ActionResult<CommandResult>> SaveCommand([FromBody] SaveComman
MaxUnits = lane.MaxUnits,
MinTimeInRole = lane.MinTimeInRole,
MaxTimeInRole = lane.MaxTimeInRole,
WorkTimeAmberMinutes = lane.WorkTimeAmberMinutes,
WorkTimeRedMinutes = lane.WorkTimeRedMinutes,
ForceRequirements = lane.ForceRequirements
};

Expand Down Expand Up @@ -253,6 +255,8 @@ private static CommandResultData ConvertCommandData(CommandDefinition command)
MaxUnits = lane.MaxUnits,
MinTimeInRole = lane.MinTimeInRole,
MaxTimeInRole = lane.MaxTimeInRole,
WorkTimeAmberMinutes = lane.WorkTimeAmberMinutes,
WorkTimeRedMinutes = lane.WorkTimeRedMinutes,
ForceRequirements = lane.ForceRequirements,
RequiredUnitTypes = lane.RequiredUnitTypes?.Select(x => x.UnitTypeId).ToList() ?? new List<int>(),
RequiredPersonnelRoles = lane.RequiredRoles?.Select(x => x.PersonnelRoleId).ToList() ?? new List<int>()
Expand Down
4 changes: 4 additions & 0 deletions Web/Resgrid.Web.Services/Models/v4/Commands/CommandModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public class CommandRoleResultData
public int MaxUnits { get; set; }
public int MinTimeInRole { get; set; }
public int MaxTimeInRole { get; set; }
public int WorkTimeAmberMinutes { get; set; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules low

WHAT: The new int property WorkTimeAmberMinutes has no explicit default initializer. WHY: Rule [32] requires properties to have sensible defaults or be initialized via constructors so the object is never in an ambiguous state. Although int defaults to 0 in C#, making the default explicit communicates intent (e.g., = 0 or a meaningful value). HOW: Add an explicit initializer such as public int WorkTimeAmberMinutes { get; set; } = 0; or set it in the constructor.

Also found in:

  • Web/Resgrid.Web.Services/Models/v4/Commands/CommandModels.cs:50-50
  • Web/Resgrid.Web.Services/Models/v4/Commands/CommandModels.cs:115-115
  • Web/Resgrid.Web.Services/Models/v4/Commands/CommandModels.cs:116-116

Kody rule violation: Initialize properties with default values

Prompt for LLM

File Web/Resgrid.Web.Services/Models/v4/Commands/CommandModels.cs:

Line 49:

WHAT: The new int property `WorkTimeAmberMinutes` has no explicit default initializer. WHY: Rule [32] requires properties to have sensible defaults or be initialized via constructors so the object is never in an ambiguous state. Although `int` defaults to `0` in C#, making the default explicit communicates intent (e.g., `= 0` or a meaningful value). HOW: Add an explicit initializer such as `public int WorkTimeAmberMinutes { get; set; } = 0;` or set it in the constructor.

Talk to Kody by mentioning @kody

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

public int WorkTimeRedMinutes { get; set; }

/// <summary>Lane identification color (hex, e.g. "#e74c3c"); null = default styling.</summary>
public string Color { get; set; }
Expand Down Expand Up @@ -110,6 +112,8 @@ public class SaveCommandLaneInput
public int MaxUnits { get; set; }
public int MinTimeInRole { get; set; }
public int MaxTimeInRole { get; set; }
public int WorkTimeAmberMinutes { get; set; }
public int WorkTimeRedMinutes { get; set; }
Comment on lines +115 to +116

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve omitted thresholds during updates.

Because these request fields are non-nullable, callers that omit the new JSON properties receive 0, and SaveCommand persists that value over existing thresholds. Since 0 disables the indicator, older or partial v4 clients can silently disable configured lane thresholds. Use nullable input fields and preserve stored values when they are omitted; reserve explicit 0 for disabling.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Web/Resgrid.Web.Services/Models/v4/Commands/CommandModels.cs` around lines
115 - 116, Update WorkTimeAmberMinutes and WorkTimeRedMinutes in the command
request model to nullable inputs, then adjust SaveCommand to retain each stored
threshold when its corresponding value is omitted while still persisting an
explicitly supplied 0 as the disable value.


/// <summary>Lane identification color (hex, e.g. "#e74c3c"); null = default styling.</summary>
public string Color { get; set; }
Expand Down
Loading