Skip to content

Commit

Permalink
Dashboard: Create Export component
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Kevin Seestrand authored and Kevin Seestrand committed May 24, 2022
1 parent 5405007 commit e1d76f7
Show file tree
Hide file tree
Showing 4 changed files with 297 additions and 0 deletions.
91 changes: 91 additions & 0 deletions dashboard/src/components/Export.css
Original file line number Diff line number Diff line change
@@ -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;
}
}
180 changes: 180 additions & 0 deletions dashboard/src/components/Export.tsx
Original file line number Diff line number Diff line change
@@ -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<Props, State> {
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 (
<>
<Button
className="export-button"
color="info"
outline={true}
onClick={this.handleExportButtonClick}
>
<i className="fa fa-share"></i>
<span className="text">Vie</span>
</Button>
{this.state.showOptions === true ? (
<div className="filter-card">
<div className="row">
<Select
isMulti={true}
options={operatorOptions}
className="filter-field multi-select-field"
onChange={(option) =>
this.handleOperatorSelect(option as Option[])
}
placeholder="Valitse operaattori"
/>
<Select
isMulti={true}
options={paymentZoneOptions}
className="filter-field multi-select-field"
onChange={(option) =>
this.handlePaymentZoneSelect(option as Option[])
}
placeholder="Valitse maksuvyöhyke"
/>
</div>
<div className="row">
<DateTime
dateFormat="DD.MM.YYYY"
timeFormat="HH.mm"
className="filter-field datepicker-field"
onChange={(moment) =>
this.handleDateChange(moment, "startTime")
}
initialValue={this.state.startTime}
inputProps={{readOnly: true}}
/>
<DateTime
dateFormat="DD.MM.YYYY"
timeFormat="HH.mm"
className="filter-field datepicker-field"
onChange={(moment) =>
this.handleDateChange(moment, "endTime")
}
initialValue={this.state.endTime}
inputProps={{readOnly: true}}
/>
<Button
className={
this.state.parking_check === true
? "filter-field parking-check-button parking-check-active"
: "filter-field parking-check-button"
}
color="info"
outline={true}
onClick={this.handleCheckParkingCheckBoxChange}
>
<i className="fa fa-check-circle" />
<span className="text">Tarkistettu</span>
</Button>
<Button
className="filter-field download-button"
color="info"
outline={true}
onClick={this.handleDownloadClick}
>
<i className="fa fa-download" />
<span className="text">Lataa</span>
</Button>
</div>
</div>
) : null}
</>
);
}
}

export default Export;
7 changes: 7 additions & 0 deletions dashboard/src/containers/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -14,6 +15,7 @@ import LastParkingsTable from './LastParkingsTable';

interface Props {
autoUpdate?: boolean;
onMount?: () => void;
onUpdate?: () => void;
onLogout?: () => void;
}
Expand All @@ -25,6 +27,9 @@ class Dashboard extends Component<Props> {
timerInterval: number = 1000; // 1 second

componentDidMount() {
if(this.props.onMount) {
this.props.onMount();
}
if (this.props.autoUpdate && !this.timer) {
this.enableAutoUpdate();
}
Expand Down Expand Up @@ -82,6 +87,7 @@ class Dashboard extends Component<Props> {
<div className="d-flex">
<div className="table-card">
<TimeSelect />
<Export />
<div className="margin-bottom-8" />
<LastParkingsTable />
</div>
Expand All @@ -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()),
});
Expand Down
19 changes: 19 additions & 0 deletions dashboard/src/containers/Export.ts
Original file line number Diff line number Diff line change
@@ -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);

0 comments on commit e1d76f7

Please sign in to comment.