Skip to main content

Componentes del Frontend CDP

Visión General

El frontend del CDP está construido con una arquitectura de componentes modulares y reutilizables. Cada componente está diseñado para ser independiente, testeable y fácil de mantener.

Componentes Principales

DashboardTremor

Ubicación: src/components/DashboardTremor.jsx
Propósito: Componente principal que orquesta todo el dashboard

Props

No recibe props - es el componente raíz

Estado

const [selectedTenant, setSelectedTenant] = useState(56); // Tenant por defecto
const [tenantName, setTenantName] = useState('Chelsea IO - Exit');

Características

  • Selector de Tenants: Dropdown con 5 tenants principales (202K+ consumidores total)
  • Sistema de Tabs: 5 pestañas principales (Overview, CDP Analytics, Data Viewer, Google Ads, VTEX)
  • Responsive Design: Adaptable a móviles y desktop
  • Auto-refresh: Actualización automática cada 30 segundos

Tenants Disponibles

{
56: 'Chelsea IO - Exit (65.2K consumidores)',
52: 'Celada SA - BAPRO (45.3K consumidores)',
53: 'Cooperativa de Trabajo (15.9K consumidores)',
55: 'EL DORADO SOCIEDAD (11.7K consumidores)',
1: 'PZ Interamericana Textiles (11.4K consumidores)'
}

Ejemplo de Uso

// En App.jsx
import DashboardTremor from './components/DashboardTremor';

function App() {
return <DashboardTremor />;
}

CDPAnalytics

Ubicación: src/components/CDPAnalytics.jsx
Propósito: Análisis completo del Customer Data Platform

Props

interface CDPAnalyticsProps {
tenantId: number; // ID del tenant actual
tenantName: string; // Nombre del tenant para display
}

Funcionalidades

1. Análisis RFM
  • Endpoint: /api/v2/cdp/analytics/rfm
  • Segmentos: 8 segmentos RFM (Champions, Loyal, En Riesgo, etc.)
  • Visualización: Cards con métricas y distribución por segmento
  • Datos: Recency promedio, Frequency, Monetary value
2. Predicción de Churn
  • Endpoint: /api/v2/cdp/analytics/churn
  • ML Model: Predicción de probabilidad de abandono
  • Risk Levels: Alto (>70%), Medio (40-70%), Bajo (<40%)
  • Acciones: Recomendaciones automáticas por nivel de riesgo
3. Perfiles de Cliente
  • Endpoint: /api/v2/cdp/analytics/customer-profile
  • Detalles: Historial de compras, CLV, segmentación
  • Búsqueda: Por email, ID o nombre
  • Timeline: Visualización cronológica de interacciones

Estado Interno

const [loading, setLoading] = useState(false);
const [activeTab, setActiveTab] = useState('rfm');
const [rfmData, setRfmData] = useState(null);
const [churnData, setChurnData] = useState(null);
const [error, setError] = useState(null);

Ejemplo de Integración

<CDPAnalytics 
tenantId={56}
tenantName="Chelsea IO - Exit"
/>

DataViewer

Ubicación: src/components/DataViewer.jsx
Propósito: Visualización de datos raw del CDP con export CSV

Props

interface DataViewerProps {
tenantId: number;
tenantName: string;
}

Características

  • Tabla de Clientes: Lista paginada de clientes con todos sus atributos
  • Métricas Agregadas: Total clientes, ingresos, ticket promedio
  • Export CSV: Descarga de datos en formato CSV
  • Filtros: Por segmento, rango de fechas, valor monetario
  • Simulación de Datos: Genera datos de ejemplo cuando no hay reales

Columnas de la Tabla

  • ID Cliente
  • Nombre
  • Email
  • Segmento RFM
  • Frecuencia de compra
  • Valor promedio
  • CLV predicho
  • Riesgo de churn (%)

Funciones Principales

const loadData = async () => {
// Carga datos desde /api/v2/cdp/analytics/rfm
};

const exportToCSV = () => {
// Exporta tabla actual a CSV
};

const getSegmentColor = (segment) => {
// Retorna color Tremor según segmento
};

VTEXIntegrationConfig

Ubicación: src/components/VTEXIntegrationConfig.jsx
Propósito: Configuración de credenciales VTEX por tenant

Props

interface VTEXIntegrationConfigProps {
tenantId: number;
tenantName: string;
}

Campos de Configuración

  • Account Name: Nombre de la cuenta VTEX
  • App Key: Clave de aplicación VTEX
  • App Token: Token de aplicación VTEX
  • Environment: vtexcommercestable (producción) o vtexcommercebeta

Endpoints

  • GET /api/v2/tenant-integrations/{tenant_id}/vtex - Obtener config
  • POST /api/v2/tenant-integrations/{tenant_id}/vtex - Crear config
  • PUT /api/v2/tenant-integrations/{tenant_id}/vtex - Actualizar
  • DELETE /api/v2/tenant-integrations/{tenant_id}/vtex - Eliminar
  • POST /api/v2/tenant-integrations/{tenant_id}/vtex/test - Test conexión

Validaciones

// Validación de campos requeridos
const isValid = account && appKey && appToken && environment;

// Test de conexión antes de guardar
const testConnection = async () => {
const response = await fetch(
`${API_URL}/api/v2/tenant-integrations/${tenantId}/vtex/test`,
{ method: 'POST', body: JSON.stringify(config) }
);
};

GoogleAds/AudienceManager

Ubicación: src/components/GoogleAds/AudienceManager.jsx
Propósito: Gestión de audiencias para Google Ads Customer Match

Props

interface AudienceManagerProps {
tenantId: number;
tenantName: string;
}

Funcionalidades

  • Lista de Tenants: Vista de todos los tenants con estadísticas
  • Upload de Audiencias: Carga masiva a Google Ads
  • Historial: Registro de uploads anteriores
  • Métricas: Clientes listos vs. en proceso
  • Auto-refresh: Actualización cada 30 segundos

Estado de Tenants

const tenantStats = {
totalCustomers: 65226,
readyForUpload: 45000,
inProgress: 20226,
lastUpload: '2024-01-15',
status: 'ready' // ready | processing | error
};

Flujo de Upload

  1. Seleccionar tenant
  2. Elegir cuenta de Google Ads
  3. Configurar opciones (hash emails, incluir teléfonos)
  4. Iniciar upload
  5. Monitorear progreso
  6. Verificar en Google Ads

Componentes de UI (Tremor)

Card

import { Card, Title, Text } from '@tremor/react';

<Card>
<Title>Título</Title>
<Text>Descripción</Text>
{children}
</Card>

Metric

import { Metric } from '@tremor/react';

<Metric>{formatNumber(123456)}</Metric>
// Output: 123,456

TabGroup/TabList

import { TabGroup, TabList, Tab, TabPanels, TabPanel } from '@tremor/react';

<TabGroup>
<TabList>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
</TabList>
<TabPanels>
<TabPanel>Content 1</TabPanel>
<TabPanel>Content 2</TabPanel>
</TabPanels>
</TabGroup>

Table

import { 
Table, TableHead, TableRow, TableHeaderCell,
TableBody, TableCell
} from '@tremor/react';

<Table>
<TableHead>
<TableRow>
<TableHeaderCell>Header</TableHeaderCell>
</TableRow>
</TableHead>
<TableBody>
<TableRow>
<TableCell>Cell</TableCell>
</TableRow>
</TableBody>
</Table>

Badge

import { Badge } from '@tremor/react';

<Badge color="emerald">Success</Badge>
<Badge color="rose">Error</Badge>
<Badge color="amber">Warning</Badge>

Button

import { Button } from '@tremor/react';
import { ArrowPathIcon } from '@heroicons/react/24/outline';

<Button
icon={ArrowPathIcon}
onClick={handleClick}
loading={isLoading}
variant="primary" // primary | secondary | light
size="sm" // xs | sm | md | lg | xl
>
Actualizar
</Button>

Select

import { Select, SelectItem } from '@tremor/react';

<Select value={value} onValueChange={setValue}>
<SelectItem value="1">Option 1</SelectItem>
<SelectItem value="2">Option 2</SelectItem>
</Select>

ProgressBar

import { ProgressBar } from '@tremor/react';

<ProgressBar value={75} color="blue" />

Callout

import { Callout } from '@tremor/react';
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';

<Callout
title="Advertencia"
icon={ExclamationTriangleIcon}
color="amber"
>
Mensaje de advertencia
</Callout>

Patrones de Componentes

1. Loading States

const Component = () => {
const [loading, setLoading] = useState(false);

if (loading) {
return (
<Card>
<div className="animate-pulse">
<div className="h-4 bg-gray-200 rounded w-3/4 mb-2"></div>
<div className="h-4 bg-gray-200 rounded w-1/2"></div>
</div>
</Card>
);
}

return <ActualContent />;
};

2. Error Handling

const Component = () => {
const [error, setError] = useState(null);

if (error) {
return (
<Callout
title="Error"
color="rose"
icon={ExclamationTriangleIcon}
>
{error}
</Callout>
);
}

return <ActualContent />;
};

3. Empty States

const Component = ({ data }) => {
if (!data || data.length === 0) {
return (
<Card>
<div className="text-center py-8">
<CircleStackIcon className="h-12 w-12 text-gray-400 mx-auto" />
<Text className="mt-2">No hay datos disponibles</Text>
<Button onClick={reload} className="mt-4">
Recargar
</Button>
</div>
</Card>
);
}

return <DataTable data={data} />;
};

4. Responsive Grid

import { Grid, Col } from '@tremor/react';

<Grid numItems={1} numItemsSm={2} numItemsLg={3} className="gap-4">
<Col>
<Card>Content 1</Card>
</Col>
<Col>
<Card>Content 2</Card>
</Col>
<Col>
<Card>Content 3</Card>
</Col>
</Grid>

Composición de Componentes

Ejemplo Completo: Dashboard con Datos

const Dashboard = () => {
const [tenant, setTenant] = useState(56);
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);

useEffect(() => {
loadData();
}, [tenant]);

const loadData = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch(`/api/data?tenant=${tenant}`);
if (!response.ok) throw new Error('Failed to load');
const json = await response.json();
setData(json);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};

return (
<div className="p-6">
<TenantSelector value={tenant} onChange={setTenant} />

{error && <ErrorCallout message={error} />}

{loading ? (
<LoadingState />
) : data ? (
<DataGrid data={data} />
) : (
<EmptyState onReload={loadData} />
)}
</div>
);
};

Optimizaciones de Performance

1. Memoización

import { useMemo } from 'react';

const ExpensiveComponent = ({ data }) => {
const processedData = useMemo(() => {
return data.map(item => ({
...item,
calculated: expensiveCalculation(item)
}));
}, [data]);

return <DataTable data={processedData} />;
};

2. Lazy Loading

import { lazy, Suspense } from 'react';

const HeavyComponent = lazy(() => import('./HeavyComponent'));

<Suspense fallback={<LoadingSpinner />}>
<HeavyComponent />
</Suspense>

3. Debouncing

import { useCallback } from 'react';

const SearchComponent = () => {
const debouncedSearch = useCallback(
debounce((query) => {
performSearch(query);
}, 300),
[]
);

return (
<input
onChange={(e) => debouncedSearch(e.target.value)}
/>
);
};

Testing (Futuro)

Unit Tests

// Component.test.jsx
import { render, screen } from '@testing-library/react';
import Component from './Component';

test('renders component', () => {
render(<Component />);
expect(screen.getByText('Expected Text')).toBeInTheDocument();
});

Integration Tests

// Integration.test.jsx
import { render, waitFor } from '@testing-library/react';
import Dashboard from './Dashboard';

test('loads and displays data', async () => {
render(<Dashboard />);
await waitFor(() => {
expect(screen.getByText('Data Loaded')).toBeInTheDocument();
});
});

Guía de Estilo

Nomenclatura

  • Componentes: PascalCase (DashboardTremor.jsx)
  • Funciones: camelCase (loadData())
  • Constantes: UPPER_SNAKE_CASE (API_URL)
  • Props: camelCase (tenantId)

Estructura de Archivos

Component.jsx
├── Imports
├── Constants
├── Component Definition
│ ├── State
│ ├── Effects
│ ├── Handlers
│ └── Render
└── Export

Comentarios

// Comentarios single-line para explicaciones breves

/**
* Comentarios multi-line para documentación
* @param {number} tenantId - ID del tenant
* @returns {Promise<Object>} Datos del tenant
*/