diff --git a/frontend/src/components/domain/unified-wizard/wizards/InventoryWizard.tsx b/frontend/src/components/domain/unified-wizard/wizards/InventoryWizard.tsx index 041e7d06..d876ab17 100644 --- a/frontend/src/components/domain/unified-wizard/wizards/InventoryWizard.tsx +++ b/frontend/src/components/domain/unified-wizard/wizards/InventoryWizard.tsx @@ -1,342 +1,828 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { WizardStep, WizardStepProps } from '../../../ui/WizardModal/WizardModal'; -import { Package, Leaf, Cookie, CheckCircle2 } from 'lucide-react'; +import { AdvancedOptionsSection } from '../../../ui/AdvancedOptionsSection'; +import Tooltip from '../../../ui/Tooltip/Tooltip'; +import { Info } from 'lucide-react'; interface WizardDataProps extends WizardStepProps { data: Record; onDataChange: (data: Record) => void; } -// Step 1: Type Selection -const TypeSelectionStep: React.FC = ({ data, onDataChange, onNext }) => { - const [selectedType, setSelectedType] = useState<'ingredient' | 'finished_product'>( - data.inventoryType || 'ingredient' - ); - - const handleContinue = () => { - onDataChange({ ...data, inventoryType: selectedType }); - onNext(); - }; - - return ( -
-
-

- ¿Qué tipo de inventario agregarás? -

-
- -
- - - -
- -
- -
-
- ); -}; - -// Step 2: Core Details -const CoreDetailsStep: React.FC = ({ data, onDataChange, onNext }) => { - const isIngredient = data.inventoryType === 'ingredient'; - - const [formData, setFormData] = useState({ +// Single comprehensive step with all fields +const InventoryDetailsStep: React.FC = ({ data, onDataChange }) => { + const [inventoryData, setInventoryData] = useState({ + // Required fields name: data.name || '', - category: data.category || '', - unit: data.unit || '', - storage: data.storage || 'dry', + unitOfMeasure: data.unitOfMeasure || '', + productType: data.productType || 'ingredient', + + // Basic fields + sku: data.sku || '', + barcode: data.barcode || '', + ingredientCategory: data.ingredientCategory || '', + productCategory: data.productCategory || '', + description: data.description || '', + brand: data.brand || '', + + // Pricing fields + averageCost: data.averageCost || '', + lastPurchasePrice: data.lastPurchasePrice || '', + standardCost: data.standardCost || '', + sellingPrice: data.sellingPrice || '', + minimumPrice: data.minimumPrice || '', + + // Inventory management + lowStockThreshold: data.lowStockThreshold || '', reorderPoint: data.reorderPoint || '', - shelfLife: data.shelfLife || '', + reorderQuantity: data.reorderQuantity || '', + maxStockLevel: data.maxStockLevel || '', + leadTimeDays: data.leadTimeDays || '', + + // Product information + packageSize: data.packageSize || '', + shelfLifeDays: data.shelfLifeDays || '', + displayLifeHours: data.displayLifeHours || '', + storageTempMin: data.storageTempMin || '', + storageTempMax: data.storageTempMax || '', + + // Storage and handling + storageInstructions: data.storageInstructions || '', + isPerishable: data.isPerishable ?? true, + handlingInstructions: data.handlingInstructions || '', + + // Supplier information + preferredSupplierId: data.preferredSupplierId || '', + supplierProductCode: data.supplierProductCode || '', + + // Quality and compliance + allergenInfo: data.allergenInfo || '', + nutritionalInfo: data.nutritionalInfo || '', + certifications: data.certifications || '', + + // Physical properties + weight: data.weight || '', + volume: data.volume || '', + dimensions: data.dimensions || '', + color: data.color || '', + + // Status and tracking + isActive: data.isActive ?? true, + trackByLot: data.trackByLot ?? false, + trackByExpiry: data.trackByExpiry ?? true, + allowNegativeStock: data.allowNegativeStock ?? false, + + // Metadata + notes: data.notes || '', + tags: data.tags || '', + customFields: data.customFields || '', }); - const handleSubmit = () => { - onDataChange({ ...data, ...formData }); - onNext(); - }; - - return ( -
-
-

- Detalles del {isIngredient ? 'Ingrediente' : 'Producto'} -

-
- -
-
- - setFormData({ ...formData, name: e.target.value })} - placeholder={isIngredient ? "Ej: Harina de trigo" : "Ej: Baguette tradicional"} - className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]" - /> -
- -
- - -
- -
- - -
- -
- - -
- -
- - setFormData({ ...formData, reorderPoint: e.target.value })} - placeholder="Cantidad mínima" - className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]" - min="0" - /> -
-
- -
- -
-
- ); -}; - -// Step 3: Initial Lot (Optional) -const InitialLotStep: React.FC = ({ data, onDataChange, onComplete }) => { - const [addLot, setAddLot] = useState(false); - const [lotData, setLotData] = useState({ - quantity: '', - batchNumber: '', - expiryDate: '', - costPerUnit: '', - }); - - const handleComplete = () => { - if (addLot) { - onDataChange({ ...data, initialLot: lotData }); + // Auto-generate SKU from name if not provided + useEffect(() => { + if (!inventoryData.sku && inventoryData.name) { + const sku = `SKU-${inventoryData.name.substring(0, 3).toUpperCase()}-${Date.now().toString().slice(-4)}`; + setInventoryData(prev => ({ ...prev, sku })); } - // Here you would save to API - console.log('Saving inventory:', data); - onComplete(); - }; + }, [inventoryData.name]); + + // Sync with parent wizard state in real-time + useEffect(() => { + onDataChange({ ...data, ...inventoryData }); + }, [inventoryData]); return (

- Inventario Inicial (Opcional) + Inventory Item Details

- Agrega un lote inicial si ya tienes stock + Fill in the required information to create an inventory item

-
- + {/* Required Fields */} +
+
+ + setInventoryData({ ...inventoryData, name: e.target.value })} + placeholder="E.g., All-Purpose Flour, Sourdough Bread" + className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]" + /> +
+ +
+ + +
+ +
+ + +
- {addLot && ( -
+ {/* Basic Information */} +
+

Basic Information

+
- setLotData({ ...lotData, quantity: e.target.value })} - placeholder="100" - className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]" - min="0" - /> -
- -
- setLotData({ ...lotData, batchNumber: e.target.value })} - placeholder="LOT-2025-001" + value={inventoryData.sku} + onChange={(e) => setInventoryData({ ...inventoryData, sku: e.target.value })} + placeholder="SKU-XXX-1234" className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]" />
setLotData({ ...lotData, expiryDate: e.target.value })} + type="text" + value={inventoryData.barcode} + onChange={(e) => setInventoryData({ ...inventoryData, barcode: e.target.value })} + placeholder="Barcode/UPC/EAN" className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]" />
+ +
+ +
+ setLotData({ ...lotData, costPerUnit: e.target.value })} - placeholder="1.50" + type="text" + value={inventoryData.brand} + onChange={(e) => setInventoryData({ ...inventoryData, brand: e.target.value })} + placeholder="Brand name" + className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]" + /> +
+ +
+ +