diff --git a/frontend/app/auth/login/page.tsx b/frontend/app/auth/login/page.tsx new file mode 100644 index 0000000..c17bfff --- /dev/null +++ b/frontend/app/auth/login/page.tsx @@ -0,0 +1,125 @@ +"use client"; +import Link from "next/link"; +import { useState } from "react"; +import Image from "next/image"; + +export default function LoginPage() { + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [error, setError] = useState(""); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(""); + + if (!email || !password) { + setError("Please fill in all fields"); + return; + } + + try { + const response = await fetch("/api/auth/login", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email, password }), + }); + + const data = await response.json(); + + if (!response.ok) { + throw new Error(data.message || "Login failed"); + } + + // Redirect or handle successful login + console.log("Login successful:", data); + } catch (err) { + setError(err.message || "Login failed"); + } + }; + + return ( +
+
+ {/* Left Section - Form */} +
+
+

Welcome Back

+

Please sign in to continue

+
+ + + +
OR
+ +
+
+
+ + setEmail(e.target.value)} + className="w-full px-4 py-3 bg-secondary/5 border border-border rounded-lg focus:ring-primary" + placeholder="Enter your email" + /> +
+ +
+ + setPassword(e.target.value)} + className="w-full px-4 py-3 bg-secondary/5 border border-border rounded-lg focus:ring-primary" + placeholder="••••••••" + /> +
+
+ + {error &&

{error}

} + + +
+ +

+ Don't have an account?{" "} + + Create account + +

+
+ + {/* Right Section - Illustration */} +
+
+ Authentication Illustration +
+
+
+
+ ); +} \ No newline at end of file diff --git a/frontend/app/auth/signup/page.tsx b/frontend/app/auth/signup/page.tsx new file mode 100644 index 0000000..c989b65 --- /dev/null +++ b/frontend/app/auth/signup/page.tsx @@ -0,0 +1,144 @@ +"use client"; +import Link from "next/link"; +import { useState } from "react"; +import Image from "next/image"; + +export default function SignupPage() { + const [name, setName] = useState(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [error, setError] = useState(""); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(""); + + if (!name || !email || !password || !confirmPassword) { + setError("Please fill in all fields"); + return; + } + + if (password !== confirmPassword) { + setError("Passwords do not match"); + return; + } + + try { + const response = await fetch("/api/auth/signup", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ name, email, password }), + }); + + const data = await response.json(); + + if (!response.ok) { + throw new Error(data.message || "Signup failed"); + } + + // Redirect or handle successful signup + console.log("Signup successful:", data); + } catch (err: any) { + setError(err.message || "Signup failed"); + } + }; + + return ( +
+
+ {/* Left Section - Form */} +
+
+

Create an Account

+

Sign up to get started

+
+ + + +
OR
+ +
+
+
+ + setName(e.target.value)} + className="w-full px-4 py-3 bg-secondary/5 border border-border rounded-lg focus:ring-primary" + placeholder="Enter your full name" + /> +
+
+ + setEmail(e.target.value)} + className="w-full px-4 py-3 bg-secondary/5 border border-border rounded-lg focus:ring-primary" + placeholder="Enter your email" + /> +
+
+ + setPassword(e.target.value)} + className="w-full px-4 py-3 bg-secondary/5 border border-border rounded-lg focus:ring-primary" + placeholder="••••••••" + /> +
+
+ + {error &&

{error}

} + + +
+ +

+ Already have an account?{" "} + + Sign in + +

+
+ + {/* Right Section - Illustration */} +
+
+ Registration Illustration +
+
+
+
+ ); +} diff --git a/frontend/app/components/navbar.tsx b/frontend/app/components/navbar.tsx new file mode 100644 index 0000000..cfbb9a4 --- /dev/null +++ b/frontend/app/components/navbar.tsx @@ -0,0 +1,25 @@ +import Link from "next/link"; + +export default function Navbar() { + return ( + + ); +} \ No newline at end of file diff --git a/frontend/app/globals.css b/frontend/app/globals.css index 6b717ad..ffa86f8 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -16,6 +16,237 @@ body { color: var(--foreground); - background: var(--background); + background: #121212; font-family: Arial, Helvetica, sans-serif; + min-height: 100vh; +} + +.min-h-screen { + min-height: 100vh; +} + +.bg-white { + background-color: #ffffff; +} + +.rounded-lg { + border-radius: 0.5rem; +} + +.shadow-md { + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), + 0 2px 4px -1px rgba(0, 0, 0, 0.06); +} + +.text-center { + text-align: center; +} + +.w-full { + width: 100%; +} + +.border { + border-width: 1px; + border-color: #e2e8f0; +} + +.rounded-lg { + border-radius: 0.5rem; +} + +.bg-blue-500 { + display: flex; + background-color: #63bb6a; + width: auto; + padding: 10px 20px; + justify-self: center; + font-size: 18; +} + +.bg-blue-500:hover { + background-color: #4f9655; +} + +.text-white { + color: #ffffff; +} + +.text-blue-500 { + color: #6efa75; +} + +.text-blue-500:hover { + text-decoration: underline; + color: #057420; +} + +.continue-with-google { + display: flex; + align-items: center; + justify-content: center; + padding: 1.2rem; + border-radius: 4rem; + font-weight: 500; + margin-bottom: 1.5rem; + color: #6efa75; + background: transparent; + border: 2px solid rgba(255, 255, 255, 0.3); + width: auto; + justify-self: center; + font-size: 16px; + outline: none; + transition: all 0.3s ease-in-out; +} + +.continue-with-google:hover { + background-color: #f8fafc; + color: #057420; +} + +.or-divider { + display: flex; + align-items: center; + color: #ffffff; + margin: 1.5rem 0; +} + +.or-divider::before, +.or-divider::after { + content: ""; + flex: 1; + border-bottom: 1px solid #e2e8f0; +} + +.or-divider::before { + margin-right: 1rem; +} + +.or-divider::after { + margin-left: 1rem; +} + +.text-red-500 { + color: #ef4444; +} + +.p-8 { + padding: 2rem; +} + +button { + transition: all 0.15s ease; +} + +input:focus { + outline: none; + border-color: #6efa75; + box-shadow: 0 0 0 3px rgba(0, 128, 0, 0.1); +} + +label { + color: #6efa75; +} + +form, form * { + color: #6efa75; +} + +input { + color: black; + background: transparent; + border: 2px solid rgba(255, 255, 255, 0.3); + padding: 12px; + width: 100%; + color: white; + font-size: 16px; + border-radius: 8px; + outline: none; + transition: all 0.3s ease-in-out; +} + +.container { + display: flex; + justify-content: space-between; + align-items: center; + padding: 2rem; + margin: 0 auto; + border-radius: 2rem; +} + +.google { + border-radius: 50px; +} + +@media (max-width: 534px) { + .container { + flex-direction: column; + align-items: center; + justify-content: center; + } + + .right-image { + display: none; + margin: 20px auto 0 auto; + } + + .bg-gray-100 { + background-color: #000; + background-image: + radial-gradient(circle at center, #2d2d2d 0%, #000 100%), + linear-gradient(135deg, #507d2a 0%, #507d2a 70%, transparent 70%), + linear-gradient(315deg, #507d2a 0%, #507d2a 70%, transparent 70%); + background-repeat: no-repeat, no-repeat, no-repeat; + background-position: center, top left, bottom right; + background-size: cover, 400px, 400px; + } +} + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 240 10% 3.9%; + --primary: 142.1 76.2% 36.3%; + --primary-foreground: 355.7 100% 97.3%; + --secondary: 240 4.8% 95.9%; + --secondary-foreground: 240 5.9% 10%; + --accent: 240 4.8% 95.9%; + --accent-foreground: 240 5.9% 10%; + --border: 240 5.9% 90%; + } + + .dark { + --background: 240 10% 3.9%; + --foreground: 0 0% 98%; + --primary: 142.1 70.6% 45.3%; + --primary-foreground: 144.9 80.4% 10%; + --secondary: 240 3.7% 15.9%; + --secondary-foreground: 0 0% 98%; + --accent: 240 3.7% 15.9%; + --accent-foreground: 0 0% 98%; + --border: 240 3.7% 15.9%; + } +} + +@layer utilities { + .bg-pattern { + @apply bg-neutral-100 dark:bg-neutral-900; + background-image: + radial-gradient(at 4% 8%, hsla(120,60%,90%,0.1) 0px, transparent 50%), + radial-gradient(at 80% 90%, hsla(120,60%,90%,0.1) 0px, transparent 50%); + } + + .bg-glass { + @apply bg-white/50 dark:bg-neutral-800/50; + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + } +} + +body { + @apply bg-background text-foreground font-sans; +} + +input, button { + @apply transition-all duration-200 ease-out; } diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx index f7fa87e..a5e4840 100644 --- a/frontend/app/layout.tsx +++ b/frontend/app/layout.tsx @@ -1,6 +1,7 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; +import Navbar from "./components/navbar"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -19,16 +20,18 @@ export const metadata: Metadata = { export default function RootLayout({ children, -}: Readonly<{ +}: { children: React.ReactNode; -}>) { +}) { return ( + {children} ); } + diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index 9007252..4966a3c 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -30,6 +30,7 @@ export default function Home() { target="_blank" rel="noopener noreferrer" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +