Imporve the routes and the menu

This commit is contained in:
Urtzi Alfaro
2025-09-19 12:06:26 +02:00
parent 105410c9d3
commit caf6d92850
5 changed files with 437 additions and 334 deletions

View File

@@ -76,10 +76,9 @@ export const OrderFormModal: React.FC<OrderFormModalProps> = ({
priority_level: PriorityLevel.NORMAL
});
// Product selection
const [showProductModal, setShowProductModal] = useState(false);
const [productSearch, setProductSearch] = useState('');
const [selectedCategory, setSelectedCategory] = useState<string>('');
// Simple product selection
const [selectedProductId, setSelectedProductId] = useState('');
const [selectedQuantity, setSelectedQuantity] = useState(1);
// API hooks
const { data: customers = [] } = useCustomers({
@@ -110,8 +109,8 @@ export const OrderFormModal: React.FC<OrderFormModalProps> = ({
// Reset form when modal closes
setSelectedCustomer(null);
setOrderItems([]);
setProductSearch('');
setSelectedCategory('');
setSelectedProductId('');
setSelectedQuantity(1);
setOrderData({
order_type: OrderType.STANDARD,
priority: PriorityLevel.NORMAL,
@@ -125,13 +124,18 @@ export const OrderFormModal: React.FC<OrderFormModalProps> = ({
}
}, [isOpen]);
const handleAddProduct = (product: any) => {
const existingItem = orderItems.find(item => item.product_id === product.id);
const handleAddSelectedProduct = () => {
if (!selectedProductId || selectedQuantity <= 0) return;
const product = finishedProducts.find(p => p.id === selectedProductId);
if (!product) return;
const existingItem = orderItems.find(item => item.product_id === selectedProductId);
if (existingItem) {
setOrderItems(items => items.map(item =>
item.product_id === product.id
? { ...item, quantity: item.quantity + 1 }
item.product_id === selectedProductId
? { ...item, quantity: item.quantity + selectedQuantity }
: item
));
} else {
@@ -140,14 +144,17 @@ export const OrderFormModal: React.FC<OrderFormModalProps> = ({
product_name: product.name,
product_sku: product.sku || undefined,
product_category: product.category || undefined,
quantity: 1,
quantity: selectedQuantity,
unit_of_measure: product.unit_of_measure || 'unidad',
unit_price: product.average_cost || product.standard_cost || 0,
line_discount: 0
};
setOrderItems(items => [...items, newItem]);
}
setShowProductModal(false);
// Reset selection
setSelectedProductId('');
setSelectedQuantity(1);
};
const handleUpdateItemQuantity = (productId: string, quantity: number) => {
@@ -213,22 +220,6 @@ export const OrderFormModal: React.FC<OrderFormModalProps> = ({
onClose();
};
// Get unique categories for filtering
const uniqueCategories = Array.from(new Set(
finishedProducts
.map(product => product.category)
.filter(Boolean)
)).sort();
const filteredProducts = finishedProducts.filter(product => {
const matchesSearch = product.name.toLowerCase().includes(productSearch.toLowerCase()) ||
(product.category && product.category.toLowerCase().includes(productSearch.toLowerCase())) ||
(product.description && product.description.toLowerCase().includes(productSearch.toLowerCase()));
const matchesCategory = !selectedCategory || product.category === selectedCategory;
return matchesSearch && matchesCategory;
});
// Convert form data to StatusModal sections
const modalSections: StatusModalSection[] = [
@@ -238,8 +229,30 @@ export const OrderFormModal: React.FC<OrderFormModalProps> = ({
fields: [
{
label: 'Cliente Seleccionado',
value: selectedCustomer ? `${selectedCustomer.name} (${selectedCustomer.customer_code})` : 'Ninguno',
span: 2
value: selectedCustomer ? selectedCustomer.id : '',
type: 'select',
editable: true,
placeholder: 'Seleccionar cliente...',
options: customers.map(customer => ({
value: customer.id,
label: `${customer.name} (${customer.customer_code})`
})),
span: 1
},
{
label: 'Nuevo Cliente',
value: (
<Button
variant="outline"
size="sm"
onClick={() => setShowCustomerForm(true)}
className="w-full"
>
<Plus className="w-4 h-4 mr-2" />
Agregar Cliente
</Button>
),
span: 1
}
]
},
@@ -281,15 +294,88 @@ export const OrderFormModal: React.FC<OrderFormModalProps> = ({
icon: Package,
fields: [
{
label: 'Total de Productos',
value: orderItems.length,
highlight: true
},
{
label: 'Lista de Productos',
value: orderItems.length > 0 ? orderItems.map(item => `${item.product_name} (x${item.quantity})`).join(', ') : 'Sin productos',
label: 'Agregar Producto',
value: (
<div className="flex gap-3 items-end">
<div className="flex-1">
<select
value={selectedProductId}
onChange={(e) => setSelectedProductId(e.target.value)}
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-md focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] focus:border-transparent bg-[var(--bg-primary)]"
>
<option value="">Seleccionar producto...</option>
{finishedProducts.map(product => (
<option key={product.id} value={product.id}>
{product.name} - {(product.average_cost || product.standard_cost || 0).toFixed(2)}
</option>
))}
</select>
</div>
<div className="w-24">
<Input
type="number"
min="1"
value={selectedQuantity}
onChange={(e) => setSelectedQuantity(Number(e.target.value))}
placeholder="Cant."
/>
</div>
<Button
onClick={handleAddSelectedProduct}
disabled={!selectedProductId || selectedQuantity <= 0}
className="whitespace-nowrap"
>
<Plus className="w-4 h-4 mr-1" />
Agregar
</Button>
</div>
),
span: 2
}
},
...(orderItems.length > 0 ? [{
label: 'Productos en el Pedido',
value: (
<div className="space-y-2">
{orderItems.map((item, index) => (
<div key={`${item.product_id}-${index}`} className="flex items-center justify-between p-3 bg-[var(--bg-secondary)] rounded-lg">
<div className="flex-1">
<h4 className="font-medium text-[var(--text-primary)]">{item.product_name}</h4>
<p className="text-sm text-[var(--text-secondary)]">
{item.unit_price.toFixed(2)} × {item.quantity} = {(item.unit_price * item.quantity).toFixed(2)}
</p>
</div>
<div className="flex items-center gap-2">
<Button
size="sm"
variant="outline"
onClick={() => handleUpdateItemQuantity(item.product_id, item.quantity - 1)}
disabled={item.quantity <= 1}
>
<Minus className="w-4 h-4" />
</Button>
<span className="min-w-[2rem] text-center font-medium">{item.quantity}</span>
<Button
size="sm"
variant="outline"
onClick={() => handleUpdateItemQuantity(item.product_id, item.quantity + 1)}
>
<Plus className="w-4 h-4" />
</Button>
<Button
size="sm"
variant="outline"
onClick={() => handleUpdateItemQuantity(item.product_id, 0)}
className="text-red-600 hover:text-red-700 ml-2"
>
<X className="w-4 h-4" />
</Button>
</div>
</div>
))}
</div>
),
span: 2
}] : [])
]
},
{
@@ -334,7 +420,12 @@ export const OrderFormModal: React.FC<OrderFormModalProps> = ({
const field = section.fields[fieldIndex];
// Update order data based on field changes
if (section.title === 'Detalles del Pedido') {
if (section.title === 'Cliente') {
if (field.label === 'Cliente Seleccionado') {
const customer = customers.find(c => c.id === value);
setSelectedCustomer(customer || null);
}
} else if (section.title === 'Detalles del Pedido') {
if (field.label === 'Tipo de Pedido') {
setOrderData(prev => ({ ...prev, order_type: value as OrderType }));
} else if (field.label === 'Prioridad') {
@@ -361,17 +452,6 @@ export const OrderFormModal: React.FC<OrderFormModalProps> = ({
size="2xl"
showDefaultActions={false}
actions={[
{
label: selectedCustomer ? 'Cambiar Cliente' : 'Seleccionar Cliente',
variant: 'outline',
onClick: () => setShowCustomerSelector(true)
},
{
label: 'Agregar Productos',
variant: 'outline',
onClick: () => setShowProductModal(true),
icon: Plus
},
{
label: 'Cancelar',
variant: 'outline',
@@ -392,118 +472,6 @@ export const OrderFormModal: React.FC<OrderFormModalProps> = ({
</div>
{/* Product Selection Modal - Using StatusModal for consistency */}
<StatusModal
isOpen={showProductModal}
onClose={() => setShowProductModal(false)}
mode="view"
title="Seleccionar Productos"
subtitle="Elija productos terminados para agregar al pedido"
size="2xl"
showDefaultActions={false}
sections={[
{
title: 'Filtros',
icon: Package,
fields: [
{
label: 'Buscar productos',
value: productSearch,
type: 'text',
editable: true,
placeholder: 'Buscar productos...',
span: 1
},
{
label: 'Categoría',
value: selectedCategory,
type: 'select',
editable: true,
placeholder: 'Todas las categorías',
options: [
{ value: '', label: 'Todas las categorías' },
...uniqueCategories.map(category => ({
value: category,
label: category
}))
],
span: 1
}
]
},
{
title: 'Productos Disponibles',
icon: ShoppingCart,
fields: [
{
label: 'Lista de productos',
value: (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 max-h-96 overflow-y-auto">
{productsLoading ? (
<div className="col-span-2 text-center py-8">
<p className="text-[var(--text-secondary)]">Cargando productos...</p>
</div>
) : filteredProducts.length === 0 ? (
<div className="col-span-2 text-center py-8">
<p className="text-[var(--text-secondary)]">
{productSearch ? 'No se encontraron productos que coincidan con la búsqueda' : 'No hay productos finalizados disponibles'}
</p>
</div>
) : (
filteredProducts.map(product => (
<div key={product.id} className="border border-[var(--border-primary)] rounded-lg p-4 hover:bg-[var(--bg-secondary)]">
<div className="flex items-start justify-between">
<div className="flex-1">
<h4 className="font-medium text-[var(--text-primary)]">{product.name}</h4>
{product.description && (
<p className="text-sm text-[var(--text-secondary)] mt-1">{product.description}</p>
)}
<div className="flex items-center mt-2 flex-wrap gap-2">
{product.category && (
<Badge variant="soft" color="gray">
{product.category}
</Badge>
)}
<span className="text-lg font-semibold text-[var(--color-info)]">
{(product.average_cost || product.standard_cost || 0).toFixed(2)}
</span>
</div>
</div>
<Button
size="sm"
onClick={() => handleAddProduct(product)}
disabled={!product.is_active || (product.total_quantity || 0) <= 0}
>
<Plus className="w-4 h-4 mr-1" />
Agregar
</Button>
</div>
<div className="text-xs text-[var(--text-tertiary)] mt-2 flex justify-between">
<span>Stock: {product.total_quantity || 0} {product.unit_of_measure}</span>
{product.sku && <span>SKU: {product.sku}</span>}
</div>
</div>
))
)}
</div>
),
span: 2
}
]
}
]}
onFieldChange={(sectionIndex, fieldIndex, value) => {
if (sectionIndex === 0) {
if (fieldIndex === 0) {
setProductSearch(String(value));
} else if (fieldIndex === 1) {
setSelectedCategory(String(value));
}
}
}}
/>
{/* New Customer Modal - Using StatusModal for consistency */}
<StatusModal