diff --git a/app/(app)/export/transactions/route.ts b/app/(app)/export/transactions/route.ts index 4d7ac19..7899daa 100644 --- a/app/(app)/export/transactions/route.ts +++ b/app/(app)/export/transactions/route.ts @@ -38,6 +38,9 @@ export async function GET(request: Request) { // Process transactions in chunks to avoid memory issues for (let i = 0; i < transactions.length; i += TRANSACTIONS_CHUNK_SIZE) { const chunk = transactions.slice(i, i + TRANSACTIONS_CHUNK_SIZE) + console.log( + `Processing transactions ${i + 1}-${Math.min(i + TRANSACTIONS_CHUNK_SIZE, transactions.length)} of ${transactions.length}` + ) for (const transaction of chunk) { const row: Record = {} @@ -90,8 +93,22 @@ export async function GET(request: Request) { throw new Error("Failed to create zip folder") } + let totalFilesProcessed = 0 + let totalFilesToProcess = 0 + + // First count total files to process + for (const transaction of transactions) { + const transactionFiles = await getFilesByTransactionId(transaction.id, user.id) + totalFilesToProcess += transactionFiles.length + } + + console.log(`Starting to process ${totalFilesToProcess} files in total`) + for (let i = 0; i < transactions.length; i += FILES_CHUNK_SIZE) { const chunk = transactions.slice(i, i + FILES_CHUNK_SIZE) + console.log( + `Processing files for transactions ${i + 1}-${Math.min(i + FILES_CHUNK_SIZE, transactions.length)} of ${transactions.length}` + ) for (const transaction of chunk) { const transactionFiles = await getFilesByTransactionId(transaction.id, user.id) @@ -108,6 +125,9 @@ export async function GET(request: Request) { for (const file of transactionFiles) { const fullFilePath = fullPathForFile(user, file) if (await fileExists(fullFilePath)) { + console.log( + `Processing file ${++totalFilesProcessed}/${totalFilesToProcess}: ${file.filename} for transaction ${transaction.id}` + ) const fileData = await fs.readFile(fullFilePath) const fileExtension = path.extname(fullFilePath) transactionFolder.file( @@ -116,11 +136,15 @@ export async function GET(request: Request) { }${fileExtension}`, fileData ) + } else { + console.log(`Skipping missing file: ${file.filename} for transaction ${transaction.id}`) } } } } + console.log(`Finished processing all ${totalFilesProcessed} files`) + // Generate zip with progress tracking const zipContent = await zip.generateAsync({ type: "uint8array", diff --git a/app/(app)/settings/fields/page.tsx b/app/(app)/settings/fields/page.tsx index 9a1e34d..8c361a5 100644 --- a/app/(app)/settings/fields/page.tsx +++ b/app/(app)/settings/fields/page.tsx @@ -36,14 +36,14 @@ export default async function FieldsSettingsPage() { { key: "llm_prompt", label: "LLM Prompt", editable: true }, { key: "isVisibleInList", - label: "Show in transactions table", + label: "Always show in transactions table", type: "checkbox", defaultValue: false, editable: true, }, { key: "isVisibleInAnalysis", - label: "Show in analysis form", + label: "Always show in analysis form", type: "checkbox", defaultValue: false, editable: true, diff --git a/components/export/transactions.tsx b/components/export/transactions.tsx index 367aa15..2ce78ea 100644 --- a/components/export/transactions.tsx +++ b/components/export/transactions.tsx @@ -17,7 +17,6 @@ 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 { Download, Loader2 } from "lucide-react" import { useState } from "react" const deselectedFields = ["files", "text"] @@ -42,49 +41,34 @@ export function ExportTransactionsDialog({ ) const [includeAttachments, setIncludeAttachments] = useState(true) - const handleSubmit = async () => { + const handleSubmit = () => { setIsLoading(true) + const exportWindow = window.open( + `/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()}` + ) - 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 { + // Check if window was opened successfully + if (!exportWindow) { setIsLoading(false) + return } + + // Monitor the export window + const checkWindow = setInterval(() => { + if (exportWindow.closed) { + clearInterval(checkWindow) + setIsLoading(false) + } + }, 1000) } return ( @@ -92,9 +76,7 @@ export function ExportTransactionsDialog({ {children} - - Export {total} Transaction{total !== 1 ? "s" : ""} - + Export {total} Transactions Export selected transactions and files as a CSV file or a ZIP archive
@@ -190,17 +172,7 @@ export function ExportTransactionsDialog({
diff --git a/docker-compose.yml b/docker-compose.yml index 66596ab..b57d7bf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,8 +28,6 @@ services: volumes: - ./pgdata:/var/lib/postgresql/data restart: unless-stopped - ports: - - "5432:5432" logging: driver: "local" options: