diff --git a/app/forgot-password/page.tsx b/app/forgot-password/page.tsx index dfa00e92..806b9e1b 100644 --- a/app/forgot-password/page.tsx +++ b/app/forgot-password/page.tsx @@ -1,7 +1,74 @@ "use client"; import React, { useState } from "react"; +import Link from "next/link"; +import MessagePage from "../ui/layout/message-page"; +import { useRouter } from "next/navigation"; export default function Page() { - return
FORGOTPASSWORD
; + const [email, setEmail] = useState(""); + const [emailSent, setEmailSent] = useState(false); + const router = useRouter(); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + if (!email) { + alert("Please enter an email address."); + return; + } + setEmailSent(true); + }; + + return ( +
+ {emailSent ? ( + router.push("/login"), + }, + ]} + /> + ) : ( +
+ {/* Title */} +

+ reset password +

+ + {/* Email */} + setEmail(e.target.value)} + className="mb-4 w-full rounded-full border px-4 py-2 focus:ring-2 focus:outline-none" + /> + +
+ {/* Forgot Password */} + + Remembered password? + + + {/* Email Button */} + +
+
+ )} +
+ ); } diff --git a/app/login/page.tsx b/app/login/page.tsx index 8df17f96..9fb51139 100644 --- a/app/login/page.tsx +++ b/app/login/page.tsx @@ -31,7 +31,7 @@ export default function Page() { {/* Email */} setEmail(e.target.value)} diff --git a/app/reset-password/page.tsx b/app/reset-password/page.tsx new file mode 100644 index 00000000..15e94f87 --- /dev/null +++ b/app/reset-password/page.tsx @@ -0,0 +1,73 @@ +"use client"; + +import { useRouter, useSearchParams } from "next/navigation"; +import React, { useState } from "react"; + +export default function Page() { + const [newPassword, setNewPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const router = useRouter(); + + const searchParams = useSearchParams(); + const pwdResetToken = searchParams.get("token"); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + if (!pwdResetToken) { + alert("Invalid or missing password reset token."); + return; + } + + if (!newPassword || !confirmPassword) { + alert("Please fill in all fields."); + return; + } + + // TODO: Replace with real password reset API call + if (newPassword !== confirmPassword) { + alert("Passwords do not match."); + } else { + router.push("/reset-password/success"); + } + }; + + return ( +
+
+ {/* Title */} +

+ reset password +

+ + {/* New Password */} + setNewPassword(e.target.value)} + className="mb-4 w-full rounded-full border px-4 py-2 focus:ring-2 focus:outline-none" + /> + + {/* Confirm Password */} + setConfirmPassword(e.target.value)} + className="mb-4 w-full rounded-full border px-4 py-2 focus:ring-2 focus:outline-none" + /> + + {/* Change Password Button */} +
+ +
+
+
+ ); +} diff --git a/app/reset-password/success/page.tsx b/app/reset-password/success/page.tsx new file mode 100644 index 00000000..cab6c194 --- /dev/null +++ b/app/reset-password/success/page.tsx @@ -0,0 +1,23 @@ +"use client"; + +import { useRouter } from "next/navigation"; +import MessagePage from "../../ui/layout/message-page"; + +export default function Page() { + const router = useRouter(); + + return ( +
+ router.push("/login"), + }, + ]} + /> +
+ ); +} diff --git a/app/sign-up/email-sent/page.tsx b/app/sign-up/email-sent/page.tsx new file mode 100644 index 00000000..8f892600 --- /dev/null +++ b/app/sign-up/email-sent/page.tsx @@ -0,0 +1,60 @@ +"use client"; + +import { useRouter } from "next/navigation"; +import { useEffect, useRef } from "react"; +import MessagePage from "../../ui/layout/message-page"; + +export default function Page() { + const router = useRouter(); + const email = sessionStorage.getItem("sign_up_email"); + const lastEmailResend = useRef(Date.now()); + + useEffect(() => { + if (!email) { + // the user shouldn't be here + router.push("/login"); + } + // clear the email from storage + sessionStorage.removeItem("sign_up_email"); + }, []); + + if (!email) { + // stop rendering if there's no email + return null; + } + + const handleResendEmail = () => { + const emailResendCooldown = 30000; // 30 seconds + let timeLeft = + (emailResendCooldown - (Date.now() - lastEmailResend.current)) / 1000; + timeLeft = Math.ceil(timeLeft); + if (timeLeft > 0) { + alert(`Slow down! ${timeLeft} seconds until you can send again.`); + return; + } + // TODO: Replace with real resend email API logic + console.log("Resending email to:", email); + lastEmailResend.current = Date.now(); + }; + + return ( +
+ router.push("/login"), + }, + ]} + /> +
+ ); +} diff --git a/app/sign-up/page.tsx b/app/sign-up/page.tsx index b0a46a96..9de47d29 100644 --- a/app/sign-up/page.tsx +++ b/app/sign-up/page.tsx @@ -1,8 +1,8 @@ "use client"; -import React, { useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; +import React, { useState } from "react"; export default function Page() { const [email, setEmail] = useState(""); @@ -18,13 +18,12 @@ export default function Page() { confirmPassword, }); - // TODO: Replace with real sign up information - if ( - email === "test" && - password === "1234" && - confirmPassword === password - ) { - router.push("/dashboard"); + // TODO: Replace with real sign up API logic + if (email && password && confirmPassword === password) { + // add the email to session storage to have it in the email-sent page without + // putting it in the URL + sessionStorage.setItem("sign_up_email", email); + router.push("/sign-up/email-sent"); } else { alert("WOMP WOMP NO ACCOUNT FOR YOU"); } @@ -40,7 +39,7 @@ export default function Page() { {/* Email */} setEmail(e.target.value)} diff --git a/app/ui/layout/message-page.tsx b/app/ui/layout/message-page.tsx new file mode 100644 index 00000000..30f54dc2 --- /dev/null +++ b/app/ui/layout/message-page.tsx @@ -0,0 +1,42 @@ +enum ButtonTypeEnum { + primary = "primary", + secondary = "secondary", +} +type ButtonType = keyof typeof ButtonTypeEnum; + +type ButtonData = { type: ButtonType; label: string; onClick: () => void }; + +type MessagePageProps = { + title: string; + description?: string; + buttons: ButtonData[]; +}; + +export default function MessagePage({ + title, + description, + buttons, +}: MessagePageProps) { + return ( +
+

{title}

+ {description &&

{description}

} +
+ {buttons.map((button, index) => ( + + ))} +
+
+ ); +} diff --git a/app/verify-email/page.tsx b/app/verify-email/page.tsx new file mode 100644 index 00000000..2009f810 --- /dev/null +++ b/app/verify-email/page.tsx @@ -0,0 +1,67 @@ +"use client"; + +import { useRouter, useSearchParams } from "next/navigation"; +import { useEffect, useState } from "react"; +import MessagePage from "../ui/layout/message-page"; + +export default function Page() { + const [verifying, setVerifying] = useState(true); + const [emailVerified, setEmailVerified] = useState(false); + const router = useRouter(); + + const searchParams = useSearchParams(); + const token = searchParams.get("code"); + + useEffect(() => { + const verifyEmail = async () => { + // simulate network delay + await new Promise((resolve) => setTimeout(resolve, 1000)); + + if (!token) { + setVerifying(false); + setEmailVerified(false); + return; + } + + // TODO: Replace with an actual API call + setVerifying(false); + setEmailVerified(true); + }; + + verifyEmail(); + }, [token]); + + return ( +
+ {verifying ? ( +
+

Verifying...

+
+ ) : emailVerified ? ( + router.push("/login"), + }, + ]} + /> + ) : ( + router.push("/sign-up"), + }, + ]} + /> + )} +
+ ); +}