diff --git a/client/src/components/Patient/PatientAppointments/AppointmentCard.jsx b/client/src/components/Patient/PatientAppointments/AppointmentCard.jsx index 2495f9f..445148d 100644 --- a/client/src/components/Patient/PatientAppointments/AppointmentCard.jsx +++ b/client/src/components/Patient/PatientAppointments/AppointmentCard.jsx @@ -29,130 +29,121 @@ const AppointmentCard = ({ appointment, prescriptionStatus, onAppointmentClick } const getStatusBadge = (status) => { const statusConfig = { pending: { - color: 'bg-yellow-100 text-yellow-800 border-yellow-200', - icon: , - text: 'Pending', + color: 'bg-blue-50 text-blue-700 border-blue-100', + icon: , + label: 'Pending', }, confirmed: { - color: 'bg-blue-100 text-blue-800 border-blue-200', - icon: , - text: 'Confirmed', + color: 'bg-blue-600 text-white border-blue-600', + icon: , + label: 'Confirmed', }, completed: { - color: 'bg-green-100 text-green-800 border-green-200', - icon: , - text: 'Completed', + color: 'bg-gray-100 text-black border-gray-200', + icon: , + label: 'Completed', }, cancelled: { - color: 'bg-red-100 text-red-800 border-red-200', - icon: , - text: 'Cancelled', - }, - 'no-show': { - color: 'bg-gray-100 text-gray-800 border-gray-200', - icon: , - text: 'No Show', + color: 'bg-gray-100 text-gray-600 border-gray-200', + icon: , + label: 'Cancelled', }, }; const config = statusConfig[status] || statusConfig.pending; - return ( {config.icon} - {config.text} + {config.label} ); }; - const { date: formattedDate, time: formattedTime } = formatDateTime( - appointment.date, - appointment.startTime - ); + const { date, time } = formatDateTime(appointment.date, appointment.startTime); const hasPrescription = prescriptionStatus[appointment._id]; return ( -
onAppointmentClick(appointment._id)} - className="bg-white rounded-xl shadow-sm border border-gray-100 p-6 hover:shadow-md hover:border-blue-200 transition-all duration-200 cursor-pointer group" - > - {/* Header */} -
-
-
- {appointment.doctorId?.firstName?.[0]} - {appointment.doctorId?.lastName?.[0]} -
-
-

- Dr. {appointment.doctorId?.firstName} {appointment.doctorId?.lastName} -

-

{appointment.doctorId?.specialization}

-
-
- {getStatusBadge(appointment.status)} -
+
+
+
+
+
+ {appointment.doctorId?.profilePicture ? ( + {`${appointment.doctorId?.firstName} + ) : ( + + )} +
- {/* Appointment Details */} -
- {/* Date and Time */} -
-
- - {formattedDate} -
-
- - {formattedTime} +
+

+ Dr. {appointment.doctorId?.firstName} {appointment.doctorId?.lastName} +

+

+ {appointment.doctorId?.specialization} +

+
+ + {appointment.reason} +
+
-
- {/* Clinic */} -
- - {appointment.clinicId?.name} +
+ {getStatusBadge(appointment.status)} +
- {/* Consultation Type */} -
- {appointment.isTeleconsultation ? ( - <> -
+
+
+ + {date} +
- {/* Reason */} - {appointment.reason && ( -
-

- Reason: - {appointment.reason} -

+
+ + + {time} - {format(new Date(`2000-01-01T${appointment.endTime}`), 'hh:mm a')} +
- )} - {/* Prescription Status */} -
-
- - Prescription: - {hasPrescription ? ( - Available +
+ {appointment.appointmentType === 'virtual' ? ( + <> +
- + + {hasPrescription && ( +
+ + Prescription Available +
+ )}
+ +
); diff --git a/client/src/components/Patient/PatientAppointments/AppointmentHeader.jsx b/client/src/components/Patient/PatientAppointments/AppointmentHeader.jsx index 069b781..788b796 100644 --- a/client/src/components/Patient/PatientAppointments/AppointmentHeader.jsx +++ b/client/src/components/Patient/PatientAppointments/AppointmentHeader.jsx @@ -2,40 +2,41 @@ import { Search, Filter } from 'lucide-react'; const AppointmentHeader = ({ searchTerm, setSearchTerm, filterStatus, setFilterStatus }) => { return ( -
-
-
-

My Appointments

-

Manage and view your medical appointments

-
+
+
+

+ My Appointments +

+

+ Manage and view your medical appointments +

+
- {/* Search and Filter */} -
-
- - setSearchTerm(e.target.value)} - className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" - /> -
+
+
+ + setSearchTerm(e.target.value)} + className="w-full pl-10 pr-4 py-2.5 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-all" + /> +
-
- - -
+
+ +
diff --git a/client/src/components/Patient/PatientAppointments/AppointmentStats.jsx b/client/src/components/Patient/PatientAppointments/AppointmentStats.jsx deleted file mode 100644 index f9de81b..0000000 --- a/client/src/components/Patient/PatientAppointments/AppointmentStats.jsx +++ /dev/null @@ -1,56 +0,0 @@ -import { Calendar, Clock, CheckCircle, FileText } from 'lucide-react'; - -const AppointmentStats = ({ appointments, prescriptionStatus }) => { - const completedCount = appointments.filter((apt) => apt.status === 'completed').length; - const prescriptionCount = Object.values(prescriptionStatus).filter(Boolean).length; - - const stats = [ - { - label: 'Total', - value: appointments.length, - icon: Calendar, - color: 'text-gray-900', - iconColor: 'text-gray-400', - }, - { - label: 'Upcoming', - value: appointments.filter((apt) => apt.status === 'confirmed' || apt.status === 'pending') - .length, - icon: Clock, - color: 'text-blue-600', - iconColor: 'text-blue-400', - }, - { - label: 'Completed', - value: completedCount, - icon: CheckCircle, - color: 'text-green-600', - iconColor: 'text-green-400', - }, - { - label: 'With Prescription', - value: prescriptionCount, - icon: FileText, - color: 'text-purple-600', - iconColor: 'text-purple-400', - }, - ]; - - return ( -
- {stats.map((stat, index) => ( -
-
-
-

{stat.label}

-

{stat.value}

-
- -
-
- ))} -
- ); -}; - -export default AppointmentStats; diff --git a/client/src/components/Patient/PatientAppointments/AppointmentTabs.jsx b/client/src/components/Patient/PatientAppointments/AppointmentTabs.jsx index ca8f5c9..2397697 100644 --- a/client/src/components/Patient/PatientAppointments/AppointmentTabs.jsx +++ b/client/src/components/Patient/PatientAppointments/AppointmentTabs.jsx @@ -10,10 +10,8 @@ const AppointmentTabs = ({ setActiveTab, onAppointmentClick, }) => { - // Categorize appointments const categorizedAppointments = useMemo(() => { const today = new Date(); - return { upcoming: appointments .filter((apt) => { @@ -25,14 +23,12 @@ const AppointmentTabs = ({ ); }) .sort((a, b) => new Date(a.date) - new Date(b.date)), - today: appointments .filter((apt) => { const aptDate = new Date(apt.date); return isToday(aptDate); }) .sort((a, b) => a.startTime.localeCompare(b.startTime)), - past: appointments .filter((apt) => { const aptDate = new Date(apt.date); @@ -42,7 +38,6 @@ const AppointmentTabs = ({ }; }, [appointments]); - // Tab Navigation const tabs = [ { key: 'upcoming', @@ -62,54 +57,47 @@ const AppointmentTabs = ({ ]; return ( -
-
- + {tab.count} + + {activeTab === tab.key && ( +
+ )} + + ))}
- {/* Appointments Content */} -
+
{categorizedAppointments[activeTab]?.length > 0 ? ( -
- {categorizedAppointments[activeTab].map((appointment) => ( - - ))} -
+ categorizedAppointments[activeTab].map((appointment) => ( + + )) ) : ( -
- -

No {activeTab} appointments

-

+

+
+ +
+

{activeTab === 'upcoming' ? 'You have no upcoming appointments.' : activeTab === 'today' diff --git a/client/src/pages/patient/PatientAppointments.jsx b/client/src/pages/patient/PatientAppointments.jsx index 1d2b488..e27529e 100644 --- a/client/src/pages/patient/PatientAppointments.jsx +++ b/client/src/pages/patient/PatientAppointments.jsx @@ -2,11 +2,11 @@ import { useEffect, useMemo, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { getPatientAppointments } from '../../service/appointmentApiService'; import { getPatientAppointmentPrescription } from '../../service/prescriptionApiSevice'; -import AppointmentStats from '../../components/Patient/PatientAppointments/AppointmentStats'; import AppointmentTabs from '../../components/Patient/PatientAppointments/AppointmentTabs'; import AppointmentHeader from '../../components/Patient/PatientAppointments/AppointmentHeader'; import ErrorState from '../../components/Patient/PatientAppointments/ErrorState'; import Loading from '../../components/ui/Loading'; + const PatientAppointments = () => { const [appointments, setAppointments] = useState([]); const [loading, setLoading] = useState(true); @@ -15,23 +15,18 @@ const PatientAppointments = () => { const [searchTerm, setSearchTerm] = useState(''); const [filterStatus, setFilterStatus] = useState('all'); const [activeTab, setActiveTab] = useState('upcoming'); - const navigate = useNavigate(); - // Fetch appointments on component mount useEffect(() => { fetchAppointments(); }, []); - // Fetch appointments function const fetchAppointments = async () => { try { setLoading(true); const response = await getPatientAppointments(); - if (response && response.appointments) { setAppointments(response.appointments); - // Check prescription status for each appointment await checkPrescriptionStatus(response.appointments); } } catch (err) { @@ -42,10 +37,8 @@ const PatientAppointments = () => { } }; - // Check prescription status for appointments const checkPrescriptionStatus = async (appointmentList) => { const prescriptionStatusMap = {}; - const statusChecks = appointmentList.map(async (appointment) => { try { await getPatientAppointmentPrescription(appointment._id); @@ -55,16 +48,13 @@ const PatientAppointments = () => { console.log(err); } }); - await Promise.all(statusChecks); setPrescriptionStatus(prescriptionStatusMap); }; - // Filter and search appointments const filteredAppointments = useMemo(() => { let filtered = appointments; - // Search filter if (searchTerm.trim()) { const term = searchTerm.toLowerCase(); filtered = filtered.filter( @@ -77,7 +67,6 @@ const PatientAppointments = () => { ); } - // Status filter if (filterStatus !== 'all') { filtered = filtered.filter((appointment) => appointment.status === filterStatus); } @@ -89,10 +78,13 @@ const PatientAppointments = () => { return ; } + if (error) { + return ; + } + return ( -

-
- {/* Header */} +
+
{ setFilterStatus={setFilterStatus} /> - {/* Error State */} - {error && } - - {/* Main Content */} navigate(`/patient/appointment/${appointmentId}`)} - /> - - {/* Quick Stats */} - navigate(`/patient/appointment/${id}`)} />