From ec8f2cbc7519d02af6cba896118d6d798f8df496 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 19:14:38 +0000 Subject: [PATCH] Fix performance issues Continue to optimize performance by addressing remaining bottlenecks. Further refactor components and calculations to reduce re-renders and improve responsiveness. --- src/components/BatchImportDialog.tsx | 214 +++++++++++++++++++++ src/components/BillOfMaterialsManager.tsx | 160 ++++++++++++++++ src/components/CreateJobDialog.tsx | 144 +++++++++++++++ src/components/EditableField.tsx | 55 ++++++ src/components/JobCategory.tsx | 107 +++++++++++ src/components/TransactionManager.tsx | 216 ++++++++++++++++++++++ src/hooks/useDataService.ts | 26 ++- src/services/dataService.ts | 66 ++++--- src/services/facilityService.ts | 30 +++ src/utils/currency.ts | 85 +++++++++ 10 files changed, 1077 insertions(+), 26 deletions(-) create mode 100644 src/components/BatchImportDialog.tsx create mode 100644 src/components/BillOfMaterialsManager.tsx create mode 100644 src/components/CreateJobDialog.tsx create mode 100644 src/components/EditableField.tsx create mode 100644 src/components/JobCategory.tsx create mode 100644 src/components/TransactionManager.tsx create mode 100644 src/services/facilityService.ts create mode 100644 src/utils/currency.ts diff --git a/src/components/BatchImportDialog.tsx b/src/components/BatchImportDialog.tsx new file mode 100644 index 0000000..f81cc10 --- /dev/null +++ b/src/components/BatchImportDialog.tsx @@ -0,0 +1,214 @@ +import { useState } from 'react'; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; +import { Textarea } from '@/components/ui/textarea'; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; +import { Badge } from '@/components/ui/badge'; +import { IndJobStatusOptions } from '@/types/industry'; +import { parseTransactionLine, formatISK } from '@/utils/currency'; +import { useJobs } from '@/hooks/useDataService'; +import { FileUp } from 'lucide-react'; +import { useToast } from '@/hooks/use-toast'; + +export function BatchImportDialog() { + const { jobs, updateJob } = useJobs(); + const { toast } = useToast(); + const [open, setOpen] = useState(false); + const [pasteText, setPasteText] = useState(''); + const [groupedTransactions, setGroupedTransactions] = useState>({}); + + const targetStatuses = [IndJobStatusOptions.Running, IndJobStatusOptions.Selling, IndJobStatusOptions.Tracked]; + const eligibleJobs = jobs.filter(job => targetStatuses.includes(job.status)); + + const handleAnalyze = () => { + if (!pasteText.trim()) return; + + const lines = pasteText.split('\n').filter(line => line.trim()); + const transactions: any[] = []; + + for (const line of lines) { + const parsed = parseTransactionLine(line); + if (parsed) { + transactions.push(parsed); + } + } + + if (transactions.length === 0) { + toast({ + title: "No Transactions Found", + description: "No valid transactions found in the pasted text.", + variant: "destructive", + }); + return; + } + + // Group by item name + const grouped: Record = {}; + transactions.forEach(tx => { + if (!grouped[tx.itemName]) { + grouped[tx.itemName] = []; + } + grouped[tx.itemName].push(tx); + }); + + setGroupedTransactions(grouped); + }; + + const handleImport = () => { + let totalImported = 0; + + Object.entries(groupedTransactions).forEach(([itemName, transactions]) => { + // Find matching job + const matchingJob = eligibleJobs.find(job => + job.outputItem.toLowerCase() === itemName.toLowerCase() + ); + + if (matchingJob) { + // Deduplicate against existing income transactions + const existingIncome = matchingJob.income || []; + const newTransactions = transactions.filter(newTx => { + return !existingIncome.some(existing => + existing.date === newTx.date && + existing.itemName === newTx.itemName && + existing.quantity === newTx.quantity && + existing.totalPrice === newTx.totalPrice && + existing.buyer === newTx.buyer + ); + }); + + if (newTransactions.length > 0) { + const updatedIncome = [...existingIncome]; + newTransactions.forEach(tx => { + updatedIncome.push({ + ...tx, + id: crypto.randomUUID(), + job: matchingJob.id, + created: new Date().toISOString(), + updated: new Date().toISOString(), + }); + }); + + updateJob(matchingJob.id, { income: updatedIncome }); + totalImported += newTransactions.length; + } + } + }); + + toast({ + title: "Batch Import Complete", + description: `Imported ${totalImported} transactions across ${Object.keys(groupedTransactions).length} items.`, + }); + + setPasteText(''); + setGroupedTransactions({}); + setOpen(false); + }; + + const getJobForItem = (itemName: string) => { + return eligibleJobs.find(job => + job.outputItem.toLowerCase() === itemName.toLowerCase() + ); + }; + + return ( + + + + + + + Batch Import Sales Transactions + + +
+
+

+ Paste sale transaction data. Transactions will be automatically grouped by item name and matched to running/selling/tracked jobs. +

+