diff --git a/AceJobAgency.client/src/App.tsx b/AceJobAgency.client/src/App.tsx index b33c6bf..eca7094 100644 --- a/AceJobAgency.client/src/App.tsx +++ b/AceJobAgency.client/src/App.tsx @@ -2,6 +2,8 @@ import { Route, Routes } from "react-router-dom"; import DefaultLayout from "./layouts/DefaultLayout"; import HomePage from "./pages/HomePage"; import ErrorPage from "./pages/ErrorPage"; +import { getAccessToken } from "./http"; +import MemberPage from "./pages/MemberPage"; export default function App() { return ( @@ -9,7 +11,10 @@ export default function App() { }> - } /> + : } + /> } /> diff --git a/AceJobAgency.client/src/components/LoginView.tsx b/AceJobAgency.client/src/components/LoginView.tsx index a5a41fc..4860cd9 100644 --- a/AceJobAgency.client/src/components/LoginView.tsx +++ b/AceJobAgency.client/src/components/LoginView.tsx @@ -1,7 +1,46 @@ import { Input, Checkbox, Button, Link } from "@heroui/react"; import { IconMail, IconLock } from "@tabler/icons-react"; +import { useState } from "react"; +import { toast } from "react-toastify"; +import http, { login } from "../http"; export default function LoginView({ onSignup }: { onSignup: () => void }) { + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + + const validateFields = () => { + if (!email || !password) { + toast.error("Both email and password are required."); + return false; + } + return true; + }; + + const handleLogin = async () => { + if (!validateFields()) return; + + const loginRequest = { + email, + password, + }; + + try { + const response = await http.post("/User/login", loginRequest); + + if (response.status !== 200) { + throw new Error("Login failed"); + } + + const { token } = response.data; + login(token); + } catch (error) { + toast.error( + (error as any).response?.data || + "Something went wrong! Please try again." + ); + } + }; + return (
@@ -11,8 +50,20 @@ export default function LoginView({ onSignup }: { onSignup: () => void }) {

- } label="Email" /> - } label="Password" type="password" /> + } + label="Email" + type="email" + value={email} + onValueChange={setEmail} + /> + } + label="Password" + type="password" + value={password} + onValueChange={setPassword} + />
void }) {
-

Don't have an account?

- { - onSignup(); - }} - className="text-sm" - > + Sign up
diff --git a/AceJobAgency.client/src/components/NavigationBar.tsx b/AceJobAgency.client/src/components/NavigationBar.tsx index 2cf572b..0b47a48 100644 --- a/AceJobAgency.client/src/components/NavigationBar.tsx +++ b/AceJobAgency.client/src/components/NavigationBar.tsx @@ -16,11 +16,13 @@ import { useState } from "react"; import { useNavigate } from "react-router-dom"; import SignupView from "./SignupView"; import LoginView from "./LoginView"; +import { getAccessToken } from "../http"; export default function NavigationBar() { const navigate = useNavigate(); const { isOpen, onOpen, onOpenChange } = useDisclosure(); const [isSignup, setIsSignup] = useState(false); + const accessToken = getAccessToken(); return ( @@ -39,30 +41,32 @@ export default function NavigationBar() { /> - - - - - - - - + {!accessToken && ( + + + + + + + + + )} {() => ( diff --git a/AceJobAgency.client/src/http.ts b/AceJobAgency.client/src/http.ts index 6a24c54..6691281 100644 --- a/AceJobAgency.client/src/http.ts +++ b/AceJobAgency.client/src/http.ts @@ -41,4 +41,27 @@ http.interceptors.response.use( } ); +export function login(token: string) { + setAccessToken(token); + window.location.reload(); +} + +export function logout() { + clearAccessToken(); + window.location.reload(); +} + +export function getAccessToken() { + return localStorage.getItem("accessToken"); +} + +function setAccessToken(token: string) { + clearAccessToken(); + localStorage.setItem("accessToken", token); +} + +function clearAccessToken() { + localStorage.clear(); +} + export default http; diff --git a/AceJobAgency.client/src/models/user-profile.ts b/AceJobAgency.client/src/models/user-profile.ts new file mode 100644 index 0000000..55f530e --- /dev/null +++ b/AceJobAgency.client/src/models/user-profile.ts @@ -0,0 +1,10 @@ +export interface UserProfile { + id: string; + email: string; + nationalRegistrationIdentityCardNumber: string; + firstName: string; + lastName: string; + dateOfBirth: string; + whoAmI: string; + resumeName: string; +} diff --git a/AceJobAgency.client/src/pages/HomePage.tsx b/AceJobAgency.client/src/pages/HomePage.tsx index b4d6de1..361c099 100644 --- a/AceJobAgency.client/src/pages/HomePage.tsx +++ b/AceJobAgency.client/src/pages/HomePage.tsx @@ -9,15 +9,22 @@ import { ModalHeader, useDisclosure, } from "@heroui/react"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import LoginView from "../components/LoginView"; import SignupView from "../components/SignupView"; +import { getAccessToken } from "../http"; export default function HomePage() { const { isOpen, onOpen, onOpenChange } = useDisclosure(); const [isSignup, setIsSignup] = useState(false); const [emailValue, setEmailValue] = useState(""); + useEffect(() => { + if (getAccessToken()) { + window.location.reload(); + } + }, []); + return (
diff --git a/AceJobAgency.client/src/pages/MemberPage.tsx b/AceJobAgency.client/src/pages/MemberPage.tsx new file mode 100644 index 0000000..44add7a --- /dev/null +++ b/AceJobAgency.client/src/pages/MemberPage.tsx @@ -0,0 +1,86 @@ +import { useEffect, useState } from "react"; +import http, { getAccessToken, logout } from "../http"; +import { useNavigate } from "react-router-dom"; +import { UserProfile } from "../models/user-profile"; +import { Button, Card, Divider, Input } from "@heroui/react"; + +export default function MemberPage() { + const accessToken = getAccessToken(); + const navigate = useNavigate(); + + const [userProfile, setUserProfile] = useState(null); + + useEffect(() => { + if (!accessToken) { + window.location.reload(); + } + http.get("/User/profile").then((response) => { + if (response.status !== 200) { + navigate("/error"); + } + setUserProfile(response.data); + }); + }, []); + + return ( +
+ {userProfile && ( + +
+

+ {userProfile.firstName} {userProfile.lastName} +

+

Ace Job Agency Member (Tier 3)

+
+ +
+
+ + + +
+

Resume

+ +
+
+
+

Who am I

+ + {userProfile.whoAmI.length > 0 + ? userProfile.whoAmI + : "You have not wrote anything about yourself."} + +
+
+ +
+ + +
+
+ )} +
+ ); +} + +function InfoCell({ label, value }: { label: string; value: string }) { + return ( +
+

{label}

+ +
+ ); +}