From 68fba563fc695fd16a794471a7359721dffdde3c Mon Sep 17 00:00:00 2001 From: Gaurav Bomble <114927759+Gauravtb2253@users.noreply.github.com> Date: Sun, 10 Nov 2024 16:17:20 +0530 Subject: [PATCH] added feature to apply for position (#184) --- backend/src/controllers/hiring.controller.ts | 47 +++++- backend/src/routes/hiring.routes.ts | 2 + frontend/dev-dist/sw.js | 3 +- .../appComponents/HiringSessionClubCard.tsx | 158 +++++++++++++++++- frontend/src/appComponents/ViewSession.tsx | 70 ++++++++ 5 files changed, 265 insertions(+), 15 deletions(-) diff --git a/backend/src/controllers/hiring.controller.ts b/backend/src/controllers/hiring.controller.ts index cf27317..c8786d4 100644 --- a/backend/src/controllers/hiring.controller.ts +++ b/backend/src/controllers/hiring.controller.ts @@ -58,12 +58,14 @@ export const GetHiringSessions = async (req: Request, res: Response) => { ClubID: parseInt(ClubID as string), }, include: { - Positions: true, + Positions: true, }, }); if (hiringSessions.length === 0) { - return res.status(404).json({ message: "No hiring sessions found for this club." }); + return res + .status(404) + .json({ message: "No hiring sessions found for this club." }); } res.status(200).json(hiringSessions); @@ -77,7 +79,7 @@ export const GetAllHiringSessions = async (req: Request, res: Response) => { try { const hiringSessions = await prisma.hiringSession.findMany({ include: { - Positions: true, + Positions: true, }, }); @@ -247,7 +249,9 @@ export const GetPositionsBySession = async (req: Request, res: Response) => { }); if (!positions.length) { - return res.status(404).json({ error: "No positions found for this session." }); + return res + .status(404) + .json({ error: "No positions found for this session." }); } res.status(200).json(positions); @@ -345,7 +349,7 @@ export const CreateApplicant = async (req: Request, res: Response) => { ResumeURL: resume, }, }); - if(!applicant){ + if (!applicant) { return res.status(500).json({ error: "Error creating applicant" }); } await prisma.hiringApplication.create({ @@ -374,3 +378,36 @@ export const CreateApplicant = async (req: Request, res: Response) => { res.status(500).json({ error: "Error creating applicant" }); } }; + +export const getApplicantsByPositionID = async ( + req: Request, + res: Response +) => { + const { PositionID } = req.query; + if (!PositionID || isNaN(parseInt(PositionID as string))) { + return res.status(400).json({ error: "Invalid or missing PositionID" }); + } + + try { + const applicants = await prisma.hiringApplication.findMany({ + where: { + Position: { + PositionID: parseInt(PositionID as string), + }, + }, + include: { + Applicant: true + }, + }); + if (!applicants.length) { + return res + .status(404) + .json({ error: "No Applications found for this session." }); + } + + res.status(200).json(applicants); + } catch (error) { + console.error(`Error retrieving Applications: ${error}`); + res.status(500).json({ error: "Error retrieving Applications" }); + } +}; diff --git a/backend/src/routes/hiring.routes.ts b/backend/src/routes/hiring.routes.ts index 9c4811f..2d879e6 100644 --- a/backend/src/routes/hiring.routes.ts +++ b/backend/src/routes/hiring.routes.ts @@ -11,6 +11,7 @@ import { GetHiringSessions, GetPositionsBySession, GetAllHiringSessions, + getApplicantsByPositionID, } from "../controllers/hiring.controller"; // @ts-ignore import jwt from "jsonwebtoken"; @@ -74,5 +75,6 @@ router.delete("/DeleteHiringPosition", checkAuth, DeleteHiringPosition); router.put("/updateHiringPosition", checkAuth, UpdateHiringPosition); router.get("/getPositions", GetPositionsBySession); router.post("/applyForPosition", CreateApplicant); +router.get("/getApplicantsByPositionId", getApplicantsByPositionID); export default router; diff --git a/frontend/dev-dist/sw.js b/frontend/dev-dist/sw.js index fd80340..308bae9 100644 --- a/frontend/dev-dist/sw.js +++ b/frontend/dev-dist/sw.js @@ -82,8 +82,7 @@ define(['./workbox-56da8dea'], (function (workbox) { 'use strict'; "revision": "3ca0b8505b4bec776b69afdba2768812" }, { "url": "index.html", - "revision": "0.ai1r4vcrr78" - + "revision": "0.p4l2jp7sac" }], {}); workbox.cleanupOutdatedCaches(); workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { diff --git a/frontend/src/appComponents/HiringSessionClubCard.tsx b/frontend/src/appComponents/HiringSessionClubCard.tsx index 90967d2..f52aff3 100644 --- a/frontend/src/appComponents/HiringSessionClubCard.tsx +++ b/frontend/src/appComponents/HiringSessionClubCard.tsx @@ -31,6 +31,18 @@ const HiringSessionClubCard: React.FC = ({ }) => { const [sessions, setSessions] = useState([]); const [expanded, setExpanded] = useState(false); + const [isModalOpen, setIsModalOpen] = useState(false); + const [selectedPosition, setSelectedPosition] = useState( + null + ); + + const [applicantData, setApplicantData] = useState({ + name: "", + yearOfStudy: new Date().getFullYear(), + department: "", + phoneNumber: "", + resume: null as File | null, + }); const handleToggleSessions = async () => { if (!expanded) { @@ -49,6 +61,50 @@ const HiringSessionClubCard: React.FC = ({ setExpanded(!expanded); }; + const handleApply = (position: PositionType) => { + setSelectedPosition(position); + setIsModalOpen(true); + }; + + const handleCloseModal = () => { + setIsModalOpen(false); + setSelectedPosition(null); + }; + + const handleInputChange = ( + e: React.ChangeEvent + ) => { + const { name, value } = e.target; + setApplicantData((prev) => ({ + ...prev, + [name]: value, + })); + }; + + const handleSubmitApplication = async () => { + if (!selectedPosition) return; + + const applicationData = { + name: applicantData.name, + yearOfStudy: parseInt(applicantData.yearOfStudy as unknown as string, 10), + department: applicantData.department, + phone: applicantData.phoneNumber, + resume: "", + }; + + try { + await axios.post( + `/api/hiring/applyForPosition?PositionID=${selectedPosition.PositionID}`, + applicationData + ); + alert("Application submitted successfully!"); + handleCloseModal(); + } catch (error) { + console.error("Error submitting application:", error); + alert("Failed to submit application."); + } + }; + return (
@@ -62,39 +118,56 @@ const HiringSessionClubCard: React.FC = ({

{description}

- {/* Collapsible sessions list */} {expanded && (
-

Hiring Sessions

+

+ Hiring Sessions +

{sessions.length > 0 ? (
    {sessions.map((session) => ( -
  • +
  • {session.Title}

    -

    {session.Description}

    +

    + {session.Description} +

    Date: {new Date(session.StartDate).toLocaleDateString()} -{" "} {new Date(session.EndDate).toLocaleDateString()}

    - {/* Positions within each session */} {session.Positions.length > 0 ? (
      {session.Positions.map((position) => (
    • -

      {position.Title}

      -

      {position.Description}

      +

      + {position.Title} +

      +

      + {position.Description} +

      Spots Available: {position.Spots}

      +
    • ))}
    ) : ( -

    No positions available.

    +

    + No positions available. +

    )}
  • ))} @@ -106,6 +179,75 @@ const HiringSessionClubCard: React.FC = ({ )}
)} + + {isModalOpen && selectedPosition && ( +
+
+

+ Apply for {selectedPosition.Title} +

+
+ + + + + + setApplicantData({ + ...applicantData, + resume: e.target.files?.[0] || null, + }) + } + className="w-full p-2 border border-gray-300 rounded mb-3" + /> + + + +
+
+
+ )}
); }; diff --git a/frontend/src/appComponents/ViewSession.tsx b/frontend/src/appComponents/ViewSession.tsx index 6348e73..08f5ccd 100644 --- a/frontend/src/appComponents/ViewSession.tsx +++ b/frontend/src/appComponents/ViewSession.tsx @@ -32,6 +32,8 @@ export const ViewSession = () => { >([]); const [isEdit, setIsEdit] = useState(false); const [selectedPosition, setSelectedPosition] = useState(null); + const [showApplicantsModal, setShowApplicantsModal] = useState(false); + const [applicants, setApplicants] = useState([]); useEffect(() => { const fetchPositions = async () => { @@ -145,6 +147,21 @@ export const ViewSession = () => { setShowModal(true); }; + const handleViewApplicants = async (positionId: number) => { + try { + const response = await axios.get( + `/api/hiring/getApplicantsByPositionId?PositionID=${positionId}`, + { + headers: { Authorization: `Bearer ${token}` }, + } + ); + setApplicants(response.data); + setShowApplicantsModal(true); + } catch (error) { + console.error("Error fetching applicants", error); + } + }; + return (
{ > +
))} @@ -281,6 +304,53 @@ export const ViewSession = () => { )} + + {showApplicantsModal && ( +
+
+

Applicants

+

Total applicants: {applicants.length}

+
    + {applicants.map((applicant, index) => ( +
  • +

    + Applicant Name: {applicant.Applicant.Name} +

    +

    + Year of Study:{" "} + {applicant.Applicant.YearOfStudy} +

    +

    + Department:{" "} + {applicant.Applicant.Department} +

    +

    + Phone Number:{" "} + {applicant.Applicant.PhoneNumber} +

    +

    + Resume URL:{" "} + + {applicant.Applicant.ResumeURL} + +

    +
  • + ))} +
+ +
+
+ )} ); };