fix(banking): UI cleanup and better statement parsing (#58817)

* fix(banking): reset scroll on searching accounts

* fix(banking): show only past dates in date filter

* fix(banking): clean up line heights and remove beta badge

* fix(banking): show accurate count of import progress
fix(banking): show latest 20 imports instead of 10

* fix(banking): layout sizing needs to be preserved on page change

* fix(banking): cleaner bank balance UI

* fix(banking): correctly parse Cr/Dr values in statement importer

* Update banking/src/components/features/BankReconciliation/BankBalance.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This commit is contained in:
Nikhil Kothari
2026-09-07 18:40:02 +05:30
committed by GitHub
parent e85e300f8f
commit ebe5decb96
22 changed files with 1162 additions and 437 deletions

View File

@@ -1,4 +1,4 @@
import BankBalance from "@/components/features/BankReconciliation/BankBalance"
import BankAccountBalancePanel from "@/components/features/BankReconciliation/BankBalance"
import BankPicker from "@/components/features/BankReconciliation/BankPicker"
import BankRecDateFilter from "@/components/features/BankReconciliation/BankRecDateFilter"
import BankTransactionUnreconcileModal from "@/components/features/BankReconciliation/BankTransactionUnreconcileModal"
@@ -9,10 +9,9 @@ import ActionLog from "@/components/features/ActionLog/ActionLog"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { TooltipProvider } from "@/components/ui/tooltip"
import _ from "@/lib/translate"
import { lazy, Suspense, useLayoutEffect, useRef, useState } from "react"
import { lazy, Suspense } from "react"
import { AlertTriangleIcon, CheckCircleIcon, HomeIcon, LandmarkIcon, ListIcon, Loader2Icon, ScrollTextIcon, ShuffleIcon } from "lucide-react"
import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "@/components/ui/breadcrumb"
import { Badge } from "@/components/ui/badge"
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "@/components/ui/empty"
import { Button } from "@/components/ui/button"
import { useAtomValue } from "jotai"
@@ -25,23 +24,13 @@ const IncorrectlyClearedEntries = lazy(() => import('@/components/features/BankR
const BankReconciliation = () => {
const [headerHeight, setHeaderHeight] = useState(0)
const ref = useRef<HTMLDivElement>(null)
useLayoutEffect(() => {
if (ref.current) {
setHeaderHeight(ref.current.clientHeight)
}
}, [])
const remainingHeightAfterTabs = window.innerHeight - headerHeight - 220
return (
<div>
<div className="p-4 flex-col gap-4 md:flex hidden">
<div ref={ref} className="flex flex-col gap-4">
<div className="flex justify-between">
{/* The page owns the viewport height and the tabs/lists below fill what's left, so
the virtualizers size themselves from layout instead of a measured pixel value. */}
<div className="px-2 pt-1 flex-col gap-4 md:flex hidden h-dvh">
<div className="flex flex-col gap-4 shrink-0">
<div className="flex justify-between shrink-0">
<div className="flex items-center gap-6">
<Breadcrumb>
<BreadcrumbList>
@@ -54,7 +43,7 @@ const BankReconciliation = () => {
<BreadcrumbItem>
<BreadcrumbPage>
<div className="flex gap-1 items-center">
{_("Banking")} <Badge theme="violet" variant="subtle">{_("Beta")}</Badge>
{_("Banking")}
</div>
</BreadcrumbPage>
@@ -71,10 +60,8 @@ const BankReconciliation = () => {
<BankRecDateFilter />
</div>
</div>
<BankPicker />
<BankBalance />
</div>
<BankRecTabs remainingHeightAfterTabs={remainingHeightAfterTabs} />
<BankRecWorkspace />
<BankTransactionUnreconcileModal />
</div>
<div className="md:hidden flex h-screen items-center justify-between">
@@ -104,42 +91,53 @@ const BankReconciliation = () => {
)
}
const BankRecTabs = ({ remainingHeightAfterTabs }: { remainingHeightAfterTabs: number }) => {
const BankRecWorkspace = () => {
const selectedBankAccount = useAtomValue(selectedBankAccountAtom)
if (!selectedBankAccount) {
return null
}
return <Tabs defaultValue="Match and Reconcile">
<TabsList>
<TabsTrigger value="Match and Reconcile"><ShuffleIcon /> {_("Match and Reconcile")}</TabsTrigger>
<TabsTrigger value="Bank Reconciliation Statement"><ScrollTextIcon /> {_("Bank Reconciliation Statement")}</TabsTrigger>
<TabsTrigger value="Bank Transactions"><ListIcon />{_("Bank Transactions")}</TabsTrigger>
<TabsTrigger value="Bank Clearance Summary"><CheckCircleIcon />{_("Bank Clearance Summary")}</TabsTrigger>
<TabsTrigger value="Incorrectly Cleared Entries"><AlertTriangleIcon /> {_("Incorrectly Cleared Entries")}</TabsTrigger>
</TabsList>
<TabsContent value="Match and Reconcile">
<MatchAndReconcile contentHeight={remainingHeightAfterTabs} />
</TabsContent>
<Suspense fallback={
<div className="flex items-center justify-center p-16">
<Loader2Icon className="size-6 animate-spin text-muted-foreground" />
return <Tabs defaultValue="Match and Reconcile" className="min-h-0 flex-1 gap-4">
{/* Picker + tab strip stack on the left, balance panel beside them - the tab strip
fills height the panel needs anyway, so it costs no row of its own. The picker
scrolls horizontally (`min-w-0` lets it shrink so its overflow-x engages) while
the panel stays put, so the figures never scroll away. */}
{/* No gap here: the panel's own `border-s ps-4` supplies the separation, and a gap
would leave dead space the picker's edge fade can't reach. */}
<div className="flex shrink-0 items-stretch">
<div className="flex min-w-0 flex-1 flex-col justify-between gap-3">
<BankPicker />
{selectedBankAccount && <TabsList>
<TabsTrigger value="Match and Reconcile"><ShuffleIcon /> {_("Match and Reconcile")}</TabsTrigger>
<TabsTrigger value="Bank Reconciliation Statement"><ScrollTextIcon /> {_("Reconciliation Statement")}</TabsTrigger>
<TabsTrigger value="Bank Transactions"><ListIcon />{_("Transactions")}</TabsTrigger>
<TabsTrigger value="Bank Clearance Summary"><CheckCircleIcon />{_("Clearance Summary")}</TabsTrigger>
<TabsTrigger value="Incorrectly Cleared Entries"><AlertTriangleIcon /> {_("Incorrectly Cleared")}</TabsTrigger>
</TabsList>}
</div>
}>
<TabsContent value="Bank Reconciliation Statement">
<BankReconciliationStatement />
{selectedBankAccount && <BankAccountBalancePanel />}
</div>
{selectedBankAccount && <>
<TabsContent value="Match and Reconcile" className="flex min-h-0 flex-col">
<MatchAndReconcile />
</TabsContent>
<TabsContent value="Bank Transactions">
<BankTransactions />
</TabsContent>
<TabsContent value="Bank Clearance Summary">
<BankClearanceSummary />
</TabsContent>
<TabsContent value="Incorrectly Cleared Entries">
<IncorrectlyClearedEntries />
</TabsContent>
</Suspense>
<Suspense fallback={
<div className="flex items-center justify-center p-16">
<Loader2Icon className="size-6 animate-spin text-muted-foreground" />
</div>
}>
<TabsContent value="Bank Reconciliation Statement" className="flex min-h-0 flex-col">
<BankReconciliationStatement />
</TabsContent>
<TabsContent value="Bank Transactions" className="flex min-h-0 flex-col">
<BankTransactions />
</TabsContent>
<TabsContent value="Bank Clearance Summary" className="flex min-h-0 flex-col">
<BankClearanceSummary />
</TabsContent>
<TabsContent value="Incorrectly Cleared Entries" className="flex min-h-0 flex-col">
<IncorrectlyClearedEntries />
</TabsContent>
</Suspense>
</>}
</Tabs>
}

View File

@@ -226,7 +226,7 @@ const StatementImportLog = () => {
field: "creation",
order: "desc"
},
limit: 10
limit: 20
}, bankAccount ? undefined : null, {
revalidateOnFocus: false
})