Compare commits

...

3 Commits

  1. 24
      server/routes/athletes.js
  2. 38
      server/routes/user.js
  3. 23
      server/schemas/joi.js
  4. 25
      web/src/store/modules/athletes.ts
  5. 1
      web/src/store/modules/user.ts
  6. 17
      web/src/views/Athlete.vue
  7. 11
      web/src/views/Athletes.vue
  8. 40
      web/src/views/Profile.vue

24
server/routes/athletes.js

@ -19,35 +19,29 @@ router.get('/api/athletes/:id',
celebrate(guid), celebrate(guid),
async (req, res) => { async (req, res) => {
const athlete = await Athlete.findById(req.params.id); const athlete = await Athlete.findById(req.params.id);
res.send(athlete) res.send(athlete);
});
router.get('/api/athletes/:id/edit',
requireAuth,
celebrate(guid),
async (req, res) => {
const athlete = await Athlete.findById(req.params.id)
res.send(athlete)
}); });
router.put('/api/athletes/:id', router.put('/api/athletes/:id',
requireAuth, requireAuth,
celebrate(athleteUpdateSchema, guid), celebrate(athleteUpdateSchema, guid),
async (req, res) => { async (req, res) => {
const {name, _trainer} = req.body const {name, _trainer} = req.body;
await Athlete.findByIdAndUpdate(req.params.id, {name, _trainer}, {}, (err, athlete) => {
await Athlete.findByIdAndUpdate(req.params.id, {name, _trainer}, {new: true}, (err, athlete) => {
if (err) if (err)
return res.status(400).json({errors: 'Something went wrong!'}); return res.status(400).json({errors: 'Something went wrong!'});
res.send(athlete) res.send(athlete);
})
}); });
}
);
router.delete('/api/athlete/:id', router.delete('/api/athletes/:id',
requireAuth, requireAuth,
celebrate(guid), celebrate(guid),
async (req, res) => { async (req, res) => {
await Athlete.findByIdAndDelete(req.params.id) await Athlete.findByIdAndDelete(req.params.id);
}); });
module.exports = router; module.exports = router;

38
server/routes/user.js

@ -8,29 +8,43 @@ const {requireAuth} = require('../middlewares/middleware');
const User = mongoose.model('User') const User = mongoose.model('User')
const {userUpdateSchema, guid} = require('../schemas/joi'); const {userUpdateSchema, guid} = require('../schemas/joi');
router.get('/api/user/:id',
requireAuth,
async (req, res) => {
const user = await User.findById(req.params.id);
user.password = '';
res.send(user);
});
router.put('/api/user/:id', router.put('/api/user/:id',
requireAuth, requireAuth,
celebrate(userUpdateSchema, guid), celebrate(userUpdateSchema, guid),
async (req, res) => { async (req, res) => {
const {username, email, password, newPassword} = req.body const {username, email, password, newPassword} = req.body
if (password && newPassword) {
bcrypt.compare(password, req.user.password, async (err, isMatch) => { bcrypt.compare(password, req.user.password, async (err, isMatch) => {
if (err) if (err)
return res.status(400).json({errors: 'Current password is wrong!'}); return res.status(400).json({errors: 'Password is wrong!'});
if (isMatch) { if (isMatch) {
const user = {username, email, newPassword} if (newPassword) {
await User.findByIdAndUpdate(req.params.id, user) await User.findByIdAndUpdate(req.params.id, {
res.send(req.user); username,
} email,
newPassword
}, {new: true}, (err, user) => {
req.user = user;
});
} else {
await User.findByIdAndUpdate(req.params.id, {
username,
email
}, {new: true}, (err, user) => {
req.user = user;
}); });
} else if (username || email) { }
const user = {username, email}
await User.findByIdAndUpdate(req.params.id, user)
res.send(req.user); res.send(req.user);
} }
}
);
}); });
module.exports = router; module.exports = router;

23
server/schemas/joi.js

@ -4,7 +4,7 @@ const guid = {
params: { params: {
userId: Joi.string().guid().required() userId: Joi.string().guid().required()
} }
} };
const userAuthSchema = { const userAuthSchema = {
body: { body: {
@ -17,15 +17,14 @@ const userUpdateSchema = {
body: { body: {
_id: Joi.string().required(), _id: Joi.string().required(),
username: Joi.string().required(), username: Joi.string().required(),
__v: Joi.number().integer(), registered: Joi.string().required(),
email: Joi.string().email(), lastLogin: Joi.string().required(),
registered: Joi.string(), __v: Joi.number().integer().required(),
lastLogin: Joi.string(), email: Joi.string().email().required(),
password: Joi.string().alphanum().allow(''), password: Joi.string().alphanum().required(),
newPassword: Joi.string().alphanum().allow(''), newPassword: Joi.string().alphanum().optional(),
}
} }
; };
const athleteUpdateSchema = { const athleteUpdateSchema = {
body: { body: {
@ -33,9 +32,9 @@ const athleteUpdateSchema = {
id: Joi.string().required(), id: Joi.string().required(),
socketID: Joi.string().required(), socketID: Joi.string().required(),
name: Joi.string().required(), name: Joi.string().required(),
__v: Joi.number().integer(), __v: Joi.number().integer().required(),
_trainer: Joi.string().allow('').default(''), _trainer: Joi.string().optional(),
}
} }
};
module.exports = {guid, userAuthSchema, userUpdateSchema, athleteUpdateSchema} module.exports = {guid, userAuthSchema, userUpdateSchema, athleteUpdateSchema}

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

@ -1,8 +1,6 @@
import {Action, Module, Mutation, VuexModule} from 'vuex-module-decorators' import {Action, Module, Mutation, VuexModule} from 'vuex-module-decorators'
import axios, {AxiosResponse} from "axios"; import axios, {AxiosResponse} from "axios";
import {UserInterface} from "@/store/modules/user";
export interface AthleteInterface { export interface AthleteInterface {
_id: string, _id: string,
id: string, id: string,
@ -14,7 +12,6 @@ export interface AthleteInterface {
@Module @Module
export default class Athletes extends VuexModule { export default class Athletes extends VuexModule {
private athlete = <AthleteInterface>{}; private athlete = <AthleteInterface>{};
private trainer = <UserInterface>{};
private athletes = [<AthleteInterface>{}]; private athletes = [<AthleteInterface>{}];
private err = <Error>{}; private err = <Error>{};
@ -22,22 +19,10 @@ export default class Athletes extends VuexModule {
return this.athlete; return this.athlete;
} }
get athlete_trainer() {
return this.trainer;
}
get athlete_currents() {
return this.athletes;
}
get athlete_err() { get athlete_err() {
return this.err; return this.err;
} }
@Mutation api_trainer(trainer: UserInterface) {
this.trainer = trainer;
}
@Mutation @Mutation
private api_athlete(athlete: AthleteInterface) { private api_athlete(athlete: AthleteInterface) {
this.athlete = athlete; this.athlete = athlete;
@ -51,7 +36,6 @@ export default class Athletes extends VuexModule {
@Mutation @Mutation
private athletes_logout() { private athletes_logout() {
this.athlete = <AthleteInterface>{}; this.athlete = <AthleteInterface>{};
this.trainer = <UserInterface>{};
this.athletes = [<AthleteInterface>{}]; this.athletes = [<AthleteInterface>{}];
} }
@ -61,11 +45,14 @@ export default class Athletes extends VuexModule {
} }
@Mutation @Mutation
private athlete_addTrainer(trainer: UserInterface) { private athlete_addTrainer(trainerId: string) {
this.trainer = trainer; this.athlete._trainer = trainerId;
this.athlete._trainer = this.trainer._id;
} }
@Mutation
private athlete_deleteTrainer() {
delete this.athlete['_trainer'];
}
@Action @Action
private athlete_getAll() { private athlete_getAll() {

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

@ -116,7 +116,6 @@ export default class User extends VuexModule {
}); });
} }
@Action @Action
private user_update() { private user_update() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

17
web/src/views/Athlete.vue

@ -30,12 +30,16 @@
<form @submit="athlete_update"> <form @submit="athlete_update">
<div class="tile is-child box"> <div class="tile is-child box">
<p class="title">Personal Details</p> <p class="title">Personal Details</p>
<div class="field"> <div v-if="useristrainer" class="field">
<label class="label">Name</label> <label class="label">Name</label>
<div class="control"> <div class="control">
<input v-model="athlete.name" class="input" type="text"> <input v-model="athlete.name" class="input" type="text">
</div> </div>
</div> </div>
<div v-else class="field">
<label class="label">Name</label>
<p>{{ athlete.name }}</p>
</div>
<div class="field"> <div class="field">
<label class="label">Client Status</label> <label class="label">Client Status</label>
<p v-if="athlete.socketID">Online</p> <p v-if="athlete.socketID">Online</p>
@ -58,7 +62,7 @@
</li> </li>
</ul> </ul>
</div> </div>
<div class="field"> <div v-if="useristrainer" class="field">
<p class="control"> <p class="control">
<button class="button is-primary is-rounded" type="submit"> <button class="button is-primary is-rounded" type="submit">
Update Update
@ -119,6 +123,7 @@ import {UserInterface} from "@/store/modules/user";
export default class Athlete extends Vue { export default class Athlete extends Vue {
private athlete = <AthleteInterface>{}; private athlete = <AthleteInterface>{};
private trainer = <UserInterface>{}; private trainer = <UserInterface>{};
private useristrainer = false;
private trainerLogin = ''; private trainerLogin = '';
private msgError = ''; private msgError = '';
private msgSuccess = ''; private msgSuccess = '';
@ -129,16 +134,16 @@ export default class Athlete extends Vue {
if (this.athlete._trainer && (this.athlete._trainer != this.$store.getters.user_current._id)) { if (this.athlete._trainer && (this.athlete._trainer != this.$store.getters.user_current._id)) {
this.$store.dispatch('user_getOne', this.athlete._trainer) this.$store.dispatch('user_getOne', this.athlete._trainer)
.then((res: any) => this.trainer === res.data) .then((res: any) => this.trainer = res.data)
.catch((err: any) => this.msgError = err.response.data.errors.message || err.message || .catch((err: any) => this.msgError = err.response.data.errors.message || err.message ||
'Something went wrong!'); 'Something went wrong!');
} else if (this.athlete._trainer === this.$store.getters.user_current._id) { } else if (this.athlete._trainer === this.$store.getters.user_current._id) {
this.trainer = this.$store.getters.user_current; this.trainer = this.$store.getters.user_current;
this.useristrainer = true;
} }
if (this.trainer) { if (this.trainer) {
this.trainerLogin = new Date(this.trainer.lastLogin).toLocaleString(); this.trainerLogin = new Date(this.trainer.lastLogin).toLocaleString();
this.$store.commit('api_trainer', this.trainer);
} }
} }
@ -148,16 +153,16 @@ export default class Athlete extends Vue {
return; return;
} }
this.$store.commit('athlete_addTrainer', this.$store.getters.user_current._id);
this.athlete_update(); this.athlete_update();
} }
private athlete_update() { private athlete_update() {
this.$store.commit('athlete_addTrainer', this.$store.getters.user_current);
this.$store.dispatch('athlete_update') this.$store.dispatch('athlete_update')
.then((res: any) => { .then((res: any) => {
this.msgSuccess = 'Athlete updated'; this.msgSuccess = 'Athlete updated';
this.athlete = res.data; this.athlete = res.data;
this.$router.go(0);
}) })
.catch(() => this.msgError = this.$store.getters.athlete_err.response.data.errors.message || .catch(() => this.msgError = this.$store.getters.athlete_err.response.data.errors.message ||
'Something went wrong!'); 'Something went wrong!');

11
web/src/views/Athletes.vue

@ -157,10 +157,13 @@ export default class Athletes extends Vue {
this.$store.commit('api_athlete', athlete); this.$store.commit('api_athlete', athlete);
} }
private athlete_delete(athlete: AthleteInterface, index: number) { private athlete_delete(index: number) {
athlete._trainer = undefined; this.$store.commit('athlete_deleteTrainer');
this.$store.dispatch('athlete_update', athlete) this.$store.dispatch('athlete_update')
.then(() => this.myAthletes.splice(index, 1)); .then(() => {
this.myAthletes.splice(index, 1);
this.$router.go(0);
});
} }
} }

40
web/src/views/Profile.vue

@ -1,5 +1,11 @@
<template> <template>
<h1 class="title is-2">/profile</h1> <h1 class="title is-2">/profile</h1>
<div v-if="msgError" class="notification is-danger has-text-centered mt-5">
<b>{{ msgError }}</b>
</div>
<div v-else-if="msgSuccess" class="notification is-success has-text-centered mt-5">
<b>{{ msgSuccess }}</b>
</div>
<form @submit.prevent="user_update"> <form @submit.prevent="user_update">
<div class="tile is-ancestor"> <div class="tile is-ancestor">
<div class="tile is-4 is-vertical is-parent"> <div class="tile is-4 is-vertical is-parent">
@ -26,24 +32,30 @@
</div> </div>
</div> </div>
</div> </div>
<div class="tile is-parent">
<div class="tile is-vertical is-parent">
<div class="tile is-child box"> <div class="tile is-child box">
<p class="title">Change Password</p> <p class="title">Change Password</p>
<div class="field"> <div class="field">
<p class="control has-icons-left"> <p class="control has-icons-left">
<input v-model="user.password" class="input" placeholder="Current Password" type="password"> <input v-model="user.newPassword" class="input" placeholder="New Password" type="password">
<span class="icon is-small is-left"><i class="fas fa-lock"></i></span> <span class="icon is-small is-left"><i class="fas fa-lock"></i></span>
</p> </p>
</div> </div>
<div class="field"> <div class="field">
<p class="control has-icons-left"> <p class="control has-icons-left">
<input v-model="user.newPassword" class="input" placeholder="New Password" type="password"> <input v-model="user.repeatNewPassword" class="input" placeholder="Confirm Password" type="password">
<span class="icon is-small is-left"><i class="fas fa-lock"></i></span> <span class="icon is-small is-left"><i class="fas fa-lock"></i></span>
</p> </p>
</div> </div>
</div>
<div class="tile is-child box">
<p class="title">Save Details</p>
<p class="subtitle">Enter your password to save!</p>
<div class="field"> <div class="field">
<p class="control has-icons-left"> <p class="control has-icons-left">
<input v-model="user.repeatNewPassword" class="input" placeholder="Confirm Password" type="password"> <input v-model="user.password" class="input" placeholder="Current Password" type="password">
<span class="icon is-small is-left"><i class="fas fa-lock"></i></span> <span class="icon is-small is-left"><i class="fas fa-lock"></i></span>
</p> </p>
</div> </div>
@ -54,21 +66,6 @@
</button> </button>
</p> </p>
</div> </div>
<div v-if="msgError" class="notification is-danger has-text-centered mt-5">
<b>{{ msgError }}</b>
</div>
<div v-else-if="msgSuccess" class="notification is-success has-text-centered mt-5">
<b>{{ msgSuccess }}</b>
</div>
<div v-else class="notification is-primary is-light mt-5">
<p>According to the traditional advicewhich is still gooda strong password:</p>
<ul>
<li>Has 12 Characters, Minimum</li>
<li>Includes Numbers, Symbols, Capital Letters, and Lower-Case Letters</li>
<li>Isnt a Dictionary Word or Combination of Dictionary Words</li>
<li>Doesn't Rely on Obvious Substitutions</li>
</ul>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -97,7 +94,10 @@ export default class Profile extends Vue {
} }
this.$store.dispatch('user_update') this.$store.dispatch('user_update')
.then(() => this.msgSuccess = 'User updated!') .then(() => {
this.msgError = '';
this.msgSuccess = 'User updated!';
})
.catch(() => this.msgError = this.$store.getters.user_err.response.data.errors.message || .catch(() => this.msgError = this.$store.getters.user_err.response.data.errors.message ||
'Something went wrong!'); 'Something went wrong!');
} }

Loading…
Cancel
Save