Client - add application configuration in Application Admin - #15
This commit is contained in:
parent
97534b698b
commit
1398c7ff4a
@ -11,8 +11,8 @@ def init_config():
|
|||||||
"""
|
"""
|
||||||
init application configuration if not existing in database
|
init application configuration if not existing in database
|
||||||
|
|
||||||
Note: get some configuration values from env variables (for FitTrackee versions
|
Note: get some configuration values from env variables
|
||||||
prior to v0.3.0)
|
(for FitTrackee versions prior to v0.3.0)
|
||||||
"""
|
"""
|
||||||
existing_config = AppConfig.query.one_or_none()
|
existing_config = AppConfig.query.one_or_none()
|
||||||
if not existing_config:
|
if not existing_config:
|
||||||
|
@ -43,6 +43,10 @@ export const addActivity = form => dispatch =>
|
|||||||
dispatch(loadProfile())
|
dispatch(loadProfile())
|
||||||
history.push('/')
|
history.push('/')
|
||||||
}
|
}
|
||||||
|
} else if (ret.status === 413) {
|
||||||
|
dispatch(
|
||||||
|
setError('activities|File size is greater than the allowed size')
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
dispatch(setError(`activities|${ret.message}`))
|
dispatch(setError(`activities|${ret.message}`))
|
||||||
}
|
}
|
||||||
|
38
fittrackee_client/src/actions/application.js
Normal file
38
fittrackee_client/src/actions/application.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import FitTrackeeGenericApi from '../fitTrackeeApi'
|
||||||
|
import { setError } from './index'
|
||||||
|
|
||||||
|
export const setAppConfig = data => ({
|
||||||
|
type: 'SET_APP_CONFIG',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const setAppStats = data => ({
|
||||||
|
type: 'SET_APP_STATS',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const getAppData = target => dispatch =>
|
||||||
|
FitTrackeeGenericApi.getData(target)
|
||||||
|
.then(ret => {
|
||||||
|
if (ret.status === 'success') {
|
||||||
|
if (target === 'config') {
|
||||||
|
dispatch(setAppConfig(ret.data))
|
||||||
|
} else if (target === 'stats/all') {
|
||||||
|
dispatch(setAppStats(ret.data))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dispatch(setError(`application|${ret.message}`))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => dispatch(setError(`application|${error}`)))
|
||||||
|
|
||||||
|
export const updateAppConfig = formData => dispatch =>
|
||||||
|
FitTrackeeGenericApi.updateData('config', formData)
|
||||||
|
.then(ret => {
|
||||||
|
if (ret.status === 'success') {
|
||||||
|
dispatch(setAppConfig(ret.data))
|
||||||
|
} else {
|
||||||
|
dispatch(setError(`application|${ret.message}`))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => dispatch(setError(`application|${error}`)))
|
@ -1,22 +1,6 @@
|
|||||||
import FitTrackeeGenericApi from '../fitTrackeeApi'
|
import FitTrackeeGenericApi from '../fitTrackeeApi'
|
||||||
import { setData, setError } from './index'
|
import { setData, setError } from './index'
|
||||||
|
|
||||||
export const setAppStats = data => ({
|
|
||||||
type: 'SET_APP_STATS',
|
|
||||||
data,
|
|
||||||
})
|
|
||||||
|
|
||||||
export const getAppStats = () => dispatch =>
|
|
||||||
FitTrackeeGenericApi.getData('stats/all')
|
|
||||||
.then(ret => {
|
|
||||||
if (ret.status === 'success') {
|
|
||||||
dispatch(setAppStats(ret.data))
|
|
||||||
} else {
|
|
||||||
dispatch(setError(`application|${ret.message}`))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => dispatch(setError(`application|${error}`)))
|
|
||||||
|
|
||||||
export const getStats = (userId, type, data) => dispatch =>
|
export const getStats = (userId, type, data) => dispatch =>
|
||||||
FitTrackeeGenericApi.getData(`stats/${userId}/${type}`, data)
|
FitTrackeeGenericApi.getData(`stats/${userId}/${type}`, data)
|
||||||
.then(ret => {
|
.then(ret => {
|
||||||
|
@ -5,17 +5,27 @@ import { connect } from 'react-redux'
|
|||||||
import { setLoading } from '../../../actions/index'
|
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'
|
||||||
import { fileSizeLimit, gpxLimit, zipSizeLimit } from '../../../utils'
|
import { getFileSize } from '../../../utils'
|
||||||
import { translateSports } from '../../../utils/activities'
|
import { translateSports } from '../../../utils/activities'
|
||||||
|
|
||||||
function FormWithGpx(props) {
|
function FormWithGpx(props) {
|
||||||
const { activity, loading, onAddActivity, onEditActivity, sports, t } = props
|
const {
|
||||||
|
activity,
|
||||||
|
appConfig,
|
||||||
|
loading,
|
||||||
|
onAddActivity,
|
||||||
|
onEditActivity,
|
||||||
|
sports,
|
||||||
|
t,
|
||||||
|
} = props
|
||||||
const sportId = activity ? activity.sport_id : ''
|
const sportId = activity ? activity.sport_id : ''
|
||||||
const translatedSports = translateSports(sports, t, true)
|
const translatedSports = translateSports(sports, t, true)
|
||||||
// prettier-ignore
|
const zipTooltip = `${t('activities:no folder inside')}, ${
|
||||||
const zipTooltip =
|
appConfig.gpx_limit_import
|
||||||
`${t('activities:no folder inside')}, ${gpxLimit} ${
|
} ${t('activities:files max')}, ${t('activities:max size')}: ${getFileSize(
|
||||||
t('activities:files max')}, ${t('activities:max size')}: ${zipSizeLimit}`
|
appConfig.max_zip_file_size
|
||||||
|
)}`
|
||||||
|
const fileSizeLimit = getFileSize(appConfig.max_single_file_size)
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
encType="multipart/form-data"
|
encType="multipart/form-data"
|
||||||
@ -130,6 +140,7 @@ function FormWithGpx(props) {
|
|||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
state => ({
|
state => ({
|
||||||
|
appConfig: state.application.config,
|
||||||
loading: state.loading,
|
loading: state.loading,
|
||||||
}),
|
}),
|
||||||
dispatch => ({
|
dispatch => ({
|
||||||
|
@ -3,15 +3,16 @@ import { Helmet } from 'react-helmet'
|
|||||||
|
|
||||||
import AdminStats from './AdminStats'
|
import AdminStats from './AdminStats'
|
||||||
|
|
||||||
export default function AdminDashboard() {
|
export default function AdminDashboard(props) {
|
||||||
|
const { t } = props
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>FitTrackee - Administration</title>
|
<title>{t('administration:FitTrackee administration')}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<div className="card activity-card">
|
<div className="card activity-card">
|
||||||
<div className="card-header">
|
<div className="card-header">
|
||||||
<strong>FitTrackee administration</strong>
|
<strong>{t('administration:FitTrackee administration')}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<AdminStats />
|
<AdminStats />
|
||||||
|
@ -2,7 +2,7 @@ import React from 'react'
|
|||||||
import { withTranslation } from 'react-i18next'
|
import { withTranslation } from 'react-i18next'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
|
|
||||||
import { getAppStats } from '../../actions/stats'
|
import { getAppData } from '../../actions/application'
|
||||||
|
|
||||||
class AdminStats extends React.Component {
|
class AdminStats extends React.Component {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@ -80,7 +80,7 @@ export default withTranslation()(
|
|||||||
}),
|
}),
|
||||||
dispatch => ({
|
dispatch => ({
|
||||||
loadAppStats: () => {
|
loadAppStats: () => {
|
||||||
dispatch(getAppStats())
|
dispatch(getAppData('stats/all'))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
)(AdminStats)
|
)(AdminStats)
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Helmet } from 'react-helmet'
|
||||||
|
|
||||||
|
import Message from '../../Common/Message'
|
||||||
|
import { history } from '../../../index'
|
||||||
|
import { getFileSize } from '../../../utils'
|
||||||
|
|
||||||
|
export default function Config({ appConfig, message, t, updateIsInEdition }) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Helmet>
|
||||||
|
<title>
|
||||||
|
FitTrackee - {t('administration:Application configuration')}
|
||||||
|
</title>
|
||||||
|
</Helmet>
|
||||||
|
<Message message={message} t={t} />
|
||||||
|
<div className="container">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-md-12">
|
||||||
|
<div className="card">
|
||||||
|
<div className="card-header">
|
||||||
|
{t('administration:Application configuration')}
|
||||||
|
</div>
|
||||||
|
<div className="card-body">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col">
|
||||||
|
<table className="table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">
|
||||||
|
{t('administration:Enable registration')}:
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
{appConfig.registration
|
||||||
|
? t('common:yes')
|
||||||
|
: t('common:no')}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">
|
||||||
|
{t('administration:Max. number of active users')}:
|
||||||
|
</th>
|
||||||
|
<td>{appConfig.max_users}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">
|
||||||
|
{t(
|
||||||
|
'administration:Max. size of ' + 'uploaded files'
|
||||||
|
)}
|
||||||
|
:
|
||||||
|
</th>
|
||||||
|
<td>{getFileSize(appConfig.max_single_file_size)}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">
|
||||||
|
{t('administration:Max. size of zip archive')}:
|
||||||
|
</th>
|
||||||
|
<td>{getFileSize(appConfig.max_zip_file_size)}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">
|
||||||
|
{t('administration:Max. files of zip archive')}:
|
||||||
|
</th>
|
||||||
|
<td>{appConfig.gpx_limit_import}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<input
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-primary btn-lg btn-block"
|
||||||
|
onClick={() => updateIsInEdition()}
|
||||||
|
value={t('common:Edit')}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-secondary btn-lg btn-block"
|
||||||
|
onClick={() => history.push('/admin')}
|
||||||
|
value={t('common:Back')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,198 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { Helmet } from 'react-helmet'
|
||||||
|
|
||||||
|
import Message from '../../Common/Message'
|
||||||
|
import { updateAppConfig } from '../../../actions/application'
|
||||||
|
import { getFileSizeInMB } from '../../../utils'
|
||||||
|
|
||||||
|
class AdminApplication extends React.Component {
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context)
|
||||||
|
this.state = {
|
||||||
|
formData: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
componentDidMount() {
|
||||||
|
this.initForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
initForm() {
|
||||||
|
const { appConfig } = this.props
|
||||||
|
const formData = {}
|
||||||
|
Object.keys(appConfig).map(k =>
|
||||||
|
appConfig[k] === null
|
||||||
|
? (formData[k] = '')
|
||||||
|
: ['max_single_file_size', 'max_zip_file_size'].includes(k)
|
||||||
|
? (formData[k] = getFileSizeInMB(appConfig[k]))
|
||||||
|
: (formData[k] = appConfig[k])
|
||||||
|
)
|
||||||
|
this.setState({ formData })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleFormChange(e) {
|
||||||
|
const { formData } = this.state
|
||||||
|
if (e.target.name === 'registration') {
|
||||||
|
formData[e.target.name] = e.target.checked
|
||||||
|
} else {
|
||||||
|
formData[e.target.name] = +e.target.value
|
||||||
|
}
|
||||||
|
this.setState(formData)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
message,
|
||||||
|
onHandleConfigFormSubmit,
|
||||||
|
t,
|
||||||
|
updateIsInEdition,
|
||||||
|
} = this.props
|
||||||
|
const { formData } = this.state
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Helmet>
|
||||||
|
<title>
|
||||||
|
FitTrackee - {t('administration:Application configuration')}
|
||||||
|
</title>
|
||||||
|
</Helmet>
|
||||||
|
{message && <Message message={message} t={t} />}
|
||||||
|
{Object.keys(formData).length > 0 && (
|
||||||
|
<div className="container">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-md-12">
|
||||||
|
<div className="card">
|
||||||
|
<div className="card-header">
|
||||||
|
{t('administration:Application configuration')}
|
||||||
|
</div>
|
||||||
|
<div className="card-body">
|
||||||
|
<form
|
||||||
|
className="app-config-form"
|
||||||
|
onSubmit={event => {
|
||||||
|
event.preventDefault()
|
||||||
|
onHandleConfigFormSubmit(formData)
|
||||||
|
updateIsInEdition()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="form-group row">
|
||||||
|
<label
|
||||||
|
className="col-sm-6 col-form-label"
|
||||||
|
htmlFor="registration"
|
||||||
|
>
|
||||||
|
{t('administration:Enable registration')}:
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className="col-sm-5"
|
||||||
|
id="registration"
|
||||||
|
name="registration"
|
||||||
|
type="checkbox"
|
||||||
|
checked={formData.registration}
|
||||||
|
onChange={e => this.handleFormChange(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form-group row">
|
||||||
|
<label
|
||||||
|
className="col-sm-6 col-form-label"
|
||||||
|
htmlFor="max_users"
|
||||||
|
>
|
||||||
|
{t('administration:Max. number of active users')}:
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className="col-sm-5"
|
||||||
|
id="max_users"
|
||||||
|
name="max_users"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
value={formData.max_users}
|
||||||
|
onChange={e => this.handleFormChange(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form-group row">
|
||||||
|
<label
|
||||||
|
className="col-sm-6 col-form-label"
|
||||||
|
htmlFor="max_single_file_size"
|
||||||
|
>
|
||||||
|
{t(
|
||||||
|
'administration:Max. size of uploaded files (in Mb)'
|
||||||
|
)}
|
||||||
|
:
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className="col-sm-5"
|
||||||
|
id="max_single_file_size"
|
||||||
|
name="max_single_file_size"
|
||||||
|
type="number"
|
||||||
|
step="0.1"
|
||||||
|
min="0"
|
||||||
|
value={formData.max_single_file_size}
|
||||||
|
onChange={e => this.handleFormChange(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form-group row">
|
||||||
|
<label
|
||||||
|
className="col-sm-6 col-form-label"
|
||||||
|
htmlFor="max_zip_file_size"
|
||||||
|
>
|
||||||
|
{t('administration:Max. size of zip archive (in Mb)')}
|
||||||
|
:
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className="col-sm-5"
|
||||||
|
id="max_zip_file_size"
|
||||||
|
name="max_zip_file_size"
|
||||||
|
type="number"
|
||||||
|
step="0.1"
|
||||||
|
min="0"
|
||||||
|
value={formData.max_zip_file_size}
|
||||||
|
onChange={e => this.handleFormChange(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form-group row">
|
||||||
|
<label
|
||||||
|
className="col-sm-6 col-form-label"
|
||||||
|
htmlFor="gpx_limit_import"
|
||||||
|
>
|
||||||
|
{t('administration:Max. files of zip archive')}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className="col-sm-5"
|
||||||
|
id="gpx_limit_import"
|
||||||
|
name="gpx_limit_import"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
value={formData.gpx_limit_import}
|
||||||
|
onChange={e => this.handleFormChange(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-primary btn-lg btn-block"
|
||||||
|
value={t('common:Submit')}
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-secondary btn-lg btn-block"
|
||||||
|
onClick={() => updateIsInEdition()}
|
||||||
|
value={t('common:Cancel')}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
() => ({}),
|
||||||
|
dispatch => ({
|
||||||
|
onHandleConfigFormSubmit: formData => {
|
||||||
|
formData.max_single_file_size *= 1048576
|
||||||
|
formData.max_zip_file_size *= 1048576
|
||||||
|
dispatch(updateAppConfig(formData))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)(AdminApplication)
|
54
fittrackee_client/src/components/Admin/Application/index.jsx
Normal file
54
fittrackee_client/src/components/Admin/Application/index.jsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
import { Helmet } from 'react-helmet'
|
||||||
|
|
||||||
|
import Config from './Config'
|
||||||
|
import ConfigForm from './ConfigForm'
|
||||||
|
import Message from '../../Common/Message'
|
||||||
|
|
||||||
|
class AdminApplication extends React.Component {
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context)
|
||||||
|
this.state = {
|
||||||
|
isInEdition: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { appConfig, message, t } = this.props
|
||||||
|
const { isInEdition } = this.state
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Helmet>
|
||||||
|
<title>FitTrackee - {t('administration:Administration')}</title>
|
||||||
|
</Helmet>
|
||||||
|
{message && <Message message={message} t={t} />}
|
||||||
|
{isInEdition ? (
|
||||||
|
<ConfigForm
|
||||||
|
appConfig={appConfig}
|
||||||
|
message={message}
|
||||||
|
updateIsInEdition={() => {
|
||||||
|
this.setState({ isInEdition: false })
|
||||||
|
}}
|
||||||
|
t={t}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Config
|
||||||
|
appConfig={appConfig}
|
||||||
|
message={message}
|
||||||
|
t={t}
|
||||||
|
updateIsInEdition={() => {
|
||||||
|
this.setState({ isInEdition: true })
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(state => ({
|
||||||
|
appConfig: state.application.config,
|
||||||
|
message: state.message,
|
||||||
|
user: state.user,
|
||||||
|
}))(AdminApplication)
|
@ -2,14 +2,13 @@ import React from 'react'
|
|||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
import { withTranslation } from 'react-i18next'
|
import { withTranslation } from 'react-i18next'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { Link, Redirect, Route, Switch } from 'react-router-dom'
|
import { Link, Route, Switch } from 'react-router-dom'
|
||||||
|
|
||||||
|
import AdminApplication from './Application'
|
||||||
import AdminDashboard from './AdminDashboard'
|
import AdminDashboard from './AdminDashboard'
|
||||||
import AdminMenu from './AdminMenu'
|
import AdminMenu from './AdminMenu'
|
||||||
import AdminSports from './Sports'
|
import AdminSports from './Sports'
|
||||||
import AccessDenied from './../Others/AccessDenied'
|
|
||||||
import NotFound from './../Others/NotFound'
|
import NotFound from './../Others/NotFound'
|
||||||
import { isLoggedIn } from '../../utils'
|
|
||||||
|
|
||||||
function Admin(props) {
|
function Admin(props) {
|
||||||
const { t, user } = props
|
const { t, user } = props
|
||||||
@ -19,6 +18,7 @@ function Admin(props) {
|
|||||||
<title>FitTrackee - {t('administration:Administration')}</title>
|
<title>FitTrackee - {t('administration:Administration')}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<div className="container dashboard">
|
<div className="container dashboard">
|
||||||
|
{user.admin ? (
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-md-3">
|
<div className="col-md-3">
|
||||||
<div className="card activity-card">
|
<div className="card activity-card">
|
||||||
@ -37,14 +37,17 @@ function Admin(props) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-9">
|
<div className="col-md-9">
|
||||||
{isLoggedIn() ? (
|
|
||||||
user.admin ? (
|
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route
|
<Route
|
||||||
exact
|
exact
|
||||||
path="/admin"
|
path="/admin"
|
||||||
render={() => <AdminDashboard t={t} />}
|
render={() => <AdminDashboard t={t} />}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
exact
|
||||||
|
path="/admin/application"
|
||||||
|
render={() => <AdminApplication t={t} />}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
exact
|
exact
|
||||||
path="/admin/sports"
|
path="/admin/sports"
|
||||||
@ -52,16 +55,13 @@ function Admin(props) {
|
|||||||
/>
|
/>
|
||||||
<Route component={NotFound} />
|
<Route component={NotFound} />
|
||||||
</Switch>
|
</Switch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<AccessDenied />
|
<NotFound />
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<Redirect to="/login" />
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,6 +177,10 @@ label {
|
|||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-config-form label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
import { Redirect, Route, Switch } from 'react-router-dom'
|
import { Redirect, Route, Switch } from 'react-router-dom'
|
||||||
|
|
||||||
import './App.css'
|
import './App.css'
|
||||||
@ -14,13 +15,17 @@ import Profile from './User/Profile'
|
|||||||
import ProfileEdit from './User/ProfileEdit'
|
import ProfileEdit from './User/ProfileEdit'
|
||||||
import Statistics from './Statistics'
|
import Statistics from './Statistics'
|
||||||
import UserForm from './User/UserForm'
|
import UserForm from './User/UserForm'
|
||||||
|
import { getAppData } from '../actions/application'
|
||||||
import { isLoggedIn } from '../utils'
|
import { isLoggedIn } from '../utils'
|
||||||
|
|
||||||
export default class App extends React.Component {
|
class App extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
this.props = props
|
this.props = props
|
||||||
}
|
}
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.loadAppConfig()
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
@ -74,7 +79,12 @@ export default class App extends React.Component {
|
|||||||
<Route exact path="/activities/history" component={Activities} />
|
<Route exact path="/activities/history" component={Activities} />
|
||||||
<Route exact path="/activities/statistics" component={Statistics} />
|
<Route exact path="/activities/statistics" component={Statistics} />
|
||||||
<Route path="/activities" component={Activity} />
|
<Route path="/activities" component={Activity} />
|
||||||
<Route path="/admin" component={Admin} />
|
<Route
|
||||||
|
path="/admin"
|
||||||
|
render={() =>
|
||||||
|
isLoggedIn() ? <Admin /> : <UserForm formType={'login'} />
|
||||||
|
}
|
||||||
|
/>
|
||||||
<Route component={NotFound} />
|
<Route component={NotFound} />
|
||||||
</Switch>
|
</Switch>
|
||||||
<Footer />
|
<Footer />
|
||||||
@ -82,3 +92,11 @@ export default class App extends React.Component {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export default connect(
|
||||||
|
() => ({}),
|
||||||
|
dispatch => ({
|
||||||
|
loadAppConfig: () => {
|
||||||
|
dispatch(getAppData('config'))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)(App)
|
||||||
|
@ -1,16 +1,24 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
|
|
||||||
export default function AccessDenied() {
|
export default function AccessDenied(props) {
|
||||||
|
const { t } = props
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>FitTrackee - Access denied</title>
|
<title>FitTrackee - {t('Access denied')}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<h1 className="page-title">Access denied</h1>
|
<div className="row">
|
||||||
<p className="App-center">
|
<div className="col-2" />
|
||||||
{"You don't have permissions to access this page."}
|
<div className="card col-8">
|
||||||
</p>
|
<div className="card-body">
|
||||||
|
<div className="text-center">
|
||||||
|
{t("You don't have permissions to access this page.")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-2" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
export default function NotFound() {
|
export default function NotFound() {
|
||||||
|
const { t } = useTranslation()
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>fittrackee - 404</title>
|
<title>fittrackee - 404</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<h1 className="page-title">Page not found</h1>
|
<h1 className="page-title">{t('Page not found')}</h1>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
|
|
||||||
import { history } from '../../index'
|
import { history } from '../../index'
|
||||||
import { isRegistrationAllowed } from '../../utils'
|
|
||||||
|
|
||||||
export default function Form(props) {
|
export default function Form(props) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@ -22,7 +21,7 @@ export default function Form(props) {
|
|||||||
<div className="col-md-6">
|
<div className="col-md-6">
|
||||||
<hr />
|
<hr />
|
||||||
<br />
|
<br />
|
||||||
{props.formType === 'register' && !isRegistrationAllowed ? (
|
{props.formType === 'register' && !props.isRegistrationAllowed ? (
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<div className="card-body">Registration is disabled.</div>
|
<div className="card-body">Registration is disabled.</div>
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
|
@ -7,15 +7,23 @@ import { Link } from 'react-router-dom'
|
|||||||
|
|
||||||
import Message from '../Common/Message'
|
import Message from '../Common/Message'
|
||||||
import { deletePicture, uploadPicture } from '../../actions/user'
|
import { deletePicture, uploadPicture } from '../../actions/user'
|
||||||
import { apiUrl, fileSizeLimit } from '../../utils'
|
import { apiUrl, getFileSize } from '../../utils'
|
||||||
|
|
||||||
function Profile({ message, onDeletePicture, onUploadPicture, t, user }) {
|
function Profile({
|
||||||
|
appConfig,
|
||||||
|
message,
|
||||||
|
onDeletePicture,
|
||||||
|
onUploadPicture,
|
||||||
|
t,
|
||||||
|
user,
|
||||||
|
}) {
|
||||||
const createdAt = user.created_at
|
const createdAt = user.created_at
|
||||||
? format(new Date(user.created_at), 'dd/MM/yyyy HH:mm')
|
? format(new Date(user.created_at), 'dd/MM/yyyy HH:mm')
|
||||||
: ''
|
: ''
|
||||||
const birthDate = user.birth_date
|
const birthDate = user.birth_date
|
||||||
? format(new Date(user.birth_date), 'dd/MM/yyyy')
|
? format(new Date(user.birth_date), 'dd/MM/yyyy')
|
||||||
: ''
|
: ''
|
||||||
|
const fileSizeLimit = getFileSize(appConfig.max_single_file_size)
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
@ -118,6 +126,7 @@ function Profile({ message, onDeletePicture, onUploadPicture, t, user }) {
|
|||||||
export default withTranslation()(
|
export default withTranslation()(
|
||||||
connect(
|
connect(
|
||||||
state => ({
|
state => ({
|
||||||
|
appConfig: state.application.config,
|
||||||
message: state.message,
|
message: state.message,
|
||||||
user: state.user,
|
user: state.user,
|
||||||
}),
|
}),
|
||||||
|
@ -42,6 +42,7 @@ class UserForm extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
formType,
|
formType,
|
||||||
|
isRegistrationAllowed,
|
||||||
message,
|
message,
|
||||||
messages,
|
messages,
|
||||||
onHandleUserFormSubmit,
|
onHandleUserFormSubmit,
|
||||||
@ -56,6 +57,7 @@ class UserForm extends React.Component {
|
|||||||
<div>
|
<div>
|
||||||
<Message message={message} messages={messages} t={t} />
|
<Message message={message} messages={messages} t={t} />
|
||||||
<Form
|
<Form
|
||||||
|
isRegistrationAllowed={isRegistrationAllowed}
|
||||||
formType={formType}
|
formType={formType}
|
||||||
userForm={formData}
|
userForm={formData}
|
||||||
onHandleFormChange={event => this.onHandleFormChange(event)}
|
onHandleFormChange={event => this.onHandleFormChange(event)}
|
||||||
@ -73,6 +75,7 @@ class UserForm extends React.Component {
|
|||||||
export default withTranslation()(
|
export default withTranslation()(
|
||||||
connect(
|
connect(
|
||||||
state => ({
|
state => ({
|
||||||
|
isRegistrationAllowed: state.application.config.is_registration_enabled,
|
||||||
location: state.router.location,
|
location: state.router.location,
|
||||||
message: state.message,
|
message: state.message,
|
||||||
messages: state.messages,
|
messages: state.messages,
|
||||||
|
@ -48,7 +48,7 @@ export default class FitTrackeeApi {
|
|||||||
|
|
||||||
static updateData(target, data) {
|
static updateData(target, data) {
|
||||||
const params = {
|
const params = {
|
||||||
url: `${target}/${data.id}`,
|
url: `${target}${data.id ? `/${data.id}` : ''}`,
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
body: data,
|
body: data,
|
||||||
type: 'application/json',
|
type: 'application/json',
|
||||||
|
@ -4,12 +4,21 @@
|
|||||||
"activities exist": "activities exist",
|
"activities exist": "activities exist",
|
||||||
"Administration": "Administration",
|
"Administration": "Administration",
|
||||||
"Application": "Application",
|
"Application": "Application",
|
||||||
|
"Application configuration": "Application configuration",
|
||||||
"Back": "Back",
|
"Back": "Back",
|
||||||
"Disable": "Disable",
|
"Disable": "Disable",
|
||||||
"Enable": "Enable",
|
"Enable": "Enable",
|
||||||
|
"Enable registration": "Enable registration",
|
||||||
|
"FitTrackee administration": "FitTrackee administration",
|
||||||
"id": "id",
|
"id": "id",
|
||||||
"Image": "Image",
|
"Image": "Image",
|
||||||
"Label": "Label",
|
"Label": "Label",
|
||||||
|
"Max. number of active users": "Max. number of active users",
|
||||||
|
"Max. files of zip archive": "Max. files of zip archive",
|
||||||
|
"Max. size of uploaded files": "Max. size of uploaded files",
|
||||||
|
"Max. size of uploaded files (in Mb)": "Max. size of uploaded files (in Mb)",
|
||||||
|
"Max. size of zip archive": "Max. size of zip archive",
|
||||||
|
"Max. size of zip archive (in Mb)": "Max. size of zip archive (in Mb)",
|
||||||
"Sports": "Sports",
|
"Sports": "Sports",
|
||||||
"user": "user",
|
"user": "user",
|
||||||
"Users": "Users",
|
"Users": "Users",
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
{
|
{
|
||||||
"Add workout": "Add workout",
|
"Add workout": "Add workout",
|
||||||
|
"Back": "Back",
|
||||||
"Cancel": "Cancel",
|
"Cancel": "Cancel",
|
||||||
"Dashboard": "Dashboard",
|
"Dashboard": "Dashboard",
|
||||||
|
"Edit": "Edit",
|
||||||
"day": "day",
|
"day": "day",
|
||||||
"days": "days",
|
"days": "days",
|
||||||
"Login": "Login",
|
"Login": "Login",
|
||||||
"Logout": "Logout",
|
"Logout": "Logout",
|
||||||
"No": "No",
|
"No": "No",
|
||||||
|
"no": "no",
|
||||||
"No records.": "No records.",
|
"No records.": "No records.",
|
||||||
"No workouts.": "No workouts.",
|
"No workouts.": "No workouts.",
|
||||||
|
"Page not found": "Page not found",
|
||||||
"Register": "Register",
|
"Register": "Register",
|
||||||
"Statistics": "Statistics",
|
"Statistics": "Statistics",
|
||||||
"Sport": "Sport",
|
"Sport": "Sport",
|
||||||
@ -21,5 +25,6 @@
|
|||||||
"Workouts": "Workouts",
|
"Workouts": "Workouts",
|
||||||
"workout": "workout",
|
"workout": "workout",
|
||||||
"workouts": "workouts",
|
"workouts": "workouts",
|
||||||
"Yes": "Yes"
|
"Yes": "Yes",
|
||||||
|
"yes": "yes"
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"Error. Registration is disabled.": "Error. Registration is disabled.",
|
"Error. Registration is disabled.": "Error. Registration is disabled.",
|
||||||
"Error. Please try again or contact the administrator.": "Error. Please try again or contact the administrator.",
|
"Error. Please try again or contact the administrator.": "Error. Please try again or contact the administrator.",
|
||||||
"File extension not allowed.": "File extension not allowed.",
|
"File extension not allowed.": "File extension not allowed.",
|
||||||
|
"File size is greater than the allowed size": "File size is greater than the allowed size",
|
||||||
"Incorrect id": "Incorrect id",
|
"Incorrect id": "Incorrect id",
|
||||||
"Invalid credentials.": "Invalid credentials.",
|
"Invalid credentials.": "Invalid credentials.",
|
||||||
"Invalid payload.": "Invalid payload.",
|
"Invalid payload.": "Invalid payload.",
|
||||||
|
@ -4,12 +4,21 @@
|
|||||||
"Administration": "Administration",
|
"Administration": "Administration",
|
||||||
"activities exist": "des activités existent",
|
"activities exist": "des activités existent",
|
||||||
"Application": "Application",
|
"Application": "Application",
|
||||||
|
"Application configuration": "Configuration de l'application",
|
||||||
"Back": "Retour",
|
"Back": "Retour",
|
||||||
"Disable": "désactiver",
|
"Disable": "désactiver",
|
||||||
"Enable": "activer",
|
"Enable": "activer",
|
||||||
|
"Enable registration": "Activer les inscriptions",
|
||||||
|
"FitTrackee administration": "Administration de FitTrackee",
|
||||||
"id": "id",
|
"id": "id",
|
||||||
"Image": "Image",
|
"Image": "Image",
|
||||||
"Label": "Label",
|
"Label": "Label",
|
||||||
|
"Max. number of active users": "Nombre maximum d'utilisateurs actifs",
|
||||||
|
"Max. files of zip archive": "Nombre max. de fichiers dans une archive zip",
|
||||||
|
"Max. size of uploaded files": "Taille max. des fichiers",
|
||||||
|
"Max. size of uploaded files (in Mb)": "Taille max. des fichiers (en Mo)",
|
||||||
|
"Max. size of zip archive": "Taille max. des archives zip",
|
||||||
|
"Max. size of zip archive (in Mb)": "Taille max. des archives zip (en Mo)",
|
||||||
"Sports": "Sports",
|
"Sports": "Sports",
|
||||||
"user": "user",
|
"user": "user",
|
||||||
"Users": "Utilisateurs",
|
"Users": "Utilisateurs",
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
{
|
{
|
||||||
"Add workout": "Ajouter une activité",
|
"Add workout": "Ajouter une activité",
|
||||||
|
"Back": "Revenir à la page précédente",
|
||||||
"Cancel": "Annuler",
|
"Cancel": "Annuler",
|
||||||
"Dashboard": "Tableau de Bord",
|
"Dashboard": "Tableau de Bord",
|
||||||
|
"Edit": "Modifier",
|
||||||
"day": "jour",
|
"day": "jour",
|
||||||
"days": "jours",
|
"days": "jours",
|
||||||
"Login": "Se connecter",
|
"Login": "Se connecter",
|
||||||
"Logout": "Se déconnecter",
|
"Logout": "Se déconnecter",
|
||||||
"No": "Non",
|
"No": "Non",
|
||||||
|
"no": "non",
|
||||||
"No records.": "Pas de records.",
|
"No records.": "Pas de records.",
|
||||||
"No workouts.": "Pas d'activités.",
|
"No workouts.": "Pas d'activités.",
|
||||||
|
"Page not found": "Page introuvable",
|
||||||
"Register": "S'inscrire",
|
"Register": "S'inscrire",
|
||||||
"Statistics": "Statistiques",
|
"Statistics": "Statistiques",
|
||||||
"Sport": "Sport",
|
"Sport": "Sport",
|
||||||
@ -21,5 +25,6 @@
|
|||||||
"Workouts": "Activités",
|
"Workouts": "Activités",
|
||||||
"workout": "activité",
|
"workout": "activité",
|
||||||
"workouts": "activités",
|
"workouts": "activités",
|
||||||
"Yes": "Oui"
|
"Yes": "Oui",
|
||||||
|
"yes": "oui"
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"Error. Registration is disabled.": "Erreur. L'inscription est désactivée.",
|
"Error. Registration is disabled.": "Erreur. L'inscription est désactivée.",
|
||||||
"Error. Please try again or contact the administrator.": "Erreur. Veuillez réessayer ou contacter l'administrateur",
|
"Error. Please try again or contact the administrator.": "Erreur. Veuillez réessayer ou contacter l'administrateur",
|
||||||
"File extension not allowed.": "Extension de fichier non autorisée.",
|
"File extension not allowed.": "Extension de fichier non autorisée.",
|
||||||
|
"File size is greater than the allowed size": "La taille du fichier est supérieure à la limite autorisée",
|
||||||
"Incorrect id": "Id incorrect",
|
"Incorrect id": "Id incorrect",
|
||||||
"Invalid credentials.": "Identifiants invalides.",
|
"Invalid credentials.": "Identifiants invalides.",
|
||||||
"Invalid payload.": "Données incorrectes.",
|
"Invalid payload.": "Données incorrectes.",
|
||||||
|
@ -34,6 +34,12 @@ const activities = (state = initial.activities, action) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const application = (state = initial.application, action) => {
|
const application = (state = initial.application, action) => {
|
||||||
|
if (action.type === 'SET_APP_CONFIG') {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
config: action.data,
|
||||||
|
}
|
||||||
|
}
|
||||||
if (action.type === 'SET_APP_STATS') {
|
if (action.type === 'SET_APP_STATS') {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
@ -14,6 +14,14 @@ export default {
|
|||||||
},
|
},
|
||||||
application: {
|
application: {
|
||||||
statistics: {},
|
statistics: {},
|
||||||
|
config: {
|
||||||
|
gpx_limit_import: null,
|
||||||
|
is_registration_enabled: null,
|
||||||
|
max_single_file_size: null,
|
||||||
|
max_users: null,
|
||||||
|
max_zip_file_size: null,
|
||||||
|
registration: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
calendarActivities: {
|
calendarActivities: {
|
||||||
...emptyData,
|
...emptyData,
|
||||||
|
@ -2,7 +2,7 @@ import { format, parse } from 'date-fns'
|
|||||||
import { DateTime } from 'luxon'
|
import { DateTime } from 'luxon'
|
||||||
|
|
||||||
const suffixes = ['bytes', 'KB', 'MB', 'GB', 'TB']
|
const suffixes = ['bytes', 'KB', 'MB', 'GB', 'TB']
|
||||||
const getFileSize = fileSize => {
|
export const getFileSize = fileSize => {
|
||||||
const i = Math.floor(Math.log(fileSize) / Math.log(1024))
|
const i = Math.floor(Math.log(fileSize) / Math.log(1024))
|
||||||
return (
|
return (
|
||||||
(!fileSize && '0 bytes') ||
|
(!fileSize && '0 bytes') ||
|
||||||
@ -10,21 +10,17 @@ const getFileSize = fileSize => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getFileSizeInMB = fileSize => {
|
||||||
|
const value = fileSize / 1048576
|
||||||
|
return (!fileSize && 0) || +value.toFixed(2)
|
||||||
|
}
|
||||||
|
|
||||||
export const version = '0.3.0-beta' // version stored in 'utils' for now
|
export const version = '0.3.0-beta' // version stored in 'utils' for now
|
||||||
export const apiUrl = `${process.env.REACT_APP_API_URL}/api/`
|
export const apiUrl = `${process.env.REACT_APP_API_URL}/api/`
|
||||||
/* prettier-ignore */
|
/* prettier-ignore */
|
||||||
export const thunderforestApiKey = `${
|
export const thunderforestApiKey = `${
|
||||||
process.env.REACT_APP_THUNDERFOREST_API_KEY
|
process.env.REACT_APP_THUNDERFOREST_API_KEY
|
||||||
}`
|
}`
|
||||||
export const gpxLimit = `${process.env.REACT_APP_GPX_LIMIT_IMPORT}`
|
|
||||||
export const fileSizeLimit = getFileSize(
|
|
||||||
+process.env.REACT_APP_MAX_SINGLE_FILE_SIZE
|
|
||||||
)
|
|
||||||
export const zipSizeLimit = getFileSize(
|
|
||||||
+process.env.REACT_APP_MAX_ZIP_FILE_SIZE
|
|
||||||
)
|
|
||||||
export const isRegistrationAllowed =
|
|
||||||
process.env.REACT_APP_ALLOW_REGISTRATION !== 'false'
|
|
||||||
|
|
||||||
export const isLoggedIn = () => !!window.localStorage.authToken
|
export const isLoggedIn = () => !!window.localStorage.authToken
|
||||||
|
|
||||||
@ -56,7 +52,11 @@ export const createApiRequest = params => {
|
|||||||
}
|
}
|
||||||
const request = new Request(`${apiUrl}${params.url}`, requestParams)
|
const request = new Request(`${apiUrl}${params.url}`, requestParams)
|
||||||
return fetch(request)
|
return fetch(request)
|
||||||
.then(response => (params.method === 'DELETE' ? response : response.json()))
|
.then(response =>
|
||||||
|
params.method === 'DELETE' || response.status === 413
|
||||||
|
? response
|
||||||
|
: response.json()
|
||||||
|
)
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
return new Error('An error occurred. Please contact the administrator.')
|
return new Error('An error occurred. Please contact the administrator.')
|
||||||
|
Loading…
Reference in New Issue
Block a user