Files

16 KiB

Plan: Implementación de Capa de Servicios API

Objetivo

Crear la estructura de servicios API en src/services/ para consumir una API externa, organizada por secciones del proyecto (Dashboard, Académico, Cursos, Empresas, Secretaría, Informes). Los archivos de servicio serán .ts puros (sin JSX) y se importarán desde cada page.tsx reemplazando los datos hardcodeados actuales.


Estructura de Directorios

src/
├── services/
│   ├── api.ts                         ← Cliente HTTP base
│   ├── types/
│   │   ├── dashboard.ts
│   │   ├── academico.ts
│   │   ├── cursos.ts
│   │   ├── empresas.ts
│   │   ├── secretaria.ts
│   │   └── informes.ts
│   ├── dashboard.ts                   ← Servicios del Dashboard
│   ├── academico.ts                   ← Servicios de Académico
│   ├── cursos.ts                      ← Servicios de Cursos
│   ├── empresas.ts                    ← Servicios de Empresas
│   ├── secretaria.ts                  ← Servicios de Secretaría
│   └── informes.ts                    ← Servicios de Informes

Total: 14 archivos nuevos


1. src/services/api.ts — Cliente HTTP Base

Wrapper de fetch con manejo de errores, headers automáticos y métodos HTTP helper.

const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || '';

async function request<T>(endpoint: string, options?: RequestInit): Promise<T> {
  const res = await fetch(`${API_BASE_URL}${endpoint}`, {
    headers: { 'Content-Type': 'application/json', ...options?.headers },
    ...options,
  });
  if (!res.ok) throw new Error(`API Error: ${res.status}`);
  return res.json();
}

export const api = {
  get:    <T>(url: string) => request<T>(url),
  post:   <T>(url: string, body: unknown) => request<T>(url, { method: 'POST', body: JSON.stringify(body) }),
  put:    <T>(url: string, body: unknown) => request<T>(url, { method: 'PUT', body: JSON.stringify(body) }),
  delete: <T>(url: string) => request<T>(url, { method: 'DELETE' }),
};

2. src/services/types/ — Interfaces TypeScript por Sección

dashboard.ts

export interface CursoDelDia {
  id: number;
  tipo: string;
  curso: string;
  profesor: string;
  sede: string;
  sala: string;
  horario: string;
  inicio: string;
  termino: string;
  sesion: string;
}

export interface EstadisticasDashboard {
  empresas: number;
  cursos: number;
  alumnosConSence: number;
  alumnosSinSence: number;
  total: number;
}

academico.ts

export interface Programa {
  nivel: string;
  curso: string;
  horasCronologicas: string;
  cefrLevel: string;
  libro: string;
  senceOnline: string;
  sencePresencial: string;
}

export interface Libro {
  id: string;
  nombre: string;
  nivel: string;
}

export interface CodigoSence {
  codigo: string;
  descripcion: string;
  tipo: string;
}

export interface Ponderacion {
  cursoId: string;
  ponderaciones: { evaluacion: string; porcentaje: number }[];
}

cursos.ts

export interface CursoAdmin {
  codigo: string;
  nombre: string;
  periodo: string;
  fechaInicio: string;
  fechaTermino: string;
  sede: string;
  estado: string;
  sala: string;
  profesor: string;
  sesiones: number;
  horario: string;
}

export interface SesionCurso {
  id: string;
  fecha: string;
  horaInicio: string;
  horaFin: string;
  estado: string;
  profesor: string;
}

export interface AsistenciaAlumno {
  rut: string;
  nombre: string;
  asistio: boolean;
  fecha: string;
}

export interface NotaAlumno {
  rut: string;
  nombre: string;
  nota: number;
  evaluacion: string;
}

empresas.ts

export interface EmpresaItem {
  rut: string;
  razonSocial: string;
  giro: string;
  correo: string;
  ultimoContacto: string;
}

export interface EmpresaDetalle {
  rut: string;
  razonSocial: string;
  giro: string;
  direccion: string;
  comuna: string;
  fono: string;
  correo: string;
  contacto: string;
}

export interface CotizacionItem {
  cotizacion: string;
  contratosAsociados: string;
  tipoCotizacion: string;
  creacion: string;
  estadoCotizacion: string;
  valor: string;
  idTipoCurso: string;
  condicionCotizacion: string;
  montoDescto: string;
}

export interface ContratoItem {
  contrato: string;
  firmaEstado: number;
  url: string;
  cursoInfo: string;
  valor: string;
  autorizacionSSEE?: string;
}

export interface DocumentoItem {
  detalle: string;
  numeroInterno?: string;
  fechaSubida: string;
  tipoDocumento?: string;
  codInscripcion?: string;
  responsable: string;
}

export interface AlumnoItem {
  rut: string;
  nombre: string;
  email: string;
}

export interface ComprobanteOC {
  detalle: string;
  numeroInterno: string;
  fechaSubida: string;
  tipoDocumento: string;
  responsable: string;
}

secretaria.ts

export interface Alumno {
  idAlumno: string;
  AP_Paterno: string;
  AP_Materno: string;
  Nombres: string;
}

export interface FichaAlumno {
  idAlumno: string;
  nombre: string;
  paterno: string;
  materno: string;
  rut: string;
  telefono: string;
  email: string;
  direccion: string;
  comuna: string;
  region: string;
  fechaNacimiento: string;
  ocupacion: string;
  profesion: string;
  cursos: CursoAlumno[];
  contratos: ContratoAlumno[];
  apoderados: ApoderadoAlumno[];
  certificados: CertificadoAlumno[];
  comentarios: ComentarioAlumno[];
}

export interface CursoAlumno {
  year: number;
  id: number;
  curso: string;
  contrato: string;
  fechaInicio: string;
  fechaTermino: string;
  estado: string;
}

export interface ContratoAlumno { /* campos por definir */ }
export interface ApoderadoAlumno { /* campos por definir */ }
export interface CertificadoAlumno { /* campos por definir */ }
export interface ComentarioAlumno { /* campos por definir */ }

export interface FichaProfesor {
  idProfesor: string;
  nombre: string;
  paterno: string;
  materno: string;
  rut: string;
  telefono: string;
  email: string;
  cursos: CursoProfesor[];
}

export interface CursoProfesor {
  codigo: string;
  curso: string;
  sede: string;
  horario: string;
  alumnos: number;
}

export interface Evaluacion {
  cursoId: string;
  alumnoId: string;
  tipo: string;
  nota: number;
  observaciones: string;
}

informes.ts

export interface CursoItem {
  Codigo: string;
  Curso: string;
  TipoCurso: string;
  Periodo: string;
  Titular: string;
  Sede: string;
  Alumnos: number;
  Inicio: string;
  Termino: string;
  Schedule: string;
  Estado: string;
}

export interface MetricasInforme {
  empresasTotal: number;
  cursosTotal: number;
  alumnosConSence: number;
  alumnosSinSence: number;
  alumnosTotal: number;
}

export interface InformeAlumno {
  rut: string;
  nombre: string;
  curso: string;
  estado: string;
  notas: number;
  asistencia: number;
}

3. Servicios por Sección — Funciones API

services/dashboard.ts

import { api } from './api';
import type { CursoDelDia, EstadisticasDashboard } from './types/dashboard';

export const obtenerCursosDelDia = (sede?: string, jornada?: string) =>
  api.get<CursoDelDia[]>(`/api/dashboard/cursos-dia?sede=${sede || ''}&jornada=${jornada || ''}`);

export const obtenerEstadisticas = () =>
  api.get<EstadisticasDashboard>('/api/dashboard/estadisticas');

services/academico.ts

import { api } from './api';
import type { Programa, Libro, CodigoSence, Ponderacion } from './types/academico';

export const obtenerProgramas = () =>
  api.get<Programa[]>('/api/academico/programas');

export const obtenerCursosPorPrograma = (programaId: string) =>
  api.get<Programa[]>(`/api/academico/programas/${programaId}/cursos`);

export const agregarCursoPrograma = (data: Omit<Programa, 'id'>) =>
  api.post('/api/academico/cursos', data);

export const editarCursoPrograma = (id: string, data: Partial<Programa>) =>
  api.put(`/api/academico/cursos/${id}`, data);

export const obtenerLibros = () =>
  api.get<Libro[]>('/api/academico/libros');

export const obtenerCodigosSence = (tipo: 'abiertos' | 'cerrados') =>
  api.get<CodigoSence[]>(`/api/academico/sence/${tipo}`);

export const obtenerPonderaciones = (cursoId: string) =>
  api.get<Ponderacion>(`/api/academico/ponderaciones/${cursoId}`);

export const guardarPonderaciones = (cursoId: string, data: Ponderacion) =>
  api.put(`/api/academico/ponderaciones/${cursoId}`, data);

services/cursos.ts

import { api } from './api';
import type { CursoAdmin, SesionCurso, AsistenciaAlumno, NotaAlumno } from './types/cursos';

export const obtenerCurso = (codigo: string) =>
  api.get<CursoAdmin>(`/api/cursos/${codigo}`);

export const editarFechasCurso = (codigo: string, data: { fechaInicio: string; fechaTermino: string }) =>
  api.put(`/api/cursos/${codigo}/fechas`, data);

export const editarHorarioCurso = (codigo: string, data: { horario: string }) =>
  api.put(`/api/cursos/${codigo}/horario`, data);

export const cambiarProfesor = (codigo: string, data: { profesorId: string }) =>
  api.put(`/api/cursos/${codigo}/profesor`, data);

export const eliminarProfesor = (codigo: string) =>
  api.delete(`/api/cursos/${codigo}/profesor`);

export const asignarReemplazo = (codigo: string, data: { profesorId: string; sesiones: string[] }) =>
  api.post(`/api/cursos/${codigo}/reemplazos`, data);

export const obtenerSesiones = (codigo: string) =>
  api.get<SesionCurso[]>(`/api/cursos/${codigo}/sesiones`);

export const reemplazarSesion = (sesionId: string, data: { nuevoProfesorId: string }) =>
  api.put(`/api/cursos/sesiones/${sesionId}/reemplazar`, data);

export const suspenderSesion = (sesionId: string) =>
  api.put(`/api/cursos/sesiones/${sesionId}/suspender`);

export const obtenerAsistencias = (codigo: string) =>
  api.get<AsistenciaAlumno[]>(`/api/cursos/${codigo}/asistencias`);

export const actualizarAsistencia = (codigo: string, data: AsistenciaAlumno[]) =>
  api.put(`/api/cursos/${codigo}/asistencias`, data);

export const obtenerNotas = (codigo: string) =>
  api.get<NotaAlumno[]>(`/api/cursos/${codigo}/notas`);

export const actualizarNotas = (codigo: string, data: NotaAlumno[]) =>
  api.put(`/api/cursos/${codigo}/notas`, data);

services/empresas.ts

import { api } from './api';
import type {
  EmpresaItem, EmpresaDetalle, CotizacionItem,
  ContratoItem, DocumentoItem, AlumnoItem, ComprobanteOC
} from './types/empresas';

export const obtenerEmpresas = () =>
  api.get<EmpresaItem[]>('/api/empresas');

export const obtenerEmpresa = (rut: string) =>
  api.get<EmpresaDetalle>(`/api/empresas/${rut}`);

export const obtenerCotizaciones = (rutEmpresa: string) =>
  api.get<CotizacionItem[]>(`/api/empresas/${rutEmpresa}/cotizaciones`);

export const obtenerContratos = (cotizacionId: string) =>
  api.get<ContratoItem[]>(`/api/cotizaciones/${cotizacionId}/contratos`);

export const verificarFirmaContrato = (contratoId: string) =>
  api.get<{ firmado: boolean }>(`/api/contratos/${contratoId}/firma`);

export const obtenerDocumentos = (contratoId: string) =>
  api.get<DocumentoItem[]>(`/api/contratos/${contratoId}/documentos`);

export const subirDocumento = (contratoId: string, data: FormData) =>
  api.post(`/api/contratos/${contratoId}/documentos`, data);

export const obtenerAlumnosContrato = (contratoId: string) =>
  api.get<AlumnoItem[]>(`/api/contratos/${contratoId}/alumnos`);

export const obtenerComprobantesOC = (contratoId: string) =>
  api.get<ComprobanteOC[]>(`/api/contratos/${contratoId}/comprobantes`);

export const obtenerInscripcionesSNC = (contratoId: string) =>
  api.get<DocumentoItem[]>(`/api/contratos/${contratoId}/inscripciones`);

services/secretaria.ts

import { api } from './api';
import type { Alumno, FichaAlumno, FichaProfesor, Evaluacion } from './types/secretaria';

export const buscarAlumno = (tipo: 'run' | 'nombre', texto: string) =>
  api.get<Alumno[]>(`/api/secretaria/alumnos?tipo=${tipo}&q=${texto}`);

export const obtenerFichaAlumno = (idAlumno: string) =>
  api.get<FichaAlumno>(`/api/secretaria/alumnos/${idAlumno}`);

export const editarAlumno = (idAlumno: string, data: Partial<FichaAlumno>) =>
  api.put(`/api/secretaria/alumnos/${idAlumno}`, data);

export const cambiarCursoAlumno = (idAlumno: string, data: { cursoNuevoId: string }) =>
  api.put(`/api/secretaria/alumnos/${idAlumno}/curso`, data);

export const obtenerFichaProfesor = (idProfesor: string) =>
  api.get<FichaProfesor>(`/api/secretaria/profesores/${idProfesor}`);

export const ingresarEvaluacion = (data: Evaluacion) =>
  api.post('/api/secretaria/evaluaciones', data);

services/informes.ts

import { api } from './api';
import type { CursoItem, MetricasInforme, InformeAlumno } from './types/informes';

export const obtenerCursosActivos = (mes: number, anio: number) =>
  api.get<CursoItem[]>(`/api/informes/cursos-activos?mes=${mes}&anio=${anio}`);

export const obtenerMetricas = (mes: number, anio: number) =>
  api.get<MetricasInforme>(`/api/informes/metricas?mes=${mes}&anio=${anio}`);

export const obtenerInformesAlumnos = (filtros?: { curso?: string; empresa?: string }) =>
  api.get<InformeAlumno[]>('/api/informes/alumnos');

export const obtenerSesionesProfesor = (profesorId: string) =>
  api.get('/api/informes/sesiones-profesor');

4. Consumo desde las Páginas (Antes vs Después)

ANTES (datos hardcodeados)

const alumnos = [
  { id: 101, tipo: "Regular", curso: "Inglés Básico", profesor: "María López", ... }
];

DESPUÉS (usando servicio)

import { obtenerCursosDelDia } from '@/services/dashboard';
import type { CursoDelDia } from '@/services/types/dashboard';

const [cursos, setCursos] = useState<CursoDelDia[]>([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
  obtenerCursosDelDia('MONEDA', 'Diurno')
    .then(setCursos)
    .catch(console.error)
    .finally(() => setLoading(false));
}, []);

5. Resumen de Archivos

# Archivo Propósito Funciones
1 services/api.ts Cliente HTTP base 4 métodos
2 services/types/dashboard.ts Interfaces Dashboard 2
3 services/types/academico.ts Interfaces Académico 4
4 services/types/cursos.ts Interfaces Cursos 4
5 services/types/empresas.ts Interfaces Empresas 7
6 services/types/secretaria.ts Interfaces Secretaría 5
7 services/types/informes.ts Interfaces Informes 3
8 services/dashboard.ts Funciones API Dashboard 2
9 services/academico.ts Funciones API Académico 8
10 services/cursos.ts Funciones API Cursos 12
11 services/empresas.ts Funciones API Empresas 10
12 services/secretaria.ts Funciones API Secretaría 6
13 services/informes.ts Funciones API Informes 4
Total 13 archivos ~47 funciones

6. Variables de Entorno Necesarias

Agregar al archivo .env.local:

NEXT_PUBLIC_API_URL=https://tu-api.cl

7. Orden de Implementación Sugerido

  1. Crear src/services/api.ts (cliente HTTP base)
  2. Crear src/services/types/ (todas las interfaces)
  3. Crear servicios por sección en orden de prioridad:
    • dashboard.ts (más visible, primer impacto)
    • empresas.ts (el más complejo, interfaces más definidas)
    • secretaria.ts (búsqueda de alumnos, fichas)
    • cursos.ts (administrador de cursos)
    • academico.ts (programas, libros, SENCE)
    • informes.ts (reportes)
  4. Reemplazar datos mock en cada page.tsx correspondiente

8. Notas Importantes

  • Todos los servicios son archivos .ts puros (sin JSX)
  • Las interfaces están separadas en types/ para reutilización
  • El cliente HTTP maneja automáticamente Content-Type: application/json
  • Las URLs de endpoints son ejemplos — ajustar según la API real
  • Las interfaces pueden necesitar campos adicionales según la respuesta real de la API
  • Las funciones de subirDocumento usan FormData para uploads de archivos