diff --git a/Core/Resgrid.Localization/Areas/User/Contacts/Contacts.en.resx b/Core/Resgrid.Localization/Areas/User/Contacts/Contacts.en.resx index 916981b1..275fe2dd 100644 --- a/Core/Resgrid.Localization/Areas/User/Contacts/Contacts.en.resx +++ b/Core/Resgrid.Localization/Areas/User/Contacts/Contacts.en.resx @@ -483,4 +483,25 @@ Edit Contact + + Routes + + + Route + + + Stop Name + + + Normal + + + High + + + Critical + + + Optional + \ No newline at end of file diff --git a/Core/Resgrid.Localization/Areas/User/Routes/Routes.en.resx b/Core/Resgrid.Localization/Areas/User/Routes/Routes.en.resx index 5643d166..0cc46f03 100644 --- a/Core/Resgrid.Localization/Areas/User/Routes/Routes.en.resx +++ b/Core/Resgrid.Localization/Areas/User/Routes/Routes.en.resx @@ -339,4 +339,74 @@ Stop + + + Add Stop + + + Manual + + + Scheduled Call + + + Waypoint + + + Linked Call + + + Loading calls... + + + Normal + + + High + + + Critical + + + Optional + + + Find + + + What3Words + + + Location + + + Click the map to set location, or use address / What3Words above. + + + Planned Arrival + + + Planned Departure + + + Est. Dwell (min) + + + Select Contact + + + -- Type manually or select existing -- + + + Selecting a contact auto-fills name and number below. + + + Contact Name + + + Contact Number + + + Notes + diff --git a/Core/Resgrid.Model/Repositories/IRouteStopsRepository.cs b/Core/Resgrid.Model/Repositories/IRouteStopsRepository.cs index 36d87557..255e3733 100644 --- a/Core/Resgrid.Model/Repositories/IRouteStopsRepository.cs +++ b/Core/Resgrid.Model/Repositories/IRouteStopsRepository.cs @@ -7,5 +7,6 @@ public interface IRouteStopsRepository : IRepository { Task> GetStopsByRoutePlanIdAsync(string routePlanId); Task> GetStopsByCallIdAsync(int callId); + Task> GetStopsByContactIdAsync(string contactId); } } diff --git a/Core/Resgrid.Model/RouteStop.cs b/Core/Resgrid.Model/RouteStop.cs index 22a893c1..a796d4b9 100644 --- a/Core/Resgrid.Model/RouteStop.cs +++ b/Core/Resgrid.Model/RouteStop.cs @@ -41,6 +41,8 @@ public class RouteStop : IEntity public string ContactNumber { get; set; } + public string ContactId { get; set; } + public string Notes { get; set; } public bool IsDeleted { get; set; } diff --git a/Core/Resgrid.Model/Services/IRouteService.cs b/Core/Resgrid.Model/Services/IRouteService.cs index 54b66339..8e3435e8 100644 --- a/Core/Resgrid.Model/Services/IRouteService.cs +++ b/Core/Resgrid.Model/Services/IRouteService.cs @@ -17,6 +17,7 @@ public interface IRouteService // Route Stop CRUD Task SaveRouteStopAsync(RouteStop routeStop, CancellationToken cancellationToken = default); Task> GetRouteStopsForPlanAsync(string routePlanId); + Task> GetRouteStopsForContactAsync(string contactId, int departmentId); Task ReorderRouteStopsAsync(string routePlanId, List orderedStopIds, CancellationToken cancellationToken = default); Task DeleteRouteStopAsync(string routeStopId, CancellationToken cancellationToken = default); diff --git a/Core/Resgrid.Services/RouteService.cs b/Core/Resgrid.Services/RouteService.cs index 01c31063..d00dd068 100644 --- a/Core/Resgrid.Services/RouteService.cs +++ b/Core/Resgrid.Services/RouteService.cs @@ -84,6 +84,18 @@ public async Task> GetRouteStopsForPlanAsync(string routePlanId) return stops?.Where(x => !x.IsDeleted).OrderBy(x => x.StopOrder).ToList() ?? new List(); } + public async Task> GetRouteStopsForContactAsync(string contactId, int departmentId) + { + var stops = await _routeStopsRepository.GetStopsByContactIdAsync(contactId); + if (stops == null) + return new List(); + + var plans = await _routePlansRepository.GetRoutePlansByDepartmentIdAsync(departmentId); + var planIds = plans?.Where(p => !p.IsDeleted).Select(p => p.RoutePlanId).ToHashSet() ?? new HashSet(); + + return stops.Where(s => !s.IsDeleted && planIds.Contains(s.RoutePlanId)).ToList(); + } + public async Task ReorderRouteStopsAsync(string routePlanId, List orderedStopIds, CancellationToken cancellationToken = default) { var stops = await _routeStopsRepository.GetStopsByRoutePlanIdAsync(routePlanId); diff --git a/Providers/Resgrid.Providers.Migrations/Migrations/M0054_AddContactIdToRouteStops.cs b/Providers/Resgrid.Providers.Migrations/Migrations/M0054_AddContactIdToRouteStops.cs new file mode 100644 index 00000000..8575a431 --- /dev/null +++ b/Providers/Resgrid.Providers.Migrations/Migrations/M0054_AddContactIdToRouteStops.cs @@ -0,0 +1,19 @@ +using FluentMigrator; + +namespace Resgrid.Providers.Migrations.Migrations +{ + [Migration(54)] + public class M0054_AddContactIdToRouteStops : Migration + { + public override void Up() + { + Alter.Table("RouteStops") + .AddColumn("ContactId").AsString(128).Nullable(); + } + + public override void Down() + { + Delete.Column("ContactId").FromTable("RouteStops"); + } + } +} diff --git a/Providers/Resgrid.Providers.MigrationsPg/Migrations/M0054_AddContactIdToRouteStopsPg.cs b/Providers/Resgrid.Providers.MigrationsPg/Migrations/M0054_AddContactIdToRouteStopsPg.cs new file mode 100644 index 00000000..09967eb5 --- /dev/null +++ b/Providers/Resgrid.Providers.MigrationsPg/Migrations/M0054_AddContactIdToRouteStopsPg.cs @@ -0,0 +1,19 @@ +using FluentMigrator; + +namespace Resgrid.Providers.MigrationsPg.Migrations +{ + [Migration(54)] + public class M0054_AddContactIdToRouteStopsPg : Migration + { + public override void Up() + { + Alter.Table("routestops") + .AddColumn("contactid").AsCustom("citext").Nullable(); + } + + public override void Down() + { + Delete.Column("contactid").FromTable("routestops"); + } + } +} diff --git a/Repositories/Resgrid.Repositories.DataRepository/Configs/SqlConfiguration.cs b/Repositories/Resgrid.Repositories.DataRepository/Configs/SqlConfiguration.cs index 29df3157..49e071c1 100644 --- a/Repositories/Resgrid.Repositories.DataRepository/Configs/SqlConfiguration.cs +++ b/Repositories/Resgrid.Repositories.DataRepository/Configs/SqlConfiguration.cs @@ -497,6 +497,7 @@ protected SqlConfiguration() { } public string SelectActiveRoutePlansByDepartmentIdQuery { get; set; } public string SelectRouteStopsByRoutePlanIdQuery { get; set; } public string SelectRouteStopsByCallIdQuery { get; set; } + public string SelectRouteStopsByContactIdQuery { get; set; } public string SelectRouteSchedulesByRoutePlanIdQuery { get; set; } public string SelectActiveSchedulesDueQuery { get; set; } public string SelectRouteInstancesByDepartmentIdQuery { get; set; } diff --git a/Repositories/Resgrid.Repositories.DataRepository/Queries/Routes/SelectRouteStopsByContactIdQuery.cs b/Repositories/Resgrid.Repositories.DataRepository/Queries/Routes/SelectRouteStopsByContactIdQuery.cs new file mode 100644 index 00000000..281b4f15 --- /dev/null +++ b/Repositories/Resgrid.Repositories.DataRepository/Queries/Routes/SelectRouteStopsByContactIdQuery.cs @@ -0,0 +1,33 @@ +using Resgrid.Model; +using Resgrid.Model.Repositories.Queries.Contracts; +using Resgrid.Repositories.DataRepository.Configs; +using Resgrid.Repositories.DataRepository.Extensions; + +namespace Resgrid.Repositories.DataRepository.Queries.Routes +{ + public class SelectRouteStopsByContactIdQuery : ISelectQuery + { + private readonly SqlConfiguration _sqlConfiguration; + public SelectRouteStopsByContactIdQuery(SqlConfiguration sqlConfiguration) + { + _sqlConfiguration = sqlConfiguration; + } + + public string GetQuery() + { + var query = _sqlConfiguration.SelectRouteStopsByContactIdQuery + .ReplaceQueryParameters(_sqlConfiguration, _sqlConfiguration.SchemaName, + _sqlConfiguration.RouteStopsTableName, + _sqlConfiguration.ParameterNotation, + new string[] { "%CONTACTID%" }, + new string[] { "ContactId" }); + + return query; + } + + public string GetQuery() where TEntity : class, IEntity + { + throw new System.NotImplementedException(); + } + } +} diff --git a/Repositories/Resgrid.Repositories.DataRepository/RouteStopsRepository.cs b/Repositories/Resgrid.Repositories.DataRepository/RouteStopsRepository.cs index 42b0038b..0166594a 100644 --- a/Repositories/Resgrid.Repositories.DataRepository/RouteStopsRepository.cs +++ b/Repositories/Resgrid.Repositories.DataRepository/RouteStopsRepository.cs @@ -102,5 +102,41 @@ public async Task> GetStopsByCallIdAsync(int callId) throw; } } + + public async Task> GetStopsByContactIdAsync(string contactId) + { + try + { + var selectFunction = new Func>>(async x => + { + var dynamicParameters = new DynamicParametersExtension(); + dynamicParameters.Add("ContactId", contactId); + + var query = _queryFactory.GetQuery(); + + return await x.QueryAsync(sql: query, param: dynamicParameters, transaction: _unitOfWork.Transaction); + }); + + DbConnection conn = null; + if (_unitOfWork?.Connection == null) + { + using (conn = _connectionProvider.Create()) + { + await conn.OpenAsync(); + return await selectFunction(conn); + } + } + else + { + conn = _unitOfWork.CreateOrGetConnection(); + return await selectFunction(conn); + } + } + catch (Exception ex) + { + Logging.LogException(ex); + throw; + } + } } } diff --git a/Repositories/Resgrid.Repositories.DataRepository/Servers/PostgreSql/PostgreSqlConfiguration.cs b/Repositories/Resgrid.Repositories.DataRepository/Servers/PostgreSql/PostgreSqlConfiguration.cs index c8399371..fbec5279 100644 --- a/Repositories/Resgrid.Repositories.DataRepository/Servers/PostgreSql/PostgreSqlConfiguration.cs +++ b/Repositories/Resgrid.Repositories.DataRepository/Servers/PostgreSql/PostgreSqlConfiguration.cs @@ -1515,6 +1515,10 @@ DELETE FROM %SCHEMA%.%TABLENAME% SELECT * FROM %SCHEMA%.%TABLENAME% WHERE CallId = %CALLID% AND IsDeleted = false"; + SelectRouteStopsByContactIdQuery = @" + SELECT * + FROM %SCHEMA%.%TABLENAME% + WHERE ContactId = %CONTACTID% AND IsDeleted = false"; SelectRouteSchedulesByRoutePlanIdQuery = @" SELECT * FROM %SCHEMA%.%TABLENAME% diff --git a/Repositories/Resgrid.Repositories.DataRepository/Servers/SqlServer/SqlServerConfiguration.cs b/Repositories/Resgrid.Repositories.DataRepository/Servers/SqlServer/SqlServerConfiguration.cs index 7e25ef0f..f733589d 100644 --- a/Repositories/Resgrid.Repositories.DataRepository/Servers/SqlServer/SqlServerConfiguration.cs +++ b/Repositories/Resgrid.Repositories.DataRepository/Servers/SqlServer/SqlServerConfiguration.cs @@ -1479,6 +1479,10 @@ DELETE FROM %SCHEMA%.%TABLENAME% SELECT * FROM %SCHEMA%.%TABLENAME% WHERE [CallId] = %CALLID% AND [IsDeleted] = 0"; + SelectRouteStopsByContactIdQuery = @" + SELECT * + FROM %SCHEMA%.%TABLENAME% + WHERE [ContactId] = %CONTACTID% AND [IsDeleted] = 0"; SelectRouteSchedulesByRoutePlanIdQuery = @" SELECT * FROM %SCHEMA%.%TABLENAME% diff --git a/Web/Resgrid.Web/Areas/User/Controllers/ContactsController.cs b/Web/Resgrid.Web/Areas/User/Controllers/ContactsController.cs index e74e5452..92002b7c 100644 --- a/Web/Resgrid.Web/Areas/User/Controllers/ContactsController.cs +++ b/Web/Resgrid.Web/Areas/User/Controllers/ContactsController.cs @@ -37,11 +37,12 @@ public class ContactsController : SecureBaseController private readonly IUserDefinedFieldsService _userDefinedFieldsService; private readonly IUdfRenderingService _udfRenderingService; private readonly IDepartmentGroupsService _departmentGroupsService; + private readonly IRouteService _routeService; public ContactsController(IContactsService contactsService, IDepartmentsService departmentsService, IUserProfileService userProfileService, IAddressService addressService, IEventAggregator eventAggregator, ICallsService callsService, IAuthorizationService authorizationService, IUserDefinedFieldsService userDefinedFieldsService, IUdfRenderingService udfRenderingService, - IDepartmentGroupsService departmentGroupsService) + IDepartmentGroupsService departmentGroupsService, IRouteService routeService) { _contactsService = contactsService; _departmentsService = departmentsService; @@ -53,6 +54,7 @@ public ContactsController(IContactsService contactsService, IDepartmentsService _userDefinedFieldsService = userDefinedFieldsService; _udfRenderingService = udfRenderingService; _departmentGroupsService = departmentGroupsService; + _routeService = routeService; } #endregion Private Members and Constructors @@ -128,6 +130,18 @@ public async Task View(string contactId) model.Notes = await _contactsService.GetContactNotesByContactIdAsync(contactId, DepartmentId); + model.RouteStops = await _routeService.GetRouteStopsForContactAsync(contactId, DepartmentId) ?? new List(); + if (model.RouteStops.Count > 0) + { + var planIds = model.RouteStops.Select(s => s.RoutePlanId).Distinct().ToList(); + var allPlans = await _routeService.GetRoutePlansForDepartmentAsync(DepartmentId); + model.RoutePlans = allPlans.Where(p => planIds.Contains(p.RoutePlanId)).ToList(); + } + else + { + model.RoutePlans = new List(); + } + var udfDefinition = await _userDefinedFieldsService.GetActiveDefinitionAsync(DepartmentId, (int)UdfEntityType.Contact); if (udfDefinition != null) { diff --git a/Web/Resgrid.Web/Areas/User/Controllers/IndoorMapsController.cs b/Web/Resgrid.Web/Areas/User/Controllers/IndoorMapsController.cs index b25e986d..48ac1bbf 100644 --- a/Web/Resgrid.Web/Areas/User/Controllers/IndoorMapsController.cs +++ b/Web/Resgrid.Web/Areas/User/Controllers/IndoorMapsController.cs @@ -27,7 +27,7 @@ public async Task SearchZones(string term) var zones = await _indoorMapService.SearchZonesAsync(DepartmentId, term); - var results = zones.Select(z => new + var results = (zones ?? Enumerable.Empty()).Select(z => new { id = z.IndoorMapZoneId, text = z.Name, diff --git a/Web/Resgrid.Web/Areas/User/Controllers/RoutesController.cs b/Web/Resgrid.Web/Areas/User/Controllers/RoutesController.cs index a7fc1c11..7607bb29 100644 --- a/Web/Resgrid.Web/Areas/User/Controllers/RoutesController.cs +++ b/Web/Resgrid.Web/Areas/User/Controllers/RoutesController.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -7,6 +8,7 @@ using Resgrid.Model; using Resgrid.Model.Services; using Resgrid.Providers.Claims; +using Resgrid.Framework; using Resgrid.Web.Areas.User.Models.Routes; namespace Resgrid.Web.Areas.User.Controllers @@ -17,12 +19,16 @@ public class RoutesController : SecureBaseController private readonly IRouteService _routeService; private readonly IUnitsService _unitsService; private readonly ICallsService _callsService; + private readonly IContactsService _contactsService; + private readonly IDepartmentsService _departmentsService; - public RoutesController(IRouteService routeService, IUnitsService unitsService, ICallsService callsService) + public RoutesController(IRouteService routeService, IUnitsService unitsService, ICallsService callsService, IContactsService contactsService, IDepartmentsService departmentsService) { _routeService = routeService; _unitsService = unitsService; _callsService = callsService; + _contactsService = contactsService; + _departmentsService = departmentsService; } [HttpGet] @@ -40,6 +46,7 @@ public async Task New() { var model = new RouteNewView(); model.Units = (await _unitsService.GetUnitsForDepartmentAsync(DepartmentId)).ToList(); + model.Contacts = (await _contactsService.GetAllContactsForDepartmentAsync(DepartmentId)).ToList(); return View(model); } @@ -48,6 +55,21 @@ public async Task New() [Authorize(Policy = ResgridResources.Route_Create)] public async Task New(RouteNewView model, CancellationToken cancellationToken) { + // Deserialize stops before saving anything so a bad payload does not leave an orphaned plan. + List pendingStops = null; + if (!string.IsNullOrWhiteSpace(model.PendingStopsJson)) + { + try + { + var options = new System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + pendingStops = System.Text.Json.JsonSerializer.Deserialize>(model.PendingStopsJson, options); + } + catch (System.Text.Json.JsonException) + { + ModelState.AddModelError(nameof(model.PendingStopsJson), "Stop data is invalid and could not be parsed."); + } + } + if (ModelState.IsValid) { model.Plan.DepartmentId = DepartmentId; @@ -57,10 +79,60 @@ public async Task New(RouteNewView model, CancellationToken cance await _routeService.SaveRoutePlanAsync(model.Plan, cancellationToken); + var dept = await _departmentsService.GetDepartmentByIdAsync(DepartmentId); + var deptTimeZone = !string.IsNullOrWhiteSpace(dept?.TimeZone) + ? DateTimeHelpers.WindowsToIana(dept.TimeZone) + : "UTC"; + + if (pendingStops != null) + { + for (int i = 0; i < pendingStops.Count; i++) + { + var ps = pendingStops[i]; + + string resolvedContactId = null; + if (!string.IsNullOrWhiteSpace(ps.ContactId)) + { + var contact = await _contactsService.GetContactByIdAsync(ps.ContactId); + if (contact != null && contact.DepartmentId == DepartmentId) + resolvedContactId = ps.ContactId; + } + + var stop = new RouteStop + { + RoutePlanId = model.Plan.RoutePlanId, + Name = ps.Name, + Description = ps.Description, + StopType = ps.StopType, + Priority = ps.Priority, + Latitude = ps.Latitude, + Longitude = ps.Longitude, + Address = ps.Address, + CallId = ps.CallId, + EstimatedDwellMinutes = ps.DwellMinutes, + ContactName = ps.ContactName, + ContactNumber = ps.ContactNumber, + ContactId = resolvedContactId, + Notes = ps.Notes, + StopOrder = i + 1, + AddedOn = DateTime.UtcNow, + IsDeleted = false + }; + + if (!string.IsNullOrWhiteSpace(ps.PlannedArrival) && DateTime.TryParse(ps.PlannedArrival, out var arrivalDt)) + stop.PlannedArrivalTime = DateTimeHelpers.ConvertToUtc(DateTime.SpecifyKind(arrivalDt, DateTimeKind.Unspecified), deptTimeZone); + if (!string.IsNullOrWhiteSpace(ps.PlannedDeparture) && DateTime.TryParse(ps.PlannedDeparture, out var departureDt)) + stop.PlannedDepartureTime = DateTimeHelpers.ConvertToUtc(DateTime.SpecifyKind(departureDt, DateTimeKind.Unspecified), deptTimeZone); + + await _routeService.SaveRouteStopAsync(stop, cancellationToken); + } + } + return RedirectToAction("Index"); } model.Units = (await _unitsService.GetUnitsForDepartmentAsync(DepartmentId)).ToList(); + model.Contacts = (await _contactsService.GetAllContactsForDepartmentAsync(DepartmentId)).ToList(); return View(model); } @@ -77,6 +149,7 @@ public async Task Edit(string id) model.Stops = await _routeService.GetRouteStopsForPlanAsync(id); model.Schedules = await _routeService.GetSchedulesForPlanAsync(id); model.Units = (await _unitsService.GetUnitsForDepartmentAsync(DepartmentId)).ToList(); + model.Contacts = (await _contactsService.GetAllContactsForDepartmentAsync(DepartmentId)).ToList(); return View(model); } @@ -103,6 +176,7 @@ public async Task Edit(RouteEditView model, CancellationToken can } model.Units = (await _unitsService.GetUnitsForDepartmentAsync(DepartmentId)).ToList(); + model.Contacts = (await _contactsService.GetAllContactsForDepartmentAsync(DepartmentId)).ToList(); return View(model); } @@ -175,12 +249,26 @@ public async Task GetCallsForLinking() return Json(all); } + [HttpGet] + [Authorize(Policy = ResgridResources.Route_View)] + public async Task GetContactsForStop() + { + var contacts = await _contactsService.GetAllContactsForDepartmentAsync(DepartmentId); + var result = contacts.OrderBy(c => c.GetName()).Select(c => new + { + id = c.ContactId, + name = c.GetName(), + phone = c.CellPhoneNumber ?? c.OfficePhoneNumber ?? c.HomePhoneNumber ?? string.Empty + }).ToList(); + return Json(result); + } + [HttpPost] [ValidateAntiForgeryToken] [Authorize(Policy = ResgridResources.Route_Update)] public async Task AddStop(string routePlanId, string name, string description, int stopType, int priority, decimal latitude, decimal longitude, string address, int? callId, int? geofenceRadius, - string plannedArrival, string plannedDeparture, int? dwellMinutes, string contactName, string contactNumber, string notes, + string plannedArrival, string plannedDeparture, int? dwellMinutes, string contactName, string contactNumber, string contactId, string notes, CancellationToken cancellationToken) { var plan = await _routeService.GetRoutePlanByIdAsync(routePlanId); @@ -188,6 +276,15 @@ public async Task AddStop(string routePlanId, string name, string return Json(new { success = false, message = "Not found" }); var existingStops = await _routeService.GetRouteStopsForPlanAsync(routePlanId); + + string resolvedContactId = null; + if (!string.IsNullOrWhiteSpace(contactId)) + { + var contact = await _contactsService.GetContactByIdAsync(contactId); + if (contact != null && contact.DepartmentId == DepartmentId) + resolvedContactId = contactId; + } + var stop = new RouteStop { RoutePlanId = routePlanId, @@ -203,16 +300,22 @@ public async Task AddStop(string routePlanId, string name, string EstimatedDwellMinutes = dwellMinutes, ContactName = contactName, ContactNumber = contactNumber, + ContactId = resolvedContactId, Notes = notes, StopOrder = existingStops.Count + 1, AddedOn = DateTime.UtcNow, IsDeleted = false }; + var dept2 = await _departmentsService.GetDepartmentByIdAsync(DepartmentId); + var deptTimeZone2 = !string.IsNullOrWhiteSpace(dept2?.TimeZone) + ? DateTimeHelpers.WindowsToIana(dept2.TimeZone) + : "UTC"; + if (!string.IsNullOrWhiteSpace(plannedArrival) && DateTime.TryParse(plannedArrival, out var arrivalDt)) - stop.PlannedArrivalTime = arrivalDt.ToUniversalTime(); + stop.PlannedArrivalTime = DateTimeHelpers.ConvertToUtc(DateTime.SpecifyKind(arrivalDt, DateTimeKind.Unspecified), deptTimeZone2); if (!string.IsNullOrWhiteSpace(plannedDeparture) && DateTime.TryParse(plannedDeparture, out var departureDt)) - stop.PlannedDepartureTime = departureDt.ToUniversalTime(); + stop.PlannedDepartureTime = DateTimeHelpers.ConvertToUtc(DateTime.SpecifyKind(departureDt, DateTimeKind.Unspecified), deptTimeZone2); await _routeService.SaveRouteStopAsync(stop, cancellationToken); return Json(new { success = true }); @@ -257,5 +360,6 @@ public async Task InstanceDetail(string instanceId) model.Stops = stops; return View(model); } + } } diff --git a/Web/Resgrid.Web/Areas/User/Models/Contacts/ViewContactView.cs b/Web/Resgrid.Web/Areas/User/Models/Contacts/ViewContactView.cs index 28eafd23..b193f4e5 100644 --- a/Web/Resgrid.Web/Areas/User/Models/Contacts/ViewContactView.cs +++ b/Web/Resgrid.Web/Areas/User/Models/Contacts/ViewContactView.cs @@ -12,5 +12,7 @@ public class ViewContactView public Address MailingAddress { get; set; } public List Notes { get; set; } public List NoteTypes { get; set; } + public List RouteStops { get; set; } + public List RoutePlans { get; set; } } } diff --git a/Web/Resgrid.Web/Areas/User/Models/Routes/RouteViewModels.cs b/Web/Resgrid.Web/Areas/User/Models/Routes/RouteViewModels.cs index 13f5f057..b1923f7d 100644 --- a/Web/Resgrid.Web/Areas/User/Models/Routes/RouteViewModels.cs +++ b/Web/Resgrid.Web/Areas/User/Models/Routes/RouteViewModels.cs @@ -13,25 +13,49 @@ public class RouteNewView : BaseUserModel { public RoutePlan Plan { get; set; } public List Units { get; set; } + public List Contacts { get; set; } + public string PendingStopsJson { get; set; } public RouteNewView() { Plan = new RoutePlan(); Units = new List(); + Contacts = new List(); } } + public class PendingStopDto + { + public string Name { get; set; } + public string Description { get; set; } + public int StopType { get; set; } + public int Priority { get; set; } + public decimal Latitude { get; set; } + public decimal Longitude { get; set; } + public string Address { get; set; } + public int? CallId { get; set; } + public string PlannedArrival { get; set; } + public string PlannedDeparture { get; set; } + public int? DwellMinutes { get; set; } + public string ContactName { get; set; } + public string ContactNumber { get; set; } + public string ContactId { get; set; } + public string Notes { get; set; } + } + public class RouteEditView : BaseUserModel { public RoutePlan Plan { get; set; } public List Stops { get; set; } public List Schedules { get; set; } public List Units { get; set; } + public List Contacts { get; set; } public RouteEditView() { Plan = new RoutePlan(); Stops = new List(); Schedules = new List(); Units = new List(); + Contacts = new List(); } } diff --git a/Web/Resgrid.Web/Areas/User/Views/Contacts/View.cshtml b/Web/Resgrid.Web/Areas/User/Views/Contacts/View.cshtml index b5c49b15..08ba46d2 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Contacts/View.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Contacts/View.cshtml @@ -1,5 +1,6 @@ @model Resgrid.Web.Areas.User.Models.Contacts.ViewContactView @inject IStringLocalizer localizer +@using System.Linq @{ ViewBag.Title = "Resgrid | " + @localizer["ViewContactHeader"]; } @@ -27,6 +28,14 @@ @localizer["Calls"] + @if (Model.RouteStops != null && Model.RouteStops.Count > 0) + { + + } @if (!string.IsNullOrEmpty(Model.UdfReadOnlyHtml)) {