Commit 731ef776 by Mac Stephens

Units is up and working

parent d011eb7b
......@@ -67,52 +67,51 @@ object ApiDatabaseModule: TApiDatabaseModule
object uqDISUnitsActive: TUniQuery
Connection = ucENTCAD
SQL.Strings = (
'SELECT'
' dua.UNITID,'
' dua.UNITNAME,'
' cun.CODE_DESC AS CARNUMBER_DESC,'
' cd.CODE_DESC AS DISTRICT_DESC,'
' cs.CODE_DESC AS SECTOR_DESC,'
' p1.PF_EMPNUM AS OFFICER1_EMPNUM,'
' p1.PF_LNAME AS OFFICER1_LAST_NAME,'
' p1.PF_FNAME AS OFFICER1_FIRST_NAME,'
' p1.PF_MI AS OFFICER1_MI,'
' p2.PF_EMPNUM AS OFFICER2_EMPNUM,'
' p2.PF_LNAME AS OFFICER2_LAST_NAME,'
' p2.PF_FNAME AS OFFICER2_FIRST_NAME,'
' p2.PF_MI AS OFFICER2_MI,'
' ca.LOCATION,'
' ca.COMPLAINT,'
' ca.UNITSTATUS,'
' cus.CODE_DESC AS UNIT_STATUS_DESC,'
' uc.ENTRYID,'
' uc.GPS_LATITUDE,'
' uc.GPS_LONGITUDE'
'SELECT dua.UNITID, dua.UNITNAME,'
' cun.CODE_DESC AS CARNUMBER_DESC,'
' cd.CODE_DESC AS DISTRICT_DESC,'
' cs.CODE_DESC AS SECTOR_DESC,'
' p1.PF_EMPNUM AS OFFICER1_EMPNUM,'
' p1.PF_LNAME AS OFFICER1_LAST_NAME,'
' p1.PF_FNAME AS OFFICER1_FIRST_NAME,'
' p1.PF_MI AS OFFICER1_MI,'
' p2.PF_EMPNUM AS OFFICER2_EMPNUM,'
' p2.PF_LNAME AS OFFICER2_LAST_NAME,'
' p2.PF_FNAME AS OFFICER2_FIRST_NAME,'
' p2.PF_MI AS OFFICER2_MI,'
' ca.LOCATION,'
' ca.COMPLAINT,'
' ca.UNITSTATUS,'
' cus.CODE_DESC AS UNIT_STATUS_DESC,'
' uc.ENTRYID,'
' uc.GPS_LATITUDE,'
' uc.GPS_LONGITUDE'
'FROM DIS_UNITS_ACTIVE dua'
'LEFT JOIN CD_UNIT_NUMBER cun ON cun.AGENCYCODE = dua.CA' +
'RNUMBER'
'LEFT JOIN CD_UNIT_NUMBER cun ON cun.AGENCYCODE = dua.CAR' +
'NUMBER'
'LEFT JOIN CD_DISTRICT cd ON cd.AGENCYCODE = dua.DI' +
'STRICT'
'LEFT JOIN CD_DISTRICT cd ON cd.AGENCYCODE = dua.DIS' +
'TRICT'
'LEFT JOIN CD_SECTOR cs ON cs.AGENCYCODE = dua.SE' +
'CTOR AND cs.CODE_TYPE = cd.AGENCYCODE'
'LEFT JOIN CD_SECTOR cs ON cs.AGENCYCODE = dua.SEC' +
'TOR AND cs.CODE_TYPE = cd.AGENCYCODE'
'LEFT JOIN PERSONNEL p1 ON dua.OFFICER1ID = p1.PF_' +
'NAMEID'
'LEFT JOIN PERSONNEL p1 ON dua.OFFICER1ID = p1.PF_N' +
'AMEID'
'LEFT JOIN PERSONNEL p2 ON dua.OFFICER2ID = p2.PF_' +
'NAMEID'
'LEFT JOIN PERSONNEL p2 ON dua.OFFICER2ID = p2.PF_N' +
'AMEID'
'LEFT JOIN CFS_ACTIVE ca ON dua.UNITID = ca.UNI' +
'TID'
'LEFT JOIN CFS_ACTIVE ca ON dua.UNITID = ca.UNIT' +
'ID'
'LEFT JOIN CD_UNITSTATUS cus ON ca.UNITSTATUS = cus.CO' +
'DE'
'LEFT JOIN CD_UNITSTATUS cus ON ca.UNITSTATUS = cus.COD' +
'E'
'LEFT JOIN UNITS_CURRENT@AVL_LINK uc ON dua.UNITID = uc.UNI' +
'TID')
'LEFT JOIN UNITS_CURRENT@AVL_LINK uc ON dua.UNITID = uc.UNIT' +
'ID'
'FETCH FIRST 100 ROWS ONLY')
ReadOnly = True
Left = 462
Top = 374
......@@ -188,14 +187,6 @@ object ApiDatabaseModule: TApiDatabaseModule
ReadOnly = True
Size = 10
end
object uqDISUnitsActiveUNITSTATUS: TFloatField
FieldName = 'UNITSTATUS'
ReadOnly = True
end
object uqDISUnitsActiveUNIT_STATUS_DESC: TStringField
FieldName = 'UNIT_STATUS_DESC'
ReadOnly = True
end
object uqDISUnitsActiveENTRYID: TFloatField
FieldName = 'ENTRYID'
ReadOnly = True
......@@ -208,6 +199,14 @@ object ApiDatabaseModule: TApiDatabaseModule
FieldName = 'GPS_LONGITUDE'
ReadOnly = True
end
object uqDISUnitsActiveUNITSTATUS: TFloatField
FieldName = 'UNITSTATUS'
ReadOnly = True
end
object uqDISUnitsActiveUNIT_STATUS_DESC: TStringField
FieldName = 'UNIT_STATUS_DESC'
ReadOnly = True
end
end
object uqCFSActive: TUniQuery
Connection = ucENTCAD
......@@ -335,17 +334,32 @@ object ApiDatabaseModule: TApiDatabaseModule
' ct.DATEDISPATCHED,'
' ct.DATERESPONDED,'
' ct.DATEARRIVED,'
' ct.DATECLEARED'
' ct.DATECLEARED,'
' cp.COLOR AS PRIORITY_COLOR,'
' cd.CODE_DESC AS DISTRICT_DESC,'
' cs.CODE_DESC AS SECTOR_DESC'
'FROM COMPLAINT_ACTIVE ca'
'JOIN COMPLAINT_TIMES ct ON ca.COMPLAINTID = ct.COMPLAINTID'
'LEFT JOIN CD_DISPATCHCODES cdc ON ca.DISPATCHCODE = cdc.CODE'
'LEFT JOIN CD_CALLSOURCES ccs ON ca.SOURCE = ccs.CODE'
'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' +
'STRICT'
'LEFT JOIN CD_SECTOR cs ON cs.AGENCYCODE = ca.DISPATCHSE' +
'CTOR AND cs.CODE_TYPE = cd.AGENCYCODE '
'WHERE ca.COMPLAINT IS NOT NULL'
'ORDER BY ct.DATEREPORTED DESC, ca.PRIORITY DESC'
'ORDER BY ca.DISPATCHDISTRICT, ct.DATEREPORTED DESC, ca.PRIORITY ' +
'DESC'
'FETCH FIRST 10 ROWS ONLY')
ReadOnly = True
OnCalcFields = uqComplaintListCalcFields
Left = 76
Top = 322
Top = 324
object uqComplaintListCOMPLAINTID: TFloatField
FieldName = 'COMPLAINTID'
Required = True
......@@ -452,6 +466,26 @@ object ApiDatabaseModule: TApiDatabaseModule
FieldName = 'DATECLEARED'
ReadOnly = True
end
object uqComplaintListcomplaintNumber: TStringField
FieldKind = fkCalculated
FieldName = 'complaintNumber'
Size = 11
Calculated = True
end
object uqComplaintListPRIORITY_COLOR: TFloatField
FieldName = 'PRIORITY_COLOR'
ReadOnly = True
end
object uqComplaintListDISTRICT_DESC: TStringField
FieldName = 'DISTRICT_DESC'
ReadOnly = True
Size = 120
end
object uqComplaintListSECTOR_DESC: TStringField
FieldName = 'SECTOR_DESC'
ReadOnly = True
Size = 120
end
end
object uqComplaintDetails: TUniQuery
Connection = ucENTCAD
......@@ -617,6 +651,7 @@ object ApiDatabaseModule: TApiDatabaseModule
Port = 1521
Username = 'ENTCAD'
Server = 'BUFENTCAD'
Connected = True
LoginPrompt = False
Left = 76
Top = 244
......
......@@ -113,12 +113,17 @@ type
uqDISUnitsActiveOFFICER2_MI: TStringField;
uqDISUnitsActiveLOCATION: TStringField;
uqDISUnitsActiveCOMPLAINT: TStringField;
uqDISUnitsActiveUNITSTATUS: TFloatField;
uqDISUnitsActiveUNIT_STATUS_DESC: TStringField;
uqDISUnitsActiveENTRYID: TFloatField;
uqDISUnitsActiveGPS_LATITUDE: TFloatField;
uqDISUnitsActiveGPS_LONGITUDE: TFloatField;
uqComplaintListcomplaintNumber: TStringField;
uqComplaintListPRIORITY_COLOR: TFloatField;
uqComplaintListDISTRICT_DESC: TStringField;
uqComplaintListSECTOR_DESC: TStringField;
uqDISUnitsActiveUNITSTATUS: TFloatField;
uqDISUnitsActiveUNIT_STATUS_DESC: TStringField;
procedure DataModuleCreate(Sender: TObject);
procedure uqComplaintListCalcFields(DataSet: TDataSet);
private
{ Private declarations }
public
......@@ -194,4 +199,19 @@ begin
end;
end;
procedure TApiDatabaseModule.uqComplaintListCalcFields(DataSet: TDataSet);
var
raw: string;
begin
raw := uqComplaintListCOMPLAINT.AsString.Trim;
if raw.Length >= 3 then
uqComplaintListcomplaintNumber.AsString := Copy(raw, 1, 2) + '-' + Copy(raw, 3, MaxInt)
else if raw.Length = 2 then
uqComplaintListcomplaintNumber.AsString := raw + '-'
else
uqComplaintListcomplaintNumber.AsString := raw;
end;
end.
......@@ -17,6 +17,7 @@ type
IApiService = interface(IInvokable)
['{4FCB7FAF-44E5-49D6-9C0F-EE44BFB33313}']
[HttpGet] function GetComplaintList: TJSONObject;
[HttpGet] function GetUnitList: TJSONObject;
end;
implementation
......
......@@ -4,8 +4,8 @@ interface
uses
XData.Server.Module, XData.Service.Common, Api.Database, Data.DB,
System.SysUtils, System.Generics.Collections, XData.Sys.Exceptions,
System.Hash, System.Classes, Common.Logging, System.JSON, Api.Service;
System.SysUtils, System.Generics.Collections, XData.Sys.Exceptions, System.StrUtils,
System.Hash, System.Classes, Common.Logging, System.JSON, Api.Service, VCL.Forms;
type
......@@ -18,6 +18,7 @@ type
procedure BeforeDestruction; override;
public
function GetComplaintList: TJSONObject;
function GetUnitList: TJSONObject;
end;
implementation
......@@ -42,6 +43,7 @@ end;
function TApiService.GetComplaintList: TJSONObject;
var
data: TJSONArray;
lastDistrict: string;
begin
Logger.Log(3, '---TApiService.GetComplaintList initiated');
......@@ -50,6 +52,7 @@ begin
data := TJSONArray.Create;
try
lastDistrict := '';
with ApiDB.uqComplaintList do
begin
Open;
......@@ -67,14 +70,35 @@ begin
status := 'Pending';
var item := TJSONObject.Create;
// Add a section header only when the district changes
var curDistrict := ApiDB.uqComplaintListDISPATCHDISTRICT.AsString;
if not SameText(curDistrict, lastDistrict) then
item.AddPair('DistrictHeader', curDistrict);
lastDistrict := curDistrict;
var districtSector := ApiDB.uqComplaintListDISTRICT_DESC.AsString + ApiDB.uqComplaintListSECTOR_DESC.AsString;
item.AddPair('DistrictSector', districtSector);
// existing color hex
var colorVal := ApiDB.uqComplaintListPRIORITY_COLOR.AsInteger;
item.AddPair('PriorityColor', '#' + IntToHex(colorVal and $FFFFFF, 6));
// Text is white only for the deep blue (255 = $0000FF), black otherwise
if (colorVal and $FFFFFF) = $0000FF then
item.AddPair('PriorityTextColor', '#FFFFFF')
else
item.AddPair('PriorityTextColor', '#000000');
item.AddPair('ComplaintId', ApiDB.uqComplaintListCOMPLAINTID.AsString);
item.AddPair('Complaint', ApiDB.uqComplaintListcomplaintNumber.AsString);
item.AddPair('Agency', ApiDB.uqComplaintListAGENCY.AsString);
item.AddPair('Priority', TJSONNumber.Create(ApiDB.uqComplaintListPRIORITY.AsString));
item.AddPair('Priority', ApiDB.uqComplaintListPRIORITY.AsString);
item.AddPair('DispatchCodeDesc', ApiDB.uqComplaintListDISPATCH_CODE_DESC.AsString);
item.AddPair('Address', ApiDB.uqComplaintListADDRESS.AsString);
item.AddPair('CFSId', ApiDB.uqComplaintListCFSID.AsString);
item.AddPair('Status', status);
item.AddPair('DispatchDistrict', ApiDB.uqComplaintListDISPATCHDISTRICT.AsString);
item.AddPair('DispatchDistrict', curDistrict);
item.AddPair('DateReported', ApiDB.uqComplaintListDATEREPORTED.AsString);
data.AddElement(item);
......@@ -95,6 +119,91 @@ begin
end;
function TApiService.GetUnitList: TJSONObject;
var
data: TJSONArray;
lastDistrict: string;
begin
Logger.Log(3, '---TApiService.GetUnitList initiated');
Result := TJSONObject.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result);
data := TJSONArray.Create;
try
lastDistrict := '';
with ApiDB.uqDISUnitsActive do
begin
Open;
First;
while not Eof do
begin
var item := TJSONObject.Create;
// Group header: show once when district changes (e.g., "1", "A")
var curDistrict := ApiDB.uqDISUnitsActiveDISTRICT_DESC.AsString;
if not SameText(curDistrict, lastDistrict) then
item.AddPair('DistrictHeader', curDistrict);
lastDistrict := curDistrict;
// Core unit identity
item.AddPair('UnitId', ApiDB.uqDISUnitsActiveUNITID.AsString);
item.AddPair('UnitName', ApiDB.uqDISUnitsActiveUNITNAME.AsString);
item.AddPair('CarNumberDesc', ApiDB.uqDISUnitsActiveCARNUMBER_DESC.AsString);
item.AddPair('District', curDistrict);
item.AddPair('Sector', ApiDB.uqDISUnitsActiveSECTOR_DESC.AsString);
// Current assignment (if any)
item.AddPair('Location', ApiDB.uqDISUnitsActiveLOCATION.AsString);
item.AddPair('Complaint', ApiDB.uqDISUnitsActiveCOMPLAINT.AsString);
// Status: default to "Available" when no active CFS row
var statusDesc := ApiDB.uqDISUnitsActiveUNIT_STATUS_DESC.AsString;
if statusDesc = '' then
statusDesc := 'Available';
item.AddPair('Status', statusDesc);
// Officers (LAST, FIRST [MI])
var o1 := Trim(ApiDB.uqDISUnitsActiveOFFICER1_LAST_NAME.AsString);
var f1 := Trim(ApiDB.uqDISUnitsActiveOFFICER1_FIRST_NAME.AsString);
var m1 := Trim(ApiDB.uqDISUnitsActiveOFFICER1_MI.AsString);
if o1 <> '' then
begin
if f1 <> '' then o1 := o1 + ', ' + f1;
if m1 <> '' then o1 := o1 + ' ' + m1;
item.AddPair('Officer1', o1);
end;
var o2 := Trim(ApiDB.uqDISUnitsActiveOFFICER2_LAST_NAME.AsString);
var f2 := Trim(ApiDB.uqDISUnitsActiveOFFICER2_FIRST_NAME.AsString);
var m2 := Trim(ApiDB.uqDISUnitsActiveOFFICER2_MI.AsString);
if o2 <> '' then
begin
if f2 <> '' then o2 := o2 + ', ' + f2;
if m2 <> '' then o2 := o2 + ' ' + m2;
item.AddPair('Officer2', o2);
end;
data.AddElement(item);
Next;
end;
end;
Result.AddPair('count', TJSONNumber.Create(data.Count));
Result.AddPair('returned', TJSONNumber.Create(data.Count));
Result.AddPair('data', data);
except
data.Free;
Logger.Log(3, '---TApiService.GetUnitList End (error)');
raise EXDataHttpException.Create(500, 'Failed to load units list');
end;
Logger.Log(3, '---TApiService.GetUnitList End');
end;
initialization
RegisterServiceType(TApiService);
......
......@@ -71,6 +71,7 @@ begin
FData.Free;
end;
procedure TFMain.ContactFormData(AText: String);
begin
if memoInfo.CanFocus then
......
[Settings]
LogFileNum=414
LogFileNum=451
webClientVersion=0.1.0
TwilioUpdateTime=1
TwilioUpdateTime=0
[Database]
Server=192.168.102.130
......
......@@ -11,7 +11,19 @@ object FViewComplaints: TFViewComplaints
ParentFont = False
Visible = True
OnCreate = WebFormCreate
object WebButton1: TWebButton
object lblEntries: TWebLabel
Left = 44
Top = 88
Width = 43
Height = 13
Caption = 'lblEntries'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
Visible = False
WidthPercent = 100.000000000000000000
end
object btnGroup: TWebButton
Left = 180
Top = 110
Width = 43
......@@ -25,7 +37,7 @@ object FViewComplaints: TFViewComplaints
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
end
object WebButton2: TWebButton
object btnFilter: TWebButton
Left = 242
Top = 110
Width = 37
......@@ -39,7 +51,7 @@ object FViewComplaints: TFViewComplaints
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
end
object Complaints: TWebButton
object btnRefresh: TWebButton
Left = 114
Top = 110
Width = 53
......@@ -52,6 +64,7 @@ object FViewComplaints: TFViewComplaints
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = btnRefreshClick
end
object dblComplaintsList: TWebDBListControl
Left = 36
......@@ -63,39 +76,82 @@ object FViewComplaints: TFViewComplaints
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
ChildOrder = 3
DefaultItemClassName = 'list-group-item'
DefaultItemClassName = 'list-group-item p-1 border-0'
DefaultItemLinkClassName = 'list-group-link'
ElementFont = efCSS
ElementListClassName = 'list-group'
Items = <
item
ItemClassName = 'list-group-item'
Items = <>
LinkClassName = 'list-group-link'
Text = 'Item 0'
end
item
ItemClassName = 'list-group-item'
Items = <>
LinkClassName = 'list-group-link'
Text = 'Item 1'
end
item
ItemClassName = 'list-group-item'
Items = <>
LinkClassName = 'list-group-link'
Text = 'Item 2'
end>
ElementListClassName = 'list-group list-group-flush border-0'
Items = <>
Style = lsListGroup
DataSource = wdsComplaints
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" style="--bs-card-bg:(%Priori' +
'tyColor%); --bs-card-color:(%PriorityTextColor%);"> <div class=' +
'"card-body py-2 px-3"> <div class="fw-bold text-uppercase sma' +
'll">(%Priority%): (%DispatchCodeDesc%)</div> <div class="smal' +
'l">(%Address%)</div> <div class="small text-opacity-75">(%Com' +
'plaint%): (%Status%)&nbsp;&nbsp;(%DistrictSector%)</div> <div' +
' class="small text-opacity-75">(%DateReported%)</div> </div></d' +
'iv>'
ListSource = wdsComplaints
end
object xdwcComplaints: TXDataWebClient
Connection = DMConnection.ApiConnection
Left = 92
Top = 416
Left = 58
Top = 410
end
object xdwdsComplaints: TXDataWebDataSet
Connection = DMConnection.ApiConnection
Left = 214
Top = 414
Left = 260
Top = 412
object xdwdsComplaintsComplaintId: TStringField
FieldName = 'ComplaintId'
end
object xdwdsComplaintsComplaint: TStringField
FieldName = 'Complaint'
end
object xdwdsComplaintsAgency: TStringField
FieldName = 'Agency'
end
object xdwdsComplaintsPriority: TStringField
FieldName = 'Priority'
end
object xdwdsComplaintsDispatchCodeDesc: TStringField
FieldName = 'DispatchCodeDesc'
end
object xdwdsComplaintsAddress: TStringField
FieldName = 'Address'
end
object xdwdsComplaintsStatus: TStringField
FieldName = 'Status'
end
object xdwdsComplaintsDispatchDistrict: TStringField
FieldName = 'DispatchDistrict'
end
object xdwdsComplaintsDateReported: TStringField
FieldName = 'DateReported'
end
object xdwdsComplaintsDistrictHeader: TStringField
FieldName = 'DistrictHeader'
end
object xdwdsComplaintsStatusColor: TStringField
FieldName = 'StatusColor'
end
object xdwdsComplaintsPriorityColor: TStringField
FieldName = 'PriorityColor'
end
object xdwdsComplaintsPriorityTextColor: TStringField
FieldName = 'PriorityTextColor'
end
object xdwdsComplaintsDistrictSector: TStringField
FieldName = 'DistrictSector'
end
end
object wdsComplaints: TWebDataSource
AutoEdit = False
DataSet = xdwdsComplaints
Left = 156
Top = 410
end
end
......@@ -48,7 +48,7 @@
</div>
<!-- Entry Count Label -->
<label id="lblentries" class="mt-2 d-block"></label>
<label id="complaints_lblentries" class="mt-2 d-block"></label>
<!-- Pagination -->
<nav aria-label="Page navigation">
......
......@@ -5,30 +5,40 @@ interface
uses
System.SysUtils, System.Classes, Web, WEBLib.Graphics, WEBLib.Forms, WEBLib.Dialogs,
Vcl.Controls, Vcl.StdCtrls, WEBLib.StdCtrls, WEBLib.Controls, WEBLib.Grids, WebLib.Lists,
XData.Web.Client, WEBLib.ExtCtrls, DB, XData.Web.JsonDataset, WebLib.DB,
XData.Web.Dataset, XData.Web.Connection, Vcl.Forms, DateUtils, WEBLib.DBCtrls;
XData.Web.Client, WEBLib.ExtCtrls, XData.Web.JsonDataset,
XData.Web.Dataset, XData.Web.Connection, Vcl.Forms, DateUtils, WEBLib.DBCtrls, System.Generics.Collections,
WEBLib.Menus, WEBLib.JSON, Auth.Service, WebLib.Storage, ConnectionModule, App.Types,
WEBLib.DB, Data.DB, XData.Web.DatasetCommon, JS, XData.Model.Classes, Utils;
type
TFViewComplaints = class(TWebForm)
xdwcComplaints: TXDataWebClient;
xdwdsComplaints: TXDataWebDataSet;
WebButton1: TWebButton;
WebButton2: TWebButton;
Complaints: TWebButton;
dblComplaintsList: TWebDBListControl;
xdwdsComplaintsComplaint: TStringField;
xdwdsComplaintsAgency: TStringField;
xdwdsComplaintsPriority: TStringField;
xdwdsComplaintsDispatchCodeDesc: TStringField;
xdwdsComplaintsAddress: TStringField;
xdwdsComplaintsStatus: TStringField;
xdwdsComplaintsDispatchDistrict: TStringField;
xdwdsComplaintsDateReported: TStringField;
lblEntries: TWebLabel;
btnRefresh: TWebButton;
btnGroup: TWebButton;
btnFilter: TWebButton;
wdsComplaints: TWebDataSource;
xdwdsComplaintsComplaintId: TStringField;
xdwdsComplaintsDistrictHeader: TStringField;
xdwdsComplaintsStatusColor: TStringField;
xdwdsComplaintsPriorityColor: TStringField;
xdwdsComplaintsPriorityTextColor: TStringField;
xdwdsComplaintsDistrictSector: TStringField;
procedure WebFormCreate(Sender: TObject);
procedure btnRefreshClick(Sender: TObject);
private
FChildForm: TWebForm;
var
PageNumber: integer;
PageSize: integer;
TotalPages: integer;
StartDate: string;
EndDate: string;
OrderBy: string;
Caller: string;
[async] procedure GetComplaints;
public
end;
var
......@@ -36,18 +46,71 @@ var
implementation
uses
JS, XData.Model.Classes,
ConnectionModule, Utils;
{$R *.dfm}
procedure TFViewComplaints.WebFormCreate(Sender: TObject);
begin
console.log('WebFormCreate: Starting setup...');
DMConnection.ApiConnection.Connected := True;
console.log('API connection active:', DMConnection.ApiConnection.Connected);
GetComplaints;
end;
//HTML for individual complaint cards can be found in the twebdblistcontrol HTMLString property
procedure TFViewComplaints.btnRefreshClick(Sender: TObject);
begin
GetComplaints;
end;
procedure TFViewComplaints.GetComplaints;
var
xdcResponse: TXDataClientResponse;
respObj: TJSObject;
complaintsCount: Integer;
begin
console.log('GetComplaints: Invoking API...');
Utils.ShowSpinner('spinner');
try
try
xdcResponse := await(xdwcComplaints.RawInvokeAsync('IApiService.GetComplaintList', []));
console.log('RawInvoke returned:', xdcResponse.Result);
respObj := TJSObject(xdcResponse.Result);
xdwdsComplaints.Close;
console.log('Dataset closed');
xdwdsComplaints.SetJsonData(respObj['data']);
console.log('JsonData set on dataset:', respObj['data']);
xdwdsComplaints.Open;
console.log('PriorityColor field name = ' +
xdwdsComplaintsPriorityColor.FieldName +
' sample value = ' +
xdwdsComplaintsPriorityColor.AsString);
if xdwdsComplaints.RecordCount > 0 then
begin
console.log('First record - Complaint:' + xdwdsComplaints.FieldByName('Complaint').AsString);
end;
complaintsCount := Integer(respObj['count']);
lblEntries.Caption := Format('%d active complaints', [complaintsCount]);
console.log('Label updated:' + lblEntries.Caption);
except
on E: EXDataClientRequestException do
begin
console.log('XData exception:' + E.ErrorResult.ErrorMessage);
Utils.ShowErrorModal(E.ErrorResult.ErrorMessage);
end;
end;
finally
Utils.HideSpinner('spinner');
console.log('GetComplaints complete');
end;
end;
end.
......
object FViewUnits: TFViewUnits
Width = 534
Height = 426
Width = 359
Height = 480
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
ParentFont = False
OnCreate = WebFormCreate
object lblEntries: TWebLabel
Left = 54
Top = 46
Width = 43
Height = 13
Caption = 'lblEntries'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
Visible = False
WidthPercent = 100.000000000000000000
end
object dblUnitsList: TWebDBListControl
Left = 50
Top = 146
Width = 263
Height = 237
ElementID = 'units_dbl_unit_list'
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
ChildOrder = 3
DefaultItemClassName = 'list-group-item p-1 border-0'
DefaultItemLinkClassName = 'list-group-link'
ElementFont = efCSS
ElementListClassName = 'list-group list-group-flush border-0'
Items = <>
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"> <div class="card-body py-2 px-' +
'3"> <div class="d-flex justify-content-between align-items-ba' +
'seline"> <div class="fw-bold fs-6">(%UnitName%)</div> ' +
'<div class="small text-end text-body-secondary ms-3 text-truncat' +
'e">(%Location%)</div> </div> <div class="small">(%Status%)' +
'</div> <div class="small">(%Officer1%)</div> <div class="s' +
'mall">(%Officer2%)</div> </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
Left = 156
Top = 410
end
object xdwdsUnits: TXDataWebDataSet
Connection = DMConnection.ApiConnection
Left = 260
Top = 412
object xdwdsUnitsDistrictHeader: TStringField
FieldName = 'DistrictHeader'
end
object xdwdsUnitsUnitId: TStringField
FieldName = 'UnitId'
end
object xdwdsUnitsUnitName: TStringField
FieldName = 'UnitName'
end
object xdwdsUnitsLocation: TStringField
FieldName = 'Location'
end
object xdwdsUnitsStatus: TStringField
FieldName = 'Status'
end
object xdwdsUnitsOfficer1: TStringField
FieldName = 'Officer1'
end
object xdwdsUnitsOfficer2: TStringField
FieldName = 'Officer2'
end
end
object xdwcUnits: TXDataWebClient
Connection = DMConnection.ApiConnection
Left = 58
Top = 410
end
end
<div class="sticky-top">
<!-- Local navbar (Complaints) -->
<!-- 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>
<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">
<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">
<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">
<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>
......@@ -31,51 +31,33 @@
<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...">
<input id="units_search" class="form-control" placeholder="Search...">
</div>
</div>
</div>
</div> <!-- /sticky-top wrapper -->
<!-- Existing content (unchanged) -->
<div class="row">
<div class="col-12">
<div class="container mt-4">
<div class="row justify-content-center">
<div class="col-12 col-md-10 col-lg-8">
<h1 class="page-header pt-3 pb-2 mb-3 border-bottom fs-4 fw-bold" id="view.calls.title">Units</h1>
<!-- Data Table -->
<div class="table-responsive mt-4">
<table class="table table-sm table-striped table-bordered align-middle" id="tblPhoneGrid">
<thead class="table-dark">
<tr>
<th scope="col">Phone Number</th>
<th scope="col">Caller</th>
<th scope="col">Time</th>
<th scope="col">Duration</th>
<th scope="col">Transcript</th>
<th scope="col">Listen</th>
</tr>
</thead>
<tbody>
<!-- Rows added dynamically in Delphi -->
</tbody>
</table>
</div>
<!-- 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 -->
</div>
<!-- Entry Count Label -->
<label id="lblentries" class="mt-2 d-block"></label>
<!-- 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 added in Delphi -->
</ul>
</nav>
</div>
</div>
<!-- Pagination -->
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center" id="pagination">
<!-- Pagination items rendered in Delphi -->
</ul>
</nav>
</div>
</div>
</div>
......@@ -3,13 +3,34 @@ unit View.Units;
interface
uses
System.SysUtils, System.Classes, WEBLib.Graphics, WEBLib.Controls, WEBLib.Forms, WEBLib.Dialogs,
Vcl.Controls, Vcl.StdCtrls,
XData.Web.Connection, WEBLib.StdCtrls;
System.SysUtils, System.Classes, Web, WEBLib.Graphics, WEBLib.Forms, WEBLib.Dialogs,
Vcl.Controls, Vcl.StdCtrls, WEBLib.StdCtrls, WEBLib.Controls, WEBLib.Grids, WebLib.Lists,
XData.Web.Client, WEBLib.ExtCtrls, XData.Web.JsonDataset,
XData.Web.Dataset, XData.Web.Connection, Vcl.Forms, DateUtils, WEBLib.DBCtrls, System.Generics.Collections,
WEBLib.Menus, WEBLib.JSON, Auth.Service, WebLib.Storage, ConnectionModule, App.Types,
WEBLib.DB, Data.DB, XData.Web.DatasetCommon, JS, XData.Model.Classes, Utils;
type
TFViewUnits = class(TWebForm)
dblUnitsList: TWebDBListControl;
btnRefresh: TWebButton;
btnGroup: TWebButton;
btnFilter: TWebButton;
wdsUnits: TWebDataSource;
xdwdsUnits: TXDataWebDataSet;
xdwcUnits: TXDataWebClient;
lblEntries: TWebLabel;
xdwdsUnitsDistrictHeader: TStringField;
xdwdsUnitsUnitId: TStringField;
xdwdsUnitsUnitName: TStringField;
xdwdsUnitsLocation: TStringField;
xdwdsUnitsStatus: TStringField;
xdwdsUnitsOfficer1: TStringField;
xdwdsUnitsOfficer2: TStringField;
procedure WebFormCreate(Sender: TObject);
procedure btnRefreshClick(Sender: TObject);
private
[async] procedure GetUnits;
public
end;
......@@ -21,7 +42,61 @@ implementation
{$R *.dfm}
{ TFViewErrorPage }
procedure TFViewUnits.WebFormCreate(Sender: TObject);
begin
console.log('Units.WebFormCreate: Starting setup...');
DMConnection.ApiConnection.Connected := True;
console.log('API connection active:', DMConnection.ApiConnection.Connected);
GetUnits;
end;
procedure TFViewUnits.btnRefreshClick(Sender: TObject);
begin
GetUnits;
end;
procedure TFViewUnits.GetUnits;
var
xdcResponse: TXDataClientResponse;
respObj: TJSObject;
unitCount: Integer;
begin
console.log('GetUnits: Invoking API...');
Utils.ShowSpinner('spinner');
try
try
xdcResponse := await(xdwcUnits.RawInvokeAsync('IApiService.GetUnitList', []));
console.log('RawInvoke returned:', xdcResponse.Result);
respObj := TJSObject(xdcResponse.Result);
xdwdsUnits.Close;
console.log('Units dataset closed');
xdwdsUnits.SetJsonData(respObj['data']);
console.log('JsonData set on units dataset:', respObj['data']);
xdwdsUnits.Open;
console.log('Units dataset opened. Record count:', xdwdsUnits.RecordCount);
unitCount := Integer(respObj['count']);
lblEntries.Caption := Format('%d units', [unitCount]);
console.log('Units label updated:', lblEntries.Caption);
except
on E: EXDataClientRequestException do
begin
console.log('XData exception (units):', E.ErrorResult.ErrorMessage);
Utils.ShowErrorModal(E.ErrorResult.ErrorMessage);
end;
end;
finally
Utils.HideSpinner('spinner');
console.log('GetUnits complete');
end;
end;
end.
{
"AuthUrl" : "http://localhost:2009/emimobile/auth/",
"ApiUrl" : "http://localhost:2009/emimobile/api/",
"AppUrl" : "http://localhost:2009/emimobile/app/"
}
\ No newline at end of file
.login-card {
display: inline-block;
width: 300px; /* Adjust width as needed */
padding: 0;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
background-color: #fff;
}
.mr-2 {
margin-right: 0.5rem;
}
.table tbody tr:hover {
background-color: #d1e7fd; /* Light blue color for hover effect */
cursor: pointer;
}
.form-input{
display: table;
}
.form-cells{
display: table-cell
}
.table tbody tr {
transition: background-color 0.3s ease;
}
.table {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-radius: 5px;
}
@media (max-width: 1200px) {
.table-responsive {
display: block;
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.table thead {
display: none;
}
.table tbody, .table tr, .table td {
display: block;
width: 100%;
}
.table tr {
margin-bottom: 1rem;
}
.table td {
text-align: right;
padding-left: 50%; /* Adjust padding to accommodate the data-label */
position: relative;
}
.table td::before {
content: attr(data-label);
position: absolute;
left: 0;
width: 50%;
padding-left: 15px; /* Adjust as necessary */
font-weight: bold;
text-align: left;
}
.table td .transcript {
margin-top: 20px; /* Set top margin to 20px */
text-align: left; /* Ensure text alignment is left */
margin-left: 8px;
white-space: normal; /* Prevent text from being cut off */
}
}
.login-navbar {
max-width: 1200px; /* Set the max-width to match a medium screen */
margin: auto;
border-bottom-left-radius: 10px; /* Round the bottom left corner */
border-bottom-right-radius: 10px; /* Round the bottom right corner */
border: 1px solid #d3d3d3;
}
.navbar-toggler {
display: none;
}
.dropdown-menu a {
display: flex; /* Use flexbox for alignment */
align-items: center; /* Vertically center the content */
width: 100%; /* Ensure they take up the full width */
padding: 0.5rem 1rem; /* Add padding to make them clickable */
color: #000; /* Adjust the text color if necessary */
text-decoration: none; /* Remove underlines */
}
.dropdown-menu a:hover {
background-color: #204d74;
color: #fff;
}
.dropdown-menu a span {
flex-grow: 1; /* Make the span take up the remaining space */
}
/* Style for the selected number */
.selected-number .page-link {
background-color: #204d74;
color: #fff !important;
}
/* Style for the unselected numbers and text (previous/next) */
.pagination .page-item a,
.pagination .page-item span {
color: #204d74;
}
.pagination .page-item.active .page-link,
.pagination .page-item.active .page-link:hover,
.pagination .page-item.active .page-link:focus {
background-color: #204d74;
border-color: #204d74;
color: #fff !important;
}
/* This is needed to get rid of the line that was appearing. */
span.card {
border: none;
}
.modal-backdrop {
z-index: 1040 !important;
}
.modal {
z-index: 1055 !important;
}
.list-section-header:empty { display: none; }
.lds-roller {
display: inline-block;
position: relative;
width: 80px;
height: 80px;
}
.lds-roller div {
animation: lds-roller 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
transform-origin: 40px 40px;
}
.lds-roller div:after {
content: " ";
display: block;
position: absolute;
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--bs-primary);
margin: -5px 0 0 -5px;
}
.lds-roller div:nth-child(1) {
animation-delay: -0.036s;
}
.lds-roller div:nth-child(1):after {
top: 63px;
left: 63px;
}
.lds-roller div:nth-child(2) {
animation-delay: -0.072s;
}
.lds-roller div:nth-child(2):after {
top: 68px;
left: 56px;
}
.lds-roller div:nth-child(3) {
animation-delay: -0.108s;
}
.lds-roller div:nth-child(3):after {
top: 71px;
left: 48px;
}
.lds-roller div:nth-child(4) {
animation-delay: -0.144s;
}
.lds-roller div:nth-child(4):after {
top: 72px;
left: 40px;
}
.lds-roller div:nth-child(5) {
animation-delay: -0.18s;
}
.lds-roller div:nth-child(5):after {
top: 71px;
left: 32px;
}
.lds-roller div:nth-child(6) {
animation-delay: -0.216s;
}
.lds-roller div:nth-child(6):after {
top: 68px;
left: 24px;
}
.lds-roller div:nth-child(7) {
animation-delay: -0.252s;
}
.lds-roller div:nth-child(7):after {
top: 63px;
left: 17px;
}
.lds-roller div:nth-child(8) {
animation-delay: -0.288s;
}
.lds-roller div:nth-child(8):after {
top: 56px;
left: 12px;
}
@keyframes lds-roller {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
<noscript>Your browser does not support JavaScript!</noscript>
<link href="data:;base64,=" rel="icon"/>
<title>emiMobile</title>
<script crossorigin="anonymous" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" src="https://code.jquery.com/jquery-3.5.1.min.js" type="text/javascript"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/2.3.1/css/flag-icon.min.css" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.0/css/all.min.css" rel="stylesheet"/>
<link href="css/app.css" rel="stylesheet" type="text/css"/>
<link href="css/spinner.css" rel="stylesheet" type="text/css"/>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/js/bootstrap.bundle.min.js" type="text/javascript"></script>
<link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" type="text/javascript"></script>
<link href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" rel="stylesheet"/>
<script src="$(ProjectName).js" type="text/javascript"></script>
<style>
</style>
</head>
<body>
<script type="text/javascript">rtl.run();</script>
</body>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
<noscript>Your browser does not support JavaScript!</noscript>
<link href="data:;base64,=" rel="icon"/>
<title>emiMobile</title>
<!-- jQuery -->
<script crossorigin="anonymous" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" src="https://code.jquery.com/jquery-3.5.1.min.js" type="text/javascript"></script>
<!-- Icons & fonts -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/2.3.1/css/flag-icon.min.css" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.0/css/all.min.css" rel="stylesheet"/>
<!-- Bootstrap -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/js/bootstrap.bundle.min.js" type="text/javascript"></script>
<link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet"/>
<!-- Leaflet -->
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" type="text/javascript"></script>
<link href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" rel="stylesheet"/>
<!-- App bundle -->
<script src="$(ProjectName).js" type="text/javascript"></script>
<!-- App styles -->
<style></style>
<link href="css/app.css" rel="stylesheet"/>
<link href="css/spinner.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<script type="text/javascript">rtl.run();</script>
</body>
</html>
......@@ -177,6 +177,7 @@
</DCCReference>
<None Include="index.html"/>
<None Include="css\app.css"/>
<None Include="css\spinner.css"/>
<BuildConfiguration Include="Base">
<Key>Base</Key>
</BuildConfiguration>
......@@ -219,6 +220,12 @@
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="css\spinner.css" Configuration="Debug" Class="ProjectFile">
<Platform Name="Win32">
<RemoteDir>.\</RemoteDir>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="index.html" Configuration="Debug" Class="ProjectFile"/>
<DeployFile LocalName="index.html" Configuration="Debug" Class="ProjectFile">
<Platform Name="Win32">
......
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