Skip to content

Commit 8ec0427

Browse files
committedFeb 11, 2022
feat: implement reset password view
1 parent d9f58ac commit 8ec0427

File tree

2 files changed

+165
-1
lines changed

2 files changed

+165
-1
lines changed
 

Diff for: ‎src/router/index.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import Messages from '@/views/Messages.vue'
1717
import PostSearch from '@/views/PostSearch.vue'
1818
import Join from '@/views/Join.vue'
1919
import ConfirmAccount from '@/views/ConfirmAccount.vue'
20+
import ResetPassword from '@/views/ResetPassword.vue'
2021
import Profile from '@/views/Profile.vue'
2122
import Forbidden from '@/views/layout/Forbidden.vue'
2223
import NotFound from '@/views/layout/NotFound.vue'
@@ -49,7 +50,14 @@ const routes = [
4950
props: true,
5051
meta: { requiresAuth: false, bodyClass: 'confirm' }
5152
},
52-
{
53+
{
54+
path: '/reset/:username/:token',
55+
name: 'ResetPassword',
56+
component: ResetPassword,
57+
props: true,
58+
meta: { requiresAuth: false, bodyClass: 'reset' }
59+
},
60+
{
5361
path: '/join',
5462
name: 'Join',
5563
component: Join,

Diff for: ‎src/views/ResetPassword.vue

+156
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
<template>
2+
<div class="reset-section">
3+
<!-- Reset Password -->
4+
<h4 class="fill-row thin-underline">Reset Password</h4>
5+
6+
<div class="fill-row">
7+
<form name="resetPassword" class="css-form">
8+
<div class="input-section">
9+
<label for="password">
10+
Password
11+
<div v-if="form.password.val && form.password.val.length > 0 && form.password.val.length < 8 && !form.password.valid" class="invalid input-validation-message">
12+
Password must be at least 8 characters
13+
</div>
14+
<div v-if="form.password.val && form.password.val.length >= 8 && form.confirmation.val && form.confirmation.val.length >= 8 && (!form.password.valid || !form.confirmation.valid)" class="invalid input-validation-message">
15+
Password and confirmation do not match
16+
</div>
17+
</label>
18+
19+
<input type="password" :disabled="tokenExpired" ref="focusInput" :class="{'invalid-mismatch': form.password.val && (form.password.val !== form.confirmation.val || !form.password.valid) }" class="icon-padding" id="password" name="password" v-model="form.password.val" placeholder="Enter a password" required />
20+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" v-if="(form.password.val && form.password.val.length > -1 && !(form.confirmation.val && form.confirmation.val.length > -1) && form.password.valid) || (form.password.val && form.password.val.length > -1 && form.confirmation.val && form.confirmation.val.length > -1 && form.password.val === form.confirmation.val && form.password.valid)" class="input-icon valid">
21+
<title></title>
22+
<polygon class="cls-1" points="19.69 37.19 7.23 24.73 10.77 21.2 19.69 30.12 37.23 12.58 40.77 16.11 19.69 37.19"/>
23+
</svg>
24+
<svg enable-background="new 0 0 16 16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" v-if="(form.password.val && form.password.val.length > -1 && !form.password.valid) || (form.password.val && form.password.val.length > -1 && form.confirmation.val && form.confirmation.val.length > -1 && form.password.val !== form.confirmation.val)" class="input-icon invalid">
25+
<path clip-rule="evenodd" d="m12.7 5.2-1.2-1.2-3.1 3.2-3.2-3.2-1.2 1.2 3.2 3.2-3.2 3.1 1.2 1.2 3.2-3.2 3.1 3.2 1.2-1.2-3.2-3.1z" fill-rule="evenodd"/>
26+
</svg>
27+
</div>
28+
29+
<div class="input-section">
30+
<label for="confirmation">
31+
Confirm Password
32+
<div v-if="form.confirmation.val && form.confirmation.val.length > 0 && form.confirmation.val.length < 8 && !form.confirmation.valid" class="invalid input-validation-message">
33+
Confirmation must be at least 8 characters
34+
</div>
35+
<div v-if="form.password.val && form.password.val.length >= 8 && form.confirmation.val && form.confirmation.val.length >= 8 && (!form.password.valid || !form.confirmation.valid)" class="invalid input-validation-message">
36+
Password and confirmation do not match
37+
</div>
38+
</label>
39+
40+
<input type="password" :disabled="tokenExpired" :class="{'invalid-mismatch': form.confirmation.val && (form.password.val !== form.confirmation.val || !form.confirmation.valid) }" class="icon-padding" name="confirmation" id="confirmation" v-model="form.confirmation.val" placeholder="Enter your password again" required />
41+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" v-if="(form.confirmation.val && form.confirmation.val.length > -1 && !(form.password.val && form.password.val.length > -1) && form.confirmation.valid) || (form.password.val && form.password.val.length > -1 && form.confirmation.val && form.confirmation.val.length > -1 && form.password.val === form.confirmation.val && form.confirmation.valid)" class="input-icon valid">
42+
<title></title>
43+
<polygon class="cls-1" points="19.69 37.19 7.23 24.73 10.77 21.2 19.69 30.12 37.23 12.58 40.77 16.11 19.69 37.19"/>
44+
</svg>
45+
<svg enable-background="new 0 0 16 16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" v-if="(form.confirmation.val && form.confirmation.val.length > -1 && !form.confirmation.valid) || (form.password.val && form.password.val.length > -1 && form.confirmation.val && form.confirmation.val.length > -1 && form.password.val !== form.confirmation.val)" class="input-icon invalid">
46+
<path clip-rule="evenodd" d="m12.7 5.2-1.2-1.2-3.1 3.2-3.2-3.2-1.2 1.2 3.2 3.2-3.2 3.1 1.2 1.2 3.2-3.2 3.1 3.2 1.2-1.2-3.2-3.1z" fill-rule="evenodd"/>
47+
</svg>
48+
</div>
49+
<button @click.prevent="resetPassword()" :disabled="!form.valid">
50+
Reset Password
51+
</button>
52+
</form>
53+
</div>
54+
55+
</div>
56+
</template>
57+
58+
<script>
59+
import { reactive, toRefs, watch, inject } from 'vue'
60+
import { useRoute, useRouter } from 'vue-router'
61+
import { cloneDeep } from 'lodash'
62+
import { AuthStore } from '@/composables/stores/auth'
63+
import { usersApi } from '@/api'
64+
65+
export default {
66+
name: 'ResetPassword',
67+
setup() {
68+
/* Internal Methods */
69+
const checkFormValid = () => v.form.valid = v.form.password.valid && v.form.confirmation.valid
70+
71+
/* Template Methods */
72+
const resetPassword = () => $auth.resetPassword($route.params.username, $route.params.token, v.form.password.val)
73+
.then(() => $router.push('/'))
74+
75+
/* Internal Data */
76+
const $auth = inject(AuthStore)
77+
const $route = useRoute()
78+
const $router = useRouter()
79+
const $alertStore = inject('$alertStore')
80+
81+
/* Template Data */
82+
const initForm = {
83+
valid: false,
84+
password: { val: undefined, valid: false },
85+
confirmation:{ val: undefined, valid: false }
86+
}
87+
88+
const v = reactive({
89+
form: cloneDeep(initForm),
90+
loggedIn: $auth.loggedIn,
91+
focusInput: null,
92+
tokenExpired: false
93+
})
94+
95+
if (v.loggedIn) $auth.logout() // If user is currently logged in for some reason, log them out
96+
97+
// Check token, redirect if invalid or expired
98+
usersApi.checkResetToken($route.params.username, $route.params.token)
99+
.then(data => {
100+
v.tokenExpired = data.token_expired
101+
if (!data.token_valid) $router.push('/')
102+
if (v.tokenExpired) {
103+
$alertStore.warn('Your reset password token has expired, you will be redirected shortly.')
104+
setTimeout(() => $router.push('/'), 4000)
105+
}
106+
})
107+
108+
/* Watch Data */
109+
watch(() => v.focusInput, f => f ? v.focusInput.focus() : null)
110+
111+
watch(() => v.form.password.val, (val) => {
112+
v.form.password.valid = val && val.length >= 8 && val.length <= 72
113+
if (v.form.password.valid && v.form.password.val && v.form.confirmation.val && v.form.password.val !== v.form.confirmation.val) {
114+
v.form.password.valid = false
115+
v.form.confirmation.valid = false
116+
}
117+
else if (v.form.password.valid && v.form.password.val && v.form.confirmation.val && v.form.password.val === v.form.confirmation.val) {
118+
v.form.password.valid = true
119+
v.form.confirmation.valid = true
120+
}
121+
checkFormValid()
122+
})
123+
124+
watch(() => v.form.confirmation.val, (val) => {
125+
v.form.confirmation.valid = val && val.length >= 8 && val.length <= 72
126+
if (v.form.confirmation.valid && v.form.password.val && v.form.confirmation.val && v.form.password.val !== v.form.confirmation.val) {
127+
v.form.password.valid = false
128+
v.form.confirmation.valid = false
129+
}
130+
else if (v.form.confirmation.valid && v.form.password.val && v.form.confirmation.val && v.form.password.val === v.form.confirmation.val) {
131+
v.form.password.valid = true
132+
v.form.confirmation.valid = true
133+
}
134+
checkFormValid()
135+
})
136+
137+
return { ...toRefs(v), resetPassword }
138+
}
139+
}
140+
</script>
141+
142+
<style lang="scss">
143+
.reset {
144+
main #public-content { grid-template-areas: 'header header' 'main sidebar' 'main sidebar'; }
145+
.reset-section {
146+
grid-area: main;
147+
.view-title {
148+
color: $secondary-font-color;
149+
font-size: $font-size-xl;
150+
font-weight: 600;
151+
text-transform: none;
152+
}
153+
.form { margin: rem 0 1rem; }
154+
}
155+
}
156+
</style>

0 commit comments

Comments
 (0)
Please sign in to comment.