Imporve the routes and the menu
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user