import { Component } from 'react'; import { Redirect, BrowserRouter as Router } from 'react-router-dom'; import classNames from 'classnames'; 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 { withFaroProfiler ,faro} from '@grafana/faro-react'; import './layout/layout.scss'; import 'primeflex/primeflex.css'; import './App.scss'; import './App.css'; import Auth from './authenticate/auth'; import pubsub from './utils/pubSub'; import { CustomDialog } from './layout/components/CustomDialog'; import AuthStore from './authenticate/auth.store'; import { Provider } from "react-redux"; import AuthComponent from './components/AuthComponent'; const { publish, subscribe } = pubsub(); export { publish, subscribe }; class App extends Component { constructor() { super(); this.state = { layoutMode: 'static', currentMenu: '', currentPath: '/', PageTitle: '', isBreadCrumbVisible: false, isDateTimeVisible: true, staticMenuInactive: localStorage.getItem('staticMenuInactive') === 'true', overlayMenuActive: localStorage.getItem('overlayMenuActive') === 'true', mobileMenuActive: localStorage.getItem('mobileMenuActive') === 'true', authenticated: true, findObjectPlaceholder: 'Sub Task', redirect: window.location?.pathname === '/' ? '/su/timelineview/week' : window.location?.pathname, isLogin: true }; this.onToggleMenu = this.onToggleMenu.bind(this); this.onMenuItemClick = this.onMenuItemClick.bind(this); this.setPageTitle = this.setPageTitle.bind(this); this.logout = this.logout.bind(this); this.validateAndLogout = this.validateAndLogout.bind(this); this.setSearchField = this.setSearchField.bind(this); this.toggleEditToggle = this.toggleEditToggle.bind(this); this.ReportSubMenu = [ { label: 'Failure ', icon: 'pi pi-fw pi-chart-bar', to: '/reports/failure', section: 'reports', isBreadCrumbVisible: false, isDateTimeVisible: false }, { label: 'Cycle ', icon: 'pi pi-fw pi-history', to: '/reports/cycle', section: 'reports', isBreadCrumbVisible: false, isDateTimeVisible: false }, { label: 'Project ', icon: 'pi pi-fw pi-table', to: '/reports/project', section: 'reports', isBreadCrumbVisible: false, isDateTimeVisible: false } ] this.menu = [ { label: 'Calendar', icon: 'pi pi-fw pi-calendar-times', to: '/su/timelineview/week', section: 'su/timelineview/week', isBreadCrumbVisible: false, isDateTimeVisible: true }, { label: 'Cycle', icon: 'pi pi-fw pi-spinner', to: '/cycle', section: 'cycle', isBreadCrumbVisible: true, isDateTimeVisible: false }, { label: 'Daily Schedule', icon: 'pi pi-fw pi-sun', to: '/constraint/view', section: 'system', isBreadCrumbVisible: false, isDateTimeVisible: true }, { label: 'Project', icon: 'pi pi-fw pi-compass', to: '/project', section: 'project', isBreadCrumbVisible: true, isDateTimeVisible: false }, { label: 'Reservations', icon: 'pi pi-fw pi-book', to: '/reservation/list', section: 'system', isBreadCrumbVisible: false, isDateTimeVisible: true }, { label: 'Reports', icon: 'pi pi-fw pi-chart-bar', to: '/reports/failure', section: 'reports', isBreadCrumbVisible: false, isDateTimeVisible: false, items: this.ReportSubMenu }, { label: 'Scheduling Units', icon: 'pi pi-fw pi-calendar', to: '/schedulingunit', section: 'schedulingunit', isBreadCrumbVisible: true, isDateTimeVisible: false }, { label: 'Stations', icon: 'pi pi-fw pi-wifi pi-rotate', to: '/station/list', section: 'system', isBreadCrumbVisible: false, isDateTimeVisible: true }, { label: 'System Events', icon: 'pi pi-fw pi-bolt', to: '/systemevent/list', section: 'system', isBreadCrumbVisible: false, isDateTimeVisible: true }, { label: 'Tasks', icon: 'pi pi-fw pi-check-square', to: '/task', isBreadCrumbVisible: true, isDateTimeVisible: false }, { label: 'Workflow', icon: 'pi pi-fw pi-sitemap', to: '/su/workflow', section: 'workflow', isBreadCrumbVisible: true, isDateTimeVisible: false }, ]; } onToggleMenu(event) { this.menuClick = true; if (this.isDesktop()) { this.setState(prevState => ({ staticMenuInactive: !prevState.staticMenuInactive }), () => { localStorage.setItem('staticMenuInactive', !this.state.staticMenuInactive); }); } else { this.setState(prevState => ({ mobileMenuActive: !prevState.mobileMenuActive }), () => { localStorage.setItem('mobileMenuActive', this.state.mobileMenuActive); }); } event.preventDefault(); } onMenuItemClick(event) { faro?.api?.pushEvent('App onMenuItemClick'); this.setState({ currentMenu: event.item.label, currentPath: event.item.path, isBreadCrumbVisible: event.item.isBreadCrumbVisible, isDateTimeVisible: event.item.isDateTimeVisible }); } isDesktop() { return window.innerWidth > 1024; } setPageTitle(PageTitle) { if (PageTitle !== this.state.PageTitle) { this.setState({ PageTitle }) } } /** * Logout and redirect to login page. */ logout() { Auth.logout(); this.setState({ redirect: "/login", isLogin: false }); faro?.api?.pushEvent('Logout'); } /** * Get confirmation if any of the page has unsaved data and then logout. * @returns */ validateAndLogout() { if (this.state.isEditDirty) { this.toggleDirtyDialog(this.logout); } else { this.logout(); } } toggleEditToggle() { this.setState(prevState => ({ showEditDialog: !prevState.showEditDialog })); } componentDidMount() { subscribe('edit-dirty', (flag) => { this.setState({ isEditDirty: flag }, () => { if (flag) { window.addEventListener("beforeunload", reloadDirty); window.history.pushState(null, document.title, window.location.href); window.addEventListener('popstate', this.onBackButtonEvent); } else { window.removeEventListener("beforeunload", reloadDirty); } }); }); let reloadDirty = function (e) { let confirmationMessage = "\\o/"; (e).returnValue = confirmationMessage; //Gecko + IE return confirmationMessage; }; } componentDidUpdate() { if (window.location?.pathname === '/') { this.setState({ redirect: '/su/timelineview/week', isBreadCrumbVisible: false, isDateTimeVisible: true }) } } onBackButtonEvent = (e) => { e.preventDefault(); if (this.state.isEditDirty) { const leavePage = window.confirm("Do you want to discard your changes? Your changes may not be saved."); if (leavePage) { this.setState({ isEditDirty: false }); window.history.back(); } else { window.history.pushState(null, document.title, window.location.href); } } } close = () => { this.setState({ showDirtyDialog: false }); } /** * Cancel edit and redirect to Cycle View page */ cancelEdit = () => { this.setState({ isEditDirty: false, showDirtyDialog: false }); this.state.toPathCallback(); } toggleDirtyDialog = (callback) => { this.setState({ showDirtyDialog: true, toPathCallback: callback }); } onBreadcrumbClick = (callback) => { if (this.state.isEditDirty) { this.toggleDirtyDialog(callback); return; } callback(); } /** * Set search param * @param {*} key * @param {*} value */ setSearchField(key, value) { this.setState({ objectType: key, findObjectId: value, redirect: "/find/object/" + key + "/" + value }); } render() { const wrapperClass = classNames('layout-wrapper', { 'layout-static': this.state.layoutMode === 'static', 'layout-static-sidebar-inactive': this.state.staticMenuInactive , 'layout-mobile-sidebar-active': this.state.mobileMenuActive }); return ( <div className="App"> <Provider store={AuthStore}> <div className={wrapperClass}> {/* Load main routes and application only if the application is authenticated */} {this.state.redirect && <AuthComponent> <AppTopbar onToggleMenu={this.onToggleMenu} isLoggedIn={this.state.authenticated} onLogout={this.validateAndLogout} setSearchField={this.setSearchField} isDateTimeVisible={this.state.isDateTimeVisible} /> <Router basename={this.state.currentPath}> <AppMenu model={this.menu} toggleDirtyDialog={this.toggleDirtyDialog} isEditDirty={this.state.isEditDirty} onMenuItemClick={this.onMenuItemClick} active={this.state.menuActive} /> <div className="layout-main"> {(this.state.redirect || this.state.redirect === "/login") && <Redirect to={{ pathname: this.state.redirect === "/login" ? "/su/timelineview/week" : this.state.redirect, state: { userrole: this.state.userrole } }} />} {(this.state.isBreadCrumbVisible) && <AppBreadcrumb setPageTitle={this.setPageTitle} section={this.state.currentMenu} onBreadcrumbClick={this.onBreadcrumbClick} /> } <RoutedContent /> </div> </Router> </AuthComponent> } <CustomDialog type="confirmation" visible={this.state.showDirtyDialog} width="40vw" header={'Confirmation'} message={'Do you want to discard your changes? Your changes may not be saved.'} content={''} onClose={this.close} actions={[{ id: "yes", title: 'Discard', callback: this.cancelEdit, className: 'act-btn-dispose' }, { id: "no", title: 'Cancel', className: 'act-btn-cancel', callback: this.close }]}> </CustomDialog> </div> </Provider> </div> ); } } export default handleResponse(withFaroProfiler(App));