fix: better transactions export UX

This commit is contained in:
Vasily Zubarev
2025-05-19 11:01:50 +02:00
parent ee368180f6
commit b7475ca57d
3 changed files with 130 additions and 74 deletions

View File

@@ -17,7 +17,7 @@ import { Separator } from "@/components/ui/separator"
import { useTransactionFilters } from "@/hooks/use-transaction-filters"
import { Category, Field, Project } from "@/prisma/client"
import { formatDate } from "date-fns"
import { useRouter } from "next/navigation"
import { Download, Loader2 } from "lucide-react"
import { useState } from "react"
const deselectedFields = ["files", "text"]
@@ -26,14 +26,15 @@ export function ExportTransactionsDialog({
fields,
categories,
projects,
total,
children,
}: {
fields: Field[]
categories: Category[]
projects: Project[]
total: number
children: React.ReactNode
}) {
const router = useRouter()
const [isLoading, setIsLoading] = useState(false)
const [exportFilters, setExportFilters] = useTransactionFilters()
const [exportFields, setExportFields] = useState<string[]>(
@@ -41,23 +42,49 @@ export function ExportTransactionsDialog({
)
const [includeAttachments, setIncludeAttachments] = useState(true)
const handleSubmit = () => {
const handleSubmit = async () => {
setIsLoading(true)
router.push(
`/export/transactions?${new URLSearchParams({
search: exportFilters?.search || "",
dateFrom: exportFilters?.dateFrom || "",
dateTo: exportFilters?.dateTo || "",
ordering: exportFilters?.ordering || "",
categoryCode: exportFilters?.categoryCode || "",
projectCode: exportFilters?.projectCode || "",
fields: exportFields.join(","),
includeAttachments: includeAttachments.toString(),
}).toString()}`
)
setTimeout(() => {
try {
const response = await fetch(
`/export/transactions?${new URLSearchParams({
search: exportFilters?.search || "",
dateFrom: exportFilters?.dateFrom || "",
dateTo: exportFilters?.dateTo || "",
ordering: exportFilters?.ordering || "",
categoryCode: exportFilters?.categoryCode || "",
projectCode: exportFilters?.projectCode || "",
fields: exportFields.join(","),
includeAttachments: includeAttachments.toString(),
}).toString()}`
)
if (!response.ok) {
throw new Error("Export failed")
}
// Get the filename from the Content-Disposition header
const contentDisposition = response.headers.get("Content-Disposition")
const filename = contentDisposition?.split("filename=")[1]?.replace(/"/g, "") || "transactions.zip"
// Create a blob from the response
const blob = await response.blob()
// Create a download link and trigger it
const url = window.URL.createObjectURL(blob)
const a = document.createElement("a")
a.href = url
a.download = filename
document.body.appendChild(a)
a.click()
window.URL.revokeObjectURL(url)
document.body.removeChild(a)
} catch (error) {
console.error("Export failed:", error)
// You might want to show an error message to the user here
} finally {
setIsLoading(false)
}, 3000)
}
}
return (
@@ -65,7 +92,9 @@ export function ExportTransactionsDialog({
<DialogTrigger asChild>{children}</DialogTrigger>
<DialogContent className="max-w-xl">
<DialogHeader>
<DialogTitle className="text-2xl font-bold">Export Transactions</DialogTitle>
<DialogTitle className="text-2xl font-bold">
Export {total} Transaction{total !== 1 ? "s" : ""}
</DialogTitle>
<DialogDescription>Export selected transactions and files as a CSV file or a ZIP archive</DialogDescription>
</DialogHeader>
<div className="flex flex-col gap-4">
@@ -161,7 +190,17 @@ export function ExportTransactionsDialog({
</div>
<DialogFooter className="sm:justify-end">
<Button type="button" onClick={handleSubmit} disabled={isLoading}>
{isLoading ? "Exporting..." : "Export Transactions"}
{isLoading ? (
<div className="flex items-center gap-2">
<Loader2 className="h-4 w-4 animate-spin" />
<span>Exporting...</span>
</div>
) : (
<div className="flex items-center gap-2">
<Download className="h-4 w-4" />
<span>Export Transactions</span>
</div>
)}
</Button>
</DialogFooter>
</DialogContent>