From f1e7368f4ab426a9612a0f940ce1cdcfb9bc4b97 Mon Sep 17 00:00:00 2001 From: Shawn Jackson Date: Tue, 17 Mar 2026 14:34:31 -0700 Subject: [PATCH 1/3] RE1-T105 More custom maps work --- Core/Resgrid.Config/MappingConfig.cs | 6 + .../Areas/User/CustomMaps/CustomMaps.cs | 6 + .../Areas/User/CustomMaps/CustomMaps.en.resx | 368 ++++++++++++++++++ .../Areas/User/IndoorMaps/IndoorMaps.cs | 6 + .../Areas/User/IndoorMaps/IndoorMaps.en.resx | 259 ++++++++++++ .../Areas/User/Mapping/Mapping.cs | 6 + .../Areas/User/Mapping/Mapping.en.resx | 287 ++++++++++++++ .../Areas/User/Routes/Routes.cs | 6 + .../Areas/User/Routes/Routes.en.resx | 342 ++++++++++++++++ Core/Resgrid.Model/PermissionTypes.cs | 3 +- .../Resgrid.Providers.Claims/ClaimsLogic.cs | 7 +- .../GeoLocationProvider.cs | 2 +- .../Controllers/v4/GeocodingController.cs | 99 +++++ .../Models/v4/Geocoding/GeocodingResults.cs | 45 +++ .../Resgrid.Web.Services.xml | 47 +++ Web/Resgrid.Web.Services/appsettings.json | 10 + .../User/Controllers/CustomMapsController.cs | 12 +- .../User/Controllers/DispatchController.cs | 5 + .../User/Controllers/IndoorMapsController.cs | 27 ++ .../User/Controllers/RoutesController.cs | 91 ++++- .../Areas/User/Views/Calendar/Edit.cshtml | 2 +- .../Areas/User/Views/Calendar/New.cshtml | 2 +- .../Areas/User/Views/CustomMaps/Edit.cshtml | 49 +-- .../Areas/User/Views/CustomMaps/Import.cshtml | 33 +- .../Areas/User/Views/CustomMaps/Index.cshtml | 37 +- .../Areas/User/Views/CustomMaps/Layers.cshtml | 55 +-- .../Areas/User/Views/CustomMaps/New.cshtml | 49 +-- .../User/Views/CustomMaps/RegionEditor.cshtml | 87 +++-- .../Views/Dispatch/AddArchivedCall.cshtml | 4 +- .../User/Views/Dispatch/CallExport.cshtml | 6 +- .../User/Views/Dispatch/CallExportEx.cshtml | 112 +++--- .../User/Views/Dispatch/Dashboard.cshtml | 12 +- .../Areas/User/Views/Dispatch/NewCall.cshtml | 3 +- .../User/Views/Dispatch/UpdateCall.cshtml | 3 +- .../Areas/User/Views/Dispatch/ViewCall.cshtml | 8 +- .../Areas/User/Views/Groups/Geofence.cshtml | 40 +- .../Areas/User/Views/IndoorMaps/Edit.cshtml | 43 +- .../Areas/User/Views/IndoorMaps/Floors.cshtml | 37 +- .../Areas/User/Views/IndoorMaps/Index.cshtml | 31 +- .../Areas/User/Views/IndoorMaps/New.cshtml | 43 +- .../User/Views/IndoorMaps/ZoneEditor.cshtml | 61 +-- .../Areas/User/Views/Logs/LogExport.cshtml | 6 +- .../Areas/User/Views/Mapping/AddPOI.cshtml | 27 +- .../User/Views/Mapping/AddPOIType.cshtml | 31 +- .../Areas/User/Views/Mapping/EditLayer.cshtml | 57 +-- .../User/Views/Mapping/ImportPOIs.cshtml | 21 +- .../Areas/User/Views/Mapping/Index.cshtml | 35 +- .../Areas/User/Views/Mapping/Layers.cshtml | 25 +- .../User/Views/Mapping/LiveRouting.cshtml | 111 +++--- .../Areas/User/Views/Mapping/NewLayer.cshtml | 57 +-- .../Areas/User/Views/Mapping/POIs.cshtml | 31 +- .../User/Views/Mapping/StationRouting.cshtml | 113 +++--- .../Areas/User/Views/Mapping/ViewType.cshtml | 46 +-- .../User/Views/Reports/ActionLogs.cshtml | 6 +- .../Reports/ActiveCallsResourcesReport.cshtml | 6 +- .../Views/Reports/CallSummaryReport.cshtml | 6 +- .../Views/Reports/CertificationsReport.cshtml | 6 +- .../Reports/DepartmentActivityReport.cshtml | 6 +- .../Reports/FlaggedCallNotesReport.cshtml | 6 +- .../Areas/User/Views/Reports/LogReport.cshtml | 6 +- .../Reports/PersonnelEventsReport.cshtml | 6 +- .../Reports/PersonnelHoursDetailReport.cshtml | 6 +- .../Views/Reports/PersonnelHoursReport.cshtml | 6 +- .../User/Views/Reports/PersonnelReport.cshtml | 6 +- .../PersonnelStaffingHistoryReport.cshtml | 6 +- .../User/Views/Reports/StaffingReport.cshtml | 6 +- .../Views/Reports/UnitEventsReport.cshtml | 6 +- .../Reports/UnitStateHistoryReport.cshtml | 6 +- .../UpcomingShiftReadinessReport.cshtml | 6 +- .../User/Views/Routes/ActiveRoutes.cshtml | 23 +- .../Areas/User/Views/Routes/Edit.cshtml | 200 ++++++++-- .../Areas/User/Views/Routes/Index.cshtml | 33 +- .../User/Views/Routes/InstanceDetail.cshtml | 39 +- .../Areas/User/Views/Routes/Instances.cshtml | 25 +- .../Areas/User/Views/Routes/New.cshtml | 55 +-- .../Areas/User/Views/Routes/View.cshtml | 29 +- .../User/Views/Shared/_ChatWidget.cshtml | 4 +- .../Views/Subscription/ViewInvoice.cshtml | 6 +- .../Views/Account/AffiliateRegister.cshtml | 2 +- .../Views/Account/CompletedInvite.cshtml | 6 +- .../Views/Account/ForgotPassword.cshtml | 2 +- Web/Resgrid.Web/Views/Account/Lockout.cshtml | 6 +- .../Views/Account/MissingCode.cshtml | 8 +- .../Views/Account/MissingInvite.cshtml | 6 +- Web/Resgrid.Web/Views/Account/Register.cshtml | 2 +- Web/Resgrid.Web/Views/Shared/Error.cshtml | 6 +- .../Views/Shared/Unauthorized.cshtml | 6 +- .../Views/Shared/_ScriptsPartial.cshtml | 4 +- .../Views/Shared/_StylePartial.cshtml | 2 +- Web/Resgrid.Web/libman.json | 2 +- .../custommaps/resgrid.custommaps.editor.js | 98 ++--- .../resgrid.custommaps.regioneditor.js | 8 +- .../resgrid.dispatch.addArchivedCall.js | 83 ++-- .../dispatch/resgrid.dispatch.editcall.js | 87 ++--- .../dispatch/resgrid.dispatch.newcall.js | 90 ++--- .../groups/resgrid.groups.geofence.js | 134 ++++--- .../indoormaps/resgrid.indoormaps.editor.js | 4 +- .../resgrid.indoormaps.zoneeditor.js | 4 +- .../mapping/resgrid.mapping.viewType.js | 146 ++----- .../internal/routes/resgrid.routes.edit.js | 267 ++++++++++++- .../routes/resgrid.routes.instancedetail.js | 2 +- .../internal/routes/resgrid.routes.view.js | 13 +- 102 files changed, 3115 insertions(+), 1294 deletions(-) create mode 100644 Core/Resgrid.Localization/Areas/User/CustomMaps/CustomMaps.cs create mode 100644 Core/Resgrid.Localization/Areas/User/CustomMaps/CustomMaps.en.resx create mode 100644 Core/Resgrid.Localization/Areas/User/IndoorMaps/IndoorMaps.cs create mode 100644 Core/Resgrid.Localization/Areas/User/IndoorMaps/IndoorMaps.en.resx create mode 100644 Core/Resgrid.Localization/Areas/User/Mapping/Mapping.cs create mode 100644 Core/Resgrid.Localization/Areas/User/Mapping/Mapping.en.resx create mode 100644 Core/Resgrid.Localization/Areas/User/Routes/Routes.cs create mode 100644 Core/Resgrid.Localization/Areas/User/Routes/Routes.en.resx create mode 100644 Web/Resgrid.Web.Services/Controllers/v4/GeocodingController.cs create mode 100644 Web/Resgrid.Web.Services/Models/v4/Geocoding/GeocodingResults.cs diff --git a/Core/Resgrid.Config/MappingConfig.cs b/Core/Resgrid.Config/MappingConfig.cs index f127a2ce..0622b9e2 100644 --- a/Core/Resgrid.Config/MappingConfig.cs +++ b/Core/Resgrid.Config/MappingConfig.cs @@ -59,6 +59,12 @@ public static class MappingConfig public static string LeafletAttribution = "© OpenStreetMap contributors CC-BY-SA"; + /*********************************** + * Geocoding and Routing Service URLs + ***********************************/ + public static string NominatimUrl = "https://nominatim.openstreetmap.org"; + public static string OsrmUrl = "https://router.project-osrm.org"; + public static string GetWebsiteOSMUrl() { if (!string.IsNullOrWhiteSpace(WebsiteOSMKey)) diff --git a/Core/Resgrid.Localization/Areas/User/CustomMaps/CustomMaps.cs b/Core/Resgrid.Localization/Areas/User/CustomMaps/CustomMaps.cs new file mode 100644 index 00000000..4aec7704 --- /dev/null +++ b/Core/Resgrid.Localization/Areas/User/CustomMaps/CustomMaps.cs @@ -0,0 +1,6 @@ +namespace Resgrid.Localization.Areas.User.CustomMaps +{ + public class CustomMaps + { + } +} diff --git a/Core/Resgrid.Localization/Areas/User/CustomMaps/CustomMaps.en.resx b/Core/Resgrid.Localization/Areas/User/CustomMaps/CustomMaps.en.resx new file mode 100644 index 00000000..d9fdbed5 --- /dev/null +++ b/Core/Resgrid.Localization/Areas/User/CustomMaps/CustomMaps.en.resx @@ -0,0 +1,368 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Cancel + + + Save + + + Name + + + Description + + + Edit + + + Delete + + + Import + + + Type + + + Created + + + Order + + + Yes + + + No + + + + Custom Maps + + + Custom Maps + + + New Custom Map + + + All + + + Indoor + + + Outdoor + + + Event + + + Custom + + + Layers + + + Are you sure you want to delete this map? + + + + New Custom Map + + + New Custom Map + + + New + + + Map Type + + + Indoor + + + Outdoor + + + Event + + + Custom + + + e.g. Main Hospital, Concert Venue + + + Optional description + + + Draw Bounds + + + Draw a rectangle on the map below to define the bounds. Click the map to set the center point. + + + Draw a rectangle on the map below to update the bounds. Click the map to set the center point. + + + + Edit Custom Map + + + Edit Custom Map + + + Edit + + + + Layers + + + Layers + + + Layers + + + Add Layer + + + Layer Name + + + e.g. Floor 1, Base Map, Infrastructure + + + Order + + + Type + + + Floor Plan + + + Overlay + + + Data Layer + + + Infrastructure + + + Layer Image + + + Images larger than 2048px will be automatically tiled. + + + Add Layer + + + Has Image + + + Tiled + + + Region Editor + + + Delete this layer? + + + + Region Editor + + + Region Editor + + + Regions + + + Region Properties + + + Name + + + Type + + + Room + + + Wing + + + Corridor + + + Stairwell + + + Elevator + + + Hazard Zone + + + Assembly Point + + + Staging Area + + + Access Point + + + Utility + + + Search Grid + + + Stage + + + Gate + + + Entrance + + + Parking Area + + + Vendor Area + + + Emergency Lane + + + District + + + Custom + + + Color + + + Description + + + Searchable in dispatch + + + Dispatchable + + + Save Region + + + Dispatchable + + + + Import + + + Import + + + Import + + + Import Geospatial Data + + + Target Layer + + + File + + + Supported formats: GeoJSON (.geojson, .json), KML (.kml), KMZ (.kmz) + + + Import + + + Import History + + + Status + + + Date + + + Error + + diff --git a/Core/Resgrid.Localization/Areas/User/IndoorMaps/IndoorMaps.cs b/Core/Resgrid.Localization/Areas/User/IndoorMaps/IndoorMaps.cs new file mode 100644 index 00000000..fc51574b --- /dev/null +++ b/Core/Resgrid.Localization/Areas/User/IndoorMaps/IndoorMaps.cs @@ -0,0 +1,6 @@ +namespace Resgrid.Localization.Areas.User.IndoorMaps +{ + public class IndoorMaps + { + } +} diff --git a/Core/Resgrid.Localization/Areas/User/IndoorMaps/IndoorMaps.en.resx b/Core/Resgrid.Localization/Areas/User/IndoorMaps/IndoorMaps.en.resx new file mode 100644 index 00000000..f03fe729 --- /dev/null +++ b/Core/Resgrid.Localization/Areas/User/IndoorMaps/IndoorMaps.en.resx @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Cancel + + + Save + + + Name + + + Description + + + Edit + + + Delete + + + Created + + + Yes + + + No + + + + Indoor Maps + + + Indoor Maps + + + New Indoor Map + + + Floors + + + Are you sure you want to delete this indoor map? + + + + New Indoor Map + + + New Indoor Map + + + New + + + e.g. Main Hospital + + + Optional description + + + Center Latitude + + + Center Longitude + + + e.g. 39.7392 + + + e.g. -104.9903 + + + NE Bound Lat + + + NE Bound Lon + + + SW Bound Lat + + + SW Bound Lon + + + + Edit Indoor Map + + + Edit Indoor Map + + + Edit + + + + Floors + + + Floors + + + Add Floor + + + Floor Name + + + e.g. Floor 1, Basement, Roof + + + Order + + + Floor Plan Image + + + Add Floor + + + Floors + + + Has Image + + + Zone Editor + + + Delete this floor? + + + + Zone Editor + + + Zone Editor + + + Zones + + + Zone Properties + + + Name + + + Type + + + Room + + + Wing + + + Corridor + + + Stairwell + + + Elevator + + + Hazard Zone + + + Assembly Point + + + Staging Area + + + Access Point + + + Utility + + + Search Grid + + + Custom + + + Color + + + Description + + + Searchable in dispatch + + + Save Zone + + diff --git a/Core/Resgrid.Localization/Areas/User/Mapping/Mapping.cs b/Core/Resgrid.Localization/Areas/User/Mapping/Mapping.cs new file mode 100644 index 00000000..585880b0 --- /dev/null +++ b/Core/Resgrid.Localization/Areas/User/Mapping/Mapping.cs @@ -0,0 +1,6 @@ +namespace Resgrid.Localization.Areas.User.Mapping +{ + public class Mapping + { + } +} diff --git a/Core/Resgrid.Localization/Areas/User/Mapping/Mapping.en.resx b/Core/Resgrid.Localization/Areas/User/Mapping/Mapping.en.resx new file mode 100644 index 00000000..2a0cb5dc --- /dev/null +++ b/Core/Resgrid.Localization/Areas/User/Mapping/Mapping.en.resx @@ -0,0 +1,287 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Mapping + + + Manage Layers + + + Custom Maps + + + Map Options + + + Show Calls + + + Show Personnel + + + Show Units + + + Show Stations + + + Show Geofences + + + Show POIs + + + Close + + + Save + + + Cancel + + + + Add POI + + + Add POI + + + Latitude + + + Latitude (Decimal Notation: i.e. 39.1517) + + + Longitude + + + Longitude (Decimal Notation: i.e. -119.4571) + + + Note + + + + Add POI Type + + + Add POI Type + + + Name + + + Is Destination + + + To denote a potential destination for a unit/person. For example an office, rally point, hospital, access point, etc. + + + Color + + + Marker + + + Icon + + + None + + + + Edit Layer + + + Edit Layer + + + New Layer + + + New Layer + + + Is Visible By Default + + + Do you want this layer to be visible every time a map loads? Note: Having too many layers visible by default will complicate the map. Users can turn layers on if they want to see them in the map control. + + + Is Destination + + + This will allow address searching to use coordinates from this layer in the search. Note, if you don't have searchable points (text fields) don't mark as searchable as it can slow down dispatching. + + + Layer Elements + + + Edit Layer + + + Add Layer + + + + Import POIs + + + Import POIs + + + Upload + + + Import POIs + + + + Layers + + + Layers + + + New Layer + + + Item Count + + + Edit + + + Delete + + + + Live Navigation + + + Live Routing + + + + POIs + + + POIs + + + Add POI Type + + + Point Count + + + View + + + Add + + + Import + + + WARNING: This will permanently delete this POI Type and all it's positions. Are you sure you want to delete the + + + POI Type? + + + + Station Navigation + + + Station Routing + + + Distance: + + + Duration: + + + + View Type + + + View Type + + + Map + + + Information + + + List + + + True + + + False + + diff --git a/Core/Resgrid.Localization/Areas/User/Routes/Routes.cs b/Core/Resgrid.Localization/Areas/User/Routes/Routes.cs new file mode 100644 index 00000000..85f715db --- /dev/null +++ b/Core/Resgrid.Localization/Areas/User/Routes/Routes.cs @@ -0,0 +1,6 @@ +namespace Resgrid.Localization.Areas.User.Routes +{ + public class Routes + { + } +} diff --git a/Core/Resgrid.Localization/Areas/User/Routes/Routes.en.resx b/Core/Resgrid.Localization/Areas/User/Routes/Routes.en.resx new file mode 100644 index 00000000..5643d166 --- /dev/null +++ b/Core/Resgrid.Localization/Areas/User/Routes/Routes.en.resx @@ -0,0 +1,342 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Cancel + + + Save + + + Name + + + Description + + + Status + + + Actions + + + + Routes + + + Routes + + + New Route + + + Active Routes + + + Stops + + + Profile + + + Created + + + View + + + Edit + + + History + + + Delete this route plan? + + + + Route History + + + Route History + + + History + + + Unit + + + Started + + + Ended + + + Detail + + + + Active Routes + + + Active Routes + + + Active + + + No Active Routes + + + There are no route instances currently in progress. + + + Unit + + + Stops + + + Started + + + View Details + + + + New Route + + + New Route Plan + + + New + + + Route Details + + + Unit + + + -- Assign at start time -- + + + Draft + + + Active + + + Paused + + + Archived + + + Route Color + + + Route Profile + + + Driving + + + Walking + + + Cycling + + + Driving (Traffic) + + + Geofence Radius (m) + + + Use Station as Start + + + Use Station as End + + + Optimize Stop Order + + + Map & Stops + + + Stops can be managed after creating the route via the API or edit form. + + + Create Route + + + + Edit Route + + + Edit Route Plan + + + Edit + + + # + + + Type + + + Address + + + Priority + + + Save Changes + + + + Route Detail + + + View + + + Route Map + + + Details + + + Status + + + Profile + + + Geofence + + + Distance + + + Duration + + + + Route Instance Detail + + + Route Instance + + + Instance Detail + + + Instance Info + + + Started + + + Ended + + + Progress + + + Stop Timeline + + + Pending + + + Checked In + + + Completed + + + Skipped + + + Unknown + + + In: + + + Out: + + + Dwell: + + + Skip: + + + Stop + + diff --git a/Core/Resgrid.Model/PermissionTypes.cs b/Core/Resgrid.Model/PermissionTypes.cs index bdc7d23f..37aa46a9 100644 --- a/Core/Resgrid.Model/PermissionTypes.cs +++ b/Core/Resgrid.Model/PermissionTypes.cs @@ -28,8 +28,7 @@ public enum PermissionTypes ManageWorkflowCredentials = 23, ViewWorkflowRuns = 24, ViewUdfFields = 25, - CreateRoute = 26, - ManageRoutes = 27 + ManageRoutes = 26 } } diff --git a/Providers/Resgrid.Providers.Claims/ClaimsLogic.cs b/Providers/Resgrid.Providers.Claims/ClaimsLogic.cs index feb0f716..81c730f3 100644 --- a/Providers/Resgrid.Providers.Claims/ClaimsLogic.cs +++ b/Providers/Resgrid.Providers.Claims/ClaimsLogic.cs @@ -1539,9 +1539,10 @@ public static void AddUdfClaims(ClaimsIdentity identity, bool isAdmin, List /// Adds Route claims based on department role. /// Department admins always receive full access (View + Update + Create + Delete). - /// Group admins receive View + Update when the permission is DepartmentAndGroupAdmins or Everyone. - /// Regular users receive View only when the permission is Everyone. - /// Default (no permission record): Everyone — all users can view routes. + /// DepartmentAdminsOnly: non-admins receive no claims. + /// DepartmentAndGroupAdmins: group admins receive View + Update + Create; regular users receive no claims. + /// Everyone: all users receive View + Update + Create. + /// Default (no permission record): all users receive View; group admins additionally receive Update + Create. /// public static void AddRouteClaims(ClaimsIdentity identity, bool isAdmin, List permissions, bool isGroupAdmin, List roles) { diff --git a/Providers/Resgrid.Providers.Geo/GeoLocationProvider.cs b/Providers/Resgrid.Providers.Geo/GeoLocationProvider.cs index 9b355782..f33a4f90 100644 --- a/Providers/Resgrid.Providers.Geo/GeoLocationProvider.cs +++ b/Providers/Resgrid.Providers.Geo/GeoLocationProvider.cs @@ -107,7 +107,7 @@ async Task getCordsFromAddress() coordinates = string.Format("{0},{1}", firstAddress.Coordinates.Latitude, firstAddress.Coordinates.Longitude); } } - catch { /* Don't report on GeoLocation failures */ } + catch (Exception ex) { var test = ex; /* Don't report on GeoLocation failures */ } if (string.IsNullOrWhiteSpace(coordinates)) { diff --git a/Web/Resgrid.Web.Services/Controllers/v4/GeocodingController.cs b/Web/Resgrid.Web.Services/Controllers/v4/GeocodingController.cs new file mode 100644 index 00000000..fd82207e --- /dev/null +++ b/Web/Resgrid.Web.Services/Controllers/v4/GeocodingController.cs @@ -0,0 +1,99 @@ +using System; +using System.Globalization; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Resgrid.Model.Providers; +using Resgrid.Providers.Claims; +using Resgrid.Web.Services.Helpers; +using Resgrid.Web.Services.Models.v4.Geocoding; + +namespace Resgrid.Web.Services.Controllers.v4 +{ + /// + /// Forward and reverse geocoding operations. Requests are proxied through the + /// server-side geocoding provider so that external API keys are never exposed + /// to the client, and so that all calls pass through the configured rate limiter. + /// + [Route("api/v{VersionId:apiVersion}/[controller]")] + [ApiVersion("4.0")] + [ApiExplorerSettings(GroupName = "v4")] + public class GeocodingController : V4AuthenticatedApiControllerbase + { + private readonly IGeoLocationProvider _geoLocationProvider; + + public GeocodingController(IGeoLocationProvider geoLocationProvider) + { + _geoLocationProvider = geoLocationProvider; + } + + /// + /// Converts a human-readable address string into geographic coordinates. + /// + /// Address string to geocode. + /// ForwardGeocodeResult with Latitude/Longitude, or nulls if not found. + [HttpGet("ForwardGeocode")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [Authorize(Policy = ResgridResources.Call_View)] + public async Task> ForwardGeocode([FromQuery] string address) + { + if (string.IsNullOrWhiteSpace(address)) + return BadRequest(); + + var result = new ForwardGeocodeResult(); + + try + { + var coordinates = await _geoLocationProvider.GetLatLonFromAddress(address); + + if (!string.IsNullOrEmpty(coordinates)) + { + var parts = coordinates.Split(','); + if (parts.Length == 2 && + double.TryParse(parts[0], NumberStyles.Any, CultureInfo.InvariantCulture, out var lat) && + double.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out var lng)) + { + result.Data.Latitude = lat; + result.Data.Longitude = lng; + } + } + } + catch { /* provider errors are non-fatal */ } + + result.PageSize = 1; + result.Status = ResponseHelper.Success; + ResponseHelper.PopulateV4ResponseData(result); + + return Ok(result); + } + + /// + /// Converts geographic coordinates into a human-readable address string. + /// + /// Latitude of the location to reverse-geocode. + /// Longitude of the location to reverse-geocode. + /// ReverseGeocodeResult with Address, or empty string if not found. + [HttpGet("ReverseGeocode")] + [ProducesResponseType(StatusCodes.Status200OK)] + [Authorize(Policy = ResgridResources.Call_View)] + public async Task> ReverseGeocode([FromQuery] double lat, [FromQuery] double lon) + { + var result = new ReverseGeocodeResult(); + + try + { + var address = await _geoLocationProvider.GetAddressFromLatLong(lat, lon); + result.Data.Address = address ?? string.Empty; + } + catch { /* provider errors are non-fatal */ } + + result.PageSize = 1; + result.Status = ResponseHelper.Success; + ResponseHelper.PopulateV4ResponseData(result); + + return Ok(result); + } + } +} diff --git a/Web/Resgrid.Web.Services/Models/v4/Geocoding/GeocodingResults.cs b/Web/Resgrid.Web.Services/Models/v4/Geocoding/GeocodingResults.cs new file mode 100644 index 00000000..54f8db09 --- /dev/null +++ b/Web/Resgrid.Web.Services/Models/v4/Geocoding/GeocodingResults.cs @@ -0,0 +1,45 @@ +namespace Resgrid.Web.Services.Models.v4.Geocoding +{ + /// + /// Result of a forward geocode (address → coordinates) request. + /// + public class ForwardGeocodeResult : StandardApiResponseV4Base + { + /// Response data + public ForwardGeocodeData Data { get; set; } + + public ForwardGeocodeResult() + { + Data = new ForwardGeocodeData(); + } + } + + public class ForwardGeocodeData + { + /// Latitude of the geocoded location, or null if not found. + public double? Latitude { get; set; } + + /// Longitude of the geocoded location, or null if not found. + public double? Longitude { get; set; } + } + + /// + /// Result of a reverse geocode (coordinates → address) request. + /// + public class ReverseGeocodeResult : StandardApiResponseV4Base + { + /// Response data + public ReverseGeocodeData Data { get; set; } + + public ReverseGeocodeResult() + { + Data = new ReverseGeocodeData(); + } + } + + public class ReverseGeocodeData + { + /// Human-readable address for the supplied coordinates, or empty if not found. + public string Address { get; set; } + } +} diff --git a/Web/Resgrid.Web.Services/Resgrid.Web.Services.xml b/Web/Resgrid.Web.Services/Resgrid.Web.Services.xml index 2a28e0d3..5bfeea13 100644 --- a/Web/Resgrid.Web.Services/Resgrid.Web.Services.xml +++ b/Web/Resgrid.Web.Services/Resgrid.Web.Services.xml @@ -574,6 +574,28 @@ The form identifier A FormResultData for the requested form + + + Forward and reverse geocoding operations. Requests are proxied through the + server-side geocoding provider so that external API keys are never exposed + to the client, and so that all calls pass through the configured rate limiter. + + + + + Converts a human-readable address string into geographic coordinates. + + Address string to geocode. + ForwardGeocodeResult with Latitude/Longitude, or nulls if not found. + + + + Converts geographic coordinates into a human-readable address string. + + Latitude of the location to reverse-geocode. + Longitude of the location to reverse-geocode. + ReverseGeocodeResult with Address, or empty string if not found. + User generated forms that are dispayed to get custom information for New Calls, Unit Checks, etc @@ -5831,6 +5853,31 @@ Default constructor + + + Result of a forward geocode (address → coordinates) request. + + + + Response data + + + Latitude of the geocoded location, or null if not found. + + + Longitude of the geocoded location, or null if not found. + + + + Result of a reverse geocode (coordinates → address) request. + + + + Response data + + + Human-readable address for the supplied coordinates, or empty if not found. + A group in the Resgrid system diff --git a/Web/Resgrid.Web.Services/appsettings.json b/Web/Resgrid.Web.Services/appsettings.json index 1ea7fa4a..6a3d8e5f 100644 --- a/Web/Resgrid.Web.Services/appsettings.json +++ b/Web/Resgrid.Web.Services/appsettings.json @@ -52,6 +52,16 @@ "Period": "60s", "Limit": 15 }, + { + "Endpoint": "*:/api/v4/Geocoding/ForwardGeocode", + "Period": "60s", + "Limit": 30 + }, + { + "Endpoint": "*:/api/v4/Geocoding/ReverseGeocode", + "Period": "60s", + "Limit": 30 + }, { "Endpoint": "*:/api/v3/Auth/Validate", "Period": "60s", diff --git a/Web/Resgrid.Web/Areas/User/Controllers/CustomMapsController.cs b/Web/Resgrid.Web/Areas/User/Controllers/CustomMapsController.cs index 8c74b38a..1b93ba53 100644 --- a/Web/Resgrid.Web/Areas/User/Controllers/CustomMapsController.cs +++ b/Web/Resgrid.Web/Areas/User/Controllers/CustomMapsController.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; @@ -9,7 +10,6 @@ using Resgrid.Model.Services; using Resgrid.Web.Areas.User.Models.CustomMaps; using Resgrid.Web.Helpers; -using System.Collections.Generic; namespace Resgrid.Web.Areas.User.Controllers { @@ -209,6 +209,7 @@ public async Task RegionEditor(string id) } [HttpPost] + [ValidateAntiForgeryToken] public async Task SaveRegion([FromBody] IndoorMapZone region, CancellationToken cancellationToken) { var layer = await _customMapService.GetLayerByIdAsync(region.IndoorMapFloorId); @@ -231,6 +232,7 @@ public async Task SaveRegion([FromBody] IndoorMapZone region, Can } [HttpPost] + [ValidateAntiForgeryToken] public async Task DeleteRegion([FromBody] DeleteRegionRequest request, CancellationToken cancellationToken) { var region = await _customMapService.GetRegionByIdAsync(request.RegionId); @@ -336,6 +338,14 @@ public async Task GetLayerImage(string id) [HttpGet] public async Task GetLayerTile(string id, int z, int x, int y) { + var layer = await _customMapService.GetLayerByIdAsync(id); + if (layer == null) + return NotFound(); + + var map = await _customMapService.GetCustomMapByIdAsync(layer.IndoorMapId); + if (map == null || map.DepartmentId != DepartmentId) + return NotFound(); + var tile = await _customMapService.GetTileAsync(id, z, x, y); if (tile == null) return NotFound(); diff --git a/Web/Resgrid.Web/Areas/User/Controllers/DispatchController.cs b/Web/Resgrid.Web/Areas/User/Controllers/DispatchController.cs index 381012d4..516de53b 100644 --- a/Web/Resgrid.Web/Areas/User/Controllers/DispatchController.cs +++ b/Web/Resgrid.Web/Areas/User/Controllers/DispatchController.cs @@ -625,6 +625,11 @@ public async Task UpdateCall(UpdateCallView model, IFormCollectio call.IndoorMapZoneId = indoorMapZoneId; call.IndoorMapFloorId = indoorMapFloorId; } + else + { + call.IndoorMapZoneId = null; + call.IndoorMapFloorId = null; + } List existingDispatches = new List(call.Dispatches); diff --git a/Web/Resgrid.Web/Areas/User/Controllers/IndoorMapsController.cs b/Web/Resgrid.Web/Areas/User/Controllers/IndoorMapsController.cs index 3dfc50bb..cbd45b94 100644 --- a/Web/Resgrid.Web/Areas/User/Controllers/IndoorMapsController.cs +++ b/Web/Resgrid.Web/Areas/User/Controllers/IndoorMapsController.cs @@ -1,12 +1,39 @@ +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Resgrid.Model.Services; namespace Resgrid.Web.Areas.User.Controllers { [Area("User")] public class IndoorMapsController : SecureBaseController { + private readonly IIndoorMapService _indoorMapService; + + public IndoorMapsController(IIndoorMapService indoorMapService) + { + _indoorMapService = indoorMapService; + } + + [HttpGet] + public async Task SearchZones(string term) + { + if (string.IsNullOrWhiteSpace(term)) + return Json(new { results = System.Array.Empty() }); + + var zones = await _indoorMapService.SearchZonesAsync(DepartmentId, term); + + var results = zones.Select(z => new + { + id = z.IndoorMapZoneId, + text = z.Name, + floorId = z.IndoorMapFloorId + }); + + return Json(new { results }); + } + [HttpGet] public IActionResult Index() { diff --git a/Web/Resgrid.Web/Areas/User/Controllers/RoutesController.cs b/Web/Resgrid.Web/Areas/User/Controllers/RoutesController.cs index 7ce2f096..a7fc1c11 100644 --- a/Web/Resgrid.Web/Areas/User/Controllers/RoutesController.cs +++ b/Web/Resgrid.Web/Areas/User/Controllers/RoutesController.cs @@ -2,9 +2,11 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Resgrid.Model; using Resgrid.Model.Services; +using Resgrid.Providers.Claims; using Resgrid.Web.Areas.User.Models.Routes; namespace Resgrid.Web.Areas.User.Controllers @@ -14,14 +16,17 @@ public class RoutesController : SecureBaseController { private readonly IRouteService _routeService; private readonly IUnitsService _unitsService; + private readonly ICallsService _callsService; - public RoutesController(IRouteService routeService, IUnitsService unitsService) + public RoutesController(IRouteService routeService, IUnitsService unitsService, ICallsService callsService) { _routeService = routeService; _unitsService = unitsService; + _callsService = callsService; } [HttpGet] + [Authorize(Policy = ResgridResources.Route_View)] public async Task Index() { var model = new RouteIndexView(); @@ -30,6 +35,7 @@ public async Task Index() } [HttpGet] + [Authorize(Policy = ResgridResources.Route_Create)] public async Task New() { var model = new RouteNewView(); @@ -39,6 +45,7 @@ public async Task New() [HttpPost] [ValidateAntiForgeryToken] + [Authorize(Policy = ResgridResources.Route_Create)] public async Task New(RouteNewView model, CancellationToken cancellationToken) { if (ModelState.IsValid) @@ -58,6 +65,7 @@ public async Task New(RouteNewView model, CancellationToken cance } [HttpGet] + [Authorize(Policy = ResgridResources.Route_Update)] public async Task Edit(string id) { var plan = await _routeService.GetRoutePlanByIdAsync(id); @@ -74,6 +82,7 @@ public async Task Edit(string id) [HttpPost] [ValidateAntiForgeryToken] + [Authorize(Policy = ResgridResources.Route_Update)] public async Task Edit(RouteEditView model, CancellationToken cancellationToken) { if (ModelState.IsValid) @@ -99,6 +108,7 @@ public async Task Edit(RouteEditView model, CancellationToken can [HttpPost] [ValidateAntiForgeryToken] + [Authorize(Policy = ResgridResources.Route_Delete)] public async Task Delete(string id, CancellationToken cancellationToken) { var plan = await _routeService.GetRoutePlanByIdAsync(id); @@ -111,6 +121,7 @@ public async Task Delete(string id, CancellationToken cancellatio } [HttpGet] + [Authorize(Policy = ResgridResources.Route_View)] public async Task View(string id) { var plan = await _routeService.GetRoutePlanByIdAsync(id); @@ -124,6 +135,7 @@ public async Task View(string id) } [HttpGet] + [Authorize(Policy = ResgridResources.Route_View)] public async Task Instances(string routePlanId) { var plan = await _routeService.GetRoutePlanByIdAsync(routePlanId); @@ -140,6 +152,7 @@ public async Task Instances(string routePlanId) } [HttpGet] + [Authorize(Policy = ResgridResources.Route_View)] public async Task ActiveRoutes() { var instances = await _routeService.GetInstancesForDepartmentAsync(DepartmentId); @@ -153,6 +166,82 @@ public async Task ActiveRoutes() } [HttpGet] + [Authorize(Policy = ResgridResources.Route_View)] + public async Task GetCallsForLinking() + { + var calls = await _callsService.GetAllNonDispatchedScheduledCallsByDepartmentIdAsync(DepartmentId); + var active = await _callsService.GetActiveCallsByDepartmentAsync(DepartmentId); + var all = calls.Union(active).Distinct().OrderBy(c => c.Name).Select(c => new { id = c.CallId, name = c.Name, address = c.Address }).ToList(); + return Json(all); + } + + [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, + CancellationToken cancellationToken) + { + var plan = await _routeService.GetRoutePlanByIdAsync(routePlanId); + if (plan == null || plan.DepartmentId != DepartmentId) + return Json(new { success = false, message = "Not found" }); + + var existingStops = await _routeService.GetRouteStopsForPlanAsync(routePlanId); + var stop = new RouteStop + { + RoutePlanId = routePlanId, + Name = name, + Description = description, + StopType = stopType, + Priority = priority, + Latitude = latitude, + Longitude = longitude, + Address = address, + CallId = callId, + GeofenceRadiusMeters = geofenceRadius, + EstimatedDwellMinutes = dwellMinutes, + ContactName = contactName, + ContactNumber = contactNumber, + Notes = notes, + StopOrder = existingStops.Count + 1, + AddedOn = DateTime.UtcNow, + IsDeleted = false + }; + + if (!string.IsNullOrWhiteSpace(plannedArrival) && DateTime.TryParse(plannedArrival, out var arrivalDt)) + stop.PlannedArrivalTime = arrivalDt.ToUniversalTime(); + if (!string.IsNullOrWhiteSpace(plannedDeparture) && DateTime.TryParse(plannedDeparture, out var departureDt)) + stop.PlannedDepartureTime = departureDt.ToUniversalTime(); + + await _routeService.SaveRouteStopAsync(stop, cancellationToken); + return Json(new { success = true }); + } + + [HttpPost] + [ValidateAntiForgeryToken] + [Authorize(Policy = ResgridResources.Route_Delete)] + public async Task DeleteStop(string stopId, CancellationToken cancellationToken) + { + // Verify ownership by loading the stop via the plan + // We load all stops for the department's plans to validate ownership + var deleted = false; + var plans = await _routeService.GetRoutePlansForDepartmentAsync(DepartmentId); + foreach (var p in plans) + { + var stops = await _routeService.GetRouteStopsForPlanAsync(p.RoutePlanId); + if (stops.Any(s => s.RouteStopId == stopId)) + { + deleted = await _routeService.DeleteRouteStopAsync(stopId, cancellationToken); + break; + } + } + + return Json(new { success = deleted }); + } + + [HttpGet] + [Authorize(Policy = ResgridResources.Route_View)] public async Task InstanceDetail(string instanceId) { var instance = await _routeService.GetInstanceByIdAsync(instanceId); diff --git a/Web/Resgrid.Web/Areas/User/Views/Calendar/Edit.cshtml b/Web/Resgrid.Web/Areas/User/Views/Calendar/Edit.cshtml index ac52874a..f1b80059 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Calendar/Edit.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Calendar/Edit.cshtml @@ -365,7 +365,7 @@
- +
diff --git a/Web/Resgrid.Web/Areas/User/Views/Calendar/New.cshtml b/Web/Resgrid.Web/Areas/User/Views/Calendar/New.cshtml index d59b7069..ec6f0462 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Calendar/New.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Calendar/New.cshtml @@ -322,7 +322,7 @@
- +
diff --git a/Web/Resgrid.Web/Areas/User/Views/CustomMaps/Edit.cshtml b/Web/Resgrid.Web/Areas/User/Views/CustomMaps/Edit.cshtml index 3d65cd66..44fac027 100644 --- a/Web/Resgrid.Web/Areas/User/Views/CustomMaps/Edit.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/CustomMaps/Edit.cshtml @@ -1,21 +1,21 @@ @model Resgrid.Web.Areas.User.Models.CustomMaps.CustomMapNewView @using Resgrid.Model +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | Edit Custom Map"; + ViewBag.Title = "Resgrid | " + localizer["EditCustomMapPageTitle"]; } @section Styles { - - + }
-

Edit Custom Map

+

@localizer["EditCustomMapHeader"]

@@ -33,34 +33,34 @@ @Html.AntiForgeryToken()
- +
- +
- +
- +
- +
- +
-

Draw a rectangle on the map below to update the bounds. Click the map to set the center point.

+

@localizer["DrawBoundsEditHelp"]

@@ -77,8 +77,8 @@
- Cancel - + @localizer["Cancel"] +
@@ -89,11 +89,12 @@ @section Scripts { - - + } diff --git a/Web/Resgrid.Web/Areas/User/Views/CustomMaps/Import.cshtml b/Web/Resgrid.Web/Areas/User/Views/CustomMaps/Import.cshtml index e07bc1ba..cecdcb09 100644 --- a/Web/Resgrid.Web/Areas/User/Views/CustomMaps/Import.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/CustomMaps/Import.cshtml @@ -1,16 +1,17 @@ @model Resgrid.Web.Areas.User.Models.CustomMaps.CustomMapImportView @using Resgrid.Model +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | Import - " + Model.Map.Name; + ViewBag.Title = "Resgrid | " + localizer["ImportPageTitlePrefix"] + " - " + Model.Map.Name; }
-

Import - @Model.Map.Name

+

@localizer["ImportHeaderPrefix"] - @Model.Map.Name

@@ -20,7 +21,7 @@
-
Import Geospatial Data
+
@localizer["ImportGeospatialTitle"]
@if (!string.IsNullOrWhiteSpace(Model.Message)) @@ -31,7 +32,7 @@ @Html.AntiForgeryToken()
- +
- Supported formats: GeoJSON (.geojson, .json), KML (.kml), KMZ (.kmz) + @localizer["ImportFileHelp"]
- +
@@ -60,18 +61,18 @@
-
Import History
+
@localizer["ImportHistoryTitle"]
- - - - - + + + + + diff --git a/Web/Resgrid.Web/Areas/User/Views/CustomMaps/Index.cshtml b/Web/Resgrid.Web/Areas/User/Views/CustomMaps/Index.cshtml index a47ecc41..7fc71710 100644 --- a/Web/Resgrid.Web/Areas/User/Views/CustomMaps/Index.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/CustomMaps/Index.cshtml @@ -1,24 +1,25 @@ @model Resgrid.Web.Areas.User.Models.CustomMaps.CustomMapIndexView @using Resgrid.Model +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | Custom Maps"; + ViewBag.Title = "Resgrid | " + localizer["CustomMapsPageTitle"]; }
-

Custom Maps

+

@localizer["CustomMapsHeader"]

@@ -30,29 +31,29 @@
FileTypeStatusDateError@localizer["File"]@localizer["Type"]@localizer["Status"]@localizer["Date"]@localizer["Error"]
- - - - + + + + @@ -67,10 +68,10 @@ } diff --git a/Web/Resgrid.Web/Areas/User/Views/CustomMaps/Layers.cshtml b/Web/Resgrid.Web/Areas/User/Views/CustomMaps/Layers.cshtml index 681e0ba6..32222e38 100644 --- a/Web/Resgrid.Web/Areas/User/Views/CustomMaps/Layers.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/CustomMaps/Layers.cshtml @@ -1,16 +1,17 @@ @model Resgrid.Web.Areas.User.Models.CustomMaps.CustomMapLayersView @using Resgrid.Model +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | Layers - " + Model.Map.Name; + ViewBag.Title = "Resgrid | " + localizer["LayersPageTitlePrefix"] + " - " + Model.Map.Name; }
-

Layers - @Model.Map.Name

+

@localizer["LayersHeaderPrefix"] - @Model.Map.Name

@@ -20,39 +21,39 @@
-
Add Layer
+
@localizer["AddLayerTitle"]
@Html.AntiForgeryToken()
- +
- +
- +
- +
- +
- Images larger than 2048px will be automatically tiled. + @localizer["LayerImageHelp"]
- +
@@ -61,18 +62,18 @@
-
Layers
+
@localizer["LayersBreadcrumb"]
NameTypeDescriptionCreated@localizer["Name"]@localizer["Type"]@localizer["Description"]@localizer["Created"]
@map.Description @map.AddedOn.ToString("g") - Layers - Import - Edit - Delete + @localizer["Layers"] + @localizer["Import"] + @localizer["Edit"] + @localizer["Delete"]
- - - - - + + + + + @@ -85,7 +86,7 @@ - + } diff --git a/Web/Resgrid.Web/Areas/User/Views/CustomMaps/New.cshtml b/Web/Resgrid.Web/Areas/User/Views/CustomMaps/New.cshtml index 788e9edc..fa8de45c 100644 --- a/Web/Resgrid.Web/Areas/User/Views/CustomMaps/New.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/CustomMaps/New.cshtml @@ -1,21 +1,21 @@ @model Resgrid.Web.Areas.User.Models.CustomMaps.CustomMapNewView @using Resgrid.Model +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | New Custom Map"; + ViewBag.Title = "Resgrid | " + localizer["NewCustomMapPageTitle"]; } @section Styles { - - + }
-

New Custom Map

+

@localizer["NewCustomMapHeader"]

@@ -32,34 +32,34 @@
@Html.AntiForgeryToken()
- +
- +
- +
- +
- +
- +
-

Draw a rectangle on the map below to define the bounds. Click the map to set the center point.

+

@localizer["DrawBoundsHelp"]

@@ -76,8 +76,8 @@
- Cancel - + @localizer["Cancel"] +
@@ -88,11 +88,12 @@ @section Scripts { - - + } diff --git a/Web/Resgrid.Web/Areas/User/Views/CustomMaps/RegionEditor.cshtml b/Web/Resgrid.Web/Areas/User/Views/CustomMaps/RegionEditor.cshtml index 81957703..3523230c 100644 --- a/Web/Resgrid.Web/Areas/User/Views/CustomMaps/RegionEditor.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/CustomMaps/RegionEditor.cshtml @@ -1,12 +1,12 @@ @model Resgrid.Web.Areas.User.Models.CustomMaps.CustomMapRegionEditorView @using Resgrid.Model +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | Region Editor - " + Model.Layer.Name; + ViewBag.Title = "Resgrid | " + localizer["RegionEditorPageTitlePrefix"] + " - " + Model.Layer.Name; } @section Styles { - - + } - -

@localizer["AddArchivedCallHeader"]

@@ -400,6 +398,8 @@ var newCallFormData = '@Html.Raw(Model.NewCallFormData)'; var osmTileUrl = '@Resgrid.Config.MappingConfig.GetWebsiteOSMUrl()'; var osmTileAttribution = '@Resgrid.Config.MappingConfig.LeafletAttribution'; + var nominatimUrl = '@Resgrid.Config.MappingConfig.NominatimUrl'; + var osrmUrl = '@Resgrid.Config.MappingConfig.OsrmUrl'; @if (Model.CenterCoordinates != null && Model.CenterCoordinates.Latitude.HasValue && Model.CenterCoordinates.Longitude.HasValue) diff --git a/Web/Resgrid.Web/Areas/User/Views/Dispatch/CallExport.cshtml b/Web/Resgrid.Web/Areas/User/Views/Dispatch/CallExport.cshtml index 2e7b8a4a..f7456a35 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Dispatch/CallExport.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Dispatch/CallExport.cshtml @@ -17,7 +17,7 @@ -
- + - + diff --git a/Web/Resgrid.Web/Areas/User/Views/Dispatch/CallExportEx.cshtml b/Web/Resgrid.Web/Areas/User/Views/Dispatch/CallExportEx.cshtml index 3265421a..4e5d92b7 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Dispatch/CallExportEx.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Dispatch/CallExportEx.cshtml @@ -17,7 +17,7 @@ -
- + - + @@ -555,67 +555,63 @@ - + + @if (Model.Station != null) { } diff --git a/Web/Resgrid.Web/Areas/User/Views/Dispatch/Dashboard.cshtml b/Web/Resgrid.Web/Areas/User/Views/Dispatch/Dashboard.cshtml index 641cf8b2..c70d7217 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Dispatch/Dashboard.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Dispatch/Dashboard.cshtml @@ -25,8 +25,6 @@ } - -

@commonLocalizer["CallsModule"]

@@ -42,15 +40,15 @@
- @localizer["ArchivedCalls"] - @if (ClaimsAuthorizationHelper.CanCreateCall()) - { - @localizer["NewCall"] - } @if (ClaimsAuthorizationHelper.CanViewRoutes()) { Routes } + @localizer["ArchivedCalls"] + @if (ClaimsAuthorizationHelper.CanCreateCall()) + { + @localizer["NewCall"] + }
diff --git a/Web/Resgrid.Web/Areas/User/Views/Dispatch/NewCall.cshtml b/Web/Resgrid.Web/Areas/User/Views/Dispatch/NewCall.cshtml index d99e37ab..e9034654 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Dispatch/NewCall.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Dispatch/NewCall.cshtml @@ -528,13 +528,14 @@ @section Scripts { - @if (Model.CenterCoordinates != null && Model.CenterCoordinates.Latitude.HasValue && Model.CenterCoordinates.Longitude.HasValue) diff --git a/Web/Resgrid.Web/Areas/User/Views/Dispatch/UpdateCall.cshtml b/Web/Resgrid.Web/Areas/User/Views/Dispatch/UpdateCall.cshtml index 7058a2bf..5a8c9880 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Dispatch/UpdateCall.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Dispatch/UpdateCall.cshtml @@ -402,7 +402,6 @@ @section Scripts { - diff --git a/Web/Resgrid.Web/Areas/User/Views/Dispatch/ViewCall.cshtml b/Web/Resgrid.Web/Areas/User/Views/Dispatch/ViewCall.cshtml index 4ffd43f5..d1fd9dbb 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Dispatch/ViewCall.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Dispatch/ViewCall.cshtml @@ -11,8 +11,8 @@ @section Styles { - - + + } @Html.HiddenFor(m => m.Latitude) @@ -801,8 +801,8 @@ - - + + - @if (Model.Group == null || String.IsNullOrWhiteSpace(Model.Group.Geofence)) - { - - } - else - { - - } + + @if (Model.Group == null || String.IsNullOrWhiteSpace(Model.Group.Geofence)) + { + + } + else + { + + } - + - + } diff --git a/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/Edit.cshtml b/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/Edit.cshtml index f812b6e3..c7d96aa1 100644 --- a/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/Edit.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/Edit.cshtml @@ -1,15 +1,16 @@ @model Resgrid.Web.Areas.User.Models.IndoorMaps.IndoorMapNewView +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | Edit Indoor Map"; + ViewBag.Title = "Resgrid | " + localizer["EditIndoorMapPageTitle"]; }
-

Edit Indoor Map

+

@localizer["EditIndoorMapHeader"]

@@ -27,43 +28,43 @@ @Html.AntiForgeryToken()
- +
- +
- +
- +
- +
- +
- +
- +
- +
- +
- +
- +
@@ -71,8 +72,8 @@
- Cancel - + @localizer["Cancel"] +
@@ -84,5 +85,9 @@
@section Scripts { + } diff --git a/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/Floors.cshtml b/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/Floors.cshtml index a82165f8..54ef0433 100644 --- a/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/Floors.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/Floors.cshtml @@ -1,15 +1,16 @@ @model Resgrid.Web.Areas.User.Models.IndoorMaps.IndoorMapFloorsView +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | Floors - " + Model.IndoorMap.Name; + ViewBag.Title = "Resgrid | " + localizer["FloorsPageTitlePrefix"] + " - " + Model.IndoorMap.Name; }
-

Floors - @Model.IndoorMap.Name

+

@localizer["FloorsPageTitlePrefix"] - @Model.IndoorMap.Name

@@ -19,29 +20,29 @@
-
Add Floor
+
@localizer["AddFloorTitle"]
@Html.AntiForgeryToken()
- +
- +
- +
- +
- +
@@ -50,16 +51,16 @@
-
Floors
+
@localizer["FloorsTableTitle"]
OrderNameTypeHas ImageTiled@localizer["Order"]@localizer["Name"]@localizer["Type"]@localizer["HasImage"]@localizer["Tiled"]
@layer.FloorOrder @layer.Name @((CustomMapLayerType)layer.LayerType)@(layer.ImageData != null || layer.IsTiled ? "Yes" : "No")@(layer.ImageData != null || layer.IsTiled ? localizer["Yes"].Value : localizer["No"].Value) @if (layer.IsTiled) { @@ -93,12 +94,12 @@ } else { - No + @localizer["No"] } - Region Editor - Delete + @localizer["RegionEditor"] + @localizer["Delete"]
- - - + + + @@ -71,10 +72,10 @@ - + } diff --git a/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/Index.cshtml b/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/Index.cshtml index 30a2457a..83b47b0d 100644 --- a/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/Index.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/Index.cshtml @@ -1,23 +1,24 @@ @model Resgrid.Web.Areas.User.Models.IndoorMaps.IndoorMapIndexView +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | Indoor Maps"; + ViewBag.Title = "Resgrid | " + localizer["IndoorMapsPageTitle"]; }
-

Indoor Maps

+

@localizer["IndoorMapsHeader"]

@@ -31,9 +32,9 @@
OrderNameHas Image@localizer["Order"]@localizer["Name"]@localizer["HasImage"]
@floor.FloorOrder @floor.Name@(floor.ImageData != null ? "Yes" : "No")@(floor.ImageData != null ? localizer["Yes"].Value : localizer["No"].Value) - Zone Editor - Delete + @localizer["ZoneEditor"] + @localizer["Delete"]
- - - + + + @@ -47,9 +48,9 @@ } @@ -64,11 +65,5 @@ @section Scripts { - + } diff --git a/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/New.cshtml b/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/New.cshtml index 31336db9..157757fd 100644 --- a/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/New.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/New.cshtml @@ -1,15 +1,16 @@ @model Resgrid.Web.Areas.User.Models.IndoorMaps.IndoorMapNewView +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | New Indoor Map"; + ViewBag.Title = "Resgrid | " + localizer["NewIndoorMapPageTitle"]; }
-

New Indoor Map

+

@localizer["NewIndoorMapHeader"]

@@ -26,43 +27,43 @@
@Html.AntiForgeryToken()
- +
- +
- +
- +
- +
- +
- +
- +
- +
- +
- +
- +
@@ -70,8 +71,8 @@
- Cancel - + @localizer["Cancel"] +
@@ -83,5 +84,9 @@
@section Scripts { + } diff --git a/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/ZoneEditor.cshtml b/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/ZoneEditor.cshtml index 361594c6..0ea94ace 100644 --- a/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/ZoneEditor.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/IndoorMaps/ZoneEditor.cshtml @@ -1,11 +1,11 @@ @model Resgrid.Web.Areas.User.Models.IndoorMaps.IndoorMapZoneEditorView +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | Zone Editor - " + Model.Floor.Name; + ViewBag.Title = "Resgrid | " + localizer["ZoneEditorPageTitlePrefix"] + " - " + Model.Floor.Name; } @section Styles { - - + } - -
-

Mapping

+

@localizer["MappingHeader"]

@@ -41,10 +40,10 @@ { } @@ -68,17 +67,17 @@
diff --git a/Web/Resgrid.Web/Areas/User/Views/Mapping/Layers.cshtml b/Web/Resgrid.Web/Areas/User/Views/Mapping/Layers.cshtml index 04dcb2ed..67a9d367 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Mapping/Layers.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Mapping/Layers.cshtml @@ -1,21 +1,22 @@ -@using Resgrid.Web.Helpers +@using Resgrid.Web.Helpers @model Resgrid.Web.Areas.User.Models.Mapping.LayersView +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | Layers"; + ViewBag.Title = "Resgrid | " + localizer["LayersPageTitle"]; }
-

Layers

+

@localizer["LayersHeader"]

@@ -23,7 +24,7 @@ { } @@ -39,13 +40,13 @@
@@ -65,8 +66,8 @@ @layer.Data.Features.Count } diff --git a/Web/Resgrid.Web/Areas/User/Views/Mapping/LiveRouting.cshtml b/Web/Resgrid.Web/Areas/User/Views/Mapping/LiveRouting.cshtml index 80d182ab..6d3b618d 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Mapping/LiveRouting.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Mapping/LiveRouting.cshtml @@ -1,6 +1,7 @@ -@model Resgrid.WebCore.Areas.User.Models.Mapping.StationRoutingView +@model Resgrid.WebCore.Areas.User.Models.Mapping.StationRoutingView +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | Live Navigation"; + ViewBag.Title = "Resgrid | " + localizer["LiveRoutingPageTitle"]; } @section Styles @@ -14,16 +15,16 @@
-

Live Routing

+

@localizer["LiveRoutingHeader"]

@@ -57,71 +58,67 @@ @section Scripts { - diff --git a/Web/Resgrid.Web/Areas/User/Views/Mapping/NewLayer.cshtml b/Web/Resgrid.Web/Areas/User/Views/Mapping/NewLayer.cshtml index f0f5b4b9..52c0bee9 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Mapping/NewLayer.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Mapping/NewLayer.cshtml @@ -1,7 +1,8 @@ - + @model Resgrid.Web.Areas.User.Models.Mapping.NewLayerView +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | New Layer"; + ViewBag.Title = "Resgrid | " + localizer["NewLayerPageTitle"]; } @section Styles @@ -39,16 +40,16 @@
-

New Layer

+

@localizer["NewLayerHeader"]

@@ -78,7 +79,7 @@
@@ -88,29 +89,29 @@
@Html.CheckBoxFor(x => x.IsOnByDefault) - Do you want this layer to be visible every time a map loads? Note: Having too many layers visible by default will complicate the map. Users can turn layers on if they want to see them in the map control. + @localizer["IsVisibleByDefaultHelp"]
@Html.CheckBoxFor(x => x.IsSearchable) - This will allow address searching to use coordinates from this layer in the search. Note, if you don't have searchable points (text fields) don't mark as searchable as it can slow down dispatching.' + @localizer["IsSearchableHelp"]
@@ -120,7 +121,7 @@
@@ -128,8 +129,8 @@
@@ -207,7 +208,7 @@ break; default: } - + return geo; }); @@ -216,30 +217,8 @@ features, }; - //for (let i = 0; i < layers.length; i++) { - - // const geo = layers[i].toGeoJSON(); - // geo.properties.shape = layers[i].pm.getShape(); - // geo.properties.color = $('#Color').val(); - // geo.properties.name = $('#Name').val(); - // geo.properties.category = "default"; - - // switch (geo.properties.shape) { - // case "Text": - // geo.properties.text = layer.pm.getText().trim(); - // break; - // default: - // } - - // //layers[i].setStyle({ color: $('#Color').val() }); - // fg.addLayer(layers[i]); - //} - - //const geojson = fg.toGeoJSON(); - //const json = JSON.stringify(geojson); const json = JSON.stringify(featureCollection); - - //const json = JSON.stringify(map.pm.getGeomanLayers(true).toGeoJSON()); + $('#GeoJson').val(json); $("#newLayerForm").submit(); diff --git a/Web/Resgrid.Web/Areas/User/Views/Mapping/POIs.cshtml b/Web/Resgrid.Web/Areas/User/Views/Mapping/POIs.cshtml index fb659e7e..6fbd943c 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Mapping/POIs.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Mapping/POIs.cshtml @@ -1,22 +1,23 @@ -@using Resgrid.Web.Helpers +@using Resgrid.Web.Helpers @model Resgrid.Web.Areas.User.Models.Mapping.POIsView +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | POIs"; + ViewBag.Title = "Resgrid | " + localizer["POIsPageTitle"]; }
-

POIs

+

@localizer["POIsHeader"]

@@ -24,7 +25,7 @@ { } @@ -40,13 +41,13 @@
@@ -66,13 +67,13 @@ @u.Pois.Count @@ -100,4 +101,4 @@ }); //]]> -} \ No newline at end of file +} diff --git a/Web/Resgrid.Web/Areas/User/Views/Mapping/StationRouting.cshtml b/Web/Resgrid.Web/Areas/User/Views/Mapping/StationRouting.cshtml index 410e78eb..d3a158e1 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Mapping/StationRouting.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Mapping/StationRouting.cshtml @@ -1,6 +1,7 @@ -@model Resgrid.WebCore.Areas.User.Models.Mapping.StationRoutingView +@model Resgrid.WebCore.Areas.User.Models.Mapping.StationRoutingView +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | Station Navigation"; + ViewBag.Title = "Resgrid | " + localizer["StationRoutingPageTitle"]; } @section Styles @@ -12,20 +13,19 @@ } -
-

Station Routing

+

@localizer["StationRoutingHeader"]

@@ -52,62 +52,57 @@ @section Scripts { } diff --git a/Web/Resgrid.Web/Areas/User/Views/Mapping/ViewType.cshtml b/Web/Resgrid.Web/Areas/User/Views/Mapping/ViewType.cshtml index df48d17c..a0e861ba 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Mapping/ViewType.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Mapping/ViewType.cshtml @@ -1,8 +1,9 @@ -@using Resgrid.Model +@using Resgrid.Model @using Resgrid.Web.Helpers @model Resgrid.Web.Areas.User.Models.Mapping.ViewTypeView +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | View Type"; + ViewBag.Title = "Resgrid | " + localizer["ViewTypePageTitle"]; Layout = "~/Areas/User/Views/Shared/_UserLayout.cshtml"; } @@ -27,19 +28,19 @@
-

View Type

+

@localizer["ViewTypeHeader"]

@@ -49,9 +50,9 @@
@@ -65,7 +66,7 @@
@@ -75,24 +76,24 @@
@if (Model.Type.IsDestination) { - True + @localizer["True"] } else { - False + @localizer["False"] }
@@ -116,11 +117,12 @@ @section Scripts { - - - + + } diff --git a/Web/Resgrid.Web/Areas/User/Views/Reports/ActionLogs.cshtml b/Web/Resgrid.Web/Areas/User/Views/Reports/ActionLogs.cshtml index 0dfe9c37..90f800fb 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Reports/ActionLogs.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Reports/ActionLogs.cshtml @@ -15,7 +15,7 @@ - - + - + diff --git a/Web/Resgrid.Web/Areas/User/Views/Reports/ActiveCallsResourcesReport.cshtml b/Web/Resgrid.Web/Areas/User/Views/Reports/ActiveCallsResourcesReport.cshtml index 01b3f2ff..d908fa10 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Reports/ActiveCallsResourcesReport.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Reports/ActiveCallsResourcesReport.cshtml @@ -15,7 +15,7 @@ -
- - + + diff --git a/Web/Resgrid.Web/Areas/User/Views/Reports/CallSummaryReport.cshtml b/Web/Resgrid.Web/Areas/User/Views/Reports/CallSummaryReport.cshtml index 6b21e438..58e6c324 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Reports/CallSummaryReport.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Reports/CallSummaryReport.cshtml @@ -15,7 +15,7 @@ -
- - + + + - + diff --git a/Web/Resgrid.Web/Areas/User/Views/Reports/DepartmentActivityReport.cshtml b/Web/Resgrid.Web/Areas/User/Views/Reports/DepartmentActivityReport.cshtml index 3433263d..ceed41bb 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Reports/DepartmentActivityReport.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Reports/DepartmentActivityReport.cshtml @@ -15,7 +15,7 @@ -
- - + + - + + diff --git a/Web/Resgrid.Web/Areas/User/Views/Reports/LogReport.cshtml b/Web/Resgrid.Web/Areas/User/Views/Reports/LogReport.cshtml index ecaff9bc..bbd1fd36 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Reports/LogReport.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Reports/LogReport.cshtml @@ -16,7 +16,7 @@ - - + - + diff --git a/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelEventsReport.cshtml b/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelEventsReport.cshtml index 27480174..d764b2d5 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelEventsReport.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelEventsReport.cshtml @@ -15,7 +15,7 @@ -
- - + + diff --git a/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelHoursDetailReport.cshtml b/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelHoursDetailReport.cshtml index ffc8b25c..05fd8dea 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelHoursDetailReport.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelHoursDetailReport.cshtml @@ -15,7 +15,7 @@ -
- + - + diff --git a/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelHoursReport.cshtml b/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelHoursReport.cshtml index c7130513..4c452597 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelHoursReport.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelHoursReport.cshtml @@ -15,7 +15,7 @@ -
- + - + diff --git a/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelReport.cshtml b/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelReport.cshtml index 1817daa1..8a1b0c3f 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelReport.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelReport.cshtml @@ -14,7 +14,7 @@ -
- + - + diff --git a/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelStaffingHistoryReport.cshtml b/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelStaffingHistoryReport.cshtml index c9deb056..f569e73c 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelStaffingHistoryReport.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Reports/PersonnelStaffingHistoryReport.cshtml @@ -15,7 +15,7 @@ - - + - + diff --git a/Web/Resgrid.Web/Areas/User/Views/Reports/StaffingReport.cshtml b/Web/Resgrid.Web/Areas/User/Views/Reports/StaffingReport.cshtml index 90af2095..3cc399a1 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Reports/StaffingReport.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Reports/StaffingReport.cshtml @@ -15,7 +15,7 @@ -
- + - + diff --git a/Web/Resgrid.Web/Areas/User/Views/Reports/UnitEventsReport.cshtml b/Web/Resgrid.Web/Areas/User/Views/Reports/UnitEventsReport.cshtml index c517ce49..7c66f716 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Reports/UnitEventsReport.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Reports/UnitEventsReport.cshtml @@ -15,7 +15,7 @@ -
- + - + diff --git a/Web/Resgrid.Web/Areas/User/Views/Reports/UnitStateHistoryReport.cshtml b/Web/Resgrid.Web/Areas/User/Views/Reports/UnitStateHistoryReport.cshtml index 8df8d95c..3cc34164 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Reports/UnitStateHistoryReport.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Reports/UnitStateHistoryReport.cshtml @@ -15,7 +15,7 @@ - - + - + diff --git a/Web/Resgrid.Web/Areas/User/Views/Reports/UpcomingShiftReadinessReport.cshtml b/Web/Resgrid.Web/Areas/User/Views/Reports/UpcomingShiftReadinessReport.cshtml index bd4c93a9..5b1f88ac 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Reports/UpcomingShiftReadinessReport.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Reports/UpcomingShiftReadinessReport.cshtml @@ -12,7 +12,7 @@ -
- + - + diff --git a/Web/Resgrid.Web/Areas/User/Views/Routes/ActiveRoutes.cshtml b/Web/Resgrid.Web/Areas/User/Views/Routes/ActiveRoutes.cshtml index 9866f83b..c33b1a4b 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Routes/ActiveRoutes.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Routes/ActiveRoutes.cshtml @@ -1,15 +1,16 @@ @model Resgrid.Web.Areas.User.Models.Routes.ActiveRoutesView +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | Active Routes"; + ViewBag.Title = "Resgrid | " + localizer["ActiveRoutesPageTitle"]; }
-

Active Routes

+

@localizer["ActiveRoutesHeader"]

@@ -21,8 +22,8 @@
-

No Active Routes

-

There are no route instances currently in progress.

+

@localizer["NoActiveRoutes"]

+

@localizer["NoActiveRoutesMessage"]

@@ -40,11 +41,11 @@
- Unit + @localizer["UnitLabel"]

@instance.UnitId

- Stops + @localizer["StopsLabel"]

@instance.StopsCompleted / @instance.StopsTotal

@@ -52,10 +53,10 @@
- Started: @(instance.ActualStartOn?.ToString("HH:mm") ?? "-") + @localizer["StartedLabel"]: @(instance.ActualStartOn?.ToString("HH:mm") ?? "-")
- View Details + @localizer["ViewDetails"]
diff --git a/Web/Resgrid.Web/Areas/User/Views/Routes/Edit.cshtml b/Web/Resgrid.Web/Areas/User/Views/Routes/Edit.cshtml index 8266d80d..c0e64316 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Routes/Edit.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Routes/Edit.cshtml @@ -1,15 +1,16 @@ @model Resgrid.Web.Areas.User.Models.Routes.RouteEditView +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | Edit Route"; + ViewBag.Title = "Resgrid | " + localizer["EditRoutePageTitle"]; }
-

Edit Route Plan

+

@localizer["EditRoutePlanHeader"]

@@ -18,31 +19,31 @@
-
Route Details
+
@localizer["RouteDetailsTitle"]
@Html.AntiForgeryToken()
- +
- +
- +
- - - - + + + +
- +
- +
- +
@@ -93,40 +94,51 @@
-

Stops (@Model.Stops.Count)

+
+

@localizer["Stops"] (@Model.Stops.Count)

+ +
NameDescriptionCreated@localizer["Name"]@localizer["Description"]@localizer["Created"]
@map.Description @map.AddedOn.ToString("g") - Floors - Edit - Delete + @localizer["Floors"] + @localizer["Edit"] + @localizer["Delete"]
- Name + @localizer["Name"] - Color + @localizer["Color"] - Item Count + @localizer["ItemCount"] - Edit - Delete + @localizer["Edit"] + @localizer["Delete"]
- Name + @localizer["Name"] - Color + @localizer["Color"] - Point Count + @localizer["PointCount"] - View + @localizer["View"] @if (ClaimsAuthorizationHelper.IsUserDepartmentAdmin()) { - Add - Import - Delete + @localizer["Add"] + @localizer["Import"] + @localizer["Delete"] }
- - - - - + + + + + + - + @foreach (var stop in Model.Stops) { - + + } @@ -139,8 +151,8 @@
- Cancel - + @localizer["Cancel"] +
@@ -150,11 +162,127 @@ + + + @section Scripts { } diff --git a/Web/Resgrid.Web/Areas/User/Views/Routes/Index.cshtml b/Web/Resgrid.Web/Areas/User/Views/Routes/Index.cshtml index 3d27dd6c..7ac3cdf5 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Routes/Index.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Routes/Index.cshtml @@ -1,25 +1,26 @@ @model Resgrid.Web.Areas.User.Models.Routes.RouteIndexView @using Resgrid.Model +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | Routes"; + ViewBag.Title = "Resgrid | " + localizer["RoutesPageTitle"]; }
-

Routes

+

@localizer["RoutesHeader"]

@@ -33,12 +34,12 @@
#NameTypeAddressPriority@localizer["StopsTableHash"]@localizer["Name"]@localizer["StopType"]@localizer["Address"]@localizer["Priority"]
@stop.StopOrder @stop.Name @((Resgrid.Model.RouteStopType)stop.StopType) @stop.Address @((Resgrid.Model.RouteStopPriority)stop.Priority) + +
- - - - - - + + + + + + @@ -52,18 +53,18 @@
NameStatusStopsProfileCreatedActions@localizer["Name"]@localizer["Status"]@localizer["Stops"]@localizer["Profile"]@localizer["Created"]@localizer["Actions"]
@plan.AddedOn.ToString("yyyy-MM-dd") - View + @localizer["View"] - Edit + @localizer["Edit"] - History + @localizer["History"]
@Html.AntiForgeryToken() -
diff --git a/Web/Resgrid.Web/Areas/User/Views/Routes/InstanceDetail.cshtml b/Web/Resgrid.Web/Areas/User/Views/Routes/InstanceDetail.cshtml index 72333d6c..dd6480d5 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Routes/InstanceDetail.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Routes/InstanceDetail.cshtml @@ -1,15 +1,16 @@ @model Resgrid.Web.Areas.User.Models.Routes.RouteInstanceDetailView +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | Route Instance Detail"; + ViewBag.Title = "Resgrid | " + localizer["RouteInstanceDetailPageTitle"]; }
-

Route Instance - @Model.Plan.Name

+

@localizer["RouteInstanceHeaderPrefix"] - @Model.Plan.Name

@@ -18,7 +19,7 @@
-
Route Map
+
@localizer["RouteMapTitle"]
@@ -26,18 +27,18 @@
-
Instance Info
+
@localizer["InstanceInfoTitle"]
-
Status
+
@localizer["StatusLabel"]
@((Resgrid.Model.RouteInstanceStatus)Model.Instance.Status)
-
Unit
+
@localizer["Unit"]
@Model.Instance.UnitId
-
Started
+
@localizer["StartedDt"]
@(Model.Instance.ActualStartOn?.ToString("yyyy-MM-dd HH:mm") ?? "-")
-
Ended
+
@localizer["EndedDt"]
@(Model.Instance.ActualEndOn?.ToString("yyyy-MM-dd HH:mm") ?? "-")
-
Progress
+
@localizer["Progress"]
@Model.Instance.StopsCompleted / @Model.Instance.StopsTotal
@@ -50,35 +51,35 @@
-
Stop Timeline
+
@localizer["StopTimelineTitle"]
@foreach (var stop in Model.Stops.OrderBy(s => s.StopOrder)) { var statusClass = stop.Status switch { 1 => "lazur-bg", 2 => "navy-bg", 3 => "warning-bg", _ => "default-bg" }; - var statusText = stop.Status switch { 0 => "Pending", 1 => "Checked In", 2 => "Completed", 3 => "Skipped", _ => "Unknown" }; + var statusText = stop.Status switch { 0 => localizer["StopStatusPending"].Value, 1 => localizer["StopStatusCheckedIn"].Value, 2 => localizer["StopStatusCompleted"].Value, 3 => localizer["StopStatusSkipped"].Value, _ => localizer["StopStatusUnknown"].Value };
-

Stop @(stop.StopOrder + 1)

+

@localizer["StopLabel"] @(stop.StopOrder + 1)

@statusText

@if (stop.CheckInOn.HasValue) { -

In: @stop.CheckInOn.Value.ToString("HH:mm:ss")

+

@localizer["StopInPrefix"] @stop.CheckInOn.Value.ToString("HH:mm:ss")

} @if (stop.CheckOutOn.HasValue) { -

Out: @stop.CheckOutOn.Value.ToString("HH:mm:ss")

+

@localizer["StopOutPrefix"] @stop.CheckOutOn.Value.ToString("HH:mm:ss")

} @if (stop.DwellSeconds.HasValue) { -

Dwell: @(stop.DwellSeconds.Value / 60)m @(stop.DwellSeconds.Value % 60)s

+

@localizer["StopDwellPrefix"] @(stop.DwellSeconds.Value / 60)m @(stop.DwellSeconds.Value % 60)s

} @if (!string.IsNullOrEmpty(stop.SkipReason)) { -

Skip: @stop.SkipReason

+

@localizer["StopSkipPrefix"] @stop.SkipReason

}
diff --git a/Web/Resgrid.Web/Areas/User/Views/Routes/Instances.cshtml b/Web/Resgrid.Web/Areas/User/Views/Routes/Instances.cshtml index feb90027..ef420dbf 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Routes/Instances.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Routes/Instances.cshtml @@ -1,15 +1,16 @@ @model Resgrid.Web.Areas.User.Models.Routes.RouteInstancesView +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | Route History"; + ViewBag.Title = "Resgrid | " + localizer["RouteHistoryPageTitle"]; }
-

Route History - @Model.Plan.Name

+

@localizer["RouteHistoryHeaderPrefix"] - @Model.Plan.Name

@@ -23,12 +24,12 @@ - - - - - - + + + + + + @@ -42,7 +43,7 @@ diff --git a/Web/Resgrid.Web/Areas/User/Views/Routes/New.cshtml b/Web/Resgrid.Web/Areas/User/Views/Routes/New.cshtml index 09b248b9..dd42b2f3 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Routes/New.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Routes/New.cshtml @@ -1,15 +1,16 @@ @model Resgrid.Web.Areas.User.Models.Routes.RouteNewView +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | New Route"; + ViewBag.Title = "Resgrid | " + localizer["NewRoutePageTitle"]; }
-

New Route Plan

+

@localizer["NewRoutePlanHeader"]

@@ -19,31 +20,31 @@
-
Route Details
+
@localizer["RouteDetailsTitle"]
@Html.AntiForgeryToken()
- +
- +
- +
- - + +
- +
- +
- +
@@ -92,29 +93,29 @@
-

Map & Stops

-

Stops can be managed after creating the route via the API or edit form.

+

@localizer["MapAndStopsTitle"]

+

@localizer["MapAndStopsHelp"]

- Cancel - + @localizer["Cancel"] +
diff --git a/Web/Resgrid.Web/Areas/User/Views/Routes/View.cshtml b/Web/Resgrid.Web/Areas/User/Views/Routes/View.cshtml index 9050d60e..14938540 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Routes/View.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Routes/View.cshtml @@ -1,15 +1,16 @@ @model Resgrid.Web.Areas.User.Models.Routes.RouteDetailView +@inject IStringLocalizer localizer @{ - ViewBag.Title = "Resgrid | Route Detail"; + ViewBag.Title = "Resgrid | " + localizer["RouteDetailPageTitle"]; }

@Model.Plan.Name

@@ -18,7 +19,7 @@
-
Route Map
+
@localizer["RouteMapTitle"]
@@ -26,23 +27,23 @@
-
Details
+
@localizer["DetailsTitle"]
-
Status
+
@localizer["StatusLabel"]
@((Resgrid.Model.RouteStatus)Model.Plan.RouteStatus)
-
Profile
+
@localizer["ProfileLabel"]
@(Model.Plan.MapboxRouteProfile ?? "driving")
-
Geofence
+
@localizer["GeofenceLabel"]
@Model.Plan.GeofenceRadiusMeters m
@if (Model.Plan.EstimatedDistanceMeters.HasValue) { -
Distance
+
@localizer["DistanceLabel"]
@(Math.Round(Model.Plan.EstimatedDistanceMeters.Value / 1000, 1)) km
} @if (Model.Plan.EstimatedDurationSeconds.HasValue) { -
Duration
+
@localizer["DurationLabel"]
@(Math.Round(Model.Plan.EstimatedDurationSeconds.Value / 60, 0)) min
}
@@ -50,7 +51,7 @@
-
Stops (@Model.Stops.Count)
+
@localizer["Stops"] (@Model.Stops.Count)
@foreach (var stop in Model.Stops.OrderBy(s => s.StopOrder)) @@ -77,8 +78,8 @@ var osmTileUrl = '@Resgrid.Config.MappingConfig.GetWebsiteOSMUrl()'; var osmTileAttribution = '@Resgrid.Config.MappingConfig.LeafletAttribution'; var routeStops = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.Stops.OrderBy(s => s.StopOrder).Select(s => new { s.Name, lat = s.Latitude, lng = s.Longitude }))); - var routeGeometry = '@Html.Raw(Model.Plan.MapboxRouteGeometry ?? "")'; - var routeColor = '@Html.Raw(Model.Plan.RouteColor ?? "#3388ff")'; + var routeGeometry = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.Plan.MapboxRouteGeometry ?? "")); + var routeColor = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.Plan.RouteColor ?? "#3388ff")); } diff --git a/Web/Resgrid.Web/Areas/User/Views/Shared/_ChatWidget.cshtml b/Web/Resgrid.Web/Areas/User/Views/Shared/_ChatWidget.cshtml index ce86b323..7bf54376 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Shared/_ChatWidget.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Shared/_ChatWidget.cshtml @@ -1,5 +1,5 @@ - - + +
diff --git a/Web/Resgrid.Web/Areas/User/Views/Subscription/ViewInvoice.cshtml b/Web/Resgrid.Web/Areas/User/Views/Subscription/ViewInvoice.cshtml index c9819a89..202989ee 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Subscription/ViewInvoice.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Subscription/ViewInvoice.cshtml @@ -15,7 +15,7 @@ -
- + - + diff --git a/Web/Resgrid.Web/Views/Account/AffiliateRegister.cshtml b/Web/Resgrid.Web/Views/Account/AffiliateRegister.cshtml index 07966569..8fc9584d 100644 --- a/Web/Resgrid.Web/Views/Account/AffiliateRegister.cshtml +++ b/Web/Resgrid.Web/Views/Account/AffiliateRegister.cshtml @@ -83,7 +83,7 @@ }
- + diff --git a/Web/Resgrid.Web/Views/Account/CompletedInvite.cshtml b/Web/Resgrid.Web/Views/Account/CompletedInvite.cshtml index 59d313c7..79f9785c 100644 --- a/Web/Resgrid.Web/Views/Account/CompletedInvite.cshtml +++ b/Web/Resgrid.Web/Views/Account/CompletedInvite.cshtml @@ -13,7 +13,7 @@ Resgrid | @localizer["CompletedInviteHeader"] -
- + - + diff --git a/Web/Resgrid.Web/Views/Account/ForgotPassword.cshtml b/Web/Resgrid.Web/Views/Account/ForgotPassword.cshtml index c05c35ae..dc31f9b0 100644 --- a/Web/Resgrid.Web/Views/Account/ForgotPassword.cshtml +++ b/Web/Resgrid.Web/Views/Account/ForgotPassword.cshtml @@ -65,7 +65,7 @@
- + diff --git a/Web/Resgrid.Web/Views/Account/Lockout.cshtml b/Web/Resgrid.Web/Views/Account/Lockout.cshtml index b771e5b6..722ecb7c 100644 --- a/Web/Resgrid.Web/Views/Account/Lockout.cshtml +++ b/Web/Resgrid.Web/Views/Account/Lockout.cshtml @@ -12,7 +12,7 @@ Resgrid | @localizer["LockedOutHeader"] -
- + - + diff --git a/Web/Resgrid.Web/Views/Account/MissingCode.cshtml b/Web/Resgrid.Web/Views/Account/MissingCode.cshtml index 9792958d..0a5e7fdc 100644 --- a/Web/Resgrid.Web/Views/Account/MissingCode.cshtml +++ b/Web/Resgrid.Web/Views/Account/MissingCode.cshtml @@ -11,7 +11,7 @@ - -
- + - + diff --git a/Web/Resgrid.Web/Views/Account/MissingInvite.cshtml b/Web/Resgrid.Web/Views/Account/MissingInvite.cshtml index 04c5e3f0..dd7163b6 100644 --- a/Web/Resgrid.Web/Views/Account/MissingInvite.cshtml +++ b/Web/Resgrid.Web/Views/Account/MissingInvite.cshtml @@ -13,7 +13,7 @@ Resgrid | @localizer["MissingInviteHeader"] -
- + - + diff --git a/Web/Resgrid.Web/Views/Account/Register.cshtml b/Web/Resgrid.Web/Views/Account/Register.cshtml index 5906d9cd..74f0b5c4 100644 --- a/Web/Resgrid.Web/Views/Account/Register.cshtml +++ b/Web/Resgrid.Web/Views/Account/Register.cshtml @@ -140,7 +140,7 @@
- + diff --git a/Web/Resgrid.Web/Views/Shared/Error.cshtml b/Web/Resgrid.Web/Views/Shared/Error.cshtml index 33b24598..6b00ab81 100644 --- a/Web/Resgrid.Web/Views/Shared/Error.cshtml +++ b/Web/Resgrid.Web/Views/Shared/Error.cshtml @@ -10,7 +10,7 @@ Resgrid - Error -
- + - + diff --git a/Web/Resgrid.Web/Views/Shared/Unauthorized.cshtml b/Web/Resgrid.Web/Views/Shared/Unauthorized.cshtml index 8efdc7aa..b68831dd 100644 --- a/Web/Resgrid.Web/Views/Shared/Unauthorized.cshtml +++ b/Web/Resgrid.Web/Views/Shared/Unauthorized.cshtml @@ -10,7 +10,7 @@ Resgrid - Unauthorized - - + - + diff --git a/Web/Resgrid.Web/Views/Shared/_ScriptsPartial.cshtml b/Web/Resgrid.Web/Views/Shared/_ScriptsPartial.cshtml index aafd1f66..1b232877 100644 --- a/Web/Resgrid.Web/Views/Shared/_ScriptsPartial.cshtml +++ b/Web/Resgrid.Web/Views/Shared/_ScriptsPartial.cshtml @@ -1,10 +1,10 @@ - - diff --git a/Web/Resgrid.Web/Areas/User/Views/Shared/_TopNavbar.cshtml b/Web/Resgrid.Web/Areas/User/Views/Shared/_TopNavbar.cshtml index e57ded54..01191333 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Shared/_TopNavbar.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Shared/_TopNavbar.cshtml @@ -85,6 +85,9 @@ + diff --git a/Web/Resgrid.Web/Views/Account/LogOn.cshtml b/Web/Resgrid.Web/Views/Account/LogOn.cshtml index a696457f..c08d520a 100644 --- a/Web/Resgrid.Web/Views/Account/LogOn.cshtml +++ b/Web/Resgrid.Web/Views/Account/LogOn.cshtml @@ -126,6 +126,9 @@ + @@ -177,7 +180,8 @@ 'fr': '/images/flags/32/France.png', 'it': '/images/flags/32/Italy.png', 'pl': '/images/flags/32/Poland.png', - 'uk': '/images/flags/32/Ukraine.png' + 'uk': '/images/flags/32/Ukraine.png', + 'ar': '/images/flags/32/Saudi-Arabia.png' }; if (langCookieValue) { diff --git a/Web/Resgrid.Web/libman.json b/Web/Resgrid.Web/libman.json index e424aecc..30257af5 100644 --- a/Web/Resgrid.Web/libman.json +++ b/Web/Resgrid.Web/libman.json @@ -323,6 +323,6 @@ "bundle.min.js", "bundle.min.js.map" ] - }, + } ] } 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 4a65551f..67a33ba6 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 @@ -121,7 +121,7 @@ var resgrid; fetch(resgrid.absoluteApiBaseUrl + '/api/v4/Geocoding/ForwardGeocode?address=' + encodeURIComponent(where), { headers: { 'Authorization': 'Bearer ' + localStorage.getItem('RgWebApp.auth-tokens') } }) .then(function(r) { return r.json(); }) .then(function(result) { - if (result && result.Data && result.Data.Latitude && result.Data.Longitude) { + if (result && result.Data && result.Data.Latitude != null && result.Data.Longitude != null) { var lat = result.Data.Latitude; var lng = result.Data.Longitude; map.panTo(new L.LatLng(lat, lng)); 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 eef3bef6..bf647001 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 @@ -114,7 +114,7 @@ var resgrid; fetch(resgrid.absoluteApiBaseUrl + '/api/v4/Geocoding/ForwardGeocode?address=' + encodeURIComponent(where), { headers: { 'Authorization': 'Bearer ' + localStorage.getItem('RgWebApp.auth-tokens') } }) .then(function(r) { return r.json(); }) .then(function(result) { - if (result && result.Data && result.Data.Latitude && result.Data.Longitude) { + if (result && result.Data && result.Data.Latitude != null && result.Data.Longitude != null) { var lat = result.Data.Latitude; var lng = result.Data.Longitude; map.panTo(new L.LatLng(lat, lng)); @@ -137,7 +137,7 @@ var resgrid; contentType: 'application/json', type: 'GET' }).done(function (data) { - if (data && data.Latitude && data.Longitude) { + if (data && data.Latitude != null && data.Longitude != null) { map.panTo(new L.LatLng(data.Latitude, data.Longitude)); $("#Latitude").val(data.Latitude); diff --git a/Web/Resgrid.Web/wwwroot/js/app/internal/resgrid.user.js b/Web/Resgrid.Web/wwwroot/js/app/internal/resgrid.user.js index 352ce6ac..672f764e 100644 --- a/Web/Resgrid.Web/wwwroot/js/app/internal/resgrid.user.js +++ b/Web/Resgrid.Web/wwwroot/js/app/internal/resgrid.user.js @@ -54,7 +54,8 @@ var resgrid; 'fr': '/images/flags/32/France.png', 'it': '/images/flags/32/Italy.png', 'pl': '/images/flags/32/Poland.png', - 'uk': '/images/flags/32/Ukraine.png' + 'uk': '/images/flags/32/Ukraine.png', + 'ar': '/images/flags/32/Saudi-Arabia.png' }; if (langCookieValue) { From ded747604ea241a85dc4dd61308f4cf9b1b1a963 Mon Sep 17 00:00:00 2001 From: Shawn Jackson Date: Tue, 17 Mar 2026 16:21:26 -0700 Subject: [PATCH 3/3] RE1-T105 Bug fixes --- Web/Resgrid.Web/Areas/User/Controllers/IndoorMapsController.cs | 1 + Web/Resgrid.Web/Areas/User/Views/Dispatch/Dashboard.cshtml | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Web/Resgrid.Web/Areas/User/Controllers/IndoorMapsController.cs b/Web/Resgrid.Web/Areas/User/Controllers/IndoorMapsController.cs index 7bf5e3f2..b25e986d 100644 --- a/Web/Resgrid.Web/Areas/User/Controllers/IndoorMapsController.cs +++ b/Web/Resgrid.Web/Areas/User/Controllers/IndoorMapsController.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Resgrid.Model.Services; namespace Resgrid.Web.Areas.User.Controllers diff --git a/Web/Resgrid.Web/Areas/User/Views/Dispatch/Dashboard.cshtml b/Web/Resgrid.Web/Areas/User/Views/Dispatch/Dashboard.cshtml index c70d7217..9740429b 100644 --- a/Web/Resgrid.Web/Areas/User/Views/Dispatch/Dashboard.cshtml +++ b/Web/Resgrid.Web/Areas/User/Views/Dispatch/Dashboard.cshtml @@ -102,8 +102,6 @@ @section Scripts { - -
StatusUnitStartedEndedStopsActions@localizer["Status"]@localizer["Unit"]@localizer["Started"]@localizer["Ended"]@localizer["Stops"]@localizer["Actions"]
@instance.StopsCompleted / @instance.StopsTotal - Detail + @localizer["Detail"]