Sync
This commit is contained in:
3
copy.sh
Normal file
3
copy.sh
Normal file
@@ -0,0 +1,3 @@
|
||||
cd ..
|
||||
cp -a industrializer/src/* industrializer-wails/frontend/src/
|
||||
cd industrializer-wails
|
@@ -11,9 +11,11 @@
|
||||
will-change: filter;
|
||||
transition: filter 300ms;
|
||||
}
|
||||
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
}
|
||||
|
||||
.logo.react:hover {
|
||||
filter: drop-shadow(0 0 2em #61dafbaa);
|
||||
}
|
||||
@@ -22,6 +24,7 @@
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
|
@@ -95,23 +95,34 @@ const JobCard: React.FC<JobCardProps> = ({
|
||||
};
|
||||
|
||||
const importBillOfMaterials = async () => {
|
||||
if (!onImportBOM) {
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "Import functionality is not available",
|
||||
variant: "destructive",
|
||||
duration: 2000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const clipboardText = await navigator.clipboard.readText();
|
||||
const lines = clipboardText.split('\n').filter(line => line.trim());
|
||||
const items: { name: string; quantity: number }[] = [];
|
||||
|
||||
for (const line of lines) {
|
||||
const parts = line.trim().split(/\s+/);
|
||||
const parts = line.trim().split(/[\s\t]+/);
|
||||
if (parts.length >= 2) {
|
||||
const name = parts.slice(0, -1).join(' ');
|
||||
const quantity = parseInt(parts[parts.length - 1]);
|
||||
const quantityPart = parts[parts.length - 1].replace(/,/g, '');
|
||||
const quantity = parseInt(quantityPart);
|
||||
if (name && !isNaN(quantity)) {
|
||||
items.push({ name, quantity });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (items.length > 0 && onImportBOM) {
|
||||
if (items.length > 0) {
|
||||
onImportBOM(job.id, items);
|
||||
toast({
|
||||
title: "BOM Imported",
|
||||
@@ -214,31 +225,6 @@ const JobCard: React.FC<JobCardProps> = ({
|
||||
<Badge className={`${getStatusColor(job.status)} text-white flex-shrink-0`}>
|
||||
{job.status}
|
||||
</Badge>
|
||||
<div className="flex gap-1 flex-shrink-0">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="p-1 h-6 w-6"
|
||||
onClick={handleImportClick}
|
||||
title="Import BOM from clipboard"
|
||||
>
|
||||
<Import className="w-4 h-4 text-blue-400" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="p-1 h-6 w-6"
|
||||
onClick={handleExportClick}
|
||||
disabled={!job.billOfMaterials?.length}
|
||||
title="Export BOM to clipboard"
|
||||
>
|
||||
{copyingBom ? (
|
||||
<Check className="w-4 h-4 text-green-400" />
|
||||
) : (
|
||||
<Upload className="w-4 h-4 text-blue-400" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-gray-400 text-sm">
|
||||
Quantity: {job.outputQuantity.toLocaleString()}
|
||||
@@ -272,7 +258,8 @@ const JobCard: React.FC<JobCardProps> = ({
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-2 flex-shrink-0">
|
||||
<div className="flex flex-col gap-2 flex-shrink-0">
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
@@ -289,6 +276,32 @@ const JobCard: React.FC<JobCardProps> = ({
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex gap-1 justify-end">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="p-1 h-6 w-6"
|
||||
onClick={handleImportClick}
|
||||
title="Import BOM from clipboard"
|
||||
>
|
||||
<Import className="w-4 h-4 text-blue-400" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="p-1 h-6 w-6"
|
||||
onClick={handleExportClick}
|
||||
disabled={!job.billOfMaterials?.length}
|
||||
title="Export BOM to clipboard"
|
||||
>
|
||||
{copyingBom ? (
|
||||
<Check className="w-4 h-4 text-green-400" />
|
||||
) : (
|
||||
<Upload className="w-4 h-4 text-blue-400" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="flex-1 flex flex-col space-y-4">
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -17,8 +16,21 @@ interface JobFormProps {
|
||||
|
||||
const formatDateForInput = (dateString: string | undefined | null): string => {
|
||||
if (!dateString) return '';
|
||||
// Convert ISO string to datetime-local format (YYYY-MM-DDTHH:MM)
|
||||
return new Date(dateString).toISOString().slice(0, 16);
|
||||
|
||||
// Create a date object in local timezone
|
||||
const date = new Date(dateString);
|
||||
|
||||
// Format to YYYY-MM-DD
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
|
||||
// Format to HH:MM
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
|
||||
// Combine into format required by datetime-local (YYYY-MM-DDTHH:MM)
|
||||
return `${year}-${month}-${day}T${hours}:${minutes}`;
|
||||
};
|
||||
|
||||
const JobForm: React.FC<JobFormProps> = ({ job, onSubmit, onCancel }) => {
|
||||
|
@@ -7,7 +7,7 @@ import { Badge } from '@/components/ui/badge';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
|
||||
import { parseTransactionLine, formatISK } from '@/utils/priceUtils';
|
||||
import { IndTransactionRecord, IndTransactionRecordNoId } from '@/lib/pbtypes';
|
||||
import { IndTransactionRecordNoId } from '@/lib/pbtypes';
|
||||
import { Check, X } from 'lucide-react';
|
||||
|
||||
interface TransactionFormProps {
|
||||
|
@@ -25,7 +25,7 @@ const badgeVariants = cva(
|
||||
|
||||
export interface BadgeProps
|
||||
extends React.HTMLAttributes<HTMLDivElement>,
|
||||
VariantProps<typeof badgeVariants> {}
|
||||
VariantProps<typeof badgeVariants> { }
|
||||
|
||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
return (
|
||||
|
@@ -21,7 +21,7 @@ const Command = React.forwardRef<
|
||||
))
|
||||
Command.displayName = CommandPrimitive.displayName
|
||||
|
||||
interface CommandDialogProps extends DialogProps {}
|
||||
interface CommandDialogProps extends DialogProps { }
|
||||
|
||||
const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
|
||||
return (
|
||||
|
@@ -128,4 +128,3 @@ export {
|
||||
Sheet, SheetClose,
|
||||
SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger
|
||||
}
|
||||
|
||||
|
@@ -3,7 +3,7 @@ import * as React from "react"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
export interface TextareaProps
|
||||
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
||||
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> { }
|
||||
|
||||
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
||||
({ className, ...props }, ref) => {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import App from './App.tsx'
|
||||
import './index.css'
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import App from './App.tsx';
|
||||
import './index.css';
|
||||
|
||||
createRoot(document.getElementById("root")!).render(<App />);
|
||||
|
@@ -1,4 +1,3 @@
|
||||
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import JobCard from '@/components/JobCard';
|
||||
@@ -21,7 +20,8 @@ const JobDetails = () => {
|
||||
updateTransaction,
|
||||
deleteTransaction,
|
||||
updateJob,
|
||||
deleteJob
|
||||
deleteJob,
|
||||
createMultipleBillItems
|
||||
} = useJobs();
|
||||
|
||||
const job = useJob(jobId || null);
|
||||
@@ -99,6 +99,19 @@ const JobDetails = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleImportBOM = async (jobId: string, items: { name: string; quantity: number }[]) => {
|
||||
try {
|
||||
const billItems = items.map(item => ({
|
||||
name: item.name,
|
||||
quantity: item.quantity,
|
||||
unitPrice: 0
|
||||
}));
|
||||
await createMultipleBillItems(jobId, billItems, 'billOfMaterials');
|
||||
} catch (error) {
|
||||
console.error('Error importing BOM:', error);
|
||||
}
|
||||
};
|
||||
|
||||
if (showJobForm) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-950 p-6">
|
||||
@@ -139,6 +152,7 @@ const JobDetails = () => {
|
||||
onEdit={handleEditJob}
|
||||
onDelete={handleDeleteJob}
|
||||
onUpdateProduced={handleUpdateProduced}
|
||||
onImportBOM={handleImportBOM}
|
||||
/>
|
||||
<TransactionForm
|
||||
jobId={job.id}
|
||||
|
@@ -6,5 +6,21 @@ export async function addBillItem(
|
||||
billItem: IndBillitemRecordNoId
|
||||
): Promise<IndBillitemRecord> {
|
||||
console.log('Adding bill item:', billItem);
|
||||
return await pb.collection<IndBillitemRecord>('ind_billItem').create(billItem);
|
||||
// Set the job ID in the bill item record
|
||||
const billItemWithJob = {
|
||||
...billItem,
|
||||
job: jobId
|
||||
};
|
||||
return await pb.collection<IndBillitemRecord>('ind_billItem').create(billItemWithJob);
|
||||
}
|
||||
|
||||
export async function deleteBillItem(id: string): Promise<void> {
|
||||
console.log('Deleting bill item:', id);
|
||||
await pb.collection('ind_billItem').delete(id);
|
||||
}
|
||||
|
||||
export async function deleteBillItems(ids: string[]): Promise<void> {
|
||||
console.log('Deleting bill items:', ids);
|
||||
// Delete items in parallel for better performance
|
||||
await Promise.all(ids.map(id => deleteBillItem(id)));
|
||||
}
|
||||
|
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
import { IndJob } from '@/lib/types';
|
||||
import { IndJobRecord, IndJobRecordNoId, IndTransactionRecord, IndTransactionRecordNoId, IndBillitemRecord, IndBillitemRecordNoId } from '@/lib/pbtypes';
|
||||
import * as jobService from './jobService';
|
||||
@@ -219,6 +217,12 @@ export class DataService {
|
||||
const job = this.getJob(jobId);
|
||||
if (!job) throw new Error(`Job with id ${jobId} not found`);
|
||||
|
||||
// Delete existing bill items
|
||||
const existingItemIds = job[type].map(item => item.id);
|
||||
if (existingItemIds.length > 0) {
|
||||
await billItemService.deleteBillItems(existingItemIds);
|
||||
}
|
||||
|
||||
const createdBillItems: IndBillitemRecord[] = [];
|
||||
|
||||
// Create all bill items
|
||||
@@ -227,17 +231,16 @@ export class DataService {
|
||||
createdBillItems.push(createdBillItem);
|
||||
}
|
||||
|
||||
// Update the job's bill item references in one database call
|
||||
const currentIds = (job[type] || []).map(item => item.id);
|
||||
// Update the job's bill item references with ONLY the new IDs
|
||||
const newIds = createdBillItems.map(item => item.id);
|
||||
await jobService.updateJob(jobId, {
|
||||
[type]: [...currentIds, ...newIds]
|
||||
[type]: newIds // Replace instead of append
|
||||
});
|
||||
|
||||
// Update local state
|
||||
const jobIndex = this.jobs.findIndex(j => j.id === jobId);
|
||||
if (jobIndex !== -1) {
|
||||
this.jobs[jobIndex][type].push(...createdBillItems);
|
||||
this.jobs[jobIndex][type] = createdBillItems; // Replace instead of append
|
||||
this.notifyListeners();
|
||||
return this.jobs[jobIndex];
|
||||
}
|
||||
|
Reference in New Issue
Block a user