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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Web/Resgrid.Web.Services/Controllers/v4/CallsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -672,8 +672,8 @@ public async Task<ActionResult<SaveCallResult>> SaveCall([FromBody] NewCallInput
if (!string.IsNullOrWhiteSpace(newCallInput.What3Words))
call.W3W = newCallInput.What3Words;

if (!string.IsNullOrWhiteSpace(newCallInput.CallFormData))
call.CallFormData = newCallInput.CallFormData;
// Forms module is disabled: CallFormData is read-only now. The input property stays on the
// contract so older clients keep deserializing, but anything they send is dropped.

if (!string.IsNullOrWhiteSpace(newCallInput.IndoorMapZoneId))
call.IndoorMapZoneId = newCallInput.IndoorMapZoneId;
Expand Down Expand Up @@ -992,8 +992,8 @@ public async Task<ActionResult<EditCallResult>> EditCall([FromBody] EditCallInpu
if (!string.IsNullOrWhiteSpace(editCallInput.What3Words))
call.W3W = editCallInput.What3Words;

if (!string.IsNullOrWhiteSpace(editCallInput.CallFormData))
call.CallFormData = editCallInput.CallFormData;
// Forms module is disabled: ignoring the posted value leaves whatever form data the call
// already carries intact, so an edit from an older client can't wipe or replace it.

if (editCallInput.DispatchOn.HasValue)
{
Expand Down
22 changes: 7 additions & 15 deletions Web/Resgrid.Web.Services/Controllers/v4/FormsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,26 +35,18 @@ public FormsController(IFormsService formsService)
[HttpGet("GetNewCallForm")]
[ProducesResponseType(StatusCodes.Status200OK)]
[Authorize(Policy = ResgridResources.Forms_View)]
public async Task<ActionResult<FormResult>> GetNewCallForm()
public Task<ActionResult<FormResult>> GetNewCallForm()
{
// Forms module is disabled. Reporting "no form configured" is the same shape clients
// already handle for departments that never built one, so shipped app versions stop
// offering call form entry without needing a release.
var result = new FormResult();
var form = await _formsService.GetNewCallFormByDepartmentIdAsync(DepartmentId);

if (form != null)
{
result.Data = ConvertFormResultData(form);
result.PageSize = 1;
result.Status = ResponseHelper.Success;
}
else
{
result.PageSize = 0;
result.Status = ResponseHelper.NotFound;
}
result.PageSize = 0;
result.Status = ResponseHelper.NotFound;

ResponseHelper.PopulateV4ResponseData(result);

return Ok(result);
return Task.FromResult<ActionResult<FormResult>>(Ok(result));
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public class MapMakerInfoData
/// <summary>
/// The POI-specific custom icon image name (only set for POI markers, Type=4).
/// New app versions should use this field instead of ImagePath for POI icons.
/// ImagePath is set to null for POI markers so old apps fall back to their default icon.
/// ImagePath carries the bitmap-asset name resolved by PoiIconHelper for clients that ship PNGs.
/// </summary>
public string PoiImage { get; set; }
}
Expand All @@ -72,7 +72,7 @@ public class PoiLayerData
/// <summary>
/// The POI-specific custom icon image name.
/// New app versions should use this field for POI type icons.
/// ImagePath is set to null so old apps fall back to their default icon.
/// ImagePath carries the bitmap-asset name resolved by PoiIconHelper for clients that ship PNGs.
/// </summary>
public string PoiImage { get; set; }
}
Expand Down
4 changes: 2 additions & 2 deletions Web/Resgrid.Web.Services/Models/v4/Mapping/PoiResultModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class PoiTypeResultData
/// <summary>
/// The POI-specific custom icon image name.
/// New app versions should use this field for POI type icons.
/// ImagePath is set to null so old apps fall back to their default icon.
/// ImagePath carries the bitmap-asset name resolved by PoiIconHelper for clients that ship PNGs.
/// </summary>
public string PoiImage { get; set; }
}
Expand All @@ -37,7 +37,7 @@ public class PoiResultData
/// <summary>
/// The POI-specific custom icon image name.
/// New app versions should use this field for POI icons.
/// ImagePath is set to null so old apps fall back to their default icon.
/// ImagePath carries the bitmap-asset name resolved by PoiIconHelper for clients that ship PNGs.
/// </summary>
public string PoiImage { get; set; }
}
Expand Down
8 changes: 4 additions & 4 deletions Web/Resgrid.Web.Services/Resgrid.Web.Services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10057,14 +10057,14 @@
<summary>
The POI-specific custom icon image name (only set for POI markers, Type=4).
New app versions should use this field instead of ImagePath for POI icons.
ImagePath is set to null for POI markers so old apps fall back to their default icon.
ImagePath carries the bitmap-asset name resolved by PoiIconHelper for clients that ship PNGs.
</summary>
</member>
<member name="P:Resgrid.Web.Services.Models.v4.Mapping.PoiLayerData.PoiImage">
<summary>
The POI-specific custom icon image name.
New app versions should use this field for POI type icons.
ImagePath is set to null so old apps fall back to their default icon.
ImagePath carries the bitmap-asset name resolved by PoiIconHelper for clients that ship PNGs.
</summary>
</member>
<member name="P:Resgrid.Web.Services.Models.v4.Mapping.GetMapLayersResult.Data">
Expand All @@ -10091,14 +10091,14 @@
<summary>
The POI-specific custom icon image name.
New app versions should use this field for POI type icons.
ImagePath is set to null so old apps fall back to their default icon.
ImagePath carries the bitmap-asset name resolved by PoiIconHelper for clients that ship PNGs.
</summary>
</member>
<member name="P:Resgrid.Web.Services.Models.v4.Mapping.PoiResultData.PoiImage">
<summary>
The POI-specific custom icon image name.
New app versions should use this field for POI icons.
ImagePath is set to null so old apps fall back to their default icon.
ImagePath carries the bitmap-asset name resolved by PoiIconHelper for clients that ship PNGs.
</summary>
</member>
<member name="P:Resgrid.Web.Services.Models.v4.Mapping.MapFeatureResultData.FeatureType">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'leaflet/dist/leaflet.css';
import {
getLayerColor,
getMarkerIconUrl,
getPoiIconClass,
getPoiMarkerShapePath,
isPoiMarker,
type MapMarkerInfo,
Expand All @@ -17,6 +18,7 @@ interface MarkerState {
longitude: number;
title: string;
imagePath: string;
poiImage: string;
markerShape: string;
color: string;
markerType: number;
Expand All @@ -26,9 +28,7 @@ interface MarkerState {

function createMarkerIcon(marker: MapMarkerInfo): L.Icon | L.DivIcon {
if (isPoiMarker(marker)) {
const iconClass = typeof marker.ImagePath === 'string' && marker.ImagePath.length > 0
? marker.ImagePath
: 'map-icon-map-pin';
const iconClass = getPoiIconClass(marker);
const color = marker.Color || '#2563eb';

return L.divIcon({
Expand Down Expand Up @@ -175,6 +175,7 @@ export default function LeafletMapView({
!existingMarkerState ||
existingMarkerState.title !== markerInfo.Title ||
existingMarkerState.imagePath !== markerInfo.ImagePath ||
existingMarkerState.poiImage !== (markerInfo.PoiImage ?? '') ||
existingMarkerState.markerShape !== (markerInfo.Marker ?? '') ||
existingMarkerState.color !== (markerInfo.Color ?? '') ||
existingMarkerState.markerType !== markerInfo.Type ||
Expand All @@ -193,6 +194,7 @@ export default function LeafletMapView({
longitude: markerInfo.Longitude,
title: markerInfo.Title,
imagePath: markerInfo.ImagePath,
poiImage: markerInfo.PoiImage ?? '',
markerShape: markerInfo.Marker ?? '',
color: markerInfo.Color ?? '',
markerType: markerInfo.Type,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'mapbox-gl/dist/mapbox-gl.css';
import {
getLayerColor,
getMarkerIconUrl,
getPoiIconClass,
getPoiMarkerShapePath,
isPoiMarker,
type MapMarkerInfo,
Expand All @@ -19,6 +20,7 @@ interface MarkerState {
longitude: number;
title: string;
imagePath: string;
poiImage: string;
markerShape: string;
color: string;
markerType: number;
Expand All @@ -44,7 +46,7 @@ function createMarkerElement(markerInfo: MapMarkerInfo, hideLabels: boolean): HT
markerShape.appendChild(path);

const icon = document.createElement('span');
icon.className = `map-icon ${markerInfo.ImagePath || 'map-icon-map-pin'} rg-map__poi-marker-icon`;
icon.className = `map-icon ${getPoiIconClass(markerInfo)} rg-map__poi-marker-icon`;
icon.setAttribute('aria-hidden', 'true');

wrapper.appendChild(markerShape);
Expand Down Expand Up @@ -327,6 +329,7 @@ export default function MapboxMapView({
!existingMarkerState ||
existingMarkerState.title !== markerInfo.Title ||
existingMarkerState.imagePath !== markerInfo.ImagePath ||
existingMarkerState.poiImage !== (markerInfo.PoiImage ?? '') ||
existingMarkerState.markerShape !== (markerInfo.Marker ?? '') ||
existingMarkerState.color !== (markerInfo.Color ?? '') ||
existingMarkerState.markerType !== markerInfo.Type ||
Expand All @@ -353,6 +356,7 @@ export default function MapboxMapView({
longitude: markerInfo.Longitude,
title: markerInfo.Title,
imagePath: markerInfo.ImagePath,
poiImage: markerInfo.PoiImage ?? '',
markerShape: markerInfo.Marker ?? '',
color: markerInfo.Color ?? '',
markerType: markerInfo.Type,
Expand Down
25 changes: 22 additions & 3 deletions Web/Resgrid.Web/Areas/User/Apps/src/components/map/mapTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface MapMarkerInfo {
Title: string;
zIndex: number;
ImagePath: string;
PoiImage?: string;
InfoWindowContent: string;
Color: string;
Type: number | string;
Expand All @@ -32,6 +33,7 @@ export interface PoiLayerInfo {
Name: string;
Color: string;
ImagePath: string;
PoiImage?: string;
Marker: string;
IsDestination: boolean;
}
Expand Down Expand Up @@ -120,6 +122,8 @@ export interface MapRendererProps {
}

const defaultPoiMarkerShape = 'MAP_PIN';
const poiIconClassPrefix = 'map-icon-';
const defaultPoiIconClass = 'map-icon-map-pin';

const poiMarkerPaths: Record<string, string> = {
MAP_PIN: 'M0-48c-9.8 0-17.7 7.8-17.7 17.4 0 15.5 17.7 30.6 17.7 30.6s17.7-15.4 17.7-30.6c0-9.6-7.9-17.4-17.7-17.4z',
Expand Down Expand Up @@ -204,7 +208,7 @@ function getMarkerTypeValue(marker: Pick<MapMarkerInfo, 'Type'>): number | null
}

export function isPoiMarker(
marker: Pick<MapMarkerInfo, 'Type' | 'PoiTypeId' | 'LayerId' | 'ImagePath'>,
marker: Pick<MapMarkerInfo, 'Type' | 'PoiTypeId' | 'LayerId' | 'ImagePath' | 'PoiImage'>,
): boolean {
if (getMarkerTypeValue(marker) === mapMarkerTypes.poi) {
return true;
Expand All @@ -218,8 +222,23 @@ export function isPoiMarker(
return true;
}

return typeof marker.ImagePath === 'string'
&& marker.ImagePath.trim().toLowerCase().startsWith('map-icon-');
return isPoiIconClass(getStringValue(marker.PoiImage, marker.ImagePath));
}

function isPoiIconClass(candidate: string): boolean {
return candidate.toLowerCase().startsWith(poiIconClassPrefix);
}

/**
* POI glyphs come from the map-icons web font, so the marker needs a "map-icon-*" class. The API
* carries that class in PoiImage; ImagePath holds a short PNG name ("hospital", "firstaid") for the
* apps that ship bitmap assets and is not a valid font class. Older API responses put the class in
* ImagePath, so that is still accepted when it looks like one.
*/
export function getPoiIconClass(marker: Pick<MapMarkerInfo, 'ImagePath' | 'PoiImage'>): string {
const candidate = getStringValue(marker.PoiImage, marker.ImagePath);

return isPoiIconClass(candidate) ? candidate : defaultPoiIconClass;
}

export function getPoiLayerId(layer: Pick<PoiLayerInfo, 'PoiTypeId'> | Pick<MapMarkerInfo, 'PoiTypeId' | 'LayerId'>): string {
Expand Down
21 changes: 11 additions & 10 deletions Web/Resgrid.Web/Areas/User/Controllers/DispatchController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ public class DispatchController : SecureBaseController
private readonly ITemplatesService _templatesService;
private readonly IPdfProvider _pdfProvider;
private readonly IProtocolsService _protocolsService;
private readonly IFormsService _formsService;
private readonly IShiftsService _shiftsService;
private readonly IContactsService _contactsService;
private readonly IMappingService _mappingService;
Expand All @@ -82,7 +81,7 @@ public DispatchController(IDepartmentsService departmentsService, IUsersService
Model.Services.IAuthorizationService authorizationService, IWorkLogsService workLogsService, IGeoLocationProvider geoLocationProvider,
IPersonnelRolesService personnelRolesService, IDepartmentSettingsService departmentSettingsService, IUserProfileService userProfileService,
IUnitsService unitsService, IActionLogsService actionLogsService, IEventAggregator eventAggregator, ICustomStateService customStateService,
ITemplatesService templatesService, IPdfProvider pdfProvider, IProtocolsService protocolsService, IFormsService formsService,
ITemplatesService templatesService, IPdfProvider pdfProvider, IProtocolsService protocolsService,
IShiftsService shiftsService, IContactsService contactsService, IMappingService mappingService,
IUserDefinedFieldsService userDefinedFieldsService, IUdfRenderingService udfRenderingService,
ICheckInTimerService checkInTimerService, IWeatherAlertService weatherAlertService,
Expand All @@ -109,7 +108,6 @@ public DispatchController(IDepartmentsService departmentsService, IUsersService
_templatesService = templatesService;
_pdfProvider = pdfProvider;
_protocolsService = protocolsService;
_formsService = formsService;
_shiftsService = shiftsService;
_contactsService = contactsService;
_mappingService = mappingService;
Expand Down Expand Up @@ -298,6 +296,10 @@ public async Task<IActionResult> NewCall(NewCallView model, IFormCollection coll
model.Call.DepartmentId = DepartmentId;
model.Call.Priority = (int)model.CallPriority;
model.Call.State = 0;

// Forms module is disabled. The hidden field is model-bound, so drop anything posted
// into it rather than trusting the client not to send form data.
model.Call.CallFormData = null;
model.Call.NatureOfCall = System.Net.WebUtility.HtmlDecode(model.Call.NatureOfCall);
model.Call.Notes = System.Net.WebUtility.HtmlDecode(model.Call.Notes);

Expand Down Expand Up @@ -1377,6 +1379,7 @@ public async Task<IActionResult> AddArchivedCall(NewCallView model, IFormCollect
model.Call.Priority = (int)model.CallPriority;
model.Call.State = 0;
model.Call.LoggedOn = model.Call.LoggedOn.ToUniversalTime();
model.Call.CallFormData = null;
model.Call.NatureOfCall = System.Net.WebUtility.HtmlDecode(model.Call.NatureOfCall);
model.Call.Notes = System.Net.WebUtility.HtmlDecode(model.Call.Notes);

Expand Down Expand Up @@ -3095,10 +3098,9 @@ private async Task<NewCallView> FillNewCallView(NewCallView model)

model.Call.ReportingUserId = UserId;

var form = await _formsService.GetNewCallFormByDepartmentIdAsync(DepartmentId);

if (form != null)
model.NewCallFormData = form.Data;
// Forms module is disabled: leaving NewCallFormData empty drops the call form button,
// its modal and the formRender call from the new/archived call views. Form data already
// on existing calls is untouched and still renders read-only on the call detail view.

model.Contacts = await _contactsService.GetAllContactsForDepartmentAsync(DepartmentId);
if (model.Contacts != null && model.Contacts.Any())
Expand Down Expand Up @@ -3150,9 +3152,8 @@ private async Task<UpdateCallView> FillUpdateCallView(UpdateCallView model)
if (templates != null)
model.CallTemplates = new SelectList(templates, "CallQuickTemplateId", "Name");

var form = await _formsService.GetNewCallFormByDepartmentIdAsync(DepartmentId);
if (form != null)
model.NewCallFormData = form.Data;
// Forms module is disabled, see FillNewCallView. UpdateCall never copies CallFormData
// from the posted model onto the stored call, so existing form data survives an edit.

var allUsers = await _departmentsService.GetAllUsersForDepartmentAsync(model.Department.DepartmentId);

Expand Down
12 changes: 12 additions & 0 deletions Web/Resgrid.Web/Areas/User/Controllers/FormsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Resgrid.Model;
using Resgrid.Model.Services;
using Resgrid.Providers.Claims;
Expand All @@ -31,6 +32,17 @@ public FormsController(IFormsService formsService, ICallsService callsService, I
_departmentsService = departmentsService;
}

/// <summary>
/// The Forms module is disabled. Existing form data stays in the database and still renders
/// read-only on the call detail view, but no new form templates can be listed, created or
/// edited, and no new form data can be captured. The actions below are left intact so the
/// module can be turned back on by removing this override.
/// </summary>
public override void OnActionExecuting(ActionExecutingContext context)
{
context.Result = NotFound();
}

[HttpGet]
[Authorize(Policy = ResgridResources.Forms_View)]
public async Task<IActionResult> Index()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@
<script src="~/lib/formBuilder/dist/form-render.min.js" type="text/javascript"></script>

<script type="text/javascript">
var newCallFormData = '@Html.Raw(Model.NewCallFormData)';
var newCallFormData = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.NewCallFormData ?? string.Empty, new Newtonsoft.Json.JsonSerializerSettings { StringEscapeHandling = Newtonsoft.Json.StringEscapeHandling.EscapeHtml }));
var osmTileUrl = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(mapConfig.TileUrl, new Newtonsoft.Json.JsonSerializerSettings { StringEscapeHandling = Newtonsoft.Json.StringEscapeHandling.EscapeHtml }));
var osmTileAttribution = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(mapConfig.Attribution, new Newtonsoft.Json.JsonSerializerSettings { StringEscapeHandling = Newtonsoft.Json.StringEscapeHandling.EscapeHtml }));
var nominatimUrl = '@Resgrid.Config.MappingConfig.NominatimUrl';
Expand Down
3 changes: 1 addition & 2 deletions Web/Resgrid.Web/Areas/User/Views/Dispatch/NewCall.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,7 @@
<script src="@Url.Content("~/lib/formBuilder/dist/form-render.min.js")" type="text/javascript"></script>

<script type="text/javascript">
var newCallFormData = '@Html.Raw(Model.NewCallFormData)';
var newCallFormData = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.NewCallFormData ?? string.Empty, new Newtonsoft.Json.JsonSerializerSettings { StringEscapeHandling = Newtonsoft.Json.StringEscapeHandling.EscapeHtml }));
var osmTileUrl = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(mapConfig.TileUrl, new Newtonsoft.Json.JsonSerializerSettings { StringEscapeHandling = Newtonsoft.Json.StringEscapeHandling.EscapeHtml }));
var osmTileAttribution = @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(mapConfig.Attribution, new Newtonsoft.Json.JsonSerializerSettings { StringEscapeHandling = Newtonsoft.Json.StringEscapeHandling.EscapeHtml }));
var nominatimUrl = '@Resgrid.Config.MappingConfig.NominatimUrl';
Expand All @@ -662,7 +662,6 @@
<script type="text/javascript">
var centerLat = @(Model.CenterCoordinates.Latitude.Value);
var centerLng = @(Model.CenterCoordinates.Longitude.Value);
var newCallFormData = '@Html.Raw(Model.NewCallFormData)';
</script>
}

Expand Down
Loading
Loading