workspace/projects/gamilit/CONTRIBUTING.md
rckrdmrd ea1879f4ad feat: Initial workspace structure with multi-level Git configuration
- Configure workspace Git repository with comprehensive .gitignore
- Add Odoo as submodule for ERP reference code
- Include documentation: SETUP.md, GIT-STRUCTURE.md
- Add gitignore templates for projects (backend, frontend, database)
- Structure supports independent repos per project/subproject level

Workspace includes:
- core/ - Reusable patterns, modules, orchestration system
- projects/ - Active projects (erp-suite, gamilit, trading-platform, etc.)
- knowledge-base/ - Reference code and patterns (includes Odoo submodule)
- devtools/ - Development tools and templates
- customers/ - Client implementations template

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 10:44:23 -06:00

14 KiB

Contributing to GAMILIT

¡Gracias por tu interés en contribuir a GAMILIT! Este documento te guiará a través del proceso de contribución.


📋 Tabla de Contenidos

  1. Código de Conducta
  2. Pre-requisitos
  3. Setup del Entorno de Desarrollo
  4. Workflow de Desarrollo
  5. Constants SSOT - Pre-Commit Checklist
  6. Estándares de Código
  7. Commits y Pull Requests
  8. Testing
  9. Documentación

Código de Conducta

Este proyecto sigue un código de conducta profesional. Se espera que todos los contribuyentes:

  • Sean respetuosos y constructivos en sus interacciones
  • Acepten críticas constructivas con profesionalismo
  • Se enfoquen en lo mejor para la comunidad y el proyecto
  • Muestren empatía hacia otros miembros de la comunidad

Pre-requisitos

Antes de contribuir, asegúrate de tener instalado:

  • Node.js >= 18.0.0
  • npm >= 9.0.0
  • Git >= 2.30
  • PostgreSQL >= 15.x (para desarrollo local)
  • Docker (opcional, para contenedores)

Setup del Entorno de Desarrollo

1. Fork y Clone

# Fork el repositorio en GitHub
# Luego clona tu fork
git clone https://github.com/TU_USUARIO/gamilit.git
cd gamilit/projects/gamilit

# Agrega el upstream
git remote add upstream https://github.com/GAMILIT_ORG/gamilit.git

2. Instalar Dependencias

# Instalar dependencias root (incluye postinstall que ejecuta sync:enums)
npm install

# Instalar dependencias Backend
cd apps/backend
npm install

# Instalar dependencias Frontend
cd ../frontend
npm install

# Volver a root
cd ../..

3. Configurar Variables de Entorno

# Backend
cp apps/backend/.env.example apps/backend/.env
# Editar apps/backend/.env con tus credenciales

# Frontend
cp apps/frontend/.env.example apps/frontend/.env
# Editar apps/frontend/.env con tu API URL

4. Setup de Base de Datos

# Crear base de datos
createdb gamilit_dev

# Ejecutar migraciones (cuando estén disponibles)
cd apps/backend
npm run migration:run

5. Verificar Setup

# Desde root, ejecutar validaciones
npm run validate:all

# Debe retornar:
# ✅ ENUMs sincronizados exitosamente
# ✅ EXCELENTE! No se encontraron violaciones de hardcoding
# ✅ API Contract validado

Workflow de Desarrollo

1. Crear Branch

# Actualizar main
git checkout main
git pull upstream main

# Crear feature branch
git checkout -b feature/mi-nueva-funcionalidad
# O bug fix branch
git checkout -b fix/corregir-bug-autenticacion

Convención de nombres de branches:

  • feature/ - Nuevas funcionalidades
  • fix/ - Corrección de bugs
  • refactor/ - Refactorización de código
  • docs/ - Cambios en documentación
  • test/ - Agregar o mejorar tests
  • chore/ - Tareas de mantenimiento

2. Desarrollar

# Backend
cd apps/backend
npm run dev  # Modo desarrollo con hot-reload

# Frontend (en otra terminal)
cd apps/frontend
npm run dev  # Vite dev server

3. Hacer Commits

Ver sección Commits y Pull Requests para convenciones.


Constants SSOT - Pre-Commit Checklist

IMPORTANTE: Antes de hacer commit, SIEMPRE ejecutar este checklist para asegurar el cumplimiento de la política SSOT.

Checklist Obligatorio

# ✅ 1. Sincronizar ENUMs Backend → Frontend
npm run sync:enums

# ✅ 2. Validar que no haya hardcoding (33 patrones detectados)
npm run validate:constants

# ✅ 3. Validar contrato API Backend ↔ Frontend
npm run validate:api-contract

# ✅ 4. Ejecutar tests
npm run test

# ✅ 5. Ejecutar linter
npm run lint

# ✅ 6. Si TODO pasa, hacer commit
git add .
git commit -m "feat: agregar nueva funcionalidad"

Atajo para validar todo:

npm run validate:all && npm run test && git commit

¿Cuándo actualizar constantes?

Al crear nueva tabla Database:

  1. Crear DDL en platform/db/ddl/schemas/{schema}/tables/{tabla}.sql
  2. INMEDIATAMENTE agregar entrada en apps/backend/src/shared/constants/database.constants.ts:
export const DB_TABLES = {
  AUTH: {
    // ... existing tables
    NEW_TABLE: 'new_table', // ← AGREGAR AQUÍ
  },
};
  1. Crear Entity usando la nueva constante:
import { DB_SCHEMAS, DB_TABLES } from '@/shared/constants';

@Entity({ schema: DB_SCHEMAS.AUTH, name: DB_TABLES.AUTH.NEW_TABLE })
export class NewTable { ... }

Al crear nueva ruta API Backend:

  1. PRIMERO agregar entrada en apps/backend/src/shared/constants/routes.constants.ts:
export const API_ROUTES = {
  NEW_MODULE: {
    BASE: '/new-module',
    BY_ID: (id: string) => `/new-module/${id}`,
  },
};
  1. Crear Controller usando la nueva constante:
import { API_ROUTES } from '@/shared/constants';

@Controller(API_ROUTES.NEW_MODULE.BASE.replace('/', ''))
export class NewModuleController { ... }
  1. INMEDIATAMENTE agregar en Frontend apps/frontend/src/shared/constants/api-endpoints.ts:
export const API_ENDPOINTS = {
  NEW_MODULE: {
    BASE: `${API_BASE_URL}/new-module`,
    BY_ID: (id: string) => `${API_BASE_URL}/new-module/${id}`,
  },
};
  1. Validar sincronización:
npm run validate:api-contract
# Debe retornar: ✅ API Contract validado

Al crear nuevo ENUM:

  1. Definir ENUM solo en Backend apps/backend/src/shared/constants/enums.constants.ts:
export enum NewEnum {
  VALUE_1 = 'value_1',
  VALUE_2 = 'value_2',
}
  1. Ejecutar script de sincronización:
npm run sync:enums
  1. Verificar que Frontend tiene el mismo ENUM:
diff apps/backend/src/shared/constants/enums.constants.ts \
     apps/frontend/src/shared/constants/enums.constants.ts

# Debe retornar: (sin diferencias)
  1. NO modificar manualmente el archivo Frontend (se sobrescribe en sync).

Violaciones Comunes y Cómo Evitarlas

Violación 1: Hardcoded schema en Entity

Detectado por: npm run validate:constants

// ❌ MAL
@Entity({ schema: 'auth_management', name: 'users' })
export class User { ... }

// ✅ BIEN
import { DB_SCHEMAS, DB_TABLES } from '@/shared/constants';

@Entity({ schema: DB_SCHEMAS.AUTH, name: DB_TABLES.AUTH.USERS })
export class User { ... }

Violación 2: Hardcoded API URL en Frontend

Detectado por: npm run validate:constants

// ❌ MAL
await fetch('http://localhost:3000/api/v1/users/123');

// ✅ BIEN
import { API_ENDPOINTS } from '@/shared/constants';

await fetch(API_ENDPOINTS.USERS.BY_ID('123'));

Violación 3: Discrepancia Backend ↔ Frontend

Detectado por: npm run validate:api-contract

❌ Ruta "/users/:id/settings" existe en Backend pero NO en Frontend

Solución: Sincronizar ambos archivos (Backend routes.constants.ts y Frontend api-endpoints.ts)

Documentación de Referencia


Estándares de Código

TypeScript

  • Estilo: Airbnb TypeScript Style Guide
  • Linter: ESLint con reglas de @typescript-eslint
  • Formatter: Prettier con configuración del proyecto
# Ejecutar linter
npm run lint

# Ejecutar formatter
npm run format

Naming Conventions

Variables y Funciones:

// camelCase
const userName = 'John';
function getUserById(id: string) { ... }

Clases y Interfaces:

// PascalCase
class UserService { ... }
interface IUserRepository { ... }

Constantes:

// UPPER_SNAKE_CASE para constantes globales
export const MAX_LOGIN_ATTEMPTS = 3;

// PascalCase para ENUMs
export enum UserRole {
  ADMIN = 'admin',
  TEACHER = 'teacher',
  STUDENT = 'student',
}

Archivos:

user.entity.ts        # Entities
user.dto.ts           # DTOs
user.service.ts       # Services
user.controller.ts    # Controllers
user.module.ts        # Modules
user.spec.ts          # Tests

Import Organization

// 1. Node modules
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';

// 2. Internal modules
import { DB_SCHEMAS, DB_TABLES } from '@/shared/constants';
import { User } from '@/modules/auth/entities/user.entity';

// 3. Relative imports
import { CreateUserDto } from './dto/create-user.dto';

Commits y Pull Requests

Conventional Commits

Usamos Conventional Commits para mensajes de commit:

<type>(<scope>): <subject>

[optional body]

[optional footer]

Types:

  • feat: Nueva funcionalidad
  • fix: Corrección de bug
  • refactor: Refactorización de código
  • docs: Cambios en documentación
  • test: Agregar o mejorar tests
  • chore: Tareas de mantenimiento
  • style: Cambios de formato (no afectan lógica)
  • perf: Mejoras de performance

Ejemplos:

feat(auth): agregar autenticación con Google OAuth

fix(gamification): corregir cálculo de puntos en quiz

refactor(database): migrar entities a usar DB_SCHEMAS constants

docs(readme): actualizar sección de Constants SSOT

test(users): agregar tests para UserService

Pull Request Process

  1. Actualizar branch con main:
git checkout main
git pull upstream main
git checkout tu-feature-branch
git rebase main
  1. Ejecutar validaciones:
# ✅ Validar SSOT
npm run validate:all

# ✅ Ejecutar tests
npm run test

# ✅ Ejecutar linter
npm run lint
  1. Crear Pull Request en GitHub:

Título: Usar Conventional Commits format

feat(auth): agregar autenticación con Google OAuth

Descripción: Incluir template

## Descripción

Breve descripción de los cambios realizados.

## Tipo de cambio

- [ ] 🐛 Bug fix (cambio que corrige un issue)
- [ ] ✨ Nueva funcionalidad (cambio que agrega funcionalidad)
- [ ] 💥 Breaking change (fix o feature que causa cambios incompatibles)
- [ ] 📝 Documentación

## Checklist

- [ ] Mi código sigue las convenciones del proyecto
- [ ] He ejecutado `npm run validate:all`- [ ] He ejecutado `npm run test` y todos los tests pasan ✅
- [ ] He ejecutado `npm run lint`- [ ] He actualizado la documentación correspondiente
- [ ] Mis cambios no generan nuevas advertencias
- [ ] He agregado tests que prueban mi fix/funcionalidad
- [ ] Tests unitarios y de integración pasan localmente
- [ ] He sincronizado ENUMs si modifiqué Backend enums (`npm run sync:enums`)
- [ ] He actualizado constantes si agregué schemas/tablas/rutas

## Screenshots (si aplica)

## Notas adicionales
  1. Code Review:
  • Mínimo 1 aprobación requerida
  • CI/CD debe pasar (validate-constants.yml + tests)
  • Resolver todos los comentarios antes de merge
  1. Merge:
  • Squash and merge preferido para features pequeñas
  • Merge commit para features grandes
  • Rebase and merge para branches con commits limpios

Testing

Backend Tests

cd apps/backend

# Ejecutar todos los tests
npm run test

# Tests en modo watch
npm run test:watch

# Cobertura de tests
npm run test:cov

Estructura de tests:

describe('UserService', () => {
  let service: UserService;
  let repository: Repository<User>;

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      providers: [UserService, /* ... */],
    }).compile();

    service = module.get<UserService>(UserService);
    repository = module.get<Repository<User>>(getRepositoryToken(User));
  });

  describe('findById', () => {
    it('should return a user when found', async () => {
      const user = { id: '1', email: 'test@test.com' };
      jest.spyOn(repository, 'findOne').mockResolvedValue(user as User);

      expect(await service.findById('1')).toBe(user);
    });

    it('should throw NotFoundException when user not found', async () => {
      jest.spyOn(repository, 'findOne').mockResolvedValue(null);

      await expect(service.findById('999')).rejects.toThrow(NotFoundException);
    });
  });
});

Frontend Tests

cd apps/frontend

# Ejecutar tests
npm run test

# Tests con UI
npm run test:ui

# Cobertura
npm run test:coverage

Estructura de tests:

import { render, screen, fireEvent } from '@testing-library/react';
import { LoginForm } from './LoginForm';

describe('LoginForm', () => {
  it('renders email and password inputs', () => {
    render(<LoginForm />);

    expect(screen.getByLabelText(/email/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/password/i)).toBeInTheDocument();
  });

  it('calls onSubmit when form is submitted', async () => {
    const mockSubmit = jest.fn();
    render(<LoginForm onSubmit={mockSubmit} />);

    fireEvent.change(screen.getByLabelText(/email/i), {
      target: { value: 'test@test.com' },
    });
    fireEvent.click(screen.getByRole('button', { name: /login/i }));

    expect(mockSubmit).toHaveBeenCalledWith({
      email: 'test@test.com',
      password: expect.any(String),
    });
  });
});

Documentación

Documentar Código

JSDoc para funciones públicas:

/**
 * Obtiene un usuario por su ID
 *
 * @param id - UUID del usuario
 * @returns Promise con el usuario encontrado
 * @throws NotFoundException si el usuario no existe
 *
 * @example
 * ```typescript
 * const user = await userService.findById('123e4567-e89b-12d3-a456-426614174000');
 * ```
 */
async findById(id: string): Promise<User> {
  // ...
}

Actualizar Documentación

Si tus cambios afectan:

  • API Endpoints: Actualizar Swagger/OpenAPI docs
  • Schemas de Database: Actualizar DDL y documentation
  • Funcionalidades nuevas: Actualizar README.md y docs relevantes
  • Cambios arquitectónicos: Crear ADR (Architecture Decision Record)

¿Preguntas?


¡Gracias por contribuir a GAMILIT! 🚀

Versión: 1.0 Última actualización: 2025-11-02