Fix UI for inventory page 3
This commit is contained in:
@@ -1,20 +1,18 @@
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { Plus, AlertTriangle, Package, CheckCircle, Eye, Edit, Clock, Euro, ArrowRight } from 'lucide-react';
|
||||
import { Button, Input, Card, StatsGrid, StatusCard, getStatusColor, StatusModal } from '../../../../components/ui';
|
||||
import { Plus, AlertTriangle, Package, CheckCircle, Eye, Clock, Euro, ArrowRight } from 'lucide-react';
|
||||
import { Button, Input, Card, StatsGrid, StatusCard, getStatusColor } from '../../../../components/ui';
|
||||
import { LoadingSpinner } from '../../../../components/shared';
|
||||
import { formatters } from '../../../../components/ui/Stats/StatsPresets';
|
||||
import { PageHeader } from '../../../../components/layout';
|
||||
import { LowStockAlert, InventoryModal } from '../../../../components/domain/inventory';
|
||||
import { useIngredients, useStockAnalytics, useUpdateIngredient, useCreateIngredient } from '../../../../api/hooks/inventory';
|
||||
import { LowStockAlert, InventoryItemModal } from '../../../../components/domain/inventory';
|
||||
import { useIngredients, useStockAnalytics } from '../../../../api/hooks/inventory';
|
||||
import { useCurrentTenant } from '../../../../stores/tenant.store';
|
||||
import { IngredientResponse, IngredientUpdate, IngredientCreate, UnitOfMeasure, IngredientCategory, ProductType } from '../../../../api/types/inventory';
|
||||
import { IngredientResponse } from '../../../../api/types/inventory';
|
||||
|
||||
const InventoryPage: React.FC = () => {
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
const [modalMode, setModalMode] = useState<'create' | 'view' | 'edit'>('view');
|
||||
const [showItemModal, setShowItemModal] = useState(false);
|
||||
const [selectedItem, setSelectedItem] = useState<IngredientResponse | null>(null);
|
||||
const [formData, setFormData] = useState<Partial<IngredientCreate & IngredientUpdate & { initial_stock?: number; product_type?: string }>>({});
|
||||
|
||||
const currentTenant = useCurrentTenant();
|
||||
const tenantId = currentTenant?.id || '';
|
||||
@@ -34,9 +32,6 @@ const InventoryPage: React.FC = () => {
|
||||
isLoading: analyticsLoading
|
||||
} = useStockAnalytics(tenantId);
|
||||
|
||||
// Mutations
|
||||
const updateIngredientMutation = useUpdateIngredient();
|
||||
const createIngredientMutation = useCreateIngredient();
|
||||
|
||||
const ingredients = ingredientsData || [];
|
||||
const lowStockItems = ingredients.filter(ingredient => ingredient.stock_status === 'low_stock');
|
||||
@@ -82,123 +77,6 @@ const InventoryPage: React.FC = () => {
|
||||
};
|
||||
|
||||
|
||||
// Form handlers
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
if (modalMode === 'create') {
|
||||
// Create new ingredient - ensure required fields are present
|
||||
const createData: IngredientCreate = {
|
||||
name: formData.name || '',
|
||||
unit_of_measure: formData.unit_of_measure || UnitOfMeasure.GRAMS,
|
||||
low_stock_threshold: formData.low_stock_threshold || 10,
|
||||
reorder_point: formData.reorder_point || 20,
|
||||
description: formData.description,
|
||||
category: formData.category,
|
||||
max_stock_level: formData.max_stock_level,
|
||||
shelf_life_days: formData.shelf_life_days,
|
||||
requires_refrigeration: formData.requires_refrigeration,
|
||||
requires_freezing: formData.requires_freezing,
|
||||
is_seasonal: formData.is_seasonal,
|
||||
supplier_id: formData.supplier_id,
|
||||
average_cost: formData.average_cost,
|
||||
notes: formData.notes
|
||||
};
|
||||
|
||||
const createdItem = await createIngredientMutation.mutateAsync({
|
||||
tenantId,
|
||||
ingredientData: createData
|
||||
});
|
||||
|
||||
// TODO: Handle initial stock if provided
|
||||
// if (formData.initial_stock && formData.initial_stock > 0) {
|
||||
// // Add initial stock using stock transaction API
|
||||
// await addStockMutation.mutateAsync({
|
||||
// tenantId,
|
||||
// ingredientId: createdItem.id,
|
||||
// quantity: formData.initial_stock,
|
||||
// transaction_type: 'addition',
|
||||
// notes: 'Stock inicial'
|
||||
// });
|
||||
// }
|
||||
} else {
|
||||
// Update existing ingredient
|
||||
if (!selectedItem) return;
|
||||
await updateIngredientMutation.mutateAsync({
|
||||
tenantId,
|
||||
ingredientId: selectedItem.id,
|
||||
updateData: formData as IngredientUpdate
|
||||
});
|
||||
}
|
||||
|
||||
// Reset form data and close modal
|
||||
setFormData({});
|
||||
setModalMode('view');
|
||||
setShowForm(false);
|
||||
setSelectedItem(null);
|
||||
} catch (error) {
|
||||
console.error(`Error ${modalMode === 'create' ? 'creating' : 'updating'} ingredient:`, error);
|
||||
// TODO: Show error toast
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setFormData({});
|
||||
setModalMode('view');
|
||||
setShowForm(false);
|
||||
setSelectedItem(null);
|
||||
};
|
||||
|
||||
const handleFieldChange = (sectionIndex: number, fieldIndex: number, value: string | number) => {
|
||||
// Map section and field indexes to actual field names
|
||||
// Different mapping for create vs view/edit modes
|
||||
const createFieldMap: { [key: string]: string } = {
|
||||
'0-0': 'product_type', // Basic Info - Product Type
|
||||
'0-1': 'name', // Basic Info - Name
|
||||
'0-2': 'category', // Basic Info - Category
|
||||
'0-3': 'unit_of_measure', // Basic Info - Unit of measure
|
||||
'1-0': 'initial_stock', // Stock Config - Initial stock
|
||||
'1-1': 'low_stock_threshold', // Stock Config - Threshold
|
||||
'1-2': 'reorder_point', // Stock Config - Reorder point
|
||||
};
|
||||
|
||||
const viewEditFieldMap: { [key: string]: string } = {
|
||||
'0-0': 'name', // Basic Info - Name
|
||||
'0-1': 'category', // Basic Info - Category
|
||||
'0-2': 'description', // Basic Info - Description
|
||||
'0-3': 'unit_of_measure', // Basic Info - Unit of measure
|
||||
'1-1': 'low_stock_threshold', // Stock - Threshold
|
||||
'1-2': 'max_stock_level', // Stock - Max level
|
||||
'1-3': 'reorder_point', // Stock - Reorder point
|
||||
'1-4': 'reorder_quantity', // Stock - Reorder quantity
|
||||
'2-0': 'average_cost', // Financial - Average cost
|
||||
'3-1': 'shelf_life_days', // Additional - Shelf life
|
||||
'3-2': 'requires_refrigeration', // Additional - Refrigeration
|
||||
'3-3': 'requires_freezing', // Additional - Freezing
|
||||
'3-4': 'is_seasonal', // Additional - Seasonal
|
||||
'4-0': 'notes' // Notes - Observations
|
||||
};
|
||||
|
||||
// Use appropriate field map based on modal mode
|
||||
const fieldMap = modalMode === 'create' ? createFieldMap : viewEditFieldMap;
|
||||
|
||||
// Boolean field mapping for proper conversion
|
||||
const booleanFields = ['requires_refrigeration', 'requires_freezing', 'is_seasonal'];
|
||||
|
||||
const fieldKey = `${sectionIndex}-${fieldIndex}`;
|
||||
const fieldName = fieldMap[fieldKey];
|
||||
|
||||
if (fieldName) {
|
||||
// Convert string boolean values to actual booleans for boolean fields
|
||||
const processedValue = booleanFields.includes(fieldName)
|
||||
? value === 'true'
|
||||
: value;
|
||||
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
[fieldName]: processedValue
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const filteredItems = useMemo(() => {
|
||||
if (!searchTerm) return ingredients;
|
||||
@@ -231,31 +109,15 @@ const InventoryPage: React.FC = () => {
|
||||
return categoryMappings[category?.toLowerCase() || ''] || category || 'Sin categoría';
|
||||
};
|
||||
|
||||
// Simplified item action handler
|
||||
const handleItemAction = (ingredient: any, action: 'view' | 'edit') => {
|
||||
// Item action handler
|
||||
const handleViewItem = (ingredient: IngredientResponse) => {
|
||||
setSelectedItem(ingredient);
|
||||
setModalMode(action);
|
||||
setFormData({});
|
||||
setShowForm(true);
|
||||
setShowItemModal(true);
|
||||
};
|
||||
|
||||
// Handle new item creation
|
||||
// Handle new item creation - TODO: Implement create functionality
|
||||
const handleNewItem = () => {
|
||||
setSelectedItem(null);
|
||||
setModalMode('create');
|
||||
setFormData({
|
||||
product_type: 'ingredient', // Default to ingredient
|
||||
name: '',
|
||||
unit_of_measure: UnitOfMeasure.GRAMS,
|
||||
low_stock_threshold: 10,
|
||||
reorder_point: 20,
|
||||
reorder_quantity: 50,
|
||||
category: IngredientCategory.OTHER,
|
||||
requires_refrigeration: false,
|
||||
requires_freezing: false,
|
||||
is_seasonal: false
|
||||
});
|
||||
setShowForm(true);
|
||||
console.log('Create new item functionality to be implemented');
|
||||
};
|
||||
|
||||
|
||||
@@ -442,14 +304,8 @@ const InventoryPage: React.FC = () => {
|
||||
{
|
||||
label: 'Ver',
|
||||
icon: Eye,
|
||||
variant: 'outline',
|
||||
onClick: () => handleItemAction(ingredient, 'view')
|
||||
},
|
||||
{
|
||||
label: 'Editar',
|
||||
icon: Edit,
|
||||
variant: 'primary',
|
||||
onClick: () => handleItemAction(ingredient, 'edit')
|
||||
onClick: () => handleViewItem(ingredient)
|
||||
}
|
||||
]}
|
||||
/>
|
||||
@@ -479,24 +335,15 @@ const InventoryPage: React.FC = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Unified Inventory Modal */}
|
||||
{showForm && (
|
||||
<InventoryModal
|
||||
isOpen={showForm}
|
||||
{/* Inventory Item Modal */}
|
||||
{showItemModal && selectedItem && (
|
||||
<InventoryItemModal
|
||||
isOpen={showItemModal}
|
||||
onClose={() => {
|
||||
setShowForm(false);
|
||||
setShowItemModal(false);
|
||||
setSelectedItem(null);
|
||||
setModalMode('view');
|
||||
setFormData({});
|
||||
}}
|
||||
mode={modalMode}
|
||||
onModeChange={(mode) => setModalMode(mode as 'create' | 'view' | 'edit')}
|
||||
selectedItem={selectedItem}
|
||||
formData={formData}
|
||||
onFieldChange={handleFieldChange}
|
||||
onSave={handleSave}
|
||||
onCancel={handleCancel}
|
||||
loading={updateIngredientMutation.isPending || createIngredientMutation.isPending}
|
||||
ingredient={selectedItem}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user