This commit is contained in:
@@ -22,6 +22,7 @@ import type React from "react";
|
||||
import { Plus } from "lucide-react";
|
||||
import { apiRequest } from "@/lib/api";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
|
||||
type CategorySelectProps = {
|
||||
categories: { _id: string; name: string; parentId?: string }[];
|
||||
@@ -213,9 +214,10 @@ const ProductBasicInfo: React.FC<{
|
||||
setProductData: React.Dispatch<React.SetStateAction<ProductData>>;
|
||||
onAddCategory: (newCategory: { _id: string; name: string; parentId?: string }) => void;
|
||||
}> = ({ productData, handleChange, categories, setProductData, onAddCategory }) => (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<label htmlFor="name" className="text-sm font-medium">
|
||||
<div className="space-y-8">
|
||||
<div className="space-y-4">
|
||||
<div className="grid gap-2">
|
||||
<label htmlFor="name" className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
||||
Product Name
|
||||
</label>
|
||||
<Input
|
||||
@@ -223,12 +225,13 @@ const ProductBasicInfo: React.FC<{
|
||||
name="name"
|
||||
value={productData.name}
|
||||
onChange={handleChange}
|
||||
placeholder="Enter product name"
|
||||
placeholder="e.g. Premium Wireless Headphones"
|
||||
className="border-border/50 bg-background/50 focus:bg-background transition-colors"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="description" className="text-sm font-medium">
|
||||
<div className="grid gap-2">
|
||||
<label htmlFor="description" className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
||||
Description
|
||||
</label>
|
||||
<textarea
|
||||
@@ -236,73 +239,61 @@ const ProductBasicInfo: React.FC<{
|
||||
name="description"
|
||||
value={productData.description}
|
||||
onChange={handleChange}
|
||||
placeholder="Enter product description"
|
||||
className="w-full min-h-[100px] rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
placeholder="Describe your product features and benefits..."
|
||||
className="flex w-full rounded-md border border-border/50 bg-background/50 px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 min-h-[120px] resize-y focus:bg-background transition-colors"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-background rounded-lg border border-border p-4">
|
||||
<h3 className="text-sm font-medium mb-4">Product Status</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium leading-none">Category</label>
|
||||
<CategorySelect
|
||||
categories={categories}
|
||||
value={productData.category}
|
||||
setProductData={setProductData}
|
||||
onAddCategory={onAddCategory}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium leading-none">Unit Type</label>
|
||||
<UnitTypeSelect
|
||||
value={productData.unitType}
|
||||
setProductData={setProductData}
|
||||
categories={categories}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="bg-muted/20 rounded-xl border border-border/40 overflow-hidden">
|
||||
<div className="p-4 border-b border-border/40 bg-muted/30">
|
||||
<h3 className="text-sm font-semibold flex items-center gap-2">
|
||||
Inventory Management
|
||||
</h3>
|
||||
</div>
|
||||
<div className="p-5 space-y-6">
|
||||
<div className="flex items-center justify-between p-3 rounded-lg border border-border/40 bg-background/40">
|
||||
<div className="space-y-0.5">
|
||||
<label htmlFor="stockTracking" className="text-sm font-medium cursor-pointer">Track Stock Quantity</label>
|
||||
<p className="text-xs text-muted-foreground">Automatically update stock when orders are placed</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="enabled"
|
||||
checked={productData.enabled !== false}
|
||||
id="stockTracking"
|
||||
checked={productData.stockTracking !== false}
|
||||
onCheckedChange={(checked) => {
|
||||
setProductData({
|
||||
...productData,
|
||||
enabled: checked
|
||||
stockTracking: checked
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="enabled" className="text-sm">
|
||||
Enable Product
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-background rounded-lg border border-border p-4">
|
||||
<h3 className="text-sm font-medium mb-4">Stock Management</h3>
|
||||
|
||||
<div className="flex items-center space-x-2 mb-4">
|
||||
<input
|
||||
id="stockTracking"
|
||||
name="stockTracking"
|
||||
type="checkbox"
|
||||
className="h-4 w-4 rounded border-gray-300"
|
||||
checked={productData.stockTracking !== false}
|
||||
onChange={(e) => {
|
||||
setProductData({
|
||||
...productData,
|
||||
stockTracking: e.target.checked
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="stockTracking" className="text-sm">
|
||||
Enable Stock Tracking
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{productData.stockTracking !== false && (
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label htmlFor="lowStockThreshold" className="text-sm font-medium">
|
||||
Low Stock Threshold
|
||||
</label>
|
||||
<Input
|
||||
id="lowStockThreshold"
|
||||
name="lowStockThreshold"
|
||||
type="number"
|
||||
min="1"
|
||||
step={productData.unitType === 'gr' || productData.unitType === 'ml' ? '0.1' : '1'}
|
||||
value={productData.lowStockThreshold || 10}
|
||||
onChange={handleChange}
|
||||
placeholder="10"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="currentStock" className="text-sm font-medium">
|
||||
Current Stock
|
||||
<div className="grid grid-cols-2 gap-5 animate-in fade-in slide-in-from-top-2 duration-200">
|
||||
<div className="space-y-2">
|
||||
<label htmlFor="currentStock" className="text-sm font-medium text-muted-foreground">
|
||||
Current Quantity
|
||||
</label>
|
||||
<Input
|
||||
id="currentStock"
|
||||
@@ -313,25 +304,42 @@ const ProductBasicInfo: React.FC<{
|
||||
value={productData.currentStock || 0}
|
||||
onChange={handleChange}
|
||||
placeholder="0"
|
||||
className="font-mono"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label htmlFor="lowStockThreshold" className="text-sm font-medium text-muted-foreground">
|
||||
Low Stock Alert At
|
||||
</label>
|
||||
<Input
|
||||
id="lowStockThreshold"
|
||||
name="lowStockThreshold"
|
||||
type="number"
|
||||
min="1"
|
||||
step={productData.unitType === 'gr' || productData.unitType === 'ml' ? '0.1' : '1'}
|
||||
value={productData.lowStockThreshold || 10}
|
||||
onChange={handleChange}
|
||||
placeholder="10"
|
||||
className="font-mono"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-background rounded-lg border border-border p-4">
|
||||
<h3 className="text-sm font-medium mb-4">💰 Cost & Profit Tracking</h3>
|
||||
<p className="text-xs text-muted-foreground mb-4">
|
||||
Track your costs to automatically calculate profit margins and markup percentages.
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<div className="bg-muted/20 rounded-xl border border-border/40 overflow-hidden">
|
||||
<div className="p-4 border-b border-border/40 bg-muted/30 flex justify-between items-center">
|
||||
<h3 className="text-sm font-semibold">Cost Analysis</h3>
|
||||
<Badge variant="outline" className="text-[10px] font-normal">Optional</Badge>
|
||||
</div>
|
||||
<div className="p-5">
|
||||
<div className="space-y-2">
|
||||
<label htmlFor="costPerUnit" className="text-sm font-medium">
|
||||
Cost Per Unit (Optional)
|
||||
Cost Per Unit
|
||||
</label>
|
||||
<p className="text-xs text-muted-foreground mb-2">
|
||||
How much you paid for each unit of this product
|
||||
</p>
|
||||
<div className="relative">
|
||||
<span className="absolute left-3 top-2.5 text-muted-foreground text-sm">$</span>
|
||||
<Input
|
||||
id="costPerUnit"
|
||||
name="costPerUnit"
|
||||
@@ -341,30 +349,33 @@ const ProductBasicInfo: React.FC<{
|
||||
value={productData.costPerUnit || ''}
|
||||
onChange={handleChange}
|
||||
placeholder="0.00"
|
||||
className="pl-7 font-mono"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-[11px] text-muted-foreground mt-1.5">
|
||||
Enter your cost to calculate profit margins automatically. This is never shown to customers.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium">Category</label>
|
||||
<CategorySelect
|
||||
categories={categories}
|
||||
value={productData.category}
|
||||
setProductData={setProductData}
|
||||
onAddCategory={onAddCategory}
|
||||
/>
|
||||
<div className="flex items-center justify-between p-4 rounded-xl border border-border/40 bg-blue-500/5">
|
||||
<div className="space-y-0.5">
|
||||
<label htmlFor="enabled" className="text-sm font-medium text-foreground">Product Visibility</label>
|
||||
<p className="text-xs text-muted-foreground">Make this product visible in your store</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium">Unit Type</label>
|
||||
<UnitTypeSelect
|
||||
value={productData.unitType}
|
||||
setProductData={setProductData}
|
||||
categories={categories}
|
||||
<Switch
|
||||
id="enabled"
|
||||
checked={productData.enabled !== false}
|
||||
onCheckedChange={(checked) => {
|
||||
setProductData({
|
||||
...productData,
|
||||
enabled: checked
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const CategorySelect: React.FC<CategorySelectProps> = ({
|
||||
|
||||
Reference in New Issue
Block a user