Client: upload a zip archive containing gpx files
This commit is contained in:
parent
373d20f0b7
commit
bddb86d765
@ -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}`)))
|
||||||
|
|
||||||
|
@ -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`))
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
|
@ -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); }
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user