Commit 47c90822 by Mac Stephens

Decided to go with Bootstrap table, save function when clicking out of row now saves cell

parent ccf05c5a
<div id="wrapper"> <div id="wrapper" class="d-flex flex-column vh-100">
<nav class="navbar navbar-expand navbar-light bg-light" style="margin-bottom: 0px;"> <nav class="navbar navbar-expand navbar-light bg-light" style="margin-bottom: 0px">
<div class="container-fluid"> <div class="container-fluid">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<a id="view.main.apptitle" class="navbar-brand" href="index.html">emT3web</a> <a id="view.main.apptitle" class="navbar-brand" href="index.html">emT3web</a>
...@@ -9,9 +9,8 @@ ...@@ -9,9 +9,8 @@
<div class="collapse navbar-collapse show" id="navbarNavDropdown"> <div class="collapse navbar-collapse show" id="navbarNavDropdown">
<ul class="navbar-nav ms-auto"> <ul class="navbar-nav ms-auto">
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" id="navbarDropdownMenuLink" role="button" <a class="nav-link dropdown-toggle" id="navbarDropdownMenuLink" role="button" data-bs-toggle="dropdown" aria-expanded="false">
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>
<i class="fa fa-user fa-fw"></i><span class="panel-title" id="view.main.username"> Username </span>
</a> </a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdownMenuLink"> <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdownMenuLink">
<li> <li>
...@@ -23,44 +22,33 @@ ...@@ -23,44 +22,33 @@
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
</nav> </nav>
<!-- Toast wrapper directly under navbar --> <!-- Toast wrapper directly under navbar -->
<div id="toast-wrapper" <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">
class="position-fixed top-0 start-0 mt-5 ms-4" <div id="bootstrapToast" class="toast align-items-center text-white bg-success border-0 shadow" role="alert" aria-live="assertive" aria-atomic="true">
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="d-flex">
<div class="toast-body" id="bootstrapToastBody"> <div class="toast-body" id="bootstrapToastBody">Success message</div>
Success message <button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</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>
<div class="container-fluid d-flex flex-column flex-grow-1" style="min-height: 0">
<div id="main.webpanel" class="flex-grow-1 d-flex flex-column" style="min-height: 0"></div>
</div> </div>
</div> </div>
<div id="spinner" class="position-absolute top-50 start-50 translate-middle d-none"> <div id="spinner" class="position-absolute top-50 start-50 translate-middle d-none">
<div class="lds-roller"> <div class="lds-roller">
<div></div><div></div><div></div><div></div> <div></div>
<div></div><div></div><div></div><div></div> <div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div> </div>
</div> </div>
...@@ -75,7 +63,9 @@ ...@@ -75,7 +63,9 @@
Please contact EMSystems to solve the issue. Please contact EMSystems to solve the issue.
</div> </div>
<div class="modal-footer justify-content-center"> <div class="modal-footer justify-content-center">
<button type="button" id="btn_modal_restart" class="btn btn-primary">Back to Orders</button> <button type="button" id="btn_modal_restart" class="btn btn-primary">
Back to Orders
</button>
</div> </div>
</div> </div>
</div> </div>
...@@ -88,12 +78,14 @@ ...@@ -88,12 +78,14 @@
<h5 class="modal-title">Confirm</h5> <h5 class="modal-title">Confirm</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body fw-bold" id="main_modal_body"> <div class="modal-body fw-bold" id="main_modal_body">Placeholder text</div>
Placeholder text
</div>
<div class="modal-footer justify-content-center"> <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-primary me-3" id="btn_confirm_left">
<button type="button" class="btn btn-secondary" id="btn_confirm_right">Confirm</button> Cancel
</button>
<button type="button" class="btn btn-secondary" id="btn_confirm_right">
Confirm
</button>
</div> </div>
</div> </div>
</div> </div>
...@@ -106,22 +98,12 @@ ...@@ -106,22 +98,12 @@
<h5 class="modal-title" id="main_notification_modal">Error</h5> <h5 class="modal-title" id="main_notification_modal">Error</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body fs-6 fw-bold" id="main_notification_modal_body"> <div class="modal-body fs-6 fw-bold" id="main_notification_modal_body">Please contact EMSystems to solve the issue.</div>
Please contact EMSystems to solve the issue.
</div>
<div class="modal-footer justify-content-center"> <div class="modal-footer justify-content-center">
<button type="button" id="btn_modal_close" class="btn btn-primary">Close</button> <button type="button" id="btn_modal_close" class="btn btn-primary">
Close
</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
...@@ -46,7 +46,10 @@ implementation ...@@ -46,7 +46,10 @@ implementation
uses uses
Auth.Service, Auth.Service,
View.Login, View.Login,
View.Tasks; View.Tasks,
View.TasksHTML,
View.TasksDataGrid,
View.TasksDBGrid;
{$R *.dfm} {$R *.dfm}
...@@ -64,7 +67,7 @@ var ...@@ -64,7 +67,7 @@ var
test: boolean; test: boolean;
begin begin
FChildForm := nil; FChildForm := nil;
ShowForm(TFTasks); ShowForm(TFTasksHTML);
lblAppTitle.Caption := 'emT3web'; lblAppTitle.Caption := 'emT3web';
lblVersion.Caption := 'v' + DMConnection.clientVersion; lblVersion.Caption := 'v' + DMConnection.clientVersion;
end; end;
......
object FTasks: TFTasks object FTasks: TFTasks
Width = 1416 Width = 1416
Height = 846 Height = 846
CSSLibrary = cssBootstrap
ElementFont = efCSS
FormContainer = 'tasks.root' FormContainer = 'tasks.root'
OnCreate = WebFormCreate OnCreate = WebFormCreate
object pnlGrid: TWebPanel object pnlGrid: TWebPanel
...@@ -87,6 +89,7 @@ object FTasks: TFTasks ...@@ -87,6 +89,7 @@ object FTasks: TFTasks
CellAppearance.SummaryLayout.Font.Name = 'Segoe UI' CellAppearance.SummaryLayout.Font.Name = 'Segoe UI'
CellAppearance.SummaryLayout.Font.Style = [] CellAppearance.SummaryLayout.Font.Style = []
CellAppearance.SummaryLayout.WordWrapping = True CellAppearance.SummaryLayout.WordWrapping = True
ColumnCount = 13
Columns = < Columns = <
item item
Appearance.FilterMatchLayout.Font.Charset = DEFAULT_CHARSET Appearance.FilterMatchLayout.Font.Charset = DEFAULT_CHARSET
...@@ -139,7 +142,8 @@ object FTasks: TFTasks ...@@ -139,7 +142,8 @@ object FTasks: TFTasks
Appearance.SummaryLayout.Font.Height = -12 Appearance.SummaryLayout.Font.Height = -12
Appearance.SummaryLayout.Font.Name = 'Segoe UI' Appearance.SummaryLayout.Font.Name = 'Segoe UI'
Appearance.SummaryLayout.Font.Style = [] Appearance.SummaryLayout.Font.Style = []
Header = 'ID' Header = 'Task Item ID'
Name = 'COL_TASK_ITEM_ID'
Width = 70.000000000000000000 Width = 70.000000000000000000
end end
item item
...@@ -193,7 +197,8 @@ object FTasks: TFTasks ...@@ -193,7 +197,8 @@ object FTasks: TFTasks
Appearance.SummaryLayout.Font.Height = -12 Appearance.SummaryLayout.Font.Height = -12
Appearance.SummaryLayout.Font.Name = 'Segoe UI' Appearance.SummaryLayout.Font.Name = 'Segoe UI'
Appearance.SummaryLayout.Font.Style = [] Appearance.SummaryLayout.Font.Style = []
Header = 'Name' Header = 'Task ID'
Name = 'COL_TASK_ID'
Width = 250.000000000000000000 Width = 250.000000000000000000
end end
item item
...@@ -247,7 +252,8 @@ object FTasks: TFTasks ...@@ -247,7 +252,8 @@ object FTasks: TFTasks
Appearance.SummaryLayout.Font.Height = -12 Appearance.SummaryLayout.Font.Height = -12
Appearance.SummaryLayout.Font.Name = 'Segoe UI' Appearance.SummaryLayout.Font.Name = 'Segoe UI'
Appearance.SummaryLayout.Font.Style = [] Appearance.SummaryLayout.Font.Style = []
Header = 'Joined' Header = 'Application'
Name = 'COL_APP'
Width = 100.000000000000000000 Width = 100.000000000000000000
end end
item item
...@@ -301,8 +307,502 @@ object FTasks: TFTasks ...@@ -301,8 +307,502 @@ object FTasks: TFTasks
Appearance.SummaryLayout.Font.Height = -12 Appearance.SummaryLayout.Font.Height = -12
Appearance.SummaryLayout.Font.Name = 'Segoe UI' Appearance.SummaryLayout.Font.Name = 'Segoe UI'
Appearance.SummaryLayout.Font.Style = [] Appearance.SummaryLayout.Font.Style = []
Header = 'Status' Header = 'Version'
Name = 'COL_VERSION'
Width = 100.000000000000000000 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 = 'Date'
Name = 'COL_TASK_DATE'
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 = 'Reported By'
Name = 'COL_REPORTED_BY'
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 = 'Assigned To'
Name = 'COL_ASSIGNED_TO'
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'
Name = 'COL_STATUS'
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 Date'
Name = 'COL_STATUS_DATE'
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 = 'Fixed Version'
Name = 'COL_FIXED_VER'
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 = 'Form (Section)'
Name = 'COL_FORM_SECTION'
Settings = [gcsEditor]
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 = []
Editor = getMemo
Header = 'Issue'
Name = 'COL_ISSUE'
Settings = [gcsEditor]
Width = 300.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 = []
Editor = getMemo
Header = 'Notes'
Name = 'COL_NOTES'
Settings = [gcsEditor]
Width = 300.000000000000000000
end> end>
Designer = False Designer = False
FilterActions = <> FilterActions = <>
......
<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 id="pnl_grid" class="h-100"></div>
</div>
</div>
</div>
...@@ -3,15 +3,14 @@ unit View.Tasks; ...@@ -3,15 +3,14 @@ unit View.Tasks;
interface interface
uses uses
System.SysUtils, System.Classes, System.Variants, System.SysUtils, System.Classes,
JS, Web, WEBLib.Graphics, WEBLib.Controls, WEBLib.Forms, WEBLib.Dialogs, JS, Web, WEBLib.Graphics, WEBLib.Controls, WEBLib.Forms, WEBLib.Dialogs,
Vcl.Controls, WebLib.JSON,
VCL.TMSFNCTypes, VCL.TMSFNCUtils, VCL.TMSFNCGraphics, VCL.TMSFNCGraphicsTypes, VCL.TMSFNCTypes, VCL.TMSFNCUtils, VCL.TMSFNCGraphics, VCL.TMSFNCGraphicsTypes,
VCL.TMSFNCDataGridCell, VCL.TMSFNCDataGridData, VCL.TMSFNCDataGridBase, VCL.TMSFNCDataGridCell, VCL.TMSFNCDataGridData, VCL.TMSFNCDataGridBase,
VCL.TMSFNCDataGridCore, VCL.TMSFNCDataGridRenderer, VCL.TMSFNCCustomControl, VCL.TMSFNCDataGridCore, VCL.TMSFNCDataGridRenderer, VCL.TMSFNCCustomControl,
VCL.TMSFNCDataGrid, VCL.TMSFNCDataGrid,
XData.Web.Client, XData.Web.Client,
Utils, System.Rtti, WEBLib.ExtCtrls; Utils, WEBLib.ExtCtrls, System.Rtti, Vcl.Controls;
type type
TFTasks = class(TWebForm) TFTasks = class(TWebForm)
...@@ -20,10 +19,7 @@ type ...@@ -20,10 +19,7 @@ type
grdTasks: TTMSFNCDataGrid; grdTasks: TTMSFNCDataGrid;
procedure WebFormCreate(Sender: TObject); procedure WebFormCreate(Sender: TObject);
private private
procedure SetupGrid;
[async] procedure LoadTasks(const AProjectId: string); [async] procedure LoadTasks(const AProjectId: string);
procedure AddComboColumn(ACol: Integer; const Items: array of string);
procedure AddDateColumn(ACol: Integer);
public public
end; end;
...@@ -55,96 +51,9 @@ const ...@@ -55,96 +51,9 @@ const
procedure TFTasks.WebFormCreate(Sender: TObject); procedure TFTasks.WebFormCreate(Sender: TObject);
begin begin
xdwcTasks.Connection := DMConnection.ApiConnection; xdwcTasks.Connection := DMConnection.ApiConnection;
SetupGrid;
LoadTasks('WPR0001'); LoadTasks('WPR0001');
end; 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); [async] procedure TFTasks.LoadTasks(const AProjectId: string);
var var
response: TXDataClientResponse; response: TXDataClientResponse;
...@@ -156,7 +65,6 @@ var ...@@ -156,7 +65,6 @@ var
taskIndex, itemIndex: Integer; taskIndex, itemIndex: Integer;
gridRow: Integer; gridRow: Integer;
statusDateVal: JSValue; statusDateVal: JSValue;
r: Integer;
begin begin
Utils.ShowSpinner('spinner'); Utils.ShowSpinner('spinner');
try try
...@@ -197,7 +105,6 @@ begin ...@@ -197,7 +105,6 @@ begin
grdTasks.Cells[COL_STATUS, gridRow] := string(itemObj['status']); grdTasks.Cells[COL_STATUS, gridRow] := string(itemObj['status']);
statusDateVal := itemObj['statusDate']; statusDateVal := itemObj['statusDate'];
if JS.isNull(statusDateVal) or JS.isUndefined(statusDateVal) then if JS.isNull(statusDateVal) or JS.isUndefined(statusDateVal) then
grdTasks.Cells[COL_STATUS_DATE, gridRow] := '' grdTasks.Cells[COL_STATUS_DATE, gridRow] := ''
else else
...@@ -215,8 +122,6 @@ begin ...@@ -215,8 +122,6 @@ begin
grdTasks.EndUpdate; grdTasks.EndUpdate;
end; end;
grdTasks.Height := Trunc((grdTasks.RowCount * grdTasks.DefaultRowHeight) + 2);
except except
on E: EXDataClientRequestException do on E: EXDataClientRequestException do
Utils.ShowErrorModal(E.ErrorResult.ErrorMessage); Utils.ShowErrorModal(E.ErrorResult.ErrorMessage);
......
object FTasksDBGrid: TFTasksDBGrid
Width = 1161
Height = 725
CSSLibrary = cssBootstrap
ElementFont = efCSS
OnCreate = WebFormCreate
object dbGridTasks: TWebDBGrid
Left = 0
Top = 0
Width = 1161
Height = 725
TabStop = False
Align = alClient
Columns = <
item
ElementClassName = 'text-nowrap tasks-nowrap'
DataField = 'taskId'
Title = 'Task Id'
Width = 95
end
item
DataField = 'application'
Editor = geCombo
Title = 'Application'
Width = 150
end
item
ElementClassName = 'text-nowrap tasks-nowrap'
DataField = 'version'
Title = 'Version'
Width = 95
end
item
DataField = 'taskDate'
Editor = geDate
Title = 'Date'
Width = 120
end
item
ComboBoxItems.Strings = (
#11
''
'Elias'
'Mark'
'Mac')
DataField = 'reportedBy'
Editor = geCombo
Title = 'Reported By'
Width = 95
end
item
ComboBoxItems.Strings = (
''
'Elias'
'Mark'
'Mac')
DataField = 'assignedTo'
Editor = geCombo
Title = 'Assigned To'
Width = 95
end
item
ElementClassName = 'text-nowrap'
DataField = 'status'
Title = 'Status'
Width = 120
end
item
DataField = 'statusDate'
Editor = geDate
Title = 'Status Date'
Width = 120
end
item
ElementClassName = 'text-nowrap tasks-nowrap'
DataField = 'formSection'
Title = 'Form/Section'
Width = 150
end
item
ElementClassName = 'text-wrap'
DataField = 'issue'
Title = 'Issue'
Width = 350
end
item
ElementClassName = 'text-wrap'
DataField = 'notes'
Editor = geMemo
Title = 'Notes'
Width = 350
end>
DataSource = wdsTasks
ElementFont = efCSS
ElementId = 'db_grid_tasks'
FixedFont.Charset = DEFAULT_CHARSET
FixedFont.Color = clWindowText
FixedFont.Height = -12
FixedFont.Name = 'Segoe UI'
FixedFont.Style = []
FixedCols = 0
Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goColSizing, goRowMoving, goEditing, goFixedRowDefAlign]
TabOrder = 0
HeightPercent = 100.000000000000000000
WidthStyle = ssAuto
WidthPercent = 100.000000000000000000
ColWidths = (
95
150
95
120
95
95
120
120
150
350
350)
end
object btnReload: TWebButton
Left = 864
Top = 259
Width = 96
Height = 25
Caption = 'Reload'
ChildOrder = 1
ElementID = 'btn_reload'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = btnReloadClick
end
object btnAddRow: TWebButton
Left = 980
Top = 259
Width = 96
Height = 25
Caption = 'Add Row'
ChildOrder = 1
ElementID = 'btn_add_row'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = btnAddRowClick
end
object xdwcTasks: TXDataWebClient
Connection = DMConnection.ApiConnection
Left = 1010
Top = 430
end
object wdsTasks: TWebDataSource
DataSet = xdwdsTasks
Left = 1014
Top = 574
end
object xdwdsTasks: TXDataWebDataSet
Left = 1010
Top = 504
object xdwdsTaskstaskID: TStringField
FieldName = 'taskId'
end
object xdwdsTasksapplication: TStringField
FieldName = 'application'
end
object xdwdsTasksversion: TStringField
FieldName = 'version'
end
object xdwdsTaskstaskDate: TStringField
FieldName = 'taskDate'
end
object xdwdsTasksreportedBy: TStringField
FieldName = 'reportedBy'
end
object xdwdsTasksassignedTo: TStringField
FieldName = 'assignedTo'
end
object xdwdsTasksstatus: TStringField
FieldName = 'status'
end
object xdwdsTasksstatusDate: TStringField
FieldName = 'statusDate'
end
object xdwdsTasksformSection: TStringField
FieldName = 'formSection'
end
object xdwdsTasksissue: TStringField
FieldName = 'issue'
end
object xdwdsTasksnotes: TStringField
FieldName = 'notes'
end
end
end
<div class="container h-100 d-flex flex-column mt-0 py-0" style="max-width: 100%;">
<div class="d-flex align-items-center justify-content-between mb-2 flex-shrink-0">
<h5 class="mb-0" id="lbl_project_name"></h5>
<div class="d-flex gap-2">
<button id="btn_add_row" class="btn btn-sm btn-outline-success">Add Row</button>
<button id="btn_reload" class="btn btn-sm btn-outline-primary">Reload</button>
</div>
</div>
<div id="db_grid_tasks" class="flex-grow-1" style="min-height:0;"></div>
</div>
unit View.TasksDBGrid;
interface
uses
System.SysUtils, System.Classes, JS, Web, WEBLib.Graphics, WEBLib.Controls,
WEBLib.Forms, WEBLib.Dialogs, WEBLib.Grids, Vcl.Controls, Vcl.Grids,
WEBLib.DBCtrls, Data.DB, WEBLib.DB, XData.Web.JsonDataset, XData.Web.Dataset,
XData.Web.Client, ConnectionModule, Vcl.StdCtrls, WEBLib.StdCtrls;
type
TFTasksDBGrid = class(TWebForm)
xdwcTasks: TXDataWebClient;
wdsTasks: TWebDataSource;
xdwdsTasks: TXDataWebDataSet;
xdwdsTaskstaskID: TStringField;
xdwdsTasksapplication: TStringField;
xdwdsTasksversion: TStringField;
xdwdsTaskstaskDate: TStringField;
xdwdsTasksreportedBy: TStringField;
xdwdsTasksassignedTo: TStringField;
xdwdsTasksstatus: TStringField;
xdwdsTasksstatusDate: TStringField;
xdwdsTasksformSection: TStringField;
xdwdsTasksissue: TStringField;
xdwdsTasksnotes: TStringField;
btnReload: TWebButton;
btnAddRow: TWebButton;
dbGridTasks: TWebDBGrid;
procedure btnAddRowClick(Sender: TObject);
procedure btnReloadClick(Sender: TObject);
procedure WebFormCreate(Sender: TObject);
private
FProjectId: string;
[async] procedure LoadTasks(const AProjectId: string);
procedure SetProjectTitle(const AProjectId: string);
public
{ Public declarations }
end;
var
FTasksDBGrid: TFTasksDBGrid;
implementation
{$R *.dfm}
procedure TFTasksDBGrid.WebFormCreate(Sender: TObject);
begin
wdsTasks.DataSet := xdwdsTasks;
dbGridTasks.DataSource := wdsTasks;
FProjectId := 'WPR0001';
SetProjectTitle(FProjectId);
LoadTasks(FProjectId);
end;
procedure TFTasksDBGrid.btnAddRowClick(Sender: TObject);
begin
xdwdsTasks.Append;
xdwdsTaskstaskID.AsString := FProjectId;
xdwdsTasks.Post;
end;
procedure TFTasksDBGrid.btnReloadClick(Sender: TObject);
begin
LoadTasks(FProjectId);
end;
procedure TFTasksDBgrid.SetProjectTitle(const AProjectId: string);
begin
TJSHTMLElement(document.getElementById('lbl_project_name')).innerText := AProjectId;
end;
[async] procedure TFTasksDBgrid.LoadTasks(const AProjectId: string);
var
response: TXDataClientResponse;
resultObj, taskObj: TJSObject;
tasksArray, itemsArray, flatItems: TJSArray;
taskIndex, itemIndex: Integer;
begin
response := await(xdwcTasks.RawInvokeAsync('IApiService.GetProjectTasks', [AProjectId]));
resultObj := TJSObject(response.Result);
tasksArray := TJSArray(resultObj['data']);
flatItems := TJSArray.new;
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
flatItems.push(itemsArray[itemIndex]);
end;
xdwdsTasks.Close;
xdwdsTasks.SetJsonData(flatItems);
xdwdsTasks.Open;
end;
end.
\ No newline at end of file
object FTasksDataGrid: TFTasksDataGrid
Width = 1274
Height = 792
CSSLibrary = cssBootstrap
ElementFont = efCSS
OnCreate = WebFormCreate
object dataGridTasks: TWebDataGrid
Left = 22
Top = 96
Width = 1223
Height = 357
ElementID = 'data_grid_tasks'
Banding.Enabled = False
Banding.OddRowsColor = 16777215
Banding.EvenRowsColor = 16777215
MaxBlocksInCache = 0
TabOrder = 0
RowMultiSelectWithClick = False
EnableClickSelection = True
BidiMode = bdLeftToRight
SuppressMoveWhenColumnDragging = False
MultilevelHeaders = <>
ColumnDefs = <
item
Field = 'taskId'
CellDataType = cdtText
HeaderName = 'Task'
Editable = True
CheckboxSelection = False
Resizable = True
Width = 95
LockPinned = True
SelectOptions = <>
end
item
Field = 'application'
CellDataType = cdtText
HeaderName = 'App'
EditModeType = cetCombobox
Editable = True
CheckboxSelection = False
LockPinned = True
SelectOptions = <
item
Value = 'null'
end
item
Text = 'WebPoliceReport - Client'
Value = 'WebPoliceReport - Client'
end
item
Text = 'WebPoliceReport - Server'
Value = 'WebPoliceReport - Server'
end>
end
item
Field = 'version'
CellDataType = cdtText
HeaderName = 'Version'
Editable = True
CheckboxSelection = False
Width = 95
LockPinned = True
SelectOptions = <>
end
item
Field = 'taskDate'
CellDataType = cdtDateString
HeaderName = 'Date'
EditModeType = cetDate
Editable = True
CheckboxSelection = False
Width = 110
LockPinned = True
SelectOptions = <>
end
item
Field = 'reportedBy'
CellDataType = cdtText
HeaderName = 'Reported By'
EditModeType = cetCombobox
Editable = True
CheckboxSelection = False
Width = 150
LockPinned = True
SelectOptions = <
item
Value = 'Null'
end
item
Text = 'Elias'
Value = 'Elias'
end
item
Text = 'Mark'
Value = 'Mark'
end
item
Text = 'Mac'
Value = 'Mac'
end>
end
item
Field = 'assignedTo'
CellDataType = cdtText
HeaderName = 'Assigned To'
EditModeType = cetCombobox
Editable = True
CheckboxSelection = False
Width = 150
LockPinned = True
SelectOptions = <
item
Value = 'Null'
end
item
Text = 'Elias'
Value = 'Elias'
end
item
Text = 'Mark'
Value = 'Mark'
end
item
Text = 'Mac'
Value = 'Mac'
end>
end
item
Field = 'status'
CellDataType = cdtText
HeaderName = 'Status'
Editable = True
CheckboxSelection = False
Width = 95
LockPinned = True
SelectOptions = <
item
Value = 'Null'
end
item
Text = 'Fixed'
Value = 'Fixed'
end
item
Text = 'Fixed - Verified'
Value = 'Fixed - Verified'
end
item
Text = 'Not Fixed'
Value = 'Not Fixed'
end>
end
item
Field = 'statusDate'
CellDataType = cdtDateString
HeaderName = 'Status Date'
EditModeType = cetDate
Editable = True
CheckboxSelection = False
Width = 110
LockPinned = True
SelectOptions = <>
end
item
Field = 'formSection'
CellDataType = cdtText
HeaderName = 'Form/Section'
Editable = True
CheckboxSelection = False
LockPinned = True
SelectOptions = <>
end
item
Field = 'issue'
CellDataType = cdtText
HeaderName = 'Issue'
EditModeType = cetMemo
Editable = True
CheckboxSelection = False
Width = 300
WrapText = True
AutoHeight = True
LockPinned = True
SelectOptions = <>
end
item
Field = 'notes'
CellDataType = cdtText
HeaderName = 'Notes'
EditModeType = cetMemo
Editable = True
CheckboxSelection = False
Width = 300
WrapText = True
AutoHeight = True
LockPinned = True
SelectOptions = <>
end>
end
object btnReload: TWebButton
Left = 356
Top = 30
Width = 96
Height = 25
Caption = 'Reload'
ChildOrder = 1
ElementID = 'btn_reload'
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
end
object btnAddRow: TWebButton
Left = 356
Top = 61
Width = 96
Height = 25
Caption = 'Add Row'
ChildOrder = 1
ElementID = 'btn_add_row'
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
end
object xdwcTasks: TXDataWebClient
Connection = DMConnection.ApiConnection
Left = 150
Top = 534
end
end
<div class="container-fluid p-2 d-flex flex-column h-100">
<div class="d-flex align-items-center justify-content-between mb-2 flex-shrink-0">
<h5 class="mb-0" id="lbl_project_name"></h5>
<div class="d-flex gap-2">
<button id="btn_add_row" class="btn btn-sm btn-outline-success">Add Row</button>
<button id="btn_reload" class="btn btn-sm btn-outline-primary">Reload</button>
</div>
</div>
<div id="data_grid_tasks" class="flex-grow-1 min-vh-0"></div>
</div>
unit View.TasksDataGrid;
interface
uses
System.SysUtils, System.Classes,
JS, Web, WEBLib.Forms, WEBLib.Dialogs, WEBLib.StdCtrls,
XData.Web.Client,
WEBLib.DataGrid,
Utils, WEBLib.DataGrid.Common, Vcl.StdCtrls, Vcl.Controls, Vcl.Grids,
WEBLib.DataGrid.DataAdapter.Base, WEBLib.DataGrid.DataAdapter.XData,
libdatagrid;
type
TFTasksDataGrid = class(TWebForm)
xdwcTasks: TXDataWebClient;
dataGridTasks: TWebDataGrid;
btnReload: TWebButton;
btnAddRow: TWebButton;
procedure WebFormCreate(Sender: TObject);
procedure btnReloadClick(Sender: TObject);
procedure btnAddRowClick(Sender: TObject);
private
FProjectId: string;
procedure SetProjectTitle(const AProjectId: string);
[async] procedure LoadTasks(const AProjectId: string);
public
end;
var
FTasksDataGrid: TFTasksDataGrid;
implementation
uses
ConnectionModule;
{$R *.dfm}
procedure TFTasksDataGrid.WebFormCreate(Sender: TObject);
begin
xdwcTasks.Connection := DMConnection.ApiConnection;
FProjectId := 'WPR0001';
SetProjectTitle(FProjectId);
LoadTasks(FProjectId);
end;
procedure TFTasksDataGrid.SetProjectTitle(const AProjectId: string);
begin
TJSHTMLElement(document.getElementById('lbl_project_name')).innerText := AProjectId;
end;
procedure TFTasksDataGrid.btnReloadClick(Sender: TObject);
begin
LoadTasks(FProjectId);
end;
procedure TFTasksDataGrid.btnAddRowClick(Sender: TObject);
begin
dataGridTasks.InsertNewRow;
dataGridTasks.EnsureLastRowVisible;
end;
[async] procedure TFTasksDataGrid.LoadTasks(const AProjectId: string);
var
response: TXDataClientResponse;
resultObj, taskObj: TJSObject;
tasksArray, itemsArray, flatItems: TJSArray;
taskIndex, itemIndex: Integer;
begin
response := await(xdwcTasks.RawInvokeAsync('IApiService.GetProjectTasks', [AProjectId]));
resultObj := TJSObject(response.Result);
tasksArray := TJSArray(resultObj['data']);
flatItems := TJSArray.new;
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
flatItems.push(itemsArray[itemIndex]);
end;
dataGridTasks.LoadFromJSON(TJSObject(flatItems));
end;
end.
object FTasksHTML: TFTasksHTML
Width = 640
Height = 480
CSSLibrary = cssBootstrap
ElementFont = efCSS
OnCreate = WebFormCreate
object btnReload: TWebButton
Left = 78
Top = 88
Width = 96
Height = 25
Caption = 'Reload'
ElementID = 'btn_reload'
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = btnReloadClick
end
object btnAddRow: TWebButton
Left = 78
Top = 119
Width = 96
Height = 25
Caption = 'Add Row'
ChildOrder = 1
ElementID = 'btn_add_row'
HeightPercent = 100.000000000000000000
WidthPercent = 100.000000000000000000
OnClick = btnAddRowClick
end
object xdwcTasks: TXDataWebClient
Connection = DMConnection.ApiConnection
Left = 506
Top = 92
end
object xdwdsTasks: TXDataWebDataSet
Left = 506
Top = 148
object xdwdsTaskstaskID: TStringField
FieldName = 'taskId'
end
object xdwdsTasksapplication: TStringField
FieldName = 'application'
end
object xdwdsTasksversion: TStringField
FieldName = 'version'
end
object xdwdsTaskstaskDate: TStringField
FieldName = 'taskDate'
end
object xdwdsTasksreportedBy: TStringField
FieldName = 'reportedBy'
end
object xdwdsTasksassignedTo: TStringField
FieldName = 'assignedTo'
end
object xdwdsTasksstatus: TStringField
FieldName = 'status'
end
object xdwdsTasksstatusDate: TStringField
FieldName = 'statusDate'
end
object xdwdsTasksformSection: TStringField
FieldName = 'formSection'
end
object xdwdsTasksissue: TStringField
FieldName = 'issue'
end
object xdwdsTasksnotes: TStringField
FieldName = 'notes'
end
object xdwdsTaskstaskItemId: TStringField
FieldName = 'taskItemId'
end
end
end
<div class="container-fluid p-2 d-flex flex-column h-100">
<div class="d-flex align-items-center justify-content-between mb-2 flex-shrink-0">
<h5 class="mb-0" id="lbl_project_name"></h5>
<div class="d-flex gap-2">
<button id="btn_add_row" class="btn btn-sm btn-outline-success">Add Row</button>
<button id="btn_reload" class="btn btn-sm btn-outline-primary">Reload</button>
</div>
</div>
<div id="tasks_table_host" class="flex-grow-1 min-vh-0"></div>
</div>
unit View.TasksHTML;
interface
uses
System.SysUtils, System.Classes,
JS, Web, WEBLib.Graphics, WEBLib.Controls, WEBLib.Forms, WEBLib.Dialogs,
WEBLib.ExtCtrls,
XData.Web.Client, XData.Web.Dataset,
Utils, Data.DB, XData.Web.JsonDataset, Vcl.Controls, Vcl.StdCtrls,
WEBLib.StdCtrls;
type
TFTasksHTML = class(TWebForm)
xdwcTasks: TXDataWebClient;
xdwdsTasks: TXDataWebDataSet;
xdwdsTaskstaskId: TStringField;
xdwdsTasksapplication: TStringField;
xdwdsTasksversion: TStringField;
xdwdsTaskstaskDate: TStringField;
xdwdsTasksreportedBy: TStringField;
xdwdsTasksassignedTo: TStringField;
xdwdsTasksstatus: TStringField;
xdwdsTasksstatusDate: TStringField;
xdwdsTasksformSection: TStringField;
xdwdsTasksissue: TStringField;
xdwdsTasksnotes: TStringField;
btnReload: TWebButton;
btnAddRow: TWebButton;
xdwdsTaskstaskItemId: TStringField;
procedure btnAddRowClick(Sender: TObject);
procedure btnReloadClick(Sender: TObject);
procedure WebFormCreate(Sender: TObject);
private
[async] procedure LoadTasks(const AProjectId: string);
procedure RenderTable;
procedure BindTableEditors;
procedure EditorInput(Event: TJSEvent);
procedure SelectChange(Event: TJSEvent);
procedure EnableColumnResize;
procedure EnableAutoGrowTextAreas;
procedure GotoRowIndex(AIndex: Integer);
function HtmlEncode(const s: string): string;
procedure SetProjectLabel(const AProjectId: string);
[async] procedure SaveRow(AIndex: Integer);
procedure EditorBlur(Event: TJSEvent);
public
end;
var
FTasksHTML: TFTasksHTML;
implementation
uses
ConnectionModule;
{$R *.dfm}
procedure TFTasksHTML.WebFormCreate(Sender: TObject);
begin
LoadTasks('WPR0001');
end;
function TFTasksHTML.HtmlEncode(const s: string): string;
begin
Result := s;
Result := StringReplace(Result, '&', '&amp;', [rfReplaceAll]);
Result := StringReplace(Result, '<', '&lt;', [rfReplaceAll]);
Result := StringReplace(Result, '>', '&gt;', [rfReplaceAll]);
Result := StringReplace(Result, '"', '&quot;', [rfReplaceAll]);
Result := StringReplace(Result, '''', '&#39;', [rfReplaceAll]);
end;
procedure TFTasksHTML.GotoRowIndex(AIndex: Integer);
var
i: Integer;
begin
if (AIndex < 0) or (not xdwdsTasks.Active) then
Exit;
xdwdsTasks.First;
i := 0;
while (i < AIndex) and (not xdwdsTasks.Eof) do
begin
xdwdsTasks.Next;
Inc(i);
end;
end;
procedure TFTasksHTML.EditorInput(Event: TJSEvent);
var
el: TJSHTMLElement;
idx: Integer;
idxStr, fieldName: string;
newVal: string;
begin
if not xdwdsTasks.Active then
Exit;
el := TJSHTMLElement(Event.target);
idxStr := string(el.getAttribute('data-idx'));
fieldName := string(el.getAttribute('data-field'));
idx := StrToIntDef(idxStr, -1);
if (idx < 0) or (fieldName = '') then
Exit;
GotoRowIndex(idx);
if xdwdsTasks.Eof then
Exit;
newVal := string(TJSObject(el)['value']);
console.log('EditorInput: idx=' + IntToStr(idx) + ' field=' + fieldName + ' val=' + newVal);
xdwdsTasks.Edit;
xdwdsTasks.FieldByName(fieldName).AsString := newVal;
xdwdsTasks.Post;
el.setAttribute('data-unsaved-data', '1');
end;
procedure TFTasksHTML.SelectChange(Event: TJSEvent);
var
el: TJSHTMLElement;
idx: Integer;
idxStr, fieldName: string;
sel: TJSHTMLSelectElement;
begin
if not xdwdsTasks.Active then
Exit;
el := TJSHTMLElement(Event.target);
idxStr := string(el.getAttribute('data-idx'));
fieldName := string(el.getAttribute('data-field'));
idx := StrToIntDef(idxStr, -1);
if (idx < 0) or (fieldName = '') then
Exit;
GotoRowIndex(idx);
if xdwdsTasks.Eof then
Exit;
sel := TJSHTMLSelectElement(el);
console.log('SelectChange: idx=' + IntToStr(idx) + ' field=' + fieldName + ' val=' + string(sel.value));
xdwdsTasks.Edit;
xdwdsTasks.FieldByName(fieldName).AsString := string(sel.value);
xdwdsTasks.Post;
el.setAttribute('data-unsaved-data', '1');
SaveRow(idx);
end;
procedure TFTasksHTML.BindTableEditors;
var
nodes: TJSNodeList;
i: Integer;
el: TJSHTMLElement;
begin
console.log('BindTableEditors: wiring handlers...');
nodes := document.querySelectorAll('.task-editor');
console.log('BindTableEditors: task-editor count=' + IntToStr(nodes.length));
for i := 0 to nodes.length - 1 do
begin
el := TJSHTMLElement(nodes.item(i));
el.addEventListener('input', TJSEventHandler(@EditorInput));
el.addEventListener('blur', TJSEventHandler(@EditorBlur));
end;
nodes := document.querySelectorAll('.task-select');
console.log('BindTableEditors: task-select count=' + IntToStr(nodes.length));
for i := 0 to nodes.length - 1 do
begin
el := TJSHTMLElement(nodes.item(i));
el.addEventListener('change', TJSEventHandler(@SelectChange));
end;
end;
procedure TFTasksHTML.btnAddRowClick(Sender: TObject);
begin
if not xdwdsTasks.Active then
Exit;
xdwdsTasks.Append;
xdwdsTaskstaskId.AsString := 'NEW_TASK_ID';
xdwdsTasksreportedBy.AsString := '';
xdwdsTasksassignedTo.AsString := '';
xdwdsTasksstatus.AsString := '';
xdwdsTasks.Post;
RenderTable;
end;
procedure TFTasksHTML.btnReloadClick(Sender: TObject);
begin
LoadTasks('WPR0001');
end;
procedure TFTasksHTML.EnableAutoGrowTextAreas;
begin
asm
(function(){
const host = document.getElementById('tasks_table_host');
if(!host) return;
host.querySelectorAll('textarea.cell-textarea').forEach(ta => {
const fit = () => { ta.style.height = 'auto'; ta.style.height = ta.scrollHeight + 'px'; };
fit();
ta.addEventListener('input', fit);
});
})();
end;
end;
procedure TFTasksHTML.SetProjectLabel(const AProjectId: string);
var
el: TJSHTMLElement;
begin
el := TJSHTMLElement(document.getElementById('lbl_project_name'));
if Assigned(el) then
el.innerText := 'Tasks - ' + AProjectId;
end;
[async] procedure TFTasksHTML.LoadTasks(const AProjectId: string);
var
response: TXDataClientResponse;
resultObj, taskObj: TJSObject;
tasksArray, itemsArray, flatItems: TJSArray;
taskIndex, itemIndex: Integer;
begin
SetProjectLabel(AProjectId);
Utils.ShowSpinner('spinner');
try
response := await(xdwcTasks.RawInvokeAsync(
'IApiService.GetProjectTasks', [AProjectId]
));
if not Assigned(response.Result) then
Exit;
resultObj := TJSObject(response.Result);
tasksArray := TJSArray(resultObj['data']);
flatItems := TJSArray.new;
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
flatItems.push(itemsArray[itemIndex]);
end;
xdwdsTasks.Close;
xdwdsTasks.SetJsonData(flatItems);
xdwdsTasks.Open;
RenderTable;
finally
Utils.HideSpinner('spinner');
end;
end;
procedure TFTasksHTML.RenderTable;
var
host: TJSHTMLElement;
html: string;
rowIdx: Integer;
function Th(const s: string): string;
begin
Result := '<th scope="col">' + s + '</th>';
end;
function TdNowrap(const s: string): string;
begin
Result := '<td class="align-top nowrap-cell">' + s + '</td>';
end;
function TdWrap(const s: string): string;
begin
Result := '<td class="align-top wrap-cell">' + s + '</td>';
end;
function TextInput(const FieldName, Value: string; const AIdx: Integer; const MinWidth: Integer = 0): string;
var
w: string;
begin
w := '';
if MinWidth > 0 then
w := ' style="min-width: ' + IntToStr(MinWidth) + 'px;"';
Result :=
'<input class="form-control form-control-sm cell-input task-editor w-100" ' +
'data-idx="' + IntToStr(AIdx) + '" data-field="' + FieldName + '" ' +
'value="' + HtmlEncode(Value) + '"' + w + '>';
end;
function TextArea(const FieldName, Value: string; const AIdx: Integer): string;
begin
Result :=
'<textarea class="form-control form-control-sm cell-textarea task-editor w-100" ' +
'data-idx="' + IntToStr(AIdx) + '" data-field="' + FieldName + '" ' +
'rows="2">' + HtmlEncode(Value) + '</textarea>';
end;
function DateInput(const FieldName, Value: string; const AIdx: Integer; const MinWidth: Integer = 0): string;
var
w: string;
begin
w := '';
if MinWidth > 0 then
w := ' style="min-width: ' + IntToStr(MinWidth) + 'px;"';
Result :=
'<input type="date" class="form-control form-control-sm cell-input task-editor w-100" ' +
'data-idx="' + IntToStr(AIdx) + '" data-field="' + FieldName + '" ' +
'value="' + HtmlEncode(Value) + '"' + w + '>';
end;
function SelectList(const FieldName, Current: string; const AIdx: Integer; const Items: array of string): string;
var
i: Integer;
sel: string;
begin
Result :=
'<select class="form-select form-select-sm task-select" ' +
'data-idx="' + IntToStr(AIdx) + '" data-field="' + FieldName + '">';
sel := '';
if Trim(Current) = '' then
sel := ' selected';
Result := Result + '<option value=""' + sel + '></option>';
for i := Low(Items) to High(Items) do
begin
sel := '';
if SameText(Current, Items[i]) then
sel := ' selected';
Result := Result + '<option value="' + HtmlEncode(Items[i]) + '"' + sel + '>' + HtmlEncode(Items[i]) + '</option>';
end;
Result := Result + '</select>';
end;
function StatusSelect(const Current: string; const AIdx: Integer): string;
const
Statuses: array[0..5] of string = ('Open', 'In Progress', 'Blocked', 'Testing', 'Done', 'Closed');
var
i: Integer;
sel: string;
begin
Result :=
'<select class="form-select form-select-sm task-select" data-idx="' + IntToStr(AIdx) + '" data-field="status">';
for i := Low(Statuses) to High(Statuses) do
begin
sel := '';
if SameText(Current, Statuses[i]) then
sel := ' selected';
Result := Result + '<option value="' + HtmlEncode(Statuses[i]) + '"' + sel + '>' + HtmlEncode(Statuses[i]) + '</option>';
end;
Result := Result + '</select>';
end;
begin
host := TJSHTMLElement(document.getElementById('tasks_table_host'));
if not Assigned(host) then
Exit;
html :=
'<div class="tasks-vscroll">' +
'<div class="tasks-hscroll">' +
'<table class="table table-sm table-bordered table-hover align-middle mb-0">' +
'<colgroup>' +
'<col style="width:110px">' +
'<col style="width:240px">' +
'<col style="width:90px">' +
'<col style="width:120px">' +
'<col style="width:120px">' +
'<col style="width:120px">' +
'<col style="width:140px">' +
'<col style="width:140px">' +
'<col style="width:160px">' +
'<col style="width:520px">' +
'<col style="width:520px">' +
'</colgroup>' +
'<thead><tr>' +
Th('Task') +
Th('App') +
Th('Version') +
Th('Date') +
Th('Reported') +
Th('Assigned') +
Th('Status') +
Th('Status Date') +
Th('Form') +
Th('Issue') +
Th('Notes') +
'</tr></thead><tbody>';
rowIdx := 0;
xdwdsTasks.First;
while not xdwdsTasks.Eof do
begin
html := html +
'<tr>' +
TdNowrap(TextInput('taskId', xdwdsTaskstaskId.AsString, rowIdx, 90)) +
TdNowrap(TextInput('application', xdwdsTasksapplication.AsString, rowIdx, 180)) +
TdNowrap(TextInput('version', xdwdsTasksversion.AsString, rowIdx, 80)) +
TdNowrap(DateInput('taskDate', xdwdsTaskstaskDate.AsString, rowIdx, 110)) +
TdNowrap(SelectList('reportedBy', xdwdsTasksreportedBy.AsString, rowIdx, ['Elias','Mac','Mark'])) +
TdNowrap(SelectList('assignedTo', xdwdsTasksassignedTo.AsString, rowIdx, ['Elias','Mac','Mark'])) +
TdNowrap(StatusSelect(xdwdsTasksstatus.AsString, rowIdx)) +
TdNowrap(DateInput('statusDate', xdwdsTasksstatusDate.AsString, rowIdx, 110)) +
TdNowrap(TextInput('formSection', xdwdsTasksformSection.AsString, rowIdx, 160)) +
TdWrap(TextArea('issue', xdwdsTasksissue.AsString, rowIdx)) +
TdWrap(TextArea('notes', xdwdsTasksnotes.AsString, rowIdx)) +
'</tr>';
xdwdsTasks.Next;
Inc(rowIdx);
end;
html := html + '</tbody></table></div></div>';
host.innerHTML := html;
BindTableEditors;
EnableAutoGrowTextAreas;
EnableColumnResize;
end;
procedure TFTasksHTML.EnableColumnResize;
begin
asm
(function(){
const host = document.getElementById('tasks_table_host');
if(!host) return;
const table = host.querySelector('table');
if(!table) return;
const ths = table.querySelectorAll('thead th');
ths.forEach(th => {
th.classList.add('th-resize');
if(th.querySelector('.th-resize-handle')) return;
const handle = document.createElement('div');
handle.className = 'th-resize-handle';
th.appendChild(handle);
handle.addEventListener('mousedown', function(e){
e.preventDefault();
const startX = e.clientX;
const startW = th.getBoundingClientRect().width;
function onMove(ev){
const w = Math.max(40, startW + (ev.clientX - startX));
th.style.width = w + 'px';
}
function onUp(){
document.removeEventListener('mousemove', onMove);
document.removeEventListener('mouseup', onUp);
}
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
});
});
})();
end;
end;
procedure TFTasksHTML.EditorBlur(Event: TJSEvent);
var
el: TJSHTMLElement;
idx: Integer;
idxStr, fieldName: string;
begin
el := TJSHTMLElement(Event.target);
idxStr := string(el.getAttribute('data-idx'));
fieldName := string(el.getAttribute('data-field'));
if string(el.getAttribute('data-unsaved-data')) <> '1' then
begin
console.log('EditorBlur: skip (not unsaved) idx=' + idxStr + ' field=' + fieldName);
Exit;
end;
el.removeAttribute('data-unsaved-data');
idx := StrToIntDef(idxStr, -1);
if idx < 0 then
Exit;
console.log('EditorBlur: SAVE idx=' + IntToStr(idx) + ' field=' + fieldName);
SaveRow(idx);
end;
[async] procedure TFTasksHTML.SaveRow(AIndex: Integer);
const
// ADD THIS DELETE BEFORE DEPLOY
ENABLE_SERVER_SAVE = True;
var
response: TXDataClientResponse;
payload: TJSObject;
payloadJson: string;
begin
if not xdwdsTasks.Active then
Exit;
GotoRowIndex(AIndex);
if xdwdsTasks.Eof then
Exit;
payload := TJSObject.new;
payload['taskItemId'] := xdwdsTaskstaskItemId.AsString;
payload['taskId'] := xdwdsTaskstaskId.AsString;
payload['application'] := xdwdsTasksapplication.AsString;
payload['version'] := xdwdsTasksversion.AsString;
payload['taskDate'] := xdwdsTaskstaskDate.AsString;
payload['reportedBy'] := xdwdsTasksreportedBy.AsString;
payload['assignedTo'] := xdwdsTasksassignedTo.AsString;
payload['status'] := xdwdsTasksstatus.AsString;
payload['statusDate'] := xdwdsTasksstatusDate.AsString;
payload['formSection'] := xdwdsTasksformSection.AsString;
payload['issue'] := xdwdsTasksissue.AsString;
payload['notes'] := xdwdsTasksnotes.AsString;
payloadJson := string(TJSJSON.stringify(payload));
console.log('SaveRow: idx=' + IntToStr(AIndex) + ' payload=' + payloadJson);
if not ENABLE_SERVER_SAVE then
Exit;
try
response := await(xdwcTasks.RawInvokeAsync('IApiService.SaveTaskRow', [payload]));
console.log('SaveRow: response=' + string(TJSJSON.stringify(response.Result)));
except
on E: EXDataClientRequestException do
begin
console.log('SaveRow ERROR: ' + E.ErrorResult.ErrorMessage);
Utils.ShowErrorModal(E.ErrorResult.ErrorMessage);
end;
end;
end;
end.
object FTasksTabulator: TFTasksTabulator
Width = 640
Height = 480
object xdwcTasks: TXDataWebClient
Connection = DMConnection.ApiConnection
Left = 506
Top = 92
end
object xdwdsTasks: TXDataWebDataSet
Left = 468
Top = 182
object xdwdsTaskstaskID: TStringField
FieldName = 'taskId'
end
object xdwdsTasksapplication: TStringField
FieldName = 'application'
end
object xdwdsTasksversion: TStringField
FieldName = 'version'
end
object xdwdsTaskstaskDate: TStringField
FieldName = 'taskDate'
end
object xdwdsTasksreportedBy: TStringField
FieldName = 'reportedBy'
end
object xdwdsTasksassignedTo: TStringField
FieldName = 'assignedTo'
end
object xdwdsTasksstatus: TStringField
FieldName = 'status'
end
object xdwdsTasksstatusDate: TStringField
FieldName = 'statusDate'
end
object xdwdsTasksformSection: TStringField
FieldName = 'formSection'
end
object xdwdsTasksissue: TStringField
FieldName = 'issue'
end
object xdwdsTasksnotes: TStringField
FieldName = 'notes'
end
end
end
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>TMS Web Project</title>
<style>
</style>
</head>
<body>
</body>
</html>
\ No newline at end of file
unit View.TasksTabulator;
interface
uses
System.SysUtils, System.Classes, JS, Web, WEBLib.Graphics, WEBLib.Controls,
WEBLib.Forms, WEBLib.Dialogs, Data.DB, XData.Web.JsonDataset,
XData.Web.Dataset, XData.Web.Client, ConnectionModule;
type
TFTasksTabulator = class(TWebForm)
xdwcTasks: TXDataWebClient;
xdwdsTasks: TXDataWebDataSet;
xdwdsTaskstaskID: TStringField;
xdwdsTasksapplication: TStringField;
xdwdsTasksversion: TStringField;
xdwdsTaskstaskDate: TStringField;
xdwdsTasksreportedBy: TStringField;
xdwdsTasksassignedTo: TStringField;
xdwdsTasksstatus: TStringField;
xdwdsTasksstatusDate: TStringField;
xdwdsTasksformSection: TStringField;
xdwdsTasksissue: TStringField;
xdwdsTasksnotes: TStringField;
private
{ Private declarations }
public
{ Public declarations }
end;
var
FTasksTabulator: TFTasksTabulator;
implementation
{$R *.dfm}
end.
\ No newline at end of file
.login-card { /* Note: Base layout */
display: inline-block; html, body{
width: 300px; /* Adjust width as needed */ height:100%;
padding: 0; margin:0;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
background-color: #fff;
} }
input[type="text"] { #wrapper{
min-width: 50px; height:100vh;
max-width: 100%; display:flex;
width: auto; flex-direction:column;
padding-left: 5px; min-height:0;
} }
is-invalid .form-check-input { /* Note: Embedded forms must be allowed to shrink inside flex containers */
border: 1px solid #dc3545 !important; #main\.webpanel{
min-height:0;
flex:1 1 auto;
display:flex;
flex-direction:column;
} }
.is-invalid .form-check-label { #main\.webpanel > *{
color: #dc3545 !important; min-height:0;
} }
.input-search input { /* Note: Primary button color */
width: 100px; /* Adjust the width of the input */ .btn-primary{
height: 35px; /* Set the height to match label height */ background-color:#286090 !important;
padding: 5px; /* Padding for input text */ border-color:#286090 !important;
font-size: 14px; /* Font size for input text */ color:#fff !important;
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 { .btn-primary:hover{
width: 100%; background-color:#204d74 !important;
text-align: left; /* Align text to the left */ border-color:#204d74 !important;
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 */ /* Note: Navbar tweaks */
#view.main.apptitle { #view\.main\.apptitle{
display: flex; display:flex;
align-items: center; 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 */ .navbar-nav .nav-link.active{
#title { color:#fff !important;
white-space: nowrap; /* Prevent the title text from wrapping */ background-color:#004F84 !important;
width: 200px; /* Fixed width for the title */ font-weight:700;
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-link:hover{
.navbar-nav .nav-item { color:#fff !important;
padding: 0 15px; /* Adjust spacing between navbar items */ background-color:#286090 !important;
z-index: 9999;
} }
/* Flexbox for the entire navbar */ .navbar-toggler{
.navbar-nav { display:none;
display: flex;
justify-content: flex-end; /* Align navbar items to the right */
width: 100%;
} }
/* Additional mobile responsiveness (optional) */ /* Note: Dropdown menu items */
@media (max-width: 1200px) { .dropdown-menu a{
.navbar-nav { display:flex;
flex-direction: column; /* Stack items vertically on smaller screens */ align-items:center;
align-items: flex-start; /* Align items to the left */ width:100%;
} padding:.5rem 1rem;
color:#000;
.navbar-nav-spaced .nav-item { text-decoration:none;
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 { .dropdown-menu a:hover{
transition: background-color 0.3s ease; background-color:#204d74;
color:#fff;
} }
.table thead th{ .dropdown-menu a span{
border: 2px; flex-grow:1;
background-color: #e6e6e6;
} }
.table thead { /* Note: Login card (used on login view) */
border-color: #e6e6e6; .login-card{
display:inline-block;
width:300px;
padding:0;
border-radius:10px;
box-shadow:0 4px 8px rgba(0,0,0,.1);
background-color:#fff;
} }
.min-w-80 { /* Note: Validation helpers */
min-width: 80px; .is-invalid .form-check-input{
border:1px solid #dc3545 !important;
} }
.table { .is-invalid .form-check-label{
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); color:#dc3545 !important;
border-radius: 5px;
} }
.navbar-nav { /* Note: Toast animation */
margin-left: auto; /* Align the dropdown to the right */ @keyframes slideInLeft{
from{transform:translateX(-120%);opacity:0;}
to{transform:translateX(0);opacity:1;}
} }
.container { .toast.slide-in{
margin-top: 50px; /* Adjust the top margin as needed */ animation:slideInLeft .4s ease-out forwards;
} }
@media (max-width: 1200px) { /* Note: Spinner overlay */
.table-responsive { #spinner{
display: block; position:fixed !important;
width: 100%; z-index:9999 !important;
overflow-x: auto; top:50%;
-webkit-overflow-scrolling: touch; left:50%;
} transform:translate(-50%,-50%);
.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 { /* Note: TasksHTML (table experiment) */
background-color: #286090 !important; #tasks_table_host{
border-color: #286090 !important; height:100%;
color: #fff !important; min-height:0;
} }
.btn-primary:hover { #tasks_table_host .tasks-vscroll{
background-color: #204d74 !important; height:100%;
border-color: #204d74 !important; overflow-y:auto;
overflow-x:hidden;
} }
.login-navbar { #tasks_table_host .tasks-hscroll{
max-width: 1200px; /* Set the max-width to match a medium screen */ overflow-x:auto;
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 { #tasks_table_host .tasks-hscroll table{
display: none; width:max-content;
min-width:100%;
table-layout:fixed;
} }
.dropdown-menu a { #tasks_table_host thead th{
display: flex; /* Use flexbox for alignment */ position:sticky;
align-items: center; /* Vertically center the content */ top:0;
width: 100%; /* Ensure they take up the full width */ z-index:2;
padding: 0.5rem 1rem; /* Add padding to make them clickable */ background:var(--bs-body-bg);
color: #000; /* Adjust the text color if necessary */
text-decoration: none; /* Remove underlines */
} }
.dropdown-menu a:hover { #tasks_table_host td,
background-color: #204d74; #tasks_table_host th{
color: #fff; padding:.25rem;
} }
.dropdown-menu a span { #tasks_table_host .nowrap-cell{white-space:nowrap;}
flex-grow: 1; /* Make the span take up the remaining space */ #tasks_table_host .wrap-cell{white-space:normal;word-break:break-word;}
}
/* 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 { #tasks_table_host .cell-input,
position: relative !important; /* Ensure absolute positioning within the parent */ #tasks_table_host .cell-textarea{
width: 100% !important; /* Make the canvas fill the container width */ border:0;
height: 100% !important; /* Make the canvas fill the container height */ background:transparent;
left: 0px !important; /* Align canvas to the left */ border-radius:0;
padding:0;
margin:0;
box-shadow:none;
} }
#TFAddOrder_ScrollBar5 { #tasks_table_host .cell-input:focus,
top: 0px !important; /* Align scrollbar to the top */ #tasks_table_host .cell-textarea:focus{
right: 0px !important; /* Align scrollbar to the right edge */ outline:0;
position: absolute !important; /* Position within the grid container */ box-shadow:inset 0 -2px 0 var(--bs-primary);
height: 400px !important; /* Match the height of the grid container */
box-sizing: border-box !important; /* Prevent borders from affecting dimensions */
} }
#tblPhoneGrid { #tasks_table_host .cell-textarea{
--bs-table-bg: #fff; resize:none;
--bs-table-border-color: #dee2e6; /* or whatever border tone you like */ overflow:hidden;
white-space:pre-wrap;
} }
/* Note: TasksDataGrid (TWebDataGrid experiment) */
/* and make sure the table collapses borders so there are no gaps */ #data_grid_tasks{
#tblPhoneGrid { height:100%;
border-collapse: collapse !important; min-height:0;
}
@keyframes slideInLeft {
from {
transform: translateX(-120%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
} }
.toast.slide-in { #data_grid_tasks .ag-cell{
animation: slideInLeft 0.4s ease-out forwards; line-height:1.25;
padding-top:4px;
padding-bottom:4px;
} }
#spinner { #data_grid_tasks .ag-cell-inline-editing textarea{
position: fixed !important; line-height:1.25;
z-index: 9999 !important; padding:4px 6px;
top: 50%; resize:none;
left: 50%; height:100%;
transform: translate(-50%, -50%); box-sizing:border-box;
} }
.tbl-min-160 {
min-width: 160px;
}
......
...@@ -11,7 +11,11 @@ uses ...@@ -11,7 +11,11 @@ uses
App.Config in 'App.Config.pas', App.Config in 'App.Config.pas',
View.Main in 'View.Main.pas' {FViewMain: TWebForm} {*.html}, View.Main in 'View.Main.pas' {FViewMain: TWebForm} {*.html},
Utils in 'Utils.pas', Utils in 'Utils.pas',
View.Tasks in 'View.Tasks.pas' {FTasks: TWebForm} {*.html}; View.Tasks in 'View.Tasks.pas' {FTasks: TWebForm} {*.html},
View.TasksHTML in 'View.TasksHTML.pas' {FTasksHTML: TWebForm} {*.html},
View.TasksDataGrid in 'View.TasksDataGrid.pas' {FTasksDataGrid: TWebForm} {*.html},
View.TasksTabulator in 'View.TasksTabulator.pas' {FTasksTabulator: TWebForm} {*.html},
View.TasksDBGrid in 'View.TasksDBGrid.pas' {FTasksDBGrid: TWebForm} {*.html};
{$R *.res} {$R *.res}
......
...@@ -144,6 +144,26 @@ ...@@ -144,6 +144,26 @@
<FormType>dfm</FormType> <FormType>dfm</FormType>
<DesignClass>TWebForm</DesignClass> <DesignClass>TWebForm</DesignClass>
</DCCReference> </DCCReference>
<DCCReference Include="View.TasksHTML.pas">
<Form>FTasksHTML</Form>
<FormType>dfm</FormType>
<DesignClass>TWebForm</DesignClass>
</DCCReference>
<DCCReference Include="View.TasksDataGrid.pas">
<Form>FTasksDataGrid</Form>
<FormType>dfm</FormType>
<DesignClass>TWebForm</DesignClass>
</DCCReference>
<DCCReference Include="View.TasksTabulator.pas">
<Form>FTasksTabulator</Form>
<FormType>dfm</FormType>
<DesignClass>TWebForm</DesignClass>
</DCCReference>
<DCCReference Include="View.TasksDBGrid.pas">
<Form>FTasksDBGrid</Form>
<FormType>dfm</FormType>
<DesignClass>TWebForm</DesignClass>
</DCCReference>
<None Include="index.html"/> <None Include="index.html"/>
<None Include="css\app.css"/> <None Include="css\app.css"/>
<None Include="config\config.json"/> <None Include="config\config.json"/>
......
...@@ -116,4 +116,85 @@ object ApiDatabase: TApiDatabase ...@@ -116,4 +116,85 @@ object ApiDatabase: TApiDatabase
Size = 1000 Size = 1000
end end
end end
object uqSaveTaskRow: TUniQuery
Connection = ucEmT3
SQL.Strings = (
'UPDATE task_items'
'SET'
' APPLICATION = :APPLICATION,'
' APP_VERSION = :APP_VERSION,'
' TASK_DATE = :TASK_DATE,'
' REPORTED_BY = :REPORTED_BY,'
' ASSIGNED_TO = :ASSIGNED_TO,'
' STATUS = :STATUS,'
' STATUS_DATE = :STATUS_DATE,'
' FIXED_VERSION = :FIXED_VERSION,'
' FORM_SECTION = :FORM_SECTION,'
' ISSUE = :ISSUE,'
' NOTES = :NOTES'
'WHERE TASK_ITEM_ID = :TASK_ITEM_ID')
Left = 308
Top = 208
ParamData = <
item
DataType = ftUnknown
Name = 'APPLICATION'
Value = nil
end
item
DataType = ftUnknown
Name = 'APP_VERSION'
Value = nil
end
item
DataType = ftUnknown
Name = 'TASK_DATE'
Value = nil
end
item
DataType = ftUnknown
Name = 'REPORTED_BY'
Value = nil
end
item
DataType = ftUnknown
Name = 'ASSIGNED_TO'
Value = nil
end
item
DataType = ftUnknown
Name = 'STATUS'
Value = nil
end
item
DataType = ftUnknown
Name = 'STATUS_DATE'
Value = nil
end
item
DataType = ftUnknown
Name = 'FIXED_VERSION'
Value = nil
end
item
DataType = ftUnknown
Name = 'FORM_SECTION'
Value = nil
end
item
DataType = ftUnknown
Name = 'ISSUE'
Value = nil
end
item
DataType = ftUnknown
Name = 'NOTES'
Value = nil
end
item
DataType = ftUnknown
Name = 'TASK_ITEM_ID'
Value = nil
end>
end
end end
...@@ -31,6 +31,7 @@ type ...@@ -31,6 +31,7 @@ type
uqProjectTasksFORM_SECTION: TStringField; uqProjectTasksFORM_SECTION: TStringField;
uqProjectTasksISSUE: TStringField; uqProjectTasksISSUE: TStringField;
uqProjectTasksNOTES: TStringField; uqProjectTasksNOTES: TStringField;
uqSaveTaskRow: TUniQuery;
procedure DataModuleCreate(Sender: TObject); procedure DataModuleCreate(Sender: TObject);
procedure uqUsersCalcFields(DataSet: TDataSet); procedure uqUsersCalcFields(DataSet: TDataSet);
private private
...@@ -52,7 +53,7 @@ uses ...@@ -52,7 +53,7 @@ uses
procedure TApiDatabase.DataModuleCreate(Sender: TObject); procedure TApiDatabase.DataModuleCreate(Sender: TObject);
begin begin
Logger.Log( 1, 'TApiDatabase.DataModuleCreate' ); Logger.Log( 5, 'TApiDatabase.DataModuleCreate' );
LoadDatabaseSettings( ucEmT3, 'emT3webServer.ini' ); LoadDatabaseSettings( ucEmT3, 'emT3webServer.ini' );
try try
ucEmT3.Connect; ucEmT3.Connect;
......
...@@ -55,12 +55,30 @@ type ...@@ -55,12 +55,30 @@ type
destructor Destroy; override; destructor Destroy; override;
end; end;
TTaskRowSave = class
public
taskItemId: string;
taskId: string;
projectId: string;
application: string;
version: string;
taskDate: string;
reportedBy: string;
assignedTo: string;
status: string;
statusDate: string;
fixedVersion: string;
formSection: string;
issue: string;
notes: string;
end;
type type
[ServiceContract, Model(API_MODEL)] [ServiceContract, Model(API_MODEL)]
IApiService = interface(IInvokable) IApiService = interface(IInvokable)
['{0EFB33D7-8C4C-4F3C-9BC3-8B4D444B5F69}'] ['{0EFB33D7-8C4C-4F3C-9BC3-8B4D444B5F69}']
[HttpGet] function Ping: string;
[HttpGet] function GetProjectTasks(projectId: string): TTasksList; [HttpGet] function GetProjectTasks(projectId: string): TTasksList;
[HttpPost] function SaveTaskRow(const Item: TTaskRowSave): Boolean;
end; end;
implementation implementation
......
...@@ -4,19 +4,20 @@ interface ...@@ -4,19 +4,20 @@ interface
uses uses
XData.Server.Module, System.Generics.Collections, XData.Server.Module, System.Generics.Collections,
XData.Service.Common, System.Variants, XData.Service.Common, System.Variants, System.DateUtils,
Api.Service, Api.Database, Common.Logging; Api.Service, Api.Database, Common.Logging, System.SysUtils;
type type
[ServiceImplementation] [ServiceImplementation]
TApiService = class(TInterfacedObject, IApiService) TApiService = class(TInterfacedObject, IApiService)
strict private strict private
ApiDB: TApiDatabase; ApiDB: TApiDatabase;
private
function SaveTaskRow(const Item: TTaskRowSave): Boolean;
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
...@@ -36,11 +37,6 @@ begin ...@@ -36,11 +37,6 @@ 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
...@@ -116,6 +112,51 @@ begin ...@@ -116,6 +112,51 @@ begin
end; end;
function TApiService.SaveTaskRow(const Item: TTaskRowSave): Boolean;
function ParseDateOrZero(const S: string; out D: TDateTime): Boolean;
begin
Result := Trim(S) <> '';
if Result then
D := ISO8601ToDate(S, False);
end;
var
d: TDateTime;
begin
ApiDB.uqSaveTaskRow.Close;
ApiDB.uqSaveTaskRow.ParamByName('TASK_ITEM_ID').AsString := Item.taskItemId;
ApiDB.uqSaveTaskRow.ParamByName('APPLICATION').AsString := Item.application;
ApiDB.uqSaveTaskRow.ParamByName('APP_VERSION').AsString := Item.version;
if ParseDateOrZero(Item.taskDate, d) then
ApiDB.uqSaveTaskRow.ParamByName('TASK_DATE').AsDateTime := d
else
ApiDB.uqSaveTaskRow.ParamByName('TASK_DATE').Clear;
ApiDB.uqSaveTaskRow.ParamByName('REPORTED_BY').AsString := Item.reportedBy;
ApiDB.uqSaveTaskRow.ParamByName('ASSIGNED_TO').AsString := Item.assignedTo;
ApiDB.uqSaveTaskRow.ParamByName('STATUS').AsString := Item.status;
if ParseDateOrZero(Item.statusDate, d) then
ApiDB.uqSaveTaskRow.ParamByName('STATUS_DATE').AsDateTime := d
else
ApiDB.uqSaveTaskRow.ParamByName('STATUS_DATE').Clear;
ApiDB.uqSaveTaskRow.ParamByName('FIXED_VERSION').AsString := Item.fixedVersion;
ApiDB.uqSaveTaskRow.ParamByName('FORM_SECTION').AsString := Item.formSection;
ApiDB.uqSaveTaskRow.ParamByName('ISSUE').AsString := Item.issue;
ApiDB.uqSaveTaskRow.ParamByName('NOTES').AsString := Item.notes;
ApiDB.uqSaveTaskRow.ExecSQL;
Result := True;
end;
initialization initialization
RegisterServiceType(TypeInfo(IApiService)); RegisterServiceType(TypeInfo(IApiService));
RegisterServiceType(TApiService); RegisterServiceType(TApiService);
......
...@@ -39,7 +39,7 @@ uses ...@@ -39,7 +39,7 @@ uses
procedure TAuthDatabase.DataModuleCreate(Sender: TObject); procedure TAuthDatabase.DataModuleCreate(Sender: TObject);
begin begin
Logger.Log( 1, 'TAuthDatabase.DataModuleCreate' ); Logger.Log( 5, 'TAuthDatabase.DataModuleCreate' );
LoadDatabaseSettings( ucKG, 'kgOrdersServer.ini' ); LoadDatabaseSettings( ucKG, 'kgOrdersServer.ini' );
try try
ucKG.Connect; ucKG.Connect;
......
[Settings] [Settings]
LogFileNum=8 LogFileNum=11
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