import React, { useState } from 'react'; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; import { Home, LayoutDashboard, Package, ShoppingBag, Megaphone, DollarSign, Link2, FileText, HelpCircle, Menu, TrendingUp, RefreshCw, Search, Filter, Download } from 'lucide-react'; export default function EcomfactoryDashboard() { const [activeView, setActiveView] = useState('dashboard'); const [sidebarOpen, setSidebarOpen] = useState(true); const [isLoading, setIsLoading] = useState(false); const [importedData, setImportedData] = useState(null); const [fileName, setFileName] = useState(''); const [calcData, setCalcData] = useState({ precioProveedor: 250, fleteBase: 200, devolucion: 30, costosAdmin: 10, fulfillment: 0, cpaAdsManager: 91, cancelacion: 10, utilidadPorcentaje: 20 }); const kpiData = importedData ? { pedidosShopify: importedData.length, pedidosDropi: importedData.length, ingresos: importedData.reduce((sum, item) => sum + (item['VALOR DE COMPRA EN PRODUCTOS'] || 0), 0), costos: importedData.reduce((sum, item) => sum + (item['TOTAL EN PRECIOS DE PROVEEDOR'] || 0), 0), marketing: importedData.reduce((sum, item) => sum + (item['PRECIO FLETE'] || 0), 0), beneficioNeto: importedData.reduce((sum, item) => sum + (item['GANANCIA'] || 0), 0), beneficioProyecto: importedData.reduce((sum, item) => sum + (item['GANANCIA'] || 0), 0) * 1.6, margenBeneficio: 94, roi: 275.15, prevROI: 442.04, ingresosBrutos: importedData.reduce((sum, item) => sum + (item['VALOR DE COMPRA EN PRODUCTOS'] || 0), 0), prevIngresos: 505654.45, cpa: 83.32, ticketPromedio: importedData.length > 0 ? importedData.reduce((sum, item) => sum + (item['VALOR DE COMPRA EN PRODUCTOS'] || 0), 0) / importedData.length : 0, ticketDevolucion: 10407.84, devoluciones: importedData.filter(item => item['ESTATUS'] === 'DEVOLUCION').reduce((sum, item) => sum + (item['VALOR DE COMPRA EN PRODUCTOS'] || 0), 0), devolucionesCount: importedData.filter(item => item['ESTATUS'] === 'DEVOLUCION').length, efectividad: importedData.length > 0 ? (importedData.filter(item => item['ESTATUS'] === 'ENTREGADO').length / importedData.length * 100).toFixed(2) : 0, pendientes: importedData.length > 0 ? (importedData.filter(item => !['ENTREGADO', 'DEVOLUCION', 'CANCELADO'].includes(item['ESTATUS'])).length / importedData.length * 100).toFixed(2) : 0, devueltos: importedData.length > 0 ? (importedData.filter(item => item['ESTATUS'] === 'DEVOLUCION').length / importedData.length * 100).toFixed(2) : 0, entregados: importedData.length > 0 ? (importedData.filter(item => item['ESTATUS'] === 'ENTREGADO').length / importedData.length * 100).toFixed(2) : 0, confirmacion: importedData.filter(item => item['ESTATUS'] === 'EN CONFIRMACION' || item['ESTATUS'] === 'CONFIRMADO').length, envio: importedData.filter(item => item['ESTATUS'] === 'EN ENVIO' || item['ESTATUS'] === 'EN TRANSITO').length, cancelados: importedData.filter(item => item['ESTATUS'] === 'CANCELADO').length, devolucionesFinal: importedData.filter(item => item['ESTATUS'] === 'DEVOLUCION').length } : { pedidosShopify: 8993, pedidosDropi: 9152, ingresos: 1235251750.00, costos: 171655772.30, marketing: 749330.00, beneficioNeto: 206177137.70, beneficioProyecto: 331236061.25, margenBeneficio: 94, roi: 275.15, prevROI: 442.04, ingresosBrutos: 219343018.70, prevIngresos: 505654.45, cpa: 83.32, ticketPromedio: 134870.69, ticketDevolucion: 10407.84, devoluciones: 12416551.00, devolucionesCount: 1193, efectividad: 91.89, pendientes: 26.06, devueltos: 13.04, entregados: 30.87, confirmacion: 1797, envio: 588, cancelados: 788, devolucionesFinal: 1193 }; const pedidosData = importedData ? importedData.slice(0, 10).map(item => ({ id: `#${item['ID'] || 'N/A'}`, fecha: item['FECHA'] || 'N/A', cliente: item['NOMBRE CLIENTE'] || 'N/A', producto: item['CATEGORÍAS'] || 'N/A', estado: item['ESTATUS'] || 'N/A', total: item['VALOR DE COMPRA EN PRODUCTOS'] || 0 })) : [ { id: '#ORD-001', fecha: '2025-01-10', cliente: 'Juan Pérez', producto: 'Producto A', estado: 'ENTREGADO', total: 1250.00 }, { id: '#ORD-002', fecha: '2025-01-10', cliente: 'María García', producto: 'Producto B', estado: 'PENDIENTE', total: 850.00 }, { id: '#ORD-003', fecha: '2025-01-09', cliente: 'Carlos López', producto: 'Producto C', estado: 'CANCELADO', total: 1500.00 }, { id: '#ORD-004', fecha: '2025-01-09', cliente: 'Ana Martínez', producto: 'Producto A', estado: 'DEVOLUCION', total: 1250.00 }, { id: '#ORD-005', fecha: '2025-01-08', cliente: 'Luis Rodríguez', producto: 'Producto D', estado: 'ENTREGADO', total: 2100.00 }, ]; const productosData = [ { nombre: 'Producto A', categoria: 'Electrónica', total: 450, entregados: 380, efectividad: 84.4, ingresos: 562500.00 }, { nombre: 'Producto B', categoria: 'Hogar', total: 320, entregados: 290, efectividad: 90.6, ingresos: 246500.00 }, { nombre: 'Producto C', categoria: 'Moda', total: 280, entregados: 210, efectividad: 75.0, ingresos: 420000.00 }, { nombre: 'Producto D', categoria: 'Deportes', total: 180, entregados: 165, efectividad: 91.7, ingresos: 346500.00 }, ]; const temporalData = Array.from({ length: 30 }, (_, i) => ({ fecha: `${18 + i}/05`, ingresos: Math.random() * 50000000 + 40000000, marketing: Math.random() * 1000000 + 500000, costos: Math.random() * 20000000 + 15000000, ganancia: Math.random() * 30000000 + 20000000 })); const estadosData = importedData ? [ { name: 'Pendientes', value: parseFloat(kpiData.pendientes), color: '#FFA726', count: importedData.filter(item => !['ENTREGADO', 'DEVOLUCION', 'CANCELADO'].includes(item['ESTATUS'])).length }, { name: 'Devueltos', value: parseFloat(kpiData.devueltos), color: '#EF5350', count: kpiData.devolucionesCount }, { name: 'Entregados', value: parseFloat(kpiData.entregados), color: '#66BB6A', count: importedData.filter(item => item['ESTATUS'] === 'ENTREGADO').length } ] : [ { name: 'Pendientes', value: kpiData.pendientes, color: '#FFA726', count: kpiData.confirmacion + kpiData.envio }, { name: 'Devueltos', value: kpiData.devueltos, color: '#EF5350', count: kpiData.devolucionesCount }, { name: 'Entregados', value: kpiData.entregados, color: '#66BB6A', count: Math.round(kpiData.pedidosDropi * 0.3087) } ]; const formatCurrency = (value) => { return new Intl.NumberFormat('es-MX', { style: 'currency', currency: 'MXN', minimumFractionDigits: 2 }).format(value); }; const formatNumber = (value) => { return new Intl.NumberFormat('es-MX').format(value); }; const calcularResultados = () => { const { precioProveedor, fleteBase, devolucion, costosAdmin, fulfillment, cpaAdsManager, cancelacion, utilidadPorcentaje } = calcData; const cancelacionDecimal = cancelacion / 100; const devolucionDecimal = devolucion / 100; const utilidadDecimal = utilidadPorcentaje / 100; const fleteAjustado = fleteBase / (1 - devolucionDecimal); const cpaBase = cpaAdsManager; const cpaAjustado = cpaBase / (1 - cancelacionDecimal - devolucionDecimal); const costosTotales = precioProveedor + fleteAjustado + costosAdmin + fulfillment + cpaAjustado; const precioVentaCalculado = costosTotales / (1 - utilidadDecimal); const utilidadMonto = precioVentaCalculado - costosTotales; const cpaObjetivoPorcentaje = precioVentaCalculado > 0 ? (cpaBase / precioVentaCalculado) * 100 : 0; const precioComparacion = precioVentaCalculado * 1.40; const roasObjetivo = cpaBase > 0 ? precioVentaCalculado / cpaBase : 0; const costosFixosSinCPA = precioProveedor + fleteAjustado + costosAdmin + fulfillment; const margenDisponibleParaCPA = precioVentaCalculado - costosFixosSinCPA; const cpaBreak = margenDisponibleParaCPA * (1 - cancelacionDecimal - devolucionDecimal); const roasBreak = cpaBreak > 0 ? precioVentaCalculado / cpaBreak : 0; const cpaMalo = cpaBase * 1.20; const cpaOptimoVal = cpaBase; const cpaBuenoVal = cpaBase * 0.80; const cpaExcelenteVal = cpaBase * 0.55; return { fleteAjustado, cpaAjustado, costosTotales, precioVentaCalculado, utilidadMonto, cpaObjetivoPorcentaje, precioComparacion, roasObjetivo, cpaBreak, roasBreak, cpaMalo, cpaOptimoVal, cpaBuenoVal, cpaExcelenteVal }; }; const MenuItem = ({ icon: Icon, label, view, active }) => ( ); const MetricCard = ({ title, value, subtitle, bgColor, icon }) => (
Carga tu archivo de órdenes de DROPI en formato Excel (.xlsx) para analizar tus datos sin necesidad de conexión API
{!importedData ? (o haz clic para seleccionar
{fileName}
{importedData.length} órdenes procesadas
Sistema automatizado para gestión de operaciones COD. Conecta DROPI y visualiza tu rentabilidad en tiempo real.
Ingresa tus credenciales de DROPI y Shopify una sola vez
El sistema obtiene automáticamente todos tus pedidos
Dashboard, alertas y reportes actualizados en tiempo real
Toma decisiones basadas en datos reales
| ID | Fecha | Cliente | Producto | Estado | Total |
|---|---|---|---|---|---|
| {pedido.id} | {pedido.fecha} | {pedido.cliente} | {pedido.producto} | {pedido.estado} | {formatCurrency(pedido.total)} |
Mostrando 10 de {importedData.length} órdenes
| Producto/Categoría | Total | Entregados | Efectividad | Ingresos | Estado |
|---|---|---|---|---|---|
| {producto.nombre} | {producto.total} | {producto.entregados} | = 80 ? '#2ed573' : producto.efectividad >= 60 ? '#ffb800' : '#ee2737', fontWeight: 'bold' }}> {producto.efectividad}% | {formatCurrency(producto.ingresos)} | = 80 ? 'rgba(46,213,115,0.2)' : producto.efectividad >= 60 ? 'rgba(255,184,0,0.2)' : 'rgba(238,39,55,0.2)', color: producto.efectividad >= 80 ? '#2ed573' : producto.efectividad >= 60 ? '#ffb800' : '#ee2737', border: `1px solid ${producto.efectividad >= 80 ? '#2ed573' : producto.efectividad >= 60 ? '#ffb800' : '#ee2737'}` }}> {producto.efectividad >= 80 ? 'EXCELENTE' : producto.efectividad >= 60 ? 'BUENO' : 'CRÍTICO'} |
{activeView === 'dashboard' ? (importedData ? `Mostrando datos de: ${fileName}` : 'Panel de control principal') : activeView === 'importar' ? 'Carga tus archivos Excel de DROPI' : activeView === 'pedidos' ? 'Gestión de pedidos y órdenes' : activeView === 'productos' ? 'Análisis de productos' : activeView === 'integraciones' ? 'Conecta tus plataformas' : activeView === 'calculadora' ? 'Calcula tu precio ideal y KPIs' : 'Gestiona tu negocio'}