diff --git a/.gitattributes b/.gitattributes index 853a7dbc1d48f501549b19c424142d86ec50bb0a..032ac93b7641cff9a37b72a2a345a1a17def8469 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1906,6 +1906,18 @@ LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/LandingPage/components/ LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/LandingPage/index.js -text LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestChildView/index.js -text LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestChildView/styles.module.scss -text +LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericStatusTd/index.js -text +LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericStatusTd/styles.module.css -text +LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericStatusTd/styles.module.scss -text +LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericTestRow/index.js -text +LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericTestRow/styles.module.css -text +LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericTestRow/styles.module.scss -text +LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/RTSMRows/index.js -text +LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/RTSMRows/styles.module.css -text +LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/RTSMRows/styles.module.scss -text +LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/index.js -text +LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/styles.module.css -text +LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/styles.module.scss -text LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/index.js -text LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/styles.module.scss -text LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/index.js -text diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericStatusTd/index.js b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericStatusTd/index.js new file mode 100644 index 0000000000000000000000000000000000000000..ebf31bb3bb613a1fc331805028fcdc1979878801 --- /dev/null +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericStatusTd/index.js @@ -0,0 +1,71 @@ +import React, { + Component +} from 'react'; +import {withRouter} from "react-router"; + + +// CSS +import styles from './styles.module.scss'; + + +class GenericStatusTdC extends Component { + + getClass() { + let cls = styles.status; + if (this.props.doHighlight) { + cls += " "+styles.highlight; + } + if (this.props.n_errors > 0) { + cls += ' hilite-serious' + } else { + cls += ' hilite-good' + } + return cls; + } + + onMouseOver = (e) => { + e.stopPropagation(); + if (this.props.n_errors > 0) { + this.props.onSelect(this.props.data); + } + }; + + onMouseOut = (e) => { + e.stopPropagation(); + if (this.props.n_errors > 0) { + this.props.onSelect(null); + } + + }; + + // left-click with mouse + onClick = () => { + this.props.history.push(`/tiles?antenna_id=${this.props.antenna_id}&antenna_type=${this.props.antenna_type}&station=${this.props.station_name}`); + }; + + // right-click with mouse + onContextMenu = (e) => { + e.preventDefault(); + if (this.props.n_errors > 0) { + this.props.onSelect(this.props.data, true); + } + }; + + render() { + const content = (this.props.n_errors===0 ? ' ' : this.props.n_errors); + + return ( + <td className={this.getClass()} + onContextMenu={this.onContextMenu} + onClick={this.onClick} + onMouseOver={this.onMouseOver} + onMouseOut={this.onMouseOut}> + {content} + </td> + ); + } +} + +const GenericStatusTd = withRouter(GenericStatusTdC); + +export default GenericStatusTd; diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericStatusTd/styles.module.css b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericStatusTd/styles.module.css new file mode 100644 index 0000000000000000000000000000000000000000..268db1b71d5a450bb21c865644e92bb492e4744c --- /dev/null +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericStatusTd/styles.module.css @@ -0,0 +1,19 @@ +/* COLORS */ +/* Color palette interface (created with https://material.io/tools/color/) */ +/* font color */ +/* font color */ +/* hover color, e.g. for background */ +/* Data colors */ +:local .status { + text-align: center; + font-size: 90%; + font-weight: 600; } + +:local .status.highlight { + background-color: #86868a !important; + color: white; } + +:local .status:hover { + color: #fff; + background-color: #86868a; + border-color: #86868a; } diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericStatusTd/styles.module.scss b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericStatusTd/styles.module.scss new file mode 100644 index 0000000000000000000000000000000000000000..3e9e5fa6280b114f79d1a436d9dd5b5bcc7286b7 --- /dev/null +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericStatusTd/styles.module.scss @@ -0,0 +1,19 @@ +@import '../../../../../../themes/lofar-variables.scss'; + + +:local .status { + text-align: center; + font-size: 90%; + font-weight: 600; +} + +:local .status.highlight { + background-color: $secondary-dark!important; + color: $secondary-color; +} + +:local .status:hover { + color: #fff; + background-color: $hover-color; + border-color: $hover-color; +} diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericTestRow/index.js b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericTestRow/index.js new file mode 100644 index 0000000000000000000000000000000000000000..b7ac9e7bd4c16a5ca8d48c55eabfb5944c258c18 --- /dev/null +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericTestRow/index.js @@ -0,0 +1,115 @@ +import React, { + Component +} from 'react'; +import {connect} from "react-redux"; +import {setChildPanelData} from 'redux/actions/stationOverviewPageActions' +import {datetime_format} from 'utils/constants' +import moment from 'moment'; + +import GenericStatusTd from '../GenericStatusTd' + +// CSS +import styles from './styles.module.scss' + + +/** + * GenericTestRow: renders a table row with the results of one station test or RTSM run. + */ +class GenericTestRowC extends Component { + + doHighlight = false; + + static formatDate(date) { + return moment(date).format(datetime_format); + } + + shouldHighlight() { + const props = this.props, + errorData = props.highlightData; + + return errorData !== null && + errorData.component_type === props.component_type && + errorData.test_type === props.test_type && + errorData.datetime === GenericTestRowC.formatDate(props.data.start_date); + } + + shouldComponentUpdate(nextProps, nextState, nextContext) { + // this.doHighlight will only be true for the previously and currently selected row + if (nextProps.highlightData !== this.props.highlightData && !this.doHighlight) { + return false; + } + return true; + } + + onSelect = (data, doPin) => { + if (doPin) { + this.doHighlight = true; + } + this.props.setChildPanelData(data, doPin); + }; + + renderComponentErrors() { + const componentErrors = this.props.data.component_errors; + + const renderedComponentErrors = this.props.ordered_component_ids.map((component_id, key) => { + let nErrors = 0, + errors = [], + errorData = {}; + + if (componentErrors.hasOwnProperty(component_id)) { + errors = componentErrors[component_id]; + nErrors = Object.keys(errors).length; + + // Data for child panel and checking if an antenna item must be highlighted permanently (see shouldHighlight) + errorData.errors = errors; + errorData.datetime= GenericTestRowC.formatDate(this.props.data.start_date); + errorData.test_type = this.props.test_type; + errorData.component_id = component_id; + errorData.component_type = this.props.component_type; + } + + return <GenericStatusTd + doHighlight={this.doHighlight && component_id === this.props.highlightData.component_id} + key={key} + data={errorData} + antenna_id={component_id} + antenna_type={this.props.component_type} + station_name={this.props.station_name} + n_errors={nErrors} + onSelect={this.onSelect} /> + }); + + return renderedComponentErrors; + } + + render() { + const date = GenericTestRowC.formatDate(this.props.data.start_date); + + // Determine if this row needs to be highlighted + this.doHighlight = this.shouldHighlight(); + + let cls = styles.testrow; + if (this.doHighlight) { + cls += " "+styles.highlight; + } + + return ( + <tr className={cls}> + <td className={styles.testrow_header}>{this.props.test_type} {date}</td> + {this.renderComponentErrors()} + </tr> + ); + } +} + +// TestLine is connected to Redux store +const GenericTestRow = connect(state => { + return { + ...state.station_page.main_panel + }; +}, { + setChildPanelData +})(GenericTestRowC); + + +export default GenericTestRow; diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericTestRow/styles.module.css b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericTestRow/styles.module.css new file mode 100644 index 0000000000000000000000000000000000000000..07a217bba1a6b2b433ec74944d67eeb941bb2dd0 --- /dev/null +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericTestRow/styles.module.css @@ -0,0 +1,16 @@ +/* COLORS */ +/* Color palette interface (created with https://material.io/tools/color/) */ +/* font color */ +/* font color */ +/* hover color, e.g. for background */ +/* Data colors */ +:local .testrow.highlight { + background-color: #86868a !important; + color: white; } + +:local .testrow.highlight td:first-child::before { + content: "> "; } + +:local .testrow_header { + width: 12rem !important; + min-width: 12rem !important; } diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericTestRow/styles.module.scss b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericTestRow/styles.module.scss new file mode 100644 index 0000000000000000000000000000000000000000..ac6838ed38278a049fea517edceebf08e09c0d8e --- /dev/null +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/GenericTestRow/styles.module.scss @@ -0,0 +1,16 @@ +@import '../../../../../../themes/lofar-variables.scss'; + + +:local .testrow.highlight { + background-color: $secondary-dark!important; + color: $secondary-color; +} + +:local .testrow.highlight td:first-child::before { + content: "> " +} + +:local .testrow_header { + width: 12rem !important; + min-width: 12rem !important; +} diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/RTSMRows/index.js b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/RTSMRows/index.js new file mode 100644 index 0000000000000000000000000000000000000000..332750cc0b19a01816c7c3a2d89354c91dc99619 --- /dev/null +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/RTSMRows/index.js @@ -0,0 +1,105 @@ +import React, { + Component +} from 'react'; +import {IoMdArrowDropdown as DropDownIcon} from 'react-icons/io'; +import {renderDateRange} from 'utils/utils' + +import GenericTestRow from '../GenericTestRow' + +// CSS +//import styles from './styles.module.scss'; +import rtsmStyles from 'themes/rtsm_collapsable.module.scss'; + + +/* + * RTSMSummaryLine: create one table row with percentages of errors per antenna. + */ +function RTSMSummaryLine(props) { + const data = props.data; + + const cols = props.ordered_component_ids.map((item, key) => { + if (data[item] > 0) { + let perc = Math.ceil(data[item]); + return (<td key={key} className={rtsmStyles.rtsm_summary_badge}>{perc + '%'} </td>); + } else { + return <td key={key}></td>; + } + }); + + const dropdownAdditionStyles = props.isExpanded ? " "+rtsmStyles.dropdownbutton_up : ""; + + return ( + <tr className={rtsmStyles.rtsm_summary_row}> + <td className={rtsmStyles.row_header} onClick={props.onClick}> + RT {props.dateRange} + <DropDownIcon className={rtsmStyles.dropdownbutton + dropdownAdditionStyles} color="black"/> + </td> + {cols} + </tr> + ); +} + +/* + * RTSMLines: create summary line + expandable data rows + */ +class RTSMRows extends Component { + + state = { + displaySingleTests: false + } + + computeSummary() { + let summary = {}; + let n_tests = this.props.data.length; + const component_id_list = this.props.ordered_component_ids; + component_id_list.forEach(component_id => summary[component_id] = 0); + + this.props.data.forEach((item, key) => { + Object.keys(item.component_errors).forEach((component_id) => { + summary[component_id] += 1 + }) + }); + + Object.keys(summary).forEach(item => summary[item] /= n_tests / 100.); + return summary + } + + toggleDisplaySingleTests = (e) => { + this.setState({ + displaySingleTests: !this.state.displaySingleTests + }); + this.props.update(); + }; + + render() { + let summaryData = this.computeSummary(); + + // RTSM data rows, only shown when expanded + let all_rtsm = this.state.displaySingleTests ? this.props.data : []; + + return ( + <React.Fragment> + <RTSMSummaryLine onClick={this.toggleDisplaySingleTests} + isExpanded={this.state.displaySingleTests} + data={summaryData} + ordered_component_ids={this.props.ordered_component_ids} + dateRange={renderDateRange(this.props.data)} /> + { // All RTSM lines in this block (expanded or folded) + all_rtsm.map((item, key) => + <GenericTestRow className="collapse open" + key={key} + test_type="RT" + ordered_component_ids={this.props.ordered_component_ids} + component_type={this.props.component_type} + station_name={this.props.station_name} + station_type={this.props.station_type} + data={item}/> + ) + } + </React.Fragment> + ); + } +} + + +export default RTSMRows; diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/RTSMRows/styles.module.css b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/RTSMRows/styles.module.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/RTSMRows/styles.module.scss b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/RTSMRows/styles.module.scss new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/RTSMRows/styles.module.scss @@ -0,0 +1 @@ + diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/index.js b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/index.js new file mode 100644 index 0000000000000000000000000000000000000000..1a515ab95b936e3a8bb038a23314c920c0384ee8 --- /dev/null +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/index.js @@ -0,0 +1,110 @@ +import React, { + Component +} from 'react'; +import {Badge} from 'reactstrap'; +import ReactTableContainer from "react-table-container"; + +import GenericTestRow from './GenericTestRow'; +import RTSMRows from './RTSMRows'; + +// CSS +import styles from './styles.module.scss'; + + +/** + * ComponentType; renders a table of station tests and rtsm data for one component (HBA, RSP, LBH, etc.) + * + * Props: + * station_type: C, R or I + * type: component type + * data: Data for this component + */ +class ComponentType extends Component { + + computeComponentIDList(componentType) { + let componentIDSet = new Set(); + this.props.data.forEach(test => { + Object.keys(test.component_errors).forEach(item => componentIDSet.add(item)) + }); + // Numerical sort + return Array.from(componentIDSet).sort((a, b) => a - b); + } + + renderGenericTestRow(key, data, component_ids) { + return (<GenericTestRow key={key} + test_type="ST" + ordered_component_ids={component_ids} + component_type={this.props.type} + station_name={this.props.station_name} + station_type={this.props.station_type} + data={data}/>) + } + + renderRTSMRows(key, data, component_ids) { + return (<RTSMRows key={key} + ordered_component_ids={component_ids} + component_type={this.props.type} + station_name={this.props.station_name} + station_type={this.props.station_type} + data={data} + update={this.updateIfComponentChanges} />) + } + + renderGenericTestRows(data, component_ids) { + const rows = []; + let tmp_rtsm_set = [], + num_items = data.length; + + for (let i = 0; i < num_items; i++) { + const current_item = data[i], + next_item = (i===num_items-1 ? null : data[i + 1]); + + // Temporarily store RTSM lines + if (current_item.test_type === 'R') { + tmp_rtsm_set.push(current_item) + + // Push lines when next item is a station test or when we are at the last item + if (next_item === null || next_item.test_type === 'S') { + rows.push(this.renderRTSMRows(rows.length, tmp_rtsm_set, component_ids)) + tmp_rtsm_set = [] + } + } + else if (current_item.test_type === 'S') { + rows.push(this.renderGenericTestRow(rows.length, current_item, component_ids)); + } + } + + return rows + } + + updateIfComponentChanges = () => { + this.setState({ state: this.state }); + } + + render() { + const comp_ids = this.computeComponentIDList(this.props.type); + + return ( + <ReactTableContainer width="100%" height={this.props.height+'px'}> + <table className={styles.comp_type_table+" table-sm table-hover table-bordered"}> + <thead className={styles.comp_type_header}> + <tr> + <th style={{textAlign: "left"}} scope="col"> + <Badge color="info">{this.props.type}</Badge> + </th> + {comp_ids.map((item, key) => + <th scope="col" key={key}>{item}</th>) + } + </tr> + </thead> + <tbody> + {this.renderGenericTestRows(this.props.data, comp_ids)} + </tbody> + </table> + </ReactTableContainer> + ); + } +} + + +export default ComponentType; diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/styles.module.css b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/styles.module.css new file mode 100644 index 0000000000000000000000000000000000000000..a34d2fa7a01011f038601250e4fd5972d8774993 --- /dev/null +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/styles.module.css @@ -0,0 +1,12 @@ +:local .comp_type_table { + font-size: .9rem; } + +:local(.comp_type_table):global(.table-sm) td, +:local(.comp_type_table):global(.table-sm) th { + padding: .1rem; + min-width: 1.8em; } + +:local .comp_type_header { + position: relative; + background-color: white; + text-align: center; } diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/styles.module.scss b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/styles.module.scss new file mode 100644 index 0000000000000000000000000000000000000000..1a2cb599bc04694e9019e4be259456cce6d8f009 --- /dev/null +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage/components/StationTestView/ComponentType/styles.module.scss @@ -0,0 +1,17 @@ + + +:local .comp_type_table { + font-size: .9rem; +} + +:local(.comp_type_table):global(.table-sm) td, +:local(.comp_type_table):global(.table-sm) th { + padding: .1rem; + min-width: 1.8em; +} + +:local .comp_type_header { + position: relative; + background-color: white; + text-align: center; +}