Improve the frontend

This commit is contained in:
Urtzi Alfaro
2025-10-21 19:50:07 +02:00
parent 05da20357d
commit 8d30172483
105 changed files with 14699 additions and 4630 deletions

View File

@@ -5,6 +5,7 @@ import { useSuppliers } from '../../../api/hooks/suppliers';
import { useCreatePurchaseOrder } from '../../../api/hooks/suppliers';
import { useIngredients } from '../../../api/hooks/inventory';
import { useTenantStore } from '../../../stores/tenant.store';
import { suppliersService } from '../../../api/services/suppliers';
import type { ProcurementRequirementResponse, PurchaseOrderItem } from '../../../api/types/orders';
import type { SupplierSummary } from '../../../api/types/suppliers';
import type { IngredientResponse } from '../../../api/types/inventory';
@@ -31,6 +32,7 @@ export const CreatePurchaseOrderModal: React.FC<CreatePurchaseOrderModalProps> =
}) => {
const [loading, setLoading] = useState(false);
const [selectedSupplier, setSelectedSupplier] = useState<string>('');
const [formData, setFormData] = useState<Record<string, any>>({});
// Get current tenant
const { currentTenant } = useTenantStore();
@@ -44,13 +46,49 @@ export const CreatePurchaseOrderModal: React.FC<CreatePurchaseOrderModalProps> =
);
const suppliers = (suppliersData || []).filter(supplier => supplier.status === 'active');
// Fetch ingredients filtered by selected supplier (only when manually adding products)
const { data: ingredientsData = [] } = useIngredients(
// State for supplier products
const [supplierProductIds, setSupplierProductIds] = useState<string[]>([]);
const [isLoadingSupplierProducts, setIsLoadingSupplierProducts] = useState(false);
// Fetch ALL ingredients (we'll filter client-side based on supplier products)
const { data: allIngredientsData = [], isLoading: isLoadingIngredients } = useIngredients(
tenantId,
selectedSupplier ? { supplier_id: selectedSupplier } : {},
{ enabled: !!tenantId && isOpen && !requirements?.length && !!selectedSupplier }
{},
{ enabled: !!tenantId && isOpen && !requirements?.length }
);
// Fetch supplier products when supplier is selected
useEffect(() => {
const fetchSupplierProducts = async () => {
if (!selectedSupplier || !tenantId) {
setSupplierProductIds([]);
return;
}
setIsLoadingSupplierProducts(true);
try {
const products = await suppliersService.getSupplierProducts(tenantId, selectedSupplier);
const productIds = products.map(p => p.inventory_product_id);
setSupplierProductIds(productIds);
} catch (error) {
console.error('Error fetching supplier products:', error);
setSupplierProductIds([]);
} finally {
setIsLoadingSupplierProducts(false);
}
};
fetchSupplierProducts();
}, [selectedSupplier, tenantId]);
// Filter ingredients based on supplier products
const ingredientsData = useMemo(() => {
if (!selectedSupplier || supplierProductIds.length === 0) {
return [];
}
return allIngredientsData.filter(ing => supplierProductIds.includes(ing.id));
}, [allIngredientsData, supplierProductIds, selectedSupplier]);
// Create purchase order mutation
const createPurchaseOrderMutation = useCreatePurchaseOrder();
@@ -66,6 +104,14 @@ export const CreatePurchaseOrderModal: React.FC<CreatePurchaseOrderModalProps> =
data: ingredient // Store full ingredient data for later use
})), [ingredientsData]);
// Reset selected supplier when modal closes
useEffect(() => {
if (!isOpen) {
setSelectedSupplier('');
setFormData({});
}
}, [isOpen]);
// Unit options for select field
const unitOptions = [
{ value: 'kg', label: 'Kilogramos' },
@@ -80,11 +126,6 @@ export const CreatePurchaseOrderModal: React.FC<CreatePurchaseOrderModalProps> =
const handleSave = async (formData: Record<string, any>) => {
setLoading(true);
// Update selectedSupplier if it changed
if (formData.supplier_id && formData.supplier_id !== selectedSupplier) {
setSelectedSupplier(formData.supplier_id);
}
try {
let items: PurchaseOrderItem[] = [];
@@ -187,8 +228,9 @@ export const CreatePurchaseOrderModal: React.FC<CreatePurchaseOrderModalProps> =
};
const sections = [
{
// Build sections dynamically based on selectedSupplier
const sections = useMemo(() => {
const supplierSection = {
title: 'Información del Proveedor',
icon: Building2,
fields: [
@@ -199,11 +241,19 @@ export const CreatePurchaseOrderModal: React.FC<CreatePurchaseOrderModalProps> =
required: true,
options: supplierOptions,
placeholder: 'Seleccionar proveedor...',
span: 2
span: 2,
validation: (value: any) => {
// Update selectedSupplier when supplier changes
if (value && value !== selectedSupplier) {
setTimeout(() => setSelectedSupplier(value), 0);
}
return null;
}
}
]
},
{
};
const orderDetailsSection = {
title: 'Detalles de la Orden',
icon: Calendar,
fields: [
@@ -222,8 +272,9 @@ export const CreatePurchaseOrderModal: React.FC<CreatePurchaseOrderModalProps> =
helpText: 'Información adicional o instrucciones especiales'
}
]
},
{
};
const ingredientsSection = {
title: requirements && requirements.length > 0 ? 'Ingredientes Requeridos' : 'Productos a Comprar',
icon: Package,
fields: [
@@ -281,7 +332,7 @@ export const CreatePurchaseOrderModal: React.FC<CreatePurchaseOrderModalProps> =
},
helpText: 'Revisa y ajusta las cantidades y precios de los ingredientes requeridos'
} : {
label: 'Productos a Comprar',
label: selectedSupplier ? 'Productos a Comprar' : 'Selecciona un proveedor primero',
name: 'manual_products',
type: 'list' as const,
span: 2,
@@ -294,8 +345,8 @@ export const CreatePurchaseOrderModal: React.FC<CreatePurchaseOrderModalProps> =
type: 'select',
required: true,
options: ingredientOptions,
placeholder: 'Seleccionar ingrediente...',
disabled: false
placeholder: isLoadingSupplierProducts || isLoadingIngredients ? 'Cargando ingredientes...' : ingredientOptions.length === 0 ? 'No hay ingredientes disponibles para este proveedor' : 'Seleccionar ingrediente...',
disabled: !selectedSupplier || isLoadingIngredients || isLoadingSupplierProducts
},
{
name: 'quantity',
@@ -322,16 +373,26 @@ export const CreatePurchaseOrderModal: React.FC<CreatePurchaseOrderModalProps> =
}
],
addButtonLabel: 'Agregar Ingrediente',
emptyStateText: 'No hay ingredientes disponibles para este proveedor',
emptyStateText: !selectedSupplier
? 'Selecciona un proveedor para agregar ingredientes'
: isLoadingSupplierProducts || isLoadingIngredients
? 'Cargando ingredientes del proveedor...'
: ingredientOptions.length === 0
? 'Este proveedor no tiene ingredientes asignados en su lista de precios'
: 'No hay ingredientes agregados',
showSubtotals: true,
subtotalFields: { quantity: 'quantity', price: 'unit_price' },
disabled: !selectedSupplier
},
helpText: 'Selecciona ingredientes disponibles del proveedor seleccionado'
helpText: !selectedSupplier
? 'Primero selecciona un proveedor en la sección anterior'
: 'Selecciona ingredientes disponibles del proveedor seleccionado'
}
]
},
];
};
return [supplierSection, orderDetailsSection, ingredientsSection];
}, [requirements, supplierOptions, ingredientOptions, selectedSupplier, isLoadingIngredients, unitOptions]);
return (
<>