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
52 changes: 52 additions & 0 deletions Core/Resgrid.Model/Events/IncidentCommandEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,56 @@ public class IncidentPublicSharingChangedEvent
public bool Enabled { get; set; }
public string UpdatedByUserId { get; set; }
}

/// <summary>Raised when a resource assignment is moved between lanes.</summary>
public class IncidentResourceMovedEvent
{
public int DepartmentId { get; set; }
public int CallId { get; set; }
public string IncidentCommandId { get; set; }
public string ResourceAssignmentId { get; set; }
public int ResourceKind { get; set; }
public string ResourceId { get; set; }
public string FromNodeId { get; set; }
public string ToNodeId { get; set; }
}

/// <summary>Raised when a lane's primary or secondary lead changes.</summary>
public class LaneLeadChangedEvent
{
public int DepartmentId { get; set; }
public int CallId { get; set; }
public string IncidentCommandId { get; set; }
public string CommandStructureNodeId { get; set; }
public string LaneName { get; set; }
public bool IsPrimary { get; set; }
/// <summary>User id of the lead going off (null when the outgoing lead was external or the slot was empty).</summary>
public string PreviousLeadUserId { get; set; }
/// <summary>Display name of the lead going off (external leads; null when the slot was empty).</summary>
public string PreviousLeadName { get; set; }
/// <summary>User id of the lead coming on (null when the incoming lead is external or the slot was cleared).</summary>
public string NewLeadUserId { get; set; }
/// <summary>Display name of the lead coming on (external leads; null when the slot was cleared).</summary>
public string NewLeadName { get; set; }
}

/// <summary>Raised when an incident need is created or its status/fulfillment changes.</summary>
public class IncidentNeedChangedEvent
{
public int DepartmentId { get; set; }
public int CallId { get; set; }
public string IncidentCommandId { get; set; }
public string IncidentNeedId { get; set; }
public string Name { get; set; }
public int Status { get; set; }
}

/// <summary>Raised when command details (estimated end / important information) are updated.</summary>
public class IncidentCommandDetailsUpdatedEvent
{
public int DepartmentId { get; set; }
public int CallId { get; set; }
public string IncidentCommandId { get; set; }
public string UpdatedByUserId { get; set; }
}
}
33 changes: 33 additions & 0 deletions Core/Resgrid.Model/IncidentCommand/CommandStructureNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,39 @@ public class CommandStructureNode : IEntity, IChangeTracked
/// <summary>When true, unmet lane requirements block assignment instead of warning. Seeded from the template role.</summary>
public bool ForceRequirements { get; set; }

/// <summary>Optional primary tactical objective this lane is working (FK to TacticalObjectives).</summary>
public string PrimaryObjectiveId { get; set; }

/// <summary>Optional secondary tactical objective this lane is working (FK to TacticalObjectives).</summary>
public string SecondaryObjectiveId { get; set; }

/// <summary>Optional incident need this lane is fulfilling (FK to IncidentNeeds).</summary>
public string LinkedNeedId { get; set; }

/// <summary>Primary lane lead when they are a Resgrid user; null for external leads.</summary>
public string PrimaryLeadUserId { get; set; }

/// <summary>Primary lane lead display name (external leads; ignored when PrimaryLeadUserId is set).</summary>
public string PrimaryLeadName { get; set; }

/// <summary>Primary lane lead contact phone (external leads).</summary>
public string PrimaryLeadPhone { get; set; }

/// <summary>Primary lane lead contact email (external leads).</summary>
public string PrimaryLeadEmail { get; set; }

/// <summary>Secondary lane lead when they are a Resgrid user; null for external leads.</summary>
public string SecondaryLeadUserId { get; set; }

/// <summary>Secondary lane lead display name (external leads; ignored when SecondaryLeadUserId is set).</summary>
public string SecondaryLeadName { get; set; }

/// <summary>Secondary lane lead contact phone (external leads).</summary>
public string SecondaryLeadPhone { get; set; }

/// <summary>Secondary lane lead contact email (external leads).</summary>
public string SecondaryLeadEmail { get; set; }

/// <summary>Soft-delete tombstone so a lane removed offline propagates on delta sync (null = live).</summary>
public DateTime? DeletedOn { get; set; }

Expand Down
6 changes: 6 additions & 0 deletions Core/Resgrid.Model/IncidentCommand/IncidentCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ public class IncidentCommand : IEntity, IChangeTracked
/// <summary>NIMS/ICS escalation level for the incident (department defined).</summary>
public int IcsLevel { get; set; }

/// <summary>Optional commander-supplied estimate of when the incident will end.</summary>
public DateTime? EstimatedEndOn { get; set; }

/// <summary>Important information every resource on the incident should see (hazards, staging notes, ...).</summary>
public string ImportantInformation { get; set; }

/// <summary>Maps to <see cref="IncidentCommandStatus"/>.</summary>
public int Status { get; set; }

Expand Down
3 changes: 3 additions & 0 deletions Core/Resgrid.Model/IncidentCommand/IncidentCommandBoard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ public class IncidentCommandBoard

public List<TacticalObjective> Objectives { get; set; } = new List<TacticalObjective>();

/// <summary>Command-level needs (resources/logistics/etc.) tracked to fulfillment.</summary>
public List<IncidentNeed> Needs { get; set; } = new List<IncidentNeed>();

public List<IncidentTimer> Timers { get; set; } = new List<IncidentTimer>();

public List<IncidentMapAnnotation> Annotations { get; set; } = new List<IncidentMapAnnotation>();
Expand Down
2 changes: 2 additions & 0 deletions Core/Resgrid.Model/IncidentCommand/IncidentCommandChanges.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public class IncidentCommandChanges

public List<TacticalObjective> Objectives { get; set; } = new List<TacticalObjective>();

public List<IncidentNeed> Needs { get; set; } = new List<IncidentNeed>();

public List<IncidentTimer> Timers { get; set; } = new List<IncidentTimer>();

public List<IncidentMapAnnotation> Annotations { get; set; } = new List<IncidentMapAnnotation>();
Expand Down
31 changes: 29 additions & 2 deletions Core/Resgrid.Model/IncidentCommand/IncidentCommandEnums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,28 @@ public enum TacticalObjectiveType
public enum TacticalObjectiveStatus
{
Pending = 0,
Complete = 1
Complete = 1,
InProgress = 2
}

/// <summary>Category of an incident need (resource/logistics request tracked at the command level).</summary>
public enum IncidentNeedCategory
{
Resource = 0,
Logistics = 1,
Medical = 2,
Equipment = 3,
Staffing = 4,
Other = 5
}

/// <summary>Fulfillment state of an incident need.</summary>
public enum IncidentNeedStatus
{
Open = 0,
PartiallyMet = 1,
Met = 2,
Cancelled = 3
}

/// <summary>Type of incident timer (personnel PAR is handled by the Checkin feature, not these).</summary>
Expand Down Expand Up @@ -121,6 +142,12 @@ public enum CommandLogEntryType
ActionPlanUpdated = 27,
CommandPostUpdated = 28,
PublicSharingEnabled = 29,
PublicSharingDisabled = 30
PublicSharingDisabled = 30,
NeedAdded = 31,
NeedUpdated = 32,
NeedMet = 33,
ObjectiveProgressUpdated = 34,
LaneLeadChanged = 35,
CommandDetailsUpdated = 36
}
}
77 changes: 77 additions & 0 deletions Core/Resgrid.Model/IncidentCommand/IncidentNeed.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using Newtonsoft.Json;

namespace Resgrid.Model
{
/// <summary>
/// A command-level need for an incident (resources, logistics, medical support, ...) tracked to
/// fulfillment. Lanes can optionally be tied to a need via <see cref="CommandStructureNode.LinkedNeedId"/>.
/// </summary>
public class IncidentNeed : IEntity, IChangeTracked
{
public string IncidentNeedId { get; set; }

public string IncidentCommandId { get; set; }

public int DepartmentId { get; set; }

public int CallId { get; set; }

public string Name { get; set; }

/// <summary>Optional free-text detail (what exactly is needed, where, by when).</summary>
public string Description { get; set; }

/// <summary>Maps to <see cref="IncidentNeedCategory"/>.</summary>
public int Category { get; set; }

/// <summary>Maps to <see cref="IncidentNeedStatus"/>.</summary>
public int Status { get; set; }

/// <summary>How many of the thing are needed (0 = unquantified).</summary>
public int QuantityRequested { get; set; }

/// <summary>How many have been fulfilled so far.</summary>
public int QuantityFulfilled { get; set; }

/// <summary>Relative priority for triage/ordering (0 = unset; higher = more urgent).</summary>
public int Priority { get; set; }

public string CreatedByUserId { get; set; }

public DateTime CreatedOn { get; set; }

/// <summary>Set when the need transitions to Met.</summary>
public string MetByUserId { get; set; }

/// <summary>Set when the need transitions to Met.</summary>
public DateTime? MetOn { get; set; }

public int SortOrder { get; set; }

/// <summary>Change cursor for offline delta sync + last-write-wins; stamped on every write.</summary>
public DateTime? ModifiedOn { get; set; }

[NotMapped]
public string TableName => "IncidentNeeds";

[NotMapped]
public string IdName => "IncidentNeedId";

[NotMapped]
public int IdType => 1;

[NotMapped]
[JsonIgnore]
public object IdValue
{
get { return IncidentNeedId; }
set { IncidentNeedId = (string)value; }
}

[NotMapped]
public IEnumerable<string> IgnoredProperties => new string[] { "IdValue", "IdType", "TableName", "IdName" };
}
}
12 changes: 12 additions & 0 deletions Core/Resgrid.Model/IncidentCommand/IncidentTacticals.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ public class TacticalObjective : IEntity, IChangeTracked

public DateTime? CompletedOn { get; set; }

/// <summary>Optional free-text detail describing the objective / how it will be met.</summary>
public string Description { get; set; }

/// <summary>Progress toward completion, 0-100. Complete objectives are implicitly 100.</summary>
public int ProgressPercent { get; set; }

/// <summary>Relative priority for triage/ordering (0 = unset; higher = more urgent).</summary>
public int Priority { get; set; }

/// <summary>Optional target time this objective should be complete by.</summary>
public DateTime? TargetCompleteOn { get; set; }

public int SortOrder { get; set; }

/// <summary>Change cursor for offline delta sync + last-write-wins; stamped on every write.</summary>
Expand Down
92 changes: 92 additions & 0 deletions Core/Resgrid.Model/IncidentCommand/ResourceIncidentView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;

namespace Resgrid.Model
{
/// <summary>
/// Read-only incident view for a responder (user) or unit assigned to — or dispatched on — an incident:
/// who has command, incident timing, important information, objectives, needs, notes and attachment
/// metadata, plus the caller's own lane assignment (leads, lane objectives) when they have one.
/// Assembled by <c>IIncidentCommandService.GetResourceIncidentViewAsync</c>; notes/attachments are
/// visibility-filtered for callers without command capabilities.
/// </summary>
public class ResourceIncidentView
{
public string IncidentCommandId { get; set; }

public int CallId { get; set; }

/// <summary>Maps to <see cref="IncidentCommandStatus"/>.</summary>
public int Status { get; set; }

public DateTime EstablishedOn { get; set; }

public DateTime? EstimatedEndOn { get; set; }

public DateTime? ClosedOn { get; set; }

/// <summary>Important information every resource on the incident should see.</summary>
public string ImportantInformation { get; set; }

public string IncidentActionPlan { get; set; }

/// <summary>The current Incident Commander.</summary>
public IncidentContactInfo Commander { get; set; }

public List<TacticalObjective> Objectives { get; set; } = new List<TacticalObjective>();

public List<IncidentNeed> Needs { get; set; } = new List<IncidentNeed>();

/// <summary>Operational status notes, visibility-filtered for the caller.</summary>
public List<IncidentNote> Notes { get; set; } = new List<IncidentNote>();

/// <summary>Attachment metadata (documents/maps/files/images), visibility-filtered; binary content downloads separately.</summary>
public List<IncidentAttachment> Attachments { get; set; } = new List<IncidentAttachment>();

/// <summary>The caller's active lane assignment, when they have one (null otherwise).</summary>
public ResourceLaneAssignmentView MyAssignment { get; set; }
}

/// <summary>Contact card for a person relevant to a resource (commander or lane lead).</summary>
public class IncidentContactInfo
{
/// <summary>Set when this contact is a Resgrid user; null for external contacts.</summary>
public string UserId { get; set; }

public string Name { get; set; }

public string Phone { get; set; }

public string Email { get; set; }
}

/// <summary>A resource's own lane assignment: the lane, its leads, and its linked objectives/need.</summary>
public class ResourceLaneAssignmentView
{
public string ResourceAssignmentId { get; set; }

public string CommandStructureNodeId { get; set; }

public string LaneName { get; set; }

/// <summary>Maps to <see cref="CommandNodeType"/>.</summary>
public int NodeType { get; set; }

public string Color { get; set; }

public DateTime AssignedOn { get; set; }

public IncidentContactInfo PrimaryLead { get; set; }

public IncidentContactInfo SecondaryLead { get; set; }

/// <summary>The lane's primary linked objective, resolved (null when not set).</summary>
public TacticalObjective PrimaryObjective { get; set; }

/// <summary>The lane's secondary linked objective, resolved (null when not set).</summary>
public TacticalObjective SecondaryObjective { get; set; }

/// <summary>The need this lane is fulfilling, resolved (null when not set).</summary>
public IncidentNeed LinkedNeed { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ public interface ITacticalObjectiveRepository : IRepository<TacticalObjective>
{
}

public interface IIncidentNeedRepository : IRepository<IncidentNeed>
{
}

public interface IIncidentTimerRepository : IRepository<IncidentTimer>
{
}
Expand Down
Loading
Loading