This commit is contained in:
MuslemRahimi 2025-03-04 19:10:39 +01:00
parent 94298ec33f
commit 946d144cc9
4 changed files with 162 additions and 97 deletions

View File

@ -3,6 +3,22 @@ import { redirect } from "@sveltejs/kit";
export const load = async ({ locals }) => { export const load = async ({ locals }) => {
const { pb } = locals; const { pb } = locals;
if (pb.authStore.isValid) { if (pb.authStore.isValid) {
redirect(303, "/"); throw redirect(303, "/");
} }
};
export const actions = {
reset: async ({ request, locals }) => {
const { pb } = locals;
const formData = await request.formData();
const email = formData.get("email");
if (!email) {
return { error: "Email is required" };
} }
await pb.collection("users").requestPasswordReset(email as string);
throw redirect(303, "/login");
}
};

View File

@ -1,29 +1,51 @@
<script lang="ts"> <script lang="ts">
import { toast } from "svelte-sonner"; import { toast } from "svelte-sonner";
import { enhance } from "$app/forms";
import { pb } from "$lib/pocketbase";
import { goto } from "$app/navigation";
import SEO from "$lib/components/SEO.svelte"; import SEO from "$lib/components/SEO.svelte";
let email: string = "";
let loading = false; let loading = false;
let email: String; let isClicked = false;
async function resetPassword(event) { const submitReset = () => {
event.preventDefault(); loading = true;
try { return async ({ result, update }) => {
await pb.collection("users").requestPasswordReset(email); switch (result.type) {
case "success":
toast.success("Password resetted. Check your emails!", { toast.success("Password resetted. Check your emails!", {
style: style:
"border-radius: 5px; background: #fff; color: #000; border-color: #4B5563; font-size: 15px;", "border-radius: 5px; background: #fff; color: #000; border-color: #4B5563; font-size: 15px;",
}); });
goto("/login"); await update();
} catch (err) { break;
toast.error(err, { case "redirect":
isClicked = true;
toast.success("Password resetted. Check your emails!", {
style: style:
"border-radius: 5px; background: #fff; color: #000; border-color: #4B5563; font-size: 15px;", "border-radius: 5px; background: #fff; color: #000; border-color: #4B5563; font-size: 15px;",
}); });
await update();
break;
case "failure":
toast.error("Invalid credentials", {
style:
"border-radius: 5px; background: #fff; color: #000; border-color: #4B5563; font-size: 15px;",
});
await update();
break;
case "error":
toast.error(result.error.message, {
style:
"border-radius: 5px; background: #fff; color: #000; border-color: #4B5563; font-size: 15px;",
});
break;
default:
await update();
} }
} loading = false;
};
};
</script> </script>
<SEO <SEO
@ -41,23 +63,39 @@
We'll send you an email with a link to reset your password. We'll send you an email with a link to reset your password.
</p> </p>
<form <form
on:submit={resetPassword} action="?/reset"
method="POST"
use:enhance={submitReset}
class="flex flex-col items-center space-y-2 w-5/6 sm:w-full pt-4" class="flex flex-col items-center space-y-2 w-5/6 sm:w-full pt-4"
> >
<input <input
class="input input-bordered w-full max-w-lg bg-[#242527] placeholder-gray-400 text-white whitespace-normal ring-2" name="email"
class="py-2.5 rounded w-full border border-gray-600 focus:outline-none max-w-lg bg-[#242527] placeholder-gray-400 text-white whitespace-normal"
type="email" type="email"
required={true} required
bind:value={email} bind:value={email}
autocomplete="off" autocomplete="off"
/> />
<div class="w-full max-w-lg pt-2"> <div class="w-full max-w-lg">
<div class="w-full max-w-lg pt-2 m-auto pb-5">
{#if !loading && !isClicked}
<button <button
type="submit" type="submit"
class="py-2.5 font-semibold rounded bg-white text-black w-full" class="cursor-pointer py-2.5 bg-[#fff] border-none sm:hover:bg-gray-300 transition duration-100 btn-md w-full rounded m-auto text-black font-semibold text-[1rem]"
> >
Request Password Reset <span> Request Password Reset</span>
</button> </button>
{:else}
<label
class="cursor-not-allowed btn bg-[#fff] opacity-[0.5] border border-gray-600 sm:hover:bg-gray-300 transition duration-100 btn-md w-full rounded-md m-auto text-black font-semibold text-[1rem]"
>
<div class="flex flex-row m-auto items-center">
<span class="loading loading-infinity"></span>
<span class=" ml-1.5">Resetting</span>
</div>
</label>
{/if}
</div>
</div> </div>
</form> </form>
</div> </div>

View File

@ -1,8 +1,51 @@
import { redirect, error } from "@sveltejs/kit"; import { redirect, error } from "@sveltejs/kit";
import { updatePasswordSchema } from "$lib/schemas";
import { fail } from "@sveltejs/kit";
import { z } from "zod";
export const load = async ({ locals }) => { export const load = async ({ locals }) => {
const { pb, user } = locals; const { pb } = locals;
if (!pb.authStore.isValid) { if (!pb.authStore.isValid) {
redirect(303, "/login"); redirect(303, "/login");
} }
} }
export const actions = {
updatePassword: async ({ request, locals }) => {
const { pb, user } = locals;
const formData = await request.formData();
const postData: Record<string, string> = {};
// Build a plain object from the form data.
for (const [key, value] of formData.entries()) {
if (typeof value === "string") {
postData[key] = value;
}
}
try {
// Validate form data using your Zod schema.
const cleanedData = updatePasswordSchema.parse(postData);
await pb.collection("users").update(user?.id, cleanedData);
return { success: true };
} catch (error) {
console.log(error)
if (error instanceof z.ZodError) {
// Map Zod errors to individual error messages.
const errors = {
errorOldPassword:
error.errors?.find((err) => err.path[0] === "oldPassword")?.message || "You're password is wrong",
errorPassword:
error.errors?.find((err) => err.path[0] === "password")?.message || "",
errorPasswordConfirm:
error.errors?.find((err) => err.path[0] === "passwordConfirm")?.message || "",
};
return fail(400, { errors, zodErrors: error.errors });
}
console.error("Unexpected error during password update:", error);
return fail(500, { error: "An unexpected error occurred" });
}
},
};

View File

@ -1,74 +1,36 @@
<script ts="lang"> <script lang="ts">
import { pb } from "$lib/pocketbase"; import { enhance } from "$app/forms";
import { toast } from "svelte-sonner"; import { toast } from "svelte-sonner";
import Input from "$lib/components/Input.svelte"; import Input from "$lib/components/Input.svelte";
import { updatePasswordSchema } from "$lib/schemas"; import SEO from "$lib/components/SEO.svelte";
import { goto } from "$app/navigation";
import { z } from "zod";
let zodErrors = [];
export let data; export let data;
export let form;
let errorOldPassword = "";
let errorPassword = "";
let errorPasswordConfirm = "";
let loading = false; let loading = false;
async function updatePassword(event) {
const submitUpdatePassword = () => {
loading = true; loading = true;
event.preventDefault(); // prevent the default form submission behavior return async ({ result, update }) => {
errorOldPassword = ""; if (result.success) {
errorPassword = "";
errorPasswordConfirm = "";
const formData = new FormData(event.target); // create a FormData object from the form
const postData = {};
for (const [key, value] of formData.entries()) {
postData[key] = value;
}
try {
// Use Zod validation
const cleanedData = updatePasswordSchema.parse(postData);
await pb.collection("users").update(data?.user?.id, cleanedData);
toast.success("Password updated!", { toast.success("Password updated!", {
style: "border-radius: 200px; background: #2A2E39; color: #fff;", style: "border-radius: 200px; background: #2A2E39; color: #fff;",
}); });
} catch (error) { } else if (result.error || result.errors) {
if (error instanceof z.ZodError) {
// Handle Zod validation errors
zodErrors = error.errors;
errorOldPassword =
zodErrors?.find((err) => err.path[0] === "oldPassword")?.message ??
"";
errorPassword =
zodErrors?.find((err) => err.path[0] === "password")?.message ?? "";
errorPasswordConfirm =
zodErrors?.find((err) => err.path[0] === "passwordConfirm")
?.message ?? "";
toast.error("Invalid credentials", { toast.error("Invalid credentials", {
style: style:
"border-radius: 5px; background: #fff; color: #000; border-color: #4B5563; font-size: 15px;", "border-radius: 5px; background: #fff; color: #000; border-color: #4B5563; font-size: 15px;",
}); });
} else {
// Handle other errors
console.error("Unexpected error during registration:", error);
toast.error("An unexpected error occurred", {
style:
"border-radius: 5px; background: #fff; color: #000; border-color: #4B5563; font-size: 15px;",
});
}
} }
await update();
loading = false; loading = false;
} };
};
</script> </script>
<SEO title="Update Password" description="Update your account password" />
<section <section
class="w-full max-w-3xl sm:max-w-[1400px] overflow-hidden min-h-screen pb-20 pt-5 px-4 lg:px-3" class="flex flex-col items-center min-h-screen w-full max-w-3xl m-auto"
> >
<div class="w-full overflow-hidden m-auto mt-5"> <div class="w-full overflow-hidden m-auto mt-5">
<div class="sm:p-0 flex justify-center w-full m-auto overflow-hidden"> <div class="sm:p-0 flex justify-center w-full m-auto overflow-hidden">
@ -77,43 +39,49 @@
> >
<main class="w-full"> <main class="w-full">
<form <form
on:submit={updatePassword} action="?/updatePassword"
method="POST"
use:enhance={submitUpdatePassword}
class="flex flex-col space-y-2 w-full max-w-lg m-auto" class="flex flex-col space-y-2 w-full max-w-lg m-auto"
> >
<h1 <h1
class="mb-1 text-white text-2xl sm:text-3xl font-bold mb-6 text-center" class="mb-1 text-white text-2xl sm:text-3xl font-bold mb-6 text-center"
> >
Set a new password Reset Your Password
</h1> </h1>
<Input <Input
id="oldPassword" id="oldPassword"
name="oldPassword"
label="Old Password" label="Old Password"
type="password" type="password"
required required
errors={errorOldPassword} errors={form?.errors?.errorOldPassword}
/> />
<Input <Input
id="password" id="password"
name="password"
label="New Password" label="New Password"
type="password" type="password"
required required
errors={errorPassword} errors={form?.errors?.errorPassword}
/> />
<Input <Input
id="passwordConfirm" id="passwordConfirm"
name="passwordConfirm"
label="Confirm New Password" label="Confirm New Password"
type="password" type="password"
required required
errors={errorPasswordConfirm} errors={form?.errors?.errorPasswordConfirm}
/> />
<div class="w-full max-w-lg pt-3"> <div class="w-full max-w-lg pt-3">
<button <button
type="submit" type="submit"
class="btn bg-[#fff] text-black font-semibold sm:hover:bg-gray-300 w-full max-w-lg normal-case text-md" class="py-2.5 cursor-pointer rounded bg-[#fff] text-black font-semibold sm:hover:bg-gray-300 w-full max-w-lg normal-case text-md"
>Update Password</button
> >
{loading ? "Updating..." : "Update Password"}
</button>
</div> </div>
</form> </form>
</main> </main>