Skip to content
2 changes: 1 addition & 1 deletion src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ function App() {
{/* doctor layout */}
<Route path="/doctor/:doctorName" element={<Doctor_Dashboard_Layout />}>
<Route index element={<Main_Grid />} />
<Route path="chat" element={<Messages />} />
{/* <Route path="chat" element={<Messages />} /> */}
<Route path="diagnosis" element={<Diagnosis_doctor />} />
<Route path="patients" element={<Patients_doctor />} />
<Route path="advice" element={<Advice />} />
Expand Down
Binary file added src/assets/logo-doctor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/logo-sidebar-white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
174 changes: 89 additions & 85 deletions src/components/doctor/Dashboard/ChartAreaAgeGender.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import {
YAxis,
ResponsiveContainer,
} from "recharts"
import { fetchAllPatients, fetchShowPatientById } from "@/store/slices/patientSlice"

import { fetchShowPatientById } from "@/store/slices/patientSlice"
import {
Card,
CardHeader,
Expand All @@ -24,7 +23,11 @@ import {
ChartTooltip,
ChartTooltipContent,
} from "@/components/ui/chart"
import { selectAllAppointments, selectAllPatients, selectPatientsLoading, selectShowPatientById } from "@/store/selectors"
import {
selectAllAppointments,
selectShowPatientById,
selectPatientsLoading,
} from "@/store/selectors"
import { fetchAppointments } from "@/store/slices/appointmentSlice"

const calculateAge = (dob) => {
Expand All @@ -39,99 +42,98 @@ const calculateAge = (dob) => {
}

const getAgeGroup = (age) => {
if (age < 18) return "0-17"
if (age < 30) return "18-29"
if (age < 45) return "30-44"
if (age < 60) return "45-59"
return "60+"
if (age < 10) return "0-9"
if (age < 20) return "10-19"
if (age < 30) return "20-29"
if (age < 40) return "30-39"
if (age < 50) return "40-49"
if (age < 60) return "50-59"
if (age < 70) return "60-69"
return "70+"
}

export default function ChartAreaAgeGender() {
const [fetchedPatients, setFetchedPatients] = useState({});
const dispatch = useDispatch();
const Appointments = useSelector(selectAllAppointments);
const currentPatient = useSelector(selectShowPatientById); // This is a single patient object
const loading = useSelector(selectPatientsLoading);

// Memoize unique patient IDs from appointments (adjusted for API structure)
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]);

// Store the current patient when it's fetched
useEffect(() => {
if (currentPatient && currentPatient._id) {
setFetchedPatients(prev => ({
...prev,
[currentPatient._id]: currentPatient
}));
}
}, [currentPatient]);

// Fetch patients individually based on appointment data
useEffect(() => {
if (uniquePatientIds.length > 0) {
uniquePatientIds.forEach(id => {
if (!fetchedPatients[id]) {
dispatch(fetchShowPatientById(id));
}
});
}
}, [dispatch, uniquePatientIds, fetchedPatients]);

// Fetch appointments once
useEffect(() => {
dispatch(fetchAppointments());
}, [dispatch]);

// Get patient list from the fetchedPatients
const patients = useMemo(() => {
console.log('Chart - fetchedPatients content:', fetchedPatients);
console.log('Chart - uniquePatientIds:', uniquePatientIds);

const result = uniquePatientIds.map(id => fetchedPatients[id]).filter(Boolean);
console.log('Chart - Patients result:', result);
return result;
}, [fetchedPatients, uniquePatientIds]);

// Check if we're still loading any patients
const isLoading = loading || (uniquePatientIds.length > 0 && uniquePatientIds.some(id => !fetchedPatients[id]));

// Chart Data
const chartData = useMemo(() => {
const dataMap = {};

patients.forEach((patient) => {
const age = calculateAge(patient.dateOfBirth);
const ageGroup = getAgeGroup(age);
const gender = patient.gender?.toLowerCase();

if (!dataMap[ageGroup]) {
dataMap[ageGroup] = { male: 0, female: 0 };
const [fetchedPatients, setFetchedPatients] = useState({})
const dispatch = useDispatch()
const Appointments = useSelector(selectAllAppointments)
const currentPatient = useSelector(selectShowPatientById)
const loading = useSelector(selectPatientsLoading)

const uniquePatientIds = useMemo(() => {
return [
...new Set(
Appointments.map((a) => a.patientId?._id || a.patientId).filter(Boolean)
),
]
}, [Appointments])

useEffect(() => {
if (currentPatient && currentPatient._id) {
setFetchedPatients((prev) => ({
...prev,
[currentPatient._id]: currentPatient,
}))
}
}, [currentPatient])

useEffect(() => {
if (uniquePatientIds.length > 0) {
uniquePatientIds.forEach((id) => {
if (!fetchedPatients[id]) {
dispatch(fetchShowPatientById(id))
}
})
}
}, [dispatch, uniquePatientIds, fetchedPatients])

useEffect(() => {
dispatch(fetchAppointments())
}, [dispatch])

if (gender === "male") dataMap[ageGroup].male++;
else if (gender === "female") dataMap[ageGroup].female++;
});
const patients = useMemo(() => {
return uniquePatientIds.map((id) => fetchedPatients[id]).filter(Boolean)
}, [fetchedPatients, uniquePatientIds])

return Object.entries(dataMap).map(([ageGroup, counts]) => ({
ageGroup,
...counts,
}));
}, [patients]);
const isLoading =
loading ||
(uniquePatientIds.length > 0 &&
uniquePatientIds.some((id) => !fetchedPatients[id]))

const chartData = useMemo(() => {
const dataMap = {}

patients.forEach((patient) => {
const age = calculateAge(patient.dateOfBirth)
const ageGroup = getAgeGroup(age)
const gender = patient.gender?.toLowerCase()

if (!dataMap[ageGroup]) {
dataMap[ageGroup] = { male: 0, female: 0 }
}

if (gender === "male") dataMap[ageGroup].male++
else if (gender === "female") dataMap[ageGroup].female++
})

return Object.entries(dataMap)
.sort((a, b) => {
const parseMin = (label) => parseInt(label.split("-")[0]) || 70
return parseMin(a[0]) - parseMin(b[0])
})
.map(([ageGroup, counts]) => ({
ageGroup,
...counts,
}))
}, [patients])

const chartConfig = {
male: {
label: "Male",
color: "#3b82f6", // blue
color: "#3b82f6",
},
female: {
label: "Female",
color: "#ec4899", // pink
color: "#ec4899",
},
}

Expand All @@ -141,7 +143,9 @@ const chartData = useMemo(() => {
<CardHeader>
<CardTitle>Patient Age & Gender Distribution</CardTitle>
</CardHeader>
<CardContent>Loading patients... ({uniquePatientIds.length} patients to fetch)</CardContent>
<CardContent>
Loading patients... ({uniquePatientIds.length} patients to fetch)
</CardContent>
</Card>
)
}
Expand Down Expand Up @@ -204,4 +208,4 @@ const chartData = useMemo(() => {
</CardContent>
</Card>
)
}
}
66 changes: 30 additions & 36 deletions src/components/doctor/Dashboard/DiagnosisChart.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
selectAllAppointments,
selectPatientsLoading,
selectShowPatientById,
} from "@/store/selectors";
import { fetchAppointments } from "@/store/slices/appointmentSlice";
Expand Down Expand Up @@ -62,20 +61,20 @@ const DiagnosisChart = () => {
const dispatch = useDispatch();
const [fetchedPatients, setFetchedPatients] = useState({});
const [diagnosisMap, setDiagnosisMap] = useState({});
const [isDiagnosisLoading, setIsDiagnosisLoading] = useState(true);
const [diagnosisError, setDiagnosisError] = useState(null);
const diagnosisFetchedRef = useRef(new Set());

const appointments = useSelector(selectAllAppointments);
const currentPatient = useSelector(selectShowPatientById);
const patientsLoading = useSelector(selectPatientsLoading);

const uniquePatientIds = useMemo(() => {
return [...new Set(
appointments
.map((a) => a.patientId?._id || a.patientId)
.filter(Boolean)
)];
return [
...new Set(
appointments
.map((a) => a.patientId?._id || a.patientId)
.filter(Boolean)
),
];
}, [appointments]);

useEffect(() => {
Expand Down Expand Up @@ -105,37 +104,27 @@ const DiagnosisChart = () => {
return uniquePatientIds.map((id) => fetchedPatients[id]).filter(Boolean);
}, [fetchedPatients, uniquePatientIds]);

const isLoadingPatients =
patientsLoading ||
(uniquePatientIds.length > 0 &&
uniquePatientIds.some((id) => !fetchedPatients[id]));

useEffect(() => {
const fetchDiagnoses = async () => {
try {
setIsDiagnosisLoading(true);
const newDiagnosisMap = {};

for (const patient of patients) {
if (!diagnosisFetchedRef.current.has(patient._id)) {
const result = await getAllDiagnosis(patient._id);
newDiagnosisMap[patient._id] = result?.diagnoses || [];
diagnosisFetchedRef.current.add(patient._id);
}
}

setDiagnosisMap((prev) => ({ ...prev, ...newDiagnosisMap }));
} catch (err) {
setDiagnosisError(err.message || "Failed to fetch diagnosis");
} finally {
setIsDiagnosisLoading(false);
}
};

if (patients.length > 0 && !isLoadingPatients) {
if (patients.length > 0) {
fetchDiagnoses();
}
}, [patients, isLoadingPatients]);
}, [patients]);

const chartData = useMemo(() => {
const counts = {};
Expand All @@ -149,27 +138,21 @@ const DiagnosisChart = () => {
return Object.entries(counts).map(([name, value]) => ({ name, value }));
}, [diagnosisMap]);

const allDiagnosesEmpty = useMemo(() => {
return Object.values(diagnosisMap).every((d) => d.length === 0);
}, [diagnosisMap]);

const noDiagnosisPatients = useMemo(() => {
return patients.filter((p) => (diagnosisMap[p._id]?.length || 0) === 0);
}, [patients, diagnosisMap]);

const renderCardContent = (content) => (
<div className="flex flex-col items-center justify-center h-64 text-center text-gray-500">
<h2 className="text-lg md:text-xl font-semibold mb-4">Average Diagnoses</h2>
{content}
</div>
);

if (isLoadingPatients || (isDiagnosisLoading && chartData.length === 0)) {
return (
<div className="rounded-lg border border-gray-200 bg-white shadow-sm p-4 col-span-4">
{renderCardContent(
<p>
{isLoadingPatients
? `Loading patients... (${uniquePatientIds.length} patients to fetch)`
: "Loading diagnoses..."}
</p>
)}
</div>
);
}

if (diagnosisError) {
return (
<div className="rounded-lg border border-gray-200 bg-white shadow-sm p-4 col-span-4">
Expand All @@ -188,10 +171,21 @@ const DiagnosisChart = () => {
);
}

if (chartData.length === 0) {
if (chartData.length === 0 || allDiagnosesEmpty) {
return (
<div className="rounded-lg border border-gray-200 bg-white shadow-sm p-4 col-span-4">
{renderCardContent(<p>No diagnosis data available</p>)}
{renderCardContent(
<>
<p>No diagnosis data available for any patients.</p>
{noDiagnosisPatients.length > 0 && (
<ul className="mt-2 text-sm text-gray-600">
{noDiagnosisPatients.map((p) => (
<li key={p._id}>• {p.name}</li>
))}
</ul>
)}
</>
)}
</div>
);
}
Expand Down
Loading