Skip to content

Commit

Permalink
added feature to apply for position (#184)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gauravtb2253 authored Nov 10, 2024
1 parent 5b14cc4 commit 68fba56
Show file tree
Hide file tree
Showing 5 changed files with 265 additions and 15 deletions.
47 changes: 42 additions & 5 deletions backend/src/controllers/hiring.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -77,7 +79,7 @@ export const GetAllHiringSessions = async (req: Request, res: Response) => {
try {
const hiringSessions = await prisma.hiringSession.findMany({
include: {
Positions: true,
Positions: true,
},
});

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -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" });
}
};
2 changes: 2 additions & 0 deletions backend/src/routes/hiring.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
GetHiringSessions,
GetPositionsBySession,
GetAllHiringSessions,
getApplicantsByPositionID,
} from "../controllers/hiring.controller";
// @ts-ignore
import jwt from "jsonwebtoken";
Expand Down Expand Up @@ -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;
3 changes: 1 addition & 2 deletions frontend/dev-dist/sw.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"), {
Expand Down
158 changes: 150 additions & 8 deletions frontend/src/appComponents/HiringSessionClubCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ const HiringSessionClubCard: React.FC<HiringSessionClubCardType> = ({
}) => {
const [sessions, setSessions] = useState<SessionType[]>([]);
const [expanded, setExpanded] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false);
const [selectedPosition, setSelectedPosition] = useState<PositionType | null>(
null
);

const [applicantData, setApplicantData] = useState({
name: "",
yearOfStudy: new Date().getFullYear(),
department: "",
phoneNumber: "",
resume: null as File | null,
});

const handleToggleSessions = async () => {
if (!expanded) {
Expand All @@ -49,6 +61,50 @@ const HiringSessionClubCard: React.FC<HiringSessionClubCardType> = ({
setExpanded(!expanded);
};

const handleApply = (position: PositionType) => {
setSelectedPosition(position);
setIsModalOpen(true);
};

const handleCloseModal = () => {
setIsModalOpen(false);
setSelectedPosition(null);
};

const handleInputChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>
) => {
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 (
<div className="bg-black hover:scale-[101%] transform transition-all duration-500 text-gray-900 rounded-lg shadow-lg p-6 mb-4">
<div className="flex justify-between items-center">
Expand All @@ -62,39 +118,56 @@ const HiringSessionClubCard: React.FC<HiringSessionClubCardType> = ({
</div>
<p className="text-white mt-2">{description}</p>

{/* Collapsible sessions list */}
{expanded && (
<div className="mt-4 p-4 bg-gray-800 rounded-lg">
<h3 className="text-lg font-semibold text-white mb-3">Hiring Sessions</h3>
<h3 className="text-lg font-semibold text-white mb-3">
Hiring Sessions
</h3>

{sessions.length > 0 ? (
<ul className="space-y-4">
{sessions.map((session) => (
<li key={session.SessionID} className="p-4 bg-gray-900 rounded-lg shadow">
<li
key={session.SessionID}
className="p-4 bg-gray-900 rounded-lg shadow"
>
<div className="mb-2">
<p className="font-bold text-white">{session.Title}</p>
<p className="text-sm text-gray-400 mb-1">{session.Description}</p>
<p className="text-sm text-gray-400 mb-1">
{session.Description}
</p>
<p className="text-sm text-gray-400">
Date: {new Date(session.StartDate).toLocaleDateString()} -{" "}
{new Date(session.EndDate).toLocaleDateString()}
</p>
</div>

{/* Positions within each session */}
{session.Positions.length > 0 ? (
<ul className="mt-2 space-y-3 pl-4 border-l border-gray-700">
{session.Positions.map((position) => (
<li key={position.PositionID} className="pl-2">
<p className="text-white font-semibold">{position.Title}</p>
<p className="text-sm text-gray-400">{position.Description}</p>
<p className="text-white font-semibold">
{position.Title}
</p>
<p className="text-sm text-gray-400">
{position.Description}
</p>
<p className="text-sm text-gray-500">
Spots Available: {position.Spots}
</p>
<button
onClick={() => handleApply(position)}
className="mt-2 px-3 py-1 text-sm font-bold text-white bg-blue-600 rounded-md hover:bg-blue-700 transition-all"
>
Apply
</button>
</li>
))}
</ul>
) : (
<p className="text-sm text-gray-400 mt-2">No positions available.</p>
<p className="text-sm text-gray-400 mt-2">
No positions available.
</p>
)}
</li>
))}
Expand All @@ -106,6 +179,75 @@ const HiringSessionClubCard: React.FC<HiringSessionClubCardType> = ({
)}
</div>
)}

{isModalOpen && selectedPosition && (
<div className="fixed inset-0 flex items-center justify-center z-50 bg-black bg-opacity-50">
<div className="bg-white p-6 rounded-lg shadow-lg max-w-md w-full">
<h3 className="text-lg font-bold mb-4">
Apply for {selectedPosition.Title}
</h3>
<form>
<input
type="text"
name="name"
placeholder="Full Name"
value={applicantData.name}
onChange={handleInputChange}
className="w-full p-2 border border-gray-300 rounded mb-3"
/>
<input
type="number"
name="yearOfStudy"
placeholder="Year of Study"
value={applicantData.yearOfStudy}
onChange={handleInputChange}
className="w-full p-2 border border-gray-300 rounded mb-3"
/>
<input
type="text"
name="department"
placeholder="Department"
value={applicantData.department}
onChange={handleInputChange}
className="w-full p-2 border border-gray-300 rounded mb-3"
/>
<input
type="text"
name="phoneNumber"
placeholder="Phone Number"
value={applicantData.phoneNumber}
onChange={handleInputChange}
className="w-full p-2 border border-gray-300 rounded mb-3"
/>
<input
type="file"
onChange={(e) =>
setApplicantData({
...applicantData,
resume: e.target.files?.[0] || null,
})
}
className="w-full p-2 border border-gray-300 rounded mb-3"
/>

<button
type="button"
onClick={handleSubmitApplication}
className="w-full py-2 bg-blue-600 text-white font-bold rounded hover:bg-blue-700 transition-all"
>
Submit Application
</button>
<button
type="button"
onClick={handleCloseModal}
className="mt-2 w-full py-2 bg-gray-300 text-gray-700 font-bold rounded hover:bg-gray-400 transition-all"
>
Cancel
</button>
</form>
</div>
</div>
)}
</div>
);
};
Expand Down
Loading

0 comments on commit 68fba56

Please sign in to comment.