This commit is contained in:
g
2025-02-07 05:22:21 +00:00
parent f19797e752
commit 3205bb1a6b
11 changed files with 1021 additions and 432 deletions

View File

@@ -30,16 +30,14 @@ interface ProductModalProps {
onClose: () => void;
onSave: (productData: Product) => void;
productData: Product;
categories: Category[]; // Define categories as an array of Category
categories: Category[];
editing: boolean;
handleChange: (
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => void;
handleChange: (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
setProductData: React.Dispatch<React.SetStateAction<Product>>;
handleTieredPricingChange: (
e: ChangeEvent<HTMLInputElement>,
index: number
) => void;
setProductData: React.Dispatch<React.SetStateAction<Product>>;
}
export const ProductModal = ({
@@ -55,10 +53,10 @@ export const ProductModal = ({
}: ProductModalProps) => {
const [imagePreview, setImagePreview] = useState<string | null>(null);
// Update image preview when product data changes (for editing purposes)
// Update image preview when product data changes
useEffect(() => {
if (productData.image && typeof productData.image === "string") {
setImagePreview(productData.image); // For existing product image
setImagePreview(productData.image);
}
}, [productData.image]);
@@ -73,21 +71,23 @@ export const ProductModal = ({
}
};
// Handle tiered pricing change
// ✅ FIXED: Moved Inside the Component & Used Type Assertion
const handleTieredPricingChangeInternal = (
e: ChangeEvent<HTMLInputElement>,
index: number
) => {
const updatedPricing = [...productData.tieredPricing];
const updatedTier = updatedPricing[index];
const field = e.target.name as keyof (typeof productData.tieredPricing)[number];
// Ensure pricePerUnit is a number
const value = e.target.name === "pricePerUnit"
? parseFloat(e.target.value) || 0 // If value is invalid, default to 0
: e.target.value;
updatedPricing[index] = {
...updatedPricing[index],
[field]: e.target.valueAsNumber || 0,
};
updatedTier[e.target.name] = value;
setProductData({ ...productData, tieredPricing: updatedPricing });
setProductData({
...productData,
tieredPricing: updatedPricing,
});
};
return (
@@ -98,36 +98,21 @@ export const ProductModal = ({
{editing ? "Edit Product" : "Add Product"}
</DialogTitle>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="space-y-2">
<label className="text-sm font-medium">Product Name</label>
<Input
name="name"
placeholder="Product Name"
value={productData.name}
onChange={handleChange}
/>
<Input name="name" placeholder="Product Name" value={productData.name} onChange={handleChange} />
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Description</label>
<Textarea
name="description"
placeholder="Product Description"
value={productData.description}
onChange={handleChange}
rows={3}
/>
<Textarea name="description" placeholder="Product Description" value={productData.description} onChange={handleChange} rows={3} />
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Category</label>
<Select
value={productData.category}
onValueChange={(value) =>
setProductData({ ...productData, category: value })
}
>
<Select value={productData.category} onValueChange={(value) => setProductData({ ...productData, category: value })}>
<SelectTrigger>
<SelectValue placeholder="Select a category" />
</SelectTrigger>
@@ -141,127 +126,25 @@ export const ProductModal = ({
</Select>
</div>
{productData.category && (
<div className="space-y-2">
<h3 className="text-sm font-medium">Tiered Pricing</h3>
{productData.tieredPricing.map((tier, index) => (
<div key={index} className="flex gap-2">
<div className="flex-1 space-y-1">
<label className="text-xs text-muted-foreground">
Quantity
</label>
<Input
name="minQuantity"
type="number"
min="1"
placeholder="Quantity"
value={tier.minQuantity}
onChange={(e) => handleTieredPricingChangeInternal(e, index)}
/>
</div>
<div className="flex-1 space-y-1">
<label className="text-xs text-muted-foreground">
Price
</label>
<Input
name="pricePerUnit"
type="number"
step="0.01"
placeholder="Price"
value={tier.pricePerUnit}
onChange={(e) => handleTieredPricingChangeInternal(e, index)}
/>
</div>
</div>
))}
<Button
variant="outline"
size="sm"
className="mt-2"
onClick={() =>
setProductData({
...productData,
tieredPricing: [
...productData.tieredPricing,
{ minQuantity: 1, pricePerUnit: 0 }, // Initialize pricePerUnit as a number
],
})
}
>
+ Add Tier
</Button>
</div>
)}
<div className="space-y-2">
<label className="text-sm font-medium">Unit Type</label>
<Select
value={productData.unitType}
onValueChange={(value) =>
setProductData({ ...productData, unitType: value })
}
>
<SelectTrigger>
<SelectValue placeholder="Select unit" />
</SelectTrigger>
<SelectContent>
<SelectItem value="pcs">Pieces</SelectItem>
<SelectItem value="gr">Grams</SelectItem>
<SelectItem value="kg">Kilograms</SelectItem>
</SelectContent>
</Select>
</div>
{/* Image Upload Section */}
<div className="space-y-2">
<label className="text-sm font-medium">Product Image</label>
<Input
type="file"
accept="image/*"
className="file:text-foreground"
onChange={handleImageChange}
/>
{imagePreview && (
<div className="mt-2">
<img
src={imagePreview}
alt="Product preview"
className="rounded-md border w-32 h-32 object-cover"
/>
<p className="text-xs text-muted-foreground mt-1">
Click the upload button above to change image
</p>
<h3 className="text-sm font-medium">Tiered Pricing</h3>
{productData.tieredPricing.map((tier, index) => (
<div key={index} className="flex gap-2">
<Input name="minQuantity" type="number" min="1" placeholder="Quantity" value={tier.minQuantity} onChange={(e) => handleTieredPricingChangeInternal(e, index)} />
<Input name="pricePerUnit" type="number" step="0.01" placeholder="Price" value={tier.pricePerUnit} onChange={(e) => handleTieredPricingChangeInternal(e, index)} />
</div>
)}
{!imagePreview &&
productData.image &&
typeof productData.image === "string" && (
<div className="mt-2">
<img
src={productData.image}
alt="Existing product"
className="rounded-md border w-32 h-32 object-cover"
/>
<p className="text-xs text-muted-foreground mt-1">
Existing product image
</p>
</div>
)}
<p className="text-xs text-muted-foreground">
Upload a product image (JPEG, PNG, WEBP)
</p>
))}
<Button variant="outline" size="sm" onClick={() => setProductData({ ...productData, tieredPricing: [...productData.tieredPricing, { minQuantity: 1, pricePerUnit: 0 }] })}>
+ Add Tier
</Button>
</div>
</div>
<DialogFooter className="flex justify-end gap-2">
<Button variant="outline" onClick={onClose}>
Cancel
</Button>
<Button onClick={() => onSave(productData)}>
{editing ? "Update Product" : "Create Product"}
</Button>
<Button variant="outline" onClick={onClose}>Cancel</Button>
<Button onClick={() => onSave(productData)}>{editing ? "Update Product" : "Create Product"}</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};
};