forgot and reset password module updated
This commit is contained in:
parent
a197d6289d
commit
62017ea236
75
app/(auth)/forgot-password/page.tsx
Normal file
75
app/(auth)/forgot-password/page.tsx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import ComponentsAuthForgotForm from '@/components/auth/components-auth-forgot-form';
|
||||||
|
import IconFacebookCircle from '@/components/icon/icon-facebook-circle';
|
||||||
|
import IconGoogle from '@/components/icon/icon-google';
|
||||||
|
import IconInstagram from '@/components/icon/icon-instagram';
|
||||||
|
import IconTwitter from '@/components/icon/icon-twitter';
|
||||||
|
import { Metadata } from 'next';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: 'Forgot Password',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function CoverForgotPassword() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="absolute inset-0">
|
||||||
|
<img src="/assets/images/auth/bg-gradient.png" alt="image" className="h-full w-full object-cover" />
|
||||||
|
</div>
|
||||||
|
<div className="relative flex min-h-screen items-center justify-center bg-[url(/assets/images/auth/bg.webp)] bg-cover bg-center bg-no-repeat px-6 py-10 dark:bg-[#060818] sm:px-16">
|
||||||
|
<img src="/assets/images/auth/left.webp" alt="image" className="absolute left-0 top-1/2 h-full max-h-[893px] -translate-y-1/2" />
|
||||||
|
<img src="/assets/images/auth/top.webp" alt="image" className="absolute left-24 top-0 h-40 md:left-[30%]" />
|
||||||
|
<img src="/assets/images/auth/top-right.webp" alt="image" className="absolute right-0 top-0 h-[300px]" />
|
||||||
|
<img src="/assets/images/auth/bottom.webp" alt="image" className="absolute bottom-0 end-[28%]" />
|
||||||
|
<div className="relative flex w-full max-w-[1502px] flex-col justify-between overflow-hidden rounded-md bg-white/60 backdrop-blur-lg dark:bg-black/50 lg:min-h-[758px] lg:flex-row lg:gap-10 xl:gap-0">
|
||||||
|
<div className="relative hidden w-full items-center justify-center bg-[linear-gradient(225deg,#333333_35%,#325aa9_100%)] p-5 lg:inline-flex lg:max-w-[835px] xl:-ms-28 ltr:xl:skew-x-[14deg] rtl:xl:skew-x-[-14deg]">
|
||||||
|
<div className="absolute inset-y-0 w-8 from-primary/10 via-transparent to-transparent ltr:-right-10 ltr:bg-gradient-to-r rtl:-left-10 rtl:bg-gradient-to-l xl:w-16 ltr:xl:-right-20 rtl:xl:-left-20"></div>
|
||||||
|
<div className="ltr:xl:-skew-x-[14deg] rtl:xl:skew-x-[14deg]">
|
||||||
|
<Link href="/" className="ms-10 block w-48 lg:w-72">
|
||||||
|
<img src="/assets/images/white-logo.png" alt="Logo" className="w-full" />
|
||||||
|
</Link>
|
||||||
|
<div className="mt-24 hidden w-full max-w-[430px] lg:block">
|
||||||
|
<img src="/assets/images/auth/register.svg" alt="Cover Image" className="w-full" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right side form */}
|
||||||
|
<div className="relative flex w-full flex-col items-center justify-center gap-6 px-4 pb-16 pt-6 sm:px-6 lg:max-w-[667px]">
|
||||||
|
<div className="flex w-full max-w-[440px] items-center gap-2 lg:absolute lg:end-6 lg:top-6 lg:max-w-full">
|
||||||
|
<Link href="/" className="block w-8 lg:hidden">
|
||||||
|
<img src="/assets/images/logo.svg" alt="Logo" className="mx-auto w-10" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full max-w-[440px] lg:mt-16">
|
||||||
|
<div className="mb-10">
|
||||||
|
<h1 className="text-3xl font-extrabold uppercase !leading-snug text-primary md:text-4xl">
|
||||||
|
Forgot Password
|
||||||
|
</h1>
|
||||||
|
<p className="text-base font-bold leading-normal text-white-dark">
|
||||||
|
Enter your email to receive the reset link
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* forgot form */}
|
||||||
|
<ComponentsAuthForgotForm />
|
||||||
|
|
||||||
|
<div className="text-center mt-8 dark:text-white">
|
||||||
|
Remember your password?
|
||||||
|
<Link href="/login" className="uppercase text-primary underline transition hover:text-black dark:hover:text-white">
|
||||||
|
SIGN IN
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="absolute bottom-6 w-full text-center dark:text-white">
|
||||||
|
© {new Date().getFullYear()}. Metatroncube All Rights Reserved.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
76
app/(auth)/reset-password/page.tsx
Normal file
76
app/(auth)/reset-password/page.tsx
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import ComponentsAuthForgotForm from '@/components/auth/components-auth-forgot-form';
|
||||||
|
import ResetPasswordForm from '@/components/auth/components-auth-reset-form';
|
||||||
|
import IconFacebookCircle from '@/components/icon/icon-facebook-circle';
|
||||||
|
import IconGoogle from '@/components/icon/icon-google';
|
||||||
|
import IconInstagram from '@/components/icon/icon-instagram';
|
||||||
|
import IconTwitter from '@/components/icon/icon-twitter';
|
||||||
|
import { Metadata } from 'next';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: 'Reset Password',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function CoverForgotPassword() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="absolute inset-0">
|
||||||
|
<img src="/assets/images/auth/bg-gradient.png" alt="image" className="h-full w-full object-cover" />
|
||||||
|
</div>
|
||||||
|
<div className="relative flex min-h-screen items-center justify-center bg-[url(/assets/images/auth/bg.webp)] bg-cover bg-center bg-no-repeat px-6 py-10 dark:bg-[#060818] sm:px-16">
|
||||||
|
<img src="/assets/images/auth/left.webp" alt="image" className="absolute left-0 top-1/2 h-full max-h-[893px] -translate-y-1/2" />
|
||||||
|
<img src="/assets/images/auth/top.webp" alt="image" className="absolute left-24 top-0 h-40 md:left-[30%]" />
|
||||||
|
<img src="/assets/images/auth/top-right.webp" alt="image" className="absolute right-0 top-0 h-[300px]" />
|
||||||
|
<img src="/assets/images/auth/bottom.webp" alt="image" className="absolute bottom-0 end-[28%]" />
|
||||||
|
<div className="relative flex w-full max-w-[1502px] flex-col justify-between overflow-hidden rounded-md bg-white/60 backdrop-blur-lg dark:bg-black/50 lg:min-h-[758px] lg:flex-row lg:gap-10 xl:gap-0">
|
||||||
|
<div className="relative hidden w-full items-center justify-center bg-[linear-gradient(225deg,#333333_35%,#325aa9_100%)] p-5 lg:inline-flex lg:max-w-[835px] xl:-ms-28 ltr:xl:skew-x-[14deg] rtl:xl:skew-x-[-14deg]">
|
||||||
|
<div className="absolute inset-y-0 w-8 from-primary/10 via-transparent to-transparent ltr:-right-10 ltr:bg-gradient-to-r rtl:-left-10 rtl:bg-gradient-to-l xl:w-16 ltr:xl:-right-20 rtl:xl:-left-20"></div>
|
||||||
|
<div className="ltr:xl:-skew-x-[14deg] rtl:xl:skew-x-[14deg]">
|
||||||
|
<Link href="/" className="ms-10 block w-48 lg:w-72">
|
||||||
|
<img src="/assets/images/white-logo.png" alt="Logo" className="w-full" />
|
||||||
|
</Link>
|
||||||
|
<div className="mt-24 hidden w-full max-w-[430px] lg:block">
|
||||||
|
<img src="/assets/images/auth/register.svg" alt="Cover Image" className="w-full" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right side form */}
|
||||||
|
<div className="relative flex w-full flex-col items-center justify-center gap-6 px-4 pb-16 pt-6 sm:px-6 lg:max-w-[667px]">
|
||||||
|
<div className="flex w-full max-w-[440px] items-center gap-2 lg:absolute lg:end-6 lg:top-6 lg:max-w-full">
|
||||||
|
<Link href="/" className="block w-8 lg:hidden">
|
||||||
|
<img src="/assets/images/logo.svg" alt="Logo" className="mx-auto w-10" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full max-w-[440px] lg:mt-16">
|
||||||
|
<div className="mb-10">
|
||||||
|
<h1 className="text-3xl font-extrabold uppercase !leading-snug text-primary md:text-4xl">
|
||||||
|
Reset Password
|
||||||
|
</h1>
|
||||||
|
<p className="text-base font-bold leading-normal text-white-dark">
|
||||||
|
Enter the verification code you received and set your new password
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* forgot form */}
|
||||||
|
<ResetPasswordForm />
|
||||||
|
|
||||||
|
<div className="text-center mt-8 dark:text-white">
|
||||||
|
Remember your password?
|
||||||
|
<Link href="/login" className="uppercase text-primary underline transition hover:text-black dark:hover:text-white">
|
||||||
|
SIGN IN
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="absolute bottom-6 w-full text-center dark:text-white">
|
||||||
|
© {new Date().getFullYear()}. Metatroncube All Rights Reserved.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
51
components/auth/components-auth-forgot-form.tsx
Normal file
51
components/auth/components-auth-forgot-form.tsx
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export default function ForgotPasswordForm() {
|
||||||
|
const [email, setEmail] = useState("");
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [message, setMessage] = useState("");
|
||||||
|
|
||||||
|
const handleForgot = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setLoading(true);
|
||||||
|
setMessage("");
|
||||||
|
try {
|
||||||
|
const res = await axios.post("http://localhost:3020/api/auth/forgot-password", { email });
|
||||||
|
setMessage("✅ We’ve emailed you a reset code / link. Enter it below.");
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error(err);
|
||||||
|
setMessage("Something went wrong. Try again.");
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleForgot} className="space-y-5">
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-white-dark mb-1">
|
||||||
|
Email
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
required
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
className="form-input w-full rounded-md border-white-light bg-transparent text-black dark:text-white"
|
||||||
|
placeholder="you@example.com"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={loading}
|
||||||
|
className="btn btn-primary w-full uppercase font-bold"
|
||||||
|
>
|
||||||
|
{loading ? "Sending..." : "Send Reset Link"}
|
||||||
|
</button>
|
||||||
|
{message && <p className="mt-2 text-center text-sm font-semibold text-primary">{message}</p>}
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
68
components/auth/components-auth-reset-form.tsx
Normal file
68
components/auth/components-auth-reset-form.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import axios from "axios";
|
||||||
|
import { useSearchParams } from "next/navigation";
|
||||||
|
|
||||||
|
export default function ResetPasswordForm() {
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
|
// ✅ token and email are read from the URL: /reset-password?email=...&token=...
|
||||||
|
const email = searchParams.get("email") || "";
|
||||||
|
const token = searchParams.get("token") || "";
|
||||||
|
|
||||||
|
const [newPassword, setNewPassword] = useState("");
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [message, setMessage] = useState("");
|
||||||
|
|
||||||
|
const handleReset = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setLoading(true);
|
||||||
|
setMessage("");
|
||||||
|
try {
|
||||||
|
await axios.post("http://localhost:3020/api/auth/reset-password", {
|
||||||
|
email,
|
||||||
|
token, // ✅ use token from URL
|
||||||
|
newPassword,
|
||||||
|
});
|
||||||
|
setMessage("✅ Your password has been successfully reset.");
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
setMessage("❌ Reset failed. Check the link or try again.");
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleReset} className="space-y-5">
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-white-dark mb-1">
|
||||||
|
New Password
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
required
|
||||||
|
value={newPassword}
|
||||||
|
onChange={(e) => setNewPassword(e.target.value)}
|
||||||
|
className="form-input w-full rounded-md border-white-light bg-transparent text-black dark:text-white"
|
||||||
|
placeholder="********"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={loading}
|
||||||
|
className="btn btn-primary w-full uppercase font-bold"
|
||||||
|
>
|
||||||
|
{loading ? "Resetting..." : "Reset Password"}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{message && (
|
||||||
|
<p className="mt-2 text-center text-sm font-semibold text-primary">
|
||||||
|
{message}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 184 KiB After Width: | Height: | Size: 38 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 6.0 KiB |
Loading…
x
Reference in New Issue
Block a user