diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4382ebd..332ceee 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -19,6 +19,7 @@ import GenerateOTP from './GenerateOTP/GenerateOTP'; import VerifyOTP from './VerifyOTP/VerifyOTP'; import { SocietyManagementPage } from './Settings/SettingsPage/SocietyManagementPage/SocietyManagementPage'; import { CreateNewSocietyPage } from './Settings/SettingsPage/SocietyManagementPage/CreateNewSociety/CreateNewSociety'; +import { SearchSocietiesPage } from './Settings/SettingsPage/SocietyManagementPage/SearchSocieties/SearchSocieties'; function App() { const [user, setUser] = useState(null); @@ -101,6 +102,10 @@ function App() { } /> } /> } /> + } + /> } /> } /> diff --git a/frontend/src/Login/Login.tsx b/frontend/src/Login/Login.tsx index 65b4789..b0f657a 100644 --- a/frontend/src/Login/Login.tsx +++ b/frontend/src/Login/Login.tsx @@ -1,28 +1,28 @@ -import classes from './Login.module.css'; -import { AuthScreen } from '../AuthScreen/AuthScreen'; -import { TextInput, TextOptions } from '../TextInput/TextInput'; -import { UserCircleIcon } from '@heroicons/react/24/outline'; -import { LockClosedIcon } from '@heroicons/react/24/outline'; -import { useState, FormEvent, useContext } from 'react'; -import { Link } from 'react-router'; -import { errorHandler, AuthError } from '../errorHandler'; -import { UserContext, User } from '../UserContext/UserContext'; +import classes from "./Login.module.css"; +import { AuthScreen } from "../AuthScreen/AuthScreen"; +import { TextInput, TextOptions } from "../TextInput/TextInput"; +import { UserCircleIcon } from "@heroicons/react/24/outline"; +import { LockClosedIcon } from "@heroicons/react/24/outline"; +import { useState, FormEvent, useContext } from "react"; +import { Link } from "react-router"; +import { errorHandler, AuthError } from "../errorHandler"; +import { UserContext, User } from "../UserContext/UserContext"; export default function LoginPage() { - const [username, setUsername] = useState(''); - const [password, setPassword] = useState(''); + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); const [error, setError] = useState(undefined); const [success, setSuccess] = useState(undefined); const { setUser } = useContext(UserContext); async function handleSubmit(event: FormEvent) { event.preventDefault(); - const res = await fetch('http://localhost:5180/auth/login', { - method: 'POST', + const res = await fetch("http://localhost:5180/auth/login", { + method: "POST", headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, - credentials: 'include', + credentials: "include", body: JSON.stringify({ username, password, @@ -34,7 +34,7 @@ export default function LoginPage() { setError(errorHandler(json.error)); } else if (setUser) { setError(undefined); - setSuccess('Logged in successfully! Redirecting...'); + setSuccess("Logged in successfully! Redirecting..."); setTimeout(() => { setUser(json as User); }, 1000); diff --git a/frontend/src/Register/Register.tsx b/frontend/src/Register/Register.tsx index 58c3ae8..1435f86 100644 --- a/frontend/src/Register/Register.tsx +++ b/frontend/src/Register/Register.tsx @@ -12,7 +12,9 @@ export default function RegisterPage() { const [password, setPassword] = useState(""); const [email, setEmail] = useState(""); const [error, setError] = useState(undefined); + const [success, setSuccess] = useState(undefined); const navigate = useNavigate(); + async function handleSubmit(event: FormEvent) { event.preventDefault(); const res = await fetch("http://localhost:5180/auth/register", { @@ -32,7 +34,10 @@ export default function RegisterPage() { setError(errorHandler(json.error)); } else { setError(undefined); - navigate("/login"); + setSuccess("Signed up successfully! Redirecting to login page..."); + setTimeout(() => { + navigate("/login"); + }, 1000); } } @@ -78,6 +83,7 @@ export default function RegisterPage() { buttonText="Sign up" onSubmit={handleSubmit} error={error} + success={success} />
diff --git a/frontend/src/Settings/SettingsNavbar/SettingsNavbar.tsx b/frontend/src/Settings/SettingsNavbar/SettingsNavbar.tsx index 6a4f85f..35ca6e5 100644 --- a/frontend/src/Settings/SettingsNavbar/SettingsNavbar.tsx +++ b/frontend/src/Settings/SettingsNavbar/SettingsNavbar.tsx @@ -2,6 +2,7 @@ import { CalendarIcon, FaceSmileIcon, KeyIcon, + MagnifyingGlassIcon, MegaphoneIcon, StarIcon, UserCircleIcon, @@ -38,6 +39,11 @@ const rows: Row[][] = [ name: 'Create a new society', to: '/settings/societies/new', }, + { + icon: , + name: 'Join a society', + to: '/settings/societies/search', + }, ], [ { diff --git a/frontend/src/Settings/SettingsPage/SocietyManagementPage/SearchSocieties/SearchSocieties.module.css b/frontend/src/Settings/SettingsPage/SocietyManagementPage/SearchSocieties/SearchSocieties.module.css new file mode 100644 index 0000000..8009195 --- /dev/null +++ b/frontend/src/Settings/SettingsPage/SocietyManagementPage/SearchSocieties/SearchSocieties.module.css @@ -0,0 +1,11 @@ +.societiesList { + list-style-type: none; +} + +.societiesList li { + display: flex; + align-items: center; + justify-content: space-between; + padding: 32px; + border-radius: 32px; +} diff --git a/frontend/src/Settings/SettingsPage/SocietyManagementPage/SearchSocieties/SearchSocieties.tsx b/frontend/src/Settings/SettingsPage/SocietyManagementPage/SearchSocieties/SearchSocieties.tsx new file mode 100644 index 0000000..c72f270 --- /dev/null +++ b/frontend/src/Settings/SettingsPage/SocietyManagementPage/SearchSocieties/SearchSocieties.tsx @@ -0,0 +1,124 @@ +import { UserGroupIcon } from "@heroicons/react/24/outline"; +import Button from "../../../../Button/Button"; +import { ButtonIcons, ButtonVariants } from "../../../../Button/ButtonTypes"; +import { TextInput, TextOptions } from "../../../../TextInput/TextInput"; +import { SettingsPage } from "../../SettingsPage"; +import { useContext, useEffect, useState } from "react"; +import { Society, UserContext } from "../../../../UserContext/UserContext"; +import classes from "./SearchSocieties.module.css"; + +export const SearchSocietiesPage = () => { + const [societyName, setSocietyName] = useState(""); + const [foundSocieties, setFoundSocieties] = useState([]); + const [error, setError] = useState(""); + const { societies, setSocieties } = useContext(UserContext); + useEffect(() => { + searchSocieties(""); + }, []); + + const searchSocieties = async (societyName: string) => { + const allSocieties = await fetch("http://localhost:5180/societies", { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + credentials: "include", + }); + + const societiesJson = await allSocieties.json(); + + const userSocieties = await fetch("http://localhost:5180/user/societies", { + method: "GET", + credentials: "include", + }); + + const userSocietiesJson = await userSocieties.json(); + setSocieties?.(userSocietiesJson); + + if (allSocieties.ok) { + setError(""); + setFoundSocieties(societiesJson); + if (societyName) { + setFoundSocieties( + societiesJson.filter((society: Society) => + society.name.toLowerCase().includes(societyName.toLowerCase()) + ) + ); + } + } else { + setError("Failed to fetch all societies."); + } + }; + + const joinSociety = async (society: Society) => { + const res = await fetch("http://localhost:5180/user/society/join", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + credentials: "include", + body: JSON.stringify({ societyId: society.id }), + }); + + if (res.ok) { + setSocieties?.({ + joined: [...(societies?.joined ?? []), society], + administering: societies?.administering ?? [], + }); + } else { + alert("Failed to join society"); + } + }; + + return ( + setSocietyName(name)} + icon={} + error={error !== ""} + noMargin + />, +