diff --git a/src/components/MaterialsActions.tsx b/src/components/MaterialsActions.tsx new file mode 100644 index 0000000..abf5a4f --- /dev/null +++ b/src/components/MaterialsActions.tsx @@ -0,0 +1,60 @@ + +import React from 'react'; +import { Button } from '@/components/ui/button'; +import { Import, Download, AlertTriangle } from 'lucide-react'; + +interface MaterialsActionsProps { + onImport: () => void; + onExport: () => void; + onExportMissing?: () => void; + importDisabled?: boolean; + missingDisabled?: boolean; + type: 'bom' | 'consumed'; +} + +const MaterialsActions: React.FC = ({ + onImport, + onExport, + onExportMissing, + importDisabled = false, + missingDisabled = false, + type +}) => { + return ( +
+
+ + {type === 'bom' && onExportMissing && ( + + )} +
+ +
+ ); +}; + +export default MaterialsActions; diff --git a/src/components/MaterialsImportExport.tsx b/src/components/MaterialsImportExport.tsx index f8651c5..c251a30 100644 --- a/src/components/MaterialsImportExport.tsx +++ b/src/components/MaterialsImportExport.tsx @@ -1,14 +1,18 @@ import React, { useState } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; import { Textarea } from '@/components/ui/textarea'; import { Label } from '@/components/ui/label'; -import { Import, Download, FileText, AlertTriangle } from 'lucide-react'; -import { IndBillitemRecord, IndBillitemRecordNoId } from '@/lib/pbtypes'; +import { FileText } from 'lucide-react'; +import { IndBillitemRecord } from '@/lib/pbtypes'; import { IndJob } from '@/lib/types'; import { dataService } from '@/services/dataService'; import { useToast } from '@/hooks/use-toast'; +import { useMaterialsCalculations } from '@/hooks/useMaterialsCalculations'; +import { parseBillOfMaterials, parseConsumedMaterials } from '@/utils/materialsParser'; +import { exportBillOfMaterials, exportConsumedMaterials, exportMissingMaterials } from '@/utils/materialsExporter'; +import MaterialsActions from './MaterialsActions'; +import MaterialsList from './MaterialsList'; interface MaterialsImportExportProps { job?: IndJob; @@ -28,84 +32,7 @@ const MaterialsImportExport: React.FC = ({ const [bomInput, setBomInput] = useState(''); const [consumedInput, setConsumedInput] = useState(''); const { toast } = useToast(); - - const parseBillOfMaterials = (text: string): IndBillitemRecordNoId[] => { - const lines = text.split('\n').filter(line => line.trim()); - const materials: IndBillitemRecordNoId[] = []; - - for (const line of lines) { - const parts = line.trim().split(/\s+/); - if (parts.length >= 2) { - const name = parts.slice(0, -1).join(' '); - const quantity = parseInt(parts[parts.length - 1]); - if (name && !isNaN(quantity)) { - materials.push({ name, quantity }); - } - } - } - - return materials; - }; - - const parseConsumedMaterials = (text: string): IndBillitemRecordNoId[] => { - const lines = text.split('\n').filter(line => line.trim()); - const materials: IndBillitemRecordNoId[] = []; - - for (const line of lines) { - const parts = line.trim().split('\t'); - if (parts.length >= 2) { - const name = parts[0]; - const quantity = parseInt(parts[1]); - if (name && !isNaN(quantity)) { - materials.push({ name, quantity }); - } - } - } - - return materials; - }; - - const calculateMissingMaterials = () => { - if (!job) return []; - - // Create a map of required materials from bill of materials - const requiredMaterials = new Map(); - billOfMaterials.forEach(item => { - requiredMaterials.set(item.name, item.quantity); - }); - - // Create a map of owned materials from expenditures - const ownedMaterials = new Map(); - job.expenditures?.forEach(transaction => { - const currentOwned = ownedMaterials.get(transaction.itemName) || 0; - ownedMaterials.set(transaction.itemName, currentOwned + transaction.quantity); - }); - - // Calculate missing materials - const missingMaterials: { name: string; quantity: number }[] = []; - requiredMaterials.forEach((required, materialName) => { - const owned = ownedMaterials.get(materialName) || 0; - const missing = required - owned; - if (missing > 0) { - missingMaterials.push({ name: materialName, quantity: missing }); - } - }); - - return missingMaterials; - }; - - const exportBillOfMaterials = (): string => { - return billOfMaterials.map(item => `${item.name} ${item.quantity}`).join('\n'); - }; - - const exportConsumedMaterials = (): string => { - return consumedMaterials.map(item => `${item.name}\t${item.quantity}`).join('\n'); - }; - - const exportMissingMaterials = (): string => { - const missing = calculateMissingMaterials(); - return missing.map(item => `${item.name}\t${item.quantity}`).join('\n'); - }; + const { calculateMissingMaterials } = useMaterialsCalculations(job, billOfMaterials); const handleImportBom = async () => { if (!job) return; @@ -138,7 +65,7 @@ const MaterialsImportExport: React.FC = ({ }; const handleExportBom = () => { - const exported = exportBillOfMaterials(); + const exported = exportBillOfMaterials(billOfMaterials); navigator.clipboard.writeText(exported); toast({ title: "Exported", @@ -148,7 +75,7 @@ const MaterialsImportExport: React.FC = ({ }; const handleExportConsumed = () => { - const exported = exportConsumedMaterials(); + const exported = exportConsumedMaterials(consumedMaterials); navigator.clipboard.writeText(exported); toast({ title: "Exported", @@ -158,7 +85,8 @@ const MaterialsImportExport: React.FC = ({ }; const handleExportMissing = () => { - const exported = exportMissingMaterials(); + const missingMaterials = calculateMissingMaterials(); + const exported = exportMissingMaterials(missingMaterials); if (exported) { navigator.clipboard.writeText(exported); toast({ @@ -187,30 +115,15 @@ const MaterialsImportExport: React.FC = ({
-
- -
- - -
-
+ +