import React, { useState, useCallback, useMemo, useEffect, useRef } from 'react'; import { Sparkles } from 'lucide-react'; import { WizardModal, WizardStep } from '../../ui/WizardModal/WizardModal'; import { ItemTypeSelector, ItemType } from './ItemTypeSelector'; import { AnyWizardData } from './types'; import { useTenant } from '../../../stores/tenant.store'; import { useCreatePurchaseOrder } from '../../../api/hooks/purchase-orders'; import { useCreateProductionBatch } from '../../../api/hooks/production'; import { toast } from 'react-hot-toast'; import type { ProductionBatchCreate } from '../../../api/types/production'; import { ProductionPriorityEnum } from '../../../api/types/production'; // Import specific wizards import { InventoryWizardSteps, ProductTypeStep, BasicInfoStep, StockConfigStep } from './wizards/InventoryWizard'; import { SupplierWizardSteps } from './wizards/SupplierWizard'; import { RecipeWizardSteps } from './wizards/RecipeWizard'; import { EquipmentWizardSteps } from './wizards/EquipmentWizard'; import { QualityTemplateWizardSteps } from './wizards/QualityTemplateWizard'; import { CustomerOrderWizardSteps } from './wizards/CustomerOrderWizard'; import { CustomerWizardSteps } from './wizards/CustomerWizard'; import { TeamMemberWizardSteps } from './wizards/TeamMemberWizard'; import { SalesEntryWizardSteps } from './wizards/SalesEntryWizard'; import { PurchaseOrderWizardSteps } from './wizards/PurchaseOrderWizard'; import { ProductionBatchWizardSteps } from './wizards/ProductionBatchWizard'; interface UnifiedAddWizardProps { isOpen: boolean; onClose: () => void; onComplete?: (itemType: ItemType, data?: any) => void; // Optional: Start with a specific item type (when opened from individual page buttons) initialItemType?: ItemType; } export const UnifiedAddWizard: React.FC = ({ isOpen, onClose, onComplete, initialItemType, }) => { const [selectedItemType, setSelectedItemType] = useState( initialItemType || null ); const [wizardData, setWizardData] = useState({}); const [isSubmitting, setIsSubmitting] = useState(false); // Get current tenant const { currentTenant } = useTenant(); // API hooks const createPurchaseOrderMutation = useCreatePurchaseOrder(); const createProductionBatchMutation = useCreateProductionBatch(); // Use a ref to store the current data - this allows step components // to always access the latest data without causing the steps array to be recreated const dataRef = useRef({}); // Update ref whenever data changes useEffect(() => { dataRef.current = wizardData; }, [wizardData]); // Reset state when modal closes const handleClose = useCallback(() => { setSelectedItemType(initialItemType || null); setWizardData({}); dataRef.current = {}; setIsSubmitting(false); onClose(); }, [onClose, initialItemType]); // Handle item type selection from step 0 const handleItemTypeSelect = useCallback((itemType: ItemType) => { setSelectedItemType(itemType); }, []); // CRITICAL FIX: Update both ref AND state, but wizardSteps won't recreate // The step component needs to re-render to show typed text (controlled inputs) // But wizardSteps useMemo ensures steps array doesn't recreate, so no component recreation const handleDataChange = useCallback((newData: AnyWizardData) => { // Update ref first for immediate access dataRef.current = newData; // Update state to trigger re-render (controlled inputs need this) setWizardData(newData); }, []); // Handle wizard completion with API submission const handleWizardComplete = useCallback( async () => { if (!selectedItemType || !currentTenant?.id) { return; } setIsSubmitting(true); try { const finalData = dataRef.current as any; // Cast to any for flexible data access // Handle Purchase Order submission if (selectedItemType === 'purchase-order') { const subtotal = (finalData.items || []).reduce( (sum: number, item: any) => sum + (item.subtotal || 0), 0 ); await createPurchaseOrderMutation.mutateAsync({ tenantId: currentTenant.id, data: { supplier_id: finalData.supplier_id, required_delivery_date: finalData.required_delivery_date, priority: finalData.priority || 'normal', subtotal: String(subtotal), tax_amount: String(finalData.tax_amount || 0), shipping_cost: String(finalData.shipping_cost || 0), discount_amount: String(finalData.discount_amount || 0), notes: finalData.notes || undefined, items: (finalData.items || []).map((item: any) => ({ inventory_product_id: item.inventory_product_id, ordered_quantity: item.ordered_quantity, unit_price: String(item.unit_price), unit_of_measure: item.unit_of_measure, })), }, }); toast.success('Orden de compra creada exitosamente'); } // Handle Production Batch submission if (selectedItemType === 'production-batch') { // Convert staff_assigned from string to array const staffArray = finalData.staff_assigned_string ? finalData.staff_assigned_string.split(',').map((s: string) => s.trim()).filter((s: string) => s.length > 0) : []; const batchData: ProductionBatchCreate = { product_id: finalData.product_id, product_name: finalData.product_name, recipe_id: finalData.recipe_id || undefined, planned_start_time: finalData.planned_start_time, planned_end_time: finalData.planned_end_time, planned_quantity: Number(finalData.planned_quantity), planned_duration_minutes: Number(finalData.planned_duration_minutes), priority: (finalData.priority || ProductionPriorityEnum.MEDIUM) as ProductionPriorityEnum, is_rush_order: finalData.is_rush_order || false, is_special_recipe: finalData.is_special_recipe || false, production_notes: finalData.production_notes || undefined, batch_number: finalData.batch_number || undefined, order_id: finalData.order_id || undefined, forecast_id: finalData.forecast_id || undefined, equipment_used: [], staff_assigned: staffArray, station_id: finalData.station_id || undefined, }; await createProductionBatchMutation.mutateAsync({ tenantId: currentTenant.id, batchData, }); toast.success('Lote de producción creado exitosamente'); } // Call the parent's onComplete callback onComplete?.(selectedItemType, finalData); // Close the modal handleClose(); } catch (error: any) { console.error('Error submitting wizard data:', error); toast.error(error.message || 'Error al crear el elemento'); } finally { setIsSubmitting(false); } }, [ selectedItemType, currentTenant, createPurchaseOrderMutation, createProductionBatchMutation, onComplete, handleClose, ] ); // Get wizard steps based on selected item type // ARCHITECTURAL SOLUTION: We pass dataRef and setWizardData to wizard step functions. // The wizard steps use these in their component wrappers, which creates a closure // that always accesses the CURRENT data from dataRef.current, without needing // to recreate the steps array on every data change. const wizardSteps = useMemo((): WizardStep[] => { if (!selectedItemType) { // Step 0: Item Type Selection return [ { id: 'item-type-selection', title: 'Seleccionar tipo', description: 'Elige qué deseas agregar', component: () => ( ), }, ]; } // Pass dataRef and setWizardData - the wizard step functions will use // dataRef.current to always access fresh data without recreating steps switch (selectedItemType) { case 'inventory': return InventoryWizardSteps(dataRef, setWizardData); case 'supplier': return SupplierWizardSteps(dataRef, setWizardData); case 'recipe': return RecipeWizardSteps(dataRef, setWizardData); case 'equipment': return EquipmentWizardSteps(dataRef, setWizardData); case 'quality-template': return QualityTemplateWizardSteps(dataRef, setWizardData); case 'customer-order': return CustomerOrderWizardSteps(dataRef, setWizardData); case 'customer': return CustomerWizardSteps(dataRef, setWizardData); case 'team-member': return TeamMemberWizardSteps(dataRef, setWizardData); case 'sales-entry': return SalesEntryWizardSteps(dataRef, setWizardData); case 'purchase-order': return PurchaseOrderWizardSteps(dataRef, setWizardData); case 'production-batch': return ProductionBatchWizardSteps(dataRef, setWizardData); default: return []; } }, [selectedItemType, handleItemTypeSelect, wizardData.entryMethod]); // Add entryMethod for dynamic sales-entry steps // Get wizard title based on selected item type const getWizardTitle = (): string => { if (!selectedItemType) { return 'Agregar Contenido'; } const titleMap: Record = { 'inventory': 'Agregar Inventario', 'supplier': 'Agregar Proveedor', 'recipe': 'Agregar Receta', 'equipment': 'Agregar Equipo', 'quality-template': 'Agregar Plantilla de Calidad', 'customer-order': 'Agregar Pedido', 'customer': 'Agregar Cliente', 'team-member': 'Agregar Miembro del Equipo', 'sales-entry': 'Registrar Ventas', 'purchase-order': 'Crear Orden de Compra', 'production-batch': 'Crear Lote de Producción', }; return titleMap[selectedItemType] || 'Agregar Contenido'; }; return ( } size="xl" dataRef={dataRef} onDataChange={handleDataChange} /> ); }; export default UnifiedAddWizard;