diff --git a/public/assets/icons/facebook.svg b/public/assets/icons/facebook.svg new file mode 100644 index 0000000..2d9708f --- /dev/null +++ b/public/assets/icons/facebook.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/icons/google.svg b/public/assets/icons/google.svg new file mode 100644 index 0000000..d3cdb87 --- /dev/null +++ b/public/assets/icons/google.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/assets/icons/x.svg b/public/assets/icons/x.svg new file mode 100644 index 0000000..e029688 --- /dev/null +++ b/public/assets/icons/x.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/logo.svg b/public/assets/logo.svg new file mode 100644 index 0000000..c0ef4f5 --- /dev/null +++ b/public/assets/logo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 1f03186..27a79e9 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -1,22 +1,24 @@ -"use client"; +'use client'; -import { signIn } from "next-auth/react"; -import { useState } from "react"; -import { useRouter } from "next/navigation"; +import { useState } from 'react'; +import { useRouter } from 'next/navigation'; +import { signIn } from 'next-auth/react'; +import Link from 'next/link'; export default function LoginPage() { const router = useRouter(); - const [email, setEmail] = useState(""); - const [password, setPassword] = useState(""); - const [error, setError] = useState(""); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [showPassword, setShowPassword] = useState(false); + const [error, setError] = useState(''); const [loading, setLoading] = useState(false); - const handleCredentialsLogin = async (e: React.FormEvent) => { + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setLoading(true); - setError(""); + setError(''); - const result = await signIn("credentials", { + const result = await signIn('credentials', { email, password, redirect: false, @@ -25,151 +27,146 @@ export default function LoginPage() { setLoading(false); if (result?.error) { - setError("Invalid email or password"); + setError('Invalid email or password'); } else { - router.push("/"); + router.push('/'); router.refresh(); } }; - const handleSocialLogin = (provider: "google" | "facebook") => { - signIn(provider, { callbackUrl: "/" }); + const handleSocialLogin = (provider: 'google' | 'facebook' | 'twitter') => { + signIn(provider, { callbackUrl: '/' }); }; return ( -
-
-
-

- Real Estate Platform -

-

- Sign in to your account +

+
+ {/* Logo */} +
+ RE-QuestN +
+ + {/* Login Heading */} +
+

Login

+

+ Sign in to your account to access your real estate dashboard and manage your properties

- {/* SSO Buttons */} -
- - - -
- -
-
-
-
-
- - Or continue with email - -
-
- - {/* Email/Password Form */} -
+ {/* Login Form */} + {error && ( -
+
{error}
)} + {/* Email Input */}
- setEmail(e.target.value)} - className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" + required + className="w-full px-6 py-6 bg-[#f0f5fc] rounded-[20px] text-[#00293d] text-sm font-light placeholder:text-[#00293d] focus:outline-none focus:ring-2 focus:ring-[#00293d]/20 transition-all" />
-
- + {/* Password Input */} +
setPassword(e.target.value)} - className="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" + required + className="w-full px-6 py-6 bg-[#f0f5fc] rounded-[20px] text-[#00293d] text-sm font-light placeholder:text-[#00293d] focus:outline-none focus:ring-2 focus:ring-[#00293d]/20 transition-all pr-14" /> -
- - + {/* Forgot Password Link */} +
+ + Forgot Password? + +
+ + {/* Login Button */} -

- Don't have an account?{" "} - + Don't Have An Account ? + + Sign Up + +

+ + {/* Divider */} +
+
+ Or +
+
+ + {/* Social Login Icons */} +
+ {/* Google */} + + + {/* X (Twitter) */} + + + {/* Facebook */} + +
); diff --git a/src/app/page.tsx b/src/app/page.tsx index 295f8fd..5d42ce7 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,63 +1,84 @@ -import Image from "next/image"; +'use client'; + +import { useSession, signOut } from 'next-auth/react'; +import Link from 'next/link'; export default function Home() { + const { data: session } = useSession(); + return ( -
-
- Next.js logo -
-

- To get started, edit the page.tsx file. -

-

- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. -

+
+ {/* Header */} +
+
+
+ {/* Logo */} + + RE-QuestN + + + {/* User Menu */} +
+ + Welcome, {session?.user?.name || session?.user?.email || 'User'} + + +
+
-
+ + {/* Main Content */} +
+
+

+ Welcome to RE-QuestN +

+

+ Your real estate journey starts here. Explore properties, connect with agents, and find your dream home. +

+ + {/* Quick Actions */} +
+
+
+ + + +
+

Browse Properties

+

Explore thousands of listings in your area

+
+ +
+
+ + + +
+

Find Agents

+

Connect with top real estate professionals

+
+ +
+
+ + + +
+

Saved Favorites

+

View your saved properties and searches

+
+
diff --git a/src/app/signup/page.tsx b/src/app/signup/page.tsx new file mode 100644 index 0000000..6afaae9 --- /dev/null +++ b/src/app/signup/page.tsx @@ -0,0 +1,231 @@ +'use client'; + +import { useState } from 'react'; +import { useRouter } from 'next/navigation'; +import { signIn } from 'next-auth/react'; +import Link from 'next/link'; + +type UserType = 'user' | 'admin'; + +export default function SignUpPage() { + const router = useRouter(); + const [userType, setUserType] = useState('user'); + const [name, setName] = useState(''); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [showPassword, setShowPassword] = useState(false); + const [error, setError] = useState(''); + const [loading, setLoading] = useState(false); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + setError(''); + + try { + const response = await fetch('/api/auth/register', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + name, + email, + password, + role: userType.toUpperCase(), + }), + }); + + const data = await response.json(); + + if (!response.ok) { + throw new Error(data.message || 'Registration failed'); + } + + const result = await signIn('credentials', { + email, + password, + redirect: false, + }); + + if (result?.error) { + router.push('/login'); + } else { + router.push('/'); + router.refresh(); + } + } catch (err) { + setError(err instanceof Error ? err.message : 'Registration failed'); + } finally { + setLoading(false); + } + }; + + const handleSocialLogin = (provider: 'google' | 'facebook' | 'twitter') => { + signIn(provider, { callbackUrl: '/' }); + }; + + return ( +
+
+ {/* Logo */} +
+ RE-QuestN +
+ + {/* Sign Up Heading */} +
+

Sign Up

+

+ Sign in or create an account to showcase your skills & expertise to millions of people starting their real estate journey today" +

+
+ + {/* User Type Toggle */} +
+
+ + +
+
+ + {/* Sign Up Form */} +
+ {error && ( +
+ {error} +
+ )} + + {/* Name Input */} +
+ setName(e.target.value)} + required + className="w-full px-6 py-6 bg-[#f0f5fc] rounded-[20px] text-[#00293d] text-sm font-light placeholder:text-[#00293d] focus:outline-none focus:ring-2 focus:ring-[#00293d]/20 transition-all" + /> +
+ + {/* Email Input */} +
+ setEmail(e.target.value)} + required + className="w-full px-6 py-6 bg-[#f0f5fc] rounded-[20px] text-[#00293d] text-sm font-light placeholder:text-[#00293d] focus:outline-none focus:ring-2 focus:ring-[#00293d]/20 transition-all" + /> +
+ + {/* Password Input */} +
+ setPassword(e.target.value)} + required + className="w-full px-6 py-6 bg-[#f0f5fc] rounded-[20px] text-[#00293d] text-sm font-light placeholder:text-[#00293d] focus:outline-none focus:ring-2 focus:ring-[#00293d]/20 transition-all pr-14" + /> + +
+ + {/* Sign Up Button */} + +
+ + {/* Already Have Account */} +
+ Already Have An Account ? + + Login + +
+ + {/* Divider */} +
+
+ Or +
+
+ + {/* Social Login Icons */} +
+ {/* Google */} + + + {/* X (Twitter) */} + + + {/* Facebook */} + +
+
+
+ ); +} diff --git a/src/middleware.ts b/src/middleware.ts new file mode 100644 index 0000000..ee594db --- /dev/null +++ b/src/middleware.ts @@ -0,0 +1,42 @@ +import { auth } from "@/auth"; +import { NextResponse } from "next/server"; + +// Public routes that don't require authentication +const publicRoutes = ["/login", "/signup", "/forgot-password", "/reset-password"]; + +export default auth((req) => { + const { nextUrl } = req; + const isLoggedIn = !!req.auth; + + const isPublicRoute = publicRoutes.some( + (route) => nextUrl.pathname === route || nextUrl.pathname.startsWith(route + "/") + ); + + const isApiRoute = nextUrl.pathname.startsWith("/api"); + const isStaticRoute = nextUrl.pathname.startsWith("/_next") || + nextUrl.pathname.startsWith("/assets") || + nextUrl.pathname.includes("."); + + // Skip middleware for API routes and static files + if (isApiRoute || isStaticRoute) { + return NextResponse.next(); + } + + // Redirect logged-in users away from public routes (login, signup) + if (isLoggedIn && isPublicRoute) { + return NextResponse.redirect(new URL("/", nextUrl.origin)); + } + + // Redirect non-logged-in users to login page for protected routes + if (!isLoggedIn && !isPublicRoute) { + const loginUrl = new URL("/login", nextUrl.origin); + loginUrl.searchParams.set("callbackUrl", nextUrl.pathname); + return NextResponse.redirect(loginUrl); + } + + return NextResponse.next(); +}); + +export const config = { + matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"], +};