Commit 546299f9 by Mac Stephens

Merge remote-tracking branch 'origin/master'

parents 3ddb7957 aa8dc61b
...@@ -211,11 +211,13 @@ type ...@@ -211,11 +211,13 @@ type
procedure UniAlerter1Event(Sender: TDAAlerter; const EventName, procedure UniAlerter1Event(Sender: TDAAlerter; const EventName,
Message: string); Message: string);
private private
{ Private declarations } { Private declarations }
public public
CADUpdate: Boolean; CADUpdate: Boolean;
function HandleUniqueFilenames(const category: string): string; function HandleUniqueFilenames(const category: string): string;
function BadgeCounts(const BaseQuery: TUniQuery): Integer; function BadgeCounts(const BaseQuery: TUniQuery): Integer;
function EnsureConnected: Boolean;
end; end;
var var
...@@ -240,7 +242,18 @@ begin ...@@ -240,7 +242,18 @@ begin
ucENTCAD.Password := IniEntries.DatabasePassword; ucENTCAD.Password := IniEntries.DatabasePassword;
ucENTCAD.LoginPrompt := False; ucENTCAD.LoginPrompt := False;
EnsureConnected;
end;
function TApiDatabaseModule.EnsureConnected: Boolean;
begin
Result := False;
try
if not ucENTCAD.Connected then if not ucENTCAD.Connected then
begin
Logger.Log(2, 'Connecting to PostgreSQL API database...');
ucENTCAD.Connect; ucENTCAD.Connect;
ucENTCAD.ExecSQL('set search_path to lems, avl, entcad, public'); ucENTCAD.ExecSQL('set search_path to lems, avl, entcad, public');
...@@ -249,6 +262,15 @@ begin ...@@ -249,6 +262,15 @@ begin
Logger.Log(1, 'Starting PostgreSQL disupdate listener'); Logger.Log(1, 'Starting PostgreSQL disupdate listener');
UniAlerter1.Start; UniAlerter1.Start;
Logger.Log(1, 'PostgreSQL disupdate listener started'); Logger.Log(1, 'PostgreSQL disupdate listener started');
CADUpdate := True;
end;
Result := True;
except
on E: Exception do
Logger.Log(1, 'PostgreSQL API database unavailable: ' + E.Message);
end;
end; end;
procedure TApiDatabaseModule.uqComplaintListCalcFields(DataSet: TDataSet); procedure TApiDatabaseModule.uqComplaintListCalcFields(DataSet: TDataSet);
......
...@@ -14,9 +14,11 @@ type ...@@ -14,9 +14,11 @@ type
strict private strict private
ApiDB: TApiDatabaseModule; ApiDB: TApiDatabaseModule;
private private
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
public public
constructor Create;
destructor Destroy; override;
function GetBadgeCounts: TJSONObject; function GetBadgeCounts: TJSONObject;
function GetComplaintList: TJSONObject; function GetComplaintList: TJSONObject;
function GetUnitList: TJSONObject; function GetUnitList: TJSONObject;
...@@ -37,21 +39,35 @@ implementation ...@@ -37,21 +39,35 @@ implementation
uses uses
uLibrary; uLibrary;
procedure TApiService.AfterConstruction;
constructor TApiService.Create;
begin begin
inherited; Logger.Log(3, 'TApiService.Create');
inherited Create;
ApiDB := TApiDatabaseModule.Create(nil); ApiDB := TApiDatabaseModule.Create(nil);
if not ApiDB.ucENTCAD.Connected then
begin
Logger.Log(1, 'Unable to connect to API database');
raise EXDataHttpException.Create(
500,
'Unable to connect to the database. Please contact EM Systems support.'
);
end;
Logger.Log(3, 'ApiDatabaseModule created'); Logger.Log(3, 'ApiDatabaseModule created');
end; end;
procedure TApiService.BeforeDestruction; destructor TApiService.Destroy;
begin begin
ApiDB.Free; ApiDB.Free;
inherited;
Logger.Log(3, 'ApiDatabaseModule destroyed'); Logger.Log(3, 'ApiDatabaseModule destroyed');
inherited Destroy;
end; end;
function TApiService.GetBadgeCounts: TJSONObject; function TApiService.GetBadgeCounts: TJSONObject;
begin begin
Logger.Log(3, '---TApiService.GetBadgeCounts initiated'); Logger.Log(3, '---TApiService.GetBadgeCounts initiated');
......
...@@ -55,10 +55,7 @@ begin ...@@ -55,10 +55,7 @@ begin
ucLemsOCSO.Connect; ucLemsOCSO.Connect;
except except
on E: Exception do on E: Exception do
begin
Logger.Log(2, Format('Failed to connect to auth database: %s', [E.Message])); Logger.Log(2, Format('Failed to connect to auth database: %s', [E.Message]));
raise;
end;
end; end;
end; end;
......
...@@ -21,12 +21,13 @@ type ...@@ -21,12 +21,13 @@ type
userBadge: string; userBadge: string;
userId: string; userId: string;
userPersonnelId: string; userPersonnelId: string;
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
function VerifyVersion(ClientVersion: string): TJSONObject; function VerifyVersion(ClientVersion: string): TJSONObject;
function CheckUser(const User, Password, Agency: string): Integer; function CheckUser(const User, Password, Agency: string): Integer;
function Decrypt(inStr, keyStr: AnsiString): AnsiString; function Decrypt(inStr, keyStr: AnsiString): AnsiString;
public public
constructor Create;
destructor Destroy; override;
function Login(const User, Password, Agency: string): string; function Login(const User, Password, Agency: string): string;
function GetAgencieslist(): TAgenciesList; function GetAgencieslist(): TAgenciesList;
function GetAgencyConfiglist: TAgencyConfigList; function GetAgencyConfiglist: TAgencyConfigList;
...@@ -46,20 +47,31 @@ uses ...@@ -46,20 +47,31 @@ uses
{ TAuthService } { TAuthService }
procedure TAuthService.AfterConstruction;
constructor TAuthService.Create;
begin begin
inherited; Logger.Log(3, 'TAuthService.Create');
inherited Create;
authDB := TAuthDatabase.Create(nil); authDB := TAuthDatabase.Create(nil);
if not authDB.ucLemsOCSO.Connected then
begin
Logger.Log(1, 'Unable to connect to auth database');
raise EXDataHttpException.Create(500, 'Unable to connect to the database. Please contact EM Systems support.');
end;
Logger.Log(3, 'AuthDatabase created'); Logger.Log(3, 'AuthDatabase created');
end; end;
procedure TAuthService.BeforeDestruction; destructor TAuthService.Destroy;
begin begin
authDB.Free; authDB.Free;
inherited;
Logger.Log(3, 'AuthDatabase destroyed'); Logger.Log(3, 'AuthDatabase destroyed');
inherited Destroy;
end; end;
function TAuthService.GetAgenciesList: TAgenciesList; function TAuthService.GetAgenciesList: TAgenciesList;
var var
agency: TAgencyItem; agency: TAgencyItem;
...@@ -194,7 +206,6 @@ var ...@@ -194,7 +206,6 @@ var
JWT: TJWT; JWT: TJWT;
begin begin
Logger.Log(1, Format('AuthService.Login - User: "%s" Agency: "%s"', [User, Agency])); Logger.Log(1, Format('AuthService.Login - User: "%s" Agency: "%s"', [User, Agency]));
userState := CheckUser(User, Password, Agency);
try try
userState := CheckUser(User, Password, Agency); userState := CheckUser(User, Password, Agency);
......
...@@ -148,7 +148,7 @@ begin ...@@ -148,7 +148,7 @@ begin
Result := ''; Result := '';
Msg := TStringList.Create; Msg := TStringList.Create;
try try
Msg.Add(Format('%s %s %s', Msg.Add(Format('%s %s %s %s',
[ [
FMethod, FMethod,
FUriPath + FUriQuery, FUriPath + FUriQuery,
......
...@@ -2,8 +2,8 @@ object FMain: TFMain ...@@ -2,8 +2,8 @@ object FMain: TFMain
Left = 0 Left = 0
Top = 0 Top = 0
Caption = 'emiMobileServer' Caption = 'emiMobileServer'
ClientHeight = 616 ClientHeight = 773
ClientWidth = 772 ClientWidth = 778
Color = clBtnFace Color = clBtnFace
Font.Charset = DEFAULT_CHARSET Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText Font.Color = clWindowText
...@@ -12,29 +12,31 @@ object FMain: TFMain ...@@ -12,29 +12,31 @@ object FMain: TFMain
Font.Style = [] Font.Style = []
OnClose = FormClose OnClose = FormClose
DesignSize = ( DesignSize = (
772 778
616) 773)
TextHeight = 13 TextHeight = 13
object pgcMain: TPageControl object pgcMain: TPageControl
Left = 8 Left = 0
Top = 39 Top = 40
Width = 756 Width = 778
Height = 575 Height = 733
ActivePage = tabConnectedClients ActivePage = tabServerLog
Align = alBottom
Anchors = [akLeft, akTop, akRight, akBottom] Anchors = [akLeft, akTop, akRight, akBottom]
TabOrder = 3 TabOrder = 3
ExplicitHeight = 575
object tabServerLog: TTabSheet object tabServerLog: TTabSheet
Caption = 'Server Log' Caption = 'Server Log'
object memoInfo: TMemo object memoInfo: TMemo
Left = 0 Left = 0
Top = 0 Top = 0
Width = 748 Width = 770
Height = 547 Height = 705
Align = alClient Align = alClient
ReadOnly = True
ScrollBars = ssVertical ScrollBars = ssVertical
TabOrder = 0 TabOrder = 0
WordWrap = False WordWrap = False
ExplicitHeight = 547
end end
end end
object tabConnectedClients: TTabSheet object tabConnectedClients: TTabSheet
...@@ -43,8 +45,8 @@ object FMain: TFMain ...@@ -43,8 +45,8 @@ object FMain: TFMain
object grdConnectedClients: TDBGrid object grdConnectedClients: TDBGrid
Left = 0 Left = 0
Top = 0 Top = 0
Width = 748 Width = 770
Height = 487 Height = 645
Align = alClient Align = alClient
DataSource = dsConnectedClients DataSource = dsConnectedClients
Options = [dgTitles, dgIndicator, dgColumnResize, dgColLines, dgRowLines, dgTabs, dgRowSelect, dgConfirmDelete, dgCancelOnExit, dgTitleClick, dgTitleHotTrack] Options = [dgTitles, dgIndicator, dgColumnResize, dgColLines, dgRowLines, dgTabs, dgRowSelect, dgConfirmDelete, dgCancelOnExit, dgTitleClick, dgTitleHotTrack]
...@@ -75,13 +77,14 @@ object FMain: TFMain ...@@ -75,13 +77,14 @@ object FMain: TFMain
end end
object pnlConnectedClientsActions: TPanel object pnlConnectedClientsActions: TPanel
Left = 0 Left = 0
Top = 487 Top = 645
Width = 748 Width = 770
Height = 60 Height = 60
Align = alBottom Align = alBottom
Caption = 'pnlConnectedClientsActions' Caption = 'pnlConnectedClientsActions'
ShowCaption = False ShowCaption = False
TabOrder = 1 TabOrder = 1
ExplicitTop = 487
object btnDisconnectClient: TButton object btnDisconnectClient: TButton
Left = 5 Left = 5
Top = 18 Top = 18
...@@ -120,7 +123,7 @@ object FMain: TFMain ...@@ -120,7 +123,7 @@ object FMain: TFMain
OnClick = btnApiSwaggerUIClick OnClick = btnApiSwaggerUIClick
end end
object btnExit: TButton object btnExit: TButton
Left = 713 Left = 695
Top = 8 Top = 8
Width = 75 Width = 75
Height = 25 Height = 25
......
...@@ -71,6 +71,9 @@ end; ...@@ -71,6 +71,9 @@ end;
procedure TWsDataModel.TimerFire(Sender: TObject); procedure TWsDataModel.TimerFire(Sender: TObject);
begin begin
if not FDb.ucENTCAD.Connected then
Exit;
if not FDb.CADUpdate then if not FDb.CADUpdate then
Exit; Exit;
...@@ -80,12 +83,13 @@ end; ...@@ -80,12 +83,13 @@ end;
procedure TWsDataModel.BroadcastAll; procedure TWsDataModel.BroadcastAll;
begin begin
Logger.Log(3, 'WsDataModel: broadcasting all'); Logger.Log(5, 'WsDataModel: BroadcastAll initiated');
try FBroadcast(BuildBadgeCountsJson); except on E: Exception do Logger.Log(2, 'WsDataModel BADGE_COUNTS error: ' + E.Message); end; try FBroadcast(BuildBadgeCountsJson); except on E: Exception do Logger.Log(2, 'WsDataModel BADGE_COUNTS error: ' + E.Message); end;
try FBroadcast(BuildUnitMapJson); except on E: Exception do Logger.Log(2, 'WsDataModel UNIT_MAP error: ' + E.Message); end; try FBroadcast(BuildUnitMapJson); except on E: Exception do Logger.Log(2, 'WsDataModel UNIT_MAP error: ' + E.Message); end;
try FBroadcast(BuildComplaintMapJson); except on E: Exception do Logger.Log(2, 'WsDataModel COMPLAINT_MAP error: ' + E.Message); end; try FBroadcast(BuildComplaintMapJson); except on E: Exception do Logger.Log(2, 'WsDataModel COMPLAINT_MAP error: ' + E.Message); end;
try FBroadcast(BuildUnitListJson); except on E: Exception do Logger.Log(2, 'WsDataModel UNIT_LIST error: ' + E.Message); end; try FBroadcast(BuildUnitListJson); except on E: Exception do Logger.Log(2, 'WsDataModel UNIT_LIST error: ' + E.Message); end;
try FBroadcast(BuildComplaintListJson); except on E: Exception do Logger.Log(2, 'WsDataModel COMPLAINT_LIST error: ' + E.Message); end; try FBroadcast(BuildComplaintListJson); except on E: Exception do Logger.Log(2, 'WsDataModel COMPLAINT_LIST error: ' + E.Message); end;
Logger.Log(5, 'WsDataModel: BroadcastAll end');
end; end;
function TWsDataModel.BuildBadgeCountsJson: string; function TWsDataModel.BuildBadgeCountsJson: string;
......
[Settings] [Settings]
LogFileNum=105 LogFileNum=144
webClientVersion=9.4.0 webClientVersion=0.9.4.1
[Database] [Database]
--Server=192.168.102.10 --Server=192.168.91.136
Server=192.168.56.129 Server=192.168.102.10
--Server=192.168.56.129
Port=5432 Port=5432
--Port=5433 --Port=5433
Database=lems_wcso Database=lems_wcso
Username=postgres Username=postgres
--Password=postgreSQL --Password=postgreSQL
Password=emsys01 Password=emsys01
--Postgre!SQL
{
"url": "http://localhost:2009/emiMobile/",
"jwtTokenSecret": "super_secret0123super_secret4567",
"adminPassword": "whatisthisusedfor?",
"webAppFolder": "static",
"memoLogLevel": 5,
"fileLogLevel": 5
}
\ No newline at end of file
...@@ -108,8 +108,9 @@ ...@@ -108,8 +108,9 @@
<VerInfo_MajorVer>0</VerInfo_MajorVer> <VerInfo_MajorVer>0</VerInfo_MajorVer>
<VerInfo_MinorVer>9</VerInfo_MinorVer> <VerInfo_MinorVer>9</VerInfo_MinorVer>
<VerInfo_Release>4</VerInfo_Release> <VerInfo_Release>4</VerInfo_Release>
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=0.9.4.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=0.9.2.0;Comments=</VerInfo_Keys> <VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=0.9.4.1;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=0.9.2.0;Comments=</VerInfo_Keys>
<DCC_UnitSearchPath>C:\RADTOOLS\FastMM4;$(DCC_UnitSearchPath)</DCC_UnitSearchPath> <DCC_UnitSearchPath>C:\RADTOOLS\FastMM4;$(DCC_UnitSearchPath)</DCC_UnitSearchPath>
<VerInfo_Build>1</VerInfo_Build>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1_Win64)'!=''"> <PropertyGroup Condition="'$(Cfg_1_Win64)'!=''">
<AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode> <AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode>
......
...@@ -11,7 +11,6 @@ type ...@@ -11,7 +11,6 @@ type
TSuccessProc = reference to procedure; TSuccessProc = reference to procedure;
TLogoutProc = reference to procedure(AMessage: string = ''); TLogoutProc = reference to procedure(AMessage: string = '');
TUnauthorizedAccessProc = reference to procedure(AMessage: string); TUnauthorizedAccessProc = reference to procedure(AMessage: string);
TVersionCheckCallback = reference to procedure(Success: Boolean; ErrorMessage: string);
TListProc = reference to procedure; TListProc = reference to procedure;
TSelectProc = reference to procedure(AParam: string); TSelectProc = reference to procedure(AParam: string);
......
...@@ -4,7 +4,7 @@ interface ...@@ -4,7 +4,7 @@ interface
uses uses
System.SysUtils, System.Classes, WEBLib.Modules, XData.Web.Connection, System.SysUtils, System.Classes, WEBLib.Modules, XData.Web.Connection,
App.Types, App.Config, XData.Web.Client; XData.Web.Client, App.Types, App.Config;
type type
TDMConnection = class(TWebDataModule) TDMConnection = class(TWebDataModule)
...@@ -18,12 +18,12 @@ type ...@@ -18,12 +18,12 @@ type
private private
FUnauthorizedAccessProc: TUnauthorizedAccessProc; FUnauthorizedAccessProc: TUnauthorizedAccessProc;
FWsUrl: string; FWsUrl: string;
procedure VerifyVersion(SuccessProc: TSuccessProc);
public public
property WsUrl: string read FWsUrl; property WsUrl: string read FWsUrl;
const clientVersion = '9.4.0'; const clientVersion = '0.9.4.1';
procedure InitApp(SuccessProc: TSuccessProc; procedure InitApp(SuccessProc: TSuccessProc;
UnauthorizedAccessProc: TUnauthorizedAccessProc); UnauthorizedAccessProc: TUnauthorizedAccessProc);
procedure SetClientConfig(Callback: TVersionCheckCallback);
end; end;
var var
...@@ -57,7 +57,8 @@ procedure TDMConnection.ApiConnectionResponse( ...@@ -57,7 +57,8 @@ procedure TDMConnection.ApiConnectionResponse(
Args: TXDataWebConnectionResponse); Args: TXDataWebConnectionResponse);
begin begin
if Args.Response.StatusCode = 401 then if Args.Response.StatusCode = 401 then
FUnauthorizedAccessProc(Format('%d: %s',[Args.Response.StatusCode, Args.Response.ContentAsText])); FUnauthorizedAccessProc(Format('%d: %s',
[Args.Response.StatusCode, Args.Response.ContentAsText]));
end; end;
procedure TDMConnection.AuthConnectionError(Error: TXDataWebConnectionError); procedure TDMConnection.AuthConnectionError(Error: TXDataWebConnectionError);
...@@ -79,7 +80,11 @@ procedure TDMConnection.InitApp(SuccessProc: TSuccessProc; ...@@ -79,7 +80,11 @@ procedure TDMConnection.InitApp(SuccessProc: TSuccessProc;
if Config.WsUrl <> '' then if Config.WsUrl <> '' then
FWsUrl := Config.WsUrl; FWsUrl := Config.WsUrl;
AuthConnection.Open(SuccessProc); AuthConnection.Open(
procedure
begin
VerifyVersion(SuccessProc);
end);
end; end;
begin begin
...@@ -87,8 +92,7 @@ begin ...@@ -87,8 +92,7 @@ begin
LoadConfig(@ConfigLoaded); LoadConfig(@ConfigLoaded);
end; end;
procedure TDMConnection.SetClientConfig(Callback: TVersionCheckCallback); procedure TDMConnection.VerifyVersion(SuccessProc: TSuccessProc);
begin begin
XDataWebClient1.Connection := AuthConnection; XDataWebClient1.Connection := AuthConnection;
...@@ -100,15 +104,19 @@ begin ...@@ -100,15 +104,19 @@ begin
begin begin
jsonResult := TJSObject(Response.Result); jsonResult := TJSObject(Response.Result);
if jsonResult.HasOwnProperty('error') then if Assigned(jsonResult) and jsonResult.HasOwnProperty('error') then
error := string(jsonResult['error']) error := string(jsonResult['error'])
else else
error := ''; error := '';
if error <> '' then if error <> '' then
Callback(False, error) TFViewErrorPage.Display(error)
else else
Callback(True, ''); SuccessProc;
end,
procedure(Error: TXDataClientError)
begin
TFViewErrorPage.Display(Error.ErrorMessage);
end); end);
end; end;
......
...@@ -15,6 +15,7 @@ function FormatPhoneNumber(PhoneNumber: string): string; ...@@ -15,6 +15,7 @@ function FormatPhoneNumber(PhoneNumber: string): string;
procedure ApplyReportTitle(CurrentReportType: string); procedure ApplyReportTitle(CurrentReportType: string);
procedure ShowToast(const MessageText: string; const ToastType: string = 'success'); procedure ShowToast(const MessageText: string; const ToastType: string = 'success');
procedure ShowConfirmationModal(msg, leftLabel, rightLabel: string; ConfirmProc: TProc<Boolean>); procedure ShowConfirmationModal(msg, leftLabel, rightLabel: string; ConfirmProc: TProc<Boolean>);
procedure ShowInformationModal(const Title, MessageText: string);
// function FormatDollarValue(ValueStr: string): string; // function FormatDollarValue(ValueStr: string): string;
...@@ -220,6 +221,28 @@ begin ...@@ -220,6 +221,28 @@ begin
end; end;
procedure ShowInformationModal(const Title, MessageText: string);
begin
asm
var modal = document.getElementById('main_information_modal');
var title = document.getElementById('main_information_modal_title');
var body = document.getElementById('main_information_modal_body');
if (!modal) return;
if (title) title.innerText = Title;
if (body) body.innerText = MessageText;
if (modal.parentNode !== document.body) {
document.body.appendChild(modal);
}
var bsModal = bootstrap.Modal.getOrCreateInstance(modal);
bsModal.show();
end;
end;
function CalculateAge(DateOfBirth: TDateTime): Integer; function CalculateAge(DateOfBirth: TDateTime): Integer;
var var
Today, BirthDate: TJSDate; Today, BirthDate: TJSDate;
......
<div class="container"> <div class="container">
<br /> <div class="row justify-content-center mt-5">
<div class="col-12 col-sm-10 col-md-8 col-lg-6">
<div class="card shadow-sm border-danger">
<div class="panel panel-red"> <div id="view.errorpage.title"
<div id="view.errorpage.title" class="panel-heading"> class="card-header bg-danger text-white fw-semibold">
Error Page Error
</div> </div>
<div id="view.errorpage.message" class="panel-body"> <div class="card-body">
<div id="view.errorpage.message"
class="text-danger">
Message Message
</div> </div>
</div>
<div class="card-footer bg-light text-end">
<a href="."
class="btn btn-outline-danger btn-sm">
Reload web application
</a>
</div>
<div class="panel-footer"> </div>
<a href=".">Reload web application</a>
</div> </div>
</div> </div>
</div> </div>
...@@ -52,8 +52,9 @@ ...@@ -52,8 +52,9 @@
Login Login
</button> </button>
</div> </div>
<div class="card-footer text-muted small"> <div class="card-footer text-muted small d-flex justify-content-between">
Please use your lems username &amp; password to login. <span>Please use your lems username &amp; password to login.</span>
<span id="view.login.version" class="opacity-75"></span>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -4,7 +4,7 @@ interface ...@@ -4,7 +4,7 @@ interface
uses uses
System.SysUtils, System.Classes, WEBLib.Graphics, WEBLib.Controls, WEBLib.Forms, WEBLib.Dialogs, System.SysUtils, System.Classes, WEBLib.Graphics, WEBLib.Controls, WEBLib.Forms, WEBLib.Dialogs,
Vcl.Controls, Vcl.StdCtrls, WEBLib.StdCtrls, WEBLib.JSON, Vcl.Controls, Vcl.StdCtrls, WEBLib.StdCtrls, WEBLib.JSON, Web,
JS, XData.Web.Connection, WEBLib.ExtCtrls, JS, XData.Web.Connection, WEBLib.ExtCtrls,
App.Types, ConnectionModule, XData.Web.Client; App.Types, ConnectionModule, XData.Web.Client;
...@@ -65,8 +65,12 @@ end; ...@@ -65,8 +65,12 @@ end;
procedure TFViewLogin.WebFormCreate(Sender: TObject); procedure TFViewLogin.WebFormCreate(Sender: TObject);
var
el: TJSElement;
begin begin
// lblAppTitle.Caption := 'EM Systems - webCharms App ver 0.9.2.22'; el := Document.getElementById('view.login.version');
if Assigned(el) then
TJSHtmlElement(el).innerText := 'v' + TDMConnection.clientVersion;
GetAgencyConfigList(); GetAgencyConfigList();
if FMessage <> '' then if FMessage <> '' then
ShowNotification(FMessage) ShowNotification(FMessage)
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
<div class="d-flex align-items-center gap-2 ms-auto"> <div class="d-flex align-items-center gap-2 ms-auto">
<span id="view.main.lblconnection" class="navbar-text text-light small"></span> <span id="view.main.lblconnection" class="navbar-text text-light small"></span>
<span id="view.main.version" class="navbar-text text-light small opacity-75"></span>
<button id="btn_logout" type="button" class="btn btn-outline-light btn-sm">Logout</button> <button id="btn_logout" type="button" class="btn btn-outline-light btn-sm">Logout</button>
</div> </div>
</div> </div>
...@@ -78,6 +79,23 @@ ...@@ -78,6 +79,23 @@
</nav> </nav>
</div> </div>
<!-- Information modal -->
<div class="modal fade" id="main_information_modal" tabindex="-1"
aria-labelledby="main_information_modal_title" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-sm mx-auto px-3">
<div class="modal-content shadow-lg">
<div class="modal-header">
<h5 class="modal-title" id="main_information_modal_title">Information</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body fs-6 fw-bold" id="main_information_modal_body"></div>
<div class="modal-footer">
<button type="button" class="btn btn-primary w-100" data-bs-dismiss="modal">OK</button>
</div>
</div>
</div>
</div>
<!-- Spinner --> <!-- Spinner -->
<div id="spinner" class="position-absolute top-50 start-50 translate-middle d-none"> <div id="spinner" class="position-absolute top-50 start-50 translate-middle d-none">
<div class="lds-roller"> <div class="lds-roller">
......
...@@ -121,9 +121,15 @@ const ...@@ -121,9 +121,15 @@ const
procedure TFViewMain.WebFormCreate(Sender: TObject); procedure TFViewMain.WebFormCreate(Sender: TObject);
var var
userName: string; userName: string;
el: TJSElement;
begin begin
userName := JS.toString(AuthService.TokenPayload.Properties['user_name']); userName := JS.toString(AuthService.TokenPayload.Properties['user_name']);
lblUsername.Caption := ' ' + userName.ToLower + ' '; lblUsername.Caption := ' ' + userName.ToLower + ' ';
el := Document.getElementById('view.main.version');
if Assigned(el) then
TJSHtmlElement(el).innerText := 'v' + TDMConnection.clientVersion;
FChildForm := nil; FChildForm := nil;
FDetailsForm := nil; FDetailsForm := nil;
FArchiveForm := nil; FArchiveForm := nil;
......
...@@ -37,6 +37,9 @@ type ...@@ -37,6 +37,9 @@ type
FPendingFocusCoord: TTMSFNCMapsCoordinateRec; FPendingFocusCoord: TTMSFNCMapsCoordinateRec;
FPendingFocusZoom: Integer; FPendingFocusZoom: Integer;
FDoFocusZoom: Boolean; FDoFocusZoom: Boolean;
FPendingFocusMarkerData: string;
FPendingWsUnitMapData: TJSArray;
FPendingWsComplaintMapData: TJSArray;
FGeoJsonLoadStep: Integer; FGeoJsonLoadStep: Integer;
[async] procedure LoadPointsAsync(showBusy: Boolean); [async] procedure LoadPointsAsync(showBusy: Boolean);
...@@ -307,6 +310,22 @@ begin ...@@ -307,6 +310,22 @@ begin
Console.Log('Complaints XData error: ' + E.ErrorResult.ErrorMessage); Console.Log('Complaints XData error: ' + E.ErrorResult.ErrorMessage);
end; end;
// A WebSocket snapshot received while the HTTP requests were in flight is
// newer than those responses, so use the latest pushed data for the map.
if Assigned(FPendingWsUnitMapData) then
begin
unitsData := FPendingWsUnitMapData;
FPendingWsUnitMapData := nil;
FUnitsLoaded := True;
end;
if Assigned(FPendingWsComplaintMapData) then
begin
complaintsData := FPendingWsComplaintMapData;
FPendingWsComplaintMapData := nil;
FComplaintsLoaded := True;
end;
// --- Place markers (BeginUpdate wraps both so the map redraws once) ------ // --- Place markers (BeginUpdate wraps both so the map redraws once) ------
lfMap.BeginUpdate; lfMap.BeginUpdate;
try try
...@@ -553,8 +572,13 @@ end; ...@@ -553,8 +572,13 @@ end;
procedure TFViewMap.ApplyWsUnitMapData(aData: TJSArray); procedure TFViewMap.ApplyWsUnitMapData(aData: TJSArray);
begin begin
// Skip if the map is still initialising or an HTTP load is in flight. if FLoadingPoints then
if (not Assigned(mapFilters)) or FLoadingPoints then begin
FPendingWsUnitMapData := aData;
Exit;
end;
if not Assigned(mapFilters) then
Exit; Exit;
lfMap.BeginUpdate; lfMap.BeginUpdate;
...@@ -570,7 +594,13 @@ end; ...@@ -570,7 +594,13 @@ end;
procedure TFViewMap.ApplyWsComplaintMapData(aData: TJSArray); procedure TFViewMap.ApplyWsComplaintMapData(aData: TJSArray);
begin begin
if (not Assigned(mapFilters)) or FLoadingPoints then if FLoadingPoints then
begin
FPendingWsComplaintMapData := aData;
Exit;
end;
if not Assigned(mapFilters) then
Exit; Exit;
lfMap.BeginUpdate; lfMap.BeginUpdate;
...@@ -641,15 +671,47 @@ begin ...@@ -641,15 +671,47 @@ begin
end; end;
procedure TFViewMap.tmrLocateTimer(Sender: TObject); procedure TFViewMap.tmrLocateTimer(Sender: TObject);
var
i: Integer;
markerFound: Boolean;
missingTarget: string;
begin begin
tmrLocate.Enabled := False; tmrLocate.Enabled := False;
if not FDoFocusZoom then if not FDoFocusZoom then
Exit; Exit;
if FPendingFocusMarkerData <> '' then
begin
markerFound := False;
for i := 0 to lfMap.Markers.Count - 1 do
if SameText(lfMap.Markers[i].DataString, FPendingFocusMarkerData) or
StartsText(FPendingFocusMarkerData + '|', lfMap.Markers[i].DataString) then
begin
markerFound := True;
Break;
end;
if not markerFound then
begin
missingTarget := FPendingFocusMarkerData;
FPendingFocusMarkerData := '';
FDoFocusZoom := False;
if StartsText('unit|', missingTarget) then
ShowInformationModal('No Longer Available',
'This unit is no longer available on the map.')
else if StartsText('complaint|', missingTarget) then
ShowInformationModal('No Longer Available',
'This complaint is no longer active on the map.');
Exit;
end;
end;
lfMap.SetCenterCoordinate(FPendingFocusCoord); lfMap.SetCenterCoordinate(FPendingFocusCoord);
lfMap.SetZoomLevel(FPendingFocusZoom); lfMap.SetZoomLevel(FPendingFocusZoom);
FPendingFocusMarkerData := '';
FDoFocusZoom := False; FDoFocusZoom := False;
end; end;
...@@ -666,6 +728,7 @@ begin ...@@ -666,6 +728,7 @@ begin
FPendingFocusCoord := coord; FPendingFocusCoord := coord;
FPendingFocusZoom := 17; FPendingFocusZoom := 17;
FDoFocusZoom := True; FDoFocusZoom := True;
FPendingFocusMarkerData := '';
tmrLocate.Interval := 250; tmrLocate.Interval := 250;
tmrLocate.Enabled := True; tmrLocate.Enabled := True;
...@@ -674,6 +737,10 @@ end; ...@@ -674,6 +737,10 @@ end;
procedure TFViewMap.FocusUnit(const unitId: string); procedure TFViewMap.FocusUnit(const unitId: string);
begin begin
tmrLocate.Enabled := False;
FDoFocusZoom := False;
FPendingFocusMarkerData := '';
FPendingComplaintId := '';
FPendingUnitId := Trim(unitId); FPendingUnitId := Trim(unitId);
if mapFilters <> nil then if mapFilters <> nil then
LoadPointsAsync(True); LoadPointsAsync(True);
...@@ -682,6 +749,10 @@ end; ...@@ -682,6 +749,10 @@ end;
procedure TFViewMap.FocusComplaint(const complaintId: string); procedure TFViewMap.FocusComplaint(const complaintId: string);
begin begin
tmrLocate.Enabled := False;
FDoFocusZoom := False;
FPendingFocusMarkerData := '';
FPendingUnitId := '';
FPendingComplaintId := Trim(complaintId); FPendingComplaintId := Trim(complaintId);
if mapFilters <> nil then if mapFilters <> nil then
LoadPointsAsync(True); LoadPointsAsync(True);
...@@ -713,6 +784,7 @@ begin ...@@ -713,6 +784,7 @@ begin
FPendingFocusCoord := coord; FPendingFocusCoord := coord;
FPendingFocusZoom := 17; FPendingFocusZoom := 17;
FDoFocusZoom := True; FDoFocusZoom := True;
FPendingFocusMarkerData := targetDs;
tmrLocate.Interval := 250; tmrLocate.Interval := 250;
tmrLocate.Enabled := True; tmrLocate.Enabled := True;
...@@ -722,8 +794,15 @@ begin ...@@ -722,8 +794,15 @@ begin
end; end;
end; end;
if found then
FPendingUnitId := ''; FPendingUnitId := '';
if not found then
begin
FPendingFocusMarkerData := '';
FDoFocusZoom := False;
ShowInformationModal('No Longer Available',
'This unit is no longer available on the map.');
end;
end; end;
procedure TFViewMap.ApplyPendingComplaintFocus; procedure TFViewMap.ApplyPendingComplaintFocus;
...@@ -751,6 +830,7 @@ begin ...@@ -751,6 +830,7 @@ begin
FPendingFocusCoord := coord; FPendingFocusCoord := coord;
FPendingFocusZoom := 17; FPendingFocusZoom := 17;
FDoFocusZoom := True; FDoFocusZoom := True;
FPendingFocusMarkerData := targetDs;
tmrLocate.Interval := 250; tmrLocate.Interval := 250;
tmrLocate.Enabled := True; tmrLocate.Enabled := True;
...@@ -760,8 +840,15 @@ begin ...@@ -760,8 +840,15 @@ begin
end; end;
end; end;
if found then
FPendingComplaintId := ''; FPendingComplaintId := '';
if not found then
begin
FPendingFocusMarkerData := '';
FDoFocusZoom := False;
ShowInformationModal('No Longer Available',
'This complaint is no longer active on the map.');
end;
end; end;
......
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