Commit d1a82903 by Mac Stephens

add business to complaint popup and forms, fix zoom issue for view on map,…

add business to complaint popup and forms, fix zoom issue for view on map, remove refresh spinner, update summary arrow, update buf removal from district, stack buttons on unit details, general style fixes
parent 8190ffe0
......@@ -179,8 +179,8 @@ object ApiDatabaseModule: TApiDatabaseModule
''
'')
ReadOnly = True
Left = 82
Top = 186
Left = 76
Top = 188
object uqUnitListUNITID: TFloatField
FieldName = 'UNITID'
end
......@@ -391,7 +391,7 @@ object ApiDatabaseModule: TApiDatabaseModule
' ca.APARTMENT,'
' ca.CITY,'
' ca.BUSINESS,'
' ca.DISPATCHDISTRICT,'
' cd.CODE_DESC AS DISPATCHDISTRICT,'
' ca.DISPATCHSECTOR,'
' ca.ADDRESSDISTRICT,'
' ca.ADDRESSSECTOR,'
......@@ -417,20 +417,17 @@ object ApiDatabaseModule: TApiDatabaseModule
'LEFT JOIN CD_CALLPRIORITIES cp ON cp.CODE = ca.PRIORITY AND cp.A' +
'GENCY = ca.AGENCY'
'LEFT JOIN CD_DISTRICT cd ON cd.AGENCYCODE = ca.DISPATCHDI' +
'JOIN CD_DISTRICT cd ON cd.AGENCYCODE = ca.DISPATCHDI' +
'STRICT'
'LEFT JOIN CD_SECTOR cs ON cs.AGENCYCODE = ca.DISPATCHSE' +
'CTOR AND cs.CODE_TYPE = cd.AGENCYCODE '
'CTOR AND cs.CODE_TYPE = cd.AGENCYCODE'
'WHERE ca.COMPLAINT IS NOT NULL'
'ORDER BY ca.DISPATCHDISTRICT, ct.DATEREPORTED DESC, ca.PRIORITY ' +
'DESC'
'')
'ORDER BY cd.CODE_DESC, ct.DATEREPORTED DESC, ca.PRIORITY DESC;')
ReadOnly = True
OnCalcFields = uqComplaintListCalcFields
Left = 78
Top = 242
Top = 244
object uqComplaintListCOMPLAINTID: TFloatField
FieldName = 'COMPLAINTID'
Required = True
......@@ -568,8 +565,9 @@ object ApiDatabaseModule: TApiDatabaseModule
' ca.PRIORITY,'
' ca.DISPATCHCODE,'
' cdc.CODE_DESC AS DISPATCH_CODE_DESC,'
' ca.DISPATCHDISTRICT,'
' cd.CODE_DESC AS DISPATCHDISTRICT,'
' ca.ADDRESS,'
' ca.BUSINESS,'
' ct.DATEREPORTED,'
' ct.DATERECEIVED,'
' ct.DATEDISPATCHED,'
......@@ -598,6 +596,8 @@ object ApiDatabaseModule: TApiDatabaseModule
' ON ca.COMPLAINTID = ct.COMPLAINTID'
'LEFT JOIN CD_DISPATCHCODES cdc'
' ON ca.DISPATCHCODE = cdc.CODE'
'JOIN CD_DISTRICT cd'
' ON cd.AGENCYCODE = ca.DISPATCHDISTRICT'
'LEFT JOIN ADDRESS a'
' ON ca.ADDRESSID = a.ADDRESSID'
'WHERE ca.COMPLAINTID = :COMPLAINTID;')
......@@ -608,7 +608,7 @@ object ApiDatabaseModule: TApiDatabaseModule
item
DataType = ftUnknown
Name = 'COMPLAINTID'
Value = nil
Value = Null
end>
object uqComplaintDetailsCOMPLAINTID: TFloatField
FieldName = 'COMPLAINTID'
......@@ -636,7 +636,7 @@ object ApiDatabaseModule: TApiDatabaseModule
end
object uqComplaintDetailsDISPATCHDISTRICT: TStringField
FieldName = 'DISPATCHDISTRICT'
Size = 6
Size = 120
end
object uqComplaintDetailsADDRESS: TStringField
FieldName = 'ADDRESS'
......@@ -709,6 +709,10 @@ object ApiDatabaseModule: TApiDatabaseModule
FieldName = 'CITY'
ReadOnly = True
end
object uqComplaintDetailsBUSINESS: TStringField
FieldName = 'BUSINESS'
Size = 35
end
end
object ucENTCAD: TUniConnection
ProviderName = 'Oracle'
......@@ -726,29 +730,24 @@ object ApiDatabaseModule: TApiDatabaseModule
SQL.Strings = (
'SELECT'
' ca.COMPLAINTID,'
' ca.DISPATCHDISTRICT,'
' cd.CODE_DESC AS DISPATCHDISTRICT,'
' ca.PRIORITY AS PRIORITY,'
' ca.BUSINESS,'
' cdc.MOBILE_MAP_CATEGORY AS DISPATCHCODECATEGORY,'
''
' CASE '
' WHEN ca.XCOORD IS NOT NULL AND ca.YCOORD IS NOT NULL THEN'
' SDO_CS.TRANSFORM('
' SDO_CS.TRANSFORM('
' SDO_GEOMETRY(2001, 2262, SDO_POINT_TYPE(ca.XCOORD, ca.YC' +
'OORD, NULL), NULL, NULL),'
' 4326'
' ).sdo_point.x'
' END AS LNG,'
' SDO_GEOMETRY(2001, 2262, SDO_POINT_TYPE(ca.XCOORD, ca.YCOORD' +
', NULL), NULL, NULL),'
' 4326'
' ).sdo_point.x AS LNG,'
''
' CASE '
' WHEN ca.XCOORD IS NOT NULL AND ca.YCOORD IS NOT NULL THEN'
' SDO_CS.TRANSFORM('
' SDO_CS.TRANSFORM('
' SDO_GEOMETRY(2001, 2262, SDO_POINT_TYPE(ca.XCOORD, ca.YC' +
'OORD, NULL), NULL, NULL),'
' 4326'
' ).sdo_point.y'
' END AS LAT,'
' SDO_GEOMETRY(2001, 2262, SDO_POINT_TYPE(ca.XCOORD, ca.YCOORD' +
', NULL), NULL, NULL),'
' 4326'
' ).sdo_point.y AS LAT,'
''
' cdc.CODE_DESC AS DISPATCH_CODE_DESC,'
' ca.ADDRESS AS ADDRESS'
......@@ -757,10 +756,14 @@ object ApiDatabaseModule: TApiDatabaseModule
' ON ct.COMPLAINTID = ca.COMPLAINTID'
'LEFT JOIN CD_DISPATCHCODES cdc'
' ON cdc.CODE = ca.DISPATCHCODE'
'WHERE ca.COMPLAINT IS NOT NULL '
' AND ca.XCOORD IS NOT NULL '
'JOIN CD_DISTRICT cd'
' ON cd.AGENCYCODE = ca.DISPATCHDISTRICT'
'WHERE ca.COMPLAINT IS NOT NULL'
' AND ca.XCOORD IS NOT NULL'
' AND ca.YCOORD IS NOT NULL;'
''
''
''
'')
ReadOnly = True
OnCalcFields = uqMapComplaintsCalcFields
......@@ -772,7 +775,7 @@ object ApiDatabaseModule: TApiDatabaseModule
end
object uqMapComplaintsDISPATCHDISTRICT: TStringField
FieldName = 'DISPATCHDISTRICT'
Size = 6
Size = 120
end
object uqMapComplaintsLNG: TFloatField
FieldName = 'LNG'
......@@ -808,6 +811,10 @@ object ApiDatabaseModule: TApiDatabaseModule
FieldName = 'pngName'
Calculated = True
end
object uqMapComplaintsBUSINESS: TStringField
FieldName = 'BUSINESS'
Size = 35
end
end
object uqBadgeCounts: TUniQuery
Connection = ucENTCAD
......@@ -854,6 +861,7 @@ object ApiDatabaseModule: TApiDatabaseModule
' AND c.YCOORD IS NOT NULL'
')'
'ORDER BY ca.COMPLAINTID, ca.DATEDISPATCHED;')
ReadOnly = True
Left = 464
Top = 202
object uqMapComplaintUnitsListCOMPLAINTID: TFloatField
......@@ -920,6 +928,7 @@ object ApiDatabaseModule: TApiDatabaseModule
'and (ctx.strsuffix is null or c.strsuffix = ctx.strsuffix)'
'and (ctx.city is null or c.city = ctx.city)'
'order by c.datereported desc')
ReadOnly = True
Left = 328
Top = 72
ParamData = <
......@@ -945,6 +954,7 @@ object ApiDatabaseModule: TApiDatabaseModule
'from dis_contact c'
'join ctx on c.addressid = ctx.addressid'
'left join cd_contact_type ct on c.contact_type = ct.code')
ReadOnly = True
Left = 330
Top = 130
ParamData = <
......@@ -1022,6 +1032,7 @@ object ApiDatabaseModule: TApiDatabaseModule
' distance,'
' decode(w.code, '#39'POL'#39', 1, '#39'FIR'#39', 2, 3),'
' wt.code_desc')
ReadOnly = True
Left = 488
Top = 76
ParamData = <
......@@ -1057,7 +1068,8 @@ object ApiDatabaseModule: TApiDatabaseModule
'LEFT JOIN PERSONNEL p2 ON dua.OFFICER2ID = p2.PF_NAMEID'
'LEFT JOIN UNITS_CURRENT@AVL_LINK uc ON dua.UNITID = uc.UNITID'
'WHERE dua.UNITID = :UNITID')
Left = 192
ReadOnly = True
Left = 194
Top = 312
ParamData = <
item
......@@ -1136,6 +1148,7 @@ object ApiDatabaseModule: TApiDatabaseModule
'from ENTCAD.DIS_UNIT_LOG dul'
'where dul.UNITID = :UNITID'
'order by dul."TIMESTAMP" desc')
ReadOnly = True
Left = 190
Top = 374
ParamData = <
......
......@@ -170,6 +170,8 @@ type
uqUnitLogsCOMPLAINT_NUM: TStringField;
uqUnitLogsLOG_TEXT: TStringField;
uqMapUnitsDIS_UNITID: TFloatField;
uqMapComplaintsBUSINESS: TStringField;
uqComplaintDetailsBUSINESS: TStringField;
procedure uqComplaintListCalcFields(DataSet: TDataSet);
procedure uqMapComplaintsCalcFields(DataSet: TDataSet);
private
......
......@@ -17,7 +17,6 @@ type
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
function GetComplaintMemos(const CfsId: string): TJSONObject;
function DispatchDistrictLetter(const value: string): string;
public
function GetBadgeCounts: TJSONObject;
function GetComplaintList: TJSONObject;
......@@ -153,13 +152,14 @@ begin
item:=TJSONObject.Create;
item.AddPair('ComplaintId',ApiDB.uqMapComplaintsCOMPLAINTID.AsString);
item.AddPair('DispatchDistrict', DispatchDistrictLetter(ApiDB.uqMapComplaintsDISPATCHDISTRICT.AsString));
item.AddPair('DispatchDistrict', ApiDB.uqMapComplaintsDISPATCHDISTRICT.AsString);
item.AddPair('DispatchCodeDesc',ApiDB.uqMapComplaintsDISPATCH_CODE_DESC.AsString);
item.AddPair('DispatchCodeCategory',ApiDB.uqMapComplaintsDISPATCHCODECATEGORY.AsString);
item.AddPair('Priority',ApiDB.uqMapComplaintsPRIORITY.AsString);
item.AddPair('priorityKey',ApiDB.uqMapComplaintspriorityKey.AsString);
item.AddPair('pngName',ApiDB.uqMapComplaintspngName.AsString);
item.AddPair('Address',ApiDB.uqMapComplaintsADDRESS.AsString);
item.AddPair('Business',ApiDB.uqMapComplaintsBUSINESS.AsString);
complaintId:=ApiDB.uqMapComplaintsCOMPLAINTID.AsString;
if UnitsByComplaintMap.TryGetValue(complaintId,unitArray) then
......@@ -331,9 +331,10 @@ begin
item.AddPair('Priority', ApiDB.uqComplaintListPRIORITY.AsString);
item.AddPair('DispatchCodeDesc', ApiDB.uqComplaintListDISPATCH_CODE_DESC.AsString);
item.AddPair('Address', ApiDB.uqComplaintListADDRESS.AsString);
item.AddPair('Business', ApiDB.uqComplaintListBUSINESS.AsString);
item.AddPair('CFSId', ApiDB.uqComplaintListCFSID.AsString);
item.AddPair('Status', status);
item.AddPair('DispatchDistrict', DispatchDistrictLetter(curDistrict));
item.AddPair('DispatchDistrict', curDistrict);
item.AddPair('DateReported', ApiDB.uqComplaintListDATEREPORTED.AsString);
data.AddElement(item);
......@@ -465,8 +466,9 @@ begin
obj.AddPair('Priority', ApiDB.uqComplaintDetailsPRIORITY.AsString);
obj.AddPair('DispatchCode', ApiDB.uqComplaintDetailsDISPATCHCODE.AsString);
obj.AddPair('DispatchCodeDesc', ApiDB.uqComplaintDetailsDISPATCH_CODE_DESC.AsString);
obj.AddPair('DispatchDistrict', DispatchDistrictLetter(ApiDB.uqComplaintDetailsDISPATCHDISTRICT.AsString));
obj.AddPair('DispatchDistrict', ApiDB.uqComplaintDetailsDISPATCHDISTRICT.AsString);
obj.AddPair('Address', ApiDB.uqComplaintDetailsADDRESS.AsString);
obj.AddPair('Business',ApiDB.uqComplaintDetailsBUSINESS.AsString);
obj.AddPair('History', ApiDB.uqComplaintDetailsHISTORY.AsString);
obj.AddPair('Contacts', ApiDB.uqComplaintDetailsCONTACTS.AsString);
obj.AddPair('Warnings', ApiDB.uqComplaintDetailsWARNINGS.AsString);
......@@ -604,6 +606,9 @@ begin
try
while not Eof do
begin
if returnedCount >= 50 then
Break;
rowObj := TJSONObject.Create;
dataArr.AddElement(rowObj);
......@@ -893,18 +898,6 @@ begin
end;
function TApiService.DispatchDistrictLetter(const value: string): string;
var
s: string;
begin
s := Trim(value);
if s = '' then
Exit('');
Result := s[Length(s)];
end;
initialization
RegisterServiceType(TApiService);
......
[Settings]
LogFileNum=597
LogFileNum=610
webClientVersion=0.1.0
......@@ -365,7 +365,7 @@ object FViewComplaintDetails: TFViewComplaintDetails
Top = 430
Width = 96
Height = 25
Caption = 'View On Map'
Caption = 'Map'
ChildOrder = 1
ElementID = 'btn_complaint_view_on_map'
ElementFont = efCSS
......
......@@ -7,16 +7,21 @@
<!-- Summary header -->
<div class="card border-0 shadow-sm mb-2">
<div class="card-header bg-white py-2">
<button
class="btn btn-link text-decoration-none p-0 w-100 d-flex align-items-center justify-content-between"
type="button"
data-bs-toggle="collapse"
data-bs-target="#cdetails_summary"
aria-expanded="true"
aria-controls="cdetails_summary">
<span class="fw-semibold text-dark" id="lbl_summary_title">Summary</span>
<i class="fa fa-chevron-down"></i>
</button>
<button
class="btn btn-link text-decoration-none p-0 w-100 d-flex align-items-center justify-content-between summary-toggle"
type="button"
data-bs-toggle="collapse"
data-bs-target="#cdetails_summary"
aria-expanded="true"
aria-controls="cdetails_summary">
<span class="fw-semibold text-dark" id="lbl_summary_title">Summary</span>
<span class="summary-chevron" aria-hidden="true">
<svg class="summary-chevron-icon" viewBox="0 0 16 16" focusable="false">
<path fill="currentColor" d="M7.646 5.354a.5.5 0 0 1 .708 0l5 5a.5.5 0 0 1-.708.708L8 6.414 3.354 11.06a.5.5 0 1 1-.708-.708l5-5z"/>
</svg>
</span>
</button>
</div>
</div>
......@@ -48,11 +53,12 @@
<th scope="row" class="fw-semibold text-nowrap pe-2 w-auto">Address:</th>
<td class="w-100" id="lbl_address"></td>
</tr>
<tr id="row_business" class="d-none">
<th scope="row" class="fw-semibold text-nowrap pe-2 w-auto">Business:</th>
<td class="w-100" id="lbl_business"></td>
</tr>
</tbody>
</table>
</div>
</div>
......
......@@ -531,6 +531,7 @@ var
resp: TXDataClientResponse;
rootObj: TJSObject;
dataObj: TJSObject;
businessRowEl: TJSHTMLElement;
complaintText: string;
priorityText: string;
......@@ -538,6 +539,7 @@ var
dispatchDescText: string;
dispatchDistrictText: string;
addressText: string;
businessText: string;
dateDispatchedText: string;
dateArrivedText: string;
......@@ -563,6 +565,9 @@ begin
dispatchDescText := string(dataObj['DispatchCodeDesc']);
dispatchDistrictText := string(dataObj['DispatchDistrict']);
addressText := string(dataObj['Address']);
businessText := '';
if dataObj['Business'] <> nil then
businessText := string(dataObj['Business']);
FCfsId := string(dataObj['CFSId']);
statusText := '';
......@@ -590,6 +595,16 @@ begin
SetTextById('lbl_dispatch_code', dispatchDescText);
SetTextById('lbl_dispatch_district', dispatchDistrictText);
SetTextById('lbl_address', addressText);
SetTextById('lbl_business', businessText);
businessRowEl := TJSHTMLElement(document.getElementById('row_business'));
if businessRowEl <> nil then
begin
if Trim(businessText) = '' then
businessRowEl.classList.add('d-none')
else
businessRowEl.classList.remove('d-none');
end;
historyFlag := '';
contactsFlag := '';
......
......@@ -42,23 +42,22 @@ object FViewComplaints: TFViewComplaints
Style = lsListGroup
DataSource = wdsComplaints
ItemTemplate =
'<div class="list-section-header small fw-semibold bg-secondary ' +
'text-white rounded-1 px-2 mb-1"> (%DistrictHeader%)</div><div ' +
'class="card border shadow-sm position-relative" style=" --bs' +
'-card-bg: (%PriorityColor%); --bs-card-color: (%PriorityTextC' +
'olor%); "> <div class="card-body py-2 px-3"> <div class="fw' +
'-bold text-uppercase small"> (%Priority%): (%DispatchCodeDe' +
'sc%) </div> <div class="small">(%Address%)</div> <div c' +
'lass="small text-opacity-75"> (%Complaint%): (%Status%)&nbs' +
'p;&nbsp;(%DistrictSector%) </div> <div class="small text-o' +
'pacity-75">(%DateReported%)</div> </div> <div class="position-' +
'absolute top-50 end-0 translate-middle-y pe-2 d-flex flex-column' +
' gap-1"> <button type="button" class="btn btn-prima' +
'ry btn-sm complaint-details-btn" data-id="(%ComplaintId%)" ' +
' > Details </button> <button type="button" ' +
' class="btn btn-primary btn-sm btn-complaint-map" data-id=' +
'"(%ComplaintId%)" > View On Map </button> </div></di' +
'v>'
'<div class="list-section-header small fw-semibold bg-secondary t' +
'ext-white rounded-1 px-2 mb-1">(%DistrictHeader%)</div><div clas' +
's="card border shadow-sm" style="--bs-card-bg:(%PriorityColor%);' +
'--bs-card-color:(%PriorityTextColor%);"><div class="card-body py' +
'-2 px-3 d-flex gap-2"><div class="flex-grow-1"><div class="fw-bo' +
'ld text-uppercase small">(%Priority%): (%DispatchCodeDesc%)</div' +
'><div class="small">(%Address%)</div><div class="small d-none co' +
'mplaint-business" data-business="(%Business%)">(%Business%)</div' +
'><div class="small text-opacity-75">(%Complaint%): (%Status%)&nb' +
'sp;&nbsp;(%DistrictSector%)</div><div class="small text-opacity-' +
'75">(%DateReported%)</div></div><div class="d-flex flex-column j' +
'ustify-content-center gap-1"><button type="button" class="btn bt' +
'n-primary btn-sm complaint-details-btn" data-id="(%ComplaintId%)' +
'">Details</button><button type="button" class="btn btn-primary b' +
'tn-sm btn-complaint-map" data-id="(%ComplaintId%)">View On Map</' +
'button></div></div></div>'
ListSource = wdsComplaints
end
object xdwcComplaints: TXDataWebClient
......@@ -88,6 +87,9 @@ object FViewComplaints: TFViewComplaints
object xdwdsComplaintsAddress: TStringField
FieldName = 'Address'
end
object xdwdsComplaintsBusiness: TStringField
FieldName = 'Business'
end
object xdwdsComplaintsStatus: TStringField
FieldName = 'Status'
end
......
......@@ -32,6 +32,7 @@ type
xdwdsComplaintsPriorityTextColor: TStringField;
xdwdsComplaintsDistrictSector: TStringField;
tmrRefresh: TWebTimer;
xdwdsComplaintsBusiness: TStringField;
procedure WebFormCreate(Sender: TObject);
procedure btnRefreshClick(Sender: TObject);
procedure tmrRefreshTimer(Sender: TObject);
......@@ -41,6 +42,7 @@ type
FLoading: Boolean;
[async] procedure GetComplaints;
procedure HandleListClick(e: TJSMouseEvent);
procedure ShowHideBusinessRows;
public
property OnShowDetails: TSelectProc read FSelectProc write FSelectProc;
end;
......@@ -73,6 +75,26 @@ begin
end;
end;
procedure TFViewComplaints.ShowHideBusinessRows;
var
nodes: TJSNodeList;
i: Integer;
el: TJSHTMLElement;
businessText: string;
begin
nodes := document.querySelectorAll('.complaint-business');
for i := 0 to nodes.length - 1 do
begin
el := TJSHTMLElement(nodes.item(i));
businessText := string(el.getAttribute('data-business'));
if Trim(businessText) <> '' then
el.classList.remove('d-none')
else
el.classList.add('d-none');
end;
end;
procedure TFViewComplaints.HandleListClick(e: TJSMouseEvent);
var
el: TJSElement;
......@@ -111,11 +133,7 @@ begin
e.stopPropagation;
asm
try {
pas['View.Main'].FViewMain.ShowMapFocusComplaint(complaintId);
} catch (e) {
console.log('ShowMapFocusComplaint failed', e);
}
pas['View.Main'].FViewMain.ShowMapFocusComplaint(complaintId);
end;
end;
end;
......@@ -127,46 +145,48 @@ begin
Document.removeEventListener('click', @HandleListClick);
end;
//Note: jjHTML for individual complaint cards can be found in the twebdblistcontrol HTMLString property
//Note: HTML for individual complaint cards can be found in the twebdblistcontrol HTMLString property
procedure TFViewComplaints.btnRefreshClick(Sender: TObject);
begin
GetComplaints;
end;
procedure TFViewComplaints.GetComplaints;
[async] procedure TFViewComplaints.GetComplaints;
var
xdcResponse: TXDataClientResponse;
respObj: TJSObject;
complaintsCount: Integer;
begin
if FLoading then Exit;
FLoading := True;
if FLoading then
Exit;
FLoading := True;
ShowSpinner('spinner');
try
try
xdcResponse := await(xdwcComplaints.RawInvokeAsync('IApiService.GetComplaintList', []));
respObj := TJSObject(xdcResponse.Result);
xdwdsComplaints.Close;
xdwdsComplaints.SetJsonData(respObj['data']);
xdwdsComplaints.Open;
ShowHideBusinessRows;
complaintsCount := Integer(respObj['count']);
lblEntries.Caption := Format('%d active complaints', [complaintsCount]);
except
on E: EXDataClientRequestException do
begin
Utils.ShowErrorModal(E.ErrorResult.ErrorMessage);
end;
on E: Exception do
Utils.ShowErrorModal(E.Message);
end;
finally
HideSpinner('spinner');
FLoading := False;
end;
FLoading := False;
HideSpinner('spinner');
end;
procedure TFViewComplaints.tmrRefreshTimer(Sender: TObject);
begin
GetComplaints;
......
......@@ -81,6 +81,7 @@ object FViewMap: TFViewMap
object tmrLocate: TWebTimer
Enabled = False
Interval = 100
OnTimer = tmrLocateTimer
Left = 174
Top = 74
end
......
......@@ -27,22 +27,24 @@ type
procedure lfMapCustomizeCSS(Sender: TObject; var ACustomizeCSS: string);
procedure tmrRefreshTimer(Sender: TObject);
procedure btnFindLocationClick(Sender: TObject);
procedure tmrLocateTimer(Sender: TObject);
private
userLocationMarker: TTMSFNCMapsMarker;
geoWatchId: Integer;
FUnitsLoaded: Boolean;
FComplaintsLoaded: Boolean;
FZoomPending: Boolean;
FLoadingPoints: Boolean;
FDeviceCentered: Boolean;
mapFilters: TMapFilters;
FPendingUnitId: string;
FPendingComplaintId: string;
[async] procedure LoadPointsAsync;
FPendingFocusCoord: TTMSFNCMapsCoordinateRec;
FPendingFocusZoom: Integer;
FDoFocusZoom: Boolean;
[async] procedure LoadPointsAsync(showBusy: Boolean);
function CarIconForDistrict(const DistrictCode: string): string;
procedure UpdateDeviceLocation(lat, lng: Double);
procedure StartDeviceLocation;
procedure StopDeviceLocation;
procedure ApplyPendingUnitFocus;
procedure ApplyPendingComplaintFocus;
public
......@@ -65,7 +67,6 @@ begin
ShowSpinner('spinner');
FUnitsLoaded := False;
FComplaintsLoaded := False;
FDeviceCentered := False;
httpReqGeoJson.Execute;
asm
window.showComplaintDetails = function (id) {
......@@ -103,12 +104,9 @@ begin
userLocationMarker.Latitude := lat;
userLocationMarker.Longitude := lng;
end;
procedure TFViewMap.StartDeviceLocation;
begin
asm
......@@ -148,25 +146,10 @@ begin
end;
procedure TFViewMap.StopDeviceLocation;
begin
asm
const self = this;
if (self.geoWatchId != null && self.geoWatchId !== 0 && navigator.geolocation) {
try { navigator.geolocation.clearWatch(self.geoWatchId); } catch(e) {}
self.geoWatchId = 0;
}
end;
end;
[async] procedure TFViewMap.httpReqGeoJsonResponse(Sender: TObject; AResponse: string);
var
i: Integer;
P: TTMSFNCMapsPolygon;
nm: string;
begin
lfMap.BeginUpdate;
try
......@@ -217,7 +200,7 @@ begin
lfMap.EndUpdate;
end;
await(LoadPointsAsync);
await(LoadPointsAsync(True));
if mapFilters = nil then
begin
......@@ -246,7 +229,7 @@ begin
end;
[async] procedure TFViewMap.LoadPointsAsync;
[async] procedure TFViewMap.LoadPointsAsync(showBusy: Boolean);
var
resp: TXDataClientResponse;
root, item, uo: TJSObject;
......@@ -256,7 +239,7 @@ var
lat, lng: Double;
uName, dist: string;
unitId, callType, priorityText, statusText: string;
complaintId, codeDesc, dispatchDist, priority: string;
complaintId, codeDesc, dispatchDist, priority, business: string;
pngName, iconUrl, rowsHtml: string;
officer1Lname, officer1Fname, officer1Empnum: string;
officer2Lname, officer2Fname, officer2Empnum: string;
......@@ -272,7 +255,9 @@ begin
Exit;
FLoadingPoints := True;
ShowSpinner('spinner');
if showBusy then
ShowSpinner('spinner');
try
FUnitsLoaded := False;
FComplaintsLoaded := False;
......@@ -435,6 +420,7 @@ begin
codeDesc := string(item['DispatchCodeDesc']);
dispatchDist := string(item['DispatchDistrict']);
priority := string(item['Priority']);
business := string(item['Business']);
lat := Double(item['Lat']);
lng := Double(item['Lng']);
......@@ -489,6 +475,12 @@ begin
'<div class="small">' +
'<span class="fw-bold">Dispatch District:</span> ' + dispatchDist +
'</div>' +
IfThen(Trim(business) <> '',
'<div class="small">' +
'<span class="fw-bold">Business:</span> ' + business +
'</div>',
''
) +
'<div class="small mb-1">' +
'<span class="fw-bold">Address:</span> ' + string(item['Address']) +
'</div>' +
......@@ -526,13 +518,13 @@ begin
ApplyPendingUnitFocus;
ApplyPendingComplaintFocus;
finally
HideSpinner('spinner');
FLoadingPoints := False;
if showBusy then
HideSpinner('spinner');
FLoadingPoints := False;
end;
end;
procedure TFViewMap.lfMapCustomizeMarker(Sender: TObject; var ACustomizeMarker: string);
begin
ACustomizeMarker :=
......@@ -640,12 +632,25 @@ begin
'.emi-marker-badge{position:absolute;top:-4px;right:-4px;min-width:16px;height:16px;padding:0 4px;border-radius:999px;background:var(--bs-danger);color:#fff;font:700 11px/16px system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;text-align:center;box-shadow:0 0 0 2px #fff;}' + #13#10;
end;
procedure TFViewMap.tmrLocateTimer(Sender: TObject);
begin
tmrLocate.Enabled := False;
if not FDoFocusZoom then
Exit;
lfMap.SetCenterCoordinate(FPendingFocusCoord);
lfMap.SetZoomLevel(FPendingFocusZoom);
FDoFocusZoom := False;
end;
procedure TFViewMap.tmrRefreshTimer(Sender: TObject);
begin
if FLoadingPoints then
Exit;
LoadPointsAsync;
LoadPointsAsync(False);
end;
procedure TFViewMap.btnFindLocationClick(Sender: TObject);
......@@ -664,7 +669,7 @@ procedure TFViewMap.FocusUnit(const unitId: string);
begin
FPendingUnitId := Trim(unitId);
if mapFilters <> nil then
LoadPointsAsync;
LoadPointsAsync(True);
end;
......@@ -672,7 +677,7 @@ procedure TFViewMap.FocusComplaint(const complaintId: string);
begin
FPendingComplaintId := Trim(complaintId);
if mapFilters <> nil then
LoadPointsAsync;
LoadPointsAsync(True);
end;
......@@ -686,15 +691,25 @@ var
begin
if Trim(FPendingUnitId) = '' then
Exit;
targetDs := 'unit|' + FPendingUnitId;
found := False;
for i := 0 to lfMap.Markers.Count - 1 do
begin
m := lfMap.Markers[i];
if SameText(m.DataString, targetDs) then
begin
coord := CreateCoordinate(m.Latitude, m.Longitude);
lfMap.SetCenterCoordinate(coord);
lfmap.SetCenterCoordinate(coord);
FPendingFocusCoord := coord;
FPendingFocusZoom := 17;
FDoFocusZoom := True;
tmrLocate.Interval := 250;
tmrLocate.Enabled := True;
found := True;
Break;
end;
......@@ -704,7 +719,6 @@ begin
FPendingUnitId := '';
end;
procedure TFViewMap.ApplyPendingComplaintFocus;
var
i: Integer;
......@@ -725,7 +739,15 @@ begin
if SameText(m.DataString, targetDs) then
begin
coord := CreateCoordinate(m.Latitude, m.Longitude);
lfMap.SetCenterCoordinate(coord);
lfmap.SetCenterCoordinate(coord);
FPendingFocusCoord := coord;
FPendingFocusZoom := 17;
FDoFocusZoom := True;
tmrLocate.Interval := 250;
tmrLocate.Enabled := True;
found := True;
Break;
end;
......@@ -735,5 +757,7 @@ begin
FPendingComplaintId := '';
end;
end.
......@@ -57,7 +57,7 @@ object FViewUnitDetails: TFViewUnitDetails
Top = 326
Width = 96
Height = 25
Caption = 'View On Map'
Caption = 'Map'
ChildOrder = 1
ElementID = 'btn_unit_view_on_map'
ElementFont = efCSS
......
......@@ -7,16 +7,21 @@
<!-- Summary header -->
<div class="card border-0 shadow-sm mb-2">
<div class="card-header bg-white py-2">
<button
class="btn btn-link text-decoration-none p-0 w-100 d-flex align-items-center justify-content-between"
type="button"
data-bs-toggle="collapse"
data-bs-target="#udetails_summary"
aria-expanded="true"
aria-controls="udetails_summary">
<span class="fw-semibold text-dark" id="lbl_summary_title">Summary</span>
<i class="fa fa-chevron-down"></i>
</button>
<button
class="btn btn-link text-decoration-none p-0 w-100 d-flex align-items-center justify-content-between summary-toggle"
type="button"
data-bs-toggle="collapse"
data-bs-target="#udetails_summary"
aria-expanded="true"
aria-controls="udetails_summary">
<span class="fw-semibold text-dark" id="lbl_summary_title">Summary</span>
<span class="summary-chevron" aria-hidden="true">
<svg class="summary-chevron-icon" viewBox="0 0 16 16" focusable="false">
<path fill="currentColor" d="M7.646 5.354a.5.5 0 0 1 .708 0l5 5a.5.5 0 0 1-.708.708L8 6.414 3.354 11.06a.5.5 0 1 1-.708-.708l5-5z"/>
</svg>
</span>
</button>
</div>
</div>
......@@ -91,7 +96,7 @@
type="button"
class="btn btn-primary btn-sm shadow position-fixed"
style="right: 12px; bottom: 72px; z-index: 1040;">
View On Map
Map
</button>
</div>
......
......@@ -39,24 +39,21 @@ object FViewUnits: TFViewUnits
Style = lsListGroup
DataSource = wdsUnits
ItemTemplate =
'<div class="list-section-header small fw-semibold bg-body-secon' +
'dary text-dark rounded-1 px-2 mb-1"> (%DistrictHeader%)</div><d' +
'iv class="card border shadow-sm position-relative"> <div class=' +
'"card-body py-2 px-3"> <!-- Unit + Status --> <div class="' +
'fw-bold text-uppercase small"> (%UnitName%)&nbsp;-&nbsp;(%S' +
'tatus%) </div> <!-- Location --> <div class="small text' +
'-body-secondary mb-1">(%Location%)</div> <!-- Call type --> ' +
' <div class="small">(%CallType%)</div> <!-- Divider line -->' +
' <hr class="unit-divider my-1" style="width: 80px; margin-lef' +
't: 0" /> <!-- Officers --> <div class="small officer1">(%O' +
'fficer1%)</div> <div class="small officer2">(%Officer2%)</div' +
'> </div> <!-- Vertically centered Details button on the right ' +
'--> <div class="position-absolute top-50 end-0 translate-middle' +
'-y pe-2"> <button type="button" class="btn btn-prim' +
'ary btn-sm btn-unit-details" data-unitid="(%UnitId%)" > ' +
' Details </button> <button type="button" class=' +
'"btn btn-primary btn-sm btn-unit-map" data-unitid="(%Unit' +
'Id%)"> View On Map </button> </div></div>'
'<div class="list-section-header small fw-semibold bg-body-second' +
'ary text-dark rounded-1 px-2 mb-1">(%DistrictHeader%)</div><div ' +
'class="card border shadow-sm"> <div class="card-body py-2 px-3 ' +
'd-flex gap-2"> <div class="flex-grow-1"> <div class="fw-' +
'bold text-uppercase small">(%UnitName%)&nbsp;-&nbsp;(%Status%)</' +
'div> <div class="small text-body-secondary mb-1">(%Location' +
'%)</div> <div class="small">(%CallType%)</div> <hr cla' +
'ss="unit-divider my-1" style="width: 80px; margin-left: 0" /> ' +
' <div class="small officer1">(%Officer1%)</div> <div clas' +
's="small officer2">(%Officer2%)</div> </div> <div class="d' +
'-flex flex-column justify-content-center gap-1"> <button ty' +
'pe="button" class="btn btn-primary btn-sm btn-unit-details" data' +
'-unitid="(%UnitId%)">Details</button> <button type="button"' +
' class="btn btn-primary btn-sm btn-unit-map" data-unitid="(%Unit' +
'Id%)">View On Map</button> </div> </div></div>'
ListSource = wdsUnits
end
object wdsUnits: TWebDataSource
......
......@@ -72,3 +72,16 @@ html, body {
.tab-hidden { display: none !important; }
.summary-chevron-icon {
width: 1rem;
height: 1rem;
display: block;
transition: transform 0.15s ease-in-out;
}
/* Expanded => show DOWN (rotate base UP chevron) */
.summary-toggle[aria-expanded="true"] .summary-chevron-icon {
transform: rotate(180deg);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment