import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useCreateIngredient } from '../../../api/hooks/inventory'; import type { Ingredient } from '../../../api/types/inventory'; import { commonIngredientTemplates } from './ingredientHelpers'; interface BatchIngredientRow { id: string; name: string; category: string; unit_of_measure: string; stock_quantity?: number; cost_per_unit?: number; error?: string; } interface BatchAddIngredientsModalProps { isOpen: boolean; onClose: () => void; onCreated: (ingredients: Ingredient[]) => void; tenantId: string; } export const BatchAddIngredientsModal: React.FC = ({ isOpen, onClose, onCreated, tenantId }) => { const { t } = useTranslation(); const createIngredient = useCreateIngredient(); const [rows, setRows] = useState([ { id: '1', name: '', category: 'Baking Ingredients', unit_of_measure: 'kg' }, { id: '2', name: '', category: 'Baking Ingredients', unit_of_measure: 'kg' }, { id: '3', name: '', category: 'Baking Ingredients', unit_of_measure: 'kg' } ]); const [isSubmitting, setIsSubmitting] = useState(false); const [globalError, setGlobalError] = useState(null); const categoryOptions = [ 'Baking Ingredients', 'Dairy', 'Fruits', 'Vegetables', 'Meat', 'Seafood', 'Spices', 'Other' ]; const unitOptions = ['kg', 'g', 'l', 'ml', 'units', 'pcs', 'pkg', 'bags', 'boxes']; const updateRow = (id: string, field: keyof BatchIngredientRow, value: any) => { setRows(rows.map(row => row.id === id ? { ...row, [field]: value, error: undefined } : row )); }; const addRow = () => { const newId = String(Date.now()); setRows([...rows, { id: newId, name: '', category: 'Baking Ingredients', unit_of_measure: 'kg' }]); }; const removeRow = (id: string) => { if (rows.length > 1) { setRows(rows.filter(row => row.id !== id)); } }; const loadFromTemplates = () => { const templateRows: BatchIngredientRow[] = commonIngredientTemplates.slice(0, 10).map((template, index) => ({ id: String(Date.now() + index), name: template.name, category: template.category, unit_of_measure: template.unit_of_measure, stock_quantity: 0, cost_per_unit: 0 })); setRows(templateRows); }; const validateRows = (): boolean => { let hasError = false; const updatedRows = rows.map(row => { if (!row.name.trim()) { hasError = true; return { ...row, error: 'El nombre es requerido' }; } if (!row.category) { hasError = true; return { ...row, error: 'La categoría es requerida' }; } return { ...row, error: undefined }; }); if (hasError) { setRows(updatedRows); return false; } // Check for duplicates within batch const names = rows.map(r => r.name.toLowerCase().trim()); const duplicates = names.filter((name, index) => names.indexOf(name) !== index); if (duplicates.length > 0) { setGlobalError(`Hay nombres duplicados en el lote: ${duplicates.join(', ')}`); return false; } return true; }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setGlobalError(null); if (!validateRows()) return; setIsSubmitting(true); try { const createdIngredients: Ingredient[] = []; const errors: string[] = []; // Create all ingredients for (const row of rows) { try { const ingredientData = { name: row.name.trim(), product_type: 'ingredient', category: row.category, unit_of_measure: row.unit_of_measure, low_stock_threshold: 1, max_stock_level: 100, reorder_point: 2, shelf_life_days: 30, requires_refrigeration: false, requires_freezing: false, is_seasonal: false, average_cost: row.cost_per_unit || 0, notes: 'Creado mediante adición por lote', metadata: { created_context: 'batch', is_complete: !!(row.stock_quantity && row.cost_per_unit), needs_review: !(row.stock_quantity && row.cost_per_unit), } }; const created = await createIngredient.mutateAsync({ tenantId, ingredientData }); createdIngredients.push(created); } catch (error: any) { errors.push(`${row.name}: ${error.message || 'Error al crear'}`); } } if (createdIngredients.length > 0) { onCreated(createdIngredients); handleClose(); } if (errors.length > 0) { setGlobalError(`Algunos ingredientes no se pudieron crear:\n${errors.join('\n')}`); } } catch (error) { console.error('Error in batch creation:', error); setGlobalError('Error al crear los ingredientes. Inténtalo de nuevo.'); } finally { setIsSubmitting(false); } }; const handleClose = () => { setRows([ { id: '1', name: '', category: 'Baking Ingredients', unit_of_measure: 'kg' }, { id: '2', name: '', category: 'Baking Ingredients', unit_of_measure: 'kg' }, { id: '3', name: '', category: 'Baking Ingredients', unit_of_measure: 'kg' } ]); setGlobalError(null); onClose(); }; if (!isOpen) return null; return ( <> {/* Backdrop */}
{/* Modal */}
e.stopPropagation()} > {/* Header */}

📋 Agregar Múltiples Ingredientes

Agrega varios ingredientes a la vez para ahorrar tiempo

{/* Form */}
{/* Quick Actions */}
{/* Table */}
{rows.map((row, index) => ( ))}
# Nombre * Categoría * Unidad * Stock Inicial Costo (€)
{index + 1} updateRow(row.id, 'name', e.target.value)} className="w-full px-2 py-1.5 bg-[var(--bg-primary)] border border-[var(--border-secondary)] rounded text-sm text-[var(--text-primary)] focus:outline-none focus:ring-1 focus:ring-[var(--color-primary)]" placeholder="Ej: Harina" /> {row.error && (

{row.error}

)}
updateRow(row.id, 'stock_quantity', parseFloat(e.target.value) || undefined)} className="w-full px-2 py-1.5 bg-[var(--bg-primary)] border border-[var(--border-secondary)] rounded text-sm text-[var(--text-primary)] focus:outline-none focus:ring-1 focus:ring-[var(--color-primary)]" placeholder="0" /> updateRow(row.id, 'cost_per_unit', parseFloat(e.target.value) || undefined)} className="w-full px-2 py-1.5 bg-[var(--bg-primary)] border border-[var(--border-secondary)] rounded text-sm text-[var(--text-primary)] focus:outline-none focus:ring-1 focus:ring-[var(--color-primary)]" placeholder="0.00" />
{/* Info Box */}

💡 Los campos de stock y costo son opcionales. Puedes completarlos más tarde en la gestión de inventario.

{/* Global Error */} {globalError && (

{globalError}

)} {/* Actions */}
{/* Animation Styles */} ); };