import React, { useCallback, useEffect, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { apiRequest } from "@/lib/queryClient"; import PatientsBalancesList, { GenericRow } from "./patients-balances-list"; import { Card, CardContent } from "@/components/ui/card"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { DoctorBalancesAndSummary } from "@repo/db/types"; import ExportReportButton from "./export-button"; type StaffOption = { id: number; name: string }; function fmtCurrency(v: number) { return new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", }).format(v); } export default function CollectionsByDoctorReport({ startDate, endDate, }: { startDate: string; endDate: string; }) { const [staffId, setStaffId] = useState(""); const perPage = 10; const [cursorStack, setCursorStack] = useState<(string | null)[]>([null]); const [cursorIndex, setCursorIndex] = useState(0); const currentCursor = cursorStack[cursorIndex] ?? null; const pageIndex = cursorIndex + 1; // load staffs list for selector const { data: staffs } = useQuery({ queryKey: ["staffs"], queryFn: async () => { const res = await apiRequest("GET", "/api/staffs"); if (!res.ok) { const b = await res .json() .catch(() => ({ message: "Failed to load staffs" })); throw new Error(b.message || "Failed to load staffs"); } return res.json(); }, staleTime: 60_000, }); // query balances+summary by doctor const { data: collectionData, isLoading: isLoadingRows, isError: isErrorRows, refetch, isFetching, } = useQuery({ queryKey: [ "collections-by-doctor-rows", staffId, currentCursor, perPage, startDate, endDate, ], queryFn: async () => { const params = new URLSearchParams(); params.set("limit", String(perPage)); if (currentCursor) params.set("cursor", currentCursor); if (staffId) params.set("staffId", staffId); if (startDate) params.set("from", startDate); if (endDate) params.set("to", endDate); const res = await apiRequest( "GET", `/api/payments-reports/by-doctor?${params.toString()}` ); if (!res.ok) { const b = await res .json() .catch(() => ({ message: "Failed to load collections" })); throw new Error(b.message || "Failed to load collections"); } return res.json(); }, enabled: Boolean(staffId), // only load when a doctor is selected }); const balances = collectionData?.balances ?? []; const totalCount = collectionData?.totalCount ?? undefined; const serverNextCursor = collectionData?.nextCursor ?? null; const hasMore = collectionData?.hasMore ?? false; const summary = collectionData?.summary ?? null; // Reset pagination when filters change useEffect(() => { setCursorStack([null]); setCursorIndex(0); }, [staffId, startDate, endDate]); const handlePrev = useCallback(() => { setCursorIndex((i) => Math.max(0, i - 1)); }, []); const handleNext = useCallback(() => { const idx = cursorIndex; const isLastKnown = idx === cursorStack.length - 1; if (isLastKnown) { if (serverNextCursor) { setCursorStack((s) => [...s, serverNextCursor]); setCursorIndex((i) => i + 1); // No manual refetch — the queryKey depends on currentCursor and React Query will fetch automatically. } } else { setCursorIndex((i) => i + 1); } }, [cursorIndex, cursorStack.length, serverNextCursor]); // Map server rows to GenericRow const genericRows: GenericRow[] = balances.map((r) => { const totalCharges = Number(r.totalCharges ?? 0); const totalPayments = Number(r.totalPayments ?? 0); const currentBalance = Number(r.currentBalance ?? 0); const name = `${r.firstName ?? ""} ${r.lastName ?? ""}`.trim() || "Unknown"; return { id: String(r.patientId), name, currentBalance, totalCharges, totalPayments, }; }); return (
{/* Summary card (time-window based) */} {staffId && (

Doctor summary

Data covers the selected time frame

{summary ? Number(summary.totalPatients ?? 0) : "—"}

Total Patients (in window)

{summary ? Number(summary.patientsWithBalance ?? 0) : "—"}

With Balance

{summary ? Math.max( 0, Number(summary.totalPatients ?? 0) - Number(summary.patientsWithBalance ?? 0) ) : "—"}

Zero Balance

{summary ? fmtCurrency(Number(summary.totalOutstanding ?? 0)) : "—"}

Outstanding

{summary ? fmtCurrency(Number(summary.totalCollected ?? 0)) : "—"}

Collected

)} {/* List (shows all patients under doctor but per-row totals are time-filtered) */} {!staffId ? (
Please select a doctor to load collections.
) : ( 0} hasNext={hasMore} headerRight={ } /> )}
); }