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&nbsp;&nbsp;</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>
+            &nbsp;
+            <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>
+            &nbsp;
+            <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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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&nbsp;&nbsp;</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>
-                &nbsp;&nbsp;&nbsp; {
-                    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>
-                }
-                &nbsp;&nbsp;&nbsp;
-                <label>Period&nbsp;&nbsp;</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>
-                &nbsp;&nbsp;&nbsp;
-                <label>Error type&nbsp;&nbsp;</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}/>
-            &nbsp;&nbsp;&nbsp;
-            <label>Type&nbsp;&nbsp;</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>
-            &nbsp;&nbsp;&nbsp;&nbsp;
-            <label>Period&nbsp;&nbsp;</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>
-            &nbsp;
-            <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>
-            &nbsp;
-            <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>
-            &nbsp;&nbsp;&nbsp;
-            <label>Error type&nbsp;&nbsp;</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}/>
-            &nbsp;&nbsp;&nbsp;
-            <label>Type&nbsp;&nbsp;</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>
-            &nbsp;&nbsp;&nbsp;
-            <label>Antenna id&nbsp;&nbsp;</label>
-            <select>{options}</select>
-            &nbsp;&nbsp;&nbsp;
-            <label>Test Type&nbsp;&nbsp;</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>
-            &nbsp;&nbsp;&nbsp;&nbsp;
-            <label>Period&nbsp;&nbsp;</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>
-            &nbsp;
-            <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>
-            &nbsp;
-            <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