diff --git a/SAS/TMSS/frontend/tmss_webapp/src/App.js b/SAS/TMSS/frontend/tmss_webapp/src/App.js index 294ed19468acfcf2970b03f7bec4508b4a353156..ff9ec6592d68f49d55ada8aacb65f52b38c38221 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/App.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/App.js @@ -3,7 +3,7 @@ import { Redirect, BrowserRouter as Router , withRouter } from 'react-router-do import classNames from 'classnames'; import AppTopbar from './layout/components/AppTopbar'; -import AppMenu from './layout/components/AppMenu'; +import AppMenu from './layout/components/AppMenu'; import { RoutedContent } from './routes'; import AppBreadcrumb from "./layout/components/AppBreadcrumb"; import handleResponse from "./response.handler" @@ -60,14 +60,20 @@ class App extends Component { 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: 'Dashboard', icon: 'pi pi-fw pi-home', to:'/dashboard',section: 'dashboard'}, - { label: 'Calendar', icon: 'pi pi-fw pi-calendar-times', to: '/su/timelineview/week', section: 'su/timelineview/week', isBreadCrumbVisible: false ,isDateTimeVisible:true}, + { 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', section: 'reports', isBreadCrumbVisible: false ,isDateTimeVisible:false}, + { 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}, @@ -94,28 +100,24 @@ class App extends Component { this.menuClick = true; if (this.isDesktop()) { if (this.state.layoutMode === 'overlay') { - this.setState({ - overlayMenuActive: !this.state.overlayMenuActive - }, () => { + this.setState(prevState => ({ + overlayMenuActive: !prevState.overlayMenuActive + }), () => { localStorage.setItem('overlayMenuActive', this.state.overlayMenuActive); - } - ); - } - else if (this.state.layoutMode === 'static') { - localStorage.setItem('staticMenuInactive', !this.state.staticMenuInactive); - this.setState({ - staticMenuInactive: !this.state.staticMenuInactive + }); + } else if (this.state.layoutMode === 'static') { + this.setState(prevState => ({ + staticMenuInactive: !prevState.staticMenuInactive + }), () => { + localStorage.setItem('staticMenuInactive', !this.state.staticMenuInactive); }); } - } - else { - const mobileMenuActive = this.state.mobileMenuActive; - this.setState({ - mobileMenuActive: !mobileMenuActive - }, () => { + } else { + this.setState(prevState => ({ + mobileMenuActive: !prevState.mobileMenuActive + }), () => { localStorage.setItem('mobileMenuActive', this.state.mobileMenuActive); - } - ); + }); } event.preventDefault(); } @@ -165,9 +167,10 @@ class App extends Component { toggleEditToggle() { - this.setState({ showEditDialog: !this.state.showEditDialog }); + this.setState(prevState => ({ + showEditDialog: !prevState.showEditDialog + })); } - componentDidMount() { subscribe('edit-dirty', (flag) => { diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/_overrides.scss b/SAS/TMSS/frontend/tmss_webapp/src/layout/_overrides.scss index 17c54f381bc4ccedaceaa3894ee19db63236bd21..e7736867c3dfb38c56832395bbbdd8126601df23 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/layout/_overrides.scss +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/_overrides.scss @@ -35,8 +35,6 @@ max-height: calc(#{$vh * 100} - 20.3em) // since calendar getting inserted to table, because of above overflow, not getting visible // so its hacky one based on calendar height - // padding-top: 350px; - // margin-top: -350px; } .tmss-table .p-datepicker table td { overflow-wrap: unset; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/components/AppMenu.js b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/AppMenu.js index b318d83f3f759c05dc07e99feb32baa4f2ec43cd..e4256aa9396591bb292d1fb32ac4c454b69e8b4f 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/layout/components/AppMenu.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/AppMenu.js @@ -1,140 +1,8 @@ -import React, { Component } from 'react'; +import { Component } from 'react'; import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import { Button } from 'primereact/button'; -import { withRouter } from 'react-router-dom/cjs/react-router-dom.min'; -import _ from 'lodash'; -class AppSubmenu extends Component { - - static defaultProps = { - className: null, - items: null, - onMenuItemClick: null, - root: false, - permissions: null - - } - - static propTypes = { - className: PropTypes.string, - items: PropTypes.array, - onMenuItemClick: PropTypes.func, - root: PropTypes.bool, - permissions: PropTypes.array - } - - constructor(props) { - super(props); - this.state = {activeIndex: null}; - } - - onMenuItemClick(event, item, index) { - if(event.ctrlKey){ - return; - } - event.preventDefault(); - //avoid processing disabled items - if(item.disabled) { - event.preventDefault(); - return true; - } - - if (this.props.isEditDirty) { - this.props.toggleDirtyDialog(() => this.props.history.push(item.to)); - return; - } - - //execute command - if(item.command) { - item.command({originalEvent: event, item: item}); - } - - if(index === this.state.activeIndex) - this.setState({activeIndex: null}); - else - this.setState({activeIndex: index}); - - if(this.props.onMenuItemClick) { - this.props.onMenuItemClick({ - originalEvent: event, - item: item - }); - } - this.props.history.push(item.to); - } - - componentDidMount() { - if (!this.props.items) { - return; - } - let pathname = window.location.pathname; - if (_.includes(pathname, '/api') && !pathname.endsWith("/")) { - pathname = pathname + '/'; - window.location.replace(pathname); - } - for (let i = 0; i < this.props.items.length; i++) { - if (pathname.indexOf(this.props.items[i].section) > -1) { - this.setState({activeIndex: i}); - break - } - } - } - - - renderLinkContent(item) { - let submenuIcon = item.items && <i className="pi pi-fw pi-angle-down menuitem-toggle-icon"></i>; - let badge = item.badge && <span className="menuitem-badge">{item.badge}</span>; - - return ( - <React.Fragment> - <i className={item.icon}></i> - <Button className="nav-btn nav-btn-tooltip" tooltip={item.label}></Button> - <Button className="nav-btn nav-btn-notooltip"></Button> - <span>{item.label}</span> - {submenuIcon} - {badge} - </React.Fragment> - ); - } - - renderLink(item, i) { - let content = this.renderLinkContent(item); - - if (item.to) { - return ( - <a href={item.to} className="active-route" onClick={(e) => this.onMenuItemClick(e, item, i)} target={item.target}> - {content} - </a> - ) - } - else { - return ( - <a href={item.url} onClick={(e) => this.onMenuItemClick(e, item, i)} target={item.target}> - {content} - </a> - ); - - } - } - - render() { - let items = this.props.items?.map((item, i) => { - let active = this.state.activeIndex === i; - let styleClass = classNames(item.badgeStyleClass, {'active-menuitem': active && item.to}); - return ( - <li className={styleClass} key={item.label}> - {item.items && this.props.root===true && <div className='arrow'></div>} - {this.renderLink(item, i)} - <AppSubmenu toggleDirtyDialog={this.props.toggleDirtyDialog} isEditDirty={this.props.isEditDirty} history={this.props.history} items={item.items} onMenuItemClick={this.props.onMenuItemClick}/> - </li> - ); - - }); - - return items ? <ul className={this.props.className}>{items}</ul> : null; - } -} +import { withRouter } from 'react-router-dom'; +import AppSubmenu from './AppSubMenu' export class AppMenu extends Component { @@ -143,16 +11,12 @@ export class AppMenu extends Component { onMenuItemClick: null } - static propTypes = { - model: PropTypes.array, - onMenuItemClick: PropTypes.func - } + render() { return ( <div className={'layout-sidebar layout-sidebar-light'} > <div className="layout-menu-container"> - {/* <AppSubmenu items={this.props.model} permissions={authenticationService.currentUserValue.permissions} className="layout-menu" onMenuItemClick={this.props.onMenuItemClick} root={true}/> */} <AppSubmenu toggleDirtyDialog={this.props.toggleDirtyDialog} isEditDirty={this.props.isEditDirty} history={this.props.history} items={this.props.model} className="layout-menu" onMenuItemClick={this.props.onMenuItemClick} root={true}/> </div> @@ -160,5 +24,11 @@ export class AppMenu extends Component { ); } } - +AppMenu.propTypes = { + model: PropTypes.array, + onMenuItemClick: PropTypes.func, + isEditDirty:PropTypes.bool, + history : PropTypes.object, + toggleDirtyDialog : PropTypes.func +} export default withRouter(AppMenu) \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/components/AppSubMenu.js b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/AppSubMenu.js new file mode 100644 index 0000000000000000000000000000000000000000..4346256fe5bde551039f1619a4460ab538446a15 --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/AppSubMenu.js @@ -0,0 +1,136 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import { Button } from 'primereact/button'; +import _ from 'lodash'; + +export default class AppSubmenu extends Component { + + static defaultProps = { + className: null, + items: null, + onMenuItemClick: null, + root: false, + permissions: null + + } + + static propTypes = { + className: PropTypes.string, + items: PropTypes.array, + onMenuItemClick: PropTypes.func, + root: PropTypes.bool, + permissions: PropTypes.array + } + + constructor(props) { + super(props); + this.state = {activeIndex: null}; + } + + onMenuItemClick(event, item, index) { + if(event.ctrlKey){ + return; + } + event.preventDefault(); + //avoid processing disabled items + if(item.disabled) { + event.preventDefault(); + return true; + } + + if (this.props.isEditDirty) { + this.props.toggleDirtyDialog(() => this.props.history.push(item.to)); + return; + } + + //execute command + if(item.command) { + item.command({originalEvent: event, item: item}); + } + + if(index === this.state.activeIndex) + this.setState({activeIndex: null}); + else + this.setState({activeIndex: index}); + + if(this.props.onMenuItemClick) { + this.props.onMenuItemClick({ + originalEvent: event, + item: item + }); + } + this.props.history.push(item.to); + } + + componentDidMount() { + if (!this.props.items) { + return; + } + let pathname = window.location.pathname; + if (_.includes(pathname, '/api') && !pathname.endsWith("/")) { + pathname = pathname + '/'; + window.location.replace(pathname); + } + for (let i = 0; i < this.props.items.length; i++) { + if (pathname.indexOf(this.props.items[i].section) > -1) { + this.setState({activeIndex: i}); + break + } + } + } + + + renderLinkContent(item) { + let submenuIcon = item.items && <i className="pi pi-fw pi-angle-down menuitem-toggle-icon"></i>; + let badge = item.badge && <span className="menuitem-badge">{item.badge}</span>; + + return ( + <React.Fragment> + <i className={item.icon}></i> + <Button className="nav-btn nav-btn-tooltip" tooltip={item.label}></Button> + <Button className="nav-btn nav-btn-notooltip"></Button> + <span>{item.label}</span> + {submenuIcon} + {badge} + </React.Fragment> + ); + } + + renderLink(item, i) { + let content = this.renderLinkContent(item); + + if (item.to) { + return ( + <a href={item.to} className="active-route" onClick={(e) => this.onMenuItemClick(e, item, i)} target={item.target}> + {content} + </a> + ) + } + else { + return ( + <a href={item.url} onClick={(e) => this.onMenuItemClick(e, item, i)} target={item.target}> + {content} + </a> + ); + + } + } + + render() { + let items = this.props.items?.map((item, i) => { + let active = this.state.activeIndex === i; + let styleClass = classNames(item.badgeStyleClass, {'active-menuitem': active && item.to}); + return ( + <li className={styleClass} key={item.label}> + {item.items && this.props.root===true && <div className='arrow'></div>} + {this.renderLink(item, i)} + <AppSubmenu toggleDirtyDialog={this.props.toggleDirtyDialog} isEditDirty={this.props.isEditDirty} history={this.props.history} items={item.items} onMenuItemClick={this.props.onMenuItemClick}/> + </li> + ); + + }); + + return items ? <ul className={this.props.className}>{items}</ul> : null; + } +} \ No newline at end of file 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 9ef04b3c9f1eec17ac5ca0f6e1a960a8ec265841..d18f9e12d711ee3debeafe3e46dd2fee821eba79 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/layout/components/AppTopbar.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/AppTopbar.js @@ -12,14 +12,6 @@ import { Dialog } from 'primereact/dialog'; import UserOverView from '../../routes/User/UserOverView'; - /* - constructor(props) { - super(props); - this.state = { - username: Auth.getUser().name, - }; - } - */ const AppTopbar = (props) => { diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/components/PageActionMenu.js b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/PageActionMenu.js index 8ccdf1d975318edd7cc3f8dd10aad7ddaeb0fcfe..9608d0e946c774c83407d39897b0a6a1335ed9ad 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/layout/components/PageActionMenu.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/PageActionMenu.js @@ -91,11 +91,6 @@ const PageActionMenu = ({ actions, className }) => { <span key={action.title + '_page_header'} className={action.className} title={action.title || ''}>{action.content}</span> ); - case 'tagv2': - return ( - <span key={action.title + '_page_header'} className={action.className} - title={action.title || ''}>{action.content}</span> - ); case 'calendar': return ( <div key={`index`}><span>{action.title}</span> <Calendar value={action.selected} onChange={(e) => setSelected(e, action)} dateFormat="yy-mm-dd" showIcon className={action.className} /> </div> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/components/PageHeader.js b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/PageHeader.js index 46f217f1ca14774346f3816ab6573b3f83e70b06..2553744abe361139c7984d67b284f7d598cea687 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/layout/components/PageHeader.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/PageHeader.js @@ -10,7 +10,7 @@ const PageHeader = ({title, subTitle, actions, className, ...props}) => { useEffect(() => { - const currentRoute = routes.find(route => matchPath(props.location.pathname, { + const currentRoute = routes.find(route => matchPath(props.location?.pathname, { path: route.path, exact: true, strict: true @@ -20,7 +20,7 @@ const PageHeader = ({title, subTitle, actions, className, ...props}) => { return; } setPage(currentRoute); - }, [props.location.pathname]); + }, [props.location?.pathname]); diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_report.scss b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_report.scss index ce2983c4dcf363f661068d25c8cb491c15316601..e9712df54048fc796cd757a6b8b43b52c32d59cd 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_report.scss +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_report.scss @@ -77,3 +77,39 @@ padding: 0.429rem 0 !important; } + + +.FailureReport { + + Button { + height: 30px; + margin-left: 10px; + } + + .FailureCalender{ + height: 30px; + + } + + +} + +.failureProgress +{ + margin-top: 5px; +} + +.chartbox{ + background-color: white; + box-shadow: 2px 2px 4px 2px rgba(0, 0, 0, 0.2); + margin:5px; + + .yaxesrotate + { + position: relative; + font-size: 10px; + z-index: 1; + top:20px; + left:5px; + } +} \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_responsive.scss b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_responsive.scss index e98865cd1182969337a4d721bb20f203cba442f3..292a5da8e3067a41e54607a30f7bd37475ec27c5 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_responsive.scss +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_responsive.scss @@ -1,4 +1,15 @@ @media (min-width: 1025px) { + .layout-static-sidebar-inactive{ + .menuitem-toggle-icon + { + left:30px; + } +} + + + ul.layout-menu > li ul { + padding-left: 10px; + } .layout-wrapper { &.layout-overlay { .layout-sidebar { @@ -58,7 +69,7 @@ } @media (max-width: 875px) { - + body { min-width: 787px; } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.main.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.main.js index a3803cb28824377baceabbbff8d68ef6dd259f2a..b7f8ea46f793a212cf719b0493c3b404f3474df6 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.main.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/cycle/report.main.js @@ -18,6 +18,7 @@ import CycleService from '../../../services/cycle.service'; import ReportService from '../../../services/report.service'; import CycleReportContent from './report.content' import ReportBusiness from './report.business' +import PageHeader from '../../../layout/components/PageHeader'; /** @@ -389,8 +390,9 @@ const CycleReportMain = forwardRef(() => { setReportCycles([]); } - return ( - <React.Fragment> + return ( <div className='routerpage'> + <PageHeader title={'Cycle Report'} /> + {pageLoading && <AppLoader />} {!pageLoading && @@ -439,7 +441,7 @@ const CycleReportMain = forwardRef(() => { content={dialog.content} width={dialog.width} showIcon={dialog.showIcon} onClose={() => closeDialog} onCancel={() => closeDialog} onSubmit={() => dialog.onSubmit} /> } - </React.Fragment> + </div> ); }) diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/failure/failure.report.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/failure/failure.report.js new file mode 100644 index 0000000000000000000000000000000000000000..0f67a766e21425c0cde180864048c7277bcc7971 --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/failure/failure.report.js @@ -0,0 +1,403 @@ +import { useEffect, useState } from 'react'; +import { Chart } from 'primereact/chart'; +import jsPDF from 'jspdf'; +import { Calendar } from 'primereact/calendar'; +import { Button } from 'primereact/button'; +import { ProgressBar } from 'primereact/progressbar'; +import ReportService from '../../../services/report.service'; +import html2canvas from 'html2canvas'; +import { DataTable } from 'primereact/datatable'; +import { Column } from 'primereact/column'; +import PageHeader from '../../../layout/components/PageHeader'; +import moment from 'moment'; + +export default function FailureReport() { + + const [dates, setDates] = useState([new Date("2023-01-01"), new Date("2023-12-31")]); + const [checked, setChecked] = useState(""); + const [isLoading, setIsLoading] = useState(null); + const [failureReport, setFailureReport] = useState(null); + const [chartData_Issue_count, setChartData_Issue_count] = useState({}); + const [chartData_Issue_count_options, setChartData_Issue_count_options] = useState({}); + + const [chartData_Issue_total_duration, setChartData_Issue_total_duration] = useState({}); + const [chartData_Issue_total_duration_options, setChartData_Issue_total_duration_options] = useState({}); + const [chartData_sub_Issue_count, setChartData_sub_Issue_count] = useState({}); + const [chartData_sub_Issue_count_options, setChartData_sub_Issue_count_options] = useState({}); + const [chartData_sub_Issue_total_duration, setChartData_sub_Issue_total_duration] = useState({}); + const [chartData_sub_Issue_total_duration_options, setChartData_sub_Issue_total_duration_options] = useState({}); + const [chartObservingTimeBarData, setChartObservingTimeBarData] = useState({}); + const [chartWallTimeData, setChartWallTimeData] = useState({}); + const [chartWallTimeSubTypeData, setChartWallTimeSubTypeData] = useState({}); + const [chartOptions, setChartOptions] = useState({}); + + useEffect(() => { + if (!dates || dates?.length < 2 || dates.length == 2 && dates[1] == null) { + setChecked("") + } else { + console.log(dates) + setChecked("pi pi-check") + } + }, [dates]); + + function formatDuration(seconds) { + const days = Math.floor(seconds / (3600 * 24)); + const hours = Math.floor((seconds % (3600 * 24)) / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + const remainingSeconds = seconds % 60; + + const formattedDays = days > 0 ? days + 'd ' : ''; + const formattedHours = hours > 0 ? hours + 'h ' : ''; + const formattedMinutes = minutes > 0 ? minutes + 'm ' : ''; + const formattedSeconds = remainingSeconds > 0 ? remainingSeconds + 's' : ''; + + return formattedDays + formattedHours + formattedMinutes + formattedSeconds; + } + + + const MakeChartData = (element, key, labelkey) => { + const documentStyle = getComputedStyle(document.documentElement); + return { + labels: element?.map(item => item[labelkey]), + datasets: [ + { + data: element?.map(item => item[key]), + backgroundColor: Colors(documentStyle, "500"), + hoverBackgroundColor: Colors(documentStyle, "400") + } + + ] + } + } + + const customTooltip = (context) => { + // context.tooltip is an object containing information about the tooltip + const { label, value } = context.tooltip; + return `Category: ${label}, Value: ${value}`; + }; + + const MakeChartOptions = (title) => { + + return { + plugins: { + legend: { + labels: { + usePointStyle: false, + position: "right", + }, + }, + title: { text: title, display: true } + }, + + tooltips: { + enabled: false, // Disable default tooltips + custom: customTooltip, // Use custom tooltip function + } + + } + } + + + function MakePieChart(failureReport) { + let pieelement = failureReport?.system_event_summary?.by_issue_type; + if (pieelement !== null) { + setChartData_Issue_count(MakeChartData(pieelement, "count", "issue_type__value")); + setChartData_Issue_count_options(MakeChartOptions("Issue Type count")); + setChartData_Issue_total_duration(MakeChartData(pieelement, "duration_lost_event", "issue_type__value")); + setChartData_Issue_total_duration_options({ plugins: { legend: { display: false }, title: { text: "Issue Type Duration Percentage", display: true } } }); + } + } + + function makeSubTypePieChart(failureReport) { + let piesubtypeelement = failureReport?.system_event_summary?.by_issue_subtype; + if (piesubtypeelement !== null) { + setChartData_sub_Issue_count(MakeChartData(piesubtypeelement, "count", "issue_subtype__value")); + setChartData_sub_Issue_count_options(MakeChartOptions("Issue sub Type count")); + setChartData_sub_Issue_total_duration(MakeChartData(piesubtypeelement, "duration_lost_event", "issue_subtype__value")); + setChartData_sub_Issue_total_duration_options({ plugins: { legend: { display: false }, title: { text: "Sub Issue Type Duration Hours ", display: true } } }); + } + } + + useEffect(() => { + const documentStyle = getComputedStyle(document.documentElement); + if (failureReport == null) return + + MakePieChart(failureReport); + makeSubTypePieChart(failureReport); + MakeLostTimeBar(failureReport, documentStyle); + MakeWallTimeIssueTypeBar(failureReport, documentStyle); + MakeWallTimeSubIssueBar(failureReport, documentStyle); + }, [failureReport]); + + function MakeLostTimeBar(failureReport, documentStyle) { + if (failureReport?.lost_time_histogram_event) { + let keymap = Object.keys(failureReport?.lost_time_histogram_event); + let valuemap = Object.values(failureReport?.lost_time_histogram_event).map(seconds => seconds / 3600); + setChartObservingTimeBarData({ + labels: keymap, + datasets: [ + { + label: 'Lost observing time', + backgroundColor: documentStyle.getPropertyValue('--orange-500'), + data: valuemap, + }, + ], + }); + } + } + + + function MakeWallTimeIssueTypeBar(failureReport, documentStyle) { + setChartWallTimeData(MakeWallTimeBarWithType(failureReport, documentStyle, "by_issue_type", "Percentage Failures by issue type", "issue_type__value")) + } + + + function MakeWallTimeSubIssueBar(failureReport, documentStyle) { + setChartWallTimeSubTypeData(MakeWallTimeBarWithType(failureReport, documentStyle, "by_issue_subtype", "Percentage failures by sub issue type", "issue_subtype__value")) + } + + function Colors(documentStyle, subfix) { + return [ + documentStyle.getPropertyValue('--red-' + subfix), + documentStyle.getPropertyValue('--orange-' + subfix), + documentStyle.getPropertyValue('--yellow-' + subfix), + documentStyle.getPropertyValue('--green-' + subfix), + documentStyle.getPropertyValue('--purple-' + subfix), + documentStyle.getPropertyValue('--pink-' + subfix), + documentStyle.getPropertyValue('--teal-' + subfix), + documentStyle.getPropertyValue('--brown-' + subfix), + documentStyle.getPropertyValue('--gray-' + subfix), + ] + } + + function MakeWallTimeBarWithType(failureReport, documentStyle, type, title, typelabel) { + + let colors = Colors(documentStyle, "500"); + if (failureReport?.lost_time_histogram_event) { + let byIssueType = failureReport?.["system_event_summary"]?.[type]?.map( + (item, index) => ({ + type: 'bar', + label: item[typelabel], + data: [item.percent_of_wall_time_lost_event], + backgroundColor: colors[index] + })); + byIssueType.push({ + type: 'bar', + label: 'no failures', + data: [100 - byIssueType.reduce((sum, item) => sum + item.data[0], 0)], + backgroundColor: documentStyle.getPropertyValue('--green-500'), + }); + return { + labels: [title], + datasets: byIssueType + }; + } + } + + function formatDateToYYYYMMDD(date) { + const year = date.getFullYear(); + const month = (date.getMonth() + 1).toString().padStart(2, '0'); + const day = date.getDate().toString().padStart(2, '0'); + + return `${year}-${month}-${day}`; + } + + const generateReport = async () => { + setIsLoading(true); + const requestedFailureReport = await ReportService.getFailureReport(formatDateToYYYYMMDD(dates[0]) + "T00:00:00Z", formatDateToYYYYMMDD(dates[1]) + "T23:59:59Z") + console.log(requestedFailureReport?.data) + setFailureReport(requestedFailureReport?.data); + setIsLoading(false); + + console.log("generating report") + } + + + useEffect(() => { + const documentStyle = getComputedStyle(document.documentElement); + const textColor = documentStyle.getPropertyValue('--text-color'); + const textColorSecondary = documentStyle.getPropertyValue('--text-color-secondary'); + const surfaceBorder = documentStyle.getPropertyValue('--surface-border'); + const options = { + maintainAspectRatio: false, + aspectRatio: 0.7, + plugins: { + tooltips: { + mode: 'index', + intersect: false + }, + legend: { + position: 'bottom', + align: 'end', + labels: { + color: textColor, + boxWidth: 10, + + } + } + }, + scales: { + x: { + barPercentage: 0.3, + categoryPercentage: 0.3, + stacked: true, + ticks: { + color: textColorSecondary + }, + grid: { + color: surfaceBorder + } + }, + y: { + max: 100, + stacked: true, + ticks: { + color: textColorSecondary, + callback: function (value) { + return value + '%'; // Add percentage symbol to the ticks + } + }, + grid: { + color: surfaceBorder + } + } + } + }; + setChartOptions(options); + }, []); + + function on_sky_start_time_Template(data) { + return data?.on_sky_start_time?.replace("T", " "); + } + function on_sky_duration_Template(data) { + if (!data?.on_sky_duration) return "" + return Math.floor(data?.on_sky_duration / 60) + " Minutes" + } + + function subtasks_Template(data) { + if (!data?.subtasks) return "" + return data.subtasks.join(", ") + } + + + function issues_Template(data) { + if (!data?.["issue_type(s)"]) return "" + return data["issue_type(s)"].filter((value, index, self) => { return self.indexOf(value) === index; }).join(", ") + + } + function issuessubtypes_Template(data) { + if (!data?.["issue_subtype(s)"]) return "" + return data["issue_subtype(s)"].filter((value, index, self) => { return self.indexOf(value) === index; }).join(", ") + } + + + + /** + * Function to generate the PDF of the report. + */ + const generatePdf = async () => { + setIsLoading(true); + // Create a PDF document with landscape orientation, 1st report div width and the max height of all report divs in pixels + const pdf = new jsPDF('l','mm',[297, 210]); + let title = `${moment(dates[0]).format("DD MMM YYYY")} - ${moment(dates[1]).format("DD MMM YYYY")}` + // Draw each report in canvas and create image of the canvas before saving the pdf + pdf.setFontSize(14); + pdf.text(10, 10, 'Failure Report ' + title + " page 1"); + await addelement("chart1",0,20); + await addelement("chart2",100,20); + await addelement("chart3",0,120); + await addelement("chart4",100,120); + await addelement("chart5",200,20); + await addelement("chart6",200,120); + pdf.addPage('l','mm',[297, 210]); + pdf.text(10, 10, 'Failure Report ' + title + " page 2"); + await addlargeelement("chart7",0,20); + pdf.addPage('l','mm',[297, 210]); + pdf.text(10, 10, 'Failure Report ' + title + " page 3"); + await addlargeelement("datatable",10,20); + + pdf.save(`FailureReport_${title}.pdf`); + setIsLoading(false); + + async function addelement(elementname,x,y) { + let element = document.getElementById(elementname); + const options = { + foreignObjectRendering: true, + width: element.clientWidth*2, + height: element.clientHeight*2 + }; + + let canvas = await html2canvas(element, options); + let dataurl = canvas.toDataURL('image/jpeg'); + pdf.addImage(dataurl, 'JPEG', x, y, element.clientWidth/3, element.clientHeight/3); + } + + + async function addlargeelement(elementname,x,y) { + let element = document.getElementById(elementname); + const options = { + foreignObjectRendering: true, + width: element.clientWidth*4, + height: element.clientHeight*4 + }; + + let canvas = await html2canvas(element, options); + let dataurl = canvas.toDataURL('image/jpeg'); + pdf.addImage(dataurl, 'JPEG', x, y, element.clientWidth, element.clientHeight); + } + } + + + return <div className='routerpage'> + <PageHeader title={'Failure Reports'} /> + + <div className='FailureReport'> + <span>Date Range </span> + <Calendar value={dates} onChange={(e) => setDates(e.value)} selectionMode="range" readOnlyInput className='FailureCalender' /> + <Button label="Generate Failure Report" icon={checked} iconPos="right" disabled={checked.length == 0} onClick={() => generateReport()} /> + <Button label="Download Pdf " icon="pi pi-file-pdf" iconPos="right" disabled={!failureReport||isLoading} onClick={() => generatePdf()} /> + </div> + {isLoading && <ProgressBar mode="indeterminate" style={{ height: '6px' }} className='failureProgress'> </ProgressBar>} + + {failureReport && <> + <div style={{ display: "flex", flexDirection: "row", marginTop: "10px" }} > + <div className='chartbox' style={{ display: "flex", flexDirection: "row" } }> + <Chart type="pie" data={chartData_Issue_count} options={chartData_Issue_count_options} style={{ position: 'relative', width: '450px' }} id="chart1" /> + <Chart type="pie" data={chartData_Issue_total_duration} options={chartData_Issue_total_duration_options} style={{ position: 'relative', width: '450px' }} id="chart2" /> + </div> + <div className='chartbox' style={{ display: "flex", flexDirection: "row" }}> + <Chart type="pie" data={chartData_sub_Issue_count} options={chartData_sub_Issue_count_options} style={{ position: 'relative', width: '450px' }} id="chart3"/> + <Chart type="pie" data={chartData_sub_Issue_total_duration} options={chartData_sub_Issue_total_duration_options} style={{ position: 'relative', width: '450px' }} id="chart4"/> </div> + </div> + + <div style={{ display: "flex", flexDirection: "row" }} className='pdfPage'> + <Chart type="bar" data={chartWallTimeData} options={chartOptions} style={{ position: 'relative', width: '400px' }} className='chartbox' id="chart5" /> + <Chart type="bar" data={chartWallTimeSubTypeData} options={chartOptions} style={{ position: 'relative', width: '400px' }} className='chartbox' id="chart6" /> + + <div style={{ width: '990px' }} className='chartbox' > + <div className='yaxesrotate'>hours</div> + <Chart type="bar" data={chartObservingTimeBarData} style={{ position: 'relative', width: '995px' }} id="chart7" /> + </div> + </div> + + + + <DataTable value={failureReport?.failed_scheduling_units} className='chartbox' id="datatable"> + <Column sortable key="name" field="name" header="name" ></Column> + <Column sortable key="project" field="project" header="project" ></Column> + <Column sortable key="status" field="status" header="status" ></Column> + <Column sortable key="on_sky_start_time" field="on_sky_start_time" header="on sky start time" body={on_sky_start_time_Template} ></Column> + <Column sortable key="on_sky_duration" field="on_sky_duration" header="on sky duration" body={on_sky_duration_Template} ></Column> + <Column sortable key="subtasks" field="subtasks" header="subtasks" body={subtasks_Template}></Column> + <Column sortable key="issue_type" field="issue_type" header="issue_type" body={issues_Template}></Column> + <Column sortable key="issue_subtype" field="issue_subtype" header="issue_subtype" body={issuessubtypes_Template}></Column> + <Column ></Column> + </DataTable></>} + </div> + + + +} + + + diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/index.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/index.js deleted file mode 100644 index a018108f98b998fbc105c465fc07b22a0b612caf..0000000000000000000000000000000000000000 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/index.js +++ /dev/null @@ -1,43 +0,0 @@ -import { useState } from 'react'; -import { TabView, TabPanel } from 'primereact/tabview'; -import PropTypes from 'prop-types'; -import PageHeader from '../../layout/components/PageHeader'; - -import CycleReportMain from "./cycle/report.main"; -import ProjectReportMain from "./project.report.main"; -export default function ReportHome(props) { - const { - location, - history - } = props - - const [reportIndex, setReportIndex] = useState(0); - - const Close = () => { - history.goBack(); - } - - - return ( - <div className='routerpage'> - <PageHeader location={location} title={'Reports'} actions={[{ - icon: 'pi pi-times', title: 'Click to Close Report', - type: 'buttonv2', actOn: 'click', props: { callback: Close } - }]} /> - <TabView activeIndex={reportIndex} onTabChange={(e) => setReportIndex(e.index)}> - <TabPanel header="Cycle"> - <CycleReportMain></CycleReportMain> - </TabPanel> - <TabPanel header="Project"> - <ProjectReportMain ></ProjectReportMain> - </TabPanel> - </TabView> - </div> - ); - -} - -ReportHome.propTypes = { - location: PropTypes.object, - history: PropTypes.object -}; \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.js index fc4467a574cf614cef3094dd681e92561898d40f..c061631ffac8d4132d6d1517964007b295c8f5d6 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.js @@ -268,15 +268,6 @@ class ProjectReport extends Component { const reportData = this.state.reportData; let barData = {}, barOptions = {}; const resourceUtilization = this.state.resourceUtilization; - // _.map(resourceUtilization, resource => { - // if (resource.type === "Observing") { - // resource.succeeded = 50; - // resource.succeededValue = 10; - // resource.failed = 25; - // resource.failedValue = 2.5 - // } - // return resource; - // }) const targetData = reportData?this.getTargetData():null; // Resource Utilization bar chart data set with options if (resourceUtilization.length > 0) { @@ -365,7 +356,7 @@ class ProjectReport extends Component { return( <React.Fragment> {reportData && - <> + <div className="report-div" id={`${this.props.project.name}-report-div`}> <div id={`${this.props.project.name}-project-details`}> <h2 style={{textAlign: "center", marginBottom:"25px"}}>Report statistics for project {this.props.project.name}</h2> @@ -436,7 +427,7 @@ class ProjectReport extends Component { </div> } </div> - </> + } </React.Fragment> ); diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.main.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.main.js index ea4533d9a98c198cdfb50e0119da4a4855550c31..49d05b890daa2fee65db56270ccee8a43b6916a1 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.main.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Report/project.report.main.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import { Component } from 'react'; import { Link } from 'react-router-dom'; import moment from 'moment'; import _ from 'lodash'; @@ -16,6 +16,7 @@ import { Button } from 'primereact/button'; import { ProgressBar } from 'primereact/progressbar'; import CycleService from '../../services/cycle.service'; import ProjectReport from './project.report'; +import PageHeader from '../../layout/components/PageHeader'; // Constants for SU details table column property name to be used for identifying the properties in the // report data and title for displaying in reports and while exporting them. @@ -314,7 +315,9 @@ class ProjectReportMain extends Component { render() { return( - <React.Fragment> + <div className='routerpage'> + <PageHeader title={'Project Report'} /> + {this.state.pageLoading && <AppLoader />} {!this.state.pageLoading && @@ -373,7 +376,7 @@ class ProjectReportMain extends Component { } </> } - </React.Fragment> + </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 446f62c0d94bf9f8c64a7029d7a00a16c167314e..775c9ecb628a7fec59b99e2138443a4122a6ec00 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js @@ -262,16 +262,6 @@ export class EditSchedulingUnit extends Component { this.setState({ paramsOutput: jsonOutput, validEditor: errors.length === 0, validForm: this.validateForm()}); - /*if ( !this.state.isDirty && this.state.paramsOutput && !_.isEqual(this.state.paramsOutput, jsonOutput) ) { - this.setState({ paramsOutput: jsonOutput, - validEditor: errors.length === 0, - validForm: this.validateForm()}); - } else { - this.setState({ paramsOutput: jsonOutput, - validEditor: errors.length === 0, - validForm: this.validateForm()}); - }*/ - } setEditorOutputConstraint(jsonOutput, errors) { @@ -811,18 +801,6 @@ export class EditSchedulingUnit extends Component { tooltipOptions={this.tooltipOptions} ></TriStateCheckbox> </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> {_.keys(this.state.stationGroups).length >0 && diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/SystemEvent/system.event.list.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/SystemEvent/system.event.list.js index 5d096f57cb3f55ee190b750dd1fe4bed17d8de8c..0082bbd0f30795c7e554ba63590d167a3013e9b7 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/SystemEvent/system.event.list.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/SystemEvent/system.event.list.js @@ -360,6 +360,19 @@ export class SystemEventList extends Component { return filters; } + + makeArrayUnique = (array) => { + const uniqueIds = new Set(); + + return array.reduce((uniqueArray, item) => { + if (!uniqueIds.has(item.id)) { + uniqueIds.add(item.id); + uniqueArray.push(item); + } + return uniqueArray; + }, []); + }; + /** * Fetch data from server side - while doing pagination, filtering, sorting * @param {Table State} Table props state @@ -381,10 +394,11 @@ export class SystemEventList extends Component { let secondaryfilters = this.filtersFromPath(filters); if (secondaryfilters.length>0 &&filters.length>0) { _.remove(filters, function (filter) { - return filter.id === 'CycleId' || filter.id === 'Start Time' || filter.id === 'End Time'; + return filter.id === 'CycleId' || filter.id === 'Start Time' || filter.id === 'End Time' ; }); } filters = [ ...secondaryfilters,...filters]; + filters = this.makeArrayUnique(filters); UtilService.localStore({ type: 'set', key: this.SYSTEM_EVENT_LIST_TABLE_NAME , value: filters }); } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/__snapshots__/WeekView.test.js.snap b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/__snapshots__/WeekView.test.js.snap index 1a18210c5226df8a95440ef6632f58f9c5c4a75e..09152631d6dae59b6785f18ea6ca670013923f45 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/__snapshots__/WeekView.test.js.snap +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/__snapshots__/WeekView.test.js.snap @@ -208,6 +208,22 @@ exports[`WeekView Render Contents WeekView renders correctly with data: DuringLo  </span> </button> + <button + class="p-button p-component p-button-icon-only" + data-pc-name="button" + data-pc-section="root" + title="Show Scheduling Units Table" + > + <span + class="p-button-icon p-c pi pi-folder" + data-pc-section="icon" + /> + <span + class="p-button-label p-c" + > +  + </span> + </button> <button class="p-button p-component p-button-icon-only" data-pc-name="button" @@ -687,9 +703,9 @@ exports[`WeekView Render Contents WeekView renders correctly with data: DuringLo <option data-pc-section="option" selected="" - value="Showing Day At" + value="Showing Week From" > - Showing Day At + Showing Week From </option> </select> </div> @@ -697,7 +713,7 @@ exports[`WeekView Render Contents WeekView renders correctly with data: DuringLo class="p-dropdown-label p-inputtext" data-pc-section="input" > - Showing Day At + Showing Week From </span> <div aria-expanded="false" @@ -1738,11 +1754,11 @@ exports[`WeekView Render Contents WeekView renders correctly with data: DuringLo </div> <div class="rct-outer" - style="height: 40px;" + style="height: 280px;" > <div class="rct-sidebar" - style="width: 150px; height: 40px;" + style="width: 150px; height: 280px;" > <div style="width: 150px;" @@ -1767,11 +1783,131 @@ exports[`WeekView Render Contents WeekView renders correctly with data: DuringLo </a> </div> </div> + <div + class="rct-sidebar-row rct-sidebar-row-odd" + style="height: 40px; line-height: 40px;" + > + <div + class="group-renderer" + > + <span + class="week" + > + 49 + </span> + <a + href="/constraint/view/2023-12-04" + target="_new" + > + Dec 04 - Mon + </a> + </div> + </div> + <div + class="rct-sidebar-row rct-sidebar-row-even" + style="height: 40px; line-height: 40px;" + > + <div + class="group-renderer" + > + <span + class="week" + > + 49 + </span> + <a + href="/constraint/view/2023-12-05" + target="_new" + > + Dec 05 - Tue + </a> + </div> + </div> + <div + class="rct-sidebar-row rct-sidebar-row-odd" + style="height: 40px; line-height: 40px;" + > + <div + class="group-renderer" + > + <span + class="week" + > + 49 + </span> + <a + href="/constraint/view/2023-12-06" + target="_new" + > + Dec 06 - Wed + </a> + </div> + </div> + <div + class="rct-sidebar-row rct-sidebar-row-even" + style="height: 40px; line-height: 40px;" + > + <div + class="group-renderer" + > + <span + class="week" + > + 49 + </span> + <a + href="/constraint/view/2023-12-07" + target="_new" + > + Dec 07 - Thu + </a> + </div> + </div> + <div + class="rct-sidebar-row rct-sidebar-row-odd" + style="height: 40px; line-height: 40px;" + > + <div + class="group-renderer" + > + <span + class="week" + > + 49 + </span> + <a + href="/constraint/view/2023-12-08" + target="_new" + > + Dec 08 - Fri + </a> + </div> + </div> + <div + class="rct-sidebar-row rct-sidebar-row-even" + style="height: 40px; line-height: 40px;" + > + <div + class="group-renderer" + > + <span + class="week" + > + 49 + </span> + <a + href="/constraint/view/2023-12-09" + target="_new" + > + Dec 09 - Sat + </a> + </div> + </div> </div> </div> <div class="rct-scroll" - style="width: -150px; height: 60px; cursor: default; position: relative;" + style="width: -150px; height: 300px; cursor: default; position: relative;" > <div style="position: absolute; left: 0px; right: 0px; top: 0px; bottom: 0px;" @@ -1781,7 +1917,7 @@ exports[`WeekView Render Contents WeekView renders correctly with data: DuringLo > <div class="rct-vl " - style="pointer-events: none; top: 0px; left: 50400.58334008495px; width: -54750.6336878899px; height: 40px;" + style="pointer-events: none; top: 0px; left: 50400.58334008495px; width: -54750.6336878899px; height: 280px;" /> </div> <div @@ -1791,6 +1927,30 @@ exports[`WeekView Render Contents WeekView renders correctly with data: DuringLo class="rct-hl-even " style="width: -150px; height: 40px;" /> + <div + class="rct-hl-odd " + style="width: -150px; height: 40px;" + /> + <div + class="rct-hl-even " + style="width: -150px; height: 40px;" + /> + <div + class="rct-hl-odd " + style="width: -150px; height: 40px;" + /> + <div + class="rct-hl-even " + style="width: -150px; height: 40px;" + /> + <div + class="rct-hl-odd " + style="width: -150px; height: 40px;" + /> + <div + class="rct-hl-even " + style="width: -150px; height: 40px;" + /> </div> <div class="rct-items" @@ -2053,6 +2213,22 @@ exports[`WeekView Render Contents WeekView renders correctly with data: Loaded 1  </span> </button> + <button + class="p-button p-component p-button-icon-only" + data-pc-name="button" + data-pc-section="root" + title="Show Scheduling Units Table" + > + <span + class="p-button-icon p-c pi pi-folder" + data-pc-section="icon" + /> + <span + class="p-button-label p-c" + > +  + </span> + </button> <button class="p-button p-component p-button-icon-only" data-pc-name="button" @@ -2532,9 +2708,9 @@ exports[`WeekView Render Contents WeekView renders correctly with data: Loaded 1 <option data-pc-section="option" selected="" - value="Showing Day At" + value="Showing Week From" > - Showing Day At + Showing Week From </option> </select> </div> @@ -2542,7 +2718,7 @@ exports[`WeekView Render Contents WeekView renders correctly with data: Loaded 1 class="p-dropdown-label p-inputtext" data-pc-section="input" > - Showing Day At + Showing Week From </span> <div aria-expanded="false" @@ -3583,11 +3759,11 @@ exports[`WeekView Render Contents WeekView renders correctly with data: Loaded 1 </div> <div class="rct-outer" - style="height: 40px;" + style="height: 280px;" > <div class="rct-sidebar" - style="width: 150px; height: 40px;" + style="width: 150px; height: 280px;" > <div style="width: 150px;" @@ -3612,11 +3788,131 @@ exports[`WeekView Render Contents WeekView renders correctly with data: Loaded 1 </a> </div> </div> + <div + class="rct-sidebar-row rct-sidebar-row-odd" + style="height: 40px; line-height: 40px;" + > + <div + class="group-renderer" + > + <span + class="week" + > + 49 + </span> + <a + href="/constraint/view/2023-12-04" + target="_new" + > + Dec 04 - Mon + </a> + </div> + </div> + <div + class="rct-sidebar-row rct-sidebar-row-even" + style="height: 40px; line-height: 40px;" + > + <div + class="group-renderer" + > + <span + class="week" + > + 49 + </span> + <a + href="/constraint/view/2023-12-05" + target="_new" + > + Dec 05 - Tue + </a> + </div> + </div> + <div + class="rct-sidebar-row rct-sidebar-row-odd" + style="height: 40px; line-height: 40px;" + > + <div + class="group-renderer" + > + <span + class="week" + > + 49 + </span> + <a + href="/constraint/view/2023-12-06" + target="_new" + > + Dec 06 - Wed + </a> + </div> + </div> + <div + class="rct-sidebar-row rct-sidebar-row-even" + style="height: 40px; line-height: 40px;" + > + <div + class="group-renderer" + > + <span + class="week" + > + 49 + </span> + <a + href="/constraint/view/2023-12-07" + target="_new" + > + Dec 07 - Thu + </a> + </div> + </div> + <div + class="rct-sidebar-row rct-sidebar-row-odd" + style="height: 40px; line-height: 40px;" + > + <div + class="group-renderer" + > + <span + class="week" + > + 49 + </span> + <a + href="/constraint/view/2023-12-08" + target="_new" + > + Dec 08 - Fri + </a> + </div> + </div> + <div + class="rct-sidebar-row rct-sidebar-row-even" + style="height: 40px; line-height: 40px;" + > + <div + class="group-renderer" + > + <span + class="week" + > + 49 + </span> + <a + href="/constraint/view/2023-12-09" + target="_new" + > + Dec 09 - Sat + </a> + </div> + </div> </div> </div> <div class="rct-scroll" - style="width: -150px; height: 60px; cursor: default; position: relative;" + style="width: -150px; height: 300px; cursor: default; position: relative;" > <div style="position: absolute; left: 0px; right: 0px; top: 0px; bottom: 0px;" @@ -3626,7 +3922,7 @@ exports[`WeekView Render Contents WeekView renders correctly with data: Loaded 1 > <div class="rct-vl " - style="pointer-events: none; top: 0px; left: 50400.58334008495px; width: -54750.6336878899px; height: 40px;" + style="pointer-events: none; top: 0px; left: 50400.58334008495px; width: -54750.6336878899px; height: 280px;" /> </div> <div @@ -3636,6 +3932,30 @@ exports[`WeekView Render Contents WeekView renders correctly with data: Loaded 1 class="rct-hl-even " style="width: -150px; height: 40px;" /> + <div + class="rct-hl-odd " + style="width: -150px; height: 40px;" + /> + <div + class="rct-hl-even " + style="width: -150px; height: 40px;" + /> + <div + class="rct-hl-odd " + style="width: -150px; height: 40px;" + /> + <div + class="rct-hl-even " + style="width: -150px; height: 40px;" + /> + <div + class="rct-hl-odd " + style="width: -150px; height: 40px;" + /> + <div + class="rct-hl-even " + style="width: -150px; height: 40px;" + /> </div> <div class="rct-items" diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js index 712a7049c25facd83391c10885afc8ca87ddbc20..6168d15a89762101d5d798883eea51a9d5f276d3 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js @@ -1,6 +1,5 @@ import { Switch, - // Redirect, } from 'react-router-dom'; import { NotFound } from '../layout/components/NotFound'; @@ -23,12 +22,14 @@ import { SystemEventCreate, SystemEventList, SystemEventView, SystemEventEdit } import { FindObjectResult } from './Search/' import SchedulingSetCreate from './Scheduling/excelview.schedulingset'; import Workflow from './Workflow'; -import ReportHome from './Report'; import { Toast } from 'primereact/toast'; import { setAppGrowl } from '../layout/components/AppGrowl'; import WorkflowList from './Workflow/workflow.list' import ProtectedRoute from '../components/ProtectedRouteComponent'; import WeekView from "./Timeline/WeekView"; +import FailureReport from './Report/failure/failure.report'; +import CycleReportMain from './Report/cycle/report.main'; +import ProjectReportMain from './Report/project.report.main'; export const routes = [ { @@ -250,12 +251,26 @@ export const routes = [ name: 'Workflow', title: 'Workflow' }, + + { + path: "/reports/failure", + component: FailureReport, + name: 'Failure Report', + title: 'Failure Report' + }, { - path: "/reports", - component: ReportHome, + path: "/reports/cycle", + component: CycleReportMain, + name: 'Cycle Report', + title: 'Cycle Report' + }, + { + path: "/reports/project", + component: ProjectReportMain, name: 'Reports', title: 'Reports' }, + { path: "/systemevent/list", component: SystemEventList, diff --git a/SAS/TMSS/frontend/tmss_webapp/src/services/report.service.js b/SAS/TMSS/frontend/tmss_webapp/src/services/report.service.js index 5114ad065dd7a5a1af2526b691a6664dafff7f34..33f1a4a766da17f440c5ce9961e0feb5c5bbe4ec 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/services/report.service.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/services/report.service.js @@ -34,7 +34,17 @@ const ReportService = { console.error(error); return null; } - } + }, + getFailureReport: async(start,stop) => { + try { + const response = await axios.get(`/api/failure_report/?start=${start}&stop=${stop}&format=json`); // todo: add UI element to alter excluded projects + return response + } catch(error) { + console.error(error); + return null; + } +} + } export default ReportService; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/services/util.service.js b/SAS/TMSS/frontend/tmss_webapp/src/services/util.service.js index d5ed8bd5857434b862e2a0495082a9ca0c31b38c..80d21acbd954680ccef49d9fe76c45a52e56c703 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/services/util.service.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/services/util.service.js @@ -213,7 +213,14 @@ getStations: async () => { let { type, key, value } = data; key = `${user}${user ? "-" : ""}${key}`; if (type === 'set') { - localStorage.setItem(key, JSON.stringify(value)); + try { + localStorage.setItem(key, JSON.stringify(value)); + } + catch (e) + { + console.log(e,data); + } + } else if (type === 'get') { const localStorageItem = localStorage.getItem(key); if (localStorageItem && localStorageItem !== "undefined") { diff --git a/SAS/TMSS/frontend/tmss_webapp/src/utils/pageheaderactions.util.js b/SAS/TMSS/frontend/tmss_webapp/src/utils/pageheaderactions.util.js index 9126213e931f2e2968b4009e940a73be0516abdf..8cdb54057639f07890ec3d7c351e58bbf28919f3 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/utils/pageheaderactions.util.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/pageheaderactions.util.js @@ -24,7 +24,7 @@ actionButtonObject(title, icon, classes, callbackProp) { actionTagObject(title, content, className) { - return this.actionObject(title, undefined, undefined, className, undefined, content, "tagv2", "click", undefined, undefined,undefined,undefined); + return this.actionObject(title, undefined, undefined, className, undefined, content, "tag", "click", undefined, undefined,undefined,undefined); }, actionDropDownObject(title, options, callbackProp, selected, classes,optionvalue ,optionlabel ,optionSelectionLabel) { return this.actionObject(title, undefined, classes, undefined, callbackProp, undefined, "dropdown", "select", options, selected,optionvalue,optionlabel,optionSelectionLabel);