Commit 4a314fa8 by Mac Stephens

v0.8.5 fixes Deployed 0.8.8

parent 071af034
...@@ -20,7 +20,7 @@ type ...@@ -20,7 +20,7 @@ type
FUnauthorizedAccessProc: TUnauthorizedAccessProc; FUnauthorizedAccessProc: TUnauthorizedAccessProc;
public public
const clientVersion = '0.8.7'; const clientVersion = '0.8.8';
procedure InitApp(SuccessProc: TSuccessProc; procedure InitApp(SuccessProc: TSuccessProc;
UnauthorizedAccessProc: TUnauthorizedAccessProc); UnauthorizedAccessProc: TUnauthorizedAccessProc);
procedure SetClientConfig(Callback: TVersionCheckCallback); procedure SetClientConfig(Callback: TVersionCheckCallback);
......
...@@ -14,6 +14,7 @@ procedure ShowToast(const MessageText: string; const ToastType: string = 'succes ...@@ -14,6 +14,7 @@ procedure ShowToast(const MessageText: string; const ToastType: string = 'succes
procedure ShowConfirmationModal(msg, leftLabel, rightLabel: string; ConfirmProc: TProc<Boolean>); procedure ShowConfirmationModal(msg, leftLabel, rightLabel: string; ConfirmProc: TProc<Boolean>);
procedure ShowNotificationModal(msg: string); procedure ShowNotificationModal(msg: string);
procedure ShowAppDialog(const Msg: string; const Title: string = 'emT3 web app'; ReloadOnClose: Boolean = False; ButtonText: string = 'Close'); procedure ShowAppDialog(const Msg: string; const Title: string = 'emT3 web app'; ReloadOnClose: Boolean = False; ButtonText: string = 'Close');
function NormalizeDateValue(const Value: string): string;
implementation implementation
...@@ -281,5 +282,22 @@ begin ...@@ -281,5 +282,22 @@ begin
end; end;
function NormalizeDateValue(const Value: string): string;
var
normalizedValue: string;
begin
normalizedValue := Trim(Value);
if (normalizedValue = '') or
SameText(normalizedValue, '1899-12-30') or
SameText(normalizedValue, '12/30/1899') or
SameText(normalizedValue, '1899-12-30T00:00:00') or
SameText(normalizedValue, '1899-12-30 00:00:00') then
Result := ''
else
Result := normalizedValue;
end;
end. end.
...@@ -5,7 +5,7 @@ interface ...@@ -5,7 +5,7 @@ interface
uses uses
System.SysUtils, System.Classes, System.SysUtils, System.Classes,
JS, Web, WEBLib.Graphics, WEBLib.Controls, WEBLib.Forms, WEBLib.Dialogs, JS, Web, WEBLib.Graphics, WEBLib.Controls, WEBLib.Forms, WEBLib.Dialogs,
WEBLib.ExtCtrls, uNameManager, WEBLib.ExtCtrls, uNameOffCanvas, uDropdownHelpers,
XData.Web.Client, XData.Web.Dataset, XData.Web.Client, XData.Web.Dataset,
Utils, Data.DB, XData.Web.JsonDataset, Vcl.Controls, Vcl.StdCtrls, Utils, Data.DB, XData.Web.JsonDataset, Vcl.Controls, Vcl.StdCtrls,
WEBLib.StdCtrls; WEBLib.StdCtrls;
...@@ -60,16 +60,12 @@ type ...@@ -60,16 +60,12 @@ type
function HtmlEncode(const s: string): string; function HtmlEncode(const s: string): string;
procedure SetTotalRowsLabel(ARowCount: Integer); procedure SetTotalRowsLabel(ARowCount: Integer);
procedure SetTaskLabel(const ATitle: string); procedure SetTaskLabel(const ATitle: string);
[async] procedure SaveRow(AIndex: Integer); [async] procedure SaveField(AIndex: Integer; const AFieldName: string);
procedure EditorBlur(Event: TJSEvent); procedure EditorBlur(Event: TJSEvent);
[async] function AddTaskRow: Boolean; [async] function AddTaskRow: Boolean;
[async] function DeleteTaskRow: Boolean; [async] function DeleteTaskRow: Boolean;
function ExtractOptionNames(const SourceArray: TJSArray): TJSArray;
function GetOptionsForField(const AFieldName: string): TJSArray;
procedure FocusTrigger(const ATriggerId: string);
procedure DropdownItemClick(Event: TJSEvent); procedure DropdownItemClick(Event: TJSEvent);
procedure DropdownEditClick(Event: TJSEvent); procedure DropdownEditClick(Event: TJSEvent);
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 ApplyPendingFocus;
procedure RowClick(Event: TJSEvent); procedure RowClick(Event: TJSEvent);
...@@ -81,15 +77,15 @@ type ...@@ -81,15 +77,15 @@ type
[async] function AddAssignedName(const AName: string): TJSArray; [async] function AddAssignedName(const AName: string): TJSArray;
[async] function RenameAssignedName(const AOldName, ANewName: string): TJSArray; [async] function RenameAssignedName(const AOldName, ANewName: string): TJSArray;
[async] function DeleteAssignedName(const AName: string): TJSArray; [async] function DeleteAssignedName(const AName: string): TJSArray;
function ExtractAssignedOptionNames(const ResponseResult: TJSObject): TJSArray; [async] function AddReportedName(const AName: string): TJSArray;
[async] function RenameReportedName(const AOldName, ANewName: string): TJSArray;
[async] function DeleteReportedName(const AName: string): TJSArray;
[async] function AddApplicationName(const AName: string): TJSArray; [async] function AddApplicationName(const AName: string): TJSArray;
[async] function RenameApplicationName(const AOldName, ANewName: string): TJSArray; [async] function RenameApplicationName(const AOldName, ANewName: string): TJSArray;
[async] function DeleteApplicationName(const AName: string): TJSArray; [async] function DeleteApplicationName(const AName: string): TJSArray;
function ExtractApplicationOptionNames(const ResponseResult: TJSObject): TJSArray;
[async] procedure HandleAddManagedName(const AFieldName: string; const ARowIndex: Integer; const ANewName: string); [async] procedure HandleAddManagedName(const AFieldName: string; const ARowIndex: Integer; const ANewName: string);
[async] procedure HandleRenameManagedName(const AFieldName, AOldName, ANewName: string); [async] procedure HandleRenameManagedName(const AFieldName, AOldName, ANewName: string);
[async] procedure HandleDeleteManagedName(const AFieldName, AName: string); [async] procedure HandleDeleteManagedName(const AFieldName, AName: string);
function FindOptionIgnoreCase(const AItems: TJSArray; const AName: string): string;
public public
end; end;
...@@ -118,11 +114,11 @@ begin ...@@ -118,11 +114,11 @@ begin
FNameManager := TNameManager.Create( FNameManager := TNameManager.Create(
function(const AFieldName: string): TJSArray function(const AFieldName: string): TJSArray
begin begin
Result := GetOptionsForField(AFieldName); Result := uDropdownHelpers.GetOptionsForField(AFieldName, FApplicationOptions, FReportedByOptions, FAssignedToOptions);
end, end,
procedure(const ATriggerId: string) procedure(const ATriggerId: string)
begin begin
FocusTrigger(ATriggerId); uDropdownHelpers.FocusTrigger(ATriggerId);
end, end,
procedure(const AFieldName: string; const ARowIndex: Integer; const ANewName: string) procedure(const AFieldName: string; const ARowIndex: Integer; const ANewName: string)
begin begin
...@@ -259,7 +255,7 @@ begin ...@@ -259,7 +255,7 @@ begin
xdwdsTasks.Post; xdwdsTasks.Post;
el.setAttribute('data-unsaved-data', '1'); el.setAttribute('data-unsaved-data', '1');
SaveRow(idx); SaveField(idx, fieldName);
end; end;
...@@ -630,13 +626,11 @@ var ...@@ -630,13 +626,11 @@ var
Result := '<td class="align-top wrap-cell">' + s + '</td>'; Result := '<td class="align-top wrap-cell">' + s + '</td>';
end; end;
function TextInput(const FieldName, Value: string; const AIdx: Integer; const MinWidth: Integer = 0): string; function TextInput(const FieldName, Value: string; const AIdx: Integer): string;
var var
w: string; w: string;
begin begin
w := ''; w := '';
if MinWidth > 0 then
w := ' style="min-width: ' + IntToStr(MinWidth) + 'px;"';
Result := Result :=
'<input class="form-control form-control-sm cell-input task-editor w-100" ' + '<input class="form-control form-control-sm cell-input task-editor w-100" ' +
...@@ -662,18 +656,19 @@ var ...@@ -662,18 +656,19 @@ var
'value="' + IntToStr(Value) + '">'; 'value="' + IntToStr(Value) + '">';
end; end;
function DateInput(const FieldName, Value: string; const AIdx: Integer; const MinWidth: Integer = 0): string; function DateInput(const FieldName, Value: string; const AIdx: Integer): string;
var var
w: string; w: string;
dateValue: string;
begin begin
w := ''; w := '';
if MinWidth > 0 then
w := ' style="min-width: ' + IntToStr(MinWidth) + 'px;"'; dateValue := Utils.NormalizeDateValue(Value);
Result := Result :=
'<input type="date" class="form-control form-control-sm cell-input task-editor w-100" ' + '<input type="date" class="form-control form-control-sm cell-input task-editor w-100" ' +
'data-idx="' + IntToStr(AIdx) + '" data-field="' + FieldName + '" ' + 'data-idx="' + IntToStr(AIdx) + '" data-field="' + FieldName + '" ' +
'value="' + HtmlEncode(Value) + '"' + w + '>'; 'value="' + HtmlEncode(dateValue) + '"' + w + '>';
end; end;
function SelectList(const FieldName, Current: string; const AIdx: Integer; const Items: TJSArray; const AllowEdit: Boolean): string; function SelectList(const FieldName, Current: string; const AIdx: Integer; const Items: TJSArray; const AllowEdit: Boolean): string;
...@@ -681,13 +676,21 @@ var ...@@ -681,13 +676,21 @@ var
i: Integer; i: Integer;
itemText: string; itemText: string;
triggerId: string; triggerId: string;
buttonClass: string;
begin begin
triggerId := 'task_dd_' + FieldName + '_' + IntToStr(AIdx); triggerId := 'task_dd_' + FieldName + '_' + IntToStr(AIdx);
buttonClass := 'btn btn-sm border w-100 d-flex justify-content-between align-items-center text-start task-dd-toggle ';
if SameText(FieldName, 'status') then
buttonClass := buttonClass + GetTaskStatusClass(Current)
else
buttonClass := buttonClass + 'btn-light';
Result := Result :=
'<div class="dropdown w-100">' + '<div class="dropdown w-100">' +
'<button id="' + triggerId + '" class="btn btn-sm btn-light border w-100 d-flex justify-content-between align-items-center text-start task-dd-toggle" ' + '<button id="' + triggerId + '" class="' + buttonClass + '" ' +
'type="button" data-bs-toggle="dropdown" aria-expanded="false">' + 'type="button" data-bs-toggle="dropdown" aria-expanded="false" ' +
'data-idx="' + IntToStr(AIdx) + '" data-field="' + FieldName + '">' +
'<span class="task-dd-label text-truncate">' + HtmlEncode(Current) + '</span>' + '<span class="task-dd-label text-truncate">' + HtmlEncode(Current) + '</span>' +
'<span class="dropdown-toggle dropdown-toggle-split border-0 ms-2"></span>' + '<span class="dropdown-toggle dropdown-toggle-split border-0 ms-2"></span>' +
'</button>' + '</button>' +
...@@ -728,31 +731,6 @@ var ...@@ -728,31 +731,6 @@ var
'</div>'; '</div>';
end; end;
function StatusSelect(const Current: string; const AIdx: Integer): string;
var
i: Integer;
statusText: string;
sel: string;
begin
Result :=
'<select class="form-select form-select-sm task-select" data-idx="' + IntToStr(AIdx) + '" data-field="status">';
Result := Result + '<option value=""></option>'; //Note: This adds the blank option on top
if Assigned(FStatusOptions) then
for i := 0 to FStatusOptions.length - 1 do
begin
statusText := string(FStatusOptions[i]);
sel := '';
if SameText(Current, statusText) then
sel := ' selected';
Result := Result + '<option value="' + HtmlEncode(statusText) + '"' + sel + '>' + HtmlEncode(statusText) + '</option>';
end;
Result := Result + '</select>';
end;
begin begin
host := TJSHTMLElement(document.getElementById('tasks_table_host')); host := TJSHTMLElement(document.getElementById('tasks_table_host'));
if not Assigned(host) then if not Assigned(host) then
...@@ -761,12 +739,12 @@ begin ...@@ -761,12 +739,12 @@ 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" style="min-width: 1700px;">' + '<table class="table table-sm table-bordered align-middle mb-0 tasks-table" style="min-width: 1700px; table-layout: fixed;">' +
'<colgroup>' + '<colgroup>' +
'<col style="width:40px">' + // Item Num '<col style="width:40px">' + // Item Num
'<col style="width:200px">' + // App '<col style="width:200px">' + // App
'<col style="width:90px">' + // Version '<col style="width:90px">' + // Version
'<col style="width:120px">' + // Date '<col style="width:140px">' + // Date
'<col style="width:120px">' + // Reported '<col style="width:120px">' + // Reported
'<col style="width:120px">' + // Assigned '<col style="width:120px">' + // Assigned
'<col style="width:195px">' + // Status '<col style="width:195px">' + // Status
...@@ -797,13 +775,13 @@ begin ...@@ -797,13 +775,13 @@ begin
'<tr class="task-row-selectable" data-task-item-id="' + IntToStr(xdwdsTaskstaskItemId.AsInteger) + '" data-task-id="' + xdwdsTaskstaskId.AsString + '" data-item-num="' + IntToStr(xdwdsTasksitemNum.AsInteger) + '">' + '<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(SelectList('application', xdwdsTasksapplication.AsString, rowIdx, FApplicationOptions, True)) + TdNowrap(SelectList('application', xdwdsTasksapplication.AsString, rowIdx, FApplicationOptions, True)) +
TdNowrap(TextInput('version', xdwdsTasksversion.AsString, rowIdx, 80)) + TdNowrap(TextInput('version', xdwdsTasksversion.AsString, rowIdx)) +
TdNowrap(DateInput('taskDate', xdwdsTaskstaskDate.AsString, rowIdx, 110)) + TdNowrap(DateInput('taskDate', xdwdsTaskstaskDate.AsString, rowIdx)) +
TdNowrap(SelectList('reportedBy', xdwdsTasksreportedBy.AsString, rowIdx, FReportedByOptions, False)) + TdNowrap(SelectList('reportedBy', xdwdsTasksreportedBy.AsString, rowIdx, FReportedByOptions, True)) +
TdNowrap(SelectList('assignedTo', xdwdsTasksassignedTo.AsString, rowIdx, FAssignedToOptions, True)) + TdNowrap(SelectList('assignedTo', xdwdsTasksassignedTo.AsString, rowIdx, FAssignedToOptions, True)) +
TdNowrap(StatusSelect(xdwdsTasksstatus.AsString, rowIdx)) + TdNowrap(SelectList('status', xdwdsTasksstatus.AsString, rowIdx, FStatusOptions, False)) +
TdNowrap(DateInput('statusDate', xdwdsTasksstatusDate.AsString, rowIdx, 110)) + TdNowrap(DateInput('statusDate', xdwdsTasksstatusDate.AsString, rowIdx)) +
TdNowrap(TextInput('formSection', xdwdsTasksformSection.AsString, rowIdx, 160)) + TdNowrap(TextInput('formSection', xdwdsTasksformSection.AsString, rowIdx)) +
TdWrap(TextArea('issue', xdwdsTasksissue.AsString, rowIdx)) + TdWrap(TextArea('issue', xdwdsTasksissue.AsString, rowIdx)) +
TdWrap(TextArea('notes', xdwdsTasksnotes.AsString, rowIdx)) + TdWrap(TextArea('notes', xdwdsTasksnotes.AsString, rowIdx)) +
'</tr>'; '</tr>';
...@@ -830,12 +808,16 @@ begin ...@@ -830,12 +808,16 @@ begin
(function(){ (function(){
const host = document.getElementById('tasks_table_host'); const host = document.getElementById('tasks_table_host');
if(!host) return; if(!host) return;
const table = host.querySelector('table'); const table = host.querySelector('table');
if(!table) return; if(!table) return;
const ths = table.querySelectorAll('thead th'); const ths = table.querySelectorAll('thead th');
ths.forEach(th => { const cols = table.querySelectorAll('colgroup col');
ths.forEach((th, index) => {
th.classList.add('th-resize'); th.classList.add('th-resize');
if(th.querySelector('.th-resize-handle')) return; if (th.querySelector('.th-resize-handle')) return;
const handle = document.createElement('div'); const handle = document.createElement('div');
handle.className = 'th-resize-handle'; handle.className = 'th-resize-handle';
...@@ -843,17 +825,27 @@ begin ...@@ -843,17 +825,27 @@ begin
handle.addEventListener('mousedown', function(e){ handle.addEventListener('mousedown', function(e){
e.preventDefault(); e.preventDefault();
e.stopPropagation();
const startX = e.clientX; const startX = e.clientX;
const startW = th.getBoundingClientRect().width; const startW = th.getBoundingClientRect().width;
function onMove(ev){ function onMove(ev){
const w = Math.max(40, startW + (ev.clientX - startX)); const w = Math.max(20, startW + (ev.clientX - startX));
th.style.width = w + 'px'; th.style.width = w + 'px';
th.style.minWidth = '0';
if (cols && cols[index]) {
cols[index].style.width = w + 'px';
}
} }
function onUp(){ function onUp(){
document.removeEventListener('mousemove', onMove); document.removeEventListener('mousemove', onMove);
document.removeEventListener('mouseup', onUp); document.removeEventListener('mouseup', onUp);
} }
document.addEventListener('mousemove', onMove); document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp); document.addEventListener('mouseup', onUp);
}); });
...@@ -896,7 +888,7 @@ begin ...@@ -896,7 +888,7 @@ begin
end; end;
console.log('EditorBlur: SAVE idx=' + IntToStr(idx) + ' field=' + fieldName); console.log('EditorBlur: SAVE idx=' + IntToStr(idx) + ' field=' + fieldName);
SaveRow(idx); SaveField(idx, fieldName);
end; end;
...@@ -941,14 +933,13 @@ begin ...@@ -941,14 +933,13 @@ begin
end; end;
[async] procedure TFTaskItems.SaveRow(AIndex: Integer); [async] procedure TFTaskItems.SaveField(AIndex: Integer; const AFieldName: string);
const const
// Note: Use this to manipulate saving to the server or not for testing
ENABLE_SERVER_SAVE = True; ENABLE_SERVER_SAVE = True;
var var
response: TXDataClientResponse; response: TXDataClientResponse;
payload: TJSObject; payload: TJSObject;
payloadJson: string; fieldValue: string;
begin begin
if not xdwdsTasks.Active then if not xdwdsTasks.Active then
Exit; Exit;
...@@ -957,35 +948,47 @@ begin ...@@ -957,35 +948,47 @@ begin
if xdwdsTasks.Eof then if xdwdsTasks.Eof then
Exit; Exit;
payload := TJSObject.new; fieldValue := '';
if SameText(AFieldName, 'application') then
fieldValue := xdwdsTasksapplication.AsString
else if SameText(AFieldName, 'version') then
fieldValue := xdwdsTasksversion.AsString
else if SameText(AFieldName, 'taskDate') then
fieldValue := xdwdsTaskstaskDate.AsString
else if SameText(AFieldName, 'reportedBy') then
fieldValue := xdwdsTasksreportedBy.AsString
else if SameText(AFieldName, 'assignedTo') then
fieldValue := xdwdsTasksassignedTo.AsString
else if SameText(AFieldName, 'status') then
fieldValue := xdwdsTasksstatus.AsString
else if SameText(AFieldName, 'statusDate') then
fieldValue := xdwdsTasksstatusDate.AsString
else if SameText(AFieldName, 'formSection') then
fieldValue := xdwdsTasksformSection.AsString
else if SameText(AFieldName, 'issue') then
fieldValue := xdwdsTasksissue.AsString
else if SameText(AFieldName, 'notes') then
fieldValue := xdwdsTasksnotes.AsString
else
Exit;
payload := TJSObject.new;
payload['taskItemId'] := xdwdsTaskstaskItemId.AsInteger; payload['taskItemId'] := xdwdsTaskstaskItemId.AsInteger;
payload['taskId'] := xdwdsTaskstaskId.AsString; payload['fieldName'] := AFieldName;
payload['value'] := fieldValue;
payload['application'] := xdwdsTasksapplication.AsString;
payload['version'] := xdwdsTasksversion.AsString; console.log('SaveField: idx=' + IntToStr(AIndex) + ' field=' + AFieldName + ' value=' + fieldValue);
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 if not ENABLE_SERVER_SAVE then
Exit; Exit;
try try
response := await(xdwcTasks.RawInvokeAsync('IApiService.SaveTaskRow', [payload])); response := await(xdwcTasks.RawInvokeAsync('IApiService.SaveTaskItemField', [payload]));
console.log('SaveRow: response=' + string(TJSJSON.stringify(response.Result))); console.log('SaveField: response=' + string(TJSJSON.stringify(response.Result)));
except except
on E: EXDataClientRequestException do on E: EXDataClientRequestException do
begin begin
console.log('SaveRow ERROR: ' + E.ErrorResult.ErrorMessage); console.log('SaveField ERROR: ' + E.ErrorResult.ErrorMessage);
Utils.ShowErrorModal(E.ErrorResult.ErrorMessage); Utils.ShowErrorModal(E.ErrorResult.ErrorMessage);
end; end;
end; end;
...@@ -1017,75 +1020,14 @@ begin ...@@ -1017,75 +1020,14 @@ begin
end; end;
end; end;
function TFTaskItems.ExtractOptionNames(const SourceArray: TJSArray): TJSArray;
var
i: Integer;
optionObj: TJSObject;
begin
Result := TJSArray.new;
if not Assigned(SourceArray) then
Exit;
for i := 0 to SourceArray.length - 1 do
begin
optionObj := TJSObject(SourceArray[i]);
if Assigned(optionObj) then
Result.push(string(optionObj['name']));
end;
end;
function TFTaskItems.ExtractCodeDescs(const SourceArray: TJSArray): TJSArray;
var
i: Integer;
optionObj: TJSObject;
begin
Result := TJSArray.new;
if not Assigned(SourceArray) then
Exit;
for i := 0 to SourceArray.length - 1 do
begin
optionObj := TJSObject(SourceArray[i]);
if Assigned(optionObj) then
Result.push(string(optionObj['codeDesc']));
end;
end;
function TFTaskItems.GetOptionsForField(const AFieldName: string): TJSArray;
begin
if SameText(AFieldName, 'application') then
Result := FApplicationOptions
else if SameText(AFieldName, 'reportedBy') then
Result := FReportedByOptions
else if SameText(AFieldName, 'assignedTo') then
Result := FAssignedToOptions
else
Result := nil;
end;
procedure TFTaskItems.FocusTrigger(const ATriggerId: string);
var
el: TJSHTMLElement;
begin
if ATriggerId = '' then
Exit;
el := TJSHTMLElement(document.getElementById(ATriggerId));
if Assigned(el) then
el.focus;
end;
procedure TFTaskItems.DropdownItemClick(Event: TJSEvent); procedure TFTaskItems.DropdownItemClick(Event: TJSEvent);
var var
el: TJSHTMLElement; el: TJSHTMLElement;
idx: Integer; idx: Integer;
idxStr, fieldName, newVal, triggerId: string; idxStr, fieldName, newVal, triggerId: string;
btn: TJSHTMLElement;
labelEl: TJSHTMLElement;
begin begin
if not xdwdsTasks.Active then if not xdwdsTasks.Active then
Exit; Exit;
...@@ -1113,18 +1055,19 @@ begin ...@@ -1113,18 +1055,19 @@ begin
if triggerId <> '' then if triggerId <> '' then
begin begin
asm btn := TJSHTMLElement(document.getElementById(triggerId));
var btn = document.getElementById(triggerId); if Assigned(btn) then
if (btn) { begin
var labelEl = btn.querySelector('.task-dd-label'); labelEl := TJSHTMLElement(btn.querySelector('.task-dd-label'));
if (labelEl) { if Assigned(labelEl) then
labelEl.textContent = newVal; labelEl.textContent := newVal;
}
} if SameText(fieldName, 'status') then
ApplyTaskStatusClass(btn, newVal);
end; end;
end; end;
SaveRow(idx); SaveField(idx, fieldName);
end; end;
...@@ -1147,7 +1090,7 @@ begin ...@@ -1147,7 +1090,7 @@ begin
if (idx < 0) or (fieldName = '') then if (idx < 0) or (fieldName = '') then
Exit; Exit;
if not (SameText(fieldName, 'assignedTo') or SameText(fieldName, 'application')) then if not (SameText(fieldName, 'assignedTo') or SameText(fieldName, 'application') or SameText(fieldName, 'reportedBy')) then
Exit; Exit;
FNameManager.OpenManager(fieldName, idx, triggerId); FNameManager.OpenManager(fieldName, idx, triggerId);
...@@ -1209,10 +1152,13 @@ begin ...@@ -1209,10 +1152,13 @@ begin
FSelectedTaskId := StrToIntDef(taskIdStr, 0); FSelectedTaskId := StrToIntDef(taskIdStr, 0);
btnDeleteRow.Enabled := FSelectedTaskItemId > 0; btnDeleteRow.Enabled := FSelectedTaskItemId > 0;
ApplySelectedRowState; ApplySelectedRowState;
asm
rowEl.scrollIntoView({ behavior: 'auto', block: 'nearest', inline: 'nearest' });
end;
end; end;
asm asm
el.scrollIntoView({ behavior: 'auto', block: 'nearest', inline: 'nearest' });
el.focus(); el.focus();
end; end;
end; end;
...@@ -1244,40 +1190,6 @@ begin ...@@ -1244,40 +1190,6 @@ begin
end; end;
function TFTaskItems.ExtractApplicationOptionNames(const ResponseResult: TJSObject): TJSArray;
begin
if not Assigned(ResponseResult) then
Exit(TJSArray.new);
Result := ExtractOptionNames(TJSArray(ResponseResult['applicationOptions']));
end;
function TFTaskItems.ExtractAssignedOptionNames(const ResponseResult: TJSObject): TJSArray;
begin
if not Assigned(ResponseResult) then
Exit(TJSArray.new);
Result := ExtractOptionNames(TJSArray(ResponseResult['assignedToOptions']));
end;
function TFTaskItems.FindOptionIgnoreCase(const AItems: TJSArray; const AName: string): string;
var
i: Integer;
itemText: string;
begin
Result := '';
if not Assigned(AItems) then
Exit;
for i := 0 to AItems.length - 1 do
begin
itemText := string(AItems[i]);
if SameText(itemText, Trim(AName)) then
Exit(itemText);
end;
end;
[async] function TFTaskItems.AddAssignedName(const AName: string): TJSArray; [async] function TFTaskItems.AddAssignedName(const AName: string): TJSArray;
var var
response: TXDataClientResponse; response: TXDataClientResponse;
...@@ -1366,6 +1278,8 @@ begin ...@@ -1366,6 +1278,8 @@ begin
newOptions := await(AddApplicationName(ANewName)) newOptions := await(AddApplicationName(ANewName))
else if SameText(AFieldName, 'assignedTo') then else if SameText(AFieldName, 'assignedTo') then
newOptions := await(AddAssignedName(ANewName)) newOptions := await(AddAssignedName(ANewName))
else if SameText(AFieldName, 'reportedBy') then
newOptions := await(AddReportedName(ANewName))
else else
Exit; Exit;
...@@ -1374,6 +1288,8 @@ begin ...@@ -1374,6 +1288,8 @@ begin
if SameText(AFieldName, 'application') then if SameText(AFieldName, 'application') then
FApplicationOptions := newOptions FApplicationOptions := newOptions
else if SameText(AFieldName, 'reportedBy') then
FReportedByOptions := newOptions
else else
FAssignedToOptions := newOptions; FAssignedToOptions := newOptions;
...@@ -1393,7 +1309,7 @@ begin ...@@ -1393,7 +1309,7 @@ begin
xdwdsTasks.Post; xdwdsTasks.Post;
RenderTable; RenderTable;
await(SaveRow(ARowIndex)); await(SaveField(ARowIndex, AFieldName));
end; end;
[async] procedure TFTaskItems.HandleRenameManagedName(const AFieldName, AOldName, ANewName: string); [async] procedure TFTaskItems.HandleRenameManagedName(const AFieldName, AOldName, ANewName: string);
...@@ -1404,6 +1320,8 @@ begin ...@@ -1404,6 +1320,8 @@ begin
newOptions := await(RenameApplicationName(AOldName, ANewName)) newOptions := await(RenameApplicationName(AOldName, ANewName))
else if SameText(AFieldName, 'assignedTo') then else if SameText(AFieldName, 'assignedTo') then
newOptions := await(RenameAssignedName(AOldName, ANewName)) newOptions := await(RenameAssignedName(AOldName, ANewName))
else if SameText(AFieldName, 'reportedBy') then
newOptions := await(RenameReportedName(AOldName, ANewName))
else else
Exit; Exit;
...@@ -1412,6 +1330,8 @@ begin ...@@ -1412,6 +1330,8 @@ begin
if SameText(AFieldName, 'application') then if SameText(AFieldName, 'application') then
FApplicationOptions := newOptions FApplicationOptions := newOptions
else if SameText(AFieldName, 'reportedBy') then
FReportedByOptions := newOptions
else else
FAssignedToOptions := newOptions; FAssignedToOptions := newOptions;
...@@ -1427,6 +1347,8 @@ begin ...@@ -1427,6 +1347,8 @@ begin
newOptions := await(DeleteApplicationName(AName)) newOptions := await(DeleteApplicationName(AName))
else if SameText(AFieldName, 'assignedTo') then else if SameText(AFieldName, 'assignedTo') then
newOptions := await(DeleteAssignedName(AName)) newOptions := await(DeleteAssignedName(AName))
else if SameText(AFieldName, 'reportedBy') then
newOptions := await(DeleteReportedName(AName))
else else
Exit; Exit;
...@@ -1435,6 +1357,8 @@ begin ...@@ -1435,6 +1357,8 @@ begin
if SameText(AFieldName, 'application') then if SameText(AFieldName, 'application') then
FApplicationOptions := newOptions FApplicationOptions := newOptions
else if SameText(AFieldName, 'reportedBy') then
FReportedByOptions := newOptions
else else
FAssignedToOptions := newOptions; FAssignedToOptions := newOptions;
...@@ -1521,6 +1445,85 @@ begin ...@@ -1521,6 +1445,85 @@ begin
end; end;
[async] function TFTaskItems.AddReportedName(const AName: string): TJSArray;
var
response: TXDataClientResponse;
resultObj: TJSObject;
begin
Result := nil;
try
response := await(xdwcTasks.RawInvokeAsync(
'IApiService.AddReportedName',
[FTaskId, Trim(AName)]
));
resultObj := TJSObject(response.Result);
Result := ExtractReportedOptionNames(resultObj);
FReportedByOptions := Result;
except
on E: EXDataClientRequestException do
begin
console.log('AddReportedName ERROR: ' + E.ErrorResult.ErrorMessage);
Utils.ShowErrorModal(E.ErrorResult.ErrorMessage);
Exit;
end;
end;
end;
[async] function TFTaskItems.RenameReportedName(const AOldName, ANewName: string): TJSArray;
var
response: TXDataClientResponse;
resultObj: TJSObject;
begin
Result := nil;
try
response := await(xdwcTasks.RawInvokeAsync(
'IApiService.RenameReportedName',
[FTaskId, Trim(AOldName), Trim(ANewName)]
));
resultObj := TJSObject(response.Result);
Result := ExtractReportedOptionNames(resultObj);
FReportedByOptions := Result;
except
on E: EXDataClientRequestException do
begin
console.log('RenameReportedName ERROR: ' + E.ErrorResult.ErrorMessage);
Utils.ShowErrorModal(E.ErrorResult.ErrorMessage);
Exit;
end;
end;
end;
[async] function TFTaskItems.DeleteReportedName(const AName: string): TJSArray;
var
response: TXDataClientResponse;
resultObj: TJSObject;
begin
Result := nil;
try
response := await(xdwcTasks.RawInvokeAsync(
'IApiService.DeleteReportedName',
[FTaskId, Trim(AName)]
));
resultObj := TJSObject(response.Result);
Result := ExtractReportedOptionNames(resultObj);
FReportedByOptions := Result;
except
on E: EXDataClientRequestException do
begin
console.log('DeleteReportedName ERROR: ' + E.ErrorResult.ErrorMessage);
Utils.ShowErrorModal(E.ErrorResult.ErrorMessage);
Exit;
end;
end;
end;
end. end.
......
...@@ -62,6 +62,9 @@ input[data-field="itemNum"] { ...@@ -62,6 +62,9 @@ input[data-field="itemNum"] {
top: 0; top: 0;
z-index: 2; z-index: 2;
background: var(--bs-body-bg); background: var(--bs-body-bg);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.tasks-vscroll thead th.th-resize { .tasks-vscroll thead th.th-resize {
...@@ -86,7 +89,155 @@ span.card { ...@@ -86,7 +89,155 @@ span.card {
user-select: none; user-select: none;
} }
. test-test {} .tasks-table {
table-layout: fixed;
}
.tasks-table th {
overflow: hidden;
}
.tasks-table td {
overflow: visible;
}
.nowrap-cell,
.wrap-cell {
overflow: visible;
}
.tasks-table .dropdown,
.task-dd-toggle,
.task-dd-label,
.cell-input,
.cell-textarea {
min-width: 0;
}
.dropdown-menu {
z-index: 1055;
}
.task-dd-toggle.status-cannot-duplicate {
--bs-btn-color: #41464b;
--bs-btn-bg: #e2e3e5;
--bs-btn-border-color: #c4c8cb;
--bs-btn-hover-color: #41464b;
--bs-btn-hover-bg: #d3d4d5;
--bs-btn-hover-border-color: #b9bcc0;
--bs-btn-active-color: #41464b;
--bs-btn-active-bg: #c6c7c8;
--bs-btn-active-border-color: #b9bcc0;
}
.task-dd-toggle.status-cannot-test {
--bs-btn-color: #842029;
--bs-btn-bg: #f8d7da;
--bs-btn-border-color: #f1aeb5;
--bs-btn-hover-color: #842029;
--bs-btn-hover-bg: #f1c2c7;
--bs-btn-hover-border-color: #ea9ca6;
--bs-btn-active-color: #842029;
--bs-btn-active-bg: #eaadb5;
--bs-btn-active-border-color: #e68592;
}
.task-dd-toggle.status-future-enhancement {
--bs-btn-color: #055160;
--bs-btn-bg: #cff4fc;
--bs-btn-border-color: #9eeaf9;
--bs-btn-hover-color: #055160;
--bs-btn-hover-bg: #b6effb;
--bs-btn-hover-border-color: #86e5f8;
--bs-btn-active-color: #055160;
--bs-btn-active-bg: #9eeaf9;
--bs-btn-active-border-color: #74dff6;
}
.task-dd-toggle.status-fixed-verified {
--bs-btn-color: #0f5132;
--bs-btn-bg: #d1e7dd;
--bs-btn-border-color: #a3cfbb;
--bs-btn-hover-color: #0f5132;
--bs-btn-hover-bg: #badbcc;
--bs-btn-hover-border-color: #8fc5a9;
--bs-btn-active-color: #0f5132;
--bs-btn-active-bg: #a3cfbb;
--bs-btn-active-border-color: #7db89d;
}
.task-dd-toggle.status-fixed {
--bs-btn-color: #146c43;
--bs-btn-bg: #d1e7dd;
--bs-btn-border-color: #a3cfbb;
--bs-btn-hover-color: #146c43;
--bs-btn-hover-bg: #badbcc;
--bs-btn-hover-border-color: #8fc5a9;
--bs-btn-active-color: #146c43;
--bs-btn-active-bg: #a3cfbb;
--bs-btn-active-border-color: #7db89d;
}
.task-dd-toggle.status-investigating {
--bs-btn-color: #664d03;
--bs-btn-bg: #fff3cd;
--bs-btn-border-color: #ffe69c;
--bs-btn-hover-color: #664d03;
--bs-btn-hover-bg: #ffecb5;
--bs-btn-hover-border-color: #ffdf7e;
--bs-btn-active-color: #664d03;
--bs-btn-active-bg: #ffe69c;
--bs-btn-active-border-color: #ffd966;
}
.task-dd-toggle.status-not-fixed {
--bs-btn-color: #842029;
--bs-btn-bg: #f8d7da;
--bs-btn-border-color: #f1aeb5;
--bs-btn-hover-color: #842029;
--bs-btn-hover-bg: #f1c2c7;
--bs-btn-hover-border-color: #ea9ca6;
--bs-btn-active-color: #842029;
--bs-btn-active-bg: #eaadb5;
--bs-btn-active-border-color: #e68592;
}
.task-dd-toggle.status-non-issue {
--bs-btn-color: #432874;
--bs-btn-bg: #e2d9f3;
--bs-btn-border-color: #cbbbe8;
--bs-btn-hover-color: #432874;
--bs-btn-hover-bg: #d6caee;
--bs-btn-hover-border-color: #bea9e2;
--bs-btn-active-color: #432874;
--bs-btn-active-bg: #cbbbe8;
--bs-btn-active-border-color: #b89ddd;
}
.task-dd-toggle.status-possibly-a-problem {
--bs-btn-color: #7a3e00;
--bs-btn-bg: #ffe5d0;
--bs-btn-border-color: #f7c79d;
--bs-btn-hover-color: #7a3e00;
--bs-btn-hover-bg: #ffd7b8;
--bs-btn-hover-border-color: #f2ba88;
--bs-btn-active-color: #7a3e00;
--bs-btn-active-bg: #f7c79d;
--bs-btn-active-border-color: #eeaf69;
}
.task-dd-toggle.status-default {
--bs-btn-color: #212529;
--bs-btn-bg: #f8f9fa;
--bs-btn-border-color: #dee2e6;
--bs-btn-hover-color: #212529;
--bs-btn-hover-bg: #e9ecef;
--bs-btn-hover-border-color: #ced4da;
--bs-btn-active-color: #212529;
--bs-btn-active-bg: #dee2e6;
--bs-btn-active-border-color: #ced4da;
}
...@@ -12,7 +12,8 @@ uses ...@@ -12,7 +12,8 @@ uses
View.Main in 'View.Main.pas' {FViewMain: TWebForm} {*.html}, View.Main in 'View.Main.pas' {FViewMain: TWebForm} {*.html},
Utils in 'Utils.pas', Utils in 'Utils.pas',
View.TaskItems in 'View.TaskItems.pas' {FTaskItems: TWebForm} {*.html}, View.TaskItems in 'View.TaskItems.pas' {FTaskItems: TWebForm} {*.html},
uNameManager in 'uNameManager.pas'; uNameOffCanvas in 'uNameOffCanvas.pas',
uDropdownHelpers in 'uDropdownHelpers.pas';
{$R *.res} {$R *.res}
......
...@@ -94,12 +94,12 @@ ...@@ -94,12 +94,12 @@
<DCC_RemoteDebug>false</DCC_RemoteDebug> <DCC_RemoteDebug>false</DCC_RemoteDebug>
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo> <VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
<VerInfo_Locale>1033</VerInfo_Locale> <VerInfo_Locale>1033</VerInfo_Locale>
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=0.8.7.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=0.9.8.0;Comments=;LastCompiledTime=2018/08/27 15:18:29</VerInfo_Keys> <VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=0.8.8.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=0.9.8.0;Comments=;LastCompiledTime=2018/08/27 15:18:29</VerInfo_Keys>
<AppDPIAwarenessMode>PerMonitor</AppDPIAwarenessMode> <AppDPIAwarenessMode>PerMonitor</AppDPIAwarenessMode>
<VerInfo_MajorVer>0</VerInfo_MajorVer> <VerInfo_MajorVer>0</VerInfo_MajorVer>
<VerInfo_MinorVer>8</VerInfo_MinorVer> <VerInfo_MinorVer>8</VerInfo_MinorVer>
<VerInfo_Release>7</VerInfo_Release> <VerInfo_Release>8</VerInfo_Release>
<TMSWebBrowser>1</TMSWebBrowser> <TMSWebBrowser>5</TMSWebBrowser>
<TMSUseJSDebugger>2</TMSUseJSDebugger> <TMSUseJSDebugger>2</TMSUseJSDebugger>
<TMSWebSingleInstance>1</TMSWebSingleInstance> <TMSWebSingleInstance>1</TMSWebSingleInstance>
<TMSWebOutputPath>..\emT3XDataServer\bin\static</TMSWebOutputPath> <TMSWebOutputPath>..\emT3XDataServer\bin\static</TMSWebOutputPath>
...@@ -142,7 +142,8 @@ ...@@ -142,7 +142,8 @@
<FormType>dfm</FormType> <FormType>dfm</FormType>
<DesignClass>TWebForm</DesignClass> <DesignClass>TWebForm</DesignClass>
</DCCReference> </DCCReference>
<DCCReference Include="uNameManager.pas"/> <DCCReference Include="uNameOffCanvas.pas"/>
<DCCReference Include="uDropdownHelpers.pas"/>
<None Include="index.html"/> <None Include="index.html"/>
<None Include="css\app.css"/> <None Include="css\app.css"/>
<None Include="config\config.json"/> <None Include="config\config.json"/>
......
unit uDropdownHelpers;
interface
uses
System.SysUtils, System.Classes, JS, Web,
WEBLib.Graphics, WEBLib.Controls, WEBLib.Forms, WEBLib.Dialogs;
function GetTaskStatusClass(const AStatus: string): string;
procedure ClearTaskStatusClasses(const AElement: TJSHTMLElement);
procedure ApplyTaskStatusClass(const AElement: TJSHTMLElement; const AStatus: string);
function ExtractOptionNames(const SourceArray: TJSArray): TJSArray;
function ExtractCodeDescs(const SourceArray: TJSArray): TJSArray;
function ExtractAssignedOptionNames(const ResponseResult: TJSObject): TJSArray;
function ExtractReportedOptionNames(const ResponseResult: TJSObject): TJSArray;
function ExtractApplicationOptionNames(const ResponseResult: TJSObject): TJSArray;
function FindOptionIgnoreCase(const AItems: TJSArray; const AName: string): string;
function GetOptionsForField(const AFieldName: string; const AApplicationOptions, AReportedByOptions, AAssignedToOptions: TJSArray): TJSArray;
procedure FocusTrigger(const ATriggerId: string);
implementation
function GetTaskStatusClass(const AStatus: string): string;
var
statusText: string;
begin
statusText := Trim(AStatus);
if SameText(statusText, 'Cannot Duplicate') then
Result := 'status-cannot-duplicate'
else if SameText(statusText, 'Cannot Test') then
Result := 'status-cannot-test'
else if SameText(statusText, 'Future Enhancement') then
Result := 'status-future-enhancement'
else if SameText(statusText, 'Fixed - Verified') then
Result := 'status-fixed-verified'
else if SameText(statusText, 'Fixed') then
Result := 'status-fixed'
else if SameText(statusText, 'Investigating') then
Result := 'status-investigating'
else if SameText(statusText, 'Not Fixed') then
Result := 'status-not-fixed'
else if SameText(statusText, 'Non-Issue') then
Result := 'status-non-issue'
else if SameText(statusText, 'Possibly a Problem') then
Result := 'status-possibly-a-problem'
else
Result := 'status-default';
end;
procedure ClearTaskStatusClasses(const AElement: TJSHTMLElement);
begin
if not Assigned(AElement) then
Exit;
AElement.classList.remove('status-cannot-duplicate');
AElement.classList.remove('status-cannot-test');
AElement.classList.remove('status-future-enhancement');
AElement.classList.remove('status-fixed-verified');
AElement.classList.remove('status-fixed');
AElement.classList.remove('status-investigating');
AElement.classList.remove('status-not-fixed');
AElement.classList.remove('status-non-issue');
AElement.classList.remove('status-possibly-a-problem');
AElement.classList.remove('status-default');
end;
procedure ApplyTaskStatusClass(const AElement: TJSHTMLElement; const AStatus: string);
begin
if not Assigned(AElement) then
Exit;
ClearTaskStatusClasses(AElement);
AElement.classList.add(GetTaskStatusClass(AStatus));
end;
function ExtractOptionNames(const SourceArray: TJSArray): TJSArray;
var
i: Integer;
optionObj: TJSObject;
begin
Result := TJSArray.new;
if not Assigned(SourceArray) then
Exit;
for i := 0 to SourceArray.length - 1 do
begin
optionObj := TJSObject(SourceArray[i]);
if Assigned(optionObj) then
Result.push(string(optionObj['name']));
end;
end;
function ExtractCodeDescs(const SourceArray: TJSArray): TJSArray;
var
i: Integer;
optionObj: TJSObject;
begin
Result := TJSArray.new;
if not Assigned(SourceArray) then
Exit;
for i := 0 to SourceArray.length - 1 do
begin
optionObj := TJSObject(SourceArray[i]);
if Assigned(optionObj) then
Result.push(string(optionObj['codeDesc']));
end;
end;
function ExtractAssignedOptionNames(const ResponseResult: TJSObject): TJSArray;
begin
if not Assigned(ResponseResult) then
Exit(TJSArray.new);
Result := ExtractOptionNames(TJSArray(ResponseResult['assignedToOptions']));
end;
function ExtractReportedOptionNames(const ResponseResult: TJSObject): TJSArray;
begin
if not Assigned(ResponseResult) then
Exit(TJSArray.new);
Result := ExtractOptionNames(TJSArray(ResponseResult['reportedByOptions']));
end;
function ExtractApplicationOptionNames(const ResponseResult: TJSObject): TJSArray;
begin
if not Assigned(ResponseResult) then
Exit(TJSArray.new);
Result := ExtractOptionNames(TJSArray(ResponseResult['applicationOptions']));
end;
function FindOptionIgnoreCase(const AItems: TJSArray; const AName: string): string;
var
i: Integer;
itemText: string;
begin
Result := '';
if not Assigned(AItems) then
Exit;
for i := 0 to AItems.length - 1 do
begin
itemText := string(AItems[i]);
if SameText(itemText, Trim(AName)) then
Exit(itemText);
end;
end;
function GetOptionsForField(const AFieldName: string; const AApplicationOptions, AReportedByOptions, AAssignedToOptions: TJSArray): TJSArray;
begin
if SameText(AFieldName, 'application') then
Result := AApplicationOptions
else if SameText(AFieldName, 'reportedBy') then
Result := AReportedByOptions
else if SameText(AFieldName, 'assignedTo') then
Result := AAssignedToOptions
else
Result := nil;
end;
procedure FocusTrigger(const ATriggerId: string);
var
el: TJSHTMLElement;
begin
if ATriggerId = '' then
Exit;
el := TJSHTMLElement(document.getElementById(ATriggerId));
if Assigned(el) then
el.focus;
end;
end.
unit uNameManager; unit uNameOffCanvas;
interface interface
...@@ -257,7 +257,7 @@ begin ...@@ -257,7 +257,7 @@ begin
Exit; Exit;
end; end;
if not (SameText(FCurrentField, 'assignedTo') or SameText(FCurrentField, 'application')) then if not (SameText(FCurrentField, 'assignedTo') or SameText(FCurrentField, 'application') or SameText(FCurrentField, 'reportedBy')) then
Exit; Exit;
if FCurrentEditName <> '' then if FCurrentEditName <> '' then
...@@ -356,7 +356,7 @@ begin ...@@ -356,7 +356,7 @@ begin
Event.preventDefault; Event.preventDefault;
Event.stopPropagation; Event.stopPropagation;
if not (SameText(FCurrentField, 'assignedTo') or SameText(FCurrentField, 'application')) then if not (SameText(FCurrentField, 'assignedTo') or SameText(FCurrentField, 'application') or SameText(FCurrentField, 'reportedBy')) then
Exit; Exit;
el := TJSHTMLElement(Event.currentTarget); el := TJSHTMLElement(Event.currentTarget);
...@@ -383,7 +383,7 @@ begin ...@@ -383,7 +383,7 @@ begin
Event.preventDefault; Event.preventDefault;
Event.stopPropagation; Event.stopPropagation;
if not (SameText(FCurrentField, 'assignedTo') or SameText(FCurrentField, 'application')) then if not (SameText(FCurrentField, 'assignedTo') or SameText(FCurrentField, 'application') or SameText(FCurrentField, 'reportedBy')) then
Exit; Exit;
el := TJSHTMLElement(Event.currentTarget); el := TJSHTMLElement(Event.currentTarget);
...@@ -401,7 +401,7 @@ procedure TNameManager.OpenManager(const AFieldName: string; const ARowIndex: In ...@@ -401,7 +401,7 @@ procedure TNameManager.OpenManager(const AFieldName: string; const ARowIndex: In
var var
titleEl: TJSHTMLElement; titleEl: TJSHTMLElement;
begin begin
if not (SameText(AFieldName, 'assignedTo') or SameText(AFieldName, 'application')) then if not (SameText(AFieldName, 'assignedTo') or SameText(AFieldName, 'application') or SameText(AFieldName, 'reportedBy')) then
Exit; Exit;
FCurrentField := AFieldName; FCurrentField := AFieldName;
...@@ -414,6 +414,8 @@ begin ...@@ -414,6 +414,8 @@ begin
begin begin
if SameText(AFieldName, 'application') then if SameText(AFieldName, 'application') then
titleEl.innerHTML := 'Manage Application' titleEl.innerHTML := 'Manage Application'
else if SameText(AFieldName, 'reportedBy') then
titleEl.innerHTML := 'Manage Reported By'
else else
titleEl.innerHTML := 'Manage Assigned To'; titleEl.innerHTML := 'Manage Assigned To';
end; end;
......
object ApiDatabase: TApiDatabase object ApiDatabase: TApiDatabase
OnCreate = DataModuleCreate OnCreate = DataModuleCreate
Height = 475 Height = 475
Width = 803 Width = 996
object ucETaskApi: TUniConnection object ucETaskApi: TUniConnection
AutoCommit = False AutoCommit = False
ProviderName = 'MySQL' ProviderName = 'MySQL'
...@@ -236,17 +236,17 @@ object ApiDatabase: TApiDatabase ...@@ -236,17 +236,17 @@ object ApiDatabase: TApiDatabase
'values (' 'values ('
' :TASK_ID,' ' :TASK_ID,'
' :ITEM_NUM,' ' :ITEM_NUM,'
' '#39#39',' ' null,'
' '#39#39',' ' null,'
' curdate(),' ' null,'
' curdate(),' ' null,'
' '#39#39',' ' null,'
' '#39#39',' ' null,'
' '#39#39',' ' null,'
' '#39#39',' ' null,'
' '#39#39',' ' null,'
' '#39#39',' ' null,'
' '#39#39 ' null'
')') ')')
Left = 536 Left = 536
Top = 282 Top = 282
...@@ -580,7 +580,8 @@ object ApiDatabase: TApiDatabase ...@@ -580,7 +580,8 @@ object ApiDatabase: TApiDatabase
object uqProjectReportedUsers: TUniQuery object uqProjectReportedUsers: TUniQuery
Connection = ucETaskApi Connection = ucETaskApi
SQL.Strings = ( SQL.Strings = (
'select distinct' 'select'
' min(tiu.TASK_ITEM_USER_ID) as TASK_ITEM_USER_ID,'
' tiu.NAME' ' tiu.NAME'
'from task_item_user tiu' 'from task_item_user tiu'
'join tasks project_tasks' 'join tasks project_tasks'
...@@ -589,6 +590,7 @@ object ApiDatabase: TApiDatabase ...@@ -589,6 +590,7 @@ object ApiDatabase: TApiDatabase
' on target_task.PROJECT_ID = project_tasks.PROJECT_ID' ' on target_task.PROJECT_ID = project_tasks.PROJECT_ID'
'where target_task.TASK_ID = :TASK_ID' 'where target_task.TASK_ID = :TASK_ID'
' and tiu.USER_TYPE = '#39'Reported'#39 ' and tiu.USER_TYPE = '#39'Reported'#39
'group by tiu.NAME'
'order by tiu.NAME') 'order by tiu.NAME')
Left = 58 Left = 58
Top = 256 Top = 256
...@@ -598,6 +600,11 @@ object ApiDatabase: TApiDatabase ...@@ -598,6 +600,11 @@ object ApiDatabase: TApiDatabase
Name = 'TASK_ID' Name = 'TASK_ID'
Value = nil Value = nil
end> end>
object uqProjectReportedUsersTASK_ITEM_USER_ID: TStringField
FieldName = 'TASK_ITEM_USER_ID'
ReadOnly = True
Size = 50
end
object uqProjectReportedUsersNAME: TStringField object uqProjectReportedUsersNAME: TStringField
FieldName = 'NAME' FieldName = 'NAME'
Required = True Required = True
...@@ -758,7 +765,7 @@ object ApiDatabase: TApiDatabase ...@@ -758,7 +765,7 @@ object ApiDatabase: TApiDatabase
Connection = ucETaskApi Connection = ucETaskApi
SQL.Strings = ( SQL.Strings = (
'update task_items' 'update task_items'
'set ASSIGNED_TO = '#39#39 'set ASSIGNED_TO = null'
'where TASK_ID = :TASK_ID' 'where TASK_ID = :TASK_ID'
' and lower(ASSIGNED_TO) = lower(:NAME)') ' and lower(ASSIGNED_TO) = lower(:NAME)')
Left = 388 Left = 388
...@@ -914,7 +921,7 @@ object ApiDatabase: TApiDatabase ...@@ -914,7 +921,7 @@ object ApiDatabase: TApiDatabase
'update task_items ti' 'update task_items ti'
'join tasks t' 'join tasks t'
' on t.TASK_ID = ti.TASK_ID' ' on t.TASK_ID = ti.TASK_ID'
'set ti.APPLICATION = '#39#39 'set ti.APPLICATION = null'
'where t.PROJECT_ID = :PROJECT_ID' 'where t.PROJECT_ID = :PROJECT_ID'
' and lower(ti.APPLICATION) = lower(:NAME)') ' and lower(ti.APPLICATION) = lower(:NAME)')
Left = 696 Left = 696
...@@ -931,4 +938,150 @@ object ApiDatabase: TApiDatabase ...@@ -931,4 +938,150 @@ object ApiDatabase: TApiDatabase
Value = nil Value = nil
end> end>
end end
object uqReportedInsert: TUniQuery
Connection = ucETaskApi
SQL.Strings = (
'insert into task_item_user ('
' TASK_ITEM_USER_ID,'
' TASK_ID,'
' USER_TYPE,'
' NAME'
')'
'values ('
' :TASK_ITEM_USER_ID,'
' :TASK_ID,'
' '#39'Reported'#39','
' :NAME'
')')
Left = 858
Top = 32
ParamData = <
item
DataType = ftUnknown
Name = 'TASK_ITEM_USER_ID'
Value = nil
end
item
DataType = ftUnknown
Name = 'TASK_ID'
Value = nil
end
item
DataType = ftUnknown
Name = 'NAME'
Value = nil
end>
end
object uqReportedRename: TUniQuery
Connection = ucETaskApi
SQL.Strings = (
'update task_item_user tiu'
'join tasks t'
' on t.TASK_ID = tiu.TASK_ID'
'join tasks target_task'
' on target_task.PROJECT_ID = t.PROJECT_ID'
'set tiu.NAME = :NEW_NAME'
'where target_task.TASK_ID = :TASK_ID'
' and tiu.USER_TYPE = '#39'Reported'#39
' and tiu.TASK_ITEM_USER_ID = :TASK_ITEM_USER_ID')
Left = 858
Top = 88
ParamData = <
item
DataType = ftUnknown
Name = 'NEW_NAME'
Value = nil
end
item
DataType = ftUnknown
Name = 'TASK_ID'
Value = nil
end
item
DataType = ftUnknown
Name = 'TASK_ITEM_USER_ID'
Value = nil
end>
end
object uqReportedDelete: TUniQuery
Connection = ucETaskApi
SQL.Strings = (
'delete tiu'
'from task_item_user tiu'
'join tasks t'
' on t.TASK_ID = tiu.TASK_ID'
'join tasks target_task'
' on target_task.PROJECT_ID = t.PROJECT_ID'
'where target_task.TASK_ID = :TASK_ID'
' and tiu.USER_TYPE = '#39'Reported'#39
' and tiu.TASK_ITEM_USER_ID = :TASK_ITEM_USER_ID')
Left = 858
Top = 144
ParamData = <
item
DataType = ftUnknown
Name = 'TASK_ID'
Value = nil
end
item
DataType = ftUnknown
Name = 'TASK_ITEM_USER_ID'
Value = nil
end>
end
object uqBlankTaskItemReportedBy: TUniQuery
Connection = ucETaskApi
SQL.Strings = (
'update task_items ti'
'join tasks t'
' on t.TASK_ID = ti.TASK_ID'
'join tasks target_task'
' on target_task.PROJECT_ID = t.PROJECT_ID'
'set ti.REPORTED_BY = null'
'where target_task.TASK_ID = :TASK_ID'
' and lower(ti.REPORTED_BY) = lower(:NAME)')
Left = 860
Top = 264
ParamData = <
item
DataType = ftUnknown
Name = 'TASK_ID'
Value = nil
end
item
DataType = ftUnknown
Name = 'NAME'
Value = nil
end>
end
object uqRenameTaskItemReportedBy: TUniQuery
Connection = ucETaskApi
SQL.Strings = (
'update task_items ti'
'join tasks t'
' on t.TASK_ID = ti.TASK_ID'
'join tasks target_task'
' on target_task.PROJECT_ID = t.PROJECT_ID'
'set ti.REPORTED_BY = :NEW_NAME'
'where target_task.TASK_ID = :TASK_ID'
' and lower(ti.REPORTED_BY) = lower(:OLD_NAME)')
Left = 858
Top = 202
ParamData = <
item
DataType = ftUnknown
Name = 'NEW_NAME'
Value = nil
end
item
DataType = ftUnknown
Name = 'TASK_ID'
Value = nil
end
item
DataType = ftUnknown
Name = 'OLD_NAME'
Value = nil
end>
end
end end
...@@ -68,7 +68,6 @@ type ...@@ -68,7 +68,6 @@ type
uqAssignedDelete: TUniQuery; uqAssignedDelete: TUniQuery;
uqRenameAssignedTo: TUniQuery; uqRenameAssignedTo: TUniQuery;
uqBlankAssignedTo: TUniQuery; uqBlankAssignedTo: TUniQuery;
uqProjectReportedUsersNAME: TStringField;
uqTaskAssignedUsersTASK_ITEM_USER_ID: TStringField; uqTaskAssignedUsersTASK_ITEM_USER_ID: TStringField;
uqTaskAssignedUsersTASK_ID: TStringField; uqTaskAssignedUsersTASK_ID: TStringField;
uqTaskAssignedUsersUSER_TYPE: TStringField; uqTaskAssignedUsersUSER_TYPE: TStringField;
...@@ -82,6 +81,13 @@ type ...@@ -82,6 +81,13 @@ type
uqProjectApplicationsTASK_ITEM_APPLICATION_ID: TIntegerField; uqProjectApplicationsTASK_ITEM_APPLICATION_ID: TIntegerField;
uqProjectApplicationsPROJECT_ID: TStringField; uqProjectApplicationsPROJECT_ID: TStringField;
uqProjectApplicationsNAME: TStringField; uqProjectApplicationsNAME: TStringField;
uqReportedInsert: TUniQuery;
uqReportedRename: TUniQuery;
uqReportedDelete: TUniQuery;
uqBlankTaskItemReportedBy: TUniQuery;
uqRenameTaskItemReportedBy: TUniQuery;
uqProjectReportedUsersTASK_ITEM_USER_ID: TStringField;
uqProjectReportedUsersNAME: TStringField;
procedure DataModuleCreate(Sender: TObject); procedure DataModuleCreate(Sender: TObject);
procedure uqUsersCalcFields(DataSet: TDataSet); procedure uqUsersCalcFields(DataSet: TDataSet);
private private
......
...@@ -76,6 +76,7 @@ type ...@@ -76,6 +76,7 @@ type
TTaskUserOptionsResponse = class TTaskUserOptionsResponse = class
public public
assignedToOptions: TList<TTaskUserOption>; assignedToOptions: TList<TTaskUserOption>;
reportedByOptions: TList<TTaskUserOption>;
constructor Create; constructor Create;
destructor Destroy; override; destructor Destroy; override;
end; end;
...@@ -105,21 +106,37 @@ type ...@@ -105,21 +106,37 @@ type
notes: string; notes: string;
end; end;
TTaskItemFieldSave = class
public
taskItemId: Integer;
fieldName: string;
value: string;
end;
type type
[ServiceContract, Model(API_MODEL)] [ServiceContract, Model(API_MODEL)]
IApiService = interface(IInvokable) IApiService = interface(IInvokable)
['{0EFB33D7-8C4C-4F3C-9BC3-8B4D444B5F69}'] ['{0EFB33D7-8C4C-4F3C-9BC3-8B4D444B5F69}']
function GetTaskItems(taskId: string): TTaskItemsResponse; function GetTaskItems(taskId: string): TTaskItemsResponse;
[HttpPost] function AddTaskRow(taskId: string; insertAfterItemNum: Integer): Boolean; [HttpPost] function AddTaskRow(taskId: string; insertAfterItemNum: Integer): Boolean;
[HttpPost] function SaveTaskRow(Item: TTaskRowSave): Boolean;
[HttpPost] function AddAssignedName(taskId: string; name: string): TTaskUserOptionsResponse; [HttpPost] function AddAssignedName(taskId: string; name: string): TTaskUserOptionsResponse;
[HttpPost] function RenameAssignedName(taskId: string; oldName: string; newName: string): TTaskUserOptionsResponse; [HttpPost] function RenameAssignedName(taskId: string; oldName: string; newName: string): TTaskUserOptionsResponse;
[HttpPost] function DeleteAssignedName(taskId: string; name: string): TTaskUserOptionsResponse; [HttpPost] function DeleteAssignedName(taskId: string; name: string): TTaskUserOptionsResponse;
[HttpPost] function AddReportedName(taskId: string; name: string): TTaskUserOptionsResponse;
[HttpPost]function RenameReportedName(taskId: string; oldName: string; newName: string): TTaskUserOptionsResponse;
[HttpPost]function DeleteReportedName(taskId: string; name: string): TTaskUserOptionsResponse;
[HttpPost] function AddApplicationName(taskId: string; name: string): TTaskApplicationOptionsResponse; [HttpPost] function AddApplicationName(taskId: string; name: string): TTaskApplicationOptionsResponse;
[HttpPost] function RenameApplicationName(taskId: string; oldName: string; newName: string): TTaskApplicationOptionsResponse; [HttpPost] function RenameApplicationName(taskId: string; oldName: string; newName: string): TTaskApplicationOptionsResponse;
[HttpPost] function DeleteApplicationName(taskId: string; name: string): TTaskApplicationOptionsResponse; [HttpPost] function DeleteApplicationName(taskId: string; name: string): TTaskApplicationOptionsResponse;
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; function DeleteTaskRow(const taskId: Integer; const taskItemId: Integer): Boolean;
function SaveTaskItemField(const Item: TTaskItemFieldSave): Boolean;
end; end;
implementation implementation
...@@ -148,11 +165,13 @@ constructor TTaskUserOptionsResponse.Create; ...@@ -148,11 +165,13 @@ constructor TTaskUserOptionsResponse.Create;
begin begin
inherited; inherited;
assignedToOptions := TList<TTaskUserOption>.Create; assignedToOptions := TList<TTaskUserOption>.Create;
reportedByOptions := TList<TTaskUserOption>.Create;
end; end;
destructor TTaskUserOptionsResponse.Destroy; destructor TTaskUserOptionsResponse.Destroy;
begin begin
assignedToOptions.Free; assignedToOptions := TList<TTaskUserOption>.Create;
reportedByOptions := TList<TTaskUserOption>.Create;
inherited; inherited;
end; end;
......
...@@ -3,14 +3,10 @@ unit Api.ServiceImpl; ...@@ -3,14 +3,10 @@ unit Api.ServiceImpl;
interface interface
uses uses
XData.Server.Module, XData.Server.Module, XData.Service.Common,
XData.Service.Common, System.Variants, System.DateUtils, Uni,
System.Variants, Api.Service, Api.Database, Common.Logging,
System.DateUtils, System.SysUtils, JSON, System.IniFiles, DBAccess;
Api.Service,
Api.Database,
Common.Logging,
System.SysUtils, JSON, System.IniFiles;
type type
[ServiceImplementation] [ServiceImplementation]
...@@ -23,7 +19,6 @@ type ...@@ -23,7 +19,6 @@ type
function FindAssignedOptionId(const taskId, name: string): string; function FindAssignedOptionId(const taskId, name: string): string;
function FindAssignedOptionName(const taskId, name: string): string; function FindAssignedOptionName(const taskId, name: string): string;
function AddTaskRow(taskId: string; insertAfterItemNum: Integer): Boolean; function AddTaskRow(taskId: string; insertAfterItemNum: Integer): Boolean;
function SaveTaskRow(Item: TTaskRowSave): Boolean;
function BuildAssignedOptionsResponse(const taskId: string): TTaskUserOptionsResponse; function BuildAssignedOptionsResponse(const taskId: string): TTaskUserOptionsResponse;
function AddAssignedName(taskId: string; name: string): TTaskUserOptionsResponse; function AddAssignedName(taskId: string; name: string): TTaskUserOptionsResponse;
function RenameAssignedName(taskId: string; oldName: string; newName: string): TTaskUserOptionsResponse; function RenameAssignedName(taskId: string; oldName: string; newName: string): TTaskUserOptionsResponse;
...@@ -37,6 +32,13 @@ type ...@@ -37,6 +32,13 @@ type
function AddApplicationName(taskId: string; name: string): TTaskApplicationOptionsResponse; function AddApplicationName(taskId: string; name: string): TTaskApplicationOptionsResponse;
function RenameApplicationName(taskId: string; oldName: string; newName: string): TTaskApplicationOptionsResponse; function RenameApplicationName(taskId: string; oldName: string; newName: string): TTaskApplicationOptionsResponse;
function DeleteApplicationName(taskId: string; name: string): TTaskApplicationOptionsResponse; function DeleteApplicationName(taskId: string; name: string): TTaskApplicationOptionsResponse;
function SaveTaskItemField(const Item: TTaskItemFieldSave): Boolean;
function FindReportedOptionId(const taskId, name: string): string;
function FindReportedOptionName(const taskId, name: string): string;
function BuildReportedOptionsResponse(const taskId: string): TTaskUserOptionsResponse;
function AddReportedName(taskId: string; name: string): TTaskUserOptionsResponse;
function RenameReportedName(taskId: string; oldName: string; newName: string): TTaskUserOptionsResponse;
function DeleteReportedName(taskId: string; name: string): TTaskUserOptionsResponse;
public public
procedure AfterConstruction; override; procedure AfterConstruction; override;
procedure BeforeDestruction; override; procedure BeforeDestruction; override;
...@@ -366,6 +368,116 @@ begin ...@@ -366,6 +368,116 @@ begin
end; end;
function TApiService.SaveTaskItemField(const Item: TTaskItemFieldSave): Boolean;
var
uqSaveField: TUniQuery;
columnName: string;
d: TDateTime;
function MapFieldNameToColumn(const AFieldName: string): string;
begin
Result := '';
if SameText(AFieldName, 'application') then
Result := 'APPLICATION'
else if SameText(AFieldName, 'version') then
Result := 'APP_VERSION'
else if SameText(AFieldName, 'taskDate') then
Result := 'TASK_DATE'
else if SameText(AFieldName, 'reportedBy') then
Result := 'REPORTED_BY'
else if SameText(AFieldName, 'assignedTo') then
Result := 'ASSIGNED_TO'
else if SameText(AFieldName, 'status') then
Result := 'STATUS'
else if SameText(AFieldName, 'statusDate') then
Result := 'STATUS_DATE'
else if SameText(AFieldName, 'formSection') then
Result := 'FORM_SECTION'
else if SameText(AFieldName, 'issue') then
Result := 'ISSUE'
else if SameText(AFieldName, 'notes') then
Result := 'NOTES'
else if SameText(AFieldName, 'fixedVersion') then
Result := 'FIXED_VERSION';
end;
function IsDateField(const AFieldName: string): Boolean;
begin
Result := SameText(AFieldName, 'taskDate') or SameText(AFieldName, 'statusDate');
end;
function ParseDateOrZero(const S: string; out ADate: TDateTime): Boolean;
var
y, m, dInt: Word;
begin
Result := False;
ADate := 0;
if Length(Trim(S)) <> 10 then
Exit;
y := StrToIntDef(Copy(S, 1, 4), 0);
m := StrToIntDef(Copy(S, 6, 2), 0);
dInt := StrToIntDef(Copy(S, 9, 2), 0);
if (y > 0) and (m > 0) and (dInt > 0) then
begin
try
ADate := EncodeDate(y, m, dInt);
Result := True;
except
Result := False;
end;
end;
end;
begin
Result := False;
if not Assigned(Item) then
raise Exception.Create('SaveTaskItemField: Item is nil.');
if Item.taskItemId <= 0 then
raise Exception.Create('SaveTaskItemField: Invalid taskItemId.');
columnName := MapFieldNameToColumn(Item.fieldName);
if columnName = '' then
raise Exception.Create('SaveTaskItemField: Invalid field name: ' + Item.fieldName);
uqSaveField := TUniQuery.Create(nil);
try
uqSaveField.Connection := apiDB.ucETaskApi;
uqSaveField.SQL.Text :=
'update task_items ' +
'set ' + columnName + ' = :' + columnName + ' ' +
'where TASK_ITEM_ID = :TASK_ITEM_ID';
uqSaveField.ParamByName('TASK_ITEM_ID').AsInteger := Item.taskItemId;
if IsDateField(Item.fieldName) then
begin
if ParseDateOrZero(Item.value, d) then
uqSaveField.ParamByName(columnName).AsDateTime := d
else
uqSaveField.ParamByName(columnName).Clear;
end
else
begin
if Trim(Item.value) = '' then
uqSaveField.ParamByName(columnName).Clear
else
uqSaveField.ParamByName(columnName).AsString := Item.value;
end;
uqSaveField.ExecSQL;
Result := True;
finally
uqSaveField.Free;
end;
end;
function TApiService.BuildTaskNumber: string; function TApiService.BuildTaskNumber: string;
procedure AddPart(const value: string); procedure AddPart(const value: string);
...@@ -409,62 +521,6 @@ begin ...@@ -409,62 +521,6 @@ begin
end; end;
function TApiService.SaveTaskRow(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
Logger.Log(4, Format('ApiService.SaveTaskRow - TASK_ITEM_ID="%d"', [Item.taskItemId]));
try
apiDB.uqSaveTaskRow.Close;
apiDB.uqSaveTaskRow.ParamByName('TASK_ITEM_ID').AsInteger := 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').AsDateTime := Date;
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;
Logger.Log(4, 'ApiService.SaveTaskRow - OK');
except
on E: Exception do
begin
Logger.Log(2, 'ApiService.SaveTaskRow - ERROR: ' + E.Message);
raise;
end;
end;
end;
function TApiService.DeleteTaskRow(const taskId: Integer; const taskItemId: Integer): Boolean; function TApiService.DeleteTaskRow(const taskId: Integer; const taskItemId: Integer): Boolean;
var var
oldItemNum: Integer; oldItemNum: Integer;
...@@ -934,6 +990,203 @@ begin ...@@ -934,6 +990,203 @@ begin
Result := BuildApplicationOptionsResponse(taskId); Result := BuildApplicationOptionsResponse(taskId);
end; end;
function TApiService.FindReportedOptionId(const taskId, name: string): string;
begin
Result := '';
apiDB.uqProjectReportedUsers.Close;
apiDB.uqProjectReportedUsers.ParamByName('TASK_ID').AsString := taskId;
apiDB.uqProjectReportedUsers.Open;
while not apiDB.uqProjectReportedUsers.Eof do
begin
if SameText(Trim(apiDB.uqProjectReportedUsersNAME.AsString), Trim(name)) then
begin
Result := apiDB.uqProjectReportedUsersTASK_ITEM_USER_ID.AsString;
Exit;
end;
apiDB.uqProjectReportedUsers.Next;
end;
end;
function TApiService.FindReportedOptionName(const taskId, name: string): string;
begin
Result := '';
apiDB.uqProjectReportedUsers.Close;
apiDB.uqProjectReportedUsers.ParamByName('TASK_ID').AsString := taskId;
apiDB.uqProjectReportedUsers.Open;
while not apiDB.uqProjectReportedUsers.Eof do
begin
if SameText(Trim(apiDB.uqProjectReportedUsersNAME.AsString), Trim(name)) then
begin
Result := apiDB.uqProjectReportedUsersNAME.AsString;
Exit;
end;
apiDB.uqProjectReportedUsers.Next;
end;
end;
function TApiService.BuildReportedOptionsResponse(const taskId: string): TTaskUserOptionsResponse;
var
taskUserOption: TTaskUserOption;
begin
Result := TTaskUserOptionsResponse.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result);
apiDB.uqProjectReportedUsers.Close;
apiDB.uqProjectReportedUsers.ParamByName('TASK_ID').AsString := taskId;
apiDB.uqProjectReportedUsers.Open;
while not apiDB.uqProjectReportedUsers.Eof do
begin
taskUserOption := TTaskUserOption.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(taskUserOption);
taskUserOption.name := apiDB.uqProjectReportedUsersNAME.AsString;
Result.reportedByOptions.Add(taskUserOption);
apiDB.uqProjectReportedUsers.Next;
end;
end;
function TApiService.AddReportedName(taskId: string; name: string): TTaskUserOptionsResponse;
var
newName: string;
existingName: string;
newGuid: TGuid;
begin
newName := Trim(name);
if newName = '' then
raise Exception.Create('Reported name cannot be blank.');
existingName := FindReportedOptionName(taskId, newName);
if existingName = '' then
begin
CreateGUID(newGuid);
apiDB.uqReportedInsert.Connection.StartTransaction;
try
apiDB.uqReportedInsert.Close;
apiDB.uqReportedInsert.ParamByName('TASK_ITEM_USER_ID').AsString := GuidToString(newGuid);
apiDB.uqReportedInsert.ParamByName('TASK_ID').AsString := taskId;
apiDB.uqReportedInsert.ParamByName('NAME').AsString := newName;
apiDB.uqReportedInsert.ExecSQL;
apiDB.uqReportedInsert.Connection.Commit;
except
on E: Exception do
begin
if apiDB.uqReportedInsert.Connection.InTransaction then
apiDB.uqReportedInsert.Connection.Rollback;
raise;
end;
end;
end;
Result := BuildReportedOptionsResponse(taskId);
end;
function TApiService.RenameReportedName(taskId: string; oldName: string; newName: string): TTaskUserOptionsResponse;
var
oldReportedId: string;
existingReportedId: string;
trimmedOldName: string;
trimmedNewName: string;
begin
trimmedOldName := Trim(oldName);
trimmedNewName := Trim(newName);
if trimmedOldName = '' then
raise Exception.Create('Old reported name cannot be blank.');
if trimmedNewName = '' then
raise Exception.Create('New reported name cannot be blank.');
oldReportedId := FindReportedOptionId(taskId, trimmedOldName);
if oldReportedId = '' then
raise Exception.Create('Reported name not found.');
existingReportedId := FindReportedOptionId(taskId, trimmedNewName);
apiDB.uqReportedRename.Connection.StartTransaction;
try
apiDB.uqRenameTaskItemReportedBy.Close;
apiDB.uqRenameTaskItemReportedBy.ParamByName('TASK_ID').AsString := taskId;
apiDB.uqRenameTaskItemReportedBy.ParamByName('OLD_NAME').AsString := trimmedOldName;
apiDB.uqRenameTaskItemReportedBy.ParamByName('NEW_NAME').AsString := trimmedNewName;
apiDB.uqRenameTaskItemReportedBy.ExecSQL;
if (existingReportedId <> '') and (not SameText(existingReportedId, oldReportedId)) then
begin
apiDB.uqReportedDelete.Close;
apiDB.uqReportedDelete.ParamByName('TASK_ITEM_USER_ID').AsString := oldReportedId;
apiDB.uqReportedDelete.ParamByName('TASK_ID').AsString := taskId;
apiDB.uqReportedDelete.ExecSQL;
end
else
begin
apiDB.uqReportedRename.Close;
apiDB.uqReportedRename.ParamByName('TASK_ITEM_USER_ID').AsString := oldReportedId;
apiDB.uqReportedRename.ParamByName('TASK_ID').AsString := taskId;
apiDB.uqReportedRename.ParamByName('NEW_NAME').AsString := trimmedNewName;
apiDB.uqReportedRename.ExecSQL;
end;
apiDB.uqReportedRename.Connection.Commit;
except
on E: Exception do
begin
if apiDB.uqReportedRename.Connection.InTransaction then
apiDB.uqReportedRename.Connection.Rollback;
raise;
end;
end;
Result := BuildReportedOptionsResponse(taskId);
end;
function TApiService.DeleteReportedName(taskId: string; name: string): TTaskUserOptionsResponse;
var
reportedId: string;
reportedName: string;
begin
reportedId := FindReportedOptionId(taskId, name);
reportedName := FindReportedOptionName(taskId, name);
if reportedId = '' then
raise Exception.Create('Reported name not found.');
apiDB.uqReportedDelete.Connection.StartTransaction;
try
apiDB.uqBlankTaskItemReportedBy.Close;
apiDB.uqBlankTaskItemReportedBy.ParamByName('TASK_ID').AsString := taskId;
apiDB.uqBlankTaskItemReportedBy.ParamByName('NAME').AsString := reportedName;
apiDB.uqBlankTaskItemReportedBy.ExecSQL;
apiDB.uqReportedDelete.Close;
apiDB.uqReportedDelete.ParamByName('TASK_ITEM_USER_ID').AsString := reportedId;
apiDB.uqReportedDelete.ParamByName('TASK_ID').AsString := taskId;
apiDB.uqReportedDelete.ExecSQL;
apiDB.uqReportedDelete.Connection.Commit;
except
on E: Exception do
begin
if apiDB.uqReportedDelete.Connection.InTransaction then
apiDB.uqReportedDelete.Connection.Rollback;
raise;
end;
end;
Result := BuildReportedOptionsResponse(taskId);
end;
initialization initialization
RegisterServiceType(TypeInfo(IApiService)); RegisterServiceType(TypeInfo(IApiService));
RegisterServiceType(TApiService); RegisterServiceType(TApiService);
......
[Settings] [Settings]
MemoLogLevel=4 MemoLogLevel=4
FileLogLevel=4 FileLogLevel=4
webClientVersion=0.8.7 webClientVersion=0.8.8
LogFileNum=155 LogFileNum=160
[Database] [Database]
Server=192.168.102.131 Server=192.168.102.131
......
...@@ -114,10 +114,10 @@ ...@@ -114,10 +114,10 @@
<VerInfo_Locale>1033</VerInfo_Locale> <VerInfo_Locale>1033</VerInfo_Locale>
<DCC_ExeOutput>.\bin</DCC_ExeOutput> <DCC_ExeOutput>.\bin</DCC_ExeOutput>
<DCC_UnitSearchPath>C:\RADTOOLS\FastMM4;$(DCC_UnitSearchPath)</DCC_UnitSearchPath> <DCC_UnitSearchPath>C:\RADTOOLS\FastMM4;$(DCC_UnitSearchPath)</DCC_UnitSearchPath>
<VerInfo_Keys>CompanyName=EM Systems;FileDescription=$(MSBuildProjectName);FileVersion=0.8.7.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=0.9.11;Comments=</VerInfo_Keys> <VerInfo_Keys>CompanyName=EM Systems;FileDescription=$(MSBuildProjectName);FileVersion=0.8.8.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=0.9.11;Comments=</VerInfo_Keys>
<VerInfo_MajorVer>0</VerInfo_MajorVer> <VerInfo_MajorVer>0</VerInfo_MajorVer>
<VerInfo_MinorVer>8</VerInfo_MinorVer> <VerInfo_MinorVer>8</VerInfo_MinorVer>
<VerInfo_Release>7</VerInfo_Release> <VerInfo_Release>8</VerInfo_Release>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1_Win64)'!=''"> <PropertyGroup Condition="'$(Cfg_1_Win64)'!=''">
<AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode> <AppDPIAwarenessMode>PerMonitorV2</AppDPIAwarenessMode>
......
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