diff --git a/backend/generate-types.js b/backend/mysql.js
similarity index 100%
rename from backend/generate-types.js
rename to backend/mysql.js
diff --git a/backend/src/controllers/volunteerController.ts b/backend/src/controllers/volunteerController.ts
index 86feb2f..be474ac 100644
--- a/backend/src/controllers/volunteerController.ts
+++ b/backend/src/controllers/volunteerController.ts
@@ -139,10 +139,9 @@ async function getProfilePicture(req: Request, res: Response) {
try {
const profilePic = await volunteerModel.getProfilePicture(volunteer_id);
- res.writeHead(200, { 'Content-Type': 'image/jpeg' });
- res.end(profilePic, 'binary');
+ res.type('image/jpeg').send(profilePic.profile_pic);
} catch (error: any) {
- return res.status(error.status).json({
+ return res.status(error.status ?? 500).json({
error: error.message,
});
}
diff --git a/backend/src/models/volunteerModel.ts b/backend/src/models/volunteerModel.ts
index 6882193..600c2bf 100644
--- a/backend/src/models/volunteerModel.ts
+++ b/backend/src/models/volunteerModel.ts
@@ -212,7 +212,7 @@ export default class VolunteerModel {
message: `No profile picture found under the given volunteer ID`,
});
}
- resolve(results[0].profile_pic);
+ resolve(results[0]);
});
});
}
diff --git a/frontend/src/App.js b/frontend/src/App.js
index 9c84483..985fdc6 100644
--- a/frontend/src/App.js
+++ b/frontend/src/App.js
@@ -1,16 +1,18 @@
-import { BrowserRouter, Routes, Route } from "react-router-dom";
+// src/App.js
+import "notyf/notyf.min.css";
import { useEffect, useState } from "react";
+import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
import { isAuthenticated } from "./api/authService";
-import VolunteerDash from "./pages/volunteerDash";
-import VolunteerSignup from "./pages/VolunteerSignup";
-import VolunteerLogin from "./pages/VolunteerLogin";
+import VolunteerLayout from "./components/volunteerLayout";
+import AdminVerify from "./pages/AdminVerify";
+import Classes from "./pages/Classes";
+import VolunteerSchedule from "./pages/Schedule";
+import VolunteerDash from "./pages/VolunteerDash";
import VolunteerForgotPassword from "./pages/VolunteerForgotPassword";
+import VolunteerLogin from "./pages/VolunteerLogin";
+import VolunteerProfile from "./pages/VolunteerProfile";
import VolunteerResetPassword from "./pages/VolunteerResetPassword";
-import Classes from "./pages/classes";
-import VolunteerSchedule from "./pages/schedule";
-import VolunteerProfile from "./pages/volunteerProfile";
-import AdminVerify from "./pages/AdminVerify";
-import "notyf/notyf.min.css";
+import VolunteerSignup from "./pages/VolunteerSignup";
function App() {
const [isVolunteer, setIsVolunteer] = useState(false);
@@ -41,19 +43,35 @@ function App() {
+ {/* Public Routes */}
} />
} />
- }/>
- }/>
- : } />
- : } />
- : }/>
- : } />
+ } />
+ } />
+
+ {/* Volunteer Routes */}
+ : }
+ >
+ {/* Nested Routes within VolunteerLayout */}
+ } />
+
+ } />
+ } />
+ } />
+
+
+
+ {/* Admin Route (Assuming it's accessible without authentication or has its own protection) */}
} />
+
+ {/* Catch-all Route for Undefined Paths */}
+ } />
);
}
-export default App;
\ No newline at end of file
+export default App;
diff --git a/frontend/src/api/volunteerService.js b/frontend/src/api/volunteerService.js
index 862baa9..e1b176b 100644
--- a/frontend/src/api/volunteerService.js
+++ b/frontend/src/api/volunteerService.js
@@ -1,3 +1,4 @@
+import { backend } from '../data/constants';
import api from './api';
export const fetchVolunteerData = async (volunteer_id) => {
@@ -33,14 +34,7 @@ export const insertProfilePicture = async (profilePicData) => {
}
export const getProfilePicture = async (volunteer_id) => {
- try {
- const response = await api.get(`/volunteer/profile-picture/${volunteer_id}`, {
- responseType: 'blob'
- });
- return URL.createObjectURL(response.data);
- } catch (error) {
- console.error('Error getting profile picture:', error);
- }
+ return `${backend}/volunteer/profile-picture/${volunteer_id}`
}
export const updateProfilePicture = async (volunteer_id, profilePicData) => {
diff --git a/frontend/src/components/ImgFallback/index.css b/frontend/src/components/ImgFallback/index.css
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/src/components/ImgFallback/index.js b/frontend/src/components/ImgFallback/index.js
new file mode 100644
index 0000000..53e2196
--- /dev/null
+++ b/frontend/src/components/ImgFallback/index.js
@@ -0,0 +1,29 @@
+import React from "react";
+import "./index.css";
+
+
+export default function ProfileImg({
+ src,
+ name,
+ className,
+}) {
+ const fallbackUrl = `https://api.dicebear.com/9.x/initials/svg?seed=${encodeURIComponent(name)}`;
+
+ const handleImageError = (e) => {
+ if (e.target.src !== fallbackUrl) {
+ e.target.src = fallbackUrl;
+ }
+ };
+
+ if (name)
+ return
+ else
+ return null
+}
diff --git a/frontend/src/components/NavProfileCard/index.css b/frontend/src/components/NavProfileCard/index.css
index 1a1b804..52dd564 100644
--- a/frontend/src/components/NavProfileCard/index.css
+++ b/frontend/src/components/NavProfileCard/index.css
@@ -20,6 +20,7 @@
flex-direction: row;
gap: 0.5rem;
align-items: center;
+ min-width: 0;
}
.nav-profile-card__avatar, .nav-profile-card__avatar-collapse {
@@ -43,12 +44,15 @@
.nav-profile-card__info {
display: flex;
flex-direction: column;
- gap:0.2rem;
+ gap: 0.2rem;
+ min-width: 0;
}
.nav-profile-card__name, .nav-profile-card__email {
margin: 0;
-
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
.nav-profile-card__name {
diff --git a/frontend/src/components/NavProfileCard/index.js b/frontend/src/components/NavProfileCard/index.js
index e816ba5..82d716a 100644
--- a/frontend/src/components/NavProfileCard/index.js
+++ b/frontend/src/components/NavProfileCard/index.js
@@ -1,5 +1,6 @@
import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
import React from "react";
+import ProfileImg from "../ImgFallback";
import "./index.css";
import { useNavigate } from "react-router-dom";
@@ -18,31 +19,19 @@ export default function NavProfileCard({
return collapse ? (
- {avatar ? (
-
- ) : (
-
- )}
+
) : (
- {avatar ? (
-
- ) : (
-
- )}
+
{name}
diff --git a/frontend/src/components/volunteerLayout/index.js b/frontend/src/components/volunteerLayout/index.js
index 77ba014..c6bd6d3 100644
--- a/frontend/src/components/volunteerLayout/index.js
+++ b/frontend/src/components/volunteerLayout/index.js
@@ -1,16 +1,16 @@
-import "./index.css";
-import { NavLink } from "react-router-dom";
-import { useState, useEffect } from "react";
+import { React, useEffect, useState } from "react";
+import { NavLink, Outlet } from "react-router-dom";
import BC_brain from "../../assets/bwp-logo-text.png";
-import sidebar_toggle from "../../assets/sidebar-toggle.png";
+import nav_item_classes from "../../assets/nav-item-classes.png";
import nav_item_dash from "../../assets/nav-item-dash.png";
import nav_item_schedule from "../../assets/nav-item-sched.png";
-import nav_item_classes from "../../assets/nav-item-classes.png";
import nav_item_settings from "../../assets/nav-item-settings.png";
+import sidebar_toggle from "../../assets/sidebar-toggle.png";
+import "./index.css";
-import NavProfileCard from "../NavProfileCard";
import { isAuthenticated } from "../../api/authService";
import { getProfilePicture } from "../../api/volunteerService";
+import NavProfileCard from "../NavProfileCard";
function VolunteerLayout({ pageTitle, children, pageStyle }) {
const [collapsed, setCollapsed] = useState(window.innerWidth <= 800);
@@ -118,23 +118,15 @@ function VolunteerLayout({ pageTitle, children, pageStyle }) {
name={volunteer?.f_name}
email={volunteer?.email}
collapse={collapsed}
- link={"/volunteer/my-profile"}
+ link="/volunteer/my-profile"
/>
{pageTitle}
- {pageTitle === "My Profile" && (
-
- )}
- {children} {/* Render page content here */}
+ {/* Render page content here */}
);
diff --git a/frontend/src/components/volunteerProfile/volunteerDetailsCard/index.js b/frontend/src/components/volunteerProfile/volunteerDetailsCard/index.js
index 9500217..fc981e4 100644
--- a/frontend/src/components/volunteerProfile/volunteerDetailsCard/index.js
+++ b/frontend/src/components/volunteerProfile/volunteerDetailsCard/index.js
@@ -5,6 +5,7 @@ import camera_icon from "../../../assets/camera.png";
import cancel_icon from "../../../assets/cancel-icon.png";
import check_icon from "../../../assets/check-icon.png";
import edit_icon from "../../../assets/edit-icon.png";
+import ProfileImg from "../../ImgFallback";
import { CgSelect } from "react-icons/cg";
import { insertProfilePicture, updateProfilePicture, updateVolunteerData } from "../../../api/volunteerService";
@@ -160,7 +161,11 @@ function VolunteerDetailsCard({ volunteer }) {
document.getElementById('fileInput').click()
}}
>
-
+
{isEditing &&
Edit
@@ -189,7 +194,7 @@ function VolunteerDetailsCard({ volunteer }) {
'font-style': 'italic'
}}
hidden={isEditing}>
- {mutableData.preferredName ? mutableData.preferredName : "not yet set"}
+ {mutableData.preferredName ?? "not yet set"}
@@ -204,7 +209,7 @@ function VolunteerDetailsCard({ volunteer }) {
'font-style': 'italic'
}}
hidden={isEditing}>
- {mutableData.pronouns ? mutableData.pronouns : "not yet set"}
+ {mutableData.pronouns ?? "not yet set"}
|
{isEditing && (
|
Location |
- {volunteer.city}, {volunteer.province} |
+ {volunteer.city && volunteer.province ? `${volunteer.city}, ${volunteer.province}` : 'not yet set'} |
diff --git a/frontend/src/pages/classes/index.js b/frontend/src/pages/classes/index.js
index 42720fb..68b9de2 100644
--- a/frontend/src/pages/classes/index.js
+++ b/frontend/src/pages/classes/index.js
@@ -1,13 +1,12 @@
-import "./index.css";
-import React, { useEffect, useState, useRef } from "react";
-import VolunteerLayout from "../../components/volunteerLayout";
-import DetailsPanel from "../../components/DetailsPanel";
+import React, { useEffect, useRef, useState } from "react";
import {
getAllClasses,
getAllClassImages,
getAllClassSchedules,
} from "../../api/classesPageService";
import ClassCategoryContainer from "../../components/ClassCategoryContainer";
+import DetailsPanel from "../../components/DetailsPanel";
+import "./index.css";
function Classes() {
const [completeClassData, setCompleteClassData] = useState(null);
@@ -139,7 +138,10 @@ function Classes() {
};
return (
-
+
+
+
Classes
+
-
+
);
}
diff --git a/frontend/src/pages/schedule/index.js b/frontend/src/pages/schedule/index.js
index 81c92c2..44cda38 100644
--- a/frontend/src/pages/schedule/index.js
+++ b/frontend/src/pages/schedule/index.js
@@ -1,12 +1,11 @@
-import "./index.css";
-import { useState, useEffect, useRef, useCallback } from 'react';
-import VolunteerLayout from "../../components/volunteerLayout";
import dayjs from 'dayjs';
+import { useCallback, useEffect, useRef, useState } from 'react';
+import { getVolunteerShiftsForMonth } from "../../api/shiftService";
import DateToolbar from "../../components/DateToolbar";
+import DetailsPanel from "../../components/DetailsPanel";
import ShiftCard from "../../components/ShiftCard";
import ShiftStatusToolbar from "../../components/ShiftStatusToolbar";
-import { getVolunteerShiftsForMonth } from "../../api/shiftService";
-import DetailsPanel from "../../components/DetailsPanel";
+import "./index.css";
function VolunteerSchedule() {
const volunteerID = localStorage.getItem('volunteerID');
@@ -99,8 +98,10 @@ function VolunteerSchedule() {
};
return (
-
+
+
+
Schedule
+
-
+
);
}
diff --git a/frontend/src/pages/volunteerDash/index.js b/frontend/src/pages/volunteerDash/index.js
index 986ff95..5fc9edb 100644
--- a/frontend/src/pages/volunteerDash/index.js
+++ b/frontend/src/pages/volunteerDash/index.js
@@ -1,33 +1,26 @@
// home/ is the landing page of the application.
-import "./index.css";
-import React from "react";
-import { useEffect, useState, useRef } from "react";
-import { useNavigate } from "react-router-dom";
-import VolunteerLayout from "../../components/volunteerLayout";
-import { isAuthenticated } from "../../api/authService";
import ArrowForwardIcon from "@mui/icons-material/ArrowForwardIos";
import HelpOutlineIcon from "@mui/icons-material/HelpOutline";
-import DashShifts from "../../components/DashShifts";
-import DashCoverShifts from "../../components/DashCoverShifts";
+import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import { DatePicker } from "@mui/x-date-pickers/DatePicker";
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
-import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import dayjs from "dayjs";
-import DashboardCoverage from "../../components/DashboardCoverage";
+import React, { useEffect, useState } from "react";
import { getVolunteerShiftsForMonth } from "../../api/shiftService";
+import DashboardCoverage from "../../components/DashboardCoverage";
+import DashCoverShifts from "../../components/DashCoverShifts";
+import DashShifts from "../../components/DashShifts";
import { SHIFT_TYPES } from "../../data/constants";
+import "./index.css";
function VolunteerDash() {
const volunteerID = localStorage.getItem("volunteerID");
- const [loading, setLoading] = useState(true);
const [checkIn, setCheckIn] = useState(false);
const [shifts, setShifts] = useState([]);
const monthDate = dayjs().date(1).hour(0).minute(0);
const [selectedDate, setSelectedDate] = useState(dayjs());
const [future, setFuture] = useState(false);
- const navigate = useNavigate();
-
const checkInItem = () => {
return checkIn ? (
@@ -51,19 +44,6 @@ function VolunteerDash() {
);
};
- useEffect(() => {
- isAuthenticated()
- .then((response) => {
- console.log(response);
- if (!response.isAuthenticated) {
- navigate("/auth/login");
- }
- })
- .catch((error) => {
- console.error(error);
- });
- }, []);
-
useEffect(() => {
const fetchShifts = async () => {
const body = {
@@ -133,7 +113,10 @@ function VolunteerDash() {
}, [selectedDate, shifts]);
return (
-
+
+
+
Dashboard
+
-
+
);
}
diff --git a/frontend/src/pages/volunteerProfile/index.js b/frontend/src/pages/volunteerProfile/index.js
index eb7c0d6..0423f81 100644
--- a/frontend/src/pages/volunteerProfile/index.js
+++ b/frontend/src/pages/volunteerProfile/index.js
@@ -1,13 +1,10 @@
// volunteer profile page
-import "./index.css";
import React, { useState } from 'react';
-import VolunteerLayout from "../../components/volunteerLayout";
import { fetchVolunteerData, getProfilePicture } from "../../api/volunteerService";
-import VolunteerDetailsCard from "../../components/volunteerProfile/volunteerDetailsCard";
-import ChangePasswordCard from "../../components/volunteerProfile/changePasswordCard";
import AvailabilityGrid from "../../components/volunteerProfile/availabilityGrid";
-
-const pageTitle = "My Profile";
+import ChangePasswordCard from "../../components/volunteerProfile/changePasswordCard";
+import VolunteerDetailsCard from "../../components/volunteerProfile/volunteerDetailsCard";
+import "./index.css";
function VolunteerProfile() {
const [availability, setAvailability] = useState([]);
@@ -31,12 +28,18 @@ function VolunteerProfile() {
}, []);
return (
-
+
+
+
My Profile
+
+
{ volunteer ?
@@ -59,7 +62,7 @@ function VolunteerProfile() {
:
Loading...
}
-
+
);
};