Se agrega servicio de APIREST y los endpoint para dashboard en conunto a funcionalidades de filtros.
This commit is contained in:
1 parent
a2fbd4b675
commit
d6e4af90b9
17 files changed
+1348
-56
No files matched your search
@@ -0,0 +1,595 @@
|
||||
# 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
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
const API_URL = process.env.API_URL || '';
|
||||
const API_KEY = process.env.API_KEY || '';
|
||||
|
||||
async function proxy(req: NextRequest, path: string[]) {
|
||||
if (!API_URL) {
|
||||
console.error('[Proxy] API_URL no está definida en .env.local');
|
||||
return NextResponse.json({ error: 'API_URL no configurada' }, { status: 500 });
|
||||
}
|
||||
|
||||
const target = `${API_URL}/${path.join('/')}`;
|
||||
const url = new URL(req.url);
|
||||
const query = url.search;
|
||||
|
||||
console.log('[Proxy] →', `${target}${query}`);
|
||||
|
||||
try {
|
||||
const headers: Record<string, string> = {
|
||||
'X-Api-Key': API_KEY,
|
||||
};
|
||||
const contentType = req.headers.get('content-type');
|
||||
if (contentType) headers['Content-Type'] = contentType;
|
||||
|
||||
const init: RequestInit = {
|
||||
method: req.method,
|
||||
headers,
|
||||
};
|
||||
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
||||
init.body = await req.text();
|
||||
}
|
||||
|
||||
const res = await fetch(`${target}${query}`, init);
|
||||
const text = await res.text();
|
||||
|
||||
console.log('[Proxy] ←', res.status, res.statusText);
|
||||
|
||||
if (!res.ok) {
|
||||
console.error('[Proxy] Respuesta no OK:', res.status, text);
|
||||
return new NextResponse(text, {
|
||||
status: res.status,
|
||||
headers: { 'Content-Type': res.headers.get('Content-Type') || 'application/json' },
|
||||
});
|
||||
}
|
||||
|
||||
return new NextResponse(text, {
|
||||
status: res.status,
|
||||
headers: { 'Content-Type': res.headers.get('Content-Type') || 'application/json' },
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('[Proxy] Error al llamar a la API externa:', target);
|
||||
console.error('[Proxy] Detalle:', err);
|
||||
return NextResponse.json(
|
||||
{ error: `Error al llamar a la API externa: ${err}` },
|
||||
{ status: 502 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
const { path } = await params;
|
||||
return proxy(req, path);
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
const { path } = await params;
|
||||
return proxy(req, path);
|
||||
}
|
||||
|
||||
export async function PUT(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
const { path } = await params;
|
||||
return proxy(req, path);
|
||||
}
|
||||
|
||||
export async function DELETE(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
|
||||
const { path } = await params;
|
||||
return proxy(req, path);
|
||||
}
|
||||
@@ -32,5 +32,36 @@ export const descargarExcelProfesor = (dataArreglo, nombreArchivo = 'Reporte_Ses
|
||||
hojaDeTrabajo['!cols'] = columnasAncho;
|
||||
|
||||
// 4. Descarga el archivo Excel .xlsx
|
||||
XLSX.writeFile(libroDeTrabajo, `${nombreArchivo}.xlsx`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Función genérica para descargar un arreglo de objetos como Excel
|
||||
* @param {Array<Object>} dataArreglo - La constante con los datos
|
||||
* @param {string} nombreArchivo - Nombre del archivo descargado (sin extensión)
|
||||
* @param {string} nombreHoja - Nombre de la hoja dentro del libro
|
||||
*/
|
||||
export const descargarExcel = (dataArreglo, nombreArchivo = 'Reporte', nombreHoja = 'Datos') => {
|
||||
if (!dataArreglo || dataArreglo.length === 0) {
|
||||
console.warn("El arreglo de datos está vacío.");
|
||||
return;
|
||||
}
|
||||
|
||||
const hojaDeTrabajo = XLSX.utils.json_to_sheet(dataArreglo);
|
||||
|
||||
const libroDeTrabajo = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(libroDeTrabajo, hojaDeTrabajo, nombreHoja);
|
||||
|
||||
const columnasAncho = Object.keys(dataArreglo[0]).map(key => {
|
||||
const maxLen = dataArreglo.reduce((max, row) => {
|
||||
const val = row[key] ? String(row[key]) : '';
|
||||
return Math.max(max, val.length);
|
||||
}, key.length);
|
||||
|
||||
return { wch: maxLen + 3 };
|
||||
});
|
||||
|
||||
hojaDeTrabajo['!cols'] = columnasAncho;
|
||||
|
||||
XLSX.writeFile(libroDeTrabajo, `${nombreArchivo}.xlsx`);
|
||||
};
|
||||
+150
-56
@@ -1,22 +1,67 @@
|
||||
'use client'
|
||||
import { useState, useEffect } from "react";
|
||||
import { Sun, Moon } from "@boxicons/react";
|
||||
import { Sun, Moon, Buildings, Book, UserCircle, ArrowFromTopStroke } from "@boxicons/react";
|
||||
import { obtenerCursosDelDia, obtenerKpiDashboard, obtenerDetalleCursoDia } from "@/services/dashboard";
|
||||
import type { CursoDelDia, KpiDashboard } from "@/services/types/dashboard";
|
||||
import { descargarExcel } from "../app/excel";
|
||||
import { color } from "chart.js/helpers";
|
||||
|
||||
export default function Home() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
const [cursosDelDia, setCursosDelDia] = useState<CursoDelDia[]>([]);
|
||||
const [fecha, setFecha] = useState(() => new Date().toISOString().split('T')[0]);
|
||||
const [Regleta, setRegleta] = useState<KpiDashboard | null>(null);
|
||||
const [sede, setSede] = useState("");
|
||||
const [jornada, setJornada] = useState("");
|
||||
|
||||
const toggleJornada = (valor: "1" | "2") => {
|
||||
setJornada((prev) => (prev === valor ? "" : valor));
|
||||
};
|
||||
|
||||
const buscarCursos = (fechaSel: string, sedeSel: string, jornadaSel: string) => {
|
||||
setLoading(true);
|
||||
setError("");
|
||||
obtenerCursosDelDia(fechaSel, sedeSel, jornadaSel)
|
||||
.then(setCursosDelDia)
|
||||
.catch((err) => setError(err.message))
|
||||
.finally(() => setLoading(false));
|
||||
|
||||
obtenerKpiDashboard(fechaSel, sedeSel, jornadaSel)
|
||||
.then(setRegleta)
|
||||
.catch((err) => console.error("Error KPI:", err));
|
||||
};
|
||||
|
||||
const exportarDetalle = async () => {
|
||||
setLoading(true);
|
||||
setError("");
|
||||
try {
|
||||
const data = await obtenerDetalleCursoDia(fecha, sede, jornada);
|
||||
descargarExcel(data, `Detalle_Cursos_${fecha}`, "Detalle");
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : String(err));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(false);
|
||||
buscarCursos(new Date().toISOString().split('T')[0], "", "");
|
||||
}, []);
|
||||
|
||||
const alumnos = [
|
||||
{ id: 101, tipo: "Regular", curso: "Inglés Básico Intensivo", profesor: "John Doe", sede: "Sede Central", sala: "Sala A", horario: "09:00 / 10:30", inicio: "09:00", inicioBadge: "bg-warning text-dark", termino: "10:30", sesion: "En proceso", sesionBadge: "bg-body-secondary text-body" },
|
||||
{ id: 102, tipo: "Intensivo", curso: "Francés Avanzado", profesor: "Maria Perez", sede: "Sede Norte", sala: "Sala B", horario: "10:30 / 12:00", inicio: "10:30", inicioBadge: "bg-success text-white", termino: "12:00", sesion: "Completada", sesionBadge: "bg-success text-white" },
|
||||
{ id: 103, tipo: "Regular", curso: "Alemán Intermedio", profesor: "Carlos López", sede: "Sede Sur", sala: "Sala C", horario: "14:00 / 15:30", inicio: "14:00", inicioBadge: "bg-warning text-dark", termino: "15:30", sesion: "En proceso", sesionBadge: "bg-body-secondary text-body" },
|
||||
{ id: 104, tipo: "Intensivo", curso: "Inglés Avanzado", profesor: "Ana Torres", sede: "Sede Central", sala: "Sala A", horario: "16:00 / 17:30", inicio: "16:00", inicioBadge: "bg-danger text-white", termino: "17:30", sesion: "Pendiente", sesionBadge: "bg-danger text-white" },
|
||||
{ id: 105, tipo: "Regular", curso: "Japonés Básico", profesor: "Takashi Tanaka", sede: "Sede Norte", sala: "Sala D", horario: "18:00 / 19:30", inicio: "18:00", inicioBadge: "bg-warning text-dark", termino: "19:30", sesion: "En proceso", sesionBadge: "bg-body-secondary text-body" },
|
||||
];
|
||||
const badgeSesion = (sesion: string | null) => {
|
||||
if (!sesion) return "bg-body-secondary text-body";
|
||||
const s = sesion.toLowerCase();
|
||||
if (s.includes("completada") || s.includes("finalizada")) return "bg-success text-white";
|
||||
if (s.includes("pendiente")) return "bg-danger text-white";
|
||||
return "bg-body-secondary text-body";
|
||||
};
|
||||
|
||||
const badgeInicio = (inicio: string | null) => {
|
||||
if (!inicio) return "bg-body-secondary text-body";
|
||||
if (inicio.toLowerCase().includes("pendiente")) return "bg-warning text-dark";
|
||||
return "bg-success text-white";
|
||||
};
|
||||
|
||||
if (loading) return <div className="text-center py-5">Cargando...</div>;
|
||||
if (error) return <div className="alert alert-danger my-3">{error}</div>;
|
||||
@@ -37,21 +82,38 @@ export default function Home() {
|
||||
<div className="col-md-6">
|
||||
<div className="row g-2">
|
||||
<div className="col-md-4">
|
||||
<input type="date" id="TextBoxFecha" className="form-control" />
|
||||
<input
|
||||
type="date"
|
||||
id="TextBoxFecha"
|
||||
className="form-control"
|
||||
value={fecha}
|
||||
onChange={(e) => setFecha(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-md-4">
|
||||
<select id="ListaSedes" className="form-control">
|
||||
<option value="">Seleccione Sede</option>
|
||||
<option value="todas">TODAS</option>
|
||||
<option value="moneda">MONEDA</option>
|
||||
<option value="providencia">PROVIDENCIA</option>
|
||||
<option value="la_florida">LA FLORIDA</option>
|
||||
<option value="online">ONLINE</option>
|
||||
<option value="empresas">EMPRESAS</option>
|
||||
<select
|
||||
id="ListaSedes"
|
||||
className="form-control"
|
||||
value={sede}
|
||||
onChange={(e) => setSede(e.target.value)}
|
||||
>
|
||||
<option value="">TODAS</option>
|
||||
<option value="1">MONEDA</option>
|
||||
<option value="2">PROVIDENCIA</option>
|
||||
<option value="3">LA FLORIDA</option>
|
||||
<option value="4">ONLINE</option>
|
||||
<option value="5">EMPRESAS</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="col-md-4 d-grid">
|
||||
<button type="button" id="ButtonSearchMod" className="btn btn-primary">Buscar</button>
|
||||
<button
|
||||
type="button"
|
||||
id="ButtonSearchMod"
|
||||
className="btn btn-primary"
|
||||
onClick={() => buscarCursos(fecha, sede, jornada)}
|
||||
>
|
||||
Buscar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -59,7 +121,12 @@ export default function Home() {
|
||||
<div className="col-md-6 mt-2 mt-md-0">
|
||||
<div className="row g-2">
|
||||
<div className="col-md-6 d-grid">
|
||||
<button type="button" id="LinkButtonJornada01" className="btn btn-outline-primary text-start p-2">
|
||||
<button
|
||||
type="button"
|
||||
id="LinkButtonJornada01"
|
||||
className={`btn text-start p-2 ${jornada === "1" ? "btn-warning" : "btn-outline-warning"}`}
|
||||
onClick={() => toggleJornada("1")}
|
||||
>
|
||||
<div className="d-flex align-items-center gap-2">
|
||||
<Sun width={24} height={24} />
|
||||
<strong>Diurno</strong>
|
||||
@@ -67,10 +134,16 @@ export default function Home() {
|
||||
</button>
|
||||
</div>
|
||||
<div className="col-md-6 d-grid">
|
||||
<button type="button" id="LinkButtonJornada02" className="btn btn-outline text-start p-2">
|
||||
<button
|
||||
type="button"
|
||||
id="LinkButtonJornada02"
|
||||
|
||||
className={`btn text-start p-2 ${jornada === "2" ? "btn-primary" : "btn-outline-primary"}`}
|
||||
onClick={() => toggleJornada("2")}
|
||||
>
|
||||
<div className="d-flex align-items-center gap-2">
|
||||
<Moon width={24} height={24} />
|
||||
<strong>Vespertino</strong>
|
||||
<strong><span>Vespertino</span></strong>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
@@ -84,55 +157,55 @@ export default function Home() {
|
||||
<div className="col-md-12">
|
||||
<div className="row g-3">
|
||||
<div className="col-md-2">
|
||||
<div className="card bg-body-tertiary border-0 shadow-sm p-3 h-100">
|
||||
<div className="card bg-body-tertiary border-0 shadow-sm p-3 h-100 d-flex flex-column justify-content-center">
|
||||
<div className="row align-items-center text-center">
|
||||
<div className="col-12">
|
||||
<h1><i className="bx bxs-buildings"></i></h1>
|
||||
<Buildings width={48} height={48}/><br/>
|
||||
<span className="fw-bold">Empresas</span>
|
||||
</div>
|
||||
<div className="col-12 mt-2">
|
||||
<span className="text-secondary small">Total</span>
|
||||
<h5><strong>0 %</strong></h5>
|
||||
<span className="small">Total</span>
|
||||
<h5><strong>{Regleta?.cantidad_empresas ?? 0}</strong></h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-md-2">
|
||||
<div className="card bg-body-tertiary border-0 shadow-sm p-3 h-100">
|
||||
<div className="card bg-body-tertiary border-0 shadow-sm p-3 h-100 d-flex flex-column justify-content-center">
|
||||
<div className="row align-items-center text-center">
|
||||
<div className="col-12">
|
||||
<h1><i className="bx bxs-book"></i></h1>
|
||||
<Book width={48} height={48}/><br/>
|
||||
<span className="fw-bold">Cursos</span>
|
||||
</div>
|
||||
<div className="col-12 mt-2">
|
||||
<span className="text-secondary small">Total</span>
|
||||
<h5><strong>0 %</strong></h5>
|
||||
<span className="small">Total</span>
|
||||
<h5><strong>{Regleta?.cantidad_cursos ?? 0}</strong></h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-md-6">
|
||||
<div className="card bg-body-tertiary border-0 shadow-sm p-3 h-100">
|
||||
<div className="card bg-body-tertiary border-0 shadow-sm p-3 h-100 d-flex flex-column justify-content-center">
|
||||
<div className="row align-items-center text-center">
|
||||
<div className="col-md-3">
|
||||
<h1><i className="bx bxs-user-circle"></i></h1>
|
||||
<UserCircle width={48} height={48}/><br/>
|
||||
<span className="fw-bold">Alumnos</span>
|
||||
</div>
|
||||
<div className="col-md-9 mt-2 mt-md-0">
|
||||
<div className="row">
|
||||
<div className="col-4">
|
||||
<span className="text-secondary small">Con SENCE</span>
|
||||
<h5><strong>0</strong></h5>
|
||||
<span className="small">Con SENCE</span>
|
||||
<h5><strong>{Regleta?.cantidad_alumnos_sence ?? 0}</strong></h5>
|
||||
</div>
|
||||
<div className="col-4">
|
||||
<span className="text-secondary small">Sin SENCE</span>
|
||||
<h5><strong>0</strong></h5>
|
||||
<span className="small">Sin SENCE</span>
|
||||
<h5><strong>{Regleta?.cantidad_alumnos_nosence ?? 0}</strong></h5>
|
||||
</div>
|
||||
<div className="col-4">
|
||||
<span className="text-secondary small">Total</span>
|
||||
<h5><strong>0</strong></h5>
|
||||
<span className="small">Total</span>
|
||||
<h5><strong>{Regleta?.cantidad_alumnos ?? 0}</strong></h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -141,8 +214,13 @@ export default function Home() {
|
||||
</div>
|
||||
|
||||
<div className="col-md-2">
|
||||
<button type="button" className="btn btn-success h-100 w-100 p-3 shadow-sm border-0 d-flex flex-column align-items-center justify-content-center">
|
||||
<h1><i className="bx bx-export mb-0"></i></h1>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-success h-100 w-100 p-3 shadow-sm border-0 d-flex flex-column align-items-center justify-content-center"
|
||||
onClick={exportarDetalle}
|
||||
>
|
||||
<ArrowFromTopStroke width={48} height={48} />
|
||||
|
||||
<span>Exportar Excel</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -169,24 +247,40 @@ export default function Home() {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{alumnos.map((a) => (
|
||||
<tr key={a.id}>
|
||||
<td className="text-center">{a.id}</td>
|
||||
<td className="text-center">{a.tipo}</td>
|
||||
<td className="text-start">{a.curso}</td>
|
||||
<td className="text-start">{a.profesor}</td>
|
||||
<td className="text-center">{a.sede}</td>
|
||||
<td className="text-center">{a.sala}</td>
|
||||
<td className="text-center">{a.horario}</td>
|
||||
<td className="text-center">
|
||||
<span className={`badge rounded-pill ${a.inicioBadge}`}>{a.inicio}</span>
|
||||
</td>
|
||||
<td className="text-center">{a.termino}</td>
|
||||
<td className="text-center">
|
||||
<span className={`badge rounded-pill ${a.sesionBadge}`}>{a.sesion}</span>
|
||||
{cursosDelDia.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={10} className="text-center text-secondary py-3">
|
||||
Sin cursos registrados
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
) : (
|
||||
cursosDelDia.map((a) => (
|
||||
<tr key={a.codigo}>
|
||||
<td className="text-center">{a.codigo}</td>
|
||||
<td className="text-center">{a.tipoCurso ?? "—"}</td>
|
||||
<td className="text-start">{a.curso ?? "—"}</td>
|
||||
<td className="text-start">{a.profesor ?? "—"}</td>
|
||||
<td className="text-center">{a.sede ?? "—"}</td>
|
||||
<td className="text-center">{a.sala ?? "—"}</td>
|
||||
<td className="text-center">
|
||||
{a.horaInicio && a.horaTermino
|
||||
? `${a.horaInicio} / ${a.horaTermino}`
|
||||
: "—"}
|
||||
</td>
|
||||
<td className="text-center">
|
||||
<span className={`badge rounded-pill ${badgeInicio(a.inicioSesion)}`}>
|
||||
{a.inicioSesion ?? "—"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="text-center">{a.terminoSesion ?? "—"}</td>
|
||||
<td className="text-center">
|
||||
<span className={`badge rounded-pill ${badgeSesion(a.sesionRegistrada)}`}>
|
||||
{a.sesionRegistrada ?? "—"}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
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);
|
||||
@@ -0,0 +1,44 @@
|
||||
const PROXY_BASE = '/api/external';
|
||||
|
||||
async function request<T>(endpoint: string, options?: RequestInit): Promise<T> {
|
||||
const url = `${PROXY_BASE}${endpoint}`;
|
||||
|
||||
console.log('[API] →', url);
|
||||
|
||||
let res: Response;
|
||||
try {
|
||||
res = await fetch(url, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options?.headers,
|
||||
},
|
||||
...options,
|
||||
});
|
||||
} catch (networkError) {
|
||||
console.error('[API] Error de red al llamar:', url);
|
||||
console.error('[API] Detalle:', networkError);
|
||||
throw new Error(`Fallo de red al llamar ${url}: ${networkError}`);
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
const body = await res.text().catch(() => '(sin body)');
|
||||
console.error('[API] Respuesta no OK:', res.status, res.statusText);
|
||||
console.error('[API] Body:', body);
|
||||
throw new Error(`API Error: ${res.status} ${res.statusText} - ${body}`);
|
||||
}
|
||||
|
||||
try {
|
||||
return await res.json();
|
||||
} catch (parseError) {
|
||||
console.error('[API] Error al parsear JSON de:', url);
|
||||
console.error('[API] Detalle:', parseError);
|
||||
throw new Error(`Respuesta no válida JSON de ${url}`);
|
||||
}
|
||||
}
|
||||
|
||||
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' }),
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
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);
|
||||
@@ -0,0 +1,14 @@
|
||||
import { api } from './api';
|
||||
import type { CursoDelDia, EstadisticasDashboard, KpiDashboard, DetalleCursoDia } from './types/dashboard';
|
||||
|
||||
export const obtenerCursosDelDia = (fecha: string, idsede?: string, idjornada?: string) =>
|
||||
api.get<CursoDelDia[]>(`/api/empresas-cursos?fecha=${fecha}&idsede=${idsede ?? ''}&idjornada=${idjornada ?? ''}`);
|
||||
|
||||
export const obtenerEstadisticas = () =>
|
||||
api.get<EstadisticasDashboard>('/api/dashboard/estadisticas');
|
||||
|
||||
export const obtenerKpiDashboard = (fecha: string, idsede?: string, idjornada?: string) =>
|
||||
api.get<KpiDashboard>(`/api/empresas-cursos/kpi-dashboard?fecha=${fecha}&idsede=${idsede ?? ''}&idjornada=${idjornada ?? ''}`);
|
||||
|
||||
export const obtenerDetalleCursoDia = (fecha: string, idsede?: string, idjornada?: string) =>
|
||||
api.get<DetalleCursoDia[]>(`/api/empresas-cursos/detalle?fecha=${fecha}&idsede=${idsede ?? ''}&idjornada=${idjornada ?? ''}`);
|
||||
@@ -0,0 +1,35 @@
|
||||
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`);
|
||||
@@ -0,0 +1,14 @@
|
||||
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');
|
||||
@@ -0,0 +1,20 @@
|
||||
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);
|
||||
@@ -0,0 +1,26 @@
|
||||
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 }[];
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
export interface CursoDelDia {
|
||||
codigo: number;
|
||||
tipoCurso: string | null;
|
||||
curso: string | null;
|
||||
profesor: string | null;
|
||||
asignacion: string | null;
|
||||
sede: string | null;
|
||||
periodo: string | null;
|
||||
sala: string | null;
|
||||
jornada: string | null;
|
||||
horaInicio: string | null;
|
||||
inicio: string | null;
|
||||
horaTermino: string | null;
|
||||
inicioSesion: string | null;
|
||||
terminoSesion: string | null;
|
||||
sesionRegistrada: string | null;
|
||||
}
|
||||
|
||||
export interface EstadisticasDashboard {
|
||||
empresas: number;
|
||||
cursos: number;
|
||||
alumnosConSence: number;
|
||||
alumnosSinSence: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
export interface KpiDashboard {
|
||||
cantidad_empresas: number;
|
||||
cantidad_cursos: number;
|
||||
cantidad_alumnos: number;
|
||||
cantidad_alumnos_sence: number;
|
||||
cantidad_alumnos_nosence: number;
|
||||
}
|
||||
|
||||
export interface DetalleCursoDia {
|
||||
codigo: number;
|
||||
TipoCurso: string | null;
|
||||
curso: string | null;
|
||||
idAlumno: string | null;
|
||||
idtiporegistroacademico: number | null;
|
||||
contrato: number | null;
|
||||
idEmpresas: string | null;
|
||||
rznscl: string | null;
|
||||
profesor: string | null;
|
||||
asignacion: string | null;
|
||||
sede: string | null;
|
||||
periodo: string | null;
|
||||
sala: string | null;
|
||||
jornada: string | null;
|
||||
Hora_Inicio: string | null;
|
||||
Hora_Termino: string | null;
|
||||
inicio: string | null;
|
||||
termino: string | null;
|
||||
SesionRegistrada: string | null;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
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 {
|
||||
id: string;
|
||||
numero: string;
|
||||
estado: string;
|
||||
}
|
||||
|
||||
export interface ApoderadoAlumno {
|
||||
rut: string;
|
||||
nombre: string;
|
||||
telefono: string;
|
||||
parentesco: string;
|
||||
}
|
||||
|
||||
export interface CertificadoAlumno {
|
||||
id: string;
|
||||
fechaEmision: string;
|
||||
curso: string;
|
||||
horas: number;
|
||||
}
|
||||
|
||||
export interface ComentarioAlumno {
|
||||
id: string;
|
||||
fecha: string;
|
||||
autor: string;
|
||||
texto: string;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in new issue
Block a user