diff --git a/frontend/src/components/Auth/AuthModal.tsx b/frontend/src/components/Auth/AuthModal.tsx index 8091f6c..6e127ba 100644 --- a/frontend/src/components/Auth/AuthModal.tsx +++ b/frontend/src/components/Auth/AuthModal.tsx @@ -1,6 +1,10 @@ import React, { useState } from "react"; -import { ToggleButton } from "../Layout/Button"; -import { LogIn as LogInIcon, User as UserIcon, CircleHelp as ForgotIcon } from "lucide-react"; +import { ToggleButton } from "../Input/Button"; +import { + LogIn as LogInIcon, + User as UserIcon, + CircleHelp as ForgotIcon, +} from "lucide-react"; import LoginForm from "./LoginForm"; import RegisterForm from "./RegisterForm"; import ForgotPasswordForm from "./ForgotPasswordForm"; @@ -23,14 +27,19 @@ const AuthModal: React.FC = ({ onClose }) => { }; const authSwitch = () => { - const formMap: { [key: string]: JSX.Element } = { - Login: setSelectedTab("Forgot")} />, - Register: , - Forgot: + Login: ( + setSelectedTab("Forgot")} + /> + ), + Register: , + Forgot: , }; return formMap[selectedTab] ||
Please select a valid option
; - {/* + { + /* if (selectedTab === "Login") { return } else if (selectedTab === "Register") { @@ -39,9 +48,9 @@ const AuthModal: React.FC = ({ onClose }) => { return } else return
Please select a valid icon
- */} - - } + */ + } + }; return ( <> @@ -52,7 +61,8 @@ const AuthModal: React.FC = ({ onClose }) => { > {/*Container*/} -
@@ -78,8 +88,6 @@ const AuthModal: React.FC = ({ onClose }) => { Register - -
= ({ onClose }) => { ✕
- <> - {authSwitch()} - - - - - + <>{authSwitch()} - - ); }; diff --git a/frontend/src/components/Auth/ForgotPasswordForm.tsx b/frontend/src/components/Auth/ForgotPasswordForm.tsx index ce0a739..0f1a646 100644 --- a/frontend/src/components/Auth/ForgotPasswordForm.tsx +++ b/frontend/src/components/Auth/ForgotPasswordForm.tsx @@ -1,95 +1,102 @@ import React, { useState } from "react"; -import Input from "../Layout/Input"; -import Button from "../Layout/Button"; +import Input from "../Input/Input"; +import Button from "../Input/Button"; interface ForgotPasswordProps { - email?: string; + email?: string; } interface SubmitProps { - onSubmit: () => void; - } + onSubmit: () => void; +} -const ForgotPasswordForm: React.FC = ( {onSubmit} ) => { - const [email, setEmail] = useState(""); - const [errors, setErrors] = useState({}); +const ForgotPasswordForm: React.FC = ({ onSubmit }) => { + const [email, setEmail] = useState(""); + const [errors, setErrors] = useState({}); - const confirmPasswordReset = () => { - alert(`Email has been sent`); + const confirmPasswordReset = () => { + alert(`Email has been sent`); + }; - }; + const handleEmailChange = (e: React.ChangeEvent) => { + setEmail(e.target.value); + if (errors.email) { + setErrors((prev) => ({ ...prev, email: undefined })); + } + }; - const handleEmailChange = (e: React.ChangeEvent) => { - setEmail(e.target.value); - if (errors.email) { - setErrors((prev) => ({ ...prev, email: undefined })); + const validateEmail = (): boolean => { + const newErrors: ForgotPasswordProps = {}; + if (!email) { + newErrors.email = "Email is required."; + } else if (!/\S+@\S+\.\S+/.test(email)) { + newErrors.email = "Please enter a valid email address."; + } + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (validateEmail()) { + try { + const response = await fetch(`/api/user/forgot_password/${email}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + credentials: "include", + }); + + if (!response.ok) { + const data = await response.json(); + throw new Error( + data.message || "An error has occurred while resetting" + ); + } else { + confirmPasswordReset(); } - }; + } catch (error: any) { + console.error("Password reset error:", error.message); + setErrors((prev) => ({ + ...prev, + general: error.message || "An unexpected error occurred.", + })); + } + } + }; + return ( +
+
+

Forgot Password

+
+ - const validateEmail = (): boolean => { - const newErrors: ForgotPasswordProps = {}; - if (!email) { - newErrors.email = "Email is required."; - } else if (!/\S+@\S+\.\S+/.test(email)) { - newErrors.email = "Please enter a valid email address."; - } - setErrors(newErrors); - return Object.keys(newErrors).length === 0; - }; + {errors.email && ( +

{errors.email}

+ )} - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - - if (validateEmail()) { - try { - const response = await fetch(`/api/user/forgot_password/${email}`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - credentials: "include", - }); - - if (!response.ok) { - const data = await response.json(); - throw new Error(data.message || "An error has occurred while resetting"); - } else { - confirmPasswordReset(); - } - } catch (error: any) { - console.error("Password reset error:", error.message); - setErrors((prev) => ({ - ...prev, - general: error.message || "An unexpected error occurred.", - - })); - } - } - }; - - return ( -
-
-

Forgot Password

- - - - {errors.email &&

{errors.email}

} - - - -
-
- ); - + + +
+
+ ); }; export default ForgotPasswordForm; diff --git a/frontend/src/components/Auth/LoginForm.tsx b/frontend/src/components/Auth/LoginForm.tsx index a4f8f8d..c26b94d 100644 --- a/frontend/src/components/Auth/LoginForm.tsx +++ b/frontend/src/components/Auth/LoginForm.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; -import Input from "../Layout/Input"; -import Button, { ToggleButton } from "../Layout/Button"; +import Input from "../Input/Input"; +import Button, { ToggleButton } from "../Input/Button"; import { useAuth } from "../../context/AuthContext"; import GoogleLogin from "./OAuth"; import { CircleHelp as ForgotIcon } from "lucide-react"; @@ -101,8 +101,8 @@ const LoginForm: React.FC = ({ onSubmit, onForgotPassword }) => { return ( <> -
-

Login

+
+

Login

@@ -112,22 +112,30 @@ const LoginForm: React.FC = ({ onSubmit, onForgotPassword }) => { className="flex flex-col" > {errors.general && ( -

{errors.general}

+

+ {errors.general} +

)} {errors.username && ( -

{errors.username}

+

+ {errors.username} +

)} {errors.password && ( -

{errors.password}

+

+ {errors.password} +

)}
@@ -137,25 +145,28 @@ const LoginForm: React.FC = ({ onSubmit, onForgotPassword }) => { placeholder="Password" value={formData.password} onChange={handleInputChange} - extraClasses={`w-full p-3 ${errors.password ? "border-red-500" : ""}`} - > - - + extraClasses={`w-full p-3 ${ + errors.password ? "border-red-500" : "" + }`} + >
- - + +
diff --git a/frontend/src/components/Auth/PasswordResetForm.tsx b/frontend/src/components/Auth/PasswordResetForm.tsx index 784a246..9e2fed1 100644 --- a/frontend/src/components/Auth/PasswordResetForm.tsx +++ b/frontend/src/components/Auth/PasswordResetForm.tsx @@ -1,122 +1,125 @@ import React, { useState } from "react"; -import Input from "../Layout/Input"; -import Button from "../Layout/Button"; +import Input from "../Input/Input"; +import Button from "../Input/Button"; import { useAuth } from "../../context/AuthContext"; interface ResetPasswordData { - newPassword: string; - confirmNewPassword: string; + newPassword: string; + confirmNewPassword: string; } interface ResetPasswordErrors { - newPasswordError?: string; - confirmNewPasswordError?: string; + newPasswordError?: string; + confirmNewPasswordError?: string; } interface SubmitProps { - onSubmit: () => void; - token: string; + onSubmit: () => void; + token: string; } const PasswordResetForm: React.FC = ({ onSubmit, token }) => { + const [errors, setErrors] = useState({}); - const [errors, setErrors] = useState({}); + const [resetData, setResetData] = useState({ + newPassword: "", + confirmNewPassword: "", + }); - const [resetData, setResetData] = useState({ - newPassword: "", - confirmNewPassword: "", + const handlePasswordChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setResetData((prev) => ({ + ...prev, + [name]: value, + })); + }; + + const validateResetForm = (): boolean => { + const newErrors: ResetPasswordErrors = {}; + + Object.keys(resetData).forEach((key) => { + if (!resetData[key as keyof ResetPasswordData]) { + newErrors[key as keyof ResetPasswordErrors] = "Confirm your password"; + } }); - - - const handlePasswordChange = (e: React.ChangeEvent) => { - const { name, value } = e.target; - setResetData((prev) => ({ - ...prev, - [name]: value, - })); - }; - - const validateResetForm = (): boolean => { - const newErrors: ResetPasswordErrors = {}; - - Object.keys(resetData).forEach((key) => { - if (!resetData[key as keyof ResetPasswordData]) { - newErrors[key as keyof ResetPasswordErrors] = "Confirm your password"; - } - }); - if (resetData.newPassword.length < 8) { - newErrors.newPasswordError = "Password must be at least 8 characters long"; - } - if (resetData.newPassword !== resetData.confirmNewPassword) { - newErrors.confirmNewPasswordError = "Passwords do not match"; - } - - - setErrors(newErrors); - return Object.keys(newErrors).length === 0; - }; - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - onSubmit(); - - if (validateResetForm()) { - try { - const response = await fetch(`/api/user/reset_password/${token}/${resetData.newPassword}`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - credentials: "include", - body: JSON.stringify(resetData), - }); - - if (!response.ok) { - const data = await response.json(); - throw new Error(data.error || "An error has occurred while resetting"); - } else { - onSubmit(true) - } - } catch (error: any) { - console.error("Password reset error:", error.message); - setErrors((prev) => ({ - ...prev, - general: error.message || "An unexpected error occurred.", - - })); - } + if (resetData.newPassword.length < 8) { + newErrors.newPasswordError = + "Password must be at least 8 characters long"; } + if (resetData.newPassword !== resetData.confirmNewPassword) { + newErrors.confirmNewPasswordError = "Passwords do not match"; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + onSubmit(); + + if (validateResetForm()) { + try { + const response = await fetch( + `/api/user/reset_password/${token}/${resetData.newPassword}`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + credentials: "include", + body: JSON.stringify(resetData), + } + ); + + if (!response.ok) { + const data = await response.json(); + throw new Error( + data.error || "An error has occurred while resetting" + ); + } else { + onSubmit(true); + } + } catch (error: any) { + console.error("Password reset error:", error.message); + setErrors((prev) => ({ + ...prev, + general: error.message || "An unexpected error occurred.", + })); + } + } + }; + + return ( +
+ + + {errors.confirmNewPasswordError && ( +

+ {errors.confirmNewPasswordError} +

+ )} + + + +
+ ); }; - return ( - -
- - - {errors.confirmNewPasswordError && ( -

{errors.confirmNewPasswordError}

- )} - - - - -
- - ); -}; - -export default PasswordResetForm \ No newline at end of file +export default PasswordResetForm; diff --git a/frontend/src/components/Auth/RegisterForm.tsx b/frontend/src/components/Auth/RegisterForm.tsx index 441115f..ff850c6 100644 --- a/frontend/src/components/Auth/RegisterForm.tsx +++ b/frontend/src/components/Auth/RegisterForm.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; -import Input from "../Layout/Input"; -import Button from "../Layout/Button"; +import Input from "../Input/Input"; +import Button from "../Input/Button"; import { useAuth } from "../../context/AuthContext"; import GoogleLogin from "./OAuth"; @@ -111,10 +111,13 @@ const RegisterForm: React.FC = ({ onSubmit }) => { <>
-

Register

-
- +

+ Register +

+
= ({ onSubmit }) => { >
{errors.general && ( -

{errors.general}

+

+ {errors.general} +

)} {errors.username && ( -

{errors.username}

+

+ {errors.username} +

)}
{errors.email && ( -

{errors.email}

+

+ {errors.email} +

)} = ({ onSubmit }) => { placeholder="Email" value={formData.email} onChange={handleInputChange} - extraClasses={`w-full mb-[1.5em] p-[0.5rem] ${errors.email ? "border-red-500" : ""}`} + extraClasses={`w-full mb-[1.5em] p-[0.5rem] ${ + errors.email ? "border-red-500" : "" + }`} />
{errors.password && ( -

{errors.password}

+

+ {errors.password} +

)} = ({ onSubmit }) => { placeholder="Password" value={formData.password} onChange={handleInputChange} - extraClasses={`w-full mb-[1.5em] p-[0.5rem] ${errors.password ? "border-red-500" : ""}`} + extraClasses={`w-full mb-[1.5em] p-[0.5rem] ${ + errors.password ? "border-red-500" : "" + }`} />
- {errors.confirmPassword && ( -

{errors.confirmPassword}

+

+ {errors.confirmPassword} +

)} = ({ onSubmit }) => { placeholder="Confirm Password" value={formData.confirmPassword} onChange={handleInputChange} - extraClasses={`w-full mb-[1.5em] p-[0.5rem] ${errors.confirmPassword ? "border-red-500" : ""}`} + extraClasses={`w-full mb-[1.5em] p-[0.5rem] ${ + errors.confirmPassword ? "border-red-500" : "" + }`} />
- +
-
- ); }; diff --git a/frontend/src/components/Layout/Button.tsx b/frontend/src/components/Input/Button.tsx similarity index 100% rename from frontend/src/components/Layout/Button.tsx rename to frontend/src/components/Input/Button.tsx diff --git a/frontend/src/components/Layout/Input.tsx b/frontend/src/components/Input/Input.tsx similarity index 100% rename from frontend/src/components/Layout/Input.tsx rename to frontend/src/components/Input/Input.tsx diff --git a/frontend/src/components/Layout/SearchBar.tsx b/frontend/src/components/Input/SearchBar.tsx similarity index 100% rename from frontend/src/components/Layout/SearchBar.tsx rename to frontend/src/components/Input/SearchBar.tsx diff --git a/frontend/src/components/Layout/Navbar.tsx b/frontend/src/components/Navigation/Navbar.tsx similarity index 75% rename from frontend/src/components/Layout/Navbar.tsx rename to frontend/src/components/Navigation/Navbar.tsx index 537761f..01ce3c2 100644 --- a/frontend/src/components/Layout/Navbar.tsx +++ b/frontend/src/components/Navigation/Navbar.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; -import Logo from "./Logo"; -import Button from "./Button"; +import Logo from "../Layout/Logo"; +import Button from "../Input/Button"; import Sidebar from "./Sidebar"; import { Sidebar as SidebarIcon } from "lucide-react"; import { @@ -8,11 +8,11 @@ import { LogOut as LogOutIcon, Settings as SettingsIcon, } from "lucide-react"; -import SearchBar from "./SearchBar"; +import SearchBar from "../Input/SearchBar"; import AuthModal from "../Auth/AuthModal"; import { useAuthModal } from "../../hooks/useAuthModal"; import { useAuth } from "../../context/AuthContext"; -import QuickSettings from "./QuickSettings"; +import QuickSettings from "../Settings/QuickSettings"; interface NavbarProps { variant?: "home" | "default"; @@ -42,14 +42,14 @@ const Navbar: React.FC = ({ variant = "default" }) => { setShowSideBar(!showSideBar); }; - return (