API & Client : add a link to display user detail in admin - #15

This commit is contained in:
Sam 2020-02-08 12:36:03 +01:00
parent f1257f98a4
commit 33ed19a7e7
8 changed files with 165 additions and 98 deletions

View File

@ -230,30 +230,32 @@
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span> <span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
<span class="p">{</span> <span class="p">{</span>
<span class="nt">&quot;data&quot;</span><span class="p">:</span> <span class="p">{</span> <span class="nt">&quot;data&quot;</span><span class="p">:</span> <span class="p">[</span>
<span class="nt">&quot;admin&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="p">{</span>
<span class="nt">&quot;bio&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span> <span class="nt">&quot;admin&quot;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="nt">&quot;birth_date&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span> <span class="nt">&quot;bio&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;created_at&quot;</span><span class="p">:</span> <span class="s2">&quot;Sun, 14 Jul 2019 14:09:58 GMT&quot;</span><span class="p">,</span> <span class="nt">&quot;birth_date&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;email&quot;</span><span class="p">:</span> <span class="s2">&quot;admin@example.com&quot;</span><span class="p">,</span> <span class="nt">&quot;created_at&quot;</span><span class="p">:</span> <span class="s2">&quot;Sun, 14 Jul 2019 14:09:58 GMT&quot;</span><span class="p">,</span>
<span class="nt">&quot;first_name&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span> <span class="nt">&quot;email&quot;</span><span class="p">:</span> <span class="s2">&quot;admin@example.com&quot;</span><span class="p">,</span>
<span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nt">&quot;first_name&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;language&quot;</span><span class="p">:</span> <span class="s2">&quot;en&quot;</span><span class="p">,</span> <span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="nt">&quot;last_name&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span> <span class="nt">&quot;language&quot;</span><span class="p">:</span> <span class="s2">&quot;en&quot;</span><span class="p">,</span>
<span class="nt">&quot;location&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span> <span class="nt">&quot;last_name&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;nb_activities&quot;</span><span class="p">:</span> <span class="mi">6</span><span class="p">,</span> <span class="nt">&quot;location&quot;</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nt">&quot;nb_sports&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span> <span class="nt">&quot;nb_activities&quot;</span><span class="p">:</span> <span class="mi">6</span><span class="p">,</span>
<span class="nt">&quot;picture&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nt">&quot;nb_sports&quot;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
<span class="nt">&quot;sports_list&quot;</span><span class="p">:</span> <span class="p">[</span> <span class="nt">&quot;picture&quot;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="mi">1</span><span class="p">,</span> <span class="nt">&quot;sports_list&quot;</span><span class="p">:</span> <span class="p">[</span>
<span class="mi">4</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span>
<span class="mi">6</span> <span class="mi">4</span><span class="p">,</span>
<span class="p">],</span> <span class="mi">6</span>
<span class="nt">&quot;timezone&quot;</span><span class="p">:</span> <span class="s2">&quot;Europe/Paris&quot;</span><span class="p">,</span> <span class="p">],</span>
<span class="nt">&quot;total_distance&quot;</span><span class="p">:</span> <span class="mf">67.895</span><span class="p">,</span> <span class="nt">&quot;timezone&quot;</span><span class="p">:</span> <span class="s2">&quot;Europe/Paris&quot;</span><span class="p">,</span>
<span class="nt">&quot;total_duration&quot;</span><span class="p">:</span> <span class="s2">&quot;6:50:27&quot;</span><span class="p">,</span> <span class="nt">&quot;total_distance&quot;</span><span class="p">:</span> <span class="mf">67.895</span><span class="p">,</span>
<span class="nt">&quot;username&quot;</span><span class="p">:</span> <span class="s2">&quot;admin&quot;</span> <span class="nt">&quot;total_duration&quot;</span><span class="p">:</span> <span class="s2">&quot;6:50:27&quot;</span><span class="p">,</span>
<span class="p">},</span> <span class="nt">&quot;username&quot;</span><span class="p">:</span> <span class="s2">&quot;admin&quot;</span>
<span class="p">}</span>
<span class="p">],</span>
<span class="nt">&quot;status&quot;</span><span class="p">:</span> <span class="s2">&quot;success&quot;</span> <span class="nt">&quot;status&quot;</span><span class="p">:</span> <span class="s2">&quot;success&quot;</span>
<span class="p">}</span> <span class="p">}</span>
</pre></div> </pre></div>

View File

@ -33,24 +33,26 @@ def test_single_user(app, user_1):
assert response.status_code == 200 assert response.status_code == 200
assert data['status'] == 'success' assert data['status'] == 'success'
assert data['data'] is not None assert len(data['data']['users']) == 1
assert data['data']['username'] == 'test'
assert data['data']['email'] == 'test@test.com' user = data['data']['users'][0]
assert data['data']['created_at'] assert user['username'] == 'test'
assert not data['data']['admin'] assert user['email'] == 'test@test.com'
assert data['data']['first_name'] is None assert user['created_at']
assert data['data']['last_name'] is None assert not user['admin']
assert data['data']['birth_date'] is None assert user['first_name'] is None
assert data['data']['bio'] is None assert user['last_name'] is None
assert data['data']['location'] is None assert user['birth_date'] is None
assert data['data']['timezone'] is None assert user['bio'] is None
assert data['data']['weekm'] is False assert user['location'] is None
assert data['data']['language'] is None assert user['timezone'] is None
assert data['data']['nb_activities'] == 0 assert user['weekm'] is False
assert data['data']['nb_sports'] == 0 assert user['language'] is None
assert data['data']['sports_list'] == [] assert user['nb_activities'] == 0
assert data['data']['total_distance'] == 0 assert user['nb_sports'] == 0
assert data['data']['total_duration'] == '0:00:00' assert user['sports_list'] == []
assert user['total_distance'] == 0
assert user['total_duration'] == '0:00:00'
def test_single_user_with_activities( def test_single_user_with_activities(
@ -80,24 +82,26 @@ def test_single_user_with_activities(
assert response.status_code == 200 assert response.status_code == 200
assert data['status'] == 'success' assert data['status'] == 'success'
assert data['data'] is not None assert len(data['data']['users']) == 1
assert data['data']['username'] == 'test'
assert data['data']['email'] == 'test@test.com' user = data['data']['users'][0]
assert data['data']['created_at'] assert user['username'] == 'test'
assert not data['data']['admin'] assert user['email'] == 'test@test.com'
assert data['data']['first_name'] is None assert user['created_at']
assert data['data']['last_name'] is None assert not user['admin']
assert data['data']['birth_date'] is None assert user['first_name'] is None
assert data['data']['bio'] is None assert user['last_name'] is None
assert data['data']['location'] is None assert user['birth_date'] is None
assert data['data']['timezone'] is None assert user['bio'] is None
assert data['data']['weekm'] is False assert user['location'] is None
assert data['data']['language'] is None assert user['timezone'] is None
assert data['data']['nb_activities'] == 2 assert user['weekm'] is False
assert data['data']['nb_sports'] == 2 assert user['language'] is None
assert data['data']['sports_list'] == [1, 2] assert user['nb_activities'] == 2
assert data['data']['total_distance'] == 22 assert user['nb_sports'] == 2
assert data['data']['total_duration'] == '1:57:04' assert user['sports_list'] == [1, 2]
assert user['total_distance'] == 22
assert user['total_duration'] == '1:57:04'
def test_single_user_no_id(app, user_1): def test_single_user_no_id(app, user_1):

View File

@ -119,30 +119,32 @@ def get_single_user(auth_user_id, user_id):
Content-Type: application/json Content-Type: application/json
{ {
"data": { "data": [
"admin": true, {
"bio": null, "admin": true,
"birth_date": null, "bio": null,
"created_at": "Sun, 14 Jul 2019 14:09:58 GMT", "birth_date": null,
"email": "admin@example.com", "created_at": "Sun, 14 Jul 2019 14:09:58 GMT",
"first_name": null, "email": "admin@example.com",
"id": 1, "first_name": null,
"language": "en", "id": 1,
"last_name": null, "language": "en",
"location": null, "last_name": null,
"nb_activities": 6, "location": null,
"nb_sports": 3, "nb_activities": 6,
"picture": false, "nb_sports": 3,
"sports_list": [ "picture": false,
1, "sports_list": [
4, 1,
6 4,
], 6
"timezone": "Europe/Paris", ],
"total_distance": 67.895, "timezone": "Europe/Paris",
"total_duration": "6:50:27", "total_distance": 67.895,
"username": "admin" "total_duration": "6:50:27",
}, "username": "admin"
}
],
"status": "success" "status": "success"
} }
@ -166,7 +168,10 @@ def get_single_user(auth_user_id, user_id):
if not user: if not user:
return jsonify(response_object), 404 return jsonify(response_object), 404
else: else:
response_object = {'status': 'success', 'data': user.serialize()} response_object = {
'status': 'success',
'data': {'users': [user.serialize()]},
}
return jsonify(response_object), 200 return jsonify(response_object), 200
except ValueError: except ValueError:
return jsonify(response_object), 404 return jsonify(response_object), 404

View File

@ -2,6 +2,7 @@ import { format } from 'date-fns'
import React from 'react' import React from 'react'
import { connect } from 'react-redux' import { connect } from 'react-redux'
import { Helmet } from 'react-helmet' import { Helmet } from 'react-helmet'
import { Link } from 'react-router-dom'
import Message from '../../Common/Message' import Message from '../../Common/Message'
import { history } from '../../../index' import { history } from '../../../index'
@ -39,7 +40,9 @@ class AdminUsers extends React.Component {
{users.map(user => ( {users.map(user => (
<tr key={user.id}> <tr key={user.id}>
<th scope="row">{user.id}</th> <th scope="row">{user.id}</th>
<td>{user.username}</td> <td>
<Link to={`/users/${user.id}`}>{user.username}</Link>
</td>
<td>{user.email}</td> <td>{user.email}</td>
<td> <td>
{format( {format(

View File

@ -6,15 +6,16 @@ import './App.css'
import Admin from './Admin' import Admin from './Admin'
import Activity from './Activity' import Activity from './Activity'
import Activities from './Activities' import Activities from './Activities'
import CurrentUserProfile from './User/CurrentUserProfile'
import Dashboard from './Dashboard' import Dashboard from './Dashboard'
import Footer from './Footer' import Footer from './Footer'
import Logout from './User/Logout' import Logout from './User/Logout'
import NavBar from './NavBar' import NavBar from './NavBar'
import NotFound from './Others/NotFound' import NotFound from './Others/NotFound'
import Profile from './User/Profile'
import ProfileEdit from './User/ProfileEdit' import ProfileEdit from './User/ProfileEdit'
import Statistics from './Statistics' import Statistics from './Statistics'
import UserForm from './User/UserForm' import UserForm from './User/UserForm'
import UserProfile from './User/UserProfile'
import { getAppData } from '../actions/application' import { getAppData } from '../actions/application'
class App extends React.Component { class App extends React.Component {
@ -44,9 +45,10 @@ class App extends React.Component {
/> />
<Route exact path="/logout" component={Logout} /> <Route exact path="/logout" component={Logout} />
<Route exact path="/profile/edit" component={ProfileEdit} /> <Route exact path="/profile/edit" component={ProfileEdit} />
<Route exact path="/profile" component={Profile} /> <Route exact path="/profile" component={CurrentUserProfile} />
<Route exact path="/activities/history" component={Activities} /> <Route exact path="/activities/history" component={Activities} />
<Route exact path="/activities/statistics" component={Statistics} /> <Route exact path="/activities/statistics" component={Statistics} />
<Route exact path="/users/:userId" component={UserProfile} />
<Route path="/activities" component={Activity} /> <Route path="/activities" component={Activity} />
<Route path="/admin" component={Admin} /> <Route path="/admin" component={Admin} />
<Route component={NotFound} /> <Route component={NotFound} />

View File

@ -4,10 +4,10 @@ import { connect } from 'react-redux'
import ProfileDetail from './ProfileDetail' import ProfileDetail from './ProfileDetail'
function Profile({ t, user }) { function CurrentUserProfile({ t, user }) {
return ( return (
<div> <div>
<ProfileDetail t={t} user={user} /> <ProfileDetail editable t={t} user={user} />
</div> </div>
) )
} }
@ -15,5 +15,5 @@ function Profile({ t, user }) {
export default withTranslation()( export default withTranslation()(
connect(state => ({ connect(state => ({
user: state.user, user: state.user,
}))(Profile) }))(CurrentUserProfile)
) )

View File

@ -11,6 +11,7 @@ import { apiUrl, getFileSize } from '../../utils'
function ProfileDetail({ function ProfileDetail({
appConfig, appConfig,
editable,
message, message,
onDeletePicture, onDeletePicture,
onUploadPicture, onUploadPicture,
@ -37,13 +38,15 @@ function ProfileDetail({
<div className="card"> <div className="card">
<div className="card-header userName"> <div className="card-header userName">
{user.username}{' '} {user.username}{' '}
<Link {editable && (
to={{ <Link
pathname: '/profile/edit', to={{
}} pathname: '/profile/edit',
> }}
<i className="fa fa-pencil-square-o" aria-hidden="true" /> >
</Link> <i className="fa fa-pencil-square-o" aria-hidden="true" />
</Link>
)}
</div> </div>
<div className="card-body"> <div className="card-body">
<div className="row"> <div className="row">

View File

@ -0,0 +1,48 @@
import React from 'react'
import { withTranslation } from 'react-i18next'
import { connect } from 'react-redux'
import ProfileDetail from './ProfileDetail'
import { getOrUpdateData } from '../../actions'
class UserProfile extends React.Component {
componentDidMount() {
this.props.loadUser(this.props.match.params.userId)
}
componentDidUpdate(prevProps) {
if (prevProps.match.params.userId !== this.props.match.params.userId) {
this.props.loadUser(this.props.match.params.userId)
}
}
render() {
const { t, currentUser, users } = this.props
const [user] = users
return (
<div>
{user && (
<ProfileDetail
editable={currentUser.id === user.id}
t={t}
user={user}
/>
)}
</div>
)
}
}
export default withTranslation()(
connect(
state => ({
currentUser: state.user,
users: state.users.data,
}),
dispatch => ({
loadUser: userId => {
dispatch(getOrUpdateData('getData', 'users', { id: userId }))
},
})
)(UserProfile)
)