From e1d76f71c354a5fa4bc4f4214f34e7ef0011282a Mon Sep 17 00:00:00 2001 From: Kevin Seestrand Date: Fri, 4 Feb 2022 17:13:19 +0200 Subject: [PATCH] Dashboard: Create Export component Create Export component. Create new 'onMount' dispatch. This calls the 'fetchData' function. The data in question which is Operators and Payment zones only need to get fetched once. --- dashboard/src/components/Export.css | 91 +++++++++++++ dashboard/src/components/Export.tsx | 180 +++++++++++++++++++++++++ dashboard/src/containers/Dashboard.tsx | 7 + dashboard/src/containers/Export.ts | 19 +++ 4 files changed, 297 insertions(+) create mode 100644 dashboard/src/components/Export.css create mode 100644 dashboard/src/components/Export.tsx create mode 100644 dashboard/src/containers/Export.ts diff --git a/dashboard/src/components/Export.css b/dashboard/src/components/Export.css new file mode 100644 index 00000000..aed11d3f --- /dev/null +++ b/dashboard/src/components/Export.css @@ -0,0 +1,91 @@ +i { + margin-right: 3px !important; +} + +.filter-card { + display: table !important; + width: 100% !important; + margin-left: 0 !important; + padding-left: 0 !important +} + +.row { + display: table !important; + width: 100% !important; + margin-left: 0 !important; + flex: 1 !important; + box-sizing: border-box; +} + +.export-button { + color: #515455 !important; + box-shadow: none !important; + background: hsl(32, 39%, 81%) !important; + border-color: #e1cfbb !important; + margin-top: 4px; +} + +.export-button:hover { + background: #f0e5dd !important; + border-color: #f0e5dd !important; +} + +.download-button { + color: #515455 !important; + box-shadow: none !important; + background: #e1cfbb !important; + border-color: #e1cfbb !important; + float: right; + margin-right: 4px; +} + +.download-button:hover { + background: #f0e5dd !important; + border-color: #f0e5dd !important; +} + +.filter-field { + display: table-cell !important; + padding: 5px !important; +} + +.multi-select-field { + width: 50% !important; +} + +.datepicker-field { + width: 160px !important; +} + +.parking-check-button { + color: #515455 !important; + box-shadow: none !important; + background: #f0e5dd !important; + border-color: #e1cfbb !important; +} + +.parking-check-button:hover { + background: #e1cfbb !important; + border-color: #f0e5dd !important; +} + +.parking-check-active { + background: #e1cfbb !important; +} + +@media screen and (max-width: 28rem) { + .export-button .text { + display: none; + margin-right: auto !important; + } + + .download-button .text { + display: none; + margin-right: auto !important; + } + + .parking-check-button .text { + display: none; + margin-right: auto !important; + } +} diff --git a/dashboard/src/components/Export.tsx b/dashboard/src/components/Export.tsx new file mode 100644 index 00000000..3229d9da --- /dev/null +++ b/dashboard/src/components/Export.tsx @@ -0,0 +1,180 @@ +import DateTime from "react-datetime"; +import Select from "react-select"; +import moment, { Moment } from "moment"; +import { Component } from "react"; +import { Button } from "reactstrap"; +import { ExportFilters } from "../api/types"; +import { Operators, PaymentZones } from "../types"; + +import "./Export.css"; + +type Option = { + value: string; + label: string; +}; + +interface State { + operatorSelections: string[]; + paymentZoneSelections: string[]; + startTime: Moment; + endTime: Moment; + parking_check: boolean; + showOptions: boolean; +} + +export interface Props { + operators: Operators, + paymentZones: PaymentZones, + downloadCSV?: (filters: ExportFilters) => void; +} + +const initialState: State = { + showOptions: false, + operatorSelections: [], + paymentZoneSelections: [], + startTime: moment().subtract(30, 'days'), + endTime: moment(), + parking_check: false, +} + +class Export extends Component { + constructor(props: Props) { + super(props); + this.state = initialState; + } + + handleExportButtonClick = () => { + this.setState((prevState) => ({...initialState, showOptions: !prevState.showOptions})); + } + + handleDownloadClick = () => { + if (this.props.downloadCSV) { + const filters: ExportFilters = { + ...(this.state.operatorSelections.length && { operators: this.state.operatorSelections }), + ...(this.state.paymentZoneSelections.length && { payment_zones: this.state.paymentZoneSelections }), + time_start: this.state.startTime.format("DD.MM.YYYY HH.mm"), + time_end: this.state.endTime.format("DD.MM.YYYY HH.mm"), + parking_check: this.state.parking_check, + }; + + this.props.downloadCSV(filters); + } + }; + + handleDateChange = (time: moment.Moment | string, name: string) => { + this.setState({ + [name]: moment(time) + } as any); + }; + + handleOperatorSelect = (options: Option[]) => { + this.setState({ + operatorSelections: options.map((option) => option.value), + }); + }; + + handlePaymentZoneSelect = (options: Option[]) => { + this.setState({ + paymentZoneSelections: options.map((option) => option.value), + }); + }; + + handleCheckParkingCheckBoxChange = () => { + this.setState((prevState) => ({ + parking_check: !prevState.parking_check, + })); + }; + + render() { + const { + operators, + paymentZones + } = this.props; + const operatorOptions = Object.keys(operators).map((k) => ({value: operators[k].id, label: operators[k].name})); + const paymentZoneOptions = Object.keys(paymentZones).map((k) => ({value: paymentZones[k].code, label: paymentZones[k].name})); + + return ( + <> + + {this.state.showOptions === true ? ( +
+
+ + this.handlePaymentZoneSelect(option as Option[]) + } + placeholder="Valitse maksuvyöhyke" + /> +
+
+ + this.handleDateChange(moment, "startTime") + } + initialValue={this.state.startTime} + inputProps={{readOnly: true}} + /> + + this.handleDateChange(moment, "endTime") + } + initialValue={this.state.endTime} + inputProps={{readOnly: true}} + /> + + +
+
+ ) : null} + + ); + } +} + +export default Export; diff --git a/dashboard/src/containers/Dashboard.tsx b/dashboard/src/containers/Dashboard.tsx index 0ddb5fb4..9e13738b 100644 --- a/dashboard/src/containers/Dashboard.tsx +++ b/dashboard/src/containers/Dashboard.tsx @@ -3,6 +3,7 @@ import { connect } from 'react-redux'; import { Button } from 'reactstrap'; +import Export from './Export'; import RegionSelector from './RegionSelector'; import * as dispatchers from '../dispatchers'; import { RootState } from '../types'; @@ -14,6 +15,7 @@ import LastParkingsTable from './LastParkingsTable'; interface Props { autoUpdate?: boolean; + onMount?: () => void; onUpdate?: () => void; onLogout?: () => void; } @@ -25,6 +27,9 @@ class Dashboard extends Component { timerInterval: number = 1000; // 1 second componentDidMount() { + if(this.props.onMount) { + this.props.onMount(); + } if (this.props.autoUpdate && !this.timer) { this.enableAutoUpdate(); } @@ -82,6 +87,7 @@ class Dashboard extends Component {
+
@@ -104,6 +110,7 @@ const mapStateToProps = (state: RootState): Props => ({ }); const mapDispatchToProps = (dispatch: any): Props => ({ + onMount: () => dispatch(dispatchers.fetchData()), onUpdate: () => dispatch(dispatchers.updateData()), onLogout: () => dispatch(dispatchers.logout()), }); diff --git a/dashboard/src/containers/Export.ts b/dashboard/src/containers/Export.ts new file mode 100644 index 00000000..b7ac08fa --- /dev/null +++ b/dashboard/src/containers/Export.ts @@ -0,0 +1,19 @@ +import { connect } from 'react-redux'; + +import { RootState } from '../types'; +import { ExportFilters } from '../api/types' +import Export, { Props } from '../components/Export'; +import * as dispatchers from '../dispatchers'; + +function mapStateToProps(state: RootState): Props { + return { + operators: state.operators, + paymentZones: state.paymentZones, + }; +} + +const mapDispatchToProps = (dispatch: any) => ({ + downloadCSV: (filters: ExportFilters) => dispatch(dispatchers.downloadCSV(filters)), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(Export);