API & Client : add a link to display user detail in admin - #15
This commit is contained in:
		@@ -230,7 +230,8 @@
 | 
				
			|||||||
<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">"data"</span><span class="p">:</span> <span class="p">{</span>
 | 
					  <span class="nt">"data"</span><span class="p">:</span> <span class="p">[</span>
 | 
				
			||||||
 | 
					    <span class="p">{</span>
 | 
				
			||||||
      <span class="nt">"admin"</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
 | 
					      <span class="nt">"admin"</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
 | 
				
			||||||
      <span class="nt">"bio"</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
 | 
					      <span class="nt">"bio"</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
 | 
				
			||||||
      <span class="nt">"birth_date"</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
 | 
					      <span class="nt">"birth_date"</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
 | 
				
			||||||
@@ -253,7 +254,8 @@
 | 
				
			|||||||
      <span class="nt">"total_distance"</span><span class="p">:</span> <span class="mf">67.895</span><span class="p">,</span>
 | 
					      <span class="nt">"total_distance"</span><span class="p">:</span> <span class="mf">67.895</span><span class="p">,</span>
 | 
				
			||||||
      <span class="nt">"total_duration"</span><span class="p">:</span> <span class="s2">"6:50:27"</span><span class="p">,</span>
 | 
					      <span class="nt">"total_duration"</span><span class="p">:</span> <span class="s2">"6:50:27"</span><span class="p">,</span>
 | 
				
			||||||
      <span class="nt">"username"</span><span class="p">:</span> <span class="s2">"admin"</span>
 | 
					      <span class="nt">"username"</span><span class="p">:</span> <span class="s2">"admin"</span>
 | 
				
			||||||
  <span class="p">},</span>
 | 
					    <span class="p">}</span>
 | 
				
			||||||
 | 
					  <span class="p">],</span>
 | 
				
			||||||
  <span class="nt">"status"</span><span class="p">:</span> <span class="s2">"success"</span>
 | 
					  <span class="nt">"status"</span><span class="p">:</span> <span class="s2">"success"</span>
 | 
				
			||||||
<span class="p">}</span>
 | 
					<span class="p">}</span>
 | 
				
			||||||
</pre></div>
 | 
					</pre></div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -119,7 +119,8 @@ def get_single_user(auth_user_id, user_id):
 | 
				
			|||||||
      Content-Type: application/json
 | 
					      Content-Type: application/json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        "data": {
 | 
					        "data": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
            "admin": true,
 | 
					            "admin": true,
 | 
				
			||||||
            "bio": null,
 | 
					            "bio": null,
 | 
				
			||||||
            "birth_date": null,
 | 
					            "birth_date": null,
 | 
				
			||||||
@@ -142,7 +143,8 @@ def get_single_user(auth_user_id, user_id):
 | 
				
			|||||||
            "total_distance": 67.895,
 | 
					            "total_distance": 67.895,
 | 
				
			||||||
            "total_duration": "6:50:27",
 | 
					            "total_duration": "6:50:27",
 | 
				
			||||||
            "username": "admin"
 | 
					            "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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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} />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -11,6 +11,7 @@ import { apiUrl, getFileSize } from '../../utils'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
function ProfileDetail({
 | 
					function ProfileDetail({
 | 
				
			||||||
  appConfig,
 | 
					  appConfig,
 | 
				
			||||||
 | 
					  editable,
 | 
				
			||||||
  message,
 | 
					  message,
 | 
				
			||||||
  onDeletePicture,
 | 
					  onDeletePicture,
 | 
				
			||||||
  onUploadPicture,
 | 
					  onUploadPicture,
 | 
				
			||||||
@@ -37,6 +38,7 @@ function ProfileDetail({
 | 
				
			|||||||
            <div className="card">
 | 
					            <div className="card">
 | 
				
			||||||
              <div className="card-header userName">
 | 
					              <div className="card-header userName">
 | 
				
			||||||
                {user.username}{' '}
 | 
					                {user.username}{' '}
 | 
				
			||||||
 | 
					                {editable && (
 | 
				
			||||||
                  <Link
 | 
					                  <Link
 | 
				
			||||||
                    to={{
 | 
					                    to={{
 | 
				
			||||||
                      pathname: '/profile/edit',
 | 
					                      pathname: '/profile/edit',
 | 
				
			||||||
@@ -44,6 +46,7 @@ function ProfileDetail({
 | 
				
			|||||||
                  >
 | 
					                  >
 | 
				
			||||||
                    <i className="fa fa-pencil-square-o" aria-hidden="true" />
 | 
					                    <i className="fa fa-pencil-square-o" aria-hidden="true" />
 | 
				
			||||||
                  </Link>
 | 
					                  </Link>
 | 
				
			||||||
 | 
					                )}
 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <div className="card-body">
 | 
					              <div className="card-body">
 | 
				
			||||||
                <div className="row">
 | 
					                <div className="row">
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										48
									
								
								fittrackee_client/src/components/User/UserProfile.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								fittrackee_client/src/components/User/UserProfile.jsx
									
									
									
									
									
										Normal 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)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
		Reference in New Issue
	
	Block a user