Browse Source

Add Athletes.vue and Athlete.vue

With all the corresponding store and router actions
main
Haris Razis 4 years ago
parent
commit
ab352ee800
  1. 13
      server/routes/athletes.js
  2. 2
      web/src/components/Navbar.vue
  3. 2
      web/src/components/Sidebar.vue
  4. 16
      web/src/router/index.ts
  5. 4
      web/src/store/index.ts
  6. 90
      web/src/store/modules/athletes.ts
  7. 34
      web/src/store/modules/user.ts
  8. 125
      web/src/views/Athlete.vue
  9. 75
      web/src/views/Athletes.vue

13
server/routes/athletes.js

@ -21,8 +21,17 @@ router.get('/api/athletes/:id/edit', requireAuth, async (req, res) => {
}); });
router.put('/api/athletes/:id', requireAuth, async (req, res) => { router.put('/api/athletes/:id', requireAuth, async (req, res) => {
const {id, user} = req.params const {name, _trainer} = req.body
await Athlete.findByIdAndUpdate(id, user) const updateAthlete = {name, _trainer}
if (name || _trainer)
await Athlete.findByIdAndUpdate(req.params.id, updateAthlete, {}, (err, athlete) => {
if (err)
return res.status(400).json({errors: 'Something went wrong!0'});
res.send(athlete)
})
}); });
router.delete('/api/athlete/:id', requireAuth, async (req, res) => { router.delete('/api/athlete/:id', requireAuth, async (req, res) => {

2
web/src/components/Navbar.vue

@ -22,7 +22,7 @@
<div id="navbarBasicExample" class="navbar-menu"> <div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-end"> <div class="navbar-end">
<a class="navbar-item"> <a class="navbar-item">
<router-link :to="{ name: 'Profile', params: { username: username }}"> <router-link :to="{ name: 'Profile', params: { username: user.username }}">
<span class="icon mr-1 has-text-white"> <span class="icon mr-1 has-text-white">
<i class="fa fa-user"></i> <i class="fa fa-user"></i>
</span> </span>

2
web/src/components/Sidebar.vue

@ -30,7 +30,7 @@
</router-link> </router-link>
</li> </li>
<li> <li>
<router-link :to="{ name: 'Profile', params: { username: $route.params.username }}"> <router-link :to="{ name: 'Athletes', params: { username: $route.params.username }}">
<a> <a>
<span class="icon-text"> <span class="icon-text">
<span class="icon"> <span class="icon">

16
web/src/router/index.ts

@ -1,7 +1,9 @@
import {createRouter, createWebHashHistory, RouteRecordRaw} from 'vue-router' import {createRouter, createWebHashHistory, RouteRecordRaw} from 'vue-router'
import store from '../store/index' import store from '../store/index'
import Home from '../views/Home.vue' import Home from '../views/Home.vue'
import Login from "@/views/Login.vue"; import Login from "@/views/Login.vue"
import Athletes from "@/views/Athletes.vue"
const routes: Array<RouteRecordRaw> = [ const routes: Array<RouteRecordRaw> = [
{ {
@ -28,7 +30,17 @@ const routes: Array<RouteRecordRaw> = [
path: 'profile', path: 'profile',
name: 'Profile', name: 'Profile',
component: () => import('../views/Profile.vue') component: () => import('../views/Profile.vue')
} },
{
path: 'athletes',
name: 'Athletes',
component: () => import('../views/Athletes.vue')
},
{
path: 'athletes/:id',
name: 'Athlete',
component: () => import('../views/Athlete.vue')
},
], ],
meta: { meta: {
requiresAuth: true requiresAuth: true

4
web/src/store/index.ts

@ -3,11 +3,13 @@ import createPersistedState from "vuex-persistedstate";
import user from './modules/user' import user from './modules/user'
import backend from './modules/backend' import backend from './modules/backend'
import athletes from './modules/athletes'
export default createStore({ export default createStore({
modules: { modules: {
user, user,
backend backend,
athletes,
}, },
plugins: [createPersistedState()] plugins: [createPersistedState()]
}) })

90
web/src/store/modules/athletes.ts

@ -0,0 +1,90 @@
import {Action, Module, Mutation, VuexModule} from 'vuex-module-decorators'
import axios, {AxiosResponse} from "axios";
import qs from 'qs';
import {UserInterface} from "@/store/modules/user";
export interface AthleteInterface {
_id: string,
id: string,
socketID: string,
name: string,
_trainer: UserInterface
}
@Module
export default class Athletes extends VuexModule {
private athlete = <AthleteInterface>{};
private trainer = <UserInterface>{};
private athletes = [<AthleteInterface>{}];
get currentAthlete() {
return this.athlete;
}
get currentTrainer() {
return this.trainer;
}
get currentAthletes() {
return this.athletes;
}
@Mutation
private save_athlete(athlete: AthleteInterface) {
this.athlete = athlete;
}
@Mutation save_trainer(trainer: UserInterface) {
this.trainer = trainer;
}
@Mutation
private api_athletes(athletes: [AthleteInterface]) {
this.athletes = athletes;
}
@Action
private getAthletes() {
return new Promise((resolve, reject) => {
axios({
method: 'GET',
url: '/api/athletes'
})
.then((resp: AxiosResponse) => {
this.context.commit('api_athletes', resp.data)
resolve(resp)
})
.catch((err: Error) => {
reject(err)
})
})
}
@Action
private updateAthlete(athlete: AthleteInterface) {
return new Promise((resolve, reject) => {
axios({
method: 'PUT',
url: `/api/athletes/${athlete._id}`,
data: qs.stringify({...athlete})
})
.then((resp: AxiosResponse) => {
this.context.commit('save_athlete', resp.data)
})
.catch((err: Error) => {
reject(err)
})
})
}
@Action
private saveTrainer(trainer: UserInterface) {
this.context.commit('save_trainer', trainer)
}
@Action
private saveAthlete(athlete: AthleteInterface) {
this.context.commit('save_athlete', athlete)
}
}

34
web/src/store/modules/user.ts

@ -13,15 +13,15 @@ export interface UserInterface {
@Module @Module
export default class User extends VuexModule { export default class User extends VuexModule {
private user = <UserInterface>{} private user = <UserInterface>{};
private userStatus = false private userStatus = false;
get currentUser() { get currentUser() {
return this.user return this.user;
} }
get isLoggedIn() { get isLoggedIn() {
return this.userStatus return this.userStatus;
} }
@Mutation @Mutation
@ -37,8 +37,8 @@ export default class User extends VuexModule {
@Mutation @Mutation
private auth_logout() { private auth_logout() {
this.user = <UserInterface>{} this.user = <UserInterface>{};
this.userStatus = false this.userStatus = false;
} }
@Action @Action
@ -68,7 +68,7 @@ export default class User extends VuexModule {
url: '/auth/logout' url: '/auth/logout'
}) })
.then((resp: AxiosResponse) => { .then((resp: AxiosResponse) => {
this.context.commit("auth_logout"); this.context.commit('auth_logout');
resolve(resp) resolve(resp)
}) })
.catch((err: Error) => { .catch((err: Error) => {
@ -85,7 +85,7 @@ export default class User extends VuexModule {
url: '/auth/current_user' url: '/auth/current_user'
}) })
.then((resp: AxiosResponse) => { .then((resp: AxiosResponse) => {
this.context.commit("auth_success", resp.data); this.context.commit('auth_success', resp.data);
resolve(resp) resolve(resp)
}) })
.catch((err: Error) => { .catch((err: Error) => {
@ -104,7 +104,23 @@ export default class User extends VuexModule {
data: qs.stringify({...user}) data: qs.stringify({...user})
}) })
.then((resp: AxiosResponse) => { .then((resp: AxiosResponse) => {
this.context.commit("auth_success", resp.data); this.context.commit('auth_success', resp.data);
resolve(resp)
})
.catch((err: Error) => {
reject(err)
})
})
}
@Action
private specificUser(user: UserInterface) {
return new Promise((resolve, reject) => {
axios({
method: 'GET',
url: `/api/user/${user._id}`
})
.then((resp: AxiosResponse) => {
resolve(resp) resolve(resp)
}) })
.catch((err: Error) => { .catch((err: Error) => {

125
web/src/views/Athlete.vue

@ -0,0 +1,125 @@
<template>
<h1 class="title is-2">Athletes/{{ athlete.name }}</h1>
<div class="tile is-ancestor">
<div class="tile is-6 is-vertical is-parent">
<form @submit="update">
<div class="tile is-child box">
<p class="title">Personal Details</p>
<div class="field">
<label class="label">Name</label>
<div class="control">
<input class="input" type="text" v-model="athlete.name">
</div>
</div>
<div class="field">
<label class="label">ID</label>
<p>{{ athlete.id }}</p>
</div>
<div class="field">
<label class="label">Client Status</label>
<p v-if="athlete.socketID">Online</p>
<p v-else>Offline</p>
</div>
<div class="notification is-danger has-text-centered mt-5" v-if="msgError">
<b>{{ msgError }}</b>
</div>
<div class="notification is-success has-text-centered mt-5" v-else-if="msgSuccess">
<b>{{ msgSuccess }}</b>
</div>
<div class="notification is-info is-light mt-5" v-else>
<ul>
<li>Client name is randomly generated on device connection. Change to athlete's name, so to be easily
identified.
</li>
<li>The ID is the mac of the client. It is persistent in the database and cannot change.</li>
<li>If the client has established a socket connection with the server he is considered online. In any
other case he is considered offline.
</li>
</ul>
</div>
<div class="field">
<p class="control">
<button class="button is-primary is-rounded" type="submit">
Update
</button>
</p>
</div>
</div>
</form>
</div>
<div class="tile is-parent">
<div class="tile is-child box">
<p class="title">Trainer Status</p>
<div class="notification is-warning is-light mt-5" v-if="!athlete._trainer">
<ul>
<p>It seems that this athlete has no trainer attached to him!</p>
<p>By adopting an athlete you can edit his personal details and view his performance stats.</p>
</ul>
<button class="button is-large is-rounded is-primary is-light" @click="update">update</button>
</div>
<div v-else>
<div class="field">
<label class="label">Username</label>
<p>{{ trainer.username }}</p>
</div>
<div class="field">
<label class="label">Email</label>
<p v-if="trainer.email">{{ trainer.email }}</p>
<p v-else>-</p>
</div>
<div class="field">
<label class="label">Last login</label>
<p>{{ trainerLogin }}</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import {Vue} from "vue-class-component";
import {AthleteInterface} from "@/store/modules/athletes";
import {UserInterface} from "@/store/modules/user";
export default class Athlete extends Vue {
private athlete = <AthleteInterface>{}
private trainer = <UserInterface>{}
private trainerLogin = '';
private msgError = ''
private msgSuccess = ''
mounted() {
this.athlete = this.$store.getters.currentAthlete
if (this.athlete._trainer && (this.athlete._trainer != this.$store.getters.currentUser._id)) {
this.$store.dispatch('specificUser', this.athlete._trainer)
.then((res: any) => this.trainer === res.data)
.catch((err: any) => this.msgError = err.response.data.errors.message || err.message || 'Something went wrong!')
} else if (this.athlete._trainer === this.$store.getters.currentUser._id) {
this.trainer = this.$store.getters.currentUser
}
if (this.trainer) {
this.trainerLogin = new Date(this.trainer.lastLogin).toLocaleString()
this.$store.dispatch('saveTrainer', this.trainer)
}
}
private update() {
const user = {_trainer: this.$store.getters.currentUser._id}
Object.assign(this.athlete, user)
this.$store.dispatch('updateAthlete', this.athlete)
.then((res: any) => {
console.log('updated')
this.msgSuccess = 'Athlete updated'
this.athlete = res.data;
})
.catch((err: any) => this.msgError = err.response.data.errors.message || err.message || 'Something went wrong!')
}
}
</script>
<style scoped>
</style>

75
web/src/views/Athletes.vue

@ -0,0 +1,75 @@
<template>
<h1 class="title is-2">Athletes</h1>
<div class="tile">
<table
class="table
is-striped
is-large
is-hoverable
is-fullwidth
has-text-left"
>
<thead>
<tr>
<th>Athlete id</th>
<th>Name</th>
<th>Trainer</th>
<th>Actions</th>
</tr>
</thead>
<tr v-for="(athlete, index) in athletes" :key="index">
<td class="is-family-monospace">
<span class="icon-text">
<span class="icon mr-1"><i class="fa fa-id-card"></i></span>
<span>{{ athlete.id }}</span>
</span>
</td>
<td>
<span class="icon-text">
<span class="icon mr-1"><i class="fa fa-signature"></i></span>
<span>{{ athlete.name }}</span>
</span>
</td>
<td v-if="athlete._trainer">
<span class="icon mr-1"><i class="fa fa-check"></i></span>
</td>
<td v-else>
<span class="icon mr-1"><i class="fa fa-times"></i></span>
</td>
<td>
<router-link :to="{ name: 'Athlete', params: { id: athlete._id }}" @click="saveAthlete(athlete)">
<span class="icon has-text-primary-dark">
<i class="fa fa-2x fa-user-edit"></i>
</span>
</router-link>
</td>
</tr>
</table>
</div>
</template>
<script lang="ts">
import {Vue} from "vue-class-component";
import {AthleteInterface} from "@/store/modules/athletes";
import {UserInterface} from "@/store/modules/user";
export default class Athletes extends Vue {
private athletes = [<AthleteInterface>{}]
private msg = ''
mounted() {
this.$store.dispatch('getAthletes')
.then((res: any) => this.athletes = res.data)
.catch((err: any) => this.msg = err.response.data.errors.message || err.message || 'Something went wrong!')
}
private saveAthlete(athlete: AthleteInterface) {
this.$store.dispatch('saveAthlete', athlete)
}
}
</script>
<style scoped>
</style>
Loading…
Cancel
Save