Add bill of materials after job creation
Add functionality to add bill of materials to a job after it has been created.
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -12,7 +11,7 @@ import { parseISKAmount } from '@/utils/priceUtils';
|
|||||||
|
|
||||||
interface JobFormProps {
|
interface JobFormProps {
|
||||||
job?: IndJob;
|
job?: IndJob;
|
||||||
onSubmit: (job: IndJobRecordNoId) => void;
|
onSubmit: (job: IndJobRecordNoId, billOfMaterials?: { name: string; quantity: number }[]) => void;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,9 +25,13 @@ const JobForm: React.FC<JobFormProps> = ({ job, onSubmit, onCancel }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const [jobDump, setJobDump] = useState('');
|
const [jobDump, setJobDump] = useState('');
|
||||||
|
const [parsedBillOfMaterials, setParsedBillOfMaterials] = useState<{ name: string; quantity: number }[]>([]);
|
||||||
|
|
||||||
const parseJobDump = (dumpText: string) => {
|
const parseJobDump = (dumpText: string) => {
|
||||||
if (!dumpText.trim()) return;
|
if (!dumpText.trim()) {
|
||||||
|
setParsedBillOfMaterials([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const lines = dumpText.trim().split('\n').filter(line => line.trim());
|
const lines = dumpText.trim().split('\n').filter(line => line.trim());
|
||||||
|
|
||||||
@@ -54,6 +57,26 @@ const JobForm: React.FC<JobFormProps> = ({ job, onSubmit, onCancel }) => {
|
|||||||
projectedCost: cost,
|
projectedCost: cost,
|
||||||
projectedRevenue: revenue
|
projectedRevenue: revenue
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Parse BOM starting from line 4 (after empty line)
|
||||||
|
const bomLines = lines.slice(4); // Skip first 3 lines and empty line
|
||||||
|
const billOfMaterials: { name: string; quantity: number }[] = [];
|
||||||
|
|
||||||
|
for (const line of bomLines) {
|
||||||
|
const trimmedLine = line.trim();
|
||||||
|
if (trimmedLine) {
|
||||||
|
const lastSpaceIndex = trimmedLine.lastIndexOf(' ');
|
||||||
|
if (lastSpaceIndex > 0) {
|
||||||
|
const materialName = trimmedLine.substring(0, lastSpaceIndex).trim();
|
||||||
|
const materialQuantity = parseInt(trimmedLine.substring(lastSpaceIndex + 1).trim()) || 0;
|
||||||
|
if (materialName && materialQuantity > 0) {
|
||||||
|
billOfMaterials.push({ name: materialName, quantity: materialQuantity });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setParsedBillOfMaterials(billOfMaterials);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -73,7 +96,7 @@ const JobForm: React.FC<JobFormProps> = ({ job, onSubmit, onCancel }) => {
|
|||||||
status: formData.status,
|
status: formData.status,
|
||||||
projectedCost: formData.projectedCost,
|
projectedCost: formData.projectedCost,
|
||||||
projectedRevenue: formData.projectedRevenue
|
projectedRevenue: formData.projectedRevenue
|
||||||
});
|
}, parsedBillOfMaterials.length > 0 ? parsedBillOfMaterials : undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusOptions = Object.values(IndJobStatusOptions);
|
const statusOptions = Object.values(IndJobStatusOptions);
|
||||||
@@ -175,6 +198,11 @@ const JobForm: React.FC<JobFormProps> = ({ job, onSubmit, onCancel }) => {
|
|||||||
className="bg-gray-800 border-gray-600 text-white min-h-[120px]"
|
className="bg-gray-800 border-gray-600 text-white min-h-[120px]"
|
||||||
rows={6}
|
rows={6}
|
||||||
/>
|
/>
|
||||||
|
{parsedBillOfMaterials.length > 0 && (
|
||||||
|
<div className="text-sm text-gray-400">
|
||||||
|
<p>Parsed {parsedBillOfMaterials.length} materials from dump</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-2 pt-4">
|
<div className="flex gap-2 pt-4">
|
||||||
|
@@ -93,9 +93,20 @@ const Index = () => {
|
|||||||
|
|
||||||
const { totalJobs, totalProfit, totalRevenue, calculateJobRevenue, calculateJobProfit } = useJobMetrics(regularJobs);
|
const { totalJobs, totalProfit, totalRevenue, calculateJobRevenue, calculateJobProfit } = useJobMetrics(regularJobs);
|
||||||
|
|
||||||
const handleCreateJob = async (jobData: IndJobRecordNoId) => {
|
const handleCreateJob = async (jobData: IndJobRecordNoId, billOfMaterials?: { name: string; quantity: number }[]) => {
|
||||||
try {
|
try {
|
||||||
await createJob(jobData);
|
const newJob = await createJob(jobData);
|
||||||
|
|
||||||
|
// If we have bill of materials from the job dump, add them to the newly created job
|
||||||
|
if (billOfMaterials && billOfMaterials.length > 0) {
|
||||||
|
const billItems = billOfMaterials.map(item => ({
|
||||||
|
name: item.name,
|
||||||
|
quantity: item.quantity,
|
||||||
|
unitPrice: 0
|
||||||
|
}));
|
||||||
|
await createMultipleBillItems(newJob.id, billItems, 'billOfMaterials');
|
||||||
|
}
|
||||||
|
|
||||||
setShowJobForm(false);
|
setShowJobForm(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error creating job:', error);
|
console.error('Error creating job:', error);
|
||||||
|
Reference in New Issue
Block a user