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">
<nav class="navbar navbar-expand navbar-light bg-light" style="margin-bottom: 0px;">
<div id="wrapper" class="d-flex flex-column vh-100">
<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>
......@@ -9,9 +9,8 @@
<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 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>
......@@ -23,44 +22,33 @@
</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 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 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 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 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></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
......@@ -75,7 +63,9 @@
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>
<button type="button" id="btn_modal_restart" class="btn btn-primary">
Back to Orders
</button>
</div>
</div>
</div>
......@@ -88,12 +78,14 @@
<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-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>
<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>
......@@ -106,22 +98,12 @@
<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-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>
<button type="button" id="btn_modal_close" class="btn btn-primary">
Close
</button>
</div>
</div>
</div>
</div>
......@@ -46,7 +46,10 @@ implementation
uses
Auth.Service,
View.Login,
View.Tasks;
View.Tasks,
View.TasksHTML,
View.TasksDataGrid,
View.TasksDBGrid;
{$R *.dfm}
......@@ -64,7 +67,7 @@ var
test: boolean;
begin
FChildForm := nil;
ShowForm(TFTasks);
ShowForm(TFTasksHTML);
lblAppTitle.Caption := 'emT3web';
lblVersion.Caption := 'v' + DMConnection.clientVersion;
end;
......
object FTasks: TFTasks
Width = 1416
Height = 846
CSSLibrary = cssBootstrap
ElementFont = efCSS
FormContainer = 'tasks.root'
OnCreate = WebFormCreate
object pnlGrid: TWebPanel
......@@ -87,6 +89,7 @@ object FTasks: TFTasks
CellAppearance.SummaryLayout.Font.Name = 'Segoe UI'
CellAppearance.SummaryLayout.Font.Style = []
CellAppearance.SummaryLayout.WordWrapping = True
ColumnCount = 13
Columns = <
item
Appearance.FilterMatchLayout.Font.Charset = DEFAULT_CHARSET
......@@ -139,7 +142,8 @@ object FTasks: TFTasks
Appearance.SummaryLayout.Font.Height = -12
Appearance.SummaryLayout.Font.Name = 'Segoe UI'
Appearance.SummaryLayout.Font.Style = []
Header = 'ID'
Header = 'Task Item ID'
Name = 'COL_TASK_ITEM_ID'
Width = 70.000000000000000000
end
item
......@@ -193,7 +197,8 @@ object FTasks: TFTasks
Appearance.SummaryLayout.Font.Height = -12
Appearance.SummaryLayout.Font.Name = 'Segoe UI'
Appearance.SummaryLayout.Font.Style = []
Header = 'Name'
Header = 'Task ID'
Name = 'COL_TASK_ID'
Width = 250.000000000000000000
end
item
......@@ -247,7 +252,8 @@ object FTasks: TFTasks
Appearance.SummaryLayout.Font.Height = -12
Appearance.SummaryLayout.Font.Name = 'Segoe UI'
Appearance.SummaryLayout.Font.Style = []
Header = 'Joined'
Header = 'Application'
Name = 'COL_APP'
Width = 100.000000000000000000
end
item
......@@ -301,8 +307,502 @@ object FTasks: TFTasks
Appearance.SummaryLayout.Font.Height = -12
Appearance.SummaryLayout.Font.Name = 'Segoe UI'
Appearance.SummaryLayout.Font.Style = []
Header = 'Status'
Header = 'Version'
Name = 'COL_VERSION'
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>
Designer = False
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>
</div>
</div>
......@@ -3,15 +3,14 @@ unit View.Tasks;
interface
uses
System.SysUtils, System.Classes, System.Variants,
System.SysUtils, System.Classes,
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;
Utils, WEBLib.ExtCtrls, System.Rtti, Vcl.Controls;
type
TFTasks = class(TWebForm)
......@@ -20,10 +19,7 @@ type
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;
......@@ -55,96 +51,9 @@ const
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;
......@@ -156,7 +65,6 @@ var
taskIndex, itemIndex: Integer;
gridRow: Integer;
statusDateVal: JSValue;
r: Integer;
begin
Utils.ShowSpinner('spinner');
try
......@@ -197,7 +105,6 @@ begin
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
......@@ -215,8 +122,6 @@ begin
grdTasks.EndUpdate;
end;
grdTasks.Height := Trunc((grdTasks.RowCount * grdTasks.DefaultRowHeight) + 2);
except
on E: EXDataClientRequestException do
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 {
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;
/* Note: Base layout */
html, body{
height:100%;
margin:0;
}
input[type="text"] {
min-width: 50px;
max-width: 100%;
width: auto;
padding-left: 5px;
#wrapper{
height:100vh;
display:flex;
flex-direction:column;
min-height:0;
}
is-invalid .form-check-input {
border: 1px solid #dc3545 !important;
/* Note: Embedded forms must be allowed to shrink inside flex containers */
#main\.webpanel{
min-height:0;
flex:1 1 auto;
display:flex;
flex-direction:column;
}
.is-invalid .form-check-label {
color: #dc3545 !important;
#main\.webpanel > *{
min-height:0;
}
.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 */
/* Note: Primary button color */
.btn-primary{
background-color:#286090 !important;
border-color:#286090 !important;
color:#fff !important;
}
.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 */
.btn-primary:hover{
background-color:#204d74 !important;
border-color:#204d74 !important;
}
/* 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 */
/* Note: Navbar tweaks */
#view\.main\.apptitle{
display:flex;
align-items:center;
}
/* 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-nav .nav-link.active{
color:#fff !important;
background-color:#004F84 !important;
font-weight:700;
}
/* Navbar items - keep them aligned and spaced out */
.navbar-nav .nav-item {
padding: 0 15px; /* Adjust spacing between navbar items */
z-index: 9999;
.navbar-nav .nav-link:hover{
color:#fff !important;
background-color:#286090 !important;
}
/* Flexbox for the entire navbar */
.navbar-nav {
display: flex;
justify-content: flex-end; /* Align navbar items to the right */
width: 100%;
.navbar-toggler{
display:none;
}
/* 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
/* Note: Dropdown menu items */
.dropdown-menu a{
display:flex;
align-items:center;
width:100%;
padding:.5rem 1rem;
color:#000;
text-decoration:none;
}
.table tbody tr {
transition: background-color 0.3s ease;
.dropdown-menu a:hover{
background-color:#204d74;
color:#fff;
}
.table thead th{
border: 2px;
background-color: #e6e6e6;
.dropdown-menu a span{
flex-grow:1;
}
.table thead {
border-color: #e6e6e6;
/* Note: Login card (used on login view) */
.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 {
min-width: 80px;
/* Note: Validation helpers */
.is-invalid .form-check-input{
border:1px solid #dc3545 !important;
}
.table {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-radius: 5px;
.is-invalid .form-check-label{
color:#dc3545 !important;
}
.navbar-nav {
margin-left: auto; /* Align the dropdown to the right */
/* Note: Toast animation */
@keyframes slideInLeft{
from{transform:translateX(-120%);opacity:0;}
to{transform:translateX(0);opacity:1;}
}
.container {
margin-top: 50px; /* Adjust the top margin as needed */
.toast.slide-in{
animation:slideInLeft .4s ease-out forwards;
}
@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 */
}
/* Note: Spinner overlay */
#spinner{
position:fixed !important;
z-index:9999 !important;
top:50%;
left:50%;
transform:translate(-50%,-50%);
}
.btn-primary {
background-color: #286090 !important;
border-color: #286090 !important;
color: #fff !important;
/* Note: TasksHTML (table experiment) */
#tasks_table_host{
height:100%;
min-height:0;
}
.btn-primary:hover {
background-color: #204d74 !important;
border-color: #204d74 !important;
#tasks_table_host .tasks-vscroll{
height:100%;
overflow-y:auto;
overflow-x:hidden;
}
.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;
#tasks_table_host .tasks-hscroll{
overflow-x:auto;
}
.navbar-toggler {
display: none;
#tasks_table_host .tasks-hscroll table{
width:max-content;
min-width:100%;
table-layout:fixed;
}
.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 */
#tasks_table_host thead th{
position:sticky;
top:0;
z-index:2;
background:var(--bs-body-bg);
}
.dropdown-menu a:hover {
background-color: #204d74;
color: #fff;
#tasks_table_host td,
#tasks_table_host th{
padding:.25rem;
}
.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 */
}
#tasks_table_host .nowrap-cell{white-space:nowrap;}
#tasks_table_host .wrap-cell{white-space:normal;word-break:break-word;}
#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 */
#tasks_table_host .cell-input,
#tasks_table_host .cell-textarea{
border:0;
background:transparent;
border-radius:0;
padding:0;
margin:0;
box-shadow:none;
}
#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 */
#tasks_table_host .cell-input:focus,
#tasks_table_host .cell-textarea:focus{
outline:0;
box-shadow:inset 0 -2px 0 var(--bs-primary);
}
#tblPhoneGrid {
--bs-table-bg: #fff;
--bs-table-border-color: #dee2e6; /* or whatever border tone you like */
#tasks_table_host .cell-textarea{
resize:none;
overflow:hidden;
white-space:pre-wrap;
}
/* 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;
}
/* Note: TasksDataGrid (TWebDataGrid experiment) */
#data_grid_tasks{
height:100%;
min-height:0;
}
.toast.slide-in {
animation: slideInLeft 0.4s ease-out forwards;
#data_grid_tasks .ag-cell{
line-height:1.25;
padding-top:4px;
padding-bottom:4px;
}
#spinner {
position: fixed !important;
z-index: 9999 !important;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
#data_grid_tasks .ag-cell-inline-editing textarea{
line-height:1.25;
padding:4px 6px;
resize:none;
height:100%;
box-sizing:border-box;
}
.tbl-min-160 {
min-width: 160px;
}
......
......@@ -11,7 +11,11 @@ uses
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};
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}
......
......@@ -144,6 +144,26 @@
<FormType>dfm</FormType>
<DesignClass>TWebForm</DesignClass>
</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="css\app.css"/>
<None Include="config\config.json"/>
......
......@@ -116,4 +116,85 @@ object ApiDatabase: TApiDatabase
Size = 1000
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
......@@ -31,6 +31,7 @@ type
uqProjectTasksFORM_SECTION: TStringField;
uqProjectTasksISSUE: TStringField;
uqProjectTasksNOTES: TStringField;
uqSaveTaskRow: TUniQuery;
procedure DataModuleCreate(Sender: TObject);
procedure uqUsersCalcFields(DataSet: TDataSet);
private
......@@ -52,7 +53,7 @@ uses
procedure TApiDatabase.DataModuleCreate(Sender: TObject);
begin
Logger.Log( 1, 'TApiDatabase.DataModuleCreate' );
Logger.Log( 5, 'TApiDatabase.DataModuleCreate' );
LoadDatabaseSettings( ucEmT3, 'emT3webServer.ini' );
try
ucEmT3.Connect;
......
......@@ -55,12 +55,30 @@ type
destructor Destroy; override;
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
[ServiceContract, Model(API_MODEL)]
IApiService = interface(IInvokable)
['{0EFB33D7-8C4C-4F3C-9BC3-8B4D444B5F69}']
[HttpGet] function Ping: string;
[HttpGet] function GetProjectTasks(projectId: string): TTasksList;
[HttpPost] function SaveTaskRow(const Item: TTaskRowSave): Boolean;
end;
implementation
......
......@@ -4,19 +4,20 @@ interface
uses
XData.Server.Module, System.Generics.Collections,
XData.Service.Common, System.Variants,
Api.Service, Api.Database, Common.Logging;
XData.Service.Common, System.Variants, System.DateUtils,
Api.Service, Api.Database, Common.Logging, System.SysUtils;
type
[ServiceImplementation]
TApiService = class(TInterfacedObject, IApiService)
strict private
ApiDB: TApiDatabase;
private
function SaveTaskRow(const Item: TTaskRowSave): Boolean;
public
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
function GetProjectTasks(projectId: string): TTasksList;
function Ping: string;
end;
implementation
......@@ -36,11 +37,6 @@ begin
end;
function TApiService.Ping: string;
begin
Result := 'pong';
end;
function TApiService.GetProjectTasks(projectId: string): TTasksList;
var
......@@ -116,6 +112,51 @@ begin
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
RegisterServiceType(TypeInfo(IApiService));
RegisterServiceType(TApiService);
......
......@@ -39,7 +39,7 @@ uses
procedure TAuthDatabase.DataModuleCreate(Sender: TObject);
begin
Logger.Log( 1, 'TAuthDatabase.DataModuleCreate' );
Logger.Log( 5, 'TAuthDatabase.DataModuleCreate' );
LoadDatabaseSettings( ucKG, 'kgOrdersServer.ini' );
try
ucKG.Connect;
......
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