From 9786f27ab94f3da1fb2dd32c58f573f69b8956e0 Mon Sep 17 00:00:00 2001 From: Tasneemfahmi Date: Fri, 11 Jul 2025 10:48:43 +0300 Subject: [PATCH 1/9] final ui touches --- src/assets/logo-doctor.png | Bin 0 -> 7316 bytes src/assets/logo-sidebar-white.png | Bin 0 -> 6713 bytes src/components/doctor/Dashboard/Topbar.jsx | 2 +- .../doctor/Diagnosis/AddDignosisForm.jsx | 10 +++++----- src/components/doctor/Sidebar/Sidebar.jsx | 5 +++-- 5 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 src/assets/logo-doctor.png create mode 100644 src/assets/logo-sidebar-white.png diff --git a/src/assets/logo-doctor.png b/src/assets/logo-doctor.png new file mode 100644 index 0000000000000000000000000000000000000000..dec903a8c24802d614394de7130f62dbd8556428 GIT binary patch literal 7316 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 literal 0 HcmV?d00001 diff --git a/src/assets/logo-sidebar-white.png b/src/assets/logo-sidebar-white.png new file mode 100644 index 0000000000000000000000000000000000000000..50614f4452cb67affa87b801bf89885fd3b93b69 GIT binary patch literal 6713 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 literal 0 HcmV?d00001 diff --git a/src/components/doctor/Dashboard/Topbar.jsx b/src/components/doctor/Dashboard/Topbar.jsx index 6ef58c5..27ea992 100644 --- a/src/components/doctor/Dashboard/Topbar.jsx +++ b/src/components/doctor/Dashboard/Topbar.jsx @@ -73,7 +73,7 @@ export const Topbar = ({ isCollapsed, setIsCollapsed }) => { onClick={() => setIsCollapsed(!isCollapsed)} > diff --git a/src/components/doctor/Diagnosis/AddDignosisForm.jsx b/src/components/doctor/Diagnosis/AddDignosisForm.jsx index eaa53fc..fff54bc 100644 --- a/src/components/doctor/Diagnosis/AddDignosisForm.jsx +++ b/src/components/doctor/Diagnosis/AddDignosisForm.jsx @@ -146,12 +146,12 @@ export const AddDiagnosisForm = ({ value={symptomInput} onChange={(e) => setSymptomInput(e.target.value)} placeholder="e.g., Headache" - className="flex-1 px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-megreen" + className="flex-1 px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-[#007eb1]" /> @@ -198,7 +198,7 @@ export const AddDiagnosisForm = ({ value={recommendations} onChange={(e) => setRecommendations(e.target.value)} placeholder="e.g., avoid junk food" - className="w-full px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-meyellow" + className="w-full px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-menavy" /> @@ -210,7 +210,7 @@ export const AddDiagnosisForm = ({ value={followUp} onChange={(e) => setFollowUp(e.target.value)} placeholder="e.g., Next Sunday" - className="w-full px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-meyellow" + className="w-full px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-menavy" /> @@ -222,7 +222,7 @@ export const AddDiagnosisForm = ({ onChange={(e) => setNotes(e.target.value)} placeholder="Additional notes or observations" rows={3} - className="w-full px-4 py-2 border border-mebeige rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-mepale" + className="w-full px-4 py-2 border border-mebeige rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-menavy" /> diff --git a/src/components/doctor/Sidebar/Sidebar.jsx b/src/components/doctor/Sidebar/Sidebar.jsx index 181eafd..0aa2f07 100644 --- a/src/components/doctor/Sidebar/Sidebar.jsx +++ b/src/components/doctor/Sidebar/Sidebar.jsx @@ -18,6 +18,7 @@ import { useSelector } from "react-redux"; import { selectMyDetails } from "@/store/selectors"; import { CiPillsBottle1 } from "react-icons/ci"; +import logo from "../../../assets/logo-sidebar-white.png"; //* links classes const linkStyles = cn( @@ -36,7 +37,7 @@ export default function Sidebar({ currentPath, isCollapsed }) { return (
{/* */}
- logo + logo {isCollapsed && MedEase}
{/* */} From 072032920e5c89980c20ccf63750ae61724bd515 Mon Sep 17 00:00:00 2001 From: Tasneemfahmi Date: Fri, 11 Jul 2025 10:48:43 +0300 Subject: [PATCH 2/9] final ui touches --- src/assets/logo-doctor.png | Bin 0 -> 7316 bytes src/assets/logo-sidebar-white.png | Bin 0 -> 6713 bytes src/components/doctor/Dashboard/Topbar.jsx | 2 +- .../doctor/Diagnosis/AddDignosisForm.jsx | 10 +++++----- src/components/doctor/Sidebar/Sidebar.jsx | 5 +++-- 5 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 src/assets/logo-doctor.png create mode 100644 src/assets/logo-sidebar-white.png diff --git a/src/assets/logo-doctor.png b/src/assets/logo-doctor.png new file mode 100644 index 0000000000000000000000000000000000000000..dec903a8c24802d614394de7130f62dbd8556428 GIT binary patch literal 7316 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 literal 0 HcmV?d00001 diff --git a/src/assets/logo-sidebar-white.png b/src/assets/logo-sidebar-white.png new file mode 100644 index 0000000000000000000000000000000000000000..50614f4452cb67affa87b801bf89885fd3b93b69 GIT binary patch literal 6713 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 literal 0 HcmV?d00001 diff --git a/src/components/doctor/Dashboard/Topbar.jsx b/src/components/doctor/Dashboard/Topbar.jsx index 6ef58c5..27ea992 100644 --- a/src/components/doctor/Dashboard/Topbar.jsx +++ b/src/components/doctor/Dashboard/Topbar.jsx @@ -73,7 +73,7 @@ export const Topbar = ({ isCollapsed, setIsCollapsed }) => { onClick={() => setIsCollapsed(!isCollapsed)} > diff --git a/src/components/doctor/Diagnosis/AddDignosisForm.jsx b/src/components/doctor/Diagnosis/AddDignosisForm.jsx index eaa53fc..fff54bc 100644 --- a/src/components/doctor/Diagnosis/AddDignosisForm.jsx +++ b/src/components/doctor/Diagnosis/AddDignosisForm.jsx @@ -146,12 +146,12 @@ export const AddDiagnosisForm = ({ value={symptomInput} onChange={(e) => setSymptomInput(e.target.value)} placeholder="e.g., Headache" - className="flex-1 px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-megreen" + className="flex-1 px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-[#007eb1]" /> @@ -198,7 +198,7 @@ export const AddDiagnosisForm = ({ value={recommendations} onChange={(e) => setRecommendations(e.target.value)} placeholder="e.g., avoid junk food" - className="w-full px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-meyellow" + className="w-full px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-menavy" />

@@ -210,7 +210,7 @@ export const AddDiagnosisForm = ({ value={followUp} onChange={(e) => setFollowUp(e.target.value)} placeholder="e.g., Next Sunday" - className="w-full px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-meyellow" + className="w-full px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-menavy" /> @@ -222,7 +222,7 @@ export const AddDiagnosisForm = ({ onChange={(e) => setNotes(e.target.value)} placeholder="Additional notes or observations" rows={3} - className="w-full px-4 py-2 border border-mebeige rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-mepale" + className="w-full px-4 py-2 border border-mebeige rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-menavy" /> diff --git a/src/components/doctor/Sidebar/Sidebar.jsx b/src/components/doctor/Sidebar/Sidebar.jsx index 181eafd..0aa2f07 100644 --- a/src/components/doctor/Sidebar/Sidebar.jsx +++ b/src/components/doctor/Sidebar/Sidebar.jsx @@ -18,6 +18,7 @@ import { useSelector } from "react-redux"; import { selectMyDetails } from "@/store/selectors"; import { CiPillsBottle1 } from "react-icons/ci"; +import logo from "../../../assets/logo-sidebar-white.png"; //* links classes const linkStyles = cn( @@ -36,7 +37,7 @@ export default function Sidebar({ currentPath, isCollapsed }) { return (
{/* */}
- logo + logo {isCollapsed && MedEase}
{/* */} From 6cb73db6f7bda7e7b067ad66c61cfd4176ae2a3c Mon Sep 17 00:00:00 2001 From: Tasneemfahmi Date: Fri, 11 Jul 2025 21:53:13 +0300 Subject: [PATCH 3/9] finally I hope --- src/components/doctor/Dashboard/StatCards.jsx | 152 +++++++++++++----- .../doctor/Patients/Patients_List.jsx | 134 ++++++++------- .../doctor/Patients/Patients_Main.jsx | 24 ++- 3 files changed, 212 insertions(+), 98 deletions(-) diff --git a/src/components/doctor/Dashboard/StatCards.jsx b/src/components/doctor/Dashboard/StatCards.jsx index b0a0a95..73a9b10 100644 --- a/src/components/doctor/Dashboard/StatCards.jsx +++ b/src/components/doctor/Dashboard/StatCards.jsx @@ -6,23 +6,39 @@ import { fetchShowPatientById } from "@/store/slices/patientSlice"; import { fetchAppointments } from "@/store/slices/appointmentSlice"; import { selectAllAppointments, selectShowPatientById, selectPatientsLoading } from "@/store/selectors"; +// Helper function to calculate percentage change +const calculateTrend = (current, previous) => { + if (previous === 0) { + return current > 0 ? "+100%" : "0%"; + } + + const percentChange = ((current - previous) / previous) * 100; + const sign = percentChange >= 0 ? "+" : ""; + return `${sign}${Math.round(percentChange)}%`; +}; + +// Helper function to determine trend direction +const getTrendDirection = (trendString) => { + return trendString.startsWith("+") || trendString === "0%" ? "up" : "down"; +}; + export const StatCards = () => { const [fetchedPatients, setFetchedPatients] = useState({}); const dispatch = useDispatch(); // Use the same selectors as other components - const Appointments = useSelector(selectAllAppointments); + const appointments = useSelector(selectAllAppointments); const currentPatient = useSelector(selectShowPatientById); const loading = useSelector(selectPatientsLoading); // Memoize unique patient IDs from appointments const uniquePatientIds = useMemo(() => { return [...new Set( - Appointments + appointments .map(a => a.patientId?._id || a.patientId) .filter(Boolean) )]; - }, [Appointments]); + }, [appointments]); // Store the current patient when it's fetched useEffect(() => { @@ -56,62 +72,124 @@ export const StatCards = () => { }, [fetchedPatients, uniquePatientIds]); // Calculate date ranges - const today = new Date(); - const todaydate = `to ${today.toLocaleDateString()}`; - const weekAgo = new Date(); - weekAgo.setDate(today.getDate() - 7); - const weekAgoDate = weekAgo.toLocaleDateString(); - const weekperiod = `${weekAgoDate} ${todaydate}`; - - // Calculate patients registered this week - const patientsThisWeek = useMemo(() => { - return patients.filter(patient => { + const { today, weekAgo, previousWeekStart, todayDate, weekPeriod } = useMemo(() => { + const today = new Date(); + const weekAgo = new Date(); + weekAgo.setDate(today.getDate() - 7); + + const previousWeekStart = new Date(); + previousWeekStart.setDate(today.getDate() - 14); + + return { + today, + weekAgo, + previousWeekStart, + todayDate: `to ${today.toLocaleDateString()}`, + weekPeriod: `${weekAgo.toLocaleDateString()} to ${today.toLocaleDateString()}` + }; + }, []); + + // Calculate metrics with proper trend analysis + const metrics = useMemo(() => { + // Debug logging + console.log('Debug - patients:', patients); + console.log('Debug - appointments:', appointments); + console.log('Debug - patients length:', patients?.length); + console.log('Debug - appointments length:', appointments?.length); + + if (!patients || !appointments || patients.length === 0 || appointments.length === 0) { + console.log('Debug - Missing data, returning zeros'); + return { + totalPatients: uniquePatientIds?.length || 0, + patientsThisWeek: 0, + patientsTrend: "0%", + totalAppointments: appointments?.length || 0, + appointmentsThisWeek: 0, + appointmentsTrend: "0%" + }; + } + + // Calculate patients registered this week + const patientsThisWeek = patients.filter(patient => { const createdDate = new Date(patient.createdAt); return createdDate >= weekAgo && createdDate <= today; }).length; - }, [patients, weekAgo, today]); - // Calculate appointments this week - const appointmentsThisWeek = useMemo(() => { - return Appointments.filter(appointment => { + // Calculate patients registered previous week for trend + const patientsPreviousWeek = patients.filter(patient => { + const createdDate = new Date(patient.createdAt); + return createdDate >= previousWeekStart && createdDate < weekAgo; + }).length; + + // Calculate appointments this week + const appointmentsThisWeek = appointments.filter(appointment => { const appointmentDate = new Date(appointment.createdAt || appointment.date); return appointmentDate >= weekAgo && appointmentDate <= today; }).length; - }, [Appointments, weekAgo, today]); - // Calculate trends (you can implement more sophisticated logic here) - const patientsTrend = patientsThisWeek > 0 ? "+100%" : "0%"; - const appointmentsTrend = appointmentsThisWeek > 0 ? "+100%" : "0%"; + // Calculate appointments previous week for trend + const appointmentsPreviousWeek = appointments.filter(appointment => { + const appointmentDate = new Date(appointment.createdAt || appointment.date); + return appointmentDate >= previousWeekStart && appointmentDate < weekAgo; + }).length; + + // Calculate trends + const patientsTrend = calculateTrend(patientsThisWeek, patientsPreviousWeek); + const appointmentsTrend = calculateTrend(appointmentsThisWeek, appointmentsPreviousWeek); + + return { + totalPatients: uniquePatientIds.length, + patientsThisWeek, + patientsTrend, + totalAppointments: appointments.length, + appointmentsThisWeek, + appointmentsTrend + }; + }, [patients, appointments, weekAgo, today, previousWeekStart]); + + if (loading) { + return ( + <> + {[...Array(4)].map((_, i) => ( +
+
+
+
+
+ ))} + + ); + } return ( <> 0 ? "+100%" : "0%"} - trend={patientsThisWeek > 0 ? "up" : "down"} - period={weekperiod} + value={metrics.patientsThisWeek} + pilltext={metrics.patientsTrend} + trend={getTrendDirection(metrics.patientsTrend)} + period={weekPeriod} /> 0 ? "+100%" : "0%"} - trend={appointmentsThisWeek > 0 ? "up" : "down"} - period={weekperiod} + value={metrics.appointmentsThisWeek} + pilltext={metrics.appointmentsTrend} + trend={getTrendDirection(metrics.appointmentsTrend)} + period={weekPeriod} /> ); diff --git a/src/components/doctor/Patients/Patients_List.jsx b/src/components/doctor/Patients/Patients_List.jsx index 6c2fdae..f9c5948 100644 --- a/src/components/doctor/Patients/Patients_List.jsx +++ b/src/components/doctor/Patients/Patients_List.jsx @@ -4,7 +4,11 @@ import { Card, CardContent } from "@/components/ui/card"; import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"; import { fetchShowPatientById } from "@/store/slices/patientSlice"; import { fetchAppointments } from "@/store/slices/appointmentSlice"; -import { selectAllAppointments, selectShowPatientById, selectPatientsLoading } from "@/store/selectors"; +import { + selectAllAppointments, + selectShowPatientById, + selectPatientsLoading, +} from "@/store/selectors"; const fallbackPatients = [ { @@ -39,88 +43,102 @@ const fallbackPatients = [ phone: "+201098765432", }, }, - { - _id: "fallback-3", - name: "Nour Khaled", - gender: "Female", - dateOfBirth: "2001-12-05", - email: "nour.khaled@example.com", - phone: "+201112223344", - city: "Giza", - country: "Egypt", - createdAt: "2024-01-20T09:45:00Z", - emergencyContact: { - name: "Khaled Youssef", - relationship: "Father", - phone: "+201234001122", - }, - }, ]; export const Patients_List = ({ onPatientSelect, sortBy }) => { const [selectedPatientId, setSelectedPatientId] = useState(null); const [fetchedPatients, setFetchedPatients] = useState({}); + const [fetchedPatientIds, setFetchedPatientIds] = useState(new Set()); + const dispatch = useDispatch(); - - // Use the same selectors as the chart - const Appointments = useSelector(selectAllAppointments); - const currentPatient = useSelector(selectShowPatientById); // This is a single patient object + const appointments = useSelector(selectAllAppointments); const loading = useSelector(selectPatientsLoading); - // Memoize unique patient IDs from appointments (adjusted for API structure) + // Extract unique patient IDs const uniquePatientIds = useMemo(() => { - return [...new Set( - Appointments - .map(a => a.patientId?._id || a.patientId) // Handle both object and string formats - .filter(Boolean) // Remove null/undefined values - )]; - }, [Appointments]); - - // Fetch appointments once (same as chart) + const ids = [ + ...new Set( + appointments + .map((a) => (a.patientId?._id || a.patientId)) + .filter(Boolean) + ), + ]; + console.log("โœ… Unique patient IDs from appointments:", ids); + return ids; + }, [appointments]); + + // Fetch appointments on mount useEffect(() => { dispatch(fetchAppointments()); }, [dispatch]); - // Store the current patient when it's fetched - useEffect(() => { - if (currentPatient && currentPatient._id) { - setFetchedPatients(prev => ({ - ...prev, - [currentPatient._id]: currentPatient - })); + // Fetch missing patients + const fetchMissingPatients = async () => { + console.log("๐Ÿ” Starting patient fetch loop"); + + for (const id of uniquePatientIds) { + if (!fetchedPatientIds.has(id) && !fetchedPatients[id]) { + console.log(`โžก๏ธ Fetching patient with ID: ${id}`); + + try { + const resultAction = await dispatch(fetchShowPatientById(id)); + console.log("๐Ÿงพ Result Action:", resultAction); + + if (fetchShowPatientById.fulfilled.match(resultAction)) { + const payload = resultAction.payload; + + if (!payload) { + console.error("โŒ No payload returned for patient ID:", id); + continue; + } + + const patient = payload?.data || payload; + + if (patient && patient._id) { + console.log("โœ… Patient fetched:", patient); + setFetchedPatients((prev) => ({ + ...prev, + [patient._id]: patient, + })); + setFetchedPatientIds((prev) => new Set([...prev, id])); + } else { + console.warn("โš ๏ธ Invalid patient structure:", payload); + } + } else { + console.error("โŒ fetchShowPatientById thunk was rejected:", resultAction); + } + } catch (err) { + console.error("โŒ Exception during patient fetch:", err); + } + } else { + console.log(`โฉ Already fetched ID: ${id}`); + } } - }, [currentPatient]); + }; - // Fetch patients individually based on appointment data + // Trigger fetch useEffect(() => { if (uniquePatientIds.length > 0) { - uniquePatientIds.forEach(id => { - if (!fetchedPatients[id]) { - dispatch(fetchShowPatientById(id)); - } - }); + fetchMissingPatients(); } - }, [dispatch, uniquePatientIds, fetchedPatients]); + }, [uniquePatientIds]); - // Get patient list from the fetchedPatients + // Construct the patient list from fetched data const patients = useMemo(() => { - console.log('fetchedPatients content:', fetchedPatients); - console.log('uniquePatientIds:', uniquePatientIds); - - const result = uniquePatientIds.map(id => fetchedPatients[id]).filter(Boolean); - console.log('Patients result:', result); + const result = uniquePatientIds.map((id) => fetchedPatients[id]).filter(Boolean); + console.log("๐Ÿ“‹ Current patients to render:", result); return result; }, [fetchedPatients, uniquePatientIds]); - // Check if we're still loading any patients - const isLoading = loading || (uniquePatientIds.length > 0 && uniquePatientIds.some(id => !fetchedPatients[id])); + const isLoading = + loading || + (uniquePatientIds.length > 0 && patients.length < uniquePatientIds.length); const handlePatientClick = (patient) => { setSelectedPatientId(patient._id); onPatientSelect(patient); }; - // Use patients from store if available, otherwise fallback const patientsToDisplay = patients.length > 0 ? patients : fallbackPatients; const sortedPatients = [...patientsToDisplay].sort((a, b) => { @@ -136,20 +154,20 @@ export const Patients_List = ({ onPatientSelect, sortBy }) => { } }); - // Show loading state if we're fetching patients if (isLoading && patients.length === 0 && uniquePatientIds.length > 0) { return (
-

Loading patients... ({uniquePatientIds.length} patients to fetch)

+

+ Fetched: {patients.length} / {uniquePatientIds.length} +

); } - // Show message if no appointments found if (uniquePatientIds.length === 0) { return (
@@ -191,4 +209,4 @@ export const Patients_List = ({ onPatientSelect, sortBy }) => { ))}
); -}; \ No newline at end of file +}; diff --git a/src/components/doctor/Patients/Patients_Main.jsx b/src/components/doctor/Patients/Patients_Main.jsx index e44fc3b..4f646a8 100644 --- a/src/components/doctor/Patients/Patients_Main.jsx +++ b/src/components/doctor/Patients/Patients_Main.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useNavigate } from "react-router-dom"; import { @@ -14,6 +14,9 @@ import { Card, CardContent } from "@/components/ui/card"; import { fetchAllPatients } from "@/store/slices/patientSlice"; import { fetchAppointments } from "@/store/slices/appointmentSlice"; import { getAllDiagnosis } from "@/services/diagnosisApi"; +import { handleNameRoute } from "@/utils/urlHelpers"; +import { selectMyDetails } from "@/store/selectors"; +import { fetchMYData } from "@/store/slices/userSlice"; export const Patients_Main = ({ selectedPatient, patientsList }) => { const dispatch = useDispatch(); @@ -34,6 +37,17 @@ export const Patients_Main = ({ selectedPatient, patientsList }) => { const patient = selectedPatient; const patientId = patient?._id; + const myDetails = useSelector(selectMyDetails); + + + + useEffect(() => { + dispatch(fetchMYData()); + }, [dispatch]); + + const doctorSlug = useMemo(() => { + return myDetails?.name ? handleNameRoute(myDetails.name) : ""; + }, [myDetails]); // Fetch all patients and appointments on mount useEffect(() => { @@ -118,7 +132,9 @@ export const Patients_Main = ({ selectedPatient, patientsList }) => { value={gender} /> } + icon={ + + } label="City" value={city} /> @@ -164,7 +180,9 @@ export const Patients_Main = ({ selectedPatient, patientsList }) => { diff --git a/src/components/doctor/Diagnosis/AddDignosisForm.jsx b/src/components/doctor/Diagnosis/AddDignosisForm.jsx index eaa53fc..fff54bc 100644 --- a/src/components/doctor/Diagnosis/AddDignosisForm.jsx +++ b/src/components/doctor/Diagnosis/AddDignosisForm.jsx @@ -146,12 +146,12 @@ export const AddDiagnosisForm = ({ value={symptomInput} onChange={(e) => setSymptomInput(e.target.value)} placeholder="e.g., Headache" - className="flex-1 px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-megreen" + className="flex-1 px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-[#007eb1]" /> @@ -198,7 +198,7 @@ export const AddDiagnosisForm = ({ value={recommendations} onChange={(e) => setRecommendations(e.target.value)} placeholder="e.g., avoid junk food" - className="w-full px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-meyellow" + className="w-full px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-menavy" />
@@ -210,7 +210,7 @@ export const AddDiagnosisForm = ({ value={followUp} onChange={(e) => setFollowUp(e.target.value)} placeholder="e.g., Next Sunday" - className="w-full px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-meyellow" + className="w-full px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-menavy" /> @@ -222,7 +222,7 @@ export const AddDiagnosisForm = ({ onChange={(e) => setNotes(e.target.value)} placeholder="Additional notes or observations" rows={3} - className="w-full px-4 py-2 border border-mebeige rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-mepale" + className="w-full px-4 py-2 border border-mebeige rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-menavy" /> diff --git a/src/components/doctor/Sidebar/Sidebar.jsx b/src/components/doctor/Sidebar/Sidebar.jsx index 181eafd..0aa2f07 100644 --- a/src/components/doctor/Sidebar/Sidebar.jsx +++ b/src/components/doctor/Sidebar/Sidebar.jsx @@ -18,6 +18,7 @@ import { useSelector } from "react-redux"; import { selectMyDetails } from "@/store/selectors"; import { CiPillsBottle1 } from "react-icons/ci"; +import logo from "../../../assets/logo-sidebar-white.png"; //* links classes const linkStyles = cn( @@ -36,7 +37,7 @@ export default function Sidebar({ currentPath, isCollapsed }) { return (
{/* */}
- logo + logo {isCollapsed && MedEase}
{/* */} From ddc3a0314175ef029d2b1b6decad670c9821b0bf Mon Sep 17 00:00:00 2001 From: Tasneemfahmi Date: Fri, 11 Jul 2025 21:53:13 +0300 Subject: [PATCH 5/9] finally I hope --- src/components/doctor/Dashboard/StatCards.jsx | 152 +++++++++++++----- .../doctor/Patients/Patients_List.jsx | 134 ++++++++------- .../doctor/Patients/Patients_Main.jsx | 24 ++- 3 files changed, 212 insertions(+), 98 deletions(-) diff --git a/src/components/doctor/Dashboard/StatCards.jsx b/src/components/doctor/Dashboard/StatCards.jsx index b0a0a95..73a9b10 100644 --- a/src/components/doctor/Dashboard/StatCards.jsx +++ b/src/components/doctor/Dashboard/StatCards.jsx @@ -6,23 +6,39 @@ import { fetchShowPatientById } from "@/store/slices/patientSlice"; import { fetchAppointments } from "@/store/slices/appointmentSlice"; import { selectAllAppointments, selectShowPatientById, selectPatientsLoading } from "@/store/selectors"; +// Helper function to calculate percentage change +const calculateTrend = (current, previous) => { + if (previous === 0) { + return current > 0 ? "+100%" : "0%"; + } + + const percentChange = ((current - previous) / previous) * 100; + const sign = percentChange >= 0 ? "+" : ""; + return `${sign}${Math.round(percentChange)}%`; +}; + +// Helper function to determine trend direction +const getTrendDirection = (trendString) => { + return trendString.startsWith("+") || trendString === "0%" ? "up" : "down"; +}; + export const StatCards = () => { const [fetchedPatients, setFetchedPatients] = useState({}); const dispatch = useDispatch(); // Use the same selectors as other components - const Appointments = useSelector(selectAllAppointments); + const appointments = useSelector(selectAllAppointments); const currentPatient = useSelector(selectShowPatientById); const loading = useSelector(selectPatientsLoading); // Memoize unique patient IDs from appointments const uniquePatientIds = useMemo(() => { return [...new Set( - Appointments + appointments .map(a => a.patientId?._id || a.patientId) .filter(Boolean) )]; - }, [Appointments]); + }, [appointments]); // Store the current patient when it's fetched useEffect(() => { @@ -56,62 +72,124 @@ export const StatCards = () => { }, [fetchedPatients, uniquePatientIds]); // Calculate date ranges - const today = new Date(); - const todaydate = `to ${today.toLocaleDateString()}`; - const weekAgo = new Date(); - weekAgo.setDate(today.getDate() - 7); - const weekAgoDate = weekAgo.toLocaleDateString(); - const weekperiod = `${weekAgoDate} ${todaydate}`; - - // Calculate patients registered this week - const patientsThisWeek = useMemo(() => { - return patients.filter(patient => { + const { today, weekAgo, previousWeekStart, todayDate, weekPeriod } = useMemo(() => { + const today = new Date(); + const weekAgo = new Date(); + weekAgo.setDate(today.getDate() - 7); + + const previousWeekStart = new Date(); + previousWeekStart.setDate(today.getDate() - 14); + + return { + today, + weekAgo, + previousWeekStart, + todayDate: `to ${today.toLocaleDateString()}`, + weekPeriod: `${weekAgo.toLocaleDateString()} to ${today.toLocaleDateString()}` + }; + }, []); + + // Calculate metrics with proper trend analysis + const metrics = useMemo(() => { + // Debug logging + console.log('Debug - patients:', patients); + console.log('Debug - appointments:', appointments); + console.log('Debug - patients length:', patients?.length); + console.log('Debug - appointments length:', appointments?.length); + + if (!patients || !appointments || patients.length === 0 || appointments.length === 0) { + console.log('Debug - Missing data, returning zeros'); + return { + totalPatients: uniquePatientIds?.length || 0, + patientsThisWeek: 0, + patientsTrend: "0%", + totalAppointments: appointments?.length || 0, + appointmentsThisWeek: 0, + appointmentsTrend: "0%" + }; + } + + // Calculate patients registered this week + const patientsThisWeek = patients.filter(patient => { const createdDate = new Date(patient.createdAt); return createdDate >= weekAgo && createdDate <= today; }).length; - }, [patients, weekAgo, today]); - // Calculate appointments this week - const appointmentsThisWeek = useMemo(() => { - return Appointments.filter(appointment => { + // Calculate patients registered previous week for trend + const patientsPreviousWeek = patients.filter(patient => { + const createdDate = new Date(patient.createdAt); + return createdDate >= previousWeekStart && createdDate < weekAgo; + }).length; + + // Calculate appointments this week + const appointmentsThisWeek = appointments.filter(appointment => { const appointmentDate = new Date(appointment.createdAt || appointment.date); return appointmentDate >= weekAgo && appointmentDate <= today; }).length; - }, [Appointments, weekAgo, today]); - // Calculate trends (you can implement more sophisticated logic here) - const patientsTrend = patientsThisWeek > 0 ? "+100%" : "0%"; - const appointmentsTrend = appointmentsThisWeek > 0 ? "+100%" : "0%"; + // Calculate appointments previous week for trend + const appointmentsPreviousWeek = appointments.filter(appointment => { + const appointmentDate = new Date(appointment.createdAt || appointment.date); + return appointmentDate >= previousWeekStart && appointmentDate < weekAgo; + }).length; + + // Calculate trends + const patientsTrend = calculateTrend(patientsThisWeek, patientsPreviousWeek); + const appointmentsTrend = calculateTrend(appointmentsThisWeek, appointmentsPreviousWeek); + + return { + totalPatients: uniquePatientIds.length, + patientsThisWeek, + patientsTrend, + totalAppointments: appointments.length, + appointmentsThisWeek, + appointmentsTrend + }; + }, [patients, appointments, weekAgo, today, previousWeekStart]); + + if (loading) { + return ( + <> + {[...Array(4)].map((_, i) => ( +
+
+
+
+
+ ))} + + ); + } return ( <> 0 ? "+100%" : "0%"} - trend={patientsThisWeek > 0 ? "up" : "down"} - period={weekperiod} + value={metrics.patientsThisWeek} + pilltext={metrics.patientsTrend} + trend={getTrendDirection(metrics.patientsTrend)} + period={weekPeriod} /> 0 ? "+100%" : "0%"} - trend={appointmentsThisWeek > 0 ? "up" : "down"} - period={weekperiod} + value={metrics.appointmentsThisWeek} + pilltext={metrics.appointmentsTrend} + trend={getTrendDirection(metrics.appointmentsTrend)} + period={weekPeriod} /> ); diff --git a/src/components/doctor/Patients/Patients_List.jsx b/src/components/doctor/Patients/Patients_List.jsx index 6c2fdae..f9c5948 100644 --- a/src/components/doctor/Patients/Patients_List.jsx +++ b/src/components/doctor/Patients/Patients_List.jsx @@ -4,7 +4,11 @@ import { Card, CardContent } from "@/components/ui/card"; import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"; import { fetchShowPatientById } from "@/store/slices/patientSlice"; import { fetchAppointments } from "@/store/slices/appointmentSlice"; -import { selectAllAppointments, selectShowPatientById, selectPatientsLoading } from "@/store/selectors"; +import { + selectAllAppointments, + selectShowPatientById, + selectPatientsLoading, +} from "@/store/selectors"; const fallbackPatients = [ { @@ -39,88 +43,102 @@ const fallbackPatients = [ phone: "+201098765432", }, }, - { - _id: "fallback-3", - name: "Nour Khaled", - gender: "Female", - dateOfBirth: "2001-12-05", - email: "nour.khaled@example.com", - phone: "+201112223344", - city: "Giza", - country: "Egypt", - createdAt: "2024-01-20T09:45:00Z", - emergencyContact: { - name: "Khaled Youssef", - relationship: "Father", - phone: "+201234001122", - }, - }, ]; export const Patients_List = ({ onPatientSelect, sortBy }) => { const [selectedPatientId, setSelectedPatientId] = useState(null); const [fetchedPatients, setFetchedPatients] = useState({}); + const [fetchedPatientIds, setFetchedPatientIds] = useState(new Set()); + const dispatch = useDispatch(); - - // Use the same selectors as the chart - const Appointments = useSelector(selectAllAppointments); - const currentPatient = useSelector(selectShowPatientById); // This is a single patient object + const appointments = useSelector(selectAllAppointments); const loading = useSelector(selectPatientsLoading); - // Memoize unique patient IDs from appointments (adjusted for API structure) + // Extract unique patient IDs const uniquePatientIds = useMemo(() => { - return [...new Set( - Appointments - .map(a => a.patientId?._id || a.patientId) // Handle both object and string formats - .filter(Boolean) // Remove null/undefined values - )]; - }, [Appointments]); - - // Fetch appointments once (same as chart) + const ids = [ + ...new Set( + appointments + .map((a) => (a.patientId?._id || a.patientId)) + .filter(Boolean) + ), + ]; + console.log("โœ… Unique patient IDs from appointments:", ids); + return ids; + }, [appointments]); + + // Fetch appointments on mount useEffect(() => { dispatch(fetchAppointments()); }, [dispatch]); - // Store the current patient when it's fetched - useEffect(() => { - if (currentPatient && currentPatient._id) { - setFetchedPatients(prev => ({ - ...prev, - [currentPatient._id]: currentPatient - })); + // Fetch missing patients + const fetchMissingPatients = async () => { + console.log("๐Ÿ” Starting patient fetch loop"); + + for (const id of uniquePatientIds) { + if (!fetchedPatientIds.has(id) && !fetchedPatients[id]) { + console.log(`โžก๏ธ Fetching patient with ID: ${id}`); + + try { + const resultAction = await dispatch(fetchShowPatientById(id)); + console.log("๐Ÿงพ Result Action:", resultAction); + + if (fetchShowPatientById.fulfilled.match(resultAction)) { + const payload = resultAction.payload; + + if (!payload) { + console.error("โŒ No payload returned for patient ID:", id); + continue; + } + + const patient = payload?.data || payload; + + if (patient && patient._id) { + console.log("โœ… Patient fetched:", patient); + setFetchedPatients((prev) => ({ + ...prev, + [patient._id]: patient, + })); + setFetchedPatientIds((prev) => new Set([...prev, id])); + } else { + console.warn("โš ๏ธ Invalid patient structure:", payload); + } + } else { + console.error("โŒ fetchShowPatientById thunk was rejected:", resultAction); + } + } catch (err) { + console.error("โŒ Exception during patient fetch:", err); + } + } else { + console.log(`โฉ Already fetched ID: ${id}`); + } } - }, [currentPatient]); + }; - // Fetch patients individually based on appointment data + // Trigger fetch useEffect(() => { if (uniquePatientIds.length > 0) { - uniquePatientIds.forEach(id => { - if (!fetchedPatients[id]) { - dispatch(fetchShowPatientById(id)); - } - }); + fetchMissingPatients(); } - }, [dispatch, uniquePatientIds, fetchedPatients]); + }, [uniquePatientIds]); - // Get patient list from the fetchedPatients + // Construct the patient list from fetched data const patients = useMemo(() => { - console.log('fetchedPatients content:', fetchedPatients); - console.log('uniquePatientIds:', uniquePatientIds); - - const result = uniquePatientIds.map(id => fetchedPatients[id]).filter(Boolean); - console.log('Patients result:', result); + const result = uniquePatientIds.map((id) => fetchedPatients[id]).filter(Boolean); + console.log("๐Ÿ“‹ Current patients to render:", result); return result; }, [fetchedPatients, uniquePatientIds]); - // Check if we're still loading any patients - const isLoading = loading || (uniquePatientIds.length > 0 && uniquePatientIds.some(id => !fetchedPatients[id])); + const isLoading = + loading || + (uniquePatientIds.length > 0 && patients.length < uniquePatientIds.length); const handlePatientClick = (patient) => { setSelectedPatientId(patient._id); onPatientSelect(patient); }; - // Use patients from store if available, otherwise fallback const patientsToDisplay = patients.length > 0 ? patients : fallbackPatients; const sortedPatients = [...patientsToDisplay].sort((a, b) => { @@ -136,20 +154,20 @@ export const Patients_List = ({ onPatientSelect, sortBy }) => { } }); - // Show loading state if we're fetching patients if (isLoading && patients.length === 0 && uniquePatientIds.length > 0) { return (
-

Loading patients... ({uniquePatientIds.length} patients to fetch)

+

+ Fetched: {patients.length} / {uniquePatientIds.length} +

); } - // Show message if no appointments found if (uniquePatientIds.length === 0) { return (
@@ -191,4 +209,4 @@ export const Patients_List = ({ onPatientSelect, sortBy }) => { ))}
); -}; \ No newline at end of file +}; diff --git a/src/components/doctor/Patients/Patients_Main.jsx b/src/components/doctor/Patients/Patients_Main.jsx index e44fc3b..4f646a8 100644 --- a/src/components/doctor/Patients/Patients_Main.jsx +++ b/src/components/doctor/Patients/Patients_Main.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useNavigate } from "react-router-dom"; import { @@ -14,6 +14,9 @@ import { Card, CardContent } from "@/components/ui/card"; import { fetchAllPatients } from "@/store/slices/patientSlice"; import { fetchAppointments } from "@/store/slices/appointmentSlice"; import { getAllDiagnosis } from "@/services/diagnosisApi"; +import { handleNameRoute } from "@/utils/urlHelpers"; +import { selectMyDetails } from "@/store/selectors"; +import { fetchMYData } from "@/store/slices/userSlice"; export const Patients_Main = ({ selectedPatient, patientsList }) => { const dispatch = useDispatch(); @@ -34,6 +37,17 @@ export const Patients_Main = ({ selectedPatient, patientsList }) => { const patient = selectedPatient; const patientId = patient?._id; + const myDetails = useSelector(selectMyDetails); + + + + useEffect(() => { + dispatch(fetchMYData()); + }, [dispatch]); + + const doctorSlug = useMemo(() => { + return myDetails?.name ? handleNameRoute(myDetails.name) : ""; + }, [myDetails]); // Fetch all patients and appointments on mount useEffect(() => { @@ -118,7 +132,9 @@ export const Patients_Main = ({ selectedPatient, patientsList }) => { value={gender} /> } + icon={ + + } label="City" value={city} /> @@ -164,7 +180,9 @@ export const Patients_Main = ({ selectedPatient, patientsList }) => { diff --git a/src/components/doctor/Diagnosis/AddDignosisForm.jsx b/src/components/doctor/Diagnosis/AddDignosisForm.jsx index eaa53fc..fff54bc 100644 --- a/src/components/doctor/Diagnosis/AddDignosisForm.jsx +++ b/src/components/doctor/Diagnosis/AddDignosisForm.jsx @@ -146,12 +146,12 @@ export const AddDiagnosisForm = ({ value={symptomInput} onChange={(e) => setSymptomInput(e.target.value)} placeholder="e.g., Headache" - className="flex-1 px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-megreen" + className="flex-1 px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-[#007eb1]" /> @@ -198,7 +198,7 @@ export const AddDiagnosisForm = ({ value={recommendations} onChange={(e) => setRecommendations(e.target.value)} placeholder="e.g., avoid junk food" - className="w-full px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-meyellow" + className="w-full px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-menavy" />
@@ -210,7 +210,7 @@ export const AddDiagnosisForm = ({ value={followUp} onChange={(e) => setFollowUp(e.target.value)} placeholder="e.g., Next Sunday" - className="w-full px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-meyellow" + className="w-full px-4 py-2 border border-mebeige rounded-lg focus:outline-none focus:ring-2 focus:ring-menavy" /> @@ -222,7 +222,7 @@ export const AddDiagnosisForm = ({ onChange={(e) => setNotes(e.target.value)} placeholder="Additional notes or observations" rows={3} - className="w-full px-4 py-2 border border-mebeige rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-mepale" + className="w-full px-4 py-2 border border-mebeige rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-menavy" /> diff --git a/src/components/doctor/Sidebar/Sidebar.jsx b/src/components/doctor/Sidebar/Sidebar.jsx index 181eafd..0aa2f07 100644 --- a/src/components/doctor/Sidebar/Sidebar.jsx +++ b/src/components/doctor/Sidebar/Sidebar.jsx @@ -18,6 +18,7 @@ import { useSelector } from "react-redux"; import { selectMyDetails } from "@/store/selectors"; import { CiPillsBottle1 } from "react-icons/ci"; +import logo from "../../../assets/logo-sidebar-white.png"; //* links classes const linkStyles = cn( @@ -36,7 +37,7 @@ export default function Sidebar({ currentPath, isCollapsed }) { return (
{/* */}
- logo + logo {isCollapsed && MedEase}
{/* */} From fdeb5fc283c22a5440fc0d222109a5092a204c5b Mon Sep 17 00:00:00 2001 From: Tasneemfahmi Date: Fri, 11 Jul 2025 21:53:13 +0300 Subject: [PATCH 8/9] finally I hope --- src/components/doctor/Dashboard/StatCards.jsx | 152 +++++++++++++----- .../doctor/Patients/Patients_List.jsx | 134 ++++++++------- .../doctor/Patients/Patients_Main.jsx | 24 ++- 3 files changed, 212 insertions(+), 98 deletions(-) diff --git a/src/components/doctor/Dashboard/StatCards.jsx b/src/components/doctor/Dashboard/StatCards.jsx index b0a0a95..73a9b10 100644 --- a/src/components/doctor/Dashboard/StatCards.jsx +++ b/src/components/doctor/Dashboard/StatCards.jsx @@ -6,23 +6,39 @@ import { fetchShowPatientById } from "@/store/slices/patientSlice"; import { fetchAppointments } from "@/store/slices/appointmentSlice"; import { selectAllAppointments, selectShowPatientById, selectPatientsLoading } from "@/store/selectors"; +// Helper function to calculate percentage change +const calculateTrend = (current, previous) => { + if (previous === 0) { + return current > 0 ? "+100%" : "0%"; + } + + const percentChange = ((current - previous) / previous) * 100; + const sign = percentChange >= 0 ? "+" : ""; + return `${sign}${Math.round(percentChange)}%`; +}; + +// Helper function to determine trend direction +const getTrendDirection = (trendString) => { + return trendString.startsWith("+") || trendString === "0%" ? "up" : "down"; +}; + export const StatCards = () => { const [fetchedPatients, setFetchedPatients] = useState({}); const dispatch = useDispatch(); // Use the same selectors as other components - const Appointments = useSelector(selectAllAppointments); + const appointments = useSelector(selectAllAppointments); const currentPatient = useSelector(selectShowPatientById); const loading = useSelector(selectPatientsLoading); // Memoize unique patient IDs from appointments const uniquePatientIds = useMemo(() => { return [...new Set( - Appointments + appointments .map(a => a.patientId?._id || a.patientId) .filter(Boolean) )]; - }, [Appointments]); + }, [appointments]); // Store the current patient when it's fetched useEffect(() => { @@ -56,62 +72,124 @@ export const StatCards = () => { }, [fetchedPatients, uniquePatientIds]); // Calculate date ranges - const today = new Date(); - const todaydate = `to ${today.toLocaleDateString()}`; - const weekAgo = new Date(); - weekAgo.setDate(today.getDate() - 7); - const weekAgoDate = weekAgo.toLocaleDateString(); - const weekperiod = `${weekAgoDate} ${todaydate}`; - - // Calculate patients registered this week - const patientsThisWeek = useMemo(() => { - return patients.filter(patient => { + const { today, weekAgo, previousWeekStart, todayDate, weekPeriod } = useMemo(() => { + const today = new Date(); + const weekAgo = new Date(); + weekAgo.setDate(today.getDate() - 7); + + const previousWeekStart = new Date(); + previousWeekStart.setDate(today.getDate() - 14); + + return { + today, + weekAgo, + previousWeekStart, + todayDate: `to ${today.toLocaleDateString()}`, + weekPeriod: `${weekAgo.toLocaleDateString()} to ${today.toLocaleDateString()}` + }; + }, []); + + // Calculate metrics with proper trend analysis + const metrics = useMemo(() => { + // Debug logging + console.log('Debug - patients:', patients); + console.log('Debug - appointments:', appointments); + console.log('Debug - patients length:', patients?.length); + console.log('Debug - appointments length:', appointments?.length); + + if (!patients || !appointments || patients.length === 0 || appointments.length === 0) { + console.log('Debug - Missing data, returning zeros'); + return { + totalPatients: uniquePatientIds?.length || 0, + patientsThisWeek: 0, + patientsTrend: "0%", + totalAppointments: appointments?.length || 0, + appointmentsThisWeek: 0, + appointmentsTrend: "0%" + }; + } + + // Calculate patients registered this week + const patientsThisWeek = patients.filter(patient => { const createdDate = new Date(patient.createdAt); return createdDate >= weekAgo && createdDate <= today; }).length; - }, [patients, weekAgo, today]); - // Calculate appointments this week - const appointmentsThisWeek = useMemo(() => { - return Appointments.filter(appointment => { + // Calculate patients registered previous week for trend + const patientsPreviousWeek = patients.filter(patient => { + const createdDate = new Date(patient.createdAt); + return createdDate >= previousWeekStart && createdDate < weekAgo; + }).length; + + // Calculate appointments this week + const appointmentsThisWeek = appointments.filter(appointment => { const appointmentDate = new Date(appointment.createdAt || appointment.date); return appointmentDate >= weekAgo && appointmentDate <= today; }).length; - }, [Appointments, weekAgo, today]); - // Calculate trends (you can implement more sophisticated logic here) - const patientsTrend = patientsThisWeek > 0 ? "+100%" : "0%"; - const appointmentsTrend = appointmentsThisWeek > 0 ? "+100%" : "0%"; + // Calculate appointments previous week for trend + const appointmentsPreviousWeek = appointments.filter(appointment => { + const appointmentDate = new Date(appointment.createdAt || appointment.date); + return appointmentDate >= previousWeekStart && appointmentDate < weekAgo; + }).length; + + // Calculate trends + const patientsTrend = calculateTrend(patientsThisWeek, patientsPreviousWeek); + const appointmentsTrend = calculateTrend(appointmentsThisWeek, appointmentsPreviousWeek); + + return { + totalPatients: uniquePatientIds.length, + patientsThisWeek, + patientsTrend, + totalAppointments: appointments.length, + appointmentsThisWeek, + appointmentsTrend + }; + }, [patients, appointments, weekAgo, today, previousWeekStart]); + + if (loading) { + return ( + <> + {[...Array(4)].map((_, i) => ( +
+
+
+
+
+ ))} + + ); + } return ( <> 0 ? "+100%" : "0%"} - trend={patientsThisWeek > 0 ? "up" : "down"} - period={weekperiod} + value={metrics.patientsThisWeek} + pilltext={metrics.patientsTrend} + trend={getTrendDirection(metrics.patientsTrend)} + period={weekPeriod} /> 0 ? "+100%" : "0%"} - trend={appointmentsThisWeek > 0 ? "up" : "down"} - period={weekperiod} + value={metrics.appointmentsThisWeek} + pilltext={metrics.appointmentsTrend} + trend={getTrendDirection(metrics.appointmentsTrend)} + period={weekPeriod} /> ); diff --git a/src/components/doctor/Patients/Patients_List.jsx b/src/components/doctor/Patients/Patients_List.jsx index 6c2fdae..f9c5948 100644 --- a/src/components/doctor/Patients/Patients_List.jsx +++ b/src/components/doctor/Patients/Patients_List.jsx @@ -4,7 +4,11 @@ import { Card, CardContent } from "@/components/ui/card"; import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"; import { fetchShowPatientById } from "@/store/slices/patientSlice"; import { fetchAppointments } from "@/store/slices/appointmentSlice"; -import { selectAllAppointments, selectShowPatientById, selectPatientsLoading } from "@/store/selectors"; +import { + selectAllAppointments, + selectShowPatientById, + selectPatientsLoading, +} from "@/store/selectors"; const fallbackPatients = [ { @@ -39,88 +43,102 @@ const fallbackPatients = [ phone: "+201098765432", }, }, - { - _id: "fallback-3", - name: "Nour Khaled", - gender: "Female", - dateOfBirth: "2001-12-05", - email: "nour.khaled@example.com", - phone: "+201112223344", - city: "Giza", - country: "Egypt", - createdAt: "2024-01-20T09:45:00Z", - emergencyContact: { - name: "Khaled Youssef", - relationship: "Father", - phone: "+201234001122", - }, - }, ]; export const Patients_List = ({ onPatientSelect, sortBy }) => { const [selectedPatientId, setSelectedPatientId] = useState(null); const [fetchedPatients, setFetchedPatients] = useState({}); + const [fetchedPatientIds, setFetchedPatientIds] = useState(new Set()); + const dispatch = useDispatch(); - - // Use the same selectors as the chart - const Appointments = useSelector(selectAllAppointments); - const currentPatient = useSelector(selectShowPatientById); // This is a single patient object + const appointments = useSelector(selectAllAppointments); const loading = useSelector(selectPatientsLoading); - // Memoize unique patient IDs from appointments (adjusted for API structure) + // Extract unique patient IDs const uniquePatientIds = useMemo(() => { - return [...new Set( - Appointments - .map(a => a.patientId?._id || a.patientId) // Handle both object and string formats - .filter(Boolean) // Remove null/undefined values - )]; - }, [Appointments]); - - // Fetch appointments once (same as chart) + const ids = [ + ...new Set( + appointments + .map((a) => (a.patientId?._id || a.patientId)) + .filter(Boolean) + ), + ]; + console.log("โœ… Unique patient IDs from appointments:", ids); + return ids; + }, [appointments]); + + // Fetch appointments on mount useEffect(() => { dispatch(fetchAppointments()); }, [dispatch]); - // Store the current patient when it's fetched - useEffect(() => { - if (currentPatient && currentPatient._id) { - setFetchedPatients(prev => ({ - ...prev, - [currentPatient._id]: currentPatient - })); + // Fetch missing patients + const fetchMissingPatients = async () => { + console.log("๐Ÿ” Starting patient fetch loop"); + + for (const id of uniquePatientIds) { + if (!fetchedPatientIds.has(id) && !fetchedPatients[id]) { + console.log(`โžก๏ธ Fetching patient with ID: ${id}`); + + try { + const resultAction = await dispatch(fetchShowPatientById(id)); + console.log("๐Ÿงพ Result Action:", resultAction); + + if (fetchShowPatientById.fulfilled.match(resultAction)) { + const payload = resultAction.payload; + + if (!payload) { + console.error("โŒ No payload returned for patient ID:", id); + continue; + } + + const patient = payload?.data || payload; + + if (patient && patient._id) { + console.log("โœ… Patient fetched:", patient); + setFetchedPatients((prev) => ({ + ...prev, + [patient._id]: patient, + })); + setFetchedPatientIds((prev) => new Set([...prev, id])); + } else { + console.warn("โš ๏ธ Invalid patient structure:", payload); + } + } else { + console.error("โŒ fetchShowPatientById thunk was rejected:", resultAction); + } + } catch (err) { + console.error("โŒ Exception during patient fetch:", err); + } + } else { + console.log(`โฉ Already fetched ID: ${id}`); + } } - }, [currentPatient]); + }; - // Fetch patients individually based on appointment data + // Trigger fetch useEffect(() => { if (uniquePatientIds.length > 0) { - uniquePatientIds.forEach(id => { - if (!fetchedPatients[id]) { - dispatch(fetchShowPatientById(id)); - } - }); + fetchMissingPatients(); } - }, [dispatch, uniquePatientIds, fetchedPatients]); + }, [uniquePatientIds]); - // Get patient list from the fetchedPatients + // Construct the patient list from fetched data const patients = useMemo(() => { - console.log('fetchedPatients content:', fetchedPatients); - console.log('uniquePatientIds:', uniquePatientIds); - - const result = uniquePatientIds.map(id => fetchedPatients[id]).filter(Boolean); - console.log('Patients result:', result); + const result = uniquePatientIds.map((id) => fetchedPatients[id]).filter(Boolean); + console.log("๐Ÿ“‹ Current patients to render:", result); return result; }, [fetchedPatients, uniquePatientIds]); - // Check if we're still loading any patients - const isLoading = loading || (uniquePatientIds.length > 0 && uniquePatientIds.some(id => !fetchedPatients[id])); + const isLoading = + loading || + (uniquePatientIds.length > 0 && patients.length < uniquePatientIds.length); const handlePatientClick = (patient) => { setSelectedPatientId(patient._id); onPatientSelect(patient); }; - // Use patients from store if available, otherwise fallback const patientsToDisplay = patients.length > 0 ? patients : fallbackPatients; const sortedPatients = [...patientsToDisplay].sort((a, b) => { @@ -136,20 +154,20 @@ export const Patients_List = ({ onPatientSelect, sortBy }) => { } }); - // Show loading state if we're fetching patients if (isLoading && patients.length === 0 && uniquePatientIds.length > 0) { return (
-

Loading patients... ({uniquePatientIds.length} patients to fetch)

+

+ Fetched: {patients.length} / {uniquePatientIds.length} +

); } - // Show message if no appointments found if (uniquePatientIds.length === 0) { return (
@@ -191,4 +209,4 @@ export const Patients_List = ({ onPatientSelect, sortBy }) => { ))}
); -}; \ No newline at end of file +}; diff --git a/src/components/doctor/Patients/Patients_Main.jsx b/src/components/doctor/Patients/Patients_Main.jsx index e44fc3b..4f646a8 100644 --- a/src/components/doctor/Patients/Patients_Main.jsx +++ b/src/components/doctor/Patients/Patients_Main.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useNavigate } from "react-router-dom"; import { @@ -14,6 +14,9 @@ import { Card, CardContent } from "@/components/ui/card"; import { fetchAllPatients } from "@/store/slices/patientSlice"; import { fetchAppointments } from "@/store/slices/appointmentSlice"; import { getAllDiagnosis } from "@/services/diagnosisApi"; +import { handleNameRoute } from "@/utils/urlHelpers"; +import { selectMyDetails } from "@/store/selectors"; +import { fetchMYData } from "@/store/slices/userSlice"; export const Patients_Main = ({ selectedPatient, patientsList }) => { const dispatch = useDispatch(); @@ -34,6 +37,17 @@ export const Patients_Main = ({ selectedPatient, patientsList }) => { const patient = selectedPatient; const patientId = patient?._id; + const myDetails = useSelector(selectMyDetails); + + + + useEffect(() => { + dispatch(fetchMYData()); + }, [dispatch]); + + const doctorSlug = useMemo(() => { + return myDetails?.name ? handleNameRoute(myDetails.name) : ""; + }, [myDetails]); // Fetch all patients and appointments on mount useEffect(() => { @@ -118,7 +132,9 @@ export const Patients_Main = ({ selectedPatient, patientsList }) => { value={gender} /> } + icon={ + + } label="City" value={city} /> @@ -164,7 +180,9 @@ export const Patients_Main = ({ selectedPatient, patientsList }) => {