request password reset form

This commit is contained in:
Hayden 2024-04-28 11:42:37 -05:00
parent ef06e4fbb2
commit c83d178182
No known key found for this signature in database
GPG key ID: 17CF79474E257545
2 changed files with 72 additions and 8 deletions

View file

@ -1,5 +1,11 @@
import { BaseAPI, route } from "./base"; import { BaseAPI, route } from "./base";
import type { APISummary, LoginForm, TokenResponse, UserRegistration } from "./types/data-contracts"; import type {
APISummary,
LoginForm,
PasswordResetRequest,
TokenResponse,
UserRegistration,
} from "./types/data-contracts";
export type StatusResult = { export type StatusResult = {
health: boolean; health: boolean;
@ -27,4 +33,11 @@ export class PublicApi extends BaseAPI {
public register(body: UserRegistration) { public register(body: UserRegistration) {
return this.http.post<UserRegistration, TokenResponse>({ url: route("/users/register"), body }); return this.http.post<UserRegistration, TokenResponse>({ url: route("/users/register"), body });
} }
public resetPasseord(email: string) {
return this.http.post<PasswordResetRequest, void>({
url: route("/users/request-password-reset"),
body: { email },
});
}
} }

View file

@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { useRouteHash } from "@vueuse/router";
import MdiGithub from "~icons/mdi/github"; import MdiGithub from "~icons/mdi/github";
import MdiTwitter from "~icons/mdi/twitter"; import MdiTwitter from "~icons/mdi/twitter";
import MdiDiscord from "~icons/mdi/discord"; import MdiDiscord from "~icons/mdi/discord";
@ -9,6 +10,12 @@
import MdiArrowRight from "~icons/mdi/arrow-right"; import MdiArrowRight from "~icons/mdi/arrow-right";
import MdiLock from "~icons/mdi/lock"; import MdiLock from "~icons/mdi/lock";
enum PageForms {
Register = "register",
Login = "login",
ForgotPassword = "forgot-password",
}
useHead({ useHead({
title: "Homebox | Organize and Tag Your Stuff", title: "Homebox | Organize and Tag Your Stuff",
}); });
@ -30,6 +37,9 @@
const api = usePublicApi(); const api = usePublicApi();
const toast = useNotifier(); const toast = useNotifier();
const pageForm = useRouteHash(PageForms.Login);
const pageFormStr = computed(() => (pageForm.value[0] === "#" ? pageForm.value.slice(1) : pageForm.value));
const { data: status } = useAsyncData(async () => { const { data: status } = useAsyncData(async () => {
const { data } = await api.status(); const { data } = await api.status();
@ -92,12 +102,12 @@
toast.success("User registered"); toast.success("User registered");
loading.value = false; loading.value = false;
registerForm.value = false; pageForm.value = PageForms.Login;
} }
onMounted(() => { onMounted(() => {
if (groupToken.value !== "") { if (groupToken.value !== "") {
registerForm.value = true; pageForm.value = PageForms.Register;
} }
}); });
@ -120,7 +130,21 @@
loading.value = false; loading.value = false;
} }
const [registerForm, toggleLogin] = useToggle(); async function resetPassword() {
if (email.value === "") {
toast.error("Email is required");
return;
}
const resp = await api.resetPasseord(email.value);
if (resp.error) {
toast.error("Problem resetting password");
return;
}
toast.success("Password reset link sent to your email");
return await Promise.resolve();
}
</script> </script>
<template> <template>
@ -167,7 +191,7 @@
<div class="grid p-6 sm:place-items-center min-h-[50vh]"> <div class="grid p-6 sm:place-items-center min-h-[50vh]">
<div> <div>
<Transition name="slide-fade"> <Transition name="slide-fade">
<form v-if="registerForm" @submit.prevent="registerUser"> <form v-if="pageFormStr === PageForms.Register" @submit.prevent="registerUser">
<div class="card w-max-[500px] md:w-[500px] bg-base-100 shadow-xl"> <div class="card w-max-[500px] md:w-[500px] bg-base-100 shadow-xl">
<div class="card-body"> <div class="card-body">
<h2 class="card-title text-2xl align-center"> <h2 class="card-title text-2xl align-center">
@ -197,6 +221,30 @@
</div> </div>
</div> </div>
</form> </form>
<form v-else-if="pageFormStr === PageForms.ForgotPassword" @submit.prevent="resetPassword">
<div class="card w-max-[500px] md:w-[500px] bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title text-2xl align-center">
<MdiAccount class="mr-1 w-7 h-7" />
Reset Password
</h2>
<FormTextField v-model="email" label="Email" />
<p class="text-sm text-base-content/50">
If you have an account with us, we will send you a password reset link.
</p>
<div class="card-actions justify-end mt-4">
<button
type="submit"
class="btn btn-primary btn-block"
:class="loading ? 'loading' : ''"
:disabled="loading"
>
Reset Password
</button>
</div>
</div>
</div>
</form>
<form v-else @submit.prevent="login"> <form v-else @submit.prevent="login">
<div class="card w-max-[500px] md:w-[500px] bg-base-100 shadow-xl"> <div class="card w-max-[500px] md:w-[500px] bg-base-100 shadow-xl">
<div class="card-body"> <div class="card-body">
@ -232,19 +280,22 @@
<BaseButton <BaseButton
v-if="status && status.allowRegistration" v-if="status && status.allowRegistration"
class="btn-primary btn-wide" class="btn-primary btn-wide"
@click="() => toggleLogin()" :to="pageFormStr === PageForms.Register ? `#${PageForms.Login}` : `#${PageForms.Register}`"
> >
<template #icon> <template #icon>
<MdiAccountPlus v-if="!registerForm" class="w-5 h-5 swap-off" /> <MdiAccountPlus v-if="pageFormStr === PageForms.Register" class="w-5 h-5 swap-off" />
<MdiLogin v-else class="w-5 h-5 swap-off" /> <MdiLogin v-else class="w-5 h-5 swap-off" />
<MdiArrowRight class="w-5 h-5 swap-on" /> <MdiArrowRight class="w-5 h-5 swap-on" />
</template> </template>
{{ registerForm ? "Login" : "Register" }} {{ pageFormStr === PageForms.Register ? "Login" : "Register" }}
</BaseButton> </BaseButton>
<p v-else class="text-base-content italic text-sm inline-flex items-center gap-2"> <p v-else class="text-base-content italic text-sm inline-flex items-center gap-2">
<MdiLock class="w-4 h-4 inline-block" /> <MdiLock class="w-4 h-4 inline-block" />
Registration Disabled Registration Disabled
</p> </p>
<NuxtLink :to="`#${PageForms.ForgotPassword}`">
<p class="text-xs text-base-content/50 mt-2">Forgot your password?</p>
</NuxtLink>
</div> </div>
</div> </div>
</div> </div>