Commit f5a72c13 by Mac Stephens

Implement map focus navigation (View On Map from lists/details), add toggleable…

Implement map focus navigation (View On Map from lists/details), add toggleable hidden location dot, remove map auto-center in favor of GeoJSON center, convert warnings to responsive cards on mobile, and normalize dispatch district display
parent 5c3199e6
......@@ -342,7 +342,7 @@ object ApiDatabaseModule: TApiDatabaseModule
' cm.REMARKS'
'FROM CFS_MEMOS cm'
'WHERE cm.CFSID = :CFSID'
'ORDER BY cm.TIMESTAMP ASC')
'ORDER BY cm.TIMESTAMP DESC')
ReadOnly = True
Left = 196
Top = 248
......@@ -350,7 +350,7 @@ object ApiDatabaseModule: TApiDatabaseModule
item
DataType = ftUnknown
Name = 'CFSID'
Value = Null
Value = nil
end>
object uqCFSMemosMEMO_ID: TFloatField
FieldName = 'MEMO_ID'
......
[Settings]
LogFileNum=583
LogFileNum=592
webClientVersion=0.1.0
......@@ -335,6 +335,45 @@ object FViewComplaintDetails: TFViewComplaintDetails
WidthPercent = 100.000000000000000000
OnClick = btnRemarksClick
end
object lstWarnings: TWebDBListControl
Left = 596
Top = 352
Width = 191
Height = 65
ElementID = 'lst_warnings'
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
ChildOrder = 12
DefaultItemClassName = 'list-group-item'
DefaultItemLinkClassName = 'list-group-link'
ElementFont = efCSS
ElementListClassName = 'list-group'
Items = <>
Style = lsListGroup
DataSource = wdsWarnings
ItemTemplate =
'<div class="card mb-2"> <div class="card-body py-2"> <div cl' +
'ass="d-flex justify-content-between gap-2"> <div class="fw-' +
'semibold">(%CodeDesc%)</div> <div class="text-muted small t' +
'ext-end">(%ADDRESS%)</div> </div> <div class="small mt-1">' +
'(%NOTES%)</div> </div></div>'
ListSource = wdsWarnings
end
object btnComplaintViewOnMap: TWebButton
Left = 510
Top = 430
Width = 96
Height = 25
Caption = 'View On Map'
ChildOrder = 1
ElementID = 'btn_complaint_view_on_map'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = btnComplaintViewOnMapClick
end
object xdwcComplaintDetails: TXDataWebClient
Connection = DMConnection.ApiConnection
Left = 378
......@@ -413,8 +452,8 @@ object FViewComplaintDetails: TFViewComplaintDetails
Top = 352
end
object xdwdsWarnings: TXDataWebDataSet
Left = 638
Top = 352
Left = 634
Top = 492
object xdwdsWarningsCodeDesc: TStringField
FieldName = 'CodeDesc'
end
......@@ -427,7 +466,7 @@ object FViewComplaintDetails: TFViewComplaintDetails
end
object wdsWarnings: TWebDataSource
DataSet = xdwdsWarnings
Left = 740
Top = 352
Left = 728
Top = 492
end
end
......@@ -80,12 +80,19 @@
<div class="card border-0 shadow-sm">
<div class="card-body p-0">
<div id="tbl_remarks"></div>
<div id="tbl_history" class="d-none"></div>
<div id="tbl_contacts" class="d-none"></div>
<div id="tbl_warnings" class="d-none"></div>
<div id="tbl_history" class="tab-hidden"></div>
<div id="tbl_warnings" class="d-none d-md-block"></div>
<div id="lst_warnings" class="d-block d-md-none"></div>
<div id="tbl_contacts" class="tab-hidden"></div>
</div>
</div>
</div>
<button id="btn_complaint_view_on_map"
type="button"
class="btn btn-primary btn-sm shadow position-fixed"
style="right: 12px; bottom: 72px; z-index: 1040;">
View On Map
</button>
</div>
......@@ -9,7 +9,7 @@ uses
ConnectionModule,
Utils, Vcl.Controls, WEBLib.Controls, WEBLib.Grids, WEBLib.DBCtrls,
Data.DB, WEBLib.DB, XData.Web.JsonDataset, XData.Web.Dataset,
Vcl.StdCtrls, WEBLib.StdCtrls;
Vcl.StdCtrls, WEBLib.StdCtrls, WEBLib.Lists;
type
TFViewComplaintDetails = class(TWebForm)
......@@ -52,12 +52,15 @@ type
btnE911: TWebButton;
btnREM: TWebButton;
btnUnt: TWebButton;
lstWarnings: TWebDBListControl;
btnComplaintViewOnMap: TWebButton;
procedure btnRemarksClick(Sender: TObject);
procedure btnHistoryClick(Sender: TObject);
procedure btnContactsClick(Sender: TObject);
procedure btnWarningsClick(Sender: TObject);
procedure btnCmpClick(Sender: TObject);
procedure btnComplaintViewOnMapClick(Sender: TObject);
procedure btnE911Click(Sender: TObject);
procedure btnREMClick(Sender: TObject);
procedure btnUntClick(Sender: TObject);
......@@ -243,9 +246,9 @@ begin
Exit;
if hidden then
TJSElement(el).classList.add('d-none')
TJSElement(el).classList.add('tab-hidden')
else
TJSElement(el).classList.remove('d-none');
TJSElement(el).classList.remove('tab-hidden');
end;
procedure TFViewComplaintDetails.UpdateToggleButtonCss(const buttonId: string; isOn: Boolean);
......@@ -349,13 +352,16 @@ begin
SetHiddenById('tbl_remarks', tabName <> 'remarks');
SetHiddenById('tbl_history', tabName <> 'history');
SetHiddenById('tbl_contacts', tabName <> 'contacts');
SetHiddenById('tbl_warnings', tabName <> 'warnings');
SetHiddenById('lst_warnings', tabName <> 'warnings');
SetHiddenById('row_remarks_toggles', tabName <> 'remarks');
UpdateTabButtonCss(tabName);
end;
procedure TFViewComplaintDetails.ApplyRemarksFilters;
var
filteredArr: TJSArray;
......@@ -496,6 +502,12 @@ begin
ApplyRemarksFilters;
end;
procedure TFViewComplaintDetails.btnComplaintViewOnMapClick(Sender: TObject);
begin
if Assigned(FViewMain) then
FViewMain.ShowMapFocusComplaint(FComplaintId);
end;
procedure TFViewComplaintDetails.btnE911Click(Sender: TObject);
begin
FShowE911 := not FShowE911;
......
......@@ -24,49 +24,6 @@ object FViewComplaints: TFViewComplaints
Visible = False
WidthPercent = 100.000000000000000000
end
object btnGroup: TWebButton
Left = 180
Top = 110
Width = 43
Height = 25
Caption = 'Group'
ChildOrder = 1
ElementClassName = 'btn btn-light'
ElementID = 'complaints_btngroup'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
end
object btnFilter: TWebButton
Left = 242
Top = 110
Width = 37
Height = 25
Caption = 'Filter'
ChildOrder = 1
ElementClassName = 'btn btn-light'
ElementID = 'complaints_btnfilter'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
end
object btnRefresh: TWebButton
Left = 114
Top = 110
Width = 53
Height = 25
Caption = 'Refresh'
ChildOrder = 1
ElementClassName = 'btn btn-light'
ElementID = 'complaints_btnrefresh'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = btnRefreshClick
end
object dblComplaintsList: TWebDBListControl
Left = 36
Top = 148
......@@ -85,19 +42,23 @@ object FViewComplaints: TFViewComplaints
Style = lsListGroup
DataSource = wdsComplaints
ItemTemplate =
'<div class="list-section-header small fw-semibold bg-secondary t' +
'ext-white rounded-1 px-2 mb-1"> (%DistrictHeader%)</div><div cl' +
'ass="card border shadow-sm" style="--bs-card-bg:(%PriorityCo' +
'lor%);--bs-card-color:(%PriorityTextColor%);"> <div class="card' +
'-body py-2 px-3"> <div class="fw-bold text-uppercase small"> ' +
' (%Priority%): (%DispatchCodeDesc%) </div> <div class=' +
'"small">(%Address%)</div> <div class="small text-opacity-75 d' +
'-flex align-items-center"> <span> (%Complaint%): (%S' +
'tatus%)&nbsp;&nbsp;(%DistrictSector%) </span> <button ' +
'type="button" class="btn btn-primary btn-sm ms-auto' +
' complaint-details-btn" data-id="(%ComplaintId%)"> ' +
' Details </button> </div> <div class="small tex' +
't-opacity-75">(%DateReported%)</div> </div></div>'
'<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-secondary btn-sm btn-complaint-map" data-i' +
'd="(%ComplaintId%)" > View On Map </button> </div></' +
'div>'
ListSource = wdsComplaints
end
object xdwcComplaints: TXDataWebClient
......
......@@ -24,9 +24,6 @@ type
xdwdsComplaintsDispatchDistrict: TStringField;
xdwdsComplaintsDateReported: TStringField;
lblEntries: TWebLabel;
btnRefresh: TWebButton;
btnGroup: TWebButton;
btnFilter: TWebButton;
wdsComplaints: TWebDataSource;
xdwdsComplaintsComplaintId: TStringField;
xdwdsComplaintsDistrictHeader: TStringField;
......@@ -77,20 +74,52 @@ begin
end;
procedure TFViewComplaints.HandleListClick(e: TJSMouseEvent);
var el: TJSElement; id: string;
var
el: TJSElement;
btn: TJSElement;
complaintId: string;
begin
btn := nil;
el := TJSElement(e.target);
if (el is TJSHtmlElement) and TJSHtmlElement(el).classList.contains('complaint-details-btn') then
asm
btn = (el && el.closest) ? el.closest('.complaint-details-btn') : null;
end;
if (btn <> nil) and (btn is TJSHtmlElement) then
begin
id := string(TJSHtmlElement(el).dataset['id']); // from data-id="(%ComplaintId%)"
complaintId := string(TJSHtmlElement(btn).getAttribute('data-id'));
e.preventDefault;
e.stopPropagation;
asm
if (window.showComplaintDetails) window.showComplaintDetails(id);
if (window.showComplaintDetails) window.showComplaintDetails(complaintId);
end;
end
else
begin
btn := nil;
asm
btn = (el && el.closest) ? el.closest('.btn-complaint-map') : null;
end;
end;
if (btn <> nil) and (btn is TJSHtmlElement) then
begin
complaintId := string(TJSHtmlElement(btn).getAttribute('data-id'));
e.preventDefault;
e.stopPropagation;
asm
try {
pas['View.Main'].FViewMain.ShowMapFocusComplaint(complaintId);
} catch (e) {
console.log('ShowMapFocusComplaint failed', e);
}
end;
end;
end;
end;
procedure TFViewComplaints.WebFormDestroy(Sender: TObject);
......@@ -98,7 +127,7 @@ begin
Document.removeEventListener('click', @HandleListClick);
end;
//HTML for individual complaint cards can be found in the twebdblistcontrol HTMLString property
//Note: jjHTML for individual complaint cards can be found in the twebdblistcontrol HTMLString property
procedure TFViewComplaints.btnRefreshClick(Sender: TObject);
begin
GetComplaints;
......@@ -133,6 +162,7 @@ begin
end;
finally
end;
FLoading := False;
HideSpinner('spinner');
end;
......
......@@ -80,7 +80,7 @@ object FViewMain: TFViewMain
object lblMainTitle: TWebLabel
Left = 131
Top = 31
Width = 61
Width = 3
Height = 15
ElementID = 'lbl_main_title'
ElementFont = efCSS
......
......@@ -56,6 +56,8 @@ type
procedure ShowUserForm(Info: string);
procedure ShowComplaintDetails(ComplaintId: string);
procedure SetActiveNavButton(const BtnId: string);
procedure ShowMapFocusUnit(const unitId: string);
procedure ShowMapFocusComplaint(const complaintId: string);
end;
var
......@@ -290,4 +292,41 @@ begin
end;
procedure TFViewMain.ShowMapFocusUnit(const unitId: string);
var
pendingUnitId: string;
begin
SetHeaderTitle('Map');
SetActiveNavButton('view.main.btnmap');
ShowForm(TFViewMap);
pendingUnitId := unitId;
window.setTimeout(
procedure
begin
if (FChildForm <> nil) and (FChildForm is TFViewMap) then
TFViewMap(FChildForm).FocusUnit(pendingUnitId);
end, 50);
end;
procedure TFViewMain.ShowMapFocusComplaint(const complaintId: string);
var
pendingComplaintId: string;
begin
SetHeaderTitle('Map');
SetActiveNavButton('view.main.btnmap');
ShowForm(TFViewMap);
pendingComplaintId := complaintId;
window.setTimeout(
procedure
begin
if (FChildForm <> nil) and (FChildForm is TFViewMap) then
TFViewMap(FChildForm).FocusComplaint(pendingComplaintId);
end, 50);
end;
end.
......@@ -48,6 +48,18 @@ object FViewMap: TFViewMap
HeadLinks = <>
end
end
object btnFindLocation: TWebButton
Left = 90
Top = 74
Width = 61
Height = 25
Caption = 'Location'
ChildOrder = 1
ElementID = 'btn_find_location'
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = btnFindLocationClick
end
object httpReqGeoJson: TWebHttpRequest
ResponseType = rtText
URL = 'assets/bpddistricts-updated.geojson'
......@@ -66,4 +78,10 @@ object FViewMap: TFViewMap
Left = 358
Top = 696
end
object tmrLocate: TWebTimer
Enabled = False
Interval = 100
Left = 174
Top = 74
end
end
<div id="map.root" class="d-flex flex-column h-100 w-100">
<!-- New: Offcanvas -->
<div class="offcanvas offcanvas-top"
<!-- New: offcavnvas is a bootstrap class that adds an easy slide in modal -->
<div class="offcanvas offcanvas-end"
tabindex="-1"
id="map_filters_offcanvas"
aria-labelledby="map_filters_offcanvas_label"
......@@ -21,8 +21,11 @@
<input class="form-check-input" type="checkbox" id="map_filter_complaints" checked>
<label class="form-check-label" for="map_filter_complaints">Show Complaints</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="map_filter_location">
<label class="form-check-label" for="map_filter_location">Show Location</label>
</div>
</div>
<div class="d-grid gap-2 mt-4">
<button type="button" class="btn btn-primary" id="map_filters_apply" data-bs-dismiss="offcanvas">
Apply
......@@ -38,10 +41,19 @@
</div>
</div>
</div>
<div class="flex-grow-1 position-relative" style="min-height:0;">
<div id="map_pnlmap" class="position-absolute w-100 h-100 top-0 start-0"></div>
<!-- Locate (bottom-right) -->
<button id="btn_find_location"
type="button"
class="btn btn-primary border shadow position-absolute bottom-0 end-0 me-2 mb-4"
style="z-index:1000;">
<i class="fa fa-crosshairs"></i>
<span class="d-none d-sm-inline">Locate</span>
</button>
<!-- Filters (top-right) -->
<button id="btn_map_filters"
type="button"
class="btn btn-primary position-absolute top-0 end-0 m-2 shadow"
......@@ -49,9 +61,9 @@
data-bs-toggle="offcanvas"
data-bs-target="#map_filters_offcanvas"
aria-controls="map_filters_offcanvas">
<i class="fa fa-sliders-h me-1"></i>Filters
<i class="fa fa-sliders-h"></i>
<span class="d-none d-sm-inline">Filters</span>
</button>
</div>
</div>
......@@ -17,6 +17,8 @@ type
httpReqGeoJson: TWebHttpRequest;
xdwcMap: TXDataWebClient;
tmrRefresh: TWebTimer;
btnFindLocation: TWebButton;
tmrLocate: TWebTimer;
procedure lfMapMapInitialized(Sender: TObject);
[async] procedure httpReqGeoJsonResponse(Sender: TObject; AResponse: string);
......@@ -24,6 +26,7 @@ type
var ACustomizeMarker: string);
procedure lfMapCustomizeCSS(Sender: TObject; var ACustomizeCSS: string);
procedure tmrRefreshTimer(Sender: TObject);
procedure btnFindLocationClick(Sender: TObject);
private
userLocationMarker: TTMSFNCMapsMarker;
geoWatchId: Integer;
......@@ -31,13 +34,20 @@ type
FComplaintsLoaded: Boolean;
FZoomPending: Boolean;
FLoadingPoints: Boolean;
FDeviceCentered: Boolean;
mapFilters: TMapFilters;
FPendingUnitId: string;
FPendingComplaintId: string;
[async] procedure LoadPointsAsync;
function CarIconForDistrict(const DistrictCode: string): string;
procedure UpdateDeviceLocation(lat, lng: Double);
procedure StartDeviceLocation;
procedure StopDeviceLocation;
procedure ApplyPendingUnitFocus;
procedure ApplyPendingComplaintFocus;
public
procedure FocusUnit(const unitId: string);
procedure FocusComplaint(const complaintId: string);
end;
var
......@@ -55,8 +65,8 @@ begin
ShowSpinner('spinner');
FUnitsLoaded := False;
FComplaintsLoaded := False;
FDeviceCentered := False;
httpReqGeoJson.Execute;
{$IFNDEF WIN32}
asm
window.showComplaintDetails = function (id) {
console.log('JS bridge showComplaintDetails called, id=', id);
......@@ -76,8 +86,6 @@ begin
}
};
end;
{$ENDIF}
StartDeviceLocation;
end;
......@@ -87,13 +95,15 @@ begin
if userLocationMarker = nil then
begin
userLocationMarker := lfMap.Markers.Add;
userLocationMarker.Title := '<div class="small fw-semibold">You are here</div>';
userLocationMarker.DataString := 'device';
userLocationMarker.IconURL := 'assets/markers/location_dot.png';
userLocationMarker.Title := ''; // keeps popup code from binding anything for this marker
userLocationMarker.Visible := False;
end;
userLocationMarker.Latitude := lat;
userLocationMarker.Longitude := lng;
end;
......@@ -152,7 +162,6 @@ begin
end;
[async] procedure TFViewMap.httpReqGeoJsonResponse(Sender: TObject; AResponse: string);
var
i: Integer;
......@@ -164,7 +173,7 @@ begin
lfMap.Polygons.Clear;
Console.Log('GeoJSON len=' + AResponse.Length.ToString);
lfMap.LoadGeoJSONFromText(AResponse, True, False);
lfMap.LoadGeoJSONFromText(AResponse, True, Trim(FPendingUnitId) = '');
Console.Log('Loaded polygons count=' + lfMap.Polygons.Count.ToString);
for i := 0 to lfMap.Polygons.Count - 1 do
......@@ -301,7 +310,7 @@ begin
begin
m := lfMap.Markers[i];
if SameText(m.DataString, 'unit') or StartsText('complaint|', m.DataString) then
if StartsText('unit|', m.DataString) or StartsText('complaint|', m.DataString) then
lfMap.Markers.Delete(i);
end;
......@@ -409,7 +418,8 @@ begin
) +
'</div>';
m.DataString := 'unit';
m.DataString := 'unit|' + unitId;
console.log('Unit marker ds=' + m.DataString);
m.IconURL := CarIconForDistrict(dist);
end;
end;
......@@ -512,6 +522,9 @@ begin
if mapFilters <> nil then
mapFilters.Apply;
ApplyPendingUnitFocus;
ApplyPendingComplaintFocus;
finally
HideSpinner('spinner');
FLoadingPoints := False;
......@@ -635,5 +648,92 @@ begin
LoadPointsAsync;
end;
procedure TFViewMap.btnFindLocationClick(Sender: TObject);
var
coord: TTMSFNCMapsCoordinateRec;
begin
if userLocationMarker = nil then
Exit;
coord := CreateCoordinate(userLocationMarker.Latitude, userLocationMarker.Longitude);
lfMap.SetCenterCoordinate(coord);
end;
procedure TFViewMap.FocusUnit(const unitId: string);
begin
FPendingUnitId := Trim(unitId);
if mapFilters <> nil then
LoadPointsAsync;
end;
procedure TFViewMap.FocusComplaint(const complaintId: string);
begin
FPendingComplaintId := Trim(complaintId);
if mapFilters <> nil then
LoadPointsAsync;
end;
procedure TFViewMap.ApplyPendingUnitFocus;
var
i: Integer;
m: TTMSFNCMapsMarker;
coord: TTMSFNCMapsCoordinateRec;
targetDs: string;
found: Boolean;
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);
found := True;
Break;
end;
end;
if found then
FPendingUnitId := '';
end;
procedure TFViewMap.ApplyPendingComplaintFocus;
var
i: Integer;
m: TTMSFNCMapsMarker;
coord: TTMSFNCMapsCoordinateRec;
targetDs: string;
found: Boolean;
begin
if Trim(FPendingComplaintId) = '' then
Exit;
targetDs := 'complaint|' + FPendingComplaintId;
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);
found := True;
Break;
end;
end;
if found then
FPendingComplaintId := '';
end;
end.
......@@ -52,6 +52,20 @@ object FViewUnitDetails: TFViewUnitDetails
end>
DataSource = wdsUnitLogs
end
object btnUnitViewOnMap: TWebButton
Left = 370
Top = 326
Width = 96
Height = 25
Caption = 'View On Map'
ChildOrder = 1
ElementID = 'btn_unit_view_on_map'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = btnUnitViewOnMapClick
end
object xdwcUnitDetails: TXDataWebClient
Connection = DMConnection.ApiConnection
Left = 150
......
......@@ -87,5 +87,14 @@
</div>
</div>
<button id="btn_unit_view_on_map"
type="button"
class="btn btn-primary btn-sm shadow position-fixed"
style="right: 12px; bottom: 72px; z-index: 1040;">
View On Map
</button>
</div>
......@@ -9,7 +9,8 @@ uses
ConnectionModule,
Utils,
Data.DB, WEBLib.DB,
Vcl.Controls, WEBLib.Controls, WEBLib.Grids, WEBLib.DBCtrls;
Vcl.Controls, WEBLib.Controls, WEBLib.Grids, WEBLib.DBCtrls, Vcl.StdCtrls,
WEBLib.StdCtrls;
type
TFViewUnitDetails = class(TWebForm)
......@@ -38,6 +39,8 @@ type
xdwdsUnitLogsComplaint: TStringField;
xdwdsUnitLogsLog: TStringField;
xdwdsUnitLogsLogTime: TStringField;
btnUnitViewOnMap: TWebButton;
procedure btnUnitViewOnMapClick(Sender: TObject);
private
FUnitId: string;
FLoading: Boolean;
......@@ -67,6 +70,13 @@ uses
{$R *.dfm}
procedure TFViewUnitDetails.btnUnitViewOnMapClick(Sender: TObject);
begin
if Assigned(FViewMain) then
FViewMain.ShowMapFocusUnit(FUnitId);
console.log('btnViewOnMapClick fired, FUnitId=' + FUnitId);
end;
class function TFViewUnitDetails.CreateForm(AElementID, UnitId: string): TWebForm;
procedure AfterCreate(AForm: TObject);
......
......@@ -39,67 +39,26 @@ object FViewUnits: TFViewUnits
Style = lsListGroup
DataSource = wdsUnits
ItemTemplate =
'<div class="list-section-header small fw-semibold bg-body-second' +
'ary text-dark rounded-1 px-2 mb-1"> (%DistrictHeader%)</div><di' +
'v class="card border shadow-sm position-relative"> <div class="' +
'card-body py-2 px-3"> <!-- Unit + Status --> <div class="f' +
'w-bold text-uppercase small"> (%UnitName%)&nbsp;-&nbsp;(%St' +
'atus%) </div> <!-- Location --> <div class="small text-' +
'body-secondary mb-1"> (%Location%) </div> <!-- Call t' +
'ype --> <div class="small">(%CallType%)</div> <!-- Divider' +
' line --> <hr class="unit-divider my-1" style="width:80px;mar' +
'gin-left:0;"> <!-- Officers --> <div class="small officer1' +
'">(%Officer1%)</div> <div class="small officer2">(%Officer2%)' +
'</div> </div> <!-- Vertically centered Details button on the r' +
'ight --> <div class="position-absolute top-50 end-0 translate-m' +
'iddle-y pe-2"> <button type="button" class="btn bt' +
'n-primary btn-sm btn-unit-details" data-unitid="(%Uni' +
'tId%)"> Details </button> </div></div>'
'<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-secondary btn-sm btn-unit-map" data-unitid="(%Un' +
'itId%)"> View On Map </button> </div></div>'
ListSource = wdsUnits
end
object btnRefresh: TWebButton
Left = 110
Top = 82
Width = 53
Height = 25
Caption = 'Refresh'
ChildOrder = 1
ElementClassName = 'btn btn-light'
ElementID = 'units_btnrefresh'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = btnRefreshClick
end
object btnGroup: TWebButton
Left = 186
Top = 82
Width = 43
Height = 25
Caption = 'Group'
ChildOrder = 1
ElementClassName = 'btn btn-light'
ElementID = 'units_btngroup'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
end
object btnFilter: TWebButton
Left = 250
Top = 82
Width = 37
Height = 25
Caption = 'Filter'
ChildOrder = 1
ElementClassName = 'btn btn-light'
ElementID = 'units_btnfilter'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
end
object wdsUnits: TWebDataSource
AutoEdit = False
DataSet = xdwdsUnits
......
......@@ -13,9 +13,6 @@ uses
type
TFViewUnits = class(TWebForm)
dblUnitsList: TWebDBListControl;
btnRefresh: TWebButton;
btnGroup: TWebButton;
btnFilter: TWebButton;
wdsUnits: TWebDataSource;
xdwdsUnits: TXDataWebDataSet;
xdwcUnits: TXDataWebClient;
......@@ -93,6 +90,29 @@ begin
asm
if (window.showUnitDetails) window.showUnitDetails(unitId);
end;
end
else
begin
btn := nil;
asm
btn = (el && el.closest) ? el.closest('.btn-unit-map') : null;
end;
if (btn <> nil) and (btn is TJSHtmlElement) then
begin
unitId := string(TJSHtmlElement(btn).getAttribute('data-unitid'));
e.preventDefault;
e.stopPropagation;
asm
try {
pas['View.Main'].FViewMain.ShowMapFocusUnit(unitId);
} catch (e) {
console.log('ShowMapFocusUnit failed', e);
}
end;
end;
end;
end;
......@@ -140,6 +160,7 @@ begin
end;
end;
finally
FLoading := False;
Utils.HideSpinner('spinner');
console.log('GetUnits complete');
end;
......
......@@ -69,4 +69,6 @@ html, body {
overflow: hidden;
}
.tab-hidden { display: none !important; }
......@@ -13,6 +13,8 @@ type
FMap: TTMSFNCLeaflet;
FShowUnits: Boolean;
FShowComplaints: Boolean;
FShowLocation: Boolean;
function GetCheckBoxChecked(const elementId: string; defaultValue: Boolean): Boolean;
procedure SetCheckBoxChecked(const elementId: string; checked: Boolean);
......@@ -43,6 +45,7 @@ begin
FShowUnits := True;
FShowComplaints := True;
FShowLocation := False;
end;
procedure TMapFilters.Init;
......@@ -119,12 +122,14 @@ procedure TMapFilters.ReadUi;
begin
FShowUnits := GetCheckBoxChecked('map_filter_units', True);
FShowComplaints := GetCheckBoxChecked('map_filter_complaints', True);
FShowLocation := GetCheckBoxChecked('map_filter_location', False);
end;
procedure TMapFilters.ResetUi;
begin
SetCheckBoxChecked('map_filter_units', True);
SetCheckBoxChecked('map_filter_complaints', True);
SetCheckBoxChecked('map_filter_location', False);
end;
procedure TMapFilters.UpdateSummary;
......@@ -143,6 +148,13 @@ begin
summaryText := summaryText + 'Complaints';
end;
if FShowLocation then
begin
if summaryText <> '' then
summaryText := summaryText + ', ';
summaryText := summaryText + 'Location';
end;
if summaryText = '' then
summaryText := 'None';
......@@ -168,13 +180,15 @@ begin
// Note: Map form sets:
// - units: m.DataString := 'unit'
// - complaints: (currently none) but you can set m.DataString := 'complaint|<id>'
// - complaints: m.DataString := 'complaint'
showMarker := True;
if SameText(ds, 'unit') then
showMarker := FShowUnits
else if StartsText('complaint', LowerCase(ds)) then
showMarker := FShowComplaints;
showMarker := FShowComplaints
else if SameText(ds, 'device') then
showMarker := FShowLocation;
// Note: TTMSFNCMapsMarker supports visibility toggling
m.Visible := showMarker;
......
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