API & Client: fix and update (pagination, style)

This commit is contained in:
Sam 2018-05-17 13:33:44 +02:00
parent ca80a8b6d5
commit f744eb0769
9 changed files with 128 additions and 122 deletions

View File

@ -160,6 +160,20 @@ class Activity(db.Model):
self.duration = duration self.duration = duration
def serialize(self): def serialize(self):
previous_activity = Activity.query.filter(
Activity.id != self.id,
Activity.user_id == self.user_id,
Activity.activity_date <= self.activity_date
).order_by(
Activity.activity_date.desc()
).first()
next_activity = Activity.query.filter(
Activity.id != self.id,
Activity.user_id == self.user_id,
Activity.activity_date >= self.activity_date
).order_by(
Activity.activity_date.asc()
).first()
return { return {
"id": self.id, "id": self.id,
"user_id": self.user_id, "user_id": self.user_id,
@ -180,6 +194,8 @@ class Activity(db.Model):
"ave_speed": float(self.ave_speed) if self.ave_speed else None, "ave_speed": float(self.ave_speed) if self.ave_speed else None,
"with_gpx": self.gpx is not None, "with_gpx": self.gpx is not None,
"bounds": [float(bound) for bound in self.bounds] if self.bounds else [], # noqa "bounds": [float(bound) for bound in self.bounds] if self.bounds else [], # noqa
"previous_activity": previous_activity.id if previous_activity else None, # noqa
"next_activity": next_activity.id if next_activity else None,
"segments": [segment.serialize() for segment in self.segments], "segments": [segment.serialize() for segment in self.segments],
"records": [record.serialize() for record in self.records] "records": [record.serialize() for record in self.records]
} }

View File

@ -3,11 +3,6 @@ import mpwoApi from '../mwpoApi/activities'
import { history } from '../index' import { history } from '../index'
import { setError } from './index' import { setError } from './index'
export const endPagination = status => ({
type: 'END_PAGINATION',
status,
})
export const pushActivities = activities => ({ export const pushActivities = activities => ({
type: 'PUSH_ACTIVITIES', type: 'PUSH_ACTIVITIES',
activities, activities,
@ -90,9 +85,6 @@ export const getMoreActivities = page => dispatch => mpwoGenericApi
if (ret.data.activities.length > 0) { if (ret.data.activities.length > 0) {
dispatch(pushActivities(ret.data.activities)) dispatch(pushActivities(ret.data.activities))
} }
if (ret.data.activities.length < 5) {
dispatch(endPagination(true))
}
} else { } else {
dispatch(setError(`activities: ${ret.message}`)) dispatch(setError(`activities: ${ret.message}`))
} }

View File

@ -51,7 +51,7 @@ class ActivityDisplay extends React.Component {
}} }}
close={() => this.setState({ displayModal: false })} close={() => this.setState({ displayModal: false })}
/>} />}
{activity && sport && ( {activity && sport && activities.length === 1 && (
<div className="row"> <div className="row">
<div className="col"> <div className="col">
<div className="card"> <div className="card">
@ -194,11 +194,11 @@ export default connect(
user: state.user, user: state.user,
}), }),
dispatch => ({ dispatch => ({
onDeleteActivity: activityId => {
dispatch(deleteActivity(activityId))
},
loadActivity: activityId => { loadActivity: activityId => {
dispatch(getData('activities', activityId)) dispatch(getData('activities', activityId))
}, },
onDeleteActivity: activityId => {
dispatch(deleteActivity(activityId))
},
}) })
)(ActivityDisplay) )(ActivityDisplay)

View File

@ -8,6 +8,10 @@ class AdminSports extends React.Component {
componentDidMount() { componentDidMount() {
this.props.loadSport(this.props.match.params.sportId) this.props.loadSport(this.props.match.params.sportId)
} }
componentWillUnmount() {
// reload all Sports
this.props.loadSport(null)
}
render() { render() {
const { sports } = this.props const { sports } = this.props

View File

@ -1,4 +1,6 @@
.App { .App {
background-color: #eaeaea;
min-height: 100vh;
text-align: center; text-align: center;
} }
@ -99,7 +101,7 @@ input, textarea {
border-radius: 5px; border-radius: 5px;
max-width: 500px; max-width: 500px;
margin: 20% auto; margin: 20% auto;
z-index: 100; z-index: 1250;
} }
.custom-modal-backdrop { .custom-modal-backdrop {
@ -110,7 +112,7 @@ input, textarea {
right: 0; right: 0;
background-color: rgba(0,0,0,0.3); background-color: rgba(0,0,0,0.3);
padding: 50px; padding: 50px;
z-index: 90; z-index: 1240;
} }
.custom-fa { .custom-fa {

View File

@ -5,7 +5,7 @@ import { connect } from 'react-redux'
import ActivityCard from './ActivityCard' import ActivityCard from './ActivityCard'
import Statistics from './Statistics' import Statistics from './Statistics'
import { getData } from '../../actions' import { getData } from '../../actions'
import { endPagination, getMoreActivities } from '../../actions/activities' import { getMoreActivities } from '../../actions/activities'
class DashBoard extends React.Component { class DashBoard extends React.Component {
constructor(props, context) { constructor(props, context) {
@ -19,14 +19,13 @@ class DashBoard extends React.Component {
this.props.loadActivities() this.props.loadActivities()
} }
componentWillUnmount() {
this.props.resetPaginationStatus(false)
}
render() { render() {
const { const {
activities, loadMoreActivities, message, paginationEnd, sports activities, loadMoreActivities, message, sports
} = this.props } = this.props
const paginationEnd = activities.length > 0
? activities[activities.length - 1].previous_activity === null
: true
const { page } = this.state const { page } = this.state
return ( return (
<div> <div>
@ -76,14 +75,10 @@ class DashBoard extends React.Component {
export default connect( export default connect(
state => ({ state => ({
activities: state.activities.data, activities: state.activities.data,
paginationEnd: state.activities.pagination_end,
message: state.message, message: state.message,
sports: state.sports.data, sports: state.sports.data,
}), }),
dispatch => ({ dispatch => ({
resetPaginationStatus: () => {
dispatch(endPagination(false))
},
loadActivities: () => { loadActivities: () => {
dispatch(getData('activities', null, 1)) dispatch(getData('activities', null, 1))
}, },

View File

@ -5,12 +5,13 @@ import { Link } from 'react-router-dom'
import { apiUrl } from '../../utils' import { apiUrl } from '../../utils'
function NavBar (props) { function NavBar(props) {
return ( return (
<header> <header>
<nav className="navbar navbar-expand-lg navbar-light bg-light"> <nav className="navbar navbar-expand-lg navbar-light bg-light">
<span className="navbar-brand">mpwo</span> <div className="container">
<button <span className="navbar-brand">mpwo</span>
<button
className="navbar-toggler" className="navbar-toggler"
type="button" type="button"
data-toggle="collapse" data-toggle="collapse"
@ -18,109 +19,111 @@ function NavBar (props) {
aria-controls="navbarSupportedContent" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-expanded="false"
aria-label="Toggle navigation" aria-label="Toggle navigation"
> >
<span className="navbar-toggler-icon" /> <span className="navbar-toggler-icon" />
</button> </button>
<div className="collapse navbar-collapse" id="navbarSupportedContent"> <div className="collapse navbar-collapse" id="navbarSupportedContent">
<ul className="navbar-nav mr-auto"> <ul className="navbar-nav mr-auto">
<li className="nav-item">
<Link
className="nav-link"
to={{
pathname: '/',
}}
>
Dashboard
</Link>
</li>
{props.user.isAuthenticated && (
<li className="nav-item">
<Link
className="nav-link"
to={{
pathname: '/activities/add',
}}
>
Add a workout
</Link>
</li>
)}
{props.user.isAdmin && (
<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"> <li className="nav-item">
<Link <Link
className="nav-link" className="nav-link"
to={{ to={{
pathname: '/register', pathname: '/',
}} }}
> >
Register Dashboard
</Link> </Link>
</li> </li>
)} {props.user.isAuthenticated && (
{!props.user.isAuthenticated && ( <li className="nav-item">
<li className="nav-item"> <Link
<Link className="nav-link"
className="nav-link" to={{
to={{ pathname: '/activities/add',
pathname: '/login', }}
}} >
> Add a workout
Login </Link>
</Link> </li>
</li> )}
)} {props.user.isAdmin && (
{props.user.picture === true && ( <li className="nav-item">
<img <Link
alt="Avatar" className="nav-link"
src={`${apiUrl}users/${props.user.id}/picture` + 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()}`} `?${Date.now()}`}
className="img-fluid App-nav-profile-img" className="img-fluid App-nav-profile-img"
/> />
)} )}
{props.user.isAuthenticated && ( {props.user.isAuthenticated && (
<li className="nav-item"> <li className="nav-item">
<Link <Link
className="nav-link" className="nav-link"
to={{ to={{
pathname: '/profile', pathname: '/profile',
}} }}
> >
{props.user.username} {props.user.username}
</Link> </Link>
</li> </li>
)} )}
{props.user.isAuthenticated && ( {props.user.isAuthenticated && (
<li className="nav-item"> <li className="nav-item">
<Link <Link
className="nav-link" className="nav-link"
to={{ to={{
pathname: '/logout', pathname: '/logout',
}} }}
> >
Logout Logout
</Link> </Link>
</li> </li>
)} )}
</ul> </ul>
</div>
</div> </div>
</nav> </nav>
</header> </header>
) )
} }
export default connect( export default connect(
state => ({ state => ({
user: state.user, user: state.user,

View File

@ -21,11 +21,6 @@ const handleDataAndError = (state, type, action) => {
const activities = (state = initial.activities, action) => { const activities = (state = initial.activities, action) => {
switch (action.type) { switch (action.type) {
case 'END_PAGINATION':
return {
...state,
pagination_end: action.status
}
case 'PUSH_ACTIVITIES': case 'PUSH_ACTIVITIES':
return { return {
...state, ...state,

View File

@ -40,7 +40,6 @@ export default {
}, },
activities: { activities: {
...emptyData, ...emptyData,
pagination_end: false
}, },
// check if storing gpx content is OK // check if storing gpx content is OK
gpx: null, gpx: null,