From 9ae190754a72decbabe23816129724fee7d2e3c5 Mon Sep 17 00:00:00 2001 From: ARJUN S THAMPI <61703062+arjun-thampi@users.noreply.github.com> Date: Mon, 16 Mar 2026 10:16:27 +0530 Subject: [PATCH] feat : add appointment apis --- .../20260313101753_appointment/migration.sql | 21 ++ .../migration.sql | 15 ++ backend/prisma/schema.prisma | 157 ++++++------ .../src/controllers/appointment.controller.js | 224 ++++++++++++++++++ backend/src/routes/appointment.routes.js | 23 ++ 5 files changed, 360 insertions(+), 80 deletions(-) create mode 100644 backend/prisma/migrations/20260313101753_appointment/migration.sql create mode 100644 backend/prisma/migrations/20260313110806_appointment_relation_fix/migration.sql create mode 100644 backend/src/controllers/appointment.controller.js create mode 100644 backend/src/routes/appointment.routes.js diff --git a/backend/prisma/migrations/20260313101753_appointment/migration.sql b/backend/prisma/migrations/20260313101753_appointment/migration.sql new file mode 100644 index 0000000..5ccb706 --- /dev/null +++ b/backend/prisma/migrations/20260313101753_appointment/migration.sql @@ -0,0 +1,21 @@ +-- CreateTable +CREATE TABLE "Appointment" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "mobileNumber" TEXT NOT NULL, + "email" TEXT, + "message" TEXT, + "date" TIMESTAMP(3) NOT NULL, + "doctorId" INTEGER NOT NULL, + "departmentId" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Appointment_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "Appointment" ADD CONSTRAINT "Appointment_doctorId_fkey" FOREIGN KEY ("doctorId") REFERENCES "Doctor"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Appointment" ADD CONSTRAINT "Appointment_departmentId_fkey" FOREIGN KEY ("departmentId") REFERENCES "Department"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/backend/prisma/migrations/20260313110806_appointment_relation_fix/migration.sql b/backend/prisma/migrations/20260313110806_appointment_relation_fix/migration.sql new file mode 100644 index 0000000..cb8202f --- /dev/null +++ b/backend/prisma/migrations/20260313110806_appointment_relation_fix/migration.sql @@ -0,0 +1,15 @@ +-- DropForeignKey +ALTER TABLE "Appointment" DROP CONSTRAINT "Appointment_departmentId_fkey"; + +-- DropForeignKey +ALTER TABLE "Appointment" DROP CONSTRAINT "Appointment_doctorId_fkey"; + +-- AlterTable +ALTER TABLE "Appointment" ALTER COLUMN "doctorId" SET DATA TYPE TEXT, +ALTER COLUMN "departmentId" SET DATA TYPE TEXT; + +-- AddForeignKey +ALTER TABLE "Appointment" ADD CONSTRAINT "Appointment_doctorId_fkey" FOREIGN KEY ("doctorId") REFERENCES "Doctor"("doctorId") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Appointment" ADD CONSTRAINT "Appointment_departmentId_fkey" FOREIGN KEY ("departmentId") REFERENCES "Department"("departmentId") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 4ad39fb..5e7da69 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -1,4 +1,3 @@ - generator client { provider = "prisma-client-js" } @@ -9,144 +8,142 @@ datasource db { } model User { - id Int @id @default(autoincrement()) - username String @unique - password String - role String? @default("admin") + id Int @id @default(autoincrement()) + username String @unique + password String + role String? @default("admin") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model Doctor { - id Int @id @default(autoincrement()) - doctorId String @unique + id Int @id @default(autoincrement()) + doctorId String @unique name String designation String? workingStatus String? qualification String? - departments DoctorDepartment[] - appointments Appointment[] + departments DoctorDepartment[] + appointments Appointment[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } - model Department { - id Int @id @default(autoincrement()) - departmentId String @unique - name String + id Int @id @default(autoincrement()) + departmentId String @unique + name String - para1 String? - para2 String? - para3 String? - facilities String? - services String? + para1 String? + para2 String? + para3 String? + facilities String? + services String? - doctors DoctorDepartment[] - appointments Appointment[] + doctors DoctorDepartment[] + appointments Appointment[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model DoctorDepartment { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) - doctorId Int - departmentId Int + doctorId Int + departmentId Int - doctor Doctor @relation(fields: [doctorId], references: [id]) - department Department @relation(fields: [departmentId], references: [id]) + doctor Doctor @relation(fields: [doctorId], references: [id]) + department Department @relation(fields: [departmentId], references: [id]) - timing DoctorTiming? + timing DoctorTiming? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt @@unique([doctorId, departmentId]) } model DoctorTiming { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) - doctorDepartmentId Int @unique - doctorDepartment DoctorDepartment @relation(fields: [doctorDepartmentId], references: [id]) + doctorDepartmentId Int @unique + doctorDepartment DoctorDepartment @relation(fields: [doctorDepartmentId], references: [id]) - monday String? - tuesday String? - wednesday String? - thursday String? - friday String? - saturday String? - sunday String? - additional String? + monday String? + tuesday String? + wednesday String? + thursday String? + friday String? + saturday String? + sunday String? + additional String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } - model Blog { - id Int @id @default(autoincrement()) - title String - writer String? - image String? - content Json - isActive Boolean @default(true) + id Int @id @default(autoincrement()) + title String + writer String? + image String? + content Json + isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model Career { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) post String designation String? qualification String? experienceNeed String? email String? number String? - status String @default("new") + status String @default("new") - candidates Candidate[] + candidates Candidate[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + 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 + id Int @id @default(autoincrement()) + fullName String + mobile String + email String + subject String + coverLetter String + careerId Int - career Career @relation(fields: [careerId], references: [id]) + career Career @relation(fields: [careerId], references: [id]) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model Appointment { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) - name String - mobileNumber String - email String? - message String? - date DateTime + name String + mobileNumber String + email String? + message String? + date DateTime - doctorId Int - departmentId Int + doctorId String + departmentId String - doctor Doctor @relation(fields: [doctorId], references: [id]) - department Department @relation(fields: [departmentId], references: [id]) + doctor Doctor @relation(fields: [doctorId], references: [doctorId]) + department Department @relation(fields: [departmentId], references: [departmentId]) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt -} \ No newline at end of file + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} diff --git a/backend/src/controllers/appointment.controller.js b/backend/src/controllers/appointment.controller.js new file mode 100644 index 0000000..cecbb94 --- /dev/null +++ b/backend/src/controllers/appointment.controller.js @@ -0,0 +1,224 @@ +import prisma from "../prisma/client.js"; +//CREATE APPOINTMENT + +export const createAppointment = async (req, res) => { + try { + const {name, mobileNumber, email, message, date, doctorId, departmentId} = + req.body; + + if (!name || !mobileNumber || !doctorId || !departmentId || !date) { + return res.status(400).json({ + success: false, + message: "Required fields missing", + }); + } + + const appointment = await prisma.appointment.create({ + data: { + name, + mobileNumber, + email, + message, + date: new Date(date), + doctorId, + departmentId, + }, + include: { + doctor: true, + department: true, + }, + }); + + res.status(201).json({ + success: true, + message: "Appointment booked successfully", + data: appointment, + }); + } catch (error) { + console.error(error); + res.status(500).json({ + success: false, + message: "Failed to create appointment", + }); + } +}; + +// GET ALL APPOINTMENTS + +export const getAppointments = async (req, res) => { + try { + const appointments = await prisma.appointment.findMany({ + include: { + doctor: true, + department: true, + }, + orderBy: { + createdAt: "desc", + }, + }); + + res.status(200).json({ + success: true, + data: appointments, + }); + } catch (error) { + console.error(error); + res.status(500).json({ + success: false, + message: "Failed to fetch appointments", + }); + } +}; + +// GET SINGLE APPOINTMENT + +export const getAppointment = async (req, res) => { + try { + const {id} = req.params; + + const appointment = await prisma.appointment.findUnique({ + where: { + id: Number(id), + }, + include: { + doctor: true, + department: true, + }, + }); + + if (!appointment) { + return res.status(404).json({ + success: false, + message: "Appointment not found", + }); + } + + res.status(200).json({ + success: true, + data: appointment, + }); + } catch (error) { + console.error(error); + res.status(500).json({ + success: false, + message: "Failed to fetch appointment", + }); + } +}; + +// GET APPOINTMENTS BY DOCTOR + +export const getAppointmentsByDoctor = async (req, res) => { + try { + const {doctorId} = req.params; + + const appointments = await prisma.appointment.findMany({ + where: { + doctorId, + }, + include: { + doctor: true, + department: true, + }, + orderBy: { + date: "asc", + }, + }); + + res.status(200).json({ + success: true, + data: appointments, + }); + } catch (error) { + console.error(error); + res.status(500).json({ + success: false, + message: "Failed to fetch doctor appointments", + }); + } +}; + +// GET APPOINTMENTS BY DEPARTMENT + +export const getAppointmentsByDepartment = async (req, res) => { + try { + const {departmentId} = req.params; + + const appointments = await prisma.appointment.findMany({ + where: { + departmentId, + }, + include: { + doctor: true, + department: true, + }, + }); + + res.status(200).json({ + success: true, + data: appointments, + }); + } catch (error) { + console.error(error); + res.status(500).json({ + success: false, + message: "Failed to fetch department appointments", + }); + } +}; + +// UPDATE APPOINTMENT + +export const updateAppointment = async (req, res) => { + try { + const {id} = req.params; + + const appointment = await prisma.appointment.update({ + where: { + id: Number(id), + }, + data: req.body, + include: { + doctor: true, + department: true, + }, + }); + + res.status(200).json({ + success: true, + message: "Appointment updated successfully", + data: appointment, + }); + } catch (error) { + console.error(error); + res.status(500).json({ + success: false, + message: "Failed to update appointment", + }); + } +}; + +//DELETE APPOINTMENT + +export const deleteAppointment = async (req, res) => { + try { + const {id} = req.params; + + await prisma.appointment.delete({ + where: { + id: Number(id), + }, + }); + + res.status(200).json({ + success: true, + message: "Appointment deleted successfully", + }); + } catch (error) { + console.error(error); + res.status(500).json({ + success: false, + message: "Failed to delete appointment", + }); + } +}; diff --git a/backend/src/routes/appointment.routes.js b/backend/src/routes/appointment.routes.js new file mode 100644 index 0000000..becd07c --- /dev/null +++ b/backend/src/routes/appointment.routes.js @@ -0,0 +1,23 @@ +import express from "express"; +import { + createAppointment, + getAppointments, + getAppointment, + updateAppointment, + deleteAppointment, +} from "../controllers/appointment.controller.js"; + +import jwtAuthMiddleware from "../middleware/auth.js"; + +const router = express.Router(); + +/* PUBLIC */ + +router.get("/getall", getAppointments); +router.post("/", createAppointment); + +router.get("/:id", getAppointment); +router.patch("/:id", updateAppointment); +router.delete("/:id", jwtAuthMiddleware, deleteAppointment); + +export default router;