Commit ccf05c5a by Mac Stephens

Added twebpanel still working on sizing

parent 60a81849
...@@ -8,3 +8,13 @@ emT3webServer/Source/__history/ ...@@ -8,3 +8,13 @@ emT3webServer/Source/__history/
emT3webServer/Win32/Debug/ emT3webServer/Win32/Debug/
emT3webServer/bin/static/ emT3webServer/bin/static/
emT3webClient/__history/
emT3webClient/css/__history/
emT3webClient/Win32/Debug/
*.local
*.identcache
unit App.Config;
interface
uses
JS,
XData.Web.Connection,
XData.Web.Request,
XData.Web.Response;
type
TAppConfig = class
private
FAuthUrl: string;
FApiUrl: string;
FAppUrl: string;
public
constructor Create;
property AuthUrl: string read FAuthUrl write FAuthUrl;
property ApiUrl: string read FApiUrl write FApiUrl;
property AppUrl: string read FAppUrl write FAppUrl;
end;
TConfigLoadedProc = reference to procedure(Config: TAppConfig);
procedure LoadConfig(LoadProc: TConfigLoadedProc);
implementation
procedure LoadConfig(LoadProc: TConfigLoadedProc);
procedure OnSuccess(Response: IHttpResponse);
var
Obj: TJSObject;
Config: TAppConfig;
begin
Config := TAppConfig.Create;
try
if Response.StatusCode = 200 then
begin
Obj := TJSObject(TJSJSON.parse(Response.ContentAsText));
if JS.toString(Obj['AuthUrl']) <> '' then
Config.AuthUrl := JS.toString(Obj['AuthUrl']);
if JS.toString(Obj['ApiUrl']) <> '' then
Config.ApiUrl := JS.toString(Obj['ApiUrl']);
end;
finally
LoadProc(Config);
Config.Free;
end;
end;
procedure OnError;
var
Config: TAppConfig;
begin
Config := TAppConfig.Create;
try
LoadProc(Config);
finally
Config.Free;
end;
end;
var
Conn: TXDataWebConnection;
begin
Conn := TXDataWebConnection.Create(nil);
try
Conn.SendRequest(THttpRequest.Create('config/config.json'), @OnSuccess, @OnError);
finally
Conn.Free;
end;
end;
{ TAppConfig }
constructor TAppConfig.Create;
begin
FAuthUrl := '';
FApiUrl := '';
FAppUrl := '';
end;
end.
unit App.Types;
interface
uses
Bcl.Rtti.Common;
type
TProc = reference to procedure;
TSuccessProc = reference to procedure;
TLogoutProc = reference to procedure(AMessage: string = '');
TUnauthorizedAccessProc = reference to procedure(AMessage: string);
TVersionCheckCallback = reference to procedure(Success: Boolean; ErrorMessage: string);
TListProc = reference to procedure;
TSelectProc = reference to procedure(AParam: string);
TSelectProc2 = reference to procedure(AParam: string; BParam: string);
TSelectProc3 = reference to procedure(AParam: string; BParam: string; CParam: Boolean);
TSelectProc4 = reference to procedure(AParam: string; BParam: string; CParam: string; DParam: Boolean);
TSearchProc = reference to procedure(AParam: string; BParam: string; CParam: Integer; DParam: Boolean);
TReportProc = reference to procedure(AParam: string);
implementation
end.
unit Auth.Service;
interface
uses
SysUtils, Web, JS,
XData.Web.Client;
const
TOKEN_NAME = 'KG_ORDERS_WEB_TOKEN';
type
TOnLoginSuccess = reference to procedure;
TOnLoginError = reference to procedure(AMsg: string);
TOnProfileSuccess = reference to procedure;
TOnProfileError = reference to procedure(AMsg: string);
TAuthService = class
private
FClient: TXDataWebClient;
procedure SetToken(AToken: string);
procedure DeleteToken;
public
constructor Create; reintroduce;
destructor Destroy; override;
procedure Login(AUser, APassword: string; ASuccess: TOnLoginSuccess;
AError: TOnLoginError);
procedure Logout;
function GetToken: string;
function Authenticated: Boolean;
function TokenExpirationDate: TDateTime;
function TokenExpired: Boolean;
function TokenPayload: JS.TJSObject;
end;
TJwtHelper = class
private
class function HasExpirationDate(AToken: string): Boolean;
public
class function TokenExpirationDate(AToken: string): TJSDate;
class function TokenExpired(AToken: string): Boolean;
class function DecodePayload(AToken: string): string;
end;
function AuthService: TAuthService;
implementation
uses
ConnectionModule;
var
_AuthService: TAuthService;
function AuthService: TAuthService;
begin
if not Assigned(_AuthService) then
begin
_AuthService := TAuthService.Create;
end;
Result := _AuthService;
end;
{ TAuthService }
function TAuthService.Authenticated: Boolean;
begin
Result := not isNull(window.localStorage.getItem(TOKEN_NAME)) and
(window.localStorage.getItem(TOKEN_NAME) <> '');
end;
constructor TAuthService.Create;
begin
FClient := TXDataWebClient.Create(nil);
FClient.Connection := DMConnection.AuthConnection;
end;
procedure TAuthService.DeleteToken;
begin
window.localStorage.removeItem(TOKEN_NAME);
end;
destructor TAuthService.Destroy;
begin
FClient.Free;
inherited;
end;
function TAuthService.GetToken: string;
begin
Result := window.localStorage.getItem(TOKEN_NAME);
end;
procedure TAuthService.Login(AUser, APassword: string; ASuccess: TOnLoginSuccess;
AError: TOnLoginError);
procedure OnLoad(Response: TXDataClientResponse);
var
Token: JS.TJSObject;
begin
Token := JS.TJSObject(Response.Result);
SetToken(JS.toString(Token.Properties['value']));
ASuccess;
end;
procedure OnError(Error: TXDataClientError);
begin
AError(Format('%s: %s', [Error.ErrorCode, Error.ErrorMessage]));
end;
begin
if (AUser = '') or (APassword = '') then
begin
AError('Please enter a username and a password');
Exit;
end;
FClient.RawInvoke(
'IAuthService.Login', [AUser, APassword],
@OnLoad, @OnError
);
end;
procedure TAuthService.Logout;
begin
DeleteToken;
end;
procedure TAuthService.SetToken(AToken: string);
begin
window.localStorage.setItem(TOKEN_NAME, AToken);
end;
function TAuthService.TokenExpirationDate: TDateTime;
var
ExpirationDate: TJSDate;
begin
if not Authenticated then
Exit(Now);
ExpirationDate := TJwtHelper.TokenExpirationDate(GetToken);
Result := EncodeDate(
ExpirationDate.FullYear,
ExpirationDate.Month + 1,
ExpirationDate.Date
) +
EncodeTime(
ExpirationDate.Hours,
ExpirationDate.Minutes,
ExpirationDate.Seconds,
0
);
end;
function TAuthService.TokenExpired: Boolean;
begin
if not Authenticated then
Exit(False);
Result := TJwtHelper.TokenExpired(GetToken);
end;
function TAuthService.TokenPayload: JS.TJSObject;
begin
if not Authenticated then
Exit(nil);
Result := TJSObject(TJSJSON.parse(TJwtHelper.DecodePayload(GetToken)));
end;
{ TJwtHelper }
class function TJwtHelper.DecodePayload(AToken: string): string;
begin
if Trim(AToken) = '' then
Exit('');
Result := '';
asm
const parts = AToken.split('.');
if (parts.length === 3) { // <- strict compare
// JWTs use url-safe base64; convert before atob
Result = atob(parts[1].replace(/-/g,'+').replace(/_/g,'/'));
}
end;
end;
class function TJwtHelper.HasExpirationDate(AToken: string): Boolean;
var
Payload: string;
Obj: TJSObject;
begin
Payload := DecodePayload(AToken);
Obj := TJSObject(TJSJSON.parse(Payload));
Result := Obj.hasOwnProperty('exp');
end;
class function TJwtHelper.TokenExpirationDate(AToken: string): TJSDate;
var
Payload: string;
Obj: TJSObject;
Epoch: NativeInt;
begin
if not HasExpirationDate(AToken) then
raise Exception.Create('Token has no expiration date');
Payload := DecodePayload(AToken);
Obj := TJSObject(TJSJSON.parse(Payload));
Epoch := toInteger(Obj.Properties['exp']);
Result := TJSDate.New(Epoch * 1000);
end;
class function TJwtHelper.TokenExpired(AToken: string): Boolean;
begin
if not HasExpirationDate(AToken) then
Exit(False);
Result := TJSDate.now > toInteger(TokenExpirationDate(AToken).valueOf);
end;
end.
object DMConnection: TDMConnection
Height = 264
Width = 395
object ApiConnection: TXDataWebConnection
OnError = ApiConnectionError
OnRequest = ApiConnectionRequest
OnResponse = ApiConnectionResponse
Left = 48
Top = 80
end
object AuthConnection: TXDataWebConnection
OnError = AuthConnectionError
Left = 48
Top = 16
end
object XDataWebClient1: TXDataWebClient
Connection = AuthConnection
Left = 269
Top = 164
end
end
unit ConnectionModule;
interface
uses
System.SysUtils, System.Classes, WEBLib.Modules, XData.Web.Connection,
App.Types, App.Config, XData.Web.Client;
type
TDMConnection = class(TWebDataModule)
ApiConnection: TXDataWebConnection;
AuthConnection: TXDataWebConnection;
XDataWebClient1: TXDataWebClient;
procedure ApiConnectionError(Error: TXDataWebConnectionError);
procedure ApiConnectionRequest(Args: TXDataWebConnectionRequest);
procedure ApiConnectionResponse(Args: TXDataWebConnectionResponse);
procedure AuthConnectionError(Error: TXDataWebConnectionError);
private
FUnauthorizedAccessProc: TUnauthorizedAccessProc;
public
const clientVersion = '0.0.1';
procedure InitApp(SuccessProc: TSuccessProc;
UnauthorizedAccessProc: TUnauthorizedAccessProc);
procedure SetClientConfig(Callback: TVersionCheckCallback);
end;
var
DMConnection: TDMConnection;
implementation
uses
JS, Web,
XData.Web.Request,
XData.Web.Response,
Auth.Service,
Utils;
{%CLASSGROUP 'Vcl.Controls.TControl'}
{$R *.dfm}
procedure TDMConnection.ApiConnectionError(Error: TXDataWebConnectionError);
begin
ShowErrorModal(Error.ToString);
end;
procedure TDMConnection.ApiConnectionRequest(Args: TXDataWebConnectionRequest);
begin
if AuthService.Authenticated then
Args.Request.Headers.SetValue('Authorization', 'Bearer ' + AuthService.GetToken);
end;
procedure TDMConnection.ApiConnectionResponse(
Args: TXDataWebConnectionResponse);
begin
if Args.Response.StatusCode = 401 then
FUnauthorizedAccessProc(Format('%d: %s',[Args.Response.StatusCode, Args.Response.ContentAsText]));
end;
procedure TDMConnection.AuthConnectionError(Error: TXDataWebConnectionError);
begin
ShowErrorModal(Error.ToString);
end;
procedure TDMConnection.InitApp(SuccessProc: TSuccessProc;
UnauthorizedAccessProc: TUnauthorizedAccessProc);
procedure ConfigLoaded(Config: TAppConfig);
begin
if Config.AuthUrl <> '' then
AuthConnection.URL := Config.AuthUrl;
if Config.ApiUrl <> '' then
ApiConnection.URL := Config.ApiUrl;
AuthConnection.Open(SuccessProc);
end;
begin
FUnauthorizedAccessProc := UnauthorizedAccessProc;
LoadConfig(@ConfigLoaded);
end;
procedure TDMConnection.SetClientConfig(Callback: TVersionCheckCallback);
begin
XDataWebClient1.Connection := AuthConnection;
XDataWebClient1.RawInvoke('IAuthService.VerifyVersion', [clientVersion],
procedure(Response: TXDataClientResponse)
var
jsonResult: TJSObject;
error: string;
begin
jsonResult := TJSObject(Response.Result);
if jsonResult.HasOwnProperty('error') then
error := string(jsonResult['error'])
else
error := '';
if error <> '' then
Callback(False, error)
else
Callback(True, '');
end);
end;
end.
unit Utils;
interface
uses
System.Classes, SysUtils, JS, Web, WEBLib.Forms, WEBLib.Toast, DateUtils, WebLib.Dialogs;
procedure ShowStatusMessage(const AMessage, AClass: string; const AElementId: string);
procedure HideStatusMessage(const AElementId: string);
procedure ShowSpinner(SpinnerID: string);
procedure HideSpinner(SpinnerID: string);
procedure ShowErrorModal(msg: string);
function CalculateAge(DateOfBirth: TDateTime): Integer;
function FormatPhoneNumber(PhoneNumber: string): string;
procedure ApplyReportTitle(CurrentReportType: string);
procedure ShowToast(const MessageText: string; const ToastType: string = 'success');
procedure ShowConfirmationModal(msg, leftLabel, rightLabel: string; ConfirmProc: TProc<Boolean>);
procedure ShowNotificationModal(msg: string);
// function FormatDollarValue(ValueStr: string): string;
implementation
procedure ShowStatusMessage(const AMessage, AClass: string; const AElementId: string);
var
StatusMessage: TJSHTMLElement;
begin
StatusMessage := TJSHTMLElement(document.getElementById(AElementId));
if Assigned(StatusMessage) then
begin
if AMessage = '' then
begin
StatusMessage.style.setProperty('display', 'none');
StatusMessage.className := '';
StatusMessage.innerHTML := '';
end
else
begin
StatusMessage.innerHTML := AMessage;
StatusMessage.className := 'alert ' + AClass;
StatusMessage.style.setProperty('display', 'block');
end
end
else
console.log('Error: Status message element not found');
end;
procedure HideStatusMessage(const AElementId: string);
var
StatusMessage: TJSHTMLElement;
begin
StatusMessage := TJSHTMLElement(document.getElementById(AElementId));
if Assigned(StatusMessage) then
begin
StatusMessage.style.setProperty('display', 'none');
StatusMessage.className := '';
StatusMessage.innerHTML := '';
end
else
console.log('Error: Status message element not found');
end;
procedure ShowSpinner(SpinnerID: string);
var
SpinnerElement: TJSHTMLElement;
begin
SpinnerElement := TJSHTMLElement(document.getElementById(SpinnerID));
if Assigned(SpinnerElement) then
begin
// Move spinner to the <body> if it's not already there
asm
if (SpinnerElement.parentNode !== document.body) {
document.body.appendChild(SpinnerElement);
}
end;
SpinnerElement.classList.remove('d-none');
SpinnerElement.classList.add('d-block');
end;
end;
procedure HideSpinner(SpinnerID: string);
var
SpinnerElement: TJSHTMLElement;
begin
SpinnerElement := TJSHTMLElement(document.getElementById(SpinnerID));
if Assigned(SpinnerElement) then
begin
SpinnerElement.classList.remove('d-block');
SpinnerElement.classList.add('d-none');
end;
end;
procedure ShowErrorModal(msg: string);
begin
asm
var modal = document.getElementById('main_errormodal');
var label = document.getElementById('main_lblmodal_body');
var reloadBtn = document.getElementById('btn_modal_restart');
if (label) label.innerText = msg;
// Ensure modal is a direct child of <body>
if (modal && modal.parentNode !== document.body) {
document.body.appendChild(modal);
}
// Bind hard reload to button
if (reloadBtn) {
reloadBtn.onclick = function () {
window.location.reload(true); // hard reload, bypass cache
};
}
// Show the Bootstrap modal
var bsModal = new bootstrap.Modal(modal, { keyboard: false });
bsModal.show();
end;
end;
procedure ShowNotificationModal(msg: string);
begin
asm
var modal = document.getElementById('main_notification_modal');
var label = document.getElementById('main_notification_modal_body');
var closeBtn = document.getElementById('btn_modal_close');
if (label) label.innerText = msg;
// Ensure modal is a direct child of <body>
if (modal && modal.parentNode !== document.body) {
document.body.appendChild(modal);
}
// Button simply closes the modal
if (closeBtn) {
closeBtn.onclick = function () {
var existing = bootstrap.Modal.getInstance(modal);
if (existing) {
existing.hide();
}
};
}
// Show the Bootstrap modal
var bsModal = new bootstrap.Modal(modal, { keyboard: false });
bsModal.show();
end;
end;
// ShowConfirmationModal displays a two-button modal with custom labels.
// Params:
// - messageText: text shown in the modal body
// - leftButtonText: label for the left button (e.g., "Cancel")
// - rightButtonText: label for the right button (e.g., "Delete")
// - callback: procedure(confirmed: Boolean); confirmed = True if right button clicked
//
// Example:
// ShowConfirmationModal('Delete this?', 'Cancel', 'Delete',
// procedure(confirmed: Boolean)
// begin
// if confirmed then DeleteOrder();
// end);
// function ShowConfirmationModal(msg, leftLabel, rightLabel: string;): Boolean;
// if ShowConfirmationModal then
// doThing()
// else
// doOtherThing();
procedure ShowConfirmationModal(msg, leftLabel, rightLabel: string; ConfirmProc: TProc<Boolean>);
begin
asm
var modal = document.getElementById('main_confirmation_modal');
var body = document.getElementById('main_modal_body');
var btnLeft = document.getElementById('btn_confirm_left');
var btnRight = document.getElementById('btn_confirm_right');
var bsModal;
if (body) body.innerText = msg;
if (btnLeft) btnLeft.innerText = leftLabel;
if (btnRight) btnRight.innerText = rightLabel;
if (modal && modal.parentNode !== document.body) {
document.body.appendChild(modal);
}
btnLeft.onclick = null;
btnRight.onclick = null;
btnLeft.onclick = function () {
bsModal.hide();
ConfirmProc(true); // user confirmed
};
btnRight.onclick = function () {
bsModal.hide();
ConfirmProc(false); // user canceled
};
bsModal = new bootstrap.Modal(modal, { keyboard: false });
bsModal.show();
end;
end;
function CalculateAge(DateOfBirth: TDateTime): Integer;
var
Today, BirthDate: TJSDate;
Year, Month, Day, BirthYear, BirthMonth, BirthDay: NativeInt;
DOBString: string;
begin
Today := TJSDate.New;
Year := Today.FullYear;
Month := Today.Month + 1;
Day := Today.Date;
// Formats the DateOfBirth as an ISO 8601 date string
DOBString := FormatDateTime('yyyy-mm-dd', DateOfBirth);
BirthDate := TJSDate.New(DOBString);
if BirthDate = nil then
begin
Exit(0); // Exit the function with an age of 0 if the date creation fails
end;
BirthYear := BirthDate.FullYear;
BirthMonth := BirthDate.Month + 1;
BirthDay := BirthDate.Date;
Result := Year - BirthYear;
if (Month < BirthMonth) or ((Month = BirthMonth) and (Day < BirthDay)) then
Dec(Result);
end;
function FormatPhoneNumber(PhoneNumber: string): string;
var
Digits: string;
begin
Digits := PhoneNumber.Replace('(', '').Replace(')', '').Replace('-', '').Replace(' ', '');
case Length(Digits) of
7: Result := Format('%s-%s', [Copy(Digits, 1, 3), Copy(Digits, 4, 4)]);
10: Result := Format('(%s) %s-%s', [Copy(Digits, 1, 3), Copy(Digits, 4, 3), Copy(Digits, 7, 4)]);
else
// If the number does not have 7 or 10 digits, whatever they typed is returned
Result := PhoneNumber;
end;
end;
procedure ShowToast(const MessageText: string; const ToastType: string = 'success');
var
ParsedText, ToastKind, MsgPrefix: string;
Parts: TArray<string>;
begin
ParsedText := MessageText.Trim;
ToastKind := ToastType.ToLower;
// Check for "Success:" or "Failure:" at the start of message
if ParsedText.Contains(':') then
begin
Parts := ParsedText.Split([':'], 2);
MsgPrefix := Parts[0].Trim.ToLower;
if (MsgPrefix = 'success') or (MsgPrefix = 'failure') then
begin
ParsedText := Parts[1].Trim;
if MsgPrefix = 'success' then
ToastKind := 'success'
else
ToastKind := 'danger';
end;
end;
asm
var toastEl = document.getElementById('bootstrapToast');
var toastBody = document.getElementById('bootstrapToastBody');
if (!toastEl || !toastBody) return;
toastBody.innerText = ParsedText;
toastEl.classList.remove('bg-success', 'bg-danger', 'bg-warning', 'bg-primary');
toastEl.classList.remove('slide-in');
switch (ToastKind) {
case 'danger':
toastEl.classList.add('bg-danger');
break;
case 'warning':
toastEl.classList.add('bg-warning');
break;
case 'info':
toastEl.classList.add('bg-primary');
break;
default:
toastEl.classList.add('bg-success');
}
// Add slide-in animation
toastEl.classList.add('slide-in');
var toast = new bootstrap.Toast(toastEl, { delay: 2500 });
toast.show();
// Remove animation class after it's done (so it can be reapplied)
setTimeout(function() {
toastEl.classList.remove('slide-in');
}, 500);
end;
end;
procedure ApplyReportTitle(CurrentReportType: string);
var
CrimeTitleElement: TJSHTMLElement;
begin
CrimeTitleElement := TJSHTMLElement(document.getElementById('crime_title'));
if Assigned(CrimeTitleElement) then
CrimeTitleElement.innerText := CurrentReportType
else
Console.Log('Element with ID "crime_title" not found.');
end;
// Used html number input type to restrict the input instead of this function
// function FormatDollarValue(ValueStr: string): string;
// var
// i: Integer;
// begin
// Result := ''; // Initialize the result
// // Filter out any characters that are not digits or decimal point
// for i := 1 to Length(ValueStr) do
// begin
// if (Pos(ValueStr[i], '0123456789.') > 0) then
// begin
// Result := Result + ValueStr[i];
// end;
// end;
// end;
end.
This source diff could not be displayed because it is too large. You can view the blob instead.
<nav class="navbar navbar-light bg-light login-navbar">
<div class="container-fluid">
<a class="navbar-brand" href="#">Koehler-Gibson Orders</a>
</div>
</nav>
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-auto">
<img id="kgpicture" style="width: 250px; height: 250px;">
</div>
<div class="col-md-6 col-lg-4">
<div class="card login-card">
<div class="card-header">
<h3 id="view.login.title" class="fs-6 card-title">Please Sign In</h3>
</div>
<div class="card-body">
<div role="form">
<div id="view.login.message" class="alert alert-danger">
<button id="view.login.message.button" type="button" class="btn-close" aria-label="Close"></button>
<span id="view.login.message.label"></span>
</div>
<fieldset>
<div class="mb-3">
<input id="view.login.edtusername" class="form-control" type="text" autofocus placeholder="Username">
</div>
<div class="mb-3">
<input id="view.login.edtpassword" class="form-control" type="password" placeholder="Password">
</div>
<div class="mb-3">
<button id="view.login.btnlogin" class="btn btn-primary w-100">Login</button>
</div>
<div class="text-end text-muted small mt-1">
<span id="lbl_client_version"></span>
</div>
</fieldset>
</div>
</div>
</div>
</div>
</div>
</div>
unit View.Login;
interface
uses
System.SysUtils, System.Classes, Web, WEBLib.Graphics, WEBLib.Controls, WEBLib.Forms, WEBLib.Dialogs,
Vcl.Controls, Vcl.StdCtrls, WEBLib.StdCtrls, WEBLib.JSON,
JS, XData.Web.Connection, WEBLib.ExtCtrls,
App.Types, ConnectionModule, XData.Web.Client, Vcl.Imaging.pngimage;
type
TFViewLogin = class(TWebForm)
WebLabel1: TWebLabel;
edtUsername: TWebEdit;
edtPassword: TWebEdit;
btnLogin: TWebButton;
pnlMessage: TWebPanel;
lblMessage: TWebLabel;
btnCloseNotification: TWebButton;
XDataWebClient: TXDataWebClient;
WebImageControl1: TWebImageControl;
lblClientVersion: TWebLabel;
procedure btnLoginClick(Sender: TObject);
procedure btnCloseNotificationClick(Sender: TObject);
procedure WebFormShow(Sender: TObject);
private
FLoginProc: TSuccessProc;
FMessage: string;
procedure ShowNotification(Notification: string);
procedure HideNotification;
public
class procedure Display(LoginProc: TSuccessProc); overload;
class procedure Display(LoginProc: TSuccessProc; AMsg: string); overload;
end;
var
FViewLogin: TFViewLogin;
implementation
uses
Auth.Service;
{$R *.dfm}
procedure TFViewLogin.btnLoginClick(Sender: TObject);
procedure LoginSuccess;
begin
FLoginProc;
end;
procedure LoginError(AMsg: string);
begin
ShowNotification('Login Error: ' + AMsg);
end;
begin
AuthService.Login(
edtUsername.Text, edtPassword.Text,
@LoginSuccess,
@LoginError
);
end;
class procedure TFViewLogin.Display(LoginProc: TSuccessProc);
begin
TFViewLogin.Display(LoginProc, '');
end;
class procedure TFViewLogin.Display(LoginProc: TSuccessProc; AMsg: string);
procedure FormCreate(AForm: TObject);
begin
TFViewLogin(AForm).FMessage := AMsg;
end;
begin
if Assigned(FViewLogin) then
FViewLogin.Free;
FViewLogin := TFViewLogin.CreateNew(@FormCreate);
FViewLogin.FLoginProc := LoginProc;
end;
procedure TFViewLogin.HideNotification;
begin
pnlMessage.ElementHandle.classList.add('d-none');
pnlMessage.Visible := False;
end;
procedure TFViewLogin.ShowNotification(Notification: string);
begin
if Notification <> '' then
begin
lblMessage.Caption := Notification;
pnlMessage.ElementHandle.classList.remove('d-none');
pnlMessage.Visible := True;
end;
end;
procedure TFViewLogin.btnCloseNotificationClick(Sender: TObject);
begin
HideNotification;
end;
procedure TFViewLogin.WebFormShow(Sender: TObject);
begin
console.log(DMConnection.clientVersion);
FViewLogin.lblClientVersion.Caption := 'v' + DMConnection.clientVersion;
if FMessage <> '' then
ShowNotification(FMessage)
else
HideNotification;
end;
end.
object FViewMain: TFViewMain
Width = 1322
Height = 764
CSSLibrary = cssBootstrap
ElementFont = efCSS
Font.Charset = ANSI_CHARSET
Font.Color = clBlack
Font.Height = -11
Font.Name = 'Arial'
Font.Style = []
ParentFont = False
OnCreate = WebFormCreate
object wllblLogout: TWebLinkLabel
Left = 501
Top = 33
Width = 36
Height = 14
ElementID = 'dropdown.menu.logout'
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = wllblLogoutClick
Caption = ' Logout'
end
object lblVersion: TWebLabel
Left = 396
Top = 33
Width = 47
Height = 14
Caption = 'lblVersion'
ElementID = 'view.main.version'
ElementFont = efCSS
ElementPosition = epRelative
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
end
object lblAppTitle: TWebLabel
Left = 57
Top = 33
Width = 48
Height = 14
Caption = 'emT3web'
ElementID = 'view.main.apptitle'
ElementFont = efCSS
ElementPosition = epRelative
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
Transparent = False
WidthPercent = 100.000000000000000000
end
object WebPanel1: TWebPanel
Left = 77
Top = 112
Width = 1322
Height = 0
ElementID = 'main.webpanel'
HeightStyle = ssAuto
WidthStyle = ssAuto
ChildOrder = 3
ElementFont = efCSS
ElementPosition = epIgnore
Font.Charset = ANSI_CHARSET
Font.Color = clBlack
Font.Height = -11
Font.Name = 'Arial'
Font.Style = []
ParentFont = False
Role = 'null'
TabOrder = 0
end
object WebMemo1: TWebMemo
Left = 77
Top = 479
Width = 471
Height = 83
ElementID = 'main.debugmemo'
ElementPosition = epRelative
Enabled = False
HeightPercent = 100.000000000000000000
Lines.Strings = (
'WebMemo1')
Role = 'null'
SelLength = 0
SelStart = 0
ShowFocus = False
Visible = False
WidthPercent = 100.000000000000000000
end
object WebMessageDlg1: TWebMessageDlg
Left = 47
Top = 232
Width = 24
Height = 24
Buttons = []
CustomButtons = <>
DialogText.Strings = (
'Warning'
'Error'
'Information'
'Confirm'
'Custom'
'OK'
'Cancel'
'Yes'
'No'
'Abort'
'Retry'
'Ignore'
'All'
'Yes to all'
'No to all'
'Help'
'Close')
Opacity = 0.200000000000000000
end
object XDataWebClient: TXDataWebClient
Connection = DMConnection.ApiConnection
Left = 44
Top = 280
end
end
<div id="wrapper">
<nav class="navbar navbar-expand navbar-light bg-light" style="margin-bottom: 0px;">
<div class="container-fluid">
<div class="d-flex align-items-center">
<a id="view.main.apptitle" class="navbar-brand" href="index.html">emT3web</a>
<span id="view.main.version" class="small text-muted ms-2"></span>
</div>
<div class="collapse navbar-collapse show" id="navbarNavDropdown">
<ul class="navbar-nav ms-auto">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" id="navbarDropdownMenuLink" role="button"
data-bs-toggle="dropdown" aria-expanded="false">
<i class="fa fa-user fa-fw"></i><span class="panel-title" id="view.main.username"> Username </span>
</a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdownMenuLink">
<li>
<a class="dropdown-item" id="dropdown.menu.logout" href="#">
<i class="fa fa-sign-out fa-fw"></i><span> Logout</span>
</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<!-- Toast wrapper directly under navbar -->
<div id="toast-wrapper"
class="position-fixed top-0 start-0 mt-5 ms-4"
style="z-index: 1080; min-width: 300px; max-width: 500px;">
<div id="bootstrapToast"
class="toast align-items-center text-white bg-success border-0 shadow"
role="alert" aria-live="assertive" aria-atomic="true">
<div class="d-flex">
<div class="toast-body" id="bootstrapToastBody">
Success message
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto"
data-bs-dismiss="toast" aria-label="Close"></button>
</div>
</div>
</div>
<div class="container-fluid d-flex flex-column vh-100">
<div class="row flex-grow-1">
<div id="main.webpanel" class="col-12 d-flex flex-column flex-grow-1"></div>
</div>
<div class="row">
<div class="col-12">
<div class="form-outline">
<textarea class="form-control" id="main.debugmemo" rows="4"></textarea>
</div>
</div>
</div>
</div>
</div>
<div id="spinner" class="position-absolute top-50 start-50 translate-middle d-none">
<div class="lds-roller">
<div></div><div></div><div></div><div></div>
<div></div><div></div><div></div><div></div>
</div>
</div>
<div class="modal fade" id="main_errormodal" tabindex="-1" aria-labelledby="main_lblmodal" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content shadow-lg">
<div class="modal-header">
<h5 class="modal-title" id="main_lblmodal">Error</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_lblmodal_body">
Please contact EMSystems to solve the issue.
</div>
<div class="modal-footer justify-content-center">
<button type="button" id="btn_modal_restart" class="btn btn-primary">Back to Orders</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="main_confirmation_modal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content shadow-lg">
<div class="modal-header">
<h5 class="modal-title">Confirm</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body fw-bold" id="main_modal_body">
Placeholder text
</div>
<div class="modal-footer justify-content-center">
<button type="button" class="btn btn-primary me-3" id="btn_confirm_left">Cancel</button>
<button type="button" class="btn btn-secondary" id="btn_confirm_right">Confirm</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="main_notification_modal" tabindex="-1" aria-labelledby="main_lblmodal" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content shadow-lg">
<div class="modal-header">
<h5 class="modal-title" id="main_notification_modal">Error</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_notification_modal_body">
Please contact EMSystems to solve the issue.
</div>
<div class="modal-footer justify-content-center">
<button type="button" id="btn_modal_close" class="btn btn-primary">Close</button>
</div>
</div>
</div>
</div>
unit View.Main;
interface
uses
System.SysUtils, System.Classes, JS, Web, WEBLib.Graphics, WEBLib.Controls,
WEBLib.Forms, WEBLib.Dialogs, WEBLib.ExtCtrls, Vcl.Controls, Vcl.StdCtrls,
WEBLib.StdCtrls, Data.DB, XData.Web.JsonDataset, XData.Web.Dataset,
App.Types, ConnectionModule, XData.Web.Client, WEBLib.Menus, Utils;
type
TFViewMain = class(TWebForm)
wllblLogout: TWebLinkLabel;
WebPanel1: TWebPanel;
WebMessageDlg1: TWebMessageDlg;
WebMemo1: TWebMemo;
XDataWebClient: TXDataWebClient;
lblVersion: TWebLabel;
lblAppTitle: TWebLabel;
procedure WebFormCreate(Sender: TObject);
procedure mnuLogoutClick(Sender: TObject);
procedure wllblLogoutClick(Sender: TObject);
private
{ Private declarations }
FUserInfo: string;
FSearchSettings: string;
FChildForm: TWebForm;
FLogoutProc: TLogoutProc;
FSearchProc: TSearchProc;
procedure ShowCrudForm( AFormClass: TWebFormClass );
procedure ConfirmLogout;
public
{ Public declarations }
class procedure Display(LogoutProc: TLogoutProc);
procedure ShowForm( AFormClass: TWebFormClass );
var
search: string;
change: boolean;
end;
var
FViewMain: TFViewMain;
implementation
uses
Auth.Service,
View.Login,
View.Tasks;
{$R *.dfm}
class procedure TFViewMain.Display(LogoutProc: TLogoutProc);
begin
if Assigned(FViewMain) then
FViewMain.Free;
FViewMain := TFViewMain.CreateNew;
FViewMain.FLogoutProc := LogoutProc;
end;
procedure TFViewMain.WebFormCreate(Sender: TObject);
var
userName: string;
test: boolean;
begin
FChildForm := nil;
ShowForm(TFTasks);
lblAppTitle.Caption := 'emT3web';
lblVersion.Caption := 'v' + DMConnection.clientVersion;
end;
procedure TFViewMain.mnuLogoutClick(Sender: TObject);
begin
ConfirmLogout;
end;
procedure TFViewMain.wllblLogoutClick(Sender: TObject);
begin
ConfirmLogout;
end;
procedure TFViewMain.ConfirmLogout;
begin
ShowConfirmationModal(
'Are you sure you want to log out?',
'Yes',
'No',
procedure(confirmed: Boolean)
begin
if confirmed and Assigned(FLogoutProc) then
FLogoutProc('');
end
);
end;
procedure TFViewMain.ShowCrudForm(AFormClass: TWebFormClass);
begin
ShowForm(AFormClass);
end;
procedure TFViewMain.ShowForm(AFormClass: TWebFormClass);
begin
if Assigned(FChildForm) then
FChildForm.Free;
Application.CreateForm(AFormClass, WebPanel1.ElementID, FChildForm);
end;
//procedure TFViewMain.ShowTasksForm(Info: string);
//begin
// if Assigned(FChildForm) then
// FChildForm.Free;
// FChildForm := TFViewUsers.CreateForm(WebPanel1.ElementID, Info);
//end;
end.
object FTasks: TFTasks
Width = 1416
Height = 846
FormContainer = 'tasks.root'
OnCreate = WebFormCreate
object pnlGrid: TWebPanel
Left = 0
Top = 0
Width = 1408
Height = 841
ElementID = 'pnl_grid'
ChildOrder = 1
ElementFont = efCSS
TabOrder = 0
object grdTasks: TTMSFNCDataGrid
Left = 12
Top = 14
Width = 1373
Height = 799
ParentDoubleBuffered = False
DoubleBuffered = True
TabOrder = 0
ShowAcceleratorChar = False
Footer.Bar.Buttons = <>
Header.VisualGrouping.Layout.Font.Charset = DEFAULT_CHARSET
Header.VisualGrouping.Layout.Font.Color = clWindowText
Header.VisualGrouping.Layout.Font.Height = -12
Header.VisualGrouping.Layout.Font.Name = 'Segoe UI'
Header.VisualGrouping.Layout.Font.Style = []
Header.Bar.Buttons = <>
CellAppearance.FilterMatchLayout.Font.Charset = DEFAULT_CHARSET
CellAppearance.FilterMatchLayout.Font.Color = clWindowText
CellAppearance.FilterMatchLayout.Font.Height = -12
CellAppearance.FilterMatchLayout.Font.Name = 'Segoe UI'
CellAppearance.FilterMatchLayout.Font.Style = []
CellAppearance.FilterMatchLayout.WordWrapping = True
CellAppearance.FilterInverseMatchLayout.Font.Charset = DEFAULT_CHARSET
CellAppearance.FilterInverseMatchLayout.Font.Color = clWindowText
CellAppearance.FilterInverseMatchLayout.Font.Height = -12
CellAppearance.FilterInverseMatchLayout.Font.Name = 'Segoe UI'
CellAppearance.FilterInverseMatchLayout.Font.Style = []
CellAppearance.FilterInverseMatchLayout.WordWrapping = True
CellAppearance.BandLayout.Font.Charset = DEFAULT_CHARSET
CellAppearance.BandLayout.Font.Color = clBlack
CellAppearance.BandLayout.Font.Height = -12
CellAppearance.BandLayout.Font.Name = 'Segoe UI'
CellAppearance.BandLayout.Font.Style = []
CellAppearance.BandLayout.WordWrapping = True
CellAppearance.FixedLayout.Font.Charset = DEFAULT_CHARSET
CellAppearance.FixedLayout.Font.Color = clBlack
CellAppearance.FixedLayout.Font.Height = -12
CellAppearance.FixedLayout.Font.Name = 'Segoe UI'
CellAppearance.FixedLayout.Font.Style = []
CellAppearance.FixedLayout.WordWrapping = True
CellAppearance.FixedSelectedLayout.Font.Charset = DEFAULT_CHARSET
CellAppearance.FixedSelectedLayout.Font.Color = clBlack
CellAppearance.FixedSelectedLayout.Font.Height = -12
CellAppearance.FixedSelectedLayout.Font.Name = 'Segoe UI'
CellAppearance.FixedSelectedLayout.Font.Style = []
CellAppearance.FixedSelectedLayout.WordWrapping = True
CellAppearance.FocusedLayout.Font.Charset = DEFAULT_CHARSET
CellAppearance.FocusedLayout.Font.Color = clBlack
CellAppearance.FocusedLayout.Font.Height = -12
CellAppearance.FocusedLayout.Font.Name = 'Segoe UI'
CellAppearance.FocusedLayout.Font.Style = []
CellAppearance.FocusedLayout.WordWrapping = True
CellAppearance.GroupLayout.Font.Charset = DEFAULT_CHARSET
CellAppearance.GroupLayout.Font.Color = clBlack
CellAppearance.GroupLayout.Font.Height = -12
CellAppearance.GroupLayout.Font.Name = 'Segoe UI'
CellAppearance.GroupLayout.Font.Style = []
CellAppearance.GroupLayout.WordWrapping = True
CellAppearance.NormalLayout.Font.Charset = DEFAULT_CHARSET
CellAppearance.NormalLayout.Font.Color = clBlack
CellAppearance.NormalLayout.Font.Height = -12
CellAppearance.NormalLayout.Font.Name = 'Segoe UI'
CellAppearance.NormalLayout.Font.Style = []
CellAppearance.NormalLayout.WordWrapping = True
CellAppearance.SelectedLayout.Font.Charset = DEFAULT_CHARSET
CellAppearance.SelectedLayout.Font.Color = clBlack
CellAppearance.SelectedLayout.Font.Height = -12
CellAppearance.SelectedLayout.Font.Name = 'Segoe UI'
CellAppearance.SelectedLayout.Font.Style = []
CellAppearance.SummaryLayout.Font.Charset = DEFAULT_CHARSET
CellAppearance.SummaryLayout.Font.Color = clBlack
CellAppearance.SummaryLayout.Font.Height = -12
CellAppearance.SummaryLayout.Font.Name = 'Segoe UI'
CellAppearance.SummaryLayout.Font.Style = []
CellAppearance.SummaryLayout.WordWrapping = True
Columns = <
item
Appearance.FilterMatchLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FilterMatchLayout.Font.Color = clWindowText
Appearance.FilterMatchLayout.Font.Height = -12
Appearance.FilterMatchLayout.Font.Name = 'Segoe UI'
Appearance.FilterMatchLayout.Font.Style = []
Appearance.FilterInverseMatchLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FilterInverseMatchLayout.Font.Color = clWindowText
Appearance.FilterInverseMatchLayout.Font.Height = -12
Appearance.FilterInverseMatchLayout.Font.Name = 'Segoe UI'
Appearance.FilterInverseMatchLayout.Font.Style = []
Appearance.BandLayout.Font.Charset = DEFAULT_CHARSET
Appearance.BandLayout.Font.Color = clWindowText
Appearance.BandLayout.Font.Height = -12
Appearance.BandLayout.Font.Name = 'Segoe UI'
Appearance.BandLayout.Font.Style = []
Appearance.FixedLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FixedLayout.Font.Color = clWindowText
Appearance.FixedLayout.Font.Height = -12
Appearance.FixedLayout.Font.Name = 'Segoe UI'
Appearance.FixedLayout.Font.Style = []
Appearance.FixedSelectedLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FixedSelectedLayout.Font.Color = clWindowText
Appearance.FixedSelectedLayout.Font.Height = -12
Appearance.FixedSelectedLayout.Font.Name = 'Segoe UI'
Appearance.FixedSelectedLayout.Font.Style = []
Appearance.FocusedLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FocusedLayout.Font.Color = clWindowText
Appearance.FocusedLayout.Font.Height = -12
Appearance.FocusedLayout.Font.Name = 'Segoe UI'
Appearance.FocusedLayout.Font.Style = []
Appearance.GroupLayout.Font.Charset = DEFAULT_CHARSET
Appearance.GroupLayout.Font.Color = clWindowText
Appearance.GroupLayout.Font.Height = -12
Appearance.GroupLayout.Font.Name = 'Segoe UI'
Appearance.GroupLayout.Font.Style = []
Appearance.NormalLayout.Font.Charset = DEFAULT_CHARSET
Appearance.NormalLayout.Font.Color = clWindowText
Appearance.NormalLayout.Font.Height = -12
Appearance.NormalLayout.Font.Name = 'Segoe UI'
Appearance.NormalLayout.Font.Style = []
Appearance.SelectedLayout.Font.Charset = DEFAULT_CHARSET
Appearance.SelectedLayout.Font.Color = clWindowText
Appearance.SelectedLayout.Font.Height = -12
Appearance.SelectedLayout.Font.Name = 'Segoe UI'
Appearance.SelectedLayout.Font.Style = []
Appearance.SummaryLayout.Font.Charset = DEFAULT_CHARSET
Appearance.SummaryLayout.Font.Color = clWindowText
Appearance.SummaryLayout.Font.Height = -12
Appearance.SummaryLayout.Font.Name = 'Segoe UI'
Appearance.SummaryLayout.Font.Style = []
Header = 'ID'
Width = 70.000000000000000000
end
item
Appearance.FilterMatchLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FilterMatchLayout.Font.Color = clWindowText
Appearance.FilterMatchLayout.Font.Height = -12
Appearance.FilterMatchLayout.Font.Name = 'Segoe UI'
Appearance.FilterMatchLayout.Font.Style = []
Appearance.FilterInverseMatchLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FilterInverseMatchLayout.Font.Color = clWindowText
Appearance.FilterInverseMatchLayout.Font.Height = -12
Appearance.FilterInverseMatchLayout.Font.Name = 'Segoe UI'
Appearance.FilterInverseMatchLayout.Font.Style = []
Appearance.BandLayout.Font.Charset = DEFAULT_CHARSET
Appearance.BandLayout.Font.Color = clWindowText
Appearance.BandLayout.Font.Height = -12
Appearance.BandLayout.Font.Name = 'Segoe UI'
Appearance.BandLayout.Font.Style = []
Appearance.FixedLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FixedLayout.Font.Color = clWindowText
Appearance.FixedLayout.Font.Height = -12
Appearance.FixedLayout.Font.Name = 'Segoe UI'
Appearance.FixedLayout.Font.Style = []
Appearance.FixedSelectedLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FixedSelectedLayout.Font.Color = clWindowText
Appearance.FixedSelectedLayout.Font.Height = -12
Appearance.FixedSelectedLayout.Font.Name = 'Segoe UI'
Appearance.FixedSelectedLayout.Font.Style = []
Appearance.FocusedLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FocusedLayout.Font.Color = clWindowText
Appearance.FocusedLayout.Font.Height = -12
Appearance.FocusedLayout.Font.Name = 'Segoe UI'
Appearance.FocusedLayout.Font.Style = []
Appearance.GroupLayout.Font.Charset = DEFAULT_CHARSET
Appearance.GroupLayout.Font.Color = clWindowText
Appearance.GroupLayout.Font.Height = -12
Appearance.GroupLayout.Font.Name = 'Segoe UI'
Appearance.GroupLayout.Font.Style = []
Appearance.NormalLayout.Font.Charset = DEFAULT_CHARSET
Appearance.NormalLayout.Font.Color = clWindowText
Appearance.NormalLayout.Font.Height = -12
Appearance.NormalLayout.Font.Name = 'Segoe UI'
Appearance.NormalLayout.Font.Style = []
Appearance.SelectedLayout.Font.Charset = DEFAULT_CHARSET
Appearance.SelectedLayout.Font.Color = clWindowText
Appearance.SelectedLayout.Font.Height = -12
Appearance.SelectedLayout.Font.Name = 'Segoe UI'
Appearance.SelectedLayout.Font.Style = []
Appearance.SummaryLayout.Font.Charset = DEFAULT_CHARSET
Appearance.SummaryLayout.Font.Color = clWindowText
Appearance.SummaryLayout.Font.Height = -12
Appearance.SummaryLayout.Font.Name = 'Segoe UI'
Appearance.SummaryLayout.Font.Style = []
Header = 'Name'
Width = 250.000000000000000000
end
item
Appearance.FilterMatchLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FilterMatchLayout.Font.Color = clWindowText
Appearance.FilterMatchLayout.Font.Height = -12
Appearance.FilterMatchLayout.Font.Name = 'Segoe UI'
Appearance.FilterMatchLayout.Font.Style = []
Appearance.FilterInverseMatchLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FilterInverseMatchLayout.Font.Color = clWindowText
Appearance.FilterInverseMatchLayout.Font.Height = -12
Appearance.FilterInverseMatchLayout.Font.Name = 'Segoe UI'
Appearance.FilterInverseMatchLayout.Font.Style = []
Appearance.BandLayout.Font.Charset = DEFAULT_CHARSET
Appearance.BandLayout.Font.Color = clWindowText
Appearance.BandLayout.Font.Height = -12
Appearance.BandLayout.Font.Name = 'Segoe UI'
Appearance.BandLayout.Font.Style = []
Appearance.FixedLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FixedLayout.Font.Color = clWindowText
Appearance.FixedLayout.Font.Height = -12
Appearance.FixedLayout.Font.Name = 'Segoe UI'
Appearance.FixedLayout.Font.Style = []
Appearance.FixedSelectedLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FixedSelectedLayout.Font.Color = clWindowText
Appearance.FixedSelectedLayout.Font.Height = -12
Appearance.FixedSelectedLayout.Font.Name = 'Segoe UI'
Appearance.FixedSelectedLayout.Font.Style = []
Appearance.FocusedLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FocusedLayout.Font.Color = clWindowText
Appearance.FocusedLayout.Font.Height = -12
Appearance.FocusedLayout.Font.Name = 'Segoe UI'
Appearance.FocusedLayout.Font.Style = []
Appearance.GroupLayout.Font.Charset = DEFAULT_CHARSET
Appearance.GroupLayout.Font.Color = clWindowText
Appearance.GroupLayout.Font.Height = -12
Appearance.GroupLayout.Font.Name = 'Segoe UI'
Appearance.GroupLayout.Font.Style = []
Appearance.NormalLayout.Font.Charset = DEFAULT_CHARSET
Appearance.NormalLayout.Font.Color = clWindowText
Appearance.NormalLayout.Font.Height = -12
Appearance.NormalLayout.Font.Name = 'Segoe UI'
Appearance.NormalLayout.Font.Style = []
Appearance.SelectedLayout.Font.Charset = DEFAULT_CHARSET
Appearance.SelectedLayout.Font.Color = clWindowText
Appearance.SelectedLayout.Font.Height = -12
Appearance.SelectedLayout.Font.Name = 'Segoe UI'
Appearance.SelectedLayout.Font.Style = []
Appearance.SummaryLayout.Font.Charset = DEFAULT_CHARSET
Appearance.SummaryLayout.Font.Color = clWindowText
Appearance.SummaryLayout.Font.Height = -12
Appearance.SummaryLayout.Font.Name = 'Segoe UI'
Appearance.SummaryLayout.Font.Style = []
Header = 'Joined'
Width = 100.000000000000000000
end
item
Appearance.FilterMatchLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FilterMatchLayout.Font.Color = clWindowText
Appearance.FilterMatchLayout.Font.Height = -12
Appearance.FilterMatchLayout.Font.Name = 'Segoe UI'
Appearance.FilterMatchLayout.Font.Style = []
Appearance.FilterInverseMatchLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FilterInverseMatchLayout.Font.Color = clWindowText
Appearance.FilterInverseMatchLayout.Font.Height = -12
Appearance.FilterInverseMatchLayout.Font.Name = 'Segoe UI'
Appearance.FilterInverseMatchLayout.Font.Style = []
Appearance.BandLayout.Font.Charset = DEFAULT_CHARSET
Appearance.BandLayout.Font.Color = clWindowText
Appearance.BandLayout.Font.Height = -12
Appearance.BandLayout.Font.Name = 'Segoe UI'
Appearance.BandLayout.Font.Style = []
Appearance.FixedLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FixedLayout.Font.Color = clWindowText
Appearance.FixedLayout.Font.Height = -12
Appearance.FixedLayout.Font.Name = 'Segoe UI'
Appearance.FixedLayout.Font.Style = []
Appearance.FixedSelectedLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FixedSelectedLayout.Font.Color = clWindowText
Appearance.FixedSelectedLayout.Font.Height = -12
Appearance.FixedSelectedLayout.Font.Name = 'Segoe UI'
Appearance.FixedSelectedLayout.Font.Style = []
Appearance.FocusedLayout.Font.Charset = DEFAULT_CHARSET
Appearance.FocusedLayout.Font.Color = clWindowText
Appearance.FocusedLayout.Font.Height = -12
Appearance.FocusedLayout.Font.Name = 'Segoe UI'
Appearance.FocusedLayout.Font.Style = []
Appearance.GroupLayout.Font.Charset = DEFAULT_CHARSET
Appearance.GroupLayout.Font.Color = clWindowText
Appearance.GroupLayout.Font.Height = -12
Appearance.GroupLayout.Font.Name = 'Segoe UI'
Appearance.GroupLayout.Font.Style = []
Appearance.NormalLayout.Font.Charset = DEFAULT_CHARSET
Appearance.NormalLayout.Font.Color = clWindowText
Appearance.NormalLayout.Font.Height = -12
Appearance.NormalLayout.Font.Name = 'Segoe UI'
Appearance.NormalLayout.Font.Style = []
Appearance.SelectedLayout.Font.Charset = DEFAULT_CHARSET
Appearance.SelectedLayout.Font.Color = clWindowText
Appearance.SelectedLayout.Font.Height = -12
Appearance.SelectedLayout.Font.Name = 'Segoe UI'
Appearance.SelectedLayout.Font.Style = []
Appearance.SummaryLayout.Font.Charset = DEFAULT_CHARSET
Appearance.SummaryLayout.Font.Color = clWindowText
Appearance.SummaryLayout.Font.Height = -12
Appearance.SummaryLayout.Font.Name = 'Segoe UI'
Appearance.SummaryLayout.Font.Style = []
Header = 'Status'
Width = 100.000000000000000000
end>
Designer = False
FilterActions = <>
FilterAppearance.Font.Charset = DEFAULT_CHARSET
FilterAppearance.Font.Color = clBlack
FilterAppearance.Font.Height = -12
FilterAppearance.Font.Name = 'Segoe UI'
FilterAppearance.Font.Style = []
Icons.ExpandIcon.Data = {
1054544D53464E435356474269746D6170080200003C73766720786D6C6E733D
22687474703A2F2F7777772E77332E6F72672F323030302F7376672220776964
74683D22333222206865696768743D223332222076696577426F783D22302030
203332200D0A3332223E3C646566733E3C7374796C653E2E636C732D317B6669
6C6C3A2330303030303B7D3C2F7374796C653E3C2F646566733E3C7469746C65
3E506C75733C2F7469746C653E203C67200D0A69643D2249636F6E223E3C7265
637420636C6173733D22636C732D312220783D22372220793D22313522207769
6474683D22313822206865696768743D2231222F3E3C72656374200D0A636C61
73733D22636C732D312220783D2231352220793D2237222077696474683D2231
22206865696768743D223138222F3E3C7265637420636C6173733D22636C732D
312220783D223122200D0A793D2231222077696474683D223122206865696768
743D223330222F3E3C7265637420636C6173733D22636C732D312220783D2231
2220793D2231222077696474683D22333022200D0A6865696768743D2231222F
3E3C7265637420636C6173733D22636C732D312220783D2233302220793D2231
222077696474683D223122206865696768743D223330222F3E3C72656374200D
0A636C6173733D22636C732D312220783D22312220793D223330222077696474
683D22333022206865696768743D2231222F3E3C2F673E3C2F7376673E}
Icons.CollapseIcon.Data = {
1054544D53464E435356474269746D6170CE0100003C73766720786D6C6E733D
22687474703A2F2F7777772E77332E6F72672F323030302F7376672220776964
74683D22333222206865696768743D223332222076696577426F783D22302030
203332200D0A3332223E3C646566733E3C7374796C653E2E636C732D317B6669
6C6C3A2330303030303B7D3C2F7374796C653E3C2F646566733E3C7469746C65
3E506C75733C2F7469746C653E203C67200D0A69643D2249636F6E223E3C7265
637420636C6173733D22636C732D312220783D22372220793D22313522207769
6474683D22313822206865696768743D2231222F3E3C7265637420636C617373
3D22636C732D312220783D223122200D0A793D2231222077696474683D223122
206865696768743D223330222F3E3C7265637420636C6173733D22636C732D31
2220783D22312220793D2231222077696474683D22333022200D0A6865696768
743D2231222F3E3C7265637420636C6173733D22636C732D312220783D223330
2220793D2231222077696474683D223122206865696768743D223330222F3E3C
72656374200D0A636C6173733D22636C732D312220783D22312220793D223330
222077696474683D22333022206865696768743D2231222F3E3C2F673E3C2F73
76673E}
Icons.FilterIcon.Data = {
1054544D53464E435356474269746D6170E30200003C73766720786D6C6E733D
22687474703A2F2F7777772E77332E6F72672F323030302F7376672220786D6C
6E733A786C696E6B3D22687474703A2F2F7777772E77332E6F72672F31393939
2F786C696E6B222077696474683D22333222206865696768743D223332222076
696577426F783D22302030203332203332223E3C646566733E3C7374796C653E
2E636C732D317B66696C6C3A6E6F6E653B7D2E636C732D327B66696C6C3A2366
61666166613B7D2E636C732D337B66696C6C3A75726C28234E65775F50617474
65726E5F5377617463685F32293B7D2E636C732D347B66696C6C3A2333613361
33383B7D3C2F7374796C653E3C7061747465726E2069643D224E65775F506174
7465726E5F5377617463685F322220646174612D6E616D653D224E6577205061
747465726E205377617463682032222077696474683D22363822206865696768
743D22363822207061747465726E556E6974733D227573657253706163654F6E
557365222076696577426F783D22302030203638203638223E3C726563742063
6C6173733D22636C732D31222077696474683D22363822206865696768743D22
3638222F3E3C7265637420636C6173733D22636C732D32222077696474683D22
363822206865696768743D223638222F3E3C2F7061747465726E3E3C2F646566
733E3C7469746C653E46696C7465723C2F7469746C653E3C672069643D224963
6F6E223E3C706F6C79676F6E20636C6173733D22636C732D332220706F696E74
733D22312E3520362031322E352031352031322E352033302E352031382E3520
33302E352031382E352031352032392E3520362032392E3520312E3520312E35
20312E3520312E352036222F3E3C7061746820636C6173733D22636C732D3422
20643D224D31392C33314831325631352E32346C2D31312D3956314833305636
2E32346C2D31312C395A6D2D362D3168355631342E37366C31312D3956324832
56352E37366C31312C395A222F3E3C2F673E3C2F7376673E}
Icons.FilterActiveIcon.Data = {
1054544D53464E435356474269746D6170E30200003C73766720786D6C6E733D
22687474703A2F2F7777772E77332E6F72672F323030302F7376672220786D6C
6E733A786C696E6B3D22687474703A2F2F7777772E77332E6F72672F31393939
2F786C696E6B222077696474683D22333222206865696768743D223332222076
696577426F783D22302030203332203332223E3C646566733E3C7374796C653E
2E636C732D317B66696C6C3A6E6F6E653B7D2E636C732D327B66696C6C3A2334
36383242343B7D2E636C732D337B66696C6C3A75726C28234E65775F50617474
65726E5F5377617463685F32293B7D2E636C732D347B66696C6C3A2333613361
33383B7D3C2F7374796C653E3C7061747465726E2069643D224E65775F506174
7465726E5F5377617463685F322220646174612D6E616D653D224E6577205061
747465726E205377617463682032222077696474683D22363822206865696768
743D22363822207061747465726E556E6974733D227573657253706163654F6E
557365222076696577426F783D22302030203638203638223E3C726563742063
6C6173733D22636C732D31222077696474683D22363822206865696768743D22
3638222F3E3C7265637420636C6173733D22636C732D32222077696474683D22
363822206865696768743D223638222F3E3C2F7061747465726E3E3C2F646566
733E3C7469746C653E46696C7465723C2F7469746C653E3C672069643D224963
6F6E223E3C706F6C79676F6E20636C6173733D22636C732D332220706F696E74
733D22312E3520362031322E352031352031322E352033302E352031382E3520
33302E352031382E352031352032392E3520362032392E3520312E3520312E35
20312E3520312E352036222F3E3C7061746820636C6173733D22636C732D3422
20643D224D31392C33314831325631352E32346C2D31312D3956314833305636
2E32346C2D31312C395A6D2D362D3168355631342E37366C31312D3956324832
56352E37366C31312C395A222F3E3C2F673E3C2F7376673E}
Icons.FilterClearIcon.Data = {
1054544D53464E435356474269746D61709D0400003C73766720786D6C6E733D
22687474703A2F2F7777772E77332E6F72672F323030302F7376672220786D6C
6E733A786C696E6B3D22687474703A2F2F7777772E77332E6F72672F31393939
2F786C696E6B222077696474683D22333222206865696768743D223332222076
696577426F783D22302030203332203332223E3C646566733E3C7374796C653E
2E636C732D317B66696C6C3A6E6F6E653B7D2E636C732D327B66696C6C3A2361
37346162303B7D2E636C732D337B66696C6C3A236433393464363B7D2E636C73
2D347B66696C6C3A236661666166613B7D2E636C732D357B66696C6C3A75726C
28234E65775F5061747465726E5F5377617463685F38293B7D2E636C732D367B
66696C6C3A75726C28234E65775F5061747465726E5F5377617463685F37293B
7D3C2F7374796C653E3C7061747465726E2069643D224E65775F506174746572
6E5F5377617463685F382220646174612D6E616D653D224E6577205061747465
726E205377617463682038222077696474683D22363822206865696768743D22
363822207061747465726E556E6974733D227573657253706163654F6E557365
222076696577426F783D22302030203638203638223E3C7265637420636C6173
733D22636C732D31222077696474683D22363822206865696768743D22363822
2F3E3C7265637420636C6173733D22636C732D33222077696474683D22363822
206865696768743D223638222F3E3C2F7061747465726E3E3C7061747465726E
2069643D224E65775F5061747465726E5F5377617463685F372220646174612D
6E616D653D224E6577205061747465726E205377617463682037222077696474
683D22363822206865696768743D22363822207061747465726E556E6974733D
227573657253706163654F6E557365222076696577426F783D22302030203638
203638223E3C7265637420636C6173733D22636C732D31222077696474683D22
363822206865696768743D223638222F3E3C7265637420636C6173733D22636C
732D32222077696474683D22363822206865696768743D223638222F3E3C2F70
61747465726E3E3C2F646566733E3C7469746C653E436C6561723C2F7469746C
653E3C672069643D2249636F6E223E3C7265637420636C6173733D22636C732D
342220783D22312E38362220793D2231302E3334222077696474683D2232382E
323822206865696768743D2231312E333122207472616E73666F726D3D227472
616E736C617465282D362E36332031362920726F74617465282D343529222F3E
3C706F6C79676F6E20636C6173733D22636C732D352220706F696E74733D2232
2E37312032322031302032392E32392031352E37392032332E3520382E352031
362E323120322E3731203232222F3E3C7061746820636C6173733D22636C732D
362220643D224D31302E37312C33306C32302D32304C32322C312E32392C312E
32392C32322C31302C33302E37315633314833315633305A4D32322C322E3731
2C32392E32392C31302C31362E352C32322E37392C392E32312C31352E355A4D
322E37312C32322C382E352C31362E32316C372E32392C372E32394C31302C32
392E32395A222F3E3C2F673E3C2F7376673E}
Icons.FilterTypeIcon.Data = {
1054544D53464E435356474269746D6170CB0700003C73766720786D6C6E733D
22687474703A2F2F7777772E77332E6F72672F323030302F7376672220786D6C
6E733A786C696E6B3D22687474703A2F2F7777772E77332E6F72672F31393939
2F786C696E6B222077696474683D22333222206865696768743D223332222076
696577426F783D22302030203332203332223E3C646566733E3C7374796C653E
2E636C732D317B66696C6C3A6E6F6E653B7D2E636C732D327B66696C6C3A2337
39373737343B7D2E636C732D337B66696C6C3A236661666166613B7D2E636C73
2D347B66696C6C3A233361336133383B7D2E636C732D357B66696C6C3A75726C
28234E65775F5061747465726E5F5377617463685F34293B7D3C2F7374796C65
3E3C7061747465726E2069643D224E65775F5061747465726E5F537761746368
5F342220646174612D6E616D653D224E6577205061747465726E205377617463
682034222077696474683D22363822206865696768743D223638222070617474
65726E556E6974733D227573657253706163654F6E557365222076696577426F
783D22302030203638203638223E3C7265637420636C6173733D22636C732D31
222077696474683D22363822206865696768743D223638222F3E3C7265637420
636C6173733D22636C732D32222077696474683D22363822206865696768743D
223638222F3E3C2F7061747465726E3E3C2F646566733E3C7469746C653E5465
78742D46696C7465723C2F7469746C653E3C672069643D224D61736B223E3C70
6F6C79676F6E20636C6173733D22636C732D332220706F696E74733D2232352E
35203920313920322E3520313920392032352E352039222F3E3C706174682063
6C6173733D22636C732D332220643D224D31382C313056324836563330483231
5632362E31384C31392E38342C323548385632344831382E38356C2D322D3248
3856323168385631394838563138683856313648385631354832347631683256
31305A4D382C396838763148385A6D302C3456313248323476315A222F3E3C70
6F6C79676F6E20636C6173733D22636C732D342220706F696E74733D22352031
2035203331203620333120323120333120323120333020362033302036203220
313820322031382E352032203139203220313920322E352032352E3520392032
36203920323620392E3520323620313020323620313620323720313620323720
392E372032372039203139203120352031222F3E3C706F6C79676F6E20636C61
73733D22636C732D342220706F696E74733D22323620313020323620392E3520
323620392032352E352039203139203920313920322E3520313920322031382E
3520322031382032203138203130203236203130222F3E3C7265637420636C61
73733D22636C732D332220783D22382220793D2239222077696474683D223822
206865696768743D2231222F3E3C7265637420636C6173733D22636C732D3522
20783D22382220793D2239222077696474683D223822206865696768743D2231
222F3E3C7265637420636C6173733D22636C732D332220783D22382220793D22
3132222077696474683D22313622206865696768743D2231222F3E3C72656374
20636C6173733D22636C732D352220783D22382220793D223132222077696474
683D22313622206865696768743D2231222F3E3C706F6C79676F6E20636C6173
733D22636C732D332220706F696E74733D223820313520382031362031362031
362032342031362032342031352038203135222F3E3C706F6C79676F6E20636C
6173733D22636C732D352220706F696E74733D22382031352038203136203136
2031362032342031362032342031352038203135222F3E3C7265637420636C61
73733D22636C732D332220783D22382220793D223138222077696474683D2238
22206865696768743D2231222F3E3C7265637420636C6173733D22636C732D35
2220783D22382220793D223138222077696474683D223822206865696768743D
2231222F3E3C706F6C79676F6E20636C6173733D22636C732D332220706F696E
74733D22382032322031362E38372032322031362032312E3132203136203231
20382032312038203232222F3E3C706F6C79676F6E20636C6173733D22636C73
2D352220706F696E74733D22382032322031362E38372032322031362032312E
313220313620323120382032312038203232222F3E3C706F6C79676F6E20636C
6173733D22636C732D332220706F696E74733D22382032352031392E38342032
352031382E383520323420382032342038203235222F3E3C706F6C79676F6E20
636C6173733D22636C732D352220706F696E74733D22382032352031392E3834
2032352031382E383520323420382032342038203235222F3E3C2F673E3C6720
69643D224F7665726C6179223E3C706F6C79676F6E20636C6173733D22636C73
2D332220706F696E74733D2233312E352031372E352031372E352031372E3520
31372E352032302E352032322E352032352E35362032322E352033312E352032
362E352033312E352032362E352032352E35362033312E352032302E35203331
2E352031372E35222F3E3C7061746820636C6173733D22636C732D342220643D
224D32372C33324832325632352E37376C2D352D352E30365631374833327633
2E37316C2D352C352E30365A6D2D342D3168335632352E33366C352D352E3037
56313848313876322E32396C352C352E30375A222F3E3C2F673E3C2F7376673E}
Icons.SortAscendingIcon.Data = {
1054544D53464E435356474269746D6170990300003C73766720786D6C6E733D
22687474703A2F2F7777772E77332E6F72672F323030302F7376672220786D6C
6E733A786C696E6B3D22687474703A2F2F7777772E77332E6F72672F31393939
2F786C696E6B222077696474683D22333222206865696768743D223332222076
696577426F783D22302030203332203332223E3C646566733E3C7374796C653E
2E636C732D317B66696C6C3A6E6F6E653B7D2E636C732D327B66696C6C3A2361
37346162303B7D2E636C732D337B66696C6C3A75726C28234E65775F50617474
65726E5F5377617463685F37293B7D2E636C732D347B66696C6C3A2331653862
63643B7D2E636C732D357B66696C6C3A233361336133383B7D3C2F7374796C65
3E3C7061747465726E2069643D224E65775F5061747465726E5F537761746368
5F372220646174612D6E616D653D224E6577205061747465726E205377617463
682037222077696474683D22363822206865696768743D223638222070617474
65726E556E6974733D227573657253706163654F6E557365222076696577426F
783D22302030203638203638223E3C7265637420636C6173733D22636C732D31
222077696474683D22363822206865696768743D223638222F3E3C7265637420
636C6173733D22636C732D32222077696474683D22363822206865696768743D
223638222F3E3C2F7061747465726E3E3C2F646566733E3C7469746C653E536F
72742D415A3C2F7469746C653E3C672069643D2249636F6E223E3C7061746820
636C6173733D22636C732D332220643D224D322C32392E36346C372D31305632
304833563138683976312E34344C352C32392E33365632396837763248325A22
2F3E3C7061746820636C6173733D22636C732D342220643D224D352E372C3131
2C342E35322C313548312E39334C362E33332C3148392E35344C31342C313568
2D322E376C2D312E32342D345A4D392E36342C392E31312C382E35362C352E36
36632D2E32372D2E38352D2E34392D312E382D2E36392D322E36316830632D2E
322E38312D2E342C312E37382D2E36352C322E36314C362E31312C392E31315A
222F3E3C706F6C79676F6E20636C6173733D22636C732D352220706F696E7473
3D2232332032352E3132203233203620323220362032322032352E3132203136
2E34342031392E35362031352E35362032302E34342032322E352032372E3338
2032392E34342032302E34342032382E35362031392E35362032332032352E31
32222F3E3C2F673E3C2F7376673E}
Icons.SortDescendingIcon.Data = {
1054544D53464E435356474269746D6170990300003C73766720786D6C6E733D
22687474703A2F2F7777772E77332E6F72672F323030302F7376672220786D6C
6E733A786C696E6B3D22687474703A2F2F7777772E77332E6F72672F31393939
2F786C696E6B222077696474683D22333222206865696768743D223332222076
696577426F783D22302030203332203332223E3C646566733E3C7374796C653E
2E636C732D317B66696C6C3A6E6F6E653B7D2E636C732D327B66696C6C3A2361
37346162303B7D2E636C732D337B66696C6C3A75726C28234E65775F50617474
65726E5F5377617463685F37293B7D2E636C732D347B66696C6C3A2331653862
63643B7D2E636C732D357B66696C6C3A233361336133383B7D3C2F7374796C65
3E3C7061747465726E2069643D224E65775F5061747465726E5F537761746368
5F372220646174612D6E616D653D224E6577205061747465726E205377617463
682037222077696474683D22363822206865696768743D223638222070617474
65726E556E6974733D227573657253706163654F6E557365222076696577426F
783D22302030203638203638223E3C7265637420636C6173733D22636C732D31
222077696474683D22363822206865696768743D223638222F3E3C7265637420
636C6173733D22636C732D32222077696474683D22363822206865696768743D
223638222F3E3C2F7061747465726E3E3C2F646566733E3C7469746C653E536F
72742D5A413C2F7469746C653E3C672069643D2249636F6E223E3C7061746820
636C6173733D22636C732D332220643D224D322C31322E36346C372D31305633
48335631683956322E34344C352C31322E33365631326837763248325A222F3E
3C7061746820636C6173733D22636C732D342220643D224D352E372C32372C34
2E35322C333148312E39336C342E342D313448392E35344C31342C3331682D32
2E376C2D312E32342D345A6D332E39342D312E39334C382E35362C32312E3636
632D2E32372D2E38352D2E34392D312E382D2E36392D322E36316830632D2E32
2E38312D2E342C312E37382D2E36352C322E36314C362E31312C32352E31315A
222F3E3C706F6C79676F6E20636C6173733D22636C732D352220706F696E7473
3D2232332032352E3132203233203620323220362032322032352E3132203136
2E34342031392E35362031352E35362032302E34342032322E352032372E3338
2032392E34342032302E34342032382E35362031392E35362032332032352E31
32222F3E3C2F673E3C2F7376673E}
Icons.CloseIcon.Data = {
1054544D53464E435356474269746D6170180400003C21444F43545950452073
7667205055424C494320222D2F2F5733432F2F4454442053564720312E312F2F
454E222022687474703A2F2F7777772E77332E6F72672F47726170686963732F
5356472F312E312F4454442F73766731312E647464223E0A0D3C212D2D205570
6C6F6164656420746F3A20535647205265706F2C207777772E7376677265706F
2E636F6D2C205472616E73666F726D65642062793A20535647205265706F204D
6978657220546F6F6C73202D2D3E0A3C7376672077696474683D223634707822
206865696768743D2236347078222076696577426F783D223020302032342032
34222066696C6C3D226E6F6E652220786D6C6E733D22687474703A2F2F777777
2E77332E6F72672F323030302F73766722207374726F6B653D22233030303030
30223E0A0D3C672069643D225356475265706F5F626743617272696572222073
74726F6B652D77696474683D2230222F3E0A0D3C672069643D22535647526570
6F5F7472616365724361727269657222207374726F6B652D6C696E656361703D
22726F756E6422207374726F6B652D6C696E656A6F696E3D22726F756E64222F
3E0A0D3C672069643D225356475265706F5F69636F6E43617272696572223E20
3C706174682066696C6C2D72756C653D226576656E6F64642220636C69702D72
756C653D226576656E6F64642220643D224D352E323932383920352E32393238
3943352E363833343220342E393032333720362E333136353820342E39303233
3720362E373037313120352E32393238394C31322031302E353835384C31372E
3239323920352E32393238394331372E3638333420342E39303233372031382E
3331363620342E39303233372031382E3730373120352E32393238394331392E
3039373620352E36383334322031392E3039373620362E33313635382031382E
3730373120362E37303731314C31332E343134322031324C31382E3730373120
31372E323932394331392E303937362031372E363833342031392E3039373620
31382E333136362031382E373037312031382E373037314331382E3331363620
31392E303937362031372E363833342031392E303937362031372E3239323920
31382E373037314C31322031332E343134324C362E37303731312031382E3730
373143362E33313635382031392E3039373620352E36383334322031392E3039
373620352E32393238392031382E3730373143342E39303233372031382E3331
363620342E39303233372031372E3638333420352E32393238392031372E3239
32394C31302E353835382031324C352E323932383920362E373037313143342E
393032333720362E333136353820342E393032333720352E363833343220352E
323932383920352E32393238395A222066696C6C3D2223304631373239222F3E
203C2F673E0A0D3C2F7376673E}
Icons.FirstPageIcon.Data = {
1054544D53464E435356474269746D6170800300003C3F786D6C207665727369
6F6E3D22312E3022203F3E3C21444F43545950452073766720205055424C4943
20272D2F2F5733432F2F4454442053564720312E312F2F454E27202027687474
703A2F2F7777772E77332E6F72672F47726170686963732F5356472F312E312F
4454442F73766731312E647464273E3C73766720656E61626C652D6261636B67
726F756E643D226E65772030203020333220333222206865696768743D223332
7078222069643D22D0A1D0BBD0BED0B95F31222076657273696F6E3D22312E31
222076696577426F783D22302030203332203332222077696474683D22333270
782220786D6C3A73706163653D2270726573657276652220786D6C6E733D2268
7474703A2F2F7777772E77332E6F72672F323030302F7376672220786D6C6E73
3A786C696E6B3D22687474703A2F2F7777772E77332E6F72672F313939392F78
6C696E6B223E3C672069643D22446F75626C655F43686576726F6E5F4C656674
223E3C7061746820643D224D31302E3435362C31366C362E3139362D362E3238
3563302E3339312D302E3339342C302E3339312D312E3033342C302D312E3432
38632D302E3339312D302E3339342D312E3032342D302E3339342D312E343134
2C306C2D362E3839392C362E393939202020632D302E3337352C302E3337392D
302E3337372C312E3034382C302C312E3432396C362E392C362E39393963302E
33392C302E3339342C312E3032342C302E3339342C312E3431342C3063302E33
39312D302E3339342C302E3339312D312E3033342C302D312E3432384C31302E
3435362C31367A222066696C6C3D2223313231333133222F3E3C706174682064
3D224D31372E3435362C31366C362E3139362D362E32383563302E3339312D30
2E3339342C302E3339312D312E3033342C302D312E343238632D302E3339312D
302E3339342D312E3032342D302E3339342D312E3431342C306C2D362E383939
2C362E393939202020632D302E3337352C302E3337392D302E3337372C312E30
34382C302C312E3432396C362E3839392C362E39393963302E3339312C302E33
39342C312E3032342C302E3339342C312E3431342C3063302E3339312D302E33
39342C302E3339312D312E3033342C302D312E3432384C31372E3435362C3136
7A222066696C6C3D2223313231333133222F3E3C2F673E3C672F3E3C672F3E3C
672F3E3C672F3E3C672F3E3C672F3E3C2F7376673E}
Icons.LastPageIcon.Data = {
1054544D53464E435356474269746D6170A60300003C3F786D6C207665727369
6F6E3D22312E3022203F3E3C21444F43545950452073766720205055424C4943
20272D2F2F5733432F2F4454442053564720312E312F2F454E27202027687474
703A2F2F7777772E77332E6F72672F47726170686963732F5356472F312E312F
4454442F73766731312E647464273E3C73766720656E61626C652D6261636B67
726F756E643D226E65772030203020333220333222206865696768743D223332
7078222069643D22D0A1D0BBD0BED0B95F31222076657273696F6E3D22312E31
222076696577426F783D22302030203332203332222077696474683D22333270
782220786D6C3A73706163653D2270726573657276652220786D6C6E733D2268
7474703A2F2F7777772E77332E6F72672F323030302F7376672220786D6C6E73
3A786C696E6B3D22687474703A2F2F7777772E77332E6F72672F313939392F78
6C696E6B223E3C672069643D22446F75626C655F43686576726F6E5F52696768
74223E3C7061746820643D224D32332E3636322C31352E3238366C2D362E392D
362E393939632D302E33392D302E3339342D312E3032342D302E3339342D312E
3431342C30632D302E3339312C302E3339342D302E3339312C312E3033342C30
2C312E3432384C32312E3534342C31362020206C2D362E3139362C362E323835
632D302E3339312C302E3339342D302E3339312C312E3033342C302C312E3432
3863302E3339312C302E3339342C312E3032342C302E3339342C312E3431342C
306C362E3839392D362E3939392020204332342E3033382C31362E3333352C32
342E3033392C31352E3636362C32332E3636322C31352E3238367A222066696C
6C3D2223313231333133222F3E3C7061746820643D224D31362E3636322C3135
2E3238364C392E3736332C382E323837632D302E3339312D302E3339342D312E
3032342D302E3339342D312E3431342C30632D302E3339312C302E3339342D30
2E3339312C312E3033342C302C312E3432384C31342E3534342C31362020206C
2D362E3139362C362E323835632D302E3339312C302E3339342D302E3339312C
312E3033342C302C312E34323863302E3339312C302E3339342C312E3032342C
302E3339342C312E3431342C306C362E3839392D362E3939392020204331372E
3033382C31362E3333352C31372E3033392C31352E3636362C31362E3636322C
31352E3238367A222066696C6C3D2223313231333133222F3E3C2F673E3C672F
3E3C672F3E3C672F3E3C672F3E3C672F3E3C672F3E3C2F7376673E}
Icons.NextPageIcon.Data = {
1054544D53464E435356474269746D6170B50200003C3F786D6C207665727369
6F6E3D22312E3022203F3E3C21444F43545950452073766720205055424C4943
20272D2F2F5733432F2F4454442053564720312E312F2F454E27202027687474
703A2F2F7777772E77332E6F72672F47726170686963732F5356472F312E312F
4454442F73766731312E647464273E3C73766720656E61626C652D6261636B67
726F756E643D226E65772030203020333220333222206865696768743D223332
7078222069643D22D0A1D0BBD0BED0B95F31222076657273696F6E3D22312E31
222076696577426F783D22302030203332203332222077696474683D22333270
782220786D6C3A73706163653D2270726573657276652220786D6C6E733D2268
7474703A2F2F7777772E77332E6F72672F323030302F7376672220786D6C6E73
3A786C696E6B3D22687474703A2F2F7777772E77332E6F72672F313939392F78
6C696E6B223E3C7061746820636C69702D72756C653D226576656E6F64642220
643D224D32312E3639382C31352E3238366C2D392E3030322D382E3939392020
632D302E3339352D302E3339342D312E3033352D302E3339342D312E3433312C
30632D302E3339352C302E3339342D302E3339352C312E3033342C302C312E34
32384C31392E3535332C31366C2D382E3238372C382E323835632D302E333935
2C302E3339342D302E3339352C312E3033342C302C312E343239202063302E33
39352C302E3339342C312E3033362C302E3339342C312E3433312C306C392E30
30322D382E3939394332322E3038382C31362E3332352C32322E3038382C3135
2E3637352C32312E3639382C31352E3238367A222066696C6C3D222331323133
3133222066696C6C2D72756C653D226576656E6F6464222069643D2243686576
726F6E5F5269676874222F3E3C672F3E3C672F3E3C672F3E3C672F3E3C672F3E
3C672F3E3C2F7376673E}
Icons.PreviousPageIcon.Data = {
1054544D53464E435356474269746D6170B20200003C3F786D6C207665727369
6F6E3D22312E3022203F3E3C21444F43545950452073766720205055424C4943
20272D2F2F5733432F2F4454442053564720312E312F2F454E27202027687474
703A2F2F7777772E77332E6F72672F47726170686963732F5356472F312E312F
4454442F73766731312E647464273E3C73766720656E61626C652D6261636B67
726F756E643D226E65772030203020333220333222206865696768743D223332
7078222069643D22D0A1D0BBD0BED0B95F31222076657273696F6E3D22312E31
222076696577426F783D22302030203332203332222077696474683D22333270
782220786D6C3A73706163653D2270726573657276652220786D6C6E733D2268
7474703A2F2F7777772E77332E6F72672F323030302F7376672220786D6C6E73
3A786C696E6B3D22687474703A2F2F7777772E77332E6F72672F313939392F78
6C696E6B223E3C7061746820636C69702D72756C653D226576656E6F64642220
643D224D31312E3236322C31362E3731346C392E3030322C382E393939202063
302E3339352C302E3339342C312E3033352C302E3339342C312E3433312C3063
302E3339352D302E3339342C302E3339352D312E3033342C302D312E3432384C
31332E3430372C31366C382E3238372D382E32383563302E3339352D302E3339
342C302E3339352D312E3033342C302D312E3432392020632D302E3339352D30
2E3339342D312E3033362D302E3339342D312E3433312C306C2D392E3030322C
382E3939394331302E3837322C31352E3637352C31302E3837322C31362E3332
352C31312E3236322C31362E3731347A222066696C6C3D222331323133313322
2066696C6C2D72756C653D226576656E6F6464222069643D2243686576726F6E
5F5269676874222F3E3C672F3E3C672F3E3C672F3E3C672F3E3C672F3E3C672F
3E3C2F7376673E}
end
end
object xdwcTasks: TXDataWebClient
Connection = DMConnection.ApiConnection
Left = 690
Top = 246
end
end
<div class="d-flex flex-column flex-grow-1 h-100">
<div class="row flex-grow-1 h-100">
<div class="col-12 flex-grow-1 h-100">
<div id="pnl_grid" class="h-100"></div>
</div>
</div>
</div>
unit View.Tasks;
interface
uses
System.SysUtils, System.Classes, System.Variants,
JS, Web, WEBLib.Graphics, WEBLib.Controls, WEBLib.Forms, WEBLib.Dialogs,
Vcl.Controls, WebLib.JSON,
VCL.TMSFNCTypes, VCL.TMSFNCUtils, VCL.TMSFNCGraphics, VCL.TMSFNCGraphicsTypes,
VCL.TMSFNCDataGridCell, VCL.TMSFNCDataGridData, VCL.TMSFNCDataGridBase,
VCL.TMSFNCDataGridCore, VCL.TMSFNCDataGridRenderer, VCL.TMSFNCCustomControl,
VCL.TMSFNCDataGrid,
XData.Web.Client,
Utils, System.Rtti, WEBLib.ExtCtrls;
type
TFTasks = class(TWebForm)
xdwcTasks: TXDataWebClient;
pnlGrid: TWebPanel;
grdTasks: TTMSFNCDataGrid;
procedure WebFormCreate(Sender: TObject);
private
procedure SetupGrid;
[async] procedure LoadTasks(const AProjectId: string);
procedure AddComboColumn(ACol: Integer; const Items: array of string);
procedure AddDateColumn(ACol: Integer);
public
end;
var
FTasks: TFTasks;
implementation
uses
ConnectionModule;
{$R *.dfm}
const
COL_TASK_ITEM_ID = 0;
COL_TASK_ID = 1;
COL_APP = 2;
COL_VERSION = 3;
COL_TASK_DATE = 4;
COL_REPORTED_BY = 5;
COL_ASSIGNED_TO = 6;
COL_STATUS = 7;
COL_STATUS_DATE = 8;
COL_FIXED_VER = 9;
COL_FORM_SECTION = 10;
COL_ISSUE = 11;
COL_NOTES = 12;
procedure TFTasks.WebFormCreate(Sender: TObject);
begin
xdwcTasks.Connection := DMConnection.ApiConnection;
SetupGrid;
LoadTasks('WPR0001');
end;
procedure TFTasks.SetupGrid;
begin
grdTasks.BeginUpdate;
try
grdTasks.Options.IO.StartColumn := 1;
grdTasks.Options.IO.StartRow := 1;
grdTasks.RowCount := 1;
grdTasks.ColumnCount := 13;
grdTasks.DefaultRowHeight := 100;
grdTasks.Options.Keyboard.TabKeyDirectEdit := True;
grdTasks.Options.Keyboard.ArrowKeyDirectEdit := True;
grdTasks.Options.Keyboard.EnterKeyDirectEdit := True;
grdTasks.Options.Editing.Enabled := True;
grdTasks.Columns[COL_TASK_ITEM_ID].Width := 110;
grdTasks.Columns[COL_TASK_ID].Width := 90;
grdTasks.Columns[COL_APP].Width := 220;
grdTasks.Columns[COL_VERSION].Width := 80;
grdTasks.Columns[COL_TASK_DATE].Width := 110;
grdTasks.Columns[COL_REPORTED_BY].Width := 110;
grdTasks.Columns[COL_ASSIGNED_TO].Width := 110;
grdTasks.Columns[COL_STATUS].Width := 140;
grdTasks.Columns[COL_STATUS_DATE].Width := 110;
grdTasks.Columns[COL_FIXED_VER].Width := 110;
grdTasks.Columns[COL_FORM_SECTION].Width := 160;
grdTasks.Columns[COL_ISSUE].Width := 650;
grdTasks.Columns[COL_NOTES].Width := 450;
grdTasks.Cells[COL_TASK_ITEM_ID, 0] := 'Task Item ID';
grdTasks.Cells[COL_TASK_ID, 0] := 'Task ID';
grdTasks.Cells[COL_APP, 0] := 'Application';
grdTasks.Cells[COL_VERSION, 0] := 'Version';
grdTasks.Cells[COL_TASK_DATE, 0] := 'Date';
grdTasks.Cells[COL_REPORTED_BY, 0] := 'Reported By';
grdTasks.Cells[COL_ASSIGNED_TO, 0] := 'Assigned To';
grdTasks.Cells[COL_STATUS, 0] := 'Status';
grdTasks.Cells[COL_STATUS_DATE, 0] := 'Status Date';
grdTasks.Cells[COL_FIXED_VER, 0] := 'Fixed Version';
grdTasks.Cells[COL_FORM_SECTION, 0] := 'Form (Section)';
grdTasks.Cells[COL_ISSUE, 0] := 'Issue';
grdTasks.Cells[COL_NOTES, 0] := 'Notes';
grdTasks.Columns[COL_TASK_ITEM_ID].ReadOnly := True;
grdTasks.Columns[COL_TASK_ID].ReadOnly := True;
AddComboColumn(COL_APP, ['webPoliceReports - Client', 'webPoliceReports - Server']);
AddComboColumn(COL_VERSION, ['0.9.4', '0.9.5']);
AddComboColumn(COL_REPORTED_BY, ['Mark', 'Mac']);
AddComboColumn(COL_ASSIGNED_TO, ['Mac', 'Mark']);
AddComboColumn(COL_STATUS, ['Open', 'In Progress', 'Blocked', 'Fixed', 'Fixed - Verified']);
AddComboColumn(COL_FIXED_VER, ['0.9.4', '0.9.5']);
AddDateColumn(COL_TASK_DATE);
AddDateColumn(COL_STATUS_DATE);
finally
grdTasks.EndUpdate;
end;
end;
procedure TFTasks.AddComboColumn(ACol: Integer; const Items: array of string);
var
I: Integer;
begin
grdTasks.Columns[ACol].AddSetting(gcsEditor);
grdTasks.Columns[ACol].AddSetting(gcsEditorItems);
grdTasks.Columns[ACol].Editor := getComboBox;
grdTasks.Columns[ACol].EditorItems.Clear;
for I := Low(Items) to High(Items) do
grdTasks.Columns[ACol].EditorItems.Add(Items[I]);
end;
procedure TFTasks.AddDateColumn(ACol: Integer);
begin
grdTasks.Columns[ACol].AddSetting(gcsEditor);
grdTasks.Columns[ACol].Editor := getDatePicker;
grdTasks.Columns[ACol].AddSetting(gcsFormatting);
grdTasks.Columns[ACol].Formatting.&Type := gdftDateTime;
end;
[async] procedure TFTasks.LoadTasks(const AProjectId: string);
var
response: TXDataClientResponse;
resultObj: TJSObject;
tasksArray: TJSArray;
taskObj: TJSObject;
itemsArray: TJSArray;
itemObj: TJSObject;
taskIndex, itemIndex: Integer;
gridRow: Integer;
statusDateVal: JSValue;
r: Integer;
begin
Utils.ShowSpinner('spinner');
try
try
response := await(xdwcTasks.RawInvokeAsync(
'IApiService.GetProjectTasks', [AProjectId]
));
if not Assigned(response.Result) then
Exit;
resultObj := TJSObject(response.Result);
tasksArray := TJSArray(resultObj['data']);
grdTasks.BeginUpdate;
try
grdTasks.RowCount := 1;
gridRow := 1;
for taskIndex := 0 to tasksArray.Length - 1 do
begin
taskObj := TJSObject(tasksArray[taskIndex]);
itemsArray := TJSArray(taskObj['items']);
for itemIndex := 0 to itemsArray.Length - 1 do
begin
itemObj := TJSObject(itemsArray[itemIndex]);
grdTasks.RowCount := gridRow + 1;
grdTasks.Cells[COL_TASK_ITEM_ID, gridRow] := string(itemObj['taskItemId']);
grdTasks.Cells[COL_TASK_ID, gridRow] := string(itemObj['taskId']);
grdTasks.Cells[COL_APP, gridRow] := string(itemObj['application']);
grdTasks.Cells[COL_VERSION, gridRow] := string(itemObj['version']);
grdTasks.Cells[COL_TASK_DATE, gridRow] := string(itemObj['taskDate']);
grdTasks.Cells[COL_REPORTED_BY, gridRow] := string(itemObj['reportedBy']);
grdTasks.Cells[COL_ASSIGNED_TO, gridRow] := string(itemObj['assignedTo']);
grdTasks.Cells[COL_STATUS, gridRow] := string(itemObj['status']);
statusDateVal := itemObj['statusDate'];
if JS.isNull(statusDateVal) or JS.isUndefined(statusDateVal) then
grdTasks.Cells[COL_STATUS_DATE, gridRow] := ''
else
grdTasks.Cells[COL_STATUS_DATE, gridRow] := string(statusDateVal);
grdTasks.Cells[COL_FIXED_VER, gridRow] := string(itemObj['fixedVersion']);
grdTasks.Cells[COL_FORM_SECTION, gridRow] := string(itemObj['formSection']);
grdTasks.Cells[COL_ISSUE, gridRow] := string(itemObj['issue']);
grdTasks.Cells[COL_NOTES, gridRow] := string(itemObj['notes']);
Inc(gridRow);
end;
end;
finally
grdTasks.EndUpdate;
end;
grdTasks.Height := Trunc((grdTasks.RowCount * grdTasks.DefaultRowHeight) + 2);
except
on E: EXDataClientRequestException do
Utils.ShowErrorModal(E.ErrorResult.ErrorMessage);
end;
finally
Utils.HideSpinner('spinner');
end;
end;
end.
{
"AuthUrl" : "http://localhost:2004/kgOrders/auth/",
"ApiUrl" : "http://localhost:2004/kgOrders/api/"
}
.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;
}
input[type="text"] {
min-width: 50px;
max-width: 100%;
width: auto;
padding-left: 5px;
}
is-invalid .form-check-input {
border: 1px solid #dc3545 !important;
}
.is-invalid .form-check-label {
color: #dc3545 !important;
}
.input-search input {
width: 100px; /* Adjust the width of the input */
height: 35px; /* Set the height to match label height */
padding: 5px; /* Padding for input text */
font-size: 14px; /* Font size for input text */
border: 1px solid #ced4da; /* Border style */
border-radius: 5px; /* Rounded corners for the input */
box-sizing: border-box; /* Ensures padding and border are included in the element's total width and height */
}
.card-header {
width: 100%;
text-align: left; /* Align text to the left */
background-color: #f8f9fa; /* Match the card background */
padding: 0.75rem 1.25rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
border-top-left-radius: 10px;
border-top-right-radius: 10px;
margin: 0; /* Remove any margin */
box-sizing: border-box; /* Ensure padding is included in the element's total width and height */
}
/* Ensure that the title does not affect the navbar layout */
#view.main.apptitle {
display: flex;
align-items: center;
justify-content: flex-start; /* Align title to the left */
width: auto; /* Ensure it doesn't stretch the container */
margin-right: 20px; /* Optional: add space between title and navbar items */
}
/* Fixed width for title area to prevent shifting */
#title {
white-space: nowrap; /* Prevent the title text from wrapping */
width: 200px; /* Fixed width for the title */
text-overflow: ellipsis; /* Truncate text with ellipsis if it overflows */
overflow: hidden;
font-weight: bold; /* Optional: make the title text bold */
}
/* Navbar items - keep them aligned and spaced out */
.navbar-nav .nav-item {
padding: 0 15px; /* Adjust spacing between navbar items */
z-index: 9999;
}
/* Flexbox for the entire navbar */
.navbar-nav {
display: flex;
justify-content: flex-end; /* Align navbar items to the right */
width: 100%;
}
/* Additional mobile responsiveness (optional) */
@media (max-width: 1200px) {
.navbar-nav {
flex-direction: column; /* Stack items vertically on smaller screens */
align-items: flex-start; /* Align items to the left */
}
.navbar-nav-spaced .nav-item {
padding: 10px 0; /* Adjust vertical spacing between items */
}
}
/* Make sure active navbar item color gets applied */
.navbar-nav .nav-item a.active {
color: #fff !important; /* Set text color to white for active item */
background-color: #004F84 !important; /* Darker blue for active background */
font-weight: bold;
}
/* Default navbar item color */
.navbar-nav .nav-item a {
color: #000 !important; /* Default color for links */
transition: color 0.3s ease;
}
/* Navbar item hover state */
.navbar-nav .nav-item a:hover {
color: #fff !important; /* Set hover text color to white */
background-color: #286090 !important; /* Light blue on hover */
}
.nav-item {
padding: 0 20px; /* Adjust this value for desired spacing between labels */
}
.mr-2 {
margin-right: 0.5rem;
}
.custom-h4 {
margin-bottom: 5px;
}
.custom-hr {
margin-top: 5px;
margin-bottom: 10px;
}
.custom-select-large {
font-size: 1.25rem;
padding: 0.5rem;
height: 2.5rem;
}
.player-container {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #fff;
border: 1px solid #ccc;
padding: 20px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
z-index: 1000;
}
.player-container audio {
width: 100%;
}
.close-btn {
position: absolute;
top: 10px;
right: 10px;
cursor: pointer;
}
.card-title {
margin: 0;
font-size: 1.25rem; /* Adjust font size as needed */
}
.card-body {
padding: 2rem;
}
.table tbody tr:hover {
background-color: #d1e7fd; /* Light blue color for hover effect */
cursor: pointer;
margin-top: 5px;
}
.form-input{
display: table;
}
.form-cells{
display: table-cell
}
.table tbody tr {
transition: background-color 0.3s ease;
}
.table thead th{
border: 2px;
background-color: #e6e6e6;
}
.table thead {
border-color: #e6e6e6;
}
.min-w-80 {
min-width: 80px;
}
.table {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-radius: 5px;
}
.navbar-nav {
margin-left: auto; /* Align the dropdown to the right */
}
.container {
margin-top: 50px; /* Adjust the top margin as needed */
}
@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 */
}
}
.btn-primary {
background-color: #286090 !important;
border-color: #286090 !important;
color: #fff !important;
}
.btn-primary:hover {
background-color: #204d74 !important;
border-color: #204d74 !important;
}
.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;
}
.card {
border: none !important;
box-shadow: none !important; /* If shadow is causing the issue */
}
.color-column {
width: 200px; /* Fixed width for the column */
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis; /* Adds ellipsis */
}
.grid-container {
position: relative; /* Ensure the container is the reference for child positioning */
height: 400px; /* Set the height for the grid */
width: 100%; /* Full width of the parent container */
overflow: hidden; /* Prevent unintended overflow */
}
#TFAddOrder_TMSFNCGrid2 {
top: 0px !important; /* Reset the top offset */
left: 0px !important; /* Reset the left offset */
position: absolute !important; /* Use relative positioning to align with the grid container */
}
#TFAddOrder_TMSFNCGrid2_Canvas {
position: relative !important; /* Ensure absolute positioning within the parent */
width: 100% !important; /* Make the canvas fill the container width */
height: 100% !important; /* Make the canvas fill the container height */
left: 0px !important; /* Align canvas to the left */
}
#TFAddOrder_ScrollBar5 {
top: 0px !important; /* Align scrollbar to the top */
right: 0px !important; /* Align scrollbar to the right edge */
position: absolute !important; /* Position within the grid container */
height: 400px !important; /* Match the height of the grid container */
box-sizing: border-box !important; /* Prevent borders from affecting dimensions */
}
#tblPhoneGrid {
--bs-table-bg: #fff;
--bs-table-border-color: #dee2e6; /* or whatever border tone you like */
}
/* and make sure the table collapses borders so there are no gaps */
#tblPhoneGrid {
border-collapse: collapse !important;
}
@keyframes slideInLeft {
from {
transform: translateX(-120%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.toast.slide-in {
animation: slideInLeft 0.4s ease-out forwards;
}
#spinner {
position: fixed !important;
z-index: 9999 !important;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.tbl-min-160 {
min-width: 160px;
}
.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: #204d74;
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);
}
}
program emT3webClient;
uses
Vcl.Forms,
XData.Web.Connection,
WEBLib.Dialogs,
Auth.Service in 'Auth.Service.pas',
App.Types in 'App.Types.pas',
ConnectionModule in 'ConnectionModule.pas' {DMConnection: TWebDataModule},
View.Login in 'View.Login.pas' {FViewLogin: TWebForm} {*.html},
App.Config in 'App.Config.pas',
View.Main in 'View.Main.pas' {FViewMain: TWebForm} {*.html},
Utils in 'Utils.pas',
View.Tasks in 'View.Tasks.pas' {FTasks: TWebForm} {*.html};
{$R *.res}
procedure DisplayLoginView(AMessage: string = ''); forward;
procedure DisplayMainView;
procedure ConnectProc;
begin
if Assigned(FViewLogin) then
FViewLogin.Free;
TFViewMain.Display(@DisplayLoginView);
end;
begin
if not DMConnection.ApiConnection.Connected then
DMConnection.ApiConnection.Open(@ConnectProc)
else
ConnectProc;
end;
procedure DisplayLoginView(AMessage: string);
begin
AuthService.Logout;
DMConnection.ApiConnection.Connected := False;
if Assigned(FViewMain) then
FViewMain.Free;
TFViewLogin.Display(@DisplayMainView, AMessage);
end;
procedure UnauthorizedAccessProc(AMessage: string);
begin
DisplayLoginView(AMessage);
end;
procedure StartApplication;
begin
DMConnection.InitApp(
procedure
begin
DMConnection.SetClientConfig(
procedure(Success: Boolean; ErrorMessage: string)
begin
if Success then
begin
if (not AuthService.Authenticated) or AuthService.TokenExpired then
DisplayLoginView
else
DisplayMainView;
end
else
begin
asm
var dlg = document.createElement("dialog");
dlg.classList.add("shadow", "rounded", "border", "p-4");
dlg.style.maxWidth = "500px";
dlg.style.width = "90%";
dlg.style.fontFamily = "system-ui, sans-serif";
dlg.innerHTML =
"<h5 class='fw-bold mb-3 text-danger'>kgOrders web app</h5>" +
"<p class='mb-3' style='white-space: pre-wrap;'>" + ErrorMessage + "</p>" +
"<div class='text-end'>" +
"<button id='refreshBtn' class='btn btn-primary'>Reload</button></div>";
document.body.appendChild(dlg);
dlg.showModal();
document.getElementById("refreshBtn").addEventListener("click", function () {
location.reload(true); // Hard refresh
});
end;
end;
end);
end,
@UnauthorizedAccessProc
);
end;
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TDMConnection, DMConnection);
StartApplication;
Application.Run;
end.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{DB6F5DBF-7E4B-45DA-AFFA-6C8DF15BA740}</ProjectGuid>
<ProjectVersion>20.3</ProjectVersion>
<FrameworkType>VCL</FrameworkType>
<MainSource>emT3webClient.dpr</MainSource>
<Base>True</Base>
<Config Condition="'$(Config)'==''">Debug</Config>
<Platform Condition="'$(Platform)'==''">Win32</Platform>
<TargetedPlatforms>1</TargetedPlatforms>
<AppType>Application</AppType>
<ProjectName Condition="'$(ProjectName)'==''">emT3webClient</ProjectName>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Base)'=='true') or '$(Base_Win32)'!=''">
<Base_Win32>true</Base_Win32>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Base)'=='true') or '$(Base_Win64)'!=''">
<Base_Win64>true</Base_Win64>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_1)'!=''">
<Cfg_1>true</Cfg_1>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_1)'=='true') or '$(Cfg_1_Win32)'!=''">
<Cfg_1_Win32>true</Cfg_1_Win32>
<CfgParent>Cfg_1</CfgParent>
<Cfg_1>true</Cfg_1>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_2)'!=''">
<Cfg_2>true</Cfg_2>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_2)'=='true') or '$(Cfg_2_Win32)'!=''">
<Cfg_2_Win32>true</Cfg_2_Win32>
<CfgParent>Cfg_2</CfgParent>
<Cfg_2>true</Cfg_2>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Base)'!=''">
<DCC_DcuOutput>.\$(Platform)\$(Config)</DCC_DcuOutput>
<DCC_ExeOutput>.\$(Platform)\$(Config)</DCC_ExeOutput>
<DCC_E>false</DCC_E>
<DCC_N>false</DCC_N>
<DCC_S>false</DCC_S>
<DCC_F>false</DCC_F>
<DCC_K>false</DCC_K>
<DCC_UsePackage>RESTComponents;emsclientfiredac;DataSnapFireDAC;FireDACIBDriver;xdata;emsclient;FireDACCommon;RESTBackendComponents;soapserver;CloudService;FireDACCommonDriver;inet;FireDAC;FireDACSqliteDriver;soaprtl;soapmidas;aurelius;$(DCC_UsePackage)</DCC_UsePackage>
<DCC_Namespace>System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace)</DCC_Namespace>
<Icon_MainIcon>$(BDS)\bin\delphi_PROJECTICON.ico</Icon_MainIcon>
<Icns_MainIcns>$(BDS)\bin\delphi_PROJECTICNS.icns</Icns_MainIcns>
<SanitizedProjectName>emT3webClient</SanitizedProjectName>
<VerInfo_Locale>1046</VerInfo_Locale>
<TMSWebProject>2</TMSWebProject>
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.802;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;LastCompiledTime=2018/07/25 12:57:53</VerInfo_Keys>
<TMSWebHTMLFile>index.html</TMSWebHTMLFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Win32)'!=''">
<DCC_UsePackage>DBXSqliteDriver;DataSnapServerMidas;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;svnui;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;Intraweb;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;vclib;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;VCLRESTComponents;vclie;TMSWEBCorePkgDXE11;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;vcl;DBXSybaseASEDriver;IndyIPServer;IndySystem;FireDACDb2Driver;TMSWEBCorePkgLibDXE11;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage)</DCC_UsePackage>
<DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
<BT_BuildType>Debug</BT_BuildType>
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=0.9.5.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=0.9.5.0;Comments=</VerInfo_Keys>
<VerInfo_Locale>1033</VerInfo_Locale>
<Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
<VerInfo_MajorVer>0</VerInfo_MajorVer>
<VerInfo_MinorVer>9</VerInfo_MinorVer>
<VerInfo_Release>5</VerInfo_Release>
<AppDPIAwarenessMode>none</AppDPIAwarenessMode>
</PropertyGroup>
<PropertyGroup Condition="'$(Base_Win64)'!=''">
<DCC_UsePackage>DBXSqliteDriver;DataSnapServerMidas;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;Intraweb;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;fmx;fmxdae;vclib;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;DataSnapConnectors;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;DataSnapClient;bindcompdbx;IndyIPCommon;vcl;DBXSybaseASEDriver;IndyIPServer;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;fmxFireDAC;FireDACInfxDriver;vclimg;TeeDB;emshosting;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;DataSnapNativeClient;fmxobj;vclwinx;ibxbindings;rtl;FireDACDSDriver;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;DataSnapProviderClient;dbrtl;inetdbxpress;FireDACMongoDBDriver;IndyProtocols;fmxase;$(DCC_UsePackage)</DCC_UsePackage>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1)'!=''">
<DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
<DCC_DebugDCUs>true</DCC_DebugDCUs>
<DCC_Optimize>false</DCC_Optimize>
<DCC_GenerateStackFrames>true</DCC_GenerateStackFrames>
<DCC_DebugInfoInExe>true</DCC_DebugInfoInExe>
<DCC_RemoteDebug>true</DCC_RemoteDebug>
<TMSWebDebugInfo>2</TMSWebDebugInfo>
<TMSWebDefines>DEBUG</TMSWebDefines>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1_Win32)'!=''">
<DCC_RemoteDebug>false</DCC_RemoteDebug>
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
<VerInfo_Locale>1033</VerInfo_Locale>
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=0.9.8.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=0.9.8.0;Comments=;LastCompiledTime=2018/08/27 15:18:29</VerInfo_Keys>
<AppDPIAwarenessMode>PerMonitor</AppDPIAwarenessMode>
<VerInfo_MajorVer>0</VerInfo_MajorVer>
<VerInfo_MinorVer>9</VerInfo_MinorVer>
<VerInfo_Release>8</VerInfo_Release>
<TMSWebBrowser>1</TMSWebBrowser>
<TMSUseJSDebugger>2</TMSUseJSDebugger>
<TMSWebSingleInstance>1</TMSWebSingleInstance>
<TMSWebOutputPath>..\kgOrdersServer\bin\static</TMSWebOutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2)'!=''">
<DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
<DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
<DCC_DebugInformation>0</DCC_DebugInformation>
<TMSWebOptimization>2</TMSWebOptimization>
<TMSWebDefines>RELEASE</TMSWebDefines>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2_Win32)'!=''">
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;LastCompiledTime=2018/08/22 16:25:56</VerInfo_Keys>
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
<VerInfo_Locale>1033</VerInfo_Locale>
<AppDPIAwarenessMode>PerMonitor</AppDPIAwarenessMode>
<TMSWebBrowser>1</TMSWebBrowser>
</PropertyGroup>
<ItemGroup>
<DelphiCompile Include="$(MainSource)">
<MainSource>MainSource</MainSource>
</DelphiCompile>
<DCCReference Include="Auth.Service.pas"/>
<DCCReference Include="App.Types.pas"/>
<DCCReference Include="ConnectionModule.pas">
<Form>DMConnection</Form>
<DesignClass>TWebDataModule</DesignClass>
</DCCReference>
<DCCReference Include="View.Login.pas">
<Form>FViewLogin</Form>
<DesignClass>TWebForm</DesignClass>
</DCCReference>
<DCCReference Include="App.Config.pas"/>
<DCCReference Include="View.Main.pas">
<Form>FViewMain</Form>
<DesignClass>TWebForm</DesignClass>
</DCCReference>
<DCCReference Include="Utils.pas"/>
<DCCReference Include="View.Tasks.pas">
<Form>FTasks</Form>
<FormType>dfm</FormType>
<DesignClass>TWebForm</DesignClass>
</DCCReference>
<None Include="index.html"/>
<None Include="css\app.css"/>
<None Include="config\config.json"/>
<None Include="css\spinner.css"/>
<BuildConfiguration Include="Base">
<Key>Base</Key>
</BuildConfiguration>
<BuildConfiguration Include="Debug">
<Key>Cfg_1</Key>
<CfgParent>Base</CfgParent>
</BuildConfiguration>
<BuildConfiguration Include="Release">
<Key>Cfg_2</Key>
<CfgParent>Base</CfgParent>
</BuildConfiguration>
</ItemGroup>
<ProjectExtensions>
<Borland.Personality>Delphi.Personality.12</Borland.Personality>
<Borland.ProjectType>Application</Borland.ProjectType>
<BorlandProject>
<Delphi.Personality>
<Source>
<Source Name="MainSource">emT3webClient.dpr</Source>
</Source>
<Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\bcboffice2k290.bpl">Embarcadero C++Builder Office 2000 Servers Package</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\bcbofficexp290.bpl">Embarcadero C++Builder Office XP Servers Package</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dcloffice2k290.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
<Excluded_Packages Name="$(BDSBIN)\dclofficexp290.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
</Excluded_Packages>
</Delphi.Personality>
<Deployment Version="5">
<DeployFile LocalName="Win32\Debug\emT3webClient.exe" Configuration="Debug" Class="ProjectOutput">
<Platform Name="Win32">
<RemoteName>emT3webClient.exe</RemoteName>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="Win32\Debug\webCharms.exe" Configuration="Debug" Class="ProjectOutput"/>
<DeployFile LocalName="Win32\Debug\webKGOrders.exe" Configuration="Debug" Class="ProjectOutput"/>
<DeployFile LocalName="Win32\Release\emT3webClient.exe" Configuration="Release" Class="ProjectOutput">
<Platform Name="Win32">
<RemoteName>emT3webClient.exe</RemoteName>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="config\config.json" Configuration="Debug" Class="ProjectFile"/>
<DeployFile LocalName="config\config.json" Configuration="Debug" Class="ProjectFile">
<Platform Name="Win32">
<RemoteDir>.\</RemoteDir>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="config\config.json" Configuration="Release" Class="ProjectFile">
<Platform Name="Win32">
<RemoteDir>.\</RemoteDir>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="css\app.css" Configuration="Debug" Class="ProjectFile"/>
<DeployFile LocalName="css\app.css" Configuration="Debug" Class="ProjectFile">
<Platform Name="Win32">
<RemoteDir>.\</RemoteDir>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="css\app.css" Configuration="Release" Class="ProjectFile">
<Platform Name="Win32">
<RemoteDir>.\</RemoteDir>
<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="css\spinner.css" Configuration="Release" 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">
<RemoteDir>.\</RemoteDir>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="index.html" Configuration="Release" Class="ProjectFile">
<Platform Name="Win32">
<RemoteDir>.\</RemoteDir>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="template\bootstrap\bootstrap.min.css" Configuration="Debug" Class="ProjectFile"/>
<DeployFile LocalName="template\bootstrap\bootstrap.min.js" Configuration="Debug" Class="ProjectFile"/>
<DeployFile LocalName="template\bootstrap\dataTables.bootstrap.css" Configuration="Debug" Class="ProjectFile"/>
<DeployFile LocalName="template\css\emsys.css" Configuration="Debug" Class="ProjectFile"/>
<DeployFile LocalName="template\css\metisMenu.min.css" Configuration="Debug" Class="ProjectFile"/>
<DeployFile LocalName="template\css\morris.css" Configuration="Debug" Class="ProjectFile"/>
<DeployFile LocalName="template\css\sb-admin-2.css" Configuration="Debug" Class="ProjectFile"/>
<DeployFile LocalName="template\font-awesome\font-awesome.min.css" Configuration="Debug" Class="ProjectFile"/>
<DeployFile LocalName="template\font-awesome\fonts\fontawesome-webfont.ttf" Configuration="Debug" Class="ProjectFile"/>
<DeployFile LocalName="template\font-awesome\fonts\fontawesome-webfont.woff2" Configuration="Debug" Class="ProjectFile"/>
<DeployFile LocalName="template\font-awesome\fonts\fontawesome-webfont.woff" Configuration="Debug" Class="ProjectFile"/>
<DeployFile LocalName="template\jquery\jquery.min.js" Configuration="Debug" Class="ProjectFile"/>
<DeployClass Name="AdditionalDebugSymbols">
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidFileProvider">
<Platform Name="Android">
<RemoteDir>res\xml</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\xml</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidLibnativeArmeabiFile">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidLibnativeArmeabiv7aFile">
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidLibnativeMipsFile">
<Platform Name="Android">
<RemoteDir>library\lib\mips</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\mips</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidServiceOutput">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\arm64-v8a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidServiceOutput_Android32">
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashImageDef">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashImageDefV21">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashStyles">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashStylesV21">
<Platform Name="Android">
<RemoteDir>res\values-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashStylesV31">
<Platform Name="Android">
<RemoteDir>res\values-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="AndroidSplashStylesV35">
<Platform Name="Android">
<RemoteDir>res\values-v35</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values-v35</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIcon">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v26</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v26</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconBackground">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconForeground">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconMonochrome">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_AdaptiveIconV33">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v33</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v33</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_Colors">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_ColorsDark">
<Platform Name="Android">
<RemoteDir>res\values-night-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values-night-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_DefaultAppIcon">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon144">
<Platform Name="Android">
<RemoteDir>res\drawable-xxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon192">
<Platform Name="Android">
<RemoteDir>res\drawable-xxxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xxxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon36">
<Platform Name="Android">
<RemoteDir>res\drawable-ldpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-ldpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon48">
<Platform Name="Android">
<RemoteDir>res\drawable-mdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-mdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon72">
<Platform Name="Android">
<RemoteDir>res\drawable-hdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-hdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_LauncherIcon96">
<Platform Name="Android">
<RemoteDir>res\drawable-xhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon24">
<Platform Name="Android">
<RemoteDir>res\drawable-mdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-mdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon36">
<Platform Name="Android">
<RemoteDir>res\drawable-hdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-hdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon48">
<Platform Name="Android">
<RemoteDir>res\drawable-xhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon72">
<Platform Name="Android">
<RemoteDir>res\drawable-xxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_NotificationIcon96">
<Platform Name="Android">
<RemoteDir>res\drawable-xxxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xxxhdpi</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_SplashImage426">
<Platform Name="Android">
<RemoteDir>res\drawable-small</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-small</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_SplashImage470">
<Platform Name="Android">
<RemoteDir>res\drawable-normal</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-normal</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_SplashImage640">
<Platform Name="Android">
<RemoteDir>res\drawable-large</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-large</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_SplashImage960">
<Platform Name="Android">
<RemoteDir>res\drawable-xlarge</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-xlarge</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_Strings">
<Platform Name="Android">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\values</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedNotificationIcon">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v24</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v24</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplash">
<Platform Name="Android">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplashDark">
<Platform Name="Android">
<RemoteDir>res\drawable-night-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-night-anydpi-v21</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplashV31">
<Platform Name="Android">
<RemoteDir>res\drawable-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="Android_VectorizedSplashV31Dark">
<Platform Name="Android">
<RemoteDir>res\drawable-night-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>res\drawable-night-anydpi-v31</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="DebugSymbols">
<Platform Name="iOSSimulator">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="DependencyFramework">
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.framework</Extensions>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.framework</Extensions>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.framework</Extensions>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="DependencyModule">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
<Extensions>.dll;.bpl</Extensions>
</Platform>
</DeployClass>
<DeployClass Required="true" Name="DependencyPackage">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
<Extensions>.dylib</Extensions>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
<Extensions>.bpl</Extensions>
</Platform>
</DeployClass>
<DeployClass Name="File">
<Platform Name="Android">
<Operation>0</Operation>
</Platform>
<Platform Name="Android64">
<Operation>0</Operation>
</Platform>
<Platform Name="iOSDevice32">
<Operation>0</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>0</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>0</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\Resources\StartUp\</RemoteDir>
<Operation>0</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\Resources\StartUp\</RemoteDir>
<Operation>0</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\Resources\StartUp\</RemoteDir>
<Operation>0</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectAndroidManifest">
<Platform Name="Android">
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXDebug">
<Platform Name="OSX64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXEntitlements">
<Platform Name="OSX32">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXInfoPList">
<Platform Name="OSX32">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOSXResource">
<Platform Name="OSX32">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\Resources</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Required="true" Name="ProjectOutput">
<Platform Name="Android">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Android64">
<RemoteDir>library\lib\arm64-v8a</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
<Platform Name="Linux64">
<Operation>1</Operation>
</Platform>
<Platform Name="OSX32">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSX64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="OSXARM64">
<RemoteDir>Contents\MacOS</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win32">
<Operation>0</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectOutput_Android32">
<Platform Name="Android64">
<RemoteDir>library\lib\armeabi-v7a</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectUWPManifest">
<Platform Name="Win32">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<Operation>1</Operation>
</Platform>
<Platform Name="Win64x">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSDeviceDebug">
<Platform Name="iOSDevice32">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSEntitlements">
<Platform Name="iOSDevice32">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSInfoPList">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSLaunchScreen">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen</RemoteDir>
<Operation>64</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen</RemoteDir>
<Operation>64</Operation>
</Platform>
</DeployClass>
<DeployClass Name="ProjectiOSResource">
<Platform Name="iOSDevice32">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSDevice64">
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo150">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="UWP_DelphiLogo44">
<Platform Name="Win32">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="Win64">
<RemoteDir>Assets</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iOS_AppStore1024">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_AppIcon152">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_AppIcon167">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Launch2x">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_LaunchDark2x">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Notification40">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_Setting58">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPad_SpotLight80">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_AppIcon120">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_AppIcon180">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch2x">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Launch3x">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_LaunchDark2x">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_LaunchDark3x">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Notification40">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Notification60">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Setting58">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Setting87">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Spotlight120">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<DeployClass Name="iPhone_Spotlight80">
<Platform Name="iOSDevice64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
<Platform Name="iOSSimARM64">
<RemoteDir>..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset</RemoteDir>
<Operation>1</Operation>
</Platform>
</DeployClass>
<ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Android64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="iOSDevice64" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="iOSSimARM64" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="iOSSimulator" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="Linux64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="OSX32" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="OSX64" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="OSXARM64" Name="$(PROJECTNAME).app"/>
<ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
<ProjectRoot Platform="Win64x" Name="$(PROJECTNAME)"/>
</Deployment>
<Platforms>
<Platform value="Win32">True</Platform>
<Platform value="Win64">False</Platform>
</Platforms>
<ModelSupport>False</ModelSupport>
</BorlandProject>
<ProjectFileVersion>12</ProjectFileVersion>
</ProjectExtensions>
<Import Project="$(BDS)\Bin\CodeGear.Delphi.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')"/>
<Import Project="$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj" Condition="Exists('$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj')"/>
<Import Project="$(MSBuildProjectName).deployproj" Condition="Exists('$(MSBuildProjectName).deployproj')"/>
</Project>
<!DOCTYPE html>
<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>EM Systems webKGOrders App</title>
<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 crossorigin="anonymous" integrity="sha256-eKhayi8LEQwp4NKxN+CfCh+3qOVUtJn3QNZ0TciWLP4=" src="https://code.jquery.com/jquery-3.7.1.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js" type="text/javascript"></script>
<link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet"/>
<script src="$(ProjectName).js" type="text/javascript"></script>
</head>
<body>
</body>
<script type="text/javascript">rtl.run();</script>
</html>
...@@ -10,21 +10,23 @@ object ApiServerModule: TApiServerModule ...@@ -10,21 +10,23 @@ object ApiServerModule: TApiServerModule
ModelName = 'Api' ModelName = 'Api'
EntitySetPermissions = <> EntitySetPermissions = <>
SwaggerOptions.Enabled = True SwaggerOptions.Enabled = True
SwaggerOptions.AuthMode = Jwt
SwaggerUIOptions.Enabled = True SwaggerUIOptions.Enabled = True
SwaggerUIOptions.ShowFilter = True SwaggerUIOptions.ShowFilter = True
SwaggerUIOptions.TryItOutEnabled = True SwaggerUIOptions.TryItOutEnabled = True
Left = 89 Left = 80
Top = 112 Top = 142
object XDataServerLogging: TSparkleGenericMiddleware object XDataServerJWT: TSparkleJwtMiddleware
OnMiddlewareCreate = XDataServerLoggingMiddlewareCreate OnGetSecret = XDataServerJWTGetSecret
end
object XDataServerCORS: TSparkleCorsMiddleware
end end
object XDataServerCompress: TSparkleCompressMiddleware object XDataServerCompress: TSparkleCompressMiddleware
end end
object XDataServerJWT: TSparkleJwtMiddleware object XDataServerCORS: TSparkleCorsMiddleware
OnGetSecret = XDataServerJWTGetSecret end
object XDataServerLogging: TSparkleLoggingMiddleware
FormatString = ':method :url :statuscode - :responsetime ms'
ExceptionFormatString = '(%1:s: %4:s) %0:s - %2:s'
ErrorResponseOptions.ErrorCode = 'ServerError'
ErrorResponseOptions.ErrorMessageFormat = 'Internal server error: %4:s'
end end
end end
end end
...@@ -15,16 +15,16 @@ uses ...@@ -15,16 +15,16 @@ uses
Sparkle.HttpServer.Module, Sparkle.HttpServer.Context, Sparkle.HttpServer.Module, Sparkle.HttpServer.Context,
Sparkle.Comp.CompressMiddleware, Sparkle.Comp.CorsMiddleware, Sparkle.Comp.CompressMiddleware, Sparkle.Comp.CorsMiddleware,
Sparkle.Comp.GenericMiddleware, Aurelius.Drivers.UniDac, UniProvider, Sparkle.Comp.GenericMiddleware, Aurelius.Drivers.UniDac, UniProvider,
Data.DB, DBAccess, Uni; Data.DB, DBAccess, Uni, Sparkle.Comp.LoggingMiddleware;
type type
TApiServerModule = class(TDataModule) TApiServerModule = class(TDataModule)
SparkleHttpSysDispatcher: TSparkleHttpSysDispatcher; SparkleHttpSysDispatcher: TSparkleHttpSysDispatcher;
XDataServer: TXDataServer; XDataServer: TXDataServer;
XDataServerLogging: TSparkleGenericMiddleware;
XDataServerCORS: TSparkleCorsMiddleware;
XDataServerCompress: TSparkleCompressMiddleware;
XDataServerJWT: TSparkleJwtMiddleware; XDataServerJWT: TSparkleJwtMiddleware;
XDataServerCompress: TSparkleCompressMiddleware;
XDataServerCORS: TSparkleCorsMiddleware;
XDataServerLogging: TSparkleLoggingMiddleware;
procedure XDataServerLoggingMiddlewareCreate(Sender: TObject; procedure XDataServerLoggingMiddlewareCreate(Sender: TObject;
var Middleware: IHttpServerMiddleware); var Middleware: IHttpServerMiddleware);
procedure XDataServerJWTGetSecret(Sender: TObject; var Secret: string); procedure XDataServerJWTGetSecret(Sender: TObject; var Secret: string);
...@@ -51,7 +51,7 @@ uses ...@@ -51,7 +51,7 @@ uses
XData.Sys.Exceptions, XData.Sys.Exceptions,
Common.Logging, Common.Logging,
Common.Middleware.Logging, Common.Middleware.Logging,
Common.Config, Vcl.Forms, IniFiles; Common.Config, Vcl.Forms, IniFiles, Api.Service;
{%CLASSGROUP 'Vcl.Controls.TControl'} {%CLASSGROUP 'Vcl.Controls.TControl'}
...@@ -80,6 +80,7 @@ begin ...@@ -80,6 +80,7 @@ begin
XDataServer.BaseUrl := Url; XDataServer.BaseUrl := Url;
XDataServer.ModelName := AModelName; XDataServer.ModelName := AModelName;
Logger.Log(1, 'API XDataServer.ModelName=' + XDataServer.ModelName);
SparkleHttpSysDispatcher.Start; SparkleHttpSysDispatcher.Start;
Logger.Log(1, Format('Api server module listening at "%s"', [XDataServer.BaseUrl])); Logger.Log(1, Format('Api server module listening at "%s"', [XDataServer.BaseUrl]));
......
...@@ -6,7 +6,10 @@ uses ...@@ -6,7 +6,10 @@ uses
XData.Service.Common, XData.Service.Common,
System.Generics.Collections, System.Generics.Collections,
System.Classes, System.Classes,
System.SysUtils; System.SysUtils,
Aurelius.Mapping.Attributes,
System.JSON,
Common.Logging;
const const
API_MODEL = 'Api'; API_MODEL = 'Api';
...@@ -26,7 +29,7 @@ type ...@@ -26,7 +29,7 @@ type
assignedTo: string; assignedTo: string;
status: string; status: string;
statusDate: TDateTime; statusDate: Variant;
fixedVersion: string; fixedVersion: string;
formSection: string; formSection: string;
...@@ -52,15 +55,11 @@ type ...@@ -52,15 +55,11 @@ type
destructor Destroy; override; destructor Destroy; override;
end; end;
[ServiceContract] type
[ServiceContract, Model(API_MODEL)]
IApiService = interface(IInvokable) IApiService = interface(IInvokable)
['{A8CBF627-BB64-4A53-821C-9A84C2752248}'] ['{0EFB33D7-8C4C-4F3C-9BC3-8B4D444B5F69}']
[HttpGet] function Ping: string;
end;
[ServiceContract]
ITasksService = interface(IInvokable)
['{D5E1B7A2-6A9D-4D9A-9F7F-9A3E8A9E3B11}']
[HttpGet] function GetProjectTasks(projectId: string): TTasksList; [HttpGet] function GetProjectTasks(projectId: string): TTasksList;
end; end;
...@@ -91,6 +90,8 @@ begin ...@@ -91,6 +90,8 @@ begin
end; end;
initialization initialization
RegisterServiceType(TypeInfo(ITasksService)); RegisterServiceType(TypeInfo(IApiService));
end. end.
...@@ -4,23 +4,24 @@ interface ...@@ -4,23 +4,24 @@ interface
uses uses
XData.Server.Module, System.Generics.Collections, XData.Server.Module, System.Generics.Collections,
XData.Service.Common, XData.Service.Common, System.Variants,
Api.Service, Api.Database; Api.Service, Api.Database, Common.Logging;
type type
[ServiceImplementation] [ServiceImplementation]
TApiService = class(TInterfacedObject, IApiService) TApiService = class(TInterfacedObject, IApiService)
private strict private
ApiDB: TApiDatabase; ApiDB: TApiDatabase;
public public
procedure AfterConstruction; override; procedure AfterConstruction; override;
procedure BeforeDestruction; override; procedure BeforeDestruction; override;
function GetProjectTasks(projectId: string): TTasksList; function GetProjectTasks(projectId: string): TTasksList;
function Ping: string;
end; end;
implementation implementation
procedure TApiService.AfterConstruction; procedure TApiService.AfterConstruction;
begin begin
inherited; inherited;
...@@ -35,6 +36,12 @@ begin ...@@ -35,6 +36,12 @@ begin
end; end;
function TApiService.Ping: string;
begin
Result := 'pong';
end;
function TApiService.GetProjectTasks(projectId: string): TTasksList; function TApiService.GetProjectTasks(projectId: string): TTasksList;
var var
taskMap: TDictionary<string, TTask>; taskMap: TDictionary<string, TTask>;
...@@ -88,7 +95,7 @@ begin ...@@ -88,7 +95,7 @@ begin
item.status := ApiDB.uqProjectTasksSTATUS.AsString; item.status := ApiDB.uqProjectTasksSTATUS.AsString;
if ApiDB.uqProjectTasksSTATUS_DATE.IsNull then if ApiDB.uqProjectTasksSTATUS_DATE.IsNull then
item.statusDate := 0 item.statusDate := Null
else else
item.statusDate := ApiDB.uqProjectTasksSTATUS_DATE.AsDateTime; item.statusDate := ApiDB.uqProjectTasksSTATUS_DATE.AsDateTime;
...@@ -109,9 +116,8 @@ begin ...@@ -109,9 +116,8 @@ begin
end; end;
initialization initialization
RegisterServiceType(TypeInfo(IApiService));
RegisterServiceType(TApiService); RegisterServiceType(TApiService);
end. end.
...@@ -18,9 +18,13 @@ object AuthDatabase: TAuthDatabase ...@@ -18,9 +18,13 @@ object AuthDatabase: TAuthDatabase
end end
object ucKG: TUniConnection object ucKG: TUniConnection
ProviderName = 'MySQL' ProviderName = 'MySQL'
Database = 'emt3_web_db'
Username = 'root'
Server = '192.168.102.129'
LoginPrompt = False LoginPrompt = False
Left = 67 Left = 67
Top = 131 Top = 131
EncryptedPassword = '9AFF92FF8CFF86FF8CFFCFFFCEFF'
end end
object MySQLUniProvider1: TMySQLUniProvider object MySQLUniProvider1: TMySQLUniProvider
Left = 230 Left = 230
......
...@@ -14,7 +14,7 @@ object AuthServerModule: TAuthServerModule ...@@ -14,7 +14,7 @@ object AuthServerModule: TAuthServerModule
SwaggerUIOptions.ShowFilter = True SwaggerUIOptions.ShowFilter = True
SwaggerUIOptions.TryItOutEnabled = True SwaggerUIOptions.TryItOutEnabled = True
Left = 91 Left = 91
Top = 92 Top = 94
object XDataServerLogging: TSparkleGenericMiddleware object XDataServerLogging: TSparkleGenericMiddleware
OnMiddlewareCreate = XDataServerLoggingMiddlewareCreate OnMiddlewareCreate = XDataServerLoggingMiddlewareCreate
end end
......
// Implementation of Auth Serice that will eventually retrieve login information
// from the auth database.
unit Auth.ServiceImpl; unit Auth.ServiceImpl;
interface interface
......
...@@ -162,6 +162,7 @@ begin ...@@ -162,6 +162,7 @@ begin
AppServerModule := TAppServerModule.Create(Self); AppServerModule := TAppServerModule.Create(Self);
AppServerModule.StartAppServer( serverConfig.url ); AppServerModule.StartAppServer( serverConfig.url );
Logger.Log(1, 'Exe=' + Application.ExeName);
UpdateGUI; UpdateGUI;
end; end;
......
[Settings] [Settings]
LogFileNum=2 LogFileNum=8
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