diff --git a/backend/prisma/migrations/20260313093706_create_candidate/migration.sql b/backend/prisma/migrations/20260313093706_create_candidate/migration.sql new file mode 100644 index 0000000..95d1200 --- /dev/null +++ b/backend/prisma/migrations/20260313093706_create_candidate/migration.sql @@ -0,0 +1,17 @@ +-- CreateTable +CREATE TABLE "Candidate" ( + "id" SERIAL NOT NULL, + "fullName" TEXT NOT NULL, + "mobile" TEXT NOT NULL, + "email" TEXT NOT NULL, + "subject" TEXT NOT NULL, + "coverLetter" TEXT NOT NULL, + "careerId" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Candidate_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "Candidate" ADD CONSTRAINT "Candidate_careerId_fkey" FOREIGN KEY ("careerId") REFERENCES "Career"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 9333495..4ad39fb 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -27,6 +27,7 @@ model Doctor { qualification String? departments DoctorDepartment[] + appointments Appointment[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@ -45,6 +46,7 @@ model Department { services String? doctors DoctorDepartment[] + appointments Appointment[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@ -109,6 +111,42 @@ model Career { number String? status String @default("new") + candidates Candidate[] + createdAt DateTime @default(now()) updatedAt DateTime @updatedAt +} + +model Candidate { + id Int @id @default(autoincrement()) + fullName String + mobile String + email String + subject String + coverLetter String + careerId Int + + career Career @relation(fields: [careerId], references: [id]) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Appointment { + id Int @id @default(autoincrement()) + + name String + mobileNumber String + email String? + message String? + date DateTime + + doctorId Int + departmentId Int + + doctor Doctor @relation(fields: [doctorId], references: [id]) + department Department @relation(fields: [departmentId], references: [id]) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } \ No newline at end of file diff --git a/backend/src/app.js b/backend/src/app.js index cdd12e1..0b50ffb 100644 --- a/backend/src/app.js +++ b/backend/src/app.js @@ -8,6 +8,8 @@ import blogRoutes from "./routes/blog.routes.js"; import uploadRoutes from "./routes/upload.routes.js"; import doctorRoutes from "./routes/doctor.routes.js"; import careerRoutes from "./routes/career.routes.js"; +import candidateRoutes from "./routes/candidate.routes.js"; +import appointmentRoutes from "./routes/appointment.routes.js"; dotenv.config(); @@ -39,6 +41,8 @@ app.use("/uploads", express.static("uploads")); app.use("/api/upload", uploadRoutes); app.use("/api/doctors", doctorRoutes); app.use("/api/careers", careerRoutes); +app.use("/api/candidates", candidateRoutes); +app.use("/api/appointments", appointmentRoutes); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { diff --git a/backend/src/controllers/candidate.controller.js b/backend/src/controllers/candidate.controller.js new file mode 100644 index 0000000..bdc6b6c --- /dev/null +++ b/backend/src/controllers/candidate.controller.js @@ -0,0 +1,183 @@ +import prisma from "../prisma/client.js"; + +// CREATE CANDIDATE + +export const createCandidate = async (req, res) => { + try { + const {fullName, mobile, email, subject, coverLetter, careerId} = req.body; + + if (!fullName || !mobile || !email || !careerId) { + return res.status(400).json({ + success: false, + message: "Required fields missing", + }); + } + + const candidate = await prisma.candidate.create({ + data: { + fullName, + mobile, + email, + subject, + coverLetter, + careerId: Number(careerId), + }, + }); + + res.status(201).json({ + success: true, + message: "Application submitted successfully", + data: candidate, + }); + } catch (error) { + console.error(error); + res.status(500).json({ + success: false, + message: "Failed to create candidate", + }); + } +}; + +// GET ALL CANDIDATES + +export const getCandidates = async (req, res) => { + try { + const candidates = await prisma.candidate.findMany({ + include: { + career: true, + }, + orderBy: { + createdAt: "desc", + }, + }); + + res.status(200).json({ + success: true, + data: candidates, + }); + } catch (error) { + console.error(error); + res.status(500).json({ + success: false, + message: "Failed to fetch candidates", + }); + } +}; + +// GET SINGLE CANDIDATE + +export const getCandidate = async (req, res) => { + try { + const {id} = req.params; + + const candidate = await prisma.candidate.findUnique({ + where: { + id: Number(id), + }, + include: { + career: true, + }, + }); + + if (!candidate) { + return res.status(404).json({ + success: false, + message: "Candidate not found", + }); + } + + res.status(200).json({ + success: true, + data: candidate, + }); + } catch (error) { + console.error(error); + res.status(500).json({ + success: false, + message: "Failed to fetch candidate", + }); + } +}; + +// GET CANDIDATES BY CAREER + +export const getCandidatesByCareer = async (req, res) => { + try { + const {careerId} = req.params; + + const candidates = await prisma.candidate.findMany({ + where: { + careerId: Number(careerId), + }, + include: { + career: true, + }, + orderBy: { + createdAt: "desc", + }, + }); + + res.status(200).json({ + success: true, + data: candidates, + }); + } catch (error) { + console.error(error); + res.status(500).json({ + success: false, + message: "Failed to fetch candidates", + }); + } +}; + +// UPDATE CANDIDATE + +export const updateCandidate = async (req, res) => { + try { + const {id} = req.params; + + const candidate = await prisma.candidate.update({ + where: { + id: Number(id), + }, + data: req.body, + }); + + res.status(200).json({ + success: true, + message: "Candidate updated successfully", + data: candidate, + }); + } catch (error) { + console.error(error); + res.status(500).json({ + success: false, + message: "Failed to update candidate", + }); + } +}; + +// DELETE CANDIDATE + +export const deleteCandidate = async (req, res) => { + try { + const {id} = req.params; + + await prisma.candidate.delete({ + where: { + id: Number(id), + }, + }); + + res.status(200).json({ + success: true, + message: "Candidate deleted successfully", + }); + } catch (error) { + console.error(error); + res.status(500).json({ + success: false, + message: "Failed to delete candidate", + }); + } +}; diff --git a/backend/src/routes/candidate.routes.js b/backend/src/routes/candidate.routes.js new file mode 100644 index 0000000..72e1dba --- /dev/null +++ b/backend/src/routes/candidate.routes.js @@ -0,0 +1,25 @@ +import express from "express"; +import { + createCandidate, + getCandidates, + getCandidate, + getCandidatesByCareer, + updateCandidate, + deleteCandidate, +} from "../controllers/candidate.controller.js"; + +import jwtAuthMiddleware from "../middleware/auth.js"; + +const router = express.Router(); + +/* PUBLIC */ + +router.get("/getAll", getCandidates); +router.get("/:id", getCandidate); +router.get("/career/:careerId", getCandidatesByCareer); + +router.post("/", createCandidate); +router.patch("/:id", updateCandidate); +router.delete("/:id", jwtAuthMiddleware, deleteCandidate); + +export default router;