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:
- Inicia sesión en landing-page → Los datos se guardan en localStorage
- Navega a broadcast-studio → El hook
useAuth()lee el mismo localStorage - Cambia el idioma en studio → El cambio se refleja en todos los módulos
- 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
- Implementar backend API endpoints (
/auth/login,/auth/register) - Agregar refresh token automático cuando el access token expire
- Implementar protección de rutas en cada módulo
- Añadir más traducciones según se necesite
- Crear más componentes compartidos (Modals, Alerts, Forms, etc.)