# 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. ```typescript const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || ''; async function request(endpoint: string, options?: RequestInit): Promise { 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: (url: string) => request(url), post: (url: string, body: unknown) => request(url, { method: 'POST', body: JSON.stringify(body) }), put: (url: string, body: unknown) => request(url, { method: 'PUT', body: JSON.stringify(body) }), delete: (url: string) => request(url, { method: 'DELETE' }), }; ``` --- ## 2. `src/services/types/` — Interfaces TypeScript por Sección ### `dashboard.ts` ```typescript 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` ```typescript 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` ```typescript 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` ```typescript 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` ```typescript 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` ```typescript 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` ```typescript import { api } from './api'; import type { CursoDelDia, EstadisticasDashboard } from './types/dashboard'; export const obtenerCursosDelDia = (sede?: string, jornada?: string) => api.get(`/api/dashboard/cursos-dia?sede=${sede || ''}&jornada=${jornada || ''}`); export const obtenerEstadisticas = () => api.get('/api/dashboard/estadisticas'); ``` ### `services/academico.ts` ```typescript import { api } from './api'; import type { Programa, Libro, CodigoSence, Ponderacion } from './types/academico'; export const obtenerProgramas = () => api.get('/api/academico/programas'); export const obtenerCursosPorPrograma = (programaId: string) => api.get(`/api/academico/programas/${programaId}/cursos`); export const agregarCursoPrograma = (data: Omit) => api.post('/api/academico/cursos', data); export const editarCursoPrograma = (id: string, data: Partial) => api.put(`/api/academico/cursos/${id}`, data); export const obtenerLibros = () => api.get('/api/academico/libros'); export const obtenerCodigosSence = (tipo: 'abiertos' | 'cerrados') => api.get(`/api/academico/sence/${tipo}`); export const obtenerPonderaciones = (cursoId: string) => api.get(`/api/academico/ponderaciones/${cursoId}`); export const guardarPonderaciones = (cursoId: string, data: Ponderacion) => api.put(`/api/academico/ponderaciones/${cursoId}`, data); ``` ### `services/cursos.ts` ```typescript import { api } from './api'; import type { CursoAdmin, SesionCurso, AsistenciaAlumno, NotaAlumno } from './types/cursos'; export const obtenerCurso = (codigo: string) => api.get(`/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(`/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(`/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(`/api/cursos/${codigo}/notas`); export const actualizarNotas = (codigo: string, data: NotaAlumno[]) => api.put(`/api/cursos/${codigo}/notas`, data); ``` ### `services/empresas.ts` ```typescript import { api } from './api'; import type { EmpresaItem, EmpresaDetalle, CotizacionItem, ContratoItem, DocumentoItem, AlumnoItem, ComprobanteOC } from './types/empresas'; export const obtenerEmpresas = () => api.get('/api/empresas'); export const obtenerEmpresa = (rut: string) => api.get(`/api/empresas/${rut}`); export const obtenerCotizaciones = (rutEmpresa: string) => api.get(`/api/empresas/${rutEmpresa}/cotizaciones`); export const obtenerContratos = (cotizacionId: string) => api.get(`/api/cotizaciones/${cotizacionId}/contratos`); export const verificarFirmaContrato = (contratoId: string) => api.get<{ firmado: boolean }>(`/api/contratos/${contratoId}/firma`); export const obtenerDocumentos = (contratoId: string) => api.get(`/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(`/api/contratos/${contratoId}/alumnos`); export const obtenerComprobantesOC = (contratoId: string) => api.get(`/api/contratos/${contratoId}/comprobantes`); export const obtenerInscripcionesSNC = (contratoId: string) => api.get(`/api/contratos/${contratoId}/inscripciones`); ``` ### `services/secretaria.ts` ```typescript import { api } from './api'; import type { Alumno, FichaAlumno, FichaProfesor, Evaluacion } from './types/secretaria'; export const buscarAlumno = (tipo: 'run' | 'nombre', texto: string) => api.get(`/api/secretaria/alumnos?tipo=${tipo}&q=${texto}`); export const obtenerFichaAlumno = (idAlumno: string) => api.get(`/api/secretaria/alumnos/${idAlumno}`); export const editarAlumno = (idAlumno: string, data: Partial) => 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(`/api/secretaria/profesores/${idProfesor}`); export const ingresarEvaluacion = (data: Evaluacion) => api.post('/api/secretaria/evaluaciones', data); ``` ### `services/informes.ts` ```typescript import { api } from './api'; import type { CursoItem, MetricasInforme, InformeAlumno } from './types/informes'; export const obtenerCursosActivos = (mes: number, anio: number) => api.get(`/api/informes/cursos-activos?mes=${mes}&anio=${anio}`); export const obtenerMetricas = (mes: number, anio: number) => api.get(`/api/informes/metricas?mes=${mes}&anio=${anio}`); export const obtenerInformesAlumnos = (filtros?: { curso?: string; empresa?: string }) => api.get('/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) ```typescript const alumnos = [ { id: 101, tipo: "Regular", curso: "Inglés Básico", profesor: "María López", ... } ]; ``` ### DESPUÉS (usando servicio) ```typescript import { obtenerCursosDelDia } from '@/services/dashboard'; import type { CursoDelDia } from '@/services/types/dashboard'; const [cursos, setCursos] = useState([]); 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