diff --git a/dashboard/src/actions.ts b/dashboard/src/actions.ts index 8800a400..30b6b9da 100644 --- a/dashboard/src/actions.ts +++ b/dashboard/src/actions.ts @@ -1,6 +1,6 @@ import { Moment } from 'moment'; -import { ParkingList, RegionList, RegionStatsList } from './api/types'; +import { ParkingList, RegionList, RegionStatsList, PaymentZoneList, OperatorList } from './api/types'; import { MapViewport } from './components/types'; interface CheckExistingLoginAction { @@ -139,6 +139,39 @@ export function receiveValidParkings( return {type: 'RECEIVE_VALID_PARKINGS', data, time}; } +interface ReceiveCsvAction { + type: 'RECEIVE_CSV'; + data: string; +} + +export function receiveCSV( + data: string +): ReceiveCsvAction { + return {type: 'RECEIVE_CSV', data} +} + +interface ReceiveOperatorsAction { + type: 'RECEIVE_OPERATORS'; + data: OperatorList; +} + +export function receiveOperators( + data: OperatorList +): ReceiveOperatorsAction { + return {type: 'RECEIVE_OPERATORS', data} +} + +interface ReceivePaymentZonesAction { + type: 'RECEIVE_PAYMENT_ZONES'; + data: PaymentZoneList; +} + +export function receivePaymentZones( + data: PaymentZoneList +): ReceivePaymentZonesAction { + return {type: 'RECEIVE_PAYMENT_ZONES', data} +} + export type Action = CheckExistingLoginAction | ResolveExistingLoginCheckAction | @@ -155,4 +188,7 @@ export type Action = SetSelectedRegionAction | ReceiveRegionStatsAction | ReceiveRegionInfoAction | - ReceiveValidParkingsAction; + ReceiveValidParkingsAction | + ReceiveOperatorsAction | + ReceivePaymentZonesAction | + ReceiveCsvAction; diff --git a/dashboard/src/converters.ts b/dashboard/src/converters.ts index 69a8813c..5b5e1247 100644 --- a/dashboard/src/converters.ts +++ b/dashboard/src/converters.ts @@ -30,6 +30,20 @@ export function convertRegionStats( }; } +export function convertOperator(operator: api.Operator): ui.Operator { + return { + id: operator.id, + name: operator.name!, + } +} + +export function convertPaymentZone(paymentZone: api.PaymentZone): ui.PaymentZone { + return { + code: paymentZone.code, + name: paymentZone.name!, + } +} + export function convertParking(parking: api.Parking): ui.Parking { const props = parking.properties; return { diff --git a/dashboard/src/dispatchers.ts b/dashboard/src/dispatchers.ts index 665d572b..15fc603c 100644 --- a/dashboard/src/dispatchers.ts +++ b/dashboard/src/dispatchers.ts @@ -5,6 +5,7 @@ import * as actions from './actions'; import { Action } from './actions'; import api from './api'; import { MapViewport } from './components/types'; +import { ExportFilters } from './api/types'; import { RootState } from './types'; const updateInterval = 5 * 60 * 1000; // 5 minutes in ms @@ -91,6 +92,13 @@ export function setDataTime(time?: moment.Moment) { }; } +export function fetchData() { + return (dispatch: Dispatch) => { + dispatch(fetchOperators()); + dispatch(fetchPaymentZones()); + }; +} + function fetchMissingData(time: moment.Moment) { return (dispatch: Dispatch, getState: () => RootState) => { const timestamp = time.valueOf(); @@ -164,3 +172,40 @@ export function fetchValidParkings(time: moment.Moment) { }); }; } + +export function downloadCSV(filters: ExportFilters) { + return (dispatch: Dispatch) => { + api.downloadCSV( + filters, + (response) => { + dispatch(actions.receiveCSV(response.data)); + }, + (error) => { + alert('CSV download failed: ' + error); + }); + }; +} + +export function fetchOperators() { + return (dispatch: Dispatch) => { + api.fetchOperators( + (response) => { + dispatch(actions.receiveOperators(response.data)); + }, + (error) => { + alert('Operator fetch failed: ' + error); + }); + }; +} + +export function fetchPaymentZones() { + return (dispatch: Dispatch) => { + api.fetchPaymentZones( + (response) => { + dispatch(actions.receivePaymentZones(response.data)); + }, + (error) => { + alert('Payment zone fetch failed: ' + error); + }); + }; +} diff --git a/dashboard/src/reducers.ts b/dashboard/src/reducers.ts index d9e33619..c00e0fdd 100644 --- a/dashboard/src/reducers.ts +++ b/dashboard/src/reducers.ts @@ -6,7 +6,7 @@ import { centerCoordinates } from './config'; import * as conv from './converters'; import { AuthenticationState, ParkingRegionMapState, ParkingsMap, RegionsMap, - RegionUsageHistory, ValidParkingsHistory, + RegionUsageHistory, ValidParkingsHistory, PaymentZones, Operators, ViewState } from './types'; // Auth state reducer //////////////////////////////////////////////// @@ -151,6 +151,22 @@ function parkings(state: ParkingsMap = {}, action: Action): ParkingsMap { return state; } +function operators(state: any = {}, action: Action): Operators { + if (action.type === 'RECEIVE_OPERATORS') { + const newOperators = mapByIdAndApply(action.data.results, conv.convertOperator as any); + return {...state, ...newOperators } as Operators; + } + return state; +} + +function paymentZones(state: any = {}, action: Action): PaymentZones { + if (action.type === 'RECEIVE_PAYMENT_ZONES') { + const newPaymentZones = _.assign({}, ...action.data.results.map((item: any) => ({[item.code]: conv.convertPaymentZone(item)}))); + return {...state, ...newPaymentZones} as PaymentZones; + } + return state; +} + function regionUsageHistory( state: RegionUsageHistory = {}, action: Action @@ -202,6 +218,8 @@ const rootReducer = () => combineReducers({ parkings, regionUsageHistory, validParkingsHistory, + operators, + paymentZones, }); export default rootReducer;