From c3a90c008e254cd30fe3880c9ac57344b936bbde Mon Sep 17 00:00:00 2001 From: rbokhorst <rbokhorst@astron.nl> Date: Mon, 3 Dec 2018 08:53:30 +0000 Subject: [PATCH] OSB-35: Refactored toolbar --- .gitattributes | 3 + .../src/components/StationAutoComplete.js | 38 +- .../src/components/Toolbar.css | 39 ++ .../src/components/Toolbar.js | 406 ++++++++++++++++++ .../src/components/Toolbar.scss | 42 ++ .../src/pages/LandingPage.js | 95 +--- .../src/pages/StationOverviewPage.js | 152 +------ .../maintenancedb_view/src/pages/TilesPage.js | 190 +------- .../src/themes/lofar-styles.css | 31 -- .../src/themes/lofar-styles.scss | 35 +- .../maintenancedb_view/src/themes/lofar.css | 31 -- 11 files changed, 549 insertions(+), 513 deletions(-) create mode 100644 LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/Toolbar.css create mode 100644 LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/Toolbar.js create mode 100644 LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/Toolbar.scss diff --git a/.gitattributes b/.gitattributes index 7430f865f95..39f55942410 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1873,6 +1873,9 @@ LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/StationTestSummary LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/StationTestView.css -text LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/StationTestView.js -text LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/StationTestView.scss -text +LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/Toolbar.css -text +LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/Toolbar.js -text +LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/Toolbar.scss -text LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/header.css -text LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/header.js -text LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/header.scss -text diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/StationAutoComplete.js b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/StationAutoComplete.js index 58aa36eeb2c..a96ecf9a334 100644 --- a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/StationAutoComplete.js +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/StationAutoComplete.js @@ -2,11 +2,15 @@ import React, {Component} from 'react'; import {connect} from "react-redux"; import Autosuggest from 'react-autosuggest'; +// History handling +import { push } from 'connected-react-router'; +import { store } from "../redux/store.js"; + import './StationAutoComplete.css' /** - * StationAutoCompleteC; class to render an input field for station name with auto-completion. + * AutoCompleteC; class to render an input field for station name with auto-completion. * * The parent component is notified about a new station name (through the onChange callback) * when the user presses 'Enter' in the input field or when an item from the list of @@ -17,9 +21,9 @@ import './StationAutoComplete.css' * selected station was changed outside this component. In a new instance the state.value is * set to the selectedStation prop. * - * Usage: <StationAutoComplete key={station} selectedStation={station} onChange={onchange} /> + * Usage: <AutoComplete key={station} onChange={onchange} stations={stations} selectedStation={selectedStation}/> */ -class StationAutoCompleteC extends Component { +class AutoComplete extends Component { // Autosuggest is a controlled component. // However the input value is decoupled from Redux state but gets its @@ -116,11 +120,35 @@ class StationAutoCompleteC extends Component { } } + + +class StationAutoCompleteC extends Component { + + + // Callback for StationAutoComplete + onStationChange = (station) => { + store.dispatch(push(`?station=${station}`)) + } + + render() { + // The key on AutoComplete is important, see the desc of AutoComplete + // That's the main reason why this wrapper exists. + return( + <div className="toolbar-ctrl"> + <AutoComplete key={this.props.selectedStation} + stations={this.props.stations} + selectedStation={this.props.selectedStation} + onChange={this.onStationChange}/> + </div> + ); + } +} + // Get full list of stations from redux const mapStateToPropsToolBar = state => { return { - //stations: [{name:'cs001c'},{name:'cs002c'},{name:'cs003c'}] - stations: state.appInitData.stations + stations: state.appInitData.stations, + selectedStation: state.mainFilters.selectedStation }; }; diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/Toolbar.css b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/Toolbar.css new file mode 100644 index 00000000000..47216986d7c --- /dev/null +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/Toolbar.css @@ -0,0 +1,39 @@ +/* COLORS */ +/* Color palette interface (created with https://material.io/tools/color/) */ +/* font color */ +/* font color */ +/* Data colors */ +.toolbar-top { + background-color: #a7689d; + color: #ffffff; + padding: 5px 10px; + /* Note: same padding as ResponsiveGridLayout */ + width: 100%; + overflow: visible; } + +.toolbar-top { + font-weight: 500; } + +.toolbar-top .btn-info:not(:disabled):not(.disabled).active, +.sts-toolbar .btn-info:not(:disabled):not(.disabled).active, +.toolbar-top .btn-info:not(:disabled):not(.disabled):active, +.toolbar-top .show > .btn-info.dropdown-toggle { + color: white; + background-color: #8d8d8d; + border-color: #8d8d8d; } + +.toolbar-top .btn-info, +.sts-toolbar .btn-info { + color: white; + background-color: #bdbdbd; + border-color: #bdbdbd; } + +.toolbar-top .btn-info:hover, +.sts-toolbar .btn-info:hover { + color: white; + background-color: #8d8d8d; + border-color: #8d8d8d; } + +.toolbar-ctrl { + display: inline-block; + margin-right: 1em; } diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/Toolbar.js b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/Toolbar.js new file mode 100644 index 00000000000..87a0d3c6946 --- /dev/null +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/Toolbar.js @@ -0,0 +1,406 @@ +import React, {Component} from 'react'; +import {connect} from "react-redux"; +import {Button, ButtonGroup} from 'reactstrap'; +import { + setDateRange, + setTestType, + setErrorTypes, + setStationGroup, + setErrorsOnly, + setPeriod +} from "../redux/actions/mainFiltersActions"; +import { setAntennaID, setAntennaType } from "../redux/actions/antennaOverviewPageActions"; +import DatePicker from 'react-datepicker'; +import moment from 'moment'; +import MultiSelectDropdown from '../components/MultiSelectDropdown.js' + +// CSS +import './Toolbar.css' + + +/** + * Class to display a secondary header for selecting data filters. + * The state is managed by the LandingPage class. + */ +class DateRangeSelectorC extends Component { + + handleChange(obj) { + var startDate = obj.startDate, + endDate = obj.endDate; + + startDate = startDate || this.props.startDate; + endDate = endDate || this.props.endDate; + + if (startDate.isAfter(endDate)) { + endDate = startDate; + } + + this.props.setDateRange({ + startDate: startDate, + endDate: endDate + }); + }; + + handleChangeStart = (startDate) => { + return this.handleChange({ + startDate: startDate + }); + }; + + handleChangeEnd = (endDate) => { + return this.handleChange({ + endDate: endDate + }); + }; + + onPeriodClick = (i) => { + this.handleChange({ + startDate: moment().subtract(i, 'days'), + endDate: moment() + }); + + } + + periodIsActive(ndays) { + let now = moment().format("YYYY-MMM-DD"), + past = moment().subtract(ndays, 'days').format("YYYY-MMM-DD"); + if (this.props.endDate.format("YYYY-MMM-DD") === now && this.props.startDate.format("YYYY-MMM-DD") === past) { + return true; + } + return false; + } + + render() { + return (<div className="toolbar-ctrl"> + <label>Period </label> + <ButtonGroup size="sm"> + <Button color="info" onClick={() => this.onPeriodClick(7)} active={this.periodIsActive(7)}>1 wk</Button> + <Button color="info" onClick={() => this.onPeriodClick(14)} active={this.periodIsActive(14)}>2 wk</Button> + <Button color="info" onClick={() => this.onPeriodClick(28)} active={this.periodIsActive(28)}>4 wk</Button> + </ButtonGroup> + + <div style={{display: 'inline-block', width: '7em'}}> + <DatePicker + selected={this.props.startDate} + selectsStart + dateFormat="YYYY-MMM-DD" + className='form-control form-control-sm' + startDate={this.props.startDate} + endDate={this.props.endDate} + onChange={this.handleChangeStart} + /> + </div> + + <div style={{display: 'inline-block', width: '7em'}}> + <DatePicker + selected={this.props.endDate} + selectsEnd + dateFormat="YYYY-MMM-DD" + className='form-control form-control-sm' + startDate={this.props.startDate} + endDate={this.props.endDate} + onChange={this.handleChangeEnd} + /> + </div> + </div>); + } +} + +const mapStateDateRangeSelector = state => { + return { + startDate: state.mainFilters.startDate, + endDate: state.mainFilters.endDate + }; +}; + +const mapDispatchDateRangeSelector = { + setDateRange +}; + +export const DateRangeSelector = connect(mapStateDateRangeSelector, mapDispatchDateRangeSelector)(DateRangeSelectorC); + + + +/** + * Radion buttons for Test Type + */ +class TestTypeSelectorC extends Component { + + onTestTypeClick(type) { + this.props.setTestType(type); + } + + render() { + return (<div className="toolbar-ctrl"> + <label>Type </label> + <ButtonGroup size="sm"> + <Button color="info" onClick={() => this.onTestTypeClick('B')} active={this.props.testType === 'B'}>BOTH</Button> + <Button color="info" onClick={() => this.onTestTypeClick('S')} active={this.props.testType === 'S'}>ST-TEST</Button> + <Button color="info" onClick={() => this.onTestTypeClick('R')} active={this.props.testType === 'R'}>RTSM</Button> + </ButtonGroup> + </div>); + } +} + +const mapStateTestTypeSelector = state => { + return { + testType: state.mainFilters.testType + }; +}; + +const mapDispatchTestTypeSelector = { + setTestType +}; + +export const TestTypeSelector = connect(mapStateTestTypeSelector, mapDispatchTestTypeSelector)(TestTypeSelectorC); + + +/** + * Radion buttons for Antenna Type + */ +class AntennaTypeSelectorC extends Component { + + onAntennaTypeClick(type){ + this.props.setAntennaType(type); + } + + render() { + return (<div className="toolbar-ctrl"> + <label>Type </label> + <ButtonGroup size="sm"> + <Button color="info" onClick={() => this.onAntennaTypeClick('HBA')} active={this.props.antenna_type === 'HBA'}>HBA</Button> + <Button color="info" onClick={() => this.onAntennaTypeClick('LBL')} active={this.props.antenna_type === 'LBL'}>LBL</Button> + <Button color="info" onClick={() => this.onAntennaTypeClick('LBH')} active={this.props.antenna_type === 'LBH'}>LBH</Button> + </ButtonGroup> + </div>); + } +} + +const mapStateAntennaTypeSelector = state => { + return { + antenna_type: state.antenna_page.antenna_type + }; +}; + +const mapDispatchAntennaTypeSelector = { + setAntennaType +}; + +export const AntennaTypeSelector = connect(mapStateAntennaTypeSelector, mapDispatchAntennaTypeSelector)(AntennaTypeSelectorC); + + +/** + * Radion buttons for Antenna Id + */ +class AntennaIdSelectorC extends Component { + + antenna_id_list(type, isInternational){ + let n_antennas = 0 + switch (type) { + case "HBA": + n_antennas = 96 + break; + case "LBL": + n_antennas = 48 + break; + case "LBH": + n_antennas = isInternational? 96: 48 + break; + default: + n_antennas = 0 + } + let options = [] + for(let i=0; i<n_antennas; i++){ + options.push(<option key={i}>{i}</option>); + } + return options + } + + render() { + const isInternational = !this.props.selectedStation.includes('CS') && !this.props.selectedStation.includes('RS') + const options = this.antenna_id_list(this.props.antenna_type, isInternational) + + return (<div className="toolbar-ctrl"> + <label>Antenna id </label> + <select>{options}</select> + </div>); + } + +} + +const mapStateAntennaIdSelector = state => { + return { + selectedStation: state.mainFilters.selectedStation, + antenna_type: state.antenna_page.antenna_type, + antenna_id: state.antenna_page.antenna_id + }; +}; + +const mapDispatchAntennaIdSelector = { + setAntennaID +}; + +export const AntennaIdSelector = connect(mapStateAntennaIdSelector, mapDispatchAntennaIdSelector)(AntennaIdSelectorC); + + +/** + * Class to display a secondary header for selecting data filters. + * The state is managed by the LandingPage class. + */ +class ErrorTypesSelectorC extends Component { + + onSelectionErrorTypes = (errorTypes) => { + this.props.setErrorTypes(errorTypes) + } + + render() { + const errorTypes = this.props.errorTypes.map(item => ({value:item, label:item})) + + return (<div className="toolbar-ctrl"> + <label>Error type </label> + <MultiSelectDropdown + className="form-input" + placeHolder="All" + options={errorTypes} + selectedItems={this.props.selectedErrorTypes} + onSelectionChange={(e)=> this.onSelectionErrorTypes(e)} + /> + </div>); + } +} + +const mapStateErrorTypesSelector = state => { + return { + selectedErrorTypes: state.mainFilters.selectedErrorTypes, + errorTypes: state.appInitData.errorTypes + }; +}; + +const mapDispatchErrorTypesSelector = { + setErrorTypes +}; + +export const ErrorTypesSelector = connect(mapStateErrorTypesSelector, mapDispatchErrorTypesSelector)(ErrorTypesSelectorC); + + + +/* + * + */ +class StationGroupSelectorC extends Component { + + onStationGroupChange = (e) => { + this.props.setStationGroup(e.target.value); + } + + render() { + return (<div className="toolbar-ctrl"> + <label htmlFor="selected-group">Station group </label> + <select className="form-control custom-select custom-select-sm" id="selected-group" + value={this.props.selectedStationGroup} + onChange={this.onStationGroupChange} + style={{ width: 'auto' }}> + <option value="A">All stations</option> + <option value="C">Core stations</option> + <option value="R">Remote stations</option> + <option value="I">ILT stations</option> + </select> + </div>); + } +} + +const mapStateStationGroupSelector = state => { + return { + selectedStationGroup: state.mainFilters.selectedStationGroup + }; +}; + +const mapDispatchStationGroupSelector = { + setStationGroup +}; + +export const StationGroupSelector = connect(mapStateStationGroupSelector, mapDispatchStationGroupSelector)(StationGroupSelectorC); + + +/* + * Toggle button for "errorsOnly" + */ +class ErrorsOnlySelectorC extends Component { + + onErrorsOnlyClick = () => { + this.props.setErrorsOnly(!this.props.errorsOnly); + } + + render() { + return (<div className="toolbar-ctrl"> + <Button color="info" size="sm" onClick={this.onErrorsOnlyClick} active={this.props.errorsOnly}>Errors only</Button> + </div>); + } +} + +const mapStateErrorsOnlySelector = state => { + return { + errorsOnly: state.mainFilters.errorsOnly + }; +}; + +const mapDispatchErrorsOnlySelector = { + setErrorsOnly +}; + +export const ErrorsOnlySelector = connect(mapStateErrorsOnlySelector, mapDispatchErrorsOnlySelector)(ErrorsOnlySelectorC); + + + +/** + * Class to display a secondary header for selecting data filters. + * The state is managed by the LandingPage class. + */ +class PeriodSelectorC extends Component { + + onPeriodClick(i) { + this.props.setPeriod(i); + } + + render() { + + return (<div className="toolbar-ctrl"> + <label>Period </label> + <ButtonGroup size="sm"> + <Button color="info" onClick={() => this.onPeriodClick(7)} active={this.props.period === 7}>1 wk</Button> + <Button color="info" onClick={() => this.onPeriodClick(14)} active={this.props.period === 14}>2 wk</Button> + <Button color="info" onClick={() => this.onPeriodClick(21)} active={this.props.period === 21}>3 wk</Button> + <Button color="info" onClick={() => this.onPeriodClick(28)} active={this.props.period === 28}>4 wk</Button> + </ButtonGroup> + </div>); + } +} + +const mapStatePeriodSelector = state => { + return { + period: state.mainFilters.period + }; +}; + +const mapDispatchPeriodSelector = { + setPeriod +}; + +export const PeriodSelector = connect(mapStatePeriodSelector, mapDispatchPeriodSelector)(PeriodSelectorC); + + + +/* + * Toolbar: the wrapper for all toolbar components. Use as: + * <Toolbar> + * <StationAutoComplete /> + * <TestTypeSelector /> + * <..> + * </Toolbar> + */ +export function Toolbar(props) { + + return (<div className="toolbar-top"> + {props.children} + </div>); +} diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/Toolbar.scss b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/Toolbar.scss new file mode 100644 index 00000000000..1448c264031 --- /dev/null +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/components/Toolbar.scss @@ -0,0 +1,42 @@ +@import '../themes/lofar-variables.scss'; + + +.toolbar-top { + background-color: $primary-light; + color: $primary-color; + padding: 5px 10px; /* Note: same padding as ResponsiveGridLayout */ + width: 100%; + overflow: visible; +} + +.toolbar-top { + font-weight: 500; +} +.toolbar-top .btn-info:not(:disabled):not(.disabled).active, +.sts-toolbar .btn-info:not(:disabled):not(.disabled).active, +.toolbar-top .btn-info:not(:disabled):not(.disabled):active, +.toolbar-top .show>.btn-info.dropdown-toggle { + color: $secondary-color; + background-color: $secondary-dark; + border-color: $secondary-dark; +} + +.toolbar-top .btn-info, +.sts-toolbar .btn-info { + color: $secondary-color; + background-color: $secondary; + border-color: $secondary; +} + +.toolbar-top .btn-info:hover, +.sts-toolbar .btn-info:hover { + color: $secondary-color; + background-color: $secondary-dark; + border-color: $secondary-dark; +} + + +.toolbar-ctrl { + display: inline-block; + margin-right: 1em; +} diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/LandingPage.js b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/LandingPage.js index c0afbcd0c9d..fab99a8ad74 100644 --- a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/LandingPage.js +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/LandingPage.js @@ -7,16 +7,14 @@ import LatestObservations from '../components/LatestObservations.js'; import StationStatistics from '../components/StationStatistics.js'; import {Responsive, WidthProvider} from 'react-grid-layout'; -import {Button, ButtonGroup, Form} from 'reactstrap'; import * as moment from 'moment'; -import {connect} from "react-redux"; +import { connect } from "react-redux"; import { setNewLayout } from "../redux/actions/landingPageActions"; -import { setPeriod, setStationGroup, setErrorsOnly, setErrorTypes } from "../redux/actions/mainFiltersActions"; import { composeQueryString } from '../utils/utils.js'; import { createGridPanel } from '../utils/grid.js'; +import { Toolbar, StationGroupSelector, ErrorTypesSelector, ErrorsOnlySelector, PeriodSelector } from '../components/Toolbar' -import MultiSelectDropdown from '../components/MultiSelectDropdown.js' import 'react-grid-layout/css/styles.css'; import 'react-resizable/css/styles.css'; import '../themes/lofar-styles.css'; @@ -24,87 +22,6 @@ import '../themes/lofar-styles.css'; const ResponsiveGridLayout = WidthProvider(Responsive); -/** - * Class to display a secondary header for selecting data filters. - * The state is managed by the LandingPage class. - */ -class ToolBarC extends Component { - - onErrorsOnlyClick(selected) { - this.props.setErrorsOnly(!this.props.errorsOnly); - } - - onStationGroupChange(e) { - //this.props.onChange('selectedStationGroup', e.target.value) - this.props.setStationGroup(e.target.value); - } - - onPeriodClick(i) { - this.props.setPeriod(i); - } - - onSelectionErrorTypes(errorTypes) { - this.props.setErrorTypes(errorTypes) - } - - render() { - - const errorTypes = this.props.errorTypes.map(item => ({value:item, label:item})) - - return (<div className="toolbar-top"> - <Form inline> - <label htmlFor="selected-group">Station group </label> - <select className="form-control custom-select custom-select-sm" id="selected-group" value={this.props.selectedStationGroup} onChange={(e) => this.onStationGroupChange(e)} style={{ - width: 'auto' - }}> - <option value="A">All stations</option> - <option value="C">Core stations</option> - <option value="R">Remote stations</option> - <option value="I">ILT stations</option> - </select> - { - this.props.errorsOnly - ? <Button color="info" size="sm" onClick={() => this.onErrorsOnlyClick()} active="active">Errors only</Button> - : <Button color="info" size="sm" onClick={() => this.onErrorsOnlyClick()}>Errors only</Button> - } - - <label>Period </label> - <ButtonGroup size="sm"> - <Button color="info" onClick={() => this.onPeriodClick(7)} active={this.props.period === 7}>1 wk</Button> - <Button color="info" onClick={() => this.onPeriodClick(14)} active={this.props.period === 14}>2 wk</Button> - <Button color="info" onClick={() => this.onPeriodClick(21)} active={this.props.period === 21}>3 wk</Button> - <Button color="info" onClick={() => this.onPeriodClick(28)} active={this.props.period === 28}>4 wk</Button> - </ButtonGroup> - - <label>Error type </label> - <MultiSelectDropdown - className="form-input" - placeHolder="All" - options={errorTypes} - selectedItems={this.props.selectedErrorTypes} - onSelectionChange={(e)=> this.onSelectionErrorTypes(e)}/> - </Form> - </div>); - } -} - -const mapStateToPropsToolBar = state => { - return { - ...state.mainFilters, - ...state.appInitData - }; -}; - -const mapDispatchToPropsToolBar = { - setStationGroup, - setErrorsOnly, - setPeriod, - setErrorTypes, -}; - -const ToolBar = connect(mapStateToPropsToolBar, mapDispatchToPropsToolBar)(ToolBarC); - - class LandingPageC extends Component { getStationOverviewURL() { @@ -178,10 +95,16 @@ class LandingPageC extends Component { return `${url}&${parametersString}`; } + render() { return (<div> <Header active_page={this.props.location}/> - <ToolBar/> + <Toolbar> + <StationGroupSelector /> + <ErrorsOnlySelector /> + <PeriodSelector /> + <ErrorTypesSelector /> + </Toolbar> <ResponsiveGridLayout className="layout" layouts={this.props.layout.panels} measureBeforeMount={true} breakpoints={this.props.layout.breakpoints} cols={this.props.layout.cols} onResizeStop={this.props.setNewLayout}> diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage.js b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage.js index d4f9413032a..0155d33790b 100644 --- a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage.js +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/StationOverviewPage.js @@ -1,159 +1,19 @@ import React, { Component } from 'react'; import {connect} from "react-redux"; -import {Alert, Button, ButtonGroup, Container, Row, Col} from 'reactstrap'; -import { setDateRange, setTestType, setStation, setErrorTypes } from "../redux/actions/mainFiltersActions"; -import DatePicker from 'react-datepicker'; +import {Alert, Container, Row, Col} from 'reactstrap'; import moment from 'moment'; import Header from '../components/header.js' import StationAutoComplete from '../components/StationAutoComplete'; import StationTestView from '../components/StationTestView'; import StationTestChildView from '../components/StationTestChildView'; -import MultiSelectDropdown from '../components/MultiSelectDropdown.js' import { composeQueryString } from '../utils/utils.js'; - -// History handling -import { push } from 'connected-react-router'; -import { store } from "../redux/store.js"; +import { Toolbar, DateRangeSelector, TestTypeSelector, ErrorTypesSelector } from '../components/Toolbar' // CSS import 'react-datepicker/dist/react-datepicker.css'; -/** - * Class to display a secondary header for selecting data filters. - * The state is managed by the LandingPage class. - */ -class ToolBarC extends Component { - - constructor(props) { - super(props); - this.handleChangeStart = this.handleChangeStart.bind(this); - this.handleChangeEnd = this.handleChangeEnd.bind(this); - this.onStationChange = this.onStationChange.bind(this); - } - - handleChange(obj) { - var startDate = obj.startDate, - endDate = obj.endDate; - - startDate = startDate || this.props.startDate; - endDate = endDate || this.props.endDate; - - if (startDate.isAfter(endDate)) { - endDate = startDate; - } - - this.props.setDateRange({ - startDate: startDate, - endDate: endDate - }); - }; - - handleChangeStart(startDate) { - return this.handleChange({ - startDate: startDate - }); - }; - - handleChangeEnd(endDate) { - return this.handleChange({ - endDate: endDate - }); - }; - - onPeriodClick(i) { - this.handleChange({ - startDate: moment().subtract(i, 'days'), - endDate: moment() - }); - - } - - onTestTypeClick(type) { - this.props.setTestType(type); - } - - // Callback for StationAutoComplete - onStationChange(station) { - //this.props.setStation(station); - store.dispatch(push(`?station=${station}`)) - } - - onSelectionErrorTypes(errorTypes) { - this.props.setErrorTypes(errorTypes) - } - - render() { - const errorTypes = this.props.errorTypes.map(item => ({value:item, label:item})) - // The key on StationAutoComplete is important, see the desc of StationAutoComplete - return (<div className="toolbar-top"> - <StationAutoComplete key={this.props.selectedStation} selectedStation={this.props.selectedStation} onChange={this.onStationChange}/> - - <label>Type </label> - <ButtonGroup size="sm"> - <Button color="info" onClick={() => this.onTestTypeClick('B')} active={this.props.testType === 'B'}>BOTH</Button> - <Button color="info" onClick={() => this.onTestTypeClick('S')} active={this.props.testType === 'S'}>ST-TEST</Button> - <Button color="info" onClick={() => this.onTestTypeClick('R')} active={this.props.testType === 'R'}>RTSM</Button> - </ButtonGroup> - - <label>Period </label> - <ButtonGroup size="sm"> - <Button color="info" onClick={() => this.onPeriodClick(7)}>1 wk</Button> - <Button color="info" onClick={() => this.onPeriodClick(14)}>2 wk</Button> - <Button color="info" onClick={() => this.onPeriodClick(28)}>4 wk</Button> - </ButtonGroup> - - <div style={{display: 'inline-block', width: '7em'}}> - <DatePicker - selected={this.props.startDate} - selectsStart - dateFormat="YYYY-MMM-DD" - className='form-control form-control-sm' - startDate={this.props.startDate} - endDate={this.props.endDate} - onChange={this.handleChangeStart} - /> - </div> - - <div style={{display: 'inline-block', width: '7em'}}> - <DatePicker - selected={this.props.endDate} - selectsEnd - dateFormat="YYYY-MMM-DD" - className='form-control form-control-sm' - startDate={this.props.startDate} - endDate={this.props.endDate} - onChange={this.handleChangeEnd} - /> - </div> - - <label>Error type </label> - <MultiSelectDropdown - className="form-input" - placeHolder="All" - options={errorTypes} - selectedItems={this.props.selectedErrorTypes} - onSelectionChange={(e)=> this.onSelectionErrorTypes(e)}/> - </div>); - } -} - -const mapStateToPropsToolBar = state => { - return { - ...state.mainFilters, - ...state.appInitData - }; -}; - -const mapDispatchToPropsToolBar = { - setTestType, - setDateRange, - setStation, - setErrorTypes, -}; - -const ToolBar = connect(mapStateToPropsToolBar, mapDispatchToPropsToolBar)(ToolBarC); class StationOverviewPageC extends Component { @@ -188,13 +48,17 @@ class StationOverviewPageC extends Component { return "" } } - setNewLayout() {} render() { return ( <React.Fragment> <Header active_page={this.props.location} /> - <ToolBar/> + <Toolbar> + <StationAutoComplete /> + <TestTypeSelector /> + <DateRangeSelector /> + <ErrorTypesSelector /> + </Toolbar> {this.renderErrorIfParameterMissing()} { this.isParameterMissing() ? "" : <Container fluid={true} style={{padding: '10px'}}> diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/TilesPage.js b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/TilesPage.js index 13092d430c2..50a2add8b8c 100644 --- a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/TilesPage.js +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/pages/TilesPage.js @@ -1,199 +1,25 @@ import React, { Component } from 'react'; import Header from '../components/header.js' -import {Alert, Button, ButtonGroup, Container, Row, Col} from 'reactstrap'; - -import { setDateRange, setTestType, setStation, setErrorTypes } from "../redux/actions/mainFiltersActions"; -import { setAntennaID, setAntennaType } from "../redux/actions/antennaOverviewPageActions"; import {connect} from "react-redux"; import StationAutoComplete from '../components/StationAutoComplete'; -import DatePicker from 'react-datepicker'; -import moment from 'moment'; +import { Toolbar, DateRangeSelector, TestTypeSelector, AntennaTypeSelector, AntennaIdSelector } from '../components/Toolbar' -import MultiSelectDropdown from '../components/MultiSelectDropdown.js' import { composeQueryString } from '../utils/utils.js'; -// History handling -import { push } from 'connected-react-router'; -import { store } from "../redux/store.js"; - -// CSS -import 'react-datepicker/dist/react-datepicker.css'; - - -/** - * Class to display a secondary header for selecting data filters. - * The state is managed by the LandingPage class. - */ -class ToolBarC extends Component { - - constructor(props) { - super(props); - this.handleChangeStart = this.handleChangeStart.bind(this); - this.handleChangeEnd = this.handleChangeEnd.bind(this); - this.onStationChange = this.onStationChange.bind(this); - } - - handleChange(obj) { - var startDate = obj.startDate, - endDate = obj.endDate; - - startDate = startDate || this.props.startDate; - endDate = endDate || this.props.endDate; - - if (startDate.isAfter(endDate)) { - endDate = startDate; - } - - this.props.setDateRange({ - startDate: startDate, - endDate: endDate - }); - }; - - handleChangeStart(startDate) { - return this.handleChange({ - startDate: startDate - }); - }; - - handleChangeEnd(endDate) { - return this.handleChange({ - endDate: endDate - }); - }; - - onPeriodClick(i) { - this.handleChange({ - startDate: moment().subtract(i, 'days'), - endDate: moment() - }); - - } - onAntennaTypeClick(type){ - this.props.setAntennaType(type); - } - onTestTypeClick(type) { - this.props.setTestType(type); - } - - // Callback for StationAutoComplete - onStationChange(station) { - //this.props.setStation(station); - store.dispatch(push(`?station=${station}`)) - } - - onSelectionErrorTypes(errorTypes) { - this.props.setErrorTypes(errorTypes) - } - - antenna_id_list(type, isInternational){ - let n_antennas = 0 - switch (type) { - case "HBA": - n_antennas = 96 - break; - case "LBL": - n_antennas = 48 - break; - case "LBH": - n_antennas = isInternational? 96: 48 - break; - default: - n_antennas = 0 - } - let options = [] - for(let i=0; i<n_antennas; i++){ - options.push(<option key={i}>{i}</option>); - } - return options - } - - render() { - const errorTypes = this.props.errorTypes.map(item => ({value:item, label:item})) - - const isInternational = !this.props.selectedStation.includes('CS') && !this.props.selectedStation.includes('RS') - const options = this.antenna_id_list(this.props.antenna_type, isInternational) - // The key on StationAutoComplete is important, see the desc of StationAutoComplete - return (<div className="toolbar-top"> - <StationAutoComplete key={this.props.selectedStation} selectedStation={this.props.selectedStation} onChange={this.onStationChange}/> - - <label>Type </label> - <ButtonGroup size="sm"> - <Button color="info" onClick={() => this.onAntennaTypeClick('HBA')} active={this.props.antenna_type === 'HBA'}>HBA</Button> - <Button color="info" onClick={() => this.onAntennaTypeClick('LBL')} active={this.props.antenna_type === 'LBL'}>LBL</Button> - <Button color="info" onClick={() => this.onAntennaTypeClick('LBH')} active={this.props.antenna_type === 'LBH'}>LBH</Button> - </ButtonGroup> - - <label>Antenna id </label> - <select>{options}</select> - - <label>Test Type </label> - <ButtonGroup size="sm"> - <Button color="info" onClick={() => this.onTestTypeClick('B')} active={this.props.testType === 'B'}>BOTH</Button> - <Button color="info" onClick={() => this.onTestTypeClick('S')} active={this.props.testType === 'S'}>ST-TEST</Button> - <Button color="info" onClick={() => this.onTestTypeClick('R')} active={this.props.testType === 'R'}>RTSM</Button> - </ButtonGroup> - - <label>Period </label> - <ButtonGroup size="sm"> - <Button color="info" onClick={() => this.onPeriodClick(7)}>1 wk</Button> - <Button color="info" onClick={() => this.onPeriodClick(14)}>2 wk</Button> - <Button color="info" onClick={() => this.onPeriodClick(28)}>4 wk</Button> - </ButtonGroup> - - <div style={{display: 'inline-block', width: '7em'}}> - <DatePicker - selected={this.props.startDate} - selectsStart - dateFormat="YYYY-MMM-DD" - className='form-control form-control-sm' - startDate={this.props.startDate} - endDate={this.props.endDate} - onChange={this.handleChangeStart} - /> - </div> - - <div style={{display: 'inline-block', width: '7em'}}> - <DatePicker - selected={this.props.endDate} - selectsEnd - dateFormat="YYYY-MMM-DD" - className='form-control form-control-sm' - startDate={this.props.startDate} - endDate={this.props.endDate} - onChange={this.handleChangeEnd} - /> - </div> - </div>); - } -} - -const mapStateToPropsToolBar = state => { - return { - ...state.mainFilters, - ...state.appInitData, - ...state.antenna_page - }; -}; - -const mapDispatchToPropsToolBar = { - setTestType, - setDateRange, - setStation, - setAntennaID, - setAntennaType -}; - -const ToolBar = connect(mapStateToPropsToolBar, mapDispatchToPropsToolBar)(ToolBarC); - class TilesPageC extends Component { render() { return ( <div> <Header active_page={this.props.location} /> - <ToolBar/> + <Toolbar> + <StationAutoComplete /> + <AntennaTypeSelector /> + <AntennaIdSelector /> + <TestTypeSelector /> + <DateRangeSelector /> + </Toolbar> <div>Tiles Overview</div> </div> ); diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/themes/lofar-styles.css b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/themes/lofar-styles.css index d17f2c77a20..84efb64f0e7 100644 --- a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/themes/lofar-styles.css +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/themes/lofar-styles.css @@ -70,37 +70,6 @@ img { text-align: center; font-weight: 700; } -.toolbar-top { - background-color: #a7689d; - color: #ffffff; - padding: 5px 10px; - /* Note: same padding as ResponsiveGridLayout */ - width: 100%; - overflow: visible; } - -.toolbar-top { - font-weight: 500; } - -.toolbar-top .btn-info:not(:disabled):not(.disabled).active, -.sts-toolbar .btn-info:not(:disabled):not(.disabled).active, -.toolbar-top .btn-info:not(:disabled):not(.disabled):active, -.toolbar-top .show > .btn-info.dropdown-toggle { - color: white; - background-color: #8d8d8d; - border-color: #8d8d8d; } - -.toolbar-top .btn-info, -.sts-toolbar .btn-info { - color: white; - background-color: #bdbdbd; - border-color: #bdbdbd; } - -.toolbar-top .btn-info:hover, -.sts-toolbar .btn-info:hover { - color: white; - background-color: #8d8d8d; - border-color: #8d8d8d; } - .tooltip > .tooltip-inner { background-color: white !important; color: black !important; diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/themes/lofar-styles.scss b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/themes/lofar-styles.scss index 4efc1894ac9..76cbcace81d 100644 --- a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/themes/lofar-styles.scss +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/themes/lofar-styles.scss @@ -79,40 +79,6 @@ $griditem-color: $secondary-dark; font-weight: 700; } -.toolbar-top { - background-color: $primary-light; - color: $primary-color; - padding: 5px 10px; /* Note: same padding as ResponsiveGridLayout */ - width: 100%; - overflow: visible; -} - -.toolbar-top { - font-weight: 500; -} -.toolbar-top .btn-info:not(:disabled):not(.disabled).active, -.sts-toolbar .btn-info:not(:disabled):not(.disabled).active, -.toolbar-top .btn-info:not(:disabled):not(.disabled):active, -.toolbar-top .show>.btn-info.dropdown-toggle { - color: $secondary-color; - background-color: $secondary-dark; - border-color: $secondary-dark; -} - -.toolbar-top .btn-info, -.sts-toolbar .btn-info { - color: $secondary-color; - background-color: $secondary; - border-color: $secondary; -} - -.toolbar-top .btn-info:hover, -.sts-toolbar .btn-info:hover { - color: $secondary-color; - background-color: $secondary-dark; - border-color: $secondary-dark; -} - .tooltip > .tooltip-inner { background-color: white !important; color: black !important; @@ -120,6 +86,7 @@ $griditem-color: $secondary-dark; border: 1px solid $secondary-dark; } + .react-select-container{ min-width: 20rem; } diff --git a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/themes/lofar.css b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/themes/lofar.css index 14569b5fa74..427ea6eb162 100644 --- a/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/themes/lofar.css +++ b/LCU/Maintenance/MDB_WebView/maintenancedb_view/src/themes/lofar.css @@ -6389,37 +6389,6 @@ img { text-align: center; font-weight: 700; } -.toolbar-top { - background-color: #a7689d; - color: #ffffff; - padding: 5px 10px; - /* Note: same padding as ResponsiveGridLayout */ - width: 100%; - overflow: visible; } - -.toolbar-top { - font-weight: 500; } - -.toolbar-top .btn-info:not(:disabled):not(.disabled).active, -.sts-toolbar .btn-info:not(:disabled):not(.disabled).active, -.toolbar-top .btn-info:not(:disabled):not(.disabled):active, -.toolbar-top .show > .btn-info.dropdown-toggle { - color: white; - background-color: #8d8d8d; - border-color: #8d8d8d; } - -.toolbar-top .btn-info, -.sts-toolbar .btn-info { - color: white; - background-color: #bdbdbd; - border-color: #bdbdbd; } - -.toolbar-top .btn-info:hover, -.sts-toolbar .btn-info:hover { - color: white; - background-color: #8d8d8d; - border-color: #8d8d8d; } - .tooltip > .tooltip-inner { background-color: white !important; color: black !important; -- GitLab