Saltar al contenido principal

Guía de Desarrollo Frontend CDP

Configuración del Entorno

Requisitos Previos

  • Node.js: v18.0.0 o superior
  • npm: v9.0.0 o superior
  • Git: v2.30.0 o superior
  • Editor: VSCode recomendado

Instalación Inicial

# Clonar el repositorio
git clone https://github.com/NomadaDigital01/nerdistan-cdp-frontend.git
cd nerdistan-cdp-frontend

# Instalar dependencias
npm install

# Configurar variables de entorno
cp .env.example .env

# Editar .env con tu configuración
VITE_API_URL=https://nerdistan-datalake-production.up.railway.app

Estructura del Proyecto

nerdistan-cdp-frontend/
├── src/
│ ├── components/ # Componentes React
│ │ ├── DashboardTremor.jsx
│ │ ├── CDPAnalytics.jsx
│ │ ├── DataViewer.jsx
│ │ ├── VTEXIntegrationConfig.jsx
│ │ └── GoogleAds/
│ │ └── AudienceManager.jsx
│ ├── assets/ # Imágenes y recursos
│ ├── styles/ # Estilos globales
│ ├── utils/ # Utilidades y helpers
│ ├── hooks/ # React hooks personalizados
│ ├── App.jsx # Componente principal
│ ├── main.jsx # Entry point
│ └── index.css # Estilos globales
├── public/ # Assets públicos
├── dist/ # Build de producción
├── package.json # Dependencias y scripts
├── vite.config.js # Configuración de Vite
├── tailwind.config.js # Configuración de Tailwind
└── README.md # Documentación

Comandos de Desarrollo

Scripts Disponibles

# Desarrollo local con hot reload
npm run dev

# Build de producción
npm run build

# Preview del build
npm run preview

# Linting (futuro)
npm run lint

# Tests (futuro)
npm run test

Desarrollo Local

# Iniciar servidor de desarrollo
npm run dev

# El servidor estará disponible en:
# http://localhost:5173

Variables de Entorno

# .env.development
VITE_API_URL=http://localhost:8000
VITE_ENV=development
VITE_DEBUG=true

# .env.production
VITE_API_URL=https://nerdistan-datalake-production.up.railway.app
VITE_ENV=production
VITE_DEBUG=false

Flujo de Desarrollo

1. Crear Nueva Feature

# Crear branch desde main
git checkout -b feature/nombre-feature

# Desarrollar la feature
# ... hacer cambios ...

# Commit con mensaje descriptivo
git add .
git commit -m "feat: agregar nueva funcionalidad X"

# Push al repositorio
git push origin feature/nombre-feature

# Crear Pull Request en GitHub

2. Convención de Commits

Seguimos Conventional Commits:

# Features
feat: agregar selector de tenants

# Fixes
fix: corregir cálculo de RFM

# Documentación
docs: actualizar README con nuevos endpoints

# Estilos
style: formatear código con prettier

# Refactor
refactor: simplificar lógica de filtrado

# Tests
test: agregar tests para DataViewer

# Chore
chore: actualizar dependencias

3. Estructura de Componentes

// Plantilla de componente
import React, { useState, useEffect } from 'react';
import { Card, Title, Text } from '@tremor/react';

const ComponentName = ({ prop1, prop2 }) => {
// Estado
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);

// Effects
useEffect(() => {
loadData();
}, [prop1]);

// Handlers
const loadData = async () => {
setLoading(true);
try {
const response = await fetch(`/api/data?param=${prop1}`);
const json = await response.json();
setData(json);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};

// Render
if (loading) return <LoadingState />;
if (error) return <ErrorState error={error} />;

return (
<Card>
<Title>{prop2}</Title>
<Text>{data?.description}</Text>
{/* Contenido del componente */}
</Card>
);
};

export default ComponentName;

Guías de Estilo

JavaScript/React

// ✅ Usar const/let, no var
const API_URL = 'https://api.example.com';
let counter = 0;

// ✅ Arrow functions para callbacks
const filtered = data.filter(item => item.active);

// ✅ Destructuring
const { name, email } = user;
const [first, ...rest] = array;

// ✅ Template literals
const message = `Hello ${name}, you have ${count} items`;

// ✅ Async/await sobre promises
const fetchData = async () => {
try {
const response = await fetch(url);
return await response.json();
} catch (error) {
console.error(error);
}
};

// ✅ Optional chaining
const city = user?.address?.city || 'Unknown';

// ✅ Nullish coalescing
const value = data ?? defaultValue;

CSS/Tailwind

// ✅ Usar clases de Tailwind
<div className="flex items-center justify-between p-4">
<h1 className="text-2xl font-bold text-gray-900">Title</h1>
</div>

// ✅ Condicionales con clsx
import clsx from 'clsx';

<div className={clsx(
'p-4 rounded',
isActive && 'bg-blue-500',
isError && 'bg-red-500'
)}>

// ❌ Evitar estilos inline
<div style={{ padding: '16px' }}> // No hacer esto

Organización de Imports

// 1. React y hooks
import React, { useState, useEffect } from 'react';

// 2. Librerías externas
import { Card, Title } from '@tremor/react';
import { ArrowPathIcon } from '@heroicons/react/24/outline';

// 3. Componentes locales
import DataTable from '../DataTable';

// 4. Utils y helpers
import { formatCurrency } from '../../utils/format';

// 5. Estilos
import './styles.css';

Debugging

Console Logging

// Solo en desarrollo
if (import.meta.env.DEV) {
console.log('Debug info:', data);
}

// Con grupos
console.group('API Response');
console.log('Status:', response.status);
console.log('Data:', response.data);
console.groupEnd();

// Con timing
console.time('API Call');
await fetchData();
console.timeEnd('API Call');

React DevTools

// Instalar extensión de Chrome/Firefox
// React Developer Tools

// Usar displayName para debugging
ComponentName.displayName = 'ComponentName';

// Props validation (desarrollo)
import PropTypes from 'prop-types';

ComponentName.propTypes = {
tenantId: PropTypes.number.isRequired,
tenantName: PropTypes.string
};

Network Debugging

// Interceptar requests
window.addEventListener('fetch', (event) => {
console.log('Fetching:', event.request.url);
});

// Mock responses en desarrollo
if (import.meta.env.DEV) {
window.fetch = new Proxy(window.fetch, {
apply(target, thisArg, args) {
console.log('Fetch:', args[0]);
return target.apply(thisArg, args);
}
});
}

Performance

Optimizaciones Comunes

// 1. Memoización de componentes
import { memo } from 'react';

const ExpensiveComponent = memo(({ data }) => {
return <ComplexVisualization data={data} />;
});

// 2. useMemo para cálculos costosos
import { useMemo } from 'react';

const processedData = useMemo(() => {
return heavyProcessing(rawData);
}, [rawData]);

// 3. useCallback para funciones estables
import { useCallback } from 'react';

const handleClick = useCallback((id) => {
doSomething(id);
}, [dependency]);

// 4. Lazy loading de componentes
import { lazy, Suspense } from 'react';

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

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

// 5. Virtual scrolling para listas largas
import { FixedSizeList } from 'react-window';

<FixedSizeList
height={600}
itemCount={items.length}
itemSize={50}
>
{Row}
</FixedSizeList>

Monitoreo de Performance

// Web Vitals
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';

getCLS(console.log);
getFID(console.log);
getFCP(console.log);
getLCP(console.log);
getTTFB(console.log);

// React Profiler
import { Profiler } from 'react';

<Profiler id="Dashboard" onRender={onRenderCallback}>
<Dashboard />
</Profiler>

const onRenderCallback = (id, phase, actualDuration) => {
console.log(`${id} (${phase}) took ${actualDuration}ms`);
};

Testing (Futuro)

Unit Tests con Vitest

// component.test.jsx
import { describe, it, expect, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import Component from './Component';

describe('Component', () => {
it('renders correctly', () => {
render(<Component title="Test" />);
expect(screen.getByText('Test')).toBeInTheDocument();
});

it('handles click events', async () => {
const handleClick = vi.fn();
render(<Component onClick={handleClick} />);

await userEvent.click(screen.getByRole('button'));
expect(handleClick).toHaveBeenCalledOnce();
});
});

Integration Tests

// integration.test.jsx
import { describe, it, expect } from 'vitest';
import { render, waitFor } from '@testing-library/react';
import Dashboard from './Dashboard';
import { server } from './mocks/server';

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

describe('Dashboard Integration', () => {
it('loads and displays tenant data', async () => {
render(<Dashboard />);

await waitFor(() => {
expect(screen.getByText('65,226 customers')).toBeInTheDocument();
});
});
});

Deployment

Build de Producción

# Build optimizado
npm run build

# Verificar el build
npm run preview

# Analizar bundle size
npx vite-bundle-visualizer

Configuración de Railway

# railway.json
{
"build": {
"builder": "NIXPACKS",
"buildCommand": "npm run build",
"watchPatterns": ["src/**"]
},
"deploy": {
"startCommand": "npm run preview",
"restartPolicyType": "ON_FAILURE",
"restartPolicyMaxRetries": 10
}
}

GitHub Actions CI/CD

# .github/workflows/deploy.yml
name: Deploy to Railway

on:
push:
branches: [main]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'

- name: Install dependencies
run: npm ci

- name: Run tests
run: npm test

- name: Build
run: npm run build

- name: Deploy to Railway
uses: bervProject/railway-deploy@main
with:
railway_token: ${{ secrets.RAILWAY_TOKEN }}

Troubleshooting

Problemas Comunes

1. CORS Errors

// Solución: Verificar configuración de backend
// O usar proxy en desarrollo
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:8000',
changeOrigin: true
}
}
}
});

2. Build Failures

# Limpiar cache
rm -rf node_modules dist
npm install
npm run build

3. Memory Issues

# Aumentar memoria para Node
NODE_OPTIONS="--max-old-space-size=4096" npm run build

4. Hot Reload No Funciona

// vite.config.js
export default defineConfig({
server: {
watch: {
usePolling: true // Para Docker/WSL
}
}
});

Recursos

Documentación Oficial

Herramientas Útiles

Comunidad

Checklist de Desarrollo

Antes de Commitear

  • Código formateado con Prettier
  • Sin errores en consola
  • Sin warnings de React
  • Responsive en móvil y desktop
  • Datos reales, no dummy
  • Error handling implementado
  • Loading states agregados

Antes de Pull Request

  • Branch actualizado con main
  • Commits siguiendo convención
  • Documentación actualizada
  • Screenshots si hay cambios UI
  • Testeado en producción-like
  • Sin console.logs de debug
  • Performance verificada

Review Checklist

  • Código legible y mantenible
  • Sigue patrones del proyecto
  • No introduce deuda técnica
  • Seguridad considerada
  • Accesibilidad verificada
  • Tests agregados (futuro)
  • Documentación clara