1 Commits

Author SHA1 Message Date
frcarlos 88f2ac13d7 fechas 2025-11-04 13:22:07 -06:00
69 changed files with 2238 additions and 5376 deletions
+503 -925
View File
File diff suppressed because it is too large Load Diff
+3 -5
View File
@@ -11,19 +11,17 @@
"dependencies": {
"axios": "^1.12.2",
"bootstrap": "^5.3.8",
"date-fns": "^4.1.0",
"file-saver": "^2.0.5",
"moment": "^2.30.1",
"next": "^16.0.7",
"next": "15.5.3",
"react": "19.1.0",
"react-bootstrap": "^2.10.10",
"react-datepicker": "^8.7.0",
"react-dom": "19.1.0",
"react-hot-toast": "^2.6.0",
"react-icons": "^5.5.0",
"react-toastify": "^11.0.5",
"sass": "^1.95.0",
"sweetalert2": "^11.26.17",
"sass": "^1.92.1",
"sweetalert2": "^11.26.2",
"sweetalert2-react-content": "^5.1.0",
"validator": "^13.15.15"
},
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

-7
View File
@@ -1,7 +0,0 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>

Before

Width:  |  Height:  |  Size: 1.1 KiB

-7
View File
@@ -1,7 +0,0 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg fill="#000000" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="800px" height="800px" viewBox="0 0 519.578 519.578" xml:space="preserve">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>

Before

Width:  |  Height:  |  Size: 1.3 KiB

-4
View File
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21 10L12 5L3 10L6 11.6667M21 10L18 11.6667M21 10V10C21.6129 10.3064 22 10.9328 22 11.618V16.9998M6 11.6667L12 15L18 11.6667M6 11.6667V17.6667L12 21L18 17.6667L18 11.6667" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 491 B

-1
View File
@@ -1 +0,0 @@
correo,nombre,institucion,dependencia,programa,clave
1 correo nombre institucion dependencia programa clave
+5 -23
View File
@@ -2,11 +2,11 @@ import axios from 'axios';
// Crea una instancia base de Axios
export const axiosInstance = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000', // poner la url
baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000/api', // poner la url
headers: {
'Content-Type': 'application/json',
},
timeout: 20000, // tiempo máximo de espera (20 segundos)
timeout: 10000, // tiempo máximo de espera (10 segundos)
});
// Interceptor para agregar el token automáticamente
@@ -19,9 +19,9 @@ axiosInstance.interceptors.request.use(
config.headers.Authorization = `Bearer ${token}`;
//Quitar estas lienas
// config.headers['token'] = token;
// config.headers['x-access-token'] = token;
// config.headers['token-v2'] = token;
config.headers['token'] = token;
config.headers['x-access-token'] = token;
config.headers['token-v2'] = token;
}
}
@@ -29,23 +29,6 @@ axiosInstance.interceptors.request.use(
},
(error) => Promise.reject(error)
);
// (config) => {
// if (typeof window !== 'undefined') {
// const token = localStorage.getItem('token');
// if (token) {
// // Múltiples formatos según lo que espere el backend
// config.headers.Authorization = `Bearer ${token}`;
// //Quitar estas lienas
// // config.headers['token'] = token;
// // config.headers['x-access-token'] = token;
// // config.headers['token-v2'] = token;
// }
// }
// return config;
// },
// (error) => Promise.reject(error)
// Interceptor para manejar respuestas y errores globales
axiosInstance.interceptors.response.use(
@@ -55,7 +38,6 @@ axiosInstance.interceptors.response.use(
// Token inválido o sesión expirada
if (error.response.status === 401) {
console.warn('Sesión expirada o token inválido.');
console.log('Expiro el token')
if (typeof window !== 'undefined') {
localStorage.clear();
window.location.href = '/'; // redirige automáticamente
@@ -1,140 +0,0 @@
'use client'
import EditarAlumno from "@/components/administrador/editar-caso-especial";
import BotonRegresar from "@/components/boton-regresar";
import { getUserCasoEspecial } from "@/services/userCasoE.services";
import { AxiosError, isAxiosError } from "axios";
import { useParams, useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import Swal from "sweetalert2";
interface Admin {
idTipoUsuario: number;
tipoUsuario?: string;
token: string;
}
interface Props {
idCasoEspecial: number
}
export default function EditarCasoEspecial() {
const [idCasoEspecial, setIdCasoEspecial] = useState<number | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [casoEspecial, setCasoEspecial] = useState<Admin>({
idTipoUsuario: 0,
tipoUsuario: "",
token: ""
});
const [datos, setDatos] = useState({}); // es de casoEspecial
const router = useRouter();
const params = useParams();
const idCasoEspe = Number(params.idCasoEspecial);
if (isNaN(idCasoEspe)) {
return <p>Servicio inválido</p>;
}
const getLocalInfo = () => {
const storedAdmin: Admin = {
idTipoUsuario: Number(localStorage.getItem('idTipoUsuario')),
tipoUsuario: localStorage.getItem('tipoUsuario') || undefined,
token: localStorage.getItem('token') || '',
}
setCasoEspecial(storedAdmin);
const storageIdCaso = Number(localStorage.getItem('idCasoEspecial'));
setIdCasoEspecial(storageIdCaso);
}
const imprimirError = (err: unknown = {}, title = '¡Hubo un error!', onConfirm = () => {}) => {
let message = 'Ocurrió un error';
if (typeof err === 'string') message = err;
else if (err instanceof Error) message = err.message;
else if (isAxiosError(err) && err.response?.data?.message) message = String(err.response.data.message);
Swal.fire({
title,
text: message,
icon: 'error',
confirmButtonText: 'Entendido',
}).then(() => onConfirm());
try {
const anyErr = err as { err?: string };
if (anyErr.err === 'token error') {
localStorage.clear();
router.replace('/');
}
} catch (_) {}
};
const imprimirMensaje = (message: string, title = '¡Felicidades!', onConfirm = () => {}) => {
Swal.fire({
title,
text: message,
icon: 'success',
confirmButtonText: 'Ok',
}).then(() => onConfirm());
};
const imprimirWarning = (
message: string,
onConfirm = () => {},
title = '¡Espera un minuto!',
onCancel = () => {}
) => {
Swal.fire({
title,
text: message,
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Confirmar',
cancelButtonText: 'Cancelar',
}).then((result) => {
if (result.isConfirmed) onConfirm();
else onCancel();
});
};
const updateIsLoading = (value: boolean) => setIsLoading(value);
useEffect(() => {
const fetchUser = async () => {
await getLocalInfo();
if (!idCasoEspecial) return
try {
setIsLoading(true);
const data = await getUserCasoEspecial(idCasoEspecial)
setDatos(data);
} catch (error) {
const axiosErr = error as AxiosError<any>;
const mensaje = axiosErr?.response?.data?.message || 'No se pudo obtener la infromacion del caso especial.'
Swal.fire({
title: 'Error',
icon: 'error',
text: mensaje,
})
}
}
}, [])
return (
<section className="container px-2 pb-5">
<BotonRegresar />
<EditarAlumno
idCasoEspecial={idCasoEspe}
admin={casoEspecial}
viejo={datos}
updateIsLoading={updateIsLoading}
imprimirError={imprimirError}
imprimirMensaje={imprimirMensaje}
imprimirWarning={imprimirWarning}
/>
</section>
)
}
@@ -3,14 +3,13 @@
import { useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { axiosInstance } from '@/api/config';
import axios, { AxiosError, isAxiosError } from 'axios';
import { isAxiosError } from 'axios';
import BotonRegresar from '@/components/boton-regresar';
import InformacionCasoEspecial from '@/components/administrador/informacion-caso-especial';
import LiberarCasoEspecial from '@/components/administrador/liberar-caso-especial';
import Swal from 'sweetalert2';
import withReactContent from 'sweetalert2-react-content';
import { resolve } from 'path';
@@ -28,10 +27,9 @@ interface Status {
}
interface Alumno {
idCasoEspecial: number;
usuario?: object;
carrera?: object;
status?: Status;
Usuario?: object;
Carrera?: object;
Status?: Status;
}
export default function CasoEspecialPage() {
@@ -40,7 +38,7 @@ export default function CasoEspecialPage() {
const [isLoading, setIsLoading] = useState(false);
const [idCasoEspecial, setIdCasoEspecial] = useState<number | null>(null);
const [admin, setAdmin] = useState<Admin>({});
const [alumno, setAlumno] = useState<Alumno>({ idCasoEspecial: 0, usuario: {}, carrera: {}, status: {} });
const [alumno, setAlumno] = useState<Alumno>({ Usuario: {}, Carrera: {}, Status: {} });
// Funciones de dialogo
const imprimirError = (err: unknown = {}, title = '¡Hubo un error!', onConfirm = () => {}) => {
@@ -60,7 +58,7 @@ export default function CasoEspecialPage() {
const anyErr = err as { err?: string };
if (anyErr.err === 'token error') {
localStorage.clear();
router.replace('/');
router.push('/');
}
} catch (_) {}
};
@@ -80,7 +78,7 @@ export default function CasoEspecialPage() {
title = '¡Espera un minuto!',
onCancel = () => {}
) => {
Swal.fire({
MySwal.fire({
title,
text: message,
icon: 'warning',
@@ -113,27 +111,14 @@ export default function CasoEspecialPage() {
updateIsLoading(true);
try {
// throw new Error('Error simulado del servidor'); Para simular error del servidor
// await new Promise(resolve => setTimeout(resolve, 3000)); // Simulamos que el back tarda en enviar la infromacion 3 seg
const res = await axiosInstance.get(`/caso-especial/caso_especial/${idCasoEspecial}`);
setAlumno(res.data.data);
console.log("Respuesta del servidor", res.data)
const res = await axiosInstance.get(`/caso_especial?idCasoEspecial=${idCasoEspecial}`, { headers: { token: admin.token } });
setAlumno(res.data);
} catch (err: unknown) {
const axiosErr = err as AxiosError<any>;
const mensaje = axiosErr?.response?.data?.message || 'No se cargo la infromacion, por favor intentelo mas tarde.'
Swal.fire({
icon: 'error',
title: 'Error',
text: mensaje,
})
router.replace('/administrador/casos_especiales');
// if (isAxiosError(err)) imprimirError(err.response?.data || err.message);
// else if (err instanceof Error) imprimirError(err.message);
// else imprimirError();
if (isAxiosError(err)) imprimirError(err.response?.data || err.message);
else if (err instanceof Error) imprimirError(err.message);
else imprimirError();
} finally {
updateIsLoading(false);
updateIsLoading(false);
}
};
@@ -144,9 +129,9 @@ export default function CasoEspecialPage() {
useEffect(() => {
if (admin.idTipoUsuario) {
if (admin.idTipoUsuario === 2) router.replace('/responsable');
if (admin.idTipoUsuario === 3) router.replace('/alumno');
if (admin.idTipoUsuario === 4) router.replace('/casoEspecial');
if (admin.idTipoUsuario === 2) router.push('/responsable');
if (admin.idTipoUsuario === 3) router.push('/alumno');
if (admin.idTipoUsuario === 4) router.push('/casoEspecial');
}
if (idCasoEspecial) {
@@ -163,7 +148,7 @@ export default function CasoEspecialPage() {
<InformacionCasoEspecial alumno={alumno} />
{(alumno.status?.idStatus === 11 || alumno.status?.idStatus === 12) && (
{(alumno.Status?.idStatus === 11 || alumno.Status?.idStatus === 12) && (
<LiberarCasoEspecial
idCasoEspecial={idCasoEspecial!}
admin={{ token: admin.token! }}
@@ -175,7 +160,7 @@ export default function CasoEspecialPage() {
)}
{isLoading && (
<div className="fixed inset-0 flex items-center justify-center bg-transparent bg-opacity-30 z-50">
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-30 z-50">
<div className="loader">Cargando...</div>
</div>
)}
@@ -1,23 +1,12 @@
'use client'
import TablaCasosEspeciales from "@/components/administrador/tabla-casos-especiales";
import BotonRegresar from "@/components/boton-regresar";
import { useEffect, useState } from "react";
export default function CasosEspeciales() {
const [admin, setAdmin] = useState<{ idTipoUsuario: number; token?: string } | null>(null);
useEffect(() => {
const idTipoUsuario = Number(localStorage.getItem('idTipoUsuario') ?? 0);
const token = localStorage.getItem('token') ?? undefined;
if (!idTipoUsuario) return; // no logueado
setAdmin({ idTipoUsuario, token });
}, []);
return(
<section className="container px-2 pb-5">
<BotonRegresar />
<TablaCasosEspeciales admin={admin ?? undefined}/>
<TablaCasosEspeciales />
</section>
);
}
+5 -2
View File
@@ -1,15 +1,18 @@
'use client'
"use client"
import Footer from "@/components/layout/footer";
import Header from "@/components/layout/header";
import Logout from "@/components/layout/logout";
import React from "react";
import "@/sass/bootstrap.scss"
console.log("Header", Header);
console.log("Footer", Footer);
export default function AdministradorLayout({
children}: {
children: React.ReactNode;
}) {
return (
return (
<>
<Logout />
<main className="bg-light">{children}</main>
-3
View File
@@ -11,9 +11,6 @@ export default function Administrador() {
const token = localStorage.getItem('token') ?? undefined;
if (!idTipoUsuario) return; // no logueado
setAdmin({ idTipoUsuario, token });
console.log('Datos del administrador', admin)
}, []);
function imprimirError(mensaje: string) {
+7 -29
View File
@@ -1,40 +1,20 @@
'use client'
import Cuestionario from "@/components/administrador/cuestionario";
import GustavoBazPrada from "@/components/administrador/gustavo-baz-prada";
import Reporte from "@/components/administrador/reporte";
import BotonRegresar from "@/components/boton-regresar";
import { type } from "node:os";
import { useEffect, useState, useMemo } from "react";
export default function Registro() {
//const years = [2020, 2021, 2022, 2023, 2024, 2025];
const [admin, setAdmin] = useState({ token: "" });
const obtenerYears = () => {
const years1 = [];
const fechaActual = new Date();
for( let i = 2020; i <= fechaActual.getFullYear(); i++) {
years1.push(i)
}
return years1
}
const years = [2020, 2021, 2022, 2023, 2024, 2025];
const admin = { token: "mi_token_de_prueba" };
const years = useMemo(() => obtenerYears(), [])
useEffect(() => {
setAdmin({ token: localStorage.getItem("token") || "" });
console.log("Estos son los datos de years", years)
console.log("Este es el tipo de datos de years", typeof(years))
console.log("Estos son los datos de admin", admin)
console.log("Este es el tipo de datos de admin", typeof(admin))
}, [years]);
//const updateIsLoading = (loading: boolean) => {
// console.log("Loading:", loading);
//};
return (
<section className="container px-2 pb-5">
<BotonRegresar />
<h2 className="title mb-4 fw-bold">Reportes</h2>
<h2 className="title mb-4">Reportes</h2>
<Cuestionario
years={years}
admin={admin}
@@ -43,9 +23,7 @@ export default function Registro() {
<Reporte admin={admin}/>
<GustavoBazPrada
years={years}
/>
<GustavoBazPrada />
</section>
);
}
@@ -49,7 +49,7 @@ export default function Editar() {
try {
updateIsLoading(true)
const res = await axiosInstance.get(`usuario/responsable/${idUsuario}`)
const res = await axiosInstance.get(`usuario/responsable?idUsuario=${idUsuario}`)
//imprimirMensaje(res.data.message)
setData(res.data)
} catch (error) {
@@ -63,35 +63,33 @@ export default function Editar() {
}, []);
return (
<section className="container px-2 pb-5">
<div className="pt-4">
<BotonRegresar />
</div>
<section>
<BotonRegresar />
<EditarResponsable
responsable={data}
imprimirError={imprimirError}
imprimirMensaje={imprimirMensaje}
imprimirWarning={imprimirWarning}
updateIsLoading={updateIsLoading}
/>
<ReasignacionProgramas
<EditarResponsable
responsable={data}
imprimirError={imprimirError}
imprimirMensaje={imprimirMensaje}
imprimirWarning={imprimirWarning}
updateIsLoading={updateIsLoading}
/>
{/*
<EditarResponsable
token={token ?? undefined}
imprimirError={imprimirError}
imprimirMensaje={imprimirMensaje}
imprimirWarning={imprimirWarning}
updateIsLoading={updateIsLoading}
/>
*/}
<ReasignacionProgramas
responsable={data}
imprimirError={imprimirError}
imprimirMensaje={imprimirMensaje}
imprimirWarning={imprimirWarning}
updateIsLoading={updateIsLoading}
/>
{/*
<EditarResponsable
token={token ?? undefined}
imprimirError={imprimirError}
imprimirMensaje={imprimirMensaje}
imprimirWarning={imprimirWarning}
updateIsLoading={updateIsLoading}
/>
*/}
</section>
);
}
@@ -20,7 +20,7 @@ export default function Servicio() {
alert(`Error: ${mensaje}`);
};
// NUEVA función para advertencias (confirmaciones)
// ⚠️ NUEVA función para advertencias (confirmaciones)
const imprimirWarning = (mensaje: string, onConfirm: () => void) => {
if (window.confirm(mensaje)) {
onConfirm();
@@ -46,10 +46,11 @@ export default function Servicio() {
}, []);
return (
<section className="container">
<section>
<BotonRegresar />
{/* <EditarServicio admin={}/> */}
<EditarServicio
admin={{ token: admin.token || '' }}
admin={admin.token || ''}
imprimirError={imprimirError}
imprimirMensaje={imprimirMensajeError}
imprimirWarning={imprimirWarning}
+57 -145
View File
@@ -10,9 +10,6 @@ import Archivo from '@/components/administrador/archivo';
import TituloStatus from '@/components/administrador/titulo-status';
import InformacionServicio from '@/components/administrador/informacion-servicio';
import { Status } from '@/types/responses';
import { resolve } from 'path';
import Swal from 'sweetalert2';
import { Spinner } from 'react-bootstrap';
// Tipos estrictos basados en tus datos
@@ -81,10 +78,10 @@ Programa: { Usuario: {} },
//Interface
interface Datos {
programa?: Programa;
carrera?: Carrera;
usuario?: Usuario;
status?: Status;
Programa?: Programa;
Carrera?: Carrera;
Usuario?: Usuario;
Status?: Status;
creditos?: string;
correo?: string;
fechaRegistro?: string;
@@ -101,10 +98,10 @@ interface Datos {
cartaTermino?: string;
informeGlobal?: string;
cuestionarioPrograma?: {idCuestionarioPrograma: number};
cuestionarioPrograma2?: {idCuestionarioPrograma2: number};
cuestionarioAlumno?: {idCuestionarioAlumno: number};
cuestionarioAlumno2?: {idCuestionarioAlumno2: number};
idCuestionarioPrograma?: number;
idCuestionarioPrograma2?: number;
idCuestionarioAlumno?: number;
idCuestionarioAlumno2?: number;
}
export default function Servicio() {
@@ -133,44 +130,22 @@ export default function Servicio() {
setAdmin({ token });
}, []);
const confirmar = (message: string, onConfirm: () => void) => {
Swal.fire({
title: message,
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'confirmar',
cancelButtonText: 'cancelar',
confirmButtonColor: "#0d6efd",
cancelButtonColor: "#dc3545",
}).then((result) => {
if (result.isConfirmed) {
onConfirm();
}
})
}
// Función para imprimir errores
const imprimirError = (error: unknown) => {
Swal.fire('Error', JSON.stringify(error), 'error')
// alert(`❌ Error: ${JSON.stringify(error)}`);
alert(`❌ Error: ${JSON.stringify(error)}`);
}
const imprimirMensaje = (message: string) => {
Swal.fire('Exito', message, 'success')
// alert(`✅ ${message}`);
alert(`${message}`);
};
const imprimirWarning = (message: string, onConfirm: () => void) => {
confirmar(message, onConfirm)
// if (confirm(`
// ${Swal.fire('Aviso', message, 'warning')}
// `)) {
// onConfirm();
// }
if (confirm(`⚠️ ${message}\n¿Deseas continuar?`)) {
onConfirm();
}
};
// Función para acualizar el estado de carga
// Función para actualizar el estado de carga
const updateIsLoading = (value: boolean) => {
setIsLoading(value);
};
@@ -193,71 +168,42 @@ export default function Servicio() {
const tipoUsuario = localStorage.getItem('tipoUsuario') ?? '';
const token = localStorage.getItem('token');
const idServicioLS = Number(localStorage.getItem('idServicio'));
setIdServicio(idServicioLS);
if (!idUsuario || !idTipoUsuario || !token) return null;
return {
idUsuario,
idTipoUsuario,
tipoUsuario,
token: {
headers: {
const adminInfo: AdminInfo = {
idUsuario,
idTipoUsuario,
tipoUsuario,
token: {
headers: {
token,
},
},
},
};
};
// const adminInfo: AdminInfo = {
// idUsuario,
// idTipoUsuario,
// tipoUsuario,
// token: {
// headers: {
// token,
// },
// },
// };
// //setAdmin(adminInfo);
// const idServisio = setIdServicio(Number(localStorage.getItem('idServicio')));
// console.log('ID Servicio obtenido:', idServisio);
// return adminInfo;
//setAdmin(adminInfo);
const idServisio = setIdServicio(Number(localStorage.getItem('idServicio')));
console.log('ID Servicio obtenido:', idServisio);
return adminInfo;
};
const obtenerRegistro = async (adminInfo: AdminInfo, idServicioVal: number) => {
try {
updateIsLoading(true);
// await new Promise(resolve => setTimeout(resolve, 3000));
// throw new Error('Api caida')
const res = await axiosInstance.get(`/servicio/admin/${idServicioVal}`); //, adminInfo.token
const res = await axiosInstance.get(`/servicio/admin?idServicio=${idServicioVal}`); //, adminInfo.token
console.log('Respuesta del servicio:', res.data);
setDatos(res.data);
console.log('Programa obtenido:', datos.status);
console.log('Programa obtenido:', datos.Status);
} catch (error) {
//imprimirError(error.response?.data || { message: 'Error al obtener el registro' });
Swal.fire('Aviso', 'Error al cargar los datos. Intente nuevamente más tarde.', 'error')
console.log('Error al obtener el registro:', error);
router.replace('/administrador')
} finally {
updateIsLoading(false);
}
};
const tieneCuestionarioPrograma =
datos.cuestionarioPrograma?.idCuestionarioPrograma != null ||
datos.cuestionarioPrograma2?.idCuestionarioPrograma2 != null;
const tieneCuestionarioAlumno =
datos.cuestionarioAlumno?.idCuestionarioAlumno != null ||
datos.cuestionarioAlumno2?.idCuestionarioAlumno2 != null;
// === Ciclo de vida (similar a created() en Vue) ===
useEffect(() => {
console.log("Datos que le llegan al useEffect de servicio page:", datos);
const adminInfo = getLocalhostInfo();
if (!adminInfo) {
@@ -293,29 +239,16 @@ export default function Servicio() {
<section className="container px-2 pb-6">
<BotonRegresar />
<h3 className="container fw-bold title">{datos.status?.status}</h3>
{isLoading ? (
<Spinner />
) : datos ? (
<InformacionServicio
datos={datos}
admin={admin}
imprimirError={imprimirError}
updateIsLoading={updateIsLoading}
/>
) : null}
<h3 className="title">{datos.Status?.status}</h3>
{/* {!isLoading && datos && (
<InformacionServicio
datos={datos}
admin={admin}
imprimirError={imprimirError}
updateIsLoading={updateIsLoading}
<InformacionServicio
datos={datos}
admin={admin}
imprimirError={imprimirError}
updateIsLoading={updateIsLoading}
/>
)} */}
<TituloStatus status={datos.status}/>
<TituloStatus status={datos.Status}/>
{/* Para mostrar si tiene la carta y poder ver los diferentes archivos */}
{datos.cartaAceptacion && (
@@ -332,7 +265,7 @@ export default function Servicio() {
{datos.cartaTermino && (
<Archivo
title={datos.cartaTermino ? "carta de termino" : ""}
title={datos.cartaTermino ? "carta de término" : ""}
datos={datos}
admin={admin}
imprimirMensaje={imprimirMensaje}
@@ -354,48 +287,31 @@ export default function Servicio() {
/>
)}
<div className="container">
{tieneCuestionarioPrograma && (
<p className="my-4">
<div>
{datos.idCuestionarioPrograma || datos.idCuestionarioPrograma2 ? (
<p className="my-4">
<strong>Cuenta con cuestionario de programa resuelto.</strong>
</p>
)}
</p>
) : null}
{tieneCuestionarioAlumno && (
<p className="my-4">
{datos.idCuestionarioAlumno || datos.idCuestionarioAlumno2 ? (
<p className="my-4">
<strong>Cuenta con cuestionario de alumno resuelto.</strong>
</p>
)}
</p>
) : null}
</div>
{/* <div className='container'>
{datos.cuestionarioPrograma2?.idCuestionarioPrograma2 != null || datos.cuestionarioPrograma?.idCuestionarioPrograma != null ? (
<p className="my-4">
<strong>Cuenta con cuestionario de programa resuelto.</strong>
</p>
) : null}
{datos.cuestionarioAlumno2?.idCuestionarioAlumno2 != null || datos.cuestionarioAlumno?.idCuestionarioAlumno != null ?(
<p className="my-4">
<strong>Cuenta con cuestionario de alumno resuelto.</strong>
</p>
) : null}
</div> */}
{idServicio && datos.status && (
<ConfirmarServicio
idServicio={idServicio ?? 0}
admin={admin}
datos={datos}
imprimirMensaje={imprimirMensaje}
imprimirWarning={imprimirWarning}
imprimirError={imprimirError}
updateIsLoading={updateIsLoading}
/>
)}
{datos.status?.idStatus != 5 && datos.status?.idStatus != 6 && (
<CancelarServicio
<ConfirmarServicio
idServicio={idServicio ?? 0}
admin={admin}
datos={datos}
imprimirMensaje={imprimirMensaje}
imprimirWarning={imprimirWarning}
imprimirError={imprimirError}
updateIsLoading={updateIsLoading}
/>
<CancelarServicio
idServicio={idServicio ?? 0}
admin={admin}
datos={datos}
@@ -404,8 +320,6 @@ export default function Servicio() {
imprimirError={imprimirError}
updateIsLoading={updateIsLoading}
/>
)}
{/*
@@ -459,9 +373,7 @@ export default function Servicio() {
*/}
<div className='container mt-4'>
<BotonRegresar />
</div>
<BotonRegresar />
{isLoading && (
<div className="loading-overlay">
@@ -1,46 +0,0 @@
'use client';
import React from 'react';
import FormularioCuestionario from '@/components/alumno/cuestionario2/full-cuestionario-newbad';
import BotonRegresar from '@/components/boton-regresar';
import FullCuestionarioNewBad2 from '@/components/alumno/cuestionario2/full-cuestionario-newbad2';
import FullCuestionario from '@/components/alumno/cuestionario2/full-cuestionario';
import { useParams } from 'next/navigation';
interface Props {
idServicio: number;
}
export default function Page() {
const params = useParams();
const idServicio = Number(params.idServicio);
if (isNaN(idServicio)) {
return <p>Servicio inválido</p>;
}
return (
<div className='container bg-light'>
<div className='mt-4'>
<BotonRegresar />
</div>
<div className='my-1'>
<h1 className='is-size-2'>2025 evaluación del universitario(a) sobre su servicio social</h1>
<p>
Te invitamos a compartir tu experiencia respecto del programa de servicio social en el que
participaste, a efecto de mejorar la oferta de programas disponibles para los alumnos que
desean liberar este requisito. Te recordamos que tus respuestas son confidenciales.
Los datos están sujetos al aviso de privacidad integral que se puede consultar en el sitio
web: <a
href="https://www.acatlan.unam.mx/normatividad"
className="column text-decoration-none text-morado"
target="_blank"> www.acatlan.unam.mx/normatividad </a>
</p>
</div>
<FullCuestionario idServicio={idServicio}/>
</div>
);
}
+16
View File
@@ -0,0 +1,16 @@
'use client';
import React from 'react';
import FormularioCuestionario from '@/components/alumno/cuestionario2/full-cuestionario-newbad';
import BotonRegresar from '@/components/boton-regresar';
import FullCuestionarioNewBad2 from '@/components/alumno/cuestionario2/full-cuestionario-newbad2';
import FullCuestionario from '@/components/alumno/cuestionario2/full-cuestionario';
export default function Page() {
return (
<div className='bg-light'>
<BotonRegresar />
<FullCuestionario />
</div>
);
}
+27 -133
View File
@@ -1,118 +1,27 @@
"use client"
import { axiosInstance } from "@/api/config";
import BarraProgreso from "@/components/alumno/barra-progreso";
import CompletarDatosPersonales from "@/components/alumno/completar-datos-personales";
import NavCues from "@/components/alumno/cuestionario/nav-cues";
import InformacinoServicio from "@/components/alumno/informacion-servicio";
import MensajeAlumno from "@/components/alumno/mensajes-alumno";
import PreTermino from "@/components/alumno/pre-termino";
import { useEffect, useState } from "react";
interface InfoAlumnoExtendido extends InfoAlumno {
Usuario: { usuario: string, nombre: string},
}
interface Usuario {
usuario: string,
nombre: string,
}
interface InfoAlumno{
idServicio: number,
creditos: string,
correo: string,
telefono: string,
direccion: string,
fechaInicio: string,
fechaFin: string,
fechaLiberacion: string,
informeGlobal: string, // Falta ajustar el tipo de datos
programaInterno: string,
profesor: string,
//createdAt: Date,
idCuestionarioAlumno: number,
idCuestionarioAlumno2: number,
carrera: Carrera,
status: Status,
programa: Programa,
}
interface Carrera {
idCarrera: number,
carrera: string,
}
interface Status {
idStatus: number,
status: string,
}
interface Programa {
idPrograma: number,
institucion: string,
dependencia: string,
programa: string,
clavePrograma: string,
}
export default function Home() {
const [data, setData] = useState<InfoAlumnoExtendido>();
// const [servicio, setServicio] = useState<InfoAlumno>();
const handleInfo = async () => {
const idUsuario = localStorage.getItem('idUsuario');
const usuario = localStorage.getItem('usuario');
const nombre = localStorage.getItem('nombre');
const tokenAlumno = localStorage.getItem('token')
console.log('entro para hacer el fetch')
console.log('Este es el id del usuario', idUsuario)
try {
const inf = await axiosInstance.get(`/servicio/alumno?idUsuario=${idUsuario}`)
const Usuario = {
usuario, nombre,
}
console.log("Esta es la inforamcion que trae el back", inf);
const combinado: InfoAlumnoExtendido = { ...inf.data, Usuario };
console.log('Esta es la info combinada', combinado)
// setServicio(inf.data.servicio);
// console.log("Este es el servicio", servicio);
setData(combinado);
console.log("Este es el status del alumno", data?.status.idStatus)
console.log("Este es la infromacion combinada", data);
} catch (error) {
console.log("Error al hacer la peticion ", error)
}
}
useEffect(() => {
if (data) {
console.log("Este es el data en el useEffect", data);
console.log("Este es el status en el useEffect", data.status.idStatus);
}
handleInfo();
}, []);
return (
<div>
{ data?.status.idStatus && (
<BarraProgreso idStatus={data?.status.idStatus}/>
)}
<BarraProgreso idStatus={5}/>
<CompletarDatosPersonales
idServicio={123}
alumno={{ token: { headers: { Authorization: "Bearer ..." } }, tokenArchivo: { headers: { Authorization: "Bearer ..." } } }}
imprimirMensaje={(msg) => console.log(msg)}
imprimirWarning={(msg, callback) => { if (confirm(msg)) callback(); }}
imprimirError={(err) => console.error(err)}
obtenerServicio={() => console.log("obtener servicio")}
updateIsLoading={(loading) => console.log("loading", loading)}
/>
<div className="my-5">
<MensajeAlumno status={{ idStatus: data?.status.idStatus ?? 6 }} />
</div>
{/* servicio={{
<InformacinoServicio
servicio={{
Programa: {
institucion: "UNAM",
dependencia: "Académicos",
@@ -126,40 +35,25 @@ export default function Home() {
fechaInicio: new Date().toISOString(),
fechaFin: new Date().toISOString(),
createdAt: new Date().toISOString(),
}} */}
}}
/>
{ data && (
<InformacinoServicio
servicio={data}
/>
)}
{ data?.status.idStatus === 2 && (
<CompletarDatosPersonales
idServicio={data.idServicio}
alumno={{ token: { headers: { Authorization: "Bearer ..." } }, tokenArchivo: { headers: { Authorization: "Bearer ..." } } }}
imprimirMensaje={(msg) => console.log(msg)}
imprimirWarning={(msg, callback) => { if (confirm(msg)) callback(); }}
imprimirError={(err) => console.error(err)}
obtenerServicio={() => console.log("obtener servicio")}
updateIsLoading={(loading) => console.log("loading", loading)}
/>
)}
<MensajeAlumno Status={{ idStatus: 2 }} />
{ data?.status.idStatus !== undefined && data?.status.idStatus >= 4 && data?.status.idStatus !== 8 && (
<PreTermino
alumno={{tokenAlumno: localStorage.getItem('token') || ""}}
servicio={data}
imprimirMensaje={(msg) => console.log(msg)}
imprimirWarning={(msg, callback) => {
if (confirm(msg)) callback();
}}
imprimirError={(err) => console.error(err)}
obtenerServicio={() => console.log("obtener servicio")}
updateIsLoading={(loading) => console.log("loading", loading)}
/>
)}
<PreTermino
alumno={{ tokenArchivo: "token123" }}
servicio={{ idServicio: 123, informeGlobal: undefined }}
imprimirMensaje={(msg) => console.log(msg)}
imprimirWarning={(msg, callback) => {
if (confirm(msg)) callback();
}}
imprimirError={(err) => console.error(err)}
obtenerServicio={() => console.log("obtener servicio")}
updateIsLoading={(loading) => console.log("loading", loading)}
/>
</div>
);
}
-11
View File
@@ -1,11 +0,0 @@
'use client'
import Logout from "@/components/layout/logout";
export default function Layout({ children }: { children: React.ReactNode}) {
return (
<>
<Logout />
{children}
</>
)
}
-62
View File
@@ -1,62 +0,0 @@
'use client';
import BotonRegresar from "@/components/boton-regresar";
import CasoEspecialForm from "@/components/casoEspecial/caso-especial-form";
import { useState } from "react";
import Swal from "sweetalert2";
export default function Nuevo() {
const [isLoading, setIsLoading] = useState<boolean>(false);
// const token = localStorage.getItem('token');
// console.log("Este es el token en nuevo caso especial:", token);
const imprimirError = (error: unknown) => {
alert(`Error: ${JSON.stringify(error)}`)
}
const imprimirMensaje = (message: string) => {
alert(`${message}`);
}
const imprimirWarning = (message: string, onConfirm: () => void, title: string) => {
Swal.fire({
title,
text: message,
icon: "warning",
showCancelButton: true,
confirmButtonText: 'confirmar',
cancelButtonText: 'cancelar',
confirmButtonColor: "#0d6efd",
cancelButtonColor: "#dc3545",
}).then((result) => {
if (result.isConfirmed) {
onConfirm();
}
})
// if (confirm(`${message}\n¿Desea continuar?`)) {
// onConfirm();
// }
}
const updateIsLoading = (value: boolean) => {
setIsLoading(value);
}
return (
<div className="container">
<BotonRegresar />
<h2 className="fw-bold">Agregar un Servicio Social</h2>
<CasoEspecialForm
imprimirError={imprimirError}
imprimirMensaje={imprimirMensaje}
imprimirWarning={imprimirWarning}
updateIsLoading={updateIsLoading}
/>
{/* <CasoEspecialForm /> */}
</div>
)
}
+2 -2
View File
@@ -49,8 +49,8 @@ export default function Page() {
return (
<section className="container px-2 pb-6">
<div className="pb-4 pt-6 mt-5 mb-4 border-b border-gray-200">
<button className="rounded-2" onClick={() => router.push('/casoEspecial/nuevo')}>Nuevo Caso Especial</button>
<div className="pb-5 pt-6">
<button className="btn btn-info" onClick={() => router.push('/casoEspecial/nuevo')}>Nuevo Caso Especial</button>
</div>
{responsable.idTipoUsuario !== undefined && responsable.token ? (
+4 -115
View File
@@ -1,4 +1,3 @@
@import "react-datepicker/dist/react-datepicker.css";
:root {
--background: #ffffff;
--foreground: #171717;
@@ -11,6 +10,9 @@
}
}
@import "react-datepicker/dist/react-datepicker.css";
html,
body {
max-width: 100vw;
@@ -18,7 +20,7 @@ body {
}
body {
/* Do not override text color globally so Bootstrap utilities and components keep their intended colors */
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
-webkit-font-smoothing: antialiased;
@@ -58,17 +60,6 @@ button {
}
}
/* Ensure fw-bold utility is available and wins if other rules override it */
.fw-bold,
h1.fw-bold, h2.fw-bold, h3.fw-bold, h4.fw-bold, h5.fw-bold, h6.fw-bold {
font-weight: 700 !important;
}
/* Ensure form labels are easily readable and bold like before */
.form-label {
font-weight: 700 !important;
}
table {
width: 100%;
@@ -147,105 +138,3 @@ table {
}
}
}
/* Para los estilos en la vista de login */
.password-wrapper {
display: flex;
align-items: center;
padding: 0.375rem 0.75rem;
border: 1px solid #ced4da;
border-radius: 0.375rem;
background-color: #fff;
}
.password-wrapper:focus-within {
border-color: #0d6efd; /* Bootstrap primary */
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
.password-input {
padding: 0.375rem 0.75rem;
border: none;
outline: none;
width: 100%;
background: transparent;
color: black;
}
/* Estilos para los calendarios de las fechas */
/* CONTENEDOR GENERAL */
.mi-calendario {
font-family: inherit;
border-radius: 8px;
border: 1px solid #0d6efd;
}
/* HEADER (mes / año) */
.mi-calendario .react-datepicker__header {
background-color: #838383;
border-bottom: none;
}
/* TEXTO DEL HEADER */
.mi-calendario .react-datepicker__current-month,
.mi-calendario .react-datepicker__day-name {
color: #ffffff;
font-weight: 500;
}
/* SELECTS DE MES Y AÑO */
.mi-calendario .react-datepicker__month-select,
.mi-calendario .react-datepicker__year-select {
background-color: transparent;
color: #ffffff;
border: 1px solid #ffffff;
border-radius: 4px;
padding: 2px 6px;
}
/* OPCIONES DEL SELECT */
.mi-calendario .react-datepicker__month-select option,
.mi-calendario .react-datepicker__year-select option {
background-color: #ffffff;
color: #212529;
}
/* DÍAS */
.mi-calendario .react-datepicker__day {
color: #212529;
}
/* DÍA SELECCIONADO */
.mi-calendario .react-datepicker__day--selected,
.mi-calendario .react-datepicker__day--keyboard-selected {
background-color: #0d6efd;
color: #ffffff;
}
/* HOVER DE DÍAS */
.mi-calendario .react-datepicker__day:hover {
background-color: #cfe2ff;
}
/* DÍAS DESHABILITADOS */
.mi-calendario .react-datepicker__day--disabled {
color: #adb5bd;
}
.mi-calendario .react-datepicker__year-select::-webkit-scrollbar {
width: 8px;
}
.mi-calendario .react-datepicker__year-select::-webkit-scrollbar-track {
background: #e9ecef;
}
.mi-calendario .react-datepicker__year-select::-webkit-scrollbar-thumb {
background-color: #0d6efd;
border-radius: 10px;
border: 2px solid #e9ecef;
}
.mi-calendario .react-datepicker__year-select::-webkit-scrollbar-thumb:hover {
background-color: #0b5ed7;
}
+1 -11
View File
@@ -3,28 +3,18 @@ import Header from "@/components/layout/header";
import "./globals.css";
import Footer from "@/components/layout/footer";
import "@/sass/bootstrap.scss"
import { Metadata } from 'next';
export const metadata: Metadata = {
title: 'IRIS',
description: 'Este es un servicio para hacer el servicio social de la FES Acatlan',
icons: {
icon: '/favicon.ico'
}
}
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
}>) {
return (
<html lang="en">
<body className="d-flex flex-column min-vh-100">
<Header />
<main className="flex-grow-1 bg-light">
{children}
{/* <ToastContainer position="top-right" autoClose={3000} /> */}
</main>
<Footer />
</body>
+11 -56
View File
@@ -1,37 +1,18 @@
'use client'
import { axiosInstance } from "@/api/config";
import Image from "next/image";
import { useRouter } from "next/navigation";
import React, { useState } from "react";
import { toast } from "react-toastify";
import Swal from "sweetalert2";
import React from "react";
export default function Home() {
const [showPassword, setShowPassword] = useState(false);
const router = useRouter();
const [error, setError] = React.useState('');
const [loadingData, setLoadingData] = React.useState({
usuario: '',
password: '',
});
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setLoadingData((prevData) => ({ ...prevData, [name]: value}));
if (error) setError('');
}
const handleOnSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
// const formData = new FormData(e.currentTarget);
// const object = Object.fromEntries(formData);
const formData = new FormData(e.currentTarget);
const object = Object.fromEntries(formData);
try {
const res = await axiosInstance.post('/auth/login', {
usuario: loadingData.usuario,
password: loadingData.password,
});
const res = await axiosInstance.post('/usuario/login', object);
// Extraer datos del backend
const token = res?.data?.token ?? '';
@@ -50,7 +31,6 @@ export default function Home() {
localStorage.setItem('nombre', String(nombre));
localStorage.setItem('idTipoUsuario', String(idTipoUsuario));
// Validar y redirigir según el idTipoUsuario
if (token && idUsuario && idTipoUsuario) {
switch (idTipoUsuario) {
@@ -68,26 +48,19 @@ export default function Home() {
break;
default:
console.warn(`Tipo de usuario desconocido: ${idTipoUsuario}`);
//localStorage.clear();
localStorage.clear();
break;
}
} else {
//localStorage.clear();
localStorage.clear();
console.error('Error: datos de usuario incompletos');
}
} catch (error) {
Swal.fire("Error", "Por favor verifique que el usuario o la contraseña sean correctos.", "error");
// toast.error("Usuario o contraseña estan mal")
//alert("Usuario o password mal")
console.error('Error en el inicio de sesión:', error);
//localStorage.clear();
localStorage.clear();
}
};
const togglePassword = () => {
setShowPassword(prev => !prev)
}
return (
<div className="d-flex justify-content-center align-items-center bg-light" style={{ minHeight: 'calc(100vh - 200px)' }}>
<form className="p-4 shadow rounded bg-white w-100" style={{ maxWidth: '400px' }} onSubmit={handleOnSubmit}>
@@ -95,30 +68,12 @@ export default function Home() {
<div className="mb-3">
<label htmlFor="usuario" className="form-label">Usuario</label>
<input type="text" name="usuario" id="usuario" className="form-control" onChange={handleChange} required />
<input type="text" name="usuario" id="usuario" className="form-control" required />
</div>
<label htmlFor="password" className="form-label">Contraseña</label>
<div className="password-wrapper d-flex align-items-center mb-4 p-0">
<input id="password" type={showPassword ? "text" : "password"} className="password-input " name="password" onChange={handleChange} required />
<button
type="button"
onClick={togglePassword}
className="btn btn-light p-1 bg-transparent border-0"
aria-label="Mostrar u ocultar contraseña"
>
<Image
src={
showPassword
? "/image/eyeopen.svg"
: "/image/eyeclose.svg"
}
width={24}
height={24}
alt="Mostrar u ocultar contraseña"
/>
</button>
<div className="mb-4">
<label htmlFor="password" className="form-label">Contraseña</label>
<input type="password" name="password" id="password" className="form-control" required />
</div>
<div className="d-grid">
@@ -98,15 +98,15 @@ export default function Page() {
{idServicio !== null && (
<UploadArchivo
path="carta_aceptacion"
tipoCarta="aceptación"
variableName="cartaAceptacion"
idServicio={idServicio}
responsable={{ tokenArchivo: responsable.tokenArchivo }}
imprimirMensaje={imprimirMensaje}
imprimirWarning={imprimirWarning}
imprimirError={imprimirError}
updateIsLoading={updateIsLoading}
path="carta_aceptacion"
tipoCarta="aceptación"
variableName="cartaAceptacion"
idServicio={idServicio}
responsable={{ tokenArchivo: responsable.tokenArchivo }}
imprimirMensaje={imprimirMensaje}
imprimirWarning={imprimirWarning}
imprimirError={imprimirError}
updateIsLoading={updateIsLoading}
/>
)}
+10 -32
View File
@@ -8,7 +8,7 @@ import { axiosInstance } from "@/api/config";
import type { AxiosError } from 'axios';
type Responsable = { idUsuario?: number; idTipoUsuario?: number; tipoUsuario?: string | null; token?: string; tokenArchivo?: string };
type Alumno = { nombre?: string; idUsuario?: number };
type Alumno = { nombre?: string; usuario?: string };
export default function Page() {
const router = useRouter();
@@ -16,8 +16,6 @@ export default function Page() {
const [idServicio, setIdServicio] = useState<number | null>(null);
const [alumno, setAlumno] = useState<Alumno>({});
const [responsable, setResponsable] = useState<Responsable>({});
// Para verificar que se guardo el idServicio
const [isReady, setIsReady] = useState(false);
const updateIsLoading = (v: boolean) => setIsLoading(v);
@@ -56,17 +54,9 @@ export default function Page() {
const tipoUsuario = localStorage.getItem('tipoUsuario');
const token = localStorage.getItem('token') || undefined;
const tokenArchivo = localStorage.getItem('token') || undefined;
setResponsable({
idUsuario: Number.isNaN(idUsuario) ? undefined : idUsuario,
idTipoUsuario: Number.isNaN(idTipoUsuario) ? undefined : idTipoUsuario,
tipoUsuario,
token: token ?? undefined,
tokenArchivo: tokenArchivo ?? undefined
});
setResponsable({ idUsuario: Number.isNaN(idUsuario) ? undefined : idUsuario, idTipoUsuario: Number.isNaN(idTipoUsuario) ? undefined : idTipoUsuario, tipoUsuario, token: token ?? undefined, tokenArchivo: tokenArchivo ?? undefined });
const s = Number(localStorage.getItem('idServicio'));
setIdServicio(Number.isNaN(s) ? null : s);
setIsReady(true);
};
const obtenerRegistro = async () => {
@@ -74,12 +64,8 @@ export default function Page() {
updateIsLoading(true);
try {
const headers = responsable.token ? { Authorization: `Bearer ${responsable.token}` } : undefined;
const res = await axiosInstance.get<{ usuario?: Alumno; Status?: { idStatus?: number } }>(`/servicio/admin/${idServicio}`);
console.log('Respuesta al obtener el servicio:', res.data);
console.log("Info Usuario: ", res.data.usuario)
console.log("Nombre del alumno: ", res.data.usuario?.nombre);
setAlumno(res.data.usuario ?? {});
const res = await axiosInstance.get<{ Usuario?: Alumno; Status?: { idStatus?: number } }>(`/servicio/admin?idServicio=${idServicio}`, { headers });
setAlumno(res.data.Usuario ?? {});
updateIsLoading(false);
const s = res.data.Status?.idStatus ?? 0;
// Se comennto para hacer pruebas
@@ -96,28 +82,20 @@ export default function Page() {
useEffect(() => { getLocalhostInfo(); }, []);
useEffect(() => {
if (!isReady) return; // Para esperar a que cargue la info del localStorage
if (responsable.idTipoUsuario === 1) router.replace('/admin');
if (responsable.idTipoUsuario === 3) router.replace('/alumno');
if (responsable.idTipoUsuario === 4) router.replace('/casoEspecial');
if (idServicio !== null) {
console.log("ID Servicio no es nulo, se obtiene el registro.", idServicio);
obtenerRegistro();
} else (
router.replace('/responsable')
)
if (responsable.idTipoUsuario === 1) router.push('/admin');
if (responsable.idTipoUsuario === 3) router.push('/alumno');
if (responsable.idTipoUsuario === 4) router.push('/casoEspecial');
// Se comento para hacer pruebas
//if (idServicio === null) router.push('/responsable');
// else if (idServicio !== null) obtenerRegistro();
else if (idServicio !== null) obtenerRegistro();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isReady, responsable, idServicio]);
}, [responsable, idServicio]);
return (
<section className="container px-2 pb-6">
<BotonRegresar />
<h3 className="title">{alumno.nombre}</h3>
<h3 className="title">{alumno.nombre} {alumno.usuario}</h3>
{idServicio !== null && (
<UploadArchivo
+7 -24
View File
@@ -19,10 +19,6 @@ export default function Page() {
const [idServicio, setIdServicio] = useState<number | null>(null);
const [alumno, setAlumno] = useState<Record<string, unknown> | null>(null);
const [responsable, setResponsable] = useState<Responsable>({});
// Para esperar a que cargue la info del localStorage
const [isReady, setIsReady] = useState(false);
const updateIsLoading = (v: boolean) => setIsLoading(v);
@@ -41,7 +37,7 @@ export default function Page() {
const anyErr = err as { err?: unknown };
if (anyErr.err === 'token error') {
try { localStorage.clear(); } catch (_) {}
router.replace('/');
router.push('/');
}
}
};
@@ -63,24 +59,17 @@ export default function Page() {
const idTipoUsuario = Number(localStorage.getItem('idTipoUsuario'));
const tipoUsuario = localStorage.getItem('tipoUsuario');
const token = localStorage.getItem('token') || undefined;
setResponsable({
idUsuario: Number.isNaN(idUsuario) ? undefined : idUsuario,
idTipoUsuario: Number.isNaN(idTipoUsuario) ? undefined : idTipoUsuario,
tipoUsuario,
token: token ?? undefined
});
setResponsable({ idUsuario: Number.isNaN(idUsuario) ? undefined : idUsuario, idTipoUsuario: Number.isNaN(idTipoUsuario) ? undefined : idTipoUsuario, tipoUsuario, token: token ?? undefined });
const s = Number(localStorage.getItem('idServicio'));
setIdServicio(Number.isNaN(s) ? null : s);
setIsReady(true);
};
const obtenerRegistro = async () => {
if (!idServicio) return;
updateIsLoading(true);
try {
//const headers = responsable.token ? { Authorization: `Bearer ${responsable.token}` } : undefined;
const res = await axiosInstance.get<{ Usuario?: Record<string, unknown>; idCuestionarioPrograma?: number }>(`/servicio/admin/${idServicio}`,);
const headers = responsable.token ? { Authorization: `Bearer ${responsable.token}` } : undefined;
const res = await axiosInstance.get<{ Usuario?: Record<string, unknown>; idCuestionarioPrograma?: number }>(`/servicio/admin?idServicio=${idServicio}`, { headers });
setAlumno(res.data.Usuario ?? null);
updateIsLoading(false);
if (res.data.idCuestionarioPrograma) router.push('/responsable');
@@ -101,20 +90,14 @@ export default function Page() {
}, []);
useEffect(() => {
// Hacemos la validacion para asegurarnos de que cargo la info del local
if (!isReady) return; // Para esperar a que cargue la info del localStorage
// Redireccionamientos segun tipo de usuario
if (responsable.idTipoUsuario === 1) router.push('/administrador');
if (responsable.idTipoUsuario === 3) router.push('/alumno');
if (responsable.idTipoUsuario === 4) router.push('/casoEspecial');
// Para verificar el servicio
//if (idServicio === null) router.push('/responsable'); // Esta mal la logica
if (idServicio !== null) {
obtenerRegistro();
} else router.push('/responsable');
else if (idServicio !== null) obtenerRegistro();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isReady, responsable, idServicio]);
}, [responsable, idServicio]);
return (
<section className="container my-5 px-3">
@@ -127,7 +110,7 @@ export default function Page() {
</p>
<p>
<a href="https://www.acatlan.unam.mx/normatividad" target="_blank" rel="noreferrer" className="text-decoration-none text-morado"> www.acatlan.unam.mx/normatividad</a>
<a href="https://www.acatlan.unam.mx/normatividad" target="_blank" rel="noreferrer"> www.acatlan.unam.mx/normatividad</a>
</p>
</div>
+3 -3
View File
@@ -27,7 +27,7 @@ export default function Page() {
const anyErr = err as { err?: unknown };
if (anyErr.err === 'token error') {
try { localStorage.clear(); } catch (_) {}
router.replace('/');
router.push('/');
}
}
};
@@ -59,7 +59,7 @@ export default function Page() {
}, []);
useEffect(() => {
if (responsable.idTipoUsuario === 1) router.push('/administrador');
if (responsable.idTipoUsuario === 1) router.push('/admin');
if (responsable.idTipoUsuario === 3) router.push('/alumno');
if (responsable.idTipoUsuario === 4) router.push('/casoEspecial');
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -81,7 +81,7 @@ export default function Page() {
</p>
</div>
<h2 className="title fw-bold mt-4 mb-4">Añadir Servicio Social</h2>
<h2 className="title">Añadir Servicio Social</h2>
<NuevoServicio
responsable={responsable}
+9 -12
View File
@@ -53,20 +53,17 @@ export default function Page() {
};
useEffect(() => {
const r: Responsable = {
idUsuario: Number(localStorage.getItem('idUsuario')) || undefined,
idTipoUsuario: Number(localStorage.getItem('idTipoUsuario')) || undefined,
//tipoUsuario: localStorage.getItem('tipoUsuario') || undefined,
token: localStorage.getItem('token') || undefined,
};
const r: Responsable = {
idUsuario: Number(localStorage.getItem('idUsuario')) || undefined,
idTipoUsuario: Number(localStorage.getItem('idTipoUsuario')) || undefined,
//tipoUsuario: localStorage.getItem('tipoUsuario') || undefined,
token: localStorage.getItem('token') || undefined,
};
setResponsable(r);
console.log('Responsable:', r);
console.log('id Usuario en componente padrre', r.idUsuario);
//ontenerCatalogoStatus();
console.log("Esta es la infromacino del useState en responsable", responsable)
if (r.idTipoUsuario === 1) router.push('/administrador');
if (r.idTipoUsuario === 3) router.push('/alumno');
if (r.idTipoUsuario === 4) router.push('/casoEspecial');
@@ -78,8 +75,8 @@ export default function Page() {
), [responsable]);
return (
<section className="container px-2 pb-6 mb-2">
<div className="pb-3">
<section className="container px-2 pb-6">
<div className="pt-6 pb-5">
<p className="is-size-4 block mt-5 h4 mb-4">Estimado(a) responsable del programa de servicio social:</p>
<p className="block">
@@ -88,7 +85,7 @@ export default function Page() {
</div>
<div className="pb-3">
<button className="button is-info rounded-2" onClick={() => router.push('/responsable/nuevo')}>Agregar alumno</button>
<button className="button is-info" onClick={() => router.push('/responsable/nuevo')}>Agregar alumno</button>
</div>
{tabla}
+18 -81
View File
@@ -4,8 +4,7 @@ import { useState } from "react";
import { useRouter } from "next/navigation";
import { axiosInstance } from '@/api/config';
import { isAxiosError } from 'axios';
import type { AxiosError, AxiosResponse } from 'axios';
import Swal from "sweetalert2";
import type { AxiosResponse } from 'axios';
interface Status {
idStatus: number;
@@ -26,7 +25,7 @@ interface Datos {
//Programa?: Programa
//Carrera?: Carrera;
//Usuario?: Usuario;
status?: Status;
Status?: Status;
creditos?: string;
correo?: string;
fechaRegistro?: string;
@@ -58,7 +57,7 @@ interface Props {
}
interface ApiResponse {
message: string;
data: { message: string };
}
interface RechazarData {
@@ -98,11 +97,6 @@ export default function Archivo({
};
const rechazarArchivo = () => {
// Depuramos para ver errores
console.log("Title", title)
console.log("status", datos.status?.idStatus)
console.log("idServicio", datos.idServicio)
switch (title) {
case "carta de aceptación":
rechazarFunc(rechazarCartaAceptacion);
@@ -124,36 +118,18 @@ export default function Archivo({
mensaje: mensajeRechazo,
};
console.log("Datos para rechazar:", data);
try {
updateIsLoading(true);
const res = await funcRechazar(data);
localStorage.removeItem("idServicio");
console.log("Respuesta al rechazar:", res.data);
const mensaje = res.data?.message || "Documento rechazado exitosamente";
Swal.fire({
title: 'Exito',
text: mensaje,
icon: 'success',
})
// imprimirMensaje(mensaje);
router.replace("/administrador");
imprimirMensaje(res.data.data.message);
router.push("/administrador");
} catch (err: unknown) {
const axiosErr = err as AxiosError<any>;
const mensaje = axiosErr?.response?.data?.message || 'No se pudo rechazar el documento'
Swal.fire({
title: 'Error',
text: mensaje,
icon: 'error',
})
// if (isAxiosError(err)) {
// imprimirError(err.response?.data || err.message);
// } else {
// imprimirError(err);
// }
if (isAxiosError(err)) {
imprimirError(err.response?.data || err.message);
} else {
imprimirError(err);
}
} finally {
updateIsLoading(false);
}
@@ -169,46 +145,13 @@ export default function Archivo({
axiosInstance.put(`/servicio/rechazar_informe`, data);
return (
<div className="container mb-4">
<div className="d-flex align-items-center">
<div className="mb-4">
<div className="flex items-center">
<h6 className="pr-4 text-lg">Ver {title}</h6>
{/* Texto */}
<h5 className="mb-0">
Ver {title}
</h5>
<div className="d-flex align-items-center gap-2">
<a
className="btn btn-link btn-light text-decoration-none ms-3 text-morado"
target="_blank"
rel="noopener noreferrer"
href={`https://drive.google.com/file/d/${archivo()}/view?usp=sharing`}
style={{ background: '#eae4f8'}}
>
Ver
</a>
{((datos.status?.idStatus === 1 && title === "carta de aceptación") ||
(datos.status?.idStatus === 5 &&
(title === "carta de termino" || title === "informe global"))) && (
<button
className="btn btn-danger"
onClick={updateRechazar}
>
Rechazar
</button>
)}
</div>
{/* Solo de moemento para la parte visual falta corregir esto */}
{/*
<div className="pr-4">
<div className="pr-4">
<a
className="btn btn-link btn-light text-decoration-none"
className="btn btn-link btn-light"
target="_blank"
rel="noopener noreferrer"
href={`https://drive.google.com/file/d/${archivo()}/view?usp=sharing`}
@@ -216,10 +159,9 @@ export default function Archivo({
Ver
</a>
</div>
*/}
{/* {((datos.status?.idStatus === 1 && title === "carta de aceptación") ||
(datos.status?.idStatus === 5 &&
{((datos.Status?.idStatus === 1 && title === "carta de aceptación") ||
(datos.Status?.idStatus === 5 &&
(title === "carta de termino" || title === "informe global"))) && (
<button
className="btn btn-danger"
@@ -228,16 +170,11 @@ export default function Archivo({
Rechazar
</button>
)}
</div> */}
</div>
{rechazar && (
<div className="mt-2">
<label>Razón del rechazo (No olvide indicar el documento rechazado):</label>
<label>Razón del rechazo:</label>
<textarea
maxLength={500}
value={mensajeRechazo}
@@ -2,9 +2,8 @@
import { useState } from "react";
import { axiosInstance } from '@/api/config';
import { AxiosError } from 'axios';
import { isAxiosError } from 'axios';
import { useRouter } from "next/navigation";
import Swal from "sweetalert2";
interface Status {
idStatus: number;
@@ -81,71 +80,61 @@ export default function CancelarServicio({
updateIsLoading(true);
const res = await axiosInstance.put(`/servicio/cancelar`, data);
localStorage.removeItem("idServicio");
Swal.fire('Exito', res.data.message, 'success')
// imprimirMensaje(res.data.message);
router.push("/administrador");
imprimirMensaje(res.data.message);
router.push("/admin");
} catch (err: unknown) {
const axiosErr = err as AxiosError<any>;
const mensaje = axiosErr?.response?.data?.message || 'No se pudo cancelar el servicio, intentelo mas tarde.';
Swal.fire({
icon: 'error',
title: 'Error',
text: mensaje,
})
// if (isAxiosError(err)) imprimirError(err.response?.data || err.message);
// else if (err instanceof Error) imprimirError(err.message);
// else imprimirError(err);
if (isAxiosError(err)) imprimirError(err.response?.data || err.message);
else if (err instanceof Error) imprimirError(err.message);
else imprimirError(err);
} finally {
updateIsLoading(false);
}
};
return (
<div className="container mt-5 space-y-4">
{/* Botón de Cancelar */}
<div className="mt-5 space-y-4">
{/* Botón de Cancelar */}
<button
disabled={datos.Status?.idStatus === 6 || datos.Status?.idStatus === 10}
onClick={updateCancelar}
className={`px-4 py-2 rounded text-white ${
datos.Status?.idStatus === 6 || datos.Status?.idStatus === 10
? "bg-gray-400 cursor-not-allowed"
: "bg-red-600 hover:bg-red-700"
}`}
>
Cancelar
</button>
{/* Área de mensaje */}
{cancelar && (
<div className="space-y-2">
<label className="block font-medium">Razón de la cancelación:</label>
<textarea
maxLength={500}
value={mensajeCancelar}
onChange={(e) => setMensajeCancelar(e.target.value)}
className="border rounded p-2 w-full"
/>
<button
disabled={datos.Status?.idStatus === 6 || datos.Status?.idStatus === 10}
onClick={updateCancelar}
className={`px-4 py-2 bg-danger rounded text-white${
datos.Status?.idStatus === 6 || datos.Status?.idStatus === 10
disabled={!mensajeCancelar}
onClick={() =>
imprimirWarning(
"¿Seguro(a) que quiere cancelar este servicio?",
cancelarServicio
)
}
className={`px-4 py-2 rounded text-white ${
!mensajeCancelar
? "bg-gray-400 cursor-not-allowed"
: "bg-red-600 hover:bg-red-700"
: "bg-blue-600 hover:bg-blue-700"
}`}
>
Cancelar
Cancelar servicio
</button>
{/* Área de mensaje */}
{cancelar && (
<div className="space-y-2 mt-3">
<label className="block font-medium mb-2">Razón de la cancelación:</label>
<textarea
maxLength={500}
value={mensajeCancelar}
onChange={(e) => setMensajeCancelar(e.target.value)}
className="form-control border rounded p-2 w-full"
/>
<button
disabled={!mensajeCancelar}
onClick={() =>
imprimirWarning(
"¿Seguro(a) que quiere cancelar este servicio?",
cancelarServicio
)
}
className={`bg-morado mt-3 mb-4 px-4 py-2 rounded text-white ${
!mensajeCancelar
? "bg-gray-400 cursor-not-allowed"
: "bg-blue-600 hover:bg-blue-700"
}`}
>
Cancelar servicio
</button>
</div>
)}
</div>
)}
</div>
);
}
+12 -46
View File
@@ -1,30 +1,19 @@
'use client'
import { axiosInstance } from "@/api/config";
import { CargaMasivaProps } from "@/types/responses";
import { AxiosError } from "axios";
import { useRouter } from "next/navigation";
import React, { useState } from "react";
import { Button, Form } from "react-bootstrap";
import { FaUpload } from "react-icons/fa6";
import Swal from "sweetalert2";
import { FaUpload } from "react-icons/fa";
export default function CargaMasiva({datos}: {datos: CargaMasivaProps}) {
const router = useRouter();
const [csv, setCsv] = useState<File | null>(null)
//const url = process.env.NEXT_PUBLIC_API_URL
//const link = `${url}/plantilla.csv`
const handleDowload = () => {
console.log("Esta entrando a la funcion");
const link = document.createElement("a");
link.href = "/plantilla/plantilla.csv";
link.download = "plantilla.csv";
link.click();
}
const url = process.env.NEXT_PUBLIC_API_URL
const link = `${url}/plantilla.csv`
//const [link] = useState(`${url}/plantilla.csv`)
//console.log("Esta es la url para descargar la plantilla", link);
const validarExt = (file: File | null) => {
if (!file) return
const extPermitidas = /(.csv)$/i
@@ -49,40 +38,18 @@ export default function CargaMasiva({datos}: {datos: CargaMasivaProps}) {
//updateIsLoading(true)
try {
console.log("Este es el archivo csv", formData)
// Cambioamos a variable de entorno
const res = await axiosInstance.post(`${process.env.NEXT_PUBLIC_API_URL}/programa/carga_masiva`, formData,{
const res = await axiosInstance.post("/programa/carga_masiva", formData,{
headers: {
Authorization: `Bearer ${datos.admin.tokenArchivo}`,
'Content-Type': 'multipart/form-data',
},
})
console.log('Respuesta de carga masiva:', res.data)
//updateIsLoading(false)
//ImprimirMensaje(res.data.message)
const message = res?.data?.message || 'Se completo la carga masiva con exito.';
Swal.fire({
title: 'Archivo enviado',
text: message, //'El archivo se subió correctamente'
icon: 'success',
});
setCsv(null);
//alert("Se subio la carga masiva correctamente.");
//router.push("/administrador/responsables")
router.push("/administrador/responsables")
} catch (error) {
//updateIsLoading(false)
//imprimirError(error.response.data || error)
const axiosErr = error as AxiosError<any>;
const mensaje = axiosErr?.response?.data?.message || 'Error en la carga masiva.'
Swal.fire({
title: 'Error al enviar el archivo',
text: mensaje,
icon: 'error',
})
setCsv(null)
console.log('Error en carga masiva:', error)
}
}
@@ -103,7 +70,7 @@ export default function CargaMasiva({datos}: {datos: CargaMasivaProps}) {
{csv?.name || 'Arrastra aquí tu archivo o da click aquí para buscar'}
</p>
<p className="is-size-6">Tamaño máximo 20MB</p>
<p className="is-size-7">Si al momento de elegir un archivo este no se selecciona, haga click en cancelar en la ventana emergente e intente de nuevo.</p>
<p className="is-size-7">Si al momento de elegir uin archivo este no se selecciona, haga click en cancelar en la ventana emergente e intente de nuevo.</p>
</div>
<input
id="csvInput"
@@ -119,17 +86,16 @@ export default function CargaMasiva({datos}: {datos: CargaMasivaProps}) {
disabled={!csv}
onClick={enviarCargaMasiva}>
Enviar archivo
</Button>
</Button>
<Button
type="button"
onClick={handleDowload}
// target="_blank"
// rel="noopener noreferrer"
<Button
href={link}
target="_blank"
rel="noopener noreferrer"
className="bg-morado border-morado"
>
Descargar plantilla
</Button>
</Button>
</div>
</div>
);
@@ -1,10 +1,9 @@
import { useState } from "react";
import { isAxiosError } from 'axios';
import { axiosInstance } from '@/api/config';
import { AxiosError, AxiosResponse } from 'axios';
import type { AxiosResponse } from 'axios';
import { useRouter } from "next/navigation";
import { Carrera, Usuario } from "@/types/responses";
import Swal from "sweetalert2";
interface Status {
idStatus: number;
@@ -12,8 +11,6 @@ interface Status {
interface Programa {
acatlan?: boolean;
clavePrograma?: string;
programa?: string;
}
interface Alumno {
@@ -27,10 +24,10 @@ interface Admin {
}
interface Datos {
programa?: Programa;
Programa?: Programa;
//Carrera?: Carrera;
//Usuario?: Usuario;
status?: Status;
Status?: Status;
creditos?: string;
correo?: string;
fechaRegistro?: string;
@@ -86,11 +83,9 @@ export default function ConfirmarServicio({
const [vistoBuenoAcatlan, setVistoBuenoAcatlan] = useState(false);
const router = useRouter();
console.log("Datos resividos de datod", datos)
const confirmarServicio = () => {
if (datos.status?.idStatus === 1) confirmar(confirmarPreRegistro);
else if (datos.status?.idStatus === 5) confirmar(confirmarLiberacion);
if (datos.Status?.idStatus === 1) confirmar(confirmarPreRegistro);
else if (datos.Status?.idStatus === 5) confirmar(confirmarLiberacion);
};
const confirmar = async (
@@ -102,33 +97,20 @@ export default function ConfirmarServicio({
updateIsLoading(true);
const res = await funcConfirmar(data);
// ✅ TS sabe que res.data.message existe
Swal.fire({
icon: 'success',
title: 'Exito',
text: res.data.message,
});
// imprimirMensaje(res.data.message);
router.replace("/administrador");
imprimirMensaje(res.data.message);
router.push("/admin");
} catch (err: unknown) {
const axiosErr = err as AxiosError<any>;
const mensaje = axiosErr?.response?.data?.message || 'No se pudo confirmar el servicio, por favor intentelo mas tarde.';
Swal.fire({
icon: 'error',
title: 'Error',
text: mensaje,
});
// if (isAxiosError(err)) {
// imprimirError(err.response?.data || err.message);
// } else {
// imprimirError(err);
// }
if (isAxiosError(err)) {
imprimirError(err.response?.data || err.message);
} else {
imprimirError(err);
}
} finally {
updateIsLoading(false);
}
};
const confirmarPreRegistro = (data: ConfirmarData) =>
const confirmarPreRegistro = (data: ConfirmarData) =>
axiosInstance.put<ApiResponse>(`/servicio/registro`, data);
const confirmarLiberacion = (data: ConfirmarData) => {
@@ -136,29 +118,9 @@ export default function ConfirmarServicio({
return axiosInstance.put<ApiResponse>(`/servicio/liberacion`, data);
};
// Validar las claves permitidas en un arreglo
const clavesPermitidas = process.env.NEXT_PUBLIC_CP?.split(',').map(c => c.trim()) || [];
const confirma = () => {
Swal.fire({
title: "¿Seguro(a) que quieres confirmar este servicio?",
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'confirmar',
cancelButtonText: 'cancelar',
confirmButtonColor: "#0d6efd",
cancelButtonColor: "#dc3545",
}).then((result) => {
if (result.isConfirmed) {
confirmarServicio();
}
})
}
return (
// datos.Programa?.acatlan && datos.programa?.programa === clavesPermitidas &&
<div className="container space-y-4">
{datos.status?.idStatus === 5 && clavesPermitidas.includes(datos?.programa?.clavePrograma || '') && (
<div className="space-y-4">
{datos.Status?.idStatus === 5 && datos.Programa?.acatlan && (
<div className="flex items-center space-x-2">
<input
type="checkbox"
@@ -167,23 +129,28 @@ export default function ConfirmarServicio({
onChange={(e) => setVistoBuenoAcatlan(e.target.checked)}
className="form-checkbox h-5 w-5 text-blue-600"
/>
<label htmlFor="vistoBuenoAcatlan" className="text-gray-700 mb-4 me-2 ms-2">
<label htmlFor="vistoBuenoAcatlan" className="text-gray-700">
Visto bueno Acatlán
</label>
</div>
)}
{(datos.status?.idStatus === 1 || datos.status?.idStatus === 5) && (
{(datos.Status?.idStatus === 1 || datos.Status?.idStatus === 5) && (
<button
disabled={
datos.status?.idStatus === 5 &&
datos.programa?.acatlan &&
datos.Status?.idStatus === 5 &&
datos.Programa?.acatlan &&
!vistoBuenoAcatlan
}
onClick={confirma}
onClick={() =>
imprimirWarning(
"¿Seguro(a) que quieres confirmar este servicio?",
confirmarServicio
)
}
className={`px-4 py-2 rounded text-white ${
datos.status?.idStatus === 5 &&
datos.programa?.acatlan &&
datos.Status?.idStatus === 5 &&
datos.Programa?.acatlan &&
!vistoBuenoAcatlan
? "bg-gray-400 cursor-not-allowed"
: "bg-green-600 hover:bg-green-700"
+17 -58
View File
@@ -1,9 +1,7 @@
"use client"
import { axiosInstance } from "@/api/config";
import { error } from "console";
import { useState } from "react";
import { Button, Col, FormGroup, FormLabel, FormSelect, InputGroup } from "react-bootstrap";
import Swal from "sweetalert2";
interface Props {
years: number[];
@@ -12,63 +10,26 @@ interface Props {
}
export default function Cuestionario({ years, admin, updateIsLoading }: Props) {
const [selectedCuestionario, setSelectedCuestionario] = useState({});
const [selectedCuestionario, setSelectedCuestionario] = useState("");
const [version, setVersion] = useState("");
const [selectedYear, setSelectedYear] = useState("");
const downloadExcel = async () => {
try {
//updateIsLoading(true);
//const res = await axiosInstance.get(`/cuestionario_alumno?year=${selectedCuestionario}&version=${version}`, {
const res = await axiosInstance.get(`/cuestionario-alumno2`, {
params: { anio: selectedYear, version },
const res = await axiosInstance.get(`/${selectedCuestionario}`, {
params: { year: selectedYear, version },
responseType: "blob",
});
console.log("Esrta es la respuesta", res)
console.log("Esta es la infromacino de la respuesta", res.data)
//saveAs(res.data, `${selectedYear}_${selectedCuestionario}_${version}.csv`);
// Crear blob manualmente
const blob = new Blob([res.data], { type: "text/csv" });
// Crear URL temporal para descargar
const url = window.URL.createObjectURL(blob);
// Crear link "virtual"
const link = document.createElement("a");
link.href = url;
link.download = `${selectedYear}_${selectedCuestionario}_${version}.csv`;
// Disparar descarga
document.body.appendChild(link);
link.click();
// limpiar URL temporal
link.remove();
window.URL.revokeObjectURL(url);
Swal.fire({
title: 'Éxito',
text: 'Se descargo el cuestionario exitosamente.',
icon: 'success',
})
// reset
setSelectedYear("");
setSelectedCuestionario("");
setVersion("");
//updateIsLoading(false);
} catch (err) {
Swal.fire({
title: 'Error',
text: 'Error al descargar el cuestionario.',
icon: 'error',
})
console.error("Error en descargar el formulario", err)
} catch (err: unknown) {
//updateIsLoading(false);
// optional: handle error, e.g. console.error(err)
}
@@ -81,10 +42,9 @@ export default function Cuestionario({ years, admin, updateIsLoading }: Props) {
<div>
<Col>
<FormGroup>
<FormLabel className="fw-semibold">Cuestionario:</FormLabel>
<FormLabel>Cuestionario:</FormLabel>
<InputGroup>
<FormSelect onChange={(e) => setSelectedCuestionario(e.target.value)}>
<option value="">Seleccione un cuestionario:</option>
<FormSelect>
<option value="cuestionario_alumno">Cuestionario de Alumnos</option>
<option value="cuestionario_programa">Cuestionario de Programas</option>
</FormSelect>
@@ -94,10 +54,9 @@ export default function Cuestionario({ years, admin, updateIsLoading }: Props) {
<Col>
<FormGroup>
<FormLabel className="fw-semibold">Version:</FormLabel>
<FormLabel>Version:</FormLabel>
<InputGroup>
<FormSelect value={version} onChange={(e) => setVersion(e.target.value)}>
<option value="">Seleccione una version:</option>
<FormSelect>
<option value="v1">V1</option>
<option value="v2">V2</option>
</FormSelect>
@@ -107,15 +66,15 @@ export default function Cuestionario({ years, admin, updateIsLoading }: Props) {
<Col>
<FormGroup>
<FormLabel className="fw-semibold">Año:</FormLabel>
<FormLabel>Año:</FormLabel>
<InputGroup>
<FormSelect value={selectedYear} onChange={(e) => setSelectedYear(e.target.value)}>
<option value="">Seleccione un año:</option>
{years.map((y, i) => (
<option key={i} value={y}>
{y}
</option>
))}
<option value="">Seleccione un año:</option>
{years.map((y, i) => (
<option key={i} value={y}>
{y}
</option>
))}
</FormSelect>
</InputGroup>
</FormGroup>
@@ -123,10 +82,10 @@ export default function Cuestionario({ years, admin, updateIsLoading }: Props) {
<div className="d-flex gap-2 mt-4 mb-3">
<Button
//disabled={!selectedYear || !selectedCuestionario || !version}
disabled={!selectedYear || !selectedCuestionario || !version}
onClick={downloadExcel}
>
Descragar Excel
Enviar archivo
</Button>
</div>
</div>
@@ -8,12 +8,6 @@ import validator from "validator";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { useRouter } from "next/navigation";
import { registerLocale } from "react-datepicker";
import { es } from "date-fns/locale/es";
import { Col, FormGroup, FormLabel, InputGroup } from "react-bootstrap";
import { FaRegCalendar } from "react-icons/fa6";
registerLocale("es", es);
interface Viejo {
correo?: string;
@@ -28,9 +22,7 @@ interface Viejo {
}
interface Admin {
idTipoUsuario: number;
tipoUsuario?: string;
token: string;
token: { headers: Record<string, string> };
}
interface Props {
@@ -92,12 +84,7 @@ export default function EditarAlumno({
};
const actualizar = async () => {
const data: Record<string, unknown> = { idCasoEspecial };
if (!data.idCasoEspecial) {
console.log('No tiene el id')
return;
}
const data: Record<string, unknown> = { idCasoEspecial };
if (nuevo.direccion) data.direccion = nuevo.direccion;
if (nuevo.correo) data.correo = nuevo.correo;
@@ -105,18 +92,16 @@ export default function EditarAlumno({
if (nuevo.motivo) data.motivo = nuevo.motivo;
if (nuevo.dependencia) data.dependencia = nuevo.dependencia;
if (nuevo.institucion) data.institucion = nuevo.institucion;
if (nuevo.fechaInicio) data.fechaInicio = formatDate(nuevo.fechaInicio);
if (nuevo.fechaFin) data.fechaFin = formatDate(nuevo.fechaFin);
if (nuevo.fechaInicio) data.fechaInicio = moment(nuevo.fechaInicio).toDate();
if (nuevo.fechaFin) data.fechaFin = moment(nuevo.fechaFin).toDate();
if (nuevo.fechaNacimiento)
data.fechaNacimiento = formatDate(nuevo.fechaNacimiento);
console.log('Esta es la info', data);
data.fechaNacimiento = moment(nuevo.fechaNacimiento).toDate();
try {
updateIsLoading(true);
const res = await axiosInstance.put(`/caso-especial/update/${idCasoEspecial}`, data);
const res = await axiosInstance.put(`/caso_especial/update`, data, admin.token);
imprimirMensaje(res.data.message);
router.push("/administrador/casos_especiales");
router.push("/admin/casos_especiales/caso_especial");
} catch (err: unknown) {
let msg: unknown = err;
if (isAxiosError(err) && err.response?.data) {
@@ -125,139 +110,65 @@ export default function EditarAlumno({
msg = err.message;
}
imprimirError(msg);
router.replace('/adminsitrador/casos_especiales')
} finally {
updateIsLoading(false);
}
};
const formatDate = (date: Date) => {
return date.toISOString().split("T")[0];
};
return (
<div className="space-y-4 mt-6">
<h2 className="fw-bold mb-4">Editar información del alumno</h2>
<h3 className="text-2xl font-semibold mb-4">Editar información del alumno</h3>
{/* Correo */}
<div className="mb-3">
<label className="form-label fw-semibold block mb-1 font-medium">Correo electrónico</label>
<div>
<label className="block mb-1 font-medium">Correo electrónico</label>
<input
type="email"
placeholder={viejo.correo}
value={nuevo.correo || ""}
onChange={(e) => setNuevo({ ...nuevo, correo: e.target.value })}
className="form-control border rounded p-2 w-full"
className="border rounded p-2 w-full"
/>
</div>
{/* Teléfono */}
<div className="mb-3">
<label className="form-label fw-semibold block mb-1 font-medium">Teléfono</label>
<div>
<label className="block mb-1 font-medium">Teléfono</label>
<input
type="tel"
maxLength={10}
placeholder={viejo.telefono}
value={nuevo.telefono || ""}
onChange={(e) => setNuevo({ ...nuevo, telefono: e.target.value })}
className="form-control border rounded p-2 w-full"
className="border rounded p-2 w-full"
/>
</div>
{/* Dirección */}
<div className="mb-3">
<label className="form-label fw-semibold block mb-1 font-medium">Dirección</label>
<div>
<label className="block mb-1 font-medium">Dirección</label>
<input
type="text"
placeholder={viejo.direccion}
value={nuevo.direccion || ""}
onChange={(e) => setNuevo({ ...nuevo, direccion: e.target.value })}
className="form-control border rounded p-2 w-full"
className="border rounded p-2 w-full"
/>
</div>
<Col>
<FormGroup>
<FormLabel>
Fecha de inicio
</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendar />
</InputGroup.Text>
<DatePicker
selected={nuevo.fechaInicio}
onChange={(date) => setNuevo({ ...nuevo, fechaInicio: date || undefined })}
minDate={minDate1}
placeholderText={fecha(viejo.fechaInicio)}
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
showMonthDropdown
showYearDropdown
scrollableYearDropdown
yearDropdownItemNumber={60}
dropdownMode="select"
locale="es"
/>
</InputGroup>
</FormGroup>
</Col>
{/* Fecha inicio */}
{/* <div>
<label className="form-label fw-semibold block mb-1 font-medium">Fecha de inicio</label>
<div>
<label className="block mb-1 font-medium">Fecha de inicio</label>
<DatePicker
selected={nuevo.fechaInicio}
onChange={(date) => setNuevo({ ...nuevo, fechaInicio: date || undefined })}
minDate={minDate1}
placeholderText={fecha(viejo.fechaInicio)}
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
showMonthDropdown
showYearDropdown
scrollableYearDropdown
yearDropdownItemNumber={60}
dropdownMode="select"
locale="es"
selected={nuevo.fechaInicio}
onChange={(date) => setNuevo({ ...nuevo, fechaInicio: date || undefined })}
minDate={minDate1}
placeholderText={fecha(viejo.fechaInicio)}
className="border rounded p-2 w-full"
/>
</div> */}
</div>
<Col>
<FormGroup>
<FormLabel>Fecha de fin</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendar />
</InputGroup.Text>
<DatePicker
selected={nuevo.fechaFin}
onChange={(date) => setNuevo({ ...nuevo, fechaFin: date || undefined })}
minDate={minDate2}
placeholderText={fecha(viejo.fechaFin)}
dateFormat={'dd-MM-yyyy'}
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
showMonthDropdown
showYearDropdown
scrollableYearDropdown
yearDropdownItemNumber={60}
dropdownMode="select"
locale='es'
/>
</InputGroup>
</FormGroup>
</Col>
{/* Fecha fin
{/* Fecha fin */}
{nuevo.fechaInicio && (
<div>
<label className="block mb-1 font-medium">Fecha de fin</label>
@@ -267,61 +178,29 @@ export default function EditarAlumno({
minDate={minDate2}
placeholderText={fecha(viejo.fechaFin)}
className="border rounded p-2 w-full"
locale='es'
/>
</div>
)} */}
<Col>
<FormGroup>
<FormLabel>Fecha de nacimiento</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendar />
</InputGroup.Text>
<DatePicker
selected={nuevo.fechaNacimiento}
onChange={(date) => setNuevo({ ...nuevo, fechaNacimiento: date || undefined })}
placeholderText={fecha(viejo.fechaNacimiento)}
dateFormat={'dd-MM-yyyy'}
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
showMonthDropdown
showYearDropdown
scrollableYearDropdown
yearDropdownItemNumber={60}
dropdownMode="select"
locale='es'
/>
</InputGroup>
</FormGroup>
</Col>
)}
{/* Fecha nacimiento */}
{/* <div>
<div>
<label className="block mb-1 font-medium">Fecha de nacimiento</label>
<DatePicker
selected={nuevo.fechaNacimiento}
onChange={(date) => setNuevo({ ...nuevo, fechaNacimiento: date || undefined })}
placeholderText={fecha(viejo.fechaNacimiento)}
className="form-control border rounded p-2 w-full"
locale='es'
className="border rounded p-2 w-full"
/>
</div> */}
</div>
{/* Motivo */}
{viejo.motivo && (
<div className="mb-3">
<label className="form-label fw-semibold block mb-1 font-medium">Motivo</label>
<div>
<label className="block mb-1 font-medium">Motivo</label>
<select
value={nuevo.motivo || ""}
onChange={(e) => setNuevo({ ...nuevo, motivo: e.target.value })}
className="form-control border rounded p-2 w-full my-2"
className="border rounded p-2 w-full my-2"
>
<option value="">Seleccione...</option>
<option value="1">Tercera edad</option>
@@ -332,28 +211,28 @@ export default function EditarAlumno({
{/* Dependencia */}
{viejo.dependencia && (
<div className="mb-3">
<label className="form-label fw-semibold block mb-1 font-medium">Dependencia</label>
<div>
<label className="block mb-1 font-medium">Dependencia</label>
<input
type="text"
placeholder={viejo.dependencia}
value={nuevo.dependencia || ""}
onChange={(e) => setNuevo({ ...nuevo, dependencia: e.target.value })}
className="form-control border rounded p-2 w-full"
className="border rounded p-2 w-full"
/>
</div>
)}
{/* Institución */}
{viejo.institucion && (
<div className="mb-3">
<label className="form-label fw-semibold block mb-1 font-medium">Institución</label>
<div>
<label className="block mb-1 font-medium">Institución</label>
<input
type="text"
placeholder={viejo.institucion}
value={nuevo.institucion || ""}
onChange={(e) => setNuevo({ ...nuevo, institucion: e.target.value })}
className="form-control border rounded p-2 w-full"
className="border rounded p-2 w-full"
/>
</div>
)}
@@ -5,8 +5,6 @@ import { axiosInstance } from "@/api/config";
import { isAxiosError } from "axios";
import validator from "validator";
import { useRouter } from "next/navigation";
import Swal from "sweetalert2";
import { toast } from "react-toastify";
interface Responsable {
idUsuario?: number;
@@ -28,11 +26,6 @@ interface Props {
updateIsLoading: (value: boolean) => void;
}
function convertirFecha(fecha: string | Date) {
return new Date(fecha).toISOString().split("T")[0];
}
export default function EditarResponsable({
//admin,
responsable,
@@ -59,81 +52,25 @@ export default function EditarResponsable({
return true;
};
// Funcion para confirmar actualizacion
const confirmarActualizacion = () => {
Swal.fire({
title: '¿Seguro(a) que quiere actualizar la información de este usuario?',
icon: "warning",
showCancelButton: true,
confirmButtonText: "Sí, actualizar",
cancelButtonText: "Cancelar",
confirmButtonColor: "#0d6efd",
cancelButtonColor: "#dc3545",
}).then((result) => {
if (result.isConfirmed) {
actualizar(); // 👈 aquí ejecutas la función
}
});
};
const confirmarCambioPassword = () => {
Swal.fire({
title: '¿Seguro(a) que quiere cambiar la contraseña para este usuario?',
icon: "warning",
showCancelButton: true,
confirmButtonText: "Sí, actualizar",
cancelButtonText: "Cancelar",
confirmButtonColor: "#0d6efd",
cancelButtonColor: "#dc3545",
}).then((result) => {
if (result.isConfirmed) {
password(); // 👈 aquí ejecutas la función
}
});
}
// Actualizar información del responsable
const actualizar = async () => {
const idResponsable = localStorage.getItem("idResponsable");
if (!idResponsable) return
const data: Record<string, unknown> = { idUsuario: responsable.idUsuario };
const data: Record<string, unknown> = { idUsuario: responsable.idUsuario };
if (nuevo.correo) data.correo = nuevo.correo;
if (nuevo.nombre) data.nombre = nuevo.nombre;
// Hacemos el parseo de la fecha
//data.fechaNacimiento = convertirFecha(data.fechaNacimiento);
try {
updateIsLoading(true);
const res = await axiosInstance.patch(`/usuario/responsable/${idResponsable}`, data);
Swal.fire({
title: 'Éxito',
text: 'Se actualizaron los datos correctamente',
icon: 'success',
})
//imprimirMensaje(res.data.message);
router.replace("/administrador/responsables/responsable");
} catch (err) {
Swal.fire({
title: 'Error',
text: 'Error al actualizar los datos',
icon: 'error',
})
// let msg: unknown = err;
// if (isAxiosError(err) && err.response?.data) {
// console.error("ERROR DEL SERVER:", err.response.data);
// msg = JSON.stringify(err.response.data, null, 2);
// } else if (err instanceof Error) {
// msg = err.message;
// }
// imprimirError(msg);
const res = await axiosInstance.put(`/usuario/responsable/update`, data);
imprimirMensaje(res.data.message);
router.push("/administrador/responsables/responsable");
} catch (err: unknown) {
let msg: unknown = err;
if (isAxiosError(err) && err.response?.data) {
msg = err.response.data;
} else if (err instanceof Error) {
msg = err.message;
}
imprimirError(msg);
} finally {
updateIsLoading(false);
}
@@ -143,92 +80,82 @@ export default function EditarResponsable({
const password = async () => {
const data = { idUsuario: responsable.idUsuario };
const idResponsable = localStorage.getItem("idResponsable");
if (!idResponsable) return
try {
updateIsLoading(true);
const res = await axiosInstance.post(`/usuario/new-password-responsable/${idResponsable}`);
Swal.fire({
title: 'Éxito',
text: 'Se envio la nueva contraseña al responsable',
icon: 'success',
})
//imprimirMensaje(res.data.message);
router.replace("/administrador/responsables/responsable");
} catch (err) {
Swal.fire({
title: 'Error',
text: 'Error al actualizar la contraseña del responsable',
icon: 'error',
})
// let msg: unknown = err;
// if (isAxiosError(err) && err.response?.data) {
// msg = err.response.data;
// } else if (err instanceof Error) {
// msg = err.message;
// }
// imprimirError(msg);
const res = await axiosInstance.put(`/usuario/new_password_responsable`, data);
imprimirMensaje(res.data.message);
router.push("/administrador/responsables/responsable");
} catch (err: unknown) {
let msg: unknown = err;
if (isAxiosError(err) && err.response?.data) {
msg = err.response.data;
} else if (err instanceof Error) {
msg = err.message;
}
imprimirError(msg);
} finally {
updateIsLoading(false);
updateIsLoading(false);
}
};
return (
<div className="sspace-y-4 mt-6">
<h2 className="text-2xl mb-4 fw-semibold">Editar información del responsable</h2>
<div className="space-y-4 mt-6">
<h3 className="text-2xl font-semibold mb-4">Editar información del responsable</h3>
{/* Nombre */}
<div>
<label className="block mb-1 font-medium">Nombre</label>
<input
type="text"
placeholder={responsable.nombre}
value={nuevo.nombre || ""}
onChange={(e) => setNuevo({ ...nuevo, nombre: e.target.value })}
className="border rounded p-2 w-full"
/>
</div>
{/* Nombre */}
<div className="mb-3">
<label className="form-label fw-semibold block mb-1 font-medium">Nombre</label>
<input
type="text"
placeholder={responsable.nombre}
value={nuevo.nombre || ""}
onChange={(e) => setNuevo({ ...nuevo, nombre: e.target.value })}
className="form-control border rounded p-2 w-full"
/>
</div>
{/* Correo */}
<div>
<label className="block mb-1 font-medium">Correo electrónico</label>
<input
type="email"
placeholder={responsable.usuario}
value={nuevo.correo || ""}
onChange={(e) => setNuevo({ ...nuevo, correo: e.target.value })}
className="border rounded p-2 w-full"
/>
</div>
{/* Correo */}
<div>
<label className="form-label block mb-1 font-medium">Correo electrónico</label>
<input
type="email"
placeholder={responsable.usuario}
value={nuevo.correo || ""}
onChange={(e) => setNuevo({ ...nuevo, correo: e.target.value })}
className="form-control border rounded p-2 w-full"
/>
</div>
{/* Botones */}
<div className="pt-5 flex gap-4">
<button
disabled={mostrarBoton()}
onClick={() =>
imprimirWarning(
"¿Seguro(a) que quiere actualizar la información de este usuario?",
actualizar
)
}
className={`px-4 py-2 rounded text-white ${
mostrarBoton() ? "bg-gray-400 cursor-not-allowed" : "bg-blue-600 hover:bg-blue-700"
}`}
>
Guardar Cambios
</button>
{/* Botones */}
<div className="pt-5 flex gap-4">
<button
disabled={mostrarBoton()}
onClick={confirmarActualizacion}
className={`mb-4 px-4 py-2 rounded text-white ${
mostrarBoton() ? "bg-gray-400 cursor-not-allowed" : "bg-blue-600 hover:bg-blue-700"
}`}
>
Guardar Cambios
</button>
<button
onClick={confirmarCambioPassword}
className="ms-3 px-4 py-2 rounded bg-morado hover:bg-blue-600 text-white"
>
Cambiar contraseña
</button>
</div>
<button
onClick={() =>
imprimirWarning(
"¿Seguro(a) que quiere cambiar la contraseña para este usuario?",
password
)
}
className="px-4 py-2 rounded bg-blue-500 hover:bg-blue-600 text-white"
>
Cambiar contraseña
</button>
</div>
</div>
);
}
+151 -353
View File
@@ -2,22 +2,15 @@
import { useState, useEffect } from "react";
import { axiosInstance } from "@/api/config";
import axios, { isAxiosError } from 'axios';
import { isAxiosError } from 'axios';
import moment from "moment";
import validator from "validator";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { useRouter } from "next/navigation";
import { Col, FormGroup, FormLabel, InputGroup } from "react-bootstrap";
import { FaRegCalendar, FaUpload } from "react-icons/fa6";
import Swal from "sweetalert2";
import { registerLocale } from "react-datepicker";
import { es } from "date-fns/locale/es";
registerLocale("es", es);
interface Servicio {
status: { idStatus?: number };
Status: { idStatus?: number };
correo?: string;
direccion?: string;
telefono?: string;
@@ -35,7 +28,7 @@ interface AdminToken {
tokenArchivo?: { headers: Record<string, string> };
}; */}
interface Props {
admin: AdminToken;
admin: string;
imprimirError: (err: unknown) => void;
imprimirMensaje: (msg: string) => void;
imprimirWarning: (msg: string, onConfirm: () => void) => void;
@@ -49,11 +42,8 @@ export default function EditarServicio({
imprimirWarning,
updateIsLoading,
}: Props) {
const [selectedInicio, setSelectedInicio] = useState<Date | null>(null);
const [selectFechaFin, setSelectedFechaFin] = useState<Date | null>(null);
const [servicioid, setServicioid] = useState<number>();
const [servicio, setServicio] = useState<Servicio>({ status: {} });
const [servicio, setServicio] = useState<Servicio>({ Status: {} });
const [correo, setCorreo] = useState("");
const [telefono, setTelefono] = useState("");
const [direccion, setDireccion] = useState("");
@@ -70,14 +60,6 @@ export default function EditarServicio({
const fecha = (date?: Date) =>
date ? moment(date).format("DD/MM/YYYY") : "";
const validarFechas = () => {
const fechaInicio = moment(selectedInicio);
const fechaFin = moment(selectFechaFin);
if (!fechaInicio.isValid() || !fechaFin.isValid()) return true;
return false
}
// Validación de archivos
const sizeFileValido = (file: File) => {
if (file.size >= 20000000) {
@@ -98,17 +80,14 @@ export default function EditarServicio({
try {
updateIsLoading(true);
console.log("Obteniendo registro para servicioid al hacer el get:", servicioid);
const res = await axiosInstance.get(`/servicio/admin/${servicioid}`);
const res = await axiosInstance.get(`/servicio/admin?idServicio=${servicioid}`);
console.log("Respuesta del servicio obtenido:", res.data);
const data = res.data;
setServicio(data);
console.log("Esta es la info de la respuesta", res.data)
console.log("Esta es la info de la respuesta con doble data", res.data.data)
// Redirección si el estado no es válido
if (data.status.idStatus === 6 || data.status.idStatus === 10) {
router.push("/administrador/servicio");
if (data.Status.idStatus === 6 || data.Status.idStatus === 10) {
router.push("/admin/servicio");
}
setFechaInicio(new Date(data.fechaInicio));
@@ -141,61 +120,25 @@ export default function EditarServicio({
if (direccion) data.direccion = direccion;
if (correo) data.correo = correo;
if (telefono) data.telefono = telefono;
// Transformamos las fechas para que no muestre error en el back
if (selectedInicio && moment(selectedInicio).isValid()) data.fechaInicio = moment(selectedInicio).format("YYYY-MM-DD");
if (selectFechaFin && moment(selectFechaFin).isValid()) data.fechaFin = moment(selectFechaFin).format("YYYY-MM-DD");
if (fechaNacimiento && moment(fechaNacimiento).isValid()) data.fechaNacimiento = moment(fechaNacimiento).format("YYYY-MM-DD");
if (fechaInicio) data.fechaInicio = fechaInicio;
if (fechaFin) data.fechaFin = fechaFin;
if (fechaNacimiento) data.fechaNacimiento = fechaNacimiento;
formData.append("data", JSON.stringify(data));
console.log("Datos a actualizar:", formData);
for (const [key, value] of formData.entries()) {
console.log("FormData:", key, value);
}
try {
updateIsLoading(true);
// Agregue esta parte para verificar que funcione
if (cartaAceptacion) {
console.log("Carta Aceptación:", {
name: cartaAceptacion.name,
size: cartaAceptacion.size,
type: cartaAceptacion.type,
});
}
if (cartaTermino) {
console.log("Carta Término:", cartaTermino.name);
}
if (informeGlobal) {
console.log("Informe Global:", informeGlobal.name);
}
const token = localStorage.getItem("token") || "";
// Falta corregir este endpoint utilizar la variable del .env
const res = await axios.put(`${process.env.NEXT_PUBLIC_API_URL}/servicio/update`, formData, {
headers: {
Authorization: `Bearer ${token}`,
},
}); //, admin.tokenArchivo
Swal.fire('Exito', 'Se actualizaron los datos correctamente.', 'success')
// imprimirMensaje(res.data.message);
const res = await axiosInstance.put(`/servicio/update`, formData); //, admin.tokenArchivo
imprimirMensaje(res.data.message);
updateIsLoading(false);
router.replace("/administrador/servicio");
router.push("/administrador/servicio");
} catch (err: unknown) {
Swal.fire('Error', 'No se pudieron actualizar los datos', 'error')
updateIsLoading(false);
// if (isAxiosError(err)) imprimirError(err.response?.data ?? err.message);
// else if (err instanceof Error) imprimirError(err.message);
// else imprimirError(err);
if (isAxiosError(err)) imprimirError(err.response?.data ?? err.message);
else if (err instanceof Error) imprimirError(err.message);
else imprimirError(err);
}
};
@@ -203,28 +146,17 @@ export default function EditarServicio({
const password = async () => {
const data = { servicioid };
console.log("Id del servicio para nueva password", data);
console.log("Id servicio entrando a la info", data.servicioid)
//const idServicio ={ idServicio: servicioid }
try {
console.log("Si entro par mandar la solicitud")
updateIsLoading(true);
const res = await axiosInstance.post(`/usuario/new-password-alumno/${data.servicioid}`);
// imprimirMensaje(res.data.message);
Swal.fire('Exito', 'Se actualizo la contraseña', 'success')
const res = await axiosInstance.put(`/usuario/new_password_alumno`, data);
imprimirMensaje(res.data.message);
updateIsLoading(false);
router.replace("/administrador");
router.push("/admin");
} catch (err: unknown) {
Swal.fire('Error', 'No se pudo mandar la nueva contraseña', 'error')
console.log("No esta mandando la solicitud")
updateIsLoading(false);
// if (isAxiosError(err)) imprimirError(err.response?.data || err.message);
// else if (err instanceof Error) imprimirError(err.message);
// else imprimirError(err);
if (isAxiosError(err)) imprimirError(err.response?.data || err.message);
else if (err instanceof Error) imprimirError(err.message);
else imprimirError(err);
}
};
@@ -258,44 +190,8 @@ export default function EditarServicio({
} else {
setActualizarFechaFin(true);
}
console.log("Esta es la info de servicio", servicio);
}, [fechaInicio]);
const confirmarActualizarDatos = () => {
Swal.fire({
title: '¿Seguro(a) que quiere actualizar estos datos?',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Confirmar',
cancelButtonText: 'Cancelar',
confirmButtonColor: "#0d6efd",
cancelButtonColor: "#dc3545",
}).then((result) => {
if (result.isConfirmed) {
actualizar()
}
})
}
const confirmarEnvioPassword = () => {
Swal.fire({
title: '¿Seguro(a) que quieres cambiar/reenviar la contraseña de este alumno?',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Confirmar',
cancelButtonText: 'Cancelar',
confirmButtonColor: "#0d6efd",
cancelButtonColor: "#dc3545",
}).then((result) => {
if (result.isConfirmed) {
password();
}
})
}
// Validar tamaños de archivos
useEffect(() => {
if (cartaAceptacion && !sizeFileValido(cartaAceptacion))
@@ -315,7 +211,7 @@ export default function EditarServicio({
useEffect(() => {
const id = Number(localStorage.getItem("idServicio"));
console.log("ID Servicio para editar:", id);
if (!id) router.replace("/administrador");
if (!id) router.push("/admin");
else {
setServicioid(id);
console.log("idServicio seteado:", id);
@@ -327,241 +223,143 @@ export default function EditarServicio({
return (
<div className="space-y-4 mt-6">
<h2 className="fw-bold mb-4">
Editar información del alumno
</h2>
<h3 className="text-2xl font-semibold mb-4">
Editar información del alumno
</h3>
{/* Correo */}
<div className="mb-3">
<label className="form-label fw-semibold block font-medium mb-1">
Correo electrónico del alumno
</label>
<input
type="email"
placeholder={servicio.correo || "Correo"}
value={correo}
onChange={(e) => setCorreo(e.target.value)}
className="form-control border rounded p-2 w-full"
/>
{/* Correo */}
<div>
<label className="block font-medium mb-1">
Correo electrónico del alumno
</label>
<input
type="email"
placeholder={servicio.correo || "Correo"}
value={correo}
onChange={(e) => setCorreo(e.target.value)}
className="border rounded p-2 w-full"
/>
</div>
{/* Fecha inicio */}
<div>
<label className="block font-medium mb-1">Fecha de inicio</label>
<DatePicker
selected={fechaFin}
onChange={(date: Date | null) => {
if (date) setFechaFin(date);
}}
minDate={fechaInicio}
className="border rounded p-2 w-full"
/>
</div>
{/* Fecha fin */}
<div>
<label className="block font-medium mb-1">Fecha de fin</label>
<DatePicker
selected={fechaInicio}
onChange={(date: Date | null) => {
if (date) setFechaInicio(date);
}}
minDate={new Date("2020-01-01")}
className="border rounded p-2 w-full"
/>
</div>
{/* Dirección */}
{servicio.direccion && (
<div>
<label className="block font-medium mb-1">Dirección</label>
<input
type="text"
placeholder={servicio.direccion}
value={direccion}
onChange={(e) => setDireccion(e.target.value)}
className="border rounded p-2 w-full"
/>
</div>
)}
{/* Fecha inicio */}
<Col>
<FormGroup>
<FormLabel>Fecha de inicio</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendar />
</InputGroup.Text>
<DatePicker
selected={selectedInicio}
onChange={(date: Date | null ) => {
if (date) setSelectedInicio(date);
}}
placeholderText="Selecciona una fecha de inicio"
// minDate={fechaInicio} Quitamos la restricción de fecha mínima
dateFormat={'dd-MM-yyyy'}
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
showMonthDropdown
showYearDropdown
scrollableYearDropdown
yearDropdownItemNumber={60}
dropdownMode="select"
locale='es'
/>
</InputGroup>
</FormGroup>
</Col>
<Col>
<FormGroup>
<FormLabel>Fecha de fin</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendar />
</InputGroup.Text>
<DatePicker
selected={selectFechaFin}
onChange={(date: Date | null) => {
if (date) setSelectedFechaFin(date);
}}
placeholderText="Selecciona una fecha de fin"
dateFormat={'dd-MM-yyyy'}
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
showMonthDropdown
showYearDropdown
scrollableYearDropdown
yearDropdownItemNumber={60}
dropdownMode="select"
locale='es'
/>
</InputGroup>
</FormGroup>
</Col>
{/*
Fecha inicio
<div className="mb-3">
<label className="form-label fw-semibold block font-medium mb-1">Fecha de inicio</label>
<DatePicker
selected={fechaFin}
onChange={(date: Date | null) => {
if (date) setFechaFin(date);
}}
minDate={fechaInicio}
className="border rounded p-2 w-full"
/>
{/* Teléfono */}
{servicio.telefono && (
<div>
<label className="block font-medium mb-1">Teléfono</label>
<input
type="tel"
maxLength={10}
placeholder={servicio.telefono}
value={telefono}
onChange={(e) => setTelefono(e.target.value)}
className="border rounded p-2 w-full"
/>
</div>
)}
fecha fin
<div className="mb-3">
<label className="form-labeñ fw-semibold block font-medium mb-1">Fecha de fin</label>
<DatePicker
selected={fechaInicio}
onChange={(date: Date | null) => {
if (date) setFechaInicio(date);
}}
minDate={new Date("2020-01-01")}
className="border rounded p-2 w-full"
/>
{/* Fecha nacimiento */}
{servicio.fechaNacimiento && (
<div>
<label className="block font-medium mb-1">
Fecha de nacimiento
</label>
<DatePicker
selected={fechaFin}
onChange={(date: Date | null) => {
if (date) setFechaFin(date);
}}
minDate={fechaInicio}
className="border rounded p-2 w-full"
/>
</div>
*/}
)}
{/* Dirección */}
{servicio.direccion && (
<div className="mb-3">
<label className="form-label fw-semibold block font-medium mb-1">Dirección</label>
<input
type="text"
placeholder={servicio.direccion}
value={direccion}
onChange={(e) => setDireccion(e.target.value)}
className="form-control border rounded p-2 w-full"
/>
</div>
)}
{/* Subir archivos */}
<div className="space-y-2">
<label className="block font-medium mb-1">Carta de aceptación</label>
<input
type="file"
accept="application/pdf"
onChange={(e) =>
setCartaAceptacion(e.target.files ? e.target.files[0] : null)
}
className="border p-2 rounded w-full"
/>
</div>
{/* Teléfono */}
{servicio.telefono && (
<div className="mb-3">
<label className="form-label fw-semibold block font-medium mb-1">Teléfono</label>
<input
type="tel"
maxLength={10}
placeholder={servicio.telefono}
value={telefono}
onChange={(e) => setTelefono(e.target.value)}
className="form-control border rounded p-2 w-full"
/>
</div>
)}
{/* Botones */}
<div className="mt-6 flex gap-4">
<button
disabled={mostrar()}
onClick={() =>
imprimirWarning(
"¿Seguro(a) que quiere actualizar estos datos?",
actualizar
)
}
className={`px-4 py-2 rounded text-white ${
mostrar()
? "bg-gray-400 cursor-not-allowed"
: "bg-blue-600 hover:bg-blue-700"
}`}
>
Actualizar Datos
</button>
{/*
{servicio.fechaNacimiento && (
<Col>
<FormGroup>
<FormLabel>Fecha de nacimiento</FormLabel>
<InputGroup>
<InputGroup.Text>
</InputGroup.Text>
</InputGroup>
</FormGroup>
</Col>
)}
{servicio.fechaNacimiento && (
<div className="mb-3">
<label className="form-label fw-semibold block font-medium mb-1">
Fecha de nacimiento
</label>
<DatePicker
selected={fechaFin}
onChange={(date: Date | null) => {
if (date) setFechaNacimiento(date);
}}
minDate={fechaInicio}
className="border rounded p-2 w-full"
/>
</div>
)}
*/}
<FormGroup className="mb-4">
<FormLabel>Carta de aceptaciòn</FormLabel>
<div
className="border p-4 text-center rounded"
style={{ cursor: "pointer" }}
onClick={() => document.getElementById("pdfInput")?.click()}
>
<FaUpload size={40} className="mb-2"/>
<p className="mb-1">
{cartaAceptacion?.name || 'Arrastra aqui tu archivo o da click aqui para buscar'}
</p>
<p className="is-size-6">Tamaño màximo 20MB</p>
<p className="is-size-7">Si al momento de elegir un archivo este no se selecciona, haga click en cancelar en la ventana emergente e intente de nuevo.</p>
</div>
<input
id="pdfInput"
type="file"
accept="application/pdf"
style={{ display: 'none'}}
onChange={(e) => {
setCartaAceptacion(e.target.files ? e.target.files[0] : null)
}}
/>
</FormGroup>
{/* Subir archivos */}
{/*
<div className="space-y-2">
<label className="block font-medium mb-1">Carta de aceptación</label>
<input
type="file"
accept="application/pdf"
onChange={(e) =>
setCartaAceptacion(e.target.files ? e.target.files[0] : null)
}
className="border p-2 rounded w-full"
/>
</div>
*/}
{/* Botones */}
<div className="mt-6 flex gap-4 mb-5">
<button
disabled={mostrar()}
onClick={confirmarActualizarDatos}
className={`me-2 px-4 py-2 rounded text-white ${
mostrar()
? "bg-gray-400 cursor-not-allowed"
: "bg-blue-600 hover:bg-blue-700"
}`}
>
Actualizar Datos
</button>
<button
disabled={
servicio.status.idStatus === 1 || servicio.status.idStatus === 7
}
onClick={confirmarEnvioPassword}
className="px-4 py-2 rounded bg-blue-500 hover:bg-blue-600 text-white"
>
Cambiar/Reenviar contraseña
</button>
</div>
<button
disabled={
servicio.Status.idStatus === 1 || servicio.Status.idStatus === 7
}
onClick={() =>
imprimirWarning(
"¿Seguro(a) que quieres cambiar/reenviar la contraseña de este alumno?",
password
)
}
className="px-4 py-2 rounded bg-blue-500 hover:bg-blue-600 text-white"
>
Cambiar/Reenviar contraseña
</button>
</div>
</div>
);
}
@@ -3,7 +3,6 @@ import { useState } from "react";
import { Col, FormGroup, FormLabel, FormSelect, InputGroup, Button } from "react-bootstrap";
import { axiosInstance } from "@/api/config"; // tu config de axios
import { isAxiosError } from 'axios';
import Swal from "sweetalert2";
//import fileDownload from "js-file-download";
interface Props {
@@ -34,47 +33,15 @@ export default function GustavoBazPrada({
}
);
// Crear blob manualmente
const blob = new Blob([res.data], { type: "text/csv" });
// Crear URL temporal para descargar
const url = window.URL.createObjectURL(blob);
// Crear link "virtual"
const link = document.createElement("a");
link.href = url;
link.download = `${selectedYear}_gustavo_baz_prada.csv`;
// Disparar descarga
document.body.appendChild(link);
link.click();
// limpiar URL temporal
link.remove();
window.URL.revokeObjectURL(url);
Swal.fire({
title: 'Éxito',
text: 'Se descargo el archivo correctamente.',
icon: 'success',
})
//fileDownload(res.data, `${selectedYear}_gustavo_baz_prada.csv`);
setSelectedYear("");
updateIsLoading?.(false);
} catch (err: unknown) {
Swal.fire({
title: 'Error',
text: 'No se pudo completar la descarga.',
icon: 'error',
})
updateIsLoading?.(false);
// if (isAxiosError(err)) imprimirError?.(String(err.response?.data) || err.message || 'Error al descargar');
// else if (err instanceof Error) imprimirError?.(err.message);
// else imprimirError?.('Error al descargar');
if (isAxiosError(err)) imprimirError?.(String(err.response?.data) || err.message || 'Error al descargar');
else if (err instanceof Error) imprimirError?.(err.message);
else imprimirError?.('Error al descargar');
}
};
@@ -84,20 +51,20 @@ export default function GustavoBazPrada({
<Col>
<FormGroup>
<FormLabel>Selecciona un año:</FormLabel>
<InputGroup>
<FormSelect
value={selectedYear}
onChange={(e) => setSelectedYear(e.target.value)}
>
<option value="">Seleccione un año:</option>
{(years || []).map((y, i) => (
<option key={i} value={y}>
{y}
</option>
))}
</FormSelect>
</InputGroup>
<FormLabel>Selecciona un año:</FormLabel>
<InputGroup>
<FormSelect
value={selectedYear}
onChange={(e) => setSelectedYear(e.target.value)}
>
<option value="">Seleccione un año:</option>
{(years || []).map((y, i) => (
<option key={i} value={y}>
{y}
</option>
))}
</FormSelect>
</InputGroup>
</FormGroup>
</Col>
@@ -43,17 +43,10 @@ export default function InformacionResponsable({ admin, imprimirError, updateIsL
const obtenerResponsable = async () => {
try {
updateIsLoading(true);
console.log("Este es el id del responsable", idResponsable)
const res = await axiosInstance.get(`/usuario/responsable/${idResponsable}`, {
const res = await axiosInstance.get(`/usuario/responsable?idUsuario=${idResponsable}`, {
headers: { Authorization: admin.token },
});
setResponsable(res.data);
console.log("Esta es la info de la res", res.data)
console.log("Esta es la info con doble data", res.data.data)
obtenerProgramas();
} catch (err: unknown) {
if (isAxiosError(err)) imprimirError(String(err.response?.data) || err.message || 'Error al obtener responsable');
@@ -67,26 +60,21 @@ export default function InformacionResponsable({ admin, imprimirError, updateIsL
// Funcion para obtener programas
const obtenerProgramas = async () => {
try {
const res = await axiosInstance.get(`/programa/programas_admin/${idResponsable}`,);
console.log("Esta es la info de la res de programas", res.data)
console.log("Esta es la info con doble data de programas", res.data.data)
setProgramas(res.data.data);
const res = await axiosInstance.get(`/programa/programas_admin?idUsuario=${idResponsable}`, {
headers: { Authorization: admin.token },
});
setProgramas(res.data);
} catch (err: unknown) {
if (isAxiosError(err)) {
console.error("ERROR COMPLETO:", err.response?.data);
imprimirError(JSON.stringify(err.response?.data, null, 2));
} else if (err instanceof Error) {
imprimirError(err.message);
} else imprimirError('Error al obtener programas');
if (isAxiosError(err)) imprimirError(String(err.response?.data) || err.message || 'Error al obtener programas');
else if (err instanceof Error) imprimirError(err.message);
else imprimirError('Error al obtener programas');
}
};
// Cargar al montar
useEffect(() => {
const id = Number(localStorage.getItem('idResponsable'));
if (!id) {
router.push('/administrador/responsables');
} else {
@@ -121,18 +109,18 @@ export default function InformacionResponsable({ admin, imprimirError, updateIsL
<div className="mb-3">
<label className="form-label fw-semibold">Clave de programa:</label>
<select
className="form-select"
value={programa.clavePrograma || ''}
onChange={(e) =>
setPrograma(programas.find((p) => p.clavePrograma === e.target.value) || {})
}
className="form-select"
value={programa.clavePrograma || ''}
onChange={(e) =>
setPrograma(programas.find((p) => p.clavePrograma === e.target.value) || {})
}
>
<option value="">Elija una clave de programa</option>
{programas.map((p, i) => (
<option key={i} value={p.clavePrograma}>
{p.clavePrograma}
</option>
))}
{programas.map((p, i) => (
<option key={i} value={p.clavePrograma}>
{p.clavePrograma}
</option>
))}
</select>
</div>
@@ -5,7 +5,6 @@ import moment from "moment";
import "moment/locale/es";
import { Button, Row, Col, Form } from "react-bootstrap";
import { useRouter } from "next/navigation";
import Link from "next/link";
interface Usuario {
usuario?: string;
@@ -22,10 +21,9 @@ interface Status {
}
interface Alumno {
idCasoEspecial: number;
usuario?: Usuario;
carrera?: Carrera;
status?: Status;
Usuario?: Usuario;
Carrera?: Carrera;
Status?: Status;
creditos?: number;
correo?: string;
fechaNacimiento?: string;
@@ -58,100 +56,11 @@ export default function InformacionCasoEspecial({ alumno }: Props) {
return (
<div className="mt-4">
{/* Título */}
<h2 className="fw-bold mb-3">{alumno.status?.status}</h2>
<h3 className="text-primary fw-bold mb-3">{alumno.Status?.status}</h3>
<h4 className="mb-4">Datos personales</h4>
<div className="mb-3">
<label className="form-label fw-semibold">Número de Cuenta: </label>
<p className="form-control">{alumno.usuario?.usuario || ''}</p>
</div>
<div className="fw-semibold mb-3">
<label className="form-label">Nombre:</label>
<p className="form-control">{alumno.usuario?.nombre || ''}</p>
</div>
<div className="fw-semibold mb-3">
<label className="form-label">Carrera:</label>
<p className="form-control">{alumno.carrera?.carrera || ''}</p>
</div>
<div className="fw-semibold mb-3">
<label className="form-label">Créditos:</label>
<p className="form-control">{alumno.creditos || ''}</p>
</div>
<div className="fw-semibold mb-3">
<label className="form-label">Correo:</label>
<p className="form-control">{alumno.correo || ''}</p>
</div>
<div className="fw-semibold mb-3">
<label className="form-label">Fecha de Nacimiento:</label>
<p className="form-control">{fecha(alumno.fechaNacimiento)}</p>
</div>
<div className="fw-semibold mb-3">
<label className="form-label">Direcciòn:</label>
<p className="form-control">{alumno.direccion}</p>
</div>
<div className="fw-semibold mb-3">
<label className="form-label">Telèfono:</label>
<p className="form-control">{alumno.telefono}</p>
</div>
{alumno.motivo && (
<div className="fw-semibold mb-3">
<label className="form-label">Motivo:</label>
<p className="form-control">{
alumno.motivo === "1"
? "Tercera edad"
: alumno.motivo === "2"
? "Capacidades diferentes"
: ""
}</p>
</div>
)}
{alumno.institucion && (
<div className="fw-semibold mb-3">
<label className="form-label">Instituciòn:</label>
<p className="form-control">{alumno.institucion}</p>
</div>
)}
{alumno.dependencia && (
<div className="fw-semibold mb-3">
<label className="form-label">Dependencia:</label>
<p className="form-control">{alumno.dependencia}</p>
</div>
)}
<div className="fw-semibold mb-3">
<label className="form-label">Fecha Inicio:</label>
<p className="form-control">{fecha(alumno.fechaInicio)}</p>
</div>
<div className="fw-semibold mb-3">
<label className="form-label">Fecha Fin:</label>
<p className="form-control">{fecha(alumno.fechaFin)}</p>
</div>
<div className="fw-semibold mb-5">
<label className="form-label">Fecha Registro:</label>
<p className="form-control">{fecha(alumno.createdAt)}</p>
</div>
{/* Campos */}
{/*
// Informacino de antes
<Form.Group as={Row} className="mb-2">
<Form.Label column sm={3}>Número de Cuenta:</Form.Label>
<Col sm={9}>
@@ -208,7 +117,7 @@ export default function InformacionCasoEspecial({ alumno }: Props) {
</Col>
</Form.Group>
{alumno.motivo && (
{alumno.motivo && (
<Form.Group as={Row} className="mb-2">
<Form.Label column sm={3}>Motivo:</Form.Label>
<Col sm={9}>
@@ -265,17 +174,15 @@ export default function InformacionCasoEspecial({ alumno }: Props) {
<Form.Control plaintext readOnly value={fecha(alumno.createdAt)} />
</Col>
</Form.Group>
*/}
{(alumno.status?.idStatus === 11 || alumno.status?.idStatus === 12) && (
<div className="mt-4 mb-4">
<Link
// variant="primary"
href={`/administrador/casos_especiales/caso_especial/editar/${alumno.idCasoEspecial}`}
className="btn btn-outline-primary is-info is-light"
{(alumno.Status?.idStatus === 11 || alumno.Status?.idStatus === 12) && (
<div className="text-end mt-4">
<Button
variant="info"
onClick={() => router.push("/admin/casos_especiales/caso_especial/editar")}
>
<span>Editar información</span>
</Link>
Editar información
</Button>
</div>
)}
</div>
@@ -1,7 +1,7 @@
import { axiosInstance } from "@/api/config";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
interface Admin {
token?: string;
}
@@ -29,15 +29,15 @@ interface Programa {
programa?: string;
clavePrograma?: string;
acatlan?: boolean;
usuario?: Usuario;
Usuario?: Usuario;
}
interface Datos {
idServicio?: number;
programa?: Programa;
carrera?: Carrera;
usuario?: Usuario;
status?: Status;
Programa?: Programa;
Carrera?: Carrera;
Usuario?: Usuario;
Status?: Status;
creditos?: string;
correo?: string;
createdAt?: string;
@@ -75,7 +75,7 @@ export default function InformacionServicio({ admin, imprimirError, updateIsLoad
const [info, setInfo] = useState<Usuario[]>([]);
const [loading, setLoading] = useState(true);
// const [programa, setPrograma] = useState<Datos>({});
const [programa, setPrograma] = useState<Datos>({});
const router = useRouter();
@@ -97,11 +97,11 @@ export default function InformacionServicio({ admin, imprimirError, updateIsLoad
}, []);
*/
// useEffect(() => {
// console.log('✅ Datos cargados correctamente:', datos);
// // setPrograma(datos);
// // console.log('Programa establecido en InformacionServicio:', programa);
// }, [datos]);
useEffect(() => {
console.log('✅ Datos cargados correctamente:', datos);
setPrograma(datos);
console.log('Programa establecido en InformacionServicio:', programa);
}, [datos]);
// Funcion para editar la informacion del servicio
@@ -132,36 +132,36 @@ export default function InformacionServicio({ admin, imprimirError, updateIsLoad
return (
<div className="container mv-5">
<div className="mb-5">
<h3 className="mt-3 mb-2">Datos del programa</h3>
<h3 className="fw-bold mb-4">Datos del programa</h3>
<div className="mb-3">
<label className="form-label fw-semibold">Institucion:</label>
<p className="form-control">{datos.programa?.institucion || '-'}</p>
<p className="form-control">{datos.Programa?.institucion || '-'}</p>
</div>
<div className="mb-3">
<label className="form-label fw-semibold">Dependencias:</label>
<p className="form-control">{datos.programa?.dependencia || '-'}</p>
<p className="form-control">{datos.Programa?.dependencia || '-'}</p>
</div>
<div className="mb-3">
<label className="form-label fw-semibold">Programa:</label>
<p className="form-control">{datos.programa?.programa}</p>
<p className="form-control">{datos.Programa?.programa}</p>
</div>
<div className="mb-3">
<label className="form-label fw-semibold">Clave de programa:</label>
<p className="form-control">{datos.programa?.clavePrograma}</p>
<p className="form-control">{datos.Programa?.clavePrograma}</p>
</div>
<div className="mb-3">
<label className="form-label fw-semibold">Responsable:</label>
<p className="form-control">{datos.programa?.usuario?.nombre}</p>
<p className="form-control">{datos.Programa?.Usuario?.nombre}</p>
</div>
<div className="mb-3">
<label className="form-label fw-semibold">Correo:</label>
<p className="form-control">{datos.programa?.usuario?.usuario}</p>
<p className="form-control">{datos.Programa?.Usuario?.usuario}</p>
</div>
</div>
@@ -170,116 +170,74 @@ export default function InformacionServicio({ admin, imprimirError, updateIsLoad
<div className="mb-3">
<label className="form-label fw-semibold">Numero de cuenta:</label>
<p className="form-control">{datos.usuario?.usuario || '-'}</p>
<p className="form-control">{datos.Usuario?.usuario || '-'}</p>
</div>
<div className="mb-3">
<label className="form-label fw-semibold">Nombre:</label>
<p className="form-control">{datos.usuario?.nombre || '-'}</p>
<p className="form-control">{datos.Usuario?.nombre || '-'}</p>
</div>
{/*
<Col>
<FormGroup>
<FormLabel>Seleccione una fecha de fin:</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
selected={selectedFin}
onChange={(date) => setSelectedFin(date)}
minDate={minDate}
maxDate={maxDate}
placeholderText="Seleccione una fecha de fin"
dateFormat="yyyy-MM-dd"
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
</InputGroup>
</FormGroup>
</Col>
<div className="mb-3">
<label className="form-label fw-semibold">Fecha de termino:</label>
<p className="form-control">{formatDate(datos.fechaFin) || '-'}</p>
<div className="mb-3">
<label className="form-label fw-semibold">Fecha de nacimiento:
<p className="form-control">{datos.fechaNacimiento}</p>
</label>
</div>
*/}
{datos.fechaNacimiento && (
<div className="mb-3">
<label className="form-label fw-semibold">Fecha de nacimiento:</label>
<p className="form-control">{formatDate(datos.fechaNacimiento)}</p>
</div>
)}
<div className="mb-3">
<label className="form-label fw-semibold">Carrera:</label>
<p className="form-control">{datos.carrera?.carrera || '-'}</p>
<p className="form-control">{datos.Carrera?.carrera || '-'}</p>
</div>
<div className="mb-3">
<label className="form-label fw-semibold">Creditos:</label>
<p className="form-control">{datos.creditos?.replace(/^0+/, '') + '%' || '-'}</p>
<p className="form-control">{datos.creditos || '-'}</p>
</div>
{datos.telefono && (
<div className="mb-3">
<label className="form-label fw-semibold">Telèfono:</label>
<p className="form-control">{datos.telefono}</p>
</div>
)}
{datos.direccion && (
<div className="mb-3">
<label className="form-label fw-semibold">Direcciòn:</label>
<p className="form-control">{datos.direccion}</p>
</div>
)}
<div className="mb-3">
<label className="form-label fw-semibold">Telèfono:</label>
<p className="form-control">{datos.telefono}</p>
</div>
<div className="mb-3">
<label className="form-label fw-semibold">Direcciòn:</label>
<p className="form-control">{datos.direccion}</p>
</div>
<div className="mb-3">
<label className="form-label fw-semibold">Email:</label>
<p className="form-control">{datos.correo || '-'}</p>
</div>
{datos.programaInterno && (
<div className="mb-3">
<label className="form-label fw-semibold">Programa Interno:</label>
<p className="form-control">{datos.programaInterno}</p>
</div>
)}
<div className="mb-3">
<label className="form-label fw-semibold">Programa Interno:</label>
<p className="form-control">{datos.programaInterno}</p>
</div>
{datos.profesor && (
<div className="mb-3">
<label className="form-label fw-semibold">Profesor:</label>
<p className="form-control">{datos.profesor}</p>
</div>
)}
<div className="mb-3">
<label className="form-label fw-semibold">Profesor:</label>
<p className="form-control">{datos.profesor}</p>
</div>
<div className="mb-3">
<label className="form-label fw-semibold">Fecha de registro:</label>
<p className="form-control">{formatDate(datos.createdAt) || '-'}</p>
<p className="form-control">{datos.createdAt || '-'}</p>
</div>
<div className="mb-3">
<label className="form-label fw-semibold">fecha de inicio:</label>
<p className="form-control">{formatDate(datos.fechaInicio)|| '-'}</p>
<p className="form-control">{datos.fechaInicio || '-'}</p>
</div>
<div className="mb-3">
<label className="form-label fw-semibold">Fecha de termino:</label>
<p className="form-control">{formatDate(datos.fechaFin) || '-'}</p>
<p className="form-control">{datos.fechaFin || '-'}</p>
</div>
{datos.fechaLiberacion && (
<div className="mb-3">
<label className="form-label fw-semibold">Fecha de liberaciòn:</label>
<p className="form-control">{formatDate(datos.fechaLiberacion)}</p>
</div>
)}
<div className="mb-3">
<label className="form-label fw-semibold">Fecha de liberaciòn:</label>
<p className="form-control">{datos.fechaLiberacion}</p>
</div>
</div>
<button className="btn btn-primary"
@@ -289,23 +247,4 @@ export default function InformacionServicio({ admin, imprimirError, updateIsLoad
</button>
</div>
);
}
// Para dar formato a la fecha
function formatDate(value?: string | null): string {
if (!value) return "";
// Si ya viene como fecha ISO, forzamos a hora local sin modificar el día
const d = new Date(value.includes("T") ? value : `${value}T00:00:00`);
if (isNaN(d.getTime())) {
// console.warn("⚠️ Fecha inválida:", value);
return "";
}
const day = String(d.getDate()).padStart(2, "0");
const month = String(d.getMonth() + 1).padStart(2, "0");
const year = d.getFullYear();
return `${day}/${month}/${year}`;
}
@@ -26,24 +26,24 @@ export default function LiberarCasoEspecial({
const data = { idCasoEspecial };
try {
updateIsLoading(true);
const res = await axiosInstance.put(`/caso-especial/liberaciony/${idCasoEspecial}`, data);
imprimirMensaje(res.data.message);
router.replace('/administrador/casos_especiales');
updateIsLoading(true);
const res = await axiosInstance.put(`/caso_especial/liberacion`, data, { headers: { Authorization: `Bearer ${admin.token}` } });
imprimirMensaje(res.data.message);
router.push('/admin/casos_especiales');
} catch (err: unknown) {
let msg: unknown = 'Error';
if (typeof err === 'object' && err !== null && 'response' in err) {
const anyErr = err as { response?: { data?: unknown } };
if (anyErr.response?.data) msg = anyErr.response.data;
} else if (err instanceof Error) msg = err.message;
imprimirError(msg);
let msg: unknown = 'Error';
if (typeof err === 'object' && err !== null && 'response' in err) {
const anyErr = err as { response?: { data?: unknown } };
if (anyErr.response?.data) msg = anyErr.response.data;
} else if (err instanceof Error) msg = err.message;
imprimirError(msg);
} finally {
updateIsLoading(false);
}
};
return (
<div className="mt-2 mb-4">
<div className="mt-2">
<button
className="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600"
onClick={() =>
@@ -4,7 +4,6 @@ import { useState } from "react";
import { axiosInstance } from "@/api/config";
import { isAxiosError } from 'axios';
import { useRouter } from "next/navigation";
import Swal from "sweetalert2";
interface Props {
//admin: { token: { headers: { token: string } } };
@@ -36,54 +35,30 @@ export default function ReasignacionProgramas({
);
};
const confirmarActualizacion = () => {
Swal.fire({
title: `¿Seguro(a) que quiere eliminar a ${correoOtroResponsable} y pasar todos los programas a ${responsable.usuario}?`,
icon: "warning",
showCancelButton: true,
confirmButtonText: "Actualizar",
cancelButtonText: "Cancelar",
confirmButtonColor: "#0d6efd",
cancelButtonColor: "#dc3545",
}).then((result) => {
if (result.isConfirmed) {
reasignarProgramas(); // 👈 aquí ejecutas la función
}
});
};
// Función principal: reasignar programas
const reasignarProgramas = async () => {
const data = {
//idUsuario: responsable.idUsuario,
correo: correoOtroResponsable,
idUsuario: responsable.idUsuario,
correoOtroResponsable,
};
console.log("Esta es la data que se pasa en el update de responsable", data)
console.log("Esta es la info del correo entrando en el data", data.correo)
try {
updateIsLoading(true);
const res = await axiosInstance.put(`/programa/reasignar_programas/${responsable.idUsuario}`, data);
Swal.fire('Exito', 'Se reasignaron los programas con exito', 'success')
const res = await axiosInstance.put(`/programa/reasignar_programas`, data);
updateIsLoading(false);
// imprimirMensaje(res.data.message);
router.replace("/administrador/responsables");
imprimirMensaje(res.data.message);
router.push("/admin/responsables/responsable");
} catch (err: unknown) {
Swal.fire('Error', 'No se pudieron reasignar los programas', 'error')
// console.log("Esta entrando al catch no sale la peticion")
updateIsLoading(false);
// if (isAxiosError(err)) imprimirError(err.response?.data || err.message);
// else if (err instanceof Error) imprimirError(err.message);
// else imprimirError(err);
if (isAxiosError(err)) imprimirError(err.response?.data || err.message);
else if (err instanceof Error) imprimirError(err.message);
else imprimirError(err);
}
};
return (
<div className="mt-6">
<h3 className="fw-semibold text-2xl font-semibold mb-3">Reasignación de programas</h3>
<h3 className="text-2xl font-semibold mb-3">Reasignación de programas</h3>
<div className="text-base mb-4">
<p>
@@ -94,27 +69,32 @@ export default function ReasignacionProgramas({
</div>
<div className="mb-4">
<label className="form-label fw-semibold block text-sm font-medium text-gray-700 mb-2">
<label className="block text-sm font-medium text-gray-700 mb-1">
Usuario/Correo electrónico
</label>
<input
type="email"
placeholder="Usuario"
value={correoOtroResponsable}
onChange={(e) => setCorreoOtroResponsable(e.target.value)}
className="form-control border border-gray-300 rounded-lg p-2 w-full"
type="email"
placeholder="Usuario"
value={correoOtroResponsable}
onChange={(e) => setCorreoOtroResponsable(e.target.value)}
className="border border-gray-300 rounded-lg p-2 w-full"
/>
</div>
<div className="pt-3">
<div className="pt-5">
<button
className={`px-4 py-2 rounded-1 text-white ${
className={`px-4 py-2 rounded-md text-white ${
mostrarBoton()
? "bg-gray-400 cursor-not-allowed"
: "bg-blue-600 hover:bg-blue-700"
}`}
disabled={mostrarBoton()}
onClick={confirmarActualizacion}
onClick={() =>
imprimirWarning(
`¿Seguro(a) que quiere eliminar a ${correoOtroResponsable} y pasar todos los programas a ${responsable.usuario}?`,
reasignarProgramas
)
}
>
Reasignar
</button>
+9 -66
View File
@@ -1,15 +1,10 @@
"use client";
import { useState } from "react";
import { axiosInstance } from "@/api/config";
import { Button, Col, FormGroup, FormLabel, InputGroup } from "react-bootstrap";
import { Button, Col, FormGroup, FormLabel, InputGroup, Row } from "react-bootstrap";
import DatePicker from "react-datepicker";
import moment from "moment";
import { FaRegCalendar } from "react-icons/fa6";
import Swal from "sweetalert2";
import { registerLocale } from "react-datepicker";
import { es } from "date-fns/locale/es";
registerLocale("es", es);
import { FaCalendarAlt, FaRegCalendarAlt } from "react-icons/fa";
//import { saveAs } from "file-saver";
interface Props {
@@ -37,44 +32,17 @@ export default function Reporte({ admin }: Props) {
try {
//updateIsLoading(true);
console.log("Fecha inicio seleccionada:", selectedInicio);
console.log("Fecha fin seleccionada:", selectedFin);
//console.log("Fechas parseadas", selectedInicio ? moment(selectedInicio).format("YYYY") : null, selectedFin ? moment(selectedFin).format("YYYY") : null);
const res = await axiosInstance.get("/servicio/reporte", {
params: {
inicio: moment(selectedInicio).format("YYYY-MM-DD"), // Mandamos solo el año, quitamos el mes y día
fin: moment(selectedFin).format("YYYY-MM-DD"), // Mandamos solo el año, quitamos el mes y día
inicio: moment(selectedInicio).format("YYYY-MM-DD"),
fin: moment(selectedFin).format("YYYY-MM-DD"),
},
headers: {
Authorization: `Bearer ${admin.token}`,
},
responseType: "blob",
});
// Creamos el blob manualmente
const blob = new Blob([res.data], {type: 'text/csv'})
// Creamos la url temporal para la descarga
const url = window.URL.createObjectURL(blob)
// Creamos el link
const link = document.createElement("a")
link.href = url;
link.download = `Reporte.csv`
// Disparar descarga
document.body.appendChild(link)
link.click()
// Limpiamos la url
link.remove()
window.URL.revokeObjectURL(url)
Swal.fire({
title: 'Éxito',
text: 'Se descargo el reporte exitosamente.',
icon: 'success',
})
//saveAs(res.data, "reporte.csv");
// reset
@@ -82,18 +50,9 @@ export default function Reporte({ admin }: Props) {
setSelectedFin(null);
//updateIsLoading(false);
} catch (err: unknown) {
Swal.fire({
title: 'Error',
text: 'No se pudo descargar el reporte.',
icon: 'error',
})
console.log("Error al descargar el reporte", err)
//updateIsLoading(false);
// optional: handle error
}
};
return (
@@ -106,7 +65,7 @@ export default function Reporte({ admin }: Props) {
<FormLabel>Seleccione una fecha de inicio:</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendar />
<FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
selected={selectedInicio}
@@ -118,14 +77,6 @@ export default function Reporte({ admin }: Props) {
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
showMonthDropdown
showYearDropdown
scrollableYearDropdown
yearDropdownItemNumber={15}
dropdownMode="select"
locale='es'
/>
</InputGroup>
</FormGroup>
@@ -136,7 +87,7 @@ export default function Reporte({ admin }: Props) {
<FormLabel>Seleccione una fecha de fin:</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendar />
<FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
selected={selectedFin}
@@ -148,14 +99,6 @@ export default function Reporte({ admin }: Props) {
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
showMonthDropdown
showYearDropdown
scrollableYearDropdown
yearDropdownItemNumber={15}
dropdownMode="select"
locale='es'
/>
</InputGroup>
</FormGroup>
@@ -7,9 +7,7 @@ import type { AxiosResponse } from 'axios';
import { Button, Col, Form, FormGroup, FormLabel, FormSelect, InputGroup, Row, Spinner } from "react-bootstrap";
import ServicioSocialTabla from "../servicio-social-tabla";
import { ServicioSocialResponse } from "@/types/responses";
import { FaCircleInfo, FaUser } from "react-icons/fa6";
import { useRouter } from "next/navigation";
import Image from "next/image";
import { FaInfoCircle, FaSchool, FaUser } from "react-icons/fa";
interface Admin {
idTipoUsuario?: number;
@@ -47,9 +45,6 @@ interface CasosEspecialesResponse {
}
export default function TablaCasosEspeciales({ admin, imprimirError }: Props) {
// Para el redirecionamiento
const router = useRouter();
const [isLoading, setIsLoading] = useState(false);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
@@ -93,12 +88,11 @@ export default function TablaCasosEspeciales({ admin, imprimirError }: Props) {
try {
const config = admin?.token ? { headers: { Authorization: `Bearer ${admin.token}` } } : undefined;
console.debug('Obtener servicios - localStorage token:', typeof window !== 'undefined' ? localStorage.getItem('token') : undefined, 'admin.token:', admin?.token);
const res = await axiosInstance.get(`/caso-especial/servicios_especiales?pagina=${pagina}${query}`);
const res = await axiosInstance.get(`/caso_especial/servicios_especiales?pagina=${pagina}${query}`, config);
console.debug('Respuesta casos especiales:', res.data);
setData(res.data.data.serviciosEspeciales ?? []);
setData(res.data.serviciosEspeciales || []);
setTotal(res.data.count);
console.log('Respuesta casos especiales:', res.data);
console.log("Respuesta Caso especial con doble data", res.data.data)
} catch (err: unknown) {
if (imprimirError) {
if (isAxiosError(err)) {
@@ -136,12 +130,9 @@ export default function TablaCasosEspeciales({ admin, imprimirError }: Props) {
useEffect(() => {
console.log("Admin recibido:", admin);
obtenerCatalogoStatus();
// Asegurar que se ejecute la petición si el usuario tiene permisos
if (admin && Number(admin.idTipoUsuario) === 1) {
obtenerCatalogoStatus();
console.log("entro para traer la informacion de los usuarios")
} else if (!admin) {
console.warn("Admin no definido, no se cargaron los casos especiales.");
}
@@ -164,7 +155,7 @@ export default function TablaCasosEspeciales({ admin, imprimirError }: Props) {
<InputGroup>
<InputGroup.Text className="rounded-4">
<Image src="/image/numCuenta.svg" width={24} height={24} alt="Icono de escuela" />
<FaSchool />
</InputGroup.Text>
<Form.Control
@@ -196,7 +187,7 @@ export default function TablaCasosEspeciales({ admin, imprimirError }: Props) {
placeholder="Nombre"
value={search.nombre}
onChange={(e) => setSearch({ ...search, nombre: e.target.value })}
onKeyDown={(e) => e.key === "Enter" && obtenerCasosEspeciales() }
onKeyDown={(e) => e.key === "Enter" && obtenerCasosEspeciales()}
/>
</InputGroup>
</FormGroup>
@@ -208,14 +199,13 @@ export default function TablaCasosEspeciales({ admin, imprimirError }: Props) {
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaCircleInfo />
<FaInfoCircle />
</InputGroup.Text>
<FormSelect
value={search.idStatus}
onChange={(e) => setSearch(prev => ({ ...prev, idStatus: e.target.value }))}
className="rounded-4"
onKeyDown={(e) => { if (e.key === 'Enter') obtenerCasosEspeciales() }}
>
<option value="">Status</option>
{status.map((s) => (
@@ -254,27 +244,15 @@ export default function TablaCasosEspeciales({ admin, imprimirError }: Props) {
{/* Tabla
<ServicioSocialTabla />
onPageChange={onPageChange}
columnasResponsable={admin?.idTipoUsuario === 1}
*/}
<ServicioSocialTabla
data={data}
total={total}
columnaFechaRegistro={true}
idTipoUsuario={admin?.idTipoUsuario}
columnasResponsable={admin?.idTipoUsuario === 1}
columnasResponsable={false}
onPageChange={onPageChange}
columnaFechaInicio={false}
columnaFechaFin={false}
onRowAction={(row, path) => {
try {
router.push(`/responsable/${path}`)
} catch (error) {
console.error('Error al manejar accion de fila', error)
}
}}
/>
</section>
@@ -5,7 +5,7 @@ import { Responsables } from "@/types/responses";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import { Button, Col, Row, Table } from "react-bootstrap";
import { FaEnvelope, FaUser } from "react-icons/fa6";
import { FaEnvelope, FaUser } from "react-icons/fa";
export default function TablaResponsables() {
const [info, setInfo] = useState<Responsables[]>([]);
@@ -68,7 +68,6 @@ export default function TablaResponsables() {
className="form-control border-0 rounded-4"
value={correo}
onChange={(e) => setCorreo(e.target.value)}
onKeyDown={(e) => {if (e.key === 'Enter') fetchData(correo, nombre)}}
/>
</div>
</Col>
@@ -81,7 +80,6 @@ export default function TablaResponsables() {
className="form-control border-0 rounded-4"
value={nombre}
onChange={(e) => setNombre(e.target.value)}
onKeyDown={(e) => { if (e.key === 'Enter') fetchData(correo, nombre)}}
/>
</div>
</Col>
@@ -1,14 +1,14 @@
"use client";
import { useEffect, useRef, useState } from "react";
import React, { useEffect, useRef, useState } from "react";
import { useRouter } from 'next/navigation';
import { Form, FormGroup, FormLabel, FormControl, FormSelect, Button, InputGroup, Row, Col } from "react-bootstrap";
import { FaUser, FaCircleInfo } from "react-icons/fa6";
import { FaUser, FaSchool, FaInfoCircle } from "react-icons/fa";
import ServicioSocialTabla from "../servicio-social-tabla";
import { axiosInstance } from "@/api/config";
import { ServicioSocialResponse } from "@/types/responses";
import { Prev } from "react-bootstrap/esm/PageItem";
import { AxiosError } from "axios";
import Image from "next/image";
interface Admin {
idTipoUsuario: number;
@@ -38,8 +38,6 @@ export default function TablaServicioSocial({ admin, imprimirError }: Props) {
if (admin?.idTipoUsuario === 1) {
obtenerCatalogoStatus();
}
console.log("Esta es la info que tiene al guardar en la state", data)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [admin?.idTipoUsuario]);
@@ -72,14 +70,10 @@ export default function TablaServicioSocial({ admin, imprimirError }: Props) {
// asegurar que enviamos Authorization si existe en admin, y loggear token para debug
const config = admin?.token ? { headers: { Authorization: `Bearer ${admin.token}` } } : undefined;
console.debug('Obtener servicios - localStorage token:', typeof window !== 'undefined' ? localStorage.getItem('token') : undefined, 'admin.token:', admin?.token);
const res = await axiosInstance.get(`/servicio/admin?pagina=${paginaActual}${query}`);
const res = await axiosInstance.get(`/servicio/servicios_admin?pagina=${paginaActual}${query}`, config);
console.debug('Respuesta servicios_admin:', res.data);
setData(res.data.serviciosAdmin || []);
//setData(res.data)
//setData(res.data.serviciosAdmin);
setTotal(res.data.count ?? 0);
console.log("Esta es la info", res.data)
} catch (err: unknown) {
// manejar error
console.error('Error obtenerServicios', err);
@@ -104,6 +98,7 @@ export default function TablaServicioSocial({ admin, imprimirError }: Props) {
}
};
return (
<section>
<div className="columns">
@@ -112,12 +107,12 @@ export default function TablaServicioSocial({ admin, imprimirError }: Props) {
<section className="container-fluid my-4">
<Form onSubmit={(e) => { e.preventDefault(); obtenerServicios(1); }}>
<Row className="g-3">
<Col xs={12} sm={6} lg={3}>
<Col md={3}>
<FormGroup>
<FormLabel>Número de Cuenta</FormLabel>
<InputGroup>
<InputGroup.Text className="rounded-4">
<Image src="/image/numCuenta.svg" width={24} height={24} alt="Icono de escuela" />
<FaSchool />
</InputGroup.Text>
<FormControl
type="text"
@@ -132,7 +127,7 @@ export default function TablaServicioSocial({ admin, imprimirError }: Props) {
</FormGroup>
</Col>
<Col xs={12} sm={6} lg={3}>
<Col md={3}>
<FormGroup>
<FormLabel>Nombre</FormLabel>
<InputGroup>
@@ -151,19 +146,17 @@ export default function TablaServicioSocial({ admin, imprimirError }: Props) {
</FormGroup>
</Col>
<Col xs={12} sm={6} lg={3}>
<Col md={3}>
<FormGroup>
<FormLabel>Status</FormLabel>
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaCircleInfo />
<FaInfoCircle />
</InputGroup.Text>
<FormSelect
suppressHydrationWarning
value={search.idStatus}
onChange={(e) => setSearch(Prev => ({ ...Prev, idStatus: e.target.value }))}
className="rounded-4"
onKeyDown={(e) => { if (e.key === 'Enter') obtenerServicios()}}
>
<option value="">Status</option>
{status.slice(0, 10).map((s) => (
@@ -174,7 +167,7 @@ export default function TablaServicioSocial({ admin, imprimirError }: Props) {
</FormGroup>
</Col>
<Col xs={12} sm={6} lg={3} className="d-flex align-items-end">
<Col md={3} className="d-flex align-items-end">
<Button type="submit" className="w-100 rounded-5" disabled={isLoading}>
{isLoading ? 'Buscando...' : 'Buscar'}
</Button>
@@ -193,7 +186,6 @@ export default function TablaServicioSocial({ admin, imprimirError }: Props) {
onPageChange={onPageChange}
idTipoUsuario={admin?.idTipoUsuario}
columnasResponsable={admin?.idTipoUsuario === 1}
columnaFechaRegistro={true}
columnaCuestionario={false}
columnaCartaTermino={false}
columnaCuestionarioCompleto={false}
@@ -14,10 +14,10 @@ export default function TituloStatus({ status }: Props) {
console.log('idStatus en TituloStatus:', idStatus);
return (
<div className="mb-4">
{idStatus === 1 && <h2 className="fw-bold mt-4">Confirmar el pre-registro del alumno</h2>}
{idStatus === 4 && <h2 className="fw-bold mt-4">Término</h2>}
{idStatus === 5 && <h2 className="fw-bold mt-4">Validar el término del alumno</h2>}
<div className="mb-5">
{idStatus === 1 && <h3 className="title">Confirmar el pre-registro del alumno</h3>}
{idStatus === 4 && <h3 className="title">Término</h3>}
{idStatus === 5 && <h3 className="title">Validar el término del alumno</h3>}
</div>
);
}
+56 -59
View File
@@ -1,76 +1,73 @@
"use client";
import { FaUserPlus, FaUserCheck, FaUser, FaUsers, FaUserClock, FaUserTie } from "react-icons/fa6";
import React from "react";
interface Props {
idStatus: number;
}
interface Step {
label: string;
icon: string; // Puedes usar iconos de heroicons o font-awesome
visible: boolean;
type?: "default" | "danger";
}
export default function BarraProgreso({ idStatus }: Props) {
// Si el status es 7, mostrar como si fuera 1 (Pre Registro)
// Si el status es 8 o 9, mostrar como 5 (Termino)
const statusMapeado = idStatus === 7 ? 1 : (idStatus === 8 || idStatus === 9 ? 5 : idStatus);
const steps = [
{ label: "Pre Registro", icon: <FaUserPlus />, id: 1 },
{ label: "Pre Registro Validado", icon: <FaUserCheck />, id: 2 },
{ label: "Registro", icon: <FaUser />, id: 3 },
{ label: "Pre Termino", icon: <FaUsers />, id: 4 },
{ label: "Termino", icon: <FaUserClock />, id: 5 },
{ label: "Liberacion", icon: <FaUserTie />, id: 6 },
const steps: Step[] = [
{ label: "Pre Registro", icon: "account-plus", visible: idStatus < 6 },
{ label: "Pre Registro Validado", icon: "account-clock", visible: idStatus < 6 },
{ label: "Registro", icon: "account", visible: idStatus < 6 },
{ label: "Pre Termino", icon: "account-details", visible: idStatus < 6 },
{ label: "Termino", icon: "account-clock", visible: idStatus < 6 },
{ label: "Liberacion", icon: "account-check", visible: idStatus < 6 },
{ label: "Carta Aceptación Rechazada", icon: "file", visible: idStatus === 6, type: "danger" },
{ label: "Carta Termino Rechazada", icon: "file", visible: idStatus === 7, type: "danger" },
{ label: "Informe Global Rechazado", icon: "file", visible: idStatus === 8, type: "danger" },
{ label: "Cancelado", icon: "account-cancel", visible: idStatus === 9, type: "danger" },
];
return (
<div className="container mt-5">
<div className="d-flex justify-content-center align-items-center position-relative">
{steps.map((step, index) => {
const isActive = statusMapeado >= step.id;
const isCurrent = statusMapeado === step.id;
<div className="b-steps-container mt-6 mb-5">
<div className="b-steps">
{steps.filter(s => s.visible).map((step, idx) => (
<div key={idx} className={`step-item ${step.type === 'danger' ? 'is-danger' : 'is-info'}`} aria-hidden>
<div className="step-marker">
{/* Placeholder icon: use font-awesome or heroicons in the app */}
<span className={`icon ${step.type === 'danger' ? 'icon-danger' : 'icon-info'}`} aria-hidden>
<i className={`fa fa-${step.icon}`} />
</span>
</div>
<div className="step-details">
<div className="step-title">{step.label}</div>
</div>
</div>
))}
</div>
return (
<div key={index} className="text-center position-relative flex-fill">
{/* Línea de conexión */}
{index > 0 && (
<div
className={`position-absolute top-50 start-0 translate-middle-y w-100 border-top ${
isActive ? "border-primary" : "border-secondary opacity-25"
}`}
style={{ zIndex: 0 }}
></div>
)}
<style jsx>{`
.b-steps { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: stretch; }
.step-item { display: flex; align-items: center; padding: 0.6rem 0.9rem; border-radius: 6px; background: #e6f2ff; color: #0b66b2; min-width: 220px; }
.step-item.is-info { background: #ecf8ff; color: #0b66b2; }
.step-item.is-danger { background: #ffecec; color: #a10b0b; }
.step-marker { display:flex; align-items:center; justify-content:center; width:40px; height:40px; border-radius:50%; margin-right:0.75rem; background: rgba(255,255,255,0.6); }
.step-item.is-info .step-marker { background: #dff4ff; }
.step-item.is-danger .step-marker { background: #ffdede; }
.icon { font-size: 1.1rem; }
.step-title { font-weight: 600; font-size: 0.95rem; }
{/* Círculo del paso */}
<div
className={`rounded-circle d-flex justify-content-center align-items-center mx-auto mb-2 border ${
isActive
? "bg-primary text-white border-primary"
: isCurrent
? "border-primary text-primary bg-white"
: "bg-light text-secondary border-secondary opacity-75"
}`}
style={{
width: "48px",
height: "48px",
position: "relative",
zIndex: 1,
}}
>
{step.icon}
</div>
/* Mimic the Buefy rule to hide ::before/::after on danger steps */
.b-steps .step-item.is-danger::before,
.b-steps .step-item.is-danger::after {
display: none !important;
content: none !important;
}
{/* Título */}
<div
className={`fw-semibold ${
isActive || isCurrent ? "text-dark" : "text-secondary"
}`}
style={{ fontSize: "0.95rem" }}
>
{step.label}
</div>
</div>
);
})}
</div>
@media (max-width: 768px) {
.step-item { min-width: 140px; padding: 0.45rem 0.6rem; }
.step-title { font-size: 0.85rem; }
}
`}</style>
</div>
);
}
@@ -1,11 +1,12 @@
"use client";
import { useState } from "react";
import React, { useState } from "react";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { axiosInstance } from '@/api/config';
import { isAxiosError } from 'axios';
import type { AxiosResponse } from 'axios';
import { useRouter } from "next/navigation";
import Swal from "sweetalert2";
import moment from "moment";
interface Alumno {
token: { headers: Record<string, string> };
@@ -16,7 +17,7 @@ interface Alumno {
interface Props {
idServicio: number;
alumno: Alumno;
imprimirMensaje: (message: string) => void;
imprimirMensaje: (msg: string) => void;
imprimirWarning: (msg: string, onConfirm: () => void) => void;
imprimirError: (err: unknown) => void;
obtenerServicio: () => void;
@@ -27,11 +28,11 @@ interface PreRegistroData {
idServicio: number;
direccion: string;
telefono: string;
// fechaNacimiento: string;
fechaNacimiento: string;
}
interface ApiResponse {
message: string ;
data: { message: string };
}
export default function CompletarDatosPersonales({
@@ -48,143 +49,89 @@ export default function CompletarDatosPersonales({
const [nacimiento, setNacimiento] = useState<Date>(new Date());
const maxDate = new Date();
const router = useRouter();
const confirmarDatos = () => {
Swal.fire({
title: '¿Estas seguro(a) que tus datos son correctos?',
icon: 'warning',
showCancelButton: true,
confirmButtonText: "Sí, actualizar",
cancelButtonText: "Cancelar",
confirmButtonColor: "#0d6efd",
cancelButtonColor: "#dc3545",
}).then((result) => {
if(result.isConfirmed) {
terminarPreRegistro();
}
})
}
const terminarPreRegistro = async () => {
const data: PreRegistroData = {
idServicio,
direccion,
telefono,
//fechaNacimiento: moment(nacimiento).format("YYYY-MM-DD"),
fechaNacimiento: moment(nacimiento).format("YYYY-MM-DD"),
};
try {
updateIsLoading(true);
console.log("Datos enviados:", data);
const res: AxiosResponse<ApiResponse> = await axiosInstance.post(`/servicio/registro-validado`, data); // Preguntar si no crear el registro por error
updateIsLoading(false);
// imprimirMensaje(res.data.message);
Swal.fire("Exito", "Se actualizaron tus datos correctamente", "success");
router.refresh();
await obtenerServicio();
setTimeout(() => {
router.replace("/"); //Estaria bien corregir esta parte.
}, 1000);
} catch (err) {
Swal.fire("Error", "No se pudieron actualizar tur datos, por favor intentelo en otro momento", "error")
// if (isAxiosError(err)) {
// imprimirError(err.response?.data || err.message);
// } else {
// imprimirError(err);
// }
const res: AxiosResponse<ApiResponse> = await axiosInstance.put(`/servicio/registro_validado`, data, alumno.token);
imprimirMensaje(res.data.data.message);
obtenerServicio();
} catch (err: unknown) {
if (isAxiosError(err)) {
imprimirError(err.response?.data || err.message);
} else {
imprimirError(err);
}
} finally {
updateIsLoading(false);
}
// } finally {
// updateIsLoading(false);
// }
};
return (
<div className="container space-y-4">
<h2 className="text-xl fw-semibold">Formulario Pre-registro</h2>
<div className="space-y-4">
<h3 className="text-xl font-semibold">Formulario Pre-registro</h3>
{/* Fecha de nacimiento */}
{/* <Col>
<FormGroup>
<FormLabel className="form-label fw-semibold">Fecha de nacimiento:</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
selected={nacimiento}
onChange={(date: Date | null) => {
if (date) setNacimiento(date);
}}
maxDate={maxDate}
className="border border-gray-300 rounded px-3 py-2 form-control"
placeholderText="Fecha de nacimiento"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
</InputGroup>
</FormGroup>
</Col> */}
{/*
<div className="mb-3">
<label className="block mb-1 fw-semibold">Fecha de nacimiento</label>
<div>
<label className="block mb-1 font-medium">Fecha de nacimiento</label>
<DatePicker
selected={nacimiento}
onChange={(date: Date | null) => {
if (date) setNacimiento(date);
}}
maxDate={maxDate}
className="border border-gray-300 rounded px-3 py-2 form-control"
className="border border-gray-300 rounded px-3 py-2 w-full"
placeholderText="Fecha de nacimiento"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
/>
</div>
*/}
{/* Teléfono */}
<div className="mb-3">
<label className="block mb-1 form-label fw-semibold mb-2">Teléfono</label>
<div>
<label className="block mb-1 font-medium">Teléfono</label>
<input
type="tel"
placeholder="Teléfono"
maxLength={10}
value={telefono}
onChange={(e) => setTelefono(e.target.value)}
className="border border-gray-300 rounded px-3 py-2 form-control"
type="tel"
placeholder="Teléfono"
maxLength={10}
value={telefono}
onChange={(e) => setTelefono(e.target.value)}
className="border border-gray-300 rounded px-3 py-2 w-full"
/>
</div>
{/* Dirección */}
<div className="mb-3">
<label className="block mb-1 form-label fw-semibold mb-2">Domicilio</label>
<div>
<label className="block mb-1 font-medium">Dirección</label>
<input
type="text"
placeholder="Domicilio"
maxLength={200}
value={direccion}
onChange={(e) => setDireccion(e.target.value)}
className="border border-gray-300 rounded px-3 py-2 form-control"
type="text"
placeholder="Dirección"
maxLength={200}
value={direccion}
onChange={(e) => setDireccion(e.target.value)}
className="border border-gray-300 rounded px-3 py-2 w-full"
/>
</div>
{/* Botón enviar */}
<div className="text-center mt-4">
<button
disabled={!telefono || !direccion}
onClick={confirmarDatos}
className={`px-4 py-2 rounded text-white mb-5 ${
!telefono || !direccion
? "bg-gray-400 cursor-not-allowed"
: "bg-green-600 hover:bg-green-700"
}`}
disabled={!telefono || !direccion}
onClick={() =>
imprimirWarning(
"¿Estas seguro(a) que tus datos son correctos?",
terminarPreRegistro
)
}
className={`px-4 py-2 rounded text-white ${
!telefono || !direccion
? "bg-gray-400 cursor-not-allowed"
: "bg-green-600 hover:bg-green-700"
}`}
>
Enviar
</button>
@@ -2,9 +2,6 @@
import React, { useEffect, useMemo, useState } from "react";
import { axiosInstance } from "@/api/config";
import { useRouter } from "next/navigation";
import Swal from "sweetalert2";
import { AxiosError } from "axios";
type Pregunta = {
id: string;
@@ -28,16 +25,9 @@ type Tabla = {
type Formulario = { titulo?: string; descripcion?: string; preguntas: Pregunta[]; tablas: Tabla[] };
interface Props{
idServicio: number;
}
export default function FullCuestionario({ idServicio}: Props) {
export default function FullCuestionario() {
const [respuestas, setRespuestas] = useState<Record<string, string | string[] | Record<number, string | null> | null>>({});
const [isLoading, setIsLoading] = useState(false);
const [isFormDisabled, setIsFormDisabled] = useState(false);
const router = useRouter();
const formulario: Formulario = useMemo(
() => ({
@@ -107,29 +97,6 @@ export default function FullCuestionario({ idServicio}: Props) {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// Verificar si el cuestionario ya fue contestado
useEffect(() => {
const checkIfFormAlreadyCompleted = async () => {
try {
const idServicio = typeof window !== 'undefined' ? localStorage.getItem('idServicio') : null;
if (!idServicio) return;
const response = await axiosInstance.get(`/servicio/admin?idServicio=${idServicio}`);
const idCuestionarioAlumno2 = response.data?.idCuestionarioAlumno2;
// Si ya existe un cuestionario contestado, deshabilitar el formulario
if (idCuestionarioAlumno2 && idCuestionarioAlumno2 > 0) {
setIsFormDisabled(true);
window.alert('Este cuestionario ya ha sido contestado. No puedes modificar tus respuestas.');
}
} catch (error) {
console.error('Error al verificar cuestionario:', error);
}
};
checkIfFormAlreadyCompleted();
}, []);
const isMobile = () => typeof window !== 'undefined' && window.innerWidth < 576;
const sortedItems = useMemo(() => {
@@ -275,41 +242,20 @@ export default function FullCuestionario({ idServicio}: Props) {
if (!validateResponses()) return;
setIsLoading(true);
try {
// const idServicioStorage = typeof window !== 'undefined' ? localStorage.getItem('idServicio') : null;
// if (!idServicioStorage) {
// window.alert('No se encontró el ID del servicio. Por favor, vuelve a iniciar sesión.');
// }
// Para verificar las respuestas de las preguntas
console.log('Respuestas a enviar:', respuestas);
console.log('Respuestas formateadas:', formatearRespuestas(respuestas));
console.log('Respouesta pregunta p_12_C_5', formatearRespuestas(respuestas)['p12_C_5']);
const data = { ...formatearRespuestas(respuestas), idServicio };
console.log('Este es el id', data.idServicio)
const res = await axiosInstance.post(`/cuestionario-alumno2`, data);
const mensaje = res?.data?.message || 'Se subio el cuestionario correctamente.';
Swal.fire('Exito', mensaje, 'success')
// window.alert('Se enviaron los datos correctamente');
const data = { ...formatearRespuestas(respuestas), idServicio: typeof window !== 'undefined' ? localStorage.getItem('idServicio') : null };
await axiosInstance.post(`/cuestionario_alumno`, data);
window.alert('Se enviaron los datos correctamente');
if (typeof window !== 'undefined') {
localStorage.removeItem('idCuestionarioAlumno');
// window.location.href = '/alumno';
router.replace('/alumno');
window.location.href = '/alumno';
}
} catch (err) {
const axiosErr = err as AxiosError<any>;
const mensaje = axiosErr?.response?.data?.message || 'No se pudo enviar el cuestionario, por favor intentelo mas tarde.'
Swal.fire('Error', mensaje, 'error')
// let message = 'Error al enviar el formulario';
// if (typeof err === 'object' && err !== null && 'response' in err) {
// const anyErr = err as { response?: { data?: { message?: unknown } } };
// if (anyErr.response?.data?.message) message = String(anyErr.response.data.message);
// } else if (err instanceof Error) message = err.message;
// window.alert(message);
} catch (err: unknown) {
let message = 'Error al enviar el formulario';
if (typeof err === 'object' && err !== null && 'response' in err) {
const anyErr = err as { response?: { data?: { message?: unknown } } };
if (anyErr.response?.data?.message) message = String(anyErr.response.data.message);
} else if (err instanceof Error) message = err.message;
window.alert(message);
} finally {
setIsLoading(false);
}
@@ -344,7 +290,7 @@ export default function FullCuestionario({ idServicio}: Props) {
{p.opciones?.map((op) => (
<div key={p.id + '-' + op} className="SINO">
<label>
<input type="radio" name={p.id} checked={respuestas[p.id] === op} onChange={() => updateRespuestas(p.id, op)} disabled={isFormDisabled} /> {op}
<input type="radio" name={p.id} checked={respuestas[p.id] === op} onChange={() => updateRespuestas(p.id, op)} /> {op}
</label>
</div>
))}
@@ -354,8 +300,8 @@ export default function FullCuestionario({ idServicio}: Props) {
<div>
{p.opciones?.map((op) => (
<div key={op} className="SINO">
<label className="mt-2">
<input className="checkb" type="checkbox" value={op} checked={Array.isArray(respuestas[p.id]) && (respuestas[p.id] as string[]).includes(op)} onChange={(e) => toggleSelection(p.id, op, e)} disabled={isFormDisabled} /> {op}
<label>
<input className="checkb" type="checkbox" value={op} checked={Array.isArray(respuestas[p.id]) && (respuestas[p.id] as string[]).includes(op)} onChange={(e) => toggleSelection(p.id, op, e)} /> {op}
</label>
</div>
))}
@@ -363,7 +309,7 @@ export default function FullCuestionario({ idServicio}: Props) {
)}
{p.tipo === 'texto' && (
<div>
<input id={p.id} className="form-control bg-transparent" type="text" value={(respuestas[p.id] as string) || ''} onChange={(e) => updateRespuestas(p.id, e.target.value)} maxLength={p.limite || 200} placeholder="Escribe tu respuesta aquí" disabled={isFormDisabled} />
<input id={p.id} className="form-control" type="text" value={(respuestas[p.id] as string) || ''} onChange={(e) => updateRespuestas(p.id, e.target.value)} maxLength={p.limite || 200} placeholder="Escribe tu respuesta aquí" />
</div>
)}
</div>
@@ -381,19 +327,19 @@ export default function FullCuestionario({ idServicio}: Props) {
<table className="table table-bordered text-center">
<thead>
<tr>
<th className="bg-transparent"></th>
<th></th>
{t.renglones[0].opciones.map((op) => (
<th className="bg-transparent" key={op}>{op}</th>
<th key={op}>{op}</th>
))}
</tr>
</thead>
<tbody>
{t.renglones.map((r) => (
<tr key={r.idRenglon}>
<td className="bg-transparent">{r.textoRenglon}</td>
<td>{r.textoRenglon}</td>
{r.opciones.map((op) => (
<td className="bg-transparent" key={op}>
<input type="radio" name={`tabla-${t.idTabla}-renglon-${r.idRenglon}`} value={op} checked={Boolean(tablaResp && tablaResp[r.idRenglon] === op)} onChange={() => handleTableResponse(t.idTabla, r.idRenglon, op)} disabled={isFormDisabled} />
<td key={op}>
<input type="radio" name={`tabla-${t.idTabla}-renglon-${r.idRenglon}`} value={op} checked={Boolean(tablaResp && tablaResp[r.idRenglon] === op)} onChange={() => handleTableResponse(t.idTabla, r.idRenglon, op)} />
</td>
))}
</tr>
@@ -405,15 +351,12 @@ export default function FullCuestionario({ idServicio}: Props) {
);
})}
<div className="my-6 mb-6 text-center mb-5">
<button className="btn btn-success is-medium" onClick={submitForm} disabled={!isFormComplete || isFormDisabled || isLoading}>
<div className="my-6 mb-6 has-text-centered">
<div>
<button className="button is-success is-medium" onClick={submitForm} disabled={!isFormComplete}>
{isLoading ? 'Enviando...' : 'Enviar'}
</button>
{isFormDisabled && (
<div className="alert alert-info mt-3" role="alert">
Este cuestionario ya ha sido contestado y no puede ser modificado.
</div>
)}
</div>
</div>
</div>
+109 -110
View File
@@ -19,9 +19,9 @@ interface Carrera {
}
export interface Servicio {
programa: Programa;
//usuario: Usuario;
carrera: Carrera;
Programa: Programa;
Usuario: Usuario;
Carrera: Carrera;
creditos?: string;
telefono?: string;
direccion?: string;
@@ -50,117 +50,116 @@ export default function InformacinoServicio({ servicio }: Props) {
};
return (
<div className="container">
{/* Datos del programa */}
<div className="mb-5">
<h4 className="is-size-4 pb-2">Datos del programa</h4>
<div>
{/* Datos del programa */}
<div className="mb-5">
<h4 className="is-size-4 pb-2">Datos del programa</h4>
<div className="mb-2">
<label className="form-label fw-semibold">Institución:</label>
<p className="form-control">{servicio.programa.institucion}</p>
</div>
<div className="mb-2">
<label className="form-label fw-semibold">Dependencia:</label>
<p className="form-control">{servicio.programa.dependencia}</p>
</div>
<div className="mb-2">
<label className="form-label fw-semibold">Programa:</label>
<p className="form-control">{servicio.programa.programa}</p>
</div>
<div className="mb-2">
<label className="form-label fw-semibold">Clave de programa:</label>
<p className="form-control">{servicio.programa.clavePrograma}</p>
</div>
<div className="mb-2">
<label>Institución:</label>
<p className="input">{servicio.Programa.institucion}</p>
</div>
{/* Datos personales */}
<div className="mb-5">
<h4 className="is-size-4 pb-2">Datos personales</h4>
{/*
<div className="mb-2">
<label className="form-label fw-semibold">Número de cuenta:</label>
<p className="form-control">{servicio.usuario.usuario}</p>
</div>
<div className="mb-2">
<label className="form-label fw-semibold">Nombre:</label>
<p className="form-control">{servicio.usuario.nombre}</p>
</div>
*/}
<div className="mb-2">
<label className="form-label fw-semibold">Carrera:</label>
<p className="form-control">{servicio.carrera.carrera}</p>
</div>
<div className="mb-2">
<label className="form-label fw-semibold">Créditos:</label>
<p className="form-control">
{servicio.creditos ? parseInt(servicio.creditos) : ""}
{servicio.creditos && "%"}
</p>
</div>
{servicio.telefono && (
<div className="mb-2">
<label className="form-label">Teléfono:</label>
<p className="form-control">{servicio.telefono}</p>
</div>
)}
{servicio.direccion && (
<div className="mb-2">
<label className="form-label fw-semibold">Dirección:</label>
<p className="form-control">{servicio.direccion}</p>
</div>
)}
<div className="mb-2">
<label className="form-label fw-semibold">Email:</label>
<p className="form-control">{servicio.correo}</p>
</div>
{servicio.programaInterno && (
<div className="mb-2">
<label className="form-label fw-semibold">Programa Interno:</label>
<p className="form-control">{servicio.programaInterno}</p>
</div>
)}
{servicio.profesor && (
<div className="mb-2">
<label className="form-label fw-semibold">Profesor:</label>
<p className="form-control">{servicio.profesor}</p>
</div>
)}
{servicio.createdAt && (
<div className="mb-2">
<label className="form-label fw-semibold">Fecha de registro:</label>
<p className="form-control">{fecha(servicio.createdAt)}</p>
</div>
)}
<div className="mb-2">
<label className="form-label fw-semibold">Fecha de inicio:</label>
<p className="form-control">{fecha(servicio.fechaInicio)}</p>
</div>
<div className="mb-2">
<label className="form-label fw-semibold">Fecha de término:</label>
<p className="form-control">{fecha(servicio.fechaFin)}</p>
</div>
{servicio.fechaLiberacion && (
<div className="mb-2">
<label className="form-label fw-semibold">Fecha de liberación:</label>
<p className="form-control">{fecha(servicio.fechaLiberacion)}</p>
</div>
)}
<div className="mb-2">
<label>Dependencia:</label>
<p className="input">{servicio.Programa.dependencia}</p>
</div>
<div className="mb-2">
<label>Programa:</label>
<p className="input">{servicio.Programa.programa}</p>
</div>
<div className="mb-2">
<label>Clave de programa:</label>
<p className="input">{servicio.Programa.clavePrograma}</p>
</div>
</div>
{/* Datos personales */}
<div className="mb-5">
<h4 className="is-size-4 pb-2">Datos personales</h4>
<div className="mb-2">
<label>Número de cuenta:</label>
<p className="input">{servicio.Usuario.usuario}</p>
</div>
<div className="mb-2">
<label>Nombre:</label>
<p className="input">{servicio.Usuario.nombre}</p>
</div>
<div className="mb-2">
<label>Carrera:</label>
<p className="input">{servicio.Carrera.carrera}</p>
</div>
<div className="mb-2">
<label>Créditos:</label>
<p className="input">
{servicio.creditos ? parseInt(servicio.creditos) : ""}
{servicio.creditos && "%"}
</p>
</div>
{servicio.telefono && (
<div className="mb-2">
<label>Teléfono:</label>
<p className="input">{servicio.telefono}</p>
</div>
)}
{servicio.direccion && (
<div className="mb-2">
<label>Dirección:</label>
<p className="input">{servicio.direccion}</p>
</div>
)}
<div className="mb-2">
<label>Email:</label>
<p className="input">{servicio.correo}</p>
</div>
{servicio.programaInterno && (
<div className="mb-2">
<label>Programa Interno:</label>
<p className="input">{servicio.programaInterno}</p>
</div>
)}
{servicio.profesor && (
<div className="mb-2">
<label>Profesor:</label>
<p className="input">{servicio.profesor}</p>
</div>
)}
{servicio.createdAt && (
<div className="mb-2">
<label>Fecha de registro:</label>
<p className="input">{fecha(servicio.createdAt)}</p>
</div>
)}
<div className="mb-2">
<label>Fecha de inicio:</label>
<p className="input">{fecha(servicio.fechaInicio)}</p>
</div>
<div className="mb-2">
<label>Fecha de término:</label>
<p className="input">{fecha(servicio.fechaFin)}</p>
</div>
{servicio.fechaLiberacion && (
<div className="mb-2">
<label>Fecha de liberación:</label>
<p className="input">{fecha(servicio.fechaLiberacion)}</p>
</div>
)}
</div>
</div>
);
}
+11 -11
View File
@@ -5,16 +5,16 @@ interface Status {
}
interface Props {
status: Status;
Status: Status;
}
export default function MensajeAlumno({ status }: Props) {
export default function MensajeAlumno({ Status }: Props) {
return (
<div className="container mb-6">
<h4 className="block is-size-4 mb-4">Estimado alumno(a):</h4>
<div className="mb-6">
<h4 className="block is-size-4">Estimado alumno(a):</h4>
<p className="has-text-justified is-size-6">
{status.idStatus === 2 && (
{Status.idStatus === 2 && (
<>
Estimado alumno(a): Para poder terminar con el registro de tu trámite
de servicio social te pedimos que verifiques que tus datos sean
@@ -27,7 +27,7 @@ export default function MensajeAlumno({ status }: Props) {
</>
)}
{status.idStatus === 3 && (
{Status.idStatus === 3 && (
<>
Estimado alumno(a): Te informamos que el Área de Registro y Control
de Servicio Social ha validado tu solicitud de registro de servicio
@@ -49,7 +49,7 @@ export default function MensajeAlumno({ status }: Props) {
</>
)}
{status.idStatus === 4 && (
{Status.idStatus === 4 && (
<>
Estimado alumno(a): Para continuar con el proceso de término de tu
servicio social debes de subir tu informe global y contestar el
@@ -57,14 +57,14 @@ export default function MensajeAlumno({ status }: Props) {
</>
)}
{status.idStatus === 5 && (
{Status.idStatus === 5 && (
<>
Estimado alumno(a): Espera a que tus documentos sean validados por
el Departamento de Servicio Social y Bolsa de Trabajo.
</>
)}
{status.idStatus === 6 && (
{Status.idStatus === 6 && (
<>
Estimado alumno(a): Te confirmamos que has concluido con los trámites
necesarios para la liberación de tu servicio social por lo que ahora
@@ -77,7 +77,7 @@ export default function MensajeAlumno({ status }: Props) {
</>
)}
{status.idStatus === 8 && (
{Status.idStatus === 8 && (
<>
Estimado alumno(a): Te informamos que la carta de término que el(la)
responsable de tu servicio social mandó fue rechazada. Espera a que
@@ -85,7 +85,7 @@ export default function MensajeAlumno({ status }: Props) {
</>
)}
{status.idStatus === 9 && (
{Status.idStatus === 9 && (
<>
Estimado alumno(a): Te informamos que el informe global que mandaste
fue rechazado. Súbelo nuevamente y asegúrate de que cumpla con todos
+71 -164
View File
@@ -1,23 +1,17 @@
"use client";
import { useState, useEffect } from "react";
import axios from 'axios';
import { Button, FormGroup } from "react-bootstrap";
import { FaUpload } from "react-icons/fa6";
import { useRouter } from "next/navigation";
import Swal from "sweetalert2";
import Link from "next/link";
import { axiosInstance } from "@/api/config";
import { isAxiosError } from 'axios';
interface Alumno {
tokenAlumno: string;
tokenArchivo: string;
}
interface Servicio {
idServicio: number;
//idCuestionarioAlumno?: number;
cuestionarioAlumno?: object;
//idCuestionarioAlumno2?: number;
cuestionarioAlumno2?: object;
idCuestionarioAlumno?: number;
idCuestionarioAlumno2?: number;
informeGlobal?: string;
}
@@ -41,39 +35,14 @@ export default function PreTermino({
updateIsLoading,
}: Props) {
const [file, setFile] = useState<File | null>(null);
const router = useRouter();
const [servicioLocal, setServicioLocal] = useState<Servicio>(servicio);
useEffect(() => {
setServicioLocal(servicio);
}, [servicio]);
useEffect(() => {
console.log("Esta es la informacion que se le pasa verificacion cuestionario alumno", servicio)
if (file && file.size >= 20000000) {
imprimirError({ message: "El tamaño del archivo excede los 20MB" });
setFile(null);
}
}, [file, imprimirError]);
const confirmarInforme = () => {
Swal.fire({
title: "¿Estas seguro(a) de querer subir este informe global?",
icon: "warning",
showCancelButton: true,
confirmButtonText: "Sí, actualizar",
cancelButtonText: "Cancelar",
confirmButtonColor: "#0d6efd",
cancelButtonColor: "#dc3545",
}).then((result) => {
if(result.isConfirmed) {
enviarInformeGlobal();
}
})
}
const enviarInformeGlobal = async () => {
if (!file) return;
@@ -83,158 +52,96 @@ export default function PreTermino({
formData.append("data", JSON.stringify(data));
formData.append("informeGlobal", file);
console.log("Token del alumno:", alumno.tokenAlumno);
console.log("Alumno completo: ", alumno)
try {
updateIsLoading(true);
// Cambiamos a la otra direccion usamos variable de .env
const res = await axios.put(`${process.env.NEXT_PUBLIC_API_URL}/servicio/informe_global`, formData, {
headers: {
// "Content-Type": "multipart/form-data",
Authorization: `Bearer ${alumno.tokenAlumno}`
}
});
Swal.fire('Exito', 'Se envio el informe global correctamente.', 'success')
setServicioLocal(prev => ({
...prev,
informeGlobal: "uploaded"
}));
// await obtenerServicio();
setFile(null);
// setTimeout(() => {
// router.refresh();
// }, 1000);
//router.refresh(); // Para recargar la pagina y valide si tiene el informe.
} catch (err) {
Swal.fire('Error', 'No se pudo enviar el archivo, por favor intentelo mas tarde.', 'error')
// if (isAxiosError(err)) {
// imprimirError((err.response?.data) || err.message);
// } else {
// imprimirError(err);
// }
updateIsLoading(true);
const res = await axiosInstance.put(`/servicio/informe_global`, formData, { headers: { "Content-Type": "multipart/form-data", Authorization: alumno.tokenArchivo } });
imprimirMensaje(res.data.message);
obtenerServicio();
setFile(null);
} catch (err: unknown) {
if (isAxiosError(err)) {
imprimirError((err.response?.data) || err.message);
} else {
imprimirError(err);
}
} finally {
updateIsLoading(false);
updateIsLoading(false);
}
};
return (
<div className="container">
<h6 className="label fw-semibold">
<div>
<h3 className="label">
Cuestionario de evaluación del programa de servicio social.
</h6>
</h3>
{/* !servicio.idCuestionarioAlumno && !servicio.idCuestionarioAlumno2 ? */}
{servicio.cuestionarioAlumno2 == null && servicio.cuestionarioAlumno == null ? (
<div className="mb-5">
<Link
href={`/alumno/cuestionario/${servicio.idServicio}`}
className="btn btn-outline-primary is-info is-light"
>
<span className="icon">
<i className="fas fa-book-open"></i>
</span>
<span>Cuestionario</span>
</Link>
{!servicio.idCuestionarioAlumno && !servicio.idCuestionarioAlumno2 ? (
<div className="mb-6">
<a
href="/alumno/cuestionario"
className="button is-info is-light"
>
<span className="icon">
<i className="fas fa-book-open"></i>
</span>
<span>Cuestionario</span>
</a>
</div>
) : (
<div className="mb-6">
<p className="block is-size-6">
Cuestionario contestado{" "}
<span className="icon has-text-success">
<i className="fas fa-check-bold"></i>
</span>
</p>
<p className="block is-size-6">
Cuestionario contestado{" "}
<span className="icon has-text-success">
<i className="fas fa-check-bold"></i>
</span>
</p>
</div>
)}
<h6 className="label fw-semibold">
<h3 className="label">
Informe global de actividades{" "}
{servicioLocal.informeGlobal && (
{!servicio.informeGlobal && (
<span>(en formato .PDF. No se aceptan fotos)</span>
)}
.
</h6>
</h3>
{/*
Codigo anterior para el envio del archivo
<div>
<div className="field">
<input
type="file"
accept="application/pdf"
onChange={(e) => {
if (e.target.files && e.target.files[0]) {
setFile(e.target.files[0]);
}
}}
/>
<p className="is-size-6">Tamaño máximo 20MB</p>
</div>
<div className="field has-text-centered my-5">
<button
className="button is-success"
disabled={!file}
onClick={() =>
imprimirWarning(
"¿Estas seguro(a) de querer subir este informe global?",
enviarInformeGlobal
)
{!servicio.informeGlobal ? (
<div>
<div className="field">
<input
type="file"
accept="application/pdf"
onChange={(e) => {
if (e.target.files && e.target.files[0]) {
setFile(e.target.files[0]);
}
>
Enviar
</button>
</div>
}}
/>
<p className="is-size-6">Tamaño máximo 20MB</p>
</div>
*/}
{!servicioLocal.informeGlobal ? (
<div className="ph-5">
<FormGroup>
<div className="border p-4 text-center rounded"
style={{ cursor: 'pointer'}}
onClick={() => document.getElementById("fileInput")?.click()}
>
<FaUpload size={40} className="mb-2"/>
<p className="mb-1">
{file?.name || 'Arrastra aquì tu archivo o da click aquì para buscar'}
</p>
<p className="is-size-6">Tamaño maximo 20MB</p>
<p className="is-size-7">Si al momento de elegir un archivo este no se selecciona, haga click en cancelar en la ventana emergente e intente de nuevo.</p>
</div>
<input
id="fileInput"
type="file"
accept="application/pdf"
onChange={(e) => {
if (e.target.files && e.target.files[0]) {
setFile(e.target.files[0]);
}
}}
hidden
/>
</FormGroup>
<div className="d-flex gap-2 justify-content-center mb-5">
<Button
className=""
disabled={!file}
onClick={confirmarInforme}
>Enviar</Button>
</div>
<div className="field has-text-centered my-5">
<button
className="button is-success"
disabled={!file}
onClick={() =>
imprimirWarning(
"¿Estas seguro(a) de querer subir este informe global?",
enviarInformeGlobal
)
}
>
Enviar
</button>
</div>
</div>
) : (
<p className="block is-size-6 mb-5">
Informe Global enviado.{" "}
<span className="icon has-text-success">
<i className="fas fa-check-bold"></i>
</span>
<p className="block is-size-6">
Informe Global enviado.{" "}
<span className="icon has-text-success">
<i className="fas fa-check-bold"></i>
</span>
</p>
)}
</div>
+238 -543
View File
@@ -1,22 +1,11 @@
'use client';
import { useState, useEffect } from 'react';
import React, { useState, useEffect } from 'react';
import { axiosInstance } from '@/api/config';
import moment from 'moment';
import 'bootstrap/dist/css/bootstrap.min.css';
import { Col, FormGroup, FormLabel, InputGroup } from 'react-bootstrap';
import { FaRegCalendar, FaUpload } from 'react-icons/fa6';
import DatePicker from 'react-datepicker';
import BotonRegresar from '../boton-regresar';
import axios, { AxiosError } from "axios";
import Image from 'next/image';
import { registerLocale } from "react-datepicker";
import { es } from "date-fns/locale/es";
import Swal from 'sweetalert2';
registerLocale("es", es);
5
// 🔹 Tipos estrictos
interface Alumno {
idUsuario?: number;
idCarrera?: number;
@@ -25,29 +14,21 @@ interface Alumno {
creditos?: string;
}
// correo);
// formData.append("alumno[creditos]", alumno.creditos.toString());
// formData.append("alumno[telefono]", alumno.telefono);
// formData.append("alumno[fechaInicio]", alumno.fechaInicio);
// formData.append("alumno[fechaFin]", alumno.fechaFin);
// formData.append("alumno[fechaNacimiento]", alumno.fechaNacimiento);
// formData.append("alumno[direccion]", alumno.direccion);
interface Responsable {
//token: { headers: Record<string, string> };
tokenArchivo: string;
token: { headers: Record<string, string> };
tokenArchivo: { headers: Record<string, string> };
}
interface Props {
// responsable: Responsable;
responsable: Responsable;
imprimirError: (msg: { message: string }) => void;
imprimirMensaje: (msg: string) => void;
imprimirWarning: (msg: string, callback: () => void, title: string) => void;
imprimirWarning: (msg: string, callback: () => void) => void;
updateIsLoading: (value: boolean) => void;
}
export default function CasoEspecialForm({
// responsable,
responsable,
imprimirError,
imprimirMensaje,
imprimirWarning,
@@ -65,32 +46,11 @@ export default function CasoEspecialForm({
const [alumno, setAlumno] = useState<Alumno>({});
const [fechaInicio, setFechaInicio] = useState<Date>(new Date());
const [fechaFin, setFechaFin] = useState<Date>(new Date());
const [fechaNacimiento, setFechaNacimiento] = useState<Date | null>(null);
const [fechaNacimiento, setFechaNacimiento] = useState<Date>(new Date());
const [file, setFile] = useState<File | null>(null);
const [minDate, setMinDate] = useState<Date>(new Date('2020-01-02'));
const [minDate2, setMinDate2] = useState<Date>(new Date());
const [tokenArchivo, setTokenArchivo] = useState('');
useEffect(() => {
setTokenArchivo(localStorage.getItem('token') || '');
}, []);
console.log("Este es el tokenArchivo en caso especial form:", tokenArchivo);
// Para mostrar las fechas
const handleFechaInicioChange = (date: Date | null) => {
setFechaInicio(date || new Date());
}
const handleFechaFinChange = (date: Date | null) => {
setFechaFin(date || new Date());
}
const handleFechaNacimientoChange = (date: Date | null) => {
setFechaNacimiento(date)
}
// Función para resetear campos
const resetear = (): void => {
setDependencia('');
@@ -103,7 +63,7 @@ export default function CasoEspecialForm({
setAlumno({});
setFechaInicio(new Date());
setFechaFin(new Date());
setFechaNacimiento(null);
setFechaNacimiento(new Date());
setFile(null);
};
@@ -112,54 +72,46 @@ export default function CasoEspecialForm({
const permitidas = [/.zip$/i, /.rar$/i];
const esValida = permitidas.some((regex) => regex.test(archivo.name));
if (!esValida) {
setFile(null);
imprimirError({
message: 'Asegúrate de ingresar un archivo con la extensión correcta (.zip o .rar).',
});
setFile(null);
imprimirError({
message: 'Asegúrate de ingresar un archivo con la extensión correcta (.zip o .rar).',
});
}
};
// Mostrar/ocultar botón de enviar
const mostrarBoton = (): boolean => {
return (
!alumno.idUsuario ||
!telefono ||
!direccion ||
!correo ||
!idStatus ||
(idStatus === '11' && !motivo) ||
(idStatus === '12' && (!dependencia || !institucion)) ||
!file
!alumno.idUsuario ||
!telefono ||
!direccion ||
!correo ||
!idStatus ||
(idStatus === '11' && !motivo) ||
(idStatus === '12' && (!dependencia || !institucion)) ||
!file
);
};
// Buscar alumno
const buscarAlumno = async (): Promise<void> => {
try {
updateIsLoading(true);
const res = await axiosInstance.post(`/usuario/escolares/${numeroCuenta}`);
resetear();
console.log('Esta es la res:' , res)
setAlumno(res.data);
updateIsLoading(true);
const res = await axiosInstance.get(`/usuario/escolares?numeroCuenta=${numeroCuenta}`, responsable.token);
resetear();
setAlumno(res.data);
} catch (err: unknown) {
resetear();
setNumeroCuenta('');
const axiosErr = err as AxiosError<any>;
const mensaje = axiosErr?.response?.data?.message || 'El alumno no cumple con los requisitos para hacer el servicio social.'
Swal.fire({
icon: 'error',
title: 'Error',
text: mensaje,
})
// let msg = { message: 'Error al buscar alumno.' };
// if (typeof err === 'object' && err !== null && 'response' in err) {
// const anyErr = err as { response?: { data?: { message?: unknown } } };
// const m = anyErr.response?.data as { message?: unknown } | undefined;
// msg = { message: m?.message ? String(m.message) : msg.message };
// } else if (err instanceof Error) msg = { message: err.message };
// imprimirError(msg);
resetear();
setNumeroCuenta('');
let msg = { message: 'Error al buscar alumno.' };
if (typeof err === 'object' && err !== null && 'response' in err) {
const anyErr = err as { response?: { data?: { message?: unknown } } };
const m = anyErr.response?.data as { message?: unknown } | undefined;
msg = { message: m?.message ? String(m.message) : msg.message };
} else if (err instanceof Error) msg = { message: err.message };
imprimirError(msg);
} finally {
updateIsLoading(false);
updateIsLoading(false);
}
};
@@ -167,177 +119,50 @@ export default function CasoEspecialForm({
const enviar = async (): Promise<void> => {
if (!file) return;
if (!tokenArchivo) {
imprimirError({ message: "Sesión expirada. Vuelve a iniciar sesión." });
return;
}
if (!fechaNacimiento) {
imprimirError({ message: "Selecciona la fecha de nacimiento" });
return;
}
if (!alumno.idUsuario || !alumno.idCarrera) {
imprimirError({ message: "Busca primero un alumno válido" });
return;
}
const data = {
idUsuario: Number(alumno.idUsuario),
idCarrera: Number(alumno.idCarrera),
idStatus: Number(idStatus),
numeroCuenta: numeroCuenta,
creditos: alumno.creditos ? alumno.creditos.toString() : '',
correo: correo.trim(),
fechaInicio: formatDate(fechaInicio),
fechaFin: formatDate(fechaFin),
fechaNacimiento: formatDate(fechaNacimiento),
direccion: direccion.trim(),
telefono: telefono,
...(idStatus === '12' && { institucion: institucion.trim() }),
...(idStatus === '12' && { dependencia: dependencia.trim() }),
...(idStatus === '11' && { motivo }),
// idUsuario: Number('19839'),
// idCarrera: Number('16'),
// idStatus: Number('11'),
// numeroCuenta: '422016698',
// creditos: '100',
// correo: correo.trim(),
// fechaInicio: formatDate(fechaInicio),
// fechaFin: formatDate(fechaFin),
// fechaNacimiento: formatDate(fechaNacimiento),
// direccion: direccion.trim(),
// telefono: telefono,
// ...(idStatus === '12' && { institucion: institucion.trim() }),
// ...(idStatus === '12' && { dependencia: dependencia.trim() }),
// ...(idStatus === '11' && { motivo }),
idUsuario: alumno.idUsuario,
idCarrera: alumno.idCarrera,
idStatus,
numeroCuenta,
creditos: alumno.creditos,
correo,
fechaInicio: moment(fechaInicio),
fechaFin: moment(fechaFin),
fechaNacimiento: moment(fechaNacimiento),
direccion,
telefono,
institucion,
dependencia,
motivo,
};
console.log("Datos a enviar en caso especial:", data);
const formData = new FormData();
// agregar el archivo.
formData.append("archivos", file);
formData.append("alumno", JSON.stringify(data));
// armas los datos para el dto
// formData.append("alumno[idUsuario]", alumno.idUsuario.toString());
// formData.append("alumno[idCarrera]", alumno.idCarrera.toString());
// formData.append("alumno[idStatus]", idStatus);
// formData.append("alumno[numeroCuenta]", numeroCuenta);
// formData.append("alumno[correo]", correo);
// formData.append("alumno[creditos]", alumno.creditos || "");
// formData.append("alumno[telefono]", telefono);
// formData.append("alumno[fechaInicio]", formatDate(fechaInicio));
// formData.append("alumno[fechaFin]", formatDate(fechaFin));
// formData.append("alumno[fechaNacimiento]", formatDate(fechaNacimiento));
// formData.append("alumno[direccion]", direccion);
// validaciones extra
// if (institucion) {
// formData.append("alumno[institucion]", institucion);
// }
// if (dependencia) {
// formData.append("alumno[dependencia]", dependencia);
// }
// if (motivo) {
// formData.append("alumno[motivo]", motivo);
// }
// if (!idStatus) {
// console.log('No se envio el idStatus')
// }
//const info = formData.append('alumno', new Blob([JSON.stringify(data)], { type: 'application/json' }));
// formData.append('archivos', file);
// formData.append("alumno[idUsuario]", alumno.idUsuario.toString());
// formData.append("alumno[idCarrera]", alumno.idCarrera.toString());
// formData.append("alumno[idStatus]", alumno.idStatus.toString());
// formData.append("alumno[numeroCuenta]", alumno.numeroCuenta);
// formData.append("alumno[correo]", alumno.correo);
// formData.append("alumno[creditos]", alumno.creditos.toString());
// formData.append("alumno[telefono]", alumno.telefono);
// formData.append("alumno[fechaInicio]", alumno.fechaInicio);
// formData.append("alumno[fechaFin]", alumno.fechaFin);
// formData.append("alumno[fechaNacimiento]", alumno.fechaNacimiento);
// formData.append("alumno[direccion]", alumno.direccion);
console.log("FormData a enviar en caso especial:", formData.getAll);
console.log("Esta es la info al registrar el servicio", formData);
formData.append('alumno', JSON.stringify(data));
formData.append('archivos', file);
try {
updateIsLoading(true);
// Cambiamos a axios normal para enviar la carta de aceptación
// const res = await axiosInstance.post(`/caso-especial`, formData);
const res = await axios.post(`${process.env.NEXT_PUBLIC_API_URL}/caso-especial`, formData, {
headers: {
'Content-Type': 'multipart/form-data',
Authorization: tokenArchivo ? `Bearer ${tokenArchivo}` : '',
},
});
resetear();
setNumeroCuenta('');
const mensaje = res?.data?.message || 'Se creo el servicio social con exito.'
Swal.fire({
icon: 'success',
title: 'Exito',
text: mensaje,
})
// imprimirMensaje(res.data.message);
updateIsLoading(true);
const res = await axiosInstance.post(`/caso_especial/nuevo`, formData, responsable.tokenArchivo);
resetear();
setNumeroCuenta('');
imprimirMensaje(res.data.message);
} catch (err: unknown) {
const axiosErr = err as AxiosError<any>;
const mensaje = axiosErr?.response?.data?.message || 'No se pudo completar el registro del servicio social, por favor intentelo mas tarde.'
Swal.fire({
icon: 'error',
title: 'Error',
text: mensaje,
})
// Quitar esta parte
// if (axios.isAxiosError(err)) {
// const backendMessage = err.response?.data?.message;
// if (backendMessage) {
// // Si el backend manda string
// if (typeof backendMessage === "string") {
// imprimirError({ message: backendMessage });
// return;
// }
// // Si el backend manda arreglo (NestJS validation)
// if (Array.isArray(backendMessage)) {
// imprimirError({ message: backendMessage.join("\n") });
// return;
// }
// }
// Fallback SOLO si no vino nada del backend
// imprimirError({ message: "Error inesperado del servidor" });
// let msg: { message: string } = { message: 'Error al enviar formulario.' };
// if (typeof err === 'object' && err !== null && 'response' in err) {
// const anyErr = err as { response?: { data?: unknown } };
// if (anyErr.response && typeof anyErr.response.data === 'object' && anyErr.response.data !== null) {
// const d = anyErr.response.data as { message?: unknown } | unknown;
// if (d && typeof d === 'object' && 'message' in d && typeof (d as { message?: unknown }).message === 'string') {
// msg = { message: (d as { message?: unknown }).message as string };
// }
// }
// } else if (err instanceof Error) {
// msg = { message: err.message };
// }
// imprimirError(msg);
} finally {
updateIsLoading(false);
let msg: { message: string } = { message: 'Error al enviar formulario.' };
if (typeof err === 'object' && err !== null && 'response' in err) {
const anyErr = err as { response?: { data?: unknown } };
if (anyErr.response && typeof anyErr.response.data === 'object' && anyErr.response.data !== null) {
const d = anyErr.response.data as { message?: unknown } | unknown;
if (d && typeof d === 'object' && 'message' in d && typeof (d as { message?: unknown }).message === 'string') {
msg = { message: (d as { message?: unknown }).message as string };
}
}
} else if (err instanceof Error) {
msg = { message: err.message };
}
imprimirError(msg);
} finally {
updateIsLoading(false);
}
};
const formatDate = (date: Date) => {
return date.toISOString().split("T")[0];
};
// Actualizar fechas automáticamente
@@ -359,11 +184,11 @@ export default function CasoEspecialForm({
}, [idStatus]);
useEffect(() => {
if (file && file.size > 20_000_000) {
imprimirError({ message: 'El tamaño del archivo excede los 20MB.' });
setFile(null);
if (file && file.size >= 20_000_000) {
imprimirError({ message: 'El tamaño del archivo excede los 20MB.' });
setFile(null);
} else if (file) {
validarExtencion(file);
validarExtencion(file);
}
}, [file]);
@@ -378,325 +203,195 @@ export default function CasoEspecialForm({
updateFechas();
}, []);
const confirmarCasoEspecial = () => {
Swal.fire({
})
}
{/* Validaciones del cuestionario */}
// Render
return (
<div className="mt-4">
<div className="mb-3">
<label className="form-label">Número de Cuenta</label>
<div className="input-group">
<InputGroup.Text>
<Image src='/image/numCuenta.svg' width={24} height={24} alt="Icono de escuela"/>
</InputGroup.Text>
<input
type="text"
className="form-control"
placeholder="Número de Cuenta"
maxLength={9}
value={numeroCuenta}
onChange={(e) => setNumeroCuenta(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && buscarAlumno()}
/>
<button className="text-white rounded-1" onClick={buscarAlumno}>
Buscar
</button>
</div>
<div className="container mt-4">
<div className="mb-3">
<label className="form-label">Número de Cuenta</label>
<div className="input-group">
<input
type="text"
className="form-control"
placeholder="Número de Cuenta"
maxLength={9}
value={numeroCuenta}
onChange={(e) => setNumeroCuenta(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && buscarAlumno()}
/>
<button className="btn btn-info text-white" onClick={buscarAlumno}>
Buscar
</button>
</div>
</div>
{alumno.nombre && (
<div className="mb-3">
<label className="form-label">Nombre</label>
<p className="form-control">{alumno.nombre || ''}</p>
</div>
)}
<div className="mb-3">
<label className="form-label">Nombre</label>
<p className="form-control">{alumno.nombre || ''}</p>
</div>
{alumno.carrera && (
<div className="mb-3">
<label className="form-label">Carrera</label>
<p className="form-control">{alumno.carrera || ''}</p>
</div>
)}
<div className="mb-3">
<label className="form-label">Carrera</label>
<p className="form-control">{alumno.carrera || ''}</p>
</div>
{alumno.creditos && (
<div className="mb-3">
<label className="form-label">Créditos</label>
<p className="form-control">
{alumno.creditos ? parseInt(alumno.creditos) : ''}{' '}
{alumno.creditos ? '%' : ''}
</p>
</div>
)}
<div className="mb-3">
<label className="form-label">Créditos</label>
<p className="form-control">
{alumno.creditos ? parseInt(alumno.creditos) : ''}{' '}
{alumno.creditos ? '%' : ''}
</p>
</div>
<Col>
<FormGroup>
<FormLabel>Fecha de nacimiento</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendar />
</InputGroup.Text>
<div className="mb-3">
<label className="form-label">Dirección</label>
<input
type="text"
className="form-control"
maxLength={200}
value={direccion}
onChange={(e) => setDireccion(e.target.value)}
/>
</div>
<DatePicker
// selected={fechaNacimiento}
selected={fechaNacimiento}
// maxDate={new Date()}
onChange={handleFechaNacimientoChange}
dateFormat="dd-MM-yyyy"
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
<div className="mb-3">
<label className="form-label">Teléfono</label>
<input
type="tel"
className="form-control"
maxLength={10}
value={telefono}
onChange={(e) => setTelefono(e.target.value)}
/>
</div>
showMonthDropdown
showYearDropdown
scrollableYearDropdown
yearDropdownItemNumber={60}
dropdownMode="select"
<div className="mb-3">
<label className="form-label">Correo electrónico</label>
<input
type="email"
className="form-control"
value={correo}
onChange={(e) => setCorreo(e.target.value)}
/>
</div>
locale='es'
/>
</InputGroup>
</FormGroup>
</Col>
<div className="mb-3">
<label className="form-label">Fecha de inicio</label>
<input
type="date"
className="form-control"
value={fechaInicio.toISOString().substring(0, 10)}
onChange={(e) => setFechaInicio(new Date(e.target.value))}
min={minDate.toISOString().substring(0, 10)}
/>
</div>
{fechaInicio && (
<div className="mb-3">
<label className="form-label">Dirección</label>
<label className="form-label">Fecha de fin</label>
<input
type="date"
className="form-control"
value={fechaFin.toISOString().substring(0, 10)}
onChange={(e) => setFechaFin(new Date(e.target.value))}
min={minDate2.toISOString().substring(0, 10)}
/>
</div>
)}
<div className="mb-3">
<label className="form-label">Fecha de nacimiento</label>
<input
type="date"
className="form-control"
max={new Date().toISOString().substring(0, 10)}
value={fechaNacimiento.toISOString().substring(0, 10)}
onChange={(e) => setFechaNacimiento(new Date(e.target.value))}
/>
</div>
<div className="mb-3">
<label className="form-label">Artículo</label>
<select
className="form-select"
value={idStatus}
onChange={(e) => setIdStatus(e.target.value)}
>
<option value="">Selecciona una opción</option>
<option value="11">Artículo 52</option>
<option value="12">Artículo 91</option>
</select>
</div>
{idStatus === '11' && (
<div className="mb-3">
<label className="form-label">Motivo</label>
<select
className="form-select"
value={motivo}
onChange={(e) => setMotivo(e.target.value)}
>
<option value="">Selecciona una opción</option>
<option value="1">Tercera edad</option>
<option value="2">Capacidades diferentes</option>
</select>
</div>
)}
{idStatus === '12' && (
<>
<div className="mb-3">
<label className="form-label">Institución</label>
<input
type="text"
className="form-control"
maxLength={200}
value={direccion}
onChange={(e) => setDireccion(e.target.value)}
maxLength={250}
value={institucion}
onChange={(e) => setInstitucion(e.target.value)}
/>
</div>
<div className="mb-3">
<label className="form-label">Teléfono</label>
<label className="form-label">Dependencia</label>
<input
type="tel"
type="text"
className="form-control"
maxLength={10}
value={telefono}
onChange={(e) => setTelefono(e.target.value)}
maxLength={250}
value={dependencia}
onChange={(e) => setDependencia(e.target.value)}
/>
</div>
</>
)}
<div className="mb-3">
<label className="form-label">Correo electrónico</label>
<input
type="email"
className="form-control"
value={correo}
onChange={(e) => setCorreo(e.target.value)}
/>
</div>
<div className="mb-3">
<label className="form-label">Archivo (.zip, .rar)</label>
<input
type="file"
accept=".zip,.rar"
className="form-control"
onChange={(e) => setFile(e.target.files ? e.target.files[0] : null)}
/>
<div className="form-text">Tamaño máximo 20MB</div>
</div>
<Col>
<FormGroup>
<FormLabel>Fecha de inicio</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendar />
</InputGroup.Text>
<DatePicker
selected={fechaInicio}
onChange={handleFechaInicioChange}
minDate={minDate}
dateFormat="dd-MM-yyyy"
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
showMonthDropdown
showYearDropdown
scrollableYearDropdown
yearDropdownItemNumber={15}
dropdownMode="select"
locale='es'
/>
</InputGroup>
</FormGroup>
</Col>
{fechaInicio && (
<Col>
<FormGroup>
<FormLabel>Fecha de fin</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendar />
</InputGroup.Text>
<DatePicker
selected={fechaFin}
onChange={handleFechaFinChange}
minDate={minDate2}
dateFormat="dd-MM-yyyy"
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
showMonthDropdown
showYearDropdown
scrollableYearDropdown
yearDropdownItemNumber={15}
dropdownMode="select"
locale='es'
/>
</InputGroup>
</FormGroup>
</Col>
)}
{/*
<div className="mb-3">
<label className="form-label">Fecha de inicio</label>
<input
type="date"
className="form-control"
value={fechaInicio.toISOString().substring(0, 10)}
onChange={(e) => setFechaInicio(new Date(e.target.value))}
min={minDate.toISOString().substring(0, 10)}
/>
</div>
{fechaInicio && (
<div className="mb-3">
<label className="form-label">Fecha de fin</label>
<input
type="date"
className="form-control"
value={fechaFin.toISOString().substring(0, 10)}
onChange={(e) => setFechaFin(new Date(e.target.value))}
min={minDate2.toISOString().substring(0, 10)}
/>
</div>
)}
<div className="mb-3">
<label className="form-label">Fecha de nacimiento</label>
<input
type="date"
className="form-control"
max={new Date().toISOString().substring(0, 10)}
value={fechaNacimiento.toISOString().substring(0, 10)}
onChange={(e) => setFechaNacimiento(new Date(e.target.value))}
/>
</div>
*/}
<div className="mb-3">
<label className="form-label">Artículo</label>
<select
className="form-select"
value={idStatus}
onChange={(e) => setIdStatus(e.target.value)}
>
<option value="">Selecciona una opción</option>
<option value="11">Artículo 52</option>
<option value="12">Artículo 91</option>
</select>
</div>
{idStatus === '11' && (
<div className="mb-3">
<label className="form-label">Motivo</label>
<select
className="form-select"
value={motivo}
onChange={(e) => setMotivo(e.target.value)}
>
<option value="">Selecciona una opción</option>
<option value="1">Tercera edad</option>
<option value="2">Capacidades diferentes</option>
</select>
</div>
)}
{idStatus === '12' && (
<>
<div className="mb-3">
<label className="form-label">Institución</label>
<input
type="text"
className="form-control"
maxLength={250}
value={institucion}
onChange={(e) => setInstitucion(e.target.value)}
/>
</div>
<div className="mb-3">
<label className="form-label">Dependencia</label>
<input
type="text"
className="form-control"
maxLength={250}
value={dependencia}
onChange={(e) => setDependencia(e.target.value)}
/>
</div>
</>
)}
<FormGroup className='mb-3'>
<FormLabel>Archivo (.zip, .rar)</FormLabel>
<div
className='border p-4 text-center rounded'
style={{ cursor: 'pointer' }}
onClick={() => document.getElementById('fileInput')?.click()}
>
<FaUpload size={40} className='mb-2'/>
<p className='mb-1'>
{file?.name || 'Arrastra aquí tu archivo o da click aquí para buscar'}
</p>
<p className='is-size-6'>Tamaño máximo 20MB</p>
<p className='is-size-6'>Si al momento de elegir un archivo este no se selecciona, haga click en cancelar en la ventana emergente e intente de nuevo.</p>
</div>
<input
id='fileInput'
type="file"
accept='.zip, .rar'
style={{ display: 'none'}}
onChange={e => setFile(e.target.files ? e.target.files[0] : null)}
/>
</FormGroup>
{/*
<div className="mb-3">
<label className="form-label">Archivo (.zip, .rar)</label>
<input
type="file"
accept=".zip,.rar"
className="form-control"
onChange={(e) => setFile(e.target.files ? e.target.files[0] : null)}
/>
<div className="form-text">Tamaño máximo 20MB</div>
</div>
*/}
<div className="text-center mt-4 mb-3">
<button
className="btn btn-success"
disabled={mostrarBoton()}
onClick={() =>
imprimirWarning(
'¿Estás seguro(a) de querer crear un nuevo caso especial?',
enviar,
'Aviso'
)}
>
Enviar
</button>
</div>
<BotonRegresar />
<div className="text-center mt-4">
<button
className="btn btn-success"
disabled={mostrarBoton()}
onClick={() =>
imprimirWarning(
'¿Estás seguro(a) de querer crear un nuevo caso especial?',
enviar
)
}
>
Enviar
</button>
</div>
</div>
);
}
@@ -3,9 +3,6 @@
import React, { useEffect, useState, useCallback } from "react";
import { axiosInstance } from "@/api/config";
import ServicioSocialTabla from "../servicio-social-tabla";
import { Button, Col, Form, FormControl, FormGroup, FormLabel, InputGroup, Row } from "react-bootstrap";
import { FaCircleInfo, FaUser } from "react-icons/fa6";
import Image from "next/image";
interface Responsable {
idTipoUsuario: number;
@@ -21,7 +18,7 @@ interface ServicioEspecial {
idServicio: number;
nombre: string;
numeroCuenta: string;
Status: string;
status: string;
// agrega más campos según lo que devuelva tu API
}
@@ -72,9 +69,13 @@ export default function LiberarCasoEspecial({ responsable, imprimirError }: Prop
if (search.nombre) query += `&nombre=${search.nombre}`;
if (search.numeroCuenta) query += `&numeroCuenta=${search.numeroCuenta}`;
const res = await axiosInstance.get(`/caso-especial/servicios_especiales?pagina=${page}${query}`);
const res = await axiosInstance.get(`/caso_especial/servicios_especiales?pagina=${page}${query}`, {
headers: {
Authorization: `Bearer ${responsable.token}`,
},
});
setData(res.data.data.serviciosEspeciales);
setData(res.data.serviciosEspeciales);
setTotal(res.data.count);
} catch (err: unknown) {
let msg = 'Error al obtener los casos especiales';
@@ -117,84 +118,10 @@ export default function LiberarCasoEspecial({ responsable, imprimirError }: Prop
return (
<div className="mb-6 container mt-4">
<h2 className="mb-4 fw-bold">Casos Especiales</h2>
<h3 className="h4 mb-4">Casos Especiales</h3>
<section className="my-3">
<Form onSubmit={(e) => { e.preventDefault(); obtenerCasosEspeciales(); }}>
<Row className="g-3">
<Col md={3}>
<FormGroup>
<FormLabel className="fw-semibold">Número de Cuenta</FormLabel>
<InputGroup>
<InputGroup.Text className="rounded-4">
<Image src="/image/numCuenta.svg" width={24} height={24} alt="Icono de escuela" />
</InputGroup.Text>
<FormControl
type="text"
placeholder="No.Cuenta"
maxLength={9}
value={search.numeroCuenta}
onChange={(e) => setSearch(prev => ({ ...prev, numeroCuenta: e.target.value }))}
onKeyDown={(e) => { if (e.key === 'Enter') obtenerCasosEspeciales(); }}
className="rounded-4"
/>
</InputGroup>
</FormGroup>
</Col>
<Col md={3}>
<FormGroup>
<FormLabel className="fw-semibold">Nombre</FormLabel>
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaUser />
</InputGroup.Text>
<FormControl
type="text"
placeholder="Nombre"
value={search.nombre}
onChange={(e) => setSearch(prev => ({ ...prev, nombre: e.target.value }))}
onKeyDown={(e) => { if (e.key === 'Enter') obtenerCasosEspeciales(); }}
className="rounded-4"
/>
</InputGroup>
</FormGroup>
</Col>
<Col md={3}>
<FormGroup>
<FormLabel className="fw-semibold">Status</FormLabel>
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaCircleInfo />
</InputGroup.Text>
<Form.Select
className="rounded-4"
value={search.idStatus}
onChange={(e) => setSearch(prev => ({ ...prev, idStatus: e.target.value }))}
>
<option value="">Status</option>
{status.slice(10, 14).map((s) => (
<option key={s.idStatus} value={s.idStatus}>
{s.status}
</option>
))}
</Form.Select>
</InputGroup>
</FormGroup>
</Col>
<Col md={3} className="d-flex align-items-end">
<Button type="submit" className="w-100 rounded-5" disabled={isLoading}>
{isLoading ? 'Buscando...' : 'Buscar'}
</Button>
</Col>
</Row>
</Form>
</section>
{/*
<div className="row g-3">
{/* Número de cuenta */}
<div className="col-md-3">
<label className="form-label">Número de Cuenta</label>
<input
@@ -208,7 +135,7 @@ export default function LiberarCasoEspecial({ responsable, imprimirError }: Prop
/>
</div>
{/* Nombre */}
<div className="col-md-3">
<label className="form-label">Nombre</label>
<input
@@ -221,6 +148,7 @@ export default function LiberarCasoEspecial({ responsable, imprimirError }: Prop
/>
</div>
{/* Status */}
<div className="col-md-3">
<label className="form-label">Status</label>
<select
@@ -239,7 +167,7 @@ export default function LiberarCasoEspecial({ responsable, imprimirError }: Prop
</select>
</div>
{/* Botón Buscar */}
<div className="col-md-3 d-flex align-items-end">
<button
className="btn btn-info w-100"
@@ -250,17 +178,6 @@ export default function LiberarCasoEspecial({ responsable, imprimirError }: Prop
</button>
</div>
</div>
*/}
<ServicioSocialTabla
columnaFechaFin={false}
columnaFechaInicio={false}
columnaFechaRegistro={true}
idTipoUsuario={responsable.idTipoUsuario}
total={total}
data={data}
onPageChange={onPageChange}
/>
{/* Tabla */}
{/*
-2
View File
@@ -1,4 +1,3 @@
'use client'
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
@@ -15,7 +14,6 @@ export default function Logout() {
const handleLogout = () => {
localStorage.removeItem("token");
localStorage.removeItem('usuario');
localStorage.clear();
router.push("/");
}
@@ -2,9 +2,6 @@
import React, { useEffect, useMemo, useState } from "react";
import { axiosInstance } from "@/api/config";
import { routerServerGlobal } from "next/dist/server/lib/router-utils/router-server-context";
import { useRouter } from "next/navigation";
import Swal from "sweetalert2";
type Responsable = { token?: string };
@@ -47,8 +44,6 @@ export default function CuestionarioResponsbale2({
const [isLoading, setIsLoading] = useState(false);
const [respuestas, setRespuestas] = useState<Respuestas>({});
const router = useRouter()
const formulario: Formulario = useMemo(() => ({
titulo: '2025 Evaluación de la institución receptora de servicio social',
descripcion: `Estimado(a) responsable de programa de servicio social:\nTe solicitamos llenar cuidadosamente los campos solicitados a continuación para poder\nvalidar el término del servicio social de nuestro alumno(a).`,
@@ -158,16 +153,12 @@ export default function CuestionarioResponsbale2({
else answered = answer !== null && String(answer) !== '';
if (!answered) missing.push(`Pregunta ${p.numeroPregunta}`);
});
formulario.tablas.forEach((t, index) => {
formulario.tablas.forEach((t) => {
const allAnswered = t.renglones.every((r) => {
const tablaResp = respuestas[String(t.idTabla)] as Record<number, string | null> | undefined;
return tablaResp && tablaResp[r.idRenglon] !== null;
});
if (!allAnswered) {
const letra = String.fromCharCode(65 + index);
missing.push(`Tabla ${t.numeroPregunta}.${letra}`);
}
if (!allAnswered) missing.push(`Tabla ${t.numeroPregunta}`);
});
return missing;
}, [formulario, respuestas]);
@@ -213,7 +204,6 @@ export default function CuestionarioResponsbale2({
}
}
}
for (const t of formulario.tablas) {
for (const r of t.renglones) {
const tablaResp = respuestas[String(t.idTabla)] as Record<number, string | null> | undefined;
@@ -245,32 +235,12 @@ export default function CuestionarioResponsbale2({
const valor = respuestasTabla[idR as unknown as number];
let clave = `p${numPregunta}_${idR}`;
if (subLetra) clave = `p${numPregunta}_${subLetra}_${idR}`;
// antes resultado[clave] = valor ?? '';
// Modifique esta linea
if (valor !== null && valor !== '') {
resultado[clave] = valor;
}
resultado[clave] = valor ?? '';
}
} else {
const preguntaDef = formulario.preguntas.find((p) => p.id === key);
if (!preguntaDef) continue;
// VALIDAR CONDICIONAL (p6)
if (preguntaDef.condicional) {
const cond = preguntaDef.condicional;
if (resps[cond.preguntaId] !== cond.valor) {
continue; // NO mandar esta pregunta
}
}
const valor = resps[key];
if (valor !== null && String(valor).trim() !== '') {
resultado[`p${preguntaDef.numeroPregunta}`] = valor as string;
}
//resultado[`p${preguntaDef.numeroPregunta}`] = resps[key] as string;
resultado[`p${preguntaDef.numeroPregunta}`] = resps[key] as string;
}
}
return resultado;
@@ -281,36 +251,21 @@ export default function CuestionarioResponsbale2({
updateIsLoading(true);
setIsLoading(true);
try {
const data = { ...formatearRespuestas(respuestas), idServicio: Number(localStorage.getItem('idServicio')) };
console.log('Datos a enviar:', data);
const data = { ...formatearRespuestas(respuestas), idServicio: localStorage.getItem('idServicio') };
const headers = responsable?.token ? { Authorization: `Bearer ${responsable.token}` } : undefined;
await axiosInstance.post('/cuestionario-programa2', data, { headers });
Swal.fire({
title: 'Éxito',
text: 'Se subio el cuestionario correctamente.',
icon: 'success',
})
//imprimirMensaje('Se enviaron los datos correctamente');
// try { localStorage.removeItem('idCuestionarioAlumno'); } catch (_) {}
if (typeof window !== 'undefined') router.replace('/responsable'); //window.location.href = '/responsable';
} catch (err) {
Swal.fire({
title: 'Error',
text: 'No se pudo enviar el cuestionario.',
icon: 'error',
})
// if (typeof err === 'object' && err !== null && 'response' in err) {
// const anyErr = err as { response?: { data?: { message?: unknown } } };
// imprimirError(String(anyErr.response?.data?.message) || 'Error al enviar el formulario');
// } else if (err instanceof Error) {
// imprimirError(err.message);
// } else {
// imprimirError('Error al enviar el formulario');
// }
await axiosInstance.post('/cuestionario_programa', data, { headers });
imprimirMensaje('Se enviaron los datos correctamente');
try { localStorage.removeItem('idCuestionarioAlumno'); } catch (_) {}
if (typeof window !== 'undefined') window.location.href = '/alumno';
} catch (err: unknown) {
if (typeof err === 'object' && err !== null && 'response' in err) {
const anyErr = err as { response?: { data?: { message?: unknown } } };
imprimirError(String(anyErr.response?.data?.message) || 'Error al enviar el formulario');
} else if (err instanceof Error) {
imprimirError(err.message);
} else {
imprimirError('Error al enviar el formulario');
}
} finally {
updateIsLoading(false);
setIsLoading(false);
@@ -336,12 +291,12 @@ export default function CuestionarioResponsbale2({
const p = item as Pregunta;
return (
<div key={p.id} className="mb-6 mt-6">
<label htmlFor={p.id} className="form-label mb-3">{p.numeroPregunta}. {p.texto}</label>
<label htmlFor={p.id} className="form-label">{p.numeroPregunta}. {p.texto}</label>
{p.tipo === 'seleccionUnica' && p.opciones && (
<div className="mb-2">
<div>
{p.opciones.map((op) => (
<div key={op} className="SINO">
<label className="mb-3">
<label>
<input type="radio" name={p.id} checked={respuestas[p.id] === op} onChange={() => setSingle(p.id, op)} /> {op}
</label>
</div>
@@ -361,7 +316,7 @@ export default function CuestionarioResponsbale2({
)}
{p.tipo === 'texto' && (
<div>
<textarea id={p.id} maxLength={p.limite} placeholder="Escribe tu respuesta aquí" value={String(respuestas[p.id] ?? '')} onChange={(e) => setSingle(p.id, e.target.value)} className="form-control mb-3 bg-transparent" />
<textarea id={p.id} maxLength={p.limite} placeholder="Escribe tu respuesta aquí" value={String(respuestas[p.id] ?? '')} onChange={(e) => setSingle(p.id, e.target.value)} className="form-control" />
</div>
)}
</div>
@@ -371,22 +326,22 @@ export default function CuestionarioResponsbale2({
return (
<div key={t.idTabla} className="mb-6 table-responsive">
{(index === 0 || t.numeroPregunta !== (sortedItems[index - 1] as Tabla).numeroPregunta) && (
<h5 className="mb-3 mt-4">{t.numeroPregunta}. {t.preguntaTabla}</h5>
<h3 className="mb-3">{t.numeroPregunta}. {t.preguntaTabla}</h3>
)}
{t.subPreguntaTabla && <h5 className="mb-3">{t.subPreguntaTabla}</h5>}
<table className="table table-bordered text-center">
<thead>
<tr>
<th className="bg-transparent"></th>
{(t.renglones[0].opciones || []).map((op) => <th key={op} className="bg-transparent">{op}</th>)}
<th></th>
{(t.renglones[0].opciones || []).map((op) => <th key={op}>{op}</th>)}
</tr>
</thead>
<tbody>
{t.renglones.map((r) => (
<tr key={r.idRenglon}>
<td className="bg-transparent">{r.textoRenglon}</td>
<td>{r.textoRenglon}</td>
{(r.opciones || []).map((op) => (
<td key={op} className="bg-transparent">
<td key={op}>
<input type="radio" name={`tabla-${t.idTabla}-renglon-${r.idRenglon}`} value={op} checked={((respuestas[String(t.idTabla)] as Record<number, string | null>) || {})[r.idRenglon] === op} onChange={() => setTableAnswer(t.idTabla, r.idRenglon, op)} />
</td>
))}
+72 -223
View File
@@ -1,19 +1,12 @@
"use client";
import { useEffect, useState } from "react";
import React, { useEffect, useState } from "react";
import { axiosInstance } from "@/api/config";
import type { AxiosError } from "axios";
import { Col, FormGroup, FormLabel, InputGroup } from "react-bootstrap";
import { FaRegCalendar, FaUpload } from "react-icons/fa6";
import { Button, Col, Form, FormGroup, FormLabel, InputGroup } from "react-bootstrap";
import { FaRegCalendarAlt, FaSchool, FaUpload } from "react-icons/fa";
import DatePicker from "react-datepicker";
import axios from "axios";
import Image from "next/image";
import Swal from "sweetalert2";
import { registerLocale } from "react-datepicker";
import { es } from "date-fns/locale/es";
registerLocale("es", es);
import { PiDotFill } from "react-icons/pi";
type Responsable = {
idUsuario?: number;
@@ -67,16 +60,6 @@ export default function NuevoServicio({
const [minDate2, setMinDate2] = useState<Date>(new Date());
const [programa, setPrograma] = useState<Programa>({});
const handleFechaInicioChange = (date: Date | null) => {
// setFechaInicio(date || new Date());
if (date) setFechaInicio(date);
}
const handleFechaFinChange = (date: Date | null) => {
// setFechaFin(date || new Date());
if (date) setFechaFin(date);
}
const resetar = () => {
setCorreo("");
setProfesor("");
@@ -88,17 +71,6 @@ export default function NuevoServicio({
setPrograma({});
};
const confirmarServicioS = () => {
Swal.fire({
title: 'Warning',
text: '¿Esta seguro(a) de querer Pre-Registrar a este alumno?',
icon: 'warning',
confirmButtonText: 'Confirmar',
cancelButtonText: 'Cancelar',
})
}
// Validacion para mostrar el boton
const mostrarBoton = () => {
if (!alumno.idUsuario) return true;
@@ -114,29 +86,18 @@ export default function NuevoServicio({
updateIsLoading(true);
try {
const headers = responsable?.token ? { Authorization: `Bearer ${responsable.token}` } : undefined;
// Enviar headers como tercer parámetro (config), no como body
const res = await axiosInstance.post<Alumno>(`/usuario/escolares/${numeroCuenta}`);
const res = await axiosInstance.get<Alumno>(`/usuario/escolares?numeroCuenta=${encodeURIComponent(numeroCuenta)}`, { headers });
resetar();
setAlumno(res.data);
obtenerProgramas();
updateIsLoading(false);
} catch (err: unknown) {
setNumeroCuenta("");
resetar();
updateIsLoading(false);
const axiosErr = err as AxiosError<any>;
const mensaje = axiosErr?.response?.data?.message || 'Ocurrio un error inesperado';
console.log("mensaje de error", mensaje)
Swal.fire({
icon: 'warning',
title: 'Aviso',
text: mensaje,
})
// const axiosErr = err as AxiosError;
// if (axiosErr?.response?.data) imprimirError(axiosErr.response.data as object);
// else if (err instanceof Error) imprimirError(err.message);
// else imprimirError({ message: "Error desconocido" });
const axiosErr = err as AxiosError;
if (axiosErr?.response?.data) imprimirError(axiosErr.response.data as object);
else if (err instanceof Error) imprimirError(err.message);
else imprimirError({ message: "Error desconocido" });
}
};
@@ -153,76 +114,35 @@ export default function NuevoServicio({
correo,
programaInterno,
profesor,
fechaInicio: formatDate(fechaInicio),
fechaFin: formatDate(fechaFin),
fechaInicio: fechaInicio.toISOString(),
fechaFin: fechaFin.toISOString(),
} as const;
formData.append("alumno", JSON.stringify(data));
if (!file) {
imprimirError({ message: 'No hay archivo seleccionado' });
return;
}
// Para verificar que se envie el archivo
console.log("Archivo a enviar:", file);
//if (file)
formData.append("cartaAceptacion", file, file.name);
if (file) formData.append("cartaAceptacion", file, file.name);
updateIsLoading(true);
try {
const headers = responsable?.tokenArchivo ? { Authorization: `Bearer ${responsable.tokenArchivo}` }: {};
// Falta poner la variable de entorno
const res = await axios.post(`${process.env.NEXT_PUBLIC_API_URL}/servicio/nuevo`, formData, {
headers: {
...(responsable?.tokenArchivo && {
Authorization: `Bearer ${responsable.tokenArchivo}`,
}),
},
});
const headers = responsable?.tokenArchivo ? { Authorization: `Bearer ${responsable.tokenArchivo}`, 'Content-Type': 'multipart/form-data' } : { 'Content-Type': 'multipart/form-data' };
const res = await axiosInstance.post("/servicio/nuevo", formData, { headers });
setNumeroCuenta("");
updateIsLoading(false);
Swal.fire('Exito', 'El alumno se registro en el servicio social', 'success')
// imprimirMensaje(res.data?.message || "Registrado");
resetar();
updateIsLoading(false);
imprimirMensaje(res.data?.message || "Registrado");
} catch (err: unknown) {
updateIsLoading(false);
const axiosErr = err as AxiosError<any>;
const mensaje = axiosErr?.response?.data?.message || 'No se pudo registrar al alumno';
Swal.fire({
icon: 'error',
title: 'Error',
text: mensaje,
})
// const axiosErr = err as AxiosError;
// if (axiosErr?.response?.data) imprimirError(axiosErr.response.data as object);
// else if (err instanceof Error) imprimirError(err.message);
// else imprimirError({ message: "Error desconocido" });
const axiosErr = err as AxiosError;
if (axiosErr?.response?.data) imprimirError(axiosErr.response.data as object);
else if (err instanceof Error) imprimirError(err.message);
else imprimirError({ message: "Error desconocido" });
}
};
const obtenerProgramas = async () => {
try {
const headers = responsable?.token ? { Authorization: `Bearer ${responsable.token}` } : undefined;
const res = await axiosInstance.get(`/programa/programas_responsable/${responsable.idUsuario}`, { headers });
// Normalizar la respuesta: soportar varias formas comunes:
// - res.data = Array
// - res.data = { data: Array }
// - res.data = { programas: Array }
const payload = res?.data ?? res;
console.log("Esta es la info que traen los programas", payload);
if (Array.isArray(payload)) {
setProgramas(payload as Programa[]);
} else if (payload && Array.isArray((payload as any).data)) {
setProgramas((payload as any).data as Programa[]);
} else if (payload && Array.isArray((payload as any).programas)) {
setProgramas((payload as any).programas as Programa[]);
} else {
console.warn('Respuesta inesperada al obtener programas, se usará lista vacía', payload);
setProgramas([]);
}
const res = await axiosInstance.get<Programa[]>(`/programa/programas_responsable?idUsuario=${responsable.idUsuario}`, { headers });
setProgramas(res.data);
} catch (err: unknown) {
const axiosErr = err as AxiosError;
if (axiosErr?.response?.data) imprimirError(axiosErr.response.data as object);
@@ -254,135 +174,86 @@ export default function NuevoServicio({
md.setDate(md.getDate() - 16);
setMinDate(md);
updateFechas();
//obtenerProgramas();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [responsable.idTipoUsuario]);
// Fetch programas when responsable id is available
useEffect(() => {
if (responsable?.idUsuario) {
obtenerProgramas();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [responsable?.idUsuario]);
}, []);
useEffect(() => {
updateFechas();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fechaInicio]);
const formatDate = (date: Date) => {
return date.toISOString().split("T")[0];
};
return (
<div>
<div className="mb-4">
<label className="form-label fw-bolder">Número de Cuenta</label>
<div className="d-flex">
<label className="form-label">Número de Cuenta</label>
<div className="d-flex gap-2">
<InputGroup.Text>
<Image src="/image/numCuenta.svg" width={24} height={24} alt="Icono de escuela" />
<FaSchool />
</InputGroup.Text>
<input
type="text"
placeholder="Número de Cuenta"
maxLength={9}
value={numeroCuenta ?? ''}
value={numeroCuenta}
onChange={(e) => setNumeroCuenta(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && buscarAlumno()}
className="form-control"
/>
<button type="button" className="" onClick={() => buscarAlumno()}>Buscar</button>
<button className="" onClick={() => buscarAlumno()}>Buscar</button>
</div>
</div>
{alumno.nombre && (
<div className="mb-3">
<label className="form-label fw-bolder">Nombre</label>
<p className="form-control">{alumno.nombre}</p>
{/* <input className="form-control" value={alumno.nombre} disabled/> */}
</div>
)}
{alumno.carrera && (
<div className="mb-3">
<label className="form-label fw-bolder">Carrera</label>
{/* <input className="form-control" value={alumno.carrera ? String(alumno.carrera) : ''} disabled/> */}
<p className="form-control">{alumno.carrera ? String(alumno.carrera) : ''}</p>
</div>
)}
{alumno.creditos && (
<div className="mb-3">
<label className="form-label fw-bolder">Créditos</label>
{/* <input className="form-control" value={alumno.creditos ? parseInt(String(alumno.creditos)) : '' + '%'} disabled/> */}
<p className="form-control">{alumno.creditos ? parseInt(String(alumno.creditos)) : ''}{alumno.creditos ? '%' : ''}</p>
</div>
)}
<div className="mb-3">
<label className="form-label fw-bolder">Seleccione programa</label>
<select className="form-select"
value={programa.idPrograma ?? ''}
onChange={(e) => {
const prog = programas.find(p => p.idPrograma === Number(e.target.value));
setPrograma(prog ?? {})
}}
>
<option value={''} disabled>Seleccionar</option>
{programas.map(p => (
<option key={p.idPrograma} value={p.idPrograma}>
{p.programa}
</option>
))}
</select>
<label className="form-label">Nombre</label>
<p className="form-control">{alumno.nombre}</p>
</div>
{/* <div className="mb-3">
<label className="form-label fw-bolder">Seleccione programa</label>
<div className="mb-3">
<label className="form-label">Carrera</label>
<p className="form-control">{alumno.carrera ? String(alumno.carrera) : ''}</p>
</div>
<div className="mb-3">
<label className="form-label">Créditos</label>
<p className="form-control">{alumno.creditos ? parseInt(String(alumno.creditos)) : ''}{alumno.creditos ? '%' : ''}</p>
</div>
<div className="mb-3">
<label className="form-label">Seleccione programa</label>
<select className="form-select" value={JSON.stringify(programa) } onChange={(e) => { try { setPrograma(JSON.parse(e.target.value)); } catch { setPrograma({}); } }}>
<option value={JSON.stringify({})} disabled>Seleccionar</option>
{(!Array.isArray(programas) || programas.length === 0) && (
<option value={JSON.stringify({})} disabled>No hay programas disponibles</option>
)}
{Array.isArray(programas) && programas.length > 0 && programas.map((p, i) => (
{programas.map((p, i) => (
<option key={i} value={JSON.stringify(p)}>{p.programa}</option>
))}
</select>
</div> */}
</div>
{programa.institucion && (
<div className="mb-3">
<label className="form-label fw-bolder">Institución</label>
<p className="form-control">{programa.institucion}</p>
</div>
)}
<div className="mb-3">
<label className="form-label">Institución</label>
<p className="form-control">{programa.institucion}</p>
</div>
{programa.dependencia && (
<div className="mb-3">
<label className="form-label fw-bolder">Dependencia</label>
<p className="form-control">{programa.dependencia}</p>
</div>
)}
<div className="mb-3">
<label className="form-label">Dependencia</label>
<p className="form-control">{programa.dependencia}</p>
</div>
{programa.clavePrograma && (
<div className="mb-3">
<label className="form-label fw-bolder">Clave del programa</label>
<p className="form-control">{programa.clavePrograma}</p>
</div>
)}
<div className="mb-3">
<label className="form-label">Clave del programa</label>
<p className="form-control">{programa.clavePrograma}</p>
</div>
{programa.acatlan && (
<>
<div className="mb-3">
<label className="form-label fw-bolder">Programa interno</label>
<label className="form-label">Programa interno</label>
<input className="form-control" placeholder="Programa interno" value={programaInterno} onChange={(e) => setProgramaInterno(e.target.value)} />
</div>
<div className="mb-3">
<label className="form-label fw-bolder">Profesor/Responsable de Programa</label>
<label className="form-label">Profesor/Responsable de Programa</label>
<input className="form-control" placeholder="Profesor/Responsable de Programa" value={profesor} onChange={(e) => setProfesor(e.target.value)} />
</div>
</>
@@ -438,28 +309,19 @@ export default function NuevoServicio({
<Col>
<FormGroup>
<FormLabel className="fw-bolder">Fecha de inicio</FormLabel>
<FormLabel>Fecha de inicio</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendar />
<FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
selected={fechaInicio}
onChange={handleFechaInicioChange}
minDate={minDate}
dateFormat="dd-MM-yyyy"
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
showMonthDropdown
showYearDropdown
scrollableYearDropdown
yearDropdownItemNumber={15}
dropdownMode="select"
locale="es"
/>
</InputGroup>
</FormGroup>
@@ -467,36 +329,26 @@ export default function NuevoServicio({
<Col>
<FormGroup>
<FormLabel className="fw-bolder">Fecha de fin</FormLabel>
<FormLabel>Fecha de fin</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendar />
<FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
selected={fechaFin}
onChange={handleFechaFinChange}
// value={fechaFin.toISOString().substring(0,10)}
value={fechaFin.toISOString().substring(0,10)}
minDate={minDate2}
dateFormat="dd-MM-yyyy"
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
showMonthDropdown
showYearDropdown
scrollableYearDropdown
yearDropdownItemNumber={15}
dropdownMode="select"
locale="es"
/>
</InputGroup>
</FormGroup>
</Col>
{/*
<div className="mb-3">
<div className="mb-3">
<label className="form-label">Fecha de inicio</label>
<input type="date" className="form-control" value={fechaInicio.toISOString().substring(0,10)} min={minDate.toISOString().substring(0,10)} onChange={(e) => setFechaInicio(new Date(e.target.value))} />
</div>
@@ -507,25 +359,24 @@ export default function NuevoServicio({
<input type="date" className="form-control" value={fechaFin.toISOString().substring(0,10)} min={minDate2.toISOString().substring(0,10)} onChange={(e) => setFechaFin(new Date(e.target.value))} />
</div>
)}
*/}
<div className="mb-3">
<label className="form-label fw-bolder">Correo electrónico del Alumno</label>
<label className="form-label">Correo electrónico del Alumno</label>
<input type="email" className="form-control" placeholder="Email" value={correo} onChange={(e) => setCorreo(e.target.value)} />
</div>
<FormGroup className="mb-3">
<FormLabel className="fw-bolder">Carta de aceptación (en formato .PDF. No se aceptan fotos).</FormLabel>
<div className="border p-4 text-center rounded" style={{ cursor: "pointer" }} onClick={() => document.getElementById("pdfInput")?.click()}>
<FormLabel>Carta de aceptación (en formato .PDF. No se aceptan fotos).</FormLabel>
<div className="border p-4 text-center rounded" style={{ cursor: "pointer" }} onClick={() => document.getElementById("fileInput")?.click()}>
<FaUpload size={40} className="mb-2"/>
<p className="mb-1">
{file?.name || 'Arrastra aquí tu archivo o da click aquí para buscar'}
</p>
<p className="is-size-6">Tamaño máximo 20MB</p>
<p className="is-size-6">Tamaño máximo 200</p>
<p className="is-size-7">Si al momento de elegir un archivo este no se selecciona, haga click en cancelar en la ventana emergente e intente de nuevo.</p>
</div>
<input
id="pdfInput"
id="fileInput"
type="file"
style={{ display: 'none'}}
accept="application/pdf"
@@ -533,8 +384,7 @@ export default function NuevoServicio({
/>
</FormGroup>
{/*
<div className="mb-3">
<div className="mb-3">
<label className="form-label">Carta de aceptación (en formato .PDF. No se aceptan fotos).</label>
<input type="file" accept="application/pdf" className="form-control" onChange={(e) => onFileChange(e.target.files?.[0])} />
<div className="mt-2 text-muted">
@@ -543,10 +393,9 @@ export default function NuevoServicio({
<p>Si al momento de elegir un archivo este no se selecciona, haga click en cancelar en la ventana emergente e intente de nuevo.</p>
</div>
</div>
*/}
<div className="text-center mt-4 mb-3">
<button className="btn btn-success" disabled={mostrarBoton()} onClick={() => imprimirWarning('¿Esta seguro(a) de querer Pre-Registrar a este alumno?', preRegistrar)}>
<div className="has-text-centered mt-4">
<button disabled={mostrarBoton()} onClick={() => imprimirWarning('¿Esta seguro(a) de querer Pre-Registrar a este alumno?', preRegistrar)}>
Enviar archivo
</button>
</div>
@@ -1,12 +1,13 @@
import { useEffect, useState } from "react";
"use client";
import React, { useEffect, useState } from "react";
import { axiosInstance } from "@/api/config";
import type { AxiosError } from "axios";
import { ServicioSocialResponse } from "@/types/responses";
import ServicioSocialTabla from "../servicio-social-tabla";
import { Button, Col, FormControl, FormGroup, FormLabel, FormSelect, InputGroup, Row } from "react-bootstrap";
import { FaCircleInfo, FaUser } from "react-icons/fa6";
import Image from "next/image";
import { useRouter } from "next/navigation";
import { FaClipboardList, FaInfoCircle, FaSchool, FaUser } from "react-icons/fa";
import { Prev } from "react-bootstrap/esm/PageItem";
type Responsable = { idUsuario?: number; token?: string; idTipoUsuario?: number };
type StatusItem = { idStatus: number; status: string };
@@ -22,26 +23,14 @@ export default function TablaServiciosResponsable({ responsable, imprimirError }
const [total, setTotal] = useState(0);
const [data, setData] = useState<ServicioSocialResponse[]>([]);
const [status, setStatus] = useState<StatusItem[]>([]);
const [search, setSearch] = useState<{ numeroCuenta?: string; nombre?: string; idStatus?: string }>({ numeroCuenta: '', nombre: '', idStatus: '' });
const [search, setSearch] = useState<{ numeroCuenta?: string; nombre?: string; idStatus?: string }>({ idStatus: '' });
const [searchAnterior, setSearchAnterior] = useState<{ numeroCuenta?: string; nombre?: string; idStatus?: string }>({});
const [mounted, setMounted] = useState(false);
const router = useRouter();
const onPageChange = (p: number) => {
setPage(p);
obtenerServicios(p);
};
const onRowAction = (item: ServicioSocialResponse, action: string) => {
if (action === "carta_aceptacion") {
localStorage.setItem("idServicio", String(item.idServicio));
router.push("/responsable/carta_aceptacion");
} else if (action === "carta_termino") {
localStorage.setItem("idServicio", String(item.idServicio));
router.push("/responsable/carta_termino");
}
};
const obtenerServicios = async (pagina = page) => {
let query = "";
setIsLoading(true);
@@ -60,21 +49,14 @@ export default function TablaServiciosResponsable({ responsable, imprimirError }
if (search.nombre) query += `&nombre=${encodeURIComponent(search.nombre)}`;
if (search.numeroCuenta) query += `&numeroCuenta=${encodeURIComponent(search.numeroCuenta)}`;
const idUsuario = localStorage.getItem("idUsuario")
try {
console.log('Informacion en el componente hijo del responsable:', responsable);
console.log('Id del usuario responsable:', responsable.idUsuario);
const idUsuario1 = localStorage.getItem('idUsuario') ?? '';
const idUsuario = localStorage.getItem('idUsuario') ?? '';
console.log('idUsuario usado en la consulta componente hijo:', idUsuario);
console.log("Este es la optencion del id del usuario desde el local", idUsuario1)
//const headers = responsable?.token ? { Authorization: `Bearer ${responsable.token}` } : undefined;
//const res = await axiosInstance.get<{ serviciosResponsable: ServicioSocialResponse[]; count: number }>(`/servicio/servicios_responsable?idUsuario=${idUsuario}&pagina=${pagina}${query}`);
const res = await axiosInstance.get(`/servicio/servicios_responsable?idUsuario=${idUsuario1}&pagina=${pagina}${query}`);
//const res = await axiosInstance.get(`/servicio/servicios_responsable?idUsuario=${idUsuario1}&pagina=${pagina}${query}`);
const res = await axiosInstance.get<{ serviciosResponsable: ServicioSocialResponse[]; count: number }>(`/servicio/servicios_responsable?idUsuario=${idUsuario}&pagina=${pagina}${query}`);
setData(res.data.serviciosResponsable);
setTotal(res.data.count);
setIsLoading(false);
@@ -105,8 +87,6 @@ export default function TablaServiciosResponsable({ responsable, imprimirError }
useEffect(() => {
obtenerCatalogoStatus();
// mark as mounted to avoid rendering dynamic options during SSR hydration
setMounted(true);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@@ -119,7 +99,7 @@ export default function TablaServiciosResponsable({ responsable, imprimirError }
<FormLabel>Número de cuenta</FormLabel>
<InputGroup>
<InputGroup.Text className="rounded-4">
<Image src="/image/numCuenta.svg" width={24} height={24} alt="Icono de escuela" />
<FaSchool />
</InputGroup.Text>
<FormControl
type="text"
@@ -153,23 +133,21 @@ export default function TablaServiciosResponsable({ responsable, imprimirError }
</InputGroup>
</FormGroup>
</Col>
{/* llega hasta el 10 */}
{/* llega hasta el 10 */}
<Col md={3}>
<FormGroup>
<FormLabel>Status</FormLabel>
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaCircleInfo />
<FaInfoCircle />
</InputGroup.Text>
<FormSelect
suppressHydrationWarning
value={search.idStatus}
onChange={(e) => setSearch(Prev => ({ ...Prev, idStatus: e.target.value}))}
className="rounded-4"
onKeyDown={(e) => {if (e.key === 'Enter') obtenerServicios()}}
>
<option value="">Status</option>
{mounted && status.slice(0, 9).map((s) => (
{status.slice(0, 9).map((s) => (
<option key={s.idStatus} value={s.idStatus}>
{s.status}
</option>
@@ -180,13 +158,10 @@ export default function TablaServiciosResponsable({ responsable, imprimirError }
</Col>
<Col md={3} className="d-flex align-items-end">
<Button
type="submit"
<Button
type="button"
className="w-100 rounded-5"
disabled={isLoading}
onClick={() => obtenerServicios(1)}
>
{isLoading ? 'Buscando...' : 'Buscar'}
disabled={isLoading}>{isLoading ? 'Buscando...' : 'Buscar'}
</Button>
</Col>
</Row>
@@ -209,7 +184,6 @@ export default function TablaServiciosResponsable({ responsable, imprimirError }
columnasResponsable={true}
columnaCuestionario={true}
columnaCartaTermino={true}
onRowAction={onRowAction}
/>
</div>
);
+17 -93
View File
@@ -1,11 +1,8 @@
"use client";
import { useEffect, useState } from "react";
import { Form } from "react-bootstrap";
import { FaUpload } from "react-icons/fa6";
import { useRouter } from "next/navigation";
import axios from "axios";
import Swal from "sweetalert2";
import React, { useEffect, useState } from "react";
import { axiosInstance } from "@/api/config";
import type { AxiosError } from "axios";
type Responsable = { tokenArchivo?: string };
@@ -34,26 +31,6 @@ export default function UploadArchivo({
}: Props) {
const [file, setFile] = useState<File | null>(null);
const router = useRouter();
console.log("Responsable en upload archivo:", responsable);
const confirmarEnvioArchivo = () => {
Swal.fire({
title: `¿Estas seguro(a) de querer subir esta carta de ${tipoCarta}?`,
icon: "warning",
showCancelButton: true,
confirmButtonText: "Sí, actualizar",
cancelButtonText: "Cancelar",
confirmButtonColor: "#0d6efd",
cancelButtonColor: "#dc3545",
}).then((result) => {
if (result.isConfirmed) {
subir(); // 👈 aquí ejecutas la función
}
});
};
const subir = async () => {
if (!file) return;
const formData = new FormData();
@@ -61,39 +38,20 @@ export default function UploadArchivo({
formData.append('data', JSON.stringify(data));
formData.append(variableName, file, file.name);
console.log("Esta es la info que se mandara al backend:", formData);
updateIsLoading(true);
try {
// Cambiamos a la direccion de axios normal
const res = await axios.put(`${process.env.NEXT_PUBLIC_API_URL}/servicio/${path}`, formData, {
headers: {
Authorization: `Bearer ${responsable.tokenArchivo}`,
'Content-Type': 'multipart/form-data',
},
});
try { localStorage.removeItem('idServicio'); }
catch (error) {}
updateIsLoading(false);
Swal.fire({
title: 'Éxito',
text: 'Se subio el archivo correctamente',
icon: 'success',
})
//imprimirMensaje(res.data?.message || 'Archivo subido');
if (typeof window !== 'undefined') router.replace('/responsable') //window.location.href = '/responsable';
} catch (err) {
Swal.fire({
title: 'Error',
text: 'Error al enviar el archivo',
icon: 'error',
})
updateIsLoading(false);
// const axiosErr = err as AxiosError;
// if (axiosErr?.response?.data) imprimirError(axiosErr.response.data as object);
// else if (err instanceof Error) imprimirError(err.message);
// else imprimirError({ message: 'Error desconocido' });
const headers = responsable?.tokenArchivo ? { Authorization: `Bearer ${responsable.tokenArchivo}`, 'Content-Type': 'multipart/form-data' } : { 'Content-Type': 'multipart/form-data' };
const res = await axiosInstance.put(`/servicio/${path}`, formData, { headers });
try { localStorage.removeItem('idServicio'); } catch (error) {}
updateIsLoading(false);
imprimirMensaje(res.data?.message || 'Archivo subido');
if (typeof window !== 'undefined') window.location.href = '/responsable';
} catch (err: unknown) {
updateIsLoading(false);
const axiosErr = err as AxiosError;
if (axiosErr?.response?.data) imprimirError(axiosErr.response.data as object);
else if (err instanceof Error) imprimirError(err.message);
else imprimirError({ message: 'Error desconocido' });
}
};
@@ -107,54 +65,20 @@ export default function UploadArchivo({
return (
<div>
{/* <div className="mb-3">
<div className="mb-3">
<label className="form-label">Carta de {tipoCarta} (en formato .pdf, no se aceptan fotos).</label>
<input type="file" accept="application/pdf" className="form-control" onChange={(e) => setFile(e.target.files?.[0] ?? null)} />
<div className="mt-2 text-center">
<p className="h5">{file?.name || 'Arrastra aquí tu archivo o da click aquí para buscarlo.'}</p>
<p className="small">Tamaño máximo 20MB</p>
</div>
</div> */}
{/*
Falta corregir y arreglar
*/}
<div className="ph-5">
<Form.Group className="mb-3">
<Form.Label>
Carta de {tipoCarta} (en formato .pdf, no se aceptan fotos).
</Form.Label>
<div
className="border p-4 text-center rounded"
style={{ cursor: 'pointer' }}
onClick={() => document.getElementById('fileInput')?.click()}
>
<FaUpload size={40} className="mb-2"/>
<p className="mb-1">
{file?.name || 'Arrastra aquí tu archivo o da click aquí para buscar'}
</p>
<p className="is-size-6">Tamaño máximo 20MB</p>
<p className="is-size-7">Si al momento de elegir un archivo este no se selecciona, haga click en cancelar en la ventana emergente e intente de nuevo.
</p>
<input
type="file"
accept="application/pdf"
id="fileInput"
className="d-none"
onChange={(e) => setFile(e.target.files?.[0] ?? null)} />
</div>
</Form.Group>
</div>
<div className="text-center my-4">
<button className="btn btn-success" disabled={!file?.name} onClick={confirmarEnvioArchivo}>
<button className="btn btn-success" disabled={!file?.name} onClick={() => imprimirWarning(`¿Estas seguro(a) de querer subir esta carta de ${tipoCarta}?`, subir)}>
Enviar
</button>
</div>
</div>
);
}
+237 -355
View File
@@ -2,389 +2,271 @@
import { useEffect, useState } from "react";
import { axiosInstance } from "@/api/config";
import { ServicioSocialConCasoEspecial, ServicioSocialResponse } from "@/types/responses";
import {
ServicioSocialConCasoEspecial,
ServicioSocialResponse,
} from "@/types/responses";
import { useRouter } from "next/navigation";
interface Props {
data?: ServicioSocialResponse[];
total?: number;
onPageChange?: (newPage: number) => void;
columnaFechaInicio?: boolean;
columnaFechaFin?: boolean;
idTipoUsuario?: number; // 1=admin, 2=responsable, 3=alumno, 4=caso especial
columnasResponsable?: boolean;
onRowAction?: (row: ServicioSocialResponse, path: string) => void;
columnaCuestionario?: boolean;
columnaCartaTermino?: boolean;
columnaCuestionarioCompleto?: boolean;
columnaFechaRegistro?: boolean;
data?: ServicioSocialResponse[];
total?: number;
onPageChange?: (newPage: number) => void;
columnaFechaInicio?: boolean;
columnaFechaFin?: boolean;
idTipoUsuario?: number; // 1=admin, 2=responsable, 3=alumno, 4=caso especial
columnasResponsable?: boolean;
onRowAction?: (row: ServicioSocialResponse, path: string) => void;
columnaCuestionario?: boolean;
columnaCartaTermino?: boolean;
columnaCuestionarioCompleto?: boolean;
columnaFechaRegistro?: boolean;
}
export default function ServicioSocialTabla({
data,
total = 0,
onPageChange,
idTipoUsuario,
columnasResponsable = false,
columnaFechaInicio = false,
columnaFechaFin = false,
columnaCuestionario = false,
columnaCartaTermino = false,
columnaCuestionarioCompleto = false,
columnaFechaRegistro = false,
onRowAction,
data,
total = 0,
onPageChange,
idTipoUsuario,
columnasResponsable = false,
columnaFechaInicio = true,
columnaFechaFin = true,
columnaCuestionario = false,
columnaCartaTermino = false,
columnaCuestionarioCompleto = false,
columnaFechaRegistro = false,
onRowAction,
}: Props) {
const [info, setInfo] = useState<ServicioSocialResponse[]>(data || []);
const [loading, setLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [info, setInfo] = useState<ServicioSocialResponse[]>(data || []);
const [loading, setLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const router = useRouter();
const router = useRouter();
//const columnaFechaInicio = true;
//const columnaFechaFin = true;
// 🔹 Cargar datos del padre si existen
useEffect(() => {
if (data && data.length > 0) {
setInfo(data);
}
}, [data]);
// 🔹 Paginación simple
function handlePrev() {
if (currentPage > 1) {
const np = currentPage - 1;
setCurrentPage(np);
onPageChange?.(np);
}
// 🔹 Cargar datos del padre si existen
useEffect(() => {
if (data && data.length > 0) {
setInfo(data);
}
}, [data]);
function handleNext() {
const lastPage = Math.max(1, Math.ceil(total / 10));
if (currentPage < lastPage) {
const np = currentPage + 1;
setCurrentPage(np);
onPageChange?.(np);
}
// 🔹 Paginación simple
function handlePrev() {
if (currentPage > 1) {
const np = currentPage - 1;
setCurrentPage(np);
onPageChange?.(np);
}
const servicioSelectedResponsable = (row: ServicioSocialResponse | ServicioSocialConCasoEspecial) => {
if (!row) return;
if (idTipoUsuario === 2) {
console.log("Responsable seleccionó un servicio social:", row);
if (row.idServicio) {
localStorage.setItem("idServicio", String(row.idServicio));
}
router.push("/responsable/cuestionario");
}
}
const servicioSelectedResponsableCartaTermino = (row: ServicioSocialResponse | ServicioSocialConCasoEspecial) => {
if (!row) return;
if (idTipoUsuario === 2) {
console.log("Responsable seleccionó un servicio social para carta de termino:", row);
if (row.idServicio) {
localStorage.setItem("idServicio", String(row.idServicio));
}
router.push("/responsable/carta_termino");
}
}
const servicioSelected = (row: ServicioSocialResponse | ServicioSocialConCasoEspecial) => {
if (!row) return;
if (idTipoUsuario === 1) {
if (row.idServicio) {
localStorage.setItem("idServicio", String(row.idServicio));
}
// esta mal esta logica
if ("idCasoEspecial" in row && row.idCasoEspecial) {
localStorage.setItem("idCasoEspecial", String(row.idCasoEspecial));
}
if ( row.idServicio ) {
router.push("/administrador/servicio");
} else if ("idCasoEspecial" in row && row.idCasoEspecial) {
router.push('/administrador/casos_especiales/caso_especial')
}
// Hacer validacion para saber que id fue y redireccionar
//router.push("/administrador/servicio");
//router.push('/administrador/casos_especiales/caso_especial')
} else if (idTipoUsuario === 2) {
servicioSelectedResponsable(row);
} else if (
row.idServicio ||
("idCasoEspecial" in row && row.idCasoEspecial)
) {
console.log("Limpiando selección (no admin)");
}
}
/*
const servicioSelected = (row: ServicioSocialResponse | ServicioSocialConCasoEspecial) => {
if (!row) return;
if (idTipoUsuario === 1) {
if (row.idServicio) {
localStorage.setItem("idServicio", String(row.idServicio));
}
// ✅ forma segura sin any
if ("idCasoEspecial" in row && row.idCasoEspecial) {
localStorage.setItem("idCasoEspecial", String(row.idCasoEspecial));
}
router.push("/administrador/servicio");
} else if (
row.idServicio ||
("idCasoEspecial" in row && row.idCasoEspecial)
) {
console.log("Limpiando selección (no admin)");
}
};
// Codigo original
const servicioSelected = (row: ServicioSocialResponse | ServicioSocialConCasoEspecial) => {
if (!row) return;
function handleNext() {
const lastPage = Math.max(1, Math.ceil(total / 10));
if (currentPage < lastPage) {
const np = currentPage + 1;
setCurrentPage(np);
onPageChange?.(np);
}
}
if (idTipoUsuario === 1) {
const servicioSelected = (
row: ServicioSocialResponse | ServicioSocialConCasoEspecial
) => {
if (!row) return;
if (row.idServicio) {
localStorage.setItem("idServicio", String(row.idServicio));
}
if ((row as any).idCasoEspecial) {
localStorage.setItem("idCasoEspecial", String((row as any).idCasoEspecial));
}
if (idTipoUsuario === 1) {
if (row.idServicio) {
localStorage.setItem("idServicio", String(row.idServicio));
}
// Redirigir a la vista de detalle
router.push("/administrador/servicio");
} else if (row.idServicio || (row as any).idCasoEspecial) {
console.log("Limpiando selección (no admin)");
if ("idCasoEspecial" in row && row.idCasoEspecial) {
localStorage.setItem("idCasoEspecial", String(row.idCasoEspecial));
}
}
};
router.push("/administrador/servicio");
} else if (
row.idServicio ||
("idCasoEspecial" in row && row.idCasoEspecial)
) {
console.log("Limpiando selección (no admin)");
}
};
*/
return (
<section>
{loading ? (
<div>Cargando...</div>
) : (
<>
<div className="table-responsive">
<table className="table table-striped table-fixed">
<thead>
<tr>
{columnaFechaRegistro && <th className="text-center align-middle">Fecha Registro</th>}
<th className="text-center align-middle">Número de Cuenta</th>
<th className="text-center align-middle">Nombre</th>
<th className="text-center align-middle">Carrera</th>
{columnaFechaInicio && <th className="text-center align-middle">Fecha Inicio</th>}
{columnaFechaFin && <th className="text-center align-middle">Fecha Fin</th>}
<th className="text-center align-middle">Status</th>
{columnaCuestionarioCompleto && <th className="text-center align-middle">Cuestionario Completo</th>}
{columnaCartaTermino && <th className="text-center align-middle">Carta Termino</th>}
{columnaCuestionario && <th className="text-center align-middle">Cuestionario</th>}
</tr>
</thead>
<tbody>
{info.map((item, index) => (
<tr
key={index}
style={{
cursor: idTipoUsuario === 1 ? "pointer" : "default",
}}
onClick={() => (
idTipoUsuario === 1 ? servicioSelected(item) : undefined
)}
//onClick={() => Falta corregir
//idTipoUsuario === 1 ? servicioSelected(item) : undefined
//}
>
{columnaFechaRegistro && <td className="text-center align-middle">{formatDate(item.createdAt)}</td>}
<td className="text-center align-middle">{item.usuario?.usuario}</td>
<td className="text-center align-middle">{item.usuario?.nombre}</td>
<td className="text-center align-middle">{item.carrera?.carrera}</td>
{/*
Poner si quieres hacer condicional para no mostrar infromacion
? formatDate(item.fechaInicio) : "-" */}
{columnaFechaInicio && (
<td className="text-center align-middle">{formatDate(item.fechaInicio)}</td>
)}
{columnaFechaFin && <td className="text-center align-middle">{formatDate(item.fechaFin)}</td>}
<td className="text-center align-middle">
{columnasResponsable && item.status?.idStatus === 7 ? (
<button
className="badge bg-danger border-0"
onClick={() =>
onRowAction?.(item, "carta_aceptacion")
}
>
Carta Aceptación Rechazada
</button>
) : (
<span
className={`badge bg-${statusClass(
item.status?.idStatus
)}`}
>
{item.status?.status}
</span>
)}
</td>
{columnaCartaTermino && (
<td className="text-center align-middle"
style={{ cursor: (idTipoUsuario === 2 && (item.status?.idStatus === 4 || item.status?.idStatus === 8) && !item.cartaTermino) ? 'pointer' : 'default'}}
onClick={() => {
if (idTipoUsuario === 2 && item.status?.idStatus === 8) {
onRowAction?.(item, "carta_termino");
} else if (idTipoUsuario === 2 && item.status?.idStatus === 4 && !item.cartaTermino) {
servicioSelectedResponsableCartaTermino(item);
}
}}
>
{item.status?.idStatus === 8 ? (
<button
className="badge bg-danger border-0"
onClick={(e) => {
e.stopPropagation();
onRowAction?.(item, "carta_termino");
}}
>
Carta Termino Rechazada
</button>
) : item.status?.idStatus === 4 ? (
item.cartaTermino ? (
<span className="badge"></span>
) : (
<span className="badge text-morado bg-light-morado">Carta Termino</span>
)
) : (
<span className="badge text-secondary"> </span>
)}
</td>
)}
{columnaCuestionarioCompleto && (
<td className="text-center align-middle"
>{item.cuestionarioCompletado ? (
<span className="badge bg-success">Completado</span>
) : (
<span className="badge text-morado bg-light-morado">No Completado</span>
)}</td>
)}
{columnaCuestionario && (
<td className="text-center align-middle"
style={{ cursor: (idTipoUsuario === 2 && item.status?.idStatus === 4 && !item.cuestionarioCompletado) ? 'pointer' : 'default'}}
onClick={() => (
(idTipoUsuario === 2 && item.status?.idStatus === 4 && !item.cuestionarioCompletado) ? servicioSelectedResponsable(item) : undefined
)}
>
{item.status?.idStatus === 4 ? (
item.cuestionarioCompletado ? (
<span className="badge"></span>
) : (
<span className="badge text-morado bg-light-morado">Cuestionario</span>
)
) : (
<span className="badge text-secondary"> </span>
)}
</td>
)}
</tr>
))}
{info.length === 0 && (
<tr>
<td colSpan={7} className="text-center">
No hay registros
</td>
</tr>
)}
</tbody>
</table>
</div>
<div className="d-flex justify-content-between align-items-center mt-2">
<div>Total: {total}</div>
<div>
<button
className="btn btn-sm btn-outline-primary me-2"
onClick={handlePrev}
disabled={currentPage <= 1}
>
Anterior
</button>
<span>Página {currentPage}</span>
<button
className="btn btn-sm btn-outline-primary ms-2"
onClick={handleNext}
disabled={
currentPage >= Math.max(1, Math.ceil(total / 10))
return (
<section>
{loading ? (
<div>Cargando...</div>
) : (
<>
<div className="table-responsive">
<table className="table table-striped">
<thead>
<tr>
{columnaFechaRegistro && <th>Fecha Registro</th>}
<th>Número de Cuenta</th>
<th>Nombre</th>
<th>Carrera</th>
{columnaFechaInicio && <th>Fecha Inicio</th>}
{columnaFechaFin && <th>Fecha Fin</th>}
<th>Status</th>
{columnaCuestionarioCompleto && (
<th>Cuestionario Completo</th>
)}
{columnaCartaTermino && <th>Carta Termino</th>}
{columnaCuestionario && <th>Cuestionario</th>}
</tr>
</thead>
<tbody>
{info.map((item, index) => (
<tr
key={index}
style={{
cursor: idTipoUsuario === 1 ? "pointer" : "default",
}}
onClick={() =>
idTipoUsuario === 1 ? servicioSelected(item) : undefined
}
>
Siguiente
</button>
</div>
</div>
</>
)}
</section>
);
}
>
{/* Si el padre pidió mostrar Fecha Registro, la mostramos aquí (createdAt) */}
{columnaFechaRegistro && (
<td>{formatDate((item as any).createdAt)}</td>
)}
<td>{item.Usuario?.usuario}</td>
<td>{item.Usuario?.nombre}</td>
<td>{item.Carrera?.carrera}</td>
{/* Usar las propiedades en camelCase que vienen en tu JSON */}
{columnaFechaInicio && (
<td>{formatDate((item as any).fechaInicio)}</td>
)}
{columnaFechaFin && (
<td>{formatDate((item as any).fechaFin)}</td>
)}
<td>
{columnasResponsable && item.Status?.idStatus === 7 ? (
<button
className="btn btn-sm btn-danger"
onClick={() =>
onRowAction?.(item, "carta_aceptacion")
}
>
Carta Aceptación Rechazada
</button>
) : (
<span
className={`badge bg-${statusClass(
item.Status?.idStatus
)}`}
>
{item.Status?.status}
</span>
)}
</td>
{columnaCartaTermino && (
<td>
{item.cartaTermino ? (
<span className="badge bg-success">Completado</span>
) : (
<span className="badge bg-danger">No Completado</span>
)}
</td>
)}
{columnaCuestionarioCompleto && (
<td>
{item.cuestionarioCompletado ? (
<span className="badge bg-success">Completado</span>
) : (
<span className="badge bg-danger">No Completado</span>
)}
</td>
)}
{columnaCuestionario && (
<td>
{item.cuestionarioCompletado ? (
<span className="badge bg-success">Completado</span>
) : (
<span className="badge bg-danger">No Completado</span>
)}
</td>
)}
</tr>
))}
{info.length === 0 && (
<tr>
<td colSpan={7} className="text-center">
No hay registros
</td>
</tr>
)}
</tbody>
</table>
</div>
<div className="d-flex justify-content-between align-items-center mt-2">
<div>Total: {total}</div>
<div>
<button
className="btn btn-sm btn-outline-primary me-2"
onClick={handlePrev}
disabled={currentPage <= 1}
>
Anterior
</button>
<span>Página {currentPage}</span>
<button
className="btn btn-sm btn-outline-primary ms-2"
onClick={handleNext}
disabled={currentPage >= Math.max(1, Math.ceil(total / 10))}
>
Siguiente
</button>
</div>
</div>
</>
)}
</section>
);
}
function formatDate(value?: string | null): string {
if (!value) return "";
if (!value) return "";
// Si ya viene como fecha ISO, forzamos a hora local sin modificar el día
const d = new Date(value.includes("T") ? value : `${value}T00:00:00`);
const d = new Date(value.includes("T") ? value : `${value}T00:00:00`);
if (isNaN(d.getTime())) {
// console.warn("⚠️ Fecha inválida:", value);
return "";
}
if (isNaN(d.getTime())) {
console.warn("⚠️ Fecha inválida:", value);
return "";
}
const day = String(d.getDate()).padStart(2, "0");
const month = String(d.getMonth() + 1).padStart(2, "0");
const year = d.getFullYear();
const day = String(d.getDate()).padStart(2, "0");
const month = String(d.getMonth() + 1).padStart(2, "0");
const year = d.getFullYear();
return `${day}/${month}/${year}`;
return `${day}/${month}/${year}`;
}
function statusClass(id?: number) {
switch (id) {
case 1:
return "dark";
case 2:
return "info";
case 3:
return "warning";
case 4:
return "primary";
case 5:
case 6:
return "success";
case 7:
case 8:
case 9:
case 10:
return "danger";
default:
return "secondary";
}
switch (id) {
case 1:
return "dark";
case 2:
return "info";
case 3:
return "warning";
case 4:
return "primary";
case 5:
case 6:
return "success";
case 7:
case 8:
case 9:
case 10:
return "danger";
default:
return "secondary";
}
}
-27
View File
@@ -1,27 +0,0 @@
// Funciones (SIEMPRE primero)
@import "bootstrap/scss/functions";
// VARIABLES PERSONALIZADAS
$primary: #5a2d82;
$secondary: #198754;
$border-radius: 1.5rem;
// Variables base (SIN maps ni dark)
@import "bootstrap/scss/variables";
// Mixins
@import "bootstrap/scss/mixins";
// Core CSS
@import "bootstrap/scss/root";
@import "bootstrap/scss/reboot";
@import "bootstrap/scss/type";
// 6Componentes que se usan
@import "bootstrap/scss/buttons";
@import "bootstrap/scss/forms";
@import "bootstrap/scss/grid";
@import "bootstrap/scss/utilities";
@import "bootstrap/scss/utilities/api";
+14 -36
View File
@@ -1,4 +1,4 @@
@import 'bootstrap/dist/css/bootstrap.min.css';
@import 'bootstrap/scss/functions';
$azul: #003d79;
$dorado: #bd8c01;
@@ -19,7 +19,8 @@ $font-family-sans-serif: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI'
'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
'Segoe UI Symbol';
@import 'bootstrap/scss/variables';
@import 'bootstrap/scss/variables-dark';
$custom-colors: (
'azul': $azul,
@@ -64,7 +65,7 @@ $custom-colors: (
}
$theme-colors: if(variable-exists("theme-colors"), map-merge($theme-colors, $custom-colors), $custom-colors);
$theme-colors: map-merge($theme-colors, $custom-colors);
.form-registro {
.form-floating {
@@ -92,6 +93,9 @@ $theme-colors: if(variable-exists("theme-colors"), map-merge($theme-colors, $cus
}
}
@import 'bootstrap/scss/maps';
@import 'bootstrap/scss/mixins';
@import 'bootstrap/scss/bootstrap';
.aviso {
@@ -133,7 +137,7 @@ $theme-colors: if(variable-exists("theme-colors"), map-merge($theme-colors, $cus
height: 2px;
background-color: $azul;
@media (min-width: 768px) {
@include media-breakpoint-up(md) {
left: 10rem;
}
}
@@ -204,12 +208,12 @@ $theme-colors: if(variable-exists("theme-colors"), map-merge($theme-colors, $cus
}
.full-center {
/* Equivalent of Bootstrap utility classes without using @extend */
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
//flex-grow-1 d-flex flex-column justify-content-center align-items-center
@extend .flex-grow-1;
@extend .d-flex;
@extend .flex-column;
@extend .justify-content-center;
@extend .align-items-center;
}
.minNavbar {
@@ -264,29 +268,3 @@ $theme-colors: if(variable-exists("theme-colors"), map-merge($theme-colors, $cus
.imageFlip:hover {
transform: rotateY(360deg);
}
/* Utility classes for custom colors (restore bg-<name> / text-<name> used in JSX)
These are minimal helpers so we don't need to import Bootstrap SCSS utilities. */
.bg-azul { background-color: #003d79 !important; }
.text-azul { color: #003d79 !important; }
.bg-dorado { background-color: #bd8c01 !important; }
.text-dorado { color: #bd8c01 !important; }
.bg-morado { background-color: #714dd2 !important; }
.bg-light-morado { background-color: #d6c9f7 !important; }
.text-morado { color: #714dd2 !important; }
.bg-verde { background-color: #48c78e !important; }
.text-verde { color: #48c78e !important; }
.bg-red-guapa { background-color: #9e1d15 !important; }
.text-red-guapa { color: #9e1d15 !important; }
.bg-blue-guapa { background-color: #3d6cb4 !important; }
.text-blue-guapa { color: #3d6cb4 !important; }
.bg-yellow-guapa { background-color: #eca728 !important; }
.text-yellow-guapa { color: #eca728 !important; }
/* Restore common Bootstrap text utilities if overridden */
.text-white { color: #fff !important; }
.text-dark { color: #000 !important; }
.text-muted { color: #6c757d !important; }
.text-body { color: var(--bs-body-color, #212529) !important; }
.fw-bold { font-weight: 700 !important; }
-8
View File
@@ -1,8 +0,0 @@
import { axiosInstance } from "@/api/config"
import { CasoEspecial } from "@/types/responses"
export const getUserCasoEspecial = async (idCasoEspecial: number): Promise<CasoEspecial[]> => {
const res = await axiosInstance.get(`caso-especial/caso_especial/${idCasoEspecial}`)
return res.data
}
+5 -18
View File
@@ -5,12 +5,12 @@ export interface ServicioSocialResponse {
id?: number;
createdAt?: string;
fecha_creacion?: string | null;
fechaFin?: string | null;
fechaInicio?: string | null;
fecha_fin?: string | null;
fecha_inicio?: string | null;
fecha_registro?: string | null;
usuario?: Usuario;
carrera?: Carrera;
status?: Status;
Usuario?: Usuario;
Carrera?: Carrera;
Status?: Status;
cartaTermino?: boolean;
idCuestionarioPrograma?: number | null;
idCuestionarioPrograma2?: number | null;
@@ -57,17 +57,4 @@ export interface Responsables {
interface ServicioSocialConCasoEspecial extends ServicioSocialResponse {
idCasoEspecial?: number;
}
interface CasoEspecial {
idCasoEspecial: number;
correo?: string;
telefono?: string;
direccion?: string;
fechaInicio?: Date;
fechaFin?: Date;
fechaNacimiento?: Date;
motivo?: string;
dependencia?: string;
institucion?: string;
}
+5 -19
View File
@@ -1,11 +1,7 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -15,7 +11,7 @@
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"jsx": "preserve",
"incremental": true,
"plugins": [
{
@@ -23,19 +19,9 @@
}
],
"paths": {
"@/*": [
"./src/*"
]
"@/*": ["./src/*"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
],
"exclude": [
"node_modules"
]
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}