# Guía de Integración Angular: Cursos, Inscripciones y Tasas de Cambio

Esta guía describe las nuevas funcionalidades del backend y provee ejemplos y directrices para integrarlas en el frontend de Angular.

---

## 1. Resumen de Funcionalidades Creadas

1. **Gestión de Horarios y Precios en Cursos**:
   - Campos adicionales en `courses`: `teacher_id`, `start_date`, `end_date`, `days` (array de strings), `start_time`, `end_time`, `duration_months`, `price_usd`.
2. **Historial y CRUD de Tasas de Cambio (BCV)**:
   - API para registrar y listar tasas BCV de Dólar y Euro.
   - Seguridad y Control de Acceso por Roles (RBAC):
     - **Root / Gerente**: CRUD Completo (Ver, Crear, Editar, Eliminar).
     - **Admin**: Ver, Crear y Editar (Eliminar deshabilitado / 403).
     - **Asistente**: Solo Ver y Crear (Editar y Eliminar deshabilitados / 403).
3. **Inscripciones con Detección de Colisiones de Horarios**:
   - Endpoint `/api/enrollments` que valida en tiempo real si el estudiante tiene otro curso activo cuyos rangos de fechas, días de la semana y horas coincidan simultáneamente.

---

## 2. Modelos e Interfaces en Angular

Define estas interfaces para modelar las respuestas del backend:

```typescript
// src/app/core/models/exchange-rate.model.ts
export interface ExchangeRate {
  id: number;
  tenant_id: string;
  rate_bcv_usd: number;
  rate_bcv_eur: number;
  rate_date: string;
  created_at: string;
  updated_at: string;
}

// src/app/core/models/course.model.ts
export interface Course {
  id: number;
  tenant_id: string;
  teacher_id?: number;
  name: string;
  description?: string;
  start_date: string; // YYYY-MM-DD
  end_date: string;   // YYYY-MM-DD
  days: string[];     // e.g. ['monday', 'wednesday']
  start_time: string; // HH:MM:SS
  end_time: string;   // HH:MM:SS
  duration_months: string; // e.g. "2 meses"
  price_usd: number;
  status: 'active' | 'inactive';
}
```

---

## 3. Servicios Angular

### Servicio para Tasas de Cambio (`ExchangeRateService`)

```typescript
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { ExchangeRate } from '../models/exchange-rate.model';

@Injectable({
  providedIn: 'root'
})
export class ExchangeRateService {
  private apiUrl = '/api/exchange-rates';

  constructor(private http: HttpClient) {}

  getRates(): Observable<ExchangeRate[]> {
    return this.http.get<ExchangeRate[]>(this.apiUrl);
  }

  createRate(rate: Omit<ExchangeRate, 'id' | 'tenant_id' | 'created_at' | 'updated_at'>): Observable<ExchangeRate> {
    return this.http.post<ExchangeRate>(this.apiUrl, rate);
  }

  updateRate(id: number, rate: Omit<ExchangeRate, 'id' | 'tenant_id' | 'created_at' | 'updated_at'>): Observable<ExchangeRate> {
    return this.http.put<ExchangeRate>(`${this.apiUrl}/${id}`, rate);
  }

  deleteRate(id: number): Observable<void> {
    return this.http.delete<void>(`${this.apiUrl}/${id}`);
  }
}
```

### Servicio para Inscripciones (`EnrollmentService`)

```typescript
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class EnrollmentService {
  private apiUrl = '/api/enrollments';

  constructor(private http: HttpClient) {}

  enrollStudent(studentId: number, courseId: number): Observable<any> {
    return this.http.post<any>(this.apiUrl, {
      student_id: studentId,
      course_id: courseId
    });
  }
}
```

---

## 4. Manejo de Errores de Colisión (HTTP 422)

Cuando el backend detecta un choque de horarios, retorna un código de estado `422 Unprocessable Entity` con un mensaje de validación claro:

```json
{
  "message": "Conflicto de horario con el curso \"Math 101\". Ambos cursos coinciden en fecha, días y horario.",
  "errors": {
    "course_id": [
      "Conflicto de horario con el curso \"Math 101\". Ambos cursos coinciden en fecha, días y horario."
    ]
  }
}
```

Puedes capturarlo en tu componente de Angular de la siguiente manera:

```typescript
this.enrollmentService.enrollStudent(this.studentId, this.courseId).subscribe({
  next: (res) => {
    this.toastService.success('¡Estudiante inscrito correctamente!');
  },
  error: (err) => {
    if (err.status === 422) {
      // Captura el conflicto de horarios
      const conflictMsg = err.error.errors?.course_id?.[0] || err.error.message;
      this.toastService.error(conflictMsg);
    } else {
      this.toastService.error('Ocurrió un error inesperado.');
    }
  }
});
```

---

## 5. Control de Visibilidad por Roles (RBAC) en la UI

Para evitar que los usuarios hagan llamadas que el backend rechazará con `403 Forbidden`, gestiona la visibilidad en tus vistas HTML utilizando el rol del usuario logueado.

### Ejemplo de Configuración en el Componente

```typescript
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../services/auth.service';

@Component({
  selector: 'app-exchange-rates',
  templateUrl: './exchange-rates.component.html'
})
export class ExchangeRatesComponent implements OnInit {
  userRole: string = '';

  constructor(private authService: AuthService) {}

  ngOnInit(): void {
    // Asume que tu servicio de Auth expone el rol actual
    this.userRole = this.authService.getCurrentUserRole(); // e.g. 'root' | 'gerente' | 'admin' | 'asistente'
  }

  canEdit(): boolean {
    return ['root', 'gerente', 'admin'].includes(this.userRole);
  }

  canDelete(): boolean {
    return ['root', 'gerente'].includes(this.userRole);
  }

  canCreate(): boolean {
    return ['root', 'gerente', 'admin', 'asistente'].includes(this.userRole);
  }
}
```

### Ejemplo en la Vista HTML (`exchange-rates.component.html`)

```html
<!-- Botón para Registrar Tasa (Visible para Asistente, Admin, Gerente, Root) -->
<button *ngIf="canCreate()" (click)="openCreateModal()">
  Registrar Nueva Tasa
</button>

<!-- Tabla de Tasas -->
<table>
  <tr *ngFor="let rate of rates">
    <td>{{ rate.rate_bcv_usd | currency }}</td>
    <td>{{ rate.rate_date | date }}</td>
    <td>
      <!-- Botón Editar (Bloqueado para Asistente) -->
      <button *ngIf="canEdit()" (click)="editRate(rate)">
        Editar
      </button>
      
      <!-- Botón Eliminar (Solo Root y Gerente) -->
      <button *ngIf="canDelete()" (click)="deleteRate(rate.id)">
        Eliminar
      </button>
    </td>
  </tr>
</table>
```
