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 }) => (
{icon} {title}
{value}
{subtitle &&
{subtitle}
}
); const InfoBox = ({ label, value, sublabel }) => (
{label}
{value}
{sublabel &&
{sublabel}
}
); const handleFileUpload = async (event) => { const file = event.target.files[0]; if (!file) return; setIsLoading(true); setFileName(file.name); try { const XLSX = await import('https://cdn.sheetjs.com/xlsx-0.20.1/package/xlsx.mjs'); const arrayBuffer = await file.arrayBuffer(); const workbook = XLSX.read(arrayBuffer, { cellStyles: true, cellDates: true, cellNF: true }); const firstSheet = workbook.Sheets[workbook.SheetNames[0]]; const data = XLSX.utils.sheet_to_json(firstSheet); setImportedData(data); setActiveView('dashboard'); setTimeout(() => setIsLoading(false), 1000); } catch (error) { console.error('Error al procesar el archivo:', error); alert('Error al procesar el archivo. Asegúrate de que sea un archivo Excel válido.'); setIsLoading(false); } }; const renderContent = () => { if (activeView === 'importar') { return (

📂 Importar Datos desde Excel

Carga tu archivo de órdenes de DROPI en formato Excel (.xlsx) para analizar tus datos sin necesidad de conexión API

{!importedData ? (
{ e.preventDefault(); e.currentTarget.style.borderColor = '#ee2737'; e.currentTarget.style.background = 'rgba(238,39,55,0.1)'; }} onDragLeave={(e) => { e.currentTarget.style.borderColor = 'rgba(238,39,55,0.5)'; e.currentTarget.style.background = 'rgba(238,39,55,0.05)'; }} onDrop={(e) => { e.preventDefault(); e.currentTarget.style.borderColor = 'rgba(238,39,55,0.5)'; e.currentTarget.style.background = 'rgba(238,39,55,0.05)'; const file = e.dataTransfer.files[0]; if (file) { const fakeEvent = { target: { files: [file] } }; handleFileUpload(fakeEvent); } }} onClick={() => document.getElementById('fileInput').click()}>

Arrastra tu archivo aquí

o haz clic para seleccionar

📋 Formato esperado del archivo:

  • Archivo Excel (.xlsx o .xls)
  • Debe contener las columnas: ID, FECHA, NOMBRE CLIENTE, ESTATUS, VALOR DE COMPRA EN PRODUCTOS, etc.
  • El sistema reconocerá automáticamente las columnas estándar de DROPI
  • Se procesarán todos los registros y se calcularán los KPIs automáticamente
) : (

¡Archivo Cargado Exitosamente!

{fileName}

{importedData.length} órdenes procesadas

ENTREGADOS
{importedData.filter(item => item['ESTATUS'] === 'ENTREGADO').length}
{((importedData.filter(item => item['ESTATUS'] === 'ENTREGADO').length / importedData.length) * 100).toFixed(1)}%
DEVUELTOS
{importedData.filter(item => item['ESTATUS'] === 'DEVOLUCION').length}
{((importedData.filter(item => item['ESTATUS'] === 'DEVOLUCION').length / importedData.length) * 100).toFixed(1)}%
INGRESOS TOTALES
{formatCurrency(importedData.reduce((sum, item) => sum + (item['VALOR DE COMPRA EN PRODUCTOS'] || 0), 0))}
)}
); } if (activeView === 'home') { return (

🚀

Bienvenido a ECOMFACTORY

Sistema automatizado para gestión de operaciones COD. Conecta DROPI y visualiza tu rentabilidad en tiempo real.

1️⃣ Conecta

Ingresa tus credenciales de DROPI y Shopify una sola vez

2️⃣ Sincroniza

El sistema obtiene automáticamente todos tus pedidos

3️⃣ Analiza

Dashboard, alertas y reportes actualizados en tiempo real

4️⃣ Optimiza

Toma decisiones basadas en datos reales

); } if (activeView === 'calculadora') { const resultados = calcularResultados(); return (

🧮 Calculadora de Precio COD

{[ { label: 'Precio Proveedor', key: 'precioProveedor' }, { label: 'Flete Base', key: 'fleteBase' }, { label: '% Devolución', key: 'devolucion' }, { label: 'Costos Admin', key: 'costosAdmin' }, { label: 'Fulfillment', key: 'fulfillment' }, { label: 'CPA Ads Manager', key: 'cpaAdsManager' }, { label: '% Cancelación', key: 'cancelacion' }, { label: '% Utilidad Objetivo', key: 'utilidadPorcentaje' } ].map(field => (
setCalcData({...calcData, [field.key]: parseFloat(e.target.value) || 0})} />
))}
PRECIO DE VENTA CALCULADO
{formatCurrency(resultados.precioVentaCalculado)}
Utilidad: {formatCurrency(resultados.utilidadMonto)} ({calcData.utilidadPorcentaje}%)

📊 KPIs Rentables

CPA OBJETIVO
{formatCurrency(calcData.cpaAdsManager)}
{resultados.cpaObjetivoPorcentaje.toFixed(1)}% del precio de venta
ROAS OBJETIVO
{resultados.roasObjetivo.toFixed(2)}x
Return on Ad Spend
CPA BREAK-EVEN
{formatCurrency(resultados.cpaBreak)}
ROAS: {resultados.roasBreak.toFixed(2)}x
PRECIO COMPARACIÓN
{formatCurrency(resultados.precioComparacion)}
+40% para descuentos

🎯 Escenarios de CPA

{[ { label: 'MALO', cpa: resultados.cpaMalo, color: '#EF5350' }, { label: 'ÓPTIMO', cpa: resultados.cpaOptimoVal, color: '#FFA726' }, { label: 'BUENO', cpa: resultados.cpaBuenoVal, color: '#66BB6A' }, { label: 'EXCELENTE', cpa: resultados.cpaExcelenteVal, color: '#2ed573' } ].map((escenario, idx) => (
{escenario.label}
{formatCurrency(escenario.cpa)}
ROAS
{(resultados.precioVentaCalculado / escenario.cpa).toFixed(2)}x
))}

📈 Rangos de KPIs Recomendados

{[ { title: 'EFECTIVIDAD', ranges: [ { label: '🔴 Malo', value: '< 50%' }, { label: '🟡 Regular', value: '50% - 70%' }, { label: '🟢 Bueno', value: '70% - 85%' }, { label: '✅ Excelente', value: '> 85%' } ] }, { title: 'MARGEN DE UTILIDAD', ranges: [ { label: '🔴 Bajo', value: '< 15%' }, { label: '🟡 Aceptable', value: '15% - 25%' }, { label: '🟢 Bueno', value: '25% - 35%' }, { label: '✅ Excelente', value: '> 35%' } ] }, { title: 'ROAS', ranges: [ { label: '🔴 Pérdida', value: '< 1.5x' }, { label: '🟡 Break-even', value: '1.5x - 2.5x' }, { label: '🟢 Rentable', value: '2.5x - 4x' }, { label: '✅ Óptimo', value: '> 4x' } ] }, { title: '% DEVOLUCIONES', ranges: [ { label: '✅ Excelente', value: '< 5%' }, { label: '🟢 Bueno', value: '5% - 15%' }, { label: '🟡 Atención', value: '15% - 25%' }, { label: '🔴 Crítico', value: '> 25%' } ] } ].map((section, idx) => (
{section.title}
{section.ranges.map((range, i) => (
{range.label} {range.value}
))}
))}
); } if (activeView === 'pedidos') { return (
{importedData && (
✅ Mostrando datos importados: {fileName} ({importedData.length} órdenes)
)}

📦 Pedidos Recientes

{pedidosData.map((pedido, idx) => ( ))}
ID Fecha Cliente Producto Estado Total
{pedido.id} {pedido.fecha} {pedido.cliente} {pedido.producto} {pedido.estado} {formatCurrency(pedido.total)}
{importedData && importedData.length > 10 && (

Mostrando 10 de {importedData.length} órdenes

)}
); } if (activeView === 'productos') { // Agrupar productos por categoría si hay datos importados const productosProcesados = importedData ? (() => { const grouped = {}; importedData.forEach(item => { const categorias = item['CATEGORÍAS'] || 'Sin categoría'; const cats = categorias.split(',').map(c => c.trim()); cats.forEach(cat => { if (!grouped[cat]) { grouped[cat] = { nombre: cat, total: 0, entregados: 0, ingresos: 0 }; } grouped[cat].total += 1; if (item['ESTATUS'] === 'ENTREGADO') { grouped[cat].entregados += 1; grouped[cat].ingresos += item['VALOR DE COMPRA EN PRODUCTOS'] || 0; } }); }); return Object.values(grouped).map(prod => ({ ...prod, efectividad: prod.total > 0 ? (prod.entregados / prod.total * 100).toFixed(1) : 0 })).sort((a, b) => b.ingresos - a.ingresos); })() : productosData; return (
{importedData && (
✅ Mostrando análisis de: {fileName}
)}

🛍️ Análisis por Producto

{productosProcesados.map((producto, idx) => ( ))}
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'}
); } if (activeView === 'dashboard') { return (
{!importedData && (
💡
Mostrando Datos de Ejemplo
Importa tu archivo de DROPI para ver tus datos reales
)} {importedData && (
Datos Importados Activos
{fileName} • {importedData.length} órdenes • Última actualización: {new Date().toLocaleDateString('es-MX')}
)}
Beneficio Neto
{formatCurrency(kpiData.beneficioNeto)}
Previsto: {formatCurrency(kpiData.ingresosBrutos - kpiData.costos - kpiData.marketing)}
Beneficio Proyecto
{formatCurrency(kpiData.beneficioProyecto)}

📊 Resumen Financiero

formatCurrency(value)} />
{estadosData.map((estado, idx) => (

{estado.name}

{estado.value.toFixed(2)}%
{estado.name}
{formatNumber(estado.count)} unidades
))}
); } return
Vista en desarrollo...
; }; return (
{sidebarOpen && (
🚀
ECOMFACTORY
)}
{sidebarOpen &&
MAIN
}
{sidebarOpen && (
L
Loja
)}

{activeView === 'home' ? '🏠 Home' : activeView === 'dashboard' ? '📊 Dashboard' : activeView === 'importar' ? '📂 Importar Datos' : activeView === 'pedidos' ? '📦 Pedidos' : activeView === 'productos' ? '🛍️ Productos' : activeView === 'anuncios' ? '📢 Anuncios' : activeView === 'valores' ? '💵 Valores Adicionales' : activeView === 'integraciones' ? '🔗 Integraciones' : activeView === 'calculadora' ? '🧮 Calculadora COD' : activeView === 'apoyo' ? '❓ Apoyo' : activeView.charAt(0).toUpperCase() + activeView.slice(1)}

{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'}

L
{renderContent()}
); }