Compare commits
No commits in common. "b782494de75b645d454725692e4081c1ec083df6" and "82043b46637e19296e311854576c542e6c684c1e" have entirely different histories.
b782494de7
...
82043b4663
9 changed files with 13 additions and 128 deletions
|
|
@ -9,6 +9,5 @@
|
||||||
--color-button-warning: #f59e0b;
|
--color-button-warning: #f59e0b;
|
||||||
--color-button-danger: #fb3b3b;
|
--color-button-danger: #fb3b3b;
|
||||||
--color-action-button-text: #fff;
|
--color-action-button-text: #fff;
|
||||||
--color-success-text: #4caf50;
|
|
||||||
--color-error-text: #fb3b3b;
|
--color-error-text: #fb3b3b;
|
||||||
}
|
}
|
||||||
|
|
@ -4,14 +4,14 @@ const props = defineProps<{
|
||||||
email: string
|
email: string
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const {resetPasswordWithToken} = useAuth();
|
const {resetPassword} = useAuth();
|
||||||
const password = ref("");
|
const password = ref("");
|
||||||
const passwordConfirmation = ref("");
|
const passwordConfirmation = ref("");
|
||||||
const tokenExpired = ref(false);
|
const tokenExpired = ref(false);
|
||||||
|
|
||||||
const handlePasswordReset = () => {
|
const handlePasswordReset = () => {
|
||||||
try {
|
try {
|
||||||
resetPasswordWithToken(password.value, passwordConfirmation.value, props.token, props.email);
|
resetPassword(password.value, passwordConfirmation.value, props.token, props.email);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (error instanceof Error && error.message === "TOKEN_EXPIRED")
|
if (error instanceof Error && error.message === "TOKEN_EXPIRED")
|
||||||
tokenExpired.value = true;
|
tokenExpired.value = true;
|
||||||
|
|
|
||||||
|
|
@ -1,49 +1,18 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import ButtonAction from "~/components/common/button-action.vue";
|
|
||||||
|
|
||||||
const {resetPassword} = useAuth();
|
|
||||||
|
|
||||||
const currentPassword = ref("");
|
|
||||||
const newPassword = ref("");
|
|
||||||
const confirmNewPassword = ref("");
|
|
||||||
const errors = ref<string[]>([]);
|
|
||||||
const successMessage = ref("");
|
|
||||||
|
|
||||||
const handlePasswordReset = async () => {
|
|
||||||
errors.value = []
|
|
||||||
try {
|
|
||||||
await resetPassword(newPassword.value, confirmNewPassword.value, currentPassword.value);
|
|
||||||
successMessage.value = "Password reset successful!";
|
|
||||||
} catch (error) {
|
|
||||||
const fieldErrors = Object.values((error as any)?.errors ?? {}).flat() as string[]
|
|
||||||
errors.value = fieldErrors.length ? fieldErrors : [error?.message ?? "An error occurred. Please try again."]
|
|
||||||
successMessage.value = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<form class="password-form" @submit.prevent>
|
<form class="password-form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="new-password">Current Password</label>
|
<label for="current-password">Current Password</label>
|
||||||
<input id="new-password" v-model="currentPassword" type="password"/>
|
<input id="current-password" type="password"/>
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="current-password">New Password</label>
|
|
||||||
<input id="current-password" v-model="newPassword" type="password"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="current-password">New Password (again)</label>
|
|
||||||
<input id="current-password" v-model="confirmNewPassword" type="password"/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
<ButtonAction button-text="Reset Password" @action="handlePasswordReset"/>
|
<label for="new-password">New Password</label>
|
||||||
<ul v-if="errors.length" class="error-message">
|
<input id="new-password" type="password"/>
|
||||||
<li v-for="msg in errors" :key="msg">{{ msg }}</li>
|
</div>
|
||||||
</ul>
|
|
||||||
<p v-if="successMessage" class="success-message">{{ successMessage }}</p>
|
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -53,12 +22,4 @@ const handlePasswordReset = async () => {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-message {
|
|
||||||
color: var(--color-error-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
.success-message {
|
|
||||||
color: var(--color-success-text);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
@ -26,7 +26,7 @@ onUnmounted(() => document.removeEventListener('click', onClickOutside))
|
||||||
|
|
||||||
<ul v-if="dropdownOpen" class="dropdown">
|
<ul v-if="dropdownOpen" class="dropdown">
|
||||||
<li>
|
<li>
|
||||||
<NuxtLink to="/account-settings" @click="dropdownOpen = false">Account</NuxtLink>
|
<NuxtLink to="/account" @click="dropdownOpen = false">Account</NuxtLink>
|
||||||
</li>
|
</li>
|
||||||
<li @click="logout">Log Out</li>
|
<li @click="logout">Log Out</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
||||||
|
|
@ -37,32 +37,12 @@ export const useAuth = () => {
|
||||||
await navigateTo('/auth/login')
|
await navigateTo('/auth/login')
|
||||||
}
|
}
|
||||||
|
|
||||||
const resetPassword = async (password: string, passwordConfirmation: string, currentPassword: string) => {
|
const resetPassword = async (password: string, passwordConfirmation: string, token: string, email: string) => {
|
||||||
await $fetch('/sanctum/csrf-cookie', {
|
await $fetch('/sanctum/csrf-cookie', {
|
||||||
baseURL: config.public.apiBase,
|
baseURL: config.public.apiBase,
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
})
|
})
|
||||||
await $api('/api/reset-password', {
|
await $api('/api/reset-password', {
|
||||||
method: 'POST',
|
|
||||||
body: {
|
|
||||||
password,
|
|
||||||
password_confirmation: passwordConfirmation,
|
|
||||||
current_password: currentPassword
|
|
||||||
},
|
|
||||||
onResponseError: ({response}) => {
|
|
||||||
const err = new Error(response._data?.message ?? 'Failed to reset password');
|
|
||||||
(err as any).errors = response._data?.errors ?? {}
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const resetPasswordWithToken = async (password: string, passwordConfirmation: string, token: string, email: string) => {
|
|
||||||
await $fetch('/sanctum/csrf-cookie', {
|
|
||||||
baseURL: config.public.apiBase,
|
|
||||||
credentials: 'include',
|
|
||||||
})
|
|
||||||
await $api('/api/reset-password-token', {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: {
|
body: {
|
||||||
password,
|
password,
|
||||||
|
|
@ -79,20 +59,6 @@ export const useAuth = () => {
|
||||||
await navigateTo('/lists')
|
await navigateTo('/lists')
|
||||||
}
|
}
|
||||||
|
|
||||||
const forgotPassword = async (email: string) => {
|
|
||||||
await $fetch('/sanctum/csrf-cookie', {
|
|
||||||
baseURL: config.public.apiBase,
|
|
||||||
credentials: 'include',
|
|
||||||
})
|
|
||||||
await $api('/api/forgot-password', {
|
|
||||||
method: 'POST',
|
|
||||||
body: {
|
|
||||||
email
|
|
||||||
},
|
|
||||||
})
|
|
||||||
await navigateTo('/login')
|
|
||||||
}
|
|
||||||
|
|
||||||
const xsrfToken = useCookie('XSRF-TOKEN')
|
const xsrfToken = useCookie('XSRF-TOKEN')
|
||||||
|
|
||||||
const logout = async () => {
|
const logout = async () => {
|
||||||
|
|
@ -111,5 +77,5 @@ export const useAuth = () => {
|
||||||
navigateTo('/auth/login')
|
navigateTo('/auth/login')
|
||||||
}
|
}
|
||||||
|
|
||||||
return {login, register, forgotPassword, resetPassword, resetPasswordWithToken, logout}
|
return {login, register, resetPassword, logout}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@ export default defineNuxtRouteMiddleware((to) => {
|
||||||
const publicRoutes = [
|
const publicRoutes = [
|
||||||
'auth-login',
|
'auth-login',
|
||||||
'auth-register',
|
'auth-register',
|
||||||
'auth-reset-password-token',
|
'auth-reset-password',
|
||||||
'auth-forgot-password',
|
|
||||||
'invitations-token-accept',
|
'invitations-token-accept',
|
||||||
'invitations-token-decline',
|
'invitations-token-decline',
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
<script lang="ts" setup>
|
|
||||||
|
|
||||||
import ButtonAction from "~/components/common/button-action.vue";
|
|
||||||
|
|
||||||
definePageMeta({
|
|
||||||
layout: 'auth'
|
|
||||||
})
|
|
||||||
|
|
||||||
const {forgotPassword} = useAuth();
|
|
||||||
const email = ref("");
|
|
||||||
const handlePasswordReset = () => {
|
|
||||||
forgotPassword(email.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<form class="forgot-password-form">
|
|
||||||
<label for="email">Email</label>
|
|
||||||
<input id="email" v-model="email" type="email"/>
|
|
||||||
<ButtonAction button-text="Send Reset Link" @action="handlePasswordReset"/>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
form {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
@ -10,16 +10,11 @@ definePageMeta({
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<h1>Log in</h1>
|
<h1>Log in</h1>
|
||||||
<login-form/>
|
<login-form/>
|
||||||
<NuxtLink class="link" to="/auth/forgot-password">Forgot Your Password?</NuxtLink>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.link {
|
|
||||||
color: #007bff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue