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);