Commit dc203ba1 by Mac Stephens

update map to hide details button on non-active units, update both details…

update map to hide details button on non-active units, update both details tables' style,  connect onclick to generated details buttons on lists, cleanup fbname code, fix scrolling issue on lists
parent f98f56bd
......@@ -20,6 +20,8 @@ object ApiDatabaseModule: TApiDatabaseModule
' cus.CODE_DESC AS UNIT_STATUS_DESC,'
' uc.UPDATETIME AS UPDATE_TIME,'
''
' dua.UNITID AS DIS_UNITID,'
''
' p1.PF_LNAME AS OFFICER1_LNAME,'
' p1.PF_FNAME AS OFFICER1_FNAME,'
' p1.PF_EMPNUM AS OFFICER1_EMPNUM,'
......@@ -39,6 +41,7 @@ object ApiDatabaseModule: TApiDatabaseModule
'LEFT JOIN PERSONNEL p1 ON p1.PF_NAMEID = dua.OFFICER1ID'
'LEFT JOIN PERSONNEL p2 ON p2.PF_NAMEID = dua.OFFICER2ID'
''
''
'')
ReadOnly = True
Left = 464
......@@ -110,6 +113,10 @@ object ApiDatabaseModule: TApiDatabaseModule
ReadOnly = True
Size = 10
end
object uqMapUnitsDIS_UNITID: TFloatField
FieldName = 'DIS_UNITID'
ReadOnly = True
end
end
object uqUnitList: TUniQuery
Connection = ucENTCAD
......
......@@ -169,6 +169,7 @@ type
uqUnitLogsLOG_TIME: TDateTimeField;
uqUnitLogsCOMPLAINT_NUM: TStringField;
uqUnitLogsLOG_TEXT: TStringField;
uqMapUnitsDIS_UNITID: TFloatField;
procedure uqComplaintListCalcFields(DataSet: TDataSet);
procedure uqMapComplaintsCalcFields(DataSet: TDataSet);
private
......
......@@ -201,6 +201,10 @@ var
begin
Logger.Log(4, '---TApiService.GetUnitMap initiated');
// Note: GetUnitMap is AVL-anchored (shows all AVL units).
// Note: DIS_UNITID is null when the unit is not dispatch-active; client should disable/hide Details in that case.
// Note: To restrict map to dispatch-active only, change uqMapUnits join to DIS_UNIT_ACTIVE from LEFT JOIN to INNER JOIN.
Result := TJSONObject.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result);
......@@ -245,6 +249,8 @@ begin
item.AddPair('Officer2Fname', ApiDB.uqMapUnitsOFFICER2_FNAME.AsString);
item.AddPair('Officer2Empnum', ApiDB.uqMapUnitsOFFICER2_EMPNUM.AsString);
item.AddPair('CanShowDetails', TJSONBool.Create(not ApiDB.uqMapUnitsDIS_UNITID.IsNull));
data.AddElement(item);
end;
......@@ -269,6 +275,7 @@ end;
function TApiService.GetComplaintList: TJSONObject;
var
data: TJSONArray;
......@@ -545,7 +552,7 @@ begin
if ApiDB.uqCFSMemosTIMESTAMP.IsNull then
ts := ''
else
ts := FormatDateTime('yyyy-mm-dd hh:nn:ss', ApiDB.uqCFSMemosTIMESTAMP.AsDateTime);
ts := FormatDateTime('yyyy-mm-dd' + ' ' + 'hh:nn:ss', ApiDB.uqCFSMemosTIMESTAMP.AsDateTime);
item.AddPair('Timestamp', ts);
item.AddPair('BadgeNumber', ApiDB.uqCFSMemosBADGE_NUMBER.AsString);
......@@ -605,7 +612,7 @@ begin
if FieldByName('DATEREPORTED').IsNull then
rowObj.AddPair('DateReported', '')
else
rowObj.AddPair('DateReported', FormatDateTime('yyyy-mm-dd hh:nn:ss', FieldByName('DATEREPORTED').AsDateTime));
rowObj.AddPair('DateReported', FormatDateTime('yyyy-mm-dd', FieldByName('DATEREPORTED').AsDateTime));
rowObj.AddPair('DPriority', FieldByName('DPRIORITY').AsString);
rowObj.AddPair('DCallType', FieldByName('DCALLTYPE').AsString);
......@@ -780,11 +787,9 @@ begin
ts := '';
if not ApiDB.uqUnitLogsLOG_TIME.IsNull then
ts := FormatDateTime('yyyy-mm-dd hh:nn:ss', ApiDB.uqUnitLogsLOG_TIME.AsDateTime);
ts := FormatDateTime('yyyy-mm-dd' + ' ' + 'HH:nn:ss', ApiDB.uqUnitLogsLOG_TIME.AsDateTime);
complaintText := Trim(ApiDB.uqUnitLogsCOMPLAINT_NUM.AsString);
if complaintText = '0' then
complaintText := 'N/A';
rowObj.AddPair('LogTime', ts);
rowObj.AddPair('Complaint', complaintText);
......
[Settings]
LogFileNum=557
LogFileNum=579
webClientVersion=0.1.0
......@@ -143,12 +143,12 @@ object FViewComplaintDetails: TFViewComplaintDetails
WordWrap = True
Columns = <
item
ElementClassName = 'text-nowrap'
ElementClassName = 'd-none'
DataField = 'MemoType'
Title = 'Type'
TitleElementClassName = 'd-none'
end
item
ElementClassName = 'text-nowrap'
DataField = 'Timestamp'
Title = 'Timestamp'
end
......@@ -253,17 +253,17 @@ object FViewComplaintDetails: TFViewComplaintDetails
item
ElementClassName = 'text-nowrap'
DataField = 'Apartment'
Title = 'Apartment'
Title = 'Apt'
end
item
ElementClassName = 'text-nowrap'
DataField = 'DateReported'
Title = 'Date Reported'
Title = 'Date'
end
item
ElementClassName = 'text-nowrap'
DataField = 'DPriority'
Title = 'Priority'
Title = 'Pri'
end
item
DataField = 'DCallType'
......
......@@ -12,7 +12,7 @@
type="button"
data-bs-toggle="collapse"
data-bs-target="#cdetails_summary"
aria-expanded="false"
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>
......@@ -21,26 +21,38 @@
</div>
<!-- Summary body (stays sticky because it's inside the sticky block) -->
<div id="cdetails_summary" class="collapse">
<div id="cdetails_summary" class="collapse show">
<div class="card border-0 shadow-sm mb-2">
<div class="card-body py-2">
<div class="row gy-1 gx-2">
<div class="col-5 col-sm-4 fw-semibold text-muted">Priority:</div>
<div class="col-7 col-sm-8" id="lbl_priority"></div>
<div class="col-5 col-sm-4 fw-semibold text-muted">Status:</div>
<div class="col-7 col-sm-8" id="lbl_status"></div>
<div class="col-5 col-sm-4 fw-semibold text-muted">Dispatch Code:</div>
<div class="col-7 col-sm-8" id="lbl_dispatch_code"></div>
<table class="table table-sm mb-0 w-100">
<tbody>
<tr>
<th scope="row" class="fw-semibold text-nowrap pe-2 w-auto">Priority:</th>
<td class="w-100" id="lbl_priority"></td>
</tr>
<tr>
<th scope="row" class="fw-semibold text-nowrap pe-2 w-auto">Status:</th>
<td class="w-100" id="lbl_status"></td>
</tr>
<tr>
<th scope="row" class="fw-semibold text-nowrap pe-2 w-auto">Dispatch Code:</th>
<td class="w-100" id="lbl_dispatch_code"></td>
</tr>
<tr>
<th scope="row" class="fw-semibold text-nowrap pe-2 w-auto">Dispatch District:</th>
<td class="w-100" id="lbl_dispatch_district"></td>
</tr>
<tr>
<th scope="row" class="fw-semibold text-nowrap pe-2 w-auto">Address:</th>
<td class="w-100" id="lbl_address"></td>
</tr>
</tbody>
</table>
<div class="col-5 col-sm-4 fw-semibold text-muted">Dispatch District:</div>
<div class="col-7 col-sm-8" id="lbl_dispatch_district"></div>
<div class="col-5 col-sm-4 fw-semibold text-muted">Address:</div>
<div class="col-7 col-sm-8" id="lbl_address"></div>
</div>
</div>
</div>
</div>
......
<div class="sticky-top">
<!-- Local navbar (Complaints) -->
<nav class="navbar navbar-dark bg-primary py-2">
<div class="container-fluid">
<div class="row w-100 g-2 align-items-stretch">
<div class="col">
<span id="complaints_title" class="navbar-brand mb-0 h5 text-white">Complaints</span>
</div>
<div class="col">
<button id="complaints_btnrefresh" type="button" class="btn btn-primary w-100 h-100">
<i class="fa fa-sync-alt me-1"></i><span class="d-none d-sm-inline">Refresh</span>
</button>
</div>
<div class="col">
<button id="complaints_btngroup" type="button" class="btn btn-primary w-100 h-100">
<i class="fa fa-layer-group me-1"></i><span class="d-none d-sm-inline">Group</span>
</button>
</div>
<div class="col">
<button id="complaints_btnfilter" type="button" class="btn btn-primary w-100 h-100">
<i class="fa fa-sliders-h me-1"></i><span class="d-none d-sm-inline">Filter</span>
</button>
<div class="d-flex flex-column h-100">
<!-- Header / controls (non-scrolling) -->
<div class="flex-shrink-0">
<!-- Local navbar (Complaints) -->
<nav class="navbar navbar-dark bg-primary py-2">
<div class="container-fluid">
<div class="row w-100 g-2 align-items-stretch">
<div class="col">
<span id="complaints_title" class="navbar-brand mb-0 h5 text-white">Complaints</span>
</div>
<div class="col">
<button id="complaints_btnrefresh" type="button" class="btn btn-primary w-100 h-100">
<i class="fa fa-sync-alt me-1"></i><span class="d-none d-sm-inline">Refresh</span>
</button>
</div>
<div class="col">
<button id="complaints_btngroup" type="button" class="btn btn-primary w-100 h-100">
<i class="fa fa-layer-group me-1"></i><span class="d-none d-sm-inline">Group</span>
</button>
</div>
<div class="col">
<button id="complaints_btnfilter" type="button" class="btn btn-primary w-100 h-100">
<i class="fa fa-sliders-h me-1"></i><span class="d-none d-sm-inline">Filter</span>
</button>
</div>
</div>
</div>
</div>
</nav>
<!-- Search bar under local navbar -->
<div class="bg-light border-bottom py-2">
<div class="container-fluid">
<div class="input-group">
<span class="input-group-text bg-white"><i class="fa fa-search"></i></span>
<input id="complaints_search" class="form-control" placeholder="Search...">
</nav>
<!-- Search bar under local navbar -->
<div class="bg-light border-bottom py-2">
<div class="container-fluid">
<div class="input-group">
<span class="input-group-text bg-white"><i class="fa fa-search"></i></span>
<input id="complaints_search" class="form-control" placeholder="Search...">
</div>
</div>
</div>
</div>
</div>
<!-- Complaints list container -->
<div class="container-fluid mt-2">
<div class="row justify-content-center">
<div class="col-12 col-md-10 col-lg-8">
<!-- This is where the DBListControl will inject cards -->
<div id="complaints_dbl_complaint_list" class="d-flex flex-column gap-2">
<!-- Cards will render here -->
<!-- Scrolling list area -->
<div class="flex-grow-1 overflow-auto" style="min-height:0;">
<div class="container-fluid mt-2">
<div class="row justify-content-center">
<div class="col-12 col-md-10 col-lg-8">
<!-- This is where the DBListControl will inject cards -->
<div id="complaints_dbl_complaint_list" class="d-flex flex-column gap-2">
<!-- Cards will render here -->
</div>
<label id="complaints_lblentries" class="mt-2 d-block"></label>
</div>
</div>
<label id="complaints_lblentries" class="mt-2 d-block"></label>
<!-- Pagination -->
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center" id="pagination">
<!-- Pagination items rendered in Delphi -->
</ul>
</nav>
</div>
</div>
</div>
</div>
......@@ -62,6 +62,18 @@ begin
tmrRefresh.Enabled := False;
GetComplaints;
tmrRefresh.Enabled := True;
asm
if (!window.showComplaintDetails) {
window.showComplaintDetails = function (id) {
console.log('JS bridge showComplaintDetails called, id=', id);
try {
pas['View.Main'].FViewMain.ShowComplaintDetails(id);
} catch (e) {
console.log('Error in TFViewMain.ShowComplaintDetails', e);
}
};
}
end;
end;
procedure TFViewComplaints.HandleListClick(e: TJSMouseEvent);
......@@ -70,12 +82,12 @@ begin
el := TJSElement(e.target);
if (el is TJSHtmlElement) and TJSHtmlElement(el).classList.contains('complaint-details-btn') then
begin
id := string(TJSHtmlElement(el).dataset['id']); // comes from (%ComplaintId%)
id := string(TJSHtmlElement(el).dataset['id']); // from data-id="(%ComplaintId%)"
e.preventDefault;
e.stopPropagation;
if Assigned(FSelectProc) then
FSelectProc(id);
asm
if (window.showComplaintDetails) window.showComplaintDetails(id);
end;
end;
end;
......
......@@ -128,13 +128,8 @@ object FViewMap: TFViewMap
end
object tmrRefresh: TWebTimer
Interval = 30000
OnTimer = tmrRefreshTimer
Left = 358
Top = 696
end
object tmrZoomToBounds: TWebTimer
Enabled = False
OnTimer = tmrZoomToBoundsTimer
Left = 358
Top = 750
end
end
......@@ -23,18 +23,18 @@ type
httpReqGeoJson: TWebHttpRequest;
xdwcMap: TXDataWebClient;
tmrRefresh: TWebTimer;
tmrZoomToBounds: TWebTimer;
procedure lfMapMapInitialized(Sender: TObject);
[async] procedure httpReqGeoJsonResponse(Sender: TObject; AResponse: string);
procedure lfMapCustomizeMarker(Sender: TObject;
var ACustomizeMarker: string);
procedure lfMapCustomizeCSS(Sender: TObject; var ACustomizeCSS: string);
procedure tmrZoomToBoundsTimer(Sender: TObject);
procedure tmrRefreshTimer(Sender: TObject);
private
FUnitsLoaded: Boolean;
FComplaintsLoaded: Boolean;
FZoomPending: Boolean;
FLoadingPoints: Boolean;
[async] procedure LoadPointsAsync;
function CarIconForDistrict(const DistrictCode: string): string;
public
......@@ -131,12 +131,6 @@ begin
P.StrokeWidth := 2;
end;
if lfMap.Polygons.Count > 0 then
begin
FZoomPending := True;
tmrZoomToBounds.Enabled := True;
end;
finally
lfMap.EndUpdate;
end;
......@@ -180,6 +174,9 @@ var
officer2Lname, officer2Fname, officer2Empnum: string;
officer1Display, officer2Display: string;
updateTimeText: string;
canShowDetails: Boolean;
canShowDetailsText: string;
detailsBtnHtml: string;
begin
ShowSpinner('spinner');
FUnitsLoaded := False;
......@@ -217,6 +214,24 @@ begin
officer2Fname := string(item['Officer2Fname']);
officer2Empnum := string(item['Officer2Empnum']);
canShowDetailsText := '';
if item['CanShowDetails'] <> nil then
canShowDetailsText := string(item['CanShowDetails']);
canShowDetails := SameText(Trim(canShowDetailsText), 'true');
if canShowDetails then
begin
detailsBtnHtml :=
'<button type="button" class="btn btn-primary btn-sm px-2 py-1" ' +
'onclick="window.showUnitDetails(''' + unitId + ''')">' +
'Details' +
'</button>';
end
else
begin
detailsBtnHtml := '';
end;
officer1Display := '';
if Trim(officer1Lname + officer1Fname + officer1Empnum) <> '' then
begin
......@@ -242,47 +257,43 @@ begin
m.Longitude := lng;
m.Title :=
'<div class="d-flex flex-column gap-1 px-1 py-1" style="width:260px;">' +
'<div class="fw-semibold small">' +
'<span class="fw-bold">Unit:</span> ' + uName +
'</div>' +
IfThen(dist <> '',
'<div class="small"><span class="fw-bold">District:</span> ' + dist + '</div>',
''
) +
IfThen(Trim(callType) <> '',
'<div class="small"><span class="fw-bold">Call Type:</span> ' + callType + '</div>',
''
) +
IfThen(Trim(priorityText) <> '',
'<div class="small"><span class="fw-bold">Priority:</span> ' + priorityText + '</div>',
''
) +
IfThen(Trim(statusText) <> '',
'<div class="small"><span class="fw-bold">Status:</span> ' + statusText + '</div>',
''
) +
IfThen(Trim(officer1Display) <> '',
'<div class="small"><span class="fw-bold">Officer 1:</span> ' + officer1Display + '</div>',
''
) +
IfThen(Trim(officer2Display) <> '',
'<div class="small"><span class="fw-bold">Officer 2:</span> ' + officer2Display + '</div>',
''
) +
IfThen(Trim(updateTimeText) <> '',
'<div class="small mb-1"><span class="fw-bold">Updated:</span> ' + updateTimeText + '</div>',
'<div class="small mb-1"></div>'
) +
'<div class="d-flex justify-content-end mt-0">' +
'<button type="button" class="btn btn-primary btn-sm px-2 py-1" ' +
'onclick="window.showUnitDetails(''' + unitId + ''')">' +
'Details' +
'</button>' +
'</div>' +
'</div>';
'<div class="d-flex flex-column gap-1 px-1 py-1" style="width:260px;">' +
'<div class="fw-semibold small">' +
'<span class="fw-bold">Unit:</span> ' + uName +
'</div>' +
IfThen(dist <> '',
'<div class="small"><span class="fw-bold">District:</span> ' + dist + '</div>',
''
) +
IfThen(Trim(callType) <> '',
'<div class="small"><span class="fw-bold">Call Type:</span> ' + callType + '</div>',
''
) +
IfThen(Trim(priorityText) <> '',
'<div class="small"><span class="fw-bold">Priority:</span> ' + priorityText + '</div>',
''
) +
IfThen(Trim(statusText) <> '',
'<div class="small"><span class="fw-bold">Status:</span> ' + statusText + '</div>',
''
) +
IfThen(Trim(officer1Display) <> '',
'<div class="small"><span class="fw-bold">Officer 1:</span> ' + officer1Display + '</div>',
''
) +
IfThen(Trim(officer2Display) <> '',
'<div class="small"><span class="fw-bold">Officer 2:</span> ' + officer2Display + '</div>',
''
) +
IfThen(Trim(updateTimeText) <> '',
'<div class="small mb-1"><span class="fw-bold">Updated:</span> ' + updateTimeText + '</div>',
'<div class="small mb-1"></div>'
) +
IfThen(detailsBtnHtml <> '',
'<div class="d-flex justify-content-end mt-0">' + detailsBtnHtml + '</div>',
''
) +
'</div>';
m.DataString := 'unit';
m.IconURL := CarIconForDistrict(dist);
......@@ -513,20 +524,12 @@ 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;}';
end;
procedure TFViewMap.tmrZoomToBoundsTimer(Sender: TObject);
procedure TFViewMap.tmrRefreshTimer(Sender: TObject);
begin
tmrZoomToBounds.Enabled := False;
if not FZoomPending then
Exit;
FZoomPending := False;
if lfMap.Polygons.Count = 0 then
if FLoadingPoints then
Exit;
// Note: Delaying avoids Leaflet moveend firing while the internal map is still null.
lfMap.ZoomToBounds(lfMap.Polygons.ToCoordinateArray);
LoadPointsAsync;
end;
end.
......
......@@ -5,11 +5,12 @@ object FViewUnitDetails: TFViewUnitDetails
ElementFont = efCSS
object tblLogs: TWebDBTableControl
Left = 166
Top = 114
Top = 116
Width = 300
Height = 200
ElementId = 'tbl_logs'
BorderColor = clSilver
ColHeader = False
ElementFont = efCSS
ElementHeaderClassName = 'table-light'
ElementTableClassName =
......@@ -36,9 +37,9 @@ object FViewUnitDetails: TFViewUnitDetails
WordWrap = True
Columns = <
item
ElementClassName = 'text-nowrap'
DataField = 'LogTime'
Title = 'Log Time'
TitleElementClassName = 'w-25'
end
item
ElementClassName = 'text-nowrap'
......@@ -57,7 +58,7 @@ object FViewUnitDetails: TFViewUnitDetails
Top = 394
end
object xdwdsUnitLogs: TXDataWebDataSet
Left = 274
Left = 278
Top = 394
object xdwdsUnitLogsLogTime: TStringField
FieldName = 'LogTime'
......
......@@ -12,7 +12,7 @@
type="button"
data-bs-toggle="collapse"
data-bs-target="#udetails_summary"
aria-expanded="false"
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>
......@@ -21,37 +21,49 @@
</div>
<!-- Summary body -->
<div id="udetails_summary" class="collapse">
<div id="udetails_summary" class="collapse show">
<div class="card border-0 shadow-sm mb-2">
<div class="card-body py-2">
<!-- Optional: Close/Back row (wire in code) -->
<table class="table table-sm mb-0 w-100">
<tbody>
<tr>
<th scope="row" class="fw-semibold text-nowrap pe-2 w-auto">Car:</th>
<td class="w-100" id="lbl_car"></td>
</tr>
<tr>
<th scope="row" class="fw-semibold text-nowrap pe-2 w-auto">Unit Name:</th>
<td class="w-100" id="lbl_unit_name"></td>
</tr>
<tr>
<th scope="row" class="fw-semibold text-nowrap pe-2 w-auto">District:</th>
<td class="w-100" id="lbl_district"></td>
</tr>
<tr>
<th scope="row" class="fw-semibold text-nowrap pe-2 w-auto">Location:</th>
<td class="w-100" id="lbl_location"></td>
</tr>
<tr>
<th scope="row" class="fw-semibold text-nowrap pe-2 w-auto">Status:</th>
<td class="w-100" id="lbl_status"></td>
</tr>
<tr>
<th scope="row" class="fw-semibold text-nowrap pe-2 w-auto">Officer 1:</th>
<td class="w-100" id="lbl_officer1"></td>
</tr>
<tr>
<th scope="row" class="fw-semibold text-nowrap pe-2 w-auto">Officer 2:</th>
<td class="w-100" id="lbl_officer2"></td>
</tr>
<tr>
<th scope="row" class="fw-semibold text-nowrap pe-2 w-auto">Updated:</th>
<td class="w-100" id="lbl_updated"></td>
</tr>
</tbody>
</table>
<div class="row gy-1 gx-2">
<div class="col-5 col-sm-4 fw-semibold text-muted">Car:</div>
<div class="col-7 col-sm-8" id="lbl_car"></div>
<div class="col-5 col-sm-4 fw-semibold text-muted">Unit Name:</div>
<div class="col-7 col-sm-8" id="lbl_unit_name"></div>
<div class="col-5 col-sm-4 fw-semibold text-muted">District:</div>
<div class="col-7 col-sm-8" id="lbl_district"></div>
<div class="col-5 col-sm-4 fw-semibold text-muted">Location:</div>
<div class="col-7 col-sm-8" id="lbl_location"></div>
<div class="col-5 col-sm-4 fw-semibold text-muted">Status:</div>
<div class="col-7 col-sm-8" id="lbl_status"></div>
<div class="col-5 col-sm-4 fw-semibold text-muted">Officer 1:</div>
<div class="col-7 col-sm-8" id="lbl_officer1"></div>
<div class="col-5 col-sm-4 fw-semibold text-muted">Officer 2:</div>
<div class="col-7 col-sm-8" id="lbl_officer2"></div>
<div class="col-5 col-sm-4 fw-semibold text-muted">Updated:</div>
<div class="col-7 col-sm-8" id="lbl_updated"></div>
</div>
</div>
</div>
......
......@@ -35,9 +35,9 @@ type
xdwdsUnitLogs: TXDataWebDataSet;
wdsUnitLogs: TWebDataSource;
tblLogs: TWebDBTableControl;
xdwdsUnitLogsLogTime: TStringField;
xdwdsUnitLogsComplaint: TStringField;
xdwdsUnitLogsLog: TStringField;
xdwdsUnitLogsLogTime: TStringField;
private
FUnitId: string;
FLoading: Boolean;
......@@ -51,6 +51,7 @@ type
[async] procedure LoadUnitAsync;
[async] procedure LoadLogsAsync;
public
class function CreateForm(AElementID, UnitId: string): TWebForm;
end;
......@@ -156,39 +157,36 @@ begin
xdwdsUnitDetails.Close;
xdwdsUnitDetails.SetJsonData(dataObj);
xdwdsUnitDetails.Open;
console.log('Officer1Lname', xdwdsUnitDetails.FieldByName('Officer1Lname').AsString);
console.log('Officer1Fname', xdwdsUnitDetails.FieldByName('Officer1Fname').AsString);
console.log('Officer1Empnum', xdwdsUnitDetails.FieldByName('Officer1Empnum').AsString);
console.log('Officer2Lname', xdwdsUnitDetails.FieldByName('Officer2Lname').AsString);
summaryTitle := xdwdsUnitDetails.FieldByName('UnitName').AsString;
summaryTitle := xdwdsUnitDetailsUnitName.AsString;
if Trim(summaryTitle) <> '' then
SetTextById('lbl_summary_title', 'Summary for Unit ' + summaryTitle)
else
SetTextById('lbl_summary_title', 'Summary for Unit ' + xdwdsUnitDetails.FieldByName('UnitId').AsString);
SetTextById('lbl_summary_title', 'Summary for Unit ' + xdwdsUnitDetailsUnitId.AsString);
SetTextById('lbl_car', xdwdsUnitDetails.FieldByName('CarNumberDesc').AsString);
SetTextById('lbl_unit_name', xdwdsUnitDetails.FieldByName('UnitName').AsString);
SetTextById('lbl_district', xdwdsUnitDetails.FieldByName('District').AsString);
SetTextById('lbl_location', xdwdsUnitDetails.FieldByName('Location').AsString);
SetTextById('lbl_status', xdwdsUnitDetails.FieldByName('Status').AsString);
SetTextById('lbl_updated', xdwdsUnitDetails.FieldByName('UpdateTime').AsString);
SetTextById('lbl_car', xdwdsUnitDetailsCarNumberDesc.AsString);
SetTextById('lbl_unit_name', xdwdsUnitDetailsUnitName.AsString);
SetTextById('lbl_district', xdwdsUnitDetailsDistrict.AsString);
SetTextById('lbl_location', xdwdsUnitDetailsLocation.AsString);
SetTextById('lbl_status', xdwdsUnitDetailsStatus.AsString);
SetTextById('lbl_updated', xdwdsUnitDetailsUpdateTime.AsString);
officer1Text := FormatOfficer(
xdwdsUnitDetails.FieldByName('Officer1Lname').AsString,
xdwdsUnitDetails.FieldByName('Officer1Fname').AsString,
xdwdsUnitDetails.FieldByName('Officer1Empnum').AsString
xdwdsUnitDetailsOfficer1Lname.AsString,
xdwdsUnitDetailsOfficer1Fname.AsString,
xdwdsUnitDetailsOfficer1Empnum.AsString
);
officer2Text := FormatOfficer(
xdwdsUnitDetails.FieldByName('Officer2Lname').AsString,
xdwdsUnitDetails.FieldByName('Officer2Fname').AsString,
xdwdsUnitDetails.FieldByName('Officer2Empnum').AsString
xdwdsUnitDetailsOfficer2Lname.AsString,
xdwdsUnitDetailsOfficer2Fname.AsString,
xdwdsUnitDetailsOfficer2Empnum.AsString
);
SetTextById('lbl_officer1', officer1Text);
SetTextById('lbl_officer2', officer2Text);
await(LoadLogsAsync);
Utils.HideSpinner('spinner');
......@@ -221,6 +219,7 @@ begin
resp := await(xdwcUnitDetails.RawInvokeAsync('IApiService.GetUnitLogs', [FUnitId]));
rootObj := TJSObject(resp.Result);
dataArr := TJSArray(rootObj['data']);
console.log('LoadLogsAsync Units rootObj: ' + rootObj.tostring);
xdwdsUnitLogs.Close;
xdwdsUnitLogs.SetJsonData(dataArr);
......
<div class="sticky-top">
<!-- Local navbar (Units) -->
<nav class="navbar navbar-dark bg-primary py-2"><!-- removed sticky-top -->
<div class="container-fluid">
<div class="row w-100 g-2 align-items-stretch">
<div class="col">
<span id="units_title" class="navbar-brand mb-0 h5 text-white">Units</span>
</div>
<div class="col">
<button id="units_btnrefresh" type="button" class="btn btn-primary w-100 h-100">
<i class="fa fa-sync-alt me-1"></i><span class="d-none d-sm-inline">Refresh</span>
</button>
</div>
<div class="col">
<button id="units_btngroup" type="button" class="btn btn-primary w-100 h-100">
<i class="fa fa-layer-group me-1"></i><span class="d-none d-sm-inline">Group</span>
</button>
</div>
<div class="col">
<button id="units_btnfilter" type="button" class="btn btn-primary w-100 h-100">
<i class="fa fa-sliders-h me-1"></i><span class="d-none d-sm-inline">Filter</span>
</button>
<div class="d-flex flex-column h-100">
<!-- Header / controls (non-scrolling) -->
<div class="flex-shrink-0">
<!-- Local navbar (Units) -->
<nav class="navbar navbar-dark bg-primary py-2">
<div class="container-fluid">
<div class="row w-100 g-2 align-items-stretch">
<div class="col">
<span id="units_title" class="navbar-brand mb-0 h5 text-white">Units</span>
</div>
<div class="col">
<button id="units_btnrefresh" type="button" class="btn btn-primary w-100 h-100">
<i class="fa fa-sync-alt me-1"></i><span class="d-none d-sm-inline">Refresh</span>
</button>
</div>
<div class="col">
<button id="units_btngroup" type="button" class="btn btn-primary w-100 h-100">
<i class="fa fa-layer-group me-1"></i><span class="d-none d-sm-inline">Group</span>
</button>
</div>
<div class="col">
<button id="units_btnfilter" type="button" class="btn btn-primary w-100 h-100">
<i class="fa fa-sliders-h me-1"></i><span class="d-none d-sm-inline">Filter</span>
</button>
</div>
</div>
</div>
</div>
</nav>
<!-- Search bar under local navbar -->
<div class="bg-light border-bottom py-2"><!-- removed sticky-top -->
<div class="container-fluid">
<div class="input-group">
<span class="input-group-text bg-white"><i class="fa fa-search"></i></span>
<input id="units_search" class="form-control" placeholder="Search...">
</nav>
<!-- Search bar under local navbar -->
<div class="bg-light border-bottom py-2">
<div class="container-fluid">
<div class="input-group">
<span class="input-group-text bg-white"><i class="fa fa-search"></i></span>
<input id="units_search" class="form-control" placeholder="Search...">
</div>
</div>
</div>
</div>
</div> <!-- /sticky-top wrapper -->
<!-- Units list container -->
<div class="container-fluid mt-2">
<div class="row justify-content-center">
<div class="col-12 col-md-10 col-lg-8">
<!-- This is where the DBListControl will inject cards -->
<div id="units_dbl_unit_list" class="d-flex flex-column gap-2">
<!-- Cards will render here -->
<!-- Scrolling list area -->
<div class="flex-grow-1 overflow-auto" style="min-height:0;">
<div class="container-fluid mt-2">
<div class="row justify-content-center">
<div class="col-12 col-md-10 col-lg-8">
<!-- This is where the DBListControl will inject cards -->
<div id="units_dbl_unit_list" class="d-flex flex-column gap-2">
<!-- Cards will render here -->
</div>
<!-- Entry Count Label -->
<label id="unitss_lblentries" class="mt-2 d-block"></label>
</div>
</div>
<!-- Entry Count Label -->
<label id="unitss_lblentries" class="mt-2 d-block"></label>
<!-- Pagination -->
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center" id="pagination">
<!-- Pagination items rendered in Delphi -->
</ul>
</nav>
</div>
</div>
</div>
</div>
......@@ -35,6 +35,7 @@ type
private
FLoading: Boolean;
[async] procedure GetUnits;
procedure HandleListClick(e: TJSMouseEvent);
public
end;
......@@ -49,36 +50,53 @@ implementation
procedure TFViewUnits.WebFormCreate(Sender: TObject);
begin
console.log('Units.WebFormCreate: Starting setup...');
DMConnection.ApiConnection.Connected := True;
console.log('API connection active:', DMConnection.ApiConnection.Connected);
Document.addEventListener('click', @HandleListClick);
tmrRefresh.Enabled := False;
GetUnits;
tmrRefresh.Enabled := True;
// {$IFNDEF WIN32}
// asm
// var root = pas.TFViewUnits(Self).dblUnitsList.ElementHandle;
// if (root && !root.__emiDelegated) {
// root.__emiDelegated = true;
//
// root.addEventListener('click', function (e) {
// // Look for a click on, or inside, the details button
// var btn = e.target && e.target.closest('.btn-unit-details');
// if (!btn || !root.contains(btn)) return;
//
// e.preventDefault();
// e.stopPropagation();
//
// var unitId = btn.getAttribute('data-unitid') || '';
// pas.TFViewUnits(Self).OpenUnitDetails(unitId);
// }, { passive: true });
// }
// end;
// {$ENDIF}
asm
if (!window.showUnitDetails) {
window.showUnitDetails = function (id) {
console.log('JS bridge showUnitDetails called, id=', id);
try {
pas['View.Main'].FViewMain.ShowUnitDetails(id);
} catch (e) {
console.log('Error in TFViewMain.ShowUnitDetails', e);
}
};
}
end;
end;
procedure TFViewUnits.HandleListClick(e: TJSMouseEvent);
var
el: TJSElement;
btn: TJSElement;
unitId: string;
begin
btn := nil;
el := TJSElement(e.target);
asm
btn = (el && el.closest) ? el.closest('.btn-unit-details') : null;
end;
if (btn <> nil) and (btn is TJSHtmlElement) then
begin
unitId := string(TJSHtmlElement(btn).getAttribute('data-unitid'));
e.preventDefault;
e.stopPropagation;
asm
if (window.showUnitDetails) window.showUnitDetails(unitId);
end;
end;
end;
procedure TFViewUnits.btnRefreshClick(Sender: TObject);
begin
GetUnits;
......
......@@ -68,3 +68,5 @@ html, body {
height: 100%;
overflow: hidden;
}
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