Skip to content

Commit

Permalink
pagination added on server
Browse files Browse the repository at this point in the history
  • Loading branch information
sinanptm committed Sep 17, 2024
1 parent 9f88774 commit 0e74ef2
Show file tree
Hide file tree
Showing 9 changed files with 65 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -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 (
<div>AppointmentTabContent {}</div>
)
Expand Down
6 changes: 1 addition & 5 deletions client/components/forms/actions/userValidation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { AppointmentType } from "@/types";
import { z } from "zod";

export const signinFormValidation = z.object({
Expand Down Expand Up @@ -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(),

Expand Down
12 changes: 6 additions & 6 deletions client/components/forms/patient/AppointmentForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
/>
Expand All @@ -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) => (
Expand All @@ -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) => (
Expand Down Expand Up @@ -231,7 +231,7 @@ const AppointmentForm = () => {
name="slotId"
render={({ field }) => (
<FormItem>
<FormLabel className="text-gray-200">Time Slot</FormLabel>
<FormLabel className="text-gray-200">Time Slot *</FormLabel>
{isDoctorSelected && (
<div className="space-y-3 p-3 rounded-lg shadow-md border-2 border-gray-400">
<h3 className="text-base font-semibold text-gray-200">
Expand Down Expand Up @@ -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"
/>
<CustomFormField
fieldType={FormFieldType.TEXTAREA}
control={form.control}
name="note"
label="Note"
label="Any Notes"
placeholder="Please schedule before evening"
/>
</div>
Expand Down
20 changes: 9 additions & 11 deletions client/lib/api/appointment/index.ts
Original file line number Diff line number Diff line change
@@ -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";

Expand All @@ -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: {
Expand All @@ -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: {
Expand All @@ -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;
}
13 changes: 10 additions & 3 deletions client/lib/hooks/appointment/useAppointment.ts
Original file line number Diff line number Diff line change
@@ -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"

Expand All @@ -12,7 +12,7 @@ export const useGetDoctorsList = () => {
};

export const useCreateAppointment = () => {
return useMutation<{ orderId: string, appointmentId: string, patient:IPatient }, AxiosError<ErrorResponse>, { appointment: IAppointment }>({
return useMutation<{ orderId: string, appointmentId: string, patient: IPatient }, AxiosError<ErrorResponse>, { appointment: IAppointment }>({
mutationFn: ({ appointment }) => createAppointment(appointment),
onError: (error) => {
console.log("Error in creating appointment", error);
Expand All @@ -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<PaginatedResult<IAppointment>, AxiosError<ErrorResponse>>({
queryKey: ["appointments", status, offset, limit,],
queryFn: () => getAppointmentsDoctor(status, offset, limit,)
})
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PaginatedResult } from "../../../types";
import IAppointment, { AppointmentStatus } from "../../entities/IAppointment";

export default interface IAppointmentRepository {
Expand All @@ -7,5 +8,5 @@ export default interface IAppointmentRepository {
findByDateAndSlot(appointmentDate: string, slotId: string): Promise<IAppointment | null>;
findManyByDateAndDoctorId(appointmentDate: string, doctorId: string): Promise<IAppointment[] | null>;
updateAppointmentStatusToConfirmed(appointmentId: string): Promise<void>;
findManyByDoctorId(doctorId: string, status:AppointmentStatus): Promise<IAppointment[] | null>
findManyByDoctorId(doctorId: string, status:AppointmentStatus,offset:number,limit:number): Promise<PaginatedResult<IAppointment> | null>
}
21 changes: 19 additions & 2 deletions server/src/infrastructure/repositories/AppointmentRepository.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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<IAppointment[] | null> {
return await this.model.find({ doctorId, status });
async findManyByDoctorId(doctorId: string, status: AppointmentStatus, offset: number, limit: number): Promise<PaginatedResult<IAppointment> | 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<IAppointment[] | null> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
5 changes: 3 additions & 2 deletions server/src/use_case/appointment/GetAppointmentUseCase.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
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(
private appointmentRepository: IAppointmentRepository,
private validatorService: IValidatorService
) { }

async getAppointmentsByDoctorId(doctorId: string, status?: AppointmentStatus): Promise<IAppointment[] | null> {
async getAppointmentsByDoctorId(doctorId: string,offset:number,limit:number, status?: AppointmentStatus): Promise<PaginatedResult<IAppointment> | 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)
}

}

1 comment on commit 0e74ef2

@vercel
Copy link

@vercel vercel bot commented on 0e74ef2 Sep 17, 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.