Feat: Configuración y Administración de Tipos

This commit is contained in:
2026-02-25 18:11:52 -03:00
parent a8b8229b41
commit b4fa74ad9b
25 changed files with 941 additions and 107 deletions

View File

@@ -9,6 +9,7 @@ import PaymentModal, { type Payment } from '../components/PaymentModal';
import { orderService } from '../services/orderService';
import type { CreateOrderRequest } from '../types/Order';
import AdEditorModal from '../components/POS/AdEditorModal';
import BundleConfiguratorModal, { type ComponentConfig } from '../components/POS/BundleConfiguratorModal';
import ClientCreateModal from '../components/POS/ClientCreateModal';
import ClientSearchModal from '../components/POS/ClientSearchModal';
import { AnimatePresence } from 'framer-motion';
@@ -24,6 +25,8 @@ export default function UniversalPosPage() {
// Estados de Modales
const [showAdEditor, setShowAdEditor] = useState(false);
const [selectedAdProduct, setSelectedAdProduct] = useState<Product | null>(null);
const [showBundleConfigurator, setShowBundleConfigurator] = useState(false);
const [selectedBundle, setSelectedBundle] = useState<Product | null>(null);
const [showCreateClient, setShowCreateClient] = useState(false);
const [showClientSearch, setShowClientSearch] = useState(false);
@@ -70,16 +73,11 @@ export default function UniversalPosPage() {
return;
}
// 2. COMBOS (BUNDLES) - Lógica de Visualización
// 2. COMBOS (BUNDLES) - Lógica de Visualización y Configuración
if (product.typeCode === 'BUNDLE') {
// Traemos los componentes para mostrarlos en el ticket
const components = await productService.getBundleComponents(product.id);
const subItemsNames = components.map(c =>
`${c.quantity}x ${c.childProduct?.name || 'Item'}`
);
addItem(product, 1, { subItems: subItemsNames });
showToast(`Combo agregado con ${components.length} ítems`, 'success');
if (!clientId) setClient(1005, "Consumidor Final (Default)");
setSelectedBundle(product);
setShowBundleConfigurator(true);
return;
}
@@ -108,6 +106,22 @@ export default function UniversalPosPage() {
}
};
const handleBundleConfirmed = (configs: ComponentConfig[]) => {
if (selectedBundle) {
const subItemsNames = configs.map(c =>
`${c.product.name} ${c.isConfigured ? '✓' : ''}`
);
addItem(selectedBundle, 1, {
subItems: subItemsNames,
componentsData: configs
});
showToast(`${selectedBundle.name} configurado y agregado`, 'success');
setShowBundleConfigurator(false);
}
};
const handleCheckout = () => {
if (items.length === 0) return showToast("El carrito está vacío", "error");
if (!clientId) setClient(1005, "Consumidor Final");
@@ -146,12 +160,31 @@ export default function UniversalPosPage() {
sellerId: sellerId || 2,
isDirectPayment: isDirectPayment,
notes: "Venta de Mostrador (Universal POS)",
items: items.map(i => ({
productId: i.productId,
quantity: i.quantity,
relatedEntityId: i.relatedEntityId,
relatedEntityType: i.relatedEntityType
}))
items: items.flatMap(i => {
if (i.componentsData && i.componentsData.length > 0) {
// Expandir el combo en sus partes para el backend
// El precio se prorratea según el total del combo
const bundleTotal = i.subTotal;
const sumAllocations = i.componentsData.reduce((acc, c) => acc + (c.allocationAmount || 0), 0);
const ratio = sumAllocations > 0 ? (bundleTotal / sumAllocations) : 1;
return i.componentsData.map(c => ({
productId: c.productId,
quantity: 1,
unitPrice: (c.allocationAmount || 0) * ratio,
relatedEntityId: c.listingId,
relatedEntityType: c.listingId ? 'Listing' : undefined
}));
}
return [{
productId: i.productId,
quantity: i.quantity,
unitPrice: i.unitPrice,
relatedEntityId: i.relatedEntityId,
relatedEntityType: i.relatedEntityType
}];
})
};
const result = await orderService.createOrder(payload);
@@ -314,6 +347,15 @@ export default function UniversalPosPage() {
/>
)}
{showBundleConfigurator && selectedBundle && (
<BundleConfiguratorModal
bundle={selectedBundle}
clientId={clientId || 1005}
onClose={() => setShowBundleConfigurator(false)}
onConfirm={handleBundleConfirmed}
/>
)}
{/* MODAL DE BÚSQUEDA DE CLIENTE (F7) */}
<AnimatePresence>
{showClientSearch && (