diff --git a/frontend/src/components/JobCard.tsx b/frontend/src/components/JobCard.tsx index 87def9f..569beb6 100644 --- a/frontend/src/components/JobCard.tsx +++ b/frontend/src/components/JobCard.tsx @@ -3,10 +3,11 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card'; -import { Calendar, Factory, TrendingUp, TrendingDown, Clock, Package, Wrench } from 'lucide-react'; +import { Calendar, Factory, TrendingUp, TrendingDown, Clock, Package, Wrench, Check } from 'lucide-react'; import { formatISK } from '@/utils/priceUtils'; import { IndJob } from '@/lib/types'; import { Input } from '@/components/ui/input'; +import { useToast } from '@/components/ui/use-toast'; interface JobCardProps { job: IndJob; @@ -19,6 +20,9 @@ interface JobCardProps { const JobCard: React.FC = ({ job, onEdit, onDelete, onUpdateProduced, isTracked = false }) => { const [isEditingProduced, setIsEditingProduced] = useState(false); const [producedValue, setProducedValue] = useState(job.produced?.toString() || '0'); + const [copyingBom, setCopyingBom] = useState(false); + const [copyingConsumed, setCopyingConsumed] = useState(false); + const { toast } = useToast(); // Sort transactions by date descending const sortedExpenditures = [...job.expenditures].sort((a, b) => @@ -83,6 +87,58 @@ const JobCard: React.FC = ({ job, onEdit, onDelete, onUpdateProduc } }; + const copyBillOfMaterials = async () => { + if (!job.billOfMaterials?.length) return; + + const text = job.billOfMaterials + .map(item => `${item.name}\t${item.quantity.toLocaleString()}`) + .join('\n'); + + try { + await navigator.clipboard.writeText(text); + setCopyingBom(true); + toast({ + title: "Copied!", + description: "Bill of materials copied to clipboard", + duration: 2000, + }); + setTimeout(() => setCopyingBom(false), 1000); + } catch (err) { + toast({ + title: "Error", + description: "Failed to copy to clipboard", + variant: "destructive", + duration: 2000, + }); + } + }; + + const copyConsumedMaterials = async () => { + if (!job.consumedMaterials?.length) return; + + const text = job.consumedMaterials + .map(item => `${item.name}\t${item.quantity.toLocaleString()}`) + .join('\n'); + + try { + await navigator.clipboard.writeText(text); + setCopyingConsumed(true); + toast({ + title: "Copied!", + description: "Consumed materials copied to clipboard", + duration: 2000, + }); + setTimeout(() => setCopyingConsumed(false), 1000); + } catch (err) { + toast({ + title: "Error", + description: "Failed to copy to clipboard", + variant: "destructive", + duration: 2000, + }); + } + }; + return ( @@ -96,13 +152,26 @@ const JobCard: React.FC = ({ job, onEdit, onDelete, onUpdateProduc {job.billOfMaterials && job.billOfMaterials.length > 0 && ( -
-

Bill of Materials

+

Bill of Materials (click to copy)

{job.billOfMaterials.map((item, index) => (
@@ -118,13 +187,26 @@ const JobCard: React.FC = ({ job, onEdit, onDelete, onUpdateProduc {job.consumedMaterials && job.consumedMaterials.length > 0 && ( -
-

Consumed Materials

+

Consumed Materials (click to copy)

{job.consumedMaterials.map((item, index) => (