Skip to content

Commit d72131b

Browse files
authored
Merge pull request #129 from codersforcauses/i45-password_reset_page
I45 password reset page
2 parents 02d0d9b + 23e2c64 commit d72131b

File tree

11 files changed

+250
-29
lines changed

11 files changed

+250
-29
lines changed

client/components/Auth/AuthSubmit.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
? 'text-darkgrey bg-lightgrey border-darkgrey'
77
: 'text-red bg-white border-red'
88
"
9-
class="place-self-center text-l font-bold w-24 h-8 rounded border border-solid my-5"
9+
class="place-self-center text-l font-bold h-8 px-2 rounded border border-solid my-5"
1010
:disabled="disabled"
1111
>
1212
<slot />

client/components/Auth/InputField.vue

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,22 +57,18 @@
5757
// ---------------------------------------
5858
import { ValidationProvider, extend } from 'vee-validate';
5959
import { required, email, min } from 'vee-validate/dist/rules';
60-
6160
extend('email', {
6261
...email,
6362
message: 'Invalid email address',
6463
});
65-
6664
extend('required', {
6765
...required,
6866
message: 'This field is required',
6967
});
70-
7168
extend('min', {
7269
...min,
7370
message: '{_field_} must have at least {length} characters',
7471
});
75-
7672
extend('password', {
7773
params: ['target'],
7874
validate(value, { target }) {
@@ -81,7 +77,6 @@ extend('password', {
8177
message: 'Password confirmation does not match',
8278
});
8379
// ---------------------------------------
84-
8580
export default {
8681
name: 'InputField',
8782
components: {

client/components/Input/ButtonElement.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@
5555
export default {
5656
name: 'ButtonElement',
5757
props: {
58+
submitType: {
59+
type: String,
60+
default: undefined,
61+
},
5862
to: {
5963
type: String,
6064
default: undefined,

client/layouts/auth.vue

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,20 @@
1919
export default {
2020
computed: {
2121
title() {
22-
const rawTitle = this.$route.name;
22+
const rawPath = this.$route.path;
23+
24+
// Remove trailing '/'
25+
let title =
26+
rawPath.charAt(rawPath.length - 1) === '/'
27+
? rawPath.substring(0, rawPath.length - 1)
28+
: rawPath;
29+
// Remove directories
30+
title = title.split('/').pop();
2331
// Make first letter uppercase
24-
return (
25-
(rawTitle.charAt(0).toUpperCase() + rawTitle.slice(1))
26-
// Change dashes to spaces
27-
.replace(/-/, ' ')
28-
);
32+
title = title.charAt(0).toUpperCase() + title.slice(1);
33+
// Change dashes to spaces
34+
title = title.replace(/-/, ' ');
35+
return title;
2936
},
3037
},
3138
};

client/layouts/default.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template>
2-
<div class="font-Montserrat">
3-
<NavBar class="sticky top-0" />
2+
<div>
3+
<NavBar class="sticky top-0 z-50" />
44
<Nuxt class="min-h-[72vh]" />
55
<PageFooter />
66
</div>

client/pages/login.vue

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
field-type="password"
2121
/>
2222
<NuxtLink
23-
to="/forgot-password"
23+
to="/reset-password"
2424
class="self-end text-xs font-semibold text-white underline underline-offset-2"
2525
>
2626
forgot password?
@@ -30,7 +30,7 @@
3030
Don't have an account?
3131
<NuxtLink
3232
to="/sign-up"
33-
class="text-blue font-semibold underline underline-offset-2"
33+
class="font-semibold underline text-blue underline-offset-2"
3434
>
3535
Sign up!
3636
</NuxtLink>
@@ -61,7 +61,10 @@ export default {
6161
await this.$auth
6262
.login({ data })
6363
.then((resp) => this.$auth.setUserToken(resp.data))
64-
.catch((error) => (this.errors = error.response.data));
64+
.catch((error) => {
65+
this.errors = error.response.data;
66+
console.log(error.response.data);
67+
});
6568
},
6669
},
6770
};

client/pages/reset-password.vue

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
<template>
2+
<AuthForm
3+
v-if="!emailSentSuccess && !$route.query.uid"
4+
v-slot="{ invalid }"
5+
error-header="Error sending reset link, please fix the following errors:"
6+
:errors="errors"
7+
@submit="sendResetLink"
8+
>
9+
<div class="mt-1 mb-3">
10+
<p class="text-center text-white">
11+
Please enter <wbr />your registered email.
12+
</p>
13+
</div>
14+
<div class="text-white" v-if="emailLoading">Sending email...</div>
15+
<InputField
16+
:id="emailField.id"
17+
:key="emailField.index"
18+
:field-name="emailField.name"
19+
:field-type="emailField.type"
20+
:field-options="emailField.options"
21+
:rules="emailField.rules"
22+
:inputvalue.sync="emailField.value"
23+
/>
24+
<AuthSubmit :disabled="invalid">Send Reset Link</AuthSubmit>
25+
</AuthForm>
26+
27+
<AuthForm
28+
v-else-if="emailSentSuccess"
29+
class="gap-5 text-2xl font-bold text-white place-items-center"
30+
>
31+
<h1 class="text-3xl">Reset Link Has Been Sent!</h1>
32+
<font-awesome-icon
33+
:icon="['fas', 'fa-face-smile-beam']"
34+
class="text-[10rem] my-10"
35+
/>
36+
<p class="text-center">
37+
Please check your spam folder if you cannot find the email.
38+
</p>
39+
</AuthForm>
40+
41+
<AuthForm
42+
v-else-if="!passwordResetSuccess"
43+
v-slot="{ invalid }"
44+
error-header="Error resetting password, please fix the following errors:"
45+
:errors="errors"
46+
@submit="resetPassword"
47+
>
48+
<div class="mt-1 mb-3 text-white">
49+
<p v-if="uid !== ''" class="text-center">
50+
Resetting password for {{ uid }}
51+
</p>
52+
<p class="text-center">Please enter <wbr />a new password.</p>
53+
</div>
54+
<InputField
55+
v-for="field in fields"
56+
:id="field.id"
57+
:key="field.index"
58+
:field-name="field.name"
59+
:field-type="field.type"
60+
:field-options="field.options"
61+
:rules="field.rules"
62+
:inputvalue.sync="field.value"
63+
/>
64+
<AuthSubmit :disabled="invalid">Reset Password</AuthSubmit>
65+
</AuthForm>
66+
67+
<AuthForm
68+
v-else
69+
class="gap-5 text-2xl font-bold text-white place-items-center"
70+
>
71+
<h1 class="text-3xl text-center">Congratulations!</h1>
72+
<p class="text-center">
73+
Your password has been reset. Now you <wbr />can login with your new
74+
password!
75+
</p>
76+
<font-awesome-icon
77+
:icon="['fas', 'fa-face-smile-beam']"
78+
class="text-[10rem] my-10"
79+
/>
80+
<p>
81+
Please proceed to the
82+
<NuxtLink to="/login" class="text-blue">login page</NuxtLink> to login!
83+
</p>
84+
</AuthForm>
85+
</template>
86+
87+
<script>
88+
let count = 0;
89+
90+
export default {
91+
name: 'ResetPassword',
92+
auth: false,
93+
layout: 'auth',
94+
95+
data: () => ({
96+
title: 'Reset Password',
97+
passwordResetSuccess: false,
98+
emailLoading: false,
99+
emailSentSuccess: false,
100+
uid: '',
101+
errors: [],
102+
103+
emailField: {
104+
name: 'Email',
105+
type: 'email',
106+
index: 0,
107+
id: 'email',
108+
rules: 'required|email',
109+
},
110+
111+
fields: [
112+
{
113+
name: 'Password',
114+
type: 'password',
115+
index: count++,
116+
id: 'password',
117+
rules: 'required|min:6',
118+
},
119+
{
120+
name: 'Confirm Password',
121+
type: 'password',
122+
index: count++,
123+
id: 'confirm',
124+
rules: 'required|password:@password',
125+
},
126+
],
127+
}),
128+
129+
head() {
130+
return {
131+
title: this.title,
132+
};
133+
},
134+
135+
async mounted() {
136+
this.resetPassword();
137+
},
138+
139+
methods: {
140+
async sendResetLink(e) {
141+
this.emailLoading = true;
142+
const elements = e.target.elements;
143+
const data = {
144+
email: elements.Email.value,
145+
};
146+
147+
const url = 'auth/reset/email/';
148+
await this.$axios
149+
.$post(url, data)
150+
.then((resp) => (this.emailSentSuccess = true))
151+
.catch((error) => {
152+
this.errors = error.response.data;
153+
this.emailLoading = false;
154+
});
155+
},
156+
157+
async resetPassword(e = null) {
158+
const rawUid = this.$route.query.uid;
159+
const rawToken = this.$route.query.token;
160+
161+
if (!rawUid) {
162+
return;
163+
}
164+
165+
const formattedUid =
166+
rawUid.charAt(rawUid.length - 1) === '/'
167+
? rawUid.substring(0, rawUid.length - 1)
168+
: rawUid;
169+
170+
const formattedToken =
171+
rawToken.charAt(rawToken.lengthrawToken - 1) === '/'
172+
? rawToken.substring(0, rawToken.length - 1)
173+
: rawToken;
174+
175+
const url =
176+
'auth/reset/' + `?token=${formattedToken}&uid=${formattedUid}/`;
177+
178+
try {
179+
this.uid = atob(formattedUid);
180+
} catch (err) {
181+
this.errors = { Errors: ['email UID is invalid'] };
182+
return;
183+
}
184+
185+
if (e !== null) {
186+
const elements = e.target.elements;
187+
const data = {
188+
password: elements.Password.value,
189+
};
190+
await this.$axios
191+
.$put(url, data)
192+
.then((resp) => (this.passwordResetSuccess = true))
193+
.catch((error) => {
194+
this.errors = error.response.data;
195+
console.log(error.response.data);
196+
});
197+
} else {
198+
await this.$axios.$get(url).catch((error) => {
199+
this.errors = error.response.data;
200+
console.log(error.response.data);
201+
});
202+
}
203+
},
204+
},
205+
};
206+
</script>

client/pages/sign-up.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
</AuthForm>
2525
<AuthForm
2626
v-else
27-
class="place-items-center gap-5 font-bold text-2xl text-white"
27+
class="gap-5 text-2xl font-bold text-white place-items-center"
2828
>
29-
<h1 class="text-3xl">Congratulations {{ name }}!</h1>
30-
<p>
29+
<h1 class="text-3xl text-center">Congratulations {{ name }}!</h1>
30+
<p class="text-center">
3131
Your new Elucidate account <wbr />
3232
has been created
3333
</p>

client/plugins/fontawesome.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ import {
66
} from '@fortawesome/vue-fontawesome';
77
import { fas } from '@fortawesome/free-solid-svg-icons';
88
import { fab } from '@fortawesome/free-brands-svg-icons';
9+
import { far } from '@fortawesome/free-regular-svg-icons';
10+
911
// This is important, we are going to let Nuxt.js worry about the CSS
1012
config.autoAddCss = false;
1113

1214
// You can add your icons directly in this plugin. See other examples for how you
1315
// can add other styles or just individual icons.
1416
library.add(fas);
1517
library.add(fab);
18+
library.add(far);
1619

1720
// Register the component globally
1821
Vue.component('FontAwesomeIcon', FontAwesomeIcon);

client/tailwind.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ module.exports = {
2121
aquablue2: '#AAF5EB75',
2222

2323
green: '#53DFCB',
24+
green1: '#81E6D9',
2425
green2: '#53DFCB75',
2526
green3: '#53DFCB50',
2627
green4: '#53DFCB25',

0 commit comments

Comments
 (0)