diff --git a/package-lock.json b/package-lock.json index c133b3b6..f8c91b75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3076,9 +3076,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001716", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001716.tgz", - "integrity": "sha512-49/c1+x3Kwz7ZIWt+4DvK3aMJy9oYXXG6/97JKsnjdCk/6n9vVyWL8NAwVt95Lwt9eigI10Hl782kDfZUUlRXw==", + "version": "1.0.30001751", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz", + "integrity": "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==", "dev": true, "funding": [ { diff --git a/src/App.tsx b/src/App.tsx index a085ce22..821ea1ed 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,7 +5,7 @@ import Home from "./shared/pages/Home.tsx"; import PageNotFound from "./shared/pages/404.tsx"; import MainNavigation from "./shared/components/Navigation/MainNavigation.tsx"; import StickyFooter from "./shared/components/Navigation/StickyFooter.tsx"; -import ProfilePage from "./shared/pages/Profile.tsx"; +import ProfilePage from "./individuals/pages/Profile.tsx"; import Departments from "./staff/pages/Departments.tsx"; import StaffPage from "./staff/pages/Staff.tsx"; import Department from "./staff/pages/Department.tsx"; @@ -15,6 +15,7 @@ import LogoutRedirection from "./auth/Logout.tsx"; import Token from "./auth/Token.tsx"; import Opportunities from "./opportunities/pages/Opportunities.tsx"; import IndividualPost from "./opportunities/pages/IndividualPost.tsx"; +import SavedPage from "./individuals/pages/Saved.tsx"; import { HelmetProvider } from 'react-helmet-async'; import { AuthProvider } from './context/AuthContext.tsx'; @@ -35,7 +36,11 @@ function App() { } /> } /> } /> - } /> + } /> + } + /> } /> } /> } /> diff --git a/src/shared/pages/Profile.tsx b/src/individuals/pages/Profile.tsx similarity index 94% rename from src/shared/pages/Profile.tsx rename to src/individuals/pages/Profile.tsx index e7f15c46..91487d4a 100644 --- a/src/shared/pages/Profile.tsx +++ b/src/individuals/pages/Profile.tsx @@ -1,6 +1,6 @@ import React, { useEffect } from "react"; import { useState } from "react"; -import ProfileComponents from "../components/Profile/ProfileComponents.tsx"; +import ProfileComponents from "../../shared/components/Profile/ProfileComponents.tsx"; import { useAuth } from "../../context/AuthContext.tsx"; import { Profile } from "../../types/profile.ts"; // import EditProfile from "./EditProfile"; diff --git a/src/individuals/pages/Saved.tsx b/src/individuals/pages/Saved.tsx new file mode 100644 index 00000000..5aba6c26 --- /dev/null +++ b/src/individuals/pages/Saved.tsx @@ -0,0 +1,126 @@ +import React, { useEffect } from "react"; +import { useState } from "react"; +import { useAuth } from "../../context/AuthContext.tsx"; +import { Opportunity } from "../../types/opportunity.ts"; +import { getCookie } from "../../utils.ts"; + +export default function SavedPage() { + const { auth } = useAuth(); + + if (!auth.isAuthenticated) { + window.location.href = "/login"; + } + + const [saved, setSaved] = useState(null); + + const csrfToken = getCookie('csrf_access_token'); + + const fetchSaved = async () => { + try { + const response = await fetch( + `${process.env.REACT_APP_BACKEND_SERVER}/savedOpportunities`, { + credentials: "include", + } + ); + + if (!response.ok) { + throw new Error("Saved not found"); + } + + const data = await response.json(); + setSaved(data); + console.log(data); + } catch { + console.log("Error fetching saved"); + } + } + + useEffect(() => { + fetchSaved(); + }, []); + + return ( +
+

+ Saved Opportunities +

+ {!saved && "Loading..."} + {saved && ( + + + + + + + + + + + + + + {saved.map((opportunity) => ( + + + + + + + + + + + + + ))} +
NameDescriptionRecommended ExperiencePayCreditsSemesterYearApplication DueLocationUnsave
{opportunity.name}{opportunity.description}{opportunity.recommended_experience}{opportunity.pay}{opportunity.credits}{opportunity.semester}{opportunity.year} { + const today = new Date(); + const dueDate = new Date(opportunity.application_due); + const oneWeek = 7 * 24 * 60 * 60 * 1000; + + if (dueDate < today) { + return "red"; + } else if (dueDate.getTime() - today.getTime() <= oneWeek) { + return "orange"; + } else { + return "black"; + } + })() + }}> + {new Date(opportunity.application_due).toLocaleDateString("en-US")} + {opportunity.location} + +
+ )} +
+ ); +}; diff --git a/src/shared/components/Navigation/MainNavigation.tsx b/src/shared/components/Navigation/MainNavigation.tsx index eafd057b..cd95f5a2 100644 --- a/src/shared/components/Navigation/MainNavigation.tsx +++ b/src/shared/components/Navigation/MainNavigation.tsx @@ -21,12 +21,13 @@ export default function MainNavigation() { // Define navigation routes based on authentication. const routes = auth.isAuthenticated ? [ - { name: "Opportunities", href: "/opportunities" }, - { name: "Create", href: "/create" }, - { name: "Staff", href: "/staff" }, - { name: "Profile", href: "/profile" }, - { name: "Sign Out", href: "/signout" } - ] + { name: "Opportunities", href: "/opportunities" }, + { name: "Create", href: "/create" }, + { name: "Staff", href: "/staff" }, + { name: "Profile", href: "/profile" }, + { name: "Saved", href: "/saved" }, + { name: "Sign Out", href: "/signout" } + ] : [{ name: "Sign In", href: "/signin" }]; return ( @@ -53,8 +54,7 @@ export default function MainNavigation() { key={item.name} to={item.href} className={({ isActive }) => - `text-2xl font-bold hover:underline ${ - isActive ? "underline" : "" + `text-2xl font-bold hover:underline ${isActive ? "underline" : "" }` } > @@ -75,9 +75,8 @@ export default function MainNavigation() { />
@@ -118,9 +117,8 @@ export default function MainNavigation() { />
diff --git a/src/staff/pages/Departments.tsx b/src/staff/pages/Departments.tsx index 1ac08851..4a004549 100644 --- a/src/staff/pages/Departments.tsx +++ b/src/staff/pages/Departments.tsx @@ -46,7 +46,7 @@ export default function Departments() { return ( <> -

+

Departments

{!departments && "Loading..."} diff --git a/src/types/opportunity.ts b/src/types/opportunity.ts new file mode 100644 index 00000000..48fc2ebd --- /dev/null +++ b/src/types/opportunity.ts @@ -0,0 +1,13 @@ +export type Opportunity = { + id: string; + name: string; + description: string; + recommended_experience?: string; + pay?: number; + credits?: string; + semester: string; + year: number; + application_due: string; + active: boolean; + location: string; +} \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 00000000..b91510b0 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,9 @@ +export function getCookie(name: string) { + const value = `; ${document.cookie}`; + const parts = value.split(`; ${name}=`); + if (parts.length === 2) { + const part = parts[1]; + return part.split(';')[0]; + } + return undefined; + } \ No newline at end of file