diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js index 21f88be23f813b2b92eb559b6f8063f8d83242ef..385b4f931ba93f2dd5a5e4c207023a13d89b3b3a 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js @@ -170,7 +170,7 @@ class ViewSchedulingUnit extends Component { "Linked Blueprint": "filter-input-50", "Linked Draft": "filter-input-50" }], - obsTasks:[], + obsoleteTasks:[], stationGroup: [], dialog: { header: 'Confirm', detail: 'Do you want to create a Scheduling Unit Blueprint?' }, dialogVisible: false, @@ -195,10 +195,10 @@ class ViewSchedulingUnit extends Component { this.statusUpdate = null; this.copyScheduleUnitID = null; this.pageUpdated = true; - this.confirmObsoletelTasks = this.confirmObsoletelTasks.bind(this); - this.obsoleteTasks = this.obsoleteTasks.bind(this); - this.getTaskObsoleteConfirmDialog = this.getTaskObsoleteConfirmDialog.bind(this); - this.getTaskObsoleteStatus = this.getTaskObsoleteStatus.bind(this); + this.confirmObsoleteTasks = this.confirmObsoleteTasks.bind(this); + this.markObsoleteTasks = this.markObsoleteTasks.bind(this); + this.getObsTaskConfirmDlgContent = this.getObsTaskConfirmDlgContent.bind(this); + this.getObsTaskStatusContent = this.getObsTaskStatusContent.bind(this); this.confirmDeleteTasks = this.confirmDeleteTasks.bind(this); this.confirmCancelTasks = this.confirmCancelTasks.bind(this); this.onRowSelection = this.onRowSelection.bind(this); @@ -749,7 +749,7 @@ class ViewSchedulingUnit extends Component { * Callback function to close the dialog prompted. */ closeDialog() { - this.setState({ dialogVisible: false, cancelledTasks: [] , obsTasks: []}); + this.setState({ dialogVisible: false, cancelledTasks: [] , obsoleteTasks: []}); } onRowSelection(selectedRows) { @@ -1089,24 +1089,56 @@ class ViewSchedulingUnit extends Component { </> } - - getTaskObsoleteConfirmDialog() { - let selectedTasks = []; - for (const obj of this.selectedRows) { - selectedTasks.push({ - id: obj.id, suId: this.state.scheduleunit.id, suName: this.state.scheduleunit.name, - taskId: obj.id, controlId: obj.subTaskID, taskName: obj.name, status: obj.status - }); + /** + * Content for Confirmation dialog to mark task as obsolete + */ + getObsTaskConfirmDlgContent() { + const blueprintsWithObsolete = this.state.blueprintsWithObsolete; + const blueprintWithoutObsolete = _.difference(this.selectedRows, blueprintsWithObsolete); + let selectedTasksWithObsolete = []; + let selectedTasksWithoutObsolete = []; + for (const obj of blueprintsWithObsolete) { + selectedTasksWithObsolete.push({ + id: obj.id, suId: this.state.scheduleunit.id, suName: this.state.scheduleunit.name, + taskId: obj.id, controlId: obj.subTaskID, taskName: obj.name, status: obj.status + }); + } + for (const obj of blueprintWithoutObsolete) { + selectedTasksWithoutObsolete.push({ + id: obj.id, suId: this.state.scheduleunit.id, suName: this.state.scheduleunit.name, + taskId: obj.id, controlId: obj.subTaskID, taskName: obj.name, status: obj.status + }); } return <> - <DataTable value={selectedTasks} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}> - <Column field="suId" header="Scheduling Unit Id"></Column> - <Column field="suName" header="Scheduling Unit Name"></Column> - <Column field="taskId" header="Task Id"></Column> - <Column field="controlId" header="Control Id"></Column> - <Column field="taskName" header="Task Name"></Column> - <Column field="status" header="Status"></Column> - </DataTable> + <div style={{marginTop: '1em'}}> + <b>Task(s) that can be marked as obsolete’ if task has null for obsolete_since.</b> + <p>Task(s) that are marked as obsolete will be ignored’. If you want to mark as obsolete for task(s) for which obsolete_since is null, click "Yes"</p> + {selectedTasksWithObsolete.length>0 && + <> + <p>Selected Task (s) with Obsolete are listed below</p> + <DataTable value={selectedTasksWithObsolete} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}> + <Column field="suId" header="Scheduling Unit Id"></Column> + <Column field="suName" header="Scheduling Unit Name"></Column> + <Column field="taskId" header="Task Id"></Column> + <Column field="controlId" header="Control Id"></Column> + <Column field="taskName" header="Task Name"></Column> + <Column field="status" header="Status"></Column> + </DataTable> + </>} + + {selectedTasksWithoutObsolete.length>0 && + <> + <p>Selected Task (s) without Obsolete are listed below</p> + <DataTable value={selectedTasksWithoutObsolete} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}> + <Column field="suId" header="Scheduling Unit Id"></Column> + <Column field="suName" header="Scheduling Unit Name"></Column> + <Column field="taskId" header="Task Id"></Column> + <Column field="controlId" header="Control Id"></Column> + <Column field="taskName" header="Task Name"></Column> + <Column field="status" header="Status"></Column> + </DataTable> + </>} + </div> </> } @@ -1126,20 +1158,33 @@ class ViewSchedulingUnit extends Component { </DataTable> </> } - - getTaskObsoleteStatus() { - let obsTasks = this.state.obsTasks; + /** + * Content to show status of marking task as obsolete + */ + getObsTaskStatusContent() { + let obsoleteTasks = this.state.obsoleteTasks; return <> - <DataTable value={obsTasks} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}> + <DataTable value={obsoleteTasks} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}> <Column field="suId" header="Scheduling Unit Id"></Column> <Column field="suName" header="Scheduling Unit Name"></Column> <Column field="taskId" header="Task Id"></Column> <Column field="controlId" header="Control Id"></Column> <Column field="taskName" header="Task Name"></Column> <Column field="status" header="Status"></Column> + <Column field="actionStatus" header="Action Status"></Column> </DataTable> </> } + /** + * To check is selected tasks are marked as obsolete or not + * @param {Array} selectedBlueprints + * @returns Array - List of tasks which have obsolete_since as null + */ + checkObsolete(selectedBlueprints) { + const blueprintsWithObsolete = selectedBlueprints.filter(task => { + return task.obsolete_since !== null}); + return blueprintsWithObsolete; + } /** * Function to get confirmation before cancelling all selected task blueprints if the task status is @@ -1168,34 +1213,47 @@ class ViewSchedulingUnit extends Component { this.setState({ dialog: dialog, dialogVisible: true }); } } - - confirmObsoletelTasks() { + /** + * Function to get conirmation before marking task(s) as obsolete + */ + confirmObsoleteTasks() { this.pageUpdated = false; let selectedBlueprints = this.selectedRows.filter(task => { - return task.tasktype === 'Blueprint'}); + return task.tasktype === 'Blueprint' + }); + const blueprintsWithObsolete = this.checkObsolete(selectedBlueprints) + const blueprintWithoutObsolete = _.difference(selectedBlueprints, blueprintsWithObsolete); if (selectedBlueprints.length === 0) { appGrowl.show({ severity: 'info', summary: 'Select Row', - detail: 'Select atleast one obsolete Task Blueprint to cancel.' }); + detail: 'Please select minimum one task to mark as obsolete' }); } else { + if (blueprintWithoutObsolete.length === 0) { + appGrowl.show({ severity: 'warn', summary: 'Already marked as obsolete', + detail: 'Selected task(s) are already marked as obsolete' }); + } + else { let dialog = this.state.dialog; dialog.type = "confirmation"; - dialog.header = "Confirm to Obsolete Task(s)"; - dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.obsoleteTasks }, + dialog.header = "Confirm to mark task(s) as obsolete"; + dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.markObsoleteTasks }, { id: 'no', title: 'No', callback: this.closeDialog }]; - dialog.detail = "Obsolete tasks will not be copied and dataproducts will not be used for pipelines and ingest. Are you sure you want to make the task obsolete?"; - dialog.content = this.getTaskObsoleteConfirmDialog; - dialog.submit = this.obsoleteTasks; + dialog.detail = "Warning: Obsolete task(s) will not be copied and dataproducts will not be used for pipelines and ingest. Are you sure you want to make the task(s) obsolete?"; + dialog.content = this.getObsTaskConfirmDlgContent; + dialog.submit = this.markObsoleteTasks; dialog.width = '55vw'; dialog.showIcon = false; - this.setState({ dialog: dialog, dialogVisible: true }); + this.setState({ dialog: dialog, dialogVisible: true, blueprintsWithObsolete: blueprintsWithObsolete}); + } } } - - async obsoleteTasks() { + /** + * Function to mark task(s) as obsolete as show the status after completion + */ + async markObsoleteTasks() { let schedulingUnitTasks = this.state.schedulingUnitTasks; - let selectedBlueprints = this.selectedRows.filter(task => {return task.tasktype === 'Blueprint'}); + const blueprintWithoutObsolete = _.difference(this.selectedRows, this.state.blueprintsWithObsolete); let obsoleteTask = [] - for (const selectedTask of selectedBlueprints) { + for (const selectedTask of blueprintWithoutObsolete) { const obsTask = await TaskService.obsoleteTask(selectedTask.id); let task = _.find(schedulingUnitTasks, {'id': selectedTask.id, tasktype: 'Blueprint'}); if (obsTask) { @@ -1204,7 +1262,8 @@ class ViewSchedulingUnit extends Component { obsoleteTask.push({ id: task.id, suId: this.state.scheduleunit.id, suName: this.state.scheduleunit.name, taskId: task.id, controlId: task.subTaskID, taskName: task.name, - status: task.status.toLowerCase()==='obsolete'?'Obsolete': task.status + status: task.status.toLowerCase()==='obsolete'?'Obsolete': task.status, + actionStatus: obsTask?'Success': 'Failed' }); } @@ -1213,12 +1272,12 @@ class ViewSchedulingUnit extends Component { dialog.header = "Obsolete Task(s) Status"; dialog.actions = [{ id: 'no', title: 'Ok', callback: this.closeDialog }]; dialog.detail = "" - dialog.content = this.getTaskObsoleteStatus; + dialog.content = this.getObsTaskStatusContent; dialog.submit = this.closeDialog; dialog.width = '55vw'; dialog.showIcon = false; this.selectedRows = []; - this.setState({ schedulingUnitTasks: schedulingUnitTasks, obsTasks: obsoleteTask, dialog: dialog, dialogVisible: true }); + this.setState({ schedulingUnitTasks: schedulingUnitTasks, obsoleteTasks: obsoleteTask, dialog: dialog, dialogVisible: true }); } /** @@ -1510,10 +1569,6 @@ class ViewSchedulingUnit extends Component { <span className="p-float-label"> {this.state.schedulingUnitTasks && this.state.schedulingUnitTasks.length > 0 && <> - <button className="p-link" href="#" onClick={this.confirmObsoletelTasks} - title="Obsolete"> - <i class="fa fa-bullseye" aria-hidden="true" ></i> - </button> {this.props.match.params.type === 'draft' && <> <button className="p-link" href="#" onClick={this.confirmCancelTasks} @@ -1528,10 +1583,14 @@ class ViewSchedulingUnit extends Component { } {this.props.match.params.type === 'blueprint' && <> - <a href="#" onClick={this.confirmCancelTasks} + <a href="#" onClick={this.confirmCancelTasks} style={{ marginLeft: '8px'}} title={userPermissions.task_blueprint.canceltask?"Cancel selected Task(s)": `${this.access_denied_message} to cancel Task(s)`}> <i class={userPermissions.task_blueprint.canceltask?"fa fa-ban":"fa fa-ban fa-disabled"} aria-hidden="true" ></i></a> - <a href="#" style={{ pointerEvents: this.props.disabled ? 'none' : 'auto' }}onClick={this.confirmDeleteTasks} + <a className="p-link" style={{ marginLeft: '8px'}} href="#" onClick={this.confirmObsoleteTasks} + title={userPermissions.task.edit?"Mark as Obsolete": `${this.access_denied_message}`}> + <i class={userPermissions.task.edit?"fa fa-bullseye":"fa fa-bullseye fa-disabled"} aria-hidden="true" ></i> + </a> + <a href="#" style={{ pointerEvents: this.props.disabled ? 'none' : 'auto', marginLeft: '8px' }} onClick={this.confirmDeleteTasks} title={userPermissions.task.delete?"Delete selected Task(s)": `${this.access_denied_message} to delete Task(s)`} > <i class={userPermissions.task.delete?"fa fa-trash":"fa fa-trash fa-disabled"} aria-hidden="true" ></i></a> </> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js index 5ae83e21d7ad3cafbe319302397fbee516072250..f203225cd178eb8b626187bbfe3d3844b42a1802 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js @@ -37,7 +37,7 @@ export class TaskList extends Component { taskType: this.taskUIAttr['listType'] || 'Draft', isLoading: true, tasks: [], - obsTasks: [], + obsoleteTasks: [], paths: [{ "View": "/task", }], @@ -223,19 +223,19 @@ export class TaskList extends Component { this.lsKeySortColumn = "Task_"+this.state.taskType+"_SortData"; this.selectedRows = []; this.subtaskTemplates = []; - this.getTaskObsoleteConfirmContent= this.getTaskObsoleteConfirmContent.bind(this); - this.obsoleteTask = this.obsoleteTask.bind(this); + this.getObsTaskConfirmDlgContent= this.getObsTaskConfirmDlgContent.bind(this); + this.markObsoleteTasks = this.markObsoleteTasks.bind(this); this.getTaskobsStatusContent =this.getTaskobsStatusContent.bind(this); this.confirmDeleteTasks = this.confirmDeleteTasks.bind(this); this.confirmCancelTasks = this.confirmCancelTasks.bind(this); - this.confirmObsolete = this.confirmObsolete.bind(this); + this.confirmObsoleteTasks = this.confirmObsoleteTasks.bind(this); this.onRowSelection = this.onRowSelection.bind(this); this.deleteTasks = this.deleteTasks.bind(this); this.cancelTasks = this.cancelTasks.bind(this); this.closeDialog = this.closeDialog.bind(this); this.getTaskDeleteDialogContent = this.getTaskDeleteDialogContent.bind(this); this.getTaskCancelConfirmContent = this.getTaskCancelConfirmContent.bind(this); - this.getTaskCancelStatusContent = this.getTaskCancelStatusContent.bind(this); + this.getObsTaskStatusContent = this.getObsTaskStatusContent.bind(this); this.changeTaskType = this.changeTaskType.bind(this); this.fetchTableData = this.fetchTableData.bind(this); this.getFilterOptions = this.getFilterOptions.bind(this); @@ -348,9 +348,7 @@ export class TaskList extends Component { this.subtaskTemplates = await TaskService.getSubtaskTemplates(); let actions = []; if(this.state.taskType === 'Draft'){ - actions = [{icon: 'fa fa-bullseye', - title:'Obsolete Tasks', - type: 'button', actOn: 'click', props: { callback: this.confirmObsolete }},{icon: 'fa fa-ban', + actions = [{icon: 'fa fa-ban', title: task_draft.canceltask?'Cancel Task(s)': `${this.access_denied_message} to cancel Task(s)`, disabled: task_draft.canceltask? !task_draft.canceltask: true, type: 'button', actOn: 'click', props: { callback: this.confirmCancelTasks }}, @@ -361,13 +359,15 @@ export class TaskList extends Component { ]; } else { - actions = [{icon: 'fa fa-bullseye', - title:'Obsolete Tasks', - type: 'button', actOn: 'click', props: { callback: this.confirmObsolete }},{icon: 'fa fa-ban', - title: task_blueprint.canceltask?'Cancel Task(s)': `${this.access_denied_message} to cancel Task(s)`, - disabled: task_blueprint.canceltask? !task_blueprint.canceltask: true, - type: 'button', actOn: 'click', props: { callback: this.confirmCancelTasks }}, - {icon: 'fa fa-trash', + actions = [ {icon: 'fa fa-bullseye', + title: task.edit?'Mark as Obsolete': `${this.access_denied_message}`, + disabled: task.edit? !task.edit: true, + type: 'button', actOn: 'click', props: { callback: this.confirmObsoleteTasks }}, + {icon: 'fa fa-ban', + title: task_blueprint.canceltask?'Cancel Task(s)': `${this.access_denied_message} to cancel Task(s)`, + disabled: task_blueprint.canceltask? !task_blueprint.canceltask: true, + type: 'button', actOn: 'click', props: { callback: this.confirmCancelTasks }}, + {icon: 'fa fa-trash', title: task.delete?'Delete Task(s)':`${this.access_denied_message} to delete Task(s)`, disabled: task.delete? !task.delete: true, type: 'button', actOn: 'click', props: { callback: this.confirmDeleteTasks }} @@ -471,27 +471,57 @@ export class TaskList extends Component { } } - - getTaskObsoleteConfirmContent(){ - let selectedTasks = []; - for (const obj of this.selectedRows) { - selectedTasks.push({ + /** + * Content for confirmation dialog before marking task as obsolete + */ + getObsTaskConfirmDlgContent(){ + const blueprintsWithObsolete = this.state.blueprintsWithObsolete; + const blueprintWithoutObsolete = _.difference(this.selectedRows, blueprintsWithObsolete); + let selectedTasksWithObsolete = []; + let selectedTasksWithoutObsolete = []; + for (const obj of blueprintsWithObsolete) { + selectedTasksWithObsolete.push({ id: obj.id, suId: obj.schedulingUnitId, suName: obj.schedulingUnitName, taskId: obj.id, controlId: obj.subTaskID, taskName: obj.name, status: obj.status }); - } + } + + for (const obj of blueprintWithoutObsolete) { + selectedTasksWithoutObsolete.push({ + id: obj.id, suId: obj.schedulingUnitId, suName: obj.schedulingUnitName, + taskId: obj.id, controlId: obj.subTaskID, taskName: obj.name, status: obj.status + }); + } return <> <div style={{marginTop: '1em'}}> - <b>Task(s) that can be obsolete</b> - <DataTable value={selectedTasks} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}> - <Column field="suId" header="Scheduling Unit Id"></Column> - <Column field="suName" header="Scheduling Unit Name"></Column> - <Column field="taskId" header="Task Id"></Column> - <Column field="controlId" header="Control Id"></Column> - <Column field="taskName" header="Task Name"></Column> - <Column field="status" header="Status"></Column> - </DataTable> + <b>Task(s) that can be marked as obsolete’ if task has null for obsolete_since.</b> + <p>Task(s) that are marked as obsolete will be ignored’. If you want to mark as obsolete for task(s) for which obsolete_since is null, click "Yes"</p> + {selectedTasksWithObsolete.length>0 && + <> + <p>Selected Task (s) with Obsolete are listed below</p> + <DataTable value={selectedTasksWithObsolete} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}> + <Column field="suId" header="Scheduling Unit Id"></Column> + <Column field="suName" header="Scheduling Unit Name"></Column> + <Column field="taskId" header="Task Id"></Column> + <Column field="controlId" header="Control Id"></Column> + <Column field="taskName" header="Task Name"></Column> + <Column field="status" header="Status"></Column> + </DataTable> + </>} + + {selectedTasksWithoutObsolete.length>0 && + <> + <p>Selected Task (s) without Obsolete are listed below</p> + <DataTable value={selectedTasksWithoutObsolete} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}> + <Column field="suId" header="Scheduling Unit Id"></Column> + <Column field="suName" header="Scheduling Unit Name"></Column> + <Column field="taskId" header="Task Id"></Column> + <Column field="controlId" header="Control Id"></Column> + <Column field="taskName" header="Task Name"></Column> + <Column field="status" header="Status"></Column> + </DataTable> + </>} </div> </> @@ -546,7 +576,7 @@ export class TaskList extends Component { /** * Prepare Task(s) details to show status of Task cancellationn */ - getTaskCancelStatusContent() { + getObsTaskStatusContent() { let cancelledTasks = this.state.cancelledTasks; return <> <DataTable value={cancelledTasks} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}> @@ -559,17 +589,20 @@ export class TaskList extends Component { </DataTable> </> } - + /** + * Content to show status after marking task as obsolete + */ getTaskobsStatusContent() { - let obsTasks = this.state.obsTasks; + let obsoleteTasks = this.state.obsoleteTasks; return <> - <DataTable value={obsTasks} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}> + <DataTable value={obsoleteTasks} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}> <Column field="suId" header="Scheduling Unit Id"></Column> <Column field="suName" header="Scheduling Unit Name"></Column> <Column field="taskId" header="Task Id"></Column> <Column field="controlId" header="Control Id"></Column> <Column field="taskName" header="Task Name"></Column> <Column field="status" header="Status"></Column> + <Column field='actionstatus' header= "Action Status"></Column> </DataTable> </> } @@ -601,27 +634,37 @@ export class TaskList extends Component { this.setState({ dialog: dialog, dialogVisible: true }); } } - - confirmObsolete() { + /** + * Function to show confirmation dialog before marking task as obsolete + */ + confirmObsoleteTasks() { this.pageUpdated = false; let selectedBlueprints = this.selectedRows.filter(task => { return task.tasktype === 'Blueprint' }); + const blueprintsWithObsolete = this.checkObsolete(selectedBlueprints) + const blueprintWithoutObsolete = _.difference(selectedBlueprints, blueprintsWithObsolete); if (selectedBlueprints.length === 0) { appGrowl.show({ severity: 'info', summary: 'Select Row', - detail: 'Select atleast one Task Blueprint to obsolete.' }); + detail: 'Please select minimum one task to mark as obsolete' }); } else { + if (blueprintWithoutObsolete.length === 0) { + appGrowl.show({ severity: 'warn', summary: 'Already marked as obsolete', + detail: 'Selected task(s) are already marked as obsolete' }); + } + else { let dialog = this.state.dialog; dialog.type = "confirmation"; - dialog.header = "Confirm to Obsolete Task(s)"; - dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.obsoleteTask }, + dialog.header = "Confirm to mark task(s) as obsolete"; + dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.markObsoleteTasks }, { id: 'no', title: 'No', callback: this.closeDialog }]; - dialog.detail = "Obsolete tasks will not be copied and dataproducts will not be used for pipelines and ingest. Are you sure you want to make the task obsolete?"; - dialog.content = this.getTaskObsoleteConfirmContent; - dialog.submit = this.obsoleteTask; + dialog.detail = "Warning: Obsolete task(s) will not be copied and dataproducts will not be used for pipelines and ingest. Are you sure you want to make the task(s) obsolete?"; + dialog.content = this.getObsTaskConfirmDlgContent; + dialog.submit = this.markObsoleteTasks; dialog.width = '55vw'; dialog.showIcon = false; - this.setState({ dialog: dialog, dialogVisible: true }); + this.setState({ dialog: dialog, dialogVisible: true, blueprintsWithObsolete: blueprintsWithObsolete}); + } } } @@ -652,19 +695,21 @@ export class TaskList extends Component { dialog.header = "Cancel Task(s) Status"; dialog.actions = [{ id: 'no', title: 'Ok', callback: this.closeDialog }]; dialog.detail = "" - dialog.content = this.getTaskCancelStatusContent; + dialog.content = this.getObsTaskStatusContent; dialog.submit = this.closeDialog; dialog.width = '55vw'; dialog.showIcon = false; this.selectedRows = []; this.setState({ tasks: tasks, cancelledTasks: cancelledTasks, dialog: dialog, dialogVisible: true }); } - - async obsoleteTask() { + /** + * Function to show status after marking task as osolete + */ + async markObsoleteTasks() { let tasks = this.state.tasks; - let selectedBlueprints = this.selectedRows.filter(task => {return task.tasktype === 'Blueprint'}); + const blueprintWithoutObsolete = _.difference(this.selectedRows, this.state.blueprintsWithObsolete); let obsoleteTasks = [] - for (const selectedTask of selectedBlueprints) { + for (const selectedTask of blueprintWithoutObsolete) { const obsTask = await TaskService.obsoleteTask(selectedTask.id); let task = _.find(tasks, {'id': selectedTask.id, tasktype: 'Blueprint'}); if (obsTask) { @@ -673,13 +718,14 @@ export class TaskList extends Component { obsoleteTasks.push({ id: task.id, suId: task.schedulingUnitId, suName: task.schedulingUnitName, taskId: task.id, controlId: task.subTaskID, taskName: task.name, - status: task.status.toLowerCase()==='obsolete'?'Obsolete': task.status + status: task.status.toLowerCase()==='obsolete'?'Obsolete': task.status, + actionstatus: obsTask?'Success': 'Failed' }); } let dialog = this.state.dialog; dialog.type = "confirmation"; - dialog.header = "Obsolete Task(s) Status"; + dialog.header = "Mark task(s) as obsolete - Status"; dialog.actions = [{ id: 'no', title: 'Ok', callback: this.closeDialog }]; dialog.detail = "" dialog.content = this.getTaskobsStatusContent; @@ -687,14 +733,24 @@ export class TaskList extends Component { dialog.width = '55vw'; dialog.showIcon = false; this.selectedRows = []; - this.setState({ tasks: tasks, obsTasks: obsoleteTasks, dialog: dialog, dialogVisible: true }); + this.setState({ tasks: tasks, obsoleteTasks: obsoleteTasks, dialog: dialog, dialogVisible: true }); + } + /** + * Function to check if task is marked as obsolete or not + * @param {Array} selectedBlueprints + * @returns Array - List of tasks which are marked as obsolete + */ + checkObsolete(selectedBlueprints) { + const blueprintsWithObsolete = selectedBlueprints.filter(task => { + return task.obsolete_since !== null}); + return blueprintsWithObsolete; } /** * Callback function to close the dialog prompted. */ closeDialog() { - this.setState({ dialogVisible: false, cancelledTasks: [], obsTasks:[] }); + this.setState({ dialogVisible: false, cancelledTasks: [], obsoleteTasks:[] }); } onRowSelection(selectedRows) { @@ -859,9 +915,7 @@ export class TaskList extends Component { let actions = []; if(task_blueprint && task_draft){ if(taskType === 'draft'){ - actions = [{icon: 'fa fa-bullseye', - title:'Obsolete Tasks', - type: 'button', actOn: 'click', props: { callback: this.confirmObsolete }},{icon: 'fa fa-ban', + actions = [{icon: 'fa fa-ban', title: task_draft.canceltask?'Cancel Task(s)': `${this.access_denied_message} to cancel Task(s)`, disabled: task_draft.canceltask? !task_draft.canceltask: true, type: 'button', actOn: 'click', props: { callback: this.confirmCancelTasks }}, @@ -872,16 +926,18 @@ export class TaskList extends Component { ]; } else { - actions = [{icon: 'fa fa-bullseye', - title:'Obsolete Tasks', - type: 'button', actOn: 'click', props: { callback: this.confirmObsolete }},{icon: 'fa fa-ban', + actions = [{icon: 'fa fa-ban', title: task_blueprint.canceltask?'Cancel Task(s)': `${this.access_denied_message} to cancel Task(s)`, disabled: task_blueprint.canceltask? !task_blueprint.canceltask: true, type: 'button', actOn: 'click', props: { callback: this.confirmCancelTasks }}, - {icon: 'fa fa-trash', + {icon: 'fa fa-bullseye', + title: task.edit?'Mark as Obsolete': `${this.access_denied_message}`, + disabled: task.edit? !task.edit: true, + type: 'button', actOn: 'click', props: { callback: this.confirmObsoleteTasks }}, + {icon: 'fa fa-trash', title: task.delete?'Delete Task(s)':`${this.access_denied_message} to delete Task(s)`, disabled: task.delete? !task.delete: true, - type: 'button', actOn: 'click', props: { callback: this.confirmDeleteTasks }} + type: 'button', actOn: 'click', props: { callback: this.confirmDeleteTasks }}, ]; } } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js index 5cf2d0ae9cb58ad8d1877888315a63bda490fee1..c8dee333f5f1ff56801a4de0ad7413c04408828f 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js @@ -114,12 +114,13 @@ export class TaskView extends Component { this.showObsoleteConfirmation = this.showObsoleteConfirmation.bind(this); this.obsoleteTask = this.obsoleteTask.bind(this); this.onRowSelection=this.onRowSelection.bind(this); - this.showSubTaskCancelConfirmation=this.showSubTaskCancelConfirmation.bind(this); - this.cancelSubTasks=this.cancelSubTasks.bind(this); - this.getSubTaskCancelConfirmContent = this.getSubTaskCancelConfirmContent.bind(this); - this.showSubTaskObsoleteConfirmation=this.showSubTaskObsoleteConfirmation.bind(this); - this.obsoleteSubTasks=this.obsoleteSubTasks.bind(this); + this.showSubtaskCancelConfirmation=this.showSubtaskCancelConfirmation.bind(this); + this.cancelSubtasks=this.cancelSubtasks.bind(this); + this.getSubtaskCancelConfirmContent = this.getSubtaskCancelConfirmContent.bind(this); + this.showSubtaskObsoleteConfirmation=this.showSubtaskObsoleteConfirmation.bind(this); + this.obsoleteSubtasks=this.obsoleteSubtasks.bind(this); this.getSubTaskObsoleteConfirmContent = this.getSubTaskObsoleteConfirmContent.bind(this); + this.getSubtaskobsStatusContent = this.getSubtaskobsStatusContent.bind(this); if (this.props.match.params.id) { this.state.taskId = this.props.match.params.id; } @@ -143,7 +144,6 @@ export class TaskView extends Component { onRowSelection(selectedRows) { this.selectedRows = selectedRows; - console.log(this.selectedRows); } async componentDidUpdate(prevProps, prevState) { @@ -240,6 +240,7 @@ export class TaskView extends Component { subtaskRow['process_start_time']= subtask.process_start_time; subtaskRow['process_stop_time']= subtask.process_stop_time; subtaskRow['duration']= moment.utc((subtask.duration || 0) * 1000).format(UIConstants.CALENDAR_TIME_FORMAT); + subtaskRow['obsolete'] = subtask.obsolete_since subtaskRow['parset']= `Parset`; subtaskRow['links'] = ['Link to Parset']; subtaskRow['linksURL'] = { @@ -353,20 +354,27 @@ export class TaskView extends Component { this.setState({ dialog: dialog, confirmDialogVisible: true }); } + /** + * Show confirmation before marking the task as obsolete + */ showObsoleteConfirmation() { let dialog = this.state.dialog; dialog.type = "confirmation"; dialog.header = "Confirm to Obsolete Task"; dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.obsoleteTask }, { id: 'no', title: 'No', callback: this.closeDialog }]; - dialog.detail = "Obsolete tasks will not be copied and dataproducts will not be used for pipelines and ingest. Are you sure you want to make the task obsolete?"; + dialog.detail = "Warning: Obsolete tasks will not be copied and dataproducts will not be used for pipelines and ingest. Are you sure you want to make the task obsolete?"; + dialog.content = ''; dialog.submit = this.obsoleteTask; dialog.width = '40vw'; dialog.showIcon = true; this.setState({ dialog: dialog, confirmDialogVisible: true }); } - getSubTaskCancelConfirmContent() { + /** + * Show confirmation dialog before marking subtask as obsolete + */ + getSubtaskCancelConfirmContent() { let selectedTasks = []; for (const obj of this.selectedRows) { selectedTasks.push({ @@ -387,10 +395,21 @@ export class TaskView extends Component { </> } + /** + * Content for confirmation dialog to mark subtask as obsolete + */ getSubTaskObsoleteConfirmContent() { - let selectedTasks = []; - for (const obj of this.selectedRows) { - selectedTasks.push({ + const subtaskWithObsolete = this.state.subtaskWithObsolete; + const subtaskWithoutObsolete = _.difference(this.selectedRows, subtaskWithObsolete); + let selectedSubtaskWithObsolete = []; + let selectedSubtasksWithoutObsolete = []; + for (const obj of subtaskWithObsolete) { + selectedSubtaskWithObsolete.push({ + id: obj.id, type: obj.type, status: obj.status + }); + } + for (const obj of subtaskWithoutObsolete) { + selectedSubtasksWithoutObsolete.push({ id: obj.id, type: obj.type, status: obj.status }); } @@ -398,23 +417,46 @@ export class TaskView extends Component { return <> <div style={{marginTop: '1em'}}> - <b>SubTask(s) that can be obsolete</b> - <DataTable value={selectedTasks} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}> + <b>Sub Task(s) that can be marked as obsolete’ if task has null for obsolete_since.</b> + <p>Sub Task(s) that are marked as obsolete already will be ignored’ if task has value for obsolete_since.</p> + {selectedSubtaskWithObsolete.length>0 && + <DataTable value={selectedSubtaskWithObsolete} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}> + <Column field="id" header="Subtask Id"></Column> + <Column field="type" header="Type"></Column> + <Column field="status" header="Status"></Column> + </DataTable>} + {selectedSubtasksWithoutObsolete.length>0 && + <DataTable value={selectedSubtasksWithoutObsolete} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}> <Column field="id" header="Subtask Id"></Column> <Column field="type" header="Type"></Column> <Column field="status" header="Status"></Column> + </DataTable>} + </div> + </> + } + /** + * Content for status dialog after marking subtask as obsolete + */ + getSubtaskobsStatusContent() { + this.selectedRows = []; + + return <> + <div style={{marginTop: '1em'}}> + <DataTable value={this.state.obsoleteTasks} resizableColumns columnResizeMode="expand" className="card" style={{ paddingLeft: '0em' }}> + <Column field="id" header="Subtask Id"></Column> + <Column field="type" header="Type"></Column> + <Column field="actionStatus" header="Action Status"></Column> </DataTable> </div> </> } - async cancelSubTasks() { + async cancelSubtasks() { let subtasks = this.state.subtaskRowList; let cancelledTasks = [] for (const selectedTask of this.selectedRows) { const cancelledTask = await TaskService.cancelsubTask(selectedTask.id); let task = _.find(subtasks, {'id': selectedTask.id}); - console.log("task",task); if (cancelledTask) { task.status = cancelledTask.state_value; } @@ -429,7 +471,7 @@ export class TaskView extends Component { dialog.header = "Cancel Task(s) Status"; dialog.actions = [{ id: 'no', title: 'Ok', callback: this.closeDialog }]; dialog.detail = "" - dialog.content = this.getSubTaskCancelConfirmContent; + dialog.content = this.getSubtaskCancelConfirmContent; dialog.submit = this.closeDialog; dialog.width = '55vw'; dialog.showIcon = false; @@ -437,28 +479,32 @@ export class TaskView extends Component { this.setState({ subtaskRowList:subtasks, cancelledTasks: cancelledTasks, dialog: dialog, confirmDialogVisible: true }); } - async obsoleteSubTasks() { - let subtasks = this.state.subtaskRowList; + /** + * Function to mark subtask as obsolete + */ + async obsoleteSubtasks() { + let subtasks = this.state.subtaskRowList; + const subtaskWithoutObsolete = _.difference(this.selectedRows, this.state.subtaskWithObsolete); let obsoleteTasks = [] - for (const selectedTask of this.selectedRows) { - const obsoleteTask = await TaskService.obsoleteSubTask(selectedTask.id); + for (const selectedTask of subtaskWithoutObsolete) { + const obsoleteTask = await TaskService.obsoleteSubtask(selectedTask.id); let task = _.find(subtasks, {'id': selectedTask.id}); - console.log("task",task); if (obsoleteTask) { task.status = obsoleteTask.state_value; } obsoleteTasks.push({ id: task.id, type: task.type, - status: task.status.toLowerCase()==='obsolete'?'Obsolete': 'Error Occured' + status: task.status.toLowerCase()==='obsolete'?'Obsolete': 'Error Occured', + actionStatus:obsoleteTask?'Success':'Failed' }); } let dialog = this.state.dialog; dialog.type = "confirmation"; - dialog.header = "Obsolete SubTask(s) Status"; + dialog.header = "Mark Subtask(s) as obsolete - Status"; dialog.actions = [{ id: 'no', title: 'Ok', callback: this.closeDialog }]; dialog.detail = "" - dialog.content = this.getSubTaskObsoleteConfirmContent; + dialog.content = this.getSubtaskobsStatusContent; dialog.submit = this.closeDialog; dialog.width = '55vw'; dialog.showIcon = false; @@ -466,9 +512,8 @@ export class TaskView extends Component { this.setState({ subtaskRowList:subtasks, obsoleteTasks: obsoleteTasks, dialog: dialog, confirmDialogVisible: true }); } - showSubTaskCancelConfirmation() { + showSubtaskCancelConfirmation() { this.pageUpdated = false; - console.log("selectedRows",this.selectedRows); let selectedBlueprints = this.selectedRows.filter(task => { return task.status !== 'cancelled'}); if (selectedBlueprints.length === 0) { @@ -478,37 +523,47 @@ export class TaskView extends Component { let dialog = this.state.dialog; dialog.type = "confirmation"; dialog.header = "Confirm to Cancel Task(s)"; - dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.cancelSubTasks }, + dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.cancelSubtasks }, { id: 'no', title: 'No', callback: this.closeDialog }]; dialog.detail = "Cancelling the task means, it will no longer be executed / will be aborted. This action cannot be undone. Already finished/cancelled task(s) will be ignored. Do you want to proceed?"; - dialog.content = this.getSubTaskCancelConfirmContent; - dialog.submit = this.cancelSubTasks; + dialog.content = this.getSubtaskCancelConfirmContent; + dialog.submit = this.cancelSubtasks; dialog.width = '55vw'; dialog.showIcon = false; this.setState({ dialog: dialog, confirmDialogVisible: true }); } } - - showSubTaskObsoleteConfirmation() { + /** + * Function to show confirmation dialog before marking subtask as obsolete + */ + showSubtaskObsoleteConfirmation() { this.pageUpdated = false; - console.log("selectedRows",this.selectedRows); let selectedBlueprints = this.selectedRows.filter(task => { return task.status !== 'obsolete'}); + const subtaskWithObsolete = this.checkObsolete(selectedBlueprints) + const subtaskWithoutObsolete = _.difference(selectedBlueprints, subtaskWithObsolete); if(selectedBlueprints.length === 0) { appGrowl.show({ severity: 'info', summary: 'Select Row', - detail: 'Please Select atleast one Subtask to obsolete.' }); + detail: 'Please select minimum one task to mark as obsolete' }); } else { - let dialog = this.state.dialog; - dialog.type = "confirmation"; - dialog.header = "Confirm to Obsolete SubTask(s)"; - dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.obsoleteSubTasks }, - { id: 'no', title: 'No', callback: this.closeDialog }]; - dialog.detail = "Obsolete tasks will not be copied and dataproducts will not be used for pipelines and ingest. Are you sure you want to make the task obsolete?"; - dialog.content = this.getSubTaskObsoleteConfirmContent; - dialog.submit = this.obsoleteSubTasks; - dialog.width = '55vw'; - dialog.showIcon = false; - this.setState({ dialog: dialog, confirmDialogVisible: true }); + if (subtaskWithoutObsolete.length === 0) { + appGrowl.show({ severity: 'warn', summary: 'Already marked as obsolete', + detail: 'Selected subtask(s) are already marked as obsolete' }); + } + else { + let dialog = this.state.dialog; + dialog.type = "confirmation"; + dialog.header = "Confirm to mark task as obsolete"; + dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.obsoleteSubtasks }, + { id: 'no', title: 'No', callback: this.closeDialog }]; + dialog.detail = "Warning: Obsolete task will not be copied and dataproducts will not be used for pipelines and ingest. Are you sure you want to make the task obsolete?"; + dialog.content = this.getSubTaskObsoleteConfirmContent; + dialog.submit = this.obsoleteSubtasks; + dialog.width = '55vw'; + dialog.showIcon = false; + this.setState({ dialog: dialog, confirmDialogVisible: true, subtaskWithObsolete: subtaskWithObsolete }); + } + } } @@ -528,7 +583,9 @@ export class TaskView extends Component { this.setState({ confirmDialogVisible: false, task: task, actions: actions}); } } - + /** + * Function to mark task as obsolete + */ async obsoleteTask() { let task = this.state.task; let obeslateTask = await TaskService.obsoleteTask(task.id); @@ -542,6 +599,14 @@ export class TaskView extends Component { this.setState({ confirmDialogVisible: false, task: task, actions: actions}); } } + /** + * Function to check if subtask is marked as obsolete + */ + checkObsolete(selectedSubtask) { + const subtasksWithObsolete = selectedSubtask.filter(subtask => { + return subtask.obsolete !== null}); + return subtasksWithObsolete; + } async getStatusList(type) { const taskFilters = await TaskService.getTaskFilterDefinition(type); @@ -608,11 +673,13 @@ export class TaskView extends Component { this.state.permissionById && this.state.permissionById[this.state.taskId].cancel? false: true, props: { callback: this.showCancelConfirmation } }); + actions.push({icon: 'fa fa-bullseye', + title: this.state.task && this.state.task.obsolete_since?'Already marked as obsolete':this.state.permissionById[this.state.taskId] && this.state.permissionById[this.state.taskId].edit?'Mark as Obsolete':`${this.access_denied_message}`, + disabled: this.state.task && this.state.task.obsolete_since? true: this.state.permissionById[this.state.taskId] && this.state.permissionById[this.state.taskId].edit?false: true, + type: 'button', actOn: 'click', props: { callback: this.showObsoleteConfirmation }}); + } } - actions.push({icon: 'fa fa-bullseye', - title:'Obsolete Tasks', - type: 'button', actOn: 'click', props: { callback: this.showObsoleteConfirmation }}); actions.push({icon: 'fa fa-trash',title:this.state.hasBlueprint ? 'Cannot delete Draft when Blueprint exists': this.state.permissionById && this.state.permissionById[this.state.taskId].delete? 'Delete Task': `${this.access_denied_message} to delete`, @@ -633,12 +700,13 @@ export class TaskView extends Component { </ul> ); - const subTaskMenu=[]; - subTaskMenu.push({icon: 'fa fa-bullseye', - title:'Obsolete Tasks', - type: 'button', actOn: 'click', props: { callback: this.showSubTaskObsoleteConfirmation }},{icon: 'fa fa-ban', - title:'Cancel Sub Tasks', - type: 'button', actOn: 'click', props: { callback: this.showSubTaskCancelConfirmation }}); + const subtaskMenu=[]; + subtaskMenu.push({icon: 'fa fa-bullseye', + title: 'Mark as Obsolete', + type: 'button', actOn: 'click', props: { callback: this.showSubtaskObsoleteConfirmation }}, + {icon: 'fa fa-ban', + title:'Cancel Sub Tasks', + type: 'button', actOn: 'click', props: { callback: this.showSubtaskCancelConfirmation }}); return ( <React.Fragment> @@ -757,7 +825,7 @@ export class TaskView extends Component { {this.state.taskType === 'blueprint' && <div style={{marginBottom: "10px"}}> <div style={{marginTop: "10px"}}> - <PageHeader location={this.props.location} title={'Subtasks'} actions={subTaskMenu} /> + <PageHeader location={this.props.location} title={'Subtasks'} actions={subtaskMenu} /> </div> <ViewTable data={this.state.subtaskRowList} diff --git a/SAS/TMSS/frontend/tmss_webapp/src/services/task.service.js b/SAS/TMSS/frontend/tmss_webapp/src/services/task.service.js index 870f30968ae15db73a4d459c81d1c47ed6c8a0e2..1f7933649b9abf57edb2d8f463004ef9c7a7e79a 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/services/task.service.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/services/task.service.js @@ -320,7 +320,7 @@ const TaskService = { return false; } }, -obsoleteSubTask: async function(id) { +obsoleteSubtask: async function(id) { try { const url = `/api/subtask/${id}/mark_as_obsolete/`; const res = await axios.post(url, {});