feat(report page) - export button added

This commit is contained in:
2025-10-24 23:39:39 +05:30
parent 54596be39f
commit 3af71cc5b8
11 changed files with 373 additions and 78 deletions

View File

@@ -12,6 +12,7 @@ import {
SelectValue,
} from "@/components/ui/select";
import { DoctorBalancesAndSummary } from "@repo/db/types";
import ExportReportButton from "./export-button";
type StaffOption = { id: number; name: string };
@@ -259,6 +260,15 @@ export default function CollectionsByDoctorReport({
onNext={handleNext}
hasPrev={cursorIndex > 0}
hasNext={hasMore}
headerRight={
<ExportReportButton
reportType="collections_by_doctor"
from={startDate}
to={endDate}
staffId={Number(staffId)}
className="mr-2"
/>
}
/>
)}
</div>

View File

@@ -0,0 +1,71 @@
import React, { useState } from "react";
import { apiRequest } from "@/lib/queryClient";
export default function ExportReportButton({
reportType,
from,
to,
staffId,
className,
labelCsv = "Download CSV",
}: {
reportType: string; // e.g. "collections_by_doctor" or "patients_with_balance"
from?: string;
to?: string;
staffId?: number | string | null;
className?: string;
labelCsv?: string;
}) {
const [loading, setLoading] = useState(false);
async function downloadCsv() {
setLoading(true);
try {
const params = new URLSearchParams();
params.set("type", reportType);
if (from) params.set("from", from);
if (to) params.set("to", to);
if (staffId) params.set("staffId", String(staffId));
params.set("format", "csv"); // server expects format=csv
const url = `/api/export-payments-reports/export?${params.toString()}`;
// Use apiRequest for consistent auth headers/cookies
const res = await apiRequest("GET", url);
if (!res.ok) {
const body = await res.text().catch(() => "Export failed");
throw new Error(body || "Export failed");
}
const blob = await res.blob();
const href = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = href;
const safeFrom = from || "all";
const safeTo = to || "all";
a.download = `${reportType}_${safeFrom}_${safeTo}.csv`;
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(href);
} catch (err: any) {
console.error("Export CSV failed", err);
alert("Export failed: " + (err?.message ?? "unknown error"));
} finally {
setLoading(false);
}
}
return (
<div className={className ?? "flex items-center gap-2"}>
<button
type="button"
onClick={downloadCsv}
disabled={loading}
className="inline-flex items-center px-3 py-2 rounded border text-sm"
>
{loading ? "Preparing..." : labelCsv}
</button>
</div>
);
}

View File

@@ -23,6 +23,7 @@ export default function PatientsBalancesList({
onNext,
hasPrev,
hasNext,
headerRight, // optional UI node to render in header
}: {
rows: GenericRow[];
reportType?: string | null;
@@ -37,6 +38,7 @@ export default function PatientsBalancesList({
onNext: () => void;
hasPrev: boolean;
hasNext: boolean;
headerRight?: React.ReactNode;
}) {
const fmt = (v: number) =>
new Intl.NumberFormat("en-US", {
@@ -66,6 +68,9 @@ export default function PatientsBalancesList({
<h3 className="font-medium text-gray-900">
{reportTypeTitle(reportType)}
</h3>
{/* headerRight rendered here (if provided) */}
<div>{headerRight ?? null}</div>
</div>
<div className="divide-y min-h-[120px]">

View File

@@ -3,6 +3,7 @@ import { useQuery } from "@tanstack/react-query";
import { apiRequest } from "@/lib/queryClient";
import type { PatientBalanceRow } from "@repo/db/types";
import PatientsBalancesList from "./patients-balances-list";
import ExportReportButton from "./export-button";
type Resp = {
balances: PatientBalanceRow[];
@@ -117,6 +118,14 @@ export default function PatientsWithBalanceReport({
onNext={handleNext}
hasPrev={cursorIndex > 0}
hasNext={hasMore}
headerRight={
<ExportReportButton
reportType="patients_with_balance"
from={startDate}
to={endDate}
className="mr-2"
/>
}
/>
</div>
</div>

View File

@@ -1,11 +1,9 @@
import React, { useState } from "react";
import { Download } from "lucide-react";
import { useAuth } from "@/hooks/use-auth";
import ReportConfig from "@/components/reports/report-config";
import PatientsWithBalanceReport from "@/components/reports/patients-with-balance-report";
import CollectionsByDoctorReport from "@/components/reports/collections-by-doctor-report";
import SummaryCards from "@/components/reports/summary-cards";
import { Button } from "@/components/ui/button";
type ReportType =
| "patients_with_balance"
@@ -32,17 +30,6 @@ export default function ReportPage() {
const [selectedReportType, setSelectedReportType] = useState<ReportType>(
"patients_with_balance"
);
const [isGenerating, setIsGenerating] = useState(false);
const generateReport = async () => {
setIsGenerating(true);
try {
// placeholder: implement export per-report endpoint
await new Promise((r) => setTimeout(r, 900));
} finally {
setIsGenerating(false);
}
};
if (!user) {
return (
@@ -62,16 +49,6 @@ export default function ReportPage() {
Generate comprehensive financial reports for your practice
</p>
</div>
{/* Export Button (Top Right) */}
<Button
onClick={generateReport}
disabled={isGenerating}
className="default"
>
<Download className="h-4 w-4 mr-2" />
{isGenerating ? "Generating..." : "Export Report"}
</Button>
</div>
<div className="mb-4">