From 15f8a6a70ba06b97faf89197f6b8cbcf2693d1bd Mon Sep 17 00:00:00 2001 From: Muthukrishnanmatriot <76949556+muthukrishnanmatriot@users.noreply.github.com> Date: Mon, 15 Mar 2021 10:39:56 +0530 Subject: [PATCH] TMSS-213 - Implemented Find Object --- SAS/TMSS/frontend/tmss_webapp/src/App.js | 26 ++- .../src/layout/components/AppTopbar.js | 9 +- .../src/layout/components/FindObejct.js | 76 +++++++ .../tmss_webapp/src/layout/sass/_content.scss | 5 + .../tmss_webapp/src/layout/sass/_topbar.scss | 9 + .../src/routes/Search/find.object.result.js | 210 ++++++++++++++++++ .../tmss_webapp/src/routes/Search/index.js | 3 + .../frontend/tmss_webapp/src/routes/index.js | 7 + 8 files changed, 338 insertions(+), 7 deletions(-) create mode 100644 SAS/TMSS/frontend/tmss_webapp/src/layout/components/FindObejct.js create mode 100644 SAS/TMSS/frontend/tmss_webapp/src/routes/Search/find.object.result.js create mode 100644 SAS/TMSS/frontend/tmss_webapp/src/routes/Search/index.js diff --git a/SAS/TMSS/frontend/tmss_webapp/src/App.js b/SAS/TMSS/frontend/tmss_webapp/src/App.js index 12ca8a5aa71..0dfefd73671 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/App.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/App.js @@ -31,7 +31,8 @@ class App extends Component { overlayMenuActive: localStorage.getItem('overlayMenuActive') === 'true' ? true : false, mobileMenuActive: localStorage.getItem('mobileMenuActive') === 'true' ? true : false, authenticated: Auth.isAuthenticated(), - redirect: (Auth.isAuthenticated() && window.location.pathname === "/login")?"/":window.location.pathname + redirect: (Auth.isAuthenticated() && window.location.pathname === "/login")?"/":window.location.pathname, + findObjectPlaceholder: 'Sub Task', }; this.onWrapperClick = this.onWrapperClick.bind(this); this.onToggleMenu = this.onToggleMenu.bind(this); @@ -40,6 +41,7 @@ class App extends Component { this.setPageTitle = this.setPageTitle.bind(this); this.loggedIn = this.loggedIn.bind(this); this.logout = this.logout.bind(this); + this.setSearchField = this.setSearchField.bind(this); this.menu = [ {label: 'Dashboard', icon: 'pi pi-fw pi-home', to:'/dashboard',section: 'dashboard'}, {label: 'Cycle', icon:'pi pi-fw pi-spinner', to:'/cycle',section: 'cycle'}, @@ -127,6 +129,19 @@ class App extends Component { this.setState({authenticated: false, redirect:"/"}); } + /** + * Set search param + * @param {*} key + * @param {*} value + */ + setSearchField(key, value) { + this.setState({ + objectType: key, + findObjectId: value, + redirect:"/find-object" + }); + } + render() { const wrapperClass = classNames('layout-wrapper', { 'layout-overlay': this.state.layoutMode === 'overlay', @@ -146,12 +161,17 @@ class App extends Component { {/* Load main routes and application only if the application is authenticated */} {this.state.authenticated && <> - <AppTopbar onToggleMenu={this.onToggleMenu} isLoggedIn={this.state.authenticated} onLogout={this.logout}></AppTopbar> + <AppTopbar + onToggleMenu={this.onToggleMenu} + isLoggedIn={this.state.authenticated} + onLogout={this.logout} + setSearchField={this.setSearchField} + /> <Router basename={ this.state.currentPath }> <AppMenu model={this.menu} onMenuItemClick={this.onMenuItemClick} layoutMode={this.state.la} active={this.state.menuActive}/> <div className="layout-main"> {this.state.redirect && - <Redirect to={{pathname: this.state.redirect}} />} + <Redirect to={{pathname: this.state.redirect, state:{objectType: this.state.objectType, findObjectId: this.state.findObjectId}}} />} <AppBreadCrumbWithRouter setPageTitle={this.setPageTitle} /> <RoutedContent /> </div> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/components/AppTopbar.js b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/AppTopbar.js index f112943d779..858538c68d0 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/layout/components/AppTopbar.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/AppTopbar.js @@ -5,19 +5,17 @@ import 'primereact/resources/themes/nova-light/theme.css'; import 'primereact/resources/primereact.css'; import 'primeflex/primeflex.css'; import { PropTypes } from 'prop-types'; - import Auth from '../../authenticate/auth'; - +import { FindObejct } from './FindObejct'; export class AppTopbar extends Component { constructor(props) { super(props); this.state = { - username: Auth.getUser().name + username: Auth.getUser().name, }; } - static defaultProps = { onToggleMenu: null } @@ -31,9 +29,11 @@ export class AppTopbar extends Component { <React.Fragment> <div className="layout-wrapper layout-static layout-static-sidebar-inactive"> <div className="layout-topbar clearfix"> + <button className="p-link layout-menu-button" onClick={this.props.onToggleMenu}> <i className="pi pi-bars"></i></button> <span className="header-title">TMSS</span> + {this.props.isLoggedIn && <div className="top-right-bar"> <span><i className="fa fa-user"></i>{this.state.username}</span> @@ -41,6 +41,7 @@ export class AppTopbar extends Component { <i className="pi pi-power-off"></i></button> </div> } + <FindObejct setSearchField={this.props.setSearchField} /> </div> </div> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/components/FindObejct.js b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/FindObejct.js new file mode 100644 index 00000000000..c6f261798ec --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/FindObejct.js @@ -0,0 +1,76 @@ +import React, {Component} from 'react'; +import { Dropdown } from 'primereact/dropdown'; +import _ from 'lodash'; +import { appGrowl , setAppGrowl } from './AppGrowl'; +import { Growl } from 'primereact/components/growl/Growl'; + +export class FindObejct extends Component { + + constructor(props) { + super(props); + this.state = { + // Find Object - dropdown list value + objectTypes: [ + {name: 'Sub Task', code: 'subtask'}, + {name: 'Task Blueprint', code: 'taskblueprint'}, + {name: 'Task Draft', code: 'taskdraft'}, + {name: 'SU Blueprint', code: 'sublueprint'}, + {name: 'SU Draft', code: 'sudraft'}, + {name: 'Project', code: 'project'}, + ], + findObjectPlaceholder: 'Sub Task', + objectid: '' + }; + this.findObject = this.findObject.bind(this); + this.setFindObject = this.setFindObject.bind(this); + } + + /** + * Set Object Type & Search Object id value + * @param {*} value + */ + setFindObject(value) { + if (value.name) { + this.setState({findObjectPlaceholder: value.name, objectid: this.state.objectid}) + } else if(this.state.findObjectPlaceholder === 'Project') { + this.setState({objectid: value}); + } else if (!isNaN(value)) { + this.setState({objectid: value}); + } + } + + /** + * Callback function to find Object + */ + findObject() { + let objectType = _.find(this.state.objectTypes, {name: this.state.findObjectPlaceholder }) + if (this.state.objectid && this.state.objectid.length > 0) { + this.props.setSearchField(objectType.code, this.state.objectid); + } else { + appGrowl.show({severity: 'info', summary: 'Information', detail: 'Enter Object id to search'}); + } + } + + render() { + return ( + <React.Fragment> + <Growl ref={(el) => setAppGrowl(el)} /> + <div className="top-right-bar find-object-search" style={{marginRight: '2em'}}> + <span className="p-input-icon-right" > + <a href="#" > <i className="pi pi-search find-object-search-btn" + onClick={this.findObject} /> </a> + <Dropdown + className="p-link layout-menu-button" + value={this.state.objectid} + options={this.state.objectTypes} + optionLabel="name" + editable + placeholder={this.state.findObjectPlaceholder} + onChange={(e) => {this.setFindObject(e.value)}} + /> + </span> + </div> + </React.Fragment> + ) + } +} diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_content.scss b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_content.scss index 5c49ad86c0d..16fda99097d 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_content.scss +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_content.scss @@ -3,4 +3,9 @@ padding: 60px 16px 16px 25px; min-height: 95vh; background-color: white; +} + +.find-obj-tree-view { + margin-left: 1em; + margin-right: 1em; } \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_topbar.scss b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_topbar.scss index 6c190d5e90b..b735f26abc1 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_topbar.scss +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_topbar.scss @@ -143,4 +143,13 @@ .top-right-bar button { padding-left: 5px; +} +.find-object-search { + padding-top: 0px; +} +.find-object-search-btn { + margin-left: 10px; + float: right; + margin-top: .3em; + color: white; } \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Search/find.object.result.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Search/find.object.result.js new file mode 100644 index 00000000000..6b190a03e36 --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Search/find.object.result.js @@ -0,0 +1,210 @@ +import React, {Component} from 'react'; +import PageHeader from '../../layout/components/PageHeader'; +import { Tree } from 'primereact/tree'; +import TaskService from './../../services/task.service'; +import ScheduleService from './../../services/schedule.service'; +import ProjectService from './../../services/project.service'; + +export class FindObjectResult extends Component{ + constructor(props){ + super(props); + this.state = { + findObjectId: '', + objNodes: [], + }; + this.schedulingSetList= {}; + this.projectsList= {}; + this.data= {}; + } + + /** + * Find Object based in search id + */ + async componentDidMount(){ + const objectType = (this.props.location.state && this.props.location.state.objectType)?this.props.location.state.objectType:''; + if (objectType === 'subtask') { + let subtaskDetails = await this.findSubTask(this.props.location.state.findObjectId); + this.setState({objNodes: (subtaskDetails)?[this.data.subtask]:[]}); + } + else if (objectType === 'taskdraft') { + let taskDetails = await this.findTask('draft', this.props.location.state.findObjectId); + this.setState({objNodes: (taskDetails)?[this.data.task]:[]}); + } + else if (objectType === 'taskblueprint') { + let taskDetails = await this.findTask('blueprint', this.props.location.state.findObjectId); + this.setState({objNodes: (taskDetails)?[this.data.task]:[]}); + } + else if (objectType === 'sublueprint') { + let suDetails = await this.findSchedulingUnit('blueprint', this.props.location.state.findObjectId); + this.setState({objNodes: (suDetails)?[this.data.schedulingUnit]:[]}); + } + else if (objectType === 'sudraft') { + let suDetails = await this.findSchedulingUnit('draft', this.props.location.state.findObjectId); + this.setState({objNodes: (suDetails)?[this.data.schedulingUnit]:[]}); + } + else if (objectType === 'project') { + let projectDetails = await this.findProject(this.props.location.state.findObjectId); + this.setState({objNodes: (projectDetails)?[this.data.project]:[]}); + } + else { + this.setState({objNodes: []}); + } + } + + /** + * Find SubTask for given id + * @param {*} id + * @returns + */ + async findSubTask(id){ + const subtaskDetails = await TaskService.getSubtaskDetails(id); + if (subtaskDetails) { + await this.findTask('blueprint', subtaskDetails.task_blueprint_id); + let subtask = {}; + subtask['key'] = 'subtask'+subtaskDetails.id; + subtask['label'] = <> SubTask ({subtaskDetails.id}) + {/* -- View page not available yet -- + <span className="find-obj-tree-view"><a href="" target='_blank'>View</a></span> */} + <span className="find-obj-tree-view"> <a href={subtaskDetails.url} target='_blank' + title=" View SubTask API"><i className="pi pi-info-circle" /></a></span></>; + subtask['icon'] = 'pi pi-fw pi-calendar'; + subtask['children'] = [this.data.task]; + this.data['subtask'] = subtask; + } + return subtaskDetails; + } + + /** + * Find Task details for given id + * @param {*} taskType + * @param {*} id + * @returns + */ + async findTask(taskType, id){ + const taskDetails = await TaskService.getTaskDetails(taskType, id); + if (taskDetails) { + if (taskType === 'blueprint') { + await this.findSchedulingUnit('blueprint', taskDetails.scheduling_unit_blueprint_id); + } else { + await this.findSchedulingUnit('draft', taskDetails.scheduling_unit_draft_id); + } + let task = {}; + task['key'] = 'task'+taskDetails.id; + task['label'] = <> Task ({taskDetails.id}) + <span className="find-obj-tree-view"> + <a href={`/task/view/${taskType}/${taskDetails.id}`} target='_blank' title=" View Task Details"> + <i className="fa fa-eye" /> + </a> + </span> + <span> <a href={taskDetails.url} target='_blank' title=" View Task API"><i className="pi pi-info-circle" /></a></span></>; + task['icon'] = 'pi pi-fw pi-calendar'; + task['children'] = [this.data.schedulingUnit]; + this.data['task'] = task; + this.setState({objNodes: [this.data.task]}); + } + return taskDetails; + } + + /** + * Find Scheduling Unit for given id + * @param {*} suType + * @param {*} id + * @returns + */ + async findSchedulingUnit(suType, id){ + let suDetails = null; + if (suType === ' blueprint') { + suDetails = await ScheduleService.getSchedulingUnitBlueprintById(id); + } else { + suDetails = await ScheduleService.getSchedulingUnitDraftById(id); + } + if (suDetails) { + await this.findProjectBySUId(suDetails.id); + let schedulingUnit = {}; + schedulingUnit['key'] = 'su'+suDetails.id; + schedulingUnit['label'] = <> Scheduling Unit ({suDetails.id}) + <span className="find-obj-tree-view"><a href={`/schedulingunit/view/${suType}/${suDetails.id}`} + target='_blank' title=" View Scheduling Unit Details"><i className="fa fa-eye" /></a> </span> + <span><a href={suDetails.url} target='_blank' title=" View Scheduling Unit API" > + <i className="pi pi-info-circle" /></a></span></>; + schedulingUnit['icon'] = 'pi pi-fw pi-calendar'; + schedulingUnit['children'] = [this.data.suSet]; + this.data['schedulingUnit'] = schedulingUnit; + this.setState({objNodes: [this.data.schedulingUnit]}); + } + return suDetails; + } + + /** + * Find project for given SU id + * @param {*} suId + */ + async findProjectBySUId(suId) { + this.schedulingSetList = await ScheduleService.getSchedulingSets(); + this.projectsList = await ScheduleService.getProjectList(); + const suSetDetails = this.schedulingSetList.find((suSet) => { return suId === suSet.id }); + if (suSetDetails) { + const project = this.projectsList.find((project) => { return suSetDetails.project_id === project.name}); + this.getProjectDetails(project); + let suSet = {}; + suSet['key'] = 'suset'+suSetDetails.id; + suSet['label'] = <> Scheduling Set ({suSetDetails.id}) + {/* -- View page not available yet -- + <span className="find-obj-tree-view"><a href="" + target='_blank' title='View Project details'><i className="fa fa-eye" /></a></span> */} + <span className="find-obj-tree-view"> + <a href={suSetDetails.url} target='_blank' title='View Scheduling Set API'><i className="pi pi-info-circle" /></a></span></>; + suSet['icon'] = 'fab fa-fw fa-wpexplorer'; + suSet['children'] = [this.data.project]; + this.data['suSet'] = suSet; + this.setState({objNodes: [this.data.suSet]}); + } + } + + /** + * Prepare Project details for tree view + * @param {*} projectDetails + */ + getProjectDetails(projectDetails) { + if (projectDetails) { + let project = {}; + project['key'] = projectDetails.name; + project['label'] = <> Project ({projectDetails.name}) + <span className="find-obj-tree-view"><a href={`/project/view/${projectDetails.name}`} + target='_blank' title='View Project details'><i className="fa fa-eye" /></a></span> + <span><a href={projectDetails.url} target='_blank' title='View Project API'><i className="pi pi-info-circle" /></a></span></>; + project['icon'] = 'fab fa-fw fa-wpexplorer'; + this.data['project'] = project; + this.setState({objNodes: [this.data.project]}); + } + } + + /** + * Find project details for given id + * @param {*} id + * @returns + */ + async findProject(id){ + const projectDetails = await ProjectService.getProjectDetails(id); + this.getProjectDetails(projectDetails); + return projectDetails; + } + + render(){ + return( + <> + <PageHeader location={this.props.location} title={'Find Object'} + actions={[]} + /> + {this.state.objNodes.length>0 && + <> + <Tree value={this.state.objNodes} selectionMode="single" expandedKeys={true} style={{width: 'auto'}} /> + </> + } + {this.state.objNodes.length === 0 && + <> No Object found ! </> + } + </> + ) + } +} diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Search/index.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Search/index.js new file mode 100644 index 00000000000..fcfd0526ca2 --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Search/index.js @@ -0,0 +1,3 @@ +import {FindObjectResult} from './find.object.result'; + +export {FindObjectResult} ; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js index c296c76ff16..4f862988865 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js @@ -15,6 +15,7 @@ import SchedulingUnitCreate from './Scheduling/create'; import EditSchedulingUnit from './Scheduling/edit'; import { CycleList, CycleCreate, CycleView, CycleEdit } from './Cycle'; import { TimelineView, WeekTimelineView, ReservationCreate, ReservationList } from './Timeline'; +import { FindObjectResult } from './Search/' import SchedulingSetCreate from './Scheduling/excelview.schedulingset'; import Workflow from './Workflow'; import { Growl } from 'primereact/components/growl/Growl'; @@ -165,6 +166,12 @@ export const routes = [ component: ReservationCreate, name: 'Reservation Add', title: 'Reservation - Add' + }, + { + path: "/find-object", + component: FindObjectResult, + name: 'Find Object', + title: 'Find Object' } ]; -- GitLab