mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-14 02:31:21 +00:00
feat: new banking module (#54720)
* feat: initial SPA setup for banking * wip: bring over new banking module * feat: added Espresso design tokens * feat: button styles * fix: add all ink colors * wip: espresso design system changes * feat: button and badge espresso components * fix: button styling for reconcile * feat: Espresso progress bar * feat: Espresso toggle switch * feat: Espresso tabs design * fix: vertical tab support * fix: button sizing across modals * feat: Espresso style table layout * feat: Espresso tooltip * feat: Espresso elevations and checkbox * feat: Dialog with Espresso styles * feat: Espresso textarea * fix: input styles * fix: colors on bank picker * fix: breadcrumb styling * fix: bank picker styling * feat: create doctypes and fields for bank reconciliation * feat: APIs for banking * fix: use date format parser * fix: font styling to match Espresso * wip: settings modal * feat: settings dialog component * fix: icons and invalid requests * feat: preferences tab * fix: adjust icon stroke width to 1.5 * feat: rule configuration in settings * fix: remove sheet component * feat: alert and error banner component * feat: dropdown in Espresso * feat: popover and select in Espresso * fix: cleanup more styles * fix: match size of link fields * feat: command styling * fix: remove unused style tokens * fix: styles for global date picker dropdown * fix: styles for match and reconcile * feat: table Espresso component * feat: remove all other design tokens * fix: remove unused tokens * fix: form elements * fix: remove unused styles and fix filters in bank transaction list * feat: fetch bank rec doctypes for filtering * fix: record payment modal * feat: support for dark mode switching * fix: move bank logos to public folder * feat: add support for RTL * feat: support for RTL * chore: send layout direction in dev boot * fix: make checkbox work in RTL * feat: dark mode support * fix: dark mode style * feat: bank logos in dark mode * feat: dark mode bank logos * chore: use dark mode bank logos everywhere * chore: move rule evaluation to controller * chore: add tests for bank transaction rules * fix: move deps to fix actions errors * fix: move tw-animate-css to deps * fix: remove shadcn * fix: do not open modal if no transactions selected * fix: add translation strings * feat: add banner on existing bank reconciliation tool * feat: bank statement import * fix: translations and layout directions * fix: validation for transaction matching rule * fix: styles * fix: show conflicting transactions in alert * fix: show help text for new banking module forms * feat: show total debits and credits * fix: dark mode colors in automatic config * feat: add keyboard shortcuts help * feat: added keyboard shortcut for settings * fix: decrease size of progress bar * chore: bump packages * feat: add tests for statement import * fix: settings dialog * fix: show banner on small screens * fix: show banner when no bank account set
This commit is contained in:
140
banking/src/pages/BankReconciliation.tsx
Normal file
140
banking/src/pages/BankReconciliation.tsx
Normal file
@@ -0,0 +1,140 @@
|
||||
import BankBalance from "@/components/features/BankReconciliation/BankBalance"
|
||||
import BankClearanceSummary from "@/components/features/BankReconciliation/BankClearanceSummary"
|
||||
import BankPicker from "@/components/features/BankReconciliation/BankPicker"
|
||||
import BankRecDateFilter from "@/components/features/BankReconciliation/BankRecDateFilter"
|
||||
import BankReconciliationStatement from "@/components/features/BankReconciliation/BankReconciliationStatement"
|
||||
import BankTransactions from "@/components/features/BankReconciliation/BankTransactionList"
|
||||
import BankTransactionUnreconcileModal from "@/components/features/BankReconciliation/BankTransactionUnreconcileModal"
|
||||
import CompanySelector from "@/components/features/BankReconciliation/CompanySelector"
|
||||
import IncorrectlyClearedEntries from "@/components/features/BankReconciliation/IncorrectlyClearedEntries"
|
||||
import MatchAndReconcile from "@/components/features/BankReconciliation/MatchAndReconcile"
|
||||
import Settings from "@/components/features/Settings/Settings"
|
||||
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 { useLayoutEffect, useRef, useState } from "react"
|
||||
import { AlertTriangleIcon, CheckCircleIcon, HomeIcon, LandmarkIcon, ListIcon, 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"
|
||||
import { selectedBankAccountAtom } from "@/components/features/BankReconciliation/bankRecAtoms"
|
||||
|
||||
|
||||
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 - 270
|
||||
|
||||
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">
|
||||
<div className="flex items-center gap-6">
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<a href="/desk" className="text-ink-gray-7">
|
||||
<HomeIcon size={16} />
|
||||
</a>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>
|
||||
<div className="flex gap-1 items-center">
|
||||
{_("Banking")} <Badge theme="violet" variant="subtle">{_("Beta")}</Badge>
|
||||
</div>
|
||||
|
||||
</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
<CompanySelector />
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<TooltipProvider>
|
||||
<Settings />
|
||||
<ActionLog />
|
||||
</TooltipProvider>
|
||||
<BankRecDateFilter />
|
||||
</div>
|
||||
</div>
|
||||
<BankPicker />
|
||||
<BankBalance />
|
||||
</div>
|
||||
<BankRecTabs remainingHeightAfterTabs={remainingHeightAfterTabs} />
|
||||
<BankTransactionUnreconcileModal />
|
||||
</div>
|
||||
<div className="md:hidden flex h-screen items-center justify-between">
|
||||
<Empty>
|
||||
<EmptyMedia>
|
||||
<LandmarkIcon />
|
||||
</EmptyMedia>
|
||||
<EmptyHeader>
|
||||
<EmptyTitle>
|
||||
{_("Banking")}
|
||||
</EmptyTitle>
|
||||
<EmptyDescription>
|
||||
{_("This screen is not supported on mobile devices.")}
|
||||
</EmptyDescription>
|
||||
</EmptyHeader>
|
||||
<EmptyContent>
|
||||
<Button asChild>
|
||||
<a href="/desk">
|
||||
{_("Go to Desktop")}
|
||||
</a>
|
||||
</Button>
|
||||
</EmptyContent>
|
||||
</Empty>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const BankRecTabs = ({ remainingHeightAfterTabs }: { remainingHeightAfterTabs: number }) => {
|
||||
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>
|
||||
<TabsContent value="Bank Reconciliation Statement">
|
||||
<BankReconciliationStatement />
|
||||
</TabsContent>
|
||||
<TabsContent value="Bank Transactions">
|
||||
<BankTransactions />
|
||||
</TabsContent>
|
||||
<TabsContent value="Bank Clearance Summary">
|
||||
<BankClearanceSummary />
|
||||
</TabsContent>
|
||||
<TabsContent value="Incorrectly Cleared Entries">
|
||||
<IncorrectlyClearedEntries />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
}
|
||||
|
||||
export default BankReconciliation
|
||||
255
banking/src/pages/BankStatementImporter.tsx
Normal file
255
banking/src/pages/BankStatementImporter.tsx
Normal file
@@ -0,0 +1,255 @@
|
||||
import BankPicker from "@/components/features/BankReconciliation/BankPicker"
|
||||
import { selectedBankAccountAtom } from "@/components/features/BankReconciliation/bankRecAtoms"
|
||||
import CompanySelector from "@/components/features/BankReconciliation/CompanySelector"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
|
||||
import { Empty, EmptyHeader, EmptyMedia, EmptyTitle } from "@/components/ui/empty"
|
||||
import ErrorBanner from "@/components/ui/error-banner"
|
||||
import { FileDropzone } from "@/components/ui/file-dropzone"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
||||
import { H3, Paragraph } from "@/components/ui/typography"
|
||||
import { useCurrentCompany } from "@/hooks/useCurrentCompany"
|
||||
import { formatDate } from "@/lib/date"
|
||||
import { flt, formatCurrency } from "@/lib/numbers"
|
||||
import _ from "@/lib/translate"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { BankStatementImportLog } from "@/types/Accounts/BankStatementImportLog"
|
||||
import { useFrappeCreateDoc, useFrappeFileUpload, useFrappeGetDocList } from "frappe-react-sdk"
|
||||
import { useAtom, useAtomValue } from "jotai"
|
||||
import { ListIcon, Loader2Icon } from "lucide-react"
|
||||
import { useState } from "react"
|
||||
import { useNavigate } from "react-router"
|
||||
|
||||
|
||||
const BankStatementImporter = () => {
|
||||
|
||||
const selectedCompany = useCurrentCompany()
|
||||
|
||||
const [selectedBankAccount] = useAtom(selectedBankAccountAtom)
|
||||
|
||||
const [files, setFiles] = useState<File[]>([])
|
||||
|
||||
const { upload, error, loading } = useFrappeFileUpload()
|
||||
|
||||
const navigate = useNavigate()
|
||||
const { createDoc, loading: createLoading, error: createError } = useFrappeCreateDoc<BankStatementImportLog>()
|
||||
|
||||
const onUpload = () => {
|
||||
|
||||
if (!selectedBankAccount) {
|
||||
return
|
||||
}
|
||||
|
||||
const id = `new-bank-statement-import-log-${Date.now()}`
|
||||
|
||||
upload(files[0], {
|
||||
isPrivate: true,
|
||||
doctype: "Bank Statement Import Log",
|
||||
docname: id,
|
||||
fieldname: 'file'
|
||||
}).then((file) => {
|
||||
return createDoc("Bank Statement Import Log",
|
||||
// @ts-expect-error - not filling everything else
|
||||
{
|
||||
name: id,
|
||||
file: file.file_url,
|
||||
bank_account: selectedBankAccount.name
|
||||
})
|
||||
}).then((doc) => {
|
||||
navigate(`/statement-importer/${doc.name}`)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex px-4">
|
||||
<div className="w-[52%]">
|
||||
{error && <ErrorBanner error={error} />}
|
||||
{createError && <ErrorBanner error={createError} />}
|
||||
<div className="py-2 flex flex-col gap-6">
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label>{_("Company")}<span className="text-ink-red-3">*</span></Label>
|
||||
<div className="min-w-56 w-fit flex flex-col">
|
||||
<CompanySelector />
|
||||
</div>
|
||||
</div>
|
||||
{selectedCompany && <div className="flex flex-col gap-2">
|
||||
<Label>{_("Bank Account")}<span className="text-ink-red-3">*</span></Label>
|
||||
<div className="">
|
||||
<BankPicker className="w-full flex-wrap" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
{selectedBankAccount && <div className="flex flex-col gap-4 pe-4">
|
||||
<div className="flex justify-between">
|
||||
<div className="flex flex-col gap-2">
|
||||
<Label>{_("Bank Statement")}<span className="text-ink-red-3">*</span></Label>
|
||||
<p
|
||||
data-slot="form-description"
|
||||
className={cn("text-ink-gray-5 text-xs")}
|
||||
>
|
||||
{_("Upload your bank statement file to start the import process. We support CSV, and XLSX files.")}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<StatementInstructions />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FileDropzone
|
||||
setFiles={setFiles}
|
||||
files={files}
|
||||
className="p-8"
|
||||
accept={{
|
||||
'text/csv': ['.csv'],
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['.xlsx'],
|
||||
'application/vnd.ms-excel': ['.xls'],
|
||||
// 'application/xml': ['.xml'],
|
||||
}}
|
||||
multiple={false}
|
||||
/>
|
||||
</div>}
|
||||
<div className="flex justify-end px-4">
|
||||
<Button
|
||||
onClick={onUpload}
|
||||
size='md'
|
||||
disabled={files.length === 0 || loading || createLoading || !selectedBankAccount || !selectedCompany}>
|
||||
{loading || createLoading ? <Loader2Icon className="size-4 animate-spin" /> : null}
|
||||
{loading || createLoading ? _("Uploading...") : _("Upload")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-[48%] border-s border-outline-gray-2 ps-4">
|
||||
{selectedBankAccount && <StatementImportLog />}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const StatementInstructions = () => {
|
||||
return <Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant='outline' size='sm'>{_("View Instructions")}</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="min-w-7xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{_("Statement Import Instructions")}</DialogTitle>
|
||||
<DialogDescription>{_("We support uploading CSV, XLSX and XLS files. Please make sure the file contains the correct columns.")}</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Paragraph className="text-sm">{_("The file should contain the following columns with a distinct header row. You can upload most bank statements as is without changing the columns.")}</Paragraph>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>{_("Column Name")}</TableHead>
|
||||
<TableHead>{_("Maps To")}</TableHead>
|
||||
<TableHead>{_("Description")}</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell>Date/Transaction Date/Value Date</TableCell>
|
||||
<TableCell>{_("Date")}</TableCell>
|
||||
<TableCell className="text-ink-gray-5">{_("The date of the transaction")}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>Amount</TableCell>
|
||||
<TableCell>{_("Amount")}</TableCell>
|
||||
<TableCell className="text-ink-gray-5">{_('This can contain "CR"/"DR" values or positive/negative values. You could also have a separate column for CR/DR.')}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>Withdrawal/Deposit</TableCell>
|
||||
<TableCell>{_("Withdrawal")}/{_("Deposit")}</TableCell>
|
||||
<TableCell className="text-ink-gray-5">{_("The withdrawal or deposit amounts - only required if there's no amount column.")}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>Description/Particulars/Remarks/Narration/Detail</TableCell>
|
||||
<TableCell>{_("Description")}</TableCell>
|
||||
<TableCell className="text-ink-gray-5">{_("The description of the transaction")}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell>Reference/Ref/Transaction ID/Cheque/Check</TableCell>
|
||||
<TableCell>{_("Reference")}</TableCell>
|
||||
<TableCell className="text-ink-gray-5">{_("The reference number of the transaction")}</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
<DialogFooter>
|
||||
<DialogClose asChild>
|
||||
<Button variant='outline'>{_("Close")}</Button>
|
||||
</DialogClose>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
}
|
||||
|
||||
const StatementImportLog = () => {
|
||||
|
||||
const bankAccount = useAtomValue(selectedBankAccountAtom)
|
||||
|
||||
const { data, error } = useFrappeGetDocList<BankStatementImportLog>("Bank Statement Import Log", {
|
||||
fields: ["name", "file", "status", "number_of_transactions", "start_date", "end_date", "closing_balance", "creation"],
|
||||
filters: [["bank_account", "=", bankAccount?.name ?? ""]],
|
||||
orderBy: {
|
||||
field: "creation",
|
||||
order: "desc"
|
||||
},
|
||||
limit: 10
|
||||
}, bankAccount ? undefined : null, {
|
||||
revalidateOnFocus: false
|
||||
})
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
const onViewDetails = (name: string) => {
|
||||
navigate(`/statement-importer/${name}`)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<H3 className="text-base">{_("Previous Imports")}</H3>
|
||||
|
||||
{error && <ErrorBanner error={error} />}
|
||||
|
||||
{data && data.length > 0 ? (
|
||||
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>{_("Imported On")}</TableHead>
|
||||
<TableHead>{_("Status")}</TableHead>
|
||||
<TableHead>{_("Transaction Dates")}</TableHead>
|
||||
<TableHead className="text-end">{_("Number of Transactions")}</TableHead>
|
||||
<TableHead className="text-end">{_("Closing Balance")}</TableHead>
|
||||
<TableHead>{_("File")}</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{data?.map((item) => (
|
||||
<TableRow key={item.name} onClick={() => onViewDetails(item.name)} className="cursor-pointer hover:bg-surface-gray-2">
|
||||
<TableCell>{formatDate(item.creation, 'Do MMM YYYY')}</TableCell>
|
||||
<TableCell><Badge theme={item.status === "Completed" ? "green" : "gray"}>{item.status}</Badge></TableCell>
|
||||
<TableCell>{formatDate(item.start_date, 'Do MMM YYYY')} to {formatDate(item.end_date, 'Do MMM YYYY')}</TableCell>
|
||||
<TableCell className="text-end">{item.number_of_transactions}</TableCell>
|
||||
<TableCell className="text-end font-numeric">{formatCurrency(flt(item.closing_balance, 2))}</TableCell>
|
||||
<TableCell><a
|
||||
href={item.file}
|
||||
target="_blank" className="underline underline-offset-4">{item.file.split('/').pop()}</a></TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>)
|
||||
: <Empty>
|
||||
<EmptyHeader>
|
||||
<EmptyMedia>
|
||||
<ListIcon />
|
||||
</EmptyMedia>
|
||||
<EmptyTitle>{_("No bank statements imported yet")}</EmptyTitle>
|
||||
</EmptyHeader>
|
||||
</Empty>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default BankStatementImporter
|
||||
37
banking/src/pages/BankStatementImporterContainer.tsx
Normal file
37
banking/src/pages/BankStatementImporterContainer.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbList } from '@/components/ui/breadcrumb'
|
||||
import _ from '@/lib/translate'
|
||||
import { HomeIcon } from 'lucide-react'
|
||||
import { Link, Outlet } from 'react-router'
|
||||
|
||||
const BankStatementImporterContainer = () => {
|
||||
return (
|
||||
<div className="flex flex-col pt-1.5">
|
||||
<div className="flex gap-2 items-baseline p-4">
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<a href="/desk" className="text-ink-gray-7">
|
||||
<HomeIcon size={16} />
|
||||
</a>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link to="/">
|
||||
{_("Banking")}
|
||||
</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>{_("Import Bank Statement")}</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
<Outlet />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default BankStatementImporterContainer
|
||||
46
banking/src/pages/ViewBankStatementImportLog.tsx
Normal file
46
banking/src/pages/ViewBankStatementImportLog.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import CSVImport from '@/components/features/BankStatementImporter/CSV/CSVImport'
|
||||
import { useGetStatementDetails } from '@/components/features/BankStatementImporter/import_utils'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { useDirection } from '@/components/ui/direction'
|
||||
import ErrorBanner from '@/components/ui/error-banner'
|
||||
import _ from '@/lib/translate'
|
||||
import { useFrappeDocumentEventListener } from 'frappe-react-sdk'
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-react'
|
||||
import { Link, useParams } from 'react-router'
|
||||
|
||||
const ViewBankStatementImportLog = () => {
|
||||
|
||||
const { id } = useParams<{ id: string }>()
|
||||
|
||||
const { data, isLoading, error } = useGetStatementDetails(id ?? "")
|
||||
|
||||
useFrappeDocumentEventListener("Bank Statement Import Log", id ?? "", () => {
|
||||
})
|
||||
|
||||
const direction = useDirection()
|
||||
|
||||
if (!data || !data.message) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <div>Loading...</div>
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <div className='flex flex-col gap-4 px-4'>
|
||||
<div>
|
||||
<Button size='sm' variant='outline' asChild>
|
||||
<Link to="/statement-importer">
|
||||
{direction === 'ltr' ? <ChevronLeftIcon /> : <ChevronRightIcon />}
|
||||
{_("Back")}
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
<ErrorBanner error={error} />
|
||||
</div>
|
||||
}
|
||||
return <CSVImport data={data} />
|
||||
}
|
||||
|
||||
export default ViewBankStatementImportLog
|
||||
Reference in New Issue
Block a user