596 lines
16 KiB
Markdown
596 lines
16 KiB
Markdown
# 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<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`
|
|
|
|
```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<CursoDelDia[]>(`/api/dashboard/cursos-dia?sede=${sede || ''}&jornada=${jornada || ''}`);
|
|
|
|
export const obtenerEstadisticas = () =>
|
|
api.get<EstadisticasDashboard>('/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<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`
|
|
|
|
```typescript
|
|
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`
|
|
|
|
```typescript
|
|
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`
|
|
|
|
```typescript
|
|
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`
|
|
|
|
```typescript
|
|
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)
|
|
```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<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
|