diff --git a/src/app/components/AuthForm.js b/src/app/components/AuthForm.js index e33555e..9736603 100644 --- a/src/app/components/AuthForm.js +++ b/src/app/components/AuthForm.js @@ -1,63 +1,392 @@ 'use client'; -import { useState } from "react"; -import { account } from "../lib/appwrite"; import { useRouter } from "next/navigation"; +import { useAuth } from "../context/AuthContext"; +import { useState, useEffect } from "react"; +import Link from "next/link"; +// import { ID } from "../lib/appwrite"; -export default function AuthForm() { - const [isLogin, setIsLogin] = useState(true); - const [email, setEmail] = useState(""); - const [password, setPassword] = useState(""); +export default function AuthPage() { const router = useRouter(); + const { user, loading: authLoading, isAuthenticated, login, register } = useAuth(); + const [isLoginForm, setIsLoginForm] = useState(true); + const [darkMode, setDarkMode] = useState(false); + const [loading, setLoading] = useState(false); + const [showPassword, setShowPassword] = useState(false); - const handleSubmit = async (e) => { - e.preventDefault(); + // Form states + const [loginData, setLoginData] = useState({ + email: "", + password: "", + rememberMe: false, + }); - try { - if (isLogin) { - await account.createEmailPasswordSession(email, password); - } else { - await account.create('unique()', email, password); - await account.createEmailPasswordSession(email, password); - } + const [signupData, setSignupData] = useState({ + fname: "", + lname: "", + email: "", + password: "", + agreeTerms: false, + }); + + const [errors, setErrors] = useState({ + agreeTerms: "", + general: "", + }); + + // Redirect if already authenticated + useEffect(() => { + if (!authLoading && isAuthenticated) { router.push("/pages/dashboard"); - } catch (err) { - alert("Authentication error: " + err.message); + } + }, [isAuthenticated, authLoading, router]); + + const handleLoginChange = (e) => { + const { name, value, type, checked } = e.target; + setLoginData({ + ...loginData, + [name]: type === "checkbox" ? checked : value, + }); + }; + + const handleSignupChange = (e) => { + const { name, value, type, checked } = e.target; + setSignupData({ + ...signupData, + [name]: type === "checkbox" ? checked : value, + }); + if (name === "agreeTerms") { + setErrors({ ...errors, agreeTerms: "" }); } }; + const validateSignupForm = () => { + let isValid = true; + const newErrors = { ...errors }; + + if (!signupData.agreeTerms) { + newErrors.agreeTerms = "You must agree to the terms and conditions"; + isValid = false; + } + + if (signupData.password.length < 8) { + newErrors.general = "Password must be at least 8 characters"; + isValid = false; + } + + setErrors(newErrors); + return isValid; + }; + + const handleLogin = async (e) => { + e.preventDefault(); + setLoading(true); + setErrors({ ...errors, general: "" }); + + try { + await login(loginData.email, loginData.password); + router.push("/pages/dashboard"); + } catch (error) { + console.error("Login error:", error); + let errorMessage = 'Login failed. Please try again.'; + + if (error.type === 'user_invalid_credentials') { + errorMessage = 'Invalid email or password'; + } else if (error.type === 'general_argument_invalid') { + errorMessage = 'Invalid email format'; + } else if (error.code === 401) { + errorMessage = 'Authentication failed. Please try again.'; + } + + setErrors({ ...errors, general: errorMessage }); + } finally { + setLoading(false); + } + }; + + const handleSignup = async (e) => { + e.preventDefault(); + if (!validateSignupForm()) return; + + setLoading(true); + setErrors({ ...errors, general: "" }); + + try { + await register( + signupData.email, + signupData.password, + `${signupData.fname} ${signupData.lname}` + ); + router.push("/pages/dashboard"); + } catch (error) { + console.error("Signup error:", error); + let errorMessage = 'Signup failed. Please try again.'; + + if (error.type === 'user_already_exists') { + errorMessage = 'Email already registered'; + } else if (error.type === 'general_argument_invalid') { + errorMessage = 'Invalid email format'; + } else if (error.code === 401) { + errorMessage = 'Authentication failed. Please try again.'; + } + + setErrors({ ...errors, general: errorMessage }); + } finally { + setLoading(false); + } + }; + + if (authLoading) { + return ( +
+
+
+

+ Loading... +

+
+
+ ); + } + + if (isAuthenticated) { + return null; // Will redirect from useEffect + } + return ( -
-

{isLogin ? 'Login' : 'Register'}

-
- setEmail(e.target.value)} - required - /> - setPassword(e.target.value)} - required - /> - -
-

- {isLogin ? "New user?" : "Already registered?"}{" "} - -

+
+
+
+
+

+ {isLoginForm ? "Sign In" : "Sign Up"} +

+

+ {isLoginForm + ? "Enter your email and password to sign in" + : "Create your account to get started"} +

+
+ + {errors.general && ( +
+ {errors.general} +
+ )} + + {isLoginForm ? ( + // Login Form +
+
+ + +
+ +
+ +
+ + +
+
+ +
+ + + +
+ + +
+ ) : ( + // Sign Up Form +
+
+
+ + +
+ +
+ + +
+
+ +
+ + +
+ +
+ +
+ + +
+
+ +
+ + {errors.agreeTerms && ( +

+ {errors.agreeTerms} +

+ )} +
+ + +
+ )} + +
+

+ {isLoginForm ? "Don't have an account?" : "Already have an account?"} + +

+
+
+
+ + {/* Dark mode toggle */} +
); -} +} \ No newline at end of file diff --git a/src/app/components/ProtectedRoute.js b/src/app/components/ProtectedRoute.js new file mode 100644 index 0000000..d4da100 --- /dev/null +++ b/src/app/components/ProtectedRoute.js @@ -0,0 +1,26 @@ +// components/ProtectedRoute.js +"use client"; +import { useRouter } from "next/navigation"; +import { useAuth } from "../context/AuthContext"; +import { useEffect } from "react"; + +export default function ProtectedRoute({ children }) { + const { user, loading } = useAuth(); + const router = useRouter(); + + useEffect(() => { + if (!loading && !user) { + router.push("/auth"); + } + }, [user, loading, router]); + + if (loading) { + return ( +
+
+
+ ); + } + + return user ? children : null; +} \ No newline at end of file diff --git a/src/app/context/AuthContext.js b/src/app/context/AuthContext.js index e558f07..2a87aea 100644 --- a/src/app/context/AuthContext.js +++ b/src/app/context/AuthContext.js @@ -1,37 +1,70 @@ +// context/AuthContext.js "use client"; - -import { createContext, useContext, useEffect, useState } from "react"; -import { account } from "../lib/appwrite"; +import { createContext, useContext, useState, useEffect } from "react"; +import { account,ID } from "../lib/appwrite"; const AuthContext = createContext(); export const AuthProvider = ({ children }) => { - const [isAuthenticated, setIsAuthenticated] = useState(false); - const [authLoading, setAuthLoading] = useState(true); + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); - const checkUserSession = async () => { + useEffect(() => { + checkSession(); + }, []); + + const checkSession = async () => { try { - const user = await account.get(); - if (user) { - setIsAuthenticated(true); - } + const currentUser = await account.get(); + setUser(currentUser); } catch (error) { - setIsAuthenticated(false); + setUser(null); } finally { - setAuthLoading(false); + setLoading(false); } }; - useEffect(() => { - checkUserSession(); - }, []); + const login = async (email, password) => { + try { + await account.createEmailPasswordSession(email, password); + const currentUser = await account.get(); + setUser(currentUser); + return currentUser; + } catch (error) { + throw error; + } + }; + + const register = async (email, password, name) => { + try { + await account.create(ID.unique(), email, password, name); + await account.createEmailPasswordSession(email, password); + const currentUser = await account.get(); + setUser(currentUser); + return currentUser; + } catch (error) { + throw error; + } + }; + + const logout = async () => { + try { + await account.deleteSession("current"); + setUser(null); + } catch (error) { + throw error; + } + }; return ( {children} @@ -39,4 +72,10 @@ export const AuthProvider = ({ children }) => { ); }; -export const useAuth = () => useContext(AuthContext); +export const useAuth = () => { + const context = useContext(AuthContext); + if (!context) { + throw new Error("useAuth must be used within an AuthProvider"); + } + return context; +}; \ No newline at end of file diff --git a/src/app/layout.js b/src/app/layout.js index 9ce38c2..0e69e48 100644 --- a/src/app/layout.js +++ b/src/app/layout.js @@ -1,16 +1,22 @@ +// app/layout.js or app/page.js if that's where RootLayout is +"use client"; + import "./globals.css"; import Navbar from "./components/Navbar"; import Sidebar from "./components/Sidebar"; +import { AuthProvider } from "./context/AuthContext"; // adjust path if needed export default function RootLayout({ children }) { return ( - -
- -
{children}
-
+ + +
+ +
{children}
+
+
); diff --git a/src/app/lib/appwrite.js b/src/app/lib/appwrite.js index 83b099e..1e5666f 100644 --- a/src/app/lib/appwrite.js +++ b/src/app/lib/appwrite.js @@ -1,4 +1,4 @@ -import { Client, Account } from "appwrite"; +import { Client, Account,ID } from "appwrite"; const client = new Client(); client @@ -14,4 +14,4 @@ if (!process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT || !process.env.NEXT_PUBLIC_APPWR } -export { account,client }; +export { account,client,ID }; diff --git a/src/app/middleware.js b/src/app/middleware.js index c0de51e..5493fc9 100644 --- a/src/app/middleware.js +++ b/src/app/middleware.js @@ -1,16 +1,16 @@ -import { NextResponse } from 'next/server'; +// import { NextResponse } from 'next/server'; -export function middleware(request) { - const isLoggedIn = request.cookies.get('appwrite-session'); // Set manually if needed - const url = request.nextUrl; +// export function middleware(request) { +// const isLoggedIn = request.cookies.get('appwrite-session'); // Set manually if needed +// const url = request.nextUrl; - if (!isLoggedIn && url.pathname.startsWith('/pages')) { - return NextResponse.redirect(new URL('/login', request.url)); - } +// if (!isLoggedIn && url.pathname.startsWith('/pages')) { +// return NextResponse.redirect(new URL('/login', request.url)); +// } - return NextResponse.next(); -} +// return NextResponse.next(); +// } -export const config = { - matcher: ['/pages/:path*'], -}; +// export const config = { +// matcher: ['/pages/:path*'], +// }; diff --git a/src/app/not-found.js b/src/app/not-found.js new file mode 100644 index 0000000..3f61aba --- /dev/null +++ b/src/app/not-found.js @@ -0,0 +1,66 @@ +// app/not-found.js (not in a 'page.js' file) +"use client"; // Add this since you're using client-side features + +import Link from "next/link"; +import Image from "next/image"; +import { useEffect } from "react"; + +export default function NotFound() { + useEffect(() => { + // Client-side dark mode initialization + const darkMode = JSON.parse(localStorage.getItem('darkMode')); + if (darkMode) { + document.documentElement.classList.add('dark'); + document.documentElement.classList.add('bg-gray-900'); + } + }, []); + + return ( +
+ {/* Metadata - App Router style */} + 404 Error Page | TailAdmin + + + {/* Centered Content */} +
+

+ ERROR +

+ + {/* Optimized Images */} + 404 + 404 + +

+ We can't seem to find the page you are looking for! +

+ + + Back to Home Page + +
+ + {/* Footer */} +

+ © {new Date().getFullYear()} - TailAdmin +

+
+ ); +} \ No newline at end of file diff --git a/src/app/pages/dashboard/page.js b/src/app/pages/dashboard/page.js index 9efb4fc..f0abf16 100644 --- a/src/app/pages/dashboard/page.js +++ b/src/app/pages/dashboard/page.js @@ -1,9 +1,33 @@ +// pages/dashboard.js +'use client'; +import { useEffect, useState } from "react"; +import { account } from "../../lib/appwrite"; +import { useRouter } from "next/navigation"; + export default function Dashboard() { + const [user, setUser] = useState(null); + const router = useRouter(); + + useEffect(() => { + const checkAuth = async () => { + try { + const currentUser = await account.get(); + setUser(currentUser); + } catch (err) { + router.push("/"); + } + }; + checkAuth(); + }, [router]); + + if (!user) { + return
Loading...
; + } + return ( - <> -
- Hello World +
+

Welcome, {user.name}

+
- ); -} +} \ No newline at end of file