Client - application translation (wip)
This commit is contained in:
parent
745d102ee2
commit
77bc32d4a5
@ -1,15 +1,18 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
import { translateSports } from '../../utils/activities'
|
||||||
|
|
||||||
export default class ActivitiesFilter extends React.PureComponent {
|
export default class ActivitiesFilter extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
const { loadActivities, sports, updateParams } = this.props
|
const { loadActivities, sports, t, updateParams } = this.props
|
||||||
|
const translatedSports = translateSports(sports, t)
|
||||||
return (
|
return (
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<div className="card-body activity-filter">
|
<div className="card-body activity-filter">
|
||||||
<form onSubmit={event => event.preventDefault()}>
|
<form onSubmit={event => event.preventDefault()}>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
From:
|
{t('activities:From')}:
|
||||||
<input
|
<input
|
||||||
className="form-control col-md"
|
className="form-control col-md"
|
||||||
name="from"
|
name="from"
|
||||||
@ -18,7 +21,7 @@ export default class ActivitiesFilter extends React.PureComponent {
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
To:
|
{t('activities:To')}:
|
||||||
<input
|
<input
|
||||||
className="form-control col-md"
|
className="form-control col-md"
|
||||||
name="to"
|
name="to"
|
||||||
@ -29,14 +32,14 @@ export default class ActivitiesFilter extends React.PureComponent {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Sport:
|
{t('common:Sport')}:
|
||||||
<select
|
<select
|
||||||
className="form-control input-lg"
|
className="form-control input-lg"
|
||||||
name="sport_id"
|
name="sport_id"
|
||||||
onChange={e => updateParams(e)}
|
onChange={e => updateParams(e)}
|
||||||
>
|
>
|
||||||
<option value="" />
|
<option value="" />
|
||||||
{sports.map(sport => (
|
{translatedSports.map(sport => (
|
||||||
<option key={sport.id} value={sport.id}>
|
<option key={sport.id} value={sport.id}>
|
||||||
{sport.label}
|
{sport.label}
|
||||||
</option>
|
</option>
|
||||||
@ -46,7 +49,7 @@ export default class ActivitiesFilter extends React.PureComponent {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Distance (km):
|
{t('activities:Distance')} (km):
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-5">
|
<div className="col-5">
|
||||||
@ -59,7 +62,9 @@ export default class ActivitiesFilter extends React.PureComponent {
|
|||||||
type="number"
|
type="number"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-2 align-middle text-center">to</div>
|
<div className="col-2 align-middle text-center">
|
||||||
|
{t('common:to')}
|
||||||
|
</div>
|
||||||
<div className="col-5">
|
<div className="col-5">
|
||||||
<input
|
<input
|
||||||
className="form-control"
|
className="form-control"
|
||||||
@ -76,7 +81,7 @@ export default class ActivitiesFilter extends React.PureComponent {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Duration:
|
{t('activities:Duration')}:
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-5">
|
<div className="col-5">
|
||||||
@ -89,7 +94,9 @@ export default class ActivitiesFilter extends React.PureComponent {
|
|||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-2 align-middle text-center">to</div>
|
<div className="col-2 align-middle text-center">
|
||||||
|
{t('common:to')}
|
||||||
|
</div>
|
||||||
<div className="col-5">
|
<div className="col-5">
|
||||||
<input
|
<input
|
||||||
className="form-control"
|
className="form-control"
|
||||||
@ -106,7 +113,7 @@ export default class ActivitiesFilter extends React.PureComponent {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Average speed (km/h):
|
{t('activities:Average speed')} (km/h):
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-5">
|
<div className="col-5">
|
||||||
@ -119,7 +126,9 @@ export default class ActivitiesFilter extends React.PureComponent {
|
|||||||
type="number"
|
type="number"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-2 align-middle text-center">to</div>
|
<div className="col-2 align-middle text-center">
|
||||||
|
{t('common:to')}
|
||||||
|
</div>
|
||||||
<div className="col-5">
|
<div className="col-5">
|
||||||
<input
|
<input
|
||||||
className="form-control"
|
className="form-control"
|
||||||
@ -136,7 +145,7 @@ export default class ActivitiesFilter extends React.PureComponent {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Max speed (km/h):
|
{t('activities:Max. speed')} (km/h):
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-5">
|
<div className="col-5">
|
||||||
@ -149,7 +158,9 @@ export default class ActivitiesFilter extends React.PureComponent {
|
|||||||
type="number"
|
type="number"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-2 align-middle text-center">to</div>
|
<div className="col-2 align-middle text-center">
|
||||||
|
{t('common:to')}
|
||||||
|
</div>
|
||||||
<div className="col-5">
|
<div className="col-5">
|
||||||
<input
|
<input
|
||||||
className="form-control"
|
className="form-control"
|
||||||
@ -168,7 +179,7 @@ export default class ActivitiesFilter extends React.PureComponent {
|
|||||||
className="btn btn-primary btn-lg btn-block"
|
className="btn btn-primary btn-lg btn-block"
|
||||||
onClick={() => loadActivities()}
|
onClick={() => loadActivities()}
|
||||||
type="submit"
|
type="submit"
|
||||||
value="Filter"
|
value={t('activities:Filter')}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,7 +7,7 @@ import { getDateWithTZ } from '../../utils'
|
|||||||
|
|
||||||
export default class ActivitiesList extends React.PureComponent {
|
export default class ActivitiesList extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
const { activities, sports, user } = this.props
|
const { activities, sports, t, user } = this.props
|
||||||
return (
|
return (
|
||||||
<div className="card activity-card">
|
<div className="card activity-card">
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
@ -15,12 +15,12 @@ export default class ActivitiesList extends React.PureComponent {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" />
|
<th scope="col" />
|
||||||
<th scope="col">Workout</th>
|
<th scope="col">{t('common:Workout')}</th>
|
||||||
<th scope="col">Date</th>
|
<th scope="col">{t('activities:Date')}</th>
|
||||||
<th scope="col">Distance</th>
|
<th scope="col">{t('activities:Distance')}</th>
|
||||||
<th scope="col">Duration</th>
|
<th scope="col">{t('activities:Duration')}</th>
|
||||||
<th scope="col">Ave. speed</th>
|
<th scope="col">{t('activities:Ave. speed')}</th>
|
||||||
<th scope="col">Max. speed</th>
|
<th scope="col">{t('activities:Max. speed')}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
|
import { withTranslation } from 'react-i18next'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { Link } from 'react-router-dom'
|
|
||||||
|
|
||||||
import ActivitiesFilter from './ActivitiesFilter'
|
import ActivitiesFilter from './ActivitiesFilter'
|
||||||
import ActivitiesList from './ActivitiesList'
|
import ActivitiesList from './ActivitiesList'
|
||||||
|
import NoActivities from '../Common/NoActivities'
|
||||||
import { getOrUpdateData } from '../../actions'
|
import { getOrUpdateData } from '../../actions'
|
||||||
import { getMoreActivities } from '../../actions/activities'
|
import { getMoreActivities } from '../../actions/activities'
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ class Activities extends React.Component {
|
|||||||
loadMoreActivities,
|
loadMoreActivities,
|
||||||
message,
|
message,
|
||||||
sports,
|
sports,
|
||||||
|
t,
|
||||||
user,
|
user,
|
||||||
} = this.props
|
} = this.props
|
||||||
const { params } = this.state
|
const { params } = this.state
|
||||||
@ -50,7 +52,7 @@ class Activities extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>FitTrackee - Workouts</title>
|
<title>FitTrackee - {t('common:Workouts')}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
{message ? (
|
{message ? (
|
||||||
<code>{message}</code>
|
<code>{message}</code>
|
||||||
@ -61,6 +63,7 @@ class Activities extends React.Component {
|
|||||||
<ActivitiesFilter
|
<ActivitiesFilter
|
||||||
sports={sports}
|
sports={sports}
|
||||||
loadActivities={() => loadActivities(params)}
|
loadActivities={() => loadActivities(params)}
|
||||||
|
t={t}
|
||||||
updateParams={e => this.setParams(e)}
|
updateParams={e => this.setParams(e)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -68,6 +71,7 @@ class Activities extends React.Component {
|
|||||||
<ActivitiesList
|
<ActivitiesList
|
||||||
activities={activities}
|
activities={activities}
|
||||||
sports={sports}
|
sports={sports}
|
||||||
|
t={t}
|
||||||
user={user}
|
user={user}
|
||||||
/>
|
/>
|
||||||
{!paginationEnd && (
|
{!paginationEnd && (
|
||||||
@ -82,16 +86,7 @@ class Activities extends React.Component {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{activities.length === 0 && (
|
{activities.length === 0 && <NoActivities t={t} />}
|
||||||
<div className="card text-center">
|
|
||||||
<div className="card-body">
|
|
||||||
No workouts.{' '}
|
|
||||||
<Link to={{ pathname: '/activities/add' }}>
|
|
||||||
Upload one !
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -101,7 +96,8 @@ class Activities extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(
|
export default withTranslation()(
|
||||||
|
connect(
|
||||||
state => ({
|
state => ({
|
||||||
activities: state.activities.data,
|
activities: state.activities.data,
|
||||||
message: state.message,
|
message: state.message,
|
||||||
@ -116,4 +112,5 @@ export default connect(
|
|||||||
dispatch(getMoreActivities(params))
|
dispatch(getMoreActivities(params))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
)(Activities)
|
)(Activities)
|
||||||
|
)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
|
import { withTranslation } from 'react-i18next'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
|
|
||||||
import FormWithGpx from './ActivityForms/FormWithGpx'
|
import FormWithGpx from './ActivityForms/FormWithGpx'
|
||||||
@ -23,13 +24,16 @@ class ActivityAddEdit extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { activity, loading, message, sports } = this.props
|
const { activity, loading, message, sports, t } = this.props
|
||||||
const { withGpx } = this.state
|
const { withGpx } = this.state
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>
|
<title>
|
||||||
FitTrackee - {activity ? 'Edit a workout' : 'Add a workout'}
|
FitTrackee -{' '}
|
||||||
|
{activity
|
||||||
|
? t('activities:Edit a workout')
|
||||||
|
: t('activities:Add a workout')}
|
||||||
</title>
|
</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<br />
|
<br />
|
||||||
@ -41,14 +45,20 @@ class ActivityAddEdit extends React.Component {
|
|||||||
<div className="col-md-8">
|
<div className="col-md-8">
|
||||||
<div className="card add-activity">
|
<div className="card add-activity">
|
||||||
<h2 className="card-header text-center">
|
<h2 className="card-header text-center">
|
||||||
{activity ? 'Edit a workout' : 'Add a workout'}
|
{activity
|
||||||
|
? t('activities:Edit a workout')
|
||||||
|
: t('activities:Add a workout')}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
{activity ? (
|
{activity ? (
|
||||||
activity.with_gpx ? (
|
activity.with_gpx ? (
|
||||||
<FormWithGpx activity={activity} sports={sports} />
|
<FormWithGpx activity={activity} sports={sports} t={t} />
|
||||||
) : (
|
) : (
|
||||||
<FormWithoutGpx activity={activity} sports={sports} />
|
<FormWithoutGpx
|
||||||
|
activity={activity}
|
||||||
|
sports={sports}
|
||||||
|
t={t}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
@ -66,7 +76,7 @@ class ActivityAddEdit extends React.Component {
|
|||||||
this.handleRadioChange(event)
|
this.handleRadioChange(event)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
with gpx file
|
{t('activities:with gpx file')}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="col">
|
<div className="col">
|
||||||
@ -81,15 +91,15 @@ class ActivityAddEdit extends React.Component {
|
|||||||
this.handleRadioChange(event)
|
this.handleRadioChange(event)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
without gpx file
|
{t('activities:without gpx file')}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{withGpx ? (
|
{withGpx ? (
|
||||||
<FormWithGpx sports={sports} />
|
<FormWithGpx sports={sports} t={t} />
|
||||||
) : (
|
) : (
|
||||||
<FormWithoutGpx sports={sports} />
|
<FormWithoutGpx sports={sports} t={t} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -104,6 +114,8 @@ class ActivityAddEdit extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(state => ({
|
export default withTranslation()(
|
||||||
|
connect(state => ({
|
||||||
loading: state.loading,
|
loading: state.loading,
|
||||||
}))(ActivityAddEdit)
|
}))(ActivityAddEdit)
|
||||||
|
)
|
||||||
|
@ -1,17 +1,21 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { Trans } from 'react-i18next'
|
||||||
import { connect } from 'react-redux'
|
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 { fileSizeLimit, gpxLimit, zipSizeLimit } from '../../../utils'
|
||||||
|
import { translateSports } from '../../../utils/activities'
|
||||||
|
|
||||||
function FormWithGpx(props) {
|
function FormWithGpx(props) {
|
||||||
const { activity, loading, onAddActivity, onEditActivity, sports } = props
|
const { activity, loading, onAddActivity, onEditActivity, sports, t } = props
|
||||||
const sportId = activity ? activity.sport_id : ''
|
const sportId = activity ? activity.sport_id : ''
|
||||||
|
const translatedSports = translateSports(sports, t)
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const zipTooltip =
|
const zipTooltip =
|
||||||
`no folder inside, ${gpxLimit} files max, max size: ${zipSizeLimit}`
|
`${t('activities:no folder inside')}, ${gpxLimit} ${
|
||||||
|
t('activities:files max')}, ${t('activities:max size')}: ${zipSizeLimit}`
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
encType="multipart/form-data"
|
encType="multipart/form-data"
|
||||||
@ -20,7 +24,7 @@ function FormWithGpx(props) {
|
|||||||
>
|
>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Sport:
|
{t('common:Sport')}:
|
||||||
<select
|
<select
|
||||||
className="form-control input-lg"
|
className="form-control input-lg"
|
||||||
defaultValue={sportId}
|
defaultValue={sportId}
|
||||||
@ -29,7 +33,7 @@ function FormWithGpx(props) {
|
|||||||
required
|
required
|
||||||
>
|
>
|
||||||
<option value="" />
|
<option value="" />
|
||||||
{sports.map(sport => (
|
{translatedSports.map(sport => (
|
||||||
<option key={sport.id} value={sport.id}>
|
<option key={sport.id} value={sport.id}>
|
||||||
{sport.label}
|
{sport.label}
|
||||||
</option>
|
</option>
|
||||||
@ -40,7 +44,7 @@ function FormWithGpx(props) {
|
|||||||
{activity ? (
|
{activity ? (
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Title:
|
{t('activities:Title')}:
|
||||||
<input
|
<input
|
||||||
name="title"
|
name="title"
|
||||||
defaultValue={activity ? activity.title : ''}
|
defaultValue={activity ? activity.title : ''}
|
||||||
@ -52,17 +56,21 @@ function FormWithGpx(props) {
|
|||||||
) : (
|
) : (
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
|
<Trans i18nKey="activities:gpxFile">
|
||||||
<strong>gpx</strong> file
|
<strong>gpx</strong> file
|
||||||
|
</Trans>
|
||||||
<sup>
|
<sup>
|
||||||
<i
|
<i
|
||||||
className="fa fa-question-circle"
|
className="fa fa-question-circle"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
data-toggle="tooltip"
|
data-toggle="tooltip"
|
||||||
title={`max size: ${fileSizeLimit}`}
|
title={`${t('activities:max size')}: ${fileSizeLimit}`}
|
||||||
/>
|
/>
|
||||||
</sup>{' '}
|
</sup>{' '}
|
||||||
|
<Trans i18nKey="activities:zipFile">
|
||||||
or <strong> zip</strong> file containing <strong>gpx </strong>
|
or <strong> zip</strong> file containing <strong>gpx </strong>
|
||||||
files
|
files
|
||||||
|
</Trans>
|
||||||
<sup>
|
<sup>
|
||||||
<i
|
<i
|
||||||
className="fa fa-question-circle"
|
className="fa fa-question-circle"
|
||||||
@ -86,7 +94,7 @@ function FormWithGpx(props) {
|
|||||||
)}
|
)}
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Notes:
|
{t('activities:Notes')}:
|
||||||
<textarea
|
<textarea
|
||||||
name="notes"
|
name="notes"
|
||||||
defaultValue={activity ? activity.notes : ''}
|
defaultValue={activity ? activity.notes : ''}
|
||||||
@ -106,13 +114,13 @@ function FormWithGpx(props) {
|
|||||||
onClick={event =>
|
onClick={event =>
|
||||||
activity ? onEditActivity(event, activity) : onAddActivity(event)
|
activity ? onEditActivity(event, activity) : onAddActivity(event)
|
||||||
}
|
}
|
||||||
value="Submit"
|
value={t('common:Submit')}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="submit"
|
type="submit"
|
||||||
className="btn btn-secondary btn-lg btn-block"
|
className="btn btn-secondary btn-lg btn-block"
|
||||||
onClick={() => history.push('/')}
|
onClick={() => history.push('/')}
|
||||||
value="Cancel"
|
value={t('common:Cancel')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -6,10 +6,11 @@ import {
|
|||||||
editActivity,
|
editActivity,
|
||||||
} from '../../../actions/activities'
|
} from '../../../actions/activities'
|
||||||
import { history } from '../../../index'
|
import { history } from '../../../index'
|
||||||
import { formatActivityDate } from '../../../utils/activities'
|
import { formatActivityDate, translateSports } from '../../../utils/activities'
|
||||||
|
|
||||||
function FormWithoutGpx(props) {
|
function FormWithoutGpx(props) {
|
||||||
const { activity, onAddOrEdit, sports } = props
|
const { activity, onAddOrEdit, sports, t } = props
|
||||||
|
const translatedSports = translateSports(sports, t)
|
||||||
let activityDate,
|
let activityDate,
|
||||||
activityTime,
|
activityTime,
|
||||||
sportId = ''
|
sportId = ''
|
||||||
@ -27,7 +28,7 @@ function FormWithoutGpx(props) {
|
|||||||
<form onSubmit={event => event.preventDefault()}>
|
<form onSubmit={event => event.preventDefault()}>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Title:
|
{t('activities:Title')}:
|
||||||
<input
|
<input
|
||||||
name="title"
|
name="title"
|
||||||
defaultValue={activity ? activity.title : ''}
|
defaultValue={activity ? activity.title : ''}
|
||||||
@ -37,7 +38,7 @@ function FormWithoutGpx(props) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Sport:
|
{t('common:Sport')}:
|
||||||
<select
|
<select
|
||||||
className="form-control input-lg"
|
className="form-control input-lg"
|
||||||
defaultValue={sportId}
|
defaultValue={sportId}
|
||||||
@ -45,7 +46,7 @@ function FormWithoutGpx(props) {
|
|||||||
required
|
required
|
||||||
>
|
>
|
||||||
<option value="" />
|
<option value="" />
|
||||||
{sports.map(sport => (
|
{translatedSports.map(sport => (
|
||||||
<option key={sport.id} value={sport.id}>
|
<option key={sport.id} value={sport.id}>
|
||||||
{sport.label}
|
{sport.label}
|
||||||
</option>
|
</option>
|
||||||
@ -55,7 +56,7 @@ function FormWithoutGpx(props) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Activity Date:
|
{t('activities:Activity Date')}:
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<input
|
<input
|
||||||
@ -78,7 +79,7 @@ function FormWithoutGpx(props) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Duration:
|
{t('activities:Duration')}:
|
||||||
<input
|
<input
|
||||||
name="duration"
|
name="duration"
|
||||||
defaultValue={activity ? activity.duration : ''}
|
defaultValue={activity ? activity.duration : ''}
|
||||||
@ -92,7 +93,7 @@ function FormWithoutGpx(props) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Distance (km):
|
{t('activities:Distance')} (km):
|
||||||
<input
|
<input
|
||||||
name="distance"
|
name="distance"
|
||||||
defaultValue={activity ? activity.distance : ''}
|
defaultValue={activity ? activity.distance : ''}
|
||||||
@ -106,7 +107,7 @@ function FormWithoutGpx(props) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Notes:
|
{t('activities:Notes')}:
|
||||||
<textarea
|
<textarea
|
||||||
name="notes"
|
name="notes"
|
||||||
defaultValue={activity ? activity.notes : ''}
|
defaultValue={activity ? activity.notes : ''}
|
||||||
@ -119,13 +120,13 @@ function FormWithoutGpx(props) {
|
|||||||
type="submit"
|
type="submit"
|
||||||
className="btn btn-primary btn-lg btn-block"
|
className="btn btn-primary btn-lg btn-block"
|
||||||
onClick={event => onAddOrEdit(event, activity)}
|
onClick={event => onAddOrEdit(event, activity)}
|
||||||
value="Submit"
|
value={t('common:Submit')}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="submit"
|
type="submit"
|
||||||
className="btn btn-secondary btn-lg btn-block"
|
className="btn btn-secondary btn-lg btn-block"
|
||||||
onClick={() => history.push('/')}
|
onClick={() => history.push('/')}
|
||||||
value="Cancel"
|
value={t('common:Cancel')}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
)
|
)
|
||||||
|
@ -400,7 +400,6 @@ label {
|
|||||||
.time-frame label {
|
.time-frame label {
|
||||||
float: left;
|
float: left;
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
width: 4em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.time-frame label input {
|
.time-frame label input {
|
||||||
@ -413,7 +412,7 @@ label {
|
|||||||
color: #7b7b7b;
|
color: #7b7b7b;
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
padding: 2px 0;
|
padding: 2px 6px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
18
fittrackee_client/src/components/Common/NoActivities.jsx
Normal file
18
fittrackee_client/src/components/Common/NoActivities.jsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
|
export default class NoActivities extends React.PureComponent {
|
||||||
|
render() {
|
||||||
|
const { t } = this.props
|
||||||
|
return (
|
||||||
|
<div className="card text-center">
|
||||||
|
<div className="card-body">
|
||||||
|
{t('common:No workouts.')}{' '}
|
||||||
|
<Link to={{ pathname: '/activities/add' }}>
|
||||||
|
{t('dashboard:Upload one !')}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -28,9 +28,9 @@ export default class StatsCharts extends React.PureComponent {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { displayedData } = this.state
|
const { displayedData } = this.state
|
||||||
const { sports, stats } = this.props
|
const { sports, stats, t } = this.props
|
||||||
if (Object.keys(stats).length === 0) {
|
if (Object.keys(stats).length === 0) {
|
||||||
return 'No workouts'
|
return t('common:No workouts.')
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="chart-stats">
|
<div className="chart-stats">
|
||||||
@ -42,7 +42,7 @@ export default class StatsCharts extends React.PureComponent {
|
|||||||
checked={displayedData === 'distance'}
|
checked={displayedData === 'distance'}
|
||||||
onChange={e => this.handleRadioChange(e)}
|
onChange={e => this.handleRadioChange(e)}
|
||||||
/>
|
/>
|
||||||
distance
|
{t('statistics:distance')}
|
||||||
</label>
|
</label>
|
||||||
<label className="radioLabel col">
|
<label className="radioLabel col">
|
||||||
<input
|
<input
|
||||||
@ -51,7 +51,7 @@ export default class StatsCharts extends React.PureComponent {
|
|||||||
checked={displayedData === 'duration'}
|
checked={displayedData === 'duration'}
|
||||||
onChange={e => this.handleRadioChange(e)}
|
onChange={e => this.handleRadioChange(e)}
|
||||||
/>
|
/>
|
||||||
duration
|
{t('statistics:duration')}
|
||||||
</label>
|
</label>
|
||||||
<label className="radioLabel col">
|
<label className="radioLabel col">
|
||||||
<input
|
<input
|
||||||
@ -60,7 +60,7 @@ export default class StatsCharts extends React.PureComponent {
|
|||||||
checked={displayedData === 'activities'}
|
checked={displayedData === 'activities'}
|
||||||
onChange={e => this.handleRadioChange(e)}
|
onChange={e => this.handleRadioChange(e)}
|
||||||
/>
|
/>
|
||||||
activities
|
{t('statistics:activities')}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<ResponsiveContainer height={300}>
|
<ResponsiveContainer height={300}>
|
||||||
|
@ -37,10 +37,11 @@ class Statistics extends React.PureComponent {
|
|||||||
statistics,
|
statistics,
|
||||||
statsParams,
|
statsParams,
|
||||||
displayEmpty,
|
displayEmpty,
|
||||||
|
t,
|
||||||
user,
|
user,
|
||||||
} = this.props
|
} = this.props
|
||||||
if (!displayEmpty && Object.keys(statistics).length === 0) {
|
if (!displayEmpty && Object.keys(statistics).length === 0) {
|
||||||
return 'No workouts'
|
return <span>{t('common:No workouts.')}</span>
|
||||||
}
|
}
|
||||||
const stats = formatStats(
|
const stats = formatStats(
|
||||||
statistics,
|
statistics,
|
||||||
@ -49,7 +50,7 @@ class Statistics extends React.PureComponent {
|
|||||||
displayedSports,
|
displayedSports,
|
||||||
user.weekm
|
user.weekm
|
||||||
)
|
)
|
||||||
return <StatsChart sports={sports} stats={stats} />
|
return <StatsChart sports={sports} stats={stats} t={t} />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import { Link } from 'react-router-dom'
|
|||||||
import { formatRecord } from '../../utils/activities'
|
import { formatRecord } from '../../utils/activities'
|
||||||
|
|
||||||
export default function RecordsCard(props) {
|
export default function RecordsCard(props) {
|
||||||
const { records, sports, user } = props
|
const { records, sports, t, user } = props
|
||||||
const recordsBySport = records.reduce((sportList, record) => {
|
const recordsBySport = records.reduce((sportList, record) => {
|
||||||
const sport = sports.find(s => s.id === record.sport_id)
|
const sport = sports.find(s => s.id === record.sport_id)
|
||||||
if (sportList[sport.label] === void 0) {
|
if (sportList[sport.label] === void 0) {
|
||||||
@ -22,7 +22,7 @@ export default function RecordsCard(props) {
|
|||||||
<div className="card-header">Personal records</div>
|
<div className="card-header">Personal records</div>
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
{Object.keys(recordsBySport).length === 0
|
{Object.keys(recordsBySport).length === 0
|
||||||
? 'No records'
|
? t('common:No records.')
|
||||||
: Object.keys(recordsBySport).map(sportLabel => (
|
: Object.keys(recordsBySport).map(sportLabel => (
|
||||||
<table
|
<table
|
||||||
className="table table-borderless table-sm record-table"
|
className="table table-borderless table-sm record-table"
|
||||||
|
@ -16,11 +16,12 @@ export default class Statistics extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { t } = this.props
|
||||||
return (
|
return (
|
||||||
<div className="card activity-card">
|
<div className="card activity-card">
|
||||||
<div className="card-header">This month</div>
|
<div className="card-header">{t('dashboard:This month')}</div>
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<Stats displayEmpty={false} statsParams={this.state} />
|
<Stats displayEmpty={false} statsParams={this.state} t={t} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
export default function UserStatistics(props) {
|
export default function UserStatistics(props) {
|
||||||
const { user } = props
|
const { t, user } = props
|
||||||
const days = user.total_duration.match(/day/g)
|
const days = user.total_duration.match(/day/g)
|
||||||
? `${user.total_duration.split(',')[0]},`
|
? `${user.total_duration.split(' ')[0]} ${
|
||||||
: '0 days,'
|
user.total_duration.match(/days/g) ? t('common:days') : t('common:day')
|
||||||
|
}`
|
||||||
|
: `0 ${t('common:days')},`
|
||||||
let duration = user.total_duration.match(/day/g)
|
let duration = user.total_duration.match(/day/g)
|
||||||
? user.total_duration.split(', ')[1]
|
? user.total_duration.split(', ')[1]
|
||||||
: user.total_duration
|
: user.total_duration
|
||||||
@ -19,7 +21,11 @@ export default function UserStatistics(props) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="col-9 text-right">
|
<div className="col-9 text-right">
|
||||||
<div className="huge">{user.nb_activities}</div>
|
<div className="huge">{user.nb_activities}</div>
|
||||||
<div>{`workout${user.nb_activities === 1 ? '' : 's'}`}</div>
|
<div>{`${
|
||||||
|
user.nb_activities === 1
|
||||||
|
? t('common:workout')
|
||||||
|
: t('common:workouts')
|
||||||
|
}`}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -60,7 +66,9 @@ export default function UserStatistics(props) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="col-9 text-right">
|
<div className="col-9 text-right">
|
||||||
<div className="huge">{user.nb_sports}</div>
|
<div className="huge">{user.nb_sports}</div>
|
||||||
<div>{`sport${user.nb_sports === 1 ? '' : 's'}`}</div>
|
<div>{`${
|
||||||
|
user.nb_sports === 1 ? t('common:sport') : t('common:sports')
|
||||||
|
}`}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
|
import { withTranslation } from 'react-i18next'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { Link } from 'react-router-dom'
|
|
||||||
|
|
||||||
import ActivityCard from './ActivityCard'
|
import ActivityCard from './ActivityCard'
|
||||||
import Calendar from './Calendar'
|
import Calendar from './Calendar'
|
||||||
|
import NoActivities from '../Common/NoActivities'
|
||||||
import Records from './Records'
|
import Records from './Records'
|
||||||
import Statistics from './Statistics'
|
import Statistics from './Statistics'
|
||||||
import UserStatistics from './UserStatistics'
|
import UserStatistics from './UserStatistics'
|
||||||
@ -30,6 +31,7 @@ class DashBoard extends React.Component {
|
|||||||
message,
|
message,
|
||||||
records,
|
records,
|
||||||
sports,
|
sports,
|
||||||
|
t,
|
||||||
user,
|
user,
|
||||||
} = this.props
|
} = this.props
|
||||||
const paginationEnd =
|
const paginationEnd =
|
||||||
@ -40,7 +42,7 @@ class DashBoard extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>FitTrackee - Dashboard</title>
|
<title>FitTrackee - {t('common:Dashboard')}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
{message ? (
|
{message ? (
|
||||||
<code>{message}</code>
|
<code>{message}</code>
|
||||||
@ -48,11 +50,16 @@ class DashBoard extends React.Component {
|
|||||||
activities &&
|
activities &&
|
||||||
sports.length > 0 && (
|
sports.length > 0 && (
|
||||||
<div className="container dashboard">
|
<div className="container dashboard">
|
||||||
<UserStatistics user={user} />
|
<UserStatistics user={user} t={t} />
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-md-4">
|
<div className="col-md-4">
|
||||||
<Statistics />
|
<Statistics t={t} />
|
||||||
<Records records={records} sports={sports} user={user} />
|
<Records
|
||||||
|
t={t}
|
||||||
|
records={records}
|
||||||
|
sports={sports}
|
||||||
|
user={user}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-8">
|
<div className="col-md-8">
|
||||||
<Calendar weekm={user.weekm} />
|
<Calendar weekm={user.weekm} />
|
||||||
@ -66,14 +73,7 @@ class DashBoard extends React.Component {
|
|||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<div className="card text-center">
|
<NoActivities t={t} />
|
||||||
<div className="card-body">
|
|
||||||
No workouts.{' '}
|
|
||||||
<Link to={{ pathname: '/activities/add' }}>
|
|
||||||
Upload one !
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
{!paginationEnd && (
|
{!paginationEnd && (
|
||||||
<input
|
<input
|
||||||
@ -96,7 +96,8 @@ class DashBoard extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(
|
export default withTranslation()(
|
||||||
|
connect(
|
||||||
state => ({
|
state => ({
|
||||||
activities: state.activities.data,
|
activities: state.activities.data,
|
||||||
message: state.message,
|
message: state.message,
|
||||||
@ -113,4 +114,5 @@ export default connect(
|
|||||||
dispatch(getMoreActivities({ page }))
|
dispatch(getMoreActivities({ page }))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
)(DashBoard)
|
)(DashBoard)
|
||||||
|
)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Translation } from 'react-i18next'
|
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
|
import { withTranslation } from 'react-i18next'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
import LanguageDropdown from './LanguageDropdown'
|
import LanguageDropdown from './LanguageDropdown'
|
||||||
@ -8,10 +8,8 @@ import { apiUrl } from '../../utils'
|
|||||||
|
|
||||||
class NavBar extends React.PureComponent {
|
class NavBar extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
const { id, isAuthenticated, picture, username } = this.props
|
const { id, isAuthenticated, picture, t, username } = this.props
|
||||||
return (
|
return (
|
||||||
<Translation>
|
|
||||||
{t => (
|
|
||||||
<header>
|
<header>
|
||||||
<nav className="navbar navbar-expand-lg navbar-light bg-light">
|
<nav className="navbar navbar-expand-lg navbar-light bg-light">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
@ -156,15 +154,15 @@ class NavBar extends React.PureComponent {
|
|||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
)}
|
|
||||||
</Translation>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(({ user }) => ({
|
export default withTranslation()(
|
||||||
|
connect(({ user }) => ({
|
||||||
id: user.id,
|
id: user.id,
|
||||||
isAuthenticated: user.isAuthenticated,
|
isAuthenticated: user.isAuthenticated,
|
||||||
picture: user.picture,
|
picture: user.picture,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
}))(NavBar)
|
}))(NavBar)
|
||||||
|
)
|
||||||
|
@ -14,9 +14,10 @@ import {
|
|||||||
} from 'date-fns'
|
} from 'date-fns'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
|
import { withTranslation } from 'react-i18next'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
|
|
||||||
import { activityColors } from '../../utils/activities'
|
import { activityColors, translateSports } from '../../utils/activities'
|
||||||
import Stats from '../Common/Stats'
|
import Stats from '../Common/Stats'
|
||||||
|
|
||||||
const durations = ['week', 'month', 'year']
|
const durations = ['week', 'month', 'year']
|
||||||
@ -114,15 +115,16 @@ class Statistics extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { displayedSports, statsParams } = this.state
|
const { displayedSports, statsParams } = this.state
|
||||||
const { sports } = this.props
|
const { sports, t } = this.props
|
||||||
|
const translatedSports = translateSports(sports, t)
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>FitTrackee - Statistics</title>
|
<title>FitTrackee - {t('statistics:Statistics')}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<div className="container dashboard">
|
<div className="container dashboard">
|
||||||
<div className="card activity-card">
|
<div className="card activity-card">
|
||||||
<div className="card-header">Statistics</div>
|
<div className="card-header">{t('statistics:Statistics')}</div>
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<div className="chart-filters row">
|
<div className="chart-filters row">
|
||||||
<div className="col chart-arrows">
|
<div className="col chart-arrows">
|
||||||
@ -145,7 +147,7 @@ class Statistics extends React.Component {
|
|||||||
checked={d === statsParams.duration}
|
checked={d === statsParams.duration}
|
||||||
onChange={e => this.handleOnChangeDuration(e)}
|
onChange={e => this.handleOnChangeDuration(e)}
|
||||||
/>
|
/>
|
||||||
<span>{d}</span>
|
<span>{t(`statistics:${d}`)}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -164,9 +166,10 @@ class Statistics extends React.Component {
|
|||||||
displayEmpty
|
displayEmpty
|
||||||
displayedSports={displayedSports}
|
displayedSports={displayedSports}
|
||||||
statsParams={statsParams}
|
statsParams={statsParams}
|
||||||
|
t={t}
|
||||||
/>
|
/>
|
||||||
<div className="row chart-activities">
|
<div className="row chart-activities">
|
||||||
{sports.map(sport => (
|
{translatedSports.map(sport => (
|
||||||
<label className="col activity-label" key={sport.id}>
|
<label className="col activity-label" key={sport.id}>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@ -188,6 +191,8 @@ class Statistics extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(state => ({
|
export default withTranslation()(
|
||||||
|
connect(state => ({
|
||||||
sports: state.sports.data,
|
sports: state.sports.data,
|
||||||
}))(Statistics)
|
}))(Statistics)
|
||||||
|
)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { Trans } from 'react-i18next'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
@ -16,8 +17,10 @@ class Logout extends React.Component {
|
|||||||
<div className="card col-8">
|
<div className="card col-8">
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
|
<Trans i18nKey="user:loggedOut">
|
||||||
You are now logged out. Click <Link to="/login">here</Link> to
|
You are now logged out. Click <Link to="/login">here</Link> to
|
||||||
log back in.
|
log back in.
|
||||||
|
</Trans>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { format } from 'date-fns'
|
import { format } from 'date-fns'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
|
import { withTranslation } from 'react-i18next'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
|
|
||||||
import { deletePicture, uploadPicture } from '../../actions/user'
|
import { deletePicture, uploadPicture } from '../../actions/user'
|
||||||
import { apiUrl, fileSizeLimit } from '../../utils'
|
import { apiUrl, fileSizeLimit } from '../../utils'
|
||||||
|
|
||||||
function Profile({ message, onDeletePicture, onUploadPicture, user }) {
|
function Profile({ 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')
|
||||||
: ''
|
: ''
|
||||||
@ -17,11 +18,11 @@ function Profile({ message, onDeletePicture, onUploadPicture, user }) {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>FitTrackee - Profile</title>
|
<title>FitTrackee - {t('user:Profile')}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
{message !== '' && <code>{message}</code>}
|
{message !== '' && <code>{message}</code>}
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<h1 className="page-title">Profile</h1>
|
<h1 className="page-title">{t('user:Profile')}</h1>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-md-12">
|
<div className="col-md-12">
|
||||||
<div className="card">
|
<div className="card">
|
||||||
@ -38,15 +39,34 @@ function Profile({ message, onDeletePicture, onUploadPicture, user }) {
|
|||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-md-8">
|
<div className="col-md-8">
|
||||||
<p>Email: {user.email}</p>
|
<p>
|
||||||
<p>Registration Date: {createdAt}</p>
|
{t('user:Email')}: {user.email}
|
||||||
<p>First Name: {user.first_name}</p>
|
</p>
|
||||||
<p>Last Name: {user.last_name}</p>
|
<p>
|
||||||
<p>Birth Date: {birthDate}</p>
|
{t('user:Registration Date')}: {createdAt}
|
||||||
<p>Location: {user.location}</p>
|
</p>
|
||||||
<p>Bio: {user.bio}</p>
|
<p>
|
||||||
<p>Time zone: {user.timezone}</p>
|
{t('user:First Name')}: {user.first_name}
|
||||||
<p>First day of week: {user.weekm ? 'Monday' : 'Sunday'}</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
{t('user:Last Name')}: {user.last_name}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{t('user:Birth Date')}: {birthDate}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{t('user:Location')}: {user.location}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{t('user:Bio')}: {user.bio}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{t('user:Timezone')}: {user.timezone}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{t('user:First day of week')}:{' '}
|
||||||
|
{user.weekm ? t('user:Monday') : t('user:Sunday')}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-4">
|
<div className="col-md-4">
|
||||||
{user.picture === true && (
|
{user.picture === true && (
|
||||||
@ -61,7 +81,7 @@ function Profile({ message, onDeletePicture, onUploadPicture, user }) {
|
|||||||
/>
|
/>
|
||||||
<br />
|
<br />
|
||||||
<button type="submit" onClick={() => onDeletePicture()}>
|
<button type="submit" onClick={() => onDeletePicture()}>
|
||||||
Delete picture
|
{t('user:Delete picture')}
|
||||||
</button>
|
</button>
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
@ -77,8 +97,8 @@ function Profile({ message, onDeletePicture, onUploadPicture, user }) {
|
|||||||
accept=".png,.jpg,.gif"
|
accept=".png,.jpg,.gif"
|
||||||
/>
|
/>
|
||||||
<br />
|
<br />
|
||||||
<button type="submit">Send</button> (max. size:{' '}
|
<button type="submit">{t('user:Send')}</button>
|
||||||
{fileSizeLimit})
|
{` (max. size: ${fileSizeLimit})`}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -91,7 +111,8 @@ function Profile({ message, onDeletePicture, onUploadPicture, user }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(
|
export default withTranslation()(
|
||||||
|
connect(
|
||||||
state => ({
|
state => ({
|
||||||
message: state.message,
|
message: state.message,
|
||||||
user: state.user,
|
user: state.user,
|
||||||
@ -104,4 +125,5 @@ export default connect(
|
|||||||
dispatch(uploadPicture(event))
|
dispatch(uploadPicture(event))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
)(Profile)
|
)(Profile)
|
||||||
|
)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { format } from 'date-fns'
|
import { format } from 'date-fns'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Helmet } from 'react-helmet'
|
import { Helmet } from 'react-helmet'
|
||||||
|
import { withTranslation } from 'react-i18next'
|
||||||
import { connect } from 'react-redux'
|
import { connect } from 'react-redux'
|
||||||
import TimezonePicker from 'react-timezone'
|
import TimezonePicker from 'react-timezone'
|
||||||
|
|
||||||
@ -49,17 +50,17 @@ class ProfileEdit extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { onHandleProfileFormSubmit, message, user } = this.props
|
const { onHandleProfileFormSubmit, message, t, user } = this.props
|
||||||
const { formData } = this.state
|
const { formData } = this.state
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>FitTrackee - Edit Profile</title>
|
<title>FitTrackee - {t('user:Profile Edition')}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
{message !== '' && <code>{message}</code>}
|
{message !== '' && <code>{message}</code>}
|
||||||
{formData.isAuthenticated && (
|
{formData.isAuthenticated && (
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<h1 className="page-title">Profile Edition</h1>
|
<h1 className="page-title">{t('user:Profile Edition')}</h1>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-md-2" />
|
<div className="col-md-2" />
|
||||||
<div className="col-md-8">
|
<div className="col-md-8">
|
||||||
@ -76,7 +77,7 @@ class ProfileEdit extends React.Component {
|
|||||||
>
|
>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Email:
|
{t('user:Email')}:
|
||||||
<input
|
<input
|
||||||
name="email"
|
name="email"
|
||||||
className="form-control input-lg"
|
className="form-control input-lg"
|
||||||
@ -88,7 +89,7 @@ class ProfileEdit extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Registration Date:
|
{t('user:Registration Date')}:
|
||||||
<input
|
<input
|
||||||
name="createdAt"
|
name="createdAt"
|
||||||
className="form-control input-lg"
|
className="form-control input-lg"
|
||||||
@ -100,7 +101,7 @@ class ProfileEdit extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Password:
|
{t('user:Password')}:
|
||||||
<input
|
<input
|
||||||
name="password"
|
name="password"
|
||||||
className="form-control input-lg"
|
className="form-control input-lg"
|
||||||
@ -111,7 +112,7 @@ class ProfileEdit extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Password Confirmation:
|
{t('user:Password Confirmation')}:
|
||||||
<input
|
<input
|
||||||
name="password_conf"
|
name="password_conf"
|
||||||
className="form-control input-lg"
|
className="form-control input-lg"
|
||||||
@ -123,7 +124,7 @@ class ProfileEdit extends React.Component {
|
|||||||
<hr />
|
<hr />
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
First Name:
|
{t('user:First Name')}:
|
||||||
<input
|
<input
|
||||||
name="first_name"
|
name="first_name"
|
||||||
className="form-control input-lg"
|
className="form-control input-lg"
|
||||||
@ -135,7 +136,7 @@ class ProfileEdit extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Last Name:
|
{t('user:Last Name')}:
|
||||||
<input
|
<input
|
||||||
name="last_name"
|
name="last_name"
|
||||||
className="form-control input-lg"
|
className="form-control input-lg"
|
||||||
@ -147,7 +148,7 @@ class ProfileEdit extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Birth Date
|
{t('user:Birth Date')}
|
||||||
<input
|
<input
|
||||||
name="birth_date"
|
name="birth_date"
|
||||||
className="form-control input-lg"
|
className="form-control input-lg"
|
||||||
@ -159,7 +160,7 @@ class ProfileEdit extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Location:
|
{t('user:Location')}:
|
||||||
<input
|
<input
|
||||||
name="location"
|
name="location"
|
||||||
className="form-control input-lg"
|
className="form-control input-lg"
|
||||||
@ -171,7 +172,7 @@ class ProfileEdit extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Bio:
|
{t('user:Bio')}:
|
||||||
<textarea
|
<textarea
|
||||||
name="bio"
|
name="bio"
|
||||||
className="form-control input-lg"
|
className="form-control input-lg"
|
||||||
@ -183,7 +184,7 @@ class ProfileEdit extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
Timezone:
|
{t('user:Timezone')}:
|
||||||
<TimezonePicker
|
<TimezonePicker
|
||||||
className="form-control timezone-custom-height"
|
className="form-control timezone-custom-height"
|
||||||
onChange={tz => {
|
onChange={tz => {
|
||||||
@ -201,28 +202,32 @@ class ProfileEdit extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>
|
<label>
|
||||||
First day of week:
|
{t('user:First day of week')}:
|
||||||
<select
|
<select
|
||||||
name="weekm"
|
name="weekm"
|
||||||
className="form-control input-lg"
|
className="form-control input-lg"
|
||||||
value={formData.weekm ? 'Monday' : 'Sunday'}
|
value={formData.weekm ? 'Monday' : 'Sunday'}
|
||||||
onChange={e => this.handleFormChange(e)}
|
onChange={e => this.handleFormChange(e)}
|
||||||
>
|
>
|
||||||
<option value="Sunday">Sunday</option>
|
<option value="Sunday">
|
||||||
<option value="Monday">Monday</option>
|
{t('user:Sunday')}
|
||||||
|
</option>
|
||||||
|
<option value="Monday">
|
||||||
|
{t('user:Monday')}
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
type="submit"
|
type="submit"
|
||||||
className="btn btn-primary btn-lg btn-block"
|
className="btn btn-primary btn-lg btn-block"
|
||||||
value="Submit"
|
value={t('common:Submit')}
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="submit"
|
type="submit"
|
||||||
className="btn btn-secondary btn-lg btn-block"
|
className="btn btn-secondary btn-lg btn-block"
|
||||||
onClick={() => history.push('/profile')}
|
onClick={() => history.push('/profile')}
|
||||||
value="Cancel"
|
value={t('common:Cancel')}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -239,7 +244,8 @@ class ProfileEdit extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(
|
export default withTranslation(
|
||||||
|
connect(
|
||||||
state => ({
|
state => ({
|
||||||
location: state.router.location,
|
location: state.router.location,
|
||||||
message: state.message,
|
message: state.message,
|
||||||
@ -250,4 +256,5 @@ export default connect(
|
|||||||
dispatch(handleProfileFormSubmit(formData))
|
dispatch(handleProfileFormSubmit(formData))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
)(ProfileEdit)
|
)(ProfileEdit)
|
||||||
|
)
|
||||||
|
@ -2,9 +2,17 @@ import i18n from 'i18next'
|
|||||||
import LanguageDetector from 'i18next-browser-languagedetector'
|
import LanguageDetector from 'i18next-browser-languagedetector'
|
||||||
import XHR from 'i18next-xhr-backend'
|
import XHR from 'i18next-xhr-backend'
|
||||||
|
|
||||||
|
import EnActivitiesTranslations from './locales/en/activities.json'
|
||||||
import EnCommonTranslations from './locales/en/common.json'
|
import EnCommonTranslations from './locales/en/common.json'
|
||||||
|
import EnDashboardTranslations from './locales/en/dashboard.json'
|
||||||
|
import EnSportsTranslations from './locales/en/sports.json'
|
||||||
|
import EnStatisticsTranslations from './locales/en/statistics.json'
|
||||||
import EnUserTranslations from './locales/en/user.json'
|
import EnUserTranslations from './locales/en/user.json'
|
||||||
|
import FrActivitiesTranslations from './locales/fr/activities.json'
|
||||||
import FrCommonTranslations from './locales/fr/common.json'
|
import FrCommonTranslations from './locales/fr/common.json'
|
||||||
|
import FrDashboardTranslations from './locales/fr/dashboard.json'
|
||||||
|
import FrSportsTranslations from './locales/fr/sports.json'
|
||||||
|
import FrStatisticsTranslations from './locales/fr/statistics.json'
|
||||||
import FrUserTranslations from './locales/fr/user.json'
|
import FrUserTranslations from './locales/fr/user.json'
|
||||||
|
|
||||||
i18n
|
i18n
|
||||||
@ -20,11 +28,19 @@ i18n
|
|||||||
},
|
},
|
||||||
resources: {
|
resources: {
|
||||||
en: {
|
en: {
|
||||||
|
activities: EnActivitiesTranslations,
|
||||||
common: EnCommonTranslations,
|
common: EnCommonTranslations,
|
||||||
|
dashboard: EnDashboardTranslations,
|
||||||
|
sports: EnSportsTranslations,
|
||||||
|
statistics: EnStatisticsTranslations,
|
||||||
user: EnUserTranslations,
|
user: EnUserTranslations,
|
||||||
},
|
},
|
||||||
fr: {
|
fr: {
|
||||||
|
activities: FrActivitiesTranslations,
|
||||||
common: FrCommonTranslations,
|
common: FrCommonTranslations,
|
||||||
|
dashboard: FrDashboardTranslations,
|
||||||
|
sports: FrSportsTranslations,
|
||||||
|
statistics: FrStatisticsTranslations,
|
||||||
user: FrUserTranslations,
|
user: FrUserTranslations,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
23
fittrackee_client/src/locales/en/activities.json
Normal file
23
fittrackee_client/src/locales/en/activities.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"Activity Date": "Activity Date",
|
||||||
|
"Add a workout": "Add a workout",
|
||||||
|
"Ave. speed": "Ave. speed",
|
||||||
|
"Average speed": "Average speed",
|
||||||
|
"Date": "Date",
|
||||||
|
"Distance": "Distance",
|
||||||
|
"Duration": "Duration",
|
||||||
|
"Edit a workout": "Edit a workout",
|
||||||
|
"Filter": "Filter",
|
||||||
|
"From": "From",
|
||||||
|
"gpxFile": "<strong>gpx</strong> file",
|
||||||
|
"Max. speed": "Max. speed",
|
||||||
|
"no folder inside": "no folder inside",
|
||||||
|
"files max": "files max",
|
||||||
|
"max size": "max size",
|
||||||
|
"Notes": "Notes",
|
||||||
|
"Title": "Title",
|
||||||
|
"To": "To",
|
||||||
|
"with gpx file": "with gpx file",
|
||||||
|
"without gpx file": "without gpx file",
|
||||||
|
"zipFile": "or <strong> zip</strong> file containing <strong>gpx </strong> files"
|
||||||
|
}
|
@ -1,10 +1,23 @@
|
|||||||
{
|
{
|
||||||
"Dashboard": "Dashboard",
|
|
||||||
"Workouts": "Workouts",
|
|
||||||
"Statistics": "Statistics",
|
|
||||||
"Add workout": "Add workout",
|
"Add workout": "Add workout",
|
||||||
"Register": "Register",
|
"Cancel": "Cancel",
|
||||||
|
"Dashboard": "Dashboard",
|
||||||
|
"day": "day",
|
||||||
|
"days": "days",
|
||||||
"Login": "Login",
|
"Login": "Login",
|
||||||
"Logout": "Logout",
|
"Logout": "Logout",
|
||||||
"Submit": "Submit"
|
"No records.": "No records.",
|
||||||
|
"No workouts.": "No workouts.",
|
||||||
|
"Register": "Register",
|
||||||
|
"Statistics": "Statistics",
|
||||||
|
"Sport": "Sport",
|
||||||
|
"sport": "sport",
|
||||||
|
"Sports": "Sports",
|
||||||
|
"sports": "sports",
|
||||||
|
"Submit": "Submit",
|
||||||
|
"to": "to",
|
||||||
|
"Workout": "Workout",
|
||||||
|
"Workouts": "Workouts",
|
||||||
|
"workout": "workout",
|
||||||
|
"workouts": "workouts"
|
||||||
}
|
}
|
||||||
|
5
fittrackee_client/src/locales/en/dashboard.json
Normal file
5
fittrackee_client/src/locales/en/dashboard.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"Personal records": "Personal records",
|
||||||
|
"This month": "This month",
|
||||||
|
"Upload one !": "Upload one !"
|
||||||
|
}
|
8
fittrackee_client/src/locales/en/sports.json
Normal file
8
fittrackee_client/src/locales/en/sports.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Cycling (Sport)": "Cycling (Sport)",
|
||||||
|
"Cycling (Transport)": "Cycling (Transport)",
|
||||||
|
"Hiking": "Hiking",
|
||||||
|
"Mountain Biking": "Mountain Biking",
|
||||||
|
"Running": "Running",
|
||||||
|
"Walking": "Walking"
|
||||||
|
}
|
9
fittrackee_client/src/locales/en/statistics.json
Normal file
9
fittrackee_client/src/locales/en/statistics.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"activities": "activities",
|
||||||
|
"distance": "distance",
|
||||||
|
"duration": "duration",
|
||||||
|
"month": "month",
|
||||||
|
"Statistics": "Statistics",
|
||||||
|
"year": "year",
|
||||||
|
"week": "week"
|
||||||
|
}
|
@ -1,8 +1,27 @@
|
|||||||
{
|
{
|
||||||
"login": "login",
|
"Bio": "Bio",
|
||||||
"register": "register",
|
"Birth Date": "Birth Date",
|
||||||
|
"Delete picture": "Delete picture",
|
||||||
|
"Edit Profile": "Edit Profile",
|
||||||
|
"Email": "Email",
|
||||||
"Enter a username": "Enter a username",
|
"Enter a username": "Enter a username",
|
||||||
"Enter an email address": "Enter an email address",
|
"Enter an email address": "Enter an email address",
|
||||||
"Enter a password": "Enter a password",
|
"Enter a password": "Enter a password",
|
||||||
"Enter the password confirmation": "Enter the password confirmation"
|
"Enter the password confirmation": "Enter the password confirmation",
|
||||||
|
"First day of week": "First day of week",
|
||||||
|
"First Name": "First Name",
|
||||||
|
"Last Name": "Last Name",
|
||||||
|
"Location": "Location",
|
||||||
|
"loggedOut": "You are now logged out. Click <1>here</1> to log back in.",
|
||||||
|
"login": "login",
|
||||||
|
"Monday": "Monday",
|
||||||
|
"Password": "Password",
|
||||||
|
"Password Confirmation": "Password Confirmation",
|
||||||
|
"Profile": "Profile",
|
||||||
|
"Profile Edition": "Profile Edition",
|
||||||
|
"register": "register",
|
||||||
|
"Registration Date": "Registration Date",
|
||||||
|
"Send": "Send",
|
||||||
|
"Sunday": "Sunday",
|
||||||
|
"Timezone": "Timezone"
|
||||||
}
|
}
|
||||||
|
23
fittrackee_client/src/locales/fr/activities.json
Normal file
23
fittrackee_client/src/locales/fr/activities.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"Activity Date": "Date de l'activité",
|
||||||
|
"Add a workout": "Ajouter une activité",
|
||||||
|
"Ave. speed": "Vitesse moyenne",
|
||||||
|
"Average speed": "Vitesse moyenne",
|
||||||
|
"Date": "Date",
|
||||||
|
"Distance": "Distance",
|
||||||
|
"Duration": "Durée",
|
||||||
|
"Edit a workout": "Editer une activité",
|
||||||
|
"Filter": "Filtrer",
|
||||||
|
"From": "A partir de",
|
||||||
|
"gpxFile": "fichier <strong>gpx</strong>",
|
||||||
|
"Max. speed": "Vitesse max",
|
||||||
|
"no folder inside": "pas de répertoire",
|
||||||
|
"files max": " fichiers max",
|
||||||
|
"max size": "taille max",
|
||||||
|
"Notes": "Notes",
|
||||||
|
"Title": "Titre",
|
||||||
|
"To": "Jusqu'au",
|
||||||
|
"with gpx file": "avec un fichier gpx",
|
||||||
|
"without gpx file": "sans fichier gpx",
|
||||||
|
"zipFile": "ou un fichier <strong> zip</strong> contenant des fichiers <strong>gpx</strong>"
|
||||||
|
}
|
@ -1,10 +1,23 @@
|
|||||||
{
|
{
|
||||||
"Dashboard": "Tableau de Bord",
|
|
||||||
"Workouts": "Activités",
|
|
||||||
"Statistics": "Statistiques",
|
|
||||||
"Add workout": "Ajouter une activité",
|
"Add workout": "Ajouter une activité",
|
||||||
"Register": "S'inscrire",
|
"Cancel": "Annuler",
|
||||||
|
"Dashboard": "Tableau de Bord",
|
||||||
|
"day": "jour",
|
||||||
|
"days": "jours",
|
||||||
"Login": "Se connecter",
|
"Login": "Se connecter",
|
||||||
"Logout": "Se déconnecter",
|
"Logout": "Se déconnecter",
|
||||||
"Submit": "Valider"
|
"No records.": "Pas de records.",
|
||||||
|
"No workouts.": "Pas d'activités.",
|
||||||
|
"Register": "S'inscrire",
|
||||||
|
"Statistics": "Statistiques",
|
||||||
|
"Sport": "Sport",
|
||||||
|
"sport": "sport",
|
||||||
|
"Sports": "Sports",
|
||||||
|
"sports": "sports",
|
||||||
|
"Submit": "Valider",
|
||||||
|
"to": "à",
|
||||||
|
"Workout": "Activité",
|
||||||
|
"Workouts": "Activités",
|
||||||
|
"workout": "activité",
|
||||||
|
"workouts": "activités"
|
||||||
}
|
}
|
||||||
|
5
fittrackee_client/src/locales/fr/dashboard.json
Normal file
5
fittrackee_client/src/locales/fr/dashboard.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"Personal records": "Mes records",
|
||||||
|
"This month": "Ce mois",
|
||||||
|
"Upload one !": "Ajoutez votre première activité !"
|
||||||
|
}
|
8
fittrackee_client/src/locales/fr/sports.json
Normal file
8
fittrackee_client/src/locales/fr/sports.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Cycling (Sport)": "Vélo (Sport)",
|
||||||
|
"Cycling (Transport)": "Vélo (Transport)",
|
||||||
|
"Hiking": "Randonnée",
|
||||||
|
"Mountain Biking": "VTT",
|
||||||
|
"Running": "Course",
|
||||||
|
"Walking": "Marche"
|
||||||
|
}
|
9
fittrackee_client/src/locales/fr/statistics.json
Normal file
9
fittrackee_client/src/locales/fr/statistics.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"activities": "activités",
|
||||||
|
"distance": "distance",
|
||||||
|
"duration": "durée",
|
||||||
|
"month": "mois",
|
||||||
|
"Statistics": "Statistiques",
|
||||||
|
"year": "année",
|
||||||
|
"week": "semaine"
|
||||||
|
}
|
@ -1,8 +1,27 @@
|
|||||||
{
|
{
|
||||||
"login": "se connecter",
|
"Bio": "Bio",
|
||||||
"register": "s'nscrire",
|
"Birth Date": "Date de naissance",
|
||||||
|
"Delete picture": "Supprimer l'image",
|
||||||
|
"Edit Profile": "Editer le profil",
|
||||||
|
"Email": "Email",
|
||||||
"Enter a username": "Saisir un nom",
|
"Enter a username": "Saisir un nom",
|
||||||
"Enter an email address": "Saisir une adresse e-mail",
|
"Enter an email address": "Saisir une adresse e-mail",
|
||||||
"Enter a password": "Saisir un mot de passe",
|
"Enter a password": "Saisir un mot de passe",
|
||||||
"Enter the password confirmation": "Confirmer le mot de passe"
|
"Enter the password confirmation": "Confirmer le mot de passe",
|
||||||
|
"First day of week": "Premier jour de la semaine",
|
||||||
|
"First Name": "Prénom",
|
||||||
|
"Last Name": "Nom",
|
||||||
|
"Location": "Lieu",
|
||||||
|
"loggedOut": "Vous êtes déconnecté. Cliquez <1>ici</1> pour vous reconnecter.",
|
||||||
|
"login": "se connecter",
|
||||||
|
"Monday": "Lundi",
|
||||||
|
"Password": "Mot de passe",
|
||||||
|
"Password Confirmation": "Confirmation du mot de passe",
|
||||||
|
"Profile": "Profil",
|
||||||
|
"Profile Edition": "Edition du profil",
|
||||||
|
"register": "s'inscrire",
|
||||||
|
"Registration Date": "Date d'inscription",
|
||||||
|
"Send": "Envoyer",
|
||||||
|
"Sunday": "Dimanche",
|
||||||
|
"Timezone": "Fuseau horaire"
|
||||||
}
|
}
|
||||||
|
@ -86,3 +86,17 @@ export const formatRecord = (record, tz) => {
|
|||||||
value: value,
|
value: value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sortSports = (a, b) => {
|
||||||
|
const sportALabel = a.label.toLowerCase()
|
||||||
|
const sportBLabel = b.label.toLowerCase()
|
||||||
|
return sportALabel > sportBLabel ? 1 : sportALabel < sportBLabel ? -1 : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export const translateSports = (sports, t) =>
|
||||||
|
sports
|
||||||
|
.map(sport => ({
|
||||||
|
...sport,
|
||||||
|
label: t(`sports:${sport.label}`),
|
||||||
|
}))
|
||||||
|
.sort(sortSports)
|
||||||
|
Loading…
Reference in New Issue
Block a user