Skip to content

Commit

Permalink
appointment presentation added
Browse files Browse the repository at this point in the history
  • Loading branch information
sinanptm committed Sep 14, 2024
1 parent fd80313 commit 7c6fd89
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 26 deletions.
1 change: 1 addition & 0 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"dotenv": "^16.4.5",
"express": "^4.19.2",
"helmet": "^7.1.0",
"joi": "^17.13.3",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.5.3",
"multer": "^1.4.5-lts.1",
Expand Down
1 change: 0 additions & 1 deletion server/src/domain/entities/IAppointment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,5 @@ export default interface IAppointment {
readonly appointmentDate?: string;
readonly reason?: string;
readonly notes?: string;
readonly startTime?:string;
status?: AppointmentStatus;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,4 @@ export default interface ISlotRepository {
findMany(doctorId: string): Promise<ISlot[] | null>;
findManyByDay(doctorId: string, day: Days): Promise<ISlot[] | null>;
findById(slotId: string): Promise<ISlot | null>;
findByDoctorIdStartTimeAndDay(slotId: string, doctorId: string, startTime: string, day:Days): Promise<ISlot | null>;
}
4 changes: 0 additions & 4 deletions server/src/infrastructure/database/AppointmentModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ const appointmentSchema = new Schema<IAppointment>(
required: true,
index: true,
},
startTime: {
type:String,
required:true
},
appointmentType: {
type: String,
enum: Object.values(AppointmentType),
Expand Down
7 changes: 2 additions & 5 deletions server/src/infrastructure/repositories/SlotRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,8 @@ export default class SlotRepository implements ISlotRepository {
return await this.model.find({ doctorId, day })
}
async findById(slotId: string): Promise<ISlot | null> {
if(!isValidObjectId(slotId)) throw new Error("Invalid Object Id")
return await this.model.findById(slotId)
}
async findByDoctorIdStartTimeAndDay(slotId: string, doctorId: string, startTime: string, day: Days): Promise<ISlot | null> {
if (!isValidObjectId(doctorId)) throw new Error("Invalid Object Id");
if (!isValidObjectId(slotId)) throw new Error("Invalid Object Id");
return await this.model.findOne({ _id: slotId, doctorId, startTime, day });
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { NextFunction, Response } from "express";
import { CustomRequest, StatusCode } from "../../../types";
import AppointmentUseCase from "../../../use_case/appointment/AppointmentUseCase";
import AppointmentValidator from "../../validators/AppointmentValidator";

export default class AppointmentController {
constructor(
private appointmentUseCase: AppointmentUseCase,
private appointmentValidator: AppointmentValidator
) { }

async create(req: CustomRequest, res: Response, next: NextFunction) {
try {
const { error } = this.appointmentValidator.validate(req.body.appointment);
if (error) {
return res.status(StatusCode.BadRequest).json({ message: error.details[0].message });
}
const { appointment } = req.body;
const patientId = req.patient?.id;
await this.appointmentUseCase.create(appointment, patientId!);
res.status(StatusCode.Success).json({ message: "Appointment created successfully" });
} catch (error) {
next(error);
}
}

}
26 changes: 26 additions & 0 deletions server/src/presentation/routers/appointment/AppointmentRoutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import express from 'express'
import AppointmentRepository from '../../../infrastructure/repositories/AppointmentRepository';
import SlotRepository from '../../../infrastructure/repositories/SlotRepository';
import AppointmentUseCase from '../../../use_case/appointment/AppointmentUseCase';
import AppointmentController from '../../controllers/appointment/AppointmentControllers';
import AppointmentValidator from '../../validators/AppointmentValidator';
import PatientAuthMiddleware from '../../middlewares/PatientAuthMiddleware';
import JWTService from '../../../infrastructure/services/JWTService';

const router = express.Router();


const appointmentRepository = new AppointmentRepository();
const slotRepository = new SlotRepository();
const tokenService = new JWTService()

const appointmentUseCase = new AppointmentUseCase(appointmentRepository, slotRepository);

const appointmentValidator = new AppointmentValidator()
const appointmentController = new AppointmentController(appointmentUseCase, appointmentValidator);

const authorizePatient = new PatientAuthMiddleware(tokenService);

router.post('/', authorizePatient.exec.bind(authorizePatient), appointmentController.create.bind(appointmentController));

export default router;
9 changes: 4 additions & 5 deletions server/src/presentation/routers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import slotRoutes from "./slots/SlotsRoutes";
import UnauthenticatedUseCases from "../../use_case/UnauthenticatedUseCases";
import UnauthenticatedControllers from "../controllers/UnauthenticatedControllers";
import DoctorRepository from "../../infrastructure/repositories/DoctorRepository";
import appointmentRoutes from "./appointment/AppointmentRoutes";

const app = express();
const tokenService = new TokenService();
Expand All @@ -25,17 +26,15 @@ const unauthenticatedController = new UnauthenticatedControllers(unauthenticated

const errorHandler = new ErrorHandler();

app.get('/doctors', unauthenticatedController.getDoctors.bind(unauthenticatedController))
app.use("/patient/auth", patientAuthentication);
app.use("/patient", authorizePatient.exec.bind(authorizePatient), protectedRoutes);

app.use("/admin/auth", adminAuthentication);
app.use("/admin", authorizeAdmin.exec.bind(authorizeAdmin), protectedAdminRoutes);

app.use("/doctor/auth", doctorAuthentication);
app.use('/slots', slotRoutes);
app.use('/appointment', appointmentRoutes)

app.use('/slots',slotRoutes);

app.get('/doctors', unauthenticatedController.getDoctors.bind(unauthenticatedController))

app.use(errorHandler.exec.bind(errorHandler));

Expand Down
22 changes: 22 additions & 0 deletions server/src/presentation/validators/AppointmentValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Joi from "joi";
import IAppointment, { AppointmentType } from "../../domain/entities/IAppointment";

export default class AppointmentValidator {
private schema: Joi.ObjectSchema;

constructor() {
this.schema = Joi.object({
doctorId: Joi.string().required(),
patientId: Joi.string().required(),
slotId: Joi.string().required(),
appointmentType: Joi.string().valid(...Object.values(AppointmentType)).required(),
appointmentDate: Joi.string().isoDate().required(),
reason: Joi.string().optional(),
notes: Joi.string().optional(),
});
}

validate(appointment: IAppointment) {
return this.schema.validate(appointment);
}
}
14 changes: 4 additions & 10 deletions server/src/use_case/appointment/AppointmentUseCase.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import IAppointment, { AppointmentStatus } from "../../domain/entities/IAppointment";
import { Days } from "../../domain/entities/ISlot";
import IAppointmentRepository from "../../domain/interface/repositories/IAppointmentRepository";
import ISlotRepository from "../../domain/interface/repositories/ISlotRepository";

Expand All @@ -9,16 +8,11 @@ export default class AppointmentUseCase {
private slotRepository: ISlotRepository
) { }

async create({ _id, startTime, doctorId, appointmentDate, ...appointment }: IAppointment, patientId: string): Promise<void> {
const day = this.getDayFromDate(appointmentDate!)
const slot = await this.slotRepository.findByDoctorIdStartTimeAndDay(_id!, doctorId!, startTime!, day);
async create({ slotId, ...appointment }: IAppointment, patientId: string): Promise<void> {
const slot = await this.slotRepository.findById(slotId!);
if (!slot) throw new Error("Slot Not Found");
await this.appointRepository.create({ ...appointment, _id, startTime, doctorId, patientId, status: AppointmentStatus.PENDING });
await this.appointRepository.create({ ...appointment, slotId, patientId, status: AppointmentStatus.PENDING });
}

private getDayFromDate(date: string): Days {
const dayOfWeek = new Date(date).getUTCDay();
const dayNames = Object.values(Days);
return dayNames[dayOfWeek] as Days;
}

}

1 comment on commit 7c6fd89

@vercel
Copy link

@vercel vercel bot commented on 7c6fd89 Sep 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.