Make the rest of the application use "new" services
This commit is contained in:
@@ -5,16 +5,11 @@ import { Badge } from '@/components/ui/badge';
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card';
|
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 } from 'lucide-react';
|
||||||
import { IndJobResponse, IndTransactionResponse, IndBillitemResponse } from '@/lib/pbtypes';
|
|
||||||
import { formatISK } from '@/utils/priceUtils';
|
import { formatISK } from '@/utils/priceUtils';
|
||||||
|
import { IndJob } from '@/lib/types';
|
||||||
|
|
||||||
interface JobCardProps {
|
interface JobCardProps {
|
||||||
job: IndJobResponse & {
|
job: IndJob;
|
||||||
expenditures: IndTransactionResponse[];
|
|
||||||
income: IndTransactionResponse[];
|
|
||||||
billOfMaterials: IndBillitemResponse[];
|
|
||||||
consumedMaterials: { name: string; required: number }[];
|
|
||||||
};
|
|
||||||
onEdit: (job: any) => void;
|
onEdit: (job: any) => void;
|
||||||
onDelete: (jobId: string) => void;
|
onDelete: (jobId: string) => void;
|
||||||
}
|
}
|
||||||
@@ -99,7 +94,7 @@ const JobCard: React.FC<JobCardProps> = ({ job, onEdit, onDelete }) => {
|
|||||||
{job.consumedMaterials.map((item, index) => (
|
{job.consumedMaterials.map((item, index) => (
|
||||||
<div key={index} className="flex justify-between">
|
<div key={index} className="flex justify-between">
|
||||||
<span>{item.name}</span>
|
<span>{item.name}</span>
|
||||||
<span className="text-gray-300">{item.required.toLocaleString()}</span>
|
<span className="text-gray-300">{item.quantity.toLocaleString()}</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,23 +6,21 @@ import { Input } from '@/components/ui/input';
|
|||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
import { IndJobRecord, IndFacilityResponse, IndBillitemRecord, IndJobStatusOptions } from '@/lib/pbtypes';
|
import { IndBillitemRecordNoId, IndJobStatusOptions, IndJobRecordNoId, IndFacilityRecord } from '@/lib/pbtypes';
|
||||||
import { facilityService } from '@/services/facilityService';
|
|
||||||
import MaterialsImportExport from './MaterialsImportExport';
|
import MaterialsImportExport from './MaterialsImportExport';
|
||||||
|
import { IndJob } from '@/lib/types';
|
||||||
|
// import { getFacilities } from '@/services/facilityService';
|
||||||
|
|
||||||
interface JobFormProps {
|
interface JobFormProps {
|
||||||
job?: IndJobRecord & {
|
job?: IndJob;
|
||||||
billOfMaterials?: IndBillitemRecord[];
|
onSubmit: (job: IndJobRecordNoId) => void;
|
||||||
consumedMaterials?: { name: string; required: number }[];
|
|
||||||
};
|
|
||||||
onSubmit: (job: Omit<IndJobRecord, 'id' | 'created' | 'updated'>) => void;
|
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const JobForm: React.FC<JobFormProps> = ({ job, onSubmit, onCancel }) => {
|
const JobForm: React.FC<JobFormProps> = ({ job, onSubmit, onCancel }) => {
|
||||||
const [facilities, setFacilities] = useState<IndFacilityResponse[]>([]);
|
// const [facilities, setFacilities] = useState<IndFacilityRecord[]>([]);
|
||||||
const [billOfMaterials, setBillOfMaterials] = useState<IndBillitemRecord[]>(job?.billOfMaterials || []);
|
const [billOfMaterials, setBillOfMaterials] = useState<IndBillitemRecordNoId[]>(job?.billOfMaterials || []);
|
||||||
const [consumedMaterials, setConsumedMaterials] = useState<{ name: string; required: number }[]>(job?.consumedMaterials || []);
|
const [consumedMaterials, setConsumedMaterials] = useState<IndBillitemRecordNoId[]>(job?.consumedMaterials || []);
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
outputItem: job?.outputItem || '',
|
outputItem: job?.outputItem || '',
|
||||||
outputQuantity: job?.outputQuantity || 0,
|
outputQuantity: job?.outputQuantity || 0,
|
||||||
@@ -37,18 +35,18 @@ const JobForm: React.FC<JobFormProps> = ({ job, onSubmit, onCancel }) => {
|
|||||||
income: job?.income || []
|
income: job?.income || []
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
const loadFacilities = async () => {
|
// const loadFacilities = async () => {
|
||||||
try {
|
// try {
|
||||||
const fetchedFacilities = await facilityService.getFacilities();
|
// const fetchedFacilities = await getFacilities();
|
||||||
setFacilities(fetchedFacilities);
|
// setFacilities(fetchedFacilities);
|
||||||
} catch (error) {
|
// } catch (error) {
|
||||||
console.error('Error loading facilities:', error);
|
// console.error('Error loading facilities:', error);
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
loadFacilities();
|
// loadFacilities();
|
||||||
}, []);
|
// }, []);
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -61,10 +59,10 @@ const JobForm: React.FC<JobFormProps> = ({ job, onSubmit, onCancel }) => {
|
|||||||
saleStart: formData.saleStart ? formData.saleStart : undefined,
|
saleStart: formData.saleStart ? formData.saleStart : undefined,
|
||||||
saleEnd: formData.saleEnd ? formData.saleEnd : undefined,
|
saleEnd: formData.saleEnd ? formData.saleEnd : undefined,
|
||||||
status: formData.status,
|
status: formData.status,
|
||||||
billOfMaterials: formData.billOfMaterials,
|
billOfMaterials: formData.billOfMaterials.map(item => item.id),
|
||||||
consumedMaterials: formData.consumedMaterials,
|
consumedMaterials: formData.consumedMaterials.map(item => item.id),
|
||||||
expenditures: formData.expenditures,
|
expenditures: formData.expenditures.map(item => item.id),
|
||||||
income: formData.income
|
income: formData.income.map(item => item.id)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -211,6 +209,7 @@ const JobForm: React.FC<JobFormProps> = ({ job, onSubmit, onCancel }) => {
|
|||||||
|
|
||||||
<TabsContent value="materials" className="space-y-4">
|
<TabsContent value="materials" className="space-y-4">
|
||||||
<MaterialsImportExport
|
<MaterialsImportExport
|
||||||
|
jobId={job?.id || ''}
|
||||||
billOfMaterials={billOfMaterials}
|
billOfMaterials={billOfMaterials}
|
||||||
consumedMaterials={consumedMaterials}
|
consumedMaterials={consumedMaterials}
|
||||||
onBillOfMaterialsUpdate={setBillOfMaterials}
|
onBillOfMaterialsUpdate={setBillOfMaterials}
|
||||||
|
|||||||
@@ -4,16 +4,19 @@ import { Button } from '@/components/ui/button';
|
|||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { Import, Download, FileText } from 'lucide-react';
|
import { Import, Download, FileText } from 'lucide-react';
|
||||||
import { IndBillitemRecord } from '@/lib/pbtypes';
|
import { IndBillitemRecordNoId } from '@/lib/pbtypes';
|
||||||
|
import { addBillItem } from '@/services/billItemService';
|
||||||
|
|
||||||
interface MaterialsImportExportProps {
|
interface MaterialsImportExportProps {
|
||||||
billOfMaterials: IndBillitemRecord[];
|
jobId: string;
|
||||||
consumedMaterials: { name: string; required: number }[];
|
billOfMaterials: IndBillitemRecordNoId[];
|
||||||
onBillOfMaterialsUpdate: (materials: IndBillitemRecord[]) => void;
|
consumedMaterials: IndBillitemRecordNoId[];
|
||||||
onConsumedMaterialsUpdate: (materials: { name: string; required: number }[]) => void;
|
onBillOfMaterialsUpdate: (materials: IndBillitemRecordNoId[]) => void;
|
||||||
|
onConsumedMaterialsUpdate: (materials: IndBillitemRecordNoId[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MaterialsImportExport: React.FC<MaterialsImportExportProps> = ({
|
const MaterialsImportExport: React.FC<MaterialsImportExportProps> = ({
|
||||||
|
jobId,
|
||||||
billOfMaterials,
|
billOfMaterials,
|
||||||
consumedMaterials,
|
consumedMaterials,
|
||||||
onBillOfMaterialsUpdate,
|
onBillOfMaterialsUpdate,
|
||||||
@@ -22,39 +25,37 @@ const MaterialsImportExport: React.FC<MaterialsImportExportProps> = ({
|
|||||||
const [bomInput, setBomInput] = useState('');
|
const [bomInput, setBomInput] = useState('');
|
||||||
const [consumedInput, setConsumedInput] = useState('');
|
const [consumedInput, setConsumedInput] = useState('');
|
||||||
|
|
||||||
const parseBillOfMaterials = (text: string): IndBillitemRecord[] => {
|
const parseBillOfMaterials = (text: string): IndBillitemRecordNoId[] => {
|
||||||
return text.split('\n')
|
const lines = text.split('\n').filter(line => line.trim());
|
||||||
.filter(line => line.trim())
|
const materials: IndBillitemRecordNoId[] = [];
|
||||||
.map(line => {
|
|
||||||
|
for (const line of lines) {
|
||||||
const parts = line.trim().split(/\s+/);
|
const parts = line.trim().split(/\s+/);
|
||||||
const quantity = parseInt(parts[parts.length - 1]);
|
if (parts.length >= 2) {
|
||||||
const name = parts.slice(0, -1).join(' ');
|
const name = parts.slice(0, -1).join(' ');
|
||||||
return {
|
const quantity = parseInt(parts[parts.length - 1]);
|
||||||
id: '',
|
if (name && !isNaN(quantity)) {
|
||||||
name,
|
materials.push({ name, quantity });
|
||||||
quantity,
|
}
|
||||||
created: undefined,
|
}
|
||||||
updated: undefined
|
}
|
||||||
};
|
return materials;
|
||||||
})
|
|
||||||
.filter(item => item.name && !isNaN(item.quantity));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseConsumedMaterials = (text: string): { name: string; required: number }[] => {
|
const parseConsumedMaterials = (text: string): IndBillitemRecordNoId[] => {
|
||||||
const lines = text.split('\n').filter(line => line.trim());
|
const lines = text.split('\n').filter(line => line.trim());
|
||||||
const materials: { name: string; required: number }[] = [];
|
const materials: IndBillitemRecordNoId[] = [];
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const parts = line.trim().split('\t');
|
const parts = line.trim().split('\t');
|
||||||
if (parts.length >= 2) {
|
if (parts.length >= 2) {
|
||||||
const name = parts[0];
|
const name = parts[0];
|
||||||
const required = parseInt(parts[1]);
|
const quantity = parseInt(parts[1]);
|
||||||
if (name && !isNaN(required)) {
|
if (name && !isNaN(quantity)) {
|
||||||
materials.push({ name, required });
|
materials.push({ name, quantity });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return materials;
|
return materials;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -63,17 +64,17 @@ const MaterialsImportExport: React.FC<MaterialsImportExportProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const exportConsumedMaterials = (): string => {
|
const exportConsumedMaterials = (): string => {
|
||||||
return consumedMaterials.map(item => `${item.name}\t${item.required}`).join('\n');
|
return consumedMaterials.map(item => `${item.name}\t${item.quantity}`).join('\n');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleImportBom = () => {
|
const handleImportBom = async () => {
|
||||||
const parsed = parseBillOfMaterials(bomInput);
|
const parsed = await parseBillOfMaterials(bomInput);
|
||||||
onBillOfMaterialsUpdate(parsed);
|
onBillOfMaterialsUpdate(parsed);
|
||||||
setBomInput('');
|
setBomInput('');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleImportConsumed = () => {
|
const handleImportConsumed = async () => {
|
||||||
const parsed = parseConsumedMaterials(consumedInput);
|
const parsed = await parseConsumedMaterials(consumedInput);
|
||||||
onConsumedMaterialsUpdate(parsed);
|
onConsumedMaterialsUpdate(parsed);
|
||||||
setConsumedInput('');
|
setConsumedInput('');
|
||||||
};
|
};
|
||||||
@@ -171,7 +172,7 @@ const MaterialsImportExport: React.FC<MaterialsImportExportProps> = ({
|
|||||||
<Label className="text-gray-300">Current Consumed Materials:</Label>
|
<Label className="text-gray-300">Current Consumed Materials:</Label>
|
||||||
<div className="text-sm text-gray-400 max-h-32 overflow-y-auto">
|
<div className="text-sm text-gray-400 max-h-32 overflow-y-auto">
|
||||||
{consumedMaterials.map((item, index) => (
|
{consumedMaterials.map((item, index) => (
|
||||||
<div key={index}>{item.name}: {item.required.toLocaleString()}</div>
|
<div key={index}>{item.name}: {item.quantity.toLocaleString()}</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,30 +7,29 @@ import { Badge } from '@/components/ui/badge';
|
|||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
||||||
import { parseTransactionLine, formatISK } from '@/utils/priceUtils';
|
import { parseTransactionLine, formatISK } from '@/utils/priceUtils';
|
||||||
import { IndTransactionRecord } from '@/lib/pbtypes';
|
import { IndTransactionRecord, IndTransactionRecordNoId } from '@/lib/pbtypes';
|
||||||
import { Check, X } from 'lucide-react';
|
import { Check, X } from 'lucide-react';
|
||||||
|
|
||||||
interface TransactionFormProps {
|
interface TransactionFormProps {
|
||||||
jobId: string;
|
jobId: string;
|
||||||
onTransactionsAdded: (transactions: IndTransactionRecord[], type: 'expenditure' | 'income') => void;
|
onTransactionsAdded: (transactions: IndTransactionRecordNoId[], type: 'expenditure' | 'income') => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TransactionForm: React.FC<TransactionFormProps> = ({ jobId, onTransactionsAdded }) => {
|
const TransactionForm: React.FC<TransactionFormProps> = ({ jobId, onTransactionsAdded }) => {
|
||||||
const [pastedData, setPastedData] = useState('');
|
const [pastedData, setPastedData] = useState('');
|
||||||
const [parsedTransactions, setParsedTransactions] = useState<IndTransactionRecord[]>([]);
|
const [parsedTransactions, setParsedTransactions] = useState<IndTransactionRecordNoId[]>([]);
|
||||||
const [transactionType, setTransactionType] = useState<'expenditure' | 'income'>('expenditure');
|
const [transactionType, setTransactionType] = useState<'expenditure' | 'income'>('expenditure');
|
||||||
|
|
||||||
const handlePaste = (value: string) => {
|
const handlePaste = (value: string) => {
|
||||||
setPastedData(value);
|
setPastedData(value);
|
||||||
|
|
||||||
const lines = value.trim().split('\n');
|
const lines = value.trim().split('\n');
|
||||||
const transactions: IndTransactionRecord[] = [];
|
const transactions: IndTransactionRecordNoId[] = [];
|
||||||
|
|
||||||
lines.forEach((line, index) => {
|
lines.forEach((line, index) => {
|
||||||
const parsed = parseTransactionLine(line);
|
const parsed = parseTransactionLine(line);
|
||||||
if (parsed) {
|
if (parsed) {
|
||||||
transactions.push({
|
transactions.push({
|
||||||
id: `temp-${index}`,
|
|
||||||
date: parsed.date.toISOString(),
|
date: parsed.date.toISOString(),
|
||||||
quantity: parsed.quantity,
|
quantity: parsed.quantity,
|
||||||
itemName: parsed.itemName,
|
itemName: parsed.itemName,
|
||||||
@@ -40,9 +39,7 @@ const TransactionForm: React.FC<TransactionFormProps> = ({ jobId, onTransactions
|
|||||||
location: parsed.location,
|
location: parsed.location,
|
||||||
corporation: parsed.corporation,
|
corporation: parsed.corporation,
|
||||||
wallet: parsed.wallet,
|
wallet: parsed.wallet,
|
||||||
job: jobId,
|
job: jobId
|
||||||
created: undefined,
|
|
||||||
updated: undefined
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
|
|
||||||
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';
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { IndTransactionResponse } from '@/lib/pbtypes';
|
import { IndTransactionRecord } from '@/lib/pbtypes';
|
||||||
import { formatISK } from '@/utils/priceUtils';
|
import { formatISK } from '@/utils/priceUtils';
|
||||||
import { Edit, Save, X, Trash2 } from 'lucide-react';
|
import { Edit, Save, X, Trash2 } from 'lucide-react';
|
||||||
|
|
||||||
interface TransactionTableProps {
|
interface TransactionTableProps {
|
||||||
title: string;
|
title: string;
|
||||||
transactions: IndTransactionResponse[];
|
transactions: IndTransactionRecord[];
|
||||||
type: 'expenditure' | 'income';
|
type: 'expenditure' | 'income';
|
||||||
onUpdateTransaction: (transactionId: string, updates: Partial<IndTransactionResponse>) => void;
|
onUpdateTransaction: (transactionId: string, updates: Partial<IndTransactionRecord>) => void;
|
||||||
onDeleteTransaction: (transactionId: string) => void;
|
onDeleteTransaction: (transactionId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,11 +24,11 @@ const TransactionTable: React.FC<TransactionTableProps> = ({
|
|||||||
onDeleteTransaction
|
onDeleteTransaction
|
||||||
}) => {
|
}) => {
|
||||||
const [editingId, setEditingId] = useState<string | null>(null);
|
const [editingId, setEditingId] = useState<string | null>(null);
|
||||||
const [editingTransaction, setEditingTransaction] = useState<IndTransactionResponse | null>(null);
|
const [editingTransaction, setEditingTransaction] = useState<IndTransactionRecord | null>(null);
|
||||||
|
|
||||||
const totalAmount = transactions.reduce((sum, tx) => sum + tx.totalPrice, 0);
|
const totalAmount = transactions.reduce((sum, tx) => sum + tx.totalPrice, 0);
|
||||||
|
|
||||||
const handleEdit = (transaction: IndTransactionResponse) => {
|
const handleEdit = (transaction: IndTransactionRecord) => {
|
||||||
setEditingId(transaction.id);
|
setEditingId(transaction.id);
|
||||||
setEditingTransaction({ ...transaction });
|
setEditingTransaction({ ...transaction });
|
||||||
};
|
};
|
||||||
@@ -53,7 +52,7 @@ const TransactionTable: React.FC<TransactionTableProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateEditingField = (field: keyof IndTransactionResponse, value: any) => {
|
const updateEditingField = (field: keyof IndTransactionRecord, value: any) => {
|
||||||
if (editingTransaction) {
|
if (editingTransaction) {
|
||||||
setEditingTransaction({
|
setEditingTransaction({
|
||||||
...editingTransaction,
|
...editingTransaction,
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ export type IndBillitemRecord = {
|
|||||||
quantity: number
|
quantity: number
|
||||||
updated?: IsoDateString
|
updated?: IsoDateString
|
||||||
}
|
}
|
||||||
|
export type IndBillitemRecordNoId = Omit<IndBillitemRecord, 'id' | 'created' | 'updated'>
|
||||||
|
|
||||||
export type IndFacilityRecord = {
|
export type IndFacilityRecord = {
|
||||||
created?: IsoDateString
|
created?: IsoDateString
|
||||||
@@ -138,6 +139,7 @@ export type IndJobRecord = {
|
|||||||
status: IndJobStatusOptions
|
status: IndJobStatusOptions
|
||||||
updated?: IsoDateString
|
updated?: IsoDateString
|
||||||
}
|
}
|
||||||
|
export type IndJobRecordNoId = Omit<IndJobRecord, 'id' | 'created' | 'updated'>
|
||||||
|
|
||||||
export type IndTransactionRecord = {
|
export type IndTransactionRecord = {
|
||||||
buyer?: string
|
buyer?: string
|
||||||
@@ -154,6 +156,7 @@ export type IndTransactionRecord = {
|
|||||||
updated?: IsoDateString
|
updated?: IsoDateString
|
||||||
wallet?: string
|
wallet?: string
|
||||||
}
|
}
|
||||||
|
export type IndTransactionRecordNoId = Omit<IndTransactionRecord, 'id' | 'created' | 'updated'>
|
||||||
|
|
||||||
export type RegionviewRecord = {
|
export type RegionviewRecord = {
|
||||||
id: string
|
id: string
|
||||||
|
|||||||
@@ -1,28 +1,25 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Plus, Factory, TrendingUp, Briefcase } from 'lucide-react';
|
import { Plus, Factory, TrendingUp, Briefcase } from 'lucide-react';
|
||||||
import { IndJobResponse, IndTransactionResponse, IndJobRecord, IndTransactionRecord } from '@/lib/pbtypes';
|
import { IndTransactionResponse, IndJobRecord, IndTransactionRecord, IndTransactionRecordNoId } from '@/lib/pbtypes';
|
||||||
import * as jobService from '@/services/jobService';
|
import * as jobService from '@/services/jobService';
|
||||||
|
import * as transactionService from '@/services/transactionService';
|
||||||
|
import * as billItemService from '@/services/billItemService';
|
||||||
import { formatISK } from '@/utils/priceUtils';
|
import { formatISK } from '@/utils/priceUtils';
|
||||||
import JobCard from '@/components/JobCard';
|
import JobCard from '@/components/JobCard';
|
||||||
import JobForm from '@/components/JobForm';
|
import JobForm from '@/components/JobForm';
|
||||||
import TransactionForm from '@/components/TransactionForm';
|
import TransactionForm from '@/components/TransactionForm';
|
||||||
import TransactionTable from '@/components/TransactionTable';
|
import TransactionTable from '@/components/TransactionTable';
|
||||||
|
import { IndJob } from '@/lib/types';
|
||||||
|
import { createJob } from '@/services/jobService';
|
||||||
|
|
||||||
// Extended job type for UI components
|
// Extended job type for UI components
|
||||||
interface JobWithRelations extends IndJobResponse {
|
|
||||||
expenditures: IndTransactionResponse[];
|
|
||||||
income: IndTransactionResponse[];
|
|
||||||
billOfMaterials: any[];
|
|
||||||
consumedMaterials: { name: string; required: number }[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const Index = () => {
|
const Index = () => {
|
||||||
const [jobs, setJobs] = useState<JobWithRelations[]>([]);
|
const [jobs, setJobs] = useState<IndJob[]>([]);
|
||||||
const [showJobForm, setShowJobForm] = useState(false);
|
const [showJobForm, setShowJobForm] = useState(false);
|
||||||
const [editingJob, setEditingJob] = useState<JobWithRelations | null>(null);
|
const [editingJob, setEditingJob] = useState<IndJob | null>(null);
|
||||||
const [selectedJob, setSelectedJob] = useState<JobWithRelations | null>(null);
|
const [selectedJob, setSelectedJob] = useState<IndJob | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadJobs();
|
loadJobs();
|
||||||
@@ -30,15 +27,9 @@ const Index = () => {
|
|||||||
|
|
||||||
const loadJobs = async () => {
|
const loadJobs = async () => {
|
||||||
try {
|
try {
|
||||||
const fetchedJobs = await jobService.getJobs();
|
const fetchedJobs = await jobService.getJobsFull();
|
||||||
// Convert to JobWithRelations format
|
// Convert to JobWithRelations format
|
||||||
const jobsWithRelations: JobWithRelations[] = fetchedJobs.map(job => ({
|
const jobsWithRelations: IndJob[] = fetchedJobs;
|
||||||
...job,
|
|
||||||
expenditures: [],
|
|
||||||
income: [],
|
|
||||||
billOfMaterials: [],
|
|
||||||
consumedMaterials: []
|
|
||||||
}));
|
|
||||||
setJobs(jobsWithRelations);
|
setJobs(jobsWithRelations);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading jobs:', error);
|
console.error('Error loading jobs:', error);
|
||||||
@@ -47,8 +38,8 @@ const Index = () => {
|
|||||||
|
|
||||||
const handleCreateJob = async (jobData: Omit<IndJobRecord, 'id' | 'created' | 'updated'>) => {
|
const handleCreateJob = async (jobData: Omit<IndJobRecord, 'id' | 'created' | 'updated'>) => {
|
||||||
try {
|
try {
|
||||||
const newJob = await jobService.createJob(jobData);
|
const newJob = await createJob(jobData);
|
||||||
const jobWithRelations: JobWithRelations = {
|
const jobWithRelations: IndJob = {
|
||||||
...newJob,
|
...newJob,
|
||||||
expenditures: [],
|
expenditures: [],
|
||||||
income: [],
|
income: [],
|
||||||
@@ -62,7 +53,7 @@ const Index = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEditJob = (job: JobWithRelations) => {
|
const handleEditJob = (job: IndJob) => {
|
||||||
setEditingJob(job);
|
setEditingJob(job);
|
||||||
setShowJobForm(true);
|
setShowJobForm(true);
|
||||||
};
|
};
|
||||||
@@ -72,7 +63,7 @@ const Index = () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const updatedJob = await jobService.updateJob(editingJob.id, jobData);
|
const updatedJob = await jobService.updateJob(editingJob.id, jobData);
|
||||||
const updatedJobWithRelations: JobWithRelations = {
|
const updatedJobWithRelations: IndJob = {
|
||||||
...updatedJob,
|
...updatedJob,
|
||||||
expenditures: editingJob.expenditures,
|
expenditures: editingJob.expenditures,
|
||||||
income: editingJob.income,
|
income: editingJob.income,
|
||||||
@@ -106,24 +97,16 @@ const Index = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTransactionsAdded = async (transactions: IndTransactionRecord[], type: 'expenditure' | 'income') => {
|
const handleTransactionsAdded = async (transactions: IndTransactionRecordNoId[], type: 'expenditure' | 'income') => {
|
||||||
if (!selectedJob) return;
|
if (!selectedJob) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
let updatedJob = selectedJob;
|
||||||
for (const transaction of transactions) {
|
for (const transaction of transactions) {
|
||||||
await jobService.addTransaction(selectedJob.id, transaction, type);
|
updatedJob = await transactionService.createTransaction(updatedJob, transaction, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update local state
|
// Update local state
|
||||||
const updatedJob = { ...selectedJob };
|
|
||||||
const newTransactions = transactions as unknown as IndTransactionResponse[];
|
|
||||||
|
|
||||||
if (type === 'expenditure') {
|
|
||||||
updatedJob.expenditures = [...updatedJob.expenditures, ...newTransactions];
|
|
||||||
} else {
|
|
||||||
updatedJob.income = [...updatedJob.income, ...newTransactions];
|
|
||||||
}
|
|
||||||
|
|
||||||
setSelectedJob(updatedJob);
|
setSelectedJob(updatedJob);
|
||||||
setJobs(jobs.map(job => job.id === selectedJob.id ? updatedJob : job));
|
setJobs(jobs.map(job => job.id === selectedJob.id ? updatedJob : job));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -135,17 +118,9 @@ const Index = () => {
|
|||||||
if (!selectedJob) return;
|
if (!selectedJob) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await jobService.updateTransaction(selectedJob.id, transactionId, updates);
|
let updatedJob = selectedJob;
|
||||||
|
updatedJob = await transactionService.updateTransaction(updatedJob, transactionId, updates);
|
||||||
// Update local state
|
// Update local state
|
||||||
const updatedJob = { ...selectedJob };
|
|
||||||
updatedJob.expenditures = updatedJob.expenditures.map(tx =>
|
|
||||||
tx.id === transactionId ? { ...tx, ...updates } : tx
|
|
||||||
);
|
|
||||||
updatedJob.income = updatedJob.income.map(tx =>
|
|
||||||
tx.id === transactionId ? { ...tx, ...updates } : tx
|
|
||||||
);
|
|
||||||
|
|
||||||
setSelectedJob(updatedJob);
|
setSelectedJob(updatedJob);
|
||||||
setJobs(jobs.map(job => job.id === selectedJob.id ? updatedJob : job));
|
setJobs(jobs.map(job => job.id === selectedJob.id ? updatedJob : job));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -157,7 +132,7 @@ const Index = () => {
|
|||||||
if (!selectedJob) return;
|
if (!selectedJob) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await jobService.deleteTransaction(selectedJob.id, transactionId);
|
await transactionService.deleteTransaction(selectedJob, transactionId);
|
||||||
|
|
||||||
// Update local state
|
// Update local state
|
||||||
const updatedJob = { ...selectedJob };
|
const updatedJob = { ...selectedJob };
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export async function createTransaction(
|
|||||||
job: IndJob,
|
job: IndJob,
|
||||||
transaction: IndTransactionRecordNoId,
|
transaction: IndTransactionRecordNoId,
|
||||||
type: 'expenditure' | 'income'
|
type: 'expenditure' | 'income'
|
||||||
): Promise<void> {
|
): Promise<IndJob> {
|
||||||
console.log('Creating transaction:', transaction);
|
console.log('Creating transaction:', transaction);
|
||||||
// Create the transaction
|
// Create the transaction
|
||||||
transaction.job = job.id;
|
transaction.job = job.id;
|
||||||
@@ -15,19 +15,30 @@ export async function createTransaction(
|
|||||||
|
|
||||||
// Update the job to include the new transaction
|
// Update the job to include the new transaction
|
||||||
const field = type === 'expenditure' ? 'expenditures' : 'income';
|
const field = type === 'expenditure' ? 'expenditures' : 'income';
|
||||||
const currentIds = job[field] || [];
|
const currentIds = (job[field] || []).map(tr => tr.id);
|
||||||
|
|
||||||
await updateJob(job.id, {
|
await updateJob(job.id, {
|
||||||
[field]: [...currentIds, createdTransaction.id]
|
[field]: [...currentIds, createdTransaction.id]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (type === 'expenditure') {
|
||||||
|
job.expenditures.push(createdTransaction);
|
||||||
|
} else {
|
||||||
|
job.income.push(createdTransaction);
|
||||||
|
}
|
||||||
|
return job;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateTransaction(
|
export async function updateTransaction(
|
||||||
|
job: IndJob,
|
||||||
transactionId: string,
|
transactionId: string,
|
||||||
updates: Partial<IndTransactionRecord>
|
updates: Partial<IndTransactionRecord>
|
||||||
): Promise<void> {
|
): Promise<IndJob> {
|
||||||
console.log('Updating transaction:', transactionId, updates);
|
console.log('Updating transaction:', transactionId, updates);
|
||||||
await pb.collection<IndTransactionRecord>('ind_transaction').update(transactionId, updates);
|
const updatedTransaction = await pb.collection<IndTransactionRecord>('ind_transaction').update(transactionId, updates);
|
||||||
|
job.expenditures = job.expenditures.map(exp => exp.id === transactionId ? updatedTransaction : exp);
|
||||||
|
job.income = job.income.map(inc => inc.id === transactionId ? updatedTransaction : inc);
|
||||||
|
return job;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteTransaction(job: IndJob, transactionId: string): Promise<void> {
|
export async function deleteTransaction(job: IndJob, transactionId: string): Promise<void> {
|
||||||
|
|||||||
Reference in New Issue
Block a user