Skip to content
Merged
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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';

Expand All @@ -35,7 +36,11 @@ function App() {
<Route path="/logout" element={<LogoutRedirection />} />
<Route path="/opportunities" element={<Opportunities />} />
<Route path="/profile" element={<ProfilePage />} />
<Route path="/staff/department/:department" element={<Department />} />
<Route path="/saved" element={<SavedPage />} />
<Route
path="/staff/department/:department"
element={<Department />}
/>
<Route path="/staff" element={<Departments />} />
<Route path="/staff/:staffId" element={<StaffPage />} />
<Route path="/create" element={<CreatePost edit={false} />} />
Expand Down
Original file line number Diff line number Diff line change
@@ -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";
Expand Down
126 changes: 126 additions & 0 deletions src/individuals/pages/Saved.tsx
Original file line number Diff line number Diff line change
@@ -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 | Opportunity[]>(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 (
<section className="center container-xl">
<h1 className="text-center my-4 text-4xl font-extrabold leading-none tracking-tight text-gray-900 md:text-5xl lg:text-6xl">
Saved Opportunities
</h1>
{!saved && "Loading..."}
{saved && (
<table>
<tr>
<th>Name</th>
<th>Description</th>
<th>Recommended Experience</th>
<th>Pay</th>
<th>Credits</th>
<th>Semester</th>
<th>Year</th>
<th>Application Due</th>
<th>Location</th>
<th>Unsave</th>
</tr>
{saved.map((opportunity) => (
<tr key={opportunity.id}>
<td>{opportunity.name}</td>
<td>{opportunity.description}</td>
<td>{opportunity.recommended_experience}</td>
<td>{opportunity.pay}</td>
<td>{opportunity.credits}</td>
<td>{opportunity.semester}</td>
<td>{opportunity.year}</td>
<td style={{
color: (() => {
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")}
</td>
<td>{opportunity.location}</td>
<td>
<button className="p-2 bg-blue-600 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500"
onClick={async () => {
try {
const headers: Record<string, string> = {
"Content-Type": "application/json", // Good practice for cross-origin requests
};
if (csrfToken) {
headers["X-CSRF-TOKEN"] = csrfToken; // Include the token only when defined
}

const response = await fetch(
`${process.env.REACT_APP_BACKEND_SERVER}/unsaveOpportunity/${opportunity.id}`, {
method: "DELETE",
credentials: "include",
headers,
});

if (!response.ok) {
throw new Error("Failed to unsave");
}

setSaved(prev => prev ? prev.filter(o => o.id !== opportunity.id) : prev);
} catch {
console.log("Error unsaving opportunity");
}
}}
>
Unsave
</button>
</td>
</tr>
))}
</table>
)}
</section>
);
};
26 changes: 12 additions & 14 deletions src/shared/components/Navigation/MainNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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" : ""
}`
}
>
Expand All @@ -75,9 +75,8 @@ export default function MainNavigation() {
/>
<div className="w-11 h-6 bg-gray-300 dark:bg-gray-600 rounded-full" />
<div
className={`absolute left-1 top-1 bg-white w-4 h-4 rounded-full transition-transform ${
isDarkMode ? "translate-x-5" : ""
}`}
className={`absolute left-1 top-1 bg-white w-4 h-4 rounded-full transition-transform ${isDarkMode ? "translate-x-5" : ""
}`}
></div>
</div>
<span className="ml-3 text-white text-lg font-bold">
Expand Down Expand Up @@ -118,9 +117,8 @@ export default function MainNavigation() {
/>
<div className="w-11 h-6 bg-gray-300 dark:bg-gray-600 rounded-full peer-focus:outline-none peer-checked:bg-blue-600 relative transition-all duration-300">
<div
className={`absolute left-1 top-1 bg-white dark:bg-gray-300 w-4 h-4 rounded-full transition-transform duration-300 ${
isDarkMode ? "translate-x-5" : ""
}`}
className={`absolute left-1 top-1 bg-white dark:bg-gray-300 w-4 h-4 rounded-full transition-transform duration-300 ${isDarkMode ? "translate-x-5" : ""
}`}
></div>
</div>
<span className="ml-3 text-gray-700 dark:text-gray-200 font-medium">
Expand Down
2 changes: 1 addition & 1 deletion src/staff/pages/Departments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default function Departments() {
return (
<>
<SEO title="Departments - Labconnect" description="Labconnect departments page" />
<h1 className="mb-4 text-4xl font-extrabold leading-none tracking-tight text-gray-900 md:text-5xl lg:text-6xl">
<h1 className="text-center my-4 text-4xl font-extrabold leading-none tracking-tight text-gray-900 md:text-5xl lg:text-6xl">
Departments
</h1>
{!departments && "Loading..."}
Expand Down
13 changes: 13 additions & 0 deletions src/types/opportunity.ts
Original file line number Diff line number Diff line change
@@ -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;
}
9 changes: 9 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -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;
}