From be095e1e2cf45065f7619e944f88e5e5439337d7 Mon Sep 17 00:00:00 2001 From: NithyaSanthanam <nithya.s@matriotsolutions.com> Date: Fri, 13 Aug 2021 13:34:54 +0530 Subject: [PATCH] TMSS-814: Fixed permission based issues, Indentation Updated --- SAS/TMSS/frontend/tmss_webapp/src/App.js | 1 - .../tmss_webapp/src/authenticate/auth.js | 7 +- .../src/authenticate/auth.store.js | 5 + .../authenticate/permission.stack.handler.js | 78 ++-- .../src/components/ProtectedRouteComponent.js | 3 +- .../tmss_webapp/src/routes/Cycle/list.js | 103 ++--- .../tmss_webapp/src/routes/Project/edit.js | 384 +++++++++--------- .../tmss_webapp/src/routes/Project/list.js | 62 ++- .../tmss_webapp/src/routes/Project/view.js | 2 +- .../routes/Scheduling/SchedulingUnitList.js | 8 +- .../routes/Scheduling/ViewSchedulingUnit.js | 40 +- .../src/routes/Scheduling/create.js | 9 +- .../tmss_webapp/src/routes/Scheduling/edit.js | 321 ++++++++------- .../Scheduling/excelview.schedulingset.js | 4 +- .../tmss_webapp/src/routes/Task/edit.js | 163 ++++---- .../tmss_webapp/src/routes/Task/list.js | 53 ++- .../tmss_webapp/src/routes/Task/view.js | 15 +- .../tmss_webapp/src/utils/auth.util.js | 21 +- 18 files changed, 675 insertions(+), 604 deletions(-) diff --git a/SAS/TMSS/frontend/tmss_webapp/src/App.js b/SAS/TMSS/frontend/tmss_webapp/src/App.js index 9071d158771..0148cea2fc7 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/App.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/App.js @@ -244,7 +244,6 @@ class App extends Component { 'layout-overlay-sidebar-active': this.state.overlayMenuActive && this.state.layoutMode === 'overlay', 'layout-mobile-sidebar-active': this.state.mobileMenuActive }); - //console.log(this.props); return ( <React.Fragment> <div className="App"> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/authenticate/auth.js b/SAS/TMSS/frontend/tmss_webapp/src/authenticate/auth.js index 27e43019029..71f858e0eba 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/authenticate/auth.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/authenticate/auth.js @@ -1,4 +1,5 @@ import AuthService from "../services/auth.service"; +import AuthStore from "./auth.store"; import PermissionStackUtil from './permission.stack.handler'; const axios = require('axios'); @@ -13,7 +14,10 @@ const Auth = { user = JSON.parse(user); if (user.token) { axios.defaults.headers.common['Authorization'] = `Token ${user.token}`; - await PermissionStackUtil.getPermissions(true); + const permissions = await AuthStore.getState(); + if(!permissions.userRolePermission.project) { + await PermissionStackUtil.getPermissions(true); + } return true; } } @@ -38,6 +42,7 @@ const Auth = { /** Remove user details from localstorage on logout */ logout: () => { AuthService.deAuthenticate(); + PermissionStackUtil.deleteStore() localStorage.removeItem("user"); } } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/authenticate/auth.store.js b/SAS/TMSS/frontend/tmss_webapp/src/authenticate/auth.store.js index 6fac71776ae..baf173dccb9 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/authenticate/auth.store.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/authenticate/auth.store.js @@ -35,6 +35,11 @@ const rolePermissionReducer = (state, action) => { state['rolePermission'][action.module] = {...state['rolePermission'][action.module] , [action.id]: permissionStack[action.module][action.id] } return {...state}; } + + case 'deletestore': { + state = action.payload; + return {...state}; + } default: { const actionType = action.type; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/authenticate/permission.stack.handler.js b/SAS/TMSS/frontend/tmss_webapp/src/authenticate/permission.stack.handler.js index 834d1b0ed8a..084853634cc 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/authenticate/permission.stack.handler.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/authenticate/permission.stack.handler.js @@ -34,18 +34,6 @@ const PermissionStackUtil = { }, async getAPIBasedPermission() { let permissionStack = {}; - const RedirectionMap = { - 'wait scheduled': 1, - 'wait processed': 2, - 'qa reporting to': 3, - 'qa reporting sos':4, - 'pi verification':5, - 'decide acceptance':6, - 'ingesting':7, - 'unpin data':8, - 'end':9 - } - // const modules = ['project', 'scheduleunit_draft', 'scheduleunit_blueprint', 'cycle', 'task_draft', 'task_blueprint', 'reservation' ]; const module_url = { project: 'project', scheduleunit_draft: 'scheduling_unit_draft', @@ -54,7 +42,10 @@ const PermissionStackUtil = { cycle: 'cycle', task_draft: 'task_draft', task_blueprint: 'task_blueprint', - reservation: 'reservation' + reservation: 'reservation', + task_relation_draft: 'task_relation_draft', + task_relation_blueprint: 'task_relation_blueprint' + } const modules = Object.keys(module_url); for(const module of modules) { @@ -69,7 +60,7 @@ const PermissionStackUtil = { edit: allowedPermission?(_.includes(allowedPermission, 'PATCH')):false, delete: allowedPermission?(_.includes(allowedPermission, 'DELETE')):false}; } - else if(module === 'scheduleunit_draft' || module === 'schedulingunit_blueprint') { + else if(module === 'scheduleunit_draft' || module === 'scheduleunit_blueprint') { let getAccesss = allowedPermission?(_.includes(allowedPermission, 'GET')):false; let putAccesss = allowedPermission?(_.includes(allowedPermission, 'PUT')):false; let patchAccess = allowedPermission?(_.includes(allowedPermission, 'PATCH')):false; @@ -77,14 +68,20 @@ const PermissionStackUtil = { let postAccess = allowedPermission?(_.includes(allowedPermission, 'POST')):false; permissionStack['scheduleunit'] ={ create: putAccesss, edit: patchAccess, delete: deleteAccess, list: getAccesss, - createsub: putAccesss, autodeletion:patchAccess, copysu:putAccesss, excelview:putAccesss, - cleanuptask:true, cancelsu:true, viewworkflow:true,dataproduct: true }; + createsub: putAccesss, autodeletion:patchAccess, copysu:putAccesss, excelview:getAccesss, + cleanuptask:true, cancelsu:true, viewworkflow:true }; permissionStack[module] = { create: putAccesss, edit: patchAccess, delete: deleteAccess, list: getAccesss, add: postAccess - } + } + } + else if(module === 'task_relation_draft') { + permissionStack['scheduleunit'].dataproduct = allowedPermission?(_.includes(allowedPermission, 'POST')):false; } - else if (module === 'Â scheduling_set') { - permissionStack['scheduleunit_draft']['Â scheduling_set'] = allowedPermission?(_.includes(allowedPermission, 'POST')):false; + else if(module === 'task_relation_blueprint') { + permissionStack['scheduleunit_blueprint'].dataproduct = allowedPermission?(_.includes(allowedPermission, 'POST')):false; + } + else if (module === 'scheduling_set') { + permissionStack['scheduleunit_draft']['scheduling_set'] = allowedPermission?(_.includes(allowedPermission, 'POST')):false; } else if(module === 'cycle') { permissionStack[module] ={ @@ -92,13 +89,18 @@ const PermissionStackUtil = { create: allowedPermission?(_.includes(allowedPermission, 'PUT')):false, edit: allowedPermission?(_.includes(allowedPermission, 'PATCH')):false, delete: allowedPermission?(_.includes(allowedPermission, 'DELETE')):false}; - } else if(module === 'task_draft' || module === 'task_blueprint'){ + } + else if(module === 'task_draft' || module === 'task_blueprint'){ permissionStack['task'] ={ list: allowedPermission?(_.includes(allowedPermission, 'GET')):false, edit: allowedPermission?(_.includes(allowedPermission, 'PATCH')):false, - delete: allowedPermission?(_.includes(allowedPermission, 'DELETE')):false + delete: allowedPermission?(_.includes(allowedPermission, 'DELETE')):false, } - } else if(module === 'reservation') { + permissionStack[module] = { + canceltask: allowedPermission?(_.includes(allowedPermission, 'POST')):false + }; + } + else if(module === 'reservation') { let getAccesss = allowedPermission?(_.includes(allowedPermission, 'GET')):false; let postAccess = allowedPermission?(_.includes(allowedPermission, 'POST')):false; permissionStack['timeline'] = { @@ -115,14 +117,7 @@ const PermissionStackUtil = { edit: allowedPermission?(_.includes(allowedPermission, 'PATCH')):false, }; } - } - // else { - // permissionStack['project'] = {create: false, edit: false, delete: false}; - // permissionStack['cycle'] = {create: false, edit: false, delete: false} - // permissionStack['scheduleunit'] = {create: false, edit: false, delete: false, createsub: false, autodeletion:false, - // copysu:false, excelview:false, cleanuptask:true, cancelsu:true, viewworkflow:true,dataproduct: true,}; - // } } permissionStack['workflow'] = { 'qa_reporting_to': true, @@ -133,21 +128,24 @@ const PermissionStackUtil = { }; return permissionStack; }, - - async getUserAccessModulebyId(module, id) { + async getAccessByModuleAndId(module, id) { let permissionStack = {}; const url = module+'/'+id; - const allowedPermission = await AuthService.getAccessControlMethod(url); - permissionStack[module] = {} - if(allowedPermission) { - permissionStack[module][id] ={ - view : allowedPermission?(_.includes(allowedPermission, 'GET')):false, - edit : allowedPermission?(_.includes(allowedPermission, 'PUT')):false, - delete : allowedPermission?(_.includes(allowedPermission, 'DELETE')):false - } + const allowedPermission = await AuthService.getAccessControlMethod(url); + permissionStack[module] = {} + if(allowedPermission) { + permissionStack[module][id] ={ + view : allowedPermission?(_.includes(allowedPermission, 'GET')):false, + edit : allowedPermission?(_.includes(allowedPermission, 'PUT')):false, + delete : allowedPermission?(_.includes(allowedPermission, 'DELETE')):false } - AuthStore.dispatch({ type: 'loadpermissionbyid', payload: permissionStack, id: id, module: module}); + } + AuthStore.dispatch({ type: 'loadpermissionbyid', payload: permissionStack, id: id, module: module}); return permissionStack[module]; + }, + async deleteStore(){ + await AuthStore.dispatch({type: 'deletestore', payload: {}}); + return } } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/ProtectedRouteComponent.js b/SAS/TMSS/frontend/tmss_webapp/src/components/ProtectedRouteComponent.js index 2ca0e9103f8..78b9ea40fa9 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/ProtectedRouteComponent.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/ProtectedRouteComponent.js @@ -22,10 +22,11 @@ class ProtectedRoute extends Component{ } hasPermission() { + const permission = this.props.permissions if(permission.length <= 2) { if(typeof(permission[0]) !== undefined && permission[1] !== undefined) { - if(this.state.permission_set['userRolePermission'][permission[0]][permission[1]]) { + if(this.state.permission_set['userRolePermission'][permission[0]] && this.state.permission_set['userRolePermission'][permission[0]][permission[1]]) { return true; } else { return false; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/list.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/list.js index eec4878327c..68e940af137 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/list.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/list.js @@ -194,59 +194,60 @@ class CycleList extends Component { const {cycle} = this.state.userrole; return ( <> - {cycle && cycle.list ? - <> - { /*<div className="p-grid"> - <div className="p-col-10 p-lg-10 p-md-10"> - <h2>Cycle - List </h2> - </div> - <div className="p-col-2 p-lg-2 p-md-2"> - <Link to={{ pathname: '/cycle/create'}} title="Add New Cycle" style={{float: "right"}}> - <i className="fa fa-plus-square" style={{marginTop: "10px"}}></i> - </Link> - </div> - </div> */} - {/* - * Call View table to show table data, the parameters are, - data - Pass API data - defaultcolumns - This colum will be populate by default in table with header mentioned - showaction - {true/false} -> to show the action column - paths - specify the path for navigation - Table will set "id" value for each row in action button - */} - <PageHeader location={this.props.location} title={'Cycle - List'} - actions={[ - { icon: 'fa-plus-square', - title: cycle.create ?'Click to Add Cycle': this.access_denied_message, - disabled: cycle.create?!cycle.create:true, - props: { pathname: '/cycle/create' } }]} /> - {/* - * Call View table to show table data, the parameters are, - data - Pass API data - defaultcolumns - This colum will be populate by default in table with header mentioned - showaction - {true/false} -> to show the action column - paths - specify the path for navigation - Table will set "id" value for each row in action button - */} + {cycle && + <> + { /*<div className="p-grid"> + <div className="p-col-10 p-lg-10 p-md-10"> + <h2>Cycle - List </h2> + </div> + <div className="p-col-2 p-lg-2 p-md-2"> + <Link to={{ pathname: '/cycle/create'}} title="Add New Cycle" style={{float: "right"}}> + <i className="fa fa-plus-square" style={{marginTop: "10px"}}></i> + </Link> + </div> + </div> */} + {/* + * Call View table to show table data, the parameters are, + data - Pass API data + defaultcolumns - This colum will be populate by default in table with header mentioned + showaction - {true/false} -> to show the action column + paths - specify the path for navigation - Table will set "id" value for each row in action button + */} + <PageHeader location={this.props.location} title={'Cycle - List'} + actions={[ + { icon: 'fa-plus-square', + title: cycle.create ?'Click to Add Cycle': this.access_denied_message, + disabled: cycle.create?!cycle.create:true, + props: { pathname: '/cycle/create' } }]} /> + {/* + * Call View table to show table data, the parameters are, + data - Pass API data + defaultcolumns - This colum will be populate by default in table with header mentioned + showaction - {true/false} -> to show the action column + paths - specify the path for navigation - Table will set "id" value for each row in action button + */} - {this.state.isLoading ? <AppLoader /> : (this.state.cyclelist && this.state.cyclelist.length) ? + {this.state.isLoading ? <AppLoader /> : (this.state.cyclelist && this.state.cyclelist.length) ? - <ViewTable - data={this.state.cyclelist} - defaultcolumns={this.defaultcolumns} - optionalcolumns={this.optionalcolumns} - columnclassname={this.columnclassname} - defaultSortColumn={this.defaultSortColumn} - columnOrders={this.columnOrder} - showaction={true} - paths={this.state.paths} - tablename={this.lsTableName} - toggleBySorting={(sortData) => this.toggleBySorting(sortData)} - lsKeySortColumn={this.lsKeySortColumn} - descendingColumn={this.descendingColumn} - pageUpdated={this.pageUpdated} - storeFilter={false} - /> : <></> - } - </>: <AccessDenied/>} + <ViewTable + data={this.state.cyclelist} + defaultcolumns={this.defaultcolumns} + optionalcolumns={this.optionalcolumns} + columnclassname={this.columnclassname} + defaultSortColumn={this.defaultSortColumn} + columnOrders={this.columnOrder} + showaction={true} + paths={this.state.paths} + tablename={this.lsTableName} + toggleBySorting={(sortData) => this.toggleBySorting(sortData)} + lsKeySortColumn={this.lsKeySortColumn} + descendingColumn={this.descendingColumn} + pageUpdated={this.pageUpdated} + storeFilter={false} + /> : <></> + } + + </>} </> ) } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/edit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/edit.js index e8b8a562381..60392020d4a 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/edit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/edit.js @@ -23,11 +23,13 @@ import UnitConverter from '../../utils/unit.converter'; import UIConstants from '../../utils/ui.constants'; import ReactTooltip from "react-tooltip"; import AuthUtil from '../../utils/auth.util'; +import AccessDenied from '../../layout/components/AccessDenied'; export class ProjectEdit extends Component { constructor(props) { super(props); this.state = { + userrole: {}, showDialog: false, isDirty: false, isLoading: true, @@ -132,6 +134,8 @@ export class ProjectEdit extends Component { let project = await ProjectService.getProjectDetails(this.props.match.params.id); let resourceList = this.state.resourceList; let projectQuota = {}; + const permissions = await AuthUtil.getUserPermissionByModuleId('project', this.props.match.params.id); + this.setState({userrole: permissions}); if (project) { // Get project_quota for the project and asssign to the component variable for (const id of project.quota_ids) { @@ -455,212 +459,214 @@ export class ProjectEdit extends Component { render() { if (this.state.redirect) { return <Redirect to={ {pathname: this.state.redirect} }></Redirect> - } - + } return ( <React.Fragment> - <Growl ref={(el) => this.growl = el} /> - <PageHeader location={this.props.location} title={'Project - Edit'} actions={[{icon:'fa-window-close', - title:'Click to Close Project Edit Page', type: 'button', actOn: 'click', props:{ callback: this.checkIsDirty }}]}/> - - { this.state.isLoading ? <AppLoader/> : + {this.state.userrole[this.props.match.params.id] && this.state.userrole[this.props.match.params.id].edit? <> - <div> - <div className="p-fluid"> - <div className="p-field p-grid"> - <label htmlFor="projectName" className="col-lg-2 col-md-2 col-sm-12">Name <span style={{color:'red'}}>*</span></label> - <div className="col-lg-3 col-md-3 col-sm-12"> - <InputText className={this.state.errors.name ?'input-error':''} id="projectName" data-testid="name" - tooltip="Enter name of the project" tooltipOptions={this.tooltipOptions} maxLength="128" - value={this.state.project.name} - onChange={(e) => this.setProjectParams('name', e.target.value, 'PROJECT_NAME')} - onBlur={(e) => this.setProjectParams('name', e.target.value, 'PROJECT_NAME')}/> - <label className={this.state.errors.name?"error":"info"}> - {this.state.errors.name ? this.state.errors.name : "Max 128 characters"} - </label> - </div> - <div className="col-lg-1 col-md-1 col-sm-12"></div> - <label htmlFor="description" className="col-lg-2 col-md-2 col-sm-12">Description <span style={{color:'red'}}>*</span></label> - <div className="col-lg-3 col-md-3 col-sm-12"> - <InputTextarea className={this.state.errors.description ?'input-error':''} rows={3} cols={30} - tooltip="Short description of the project" tooltipOptions={this.tooltipOptions} maxLength="128" - data-testid="description" value={this.state.project.description} - onChange={(e) => this.setProjectParams('description', e.target.value)} - onBlur={(e) => this.setProjectParams('description', e.target.value)}/> - <label className={this.state.errors.description ?"error":"info"}> - {this.state.errors.description ? this.state.errors.description : "Max 255 characters"} - </label> - </div> - </div> - <div className="p-field p-grid"> - <label htmlFor="triggerPriority" className="col-lg-2 col-md-2 col-sm-12">Trigger Priority </label> - <div className="col-lg-3 col-md-3 col-sm-12"> - <InputNumber inputId="trig_prio" name="trig_prio" className={this.state.errors.name ?'input-error':''} - tooltip="Priority of this project with respect to triggers" tooltipOptions={this.tooltipOptions} - value={this.state.project.trigger_priority} showButtons - min={0} max={1001} step={10} useGrouping={false} - onChange={(e) => this.setProjectParams('trigger_priority', e.value)} - onBlur={(e) => this.setProjectParams('trigger_priority', e.target.value, 'NUMBER')} /> - <label className="error"> - {this.state.errors.trigger_priority ? this.state.errors.trigger_priority : ""} - </label> - </div> - <div className="col-lg-1 col-md-1 col-sm-12"></div> - <label htmlFor="trigger" className="col-lg-2 col-md-2 col-sm-12">Allows Trigger Submission</label> - <div className="col-lg-3 col-md-3 col-sm-12"> - <Checkbox inputId="trigger" role="trigger" - tooltip="Is this project allowed to supply observation requests on the fly, possibly interrupting currently running observations (responsive telescope)?" - tooltipOptions={this.tooltipOptions} - checked={this.state.project.can_trigger} onChange={e => this.setProjectParams('can_trigger', e.target.checked)}></Checkbox> - </div> - </div> - <div className="p-field p-grid"> - <label htmlFor="projCategory" className="col-lg-2 col-md-2 col-sm-12">Project Category </label> - <div className="col-lg-3 col-md-3 col-sm-12"> - <Dropdown inputId="projCat" optionLabel="value" optionValue="url" - tooltip="Project Category" tooltipOptions={this.tooltipOptions} - value={this.state.project.project_category} - options={this.state.projectCategories} - onChange={(e) => {this.setProjectParams('project_category', e.value)}} - placeholder="Select Project Category" /> - </div> - <div className="col-lg-1 col-md-1 col-sm-12"></div> - <label htmlFor="periodCategory" className="col-lg-2 col-md-2 col-sm-12">Period Category</label> - <div className="col-lg-3 col-md-3 col-sm-12"> - <Dropdown data-testid="period-cat" id="period-cat" optionLabel="value" optionValue="url" - tooltip="Period Category" tooltipOptions={this.tooltipOptions} - value={this.state.project.period_category} - options={this.state.periodCategories} - onChange={(e) => {this.setProjectParams('period_category',e.value)}} - placeholder="Select Period Category" /> + <Growl ref={(el) => this.growl = el} /> + <PageHeader location={this.props.location} title={'Project - Edit'} actions={[{icon:'fa-window-close', + title:'Click to Close Project Edit Page', type: 'button', actOn: 'click', props:{ callback: this.checkIsDirty }}]}/> + + { this.state.isLoading ? <AppLoader/> : + <> + <div> + <div className="p-fluid"> + <div className="p-field p-grid"> + <label htmlFor="projectName" className="col-lg-2 col-md-2 col-sm-12">Name <span style={{color:'red'}}>*</span></label> + <div className="col-lg-3 col-md-3 col-sm-12"> + <InputText className={this.state.errors.name ?'input-error':''} id="projectName" data-testid="name" + tooltip="Enter name of the project" tooltipOptions={this.tooltipOptions} maxLength="128" + value={this.state.project.name} + onChange={(e) => this.setProjectParams('name', e.target.value, 'PROJECT_NAME')} + onBlur={(e) => this.setProjectParams('name', e.target.value, 'PROJECT_NAME')}/> + <label className={this.state.errors.name?"error":"info"}> + {this.state.errors.name ? this.state.errors.name : "Max 128 characters"} + </label> + </div> + <div className="col-lg-1 col-md-1 col-sm-12"></div> + <label htmlFor="description" className="col-lg-2 col-md-2 col-sm-12">Description <span style={{color:'red'}}>*</span></label> + <div className="col-lg-3 col-md-3 col-sm-12"> + <InputTextarea className={this.state.errors.description ?'input-error':''} rows={3} cols={30} + tooltip="Short description of the project" tooltipOptions={this.tooltipOptions} maxLength="128" + data-testid="description" value={this.state.project.description} + onChange={(e) => this.setProjectParams('description', e.target.value)} + onBlur={(e) => this.setProjectParams('description', e.target.value)}/> + <label className={this.state.errors.description ?"error":"info"}> + {this.state.errors.description ? this.state.errors.description : "Max 255 characters"} + </label> + </div> </div> - </div> - <div className="p-field p-grid"> - <label htmlFor="triggerPriority" className="col-lg-2 col-md-2 col-sm-12">Cycle(s)</label> - <div className="col-lg-3 col-md-3 col-sm-12"> - <MultiSelect data-testid="cycle" id="cycle" optionLabel="name" optionValue="url" filter={true} - tooltip="Cycle(s) to which this project belongs" tooltipOptions={this.tooltipOptions} - value={this.state.project.cycles} - options={this.state.cycles} - onChange={(e) => {this.setProjectParams('cycles',e.value)}} - - /> + <div className="p-field p-grid"> + <label htmlFor="triggerPriority" className="col-lg-2 col-md-2 col-sm-12">Trigger Priority </label> + <div className="col-lg-3 col-md-3 col-sm-12"> + <InputNumber inputId="trig_prio" name="trig_prio" className={this.state.errors.name ?'input-error':''} + tooltip="Priority of this project with respect to triggers" tooltipOptions={this.tooltipOptions} + value={this.state.project.trigger_priority} showButtons + min={0} max={1001} step={10} useGrouping={false} + onChange={(e) => this.setProjectParams('trigger_priority', e.value)} + onBlur={(e) => this.setProjectParams('trigger_priority', e.target.value, 'NUMBER')} /> + <label className="error"> + {this.state.errors.trigger_priority ? this.state.errors.trigger_priority : ""} + </label> + </div> + <div className="col-lg-1 col-md-1 col-sm-12"></div> + <label htmlFor="trigger" className="col-lg-2 col-md-2 col-sm-12">Allows Trigger Submission</label> + <div className="col-lg-3 col-md-3 col-sm-12"> + <Checkbox inputId="trigger" role="trigger" + tooltip="Is this project allowed to supply observation requests on the fly, possibly interrupting currently running observations (responsive telescope)?" + tooltipOptions={this.tooltipOptions} + checked={this.state.project.can_trigger} onChange={e => this.setProjectParams('can_trigger', e.target.checked)}></Checkbox> + </div> </div> - <div className="col-lg-1 col-md-1 col-sm-12"></div> - <label htmlFor="projRank" className="col-lg-2 col-md-2 col-sm-12">Project Rank <span style={{color:'red'}}>*</span></label> - <div className="col-lg-3 col-md-3 col-sm-12"> - <input type="number" - data-for="reacttooltip" - data-iscapture="true" - data-tip="Priority of this project w.r.t. other projects. Projects can interrupt observations of lower-priority projects. Min-0.00, Max-6.00" - inputId="proj-rank" name="rank" data-testid="rank" - className="p-inputtext p-component" - value={this.state.project.priority_rank} - step="0.01" - onChange={(e)=> this.setProjectParams('priority_rank', e.target.value, "DECIMAL")}/> - <label className="error"> - {this.state.errors.priority_rank ? this.state.errors.priority_rank : ""} - </label> + <div className="p-field p-grid"> + <label htmlFor="projCategory" className="col-lg-2 col-md-2 col-sm-12">Project Category </label> + <div className="col-lg-3 col-md-3 col-sm-12"> + <Dropdown inputId="projCat" optionLabel="value" optionValue="url" + tooltip="Project Category" tooltipOptions={this.tooltipOptions} + value={this.state.project.project_category} + options={this.state.projectCategories} + onChange={(e) => {this.setProjectParams('project_category', e.value)}} + placeholder="Select Project Category" /> + </div> + <div className="col-lg-1 col-md-1 col-sm-12"></div> + <label htmlFor="periodCategory" className="col-lg-2 col-md-2 col-sm-12">Period Category</label> + <div className="col-lg-3 col-md-3 col-sm-12"> + <Dropdown data-testid="period-cat" id="period-cat" optionLabel="value" optionValue="url" + tooltip="Period Category" tooltipOptions={this.tooltipOptions} + value={this.state.project.period_category} + options={this.state.periodCategories} + onChange={(e) => {this.setProjectParams('period_category',e.value)}} + placeholder="Select Period Category" /> + </div> </div> - </div> - <div className="p-field p-grid"> - <label htmlFor="ltaStorage" className="col-lg-2 col-md-2 col-sm-12">LTA Storage Location</label> - <div className="col-lg-3 col-md-3 col-sm-12" > - <Dropdown inputId="ltaStore" optionValue="url" - tooltip="LTA Storage" tooltipOptions={this.tooltipOptions} - value={this.state.project.archive_location} - options={this.state.ltaStorage} - onChange={(e) => {this.setProjectParams('archive_location', e.value)}} - placeholder="Select LTA Storage" /> + <div className="p-field p-grid"> + <label htmlFor="triggerPriority" className="col-lg-2 col-md-2 col-sm-12">Cycle(s)</label> + <div className="col-lg-3 col-md-3 col-sm-12"> + <MultiSelect data-testid="cycle" id="cycle" optionLabel="name" optionValue="url" filter={true} + tooltip="Cycle(s) to which this project belongs" tooltipOptions={this.tooltipOptions} + value={this.state.project.cycles} + options={this.state.cycles} + onChange={(e) => {this.setProjectParams('cycles',e.value)}} + + /> </div> - - <div className="col-lg-1 col-md-1 col-sm-12"></div> - <label htmlFor="ltastoragepath" className="col-lg-2 col-md-2 col-sm-12">LTA Storage Path <span style={{color:'red'}}>*</span> </label> + <div className="col-lg-1 col-md-1 col-sm-12"></div> + <label htmlFor="projRank" className="col-lg-2 col-md-2 col-sm-12">Project Rank <span style={{color:'red'}}>*</span></label> <div className="col-lg-3 col-md-3 col-sm-12"> - <InputText className={this.state.errors.archive_subdirectory ?'input-error':''} id="StoragePath" data-testid="name" - tooltip="Enter storage relative path" tooltipOptions={this.tooltipOptions} maxLength="1024" - value={this.state.project.archive_subdirectory} - onChange={(e) => this.setProjectParams('archive_subdirectory', e.target.value)} - onBlur={(e) => this.setProjectParams('archive_subdirectory', e.target.value,'SUB-DIRECTORY')}/> - <label className={this.state.errors.archive_subdirectory?"error":"info"}> - {this.state.errors.archive_subdirectory? this.state.errors.archive_subdirectory : "Max 1024 characters"} + <input type="number" + data-for="reacttooltip" + data-iscapture="true" + data-tip="Priority of this project w.r.t. other projects. Projects can interrupt observations of lower-priority projects. Min-0.00, Max-6.00" + inputId="proj-rank" name="rank" data-testid="rank" + className="p-inputtext p-component" + value={this.state.project.priority_rank} + step="0.01" + onChange={(e)=> this.setProjectParams('priority_rank', e.target.value, "DECIMAL")}/> + <label className="error"> + {this.state.errors.priority_rank ? this.state.errors.priority_rank : ""} </label> - </div> - <div className="col-lg-1 col-md-1 col-sm-12"></div> - <label htmlFor="preventdeletionafteringest" className="col-lg-2 col-md-2 col-sm-12">Prevent Automatic Deletion After Ingest</label> - <div className="col-lg-3 col-md-3 col-sm-12" data-testid="preventdeletionafteringest"> - <Checkbox inputId="preventdeletionafteringest" role="preventdeletionafteringest" - tooltip="Prevent automatic deletion after ingest" - tooltipOptions={this.tooltipOptions} - checked={this.state.project.auto_pin} onChange={e => this.setProjectParams('auto_pin', e.target.checked)}></Checkbox> - </div> - </div> - {this.state.resourceList && - <div className="p-fluid"> - <div className="p-field p-grid"> - <div className="col-lg-2 col-md-2 col-sm-12"> - <h5>Resource Allocations:</h5> - </div> - <div className="col-lg-3 col-md-3 col-sm-10"> - <Dropdown optionLabel="name" optionValue="name" - tooltip="Resources to be allotted for the project" - tooltipOptions={this.tooltipOptions} - value={this.state.newResource} - options={_.sortBy(this.state.resourceList, ['name'])} - onChange={(e) => {this.setState({'newResource': e.value})}} - placeholder="Add Resources" /> - </div> - <div className="col-lg-2 col-md-2 col-sm-2"> - <Button label="" className="p-button-primary" icon="pi pi-plus" onClick={this.addNewResource} disabled={!this.state.newResource} data-testid="add_res_btn" /> - </div> </div> - {/* {_.keys(this.state.projectQuota).length>0 && */} - <div className="p-field p-grid resource-input-grid"> - <ResourceInputList list={this.state.resources} unitMap={this.resourceUnitMap} - projectQuota={this.state.projectQuota} callback={this.setProjectQuotaParams} - removeInputCallback={this.removeResource} /> + </div> + <div className="p-field p-grid"> + <label htmlFor="ltaStorage" className="col-lg-2 col-md-2 col-sm-12">LTA Storage Location</label> + <div className="col-lg-3 col-md-3 col-sm-12" > + <Dropdown inputId="ltaStore" optionValue="url" + tooltip="LTA Storage" tooltipOptions={this.tooltipOptions} + value={this.state.project.archive_location} + options={this.state.ltaStorage} + onChange={(e) => {this.setProjectParams('archive_location', e.value)}} + placeholder="Select LTA Storage" /> </div> - {/* } */} + + <div className="col-lg-1 col-md-1 col-sm-12"></div> + <label htmlFor="ltastoragepath" className="col-lg-2 col-md-2 col-sm-12">LTA Storage Path <span style={{color:'red'}}>*</span> </label> + <div className="col-lg-3 col-md-3 col-sm-12"> + <InputText className={this.state.errors.archive_subdirectory ?'input-error':''} id="StoragePath" data-testid="name" + tooltip="Enter storage relative path" tooltipOptions={this.tooltipOptions} maxLength="1024" + value={this.state.project.archive_subdirectory} + onChange={(e) => this.setProjectParams('archive_subdirectory', e.target.value)} + onBlur={(e) => this.setProjectParams('archive_subdirectory', e.target.value,'SUB-DIRECTORY')}/> + <label className={this.state.errors.archive_subdirectory?"error":"info"}> + {this.state.errors.archive_subdirectory? this.state.errors.archive_subdirectory : "Max 1024 characters"} + </label> + </div> + <div className="col-lg-1 col-md-1 col-sm-12"></div> + <label htmlFor="preventdeletionafteringest" className="col-lg-2 col-md-2 col-sm-12">Prevent Automatic Deletion After Ingest</label> + <div className="col-lg-3 col-md-3 col-sm-12" data-testid="preventdeletionafteringest"> + <Checkbox inputId="preventdeletionafteringest" role="preventdeletionafteringest" + tooltip="Prevent automatic deletion after ingest" + tooltipOptions={this.tooltipOptions} + checked={this.state.project.auto_pin} onChange={e => this.setProjectParams('auto_pin', e.target.checked)}></Checkbox> + </div> </div> - } - <ReactTooltip id="reacttooltip" place={'left'} type={'dark'} effect={'solid'} multiline={true} /> - </div> - </div> - <div className="p-grid p-justify-start act-btn-grp"> - <div className="p-col-1"> - <Button label="Save" className="p-button-primary" id="save-btn" data-testid="save-btn" icon="pi pi-check" onClick={this.saveProject} disabled={!this.state.validForm} /> + {this.state.resourceList && + <div className="p-fluid"> + <div className="p-field p-grid"> + <div className="col-lg-2 col-md-2 col-sm-12"> + <h5>Resource Allocations:</h5> + </div> + <div className="col-lg-3 col-md-3 col-sm-10"> + <Dropdown optionLabel="name" optionValue="name" + tooltip="Resources to be allotted for the project" + tooltipOptions={this.tooltipOptions} + value={this.state.newResource} + options={_.sortBy(this.state.resourceList, ['name'])} + onChange={(e) => {this.setState({'newResource': e.value})}} + placeholder="Add Resources" /> + </div> + <div className="col-lg-2 col-md-2 col-sm-2"> + <Button label="" className="p-button-primary" icon="pi pi-plus" onClick={this.addNewResource} disabled={!this.state.newResource} data-testid="add_res_btn" /> + </div> + </div> + {/* {_.keys(this.state.projectQuota).length>0 && */} + <div className="p-field p-grid resource-input-grid"> + <ResourceInputList list={this.state.resources} unitMap={this.resourceUnitMap} + projectQuota={this.state.projectQuota} callback={this.setProjectQuotaParams} + removeInputCallback={this.removeResource} /> + </div> + {/* } */} + </div> + } + <ReactTooltip id="reacttooltip" place={'left'} type={'dark'} effect={'solid'} multiline={true} /> + </div> </div> - <div className="p-col-1"> - <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.checkIsDirty} /> + <div className="p-grid p-justify-start act-btn-grp"> + <div className="p-col-1"> + <Button label="Save" className="p-button-primary" id="save-btn" data-testid="save-btn" icon="pi pi-check" onClick={this.saveProject} disabled={!this.state.validForm} /> + </div> + <div className="p-col-1"> + <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.checkIsDirty} /> + </div> </div> - </div> - </> - } - {/* Dialog component to show messages and get input */} - <div className="p-grid" data-testid="confirm_dialog"> - <Dialog header={this.state.dialog.header} visible={this.state.dialogVisible} style={{width: '30vw'}} inputId="confirm_dialog" - modal={true} onHide={() => {this.setState({dialogVisible: false})}} - footer={<div> - <Button key="back" onClick={() => {this.setState({dialogVisible: false}); this.cancelEdit();}} label="Ok" /> - {/* <Button key="submit" type="primary" onClick={this.reset} label="Yes" /> */} - </div> - } > - <div className="p-grid"> - <div className="col-lg-2 col-md-2 col-sm-2"> - <i className="pi pi-check-circle pi-large pi-success"></i> - </div> - <div className="col-lg-10 col-md-10 col-sm-10"> - <span style={{marginTop:"5px"}}>{this.state.dialog.detail}</span> + </> + } + {/* Dialog component to show messages and get input */} + <div className="p-grid" data-testid="confirm_dialog"> + <Dialog header={this.state.dialog.header} visible={this.state.dialogVisible} style={{width: '30vw'}} inputId="confirm_dialog" + modal={true} onHide={() => {this.setState({dialogVisible: false})}} + footer={<div> + <Button key="back" onClick={() => {this.setState({dialogVisible: false}); this.cancelEdit();}} label="Ok" /> + {/* <Button key="submit" type="primary" onClick={this.reset} label="Yes" /> */} + </div> + } > + <div className="p-grid"> + <div className="col-lg-2 col-md-2 col-sm-2"> + <i className="pi pi-check-circle pi-large pi-success"></i> + </div> + <div className="col-lg-10 col-md-10 col-sm-10"> + <span style={{marginTop:"5px"}}>{this.state.dialog.detail}</span> + </div> </div> - </div> - </Dialog> + </Dialog> - <CustomDialog type="confirmation" visible={this.state.showDialog} width="40vw" - header={'Edit Project'} message={'Do you want to leave this page? Your changes may not be saved.'} - content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelEdit}> - </CustomDialog> - </div> + <CustomDialog type="confirmation" visible={this.state.showDialog} width="40vw" + header={'Edit Project'} message={'Do you want to leave this page? Your changes may not be saved.'} + content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelEdit}> + </CustomDialog> + </div> + </>: <AccessDenied/>} </React.Fragment> ); } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/list.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/list.js index 440c1e3d367..06af4170bad 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/list.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/list.js @@ -210,41 +210,35 @@ export class ProjectList extends Component { </Link> </div> </div> */} - {project && + { (this.props.cycle) ? <> - {project.list? - <> - { (this.props.cycle) ? - <> - </> - : - <PageHeader location={this.props.location} title={'Project - List'} - actions={[{ icon: 'fa-plus-square', title: this.state.userrole && this.state.userrole.userRolePermission.project && this.state.userrole.userRolePermission.project.create?'Click to Add Project':"Don't have permission", - disabled: this.state.userrole && this.state.userrole.userRolePermission.project?!this.state.userrole.userRolePermission.project.create:true, props: { pathname: '/project/create' } }]} - /> - } - {this.state.isLoading ? <AppLoader /> : (this.state.isprocessed && this.state.projectlist.length > 0) ? - <ViewTable - data={this.state.projectlist} - defaultcolumns={this.state.defaultcolumns} - optionalcolumns={this.state.optionalcolumns} - columnclassname={this.state.columnclassname} - columnOrders={this.state.columnOrder} - defaultSortColumn={this.defaultSortColumn} - showaction={true} - paths={this.state.paths} - keyaccessor="name" - unittest={this.state.unittest} - tablename={this.lsTableName} - toggleBySorting={(sortData) => this.toggleBySorting(sortData)} - lsKeySortColumn={this.lsKeySortColumn} - pageUpdated={this.pageUpdated} - storeFilter={false} - /> - : <div>No project found </div> - } - </>: <AccessDenied/>} - </>} + </> + : + <PageHeader location={this.props.location} title={'Project - List'} + actions={[{ icon: 'fa-plus-square', title: this.state.userrole && this.state.userrole.userRolePermission.project && this.state.userrole.userRolePermission.project.create?'Click to Add Project':"Don't have permission", + disabled: this.state.userrole && this.state.userrole.userRolePermission.project?!this.state.userrole.userRolePermission.project.create:true, props: { pathname: '/project/create' } }]} + /> + } + {this.state.isLoading ? <AppLoader /> : (this.state.isprocessed && this.state.projectlist.length > 0) ? + <ViewTable + data={this.state.projectlist} + defaultcolumns={this.state.defaultcolumns} + optionalcolumns={this.state.optionalcolumns} + columnclassname={this.state.columnclassname} + columnOrders={this.state.columnOrder} + defaultSortColumn={this.defaultSortColumn} + showaction={true} + paths={this.state.paths} + keyaccessor="name" + unittest={this.state.unittest} + tablename={this.lsTableName} + toggleBySorting={(sortData) => this.toggleBySorting(sortData)} + lsKeySortColumn={this.lsKeySortColumn} + pageUpdated={this.pageUpdated} + storeFilter={false} + /> + : <div>No project found </div> + } </> ) diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/view.js index fe20eb5f2a0..dd1959d1fea 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/view.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/view.js @@ -64,7 +64,7 @@ export class ProjectView extends Component { async getProjectDetails() { const permission = await AuthUtil.getUserRolePermission(); - const permissionById = await AuthUtil.getUserRoleById('project', this.state.projectId) + const permissionById = await AuthUtil.getUserPermissionByModuleId('project', this.state.projectId) this.setState({userrole: permission, permissionById: permissionById}); let project = await ProjectService.getProjectDetails(this.state.projectId); let projectQuota = []; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js index ea5c0dd490e..228c20fc073 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js @@ -1445,13 +1445,13 @@ class SchedulingUnitList extends Component{ {this.state.suType === 'Blueprint' && <> <a href="#" style={{marginLeft: "5px"}} onClick={this.cleanUpSUTask} - title={this.state.userrole && this.state.userrole.userRolePermission.scheduleunit && this.state.userrole.userRolePermission.scheduleunit.cleanuptask ? "Create Clean-up Task(s)":this.access_dined_message} > - <i class={this.state.userrole && this.state.userrole.userRolePermission.scheduleunit && this.state.userrole.userRolePermission.scheduleunit.cleanuptask?"fa fa-recycle":"fa fa-disabled fa-recycle"} + title={this.state.userrole && this.state.userrole.userRolePermission.task_blueprint && this.state.userrole.userRolePermission.task_blueprint.canceltask ? "Create Clean-up Task(s)":this.access_dined_message} > + <i class={this.state.userrole && this.state.userrole.userRolePermission.task_blueprint && this.state.userrole.userRolePermission.task_blueprint.canceltask?"fa fa-recycle":"fa fa-disabled fa-recycle"} aria-hidden="true" ></i> </a> <a href="#" style={{marginLeft: "5px"}} onClick={this.confirmCancelSchedulingUnit} - title={this.state.userrole && this.state.userrole.userRolePermission.scheduleunit && this.state.userrole.userRolePermission.scheduleunit.cancelsu ? "Cancel selected Scheduling Unit(s)":this.access_dined_message} > - <i class={this.state.userrole && this.state.userrole.userRolePermission.scheduleunit && this.state.userrole.userRolePermission.scheduleunit.cancelsu?"fa fa-ban":"fa fa-disabled fa-ban"} + title={this.state.userrole && this.state.userrole.userRolePermission.scheduleunit_blueprint && this.state.userrole.userRolePermission.scheduleunit_blueprint.edit ? "Cancel selected Scheduling Unit(s)":this.access_dined_message} > + <i class={this.state.userrole && this.state.userrole.userRolePermission.scheduleunit_blueprint && this.state.userrole.userRolePermission.scheduleunit_blueprint.edit?"fa fa-ban":"fa fa-disabled fa-ban"} aria-hidden="true" ></i> </a> </> 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 40675be6640..b011f9bdfc1 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js @@ -222,7 +222,7 @@ class ViewSchedulingUnit extends Component { this.setToggleBySorting(); let schedule_id = this.props.match.params.id; let schedule_type = this.props.match.params.type; - const permissionById = await AuthUtil.getUserRoleById('scheduling_unit_draft', schedule_id) + const permissionById = await AuthUtil.getUserPermissionByModuleId('scheduling_unit_draft', schedule_id) this.setState({userrole: permission, permissionById: permissionById, schedule_id: schedule_id}); if (schedule_type && schedule_id) { this.stations = await ScheduleService.getStationGroup(); @@ -376,7 +376,7 @@ class ViewSchedulingUnit extends Component { }); this.actions.unshift({ icon: 'fa-stamp', - title: this.state.userrole && this.state.userrole.userRolePermission.scheduleunit && this.state.userrole.userRolePermission.scheduleunit.createsub?blueprintExist?'Blueprint already exists': 'Create Blueprint':this.access_dined_message, + title: this.state.userrole && this.state.userrole.userRolePermission.scheduleunit && this.state.userrole.userRolePermission.scheduleunit.createsub?blueprintExist?'Blueprint already exists': 'Create Blueprint':this.access_dined_message, type: 'button', disabled: this.state.userrole && this.state.userrole.userRolePermission.scheduleunit?!this.state.userrole.userRolePermission.scheduleunit.createsub:true, actOn: 'click', disabled: blueprintExist, props: { callback: this.checkAndCreateBlueprint }, @@ -399,15 +399,15 @@ class ViewSchedulingUnit extends Component { } this.actions.unshift({ icon: 'fa-ban', type: 'button', actOn: 'click', - title: this.state.userrole && this.state.userrole.userRolePermission.scheduleunit && this.state.userrole.userRolePermission.scheduleunit.cancelsu?this.SU_END_STATUSES.indexOf(this.state.scheduleunit.status.toLowerCase())>=0?'Cannot Cancel Scheduling Unit':'Cancel Scheduling Unit':this.access_dined_message, - disabled:this.state.userrole && this.state.userrole.userRolePermission.scheduleunit && this.state.userrole.userRolePermission.scheduleunit.cancelsu? this.SU_END_STATUSES.indexOf(this.state.scheduleunit.status.toLowerCase())>=0 ? true : false:true, + title: this.state.userrole && this.state.userrole.userRolePermission.scheduleunit_blueprint && this.state.userrole.userRolePermission.scheduleunit_blueprint.edit?this.SU_END_STATUSES.indexOf(this.state.scheduleunit.status.toLowerCase())>=0?'Cannot Cancel Scheduling Unit':'Cancel Scheduling Unit':this.access_dined_message, + disabled:this.state.userrole && this.state.userrole.userRolePermission.scheduleunit_blueprint && this.state.userrole.userRolePermission.scheduleunit_blueprint.edit? this.SU_END_STATUSES.indexOf(this.state.scheduleunit.status.toLowerCase())>=0 ? true : false:true, props: { callback: this.showCancelSUConfirmation } }); this.actions.unshift({ icon: 'fa fa-recycle', - title: this.state.userrole && this.state.userrole.userRolePermission.scheduleunit && this.state.userrole.userRolePermission.scheduleunit.cleanuptask?'Create Clean-up Task':this.access_dined_message, + title: this.state.userrole && this.state.userrole.userRolePermission.scheduleunit_blueprint && this.state.userrole.userRolePermission.scheduleunit_blueprint.add?'Create Clean-up Task':this.access_dined_message, type: 'button', - disabled: this.state.userrole && this.state.userrole.userRolePermission.scheduleunit?!this.state.userrole.userRolePermission.scheduleunit.cleanuptask:true, + disabled: this.state.userrole && this.state.userrole.userRolePermission.scheduleunit_blueprint?!this.state.userrole.userRolePermission.scheduleunit_blueprint.add:true, actOn: 'click', props: { callback: this.cleanUpSUTask } }); this.actions.unshift({ @@ -1372,12 +1372,28 @@ class ViewSchedulingUnit extends Component { <span className="p-float-label"> {this.state.schedulingUnitTasks && this.state.schedulingUnitTasks.length > 0 && <> - <a href="#" onClick={this.confirmCancelTasks} title="Cancel selected Task(s)"> - <i class="fa fa-ban" aria-hidden="true" ></i> - </a> - <a href="#" style={{ pointerEvents: this.props.disabled ? 'none' : 'auto' }}onClick={this.confirmDeleteTasks} title="Delete selected Task(s)" > - <i class={this.state.userrole.userRolePermission.task.delete?"fa fa-trash":"fa fa-trash fa-disabled"} aria-hidden="true" ></i> - </a> + {this.props.match.params.type === 'draft' && + <> + <a href="#" onClick={this.confirmCancelTasks} + title={this.state.userrole.userRolePermission.task_draft.canceltask?"Cancel selected Task(s)": this.access_dined_message}> + <i class={this.state.userrole.userRolePermission.task_draft.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} + title={this.state.userrole.userRolePermission.task.delete?"Delete selected Task(s)": this.access_dined_message} > + <i class={this.state.userrole.userRolePermission.task.delete?"fa fa-trash":"fa fa-trash fa-disabled"} aria-hidden="true" ></i> + </a> + </> + } + {this.props.match.params.type === 'blueprint' && + <> + <a href="#" onClick={this.confirmCancelTasks} + title={this.state.userrole.userRolePermission.task_blueprint.canceltask?"Cancel selected Task(s)": this.access_dined_message}> + <i class={this.state.userrole.userRolePermission.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} + title={this.state.userrole.userRolePermission.task.delete?"Delete selected Task(s)": this.access_dined_message} > + <i class={this.state.userrole.userRolePermission.task.delete?"fa fa-trash":"fa fa-trash fa-disabled"} aria-hidden="true" ></i></a> + </> + } </> } </span> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js index 346f9f6e263..c4ac8cc7927 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js @@ -28,6 +28,7 @@ import { CustomDialog } from '../../layout/components/CustomDialog'; import SchedulingSet from './schedulingset.create'; import UtilService from '../../services/util.service'; import ReactTooltip from "react-tooltip"; +import AuthUtil from '../../utils/auth.util'; /** * Component to create a new SchedulingUnit from Observation strategy template @@ -36,6 +37,7 @@ export class SchedulingUnitCreate extends Component { constructor(props) { super(props); this.state = { + userrole: {}, selectedProject: {}, showAddSet: false, showDialog: false, @@ -99,7 +101,9 @@ export class SchedulingUnitCreate extends Component { this.setSUSet = this.setSUSet.bind(this); } - componentDidMount() { + async componentDidMount() { + const permission = await AuthUtil.getUserPermissionByModule('scheduleunit_draft'); + this.setState({userrole: permission}); const promises = [ ProjectService.getProjectList(), ScheduleService.getSchedulingSets(), ScheduleService.getObservationStrategies(), @@ -555,6 +559,7 @@ export class SchedulingUnitCreate extends Component { return <Redirect to={ {pathname: this.state.redirect} }></Redirect> } const schema = this.state.paramsSchema; + const {scheduleunit_draft} = this.state.userrole; let jeditor = null; if (schema) { @@ -634,7 +639,7 @@ export class SchedulingUnitCreate extends Component { onClick={() => {this.setState({showAddSet: true})}} tooltip="Add new Scheduling Set" style={{marginLeft: '-10px'}} - disabled={this.state.schedulingUnit.project !== null ? false : true }/> + disabled={this.state.schedulingUnit.project !== null && scheduleunit_draft.scheduling_set? false : true }/> </div> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js index 32a06b065c2..fbc40a92f10 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js @@ -27,6 +27,8 @@ import ParserUtility from '../../utils/parser.utility'; import SchedulingConstraint from './Scheduling.Constraints'; import UtilService from '../../services/util.service'; import ReactTooltip from "react-tooltip"; +import AuthUtil from '../../utils/auth.util'; +import AccessDenied from '../../layout/components/AccessDenied'; /** * Compoenent to edit scheduling unit draft @@ -52,7 +54,8 @@ export class EditSchedulingUnit extends Component { observStrategyVisible: false, missingStationFieldsErrors: [], // Validation for max no.of missing station stationGroup: [], - customSelectedStations: [] // Custom Stations + customSelectedStations: [], + permissionById: {} // Custom Stations } this.priorityQueueTypes = []; this.projects = []; // All projects to load project dropdown @@ -151,7 +154,7 @@ export class EditSchedulingUnit extends Component { } } - componentDidMount() { + async componentDidMount() { const promises = [ ProjectService.getProjectList(), ScheduleService.getSchedulingSets(), ScheduleService.getObservationStrategies(), @@ -162,6 +165,8 @@ export class EditSchedulingUnit extends Component { ScheduleService.getStationGroup(), UtilService.getPriorityQueueType() ]; + const permissionById = await AuthUtil.getUserPermissionByModuleId('scheduling_unit_draft', this.props.match.params.id) + this.setState({permissionById: permissionById}) Promise.all(promises).then(responses => { this.projects = responses[0]; this.schedulingSets = responses[1]; @@ -486,175 +491,177 @@ export class EditSchedulingUnit extends Component { return ( <React.Fragment> - <Growl ref={el => (this.growl = el)} /> - <PageHeader location={this.props.location} title={'Scheduling Unit - Edit'} - actions={[{icon: 'fa-window-close', title:'Click to Close Scheduling Unit View', - type: 'button', actOn: 'click', props:{ callback: this.checkIsDirty }}]}/> - { this.state.isLoading ? <AppLoader /> : + {this.state.permissionById[this.props.match.params.id] && this.state.permissionById[this.props.match.params.id].edit? <> - <div> - <div className="p-fluid"> - <div className="p-field p-grid"> - <label htmlFor="schedUnitName" className="col-lg-2 col-md-2 col-sm-12">Name <span style={{color:'red'}}>*</span></label> - <div className="col-lg-3 col-md-3 col-sm-12"> - <InputText className={this.state.errors.name ?'input-error':''} id="schedUnitName" data-testid="name" - tooltip="Enter name of the Scheduling Unit" tooltipOptions={this.tooltipOptions} maxLength="128" - ref={input => {this.nameInput = input;}} - value={this.state.schedulingUnit.name} autoFocus - onChange={(e) => this.setSchedUnitParams('name', e.target.value)} - onBlur={(e) => this.setSchedUnitParams('name', e.target.value)}/> - <label className={this.state.errors.name?"error":"info"}> - {this.state.errors.name ? this.state.errors.name : "Max 128 characters"} - </label> + <Growl ref={el => (this.growl = el)} /> + <PageHeader location={this.props.location} title={'Scheduling Unit - Edit'} + actions={[{icon: 'fa-window-close', title:'Click to Close Scheduling Unit View', + type: 'button', actOn: 'click', props:{ callback: this.checkIsDirty }}]}/> + { this.state.isLoading ? <AppLoader /> : + <> + <div> + <div className="p-fluid"> + <div className="p-field p-grid"> + <label htmlFor="schedUnitName" className="col-lg-2 col-md-2 col-sm-12">Name <span style={{color:'red'}}>*</span></label> + <div className="col-lg-3 col-md-3 col-sm-12"> + <InputText className={this.state.errors.name ?'input-error':''} id="schedUnitName" data-testid="name" + tooltip="Enter name of the Scheduling Unit" tooltipOptions={this.tooltipOptions} maxLength="128" + ref={input => {this.nameInput = input;}} + value={this.state.schedulingUnit.name} autoFocus + onChange={(e) => this.setSchedUnitParams('name', e.target.value)} + onBlur={(e) => this.setSchedUnitParams('name', e.target.value)}/> + <label className={this.state.errors.name?"error":"info"}> + {this.state.errors.name ? this.state.errors.name : "Max 128 characters"} + </label> + </div> + <div className="col-lg-1 col-md-1 col-sm-12"></div> + <label htmlFor="description" className="col-lg-2 col-md-2 col-sm-12">Description <span style={{color:'red'}}>*</span></label> + <div className="col-lg-3 col-md-3 col-sm-12"> + <InputTextarea className={this.state.errors.description ?'input-error':''} rows={3} cols={30} + tooltip="Longer description of the scheduling unit" tooltipOptions={this.tooltipOptions} maxLength="128" + data-testid="description" value={this.state.schedulingUnit.description} + onChange={(e) => this.setSchedUnitParams('description', e.target.value)} + onBlur={(e) => this.setSchedUnitParams('description', e.target.value)}/> + <label className={this.state.errors.description ?"error":"info"}> + {this.state.errors.description ? this.state.errors.description : "Max 255 characters"} + </label> + </div> </div> - <div className="col-lg-1 col-md-1 col-sm-12"></div> - <label htmlFor="description" className="col-lg-2 col-md-2 col-sm-12">Description <span style={{color:'red'}}>*</span></label> - <div className="col-lg-3 col-md-3 col-sm-12"> - <InputTextarea className={this.state.errors.description ?'input-error':''} rows={3} cols={30} - tooltip="Longer description of the scheduling unit" tooltipOptions={this.tooltipOptions} maxLength="128" - data-testid="description" value={this.state.schedulingUnit.description} - onChange={(e) => this.setSchedUnitParams('description', e.target.value)} - onBlur={(e) => this.setSchedUnitParams('description', e.target.value)}/> - <label className={this.state.errors.description ?"error":"info"}> - {this.state.errors.description ? this.state.errors.description : "Max 255 characters"} - </label> + <div className="p-field p-grid"> + <label htmlFor="project" className="col-lg-2 col-md-2 col-sm-12">Project </label> + <div className="col-lg-3 col-md-3 col-sm-12" data-testid="project" > + <Dropdown inputId="project" optionLabel="name" optionValue="name" + tooltip="Project" tooltipOptions={this.tooltipOptions} + value={this.state.schedulingUnit.project} disabled={this.state.schedulingUnit.project?true:false} + options={this.projects} + placeholder="Select Project" /> + </div> + <div className="col-lg-1 col-md-1 col-sm-12"></div> + <label htmlFor="schedSet" className="col-lg-2 col-md-2 col-sm-12">Scheduling Set </label> + <div className="col-lg-3 col-md-3 col-sm-12"> + <Dropdown data-testid="schedSet" id="schedSet" optionLabel="name" optionValue="id" + tooltip="Scheduling set of the project" tooltipOptions={this.tooltipOptions} + value={this.state.schedulingUnit.scheduling_set_id} + options={this.state.schedulingSets} + disabled={this.state.schedulingUnit.scheduling_set_id?true:false} + placeholder="Select Scheduling Set" /> + </div> </div> - </div> - <div className="p-field p-grid"> - <label htmlFor="project" className="col-lg-2 col-md-2 col-sm-12">Project </label> - <div className="col-lg-3 col-md-3 col-sm-12" data-testid="project" > - <Dropdown inputId="project" optionLabel="name" optionValue="name" - tooltip="Project" tooltipOptions={this.tooltipOptions} - value={this.state.schedulingUnit.project} disabled={this.state.schedulingUnit.project?true:false} - options={this.projects} - placeholder="Select Project" /> + <div className="p-field p-grid"> + <label htmlFor="priorityRank" className="col-lg-2 col-md-2 col-sm-12">Priority Rank</label> + <div className="col-lg-3 col-md-3 col-sm-12" data-testid="priority_rank" > + <input type="number" + data-for="reacttooltip" + data-iscapture="true" + data-tip="Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project.. Min-0.0000, Max-1.0000" + inputId="proj-rank" name="rank" data-testid="rank" + className="p-inputtext p-component p-filled" + value={this.state.schedulingUnit.priority_rank} + step="0.0001" + onChange={(e)=> this.setSchedUnitParams('priority_rank', e.target.value)}/> + <label className="error"> + {this.state.errors.priority_rank ? this.state.errors.priority_rank : ""} + </label> + </div> + <div className="col-lg-1 col-md-1 col-sm-12"></div> + <label htmlFor="priority_queue" className="col-lg-2 col-md-2 col-sm-12">Priority Queue</label> + <div className="col-lg-3 col-md-3 col-sm-10"> + <Dropdown data-testid="priority_queue" id="priority_queue" optionLabel="value" optionValue="url" + tooltip="Priority Queue of the Scheduling Unit" tooltipOptions={this.tooltipOptions} + value={this.state.schedulingUnit.priority_queue} + options={this.priorityQueueTypes} + onChange={(e) => {this.setSchedUnitParams('priority_queue',e.value)}} + placeholder="Select Priority Queue" /> + <label className={(this.state.errors.priority_queue && this.state.touched.priority_queue) ?"error":"info"}> + {(this.state.errors.priority_queue && this.state.touched.priority_queue) ? this.state.errors.priority_queue : ""} + </label> + </div> </div> - <div className="col-lg-1 col-md-1 col-sm-12"></div> - <label htmlFor="schedSet" className="col-lg-2 col-md-2 col-sm-12">Scheduling Set </label> - <div className="col-lg-3 col-md-3 col-sm-12"> - <Dropdown data-testid="schedSet" id="schedSet" optionLabel="name" optionValue="id" - tooltip="Scheduling set of the project" tooltipOptions={this.tooltipOptions} - value={this.state.schedulingUnit.scheduling_set_id} - options={this.state.schedulingSets} - disabled={this.state.schedulingUnit.scheduling_set_id?true:false} - placeholder="Select Scheduling Set" /> + <div className="p-field p-grid"> + { this.state.observStrategyVisible && + <> + <label htmlFor="observStrategy" className="col-lg-2 col-md-2 col-sm-12">Observation Strategy </label> + <div className="col-lg-3 col-md-3 col-sm-12" data-testid="observStrategy" > + <Dropdown inputId="observStrategy" optionLabel="name" optionValue="id" + tooltip="Observation Strategy Template to be used to create the Scheduling Unit and Tasks" tooltipOptions={this.tooltipOptions} + value={this.state.schedulingUnit.observation_strategy_template_id} + disabled={this.state.schedulingUnit.observation_strategy_template_id?true:false} + options={this.observStrategies} + onChange={(e) => {this.changeStrategy(e)}} + placeholder="Select Strategy" /> + <label className="info"> + {this.state.observStrategy? this.state.observStrategy.description : "Select Observation Strategy"} + </label> + </div> + </> + } + <div className="col-lg-1 col-md-1 col-sm-12"></div> + <label htmlFor="project" className="col-lg-2 col-md-2 col-sm-12">Prevent Automatic Deletion</label> + <div className="col-lg-3 col-md-3 col-sm-12" data-testid="project" > + <Checkbox inputId="trigger" role="trigger" + tooltip="Select to Prevent or Unselect to Allow Automatic Deletion of Dataproducts" + tooltipOptions={this.tooltipOptions} + checked={this.state.schedulingUnit.output_pinned} + onChange={(e) => this.setSchedUnitParams('output_pinned', e.target.checked)} + ></Checkbox> + </div> + {/* + <label htmlFor="schedulingConstraintsTemp" className="col-lg-2 col-md-2 col-sm-12 hide">Scheduling Constraints Template</label> + <div className="col-lg-3 col-md-3 col-sm-12 hide" data-testid="schedulingConstraintsTemp"> + <Dropdown inputId="schedulingConstraintsTemp" optionLabel="name" optionValue="id" + tooltip="Scheduling Constraints Template to add scheduling constraints to a scheduling unit" tooltipOptions={this.tooltipOptions} + value={this.state.schedulingUnit.scheduling_constraints_template_id} + disabled + options={this.constraintTemplates} + //onChange={(e) => { this.constraintStrategy(e);}} + placeholder="Select Constraints Template"/> + + </div> */} </div> </div> - <div className="p-field p-grid"> - <label htmlFor="priorityRank" className="col-lg-2 col-md-2 col-sm-12">Priority Rank</label> - <div className="col-lg-3 col-md-3 col-sm-12" data-testid="priority_rank" > - <input type="number" - data-for="reacttooltip" - data-iscapture="true" - data-tip="Priority of this scheduling unit w.r.t. other scheduling units within the same queue and project.. Min-0.0000, Max-1.0000" - inputId="proj-rank" name="rank" data-testid="rank" - className="p-inputtext p-component p-filled" - value={this.state.schedulingUnit.priority_rank} - step="0.0001" - onChange={(e)=> this.setSchedUnitParams('priority_rank', e.target.value)}/> - <label className="error"> - {this.state.errors.priority_rank ? this.state.errors.priority_rank : ""} - </label> - </div> - <div className="col-lg-1 col-md-1 col-sm-12"></div> - <label htmlFor="priority_queue" className="col-lg-2 col-md-2 col-sm-12">Priority Queue</label> - <div className="col-lg-3 col-md-3 col-sm-10"> - <Dropdown data-testid="priority_queue" id="priority_queue" optionLabel="value" optionValue="url" - tooltip="Priority Queue of the Scheduling Unit" tooltipOptions={this.tooltipOptions} - value={this.state.schedulingUnit.priority_queue} - options={this.priorityQueueTypes} - onChange={(e) => {this.setSchedUnitParams('priority_queue',e.value)}} - placeholder="Select Priority Queue" /> - <label className={(this.state.errors.priority_queue && this.state.touched.priority_queue) ?"error":"info"}> - {(this.state.errors.priority_queue && this.state.touched.priority_queue) ? this.state.errors.priority_queue : ""} - </label> + + <Stations + stationGroup={this.state.stationGroup} + onUpdateStations={this.onUpdateStations.bind(this)} + /> + + {this.state.constraintSchema && <div className="p-fluid"> + <div className="p-grid"> + <div className="p-col-12"> + <SchedulingConstraint initValue={this.state.initValue} constraintTemplate={this.state.constraintSchema} callback={this.setEditorOutputConstraint} /> + </div> </div> - </div> - <div className="p-field p-grid"> - { this.state.observStrategyVisible && - <> - <label htmlFor="observStrategy" className="col-lg-2 col-md-2 col-sm-12">Observation Strategy </label> - <div className="col-lg-3 col-md-3 col-sm-12" data-testid="observStrategy" > - <Dropdown inputId="observStrategy" optionLabel="name" optionValue="id" - tooltip="Observation Strategy Template to be used to create the Scheduling Unit and Tasks" tooltipOptions={this.tooltipOptions} - value={this.state.schedulingUnit.observation_strategy_template_id} - disabled={this.state.schedulingUnit.observation_strategy_template_id?true:false} - options={this.observStrategies} - onChange={(e) => {this.changeStrategy(e)}} - placeholder="Select Strategy" /> - <label className="info"> - {this.state.observStrategy? this.state.observStrategy.description : "Select Observation Strategy"} - </label> - </div> - </> - } - <div className="col-lg-1 col-md-1 col-sm-12"></div> - <label htmlFor="project" className="col-lg-2 col-md-2 col-sm-12">Prevent Automatic Deletion</label> - <div className="col-lg-3 col-md-3 col-sm-12" data-testid="project" > - <Checkbox inputId="trigger" role="trigger" - tooltip="Select to Prevent or Unselect to Allow Automatic Deletion of Dataproducts" - tooltipOptions={this.tooltipOptions} - checked={this.state.schedulingUnit.output_pinned} - onChange={(e) => this.setSchedUnitParams('output_pinned', e.target.checked)} - ></Checkbox> + </div>} + + <div className="p-fluid"> + <div className="p-grid"> + <div className="p-col-12"> + {this.state.paramsSchema?jeditor:""} </div> - {/* - <label htmlFor="schedulingConstraintsTemp" className="col-lg-2 col-md-2 col-sm-12 hide">Scheduling Constraints Template</label> - <div className="col-lg-3 col-md-3 col-sm-12 hide" data-testid="schedulingConstraintsTemp"> - <Dropdown inputId="schedulingConstraintsTemp" optionLabel="name" optionValue="id" - tooltip="Scheduling Constraints Template to add scheduling constraints to a scheduling unit" tooltipOptions={this.tooltipOptions} - value={this.state.schedulingUnit.scheduling_constraints_template_id} - disabled - options={this.constraintTemplates} - //onChange={(e) => { this.constraintStrategy(e);}} - placeholder="Select Constraints Template"/> - - </div> */} - </div> - </div> - - <Stations - stationGroup={this.state.stationGroup} - onUpdateStations={this.onUpdateStations.bind(this)} - /> - - {this.state.constraintSchema && <div className="p-fluid"> - <div className="p-grid"> - <div className="p-col-12"> - <SchedulingConstraint initValue={this.state.initValue} constraintTemplate={this.state.constraintSchema} callback={this.setEditorOutputConstraint} /> </div> </div> - </div>} - - <div className="p-fluid"> - <div className="p-grid"> - <div className="p-col-12"> - {this.state.paramsSchema?jeditor:""} + <ReactTooltip id="reacttooltip" place={'left'} type={'dark'} effect={'solid'} multiline={true} /> + <div className="p-grid p-justify-start"> + <div className="p-col-1"> + <Button label="Save" className="p-button-primary" icon="pi pi-check" onClick={this.saveSchedulingUnit} + disabled={!this.state.validEditor || !this.state.validForm} data-testid="save-btn" /> + </div> + <div className="p-col-1"> + <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.checkIsDirty} /> </div> </div> - </div> - <ReactTooltip id="reacttooltip" place={'left'} type={'dark'} effect={'solid'} multiline={true} /> - <div className="p-grid p-justify-start"> - <div className="p-col-1"> - <Button label="Save" className="p-button-primary" icon="pi pi-check" onClick={this.saveSchedulingUnit} - disabled={!this.state.validEditor || !this.state.validForm} data-testid="save-btn" /> - </div> - <div className="p-col-1"> - <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.checkIsDirty} /> - </div> - </div> - <div className="p-grid" data-testid="confirm_dialog"> - <CustomDialog type="confirmation" visible={this.state.showDialog} width="40vw" - header={'Edit Scheduling Unit'} message={'Do you want to leave this page? Your changes may not be saved.'} - content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelCreate}> - </CustomDialog> + <div className="p-grid" data-testid="confirm_dialog"> + <CustomDialog type="confirmation" visible={this.state.showDialog} width="40vw" + header={'Edit Scheduling Unit'} message={'Do you want to leave this page? Your changes may not be saved.'} + content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelCreate}> + </CustomDialog> + </div> </div> - </div> - - </> - } - + + </> + } + </>:<AccessDenied/>} </React.Fragment> ); } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js index 68b276c5fe6..52eaac70f80 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js @@ -2538,7 +2538,7 @@ export class SchedulingSetCreate extends Component { onClick={this.showAddSchedulingSet} tooltip="Add new Scheduling Set" style={{marginLeft: '-10px'}} - disabled={this.state.schedulingUnit.project !== null && scheduleunit_draft.add? false : true }/> + disabled={this.state.schedulingUnit.project !== null && scheduleunit_draft.scheduling_set? false : true }/> </div> </div> <div className="p-field p-grid"> @@ -2672,7 +2672,7 @@ export class SchedulingSetCreate extends Component { </> <div className="p-grid p-justify-start"> <div className="p-col-1"> - <Button label="Save" className="p-button-primary" icon="pi pi-check" disabled={scheduleunit_draft.create || scheduleunit_draft.edit ? false: true} onClick={this.saveSchedulingUnit} + <Button label="Save" className="p-button-primary" icon="pi pi-check" disabled={scheduleunit_draft.create && scheduleunit_draft.edit ? false: true} onClick={this.saveSchedulingUnit} data-testid="save-btn" /> </div> <div className="p-col-1"> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/edit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/edit.js index 6383e13aeae..a71b2eb11b0 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/edit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/edit.js @@ -14,6 +14,8 @@ import { publish } from '../../App'; import TaskService from '../../services/task.service'; import AppLoader from "./../../layout/components/AppLoader"; import PageHeader from '../../layout/components/PageHeader'; +import AuthUtil from '../../utils/auth.util'; +import AccessDenied from '../../layout/components/AccessDenied'; export class TaskEdit extends Component { @@ -36,7 +38,9 @@ export class TaskEdit extends Component { validEditor: false, validForm: false, errors: {}, - isLoading: true + isLoading: true, + userrole: {}, + taskId: '' }; this.formRules = { name: {required: true, message: "Name can not be empty"}, @@ -175,7 +179,7 @@ export class TaskEdit extends Component { this.props.history.goBack(); } - componentDidMount() { + async componentDidMount() { this.setState({ isLoading: true }); TaskService.getTaskTemplates() .then((templates) => { @@ -183,6 +187,8 @@ export class TaskEdit extends Component { }); let taskId = this.props.match.params?this.props.match.params.id:null; taskId = taskId?taskId:(this.props.taskId?this.props.taskId:this.props.location.state.taskId); + const permission = await AuthUtil.getUserPermissionByModuleId('task_draft',taskId); + this.setState({userrole: permission, taskId:taskId}) TaskService.getTaskDetails("draft", taskId) .then((task) => { if (task) { @@ -236,89 +242,92 @@ export class TaskEdit extends Component { </Link> </div> </div> */} - <PageHeader location={this.props.location} title={'Task - Edit'} actions={[{icon: 'fa-window-close', - title:'Click to Close Task Edit Page',type: 'button', actOn: 'click',props : { pathname: `/task/view/draft/${this.state.task?this.state.task.id:''}`,callback: this.checkIsDirty}}]}/> - {isLoading ? <AppLoader/> : - <div> - <div className="p-fluid"> - <div className="p-field p-grid"> - <label htmlFor="taskName" className="col-lg-2 col-md-2 col-sm-12">Name <span style={{color:'red'}}>*</span></label> - <div className="col-lg-4 col-md-4 col-sm-12"> - <InputText className={this.state.errors.name ?'input-error':''} id="taskName" type="text" value={this.state.task.name} - onChange={(e) => this.setTaskParams('name', e.target.value)}/> - <label className="error"> - {this.state.errors.name ? this.state.errors.name : ""} - </label> - </div> - <label htmlFor="description" className="col-lg-2 col-md-2 col-sm-12">Description <span style={{color:'red'}}>*</span></label> - <div className="col-lg-4 col-md-4 col-sm-12"> - <InputTextarea className={this.state.errors.description ?'input-error':''} rows={3} cols={30} value={this.state.task.description} onChange={(e) => this.setTaskParams('description', e.target.value)}/> - <label className="error"> - {this.state.errors.description ? this.state.errors.description : ""} - </label> - </div> - </div> - {/* <div className="p-field p-grid"> - <label htmlFor="createdAt" className="col-lg-2 col-md-2 col-sm-12">Created At</label> + {this.state.userrole[this.state.taskId] && this.state.userrole[this.state.taskId].edit ? + <> + <PageHeader location={this.props.location} title={'Task - Edit'} actions={[{icon: 'fa-window-close', + title:'Click to Close Task Edit Page',type: 'button', actOn: 'click',props : { pathname: `/task/view/draft/${this.state.task?this.state.task.id:''}`,callback: this.checkIsDirty}}]}/> + {isLoading ? <AppLoader/> : + <div> + <div className="p-fluid"> + <div className="p-field p-grid"> + <label htmlFor="taskName" className="col-lg-2 col-md-2 col-sm-12">Name <span style={{color:'red'}}>*</span></label> <div className="col-lg-4 col-md-4 col-sm-12"> - <Calendar showTime={true} hourFormat="24" value={created_at} onChange={(e) => this.setState({date2: e.value})}></Calendar> + <InputText className={this.state.errors.name ?'input-error':''} id="taskName" type="text" value={this.state.task.name} + onChange={(e) => this.setTaskParams('name', e.target.value)}/> + <label className="error"> + {this.state.errors.name ? this.state.errors.name : ""} + </label> </div> - <label htmlFor="updatedAt" className="col-lg-2 col-md-2 col-sm-12">Updated At</label> + <label htmlFor="description" className="col-lg-2 col-md-2 col-sm-12">Description <span style={{color:'red'}}>*</span></label> <div className="col-lg-4 col-md-4 col-sm-12"> - <Calendar showTime={true} hourFormat="24" value={updated_at} onChange={(e) => this.setState({date2: e.value})}></Calendar> + <InputTextarea className={this.state.errors.description ?'input-error':''} rows={3} cols={30} value={this.state.task.description} onChange={(e) => this.setTaskParams('description', e.target.value)}/> + <label className="error"> + {this.state.errors.description ? this.state.errors.description : ""} + </label> </div> - </div> - */} - <div className="p-field p-grid"> - <label htmlFor="tags" className="col-lg-2 col-md-2 col-sm-12">Tags</label> - <div className="col-lg-4 col-md-4 col-sm-12"> - <Chips value={this.state.task.tags?this.state.task.tags:[]} onChange={(e) => this.setTaskParams('tags', e.value)}></Chips> - </div> - {/* <label htmlFor="doCancel" className="col-lg-2 col-md-2 col-sm-12">Do Cancel</label> - <div className="col-lg-4 col-md-4 col-sm-12"> - <Checkbox onChange={e => this.setTaskParams('do_cancel', e.checked)} checked={this.state.task.do_cancel}></Checkbox> - </div> */} - {this.state.schedulingUnit && - <> - <label className="col-lg-2 col-md-2 col-sm-12">Scheduling Unit</label> - <Link className="col-lg-4 col-md-4 col-sm-12" to={ { pathname:'/schedulingunit/view', state: {id: this.state.schedulingUnit.id}}}>{this.state.schedulingUnit?this.state.schedulingUnit.name:''}</Link> - </> - } - </div> - <div className="p-field p-grid"> - <label htmlFor="tags" className="col-lg-2 col-md-2 col-sm-12">Template</label> + </div> + {/* <div className="p-field p-grid"> + <label htmlFor="createdAt" className="col-lg-2 col-md-2 col-sm-12">Created At</label> + <div className="col-lg-4 col-md-4 col-sm-12"> + <Calendar showTime={true} hourFormat="24" value={created_at} onChange={(e) => this.setState({date2: e.value})}></Calendar> + </div> + <label htmlFor="updatedAt" className="col-lg-2 col-md-2 col-sm-12">Updated At</label> + <div className="col-lg-4 col-md-4 col-sm-12"> + <Calendar showTime={true} hourFormat="24" value={updated_at} onChange={(e) => this.setState({date2: e.value})}></Calendar> + </div> + </div> + */} + <div className="p-field p-grid"> + <label htmlFor="tags" className="col-lg-2 col-md-2 col-sm-12">Tags</label> <div className="col-lg-4 col-md-4 col-sm-12"> - <Dropdown optionLabel="name" optionValue="id" - value={this.state.task.specifications_template_id} - options={this.state.taskTemplates} - onChange={(e) => {this.changeTaskTemplate(e.value)}} - placeholder="Select Task Template"/> + <Chips value={this.state.task.tags?this.state.task.tags:[]} onChange={(e) => this.setTaskParams('tags', e.value)}></Chips> + </div> + {/* <label htmlFor="doCancel" className="col-lg-2 col-md-2 col-sm-12">Do Cancel</label> + <div className="col-lg-4 col-md-4 col-sm-12"> + <Checkbox onChange={e => this.setTaskParams('do_cancel', e.checked)} checked={this.state.task.do_cancel}></Checkbox> + </div> */} + {this.state.schedulingUnit && + <> + <label className="col-lg-2 col-md-2 col-sm-12">Scheduling Unit</label> + <Link className="col-lg-4 col-md-4 col-sm-12" to={ { pathname:'/schedulingunit/view', state: {id: this.state.schedulingUnit.id}}}>{this.state.schedulingUnit?this.state.schedulingUnit.name:''}</Link> + </> + } + </div> + <div className="p-field p-grid"> + <label htmlFor="tags" className="col-lg-2 col-md-2 col-sm-12">Template</label> + <div className="col-lg-4 col-md-4 col-sm-12"> + <Dropdown optionLabel="name" optionValue="id" + value={this.state.task.specifications_template_id} + options={this.state.taskTemplates} + onChange={(e) => {this.changeTaskTemplate(e.value)}} + placeholder="Select Task Template"/> + </div> </div> + </div> + </div> + } + <div className="p-fluid"> + <div className="p-grid"><div className="p-col-12"> + {this.state.taskSchema?jeditor:""} </div> </div> - </div> - } - <div className="p-fluid"> - <div className="p-grid"><div className="p-col-12"> - {this.state.taskSchema?jeditor:""} - </div> - </div> - </div> - - <div className="p-grid p-justify-start"> - <div className="p-col-1"> - <Button label="Save" className="p-button-primary" icon="pi pi-check" onClick={this.saveTask} disabled={!this.state.validEditor || !this.state.validForm} /> - </div> - <div className="p-col-1"> - <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.checkIsDirty} /> - </div> - </div> - <div className="p-grid" data-testid="confirm_dialog"> - <CustomDialog type="confirmation" visible={this.state.showDialog} width="40vw" - header={'Edit Task'} message={'Do you want to leave this page? Your changes may not be saved.'} - content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelEdit}> - </CustomDialog> - </div> + </div> + + <div className="p-grid p-justify-start"> + <div className="p-col-1"> + <Button label="Save" className="p-button-primary" icon="pi pi-check" onClick={this.saveTask} disabled={!this.state.validEditor || !this.state.validForm} /> + </div> + <div className="p-col-1"> + <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.checkIsDirty} /> + </div> + </div> + <div className="p-grid" data-testid="confirm_dialog"> + <CustomDialog type="confirmation" visible={this.state.showDialog} width="40vw" + header={'Edit Task'} message={'Do you want to leave this page? Your changes may not be saved.'} + content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelEdit}> + </CustomDialog> + </div> + </>: <AccessDenied/>} </React.Fragment> ); } 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 d75c84e8d1f..ac9c3408186 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js @@ -303,15 +303,31 @@ export class TaskList extends Component { await this.getFilterColumns(this.state.taskType.toLowerCase()); this.setToggleBySorting(); const permission = await AuthUtil.getUserRolePermission(); - const {task} = permission.userRolePermission - this.subtaskTemplates = await TaskService.getSubtaskTemplates() - const actions = [{icon: 'fa fa-ban', title: 'Cancel Task(s)', - type: 'button', actOn: 'click', props: { callback: this.confirmCancelTasks }}, + const {task, task_blueprint, task_draft} = permission.userRolePermission + this.subtaskTemplates = await TaskService.getSubtaskTemplates(); + let actions = []; + if(this.state.taskType === 'Draft'){ + actions = [{icon: 'fa fa-ban', + title: task_draft.canceltask?'Cancel Task(s)': this.access_denied_message, + disabled: task_draft.canceltask? !task_draft.canceltask: true, + type: 'button', actOn: 'click', props: { callback: this.confirmCancelTasks }}, + {icon: 'fa fa-trash', + title: task.delete?'Delete Task(s)':this.access_denied_message, + disabled: task.delete? !task.delete: true, + type: 'button', actOn: 'click', props: { callback: this.confirmDeleteTasks }} + ]; + } + else { + actions = [{icon: 'fa fa-ban', + title: task_blueprint.canceltask?'Cancel Task(s)': this.access_denied_message, + 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, disabled: task.delete? !task.delete: true, type: 'button', actOn: 'click', props: { callback: this.confirmDeleteTasks }} ]; + } this.setState({ tasks: [], isLoading: false, actions: actions, loadingStatus: false, userrole: permission }); } @@ -638,21 +654,38 @@ export class TaskList extends Component { async getTaskLists(taskType, filterQry, orderBy, limit, offset) { let expand = taskType.toLowerCase() === 'draft' ? this.TASK_DRAFT_EXPAND: this.TASK_BLUEPRINT_EXPAND; let response = await TaskService.getExpandedTasksWithFilter(taskType.toLowerCase(), expand, filterQry, orderBy, limit, offset); - const permission = await AuthUtil.getUserRolePermission(); - const {task} = permission.userRolePermission + const {task, task_draft, task_blueprint} = this.state.userrole.userRolePermission if (response && response.data) { this.totalPage = response.data.count; let tasks = taskType.toLowerCase() === 'draft' ? (await this.getFormattedTaskDrafts(response.data.results)) : this.getFormattedTaskBlueprints(response.data.results); let ingestGroup = tasks.map(task => ({ name: task.name, canIngest: task.canIngest, type_value: task.type_value, id: task.id })); ingestGroup = _.groupBy(_.filter(ingestGroup, 'type_value'), 'type_value'); tasks = await this.formatDataProduct(tasks); - const actions = [{icon: 'fa fa-ban', title: 'Cancel Task(s)', - type: 'button', actOn: 'click', props: { callback: this.confirmCancelTasks }}, + let actions = []; + if(task_blueprint && task_draft){ + if(taskType === 'draft'){ + actions = [{icon: 'fa fa-ban', + title: task_draft.canceltask?'Cancel Task(s)': this.access_denied_message, + disabled: task_draft.canceltask? !task_draft.canceltask: true, + type: 'button', actOn: 'click', props: { callback: this.confirmCancelTasks }}, {icon: 'fa fa-trash', - title: task.delete?'Delete Task(s)': this.access_denied_message, - disabled: task.delete? !task.delete : true, + title: task.delete?'Delete Task(s)':this.access_denied_message, + disabled: task.delete? !task.delete: true, + type: 'button', actOn: 'click', props: { callback: this.confirmDeleteTasks }} + ]; + } + else { + actions = [{icon: 'fa fa-ban', + title: task_blueprint.canceltask?'Cancel Task(s)': this.access_denied_message, + 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, + disabled: task.delete? !task.delete: true, type: 'button', actOn: 'click', props: { callback: this.confirmDeleteTasks }} ]; + } + } this.setState({ tasks: tasks, isLoading: false, actions: actions, loadingStatus: false }); } else { appGrowl.show({severity: 'error', summary: 'Error', detail: 'Unable to fetch records'}); 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 48e5591da7e..649111d00b6 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js @@ -33,7 +33,8 @@ export class TaskView extends Component { dialog: {}, userrole: { userRolePermission: {} - } + }, + permissionById: {} }; this.access_denied_message = "Don't have permission"; this.setEditorFunction = this.setEditorFunction.bind(this); @@ -74,8 +75,8 @@ export class TaskView extends Component { this.getTaskDetails(this.props.match.params.id, this.props.match.params.type); } const moduleName = this.props.match.params.type === 'draft' ? 'task_draft': 'task_blueprint' - if(prevState.taskId != this.props.match.params.id && prevState.moduleName !== moduleName ) { - const permissionById = await AuthUtil.getUserRoleById(moduleName, this.props.match.params.id) + if(prevState.taskId != this.props.match.params.id || prevState.moduleName !== moduleName ) { + const permissionById = await AuthUtil.getUserPermissionByModuleId(moduleName, this.props.match.params.id) this.setState({ permissionById: permissionById, moduleName: moduleName, taskId: this.props.match.params.id}); } @@ -88,7 +89,7 @@ export class TaskView extends Component { taskType = taskType?taskType:'draft'; const moduleName = taskType === 'draft' ? 'task_draft': 'task_blueprint' const permission = await AuthUtil.getUserRolePermission(); - const permissionById = await AuthUtil.getUserRoleById(moduleName, taskId) + const permissionById = await AuthUtil.getUserPermissionByModuleId(moduleName, taskId) this.setState({userrole: permission, permissionById: permissionById, moduleName: moduleName, taskId: taskId}); if (taskId && taskType) { this.getTaskDetails(taskId, taskType); @@ -258,8 +259,8 @@ export class TaskView extends Component { if (this.state.taskType === 'draft') { const taskId = this.state.task?this.state.task.id:''; actions = [{ icon: 'fa-edit', - title:this.state.permissionById && this.state.permissionById[this.state.taskId].edit ?'Click to Edit Task':this.access_denied_message, - disabled: this.state.permissionById ? !this.state.permissionById[this.state.taskId].edit: true, + title:this.state.permissionById[this.state.taskId] && this.state.permissionById[this.state.taskId].edit ?'Click to Edit Task':this.access_denied_message, + disabled: this.state.permissionById[this.state.taskId] ? !this.state.permissionById[this.state.taskId].edit: true, props : { pathname:`/task/edit/draft/${taskId}`, state: {taskId: taskId} } @@ -295,7 +296,7 @@ export class TaskView extends Component { return ( <React.Fragment> - {this.state.permissionById && + {this.state.permissionById[this.state.taskId] && <> { this.state.permissionById[this.state.taskId].view ? <> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/utils/auth.util.js b/SAS/TMSS/frontend/tmss_webapp/src/utils/auth.util.js index c50604ac136..0bd958f5f01 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/utils/auth.util.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/auth.util.js @@ -6,11 +6,6 @@ const AuthUtil = { //await AuthStore.dispatch({ type: module }); return AuthStore.getState(); }, - getUserModulePermission: module => { - const allPermissions = AuthStore.getState(); - const modulePermissions = allPermissions.userRolePermission[module]; - return modulePermissions?modulePermissions:{}; - }, getUserPermissionByModule: module => { const allPermissions = AuthStore.getState(); let modulePermissions = {} @@ -18,19 +13,15 @@ const AuthUtil = { return modulePermissions?modulePermissions:{}; }, - getUserRoleById: async (module, id) => { + getUserPermissionByModuleId: async (module, id) => { const allPermissions = AuthStore.getState(); - let modulePermissions = allPermissions.userRolePermission.rolePermission && allPermissions.userRolePermission.rolePermission[module] ? allPermissions.userRolePermission.rolePermission[module]:undefined; - if(modulePermissions){ - if(modulePermissions[id]) - return modulePermissions; - else { - modulePermissions = await PermissionStackUtil.getUserAccessModulebyId(module, id); - return modulePermissions; - } + let modulePermissions ={}; + modulePermissions = allPermissions.userRolePermission.rolePermission && allPermissions.userRolePermission.rolePermission[module] ? allPermissions.userRolePermission.rolePermission[module]:{}; + if(modulePermissions[id]) { + return modulePermissions; } else { - modulePermissions = await PermissionStackUtil.getUserAccessModulebyId(module, id); + modulePermissions = await PermissionStackUtil.getAccessByModuleAndId(module, id); return modulePermissions; } } -- GitLab