[dyad] Added a QR scanner to the 'Book Out Stock' form - wrote 1 file(s)
This commit is contained in:
parent
98ad0d5b7b
commit
26cd13764e
1 changed files with 115 additions and 88 deletions
|
|
@ -9,12 +9,13 @@ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "
|
|||
import { Input } from "../ui/input"
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "../ui/card"
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../ui/table"
|
||||
import { Trash2 } from "lucide-react"
|
||||
import { Trash2, Camera } from "lucide-react"
|
||||
import { showSuccess, showError } from "../../utils/toast"
|
||||
import { CustomerCombobox } from "./CustomerCombobox"
|
||||
import { StockItemSearch } from "./StockItemSearch"
|
||||
import { Customer } from "../customers/columns"
|
||||
import { StockItem } from "../stock/columns"
|
||||
import QRScannerDialog from "../stock/QRScannerDialog"
|
||||
|
||||
// Sample data - in a real app, this would come from an API
|
||||
const customersData: Customer[] = [
|
||||
|
|
@ -53,6 +54,7 @@ const formSchema = z.object({
|
|||
export function BookOutForm() {
|
||||
const [customers, setCustomers] = React.useState(customersData.map(c => ({ value: c.id, label: c.companyName })));
|
||||
const [stockItems, setStockItems] = React.useState(stockData);
|
||||
const [scannerOpen, setScannerOpen] = React.useState(false);
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
|
|
@ -85,6 +87,16 @@ export function BookOutForm() {
|
|||
});
|
||||
};
|
||||
|
||||
const handleScanSuccess = (decodedText: string) => {
|
||||
const item = stockItems.find(i => i.sku.toLowerCase() === decodedText.toLowerCase());
|
||||
if (item) {
|
||||
handleAddItem(item);
|
||||
} else {
|
||||
showError("Scanned item not found in stock.");
|
||||
}
|
||||
setScannerOpen(false);
|
||||
};
|
||||
|
||||
function onSubmit(values: z.infer<typeof formSchema>) {
|
||||
console.log(values)
|
||||
showSuccess("Stock booked out successfully!")
|
||||
|
|
@ -92,98 +104,113 @@ export function BookOutForm() {
|
|||
}
|
||||
|
||||
return (
|
||||
<Card className="max-w-4xl mx-auto">
|
||||
<CardHeader>
|
||||
<CardTitle>Book Out Stock</CardTitle>
|
||||
<CardDescription>Select a customer and add items to book out from your inventory.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="customerId"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Customer</FormLabel>
|
||||
<CustomerCombobox
|
||||
customers={customers}
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormItem>
|
||||
<FormLabel>Add Stock Item</FormLabel>
|
||||
<StockItemSearch
|
||||
stockItems={stockItems}
|
||||
onItemSelect={handleAddItem}
|
||||
<>
|
||||
<QRScannerDialog
|
||||
open={scannerOpen}
|
||||
onOpenChange={setScannerOpen}
|
||||
onScanSuccess={handleScanSuccess}
|
||||
/>
|
||||
<Card className="max-w-4xl mx-auto">
|
||||
<CardHeader>
|
||||
<CardTitle>Book Out Stock</CardTitle>
|
||||
<CardDescription>Select a customer and add items to book out from your inventory.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="customerId"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Customer</FormLabel>
|
||||
<CustomerCombobox
|
||||
customers={customers}
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
<FormItem>
|
||||
<FormLabel>Add Stock Item</FormLabel>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex-grow">
|
||||
<StockItemSearch
|
||||
stockItems={stockItems}
|
||||
onItemSelect={handleAddItem}
|
||||
/>
|
||||
</div>
|
||||
<Button variant="outline" size="icon" type="button" onClick={() => setScannerOpen(true)}>
|
||||
<Camera className="h-4 w-4" />
|
||||
<span className="sr-only">Scan Barcode</span>
|
||||
</Button>
|
||||
</div>
|
||||
</FormItem>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-sm font-medium">Selected Items</h3>
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Item</TableHead>
|
||||
<TableHead className="w-[120px] text-center">Available</TableHead>
|
||||
<TableHead className="w-[120px]">Quantity</TableHead>
|
||||
<TableHead className="w-[50px]"></TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{fields.length > 0 ? (
|
||||
fields.map((field, index) => (
|
||||
<TableRow key={field.id}>
|
||||
<TableCell>
|
||||
<p className="font-medium">{field.name}</p>
|
||||
<p className="text-sm text-muted-foreground">SKU: {field.sku}</p>
|
||||
</TableCell>
|
||||
<TableCell className="text-center">{field.maxQuantity}</TableCell>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`items.${index}.quantity`}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input type="number" {...field} className="text-center" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Button variant="ghost" size="icon" onClick={() => remove(index)}>
|
||||
<Trash2 className="h-4 w-4 text-red-500" />
|
||||
</Button>
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-sm font-medium">Selected Items</h3>
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Item</TableHead>
|
||||
<TableHead className="w-[120px] text-center">Available</TableHead>
|
||||
<TableHead className="w-[120px]">Quantity</TableHead>
|
||||
<TableHead className="w-[50px]"></TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{fields.length > 0 ? (
|
||||
fields.map((field, index) => (
|
||||
<TableRow key={field.id}>
|
||||
<TableCell>
|
||||
<p className="font-medium">{field.name}</p>
|
||||
<p className="text-sm text-muted-foreground">SKU: {field.sku}</p>
|
||||
</TableCell>
|
||||
<TableCell className="text-center">{field.maxQuantity}</TableCell>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`items.${index}.quantity`}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input type="number" {...field} className="text-center" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Button variant="ghost" size="icon" onClick={() => remove(index)}>
|
||||
<Trash2 className="h-4 w-4 text-red-500" />
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={4} className="text-center h-24">
|
||||
No items added yet.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={4} className="text-center h-24">
|
||||
No items added yet.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<FormMessage>{form.formState.errors.items?.message || form.formState.errors.items?.root?.message}</FormMessage>
|
||||
</div>
|
||||
<FormMessage>{form.formState.errors.items?.message || form.formState.errors.items?.root?.message}</FormMessage>
|
||||
</div>
|
||||
|
||||
<Button type="submit" className="w-full" disabled={fields.length === 0}>Book Out Stock</Button>
|
||||
</form>
|
||||
</Form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Button type="submit" className="w-full" disabled={fields.length === 0}>Book Out Stock</Button>
|
||||
</form>
|
||||
</Form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
Loading…
Reference in a new issue