Client: upload a zip archive containing gpx files

This commit is contained in:
Sam 2018-05-29 20:08:34 +02:00
parent 373d20f0b7
commit bddb86d765
7 changed files with 87 additions and 24 deletions

View File

@ -2,7 +2,7 @@ import mpwoGenericApi from '../mwpoApi'
import mpwoApi from '../mwpoApi/activities' import mpwoApi from '../mwpoApi/activities'
import { history } from '../index' import { history } from '../index'
import { formatChartData } from '../utils' import { formatChartData } from '../utils'
import { setError } from './index' import { setError, setLoading } from './index'
export const pushActivities = activities => ({ export const pushActivities = activities => ({
type: 'PUSH_ACTIVITIES', type: 'PUSH_ACTIVITIES',
@ -23,10 +23,17 @@ export const addActivity = form => dispatch => mpwoApi
.addActivity(form) .addActivity(form)
.then(ret => { .then(ret => {
if (ret.status === 'created') { if (ret.status === 'created') {
history.push(`/activities/${ret.data.activities[0].id}`) if (ret.data.activities.length === 0) {
dispatch(setError('activities: no correct file'))
} else if (ret.data.activities.length === 1) {
history.push(`/activities/${ret.data.activities[0].id}`)
} else { // ret.data.activities.length > 1
history.push('/')
}
} else { } else {
dispatch(setError(`activities: ${ret.message}`)) dispatch(setError(`activities: ${ret.message}`))
} }
dispatch(setLoading())
}) })
.catch(error => dispatch(setError(`activities: ${error}`))) .catch(error => dispatch(setError(`activities: ${error}`)))
@ -97,6 +104,7 @@ export const editActivity = form => dispatch => mpwoGenericApi
} else { } else {
dispatch(setError(`activities: ${ret.message}`)) dispatch(setError(`activities: ${ret.message}`))
} }
dispatch(setLoading())
}) })
.catch(error => dispatch(setError(`activities: ${error}`))) .catch(error => dispatch(setError(`activities: ${error}`)))

View File

@ -13,6 +13,10 @@ export const setError = message => ({
message, message,
}) })
export const setLoading = () => ({
type: 'SET_LOADING',
})
export const getData = (target, id = null, data = null) => dispatch => { export const getData = (target, id = null, data = null) => dispatch => {
if (id !== null && isNaN(id)) { if (id !== null && isNaN(id)) {
return dispatch(setError(target, `${target}: Incorrect id`)) return dispatch(setError(target, `${target}: Incorrect id`))

View File

@ -1,10 +1,11 @@
import React from 'react' import React from 'react'
import { Helmet } from 'react-helmet' import { Helmet } from 'react-helmet'
import { connect } from 'react-redux'
import FormWithGpx from './ActivityForms/FormWithGpx' import FormWithGpx from './ActivityForms/FormWithGpx'
import FormWithoutGpx from './ActivityForms/FormWithoutGpx' import FormWithoutGpx from './ActivityForms/FormWithoutGpx'
export default class ActivityAddEdit extends React.Component { class ActivityAddEdit extends React.Component {
constructor(props, context) { constructor(props, context) {
super(props, context) super(props, context)
this.state = { this.state = {
@ -21,7 +22,7 @@ export default class ActivityAddEdit extends React.Component {
} }
render() { render() {
const { activity, message, sports } = this.props const { activity, loading, message, sports } = this.props
const { withGpx } = this.state const { withGpx } = this.state
return ( return (
<div> <div>
@ -59,6 +60,7 @@ export default class ActivityAddEdit extends React.Component {
<input <input
type="radio" type="radio"
name="withGpx" name="withGpx"
disabled={loading}
checked={withGpx} checked={withGpx}
onChange={event => this.handleRadioChange(event)} onChange={event => this.handleRadioChange(event)}
/> />
@ -70,6 +72,7 @@ export default class ActivityAddEdit extends React.Component {
<input <input
type="radio" type="radio"
name="withoutGpx" name="withoutGpx"
disabled={loading}
checked={!withGpx} checked={!withGpx}
onChange={event => this.handleRadioChange(event)} onChange={event => this.handleRadioChange(event)}
/> />
@ -96,3 +99,8 @@ export default class ActivityAddEdit extends React.Component {
} }
} }
export default connect(
state => ({
loading: state.loading
}),
)(ActivityAddEdit)

View File

@ -1,12 +1,15 @@
import React from 'react' import React from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { setLoading } from '../../../actions/index'
import { addActivity, editActivity } from '../../../actions/activities' import { addActivity, editActivity } from '../../../actions/activities'
import { history } from '../../../index' import { history } from '../../../index'
function FormWithGpx (props) { function FormWithGpx (props) {
const { activity, onAddActivity, onEditActivity, sports } = props const {
activity, loading, onAddActivity, onEditActivity, sports
} = props
const sportId = activity ? activity.sport_id : '' const sportId = activity ? activity.sport_id : ''
return ( return (
<form <form
@ -20,6 +23,7 @@ function FormWithGpx (props) {
<select <select
className="form-control input-lg" className="form-control input-lg"
defaultValue={sportId} defaultValue={sportId}
disabled={loading}
name="sport" name="sport"
required required
> >
@ -39,6 +43,7 @@ function FormWithGpx (props) {
<input <input
name="title" name="title"
defaultValue={activity ? activity.title : ''} defaultValue={activity ? activity.title : ''}
disabled={loading}
className="form-control input-lg" className="form-control input-lg"
/> />
</label> </label>
@ -46,10 +51,12 @@ function FormWithGpx (props) {
) : ( ) : (
<div className="form-group"> <div className="form-group">
<label> <label>
GPX file: <strong>gpx</strong> file or <strong>zip</strong>{' '}
file containing <strong>gpx</strong> (no folder inside):
<input <input
accept=".gpx" accept=".gpx, .zip"
className="form-control input-lg" className="form-control input-lg"
disabled={loading}
name="gpxFile" name="gpxFile"
required required
type="file" type="file"
@ -57,30 +64,39 @@ function FormWithGpx (props) {
</label> </label>
</div> </div>
)} )}
<input {loading ? (
type="submit" <div className="loader" />
className="btn btn-primary btn-lg btn-block" ) : (
onClick={ <div>
event => activity <input
? onEditActivity(event, activity) type="submit"
: onAddActivity(event) className="btn btn-primary btn-lg btn-block"
} onClick={
value="Submit" event => activity
/> ? onEditActivity(event, activity)
<input : onAddActivity(event)
type="submit" }
className="btn btn-secondary btn-lg btn-block" value="Submit"
onClick={() => history.go(-1)} />
value="Cancel" <input
/> type="submit"
className="btn btn-secondary btn-lg btn-block"
onClick={() => history.go(-1)}
value="Cancel"
/>
</div>
)}
</form> </form>
) )
} }
export default connect( export default connect(
() => ({ }), state => ({
loading: state.loading
}),
dispatch => ({ dispatch => ({
onAddActivity: e => { onAddActivity: e => {
dispatch(setLoading())
const form = new FormData() const form = new FormData()
form.append('file', e.target.form.gpxFile.files[0]) form.append('file', e.target.form.gpxFile.files[0])
form.append( form.append(
@ -89,6 +105,7 @@ export default connect(
dispatch(addActivity(form)) dispatch(addActivity(form))
}, },
onEditActivity: (e, activity) => { onEditActivity: (e, activity) => {
dispatch(setLoading())
dispatch(editActivity({ dispatch(editActivity({
id: activity.id, id: activity.id,
sport_id: +e.target.form.sport.value, sport_id: +e.target.form.sport.value,

View File

@ -173,3 +173,18 @@ input, textarea {
.unlink { .unlink {
color: black; color: black;
} }
.loader {
animation: spin 2s linear infinite;
border: 16px solid #f3f3f3;
border-top: 16px solid #3498db;
border-radius: 50%;
height: 120px;
margin-left: 41%;
width: 120px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

View File

@ -93,6 +93,15 @@ const gpx = (state = initial.gpx, action) => {
} }
} }
const loading = (state = initial.loading, action) => {
switch (action.type) {
case 'SET_LOADING':
return !state
default:
return state
}
}
const message = (state = initial.message, action) => { const message = (state = initial.message, action) => {
switch (action.type) { switch (action.type) {
case 'AUTH_ERROR': case 'AUTH_ERROR':
@ -176,6 +185,7 @@ const reducers = combineReducers({
formData, formData,
formProfile, formProfile,
gpx, gpx,
loading,
message, message,
messages, messages,
records, records,

View File

@ -44,6 +44,7 @@ export default {
chartData: [], chartData: [],
// check if storing gpx content is OK // check if storing gpx content is OK
gpx: null, gpx: null,
loading: false,
records: { records: {
...emptyData, ...emptyData,
}, },