diff --git a/Core/Resgrid.Localization/Areas/User/Dispatch/Call.en.resx b/Core/Resgrid.Localization/Areas/User/Dispatch/Call.en.resx index 90fecf8e..a641af4c 100644 --- a/Core/Resgrid.Localization/Areas/User/Dispatch/Call.en.resx +++ b/Core/Resgrid.Localization/Areas/User/Dispatch/Call.en.resx @@ -144,6 +144,9 @@ Add Image + + Additional Contacts + Add Linked Call @@ -153,12 +156,18 @@ Enter note here... + + Alert Notes for Contact + Archived Calls Attached Images + + Call Contact Type + Call Documents @@ -237,6 +246,9 @@ Completed Type + + Contacts + Create and Dispatch Call @@ -334,7 +346,7 @@ Name of the Call - + New Call Form New Call @@ -372,6 +384,9 @@ Personnel Events + + Primary Contact + Priority diff --git a/Core/Resgrid.Localization/Areas/User/Dispatch/Call.es.resx b/Core/Resgrid.Localization/Areas/User/Dispatch/Call.es.resx index 3014e272..2207305c 100644 --- a/Core/Resgrid.Localization/Areas/User/Dispatch/Call.es.resx +++ b/Core/Resgrid.Localization/Areas/User/Dispatch/Call.es.resx @@ -144,6 +144,9 @@ Añadir imagen + + + Agregar llamada vinculada @@ -153,12 +156,18 @@ Introduzca la nota aquí... + + + Llamadas archivadas Imágenes adjuntas + + + Documentos de llamadas @@ -237,6 +246,9 @@ Tipo completado + + + Crear y enviar llamadas @@ -372,6 +384,9 @@ Eventos de personal + + + Prioridad diff --git a/Core/Resgrid.Model/Call.cs b/Core/Resgrid.Model/Call.cs index 4c6f374e..7c60ae6e 100644 --- a/Core/Resgrid.Model/Call.cs +++ b/Core/Resgrid.Model/Call.cs @@ -136,6 +136,9 @@ public class Call : IEntity [ProtoMember(31)] public virtual ICollection References { get; set; } + [ProtoMember(32)] + public virtual ICollection Contacts { get; set; } + public string ContactName { get; set; } public string ContactNumber { get; set; } @@ -190,7 +193,7 @@ public object IdValue public int IdType => 0; [NotMapped] - public IEnumerable IgnoredProperties => new string[] { "IdValue", "IdType", "TableName", "IdName", "ReportingUser", "ClosedByUser", "Department", "Dispatches", "Attachments", "CallNotes", "GroupDispatches", "UnitDispatches", "RoleDispatches", "Protocols", "ShortenedAudioUrl", "ShortenedCallUrl", "CallPriority", "PreviousDispatchCount", "References" }; + public IEnumerable IgnoredProperties => new string[] { "IdValue", "IdType", "TableName", "IdName", "ReportingUser", "ClosedByUser", "Department", "Dispatches", "Attachments", "CallNotes", "GroupDispatches", "UnitDispatches", "RoleDispatches", "Protocols", "ShortenedAudioUrl", "ShortenedCallUrl", "CallPriority", "PreviousDispatchCount", "References", "Contacts" }; public string GetIdentifier() { diff --git a/Core/Resgrid.Model/CallContact.cs b/Core/Resgrid.Model/CallContact.cs index d538b6af..c2610012 100644 --- a/Core/Resgrid.Model/CallContact.cs +++ b/Core/Resgrid.Model/CallContact.cs @@ -29,6 +29,14 @@ public class CallContact : IEntity [ProtoMember(5)] public int CallContactType { get; set; } // 0 = Primary + public string GetContactTypeName() + { + if (CallContactType == 0) + return "Primary"; + + return "Additional"; + } + [NotMapped] [JsonIgnore] public object IdValue diff --git a/Core/Resgrid.Model/Contact.cs b/Core/Resgrid.Model/Contact.cs index 1704c57b..a2db0658 100644 --- a/Core/Resgrid.Model/Contact.cs +++ b/Core/Resgrid.Model/Contact.cs @@ -93,6 +93,22 @@ public class Contact : IEntity public string EditedByUserId { get; set; } + [NotMapped] + public string Name + { + get + { + if (ContactType == 0) + { + return $"{FirstName} {LastName}"; + } + else + { + return CompanyName; + } + } + } + public string GetName() { if (ContactType == 0) @@ -143,6 +159,6 @@ public object IdValue } [NotMapped] - public IEnumerable IgnoredProperties => new string[] { "IdValue", "IdType", "TableName", "IdName", "Category" }; + public IEnumerable IgnoredProperties => new string[] { "IdValue", "IdType", "TableName", "IdName", "Category", "Name" }; } } diff --git a/Core/Resgrid.Model/Repositories/ICallContactsRepository.cs b/Core/Resgrid.Model/Repositories/ICallContactsRepository.cs index 1b95ade9..94a9d7db 100644 --- a/Core/Resgrid.Model/Repositories/ICallContactsRepository.cs +++ b/Core/Resgrid.Model/Repositories/ICallContactsRepository.cs @@ -10,6 +10,6 @@ namespace Resgrid.Model.Repositories /// public interface ICallContactsRepository : IRepository { - + Task> GetCallContactsByCallIdAsync(int callId); } } diff --git a/Core/Resgrid.Model/Services/ICallsService.cs b/Core/Resgrid.Model/Services/ICallsService.cs index 7d57922c..249b27b0 100644 --- a/Core/Resgrid.Model/Services/ICallsService.cs +++ b/Core/Resgrid.Model/Services/ICallsService.cs @@ -398,7 +398,7 @@ Task ClearGroupForDispatchesAsync(int departmentGroupId, /// if set to true [get protocols]. /// if set to true [get references]. /// Task<Call>. - Task PopulateCallData(Call call, bool getDispatches, bool getAttachments, bool getNotes, bool getGroupDispatches, bool getUnitDispatches, bool getRoleDispatches, bool getProtocols, bool getReferences); + Task PopulateCallData(Call call, bool getDispatches, bool getAttachments, bool getNotes, bool getGroupDispatches, bool getUnitDispatches, bool getRoleDispatches, bool getProtocols, bool getReferences, bool getContacts); Task> GetAllNonDispatchedScheduledCallsWithinDateRange(DateTime startDate, DateTime endDate); @@ -410,5 +410,7 @@ Task ClearGroupForDispatchesAsync(int departmentGroupId, Task DeleteCallReferenceAsync(CallReference callReference, CancellationToken cancellationToken = default(CancellationToken)); Task> GetCallsByContactIdAsync(string contactId, int departmentId); + + Task DeleteCallContactsAsync(int callId, CancellationToken cancellationToken = default(CancellationToken)); } } diff --git a/Core/Resgrid.Services/AuthorizationService.cs b/Core/Resgrid.Services/AuthorizationService.cs index 321d5967..a1af8817 100644 --- a/Core/Resgrid.Services/AuthorizationService.cs +++ b/Core/Resgrid.Services/AuthorizationService.cs @@ -868,7 +868,7 @@ public async Task CanUserDeleteCallAsync(string userId, int callId, int de if (call == null || call.DepartmentId != departmentId) return false; - call = await _callsService.PopulateCallData(call, false, false, false, true, false, false, false, false); + call = await _callsService.PopulateCallData(call, false, false, false, true, false, false, false, false, false); if (group != null) { @@ -907,7 +907,7 @@ public async Task CanUserCloseCallAsync(string userId, int callId, int dep if (call == null || call.DepartmentId != departmentId) return false; - call = await _callsService.PopulateCallData(call, false, false, false, true, false, false, false, false); + call = await _callsService.PopulateCallData(call, false, false, false, true, false, false, false, false, false); if (group != null) { @@ -946,7 +946,7 @@ public async Task CanUserAddCallDataAsync(string userId, int callId, int d if (call == null || call.DepartmentId != departmentId) return false; - call = await _callsService.PopulateCallData(call, false, false, false, true, false, false, false, false); + call = await _callsService.PopulateCallData(call, false, false, false, true, false, false, false, false, false); if (group != null) { diff --git a/Core/Resgrid.Services/CallsService.cs b/Core/Resgrid.Services/CallsService.cs index 0b82ea52..4e8ded3c 100644 --- a/Core/Resgrid.Services/CallsService.cs +++ b/Core/Resgrid.Services/CallsService.cs @@ -49,7 +49,7 @@ public CallsService(ICallsRepository callsRepository, ICommunicationService comm ICallDispatchUnitRepository callDispatchUnitRepository, ICallDispatchRoleRepository callDispatchRoleRepository, IDepartmentCallPriorityRepository departmentCallPriorityRepository, IShortenUrlProvider shortenUrlProvider, ICallProtocolsRepository callProtocolsRepository, IGeoLocationProvider geoLocationProvider, IDepartmentsService departmentsService, - ICallReferencesRepository callReferencesRepository, ICallContactsRepository callContactsRepository) + ICallReferencesRepository callReferencesRepository, ICallContactsRepository callContactsRepository) { _callsRepository = callsRepository; _communicationService = communicationService; @@ -83,41 +83,41 @@ public CallsService(ICallsRepository callsRepository, ICommunicationService comm if (!String.IsNullOrWhiteSpace(call.GeoLocationData) && call.GeoLocationData.Length == 1) call.GeoLocationData = ""; - if (call.Dispatches != null && call.Dispatches.Any()) + if (call.Dispatches != null && call.Dispatches.Any()) + { + foreach (var dispatch in call.Dispatches) { - foreach (var dispatch in call.Dispatches) - { - if (dispatch.CallDispatchId == 0) - dispatch.DispatchedOn = DateTime.UtcNow; - } + if (dispatch.CallDispatchId == 0) + dispatch.DispatchedOn = DateTime.UtcNow; } + } - if (call.GroupDispatches != null && call.GroupDispatches.Any()) + if (call.GroupDispatches != null && call.GroupDispatches.Any()) + { + foreach (var dispatch in call.GroupDispatches) { - foreach (var dispatch in call.GroupDispatches) - { - if (dispatch.CallDispatchGroupId == 0) - dispatch.DispatchedOn = DateTime.UtcNow; - } + if (dispatch.CallDispatchGroupId == 0) + dispatch.DispatchedOn = DateTime.UtcNow; } + } - if (call.RoleDispatches != null && call.RoleDispatches.Any()) + if (call.RoleDispatches != null && call.RoleDispatches.Any()) + { + foreach (var dispatch in call.RoleDispatches) { - foreach (var dispatch in call.RoleDispatches) - { - if (dispatch.CallDispatchRoleId == 0) - dispatch.DispatchedOn = DateTime.UtcNow; - } + if (dispatch.CallDispatchRoleId == 0) + dispatch.DispatchedOn = DateTime.UtcNow; } + } - if (call.UnitDispatches != null && call.UnitDispatches.Any()) + if (call.UnitDispatches != null && call.UnitDispatches.Any()) + { + foreach (var dispatch in call.UnitDispatches) { - foreach (var dispatch in call.UnitDispatches) - { - if (dispatch.CallDispatchUnitId == 0) - dispatch.DispatchedOn = DateTime.UtcNow; - } + if (dispatch.CallDispatchUnitId == 0) + dispatch.DispatchedOn = DateTime.UtcNow; } + } if (call.References != null && call.References.Any()) { @@ -445,8 +445,11 @@ public async Task> GetActiveCallPrioritiesForDepart return activePriorities; } - public async Task PopulateCallData(Call call, bool getDispatches, bool getAttachments, bool getNotes, bool getGroupDispatches, bool getUnitDispatches, bool getRoleDispatches, bool getProtocols, bool getReferences) + public async Task PopulateCallData(Call call, bool getDispatches, bool getAttachments, bool getNotes, bool getGroupDispatches, bool getUnitDispatches, bool getRoleDispatches, bool getProtocols, bool getReferences, bool getContacts) { + if (call == null) + return null; + if (getDispatches && call.Dispatches == null) { var items = await _callDispatchesRepository.GetCallDispatchesByCallIdAsync(call.CallId); @@ -520,10 +523,36 @@ public async Task PopulateCallData(Call call, bool getDispatches, bool get else call.References = new List(); } + if (getContacts && call.Contacts == null) + { + var items = await _callContactsRepository.GetCallContactsByCallIdAsync(call.CallId); + + if (items != null) + call.Contacts = items.ToList(); + else + call.Contacts = new List(); + } return call; } + public async Task DeleteCallContactsAsync(int callId, CancellationToken cancellationToken = default(CancellationToken)) + { + var callContacts = await _callContactsRepository.GetCallContactsByCallIdAsync(callId); + + if (callContacts != null || callContacts.Any()) + { + foreach (var contact in callContacts) + { + await _callContactsRepository.DeleteAsync(contact, cancellationToken); + } + + return true; + } + + return false; + } + public List GetDefaultCallPriorities() { if (_callPriorities == null) diff --git a/Providers/Resgrid.Providers.Bus.Rabbit/RabbitConnection.cs b/Providers/Resgrid.Providers.Bus.Rabbit/RabbitConnection.cs index 63b64d76..11311af7 100644 --- a/Providers/Resgrid.Providers.Bus.Rabbit/RabbitConnection.cs +++ b/Providers/Resgrid.Providers.Bus.Rabbit/RabbitConnection.cs @@ -164,10 +164,10 @@ await channel.QueueDeclareAsync(queue: SetQueueNameForEnv(ServiceBusConfig.Secur return false; } - public static IConnection CreateConnection(string clientName) + public static async Task CreateConnection(string clientName) { if (_connection == null) - VerifyAndCreateClients(clientName); + await VerifyAndCreateClients(clientName); if (!_connection.IsOpen) { @@ -175,7 +175,7 @@ public static IConnection CreateConnection(string clientName) _connection = null; _factory = null; - VerifyAndCreateClients(clientName); + await VerifyAndCreateClients(clientName); } return _connection; diff --git a/Providers/Resgrid.Providers.Bus.Rabbit/RabbitInboundEventProvider.cs b/Providers/Resgrid.Providers.Bus.Rabbit/RabbitInboundEventProvider.cs index 8ad1ea6a..cbb04081 100644 --- a/Providers/Resgrid.Providers.Bus.Rabbit/RabbitInboundEventProvider.cs +++ b/Providers/Resgrid.Providers.Bus.Rabbit/RabbitInboundEventProvider.cs @@ -38,7 +38,7 @@ private async Task VerifyAndCreateClients(string clientName) { try { - _connection = RabbitConnection.CreateConnection(clientName); + _connection = await RabbitConnection.CreateConnection(clientName); if (_connection != null) { diff --git a/Providers/Resgrid.Providers.Bus.Rabbit/RabbitInboundQueueProvider.cs b/Providers/Resgrid.Providers.Bus.Rabbit/RabbitInboundQueueProvider.cs index bbff8f4c..5c3c6292 100644 --- a/Providers/Resgrid.Providers.Bus.Rabbit/RabbitInboundQueueProvider.cs +++ b/Providers/Resgrid.Providers.Bus.Rabbit/RabbitInboundQueueProvider.cs @@ -36,7 +36,7 @@ public RabbitInboundQueueProvider() public async Task Start(string clientName) { _clientName = clientName; - var connection = RabbitConnection.CreateConnection(clientName); + var connection = await RabbitConnection.CreateConnection(clientName); if (connection != null) { @@ -586,7 +586,7 @@ private async Task RetryQueueItem(BasicDeliverEventArgs ea, Exception mex) //var factory = new ConnectionFactory() { HostName = ServiceBusConfig.RabbitHostname, UserName = ServiceBusConfig.RabbitUsername, Password = ServiceBusConfig.RabbbitPassword }; //using (var connection = RabbitConnection.CreateConnection()) //{ - var connection = RabbitConnection.CreateConnection(_clientName); + var connection = await RabbitConnection.CreateConnection(_clientName); if (connection != null) { using (var channel = await connection.CreateChannelAsync()) diff --git a/Providers/Resgrid.Providers.Bus.Rabbit/RabbitOutboundQueueProvider.cs b/Providers/Resgrid.Providers.Bus.Rabbit/RabbitOutboundQueueProvider.cs index e589e5ac..bbb9781b 100644 --- a/Providers/Resgrid.Providers.Bus.Rabbit/RabbitOutboundQueueProvider.cs +++ b/Providers/Resgrid.Providers.Bus.Rabbit/RabbitOutboundQueueProvider.cs @@ -110,7 +110,7 @@ private async Task SendMessage(string queueName, string message, bool dura try { - var connection = RabbitConnection.CreateConnection(_clientName); + var connection = await RabbitConnection.CreateConnection(_clientName); if (connection != null) { using (var channel = await connection.CreateChannelAsync()) diff --git a/Providers/Resgrid.Providers.Bus.Rabbit/RabbitTopicProvider.cs b/Providers/Resgrid.Providers.Bus.Rabbit/RabbitTopicProvider.cs index f8281eea..29f3ecb8 100644 --- a/Providers/Resgrid.Providers.Bus.Rabbit/RabbitTopicProvider.cs +++ b/Providers/Resgrid.Providers.Bus.Rabbit/RabbitTopicProvider.cs @@ -117,7 +117,7 @@ private static async Task VerifyAndCreateClients(string clientName) { try { - var connection = RabbitConnection.CreateConnection(clientName); + var connection = await RabbitConnection.CreateConnection(clientName); if (connection != null) { @@ -142,7 +142,7 @@ private async Task SendMessage(string topicName, string message) try { - var connection = RabbitConnection.CreateConnection(_clientName); + var connection = await RabbitConnection.CreateConnection(_clientName); if (connection != null) { using (var channel = await connection.CreateChannelAsync()) diff --git a/Repositories/Resgrid.Repositories.DataRepository/CallContactsRepository.cs b/Repositories/Resgrid.Repositories.DataRepository/CallContactsRepository.cs index 54fbb0e7..c995365e 100644 --- a/Repositories/Resgrid.Repositories.DataRepository/CallContactsRepository.cs +++ b/Repositories/Resgrid.Repositories.DataRepository/CallContactsRepository.cs @@ -3,6 +3,14 @@ using Resgrid.Model.Repositories.Connection; using Resgrid.Model.Repositories.Queries; using Resgrid.Repositories.DataRepository.Configs; +using System.Collections.Generic; +using System.Data.Common; +using System.Threading.Tasks; +using System; +using Resgrid.Repositories.DataRepository.Queries.Calls; +using Resgrid.Repositories.DataRepository.Queries.CallContacts; +using Dapper; +using Resgrid.Framework; namespace Resgrid.Repositories.DataRepository { @@ -21,5 +29,46 @@ public CallContactsRepository(IConnectionProvider connectionProvider, SqlConfigu _queryFactory = queryFactory; _unitOfWork = unitOfWork; } + + public async Task> GetCallContactsByCallIdAsync(int callId) + { + try + { + var selectFunction = new Func>>(async x => + { + var dynamicParameters = new DynamicParametersExtension(); + dynamicParameters.Add("CallId", callId); + + 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/Configs/SqlConfiguration.cs b/Repositories/Resgrid.Repositories.DataRepository/Configs/SqlConfiguration.cs index d77381e7..754317c7 100644 --- a/Repositories/Resgrid.Repositories.DataRepository/Configs/SqlConfiguration.cs +++ b/Repositories/Resgrid.Repositories.DataRepository/Configs/SqlConfiguration.cs @@ -446,6 +446,7 @@ protected SqlConfiguration() { } public string CallContactTableName { get; set; } public string SelectContactsByCategoryIdQuery { get; set; } public string SelectContactNotesByContactIdQuery { get; set; } + public string SelectAllCallContactsByCallIdQuery { get; set; } #endregion Contacts // Identity diff --git a/Repositories/Resgrid.Repositories.DataRepository/Queries/CallContacts/SelectAllCallContactsByCallIdQuery.cs b/Repositories/Resgrid.Repositories.DataRepository/Queries/CallContacts/SelectAllCallContactsByCallIdQuery.cs new file mode 100644 index 00000000..7b67a3ff --- /dev/null +++ b/Repositories/Resgrid.Repositories.DataRepository/Queries/CallContacts/SelectAllCallContactsByCallIdQuery.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.CallContacts +{ + public class SelectAllCallContactsByCallIdQuery : ISelectQuery + { + private readonly SqlConfiguration _sqlConfiguration; + public SelectAllCallContactsByCallIdQuery(SqlConfiguration sqlConfiguration) + { + _sqlConfiguration = sqlConfiguration; + } + + public string GetQuery() + { + var query = _sqlConfiguration.SelectAllCallContactsByCallIdQuery + .ReplaceQueryParameters(_sqlConfiguration, _sqlConfiguration.SchemaName, + _sqlConfiguration.CallContactsTable, + _sqlConfiguration.ParameterNotation, + new string[] { "%CALLID%" }, + new string[] { "CallId" }); + + return query; + } + + public string GetQuery() where TEntity : class, IEntity + { + throw new System.NotImplementedException(); + } + } +} diff --git a/Repositories/Resgrid.Repositories.DataRepository/Servers/PostgreSql/PostgreSqlConfiguration.cs b/Repositories/Resgrid.Repositories.DataRepository/Servers/PostgreSql/PostgreSqlConfiguration.cs index 620e53ef..0382b6cc 100644 --- a/Repositories/Resgrid.Repositories.DataRepository/Servers/PostgreSql/PostgreSqlConfiguration.cs +++ b/Repositories/Resgrid.Repositories.DataRepository/Servers/PostgreSql/PostgreSqlConfiguration.cs @@ -1403,6 +1403,10 @@ SELECT dvc.* SELECT * FROM %SCHEMA%.%TABLENAME% WHERE ContactId = %CONTACTID%"; + SelectAllCallContactsByCallIdQuery = @" + SELECT * + FROM %SCHEMA%.%TABLENAME% + WHERE CallId = %CALLID%"; #endregion Contacts } } diff --git a/Repositories/Resgrid.Repositories.DataRepository/Servers/SqlServer/SqlServerConfiguration.cs b/Repositories/Resgrid.Repositories.DataRepository/Servers/SqlServer/SqlServerConfiguration.cs index f2cc9c8e..84f2a67c 100644 --- a/Repositories/Resgrid.Repositories.DataRepository/Servers/SqlServer/SqlServerConfiguration.cs +++ b/Repositories/Resgrid.Repositories.DataRepository/Servers/SqlServer/SqlServerConfiguration.cs @@ -1367,6 +1367,10 @@ SELECT dvc.* SELECT * FROM %SCHEMA%.%TABLENAME% WHERE [ContactId] = %CONTACTID%"; + SelectAllCallContactsByCallIdQuery = @" + SELECT * + FROM %SCHEMA%.%TABLENAME% + WHERE CallId = %CALLID%"; #endregion Contacts } } diff --git a/Web/Resgrid.Web.Services/Controllers/TwilioController.cs b/Web/Resgrid.Web.Services/Controllers/TwilioController.cs index 45dc171f..c4ebb1e4 100644 --- a/Web/Resgrid.Web.Services/Controllers/TwilioController.cs +++ b/Web/Resgrid.Web.Services/Controllers/TwilioController.cs @@ -431,7 +431,7 @@ public async Task VoiceCall(string userId, int callId) { var response = new VoiceResponse(); var call = await _callsService.GetCallByIdAsync(callId); - call = await _callsService.PopulateCallData(call, true, true, false, false, false, false, false, false); + call = await _callsService.PopulateCallData(call, true, true, false, false, false, false, false, false, false); if (call == null) { diff --git a/Web/Resgrid.Web.Services/Controllers/v3/CallsController.cs b/Web/Resgrid.Web.Services/Controllers/v3/CallsController.cs index 0a8c9a35..24c59209 100644 --- a/Web/Resgrid.Web.Services/Controllers/v3/CallsController.cs +++ b/Web/Resgrid.Web.Services/Controllers/v3/CallsController.cs @@ -383,7 +383,7 @@ public async Task>> GetActiveCallsForDep public async Task> GetCall(int callId) { var c = await _callsService.GetCallByIdAsync(callId); - c = await _callsService.PopulateCallData(c, false, true, true, false, false, false, false, true); + c = await _callsService.PopulateCallData(c, false, true, true, false, false, false, false, true, true); var department = await _departmentsService.GetDepartmentByIdAsync(DepartmentId, false); @@ -476,7 +476,7 @@ public async Task> GetCallExtraData(int callId) if (call.DepartmentId != DepartmentId) Unauthorized(); - call = await _callsService.PopulateCallData(call, true, true, true, true, true, true, true, true); + call = await _callsService.PopulateCallData(call, true, true, true, true, true, true, true, true, true); var groups = await _departmentGroupsService.GetAllGroupsForDepartmentAsync(DepartmentId); var units = await _unitsService.GetUnitsForDepartmentAsync(call.DepartmentId); @@ -1138,7 +1138,7 @@ public async Task EditCall([FromBody] EditCallInput editCallInput, { var call = await _callsService.GetCallByIdAsync(editCallInput.Cid); - call = await _callsService.PopulateCallData(call, true, true, true, true, true, true, true, true); + call = await _callsService.PopulateCallData(call, true, true, true, true, true, true, true, true, true); var department = await _departmentsService.GetDepartmentByIdAsync(DepartmentId); if (call == null) @@ -1402,7 +1402,7 @@ public async Task>> GetCallNotes(int callId) if (call.DepartmentId != DepartmentId) return Unauthorized(); - call = await _callsService.PopulateCallData(call, false, false, true, false, false, false, false, false); + call = await _callsService.PopulateCallData(call, false, false, true, false, false, false, false, false, false); if (call.CallNotes == null || !call.CallNotes.Any()) return NotFound(); @@ -1453,7 +1453,7 @@ public async Task>> GetFilesForCall(int callId return Unauthorized(); var department = await _departmentsService.GetDepartmentByIdAsync(DepartmentId); - call = await _callsService.PopulateCallData(call, false, true, false, false, false, false, false, false); + call = await _callsService.PopulateCallData(call, false, true, false, false, false, false, false, false, false); var baseUrl = Config.SystemBehaviorConfig.ResgridApiBaseUrl; diff --git a/Web/Resgrid.Web.Services/Controllers/v4/CallFilesController.cs b/Web/Resgrid.Web.Services/Controllers/v4/CallFilesController.cs index 8f028ad8..9621bcda 100644 --- a/Web/Resgrid.Web.Services/Controllers/v4/CallFilesController.cs +++ b/Web/Resgrid.Web.Services/Controllers/v4/CallFilesController.cs @@ -64,7 +64,7 @@ public async Task> GetFilesForCall(int callId, boo return Unauthorized(); var department = await _departmentsService.GetDepartmentByIdAsync(DepartmentId); - call = await _callsService.PopulateCallData(call, false, true, false, false, false, false, false, false); + call = await _callsService.PopulateCallData(call, false, true, false, false, false, false, false, false, false); if (call.Attachments != null && call.Attachments.Any()) { diff --git a/Web/Resgrid.Web.Services/Controllers/v4/CallNotesController.cs b/Web/Resgrid.Web.Services/Controllers/v4/CallNotesController.cs index 1af49be6..0837b96b 100644 --- a/Web/Resgrid.Web.Services/Controllers/v4/CallNotesController.cs +++ b/Web/Resgrid.Web.Services/Controllers/v4/CallNotesController.cs @@ -65,7 +65,7 @@ public async Task> GetCallNotes(string callId) if (call.DepartmentId != DepartmentId) return Unauthorized(); - call = await _callsService.PopulateCallData(call, false, false, true, false, false, false, false, false); + call = await _callsService.PopulateCallData(call, false, false, true, false, false, false, false, false, false); if (call.CallNotes != null && call.CallNotes.Any()) { diff --git a/Web/Resgrid.Web.Services/Controllers/v4/CallsController.cs b/Web/Resgrid.Web.Services/Controllers/v4/CallsController.cs index 2b96fee9..cc48f184 100644 --- a/Web/Resgrid.Web.Services/Controllers/v4/CallsController.cs +++ b/Web/Resgrid.Web.Services/Controllers/v4/CallsController.cs @@ -105,7 +105,7 @@ public async Task> GetActiveCalls() { foreach (var c in calls) { - var callWithData = await _callsService.PopulateCallData(c, false, true, true, false, false, false, true, true); + var callWithData = await _callsService.PopulateCallData(c, false, true, true, false, false, false, true, true, true); string address = ""; if (String.IsNullOrWhiteSpace(c.Address) && c.HasValidGeolocationData()) @@ -161,7 +161,7 @@ public async Task> GetCall(string callId) if (!await _authorizationService.CanUserViewCallAsync(UserId, int.Parse(callId))) return Unauthorized(); - c = await _callsService.PopulateCallData(c, false, true, true, false, false, false, true, true); + c = await _callsService.PopulateCallData(c, false, true, true, false, false, false, true, true, true); string address = ""; if (String.IsNullOrWhiteSpace(c.Address) && c.HasValidGeolocationData()) @@ -220,7 +220,7 @@ public async Task> GetCallExtraData(int callId if (!await _authorizationService.CanUserViewCallAsync(UserId, callId)) return Unauthorized(); - call = await _callsService.PopulateCallData(call, true, true, true, true, true, true, true, true); + call = await _callsService.PopulateCallData(call, true, true, true, true, true, true, true, true, true); result.Data.CallFormData = call.CallFormData; @@ -829,7 +829,7 @@ public async Task> EditCall([FromBody] EditCallInpu var call = await _callsService.GetCallByIdAsync(int.Parse(editCallInput.Id)); - call = await _callsService.PopulateCallData(call, true, true, true, true, true, true, true, true); + call = await _callsService.PopulateCallData(call, true, true, true, true, true, true, true, true, true); var department = await _departmentsService.GetDepartmentByIdAsync(DepartmentId); if (call == null) @@ -1313,7 +1313,7 @@ public async Task> GetCallHistory(int callId) if (call.DepartmentId != DepartmentId) Unauthorized(); - call = await _callsService.PopulateCallData(call, true, true, true, true, true, true, true, true); + call = await _callsService.PopulateCallData(call, true, true, true, true, true, true, true, true, true); var department = await _departmentsService.GetDepartmentByIdAsync(DepartmentId); result.Data.Add(new CallHistoryResultData() @@ -1521,7 +1521,7 @@ public async Task> GetCalls(DateTime startDate, { foreach (var c in calls) { - var callWithData = await _callsService.PopulateCallData(c, false, true, true, false, false, false, true, true); + var callWithData = await _callsService.PopulateCallData(c, false, true, true, false, false, false, true, true, true); string address = ""; if (String.IsNullOrWhiteSpace(c.Address) && c.HasValidGeolocationData()) diff --git a/Web/Resgrid.Web.Services/Controllers/v4/ConfigController.cs b/Web/Resgrid.Web.Services/Controllers/v4/ConfigController.cs index 001ccebd..3cee21a5 100644 --- a/Web/Resgrid.Web.Services/Controllers/v4/ConfigController.cs +++ b/Web/Resgrid.Web.Services/Controllers/v4/ConfigController.cs @@ -78,6 +78,9 @@ public async Task> GetConfig(string key) result.Data.NovuBackendApiUrl = ChatConfig.NovuBackendUrl; result.Data.NovuSocketUrl = ChatConfig.NovuSocketUrl; + result.Data.PostHogApiKey = TelemetryConfig.PostHogApiKey; + result.Data.PostHogHost = TelemetryConfig.PostHogUrl; + result.PageSize = 1; result.Status = ResponseHelper.Success; ResponseHelper.PopulateV4ResponseData(result); diff --git a/Web/Resgrid.Web.Services/Controllers/v4/ContactsController.cs b/Web/Resgrid.Web.Services/Controllers/v4/ContactsController.cs new file mode 100644 index 00000000..a805fe39 --- /dev/null +++ b/Web/Resgrid.Web.Services/Controllers/v4/ContactsController.cs @@ -0,0 +1,250 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Resgrid.Model.Providers; +using Resgrid.Model.Services; +using Resgrid.Providers.Claims; +using System.Threading.Tasks; +using Resgrid.Web.Services.Helpers; +using Resgrid.Web.Services.Models.v4.CallTypes; +using System.Linq; +using Resgrid.Model; +using Resgrid.Web.Services.Models.v4.Contacts; +using System; +using Resgrid.Model.Helpers; + +namespace Resgrid.Web.Services.Controllers.v4 +{ + /// + /// Contacts, which are people, entities, and things that can be contacted (i.e. people, departments, groups, etc.) to dispatch a call to. + /// + [Route("api/v{VersionId:apiVersion}/[controller]")] + [ApiVersion("4.0")] + [ApiExplorerSettings(GroupName = "v4")] + public class ContactsController : V4AuthenticatedApiControllerbase + { + #region Members and Constructors + private readonly IContactsService _contactsService; + private readonly IDepartmentsService _departmentsService; + private readonly IUserProfileService _userProfileService; + private readonly Model.Services.IAuthorizationService _authorizationService; + private readonly IEventAggregator _eventAggregator; + + public ContactsController( + IContactsService contactsService, + IDepartmentsService departmentsService, + IUserProfileService userProfileService, + Model.Services.IAuthorizationService authorizationService, + IEventAggregator eventAggregator + ) + { + _contactsService = contactsService; + _departmentsService = departmentsService; + _userProfileService = userProfileService; + _authorizationService = authorizationService; + _eventAggregator = eventAggregator; + } + #endregion Members and Constructors + + /// + /// Gets all the contact categories for the department. + /// + /// + [HttpGet("GetAllContactCategories")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Authorize(Policy = ResgridResources.Contacts_View)] + public async Task> GetAllContactCategories() + { + var result = new ContactsCategoriesResult(); + + var contractCategories = await _contactsService.GetContactCategoriesForDepartmentAsync(DepartmentId); + var department = await _departmentsService.GetDepartmentByIdAsync(DepartmentId); + + if (contractCategories != null && contractCategories.Any()) + { + foreach (var category in contractCategories) + { + var addedOnPerson = await _userProfileService.GetProfileByUserIdAsync(category.AddedByUserId); + UserProfile editedPerson = null; + + if (!String.IsNullOrWhiteSpace(category.EditedByUserId)) + editedPerson = await _userProfileService.GetProfileByUserIdAsync(category.AddedByUserId); + + result.Data.Add(ConvertCategoryData(category, department, addedOnPerson, editedPerson)); + } + + result.PageSize = result.Data.Count; + result.Status = ResponseHelper.Success; + } + else + { + result.PageSize = 0; + result.Status = ResponseHelper.NotFound; + } + + ResponseHelper.PopulateV4ResponseData(result); + + return result; + } + + /// + /// Gets all the contacts for the department. + /// + /// + [HttpGet("GetAllContacts")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Authorize(Policy = ResgridResources.Contacts_View)] + public async Task> GetAllContacts() + { + var result = new ContactsResult(); + + var contacts = await _contactsService.GetAllContactsForDepartmentAsync(DepartmentId); + var department = await _departmentsService.GetDepartmentByIdAsync(DepartmentId); + + if (contacts != null && contacts.Any()) + { + foreach (var contact in contacts) + { + var addedOnPerson = await _userProfileService.GetProfileByUserIdAsync(contact.AddedByUserId); + UserProfile editedPerson = null; + + if (!String.IsNullOrWhiteSpace(contact.EditedByUserId)) + editedPerson = await _userProfileService.GetProfileByUserIdAsync(contact.AddedByUserId); + + result.Data.Add(ConvertContactData(contact, department, addedOnPerson, editedPerson)); + } + + result.PageSize = result.Data.Count; + result.Status = ResponseHelper.Success; + } + else + { + result.PageSize = 0; + result.Status = ResponseHelper.NotFound; + } + + ResponseHelper.PopulateV4ResponseData(result); + + return result; + } + + /// + /// Gets the contact by id + /// + /// + [HttpGet("GetContactById")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Authorize(Policy = ResgridResources.Contacts_View)] + public async Task> GetContactById(string contactId) + { + var result = new ContactResult(); + + var contact = await _contactsService.GetContactByIdAsync(contactId); + var department = await _departmentsService.GetDepartmentByIdAsync(DepartmentId); + + if (contact != null && contact.DepartmentId == DepartmentId) + { + var addedOnPerson = await _userProfileService.GetProfileByUserIdAsync(contact.AddedByUserId); + UserProfile editedPerson = null; + + if (!String.IsNullOrWhiteSpace(contact.EditedByUserId)) + editedPerson = await _userProfileService.GetProfileByUserIdAsync(contact.AddedByUserId); + + result.Data = ConvertContactData(contact, department, addedOnPerson, editedPerson); + + result.PageSize = 1; + result.Status = ResponseHelper.Success; + } + else + { + result.PageSize = 0; + result.Status = ResponseHelper.NotFound; + } + + ResponseHelper.PopulateV4ResponseData(result); + + return result; + } + + public static ContactCategoryResultData ConvertCategoryData(ContactCategory category, Department department, UserProfile addedProfile, UserProfile editedProfile) + { + var cat = new ContactCategoryResultData(); + + cat.ContactCategoryId = category.ContactCategoryId; + cat.Name = category.Name; + cat.Description = category.Description; + cat.Color = category.Color; + cat.AddedOnUtc = category.AddedOn; + cat.AddedOn = category.AddedOn.FormatForDepartment(department); + cat.AddedByUserId = category.AddedByUserId; + cat.AddedByUserName = addedProfile.FullName.AsFirstNameLastName; + cat.EditedOnUtc = category.EditedOn; + + if (category.EditedOn.HasValue) + cat.EditedOn = category.EditedOn.Value.FormatForDepartment(department); + + cat.EditedByUserId = category.EditedByUserId; + + if (editedProfile != null) + cat.EditedByUserName = editedProfile.FullName.AsFirstNameLastName; + + return cat; + } + + public static ContactResultData ConvertContactData(Contact contact, Department department, UserProfile addedProfile, UserProfile editedProfile) + { + var con = new ContactResultData(); + + con.ContactId = contact.ContactId; + con.ContactType = contact.ContactType; + con.OtherName = contact.OtherName; + con.ContactCategoryId = contact.ContactCategoryId; + //public virtual ContactCategory Category { get; set; } + con.FirstName = contact.FirstName; + con.MiddleName = contact.MiddleName; + con.LastName = contact.LastName; + con.CompanyName = contact.CompanyName; + con.Email = contact.Email; + con.PhysicalAddressId = contact.PhysicalAddressId; + con.MailingAddressId = contact.MailingAddressId; + con.Website = contact.Website; + con.Twitter = contact.Twitter; + con.Facebook = contact.Facebook; + con.LinkedIn = contact.LinkedIn; + con.Instagram = contact.Instagram; + con.Threads = contact.Threads; + con.Bluesky = contact.Bluesky; + con.Mastodon = contact.Mastodon; + con.LocationGpsCoordinates = contact.LocationGpsCoordinates; + con.EntranceGpsCoordinates = contact.EntranceGpsCoordinates; + con.ExitGpsCoordinates = contact.ExitGpsCoordinates; + con.LocationGeofence = contact.LocationGeofence; + con.CountryIssuedIdNumber = contact.CountryIssuedIdNumber; + con.CountryIdName = contact.CountryIdName; + con.StateIdNumber = contact.StateIdNumber; + con.StateIdName = contact.StateIdName; + con.StateIdCountryName = contact.StateIdCountryName; + con.Description = contact.Description; + con.OtherInfo = contact.OtherInfo; + con.HomePhoneNumber = contact.HomePhoneNumber; + con.CellPhoneNumber = contact.CellPhoneNumber; + con.FaxPhoneNumber = contact.FaxPhoneNumber; + con.OfficePhoneNumber = contact.OfficePhoneNumber; + + con.AddedOnUtc = contact.AddedOn; + con.AddedOn = contact.AddedOn.FormatForDepartment(department); + con.AddedByUserId = contact.AddedByUserId; + con.AddedByUserName = addedProfile.FullName.AsFirstNameLastName; + + con.EditedOnUtc = contact.EditedOn; + if (contact.EditedOn.HasValue) + con.EditedOn = contact.EditedOn.Value.FormatForDepartment(department); + + con.EditedByUserId = contact.EditedByUserId; + if (editedProfile != null) + con.EditedByUserName = editedProfile.FullName.AsFirstNameLastName; + + return con; + } + } +} diff --git a/Web/Resgrid.Web.Services/Models/v4/Configs/GetConfigResult.cs b/Web/Resgrid.Web.Services/Models/v4/Configs/GetConfigResult.cs index f1c4faf7..63114d94 100644 --- a/Web/Resgrid.Web.Services/Models/v4/Configs/GetConfigResult.cs +++ b/Web/Resgrid.Web.Services/Models/v4/Configs/GetConfigResult.cs @@ -105,5 +105,15 @@ public class GetConfigResultData /// Novu Environment Id /// public string NovuEnvironmentId { get; set; } + + /// + /// PostHog Api Key + /// + public string PostHogApiKey { get; set; } + + /// + /// PostHog Host + /// + public string PostHogHost { get; set; } } } diff --git a/Web/Resgrid.Web.Services/Models/v4/Contacts/ContactCategoryResult.cs b/Web/Resgrid.Web.Services/Models/v4/Contacts/ContactCategoryResult.cs new file mode 100644 index 00000000..221140a0 --- /dev/null +++ b/Web/Resgrid.Web.Services/Models/v4/Contacts/ContactCategoryResult.cs @@ -0,0 +1,82 @@ +using System; + +namespace Resgrid.Web.Services.Models.v4.CallTypes +{ + /// + /// Gets the contact category + /// + public class ContactCategoryResult : StandardApiResponseV4Base + { + /// + /// Response Data + /// + public ContactResultData Data { get; set; } + } + + /// + /// A contact category + /// + public class ContactCategoryResultData + { + /// + /// Unique identifier for the contact category + /// + public string ContactCategoryId { get; set; } + + /// + /// Display name of the contact category + /// + public string Name { get; set; } + + /// + /// Optional description of the contact category + /// + public string Description { get; set; } + + /// + /// Color code used for visual identification of this category + /// + public string Color { get; set; } + + /// + /// Date and time when this category was created in UTC + /// + public DateTime AddedOnUtc { get; set; } + + /// + /// Date and time when this category was created in local department time zone + /// + public string AddedOn { get; set; } + + /// + /// User identifier of who created this category + /// + public string AddedByUserId { get; set; } + + /// + /// Name of who created this category + /// + public string AddedByUserName { get; set; } + + /// + /// Date and time when this category was last modified, null if never edited in UTC + /// + public DateTime? EditedOnUtc { get; set; } + + /// + /// Date and time when this category was last modified, null if never edited in local department time zone + /// + public string EditedOn { get; set; } + + /// + /// User identifier of who last modified this category + /// + public string EditedByUserId { get; set; } + + /// + /// Name of who last edited the Category + /// + public string EditedByUserName { get; set; } + + } +} diff --git a/Web/Resgrid.Web.Services/Models/v4/Contacts/ContactResult.cs b/Web/Resgrid.Web.Services/Models/v4/Contacts/ContactResult.cs new file mode 100644 index 00000000..6951e68e --- /dev/null +++ b/Web/Resgrid.Web.Services/Models/v4/Contacts/ContactResult.cs @@ -0,0 +1,113 @@ +using Resgrid.Model; +using System; +using System.Collections.Generic; + +namespace Resgrid.Web.Services.Models.v4.CallTypes +{ + /// + /// Gets the contact + /// + public class ContactResult : StandardApiResponseV4Base + { + /// + /// Response Data + /// + public ContactResultData Data { get; set; } + } + + /// + /// A contact + /// + public class ContactResultData + { + public string ContactId { get; set; } + + public int ContactType { get; set; } // 0 = Person, 1 = Company + + public string OtherName { get; set; } + + public string ContactCategoryId { get; set; } + + public virtual ContactCategory Category { get; set; } + + public string FirstName { get; set; } + + public string MiddleName { get; set; } + + public string LastName { get; set; } + + public string CompanyName { get; set; } + + public string Email { get; set; } + + public int? PhysicalAddressId { get; set; } + + public int? MailingAddressId { get; set; } + + public string Website { get; set; } + + public string Twitter { get; set; } + + public string Facebook { get; set; } + + public string LinkedIn { get; set; } + + public string Instagram { get; set; } + + public string Threads { get; set; } + + public string Bluesky { get; set; } + + public string Mastodon { get; set; } + + public string LocationGpsCoordinates { get; set; } + + public string EntranceGpsCoordinates { get; set; } + + public string ExitGpsCoordinates { get; set; } + + public string LocationGeofence { get; set; } + + public string CountryIssuedIdNumber { get; set; } + + public string CountryIdName { get; set; } + + public string StateIdNumber { get; set; } + + public string StateIdName { get; set; } + + public string StateIdCountryName { get; set; } + + public string Description { get; set; } + + public string OtherInfo { get; set; } + + public string HomePhoneNumber { get; set; } + + public string CellPhoneNumber { get; set; } + + public string FaxPhoneNumber { get; set; } + + public string OfficePhoneNumber { get; set; } + + public byte[] Image { get; set; } + + public bool IsDeleted { get; set; } + + public DateTime AddedOnUtc { get; set; } + + public string AddedOn { get; set; } + + public string AddedByUserId { get; set; } + + public string AddedByUserName { get; set; } + + public DateTime? EditedOnUtc { get; set; } + + public string EditedOn { get; set; } + + public string EditedByUserId { get; set; } + + public string EditedByUserName { get; set; } + } +} diff --git a/Web/Resgrid.Web.Services/Models/v4/Contacts/ContactsCategoriesResult.cs b/Web/Resgrid.Web.Services/Models/v4/Contacts/ContactsCategoriesResult.cs new file mode 100644 index 00000000..782a1cc1 --- /dev/null +++ b/Web/Resgrid.Web.Services/Models/v4/Contacts/ContactsCategoriesResult.cs @@ -0,0 +1,24 @@ +using Resgrid.Web.Services.Models.v4.CallTypes; +using System.Collections.Generic; + +namespace Resgrid.Web.Services.Models.v4.Contacts +{ + /// + /// Gets the contact categories + /// + public class ContactsCategoriesResult : StandardApiResponseV4Base + { + /// + /// Response Data + /// + public List Data { get; set; } + + /// + /// Default constructor + /// + public ContactsCategoriesResult() + { + Data = new List(); + } + } +} diff --git a/Web/Resgrid.Web.Services/Models/v4/Contacts/ContactsResult.cs b/Web/Resgrid.Web.Services/Models/v4/Contacts/ContactsResult.cs new file mode 100644 index 00000000..aa58868f --- /dev/null +++ b/Web/Resgrid.Web.Services/Models/v4/Contacts/ContactsResult.cs @@ -0,0 +1,24 @@ +using Resgrid.Web.Services.Models.v4.CallTypes; +using System.Collections.Generic; + +namespace Resgrid.Web.Services.Models.v4.Contacts +{ + /// + /// Gets the contact + /// + public class ContactsResult : StandardApiResponseV4Base + { + /// + /// Response Data + /// + public List Data { get; set; } + + /// + /// Default constructor + /// + public ContactsResult() + { + Data = new List(); + } + } +} diff --git a/Web/Resgrid.Web.Services/Resgrid.Web.Services.xml b/Web/Resgrid.Web.Services/Resgrid.Web.Services.xml index 71c40b65..0a19b631 100644 --- a/Web/Resgrid.Web.Services/Resgrid.Web.Services.xml +++ b/Web/Resgrid.Web.Services/Resgrid.Web.Services.xml @@ -3360,6 +3360,29 @@ ValidateResult object, with IsValid set if the settings are correct + + + Contacts, which are people, entities, and things that can be contacted (i.e. people, departments, groups, etc.) to dispatch a call to. + + + + + Gets all the contact categories for the department. + + + + + + Gets all the contacts for the department. + + + + + + Gets the contact by id + + + Custom statuses @@ -5288,6 +5311,96 @@ Default constructor + + + Gets the contact category + + + + + Response Data + + + + + A contact category + + + + + Unique identifier for the contact category + + + + + Display name of the contact category + + + + + Optional description of the contact category + + + + + Color code used for visual identification of this category + + + + + Date and time when this category was created in UTC + + + + + Date and time when this category was created in local department time zone + + + + + User identifier of who created this category + + + + + Name of who created this category + + + + + Date and time when this category was last modified, null if never edited in UTC + + + + + Date and time when this category was last modified, null if never edited in local department time zone + + + + + User identifier of who last modified this category + + + + + Name of who last edited the Category + + + + + Gets the contact + + + + + Response Data + + + + + A contact + + Gets Configuration Information by a key @@ -5388,6 +5501,46 @@ Novu Environment Id + + + PostHog Api Key + + + + + PostHog Host + + + + + Gets the contact categories + + + + + Response Data + + + + + Default constructor + + + + + Gets the contact + + + + + Response Data + + + + + Default constructor + + Custom defined Status for Personnel and Units diff --git a/Web/Resgrid.Web/Areas/User/Controllers/ContactsController.cs b/Web/Resgrid.Web/Areas/User/Controllers/ContactsController.cs index 2ea5603f..e81faf26 100644 --- a/Web/Resgrid.Web/Areas/User/Controllers/ContactsController.cs +++ b/Web/Resgrid.Web/Areas/User/Controllers/ContactsController.cs @@ -40,6 +40,7 @@ using SharpKml.Dom.Atom; using SharpKml.Dom; using IAuthorizationService = Resgrid.Model.Services.IAuthorizationService; +using Resgrid.WebCore.Areas.User.Models.Voice; namespace Resgrid.Web.Areas.User.Controllers { @@ -119,6 +120,11 @@ public async Task View(string contactId) model.Department = await _departmentsService.GetDepartmentByIdAsync(DepartmentId); model.Contact = await _contactsService.GetContactByIdAsync(contactId); + var noteTypes = new List(); + noteTypes.Add(new ContactNoteType { ContactNoteTypeId = "", Name = "No Type" }); + noteTypes.AddRange(await _contactsService.GetContactNoteTypesByDepartmentIdAsync(DepartmentId)); + model.NoteTypes = noteTypes; + if (model.Contact == null) Unauthorized(); diff --git a/Web/Resgrid.Web/Areas/User/Controllers/DispatchController.cs b/Web/Resgrid.Web/Areas/User/Controllers/DispatchController.cs index 545d8241..fea83469 100644 --- a/Web/Resgrid.Web/Areas/User/Controllers/DispatchController.cs +++ b/Web/Resgrid.Web/Areas/User/Controllers/DispatchController.cs @@ -62,6 +62,7 @@ public class DispatchController : SecureBaseController private readonly IProtocolsService _protocolsService; private readonly IFormsService _formsService; private readonly IShiftsService _shiftsService; + private readonly IContactsService _contactsService; public DispatchController(IDepartmentsService departmentsService, IUsersService usersService, ICallsService callsService, IDepartmentGroupsService departmentGroupsService, ICommunicationService communicationService, IQueueService queueService, @@ -69,7 +70,7 @@ public DispatchController(IDepartmentsService departmentsService, IUsersService IPersonnelRolesService personnelRolesService, IDepartmentSettingsService departmentSettingsService, IUserProfileService userProfileService, IUnitsService unitsService, IActionLogsService actionLogsService, IEventAggregator eventAggregator, ICustomStateService customStateService, ITemplatesService templatesService, IPdfProvider pdfProvider, IProtocolsService protocolsService, IFormsService formsService, - IShiftsService shiftsService) + IShiftsService shiftsService, IContactsService contactsService) { _departmentsService = departmentsService; _usersService = usersService; @@ -92,6 +93,7 @@ public DispatchController(IDepartmentsService departmentsService, IUsersService _protocolsService = protocolsService; _formsService = formsService; _shiftsService = shiftsService; + _contactsService = contactsService; } #endregion Private Members and Constructors @@ -427,6 +429,33 @@ public async Task NewCall(NewCallView model, IFormCollection coll } } + model.Call.Contacts = new List(); + if (!String.IsNullOrWhiteSpace(model.PrimaryContact)) + { + CallContact contact = new CallContact(); + contact.DepartmentId = DepartmentId; + contact.ContactId = model.PrimaryContact; + contact.CallContactType = 0; + + model.Call.Contacts.Add(contact); + } + + if (model.AdditionalContacts != null && model.AdditionalContacts.Any()) + { + foreach (var additionalContact in model.AdditionalContacts) + { + if (!String.IsNullOrWhiteSpace(additionalContact)) + { + CallContact contact = new CallContact(); + contact.DepartmentId = DepartmentId; + contact.ContactId = additionalContact; + contact.CallContactType = 1; + + model.Call.Contacts.Add(contact); + } + } + } + model.Call.CallSource = (int)CallSources.User; if (!string.IsNullOrWhiteSpace(model.Call.GeoLocationData) && model.Call.GeoLocationData.Length > 1 && string.IsNullOrWhiteSpace(model.Call.Address)) @@ -480,10 +509,10 @@ public async Task UpdateCall(int callId) Unauthorized(); UpdateCallView model = new UpdateCallView(); - model = await FillUpdateCallView(model); model.Call = await _callsService.GetCallByIdAsync(callId); - model.Call = await _callsService.PopulateCallData(model.Call, true, true, true, true, true, true, true, true); + model.Call = await _callsService.PopulateCallData(model.Call, true, true, true, true, true, true, true, true, true); model.CallPriority = model.Call.Priority; + model = await FillUpdateCallView(model); if (!String.IsNullOrEmpty(model.Call.GeoLocationData)) { @@ -508,7 +537,7 @@ public async Task UpdateCall(UpdateCallView model, IFormCollectio if (ModelState.IsValid) { var call = await _callsService.GetCallByIdAsync(model.Call.CallId); - call = await _callsService.PopulateCallData(call, true, true, true, true, true, true, true, true); + call = await _callsService.PopulateCallData(call, true, true, true, true, true, true, true, true, true); call.NatureOfCall = System.Net.WebUtility.HtmlDecode(model.Call.NatureOfCall); call.Notes = System.Net.WebUtility.HtmlDecode(model.Call.Notes); @@ -711,6 +740,37 @@ public async Task UpdateCall(UpdateCallView model, IFormCollectio } } + + var contacts = new List(); + if (!String.IsNullOrWhiteSpace(model.PrimaryContact)) + { + CallContact contact = new CallContact(); + contact.DepartmentId = DepartmentId; + contact.ContactId = model.PrimaryContact; + contact.CallContactType = 0; + + contacts.Add(contact); + } + + if (model.AdditionalContacts != null && model.AdditionalContacts.Any()) + { + foreach (var additionalContact in model.AdditionalContacts) + { + if (!String.IsNullOrWhiteSpace(additionalContact)) + { + CallContact contact = new CallContact(); + contact.DepartmentId = DepartmentId; + contact.ContactId = additionalContact; + contact.CallContactType = 1; + + contacts.Add(contact); + } + } + } + + await _callsService.DeleteCallContactsAsync(call.CallId); + call.Contacts = contacts; + await _callsService.SaveCallAsync(call, cancellationToken); _eventAggregator.SendMessage(new CallUpdatedEvent() { DepartmentId = DepartmentId, Call = call }); @@ -789,7 +849,7 @@ public async Task ViewCall(int callId) model.Stations = await _departmentGroupsService.GetAllStationGroupsForDepartmentAsync(DepartmentId); model.Protocols = await _protocolsService.GetAllProtocolsForDepartmentAsync(DepartmentId); model.ChildCalls = await _callsService.GetChildCallsForCallAsync(callId); - model.Call = await _callsService.PopulateCallData(model.Call, true, true, true, true, true, true, true, true); + model.Call = await _callsService.PopulateCallData(model.Call, true, true, true, true, true, true, true, true, true); if (model.Stations == null) model.Stations = new List(); @@ -1086,7 +1146,7 @@ public async Task FlagCallNote(int callId, int callNoteId) if (call == null) Unauthorized(); - call = await _callsService.PopulateCallData(call, false, false, true, false, false, false, false, false); + call = await _callsService.PopulateCallData(call, false, false, true, false, false, false, false, false, false); var department = await _departmentsService.GetDepartmentByIdAsync(call.DepartmentId); if (call.CallNotes == null || !call.CallNotes.Any()) @@ -1128,7 +1188,7 @@ public async Task FlagCallNote(FlagCallNoteView model, Cancellati if (call == null) Unauthorized(); - call = await _callsService.PopulateCallData(call, false, false, true, false, false, false, false, false); + call = await _callsService.PopulateCallData(call, false, false, true, false, false, false, false, false, false); var department = await _departmentsService.GetDepartmentByIdAsync(call.DepartmentId); if (call.CallNotes == null || !call.CallNotes.Any()) @@ -1210,7 +1270,7 @@ public async Task GetCallNotes(int callId) Unauthorized(); call.Department = await _departmentsService.GetDepartmentByIdAsync(call.DepartmentId); - call = await _callsService.PopulateCallData(call, false, false, true, false, false, false, false, false); + call = await _callsService.PopulateCallData(call, false, false, true, false, false, false, false, false, false); var personnelNames = await _departmentsService.GetAllPersonnelNamesForDepartmentAsync(DepartmentId); List callNotes = new List(); @@ -1261,9 +1321,10 @@ public async Task CallExport(int callId) model.ActionLogs = (await _actionLogsService.GetActionLogsForCallAsync(model.Call.DepartmentId, callId)).OrderBy(x => x.UserId).OrderBy(y => y.Timestamp).ToList(); model.Groups = await _departmentGroupsService.GetAllGroupsForDepartmentAsync(DepartmentId); model.Units = await _unitsService.GetUnitsForDepartmentAsync(DepartmentId); - model.Call = await _callsService.PopulateCallData(model.Call, true, true, true, true, true, true, true, true); + model.Call = await _callsService.PopulateCallData(model.Call, true, true, true, true, true, true, true, true, true); model.Names = await _departmentsService.GetAllPersonnelNamesForDepartmentAsync(DepartmentId); model.ChildCalls = await _callsService.GetChildCallsForCallAsync(callId); + model.Contacts = await _contactsService.GetAllContactsForDepartmentAsync(DepartmentId); return View(model); } @@ -1304,7 +1365,7 @@ public async Task CallExportEx(string query) Unauthorized(); var model = new CallExportView(); - model.Call = await _callsService.PopulateCallData(call, true, true, true, true, true, true, true, true); + model.Call = await _callsService.PopulateCallData(call, true, true, true, true, true, true, true, true, true); model.CallLogs = await _workLogsService.GetCallLogsForCallAsync(call.CallId); model.Department = await _departmentsService.GetDepartmentByIdAsync(model.Call.DepartmentId, false); model.UnitStates = (await _unitsService.GetUnitStatesForCallAsync(model.Call.DepartmentId, call.CallId)).OrderBy(x => x.UnitId).OrderBy(y => y.Timestamp).ToList(); @@ -1313,6 +1374,7 @@ public async Task CallExportEx(string query) model.Units = await _unitsService.GetUnitsForDepartmentAsync(DepartmentId); model.Names = await _departmentsService.GetAllPersonnelNamesForDepartmentAsync(DepartmentId); model.ChildCalls = await _callsService.GetChildCallsForCallAsync(call.CallId); + model.Contacts = await _contactsService.GetAllContactsForDepartmentAsync(DepartmentId); return View(model); } @@ -1332,7 +1394,7 @@ public async Task CallExportEx(string query) Unauthorized(); var model = new CallExportView(); - model.Call = await _callsService.PopulateCallData(call, true, true, true, true, true, true, true, true); + model.Call = await _callsService.PopulateCallData(call, true, true, true, true, true, true, true, true, true); model.CallLogs = await _workLogsService.GetCallLogsForCallAsync(call.CallId); model.Department = await _departmentsService.GetDepartmentByIdAsync(model.Call.DepartmentId, false); model.UnitStates = (await _unitsService.GetUnitStatesForCallAsync(model.Call.DepartmentId, call.CallId)).OrderBy(x => x.UnitId).OrderBy(y => y.Timestamp).ToList(); @@ -1434,7 +1496,7 @@ public async Task GetCallDispatchAudio(int callId) Unauthorized(); var call = await _callsService.GetCallByIdAsync(callId); - call = await _callsService.PopulateCallData(call, false, true, false, false, false, false, false, false); + call = await _callsService.PopulateCallData(call, false, true, false, false, false, false, false, false, false); if (call.Attachments != null && call.Attachments.Count > 0) { @@ -1573,7 +1635,7 @@ public async Task GetAllDispatchesForCall(int callId) List dispatchJson = new List(); var users = await _departmentsService.GetAllUsersForDepartmentUnlimitedMinusDisabledAsync(DepartmentId); var call = await _callsService.GetCallByIdAsync(callId); - call = await _callsService.PopulateCallData(call, true, true, true, true, true, true, true, true); + call = await _callsService.PopulateCallData(call, true, true, true, true, true, true, true, true, true); foreach (var userDispatch in call.Dispatches) { @@ -1925,6 +1987,53 @@ public async Task GetCallTypes() return Json(callTypesJson); } + [HttpGet] + [Authorize(Policy = ResgridResources.Call_View)] + public async Task GetAlertNotesForContact(string contactId) + { + List contactNotesJson = new List(); + + var contact = await _contactsService.GetContactByIdAsync(contactId); + + if (contact != null && contact.DepartmentId == DepartmentId) + { + var notes = await _contactsService.GetContactNotesByContactIdAsync(contactId, DepartmentId, false); + var types = await _contactsService.GetContactNoteTypesByDepartmentIdAsync(DepartmentId); + var department = await _departmentsService.GetDepartmentByIdAsync(DepartmentId); + + if (notes != null && notes.Any()) + { + foreach (var note in notes) + { + if (note.ShouldAlert) + { + ContactNoteJson json = new ContactNoteJson(); + json.Id = note.ContactNoteTypeId; + json.ContactId = contactId; + json.Note = note.Note; + json.ShouldAlert = note.ShouldAlert; + json.AddedOn = note.AddedOn.FormatForDepartment(department, true); + json.AddedBy = await UserHelper.GetFullNameForUser(note.AddedByUserId); + + if (!string.IsNullOrWhiteSpace(note.ContactNoteTypeId)) + { + var type = types.FirstOrDefault(x => x.ContactNoteTypeId == note.ContactNoteTypeId); + if (type != null) + { + json.TypeName = type.Name; + json.TypeColor = type.Color; + } + } + + contactNotesJson.Add(json); + } + } + } + } + + return Json(contactNotesJson); + } + [HttpGet] [Authorize(Policy = ResgridResources.Call_View)] public async Task GetCallPriorities() @@ -2028,6 +2137,20 @@ private async Task FillNewCallView(NewCallView model) if (form != null) model.NewCallFormData = form.Data; + model.Contacts = await _contactsService.GetAllContactsForDepartmentAsync(DepartmentId); + if (model.Contacts != null && model.Contacts.Any()) + { + SelectListItem selListItem = new SelectListItem() { Value = "", Text = "Select Contact" }; + List newList = new List(); + newList.Add(selListItem); + newList.AddRange(new SelectList(model.Contacts, "ContactId", "Name")); + + //Return the list of selectlistitems as a selectlist + model.ContactsList = new SelectList(newList, "Value", "Text", null); + + //model.ContactsList = new SelectList(model.Contacts, "ContactId", "Name"); + } + return model; } @@ -2070,6 +2193,37 @@ private async Task FillUpdateCallView(UpdateCallView model) model.UnGroupedUsers.Add(allUsers.Where(x => x.UserId == u.UserId).FirstOrDefault()); } + model.Contacts = await _contactsService.GetAllContactsForDepartmentAsync(DepartmentId); + if (model.Contacts != null && model.Contacts.Any()) + { + SelectListItem selListItem = new SelectListItem() { Value = "", Text = "Select Contact" }; + List newList = new List(); + newList.Add(selListItem); + newList.AddRange(new SelectList(model.Contacts, "ContactId", "Name")); + + model.ContactsList = new SelectList(newList, "Value", "Text", null); + + if (model.Call.Contacts != null && model.Call.Contacts.Any()) + { + var primaryContact = model.Call.Contacts.FirstOrDefault(x => x.CallContactType == 0); + + if (primaryContact != null) + { + model.PrimaryContact = primaryContact.ContactId; + } + + var additionalContacts = model.Call.Contacts.Where(x => x.CallContactType == 1); + + if (additionalContacts != null && additionalContacts.Any()) + { + foreach (var contact in additionalContacts) + { + model.AdditionalContacts.Add(contact.ContactId); + } + } + } + } + return model; } @@ -2102,7 +2256,7 @@ private async Task FillViewCallView(ViewCallView model) var ungroupedUsers = from u in allUsers where !(groupedUserIds.Contains(u.UserId)) select u; - + foreach (var u in ungroupedUsers) { model.UnGroupedUsers.Add(allUsers.Where(x => x.UserId == u.UserId).FirstOrDefault()); @@ -2116,6 +2270,13 @@ private async Task FillViewCallView(ViewCallView model) else model.Units = new List(); + var contacts = await _contactsService.GetAllContactsForDepartmentAsync(model.Call.DepartmentId); + + if (contacts != null) + model.Contacts = contacts; + else + model.Contacts = new List(); + return model; } diff --git a/Web/Resgrid.Web/Areas/User/Controllers/ReportsController.cs b/Web/Resgrid.Web/Areas/User/Controllers/ReportsController.cs index 47826343..dba28c5a 100644 --- a/Web/Resgrid.Web/Areas/User/Controllers/ReportsController.cs +++ b/Web/Resgrid.Web/Areas/User/Controllers/ReportsController.cs @@ -1766,7 +1766,7 @@ private async Task ActiveCallsResourcesReportModel(int dep summary.LoggedOn = call.LoggedOn.TimeConverter(model.Department); summary.Type = call.Type; - var callData = await _callsService.PopulateCallData(call, true, false, false, true, true, true, false, false); + var callData = await _callsService.PopulateCallData(call, true, false, false, true, true, true, false, false, false); if (callData != null) { diff --git a/Web/Resgrid.Web/Areas/User/Controllers/ShiftsController.cs b/Web/Resgrid.Web/Areas/User/Controllers/ShiftsController.cs index 6a88d4a4..51939b11 100644 --- a/Web/Resgrid.Web/Areas/User/Controllers/ShiftsController.cs +++ b/Web/Resgrid.Web/Areas/User/Controllers/ShiftsController.cs @@ -916,7 +916,7 @@ public async Task ShiftStaffing(ShiftStaffingView model, IFormCol // { // var personId = form[i].ToString(); - + // } //} @@ -1257,13 +1257,19 @@ public async Task GetShiftGroups(int shiftId) var groups = new List(); var shift = await _shiftsService.GetShiftByIdAsync(shiftId); - foreach (var group in shift.Groups) + if (shift != null && shift.Groups != null) { - groups.Add(new + if (shift.DepartmentId != DepartmentId) + Unauthorized(); + + foreach (var group in shift.Groups) { - Id = group.DepartmentGroupId, - Name = group.DepartmentGroup.Name - }); + groups.Add(new + { + Id = group.DepartmentGroupId, + Name = group.DepartmentGroup.Name + }); + } } return Json(groups); @@ -1278,6 +1284,9 @@ public async Task GetShiftDays(int shiftId) if (shift != null && shift.Days != null) { + if (shift.DepartmentId != DepartmentId) + Unauthorized(); + foreach (var day in shift.Days) { days.Add(new @@ -1313,13 +1322,16 @@ public async Task GetShiftsForDepartmentJson() var shiftsJson = new List(); var shifts = await _shiftsService.GetAllShiftsByDepartmentAsync(DepartmentId); - foreach (var shift in shifts) + if (shifts != null && shifts.Any()) { - shiftsJson.Add(new + foreach (var shift in shifts) { - Id = shift.ShiftId, - Name = shift.Name - }); + shiftsJson.Add(new + { + Id = shift.ShiftId, + Name = shift.Name + }); + } } return Json(shiftsJson); diff --git a/Web/Resgrid.Web/Areas/User/Models/Calls/NewCallView.cs b/Web/Resgrid.Web/Areas/User/Models/Calls/NewCallView.cs index e73b8f26..019b4288 100644 --- a/Web/Resgrid.Web/Areas/User/Models/Calls/NewCallView.cs +++ b/Web/Resgrid.Web/Areas/User/Models/Calls/NewCallView.cs @@ -34,10 +34,15 @@ public class NewCallView : BaseUserModel public string ClosedCallNotes { get; set; } public ClosedOnlyCallStates CallState { get; set; } public SelectList CallStates { get; set; } + public List Contacts { get; set; } + public SelectList ContactsList { get; set; } + public string PrimaryContact { get; set; } + public List AdditionalContacts { get; set; } public NewCallView() { What3Words = new W3W(); + AdditionalContacts = new List(); } } } diff --git a/Web/Resgrid.Web/Areas/User/Models/Calls/UpdateCallView.cs b/Web/Resgrid.Web/Areas/User/Models/Calls/UpdateCallView.cs index 7a450e59..115e1cfb 100644 --- a/Web/Resgrid.Web/Areas/User/Models/Calls/UpdateCallView.cs +++ b/Web/Resgrid.Web/Areas/User/Models/Calls/UpdateCallView.cs @@ -26,5 +26,9 @@ public class UpdateCallView: BaseUserModel public bool RebroadcastCall { get; set; } public List Units { get; set; } public List UnitStates { get; set; } + public List Contacts { get; set; } + public SelectList ContactsList { get; set; } + public string PrimaryContact { get; set; } + public List AdditionalContacts { get; set; } } } diff --git a/Web/Resgrid.Web/Areas/User/Models/Calls/ViewCallView.cs b/Web/Resgrid.Web/Areas/User/Models/Calls/ViewCallView.cs index ad51734b..db10d1e5 100644 --- a/Web/Resgrid.Web/Areas/User/Models/Calls/ViewCallView.cs +++ b/Web/Resgrid.Web/Areas/User/Models/Calls/ViewCallView.cs @@ -25,6 +25,7 @@ public class ViewCallView: BaseUserModel public List Stations { get; set; } public List Protocols { get; set; } public List ChildCalls { get; set; } + public List Contacts { get; set; } public string IsMapTabActive() { diff --git a/Web/Resgrid.Web/Areas/User/Models/Dispatch/CallExportView.cs b/Web/Resgrid.Web/Areas/User/Models/Dispatch/CallExportView.cs index 6b632ebc..97b9780b 100644 --- a/Web/Resgrid.Web/Areas/User/Models/Dispatch/CallExportView.cs +++ b/Web/Resgrid.Web/Areas/User/Models/Dispatch/CallExportView.cs @@ -22,5 +22,6 @@ public class CallExportView : BaseUserModel public string EndLon { get; set; } public List Names { get; set; } public List ChildCalls { get; set; } + public List Contacts { get; set; } } } diff --git a/Web/Resgrid.Web/Areas/User/Models/Dispatch/ContactNoteJson.cs b/Web/Resgrid.Web/Areas/User/Models/Dispatch/ContactNoteJson.cs new file mode 100644 index 00000000..eae2b4f7 --- /dev/null +++ b/Web/Resgrid.Web/Areas/User/Models/Dispatch/ContactNoteJson.cs @@ -0,0 +1,14 @@ +namespace Resgrid.WebCore.Areas.User.Models.Dispatch +{ + public class ContactNoteJson + { + public string Id { get; set; } + public string ContactId { get; set; } + public string TypeName { get; set; } + public string TypeColor { get; set; } + public string Note { get; set; } + public bool ShouldAlert { get; set; } + public string AddedOn { get; set; } + public string AddedBy { get; set; } + } +} diff --git a/Web/Resgrid.Web/Areas/User/Views/Contacts/View.cshtml b/Web/Resgrid.Web/Areas/User/Views/Contacts/View.cshtml index ed1bc365..18213981 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Contacts/View.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Contacts/View.cshtml @@ -746,7 +746,7 @@ quillNote.root.innerHTML = ''; // Refresh notes container - refreshNotesContainer(); + loadNotes(); // Show success message toastr.success('Note added successfully'); @@ -827,24 +827,23 @@

- ${call.PriorityText} - ${call.Name} + ${call.PriorityName} + ${call.CallName}

- ${call.Number} + ${call.CallNumber} | ${call.LoggedOn}

-

${call.NatureOfCall || ''}

-

Address: ${call.Address || 'No address provided'}

+

${call.CallNature || ''}

@@ -853,6 +852,8 @@
`); + + $container.append($callCard); }) } diff --git a/Web/Resgrid.Web/Areas/User/Views/Dispatch/CallExport.cshtml b/Web/Resgrid.Web/Areas/User/Views/Dispatch/CallExport.cshtml index 49674f5e..42c86b58 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Dispatch/CallExport.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Dispatch/CallExport.cshtml @@ -220,6 +220,38 @@ + @if (Model.Call.Contacts != null && Model.Call.Contacts.Any()) + { +
+
+ @localizer["Contacts"] + + + + + + + + + + @foreach (var c in Model.Call.Contacts) + { + var contact = Model.Contacts.FirstOrDefault(x => x.ContactId == c.ContactId); + + if (contact != null) + { + + + + + + } + } + +
@commonLocalizer["Name"]@commonLocalizer["Type"]@localizer["CallContactType"]
@contact.GetName()@contact.GetTypeName()@c.GetContactTypeName()
+
+
+ }
@localizer["DispatchedUnits"] diff --git a/Web/Resgrid.Web/Areas/User/Views/Dispatch/CallExportEx.cshtml b/Web/Resgrid.Web/Areas/User/Views/Dispatch/CallExportEx.cshtml index 7a6a77e4..c459b5c9 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Dispatch/CallExportEx.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Dispatch/CallExportEx.cshtml @@ -195,6 +195,38 @@
+ @if (Model.Call.Contacts != null && Model.Call.Contacts.Any()) + { +
+
+ @localizer["Contacts"] + + + + + + + + + + @foreach (var c in Model.Call.Contacts) + { + var contact = Model.Contacts.FirstOrDefault(x => x.ContactId == c.ContactId); + + if (contact != null) + { + + + + + + } + } + +
@commonLocalizer["Name"]@commonLocalizer["Type"]@localizer["CallContactType"]
@contact.GetName()@contact.GetTypeName()@c.GetContactTypeName()
+
+
+ }
@localizer["DispatchedUnits"] diff --git a/Web/Resgrid.Web/Areas/User/Views/Dispatch/NewCall.cshtml b/Web/Resgrid.Web/Areas/User/Views/Dispatch/NewCall.cshtml index 5e365426..0cbd5b7e 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Dispatch/NewCall.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Dispatch/NewCall.cshtml @@ -14,6 +14,14 @@ .select2-dropdown { z-index: 3051; } + + .alert-icon { + cursor: pointer; + font-size: 18px; + padding-left: 5px; + vertical-align: middle; + display: none; + } } @@ -90,6 +98,24 @@
+
+ +
+ + + + +
+
+
+ +
+ + + + +
+
@@ -454,6 +480,24 @@
+ + + @section Scripts { @@ -475,4 +519,99 @@ } + + } diff --git a/Web/Resgrid.Web/Areas/User/Views/Dispatch/UpdateCall.cshtml b/Web/Resgrid.Web/Areas/User/Views/Dispatch/UpdateCall.cshtml index ad455a6b..80121f07 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Dispatch/UpdateCall.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Dispatch/UpdateCall.cshtml @@ -14,6 +14,14 @@ .select2-dropdown { z-index: 3051; } + + .alert-icon { + cursor: pointer; + font-size: 18px; + padding-left: 5px; + vertical-align: middle; + display: none; + } } @@ -79,6 +87,24 @@
+
+ +
+ + + + +
+
+
+ +
+ + + + +
+
@@ -252,4 +278,104 @@ + + } diff --git a/Web/Resgrid.Web/Areas/User/Views/Dispatch/ViewCall.cshtml b/Web/Resgrid.Web/Areas/User/Views/Dispatch/ViewCall.cshtml index b0ed4aad..e7316e8c 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Dispatch/ViewCall.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Dispatch/ViewCall.cshtml @@ -10,33 +10,33 @@ } @section Styles { - - - + + + } - @Html.HiddenFor(m => m.Latitude) - @Html.HiddenFor(m => m.Longitude) - -
-
-

@localizer["ViewCallHeader"]

- -
-
-
-
- @if (ClaimsAuthorizationHelper.CanCreateCall() && Model.Call.State != 0) +@Html.HiddenFor(m => m.Latitude) +@Html.HiddenFor(m => m.Longitude) + +
+
+

@localizer["ViewCallHeader"]

+ +
+
+
+
+ @if (ClaimsAuthorizationHelper.CanCreateCall() && Model.Call.State != 0) { } @@ -180,6 +180,7 @@
  • +
  • @if (!String.IsNullOrWhiteSpace(Model.Call.CallFormData)) {
  • @@ -634,6 +635,49 @@
    +
    + + + + + + + + + + + @foreach (var callContact in Model.Call.Contacts) + { + var contact = Model.Contacts.FirstOrDefault(x => x.ContactId == callContact.ContactId); + + if (contact != null) + { + + + + + + + } + } + +
    @commonLocalizer["Type"]@commonLocalizer["Name"]@localizer["CallContactType"]
    + @contact.GetTypeName() + + @contact.Name + + @if (callContact.CallContactType == 0) + { + Primary + } + else + { + Additional + } + + @commonLocalizer["View"] +
    +
    @if (!String.IsNullOrWhiteSpace(Model.Call.CallFormData)) {
    diff --git a/Web/Resgrid.Web/Areas/User/Views/Subscription/Index.cshtml b/Web/Resgrid.Web/Areas/User/Views/Subscription/Index.cshtml index 59e1ca5d..1da4ba9c 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Subscription/Index.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Subscription/Index.cshtml @@ -99,7 +99,7 @@ .total { border-bottom: 1px solid #7f8c8d; /*display: inline; - padding: 10px 5px;*/ + padding: 10px 5px;*/ position: relative; padding-bottom: 20px; } @@ -140,8 +140,8 @@ border: 1px solid #eeeeee; border-radius: 4px; /*-moz-box-shadow: 0 5px 5px 0 #ccc; - -webkit-box-shadow: 0 5px 5px 0 #ccc; - box-shadow: 0 5px 5px 0 #ccc;*/ + -webkit-box-shadow: 0 5px 5px 0 #ccc; + box-shadow: 0 5px 5px 0 #ccc;*/ } .form-group { @@ -450,9 +450,15 @@
    + +
    + @*

    The Push-To-Talk Addon is only available on paid plans. You can buy as many 10 user packs as you wish, and they are billed monthly separately from your Resgrid bill. Allowing you to size up and down as you need. You can have up to 50 active people per channel. Push-To-Talk requires devices with an active Internet (data) connection and uses VOIP technology.

    Manage PTT -
    +
    *@
    diff --git a/Web/Resgrid.Web/wwwroot/js/app/internal/dispatch/resgrid.dispatch.editcall.js b/Web/Resgrid.Web/wwwroot/js/app/internal/dispatch/resgrid.dispatch.editcall.js index 48b2e02d..d83b0a1f 100644 --- a/Web/Resgrid.Web/wwwroot/js/app/internal/dispatch/resgrid.dispatch.editcall.js +++ b/Web/Resgrid.Web/wwwroot/js/app/internal/dispatch/resgrid.dispatch.editcall.js @@ -27,6 +27,9 @@ var resgrid; } }); + $('#PrimaryContact').select2(); + $('#AdditionalContacts').select2(); + let quillNote = new Quill('#note-container', { placeholder: '', theme: 'snow' diff --git a/Web/Resgrid.Web/wwwroot/js/app/internal/dispatch/resgrid.dispatch.newcall.js b/Web/Resgrid.Web/wwwroot/js/app/internal/dispatch/resgrid.dispatch.newcall.js index adb5b7c7..f1dde5c3 100644 --- a/Web/Resgrid.Web/wwwroot/js/app/internal/dispatch/resgrid.dispatch.newcall.js +++ b/Web/Resgrid.Web/wwwroot/js/app/internal/dispatch/resgrid.dispatch.newcall.js @@ -17,6 +17,9 @@ var resgrid; theme: 'snow' }); + $('#PrimaryContact').select2(); + $('#AdditionalContacts').select2(); + $("#Call_Address").bind("keypress", function (event) { if (event.keyCode == 13) { $("#searchButton").click(); diff --git a/Workers/Resgrid.Workers.Console/Tasks/DispatchScheduledCallsTask.cs b/Workers/Resgrid.Workers.Console/Tasks/DispatchScheduledCallsTask.cs index 3d274f33..91afc1e6 100644 --- a/Workers/Resgrid.Workers.Console/Tasks/DispatchScheduledCallsTask.cs +++ b/Workers/Resgrid.Workers.Console/Tasks/DispatchScheduledCallsTask.cs @@ -41,7 +41,7 @@ public async Task ProcessAsync(DispatchScheduledCallsCommand command, IQuidjiboP foreach (var call in pendingCalls) { var cqi = new CallQueueItem(); - cqi.Call = await callsService.PopulateCallData(call, true, false, false, true, true, true, true, false); + cqi.Call = await callsService.PopulateCallData(call, true, false, false, true, true, true, true, false, false); if (cqi.Call.Dispatches != null && cqi.Call.Dispatches.Any()) cqi.Profiles = await _userProfileService.GetSelectedUserProfilesAsync(cqi.Call.Dispatches.Select(x => x.UserId).ToList());