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),
async (req, res) => {
const athlete = await Athlete.findById(req.params.id);
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)
res.send(athlete);
});
router.put('/api/athletes/:id',
requireAuth,
celebrate(athleteUpdateSchema, guid),
async (req, res) => {
const {name, _trainer} = req.body
await Athlete.findByIdAndUpdate(req.params.id, {name, _trainer}, {}, (err, athlete) => {
const {name, _trainer} = req.body;
await Athlete.findByIdAndUpdate(req.params.id, {name, _trainer}, {new: true}, (err, athlete) => {
if (err)
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,
celebrate(guid),
async (req, res) => {
await Athlete.findByIdAndDelete(req.params.id)
await Athlete.findByIdAndDelete(req.params.id);
});
module.exports = router;

38
server/routes/user.js

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

23
server/schemas/joi.js

@ -4,7 +4,7 @@ const guid = {
params: {
userId: Joi.string().guid().required()
}
}
};
const userAuthSchema = {
body: {
@ -17,15 +17,14 @@ const userUpdateSchema = {
body: {
_id: Joi.string().required(),
username: Joi.string().required(),
__v: Joi.number().integer(),
email: Joi.string().email(),
registered: Joi.string(),
lastLogin: Joi.string(),
password: Joi.string().alphanum().allow(''),
newPassword: Joi.string().alphanum().allow(''),
}
registered: Joi.string().required(),
lastLogin: Joi.string().required(),
__v: Joi.number().integer().required(),
email: Joi.string().email().required(),
password: Joi.string().alphanum().required(),
newPassword: Joi.string().alphanum().optional(),
}
;
};
const athleteUpdateSchema = {
body: {
@ -33,9 +32,9 @@ const athleteUpdateSchema = {
id: Joi.string().required(),
socketID: Joi.string().required(),
name: Joi.string().required(),
__v: Joi.number().integer(),
_trainer: Joi.string().allow('').default(''),
__v: Joi.number().integer().required(),
_trainer: Joi.string().optional(),
}
}
};
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 axios, {AxiosResponse} from "axios";
import {UserInterface} from "@/store/modules/user";
export interface AthleteInterface {
_id: string,
id: string,
@ -14,7 +12,6 @@ export interface AthleteInterface {
@Module
export default class Athletes extends VuexModule {
private athlete = <AthleteInterface>{};
private trainer = <UserInterface>{};
private athletes = [<AthleteInterface>{}];
private err = <Error>{};
@ -22,22 +19,10 @@ export default class Athletes extends VuexModule {
return this.athlete;
}
get athlete_trainer() {
return this.trainer;
}
get athlete_currents() {
return this.athletes;
}
get athlete_err() {
return this.err;
}
@Mutation api_trainer(trainer: UserInterface) {
this.trainer = trainer;
}
@Mutation
private api_athlete(athlete: AthleteInterface) {
this.athlete = athlete;
@ -51,7 +36,6 @@ export default class Athletes extends VuexModule {
@Mutation
private athletes_logout() {
this.athlete = <AthleteInterface>{};
this.trainer = <UserInterface>{};
this.athletes = [<AthleteInterface>{}];
}
@ -61,11 +45,14 @@ export default class Athletes extends VuexModule {
}
@Mutation
private athlete_addTrainer(trainer: UserInterface) {
this.trainer = trainer;
this.athlete._trainer = this.trainer._id;
private athlete_addTrainer(trainerId: string) {
this.athlete._trainer = trainerId;
}
@Mutation
private athlete_deleteTrainer() {
delete this.athlete['_trainer'];
}
@Action
private athlete_getAll() {

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

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

17
web/src/views/Athlete.vue

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

40
web/src/views/Profile.vue

@ -1,5 +1,11 @@
<template>
<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">
<div class="tile is-ancestor">
<div class="tile is-4 is-vertical is-parent">
@ -26,24 +32,30 @@
</div>
</div>
</div>
<div class="tile is-parent">
<div class="tile is-vertical is-parent">
<div class="tile is-child box">
<p class="title">Change Password</p>
<div class="field">
<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>
</p>
</div>
<div class="field">
<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>
</p>
</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">
<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>
</p>
</div>
@ -54,21 +66,6 @@
</button>
</p>
</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>
@ -97,7 +94,10 @@ export default class Profile extends Vue {
}
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 ||
'Something went wrong!');
}

Loading…
Cancel
Save