From 11ee7635b0083fb699cae73cb5160474d8888334 Mon Sep 17 00:00:00 2001 From: Sinan Date: Sun, 22 Sep 2024 20:25:01 +0530 Subject: [PATCH] appointment details page updated --- client/app/doctor/appointments/[id]/page.tsx | 205 +++++++++++++++++ .../button/NotificationButtonDoctor.tsx | 2 +- .../AppointmentDetailsDoctorModel.tsx | 213 ------------------ ...tsx => ConfirmCancelAppointmentDoctor.tsx} | 0 .../view/table/DoctorAppointmentTable.tsx | 14 +- 5 files changed, 207 insertions(+), 227 deletions(-) create mode 100644 client/app/doctor/appointments/[id]/page.tsx delete mode 100644 client/components/models/appointment/AppointmentDetailsDoctorModel.tsx rename client/components/models/appointment/{ConfirmCancleAppointment.tsx => ConfirmCancelAppointmentDoctor.tsx} (100%) diff --git a/client/app/doctor/appointments/[id]/page.tsx b/client/app/doctor/appointments/[id]/page.tsx new file mode 100644 index 00000000..f0ff3146 --- /dev/null +++ b/client/app/doctor/appointments/[id]/page.tsx @@ -0,0 +1,205 @@ +'use client' + +import { useParams } from 'next/navigation' +import Image from 'next/image' +import { useGetAppointmentDetailsDoctor, useUpdateAppointmentStatusDoctor } from '@/lib/hooks/appointment/useAppointmentDoctor' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { Skeleton } from '@/components/ui/skeleton' +import { AppointmentStatus } from '@/types' +import { toast } from '@/components/ui/use-toast' +import GetStatusBadge from '@/components/doctor/appointment/GetStatusBadge' +import AppointmentCancellationModal from '@/components/models/appointment/ConfirmCancelAppointmentDoctor' +import { Calendar, Clock, FileText, Video, User, Phone, AlertCircle } from 'lucide-react' +import { format } from 'date-fns' +import { useState } from 'react' +import { ButtonV2 } from '@/components/common/ButtonV2' + +export default function AppointmentDetailsPage() { + const params = useParams() + const appointmentId = params.id as string + const [isCancelModelOpen, setCancelModelOpen] = useState(false) + const { data: appointment, isLoading, error, refetch } = useGetAppointmentDetailsDoctor(appointmentId) + const { mutate: updateStatus, isPending } = useUpdateAppointmentStatusDoctor() + + const handleAcceptAppointment = () => { + if (!appointmentId) return + updateStatus( + { appointmentId, status: AppointmentStatus.CONFIRMED }, + { + onSuccess: () => { + toast({ + title: "Appointment Confirmed", + description: "The appointment has been successfully confirmed.", + variant: "success", + }) + refetch() + }, + onError: (error) => { + toast({ + title: "Failed to Accept Appointment", + description: error?.response?.data?.message || "An error occurred. Please try again.", + variant: "destructive", + }) + }, + } + ) + } + + const handleCancelAppointment = async () => { + if (!appointmentId) return + updateStatus( + { appointmentId, status: AppointmentStatus.CANCELLED }, + { + onSuccess: () => { + toast({ + title: "Appointment Cancelled", + description: "The appointment has been cancelled.", + variant: "warning", + }) + setCancelModelOpen(false) + refetch() + }, + onError: (error) => { + toast({ + title: "Failed to Cancel Appointment", + description: error?.response?.data?.message || "An error occurred. Please try again.", + variant: "destructive", + }) + }, + } + ) + } + + if (isLoading) { + return ( +
+ + +
+ ) + } + + if (error || !appointment) { + return ( +
+

Error loading appointment details. Please try again.

+
+ ) + } + + return ( +
+
+

Appointment Details

+
+ + {appointment.status === AppointmentStatus.PENDING && ( + <> + + Accept + + setCancelModelOpen(true)}> + Reject + + + )} + {appointment.status === AppointmentStatus.CONFIRMED && ( + setCancelModelOpen(true)}> + Cancel + + )} +
+
+ +
+ + + + + Appointment Details + + + +
+
+ + {format(new Date(appointment.appointmentDate!), "PPP")} +
+
+ + {appointment.slot?.startTime || "N/A"} - {appointment.slot?.endTime || "N/A"} +
+
+ {appointment.appointmentType === "video-consulting" ? ( +
+
+
+

+ + Reason for Visit +

+

{appointment.reason}

+
+
+

+ + Additional Notes +

+

{appointment.notes || "No additional notes provided"}

+
+
+
+ + + + + + Patient Information + + + +
+
+ {appointment.patient?.name +
+
+

{appointment.patient?.name}

+

{appointment.patient?.email}

+
+
+
+
+ + {appointment.patient?.phone} +
+
+ + {appointment.patient?.gender} +
+
+ + Blood Group: {appointment.patient?.bloodGroup} +
+
+
+
+
+ + +
+ ) +} \ No newline at end of file diff --git a/client/components/button/NotificationButtonDoctor.tsx b/client/components/button/NotificationButtonDoctor.tsx index d63c323b..59086443 100644 --- a/client/components/button/NotificationButtonDoctor.tsx +++ b/client/components/button/NotificationButtonDoctor.tsx @@ -79,7 +79,7 @@ const NotificationButtonDoctor = forwardRef((props, ref) => { unauthorized={!!unauthorized} handleClearSingleNotification={handleClearSingleNotification} handleClearAllNotifications={handleClearAllNotifications} - link="" + link="/doctor/appointments" /> )} diff --git a/client/components/models/appointment/AppointmentDetailsDoctorModel.tsx b/client/components/models/appointment/AppointmentDetailsDoctorModel.tsx deleted file mode 100644 index ae8386ac..00000000 --- a/client/components/models/appointment/AppointmentDetailsDoctorModel.tsx +++ /dev/null @@ -1,213 +0,0 @@ -"use client"; - -import { - useGetAppointmentDetailsDoctor, - useUpdateAppointmentStatusDoctor, -} from "@/lib/hooks/appointment/useAppointmentDoctor"; -import { Dispatch, SetStateAction, useEffect, useState } from "react"; -import { - AlertDialog, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from "@/components/ui/alert-dialog"; -import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import Image from "next/image"; -import { Skeleton } from "@/components/ui/skeleton"; -import { AppointmentStatus } from "@/types"; -import { toast } from "@/components/ui/use-toast"; -import { useQueryClient } from "@tanstack/react-query"; -import GetStatusBadge from "../../doctor/appointment/GetStatusBadge"; -import AppointmentCancellationModal from "./ConfirmCancleAppointment"; - -type Props = { - isOpen: boolean; - setIsOpen: Dispatch>; - appointmentId: string; - refetch: any; -}; - -export default function AppointmentDetailsModelDoctor({ isOpen, setIsOpen, appointmentId, refetch }: Props) { - const [isCancelModelOpen, setCancelModelOpen] = useState(false); - const { data: appointment, isLoading, error } = useGetAppointmentDetailsDoctor(appointmentId); - const { mutate: updateStatus, isPending } = useUpdateAppointmentStatusDoctor(); - const query = useQueryClient(); - - const handleAcceptAppointment = () => { - if (!appointmentId) return; - updateStatus( - { appointmentId, status: AppointmentStatus.CONFIRMED }, - { - onSuccess: () => { - toast({ - title: "🎉 Appointment Confirmed!", - description: - "✅ Your appointment has been successfully confirmed! The patient will be notified shortly.", - variant: "success", - }); - refetch(); - query.invalidateQueries({ - queryKey: ["appointmentDetails", appointmentId], - }); - }, - onError: (error) => { - toast({ - title: "❌ Failed to Accept Appointment", - description: - error?.response?.data?.message || "⚠️ Oops! Something went wrong. Please try again later.", - variant: "destructive", - }); - }, - } - ); - }; - - const handleCancelAppointment = async () => { - if (!appointmentId) return; - updateStatus( - { appointmentId, status: AppointmentStatus.CANCELLED }, - { - onSuccess: () => { - toast({ - title: "🚫 Appointment Cancelled", - description: "❗ The appointment request has been cancelled. The patient will be notified.", - variant: "warning", - }); - setCancelModelOpen(false); - refetch(); - query.invalidateQueries({ - queryKey: ["appointmentDetails", appointmentId], - }); - }, - onError: (error) => { - toast({ - title: "❌ Failed to Cancel Appointment", - description: - error?.response?.data?.message || "⚠️ Oops! Something went wrong. Please try again later.", - variant: "destructive", - }); - }, - } - ); - }; - - return ( - - - - Appointment Details - View the details of the selected appointment. - - - {isLoading ? ( -
- - - -
- ) : error ? ( -
-

Error loading appointment details. Please try again.

-
- ) : appointment ? ( -
-
- {appointment.patient?.name -
-

{appointment.patient?.name || "N/A"}

-

{appointment.patient?.email || "N/A"}

-
-
- -
- - - Appointment Info - - -
- Date: - - {appointment.appointmentDate - ? new Date(appointment.appointmentDate).toLocaleString().split(",")[0] - : "N/A"} - , {appointment.slot?.startTime || "N/A"} - -
-
- Type: - {appointment.appointmentType || "N/A"} -
-
- Status: - -
-
-
- - - - Patient Details - - -
- Phone: - {appointment.patient?.phone || "N/A"} -
-
- Gender: - {appointment.patient?.gender || "N/A"} -
-
- Blood Group: - {appointment.patient?.bloodGroup || "N/A"} -
-
-
-
-
- ) : ( -
-

No appointment details found.

-
- )} - - - - {appointment && - (appointment.status === AppointmentStatus.PENDING || - appointment.status === AppointmentStatus.CONFIRMED) && ( -
- - {appointment.status === AppointmentStatus.PENDING && ( - - )} -
- )} -
-
- - -
- ); -} - diff --git a/client/components/models/appointment/ConfirmCancleAppointment.tsx b/client/components/models/appointment/ConfirmCancelAppointmentDoctor.tsx similarity index 100% rename from client/components/models/appointment/ConfirmCancleAppointment.tsx rename to client/components/models/appointment/ConfirmCancelAppointmentDoctor.tsx diff --git a/client/components/view/table/DoctorAppointmentTable.tsx b/client/components/view/table/DoctorAppointmentTable.tsx index 6ec450cb..c158fae9 100644 --- a/client/components/view/table/DoctorAppointmentTable.tsx +++ b/client/components/view/table/DoctorAppointmentTable.tsx @@ -8,7 +8,6 @@ import { useGetAppointmentsDoctor } from "@/lib/hooks/appointment/useAppointment import { AppointmentStatus } from "@/types"; import TableSkeleton from "@/components/skeletons/TableSkelton"; import Pagination from "@/components/navigation/Pagination"; -import AppointmentDetailsModelDoctor from "@/components/models/appointment/AppointmentDetailsDoctorModel"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import GetStatusBadge from "@/components/doctor/appointment/GetStatusBadge"; import { ButtonV2 } from "@/components/common/ButtonV2"; @@ -27,8 +26,6 @@ type Props = { export default function AppointmentTable({ page }: Props) { const [currentPage, setCurrentPage] = useState(page); - const [isModelOpen, setModelOpen] = useState(false); - const [appointmentId, setAppointmentId] = useState(""); const [statusFilter, setStatusFilter] = useState("all"); const limit = 7; const { data, isLoading, error, refetch } = useGetAppointmentsDoctor( @@ -47,10 +44,7 @@ export default function AppointmentTable({ page }: Props) { }; const handleViewDetails = (appointmentId: string) => { - setAppointmentId(appointmentId); - setModelOpen(true); - console.log(appointmentId); - + router.push(`/doctor/appointments/${appointmentId}`) }; const handleStatusChange = (status: AppointmentStatus | "all") => { @@ -161,12 +155,6 @@ export default function AppointmentTable({ page }: Props) { /> )} - ); }