AvanzaCast/docs/SHARED_MODULES.md

8.2 KiB

Módulos Compartidos - AvanzaCast

📋 Descripción

Los módulos compartidos (shared/) contienen código reutilizable que se usa en todos los módulos de la aplicación (landing-page, broadcast-studio, admin-panel). Esto garantiza consistencia en autenticación, idioma y componentes UI.

📦 Estructura de Módulos Compartidos

shared/
├── types/          # TypeScript types compartidos
├── utils/          # Utilidades (auth, i18n, api client)
├── hooks/          # React hooks compartidos
└── components/     # Componentes React compartidos

🔑 Autenticación Compartida

Hook useAuth()

Todos los módulos frontend usan el mismo hook de autenticación:

import { useAuth } from '@avanzacast/shared-hooks';

function MyComponent() {
  const { user, isAuthenticated, login, logout, register } = useAuth();

  const handleLogin = async () => {
    const result = await login({ 
      email: 'user@example.com', 
      password: 'password123' 
    });
    
    if (result.success) {
      console.log('Login exitoso');
    } else {
      console.error(result.error);
    }
  };

  return (
    <div>
      {isAuthenticated ? (
        <p>Bienvenido, {user?.name}</p>
      ) : (
        <button onClick={handleLogin}>Iniciar sesión</button>
      )}
    </div>
  );
}

Funciones de utilidad

import { 
  saveTokens, 
  getTokens, 
  clearAuth, 
  isAuthenticated 
} from '@avanzacast/shared-utils';

// Guardar tokens después del login
saveTokens({ accessToken: '...', refreshToken: '...', expiresIn: 3600 });

// Verificar si está autenticado
if (isAuthenticated()) {
  // Usuario logueado
}

// Cerrar sesión
clearAuth();

🌍 Internacionalización (i18n)

Hook useLanguage()

import { useLanguage } from '@avanzacast/shared-hooks';

function MyComponent() {
  const { language, setLanguage, t, availableLanguages } = useLanguage();

  return (
    <div>
      <h1>{t('landing.hero_title')}</h1>
      <p>Idioma actual: {language}</p>
      
      <select 
        value={language} 
        onChange={(e) => setLanguage(e.target.value as any)}
      >
        {availableLanguages.map(lang => (
          <option key={lang.code} value={lang.code}>
            {lang.flag} {lang.name}
          </option>
        ))}
      </select>
    </div>
  );
}

Traducciones disponibles

Las traducciones están en shared/utils/i18n.ts y soportan:

  • Español (es) 🇪🇸
  • English (en) 🇺🇸
  • Português (pt) 🇧🇷
  • Français (fr) 🇫🇷

Claves de traducción:

// Comunes
t('common.login')        // "Iniciar sesión"
t('common.register')     // "Registrarse"
t('common.email')        // "Correo electrónico"
t('common.password')     // "Contraseña"

// Navegación
t('nav.home')            // "Inicio"
t('nav.features')        // "Características"
t('nav.broadcasts')      // "Transmisiones"
t('nav.studio')          // "Estudio"

// Landing page
t('landing.hero_title')  // "La manera más sencilla de transmitir..."
t('landing.get_started') // "Empecemos"

// Autenticación
t('auth.login_title')    // "Inicia sesión en tu cuenta"
t('auth.register_title') // "Crea tu cuenta"

🧩 Componentes Compartidos

LanguageSelector

Selector de idioma que se sincroniza entre todos los módulos:

import { LanguageSelector } from '@avanzacast/shared-components';

function Header() {
  return (
    <header>
      {/* Dropdown con banderas */}
      <LanguageSelector variant="dropdown" position="right" />
      
      {/* Solo banderas */}
      <LanguageSelector variant="flags" />
    </header>
  );
}

AuthButton

Botón de login/logout que se adapta al estado de autenticación:

import { AuthButton } from '@avanzacast/shared-components';

function Header() {
  return (
    <header>
      <AuthButton 
        variant="primary" 
        size="md"
        loginUrl="/auth/login"
        dashboardUrl="/broadcasts"
      />
    </header>
  );
}

🔌 API Client

Cliente HTTP centralizado para hacer peticiones al backend:

import { apiClient } from '@avanzacast/shared-utils';

// Login
const response = await apiClient.login({ 
  email: 'user@example.com', 
  password: 'password123' 
});

// Register
const response = await apiClient.register({
  email: 'new@example.com',
  password: 'password123',
  name: 'John Doe'
});

// Get profile
const response = await apiClient.getProfile();

// Custom endpoint
const response = await apiClient.get('/broadcasts');
const response = await apiClient.post('/broadcasts', { title: 'Mi transmisión' });

📘 TypeScript Types

Todos los tipos están centralizados en shared/types/:

import type { 
  User, 
  AuthTokens, 
  LoginCredentials, 
  RegisterData,
  SupportedLanguage,
  Broadcast,
  Guest
} from '@avanzacast/shared-types';

const user: User = {
  id: '123',
  email: 'user@example.com',
  name: 'John Doe',
  role: 'user',
  createdAt: new Date(),
  updatedAt: new Date()
};

🚀 Uso en los Módulos

Landing Page

// packages/landing-page/src/App.tsx
import { useAuth, useLanguage } from '@avanzacast/shared-hooks';
import { LanguageSelector, AuthButton } from '@avanzacast/shared-components';

export function App() {
  const { t } = useLanguage();
  const { isAuthenticated } = useAuth();

  return (
    <div>
      <header>
        <LanguageSelector />
        <AuthButton />
      </header>
      <h1>{t('landing.hero_title')}</h1>
    </div>
  );
}

Broadcast Studio

// packages/broadcast-studio/src/App.tsx
import { useAuth } from '@avanzacast/shared-hooks';
import { useEffect } from 'react';

export function App() {
  const { isAuthenticated, user } = useAuth();

  useEffect(() => {
    if (!isAuthenticated) {
      window.location.href = '/auth/login?redirect=/studio';
    }
  }, [isAuthenticated]);

  return (
    <div>
      <h1>Estudio de {user?.name}</h1>
    </div>
  );
}

Admin Panel

// packages/admin-panel/src/App.tsx
import { useAuth, useLanguage } from '@avanzacast/shared-hooks';

export function App() {
  const { user } = useAuth();
  const { language, setLanguage } = useLanguage();

  return (
    <div>
      <h1>Admin Panel - {user?.name}</h1>
      <p>Idioma: {language}</p>
    </div>
  );
}

🔄 Sincronización entre módulos

Cuando un usuario:

  1. Inicia sesión en landing-page → Los datos se guardan en localStorage
  2. Navega a broadcast-studio → El hook useAuth() lee el mismo localStorage
  3. Cambia el idioma en studio → El cambio se refleja en todos los módulos
  4. Cierra sesión en admin-panel → Todos los módulos detectan el logout

⚙️ Configuración de Variables de Entorno

Cada módulo frontend necesita configurar la URL del backend:

# packages/landing-page/.env
VITE_API_URL=http://localhost:4000/api/v1

# packages/broadcast-studio/.env
VITE_API_URL=http://localhost:4000/api/v1

# packages/admin-panel/.env
VITE_API_URL=http://localhost:4000/api/v1

📝 Añadir nuevas traducciones

Edita shared/utils/i18n.ts:

const translations: Record<SupportedLanguage, Translation> = {
  es: {
    myNewSection: {
      title: 'Mi nuevo título',
      description: 'Mi nueva descripción'
    }
  },
  en: {
    myNewSection: {
      title: 'My new title',
      description: 'My new description'
    }
  },
  // ... pt, fr
};

Luego úsalo:

const { t } = useLanguage();
console.log(t('myNewSection.title'));

🎯 Beneficios de los Módulos Compartidos

Consistencia: Mismo comportamiento de auth e i18n en todos los módulos Mantenibilidad: Cambiar una vez, actualiza en todos lados Reutilización: No duplicar código entre módulos Type Safety: TypeScript types compartidos evitan inconsistencias Sincronización: Cambios en un módulo se reflejan en otros automáticamente

🔧 Próximos Pasos

  1. Implementar backend API endpoints (/auth/login, /auth/register)
  2. Agregar refresh token automático cuando el access token expire
  3. Implementar protección de rutas en cada módulo
  4. Añadir más traducciones según se necesite
  5. Crear más componentes compartidos (Modals, Alerts, Forms, etc.)