diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/ResourceInputList.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/ResourceInputList.js index 36e10186e908df3858d6189095595b094a35fc3e..33c470258683f5bdd92db3847940543fe32e3663 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/ResourceInputList.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/ResourceInputList.js @@ -41,7 +41,7 @@ export class ResourceInputList extends Component { <InputNumber key={'item1-'+ index} id={'item1-'+ index} name={'item1-'+ index} suffix={` ${this.props.unitMap[item.quantity_value]?this.props.unitMap[item.quantity_value].display:''}`} placeholder={` ${this.props.unitMap[item.quantity_value]?this.props.unitMap[item.quantity_value].display:item.name}`} min={0} useGrouping={false} - value={this.state.cycleQuota[item.name]} + value={this.props.cycleQuota[item.name]} onChange={(e) => this.onInputChange(item.name, e)} onBlur={(e) => this.onInputChange(item.name, e)} style={{width:"90%", marginRight: "5px"}} diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/edit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/edit.js index 996d567a9b1fab804f964889ada9024a226a043a..4d599c33ef64cd790084b5f0012a3c2ad3fde1e4 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/edit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/edit.js @@ -458,13 +458,13 @@ export class CycleEdit extends Component { <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.cycleQuota).length>0 && + {/* {_.keys(this.state.cycleQuota).length>0 && */} <div className="p-field p-grid resource-input-grid"> <ResourceInputList list={this.state.resources} unitMap={this.resourceUnitMap} cycleQuota={this.state.cycleQuota} callback={this.setCycleQuotaParams} removeInputCallback={this.removeResource} /> </div> - } + {/* } */} </div> } </div> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/index.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/index.js index 5b2e8f32413179c62a57035f2e3590afa1c315d0..82d06e5d2d8a92df93bba49ac7a82b1c390116ee 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/index.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/index.js @@ -1,3 +1,3 @@ import {CycleEdit} from './edit'; - -export {CycleEdit} ; +import {CycleView} from './view'; +export {CycleView, CycleEdit} ; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/view.js new file mode 100644 index 0000000000000000000000000000000000000000..55430cc1fd440589988e30d2a1e13aa1e91c4d51 --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/view.js @@ -0,0 +1,140 @@ +import React, {Component} from 'react'; +import {Link, Redirect} from 'react-router-dom' +import moment from 'moment'; +import _ from 'lodash'; + +import { Chips } from 'primereact/chips'; + +import ResourceDisplayList from './ResourceDisplayList'; + +import AppLoader from '../../layout/components/AppLoader'; +import CycleService from '../../services/cycle.service'; +import UnitConverter from '../../utils/unit.converter'; + +/** + * Component to view the details of a cycle + */ +export class CycleView extends Component { + DATE_FORMAT = 'YYYY-MMM-DD HH:mm:ss'; + constructor(props) { + super(props); + this.state = { + isLoading: true, + }; + if (this.props.match.params.id) { + this.state.cycleId = this.props.match.params.id; + } else if (this.props.location.state && this.props.location.state.id) { + this.state.cycleId = this.props.location.state.id; + } + this.state.redirect = this.state.cycleId?"":'/cycle' // If no cycle id is passed, redirect to cycle list page + this.resourceUnitMap = UnitConverter.resourceUnitMap; // Resource unit conversion factor and constraints + } + + componentDidMount() { + const cycleId = this.state.cycleId; + if (cycleId) { + this.getCycleDetails(); + } else { + this.setState({redirect: "/not-found"}); + } + } + + /** + * To get the cycle details from the backend using the service + * + */ + async getCycleDetails() { + let cycle = await CycleService.getCycleDetails(this.state.cycleId); + let cycleQuota = []; + let resources = []; + + if (cycle) { + // If resources are allocated for the cycle quota fetch the resources master from the API + if (cycle.quota) { + resources = await CycleService.getResources(); + } + + // For every cycle quota, get the resource type & assign to the resource variable of the quota object + for (const id of cycle.quota_ids) { + let quota = await CycleService.getCycleQuota(id); + let resource = _.find(resources, ['name', quota.resource_type_id]); + quota.resource = resource; + cycleQuota.push(quota); + }; + this.setState({cycle: cycle, cycleQuota: cycleQuota, isLoading: false}); + } else { + this.setState({redirect: "../../not-found"}) + } + + } + + render() { + if (this.state.redirect) { + return <Redirect to={ {pathname: this.state.redirect} }></Redirect> + } + + return ( + <React.Fragment> + <div className="p-grid"> + <div className="p-col-10 p-lg-10 p-md-10"> + <h2>Cycle - Details </h2> + </div> + { this.state.cycle && + <div className="p-col-2 p-lg-2 p-md-2"> + <Link to={{ pathname: `/cycle`}} title="Close View" style={{float: "right"}}> + <i className="fa fa-times" style={{marginTop: "10px", marginLeft: "5px"}}></i> + </Link> + <Link to={{ pathname: `/cycle/edit/${this.state.cycle.name}`, state: {id: this.state.cycle?this.state.cycle.name:''}}} title="Edit Cycle" + style={{float: "right"}}> + <i className="fa fa-edit" style={{marginTop: "10px"}}></i> + </Link> + </div> + } + </div> + { this.state.isLoading && <AppLoader /> } + { this.state.cycle && + <React.Fragment> + <div className="main-content"> + <div className="p-grid"> + <label className="col-lg-2 col-md-2 col-sm-12">Name</label> + <span className="col-lg-4 col-md-4 col-sm-12">{this.state.cycle.name}</span> + <label className="col-lg-2 col-md-2 col-sm-12">Description</label> + <span className="col-lg-4 col-md-4 col-sm-12">{this.state.cycle.description}</span> + </div> + <div className="p-grid"> + <label className="col-lg-2 col-md-2 col-sm-12">Created At</label> + <span className="col-lg-4 col-md-4 col-sm-12">{moment.utc(this.state.cycle.created_at).format(this.DATE_FORMAT)}</span> + <label className="col-lg-2 col-md-2 col-sm-12">Updated At</label> + <span className="col-lg-4 col-md-4 col-sm-12">{moment.utc(this.state.cycle.updated_at).format(this.DATE_FORMAT)}</span> + </div> + + <div className="p-grid"> + <label className="col-lg-2 col-md-2 col-sm-12">Projects</label> + <Chips className="col-lg-4 col-md-4 col-sm-12 chips-readonly" disabled value={this.state.cycle.projects_ids}></Chips> + </div> + <div className="p-fluid"> + <div className="p-field p-grid"> + <div className="col-lg-3 col-md-3 col-sm-12"> + <h5 data-testid="resource_alloc">Resource Allocations</h5> + </div> + </div> + </div> + {this.state.cycleQuota.length===0 && + <div className="p-field p-grid"> + <div className="col-lg-12 col-md-12 col-sm-12"> + <span>Reosurces not yet allocated. + <Link to={{ pathname: `/cycle/edit/${this.state.cycle.name}`, state: {id: this.state.cycle?this.state.cycle.name:''}}} title="Edit Cycle" > Click</Link> to add. + </span> + </div> + </div> + } + <div className="p-field p-grid resource-input-grid"> + <ResourceDisplayList cycleQuota={this.state.cycleQuota} unitMap={this.resourceUnitMap} /> + </div> + </div> + </React.Fragment> + } + </React.Fragment> + ); + } +} \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js index a2f5f5ea78e0ce08b5922eca6b45403e6f3b52b4..32f6b957b50c06642ee4396b25aadf230d21d3d6 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js @@ -9,7 +9,7 @@ import {NotFound} from '../layout/components/NotFound'; import {ProjectList, ProjectCreate, ProjectView, ProjectEdit} from './Project'; import {Dashboard} from './Dashboard'; import {Scheduling} from './Scheduling'; -import {CycleEdit} from './Cycle'; +import {CycleView, CycleEdit} from './Cycle'; import {TaskEdit, TaskView} from './Task'; import ViewSchedulingUnit from './Scheduling/ViewSchedulingUnit' @@ -69,6 +69,14 @@ export const routes = [ path: "/cycle/edit/:id", component: CycleEdit, name: 'Cycle Edit' + },{ + path: "/cycle/view", + component: CycleView, + name: 'Cycle View' + },{ + path: "/cycle/view/:id", + component: CycleView, + name: 'Cycle View' } ]; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/services/cycle.service.js b/SAS/TMSS/frontend/tmss_webapp/src/services/cycle.service.js index 3501b674aac89590fce082f1078e0a589a923579..9c19c287d4fc0ebc955964d47e996eec90d449e3 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/services/cycle.service.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/services/cycle.service.js @@ -108,6 +108,15 @@ const CycleService = { console.error(error); return null; } + }, getCycleDetails: async function(id) { + try { + const response = await axios.get((`/api/cycle/${id}`)); + let cycle = response.data; + return cycle; + } catch(error) { + console.error(error); + return null; + } }, }