Reverted all changes back to version e3a69e9f56109321d262e842e4b72b0729fbc662
This commit is contained in:
parent
6ce9fddb30
commit
4ea97b66a6
4 changed files with 167 additions and 383 deletions
|
|
@ -4,8 +4,6 @@ import * as React from "react"
|
|||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useForm } from "react-hook-form"
|
||||
import * as z from "zod"
|
||||
import { useState, useEffect, useCallback } from "react"
|
||||
// Removed unused import: import { useRouter } from "next/navigation"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
|
|
@ -17,190 +15,110 @@ import {
|
|||
FormMessage,
|
||||
} from "@/components/ui/form"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "@/components/ui/command"
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover"
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog"
|
||||
import { toast } from "@/components/ui/use-toast" // Assuming you use shadcn/ui toast
|
||||
import { showSuccess, showError } from "@/utils/toast"
|
||||
import { CustomerCombobox } from "./CustomerCombobox"
|
||||
import { StockItemCombobox } from "./StockItemCombobox" // Import the new combobox
|
||||
import pb from "@/lib/pocketbase"
|
||||
import { StockItemCombobox } from "@/components/shared/StockItemCombobox" // Corrected import path
|
||||
|
||||
// Define the schema for a single item in the book-out form
|
||||
const bookOutItemSchema = z.object({
|
||||
stockItemId: z.string().min(1, { message: "Stock item ID is required." }),
|
||||
itemName: z.string().min(1, { message: "Item name is required." }),
|
||||
quantity: z.number().int().positive(),
|
||||
sku: z.string(),
|
||||
costPrice: z.number(), // Added costPrice to the schema
|
||||
})
|
||||
|
||||
// Define the type for the items array
|
||||
type BookOutItemSchemaType = z.infer<typeof bookOutItemSchema>;
|
||||
|
||||
// Define the schema for the book-out form
|
||||
const bookOutFormSchema = z.object({
|
||||
bookOutDate: z.date({ required_error: "Book-out date is required." }),
|
||||
items: z.array(bookOutItemSchema).min(1, { message: "At least one item is required." }),
|
||||
const formSchema = z.object({
|
||||
customer: z.string({ required_error: "Please select a customer." }),
|
||||
stockItemId: z.string({ required_error: "Please select an item." }), // Changed from 'item' to 'stockItemId'
|
||||
quantity: z.coerce.number().int().positive({ message: "Quantity must be a positive number." }),
|
||||
})
|
||||
|
||||
interface BookOutFormProps {
|
||||
setOpen: (open: boolean) => void
|
||||
refreshData: () => void
|
||||
}
|
||||
|
||||
// Define the type for a stock item
|
||||
type StockItem = {
|
||||
id: string
|
||||
name: string
|
||||
sku: string
|
||||
costPrice: number
|
||||
setOpen: (open: boolean) => void;
|
||||
refreshData: () => void; // Added refreshData prop
|
||||
}
|
||||
|
||||
export function BookOutForm({ setOpen, refreshData }: BookOutFormProps) {
|
||||
const [stockItems, setStockItems] = useState<StockItem[]>([]) // State to hold stock items
|
||||
const [customerOptions, setCustomerOptions] = React.useState<{ value: string; label: string }[]>([]);
|
||||
const [stockItemOptions, setStockItemOptions] = React.useState<{ value: string; label: string; quantity: number }[]>([]);
|
||||
|
||||
// Fetch stock items when the component mounts
|
||||
const fetchStockItems = useCallback(async () => {
|
||||
const fetchCustomers = React.useCallback(async () => {
|
||||
try {
|
||||
const records = await pb.collection('stock_items').getList(1, 50, {
|
||||
sort: '-created',
|
||||
});
|
||||
const formattedStockItems: StockItem[] = records.items.map((item: any) => ({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
sku: item.sku,
|
||||
costPrice: item.cost_price, // Assuming cost_price is the field in stock_items
|
||||
const records = await pb.collection('customers').getList(1, 50);
|
||||
const options = records.items.map((customer: any) => ({
|
||||
value: customer.id,
|
||||
label: customer.comapny_name,
|
||||
}));
|
||||
setStockItems(formattedStockItems);
|
||||
setCustomerOptions(options);
|
||||
} catch (error) {
|
||||
console.error("Error fetching stock items:", error);
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "Failed to load stock items.",
|
||||
variant: "destructive",
|
||||
});
|
||||
console.error("Error fetching customers for combobox:", error);
|
||||
showError("Failed to load customers.");
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchStockItems();
|
||||
}, [fetchStockItems]);
|
||||
const fetchStockItems = React.useCallback(async () => {
|
||||
try {
|
||||
const records = await pb.collection('stock_items').getList(1, 50, {
|
||||
filter: 'quantity > 0', // Only show items with quantity > 0
|
||||
});
|
||||
const options = records.items.map((item: any) => ({
|
||||
value: item.id,
|
||||
label: item.name,
|
||||
quantity: item.quantity,
|
||||
}));
|
||||
setStockItemOptions(options);
|
||||
} catch (error) {
|
||||
console.error("Error fetching stock items for combobox:", error);
|
||||
showError("Failed to load stock items.");
|
||||
}
|
||||
}, []);
|
||||
|
||||
const form = useForm<z.infer<typeof bookOutFormSchema>>({
|
||||
resolver: zodResolver(bookOutFormSchema),
|
||||
React.useEffect(() => {
|
||||
fetchCustomers();
|
||||
fetchStockItems();
|
||||
}, [fetchCustomers, fetchStockItems]);
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
bookOutDate: new Date(),
|
||||
items: [{
|
||||
stockItemId: "",
|
||||
itemName: "",
|
||||
quantity: 1,
|
||||
sku: "",
|
||||
costPrice: 0, // Included costPrice in default values
|
||||
}],
|
||||
quantity: 1,
|
||||
},
|
||||
})
|
||||
|
||||
// Function to add a new item row
|
||||
const addItem = () => {
|
||||
const currentItems = form.getValues("items");
|
||||
form.setValue("items", [...currentItems, {
|
||||
stockItemId: "",
|
||||
itemName: "",
|
||||
quantity: 1,
|
||||
sku: "",
|
||||
costPrice: 0, // Included costPrice in new item
|
||||
}]);
|
||||
}
|
||||
|
||||
// Function to remove an item row
|
||||
const removeItem = (index: number) => {
|
||||
const currentItems = form.getValues("items");
|
||||
const newItems = currentItems.filter((_, i) => i !== index);
|
||||
form.setValue("items", newItems);
|
||||
}
|
||||
|
||||
// Function to handle changes in item fields
|
||||
const handleItemChange = (index: number, field: keyof BookOutItemSchemaType, value: any) => {
|
||||
const currentItems = form.getValues("items");
|
||||
const newItems = [...currentItems];
|
||||
const currentItem = newItems[index] as BookOutItemSchemaType;
|
||||
|
||||
if (field === "quantity") {
|
||||
currentItem[field] = parseInt(value, 10) || 0;
|
||||
} else if (field === "costPrice") { // Handle costPrice change
|
||||
currentItem[field] = parseFloat(value) || 0;
|
||||
}
|
||||
else {
|
||||
currentItem[field] = value;
|
||||
}
|
||||
newItems[index] = currentItem;
|
||||
form.setValue("items", newItems);
|
||||
}
|
||||
|
||||
// Function to handle stock item selection from dropdown
|
||||
const handleStockItemSelect = (index: number, selectedItemName: string) => {
|
||||
const selectedStockItem = stockItems.find(item => item.name === selectedItemName);
|
||||
if (selectedStockItem) {
|
||||
// Update the form fields with the selected stock item's details
|
||||
handleItemChange(index, "stockItemId", selectedStockItem.id);
|
||||
handleItemChange(index, "itemName", selectedStockItem.name);
|
||||
handleItemChange(index, "sku", selectedStockItem.sku);
|
||||
handleItemChange(index, "costPrice", selectedStockItem.costPrice); // Set costPrice
|
||||
// Quantity remains as is or defaults to 1 if not set
|
||||
if (form.getValues("items")[index].quantity === 0) {
|
||||
handleItemChange(index, "quantity", 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function onSubmit(values: z.infer<typeof bookOutFormSchema>) {
|
||||
async function onSubmit(values: z.infer<typeof formSchema>) {
|
||||
try {
|
||||
// Prepare data for PocketBase
|
||||
const bookOutData = {
|
||||
bookOutDate: values.bookOutDate.toISOString(),
|
||||
// PocketBase expects an array of item details for the 'items' field
|
||||
items: values.items.map(item => ({
|
||||
stockItemId: item.stockItemId,
|
||||
itemName: item.itemName,
|
||||
quantity: item.quantity,
|
||||
sku: item.sku,
|
||||
// costPrice is not explicitly stored in the 'book_outs' items array in PocketBase schema based on previous context,
|
||||
// but if it were, it would be included here.
|
||||
})),
|
||||
};
|
||||
// Fetch the latest stock item data to prevent race conditions
|
||||
const selectedStockItem = await pb.collection('stock_items').getOne(values.stockItemId);
|
||||
|
||||
await pb.collection('book_outs').create(bookOutData);
|
||||
toast({
|
||||
title: "Success",
|
||||
description: "Stock item booked out successfully!",
|
||||
if (!selectedStockItem) {
|
||||
showError("Selected item not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
const currentQuantity = selectedStockItem.quantity;
|
||||
const bookedOutQuantity = values.quantity;
|
||||
|
||||
if (bookedOutQuantity > currentQuantity) {
|
||||
showError(`Not enough stock. Only ${currentQuantity} available.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const newQuantity = currentQuantity - bookedOutQuantity;
|
||||
|
||||
// Update the stock item quantity in PocketBase
|
||||
await pb.collection('stock_items').update(values.stockItemId, {
|
||||
quantity: newQuantity,
|
||||
});
|
||||
|
||||
// Create a new record in the 'book_out_records' collection
|
||||
await pb.collection('book_out_records').create({
|
||||
customer: values.customer,
|
||||
items: values.stockItemId, // 'items' is a relation field to 'stock_items'
|
||||
quantity: bookedOutQuantity,
|
||||
date_booked_out: new Date().toISOString(), // Record the current date/time
|
||||
});
|
||||
|
||||
showSuccess(`Successfully booked out ${bookedOutQuantity} of ${selectedStockItem.name}!`);
|
||||
form.reset();
|
||||
setOpen(false);
|
||||
refreshData();
|
||||
fetchStockItems(); // Re-fetch stock items to update quantities in the combobox
|
||||
refreshData(); // Trigger refresh in parent component if needed
|
||||
} catch (error) {
|
||||
console.error("Error booking out stock item:", error);
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "Failed to book out stock item. Please check the form and try again.",
|
||||
variant: "destructive",
|
||||
});
|
||||
console.error("Error booking out item:", error);
|
||||
showError("Failed to book out item. Please try again.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -209,86 +127,48 @@ export function BookOutForm({ setOpen, refreshData }: BookOutFormProps) {
|
|||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="bookOutDate"
|
||||
name="customer"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex flex-col">
|
||||
<FormLabel>Book-Out Date</FormLabel>
|
||||
<FormLabel>Customer</FormLabel>
|
||||
<CustomerCombobox
|
||||
customers={customerOptions}
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="stockItemId" // Changed name to stockItemId
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex flex-col">
|
||||
<FormLabel>Item</FormLabel>
|
||||
<StockItemCombobox
|
||||
stockItems={stockItemOptions}
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="quantity"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Quantity</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="date"
|
||||
{...field}
|
||||
onChange={(e) => field.onChange(new Date(e.target.value))}
|
||||
value={field.value instanceof Date ? field.value.toISOString().split('T')[0] : ''}
|
||||
/>
|
||||
<Input type="number" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Items Section */}
|
||||
<div className="border p-4 rounded-md">
|
||||
<h3 className="text-lg font-semibold mb-4">Book-Out Items</h3>
|
||||
{form.getValues("items").map((item, index) => (
|
||||
<div key={index} className="grid grid-cols-4 gap-4 mb-4 items-center">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`items.${index}.itemName`}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Item Name</FormLabel>
|
||||
<StockItemCombobox
|
||||
stockItems={stockItems} // Pass the fetched stock items
|
||||
selectedItemName={field.value}
|
||||
onSelectItem={(selectedName) => handleStockItemSelect(index, selectedName)}
|
||||
placeholder="Search item..."
|
||||
className="w-full"
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`items.${index}.sku`}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>SKU</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} readOnly placeholder="SKU" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`items.${index}.quantity`}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Quantity</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="number" {...field} onChange={(e) => handleItemChange(index, "quantity", e.target.value)} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="flex items-center justify-center pt-5"> {/* Adjusted for alignment */}
|
||||
{form.getValues("items").length > 1 && (
|
||||
<Button type="button" variant="outline" size="icon" onClick={() => removeItem(index)}>
|
||||
-
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<Button type="button" variant="outline" onClick={addItem} className="mt-2">
|
||||
Add Item
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Button type="submit" className="w-full">Book Out Stock</Button>
|
||||
<Button type="submit" className="w-full">Book Out Item</Button>
|
||||
</form>
|
||||
</Form>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import {
|
|||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "@/components/ui/command"
|
||||
import {
|
||||
Popover,
|
||||
|
|
@ -19,29 +18,19 @@ import {
|
|||
PopoverTrigger,
|
||||
} from "@/components/ui/popover"
|
||||
|
||||
// Define the type for a stock item
|
||||
type StockItem = {
|
||||
id: string
|
||||
name: string
|
||||
sku: string
|
||||
costPrice: number
|
||||
interface StockItemOption {
|
||||
value: string; // PocketBase ID of the stock item
|
||||
label: string; // Item name
|
||||
quantity: number; // Current quantity
|
||||
}
|
||||
|
||||
interface StockItemComboboxProps {
|
||||
stockItems: StockItem[];
|
||||
selectedItemName: string;
|
||||
onSelectItem: (itemName: string) => void;
|
||||
placeholder: string;
|
||||
className?: string;
|
||||
stockItems: StockItemOption[];
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
}
|
||||
|
||||
export function StockItemCombobox({
|
||||
stockItems,
|
||||
selectedItemName,
|
||||
onSelectItem,
|
||||
placeholder,
|
||||
className,
|
||||
}: StockItemComboboxProps) {
|
||||
export function StockItemCombobox({ stockItems, value, onChange }: StockItemComboboxProps) {
|
||||
const [open, setOpen] = React.useState(false)
|
||||
|
||||
return (
|
||||
|
|
@ -51,41 +40,38 @@ export function StockItemCombobox({
|
|||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
className={cn(
|
||||
"w-[200px] justify-between", // Adjust width as needed
|
||||
className
|
||||
)}
|
||||
className="w-full justify-between"
|
||||
>
|
||||
{selectedItemName ? stockItems.find((item) => item.name === selectedItemName)?.name : placeholder}
|
||||
{value
|
||||
? stockItems.find((item) => item.value === value)?.label
|
||||
: "Select an item..."}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[300px] p-0"> {/* Adjust width as needed */}
|
||||
<PopoverContent className="w-[--radix-popover-trigger-width] p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder={placeholder} className="h-9" />
|
||||
<CommandList>
|
||||
<CommandEmpty>No items found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{stockItems.map((item) => (
|
||||
<CommandItem
|
||||
key={item.id}
|
||||
value={item.name}
|
||||
onSelect={(value) => {
|
||||
onSelectItem(value)
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
selectedItemName === item.name ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
{item.name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
<CommandInput placeholder="Search item..." />
|
||||
<CommandEmpty>No item found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{stockItems.map((item) => (
|
||||
<CommandItem
|
||||
key={item.value}
|
||||
value={item.label} // Use label for search, but value for selection
|
||||
onSelect={() => {
|
||||
onChange(item.value === value ? "" : item.value);
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
value === item.value ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
{item.label} (Qty: {item.quantity})
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import {
|
|||
} from "@/components/ui/select" // Import Select components
|
||||
import { showSuccess, showError } from "@/utils/toast"
|
||||
import pb from "@/lib/pocketbase"
|
||||
import { StockItemCombobox } from "@/components/shared/StockItemCombobox" // Corrected import path
|
||||
|
||||
// Define the schema for a single item in the purchase order
|
||||
const itemSchema = z.object({
|
||||
|
|
@ -192,6 +191,8 @@ export function PurchaseOrderForm({ setOpen, refreshData }: PurchaseOrderFormPro
|
|||
supplier: selectedSupplier.id, // This will be the relation to the suppliers collection
|
||||
orderDate: values.orderDate.toISOString(),
|
||||
status: "Pending Approval",
|
||||
// Ensure the structure matches what your PocketBase 'purchase_orders' collection expects for 'items'
|
||||
// Based on your schema, it seems 'embedded_items' is used for storing item details directly.
|
||||
embedded_items: values.items.map(item => ({
|
||||
stockItemId: item.stockItemId,
|
||||
itemName: item.itemName,
|
||||
|
|
@ -199,7 +200,7 @@ export function PurchaseOrderForm({ setOpen, refreshData }: PurchaseOrderFormPro
|
|||
sku: item.sku,
|
||||
costPrice: item.costPrice,
|
||||
})),
|
||||
quantities: values.items.map(item => item.quantity),
|
||||
quantities: values.items.map(item => item.quantity), // Keep quantities array as well if needed elsewhere
|
||||
};
|
||||
|
||||
await pb.collection('purchase_orders').create(purchaseOrderData);
|
||||
|
|
@ -273,13 +274,23 @@ export function PurchaseOrderForm({ setOpen, refreshData }: PurchaseOrderFormPro
|
|||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Item Name</FormLabel>
|
||||
<StockItemCombobox
|
||||
stockItems={stockItems}
|
||||
selectedItemName={field.value}
|
||||
onSelectItem={(selectedName) => handleStockItemSelect(index, selectedName)}
|
||||
placeholder="Search item..."
|
||||
className="w-full" // Make combobox take full width of its column
|
||||
/>
|
||||
<Select
|
||||
onValueChange={(selectedValue) => handleStockItemSelect(index, selectedValue)}
|
||||
value={field.value} // Set the current value of the item name
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select item" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{stockItems.map((stockItem) => (
|
||||
<SelectItem key={stockItem.id} value={stockItem.name}>
|
||||
{stockItem.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,93 +0,0 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Check, ChevronsUpDown } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "@/components/ui/command"
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover"
|
||||
|
||||
// Define the type for a stock item
|
||||
type StockItem = {
|
||||
id: string
|
||||
name: string
|
||||
sku: string
|
||||
costPrice: number
|
||||
}
|
||||
|
||||
interface StockItemComboboxProps {
|
||||
stockItems: StockItem[];
|
||||
selectedItemName: string;
|
||||
onSelectItem: (itemName: string) => void;
|
||||
placeholder: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function StockItemCombobox({
|
||||
stockItems,
|
||||
selectedItemName,
|
||||
onSelectItem,
|
||||
placeholder,
|
||||
className,
|
||||
}: StockItemComboboxProps) {
|
||||
const [open, setOpen] = React.useState(false)
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
className={cn(
|
||||
"w-[200px] justify-between", // Adjust width as needed
|
||||
className
|
||||
)}
|
||||
>
|
||||
{selectedItemName ? stockItems.find((item) => item.name === selectedItemName)?.name : placeholder}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[300px] p-0"> {/* Adjust width as needed */}
|
||||
<Command>
|
||||
<CommandInput placeholder={placeholder} className="h-9" />
|
||||
<CommandList>
|
||||
<CommandEmpty>No items found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{stockItems.map((item) => (
|
||||
<CommandItem
|
||||
key={item.id}
|
||||
value={item.name}
|
||||
onSelect={(value) => {
|
||||
onSelectItem(value)
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
selectedItemName === item.name ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
{item.name}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
Loading…
Reference in a new issue