Files
acmcc/src/pages/accounting/AccountingReconcileDetailPage.tsx
T
admin e302fb91f0 Accounting platform: remove Zoho, unify reports, board access, vendor sharing
- Remove the Zoho Books integration (edge functions, sync libs, settings,
  reports/overview, banking links, fees tab, import dialog); preserve fee
  rules as a standalone FeesTab and the COA accounting_system classification.
- Financial Overview/Reports (staff + board) render the Accounting dashboard
  and reports; board reports mirror the rich Accounting Reports.
- New Reserve Fund Schedule report + an is_reserve flag on accounts.
- Unify all report exports to a branded format (logo + centered header +
  footer): shared ReportSheet (on-screen) and reportHeader (PDF). Budget vs
  Actuals and Bank Reconciliation PDFs now match the reference layout.
- Render financial reports inline (no preview pop-up).
- Budget Management mirrors Accounting Budgeting (staff-accessible) with SPA
  navigation; editable bills in the Accounting Bills page.
- Negative opening balances flow through to the GL and reports (allow negative
  input; keep non-zero on save; signed CSV import).
- Upload a per-account trial balance via CSV on Opening Balances.
- Board members: read-only RLS access to their association's accounting ledger;
  editable board-members panel on the association page; share vendor contacts
  with the board (toggle + directory section).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 18:29:31 -04:00

609 lines
28 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Link, useParams } from "react-router-dom";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useMemo, useState, useEffect } from "react";
import { accounting } from "@/lib/accountingClient";
import { useCompanyId } from "./lib/useCompanyId";
import { useAuth } from "@/contexts/AuthContext";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import { Badge } from "@/components/ui/badge";
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
import {
Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription,
} from "@/components/ui/dialog";
import {
Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
} from "@/components/ui/select";
import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from "@/components/ui/table";
import { ArrowLeft, CheckCircle2, AlertTriangle, FileDown, Search, Loader2 } from "lucide-react";
import { toast } from "sonner";
import { money, fmtDate } from "./lib/format";
import { renderReconciliationPdf, type ReconReportData } from "./lib/reconciliationPdf";
type Tx = {
id: string;
date: string;
description: string;
reference: string | null;
amount: number;
type: "debit" | "credit";
cleared: boolean;
reconciliation_id: string | null;
};
export default function AccountingReconcileDetailPage() {
const { accountId = "" } = useParams();
const { companyId, loading: companyLoading, error: companyError, associationId, associationName } = useCompanyId();
const { user } = useAuth();
const cid = companyId ?? "";
const cur = "USD";
const qc = useQueryClient();
const [setupOpen, setSetupOpen] = useState(false);
const [setup, setSetup] = useState({
statement_end_date: new Date().toLocaleDateString("en-CA", { timeZone: "America/New_York" }),
statement_balance: 0,
});
const [active, setActive] = useState<{
statement_end_date: string;
statement_balance: number;
opening_balance: number;
} | null>(null);
const [search, setSearch] = useState("");
const [filter, setFilter] = useState<"all" | "deposits" | "withdrawals">("all");
const [checked, setChecked] = useState<Set<string>>(new Set());
const [successOpen, setSuccessOpen] = useState(false);
const [successData, setSuccessData] = useState<ReconReportData | null>(null);
const [adjOpen, setAdjOpen] = useState(false);
const [adjAccount, setAdjAccount] = useState("");
const [adjNote, setAdjNote] = useState("Bank reconciliation adjustment");
const { data: account } = useQuery({
queryKey: ["account", accountId],
enabled: !!accountId,
queryFn: async () =>
(await accounting.from("accounts").select("*").eq("id", accountId).single()).data,
});
const { data: history = [] } = useQuery({
queryKey: ["recon-history", accountId],
enabled: !!accountId,
queryFn: async () =>
(
await accounting
.from("reconciliations")
.select("*")
.eq("account_id", accountId)
.order("statement_end_date", { ascending: false })
).data ?? [],
});
const lastCompleted = (history as any[]).find((h: any) => h.status === "completed");
const openingBalance = active?.opening_balance ?? Number(lastCompleted?.statement_balance ?? 0);
const { data: allAccounts = [] } = useQuery({
queryKey: ["accounts", cid],
enabled: !!cid,
queryFn: async () =>
(await accounting.from("accounts").select("id,name,type").eq("company_id", cid).order("name")).data ?? [],
});
const { data: txs = [] } = useQuery({
queryKey: ["recon-txs", accountId, active?.statement_end_date],
enabled: !!accountId && !!active,
queryFn: async () => {
const { data } = await accounting
.from("transactions")
.select("id,date,description,reference,amount,type,cleared,reconciliation_id")
.eq("account_id", accountId)
.is("reconciliation_id", null)
.lte("date", active!.statement_end_date)
.order("date");
return (data ?? []) as Tx[];
},
});
// Preload checked from already-cleared rows
useEffect(() => {
if (active) {
setChecked(new Set((txs as Tx[]).filter((t) => t.cleared).map((t) => t.id)));
}
}, [active, txs]);
const filtered = useMemo(() => {
const s = search.toLowerCase().trim();
return (txs as Tx[]).filter((t) => {
if (filter === "deposits" && t.type !== "credit") return false;
if (filter === "withdrawals" && t.type !== "debit") return false;
if (s && !`${t.description} ${t.reference ?? ""}`.toLowerCase().includes(s)) return false;
return true;
});
}, [txs, search, filter]);
const clearedDeposits = useMemo(
() => (txs as Tx[]).filter((t) => checked.has(t.id) && t.type === "credit")
.reduce((s, t) => s + Number(t.amount), 0),
[txs, checked],
);
const clearedWithdrawals = useMemo(
() => (txs as Tx[]).filter((t) => checked.has(t.id) && t.type === "debit")
.reduce((s, t) => s + Number(t.amount), 0),
[txs, checked],
);
const clearedBalance = openingBalance + clearedDeposits - clearedWithdrawals;
const difference = Number(active?.statement_balance ?? 0) - clearedBalance;
const balanced = Math.abs(difference) < 0.005;
const startReconciling = () => {
if (!setup.statement_end_date) return toast.error("Statement date required");
setActive({
statement_end_date: setup.statement_end_date,
statement_balance: Number(setup.statement_balance) || 0,
opening_balance: Number(lastCompleted?.statement_balance ?? 0),
});
setSetupOpen(false);
};
const toggleAll = (on: boolean) => {
if (on) setChecked(new Set(filtered.map((t) => t.id)));
else setChecked(new Set());
};
const saveForLater = async () => {
if (!active) return;
const ids = Array.from(checked);
if (ids.length === 0) return toast.error("No transactions selected");
const { error } = await accounting.from("transactions").update({ cleared: true }).in("id", ids);
if (error) return toast.error(error.message);
toast.success("Progress saved");
qc.invalidateQueries({ queryKey: ["recon-txs", accountId] });
};
const finish = async () => {
if (!active || !balanced) return;
if (!cid) return toast.error("No company selected");
const ids = Array.from(checked);
const { data: rec, error: recErr } = await accounting
.from("reconciliations")
.insert({
company_id: cid,
account_id: accountId,
statement_end_date: active.statement_end_date,
statement_balance: active.statement_balance,
opening_balance: active.opening_balance,
cleared_deposits: clearedDeposits,
cleared_withdrawals: clearedWithdrawals,
status: "completed",
completed_at: new Date().toISOString(),
completed_by: user?.id ?? null,
})
.select()
.single();
if (recErr || !rec) return toast.error(recErr?.message ?? "Failed to save reconciliation");
if (ids.length > 0) {
const { error: txErr } = await accounting
.from("transactions")
.update({ cleared: true, reconciliation_id: rec.id })
.in("id", ids);
if (txErr) {
toast.error(`Reconciliation saved but transaction update failed: ${txErr.message}`);
}
}
const mapRow = (t: Tx) => ({ name: t.description ?? "", date: t.date, number: t.reference ?? "", memo: "", amount: Number(t.amount) });
const clearedDepRows = (txs as Tx[]).filter((t) => checked.has(t.id) && t.type === "credit").map(mapRow);
const clearedWdRows = (txs as Tx[]).filter((t) => checked.has(t.id) && t.type === "debit").map(mapRow);
const unclearedDepRows = (txs as Tx[]).filter((t) => !checked.has(t.id) && t.type === "credit").map(mapRow);
const unclearedWdRows = (txs as Tx[]).filter((t) => !checked.has(t.id) && t.type === "debit").map(mapRow);
const sumAmt = (rs: { amount: number }[]) => rs.reduce((s, r) => s + r.amount, 0);
const begin = Number(active.opening_balance);
const ending = begin + sumAmt(clearedDepRows) - sumAmt(clearedWdRows);
const book = ending + sumAmt(unclearedDepRows) - sumAmt(unclearedWdRows);
const acctLabel = (account as any)?.code ? `${(account as any).code} ${(account as any).name}` : ((account as any)?.name ?? "Account");
setSuccessData({
companyName: associationName ?? "Company",
accountName: acctLabel,
statementEndDate: active.statement_end_date,
beginningBalance: begin,
endingBalance: ending,
bookBalance: book,
clearedDeposits: clearedDepRows,
clearedWithdrawals: clearedWdRows,
unclearedDeposits: unclearedDepRows,
unclearedWithdrawals: unclearedWdRows,
preparedBy: user?.email ?? "—",
currency: cur,
completedAt: new Date().toISOString(),
});
setSuccessOpen(true);
setActive(null);
setChecked(new Set());
qc.invalidateQueries({ queryKey: ["recon-history", accountId] });
qc.invalidateQueries({ queryKey: ["recon-last", cid] });
qc.invalidateQueries({ queryKey: ["recon-txs", accountId] });
};
const postAdjustment = async () => {
if (!active || !adjAccount) return toast.error("Pick an account");
const adjAmount = difference; // sign: positive if statement > cleared
const type: "credit" | "debit" = adjAmount >= 0 ? "credit" : "debit";
const { data, error } = await accounting
.from("transactions")
.insert({
company_id: cid,
account_id: accountId,
date: active.statement_end_date,
description: adjNote || "Reconciliation adjustment",
amount: Math.abs(adjAmount),
type,
cleared: true,
reference: "ADJ",
category: "Adjustment",
})
.select()
.single();
if (error || !data) return toast.error(error?.message ?? "Failed");
// Offset entry to chosen account
await accounting.from("transactions").insert({
company_id: cid,
account_id: adjAccount,
date: active.statement_end_date,
description: adjNote || "Reconciliation adjustment",
amount: Math.abs(adjAmount),
type: type === "credit" ? "debit" : "credit",
reference: "ADJ",
category: "Adjustment",
});
setAdjOpen(false);
toast.success("Adjustment posted");
qc.invalidateQueries({ queryKey: ["recon-txs", accountId] });
// auto-check the new adjustment
setChecked((prev) => new Set(prev).add(data.id));
};
if (!associationId) return <p className="text-sm text-muted-foreground">Select an association.</p>;
if (companyLoading) return <div className="flex justify-center py-12"><Loader2 className="h-6 w-6 animate-spin text-muted-foreground" /></div>;
if (companyError || !companyId) return <p className="text-sm text-muted-foreground text-center py-12">{companyError || "Accounting setup is not ready."}</p>;
return (
<div className="space-y-6">
<div className="flex items-center justify-between gap-2 flex-wrap">
<div className="flex items-center gap-3">
<Button asChild variant="ghost" size="sm">
<Link to="/dashboard/accounting/reconciliation"><ArrowLeft className="h-4 w-4 mr-1" />Bank Reconciliation</Link>
</Button>
<div>
<h1 className="text-xl font-semibold">{(account as any)?.name ?? "Account"}</h1>
<p className="text-xs text-muted-foreground">
Current balance {money((account as any)?.balance, cur)}
</p>
</div>
</div>
{!active && (
<Button onClick={() => {
setSetup({
statement_end_date: new Date().toLocaleDateString("en-CA", { timeZone: "America/New_York" }),
statement_balance: 0,
});
setSetupOpen(true);
}}>Reconcile</Button>
)}
</div>
{active ? (
<div className="grid gap-4 lg:grid-cols-[1fr_360px]">
{/* Left panel */}
<Card>
<CardHeader className="pb-3">
<div className="flex items-center gap-2 flex-wrap">
<CardTitle className="text-base mr-auto">Statement Transactions</CardTitle>
<div className="relative">
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
className="pl-8 h-9 w-56"
placeholder="Search…"
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
</div>
<Select value={filter} onValueChange={(v) => setFilter(v as any)}>
<SelectTrigger className="h-9 w-40"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="all">All</SelectItem>
<SelectItem value="deposits">Deposits only</SelectItem>
<SelectItem value="withdrawals">Withdrawals only</SelectItem>
</SelectContent>
</Select>
</div>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-8">
<Checkbox
checked={filtered.length > 0 && filtered.every((t) => checked.has(t.id))}
onCheckedChange={(v) => toggleAll(!!v)}
/>
</TableHead>
<TableHead>Date</TableHead>
<TableHead>Description</TableHead>
<TableHead>Ref #</TableHead>
<TableHead className="text-right">Deposit</TableHead>
<TableHead className="text-right">Withdrawal</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{filtered.map((t) => {
const on = checked.has(t.id);
return (
<TableRow
key={t.id}
className={on ? "bg-emerald-50/60 border-l-4 border-l-emerald-500" : ""}
>
<TableCell>
<Checkbox
checked={on}
onCheckedChange={(v) => {
setChecked((prev) => {
const next = new Set(prev);
if (v) next.add(t.id); else next.delete(t.id);
return next;
});
}}
/>
</TableCell>
<TableCell>{fmtDate(t.date)}</TableCell>
<TableCell className="max-w-[280px] truncate">{t.description}</TableCell>
<TableCell className="text-muted-foreground">{t.reference ?? "—"}</TableCell>
<TableCell className="text-right text-emerald-700">
{t.type === "credit" ? money(t.amount, cur) : ""}
</TableCell>
<TableCell className="text-right text-red-700">
{t.type === "debit" ? money(t.amount, cur) : ""}
</TableCell>
</TableRow>
);
})}
{filtered.length === 0 && (
<TableRow><TableCell colSpan={6} className="text-center text-muted-foreground py-8">
No unreconciled transactions in this period.
</TableCell></TableRow>
)}
</TableBody>
</Table>
</CardContent>
</Card>
{/* Right panel */}
<div className="space-y-4 lg:sticky lg:top-4 lg:self-start">
<Card>
<CardHeader className="pb-3"><CardTitle className="text-base">Summary</CardTitle></CardHeader>
<CardContent className="space-y-3 text-sm">
<div>
<Label className="text-xs">Statement ending balance</Label>
<Input
type="number" step="0.01"
value={active.statement_balance}
onChange={(e) => setActive({ ...active, statement_balance: Number(e.target.value) })}
/>
</div>
<div className="flex justify-between text-xs"><span className="text-muted-foreground">Opening balance</span><span>{money(openingBalance, cur)}</span></div>
<div className="flex justify-between text-xs"><span className="text-muted-foreground">+ Cleared deposits ({(txs as Tx[]).filter(t => checked.has(t.id) && t.type === "credit").length})</span><span className="text-emerald-700">+{money(clearedDeposits, cur)}</span></div>
<div className="flex justify-between text-xs"><span className="text-muted-foreground"> Cleared withdrawals ({(txs as Tx[]).filter(t => checked.has(t.id) && t.type === "debit").length})</span><span className="text-red-700">{money(clearedWithdrawals, cur)}</span></div>
<div className="flex justify-between border-t pt-2 font-medium text-sm"><span>Cleared balance</span><span>{money(clearedBalance, cur)}</span></div>
<div className="flex justify-between text-xs text-muted-foreground"><span>Statement balance</span><span>{money(active.statement_balance, cur)}</span></div>
<div className="rounded-md border p-3 text-center">
<div className="text-xs text-muted-foreground mb-1">Difference</div>
<div className={`text-2xl font-bold flex items-center justify-center gap-2 ${balanced ? "text-emerald-600" : "text-red-600"}`}>
{balanced ? <CheckCircle2 className="h-6 w-6" /> : <AlertTriangle className="h-6 w-6" />}
{money(difference, cur)}
</div>
{!balanced && (
<button
onClick={() => setAdjOpen(true)}
className="mt-2 text-xs text-primary hover:underline"
>
Create adjustment entry
</button>
)}
</div>
<div className="flex flex-col gap-2 pt-1">
<Button disabled={!balanced} onClick={finish}>Finish Reconciling</Button>
<Button variant="outline" onClick={saveForLater}>Save for Later</Button>
<Button variant="ghost" onClick={() => { setActive(null); setChecked(new Set()); }}>Cancel</Button>
</div>
</CardContent>
</Card>
</div>
</div>
) : (
<Tabs defaultValue="overview">
<TabsList>
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="history">History</TabsTrigger>
</TabsList>
<TabsContent value="overview">
<Card>
<CardContent className="py-10 text-center space-y-2">
<p className="text-muted-foreground">
{lastCompleted
? <>Last reconciled on <strong>{fmtDate(lastCompleted.statement_end_date)}</strong> at <strong>{money(lastCompleted.statement_balance, cur)}</strong>.</>
: "This account has never been reconciled."}
</p>
<Button onClick={() => setSetupOpen(true)}>Start Reconciling</Button>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="history">
<Card>
<CardContent className="pt-6">
<Table>
<TableHeader><TableRow>
<TableHead>Period (Statement End)</TableHead>
<TableHead>Statement Balance</TableHead>
<TableHead>Reconciled Date</TableHead>
<TableHead>Status</TableHead>
<TableHead></TableHead>
</TableRow></TableHeader>
<TableBody>
{(history as any[]).map((h: any) => (
<TableRow key={h.id}>
<TableCell>{fmtDate(h.statement_end_date)}</TableCell>
<TableCell>{money(h.statement_balance, cur)}</TableCell>
<TableCell>{h.completed_at ? fmtDate(h.completed_at) : "—"}</TableCell>
<TableCell>
{h.status === "completed"
? <Badge className="bg-emerald-600">Completed</Badge>
: <Badge variant="secondary">In progress</Badge>}
</TableCell>
<TableCell className="text-right">
<Button size="sm" variant="ghost" onClick={async () => {
const { data: rows } = await accounting
.from("transactions")
.select("date,description,reference,amount,type")
.eq("reconciliation_id", h.id);
const mapRow = (r: any) => ({ name: r.description ?? "", date: r.date, number: r.reference ?? "", memo: "", amount: Number(r.amount) });
const clearedDeposits = (rows ?? []).filter((r: any) => r.type === "credit").map(mapRow);
const clearedWithdrawals = (rows ?? []).filter((r: any) => r.type === "debit").map(mapRow);
const sumAmt = (rs: { amount: number }[]) => rs.reduce((s, r) => s + r.amount, 0);
const begin = Number(h.opening_balance);
const ending = begin + sumAmt(clearedDeposits) - sumAmt(clearedWithdrawals);
const acctLabel = (account as any)?.code ? `${(account as any).code} ${(account as any).name}` : ((account as any)?.name ?? "Account");
renderReconciliationPdf({
companyName: associationName ?? "Company",
accountName: acctLabel,
statementEndDate: h.statement_end_date,
beginningBalance: begin,
endingBalance: ending,
bookBalance: ending,
clearedDeposits, clearedWithdrawals, unclearedDeposits: [], unclearedWithdrawals: [],
preparedBy: user?.email ?? "—",
currency: cur,
completedAt: h.completed_at ?? new Date().toISOString(),
});
}}>
<FileDown className="h-4 w-4 mr-1" />View Report
</Button>
</TableCell>
</TableRow>
))}
{(history as any[]).length === 0 && (
<TableRow><TableCell colSpan={5} className="text-center text-muted-foreground py-8">No reconciliations yet.</TableCell></TableRow>
)}
</TableBody>
</Table>
</CardContent>
</Card>
</TabsContent>
</Tabs>
)}
{/* Setup modal */}
<Dialog open={setupOpen} onOpenChange={setSetupOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Start Reconciliation</DialogTitle>
<DialogDescription>Enter the details from your bank statement.</DialogDescription>
</DialogHeader>
<div className="space-y-3">
<div>
<Label>Statement end date</Label>
<Input type="date" value={setup.statement_end_date}
onChange={(e) => setSetup({ ...setup, statement_end_date: e.target.value })} />
</div>
<div>
<Label>Ending statement balance</Label>
<Input type="number" step="0.01" value={setup.statement_balance}
onChange={(e) => setSetup({ ...setup, statement_balance: Number(e.target.value) })} />
</div>
<div>
<Label>Opening balance</Label>
<Input readOnly value={Number(lastCompleted?.statement_balance ?? 0).toFixed(2)} />
<p className="text-xs text-muted-foreground mt-1">
Pulled from last reconciliation.
</p>
</div>
</div>
<DialogFooter>
<Button variant="ghost" onClick={() => setSetupOpen(false)}>Cancel</Button>
<Button onClick={startReconciling}>Start Reconciling</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* Adjustment modal */}
<Dialog open={adjOpen} onOpenChange={setAdjOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Create Adjustment Entry</DialogTitle>
<DialogDescription>
Post a journal entry of {money(difference, cur)} to balance the reconciliation.
</DialogDescription>
</DialogHeader>
<div className="space-y-3">
<div>
<Label>Offset account (suspense / bank charges)</Label>
<Select value={adjAccount} onValueChange={setAdjAccount}>
<SelectTrigger><SelectValue placeholder="Select account" /></SelectTrigger>
<SelectContent>
{(allAccounts as any[])
.filter((a: any) => a.id !== accountId)
.map((a: any) => (
<SelectItem key={a.id} value={a.id}>{a.name} ({a.type})</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<Label>Note</Label>
<Input value={adjNote} onChange={(e) => setAdjNote(e.target.value)} />
</div>
</div>
<DialogFooter>
<Button variant="ghost" onClick={() => setAdjOpen(false)}>Cancel</Button>
<Button onClick={postAdjustment}>Post Adjustment</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* Success modal */}
<Dialog open={successOpen} onOpenChange={setSuccessOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<CheckCircle2 className="h-5 w-5 text-emerald-600" /> Reconciliation Complete
</DialogTitle>
</DialogHeader>
{successData && (
<div className="space-y-2 text-sm">
<div className="flex justify-between"><span className="text-muted-foreground">Period reconciled</span><span>{fmtDate(successData.statementEndDate)}</span></div>
<div className="flex justify-between"><span className="text-muted-foreground">Ending balance</span><span>{money(successData.endingBalance, successData.currency)}</span></div>
<div className="flex justify-between"><span className="text-muted-foreground">Cleared transactions</span><span>{successData.clearedDeposits.length + successData.clearedWithdrawals.length}</span></div>
<div className="flex justify-between"><span className="text-muted-foreground">Date completed</span><span>{fmtDate(successData.completedAt)}</span></div>
</div>
)}
<DialogFooter>
<Button variant="outline" onClick={() => setSuccessOpen(false)}>Close</Button>
<Button onClick={() => successData && renderReconciliationPdf(successData)}>
<FileDown className="h-4 w-4 mr-1" />Export PDF
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
);
}