diff --git a/client/components/doctor/appointments/AppointmentTabContent.tsx b/client/components/doctor/appointments/AppointmentTabContent.tsx index e388a9c6..c3dfc66f 100644 --- a/client/components/doctor/appointments/AppointmentTabContent.tsx +++ b/client/components/doctor/appointments/AppointmentTabContent.tsx @@ -1,6 +1,12 @@ -import React from 'react' +'use client' + +import { useGetAppointmentsDoctor } from "@/lib/hooks/appointment/useAppointment" +import { AppointmentStatus } from "@/types" const AppointmentTabContent = () => { + const {data,isLoading} = useGetAppointmentsDoctor(AppointmentStatus.PENDING,0,10); + console.log(data); + return (
AppointmentTabContent {}
) diff --git a/client/components/forms/actions/userValidation.ts b/client/components/forms/actions/userValidation.ts index e9ab154a..59ff35ff 100644 --- a/client/components/forms/actions/userValidation.ts +++ b/client/components/forms/actions/userValidation.ts @@ -1,4 +1,3 @@ -import { AppointmentType } from "@/types"; import { z } from "zod"; export const signinFormValidation = z.object({ @@ -86,10 +85,7 @@ export const appointmentFormValidation = z.object({ }).trim().min(5, "Reason must be at least 5 characters long") .max(500, "Reason must not exceed 500 characters"), - note: z.string().trim() - .min(5, "Notes must be at least 5 characters long") - .max(1000, "Notes must not exceed 1000 characters") - .optional(), + note: z.string(), date: z.coerce.date(), diff --git a/client/components/forms/patient/AppointmentForm.tsx b/client/components/forms/patient/AppointmentForm.tsx index 9cda2104..b66a8114 100644 --- a/client/components/forms/patient/AppointmentForm.tsx +++ b/client/components/forms/patient/AppointmentForm.tsx @@ -170,7 +170,7 @@ const AppointmentForm = () => { showTimeSelect={false} name="date" showDateText="Appointment date must be in the future" - label="Expected appointment date" + label="Expected appointment date *" isLimited dateFormat="dd/MM/yyyy" /> @@ -179,7 +179,7 @@ const AppointmentForm = () => { fieldType={FormFieldType.SELECT} control={form.control} name="appointmentType" - label="Appointment Type" + label="Appointment Type *" placeholder="Select an Appointment" > {AppointmentTypes.map((appointment, i) => ( @@ -202,7 +202,7 @@ const AppointmentForm = () => { fieldType={FormFieldType.SELECT} control={form.control} name="doctor" - label="Doctor" + label="Doctor *" > {!isDoctorsLoading && doctorsData?.items ? ( doctorsData.items.map((doctor) => ( @@ -231,7 +231,7 @@ const AppointmentForm = () => { name="slotId" render={({ field }) => ( - Time Slot + Time Slot * {isDoctorSelected && (

@@ -275,14 +275,14 @@ const AppointmentForm = () => { fieldType={FormFieldType.TEXTAREA} control={form.control} name="reason" - label="Reason for Appointment" + label="Reason for Appointment *" placeholder="Annual monthly check-up" />

diff --git a/client/lib/api/appointment/index.ts b/client/lib/api/appointment/index.ts index 5ed4e734..162d1886 100644 --- a/client/lib/api/appointment/index.ts +++ b/client/lib/api/appointment/index.ts @@ -1,6 +1,6 @@ +import axios from "axios"; import { withTempBaseUrl } from "@/lib/utils/withTempBaseUrl"; import IAppointment, { AppointmentStatus } from "@/types"; -import axios from "axios"; import patientAxiosInstance from "../patient/authorizedRoutes"; import doctorAxiosInstance from "../doctor/authorizedRoutes"; @@ -10,18 +10,16 @@ export type verifyPaymentProps = { } -const { doctorInstance, patientInstance, baseUrl } = { - doctorInstance: doctorAxiosInstance, - patientInstance: patientAxiosInstance, - baseUrl: `${process.env.NEXT_PUBLIC_API_URL}/appointments` -} + const baseUrl = `${process.env.NEXT_PUBLIC_API_URL}/appointments` + export const getDoctorsList = async () => { const response = await axios.get(`${process.env.NEXT_PUBLIC_API_URL}/doctors`); return response.data; }; + export const createAppointment = async (appointment: IAppointment) => { - const response = await withTempBaseUrl(patientInstance, baseUrl, { + const response = await withTempBaseUrl(patientAxiosInstance, baseUrl, { method: 'POST', url: '/', data: { @@ -32,7 +30,7 @@ export const createAppointment = async (appointment: IAppointment) => { }; export const verifyPayment = async ({ appointmentId, paymentData }: verifyPaymentProps) => { - const response = await withTempBaseUrl(patientInstance, baseUrl, { + const response = await withTempBaseUrl(patientAxiosInstance, baseUrl, { method: 'POST', url: '/verify-payment', data: { @@ -43,10 +41,10 @@ export const verifyPayment = async ({ appointmentId, paymentData }: verifyPaymen }; -export const getAppointmentsDoctor = async (status: AppointmentStatus) => { - const response = await withTempBaseUrl(doctorInstance, baseUrl, { +export const getAppointmentsDoctor = async (status: AppointmentStatus,offset: number, limit: number,) => { + const response = await withTempBaseUrl(doctorAxiosInstance, baseUrl, { method: 'GET', - url: `/doctor?status=${status}`, + url: `/doctor?status=${status}&${offset}&${limit}`, }) return response.data; } \ No newline at end of file diff --git a/client/lib/hooks/appointment/useAppointment.ts b/client/lib/hooks/appointment/useAppointment.ts index 5f52c78a..25d170ea 100644 --- a/client/lib/hooks/appointment/useAppointment.ts +++ b/client/lib/hooks/appointment/useAppointment.ts @@ -1,5 +1,5 @@ -import { verifyPayment, createAppointment, getDoctorsList, verifyPaymentProps } from "@/lib/api/appointment" -import IAppointment, { ErrorResponse, IDoctor, IPatient, MessageResponse, PaginatedResult } from "@/types" +import { verifyPayment, createAppointment, getDoctorsList, verifyPaymentProps, getAppointmentsDoctor } from "@/lib/api/appointment" +import IAppointment, { AppointmentStatus, ErrorResponse, IDoctor, IPatient, MessageResponse, PaginatedResult } from "@/types" import { useMutation, useQuery } from "@tanstack/react-query" import { AxiosError } from "axios" @@ -12,7 +12,7 @@ export const useGetDoctorsList = () => { }; export const useCreateAppointment = () => { - return useMutation<{ orderId: string, appointmentId: string, patient:IPatient }, AxiosError, { appointment: IAppointment }>({ + return useMutation<{ orderId: string, appointmentId: string, patient: IPatient }, AxiosError, { appointment: IAppointment }>({ mutationFn: ({ appointment }) => createAppointment(appointment), onError: (error) => { console.log("Error in creating appointment", error); @@ -27,4 +27,11 @@ export const useVerifyPaymentAppointment = () => { console.log("Error in Verifying payment ", error); } }) +} + +export const useGetAppointmentsDoctor = (status: AppointmentStatus, offset: number, limit: number,) => { + return useQuery, AxiosError>({ + queryKey: ["appointments", status, offset, limit,], + queryFn: () => getAppointmentsDoctor(status, offset, limit,) + }) } \ No newline at end of file diff --git a/server/src/domain/interface/repositories/IAppointmentRepository.ts b/server/src/domain/interface/repositories/IAppointmentRepository.ts index 1abccf35..94999421 100644 --- a/server/src/domain/interface/repositories/IAppointmentRepository.ts +++ b/server/src/domain/interface/repositories/IAppointmentRepository.ts @@ -1,3 +1,4 @@ +import { PaginatedResult } from "../../../types"; import IAppointment, { AppointmentStatus } from "../../entities/IAppointment"; export default interface IAppointmentRepository { @@ -7,5 +8,5 @@ export default interface IAppointmentRepository { findByDateAndSlot(appointmentDate: string, slotId: string): Promise; findManyByDateAndDoctorId(appointmentDate: string, doctorId: string): Promise; updateAppointmentStatusToConfirmed(appointmentId: string): Promise; - findManyByDoctorId(doctorId: string, status:AppointmentStatus): Promise + findManyByDoctorId(doctorId: string, status:AppointmentStatus,offset:number,limit:number): Promise | null> } diff --git a/server/src/infrastructure/repositories/AppointmentRepository.ts b/server/src/infrastructure/repositories/AppointmentRepository.ts index 712ab900..839335e2 100644 --- a/server/src/infrastructure/repositories/AppointmentRepository.ts +++ b/server/src/infrastructure/repositories/AppointmentRepository.ts @@ -1,5 +1,6 @@ import IAppointment, { AppointmentStatus } from "../../domain/entities/IAppointment"; import IAppointmentRepository from "../../domain/interface/repositories/IAppointmentRepository"; +import { PaginatedResult } from "../../types"; import AppointmentModel from "../database/AppointmentModel"; export default class AppointmentRepository implements IAppointmentRepository { @@ -12,8 +13,24 @@ export default class AppointmentRepository implements IAppointmentRepository { await this.model.findByIdAndUpdate(appointment._id, appointment, { new: true }) } - async findManyByDoctorId(doctorId: string, status: AppointmentStatus): Promise { - return await this.model.find({ doctorId, status }); + async findManyByDoctorId(doctorId: string, status: AppointmentStatus, offset: number, limit: number): Promise | null> { + const totalItems = await this.model.countDocuments({ doctorId, status }); + const items = await this.model.find({ doctorId, status }) + .skip(offset) + .limit(limit) + .exec(); + const currentPage = Math.floor(offset / limit) + 1; + const totalPages = Math.ceil(totalItems / limit); + const hasNextPage = currentPage < totalPages; + const hasPreviousPage = currentPage > 1; + return { + currentPage, + hasNextPage, + hasPreviousPage, + items, + totalItems, + totalPages, + }; } async findManyByDateAndDoctorId(appointmentDate: string, doctorId: string): Promise { diff --git a/server/src/presentation/controllers/appointment/AppointmentControllers.ts b/server/src/presentation/controllers/appointment/AppointmentControllers.ts index 519d8f8b..fd86a682 100644 --- a/server/src/presentation/controllers/appointment/AppointmentControllers.ts +++ b/server/src/presentation/controllers/appointment/AppointmentControllers.ts @@ -35,8 +35,14 @@ export default class AppointmentController { try { const doctorId = req.doctor?.id; const status = req.query.status as AppointmentStatus; - const appointments = await this.getAppointmentUseCase.getAppointmentsByDoctorId(doctorId!,status); - res.status(StatusCode.Success).json({appointments}) + let offset = parseInt(req.query.offset as string); + let limit = parseInt(req.query.limit as string); + + offset = isNaN(offset) || offset < 0 ? 0 : offset; + limit = isNaN(limit) || limit < 0 ? 10 : Math.min(limit, 100); + + const data = await this.getAppointmentUseCase.getAppointmentsByDoctorId(doctorId!,offset,limit,status); + res.status(StatusCode.Success).json(data) } catch (error) { next(error) } diff --git a/server/src/use_case/appointment/GetAppointmentUseCase.ts b/server/src/use_case/appointment/GetAppointmentUseCase.ts index a4282376..24befcd0 100644 --- a/server/src/use_case/appointment/GetAppointmentUseCase.ts +++ b/server/src/use_case/appointment/GetAppointmentUseCase.ts @@ -1,6 +1,7 @@ import IAppointmentRepository from "../../domain/interface/repositories/IAppointmentRepository"; import IAppointment, { AppointmentStatus } from "../../domain/entities/IAppointment"; import IValidatorService from "../../domain/interface/services/IValidatorService"; +import { PaginatedResult } from "../../types"; export default class GetAppointmentUseCase { constructor( @@ -8,12 +9,12 @@ export default class GetAppointmentUseCase { private validatorService: IValidatorService ) { } - async getAppointmentsByDoctorId(doctorId: string, status?: AppointmentStatus): Promise { + async getAppointmentsByDoctorId(doctorId: string,offset:number,limit:number, status?: AppointmentStatus): Promise | null> { this.validatorService.validateIdFormat(doctorId); if(status){ this.validatorService.validateEnum(status,Object.values(AppointmentStatus)) } - return await this.appointmentRepository.findManyByDoctorId(doctorId, status ?? AppointmentStatus.CONFIRMED) + return await this.appointmentRepository.findManyByDoctorId(doctorId, status ?? AppointmentStatus.CONFIRMED, offset,limit) } } \ No newline at end of file