workspace-v1/orchestration/directivas/simco/SIMCO-FRONTEND.md
rckrdmrd 66161b1566 feat: Workspace-v1 complete migration with NEXUS v3.4
Sistema NEXUS v3.4 migrado con:

Estructura principal:
- core/orchestration: Sistema SIMCO + CAPVED (27 directivas, 28 perfiles)
- core/catalog: Catalogo de funcionalidades reutilizables
- shared/knowledge-base: Base de conocimiento compartida
- devtools/scripts: Herramientas de desarrollo
- control-plane/registries: Control de servicios y CI/CD
- orchestration/: Configuracion de orquestacion de agentes

Proyectos incluidos (11):
- gamilit (submodule -> GitHub)
- trading-platform (OrbiquanTIA)
- erp-suite con 5 verticales:
  - erp-core, construccion, vidrio-templado
  - mecanicas-diesel, retail, clinicas
- betting-analytics
- inmobiliaria-analytics
- platform_marketing_content
- pos-micro, erp-basico

Configuracion:
- .gitignore completo para Node.js/Python/Docker
- gamilit como submodule (git@github.com:rckrdmrd/gamilit-workspace.git)
- Sistema de puertos estandarizado (3005-3199)

Generated with NEXUS v3.4 Migration System
EPIC-010: Configuracion Git y Repositorios
2026-01-04 03:37:42 -06:00

14 KiB

SIMCO: OPERACIONES FRONTEND (React/TypeScript)

Versión: 1.0.0 Fecha: 2025-12-08 Aplica a: Todo agente que trabaje con código frontend React Prioridad: OBLIGATORIA para operaciones de frontend


RESUMEN EJECUTIVO

Types alineados con Backend + Componentes tipados + Hooks para lógica = Frontend robusto.


PRINCIPIO FUNDAMENTAL

╔══════════════════════════════════════════════════════════════════════╗
║  ALINEACIÓN BACKEND ↔ FRONTEND                                       ║
║                                                                       ║
║  • Types/Interfaces DEBEN coincidir con DTOs del backend             ║
║  • Endpoints DEBEN consumirse según Swagger                          ║
║  • Estados de error DEBEN manejarse consistentemente                 ║
║  • Si Backend cambia, Frontend DEBE cambiar                          ║
╚══════════════════════════════════════════════════════════════════════╝

ESTRUCTURA DE FRONTEND

{FRONTEND_SRC}/
├── apps/                            # Aplicaciones
│   └── {app}/
│       ├── pages/                   # Páginas/rutas
│       │   └── {Nombre}Page.tsx
│       ├── components/              # Componentes de la app
│       │   └── {Nombre}.tsx
│       ├── hooks/                   # Hooks de la app
│       │   └── use{Nombre}.ts
│       └── layouts/                 # Layouts de la app
│           └── {Nombre}Layout.tsx
├── shared/                          # Compartido entre apps
│   ├── components/                  # Componentes compartidos
│   │   ├── ui/                      # Componentes UI base
│   │   └── common/                  # Componentes comunes
│   ├── hooks/                       # Hooks compartidos
│   │   └── use{Nombre}.ts
│   ├── services/                    # Servicios
│   │   └── api/                     # Servicios de API
│   │       └── {nombre}.api.ts
│   ├── stores/                      # State management
│   │   └── {nombre}Store.ts
│   ├── types/                       # Tipos/Interfaces
│   │   └── {nombre}.types.ts
│   └── utils/                       # Utilidades
│       └── {nombre}.utils.ts
└── config/                          # Configuraciones
    └── api.config.ts

CONVENCIONES DE NOMENCLATURA

Archivos

// Componentes: PascalCase.tsx
UserProfile.tsx
LoginForm.tsx
DashboardCard.tsx

// Páginas: PascalCase + Page.tsx
DashboardPage.tsx
UserProfilePage.tsx
SettingsPage.tsx

// Hooks: use + PascalCase.ts
useUser.ts
useAuth.ts
useDashboard.ts

// Types: camelCase.types.ts
user.types.ts
auth.types.ts
dashboard.types.ts

// API services: camelCase.api.ts
user.api.ts
auth.api.ts

// Stores: camelCase + Store.ts
userStore.ts
authStore.ts

Componentes y Funciones

// Componentes: PascalCase
export const UserProfile: React.FC<UserProfileProps> = () => {}
export const LoginForm: React.FC<LoginFormProps> = () => {}

// Hooks: use + PascalCase
export const useUser = () => {}
export const useAuth = () => {}

// Funciones helper: camelCase
export const formatDate = () => {}
export const validateEmail = () => {}

// Constantes: UPPER_SNAKE_CASE
export const API_BASE_URL = ''
export const MAX_RETRIES = 3

Types e Interfaces

// Interfaces para props: PascalCase + Props
interface UserProfileProps {}
interface LoginFormProps {}

// Types de dominio: PascalCase
type User = {}
type AuthState = {}

// Types de API response: PascalCase + Response
type UserResponse = {}
type LoginResponse = {}

// Enums: PascalCase
enum UserStatus {
    Active = 'active',
    Inactive = 'inactive',
}

TEMPLATES

Types (alineados con Backend)

// shared/types/user.types.ts

/**
 * User Types
 *
 * Tipos alineados con DTOs del backend.
 * @see {BACKEND_SRC}/modules/users/dto/
 */

/**
 * User entity type
 * Alineado con: UserEntity del backend
 */
export interface User {
    id: string;
    email: string;
    username: string;
    firstName: string;
    lastName: string;
    status: UserStatus;
    createdAt: string;
    updatedAt: string;
}

/**
 * Estado del usuario
 */
export enum UserStatus {
    Active = 'active',
    Inactive = 'inactive',
    Suspended = 'suspended',
}

/**
 * DTO para crear usuario
 * Alineado con: CreateUserDto del backend
 */
export interface CreateUserInput {
    email: string;
    username: string;
    password: string;
    firstName: string;
    lastName: string;
}

/**
 * DTO para actualizar usuario
 * Alineado con: UpdateUserDto del backend
 */
export interface UpdateUserInput {
    email?: string;
    username?: string;
    firstName?: string;
    lastName?: string;
    status?: UserStatus;
}

/**
 * Respuesta de lista de usuarios
 */
export interface UsersListResponse {
    data: User[];
    total: number;
    page: number;
    limit: number;
}

API Service

// shared/services/api/user.api.ts

import { apiClient } from '@/config/api.config';
import type {
    User,
    CreateUserInput,
    UpdateUserInput,
    UsersListResponse,
} from '@/shared/types/user.types';

/**
 * User API Service
 *
 * Servicios para interactuar con endpoints de usuarios.
 * @see Swagger: /api/docs#/Users
 */

const BASE_PATH = '/users';

/**
 * Obtiene lista de usuarios
 */
export const getUsers = async (
    page = 1,
    limit = 20,
): Promise<UsersListResponse> => {
    const response = await apiClient.get(BASE_PATH, {
        params: { page, limit },
    });
    return response.data;
};

/**
 * Obtiene un usuario por ID
 */
export const getUserById = async (id: string): Promise<User> => {
    const response = await apiClient.get(`${BASE_PATH}/${id}`);
    return response.data;
};

/**
 * Crea un nuevo usuario
 */
export const createUser = async (data: CreateUserInput): Promise<User> => {
    const response = await apiClient.post(BASE_PATH, data);
    return response.data;
};

/**
 * Actualiza un usuario
 */
export const updateUser = async (
    id: string,
    data: UpdateUserInput,
): Promise<User> => {
    const response = await apiClient.put(`${BASE_PATH}/${id}`, data);
    return response.data;
};

/**
 * Elimina un usuario
 */
export const deleteUser = async (id: string): Promise<void> => {
    await apiClient.delete(`${BASE_PATH}/${id}`);
};

Hook

// shared/hooks/useUser.ts

import { useState, useEffect, useCallback } from 'react';
import type { User } from '@/shared/types/user.types';
import * as userApi from '@/shared/services/api/user.api';

/**
 * Hook para gestión de usuario
 *
 * Proporciona estado y operaciones para un usuario.
 *
 * @example
 * ```tsx
 * const { user, loading, error, refetch } = useUser(userId);
 * ```
 */
export const useUser = (userId: string | null) => {
    const [user, setUser] = useState<User | null>(null);
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState<Error | null>(null);

    /**
     * Carga el usuario
     */
    const fetchUser = useCallback(async () => {
        if (!userId) {
            setUser(null);
            return;
        }

        setLoading(true);
        setError(null);

        try {
            const data = await userApi.getUserById(userId);
            setUser(data);
        } catch (err) {
            setError(err instanceof Error ? err : new Error('Unknown error'));
            setUser(null);
        } finally {
            setLoading(false);
        }
    }, [userId]);

    /**
     * Actualiza el usuario
     */
    const updateUser = useCallback(
        async (data: Partial<User>) => {
            if (!userId) return;

            setLoading(true);
            setError(null);

            try {
                const updated = await userApi.updateUser(userId, data);
                setUser(updated);
                return updated;
            } catch (err) {
                setError(err instanceof Error ? err : new Error('Update failed'));
                throw err;
            } finally {
                setLoading(false);
            }
        },
        [userId],
    );

    // Cargar usuario al montar o cambiar ID
    useEffect(() => {
        fetchUser();
    }, [fetchUser]);

    return {
        user,
        loading,
        error,
        refetch: fetchUser,
        updateUser,
    };
};

Componente

// apps/{app}/components/UserProfile.tsx

import React from 'react';
import type { User } from '@/shared/types/user.types';

/**
 * Props del componente UserProfile
 */
interface UserProfileProps {
    /** Usuario a mostrar */
    user: User;
    /** Si mostrar avatar */
    showAvatar?: boolean;
    /** Callback al editar */
    onEdit?: (user: User) => void;
    /** Clases CSS adicionales */
    className?: string;
}

/**
 * UserProfile - Muestra información del perfil de usuario
 *
 * @component
 * @example
 * ```tsx
 * <UserProfile
 *   user={currentUser}
 *   showAvatar={true}
 *   onEdit={handleEdit}
 * />
 * ```
 */
export const UserProfile: React.FC<UserProfileProps> = ({
    user,
    showAvatar = true,
    onEdit,
    className = '',
}) => {
    const handleEditClick = () => {
        onEdit?.(user);
    };

    return (
        <div className={`user-profile ${className}`}>
            {showAvatar && (
                <div className="user-profile__avatar">
                    {/* Avatar implementation */}
                </div>
            )}

            <div className="user-profile__info">
                <h2 className="user-profile__name">
                    {user.firstName} {user.lastName}
                </h2>
                <p className="user-profile__email">{user.email}</p>
                <span className={`user-profile__status user-profile__status--${user.status}`}>
                    {user.status}
                </span>
            </div>

            {onEdit && (
                <button
                    className="user-profile__edit-btn"
                    onClick={handleEditClick}
                    aria-label="Edit profile"
                >
                    Edit
                </button>
            )}
        </div>
    );
};

export default UserProfile;

Página

// apps/{app}/pages/UserProfilePage.tsx

import React from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { useUser } from '@/shared/hooks/useUser';
import { UserProfile } from '../components/UserProfile';
import { LoadingSpinner } from '@/shared/components/ui/LoadingSpinner';
import { ErrorMessage } from '@/shared/components/ui/ErrorMessage';

/**
 * UserProfilePage - Página de perfil de usuario
 *
 * @page
 * @route /users/:id
 */
export const UserProfilePage: React.FC = () => {
    const { id } = useParams<{ id: string }>();
    const navigate = useNavigate();
    const { user, loading, error, refetch } = useUser(id ?? null);

    const handleEdit = () => {
        navigate(`/users/${id}/edit`);
    };

    if (loading) {
        return <LoadingSpinner message="Loading user profile..." />;
    }

    if (error) {
        return (
            <ErrorMessage
                message={error.message}
                onRetry={refetch}
            />
        );
    }

    if (!user) {
        return <ErrorMessage message="User not found" />;
    }

    return (
        <div className="user-profile-page">
            <header className="user-profile-page__header">
                <h1>User Profile</h1>
            </header>

            <main className="user-profile-page__content">
                <UserProfile
                    user={user}
                    showAvatar={true}
                    onEdit={handleEdit}
                />
            </main>
        </div>
    );
};

export default UserProfilePage;

VALIDACIONES OBLIGATORIAS

# 1. Build (OBLIGATORIO)
cd @FRONTEND_ROOT
npm run build
# ✅ Debe completar sin errores

# 2. Lint (OBLIGATORIO)
npm run lint
# ✅ Debe pasar

# 3. Type check
npm run typecheck  # o tsc --noEmit
# ✅ Debe pasar

# 4. Iniciar aplicación
npm run dev
# ✅ Debe iniciar sin errores

# 5. Verificar en navegador
# - Sin errores en consola
# - Componentes renderizan correctamente
# - Interacciones funcionan

CHECKLIST FRONTEND

TYPES
├── [ ] Alineados con DTOs del backend
├── [ ] Interfaces para props documentadas
├── [ ] Enums para valores fijos
├── [ ] Types de API response
└── [ ] JSDoc en types públicos

API SERVICE
├── [ ] Endpoints según Swagger
├── [ ] Manejo de errores
├── [ ] Tipos de request/response
└── [ ] Documentación de cada función

HOOKS
├── [ ] Estados: data, loading, error
├── [ ] useCallback para funciones
├── [ ] useEffect para side effects
├── [ ] Cleanup si necesario
└── [ ] JSDoc con ejemplo

COMPONENTES
├── [ ] Props tipadas e interface
├── [ ] Props documentadas con JSDoc
├── [ ] defaultProps donde aplique
├── [ ] Accesibilidad (aria-*, roles)
├── [ ] Manejo de estados (loading, error)
└── [ ] className para estilos externos

PÁGINAS
├── [ ] Manejo de parámetros de ruta
├── [ ] Estados de carga y error
├── [ ] Layout apropiado
└── [ ] SEO (title, meta) si aplica

VALIDACIÓN
├── [ ] npm run build pasa
├── [ ] npm run lint pasa
├── [ ] npm run typecheck pasa
├── [ ] Sin errores en consola del navegador
└── [ ] Interacciones funcionan

ERRORES COMUNES

Error Causa Solución
Types desalineados Backend cambió Actualizar types según Swagger
Infinite loop useEffect sin deps Agregar dependencias correctas
Memory leak Sin cleanup Agregar cleanup en useEffect
Props any Falta tipar Crear interface de props
Build falla Errores TypeScript Corregir antes de continuar

REFERENCIAS

  • Crear archivos: @CREAR (SIMCO-CREAR.md)
  • Validar: @VALIDAR (SIMCO-VALIDAR.md)
  • Backend: @OP_BACKEND (SIMCO-BACKEND.md)
  • Guías Frontend: @GUIAS_FE
  • Nomenclatura: @DIRECTIVAS/ESTANDARES-NOMENCLATURA-BASE.md

Versión: 1.0.0 | Sistema: SIMCO | Mantenido por: Tech Lead