Commit 38ccc4e0 by Mac Stephens

Add task row delete/select workflow with focus retention, sticky column headers,…

Add task row delete/select workflow with focus retention, sticky column headers, and Enter-to-commit item number reordering.
parent 41b5088d
...@@ -56,9 +56,9 @@ object FViewMain: TFViewMain ...@@ -56,9 +56,9 @@ object FViewMain: TFViewMain
object lblAppTitle: TWebLabel object lblAppTitle: TWebLabel
Left = 57 Left = 57
Top = 33 Top = 33
Width = 42 Width = 48
Height = 14 Height = 14
Caption = 'App Title' Caption = 'emT3Web'
ElementID = 'lbl_app_title' ElementID = 'lbl_app_title'
ElementPosition = epRelative ElementPosition = epRelative
HeightPercent = 100.000000000000000000 HeightPercent = 100.000000000000000000
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<nav class="navbar navbar-expand-lg bg-body-tertiary border-bottom shadow-sm"> <nav class="navbar navbar-expand-lg bg-body-tertiary border-bottom shadow-sm">
<div class="container-fluid"> <div class="container-fluid">
<div class="d-flex align-items-center gap-2"> <div class="d-flex align-items-center gap-2">
<a id="lbl_app_title" class="navbar-brand fw-semibold" href="index.html">Koehler-Gibson Orders</a> <a id="lbl_app_title" class="navbar-brand fw-semibold" href="index.html">emT3Web</a>
<span id="lbl_version" class="badge text-bg-light border text-muted fw-normal"></span> <span id="lbl_version" class="badge text-bg-light border text-muted fw-normal"></span>
</div> </div>
......
...@@ -12,6 +12,7 @@ object FTasksHTML: TFTasksHTML ...@@ -12,6 +12,7 @@ object FTasksHTML: TFTasksHTML
Caption = 'Reload' Caption = 'Reload'
ElementID = 'btn_reload' ElementID = 'btn_reload'
HeightPercent = 100.000000000000000000 HeightPercent = 100.000000000000000000
TabStop = False
WidthPercent = 100.000000000000000000 WidthPercent = 100.000000000000000000
OnClick = btnReloadClick OnClick = btnReloadClick
end end
...@@ -24,9 +25,25 @@ object FTasksHTML: TFTasksHTML ...@@ -24,9 +25,25 @@ object FTasksHTML: TFTasksHTML
ChildOrder = 1 ChildOrder = 1
ElementID = 'btn_add_row' ElementID = 'btn_add_row'
HeightPercent = 100.000000000000000000 HeightPercent = 100.000000000000000000
TabStop = False
WidthPercent = 100.000000000000000000 WidthPercent = 100.000000000000000000
OnClick = btnAddRowClick OnClick = btnAddRowClick
end end
object btnDeleteRow: TWebButton
Left = 78
Top = 150
Width = 96
Height = 25
Caption = 'Delete Row'
ChildOrder = 2
ElementID = 'btn_delete_row'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
TabStop = False
WidthPercent = 100.000000000000000000
OnClick = btnDeleteRowClick
end
object xdwcTasks: TXDataWebClient object xdwcTasks: TXDataWebClient
Connection = DMConnection.ApiConnection Connection = DMConnection.ApiConnection
Left = 506 Left = 506
......
...@@ -3,11 +3,12 @@ ...@@ -3,11 +3,12 @@
<h5 class="mb-0" id="lbl_project_name"></h5> <h5 class="mb-0" id="lbl_project_name"></h5>
<div class="d-flex gap-2"> <div class="d-flex gap-2">
<button id="btn_add_row" class="btn btn-sm btn-outline-success">Add Row</button> <button id="btn_add_row" class="btn btn-sm btn-outline-success">Add Row</button>
<button id="btn_delete_row" class="btn btn-sm btn-outline-danger">Delete Row</button>
<button id="btn_reload" class="btn btn-sm btn-outline-primary">Reload</button> <button id="btn_reload" class="btn btn-sm btn-outline-primary">Reload</button>
</div> </div>
</div> </div>
<div id="tasks_table_host" class="flex-grow-1 min-vh-0"></div> <div id="tasks_table_host" class="flex-grow-1 min-vh-0 overflow-auto"></div>
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasNameManager" aria-labelledby="nm_title"> <div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasNameManager" aria-labelledby="nm_title">
<div class="offcanvas-header"> <div class="offcanvas-header">
......
...@@ -29,14 +29,22 @@ type ...@@ -29,14 +29,22 @@ type
btnAddRow: TWebButton; btnAddRow: TWebButton;
xdwdsTasksitemNum: TIntegerField; xdwdsTasksitemNum: TIntegerField;
xdwdsTaskstaskItemId: TIntegerField; xdwdsTaskstaskItemId: TIntegerField;
btnDeleteRow: TWebButton;
[async] procedure btnAddRowClick(Sender: TObject); [async] procedure btnAddRowClick(Sender: TObject);
procedure btnReloadClick(Sender: TObject); procedure btnReloadClick(Sender: TObject);
procedure WebFormCreate(Sender: TObject); procedure WebFormCreate(Sender: TObject);
[async] procedure btnDeleteRowClick(Sender: TObject);
private private
FTaskId: string; FTaskId: string;
FReportedByOptions: TJSArray; FReportedByOptions: TJSArray;
FAssignedToOptions: TJSArray; FAssignedToOptions: TJSArray;
FStatusOptions: TJSArray; FStatusOptions: TJSArray;
FPendingFocusTaskItemId: integer;
FPendingFocusField: string;
FSelectedTaskItemId: Integer;
FSelectedTaskId: Integer;
FPendingFocusItemNum: Integer;
FPendingFocusTaskField: string;
FNameManager: TNameManager; FNameManager: TNameManager;
[async] procedure LoadTasks(const ATaskId: string); [async] procedure LoadTasks(const ATaskId: string);
procedure RenderTable; procedure RenderTable;
...@@ -51,6 +59,7 @@ type ...@@ -51,6 +59,7 @@ type
[async] procedure SaveRow(AIndex: Integer); [async] procedure SaveRow(AIndex: Integer);
procedure EditorBlur(Event: TJSEvent); procedure EditorBlur(Event: TJSEvent);
[async] function AddTaskRow: Boolean; [async] function AddTaskRow: Boolean;
[async] function DeleteTaskRow: Boolean;
function ExtractOptionNames(const SourceArray: TJSArray): TJSArray; function ExtractOptionNames(const SourceArray: TJSArray): TJSArray;
function GetOptionsForField(const AFieldName: string): TJSArray; function GetOptionsForField(const AFieldName: string): TJSArray;
procedure FocusTrigger(const ATriggerId: string); procedure FocusTrigger(const ATriggerId: string);
...@@ -58,6 +67,11 @@ type ...@@ -58,6 +67,11 @@ type
procedure DropdownEditClick(Event: TJSEvent); procedure DropdownEditClick(Event: TJSEvent);
function ExtractCodeDescs(const SourceArray: TJSArray): TJSArray; function ExtractCodeDescs(const SourceArray: TJSArray): TJSArray;
[async] procedure MoveTaskRow(AIndex: Integer; const newItemNum: Integer); [async] procedure MoveTaskRow(AIndex: Integer; const newItemNum: Integer);
procedure ApplyPendingFocus;
procedure RowClick(Event: TJSEvent);
procedure ApplySelectedRowState;
procedure ApplyPendingDeleteFocus;
procedure EditorKeyDown(Event: TJSEvent);
public public
end; end;
...@@ -79,6 +93,8 @@ begin ...@@ -79,6 +93,8 @@ begin
FReportedByOptions := TJSArray.new; FReportedByOptions := TJSArray.new;
FAssignedToOptions := TJSArray.new; FAssignedToOptions := TJSArray.new;
FStatusOptions := TJSArray.new; FStatusOptions := TJSArray.new;
FSelectedTaskItemId := 0;
FSelectedTaskId := 0;
FNameManager := TNameManager.Create( FNameManager := TNameManager.Create(
function(const AFieldName: string): TJSArray function(const AFieldName: string): TJSArray
...@@ -104,6 +120,7 @@ begin ...@@ -104,6 +120,7 @@ begin
end; end;
btnAddRow.Enabled := False; btnAddRow.Enabled := False;
btnDeleteRow.Enabled := False;
if not DMConnection.ApiConnection.Connected then if not DMConnection.ApiConnection.Connected then
begin begin
...@@ -234,6 +251,7 @@ begin ...@@ -234,6 +251,7 @@ begin
el := TJSHTMLElement(nodes.item(i)); el := TJSHTMLElement(nodes.item(i));
el.addEventListener('input', TJSEventHandler(@EditorInput)); el.addEventListener('input', TJSEventHandler(@EditorInput));
el.addEventListener('blur', TJSEventHandler(@EditorBlur)); el.addEventListener('blur', TJSEventHandler(@EditorBlur));
el.addEventListener('keydown', TJSEventHandler(@EditorKeyDown));
end; end;
nodes := document.querySelectorAll('.task-select'); nodes := document.querySelectorAll('.task-select');
...@@ -257,6 +275,45 @@ begin ...@@ -257,6 +275,45 @@ begin
el := TJSHTMLElement(nodes.item(i)); el := TJSHTMLElement(nodes.item(i));
el.addEventListener('click', TJSEventHandler(@DropdownEditClick)); el.addEventListener('click', TJSEventHandler(@DropdownEditClick));
end; end;
nodes := document.querySelectorAll('.task-row-selectable');
for i := 0 to nodes.length - 1 do
begin
el := TJSHTMLElement(nodes.item(i));
el.addEventListener('click', TJSEventHandler(@RowClick));
end;
ApplySelectedRowState;
end;
procedure TFTasksHTML.EditorKeyDown(Event: TJSEvent);
var
el: TJSHTMLElement;
fieldName: string;
newItemNum: Integer;
idx: Integer;
begin
el := TJSHTMLElement(Event.target);
fieldName := string(el.getAttribute('data-field'));
if not SameText(fieldName, 'itemNum') then
Exit;
if TJSKeyboardEvent(Event).key <> 'Enter' then
Exit;
Event.preventDefault;
idx := StrToIntDef(string(el.getAttribute('data-idx')), -1);
if idx < 0 then
Exit;
newItemNum := StrToIntDef(string(TJSObject(el)['value']), 0);
el.removeAttribute('data-unsaved-data');
MoveTaskRow(idx, newItemNum);
end; end;
...@@ -271,6 +328,81 @@ begin ...@@ -271,6 +328,81 @@ begin
end; end;
end; end;
[async] procedure TFTasksHTML.btnDeleteRowClick(Sender: TObject);
var
deletedItemNum: Integer;
begin
if FSelectedTaskItemId <= 0 then
Exit;
deletedItemNum := 0;
if xdwdsTasks.Active then
begin
xdwdsTasks.First;
while not xdwdsTasks.Eof do
begin
if xdwdsTaskstaskItemId.AsInteger = FSelectedTaskItemId then
begin
deletedItemNum := xdwdsTasksitemNum.AsInteger;
Break;
end;
xdwdsTasks.Next;
end;
end;
Utils.ShowSpinner('spinner');
try
if await(DeleteTaskRow) then
begin
FSelectedTaskItemId := 0;
FSelectedTaskId := 0;
btnDeleteRow.Enabled := False;
FPendingFocusItemNum := deletedItemNum;
FPendingFocusTaskField := 'application';
LoadTasks(FTaskId);
end;
finally
Utils.HideSpinner('spinner');
end;
end;
procedure TFTasksHTML.RowClick(Event: TJSEvent);
var
rowEl: TJSHTMLElement;
taskItemIdStr: string;
taskIdStr: string;
begin
rowEl := TJSHTMLElement(Event.currentTarget);
if not Assigned(rowEl) then
Exit;
taskItemIdStr := string(rowEl.getAttribute('data-task-item-id'));
taskIdStr := string(rowEl.getAttribute('data-task-id'));
FSelectedTaskItemId := StrToIntDef(taskItemIdStr, 0);
FSelectedTaskId := StrToIntDef(taskIdStr, 0);
btnDeleteRow.Enabled := FSelectedTaskItemId > 0;
ApplySelectedRowState;
end;
procedure TFTasksHTML.ApplySelectedRowState;
begin
asm
const selectedTaskItemId = this.FSelectedTaskItemId;
document.querySelectorAll('.task-row-selectable').forEach(function(row){
const rowTaskItemId = parseInt(row.getAttribute('data-task-item-id') || '0', 10);
if ((selectedTaskItemId > 0) && (rowTaskItemId === selectedTaskItemId))
row.classList.add('table-active');
else
row.classList.remove('table-active');
});
end;
end;
[async] function TFTasksHTML.AddTaskRow: Boolean; [async] function TFTasksHTML.AddTaskRow: Boolean;
var var
...@@ -387,6 +519,7 @@ begin ...@@ -387,6 +519,7 @@ begin
xdwdsTasks.Open; xdwdsTasks.Open;
btnAddRow.Enabled := True; btnAddRow.Enabled := True;
btnDeleteRow.Enabled := FSelectedTaskItemId > 0;
RenderTable; RenderTable;
finally finally
...@@ -549,7 +682,7 @@ begin ...@@ -549,7 +682,7 @@ begin
html := html :=
'<div class="tasks-vscroll">' + '<div class="tasks-vscroll">' +
'<div class="tasks-hscroll">' + '<div class="tasks-hscroll">' +
'<table class="table table-sm table-bordered align-middle mb-0">' + '<table class="table table-sm align-middle mb-0" style="min-width: 2000px;">' +
'<colgroup>' + '<colgroup>' +
'<col style="width:40px">' + '<col style="width:40px">' +
'<col style="width:240px">' + '<col style="width:240px">' +
...@@ -582,7 +715,7 @@ begin ...@@ -582,7 +715,7 @@ begin
while not xdwdsTasks.Eof do while not xdwdsTasks.Eof do
begin begin
html := html + html := html +
'<tr>' + '<tr class="task-row-selectable" data-task-item-id="' + IntToStr(xdwdsTaskstaskItemId.AsInteger) + '" data-task-id="' + xdwdsTaskstaskId.AsString + '" data-item-num="' + IntToStr(xdwdsTasksitemNum.AsInteger) + '">' +
TdNowrap(ItemNumInput(xdwdsTasksitemNum.AsInteger, rowIdx)) + TdNowrap(ItemNumInput(xdwdsTasksitemNum.AsInteger, rowIdx)) +
TdNowrap(TextInput('application', xdwdsTasksapplication.AsString, rowIdx, 180)) + TdNowrap(TextInput('application', xdwdsTasksapplication.AsString, rowIdx, 180)) +
TdNowrap(TextInput('version', xdwdsTasksversion.AsString, rowIdx, 80)) + TdNowrap(TextInput('version', xdwdsTasksversion.AsString, rowIdx, 80)) +
...@@ -606,6 +739,8 @@ begin ...@@ -606,6 +739,8 @@ begin
BindTableEditors; BindTableEditors;
EnableAutoGrowTextAreas; EnableAutoGrowTextAreas;
EnableColumnResize; EnableColumnResize;
ApplyPendingFocus;
ApplyPendingDeleteFocus;
end; end;
...@@ -688,6 +823,7 @@ end; ...@@ -688,6 +823,7 @@ end;
[async] procedure TFTasksHTML.MoveTaskRow(AIndex: Integer; const newItemNum: Integer); [async] procedure TFTasksHTML.MoveTaskRow(AIndex: Integer; const newItemNum: Integer);
var var
response: TXDataClientResponse; response: TXDataClientResponse;
movedTaskItemId: Integer;
begin begin
if not xdwdsTasks.Active then if not xdwdsTasks.Active then
Exit; Exit;
...@@ -696,16 +832,22 @@ begin ...@@ -696,16 +832,22 @@ begin
if xdwdsTasks.Eof then if xdwdsTasks.Eof then
Exit; Exit;
movedTaskItemId := xdwdsTaskstaskItemId.AsInteger;
try try
response := await(xdwcTasks.RawInvokeAsync( response := await(xdwcTasks.RawInvokeAsync(
'IApiService.MoveTaskRow', 'IApiService.MoveTaskRow',
[ [
StrToIntDef(xdwdsTaskstaskId.AsString, 0), StrToIntDef(xdwdsTaskstaskId.AsString, 0),
xdwdsTaskstaskItemId.AsInteger, movedTaskItemId,
newItemNum newItemNum
] ]
)); ));
console.log('MoveTaskRow: response=' + string(TJSJSON.stringify(response.Result))); console.log('MoveTaskRow: response=' + string(TJSJSON.stringify(response.Result)));
FPendingFocusTaskItemId := movedTaskItemId;
FPendingFocusField := 'application';
LoadTasks(FTaskId); LoadTasks(FTaskId);
except except
on E: EXDataClientRequestException do on E: EXDataClientRequestException do
...@@ -769,6 +911,31 @@ begin ...@@ -769,6 +911,31 @@ begin
end; end;
[async] function TFTasksHTML.DeleteTaskRow: Boolean;
var
response: TXDataClientResponse;
begin
Result := False;
if (FSelectedTaskId <= 0) or (FSelectedTaskItemId <= 0) then
Exit;
try
response := await(xdwcTasks.RawInvokeAsync(
'IApiService.DeleteTaskRow',
[FSelectedTaskId, FSelectedTaskItemId]
));
console.log('DeleteTaskRow response=' + string(TJSJSON.stringify(response.Result)));
Result := True;
except
on E: EXDataClientRequestException do
begin
console.log('DeleteTaskRow ERROR: ' + E.ErrorResult.ErrorMessage);
Utils.ShowErrorModal(E.ErrorResult.ErrorMessage);
end;
end;
end;
function TFTasksHTML.ExtractOptionNames(const SourceArray: TJSArray): TJSArray; function TFTasksHTML.ExtractOptionNames(const SourceArray: TJSArray): TJSArray;
var var
i: Integer; i: Integer;
...@@ -901,6 +1068,56 @@ begin ...@@ -901,6 +1068,56 @@ begin
end; end;
procedure TFTasksHTML.ApplyPendingFocus;
var
el: TJSHTMLElement;
selector: string;
begin
if (FPendingFocusTaskItemId <= 0) or (FPendingFocusField = '') then
Exit;
selector :=
'[data-task-item-id="' + IntToStr(FPendingFocusTaskItemId) + '"] ' +
'[data-field="' + FPendingFocusField + '"]';
el := TJSHTMLElement(document.querySelector(selector));
if Assigned(el) then
begin
asm
el.scrollIntoView({ behavior: 'auto', block: 'nearest', inline: 'nearest' });
el.focus();
end;
end;
FPendingFocusTaskItemId := 0;
FPendingFocusField := '';
end;
procedure TFTasksHTML.ApplyPendingDeleteFocus;
var
el: TJSHTMLElement;
selector: string;
begin
if (FPendingFocusItemNum <= 0) or (FPendingFocusTaskField = '') then
Exit;
selector :=
'tr[data-item-num="' + IntToStr(FPendingFocusItemNum) + '"] ' +
'[data-field="' + FPendingFocusTaskField + '"]';
el := TJSHTMLElement(document.querySelector(selector));
if Assigned(el) then
begin
asm
el.scrollIntoView({ behavior: 'auto', block: 'nearest', inline: 'nearest' });
el.focus();
end;
end;
FPendingFocusItemNum := 0;
FPendingFocusTaskField := '';
end;
......
...@@ -40,6 +40,7 @@ is-invalid .form-check-input { ...@@ -40,6 +40,7 @@ is-invalid .form-check-input {
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
} }
/* This hides the up and down arrows on the item_num box, comment or remove it to add them back */ /* This hides the up and down arrows on the item_num box, comment or remove it to add them back */
input[data-field="itemNum"]::-webkit-outer-spin-button, input[data-field="itemNum"]::-webkit-outer-spin-button,
input[data-field="itemNum"]::-webkit-inner-spin-button { input[data-field="itemNum"]::-webkit-inner-spin-button {
-webkit-appearance: none; -webkit-appearance: none;
...@@ -51,4 +52,21 @@ input[data-field="itemNum"] { ...@@ -51,4 +52,21 @@ input[data-field="itemNum"] {
appearance: textfield; appearance: textfield;
} }
.tasks-vscroll {
max-height: calc(100vh - 260px);
overflow: auto;
}
.tasks-vscroll thead th {
position: sticky;
top: 0;
z-index: 2;
background: var(--bs-body-bg);
}
.tasks-vscroll thead th.th-resize {
z-index: 3;
}
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta content="width=device-width, initial-scale=1" name="viewport"/> <meta content="width=device-width, initial-scale=1" name="viewport"/>
<link href="data:;base64,=" rel="icon"/> <link href="data:;base64,=" rel="icon"/>
<title>EM Systems Template App</title> <title>emT3Web</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/2.3.1/css/flag-icon.min.css" rel="stylesheet"/> <link href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/2.3.1/css/flag-icon.min.css" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>
......
...@@ -563,4 +563,45 @@ object ApiDatabase: TApiDatabase ...@@ -563,4 +563,45 @@ object ApiDatabase: TApiDatabase
ReadOnly = True ReadOnly = True
end end
end end
object uqDeleteTaskRow: TUniQuery
Connection = ucETaskApi
SQL.Strings = (
'delete from task_items'
'where TASK_ITEM_ID = :TASK_ITEM_ID'
' and TASK_ID = :TASK_ID')
Left = 408
Top = 182
ParamData = <
item
DataType = ftUnknown
Name = 'TASK_ITEM_ID'
Value = nil
end
item
DataType = ftUnknown
Name = 'TASK_ID'
Value = nil
end>
end
object uqShiftTaskRowsAfterDelete: TUniQuery
Connection = ucETaskApi
SQL.Strings = (
'update task_items'
'set ITEM_NUM = ITEM_NUM - 1'
'where TASK_ID = :TASK_ID'
' and ITEM_NUM > :OLD_ITEM_NUM')
Left = 408
Top = 130
ParamData = <
item
DataType = ftUnknown
Name = 'TASK_ID'
Value = nil
end
item
DataType = ftUnknown
Name = 'OLD_ITEM_NUM'
Value = nil
end>
end
end end
...@@ -64,6 +64,8 @@ type ...@@ -64,6 +64,8 @@ type
uqGetTaskMaxItemNumMAX_ITEM_NUM: TIntegerField; uqGetTaskMaxItemNumMAX_ITEM_NUM: TIntegerField;
uqTaskItemsTASK_ITEM_ID: TIntegerField; uqTaskItemsTASK_ITEM_ID: TIntegerField;
uqGetTaskRowPositionTASK_ITEM_ID: TIntegerField; uqGetTaskRowPositionTASK_ITEM_ID: TIntegerField;
uqDeleteTaskRow: TUniQuery;
uqShiftTaskRowsAfterDelete: TUniQuery;
procedure DataModuleCreate(Sender: TObject); procedure DataModuleCreate(Sender: TObject);
procedure uqUsersCalcFields(DataSet: TDataSet); procedure uqUsersCalcFields(DataSet: TDataSet);
private private
......
...@@ -96,6 +96,7 @@ type ...@@ -96,6 +96,7 @@ type
[HttpPost] function SaveTaskRow(Item: TTaskRowSave): Boolean; [HttpPost] function SaveTaskRow(Item: TTaskRowSave): Boolean;
function TestApi(messageText: string): TJSONObject; function TestApi(messageText: string): TJSONObject;
procedure MoveTaskRow(const taskId: Integer; const taskItemId: Integer; const newItemNum: Integer); procedure MoveTaskRow(const taskId: Integer; const taskItemId: Integer; const newItemNum: Integer);
function DeleteTaskRow(const taskId: Integer; const taskItemId: Integer): Boolean;
end; end;
implementation implementation
......
...@@ -25,6 +25,7 @@ type ...@@ -25,6 +25,7 @@ type
function TestApi(messageText: string): TJSONObject; function TestApi(messageText: string): TJSONObject;
function GetWebClientVersion: string; function GetWebClientVersion: string;
procedure MoveTaskRow(const taskId: Integer; const taskItemId: Integer; const newItemNum: Integer); procedure MoveTaskRow(const taskId: Integer; const taskItemId: Integer; const newItemNum: Integer);
function DeleteTaskRow(const taskId, taskItemId: Integer): Boolean;
public public
procedure AfterConstruction; override; procedure AfterConstruction; override;
procedure BeforeDestruction; override; procedure BeforeDestruction; override;
...@@ -393,6 +394,52 @@ begin ...@@ -393,6 +394,52 @@ begin
end; end;
function TApiService.DeleteTaskRow(const taskId: Integer; const taskItemId: Integer): Boolean;
var
oldItemNum: Integer;
begin
Logger.Log(4, Format('ApiService.DeleteTaskRow - TASK_ID="%d" TASK_ITEM_ID="%d"', [taskId, taskItemId]));
apiDB.uqGetTaskRowPosition.Close;
apiDB.uqGetTaskRowPosition.ParamByName('TASK_ID').AsInteger := taskId;
apiDB.uqGetTaskRowPosition.ParamByName('TASK_ITEM_ID').AsInteger := taskItemId;
apiDB.uqGetTaskRowPosition.Open;
if apiDB.uqGetTaskRowPosition.IsEmpty then
begin
Logger.Log(2, Format('ApiService.DeleteTaskRow - row not found TASK_ID="%d" TASK_ITEM_ID="%d"', [taskId, taskItemId]));
raise Exception.Create('Task row not found.');
end;
oldItemNum := apiDB.uqGetTaskRowPositionITEM_NUM.AsInteger;
apiDB.uqGetTaskRowPosition.Connection.StartTransaction;
try
apiDB.uqDeleteTaskRow.ParamByName('TASK_ID').AsInteger := taskId;
apiDB.uqDeleteTaskRow.ParamByName('TASK_ITEM_ID').AsInteger := taskItemId;
apiDB.uqDeleteTaskRow.ExecSQL;
apiDB.uqShiftTaskRowsAfterDelete.ParamByName('TASK_ID').AsInteger := taskId;
apiDB.uqShiftTaskRowsAfterDelete.ParamByName('OLD_ITEM_NUM').AsInteger := oldItemNum;
apiDB.uqShiftTaskRowsAfterDelete.ExecSQL;
apiDB.uqGetTaskRowPosition.Connection.Commit;
Result := True;
Logger.Log(4, Format('ApiService.DeleteTaskRow - OK TASK_ID="%d" TASK_ITEM_ID="%d" OLD_ITEM_NUM="%d"', [taskId, taskItemId, oldItemNum]));
except
on E: Exception do
begin
if apiDB.uqGetTaskRowPosition.Connection.InTransaction then
apiDB.uqGetTaskRowPosition.Connection.Rollback;
Logger.Log(2, Format('ApiService.DeleteTaskRow - ERROR TASK_ID="%d" TASK_ITEM_ID="%d" MSG="%s"', [taskId, taskItemId, E.Message]));
raise;
end;
end;
end;
function TApiService.TestApi(messageText: string): TJSONObject; function TApiService.TestApi(messageText: string): TJSONObject;
var var
requiredVersion: string; requiredVersion: string;
......
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