Merge branch 'master' into v0.2

This commit is contained in:
Sam
2019-01-13 15:35:44 +01:00
25 changed files with 558 additions and 480 deletions

View File

@ -1,4 +1,4 @@
FROM node:10.9.0
FROM node:11.6.0
MAINTAINER SamR1@users.noreply.github.com

View File

@ -12,5 +12,5 @@
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff",
"version": "0.2.0"
"version": "0.2.0-beta"
}

View File

@ -1,176 +1,178 @@
import React from 'react'
export default function ActivitiesList (props) {
const { loadActivities, sports, updateParams } = props
return (
<div className="card">
<div className="card-body activity-filter">
<form onSubmit={event => event.preventDefault()}>
<div className="form-group">
<label>
From:
<input
className="form-control col-md"
name="from"
onChange={e => updateParams(e)}
type="date"
/>
</label>
<label>
To:
<input
className="form-control col-md"
name="to"
onChange={e => updateParams(e)}
type="date"
/>
</label>
</div>
<div className="form-group">
<label>
Sport:
<select
className="form-control input-lg"
name="sport_id"
onChange={e => updateParams(e)}
>
<option value="" />
{sports.map(sport => (
<option key={sport.id} value={sport.id}>
{sport.label}
</option>
))}
</select>
</label>
</div>
<div className="form-group">
<label>
Distance (km):
<div className="container">
<div className="row">
<div className="col-5">
<input
className="form-control"
min={0}
name="distance_from"
onChange={e => updateParams(e)}
step="1"
type="number"
/>
</div>
<div className="col-2 align-middle text-center">to</div>
<div className="col-5">
<input
className="form-control"
min={0}
name="distance_to"
onChange={e => updateParams(e)}
step="1"
type="number"
/>
export default class ActivitiesFilter extends React.PureComponent {
render() {
const { loadActivities, sports, updateParams } = this.props
return (
<div className="card">
<div className="card-body activity-filter">
<form onSubmit={event => event.preventDefault()}>
<div className="form-group">
<label>
From:
<input
className="form-control col-md"
name="from"
onChange={e => updateParams(e)}
type="date"
/>
</label>
<label>
To:
<input
className="form-control col-md"
name="to"
onChange={e => updateParams(e)}
type="date"
/>
</label>
</div>
<div className="form-group">
<label>
Sport:
<select
className="form-control input-lg"
name="sport_id"
onChange={e => updateParams(e)}
>
<option value="" />
{sports.map(sport => (
<option key={sport.id} value={sport.id}>
{sport.label}
</option>
))}
</select>
</label>
</div>
<div className="form-group">
<label>
Distance (km):
<div className="container">
<div className="row">
<div className="col-5">
<input
className="form-control"
min={0}
name="distance_from"
onChange={e => updateParams(e)}
step="1"
type="number"
/>
</div>
<div className="col-2 align-middle text-center">to</div>
<div className="col-5">
<input
className="form-control"
min={0}
name="distance_to"
onChange={e => updateParams(e)}
step="1"
type="number"
/>
</div>
</div>
</div>
</div>
</label>
</div>
<div className="form-group">
<label>
Duration:
<div className="container">
<div className="row">
<div className="col-5">
<input
className="form-control"
name="duration_from"
onChange={e => updateParams(e)}
pattern="^([0-9]*[0-9]):([0-5][0-9])$"
placeholder="hh:mm"
type="text"
/>
</div>
<div className="col-2 align-middle text-center">to</div>
<div className="col-5">
<input
className="form-control"
name="duration_to"
onChange={e => updateParams(e)}
pattern="^([0-9]*[0-9]):([0-5][0-9])$"
placeholder="hh:mm"
type="text"
/>
</label>
</div>
<div className="form-group">
<label>
Duration:
<div className="container">
<div className="row">
<div className="col-5">
<input
className="form-control"
name="duration_from"
onChange={e => updateParams(e)}
pattern="^([0-9]*[0-9]):([0-5][0-9])$"
placeholder="hh:mm"
type="text"
/>
</div>
<div className="col-2 align-middle text-center">to</div>
<div className="col-5">
<input
className="form-control"
name="duration_to"
onChange={e => updateParams(e)}
pattern="^([0-9]*[0-9]):([0-5][0-9])$"
placeholder="hh:mm"
type="text"
/>
</div>
</div>
</div>
</div>
</label>
</div>
<div className="form-group">
<label>
Average speed (km/h):
<div className="container">
<div className="row">
<div className="col-5">
<input
className="form-control"
min={0}
name="ave_speed_from"
onChange={e => updateParams(e)}
step="1"
type="number"
/>
</div>
<div className="col-2 align-middle text-center">to</div>
<div className="col-5">
<input
className="form-control"
min={0}
name="ave_speed_to"
onChange={e => updateParams(e)}
step="1"
type="number"
/>
</label>
</div>
<div className="form-group">
<label>
Average speed (km/h):
<div className="container">
<div className="row">
<div className="col-5">
<input
className="form-control"
min={0}
name="ave_speed_from"
onChange={e => updateParams(e)}
step="1"
type="number"
/>
</div>
<div className="col-2 align-middle text-center">to</div>
<div className="col-5">
<input
className="form-control"
min={0}
name="ave_speed_to"
onChange={e => updateParams(e)}
step="1"
type="number"
/>
</div>
</div>
</div>
</div>
</label>
</div>
<div className="form-group">
<label>
Max speed (km/h):
<div className="container">
<div className="row">
<div className="col-5">
<input
className="form-control"
min={0}
name="max_speed_from"
onChange={e => updateParams(e)}
step="1"
type="number"
/>
</div>
<div className="col-2 align-middle text-center">to</div>
<div className="col-5">
<input
className="form-control"
min={0}
name="max_speed_to"
onChange={e => updateParams(e)}
step="1"
type="number"
/>
</label>
</div>
<div className="form-group">
<label>
Max speed (km/h):
<div className="container">
<div className="row">
<div className="col-5">
<input
className="form-control"
min={0}
name="max_speed_from"
onChange={e => updateParams(e)}
step="1"
type="number"
/>
</div>
<div className="col-2 align-middle text-center">to</div>
<div className="col-5">
<input
className="form-control"
min={0}
name="max_speed_to"
onChange={e => updateParams(e)}
step="1"
type="number"
/>
</div>
</div>
</div>
</div>
</label>
</div>
<input
className="btn btn-primary btn-lg btn-block"
onClick={() => loadActivities()}
type="submit"
value="Filter"
/>
</form>
</label>
</div>
<input
className="btn btn-primary btn-lg btn-block"
onClick={() => loadActivities()}
type="submit"
value="Filter"
/>
</form>
</div>
</div>
</div>
)
)
}
}

View File

@ -4,13 +4,14 @@ import { Link } from 'react-router-dom'
import { getDateWithTZ } from '../../utils'
export default function ActivitiesList (props) {
const { activities, sports, user } = props
return (
<div className="card activity-card">
<div className="card-body">
<table className="table">
<thead>
export default class ActivitiesList extends React.PureComponent {
render() {
const { activities, sports, user } = this.props
return (
<div className="card activity-card">
<div className="card-body">
<table className="table">
<thead>
<tr>
<th scope="col" />
<th scope="col">Workout</th>
@ -20,12 +21,12 @@ export default function ActivitiesList (props) {
<th scope="col">Ave. speed</th>
<th scope="col">Max. speed</th>
</tr>
</thead>
<tbody>
{ sports && activities.map((activity, idx) => (
</thead>
<tbody>
{sports && activities.map((activity, idx) => (
// eslint-disable-next-line react/no-array-index-key
<tr key={idx}>
<td >
<td>
<img
className="activity-sport"
src={sports
@ -34,11 +35,11 @@ export default function ActivitiesList (props) {
alt="activity sport logo"
/>
</td>
<td >
<td>
<Link to={`/activities/${activity.id}`}>
{activity.title}
</Link>
</td>
</td>
<td>
{format(
getDateWithTZ(activity.activity_date, user.timezone),
@ -48,14 +49,15 @@ export default function ActivitiesList (props) {
<td className="text-right">
{Number(activity.distance).toFixed(2)} km
</td>
<td className="text-right">{activity.duration}</td>
<td className="text-right">{activity.moving}</td>
<td className="text-right">{activity.ave_speed} km/h</td>
<td className="text-right">{activity.max_speed} km/h</td>
</tr>
))}
</tbody>
</table>
</tbody>
</table>
</div>
</div>
</div>
)
)
}
}

View File

@ -13,15 +13,7 @@ export default function ActivityDetails(props) {
className="fa fa-clock-o custom-fa"
aria-hidden="true"
/>
Duration: {activity.duration}
{withPauses && (
<span>
{' '}
(pauses: {activity.pauses})
<br />
Moving duration: {activity.moving}
</span>
)}
Duration: {activity.moving}
{recordLDexists && (
<sup>
<i
@ -30,6 +22,12 @@ export default function ActivityDetails(props) {
/>
</sup>
)}
{withPauses && (
<span>
<br />
(pauses: {activity.pauses}, total duration: {activity.duration})
</span>
)}
</p>
<p>
<i

View File

@ -57,7 +57,7 @@ function FormWithGpx (props) {
gpxLimit} files max):
<input
accept=".gpx, .zip"
className="form-control input-lg"
className="form-control form-control-file gpx-file"
disabled={loading}
name="gpxFile"
required
@ -95,7 +95,7 @@ function FormWithGpx (props) {
<input
type="submit"
className="btn btn-secondary btn-lg btn-block"
onClick={() => history.go(-1)}
onClick={() => history.push('/')}
value="Cancel"
/>
</div>

View File

@ -123,7 +123,7 @@ function FormWithoutGpx (props) {
<input
type="submit"
className="btn btn-secondary btn-lg btn-block"
onClick={() => history.go(-1)}
onClick={() => history.push('/')}
value="Cancel"
/>
</form>

View File

@ -251,6 +251,10 @@ label {
width: 100%;
}
.gpx-file {
height: inherit;
}
.huge {
font-size: 25px;
}
@ -325,6 +329,10 @@ label {
max-height: 45px;
}
.timezone-custom-height {
height: calc(2.25rem + 14px);
}
.timezone-picker {
padding: 0;
}

View File

@ -49,7 +49,7 @@ export default function ActivityCard (props) {
<div className="col">
<p>
<i className="fa fa-clock-o" aria-hidden="true" />{' '}
Duration: {activity.duration}
Duration: {activity.moving}
{activity.map ? (
<span><br /><br /></span>
) : (

View File

@ -4,152 +4,160 @@ import { Link } from 'react-router-dom'
import { apiUrl } from '../../utils'
function NavBar(props) {
return (
<header>
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<div className="container">
<span className="navbar-brand">FitTrackee</span>
<button
className="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span className="navbar-toggler-icon" />
</button>
<div className="collapse navbar-collapse" id="navbarSupportedContent">
<ul className="navbar-nav mr-auto">
<li className="nav-item">
<Link
className="nav-link"
to={{
pathname: '/',
}}
>
Dashboard
</Link>
</li>
{props.user.isAuthenticated && (
class NavBar extends React.PureComponent {
render() {
const { id, isAuthenticated, picture, username } = this.props
return (
<header>
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<div className="container">
<span className="navbar-brand">FitTrackee</span>
<button
className="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span className="navbar-toggler-icon" />
</button>
<div
className="collapse navbar-collapse"
id="navbarSupportedContent"
>
<ul className="navbar-nav mr-auto">
<li className="nav-item">
<Link
className="nav-link"
to={{
pathname: '/activities/history',
pathname: '/',
}}
>
Workouts
Dashboard
</Link>
</li>
)}
{props.user.isAuthenticated && (
<li className="nav-item">
<Link
className="nav-link"
to={{
pathname: '/activities/statistics',
}}
>
Statistics
</Link>
</li>
)}
{props.user.isAuthenticated && (
<li className="nav-item">
<Link
className="nav-link"
to={{
pathname: '/activities/add',
}}
>
<strong>Add workout</strong>
</Link>
</li>
)}
{/* {props.user.admin && ( */}
{/* <li className="nav-item"> */}
{/* <Link */}
{/* className="nav-link" */}
{/* to={{ */}
{/* pathname: '/admin', */}
{/* }} */}
{/* > */}
{/* Admin */}
{/* </Link> */}
{/* </li> */}
{/* )} */}
</ul>
<ul className="navbar-nav flex-row ml-md-auto d-none d-md-flex">
{!props.user.isAuthenticated && (
<li className="nav-item">
<Link
className="nav-link"
to={{
pathname: '/register',
}}
>
Register
</Link>
</li>
)}
{!props.user.isAuthenticated && (
<li className="nav-item">
<Link
className="nav-link"
to={{
pathname: '/login',
}}
>
Login
</Link>
</li>
)}
{props.user.picture === true && (
<img
alt="Avatar"
src={`${apiUrl}users/${props.user.id}/picture` +
`?${Date.now()}`}
className="img-fluid App-nav-profile-img"
/>
)}
{props.user.isAuthenticated && (
<li className="nav-item">
<Link
className="nav-link"
to={{
pathname: '/profile',
}}
>
{props.user.username}
</Link>
</li>
)}
{props.user.isAuthenticated && (
<li className="nav-item">
<Link
className="nav-link"
to={{
pathname: '/logout',
}}
>
Logout
</Link>
</li>
)}
</ul>
{isAuthenticated && (
<li className="nav-item">
<Link
className="nav-link"
to={{
pathname: '/activities/history',
}}
>
Workouts
</Link>
</li>
)}
{isAuthenticated && (
<li className="nav-item">
<Link
className="nav-link"
to={{
pathname: '/activities/statistics',
}}
>
Statistics
</Link>
</li>
)}
{isAuthenticated && (
<li className="nav-item">
<Link
className="nav-link"
to={{
pathname: '/activities/add',
}}
>
<strong>Add workout</strong>
</Link>
</li>
)}
{/* {user.admin && ( */}
{/* <li className="nav-item"> */}
{/* <Link */}
{/* className="nav-link" */}
{/* to={{ */}
{/* pathname: '/admin', */}
{/* }} */}
{/* > */}
{/* Admin */}
{/* </Link> */}
{/* </li> */}
{/* )} */}
</ul>
<ul className="navbar-nav flex-row ml-md-auto d-none d-md-flex">
{!isAuthenticated && (
<li className="nav-item">
<Link
className="nav-link"
to={{
pathname: '/register',
}}
>
Register
</Link>
</li>
)}
{!isAuthenticated && (
<li className="nav-item">
<Link
className="nav-link"
to={{
pathname: '/login',
}}
>
Login
</Link>
</li>
)}
{picture === true && (
<img
alt="Avatar"
src={`${apiUrl}users/${id}/picture` +
`?${Date.now()}`}
className="img-fluid App-nav-profile-img"
/>
)}
{isAuthenticated && (
<li className="nav-item">
<Link
className="nav-link"
to={{
pathname: '/profile',
}}
>
{username}
</Link>
</li>
)}
{isAuthenticated && (
<li className="nav-item">
<Link
className="nav-link"
to={{
pathname: '/logout',
}}
>
Logout
</Link>
</li>
)}
</ul>
</div>
</div>
</div>
</nav>
</header>
)
</nav>
</header>
)
}
}
export default connect(
state => ({
user: state.user,
({ user }) => ({
id: user.id,
isAuthenticated: user.isAuthenticated,
picture: user.picture,
username: user.username,
})
)(NavBar)

View File

@ -173,7 +173,6 @@ class ProfileEdit extends React.Component {
name="bio"
className="form-control input-lg"
maxLength="200"
type="text"
value={formData.bio}
onChange={e => this.handleFormChange(e)}
/>
@ -183,7 +182,7 @@ class ProfileEdit extends React.Component {
<label>
Timezone:
<TimezonePicker
className="form-control"
className="form-control timezone-custom-height"
onChange={tz => {
const e = { target:
{

View File

@ -1,7 +1,7 @@
import { format, parse } from 'date-fns'
import { DateTime } from 'luxon'
export const version = '0.2.0' // version stored in 'utils' for now
export const version = '0.2.0-beta' // version stored in 'utils' for now
export const apiUrl = `${process.env.REACT_APP_API_URL}/api/`
export const thunderforestApiKey = `${
process.env.REACT_APP_THUNDERFOREST_API_KEY