diff --git a/SAS/TMSS/frontend/tmss_webapp/src/App.js b/SAS/TMSS/frontend/tmss_webapp/src/App.js index 0093bcdce0d3e12657aac0152cf56fb689bb5464..4929c293d561f889dde4fa7d5545e55faeb58ef9 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/App.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/App.js @@ -1,14 +1,13 @@ import { Component } from 'react'; -import { Redirect, BrowserRouter as Router } from 'react-router-dom'; +import { Redirect, BrowserRouter as Router, withRouter } from 'react-router-dom'; import classNames from 'classnames'; -import { AppTopbar } from './layout/components/AppTopbar'; +import AppTopbar from './layout/components/AppTopbar'; import AppMenu from './layout/components/AppMenu'; import { RoutedContent } from './routes'; import AppBreadcrumb from "./layout/components/AppBreadcrumb"; import handleResponse from "./response.handler" import 'primeicons/primeicons.css'; - import 'primereact/resources/primereact.css'; import 'primereact/resources/themes/nova/theme.css'; import './layout/layout.scss'; @@ -24,6 +23,7 @@ import { CustomDialog } from './layout/components/CustomDialog'; import AuthStore from './authenticate/auth.store'; import { Provider } from "react-redux"; import AuthComponent from './components/AuthComponent'; +import DateTimeInfo from './layout/components/DateTimeInfo'; const { publish, subscribe } = pubsub(); @@ -69,6 +69,8 @@ class App extends Component { { label: 'Workflow', icon: 'pi pi-fw pi-sitemap', to: '/su/workflow', section: 'workflow', isBreadCrumbVisible: true,isDateTimeVisible:false }, { label: 'Week View', icon: 'pi pi-fw pi-calendar-times', to: '/su/timelineview/week', section: 'su/timelineview/week', isBreadCrumbVisible: false ,isDateTimeVisible:true}, { label: 'Reports', icon: 'pi pi-fw pi-chart-bar', to: '/reports', section: 'reports', isBreadCrumbVisible: false ,isDateTimeVisible:false}, + { label: 'System Events', icon: 'pi pi-fw pi-bolt', to: '/systemevent/list', section: 'reports', isBreadCrumbVisible: false ,isDateTimeVisible:false}, + ]; } onWrapperClick() { @@ -154,6 +156,7 @@ class App extends Component { } } + toggleEditToggle() { this.setState({ showEditDialog: !this.state.showEditDialog }); } @@ -249,7 +252,6 @@ class App extends Component { <div className={wrapperClass}> {/* Load main routes and application only if the application is authenticated */} {this.state.redirect && - // <AuthComponent isLogin = {this.state.isLogin}> <AuthComponent> <AppTopbar onToggleMenu={this.onToggleMenu} diff --git a/SAS/TMSS/frontend/tmss_webapp/src/authenticate/auth.js b/SAS/TMSS/frontend/tmss_webapp/src/authenticate/auth.js index 60a488ec8e399c2ddfbf71e965dc2729921214ef..34784aee42baecb3ac703db93b9d974cf2f12f78 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/authenticate/auth.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/authenticate/auth.js @@ -20,7 +20,7 @@ const Auth = { } else { //Keycloak authentication const res = await AuthService.getKeycloakAuthState(); - if (res && res.is_authenticated) { + if (res?.is_authenticated) { localStorage.setItem("loginType", 'Keycloak'); const cookies = document.cookie.split(';').reduce((prev, current) => { const [name, value] = current.split(/\s?(.*?)=(.*)/).splice(1, 2); 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 b8e0fb0582e4bf5492dd771025da2e181728d95b..e5b5f1676cb1565bf5c75f24e3d032cfe724dd9b 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/authenticate/auth.store.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/authenticate/auth.store.js @@ -10,19 +10,7 @@ let permissionStack = {}; */ const rolePermissionReducer = (state, action) => { switch (action.type) { - /* case 'project': { - if(state && state['project']) { - delete state['project']; - } - return { ...state, project: permissionStack['project']}; - } - case 'scheduleunit': { - if(state && state['scheduleunit']) { - delete state['scheduleunit']; - } - return { ...state, scheduleunit:permissionStack['scheduleunit']}; - }*/ - case 'loadpermission': { + case 'loadpermission': { permissionStack = action.payload; const keys = Object. keys(permissionStack); for ( const key of keys) { @@ -43,10 +31,7 @@ const rolePermissionReducer = (state, action) => { default: { const actionType = action.type; - if (permissionStack && permissionStack[actionType]) { - //if(state && state[actionType]) { - //delete state[actionType]; - // } + if (permissionStack?.[actionType]) { return { ...state, [actionType]:permissionStack[actionType]}; } else { return { ...state, rolePermission: {}}; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/images/Keycloak_Logo.png b/SAS/TMSS/frontend/tmss_webapp/src/images/Keycloak_Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..48e18430cb06664c10a32008e13dd7b02ab3d879 Binary files /dev/null and b/SAS/TMSS/frontend/tmss_webapp/src/images/Keycloak_Logo.png differ 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 03e8e60b54347ada33e404bd578952206551a46f..9ef04b3c9f1eec17ac5ca0f6e1a960a8ec265841 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/layout/components/AppTopbar.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/AppTopbar.js @@ -1,5 +1,5 @@ -import React, {Component} from 'react'; +import React, { useState} from 'react'; import 'primeicons/primeicons.css'; import 'primereact/resources/themes/nova/theme.css'; import 'primereact/resources/primereact.css'; @@ -8,64 +8,77 @@ import { PropTypes } from 'prop-types'; import Auth from '../../authenticate/auth'; import { FindObject } from './FindObject'; import DateTimeInfo from "./DateTimeInfo"; -export class AppTopbar extends Component { +import { Dialog } from 'primereact/dialog'; +import UserOverView from '../../routes/User/UserOverView'; + + /* constructor(props) { super(props); this.state = { username: Auth.getUser().name, }; } - - static defaultProps = { - onToggleMenu: null - } - - static propTypes = { - onToggleMenu: PropTypes.func.isRequired, - isLoggedIn: PropTypes.bool, - isDateTimeVisible:PropTypes.bool, - setSearchField:PropTypes.func - } - - render() { - return ( + */ +const AppTopbar = (props) => { + + + const [visible, setVisible] = useState(false); + const user = Auth.getUser(); + + return ( <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}> + <button className="p-link layout-menu-button" onClick={props.onToggleMenu}> <i className="pi pi-bars"></i></button> <span className="header-title">TMSS</span> <span className="header-by">by</span> <span className="header-company">ASTRON</span> - {this.props.isLoggedIn && + {props.isLoggedIn && <React.Fragment> - {this.props.isDateTimeVisible && + {props.isDateTimeVisible && <div className="top-date-bar"> <DateTimeInfo/> </div> } - - <div className="top-right-bar"> + <div className="top-right-bar"> <a className="p-link layout-menu-button" style= {{marginLeft: '8px', marginRight: '8px'}} title="Documentation" href="https://support.astron.nl/confluence/display/public/TMSS+User+Manual" target="_blank" rel="noreferrer "> <i className="pi pi-file-o"></i></a> <a className="p-link layout-menu-button" title="Helpdesk" href="https://support.astron.nl/sdchelpdesk" target="_blank" rel="noreferrer "> <span><i className="pi pi-question-circle"></i></span></a> - <button className="p-link layout-menu-button" onClick={this.props.onLogout} title="Logout"> + + <button className="p-link layout-menu-button" onClick={props.onLogout} title="Logout"> <i className="pi pi-power-off"></i></button> - <span style= {{marginLeft: '8px'}}><i className="fa fa-user" title={`Logged in as ${this.state.username}`}></i></span> + <button className="p-link layout-menu-button" onClick={() => setVisible(true)} title={`Logged in as ${user?.name}`}> + <i className="pi pi-user"></i></button> </div> + <Dialog header="TMSS User Permission" visible={visible} style={{ width: '50vw' }} onHide={() => setVisible(false)} maximizable > + <UserOverView></UserOverView> + </Dialog> + </React.Fragment> } - <FindObject setSearchField={this.props.setSearchField} /> + <FindObject setSearchField={props.setSearchField} /> </div> </div> ) - } } + + +AppTopbar.propTypes = { + onToggleMenu: PropTypes.func.isRequired, + isLoggedIn: PropTypes.bool, + isDateTimeVisible:PropTypes.bool, + setSearchField:PropTypes.func, + onLogout:PropTypes.func, + username:PropTypes.string +} + +export default AppTopbar \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/User/UserOverView.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/User/UserOverView.js new file mode 100644 index 0000000000000000000000000000000000000000..c58219c62ab1e84bc4e1198fbde3c72b421202b9 --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/User/UserOverView.js @@ -0,0 +1,62 @@ +import { useState, useEffect } from 'react'; +import { DataTable } from 'primereact/datatable'; +import { Column } from 'primereact/column'; +import 'primereact/resources/themes/saga-blue/theme.css'; +import 'primereact/resources/primereact.min.css'; +import 'primeicons/primeicons.css'; +import Auth from '../../authenticate/auth.js'; +import PermissionStackUtil from '../../authenticate/permission.stack.handler'; +const UserOverView = () => { + const [permissions, setPermissions] = useState([]); + + useEffect(() => { + const fetchData = async () => { + try { + const user = Auth.getUser(); + const userPermissions = await PermissionStackUtil.getPermissions(user); + const objectpermissions = Object.entries(userPermissions); + + setPermissions(objectpermissions); + } catch (error) { + console.error('Error fetching user permissions:', error); + } + }; + + fetchData(); + }, []); // The empty dependency array ensures that this effect runs once when the component mounts + + const checkMarkTemplate = (fieldvalue) => { + const classes = fieldvalue===true ? 'pi true-icon pi-check-circle' : ''; + return <i className={classes}></i>; +}; + + + + + + return ( + <div> + <DataTable value={permissions} resizableColumns columnResizeMode="expand" scrollable showGridlines stripedRows > + <Column field="0" header="Module" frozen /> + <Column field="1.create" header="Create" dataType="boolean" body={(rowData ) => checkMarkTemplate(rowData[1].create)} /> + <Column field="1.edit" header="Edit" dataType="boolean" body={(rowData ) => checkMarkTemplate(rowData[1].edit)} /> + <Column field="1.delete" header="Delete" dataType="boolean" body={(rowData ) => checkMarkTemplate(rowData[1].delete)} /> + <Column field="1.list" header="List" dataType="boolean" body={(rowData ) => checkMarkTemplate(rowData[1].list)} /> + <Column field="1.add" header="Add" dataType="boolean" body={(rowData ) => checkMarkTemplate(rowData[1].add)} /> + <Column field="1.setting" header="Setting" dataType="boolean" body={(rowData ) => checkMarkTemplate(rowData[1].setting)} /> + <Column field="1.canceltask" header="Cancel Task" dataType="boolean" body={(rowData ) => checkMarkTemplate(rowData[1].canceltask)} /> + <Column field="1.addreservation" header="addreservation" dataType="boolean" body={(rowData ) => checkMarkTemplate(rowData[1].addreservation)} /> + <Column field="1.addsystemevent" header="addsystemevent" dataType="boolean" body={(rowData ) => checkMarkTemplate(rowData[1].addsystemevent)} /> + <Column field="1.listreservation" header="listreservation" dataType="boolean" body={(rowData ) => checkMarkTemplate(rowData[1].listreservation)} /> + <Column field="1.listsystemevent" header="listsystemevent" dataType="boolean" body={(rowData ) => checkMarkTemplate(rowData[1].listsystemevent)} /> + <Column field="1.decide_acceptance" header="decide_acceptance" dataType="boolean" body={(rowData ) => checkMarkTemplate(rowData[1].decide_acceptance)} /> + <Column field="1.pi_verification" header="pi_verification" dataType="boolean" body={(rowData ) => checkMarkTemplate(rowData[1].pi_verification)} /> + <Column field="1.qa_reporting_sos" header="qa_reporting_sos" dataType="boolean" body={(rowData ) => checkMarkTemplate(rowData[1].qa_reporting_sos)} /> + <Column field="1.qa_reporting_to" header="qa_reporting_to" dataType="boolean" body={(rowData ) => checkMarkTemplate(rowData[1].qa_reporting_to)} /> + <Column field="1.unpin_data" header="unpin_data" dataType="boolean" body={(rowData ) => checkMarkTemplate(rowData[1].unpin_data)} /> + </DataTable> + </div> + ); +}; + +export default UserOverView; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js index 01aea2f4fb2f9f09f9fe5afd7004e9057d74e8e8..066981bce81b6786aa0273246ca6e459e9b366aa 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js @@ -11,7 +11,8 @@ import { Scheduling } from './Scheduling'; import { TaskEdit, TaskView, DataProduct, TaskList } from './Task'; import ViewSchedulingUnit from './Scheduling/ViewSchedulingUnit' import SchedulingUnitCreate from './Scheduling/create'; -import EditSchedulingUnit from './Scheduling/edit'; +import UserOverView from './User/UserOverView'; +import EditSchedulingUnit from './Scheduling/edit'; import { CycleList, CycleCreate, CycleView, CycleEdit } from './Cycle'; import { ReservationCreate, ReservationList, ReservationView, ReservationEdit } from './Reservation'; import { SystemEventCreate, SystemEventList, SystemEventView, SystemEventEdit } from './SystemEvent'; @@ -29,7 +30,9 @@ export const routes = [ { path: "/not-found", component: NotFound, - }, { + }, + + { path: "/access-denied", component: AccessDenied, name: 'Access Denied', @@ -39,7 +42,15 @@ export const routes = [ component: Dashboard, name: 'Dashboard', title: 'Dashboard' - }, { + }, + { + path: "/useroverview", + component: UserOverView, + name: 'User Overview', + title: 'User Overview' + }, + + { path: "/schedulingunit", component: Scheduling, name: 'Scheduling Unit', @@ -267,7 +278,7 @@ export const RoutedContent = () => { <Toast ref={(el) => setAppGrowl(el)} /> <Switch> {/* <Redirect from="/" to="/" exact /> */} - {routes.map(routeProps => <ProtectedRoute {...routeProps} exact="true" key={(routeProps.path || routeProps.name) + '_switch'} />)} + {routes.map(routeProps => <ProtectedRoute {...routeProps} exact key={(routeProps.path || routeProps.name) + '_switch'} />)} </Switch> </> );