11 Commits

42 changed files with 2166 additions and 1481 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

+45 -43
View File
@@ -1,59 +1,61 @@
import axios from 'axios'; import axios from "axios";
// Crea una instancia base de Axios // Crea una instancia base de Axios
export const axiosInstance = axios.create({ export const axiosInstance = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000/api', // poner la url baseURL: process.env.NEXT_PUBLIC_API_URL || "http://localhost:3411", // poner la url
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
timeout: 10000, // tiempo máximo de espera (10 segundos) timeout: 10000, // tiempo máximo de espera (10 segundos)
}); });
// Interceptor para agregar el token automáticamente // Interceptor para agregar el token automáticamente
axiosInstance.interceptors.request.use( axiosInstance.interceptors.request.use(
(config) => { (config) => {
if (typeof window !== 'undefined') { if (typeof window !== "undefined") {
const token = localStorage.getItem('token'); const token = localStorage.getItem("token");
if (token) { if (token) {
// Múltiples formatos según lo que espere el backend // Múltiples formatos según lo que espere el backend
config.headers.Authorization = `Bearer ${token}`; config.headers.Authorization = `Bearer ${token}`;
//Quitar estas lienas
config.headers['token'] = token;
config.headers['x-access-token'] = token;
config.headers['token-v2'] = token;
}
} //Quitar estas lienas
return config; config.headers["token"] = token;
}, config.headers["x-access-token"] = token;
(error) => Promise.reject(error) config.headers["token-v2"] = token;
}
}
return config;
},
(error) => Promise.reject(error)
); );
// Interceptor para manejar respuestas y errores globales // Interceptor para manejar respuestas y errores globales
axiosInstance.interceptors.response.use( axiosInstance.interceptors.response.use(
(response) => response, (response) => response,
(error) => { (error) => {
if (error.response) { if (error.response) {
// Token inválido o sesión expirada // Token inválido o sesión expirada
if (error.response.status === 401) { if (error.response.status === 401) {
console.warn('Sesión expirada o token inválido.'); console.warn("Sesión expirada o token inválido.");
if (typeof window !== 'undefined') { if (typeof window !== "undefined") {
localStorage.clear(); localStorage.clear();
window.location.href = '/'; // redirige automáticamente window.location.href = "/"; // redirige automáticamente
}
} }
}
// Errores del servidor // Errores del servidor
console.error('Error en la respuesta del servidor:', error.response.data); console.error("Error en la respuesta del servidor:", error.response.data);
} else if (error.request) { } else if (error.request) {
// No hubo respuesta del servidor // No hubo respuesta del servidor
console.error('No se recibió respuesta del servidor.'); console.error("No se recibió respuesta del servidor.");
} else { } else {
// Error en la configuración de la petición // Error en la configuración de la petición
console.error('Error en la configuración de la solicitud:', error.message); console.error(
} "Error en la configuración de la solicitud:",
error.message
return Promise.reject(error); );
} }
return Promise.reject(error);
}
); );
@@ -1,12 +1,23 @@
'use client'
import TablaCasosEspeciales from "@/components/administrador/tabla-casos-especiales"; import TablaCasosEspeciales from "@/components/administrador/tabla-casos-especiales";
import BotonRegresar from "@/components/boton-regresar"; import BotonRegresar from "@/components/boton-regresar";
import { useEffect, useState } from "react";
export default function CasosEspeciales() { 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( return(
<section className="container px-2 pb-5"> <section className="container px-2 pb-5">
<BotonRegresar /> <BotonRegresar />
<TablaCasosEspeciales /> <TablaCasosEspeciales admin={admin ?? undefined}/>
</section> </section>
); );
} }
+24 -6
View File
@@ -1,20 +1,36 @@
'use client'
import Cuestionario from "@/components/administrador/cuestionario"; import Cuestionario from "@/components/administrador/cuestionario";
import GustavoBazPrada from "@/components/administrador/gustavo-baz-prada"; import GustavoBazPrada from "@/components/administrador/gustavo-baz-prada";
import Reporte from "@/components/administrador/reporte"; import Reporte from "@/components/administrador/reporte";
import BotonRegresar from "@/components/boton-regresar"; import BotonRegresar from "@/components/boton-regresar";
import { type } from "node:os";
import { useEffect } from "react";
export default function Registro() { export default function Registro() {
const years = [2020, 2021, 2022, 2023, 2024, 2025]; //const years = [2020, 2021, 2022, 2023, 2024, 2025];
const admin = { token: "mi_token_de_prueba" }; const admin = { token: "mi_token_de_prueba" };
const obtenerYears = () => {
const years1 = [];
const fechaActual = new Date();
for( let i = 2020; i <= fechaActual.getFullYear(); i++) {
years1.push(i)
}
return years1
}
//const updateIsLoading = (loading: boolean) => { const years = obtenerYears()
// console.log("Loading:", loading);
//}; useEffect(() => {
console.log("Estos son los datos de years", years)
console.log("Este es el tipo de datos de years", typeof(years))
}, [years]);
return ( return (
<section className="container px-2 pb-5"> <section className="container px-2 pb-5">
<BotonRegresar /> <BotonRegresar />
<h2 className="title mb-4">Reportes</h2> <h2 className="title mb-4 fw-bold">Reportes</h2>
<Cuestionario <Cuestionario
years={years} years={years}
admin={admin} admin={admin}
@@ -23,7 +39,9 @@ export default function Registro() {
<Reporte admin={admin}/> <Reporte admin={admin}/>
<GustavoBazPrada /> <GustavoBazPrada
years={years}
/>
</section> </section>
); );
} }
@@ -63,33 +63,35 @@ export default function Editar() {
}, []); }, []);
return ( return (
<section> <section className="container px-2 pb-5">
<BotonRegresar /> <div className="pt-4">
<BotonRegresar />
</div>
<EditarResponsable <EditarResponsable
responsable={data}
imprimirError={imprimirError}
imprimirMensaje={imprimirMensaje}
imprimirWarning={imprimirWarning}
updateIsLoading={updateIsLoading}
/>
<ReasignacionProgramas
responsable={data} responsable={data}
imprimirError={imprimirError} imprimirError={imprimirError}
imprimirMensaje={imprimirMensaje} imprimirMensaje={imprimirMensaje}
imprimirWarning={imprimirWarning} imprimirWarning={imprimirWarning}
updateIsLoading={updateIsLoading} updateIsLoading={updateIsLoading}
/> />
{/*
<ReasignacionProgramas <EditarResponsable
responsable={data} token={token ?? undefined}
imprimirError={imprimirError} imprimirError={imprimirError}
imprimirMensaje={imprimirMensaje} imprimirMensaje={imprimirMensaje}
imprimirWarning={imprimirWarning} imprimirWarning={imprimirWarning}
updateIsLoading={updateIsLoading} updateIsLoading={updateIsLoading}
/> />
{/* */}
<EditarResponsable
token={token ?? undefined}
imprimirError={imprimirError}
imprimirMensaje={imprimirMensaje}
imprimirWarning={imprimirWarning}
updateIsLoading={updateIsLoading}
/>
*/}
</section> </section>
); );
} }
@@ -46,7 +46,7 @@ export default function Servicio() {
}, []); }, []);
return ( return (
<section> <section className="container">
<BotonRegresar /> <BotonRegresar />
{/* <EditarServicio admin={}/> */} {/* <EditarServicio admin={}/> */}
<EditarServicio <EditarServicio
+6 -4
View File
@@ -239,7 +239,7 @@ export default function Servicio() {
<section className="container px-2 pb-6"> <section className="container px-2 pb-6">
<BotonRegresar /> <BotonRegresar />
<h3 className="title">{datos.Status?.status}</h3> <h3 className="container fw-bold title">{datos.Status?.status}</h3>
<InformacionServicio <InformacionServicio
datos={datos} datos={datos}
@@ -265,7 +265,7 @@ export default function Servicio() {
{datos.cartaTermino && ( {datos.cartaTermino && (
<Archivo <Archivo
title={datos.cartaTermino ? "carta de término" : ""} title={datos.cartaTermino ? "carta de termino" : ""}
datos={datos} datos={datos}
admin={admin} admin={admin}
imprimirMensaje={imprimirMensaje} imprimirMensaje={imprimirMensaje}
@@ -287,7 +287,7 @@ export default function Servicio() {
/> />
)} )}
<div> <div className='container'>
{datos.idCuestionarioPrograma || datos.idCuestionarioPrograma2 ? ( {datos.idCuestionarioPrograma || datos.idCuestionarioPrograma2 ? (
<p className="my-4"> <p className="my-4">
<strong>Cuenta con cuestionario de programa resuelto.</strong> <strong>Cuenta con cuestionario de programa resuelto.</strong>
@@ -373,7 +373,9 @@ export default function Servicio() {
*/} */}
<BotonRegresar /> <div className='container mt-4'>
<BotonRegresar />
</div>
{isLoading && ( {isLoading && (
<div className="loading-overlay"> <div className="loading-overlay">
+20 -2
View File
@@ -8,8 +8,26 @@ import FullCuestionario from '@/components/alumno/cuestionario2/full-cuestionari
export default function Page() { export default function Page() {
return ( return (
<div className='bg-light'> <div className='container bg-light'>
<BotonRegresar /> <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 /> <FullCuestionario />
</div> </div>
); );
+122 -27
View File
@@ -1,27 +1,107 @@
"use client" "use client"
import { axiosInstance } from "@/api/config";
import BarraProgreso from "@/components/alumno/barra-progreso"; import BarraProgreso from "@/components/alumno/barra-progreso";
import CompletarDatosPersonales from "@/components/alumno/completar-datos-personales"; import CompletarDatosPersonales from "@/components/alumno/completar-datos-personales";
import NavCues from "@/components/alumno/cuestionario/nav-cues"; import NavCues from "@/components/alumno/cuestionario/nav-cues";
import InformacinoServicio from "@/components/alumno/informacion-servicio"; import InformacinoServicio from "@/components/alumno/informacion-servicio";
import MensajeAlumno from "@/components/alumno/mensajes-alumno"; import MensajeAlumno from "@/components/alumno/mensajes-alumno";
import PreTermino from "@/components/alumno/pre-termino"; 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() { export default function Home() {
const [data, setData] = useState<InfoAlumnoExtendido>();
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)
setData(combinado);
console.log("Este es la infromacion combinada", data);
} catch (error) {
console.log("Error al hacer la peticion ", error)
}
}
useEffect(() => {
handleInfo();
}, []);
return ( return (
<div> <div>
<BarraProgreso idStatus={5}/> { data?.Status.idStatus && (
<CompletarDatosPersonales <BarraProgreso idStatus={data?.Status.idStatus}/>
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)}
/>
<InformacinoServicio <div className="my-5">
servicio={{ <MensajeAlumno Status={{ idStatus: 2 }} />
</div>
{/* servicio={{
Programa: { Programa: {
institucion: "UNAM", institucion: "UNAM",
dependencia: "Académicos", dependencia: "Académicos",
@@ -35,25 +115,40 @@ export default function Home() {
fechaInicio: new Date().toISOString(), fechaInicio: new Date().toISOString(),
fechaFin: new Date().toISOString(), fechaFin: new Date().toISOString(),
createdAt: new Date().toISOString(), createdAt: new Date().toISOString(),
}} }} */}
/>
{ data && (
<InformacinoServicio
servicio={data}
/>
)}
<MensajeAlumno Status={{ idStatus: 2 }} /> { data?.Status.idStatus === 2 && (
<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)}
/>
)}
<PreTermino { data?.Status.idStatus !== undefined && data?.Status.idStatus >= 4 && (
alumno={{ tokenArchivo: "token123" }} <PreTermino
servicio={{ idServicio: 123, informeGlobal: undefined }} alumno={{ tokenArchivo: "token123" }}
imprimirMensaje={(msg) => console.log(msg)} servicio={data}
imprimirWarning={(msg, callback) => { imprimirMensaje={(msg) => console.log(msg)}
if (confirm(msg)) callback(); imprimirWarning={(msg, callback) => {
}} if (confirm(msg)) callback();
imprimirError={(err) => console.error(err)} }}
obtenerServicio={() => console.log("obtener servicio")} imprimirError={(err) => console.error(err)}
updateIsLoading={(loading) => console.log("loading", loading)} obtenerServicio={() => console.log("obtener servicio")}
/> updateIsLoading={(loading) => console.log("loading", loading)}
/>
)}
</div> </div>
); );
} }
+2 -2
View File
@@ -25,10 +25,10 @@ export default function Nuevo() {
} }
return ( return (
<div> <div className="container">
<BotonRegresar /> <BotonRegresar />
<h2>Agregar un Servicio Social</h2> <h2 className="fw-bold">Agregar un Servicio Social</h2>
<CasoEspecialForm <CasoEspecialForm
imprimirError={imprimirError} imprimirError={imprimirError}
+2 -2
View File
@@ -49,8 +49,8 @@ export default function Page() {
return ( return (
<section className="container px-2 pb-6"> <section className="container px-2 pb-6">
<div className="pb-5 pt-6 mt-5 mb-4 border-b border-gray-200"> <div className="pb-4 pt-6 mt-5 mb-4 border-b border-gray-200">
<button onClick={() => router.push('/casoEspecial/nuevo')}>Nuevo Caso Especial</button> <button className="rounded-2" onClick={() => router.push('/casoEspecial/nuevo')}>Nuevo Caso Especial</button>
</div> </div>
{responsable.idTipoUsuario !== undefined && responsable.token ? ( {responsable.idTipoUsuario !== undefined && responsable.token ? (
+9
View File
@@ -3,6 +3,15 @@ import Header from "@/components/layout/header";
import "./globals.css"; import "./globals.css";
import Footer from "@/components/layout/footer"; import Footer from "@/components/layout/footer";
import "@/sass/bootstrap.scss" 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({ export default function RootLayout({
children, children,
+93 -69
View File
@@ -1,85 +1,109 @@
'use client' "use client";
import { axiosInstance } from "@/api/config"; import { axiosInstance } from "@/api/config";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import React from "react"; import React from "react";
export default function Home() { export default function Home() {
const router = useRouter(); const router = useRouter();
const handleOnSubmit = async (e: React.FormEvent<HTMLFormElement>) => { const handleOnSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault(); e.preventDefault();
const formData = new FormData(e.currentTarget); const formData = new FormData(e.currentTarget);
const object = Object.fromEntries(formData); const object = Object.fromEntries(formData);
try { try {
const res = await axiosInstance.post('/usuario/login', object); const res = await axiosInstance.post("/auth/login", object);
// Extraer datos del backend // Extraer datos del backend
const token = res?.data?.token ?? ''; const token = res?.data?.token ?? "";
const usuarioObj = res?.data?.Usuario ?? res?.data ?? {}; const usuarioObj = res?.data?.Usuario ?? res?.data ?? {};
const idUsuario = usuarioObj.idUsuario ?? ''; const idUsuario = usuarioObj.idUsuario ?? "";
const usuario = usuarioObj.usuario ?? ''; const usuario = usuarioObj.usuario ?? "";
const nombre = usuarioObj.nombre ?? ''; const nombre = usuarioObj.nombre ?? "";
const idTipoUsuario = Number(usuarioObj.TipoUsuario?.idTipoUsuario ?? 0); const idTipoUsuario = Number(usuarioObj.TipoUsuario?.idTipoUsuario ?? 0);
//const idTipoUsuario = Number((usuarioObj as any).TipoUsuario?.idTipoUsuario ?? 0); //const idTipoUsuario = Number((usuarioObj as any).TipoUsuario?.idTipoUsuario ?? 0);
// Guardar en localStorage
localStorage.setItem("token", String(token));
localStorage.setItem("idUsuario", String(idUsuario));
localStorage.setItem("usuario", String(usuario));
localStorage.setItem("nombre", String(nombre));
localStorage.setItem("idTipoUsuario", String(idTipoUsuario));
// Guardar en localStorage // Validar y redirigir según el idTipoUsuario
localStorage.setItem('token', String(token)); if (token && idUsuario && idTipoUsuario) {
localStorage.setItem('idUsuario', String(idUsuario)); switch (idTipoUsuario) {
localStorage.setItem('usuario', String(usuario)); case 1:
localStorage.setItem('nombre', String(nombre)); router.push("/administrador");
localStorage.setItem('idTipoUsuario', String(idTipoUsuario)); break;
case 2:
// Validar y redirigir según el idTipoUsuario router.push("/responsable");
if (token && idUsuario && idTipoUsuario) { break;
switch (idTipoUsuario) { case 3:
case 1: router.push("/alumno");
router.push('/administrador'); break;
break; case 4:
case 2: router.push("/casoEspecial");
router.push('/responsable'); break;
break; default:
case 3: console.warn(`Tipo de usuario desconocido: ${idTipoUsuario}`);
router.push('/alumno');
break;
case 4:
router.push('/casoEspecial');
break;
default:
console.warn(`Tipo de usuario desconocido: ${idTipoUsuario}`);
localStorage.clear();
break;
}
} else {
localStorage.clear();
console.error('Error: datos de usuario incompletos');
}
} catch (error) {
console.error('Error en el inicio de sesión:', error);
localStorage.clear(); localStorage.clear();
break;
} }
}; } else {
localStorage.clear();
console.error("Error: datos de usuario incompletos");
}
} catch (error) {
console.error("Error en el inicio de sesión:", error);
localStorage.clear();
}
};
return ( return (
<div className="d-flex justify-content-center align-items-center bg-light" style={{ minHeight: 'calc(100vh - 200px)' }}> <div
<form className="p-4 shadow rounded bg-white w-100" style={{ maxWidth: '400px' }} onSubmit={handleOnSubmit}> className="d-flex justify-content-center align-items-center bg-light"
<h2 className="text-center mb-4 fw-bold">IRIS</h2> style={{ minHeight: "calc(100vh - 200px)" }}
>
<form
className="p-4 shadow rounded bg-white w-100"
style={{ maxWidth: "400px" }}
onSubmit={handleOnSubmit}
>
<h2 className="text-center mb-4 fw-bold">IRIS</h2>
<div className="mb-3"> <div className="mb-3">
<label htmlFor="usuario" className="form-label">Usuario</label> <label htmlFor="usuario" className="form-label">
<input type="text" name="usuario" id="usuario" className="form-control" required /> Usuario
</div> </label>
<input
<div className="mb-4"> type="text"
<label htmlFor="password" className="form-label">Contraseña</label> name="usuario"
<input type="password" name="password" id="password" className="form-control" required /> id="usuario"
</div> className="form-control"
required
<div className="d-grid"> />
<button type="submit" className="btn btn-primary btn-lg">Iniciar Sesión</button>
</div>
</form>
</div> </div>
);
<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">
<button type="submit" className="btn btn-primary btn-lg">
Iniciar Sesión
</button>
</div>
</form>
</div>
);
} }
+1 -1
View File
@@ -110,7 +110,7 @@ export default function Page() {
</p> </p>
<p> <p>
<a href="https://www.acatlan.unam.mx/normatividad" target="_blank" rel="noreferrer"> www.acatlan.unam.mx/normatividad</a> <a href="https://www.acatlan.unam.mx/normatividad" target="_blank" rel="noreferrer" className="text-decoration-none text-morado"> www.acatlan.unam.mx/normatividad</a>
</p> </p>
</div> </div>
+1 -1
View File
@@ -81,7 +81,7 @@ export default function Page() {
</p> </p>
</div> </div>
<h2 className="title">Añadir Servicio Social</h2> <h2 className="title fw-bold mt-4 mb-4">Añadir Servicio Social</h2>
<NuevoServicio <NuevoServicio
responsable={responsable} responsable={responsable}
+1 -1
View File
@@ -85,7 +85,7 @@ export default function Page() {
</div> </div>
<div className="pb-3"> <div className="pb-3">
<button className="button is-info" onClick={() => router.push('/responsable/nuevo')}>Agregar alumno</button> <button className="button is-info rounded-2" onClick={() => router.push('/responsable/nuevo')}>Agregar alumno</button>
</div> </div>
{tabla} {tabla}
+18 -5
View File
@@ -145,13 +145,25 @@ export default function Archivo({
axiosInstance.put(`/servicio/rechazar_informe`, data); axiosInstance.put(`/servicio/rechazar_informe`, data);
return ( return (
<div className="mb-4"> <div className="container mb-4">
<div className="flex items-center"> <div className="flex items-center">
<h6 className="pr-4 text-lg">Ver {title}</h6> <h5 className="pr-4 text-lg">
Ver {title}
<div className="pr-4">
<a <a
className="btn btn-link btn-light" 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>
</h5>
{/*
<div className="pr-4">
<a
className="btn btn-link btn-light text-decoration-none"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
href={`https://drive.google.com/file/d/${archivo()}/view?usp=sharing`} href={`https://drive.google.com/file/d/${archivo()}/view?usp=sharing`}
@@ -159,6 +171,7 @@ export default function Archivo({
Ver Ver
</a> </a>
</div> </div>
*/}
{((datos.Status?.idStatus === 1 && title === "carta de aceptación") || {((datos.Status?.idStatus === 1 && title === "carta de aceptación") ||
(datos.Status?.idStatus === 5 && (datos.Status?.idStatus === 5 &&
@@ -92,49 +92,49 @@ export default function CancelarServicio({
}; };
return ( return (
<div className="mt-5 space-y-4"> <div className="container mt-5 space-y-4">
{/* Botón de Cancelar */} {/* 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 <button
disabled={!mensajeCancelar} disabled={datos.Status?.idStatus === 6 || datos.Status?.idStatus === 10}
onClick={() => onClick={updateCancelar}
imprimirWarning( className={`px-4 py-2 bg-danger rounded text-white${
"¿Seguro(a) que quiere cancelar este servicio?", datos.Status?.idStatus === 6 || datos.Status?.idStatus === 10
cancelarServicio
)
}
className={`px-4 py-2 rounded text-white ${
!mensajeCancelar
? "bg-gray-400 cursor-not-allowed" ? "bg-gray-400 cursor-not-allowed"
: "bg-blue-600 hover:bg-blue-700" : "bg-red-600 hover:bg-red-700"
}`} }`}
> >
Cancelar servicio Cancelar
</button> </button>
</div>
)} {/* Á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>
); );
} }
@@ -119,7 +119,7 @@ export default function ConfirmarServicio({
}; };
return ( return (
<div className="space-y-4"> <div className="container space-y-4">
{datos.Status?.idStatus === 5 && datos.Programa?.acatlan && ( {datos.Status?.idStatus === 5 && datos.Programa?.acatlan && (
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<input <input
+42 -15
View File
@@ -1,5 +1,6 @@
"use client" "use client"
import { axiosInstance } from "@/api/config"; import { axiosInstance } from "@/api/config";
import { error } from "console";
import { useState } from "react"; import { useState } from "react";
import { Button, Col, FormGroup, FormLabel, FormSelect, InputGroup } from "react-bootstrap"; import { Button, Col, FormGroup, FormLabel, FormSelect, InputGroup } from "react-bootstrap";
@@ -10,26 +11,50 @@ interface Props {
} }
export default function Cuestionario({ years, admin, updateIsLoading }: Props) { export default function Cuestionario({ years, admin, updateIsLoading }: Props) {
const [selectedCuestionario, setSelectedCuestionario] = useState(""); const [selectedCuestionario, setSelectedCuestionario] = useState({});
const [version, setVersion] = useState(""); const [version, setVersion] = useState("");
const [selectedYear, setSelectedYear] = useState(""); const [selectedYear, setSelectedYear] = useState("");
const downloadExcel = async () => { const downloadExcel = async () => {
try { try {
//updateIsLoading(true); //updateIsLoading(true);
const res = await axiosInstance.get(`/${selectedCuestionario}`, { //const res = await axiosInstance.get(`/cuestionario_alumno?year=${selectedCuestionario}&version=${version}`, {
const res = await axiosInstance.get(`/cuestionario_alumno`, {
params: { year: selectedYear, version }, params: { year: selectedYear, version },
responseType: "blob", 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`); //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);
// reset // reset
setSelectedYear(""); setSelectedYear("");
setSelectedCuestionario(""); setSelectedCuestionario("");
setVersion(""); setVersion("");
//updateIsLoading(false); //updateIsLoading(false);
} catch (err: unknown) { } catch (err: unknown) {
console.error("Error en descargar el formulario", err)
//updateIsLoading(false); //updateIsLoading(false);
// optional: handle error, e.g. console.error(err) // optional: handle error, e.g. console.error(err)
} }
@@ -42,9 +67,10 @@ export default function Cuestionario({ years, admin, updateIsLoading }: Props) {
<div> <div>
<Col> <Col>
<FormGroup> <FormGroup>
<FormLabel>Cuestionario:</FormLabel> <FormLabel className="fw-semibold">Cuestionario:</FormLabel>
<InputGroup> <InputGroup>
<FormSelect> <FormSelect onChange={(e) => setSelectedCuestionario(e.target.value)}>
<option value="">Seleccione un cuestionario:</option>
<option value="cuestionario_alumno">Cuestionario de Alumnos</option> <option value="cuestionario_alumno">Cuestionario de Alumnos</option>
<option value="cuestionario_programa">Cuestionario de Programas</option> <option value="cuestionario_programa">Cuestionario de Programas</option>
</FormSelect> </FormSelect>
@@ -54,9 +80,10 @@ export default function Cuestionario({ years, admin, updateIsLoading }: Props) {
<Col> <Col>
<FormGroup> <FormGroup>
<FormLabel>Version:</FormLabel> <FormLabel className="fw-semibold">Version:</FormLabel>
<InputGroup> <InputGroup>
<FormSelect> <FormSelect value={version} onChange={(e) => setVersion(e.target.value)}>
<option value="">Seleccione una version:</option>
<option value="v1">V1</option> <option value="v1">V1</option>
<option value="v2">V2</option> <option value="v2">V2</option>
</FormSelect> </FormSelect>
@@ -66,15 +93,15 @@ export default function Cuestionario({ years, admin, updateIsLoading }: Props) {
<Col> <Col>
<FormGroup> <FormGroup>
<FormLabel>Año:</FormLabel> <FormLabel className="fw-semibold">Año:</FormLabel>
<InputGroup> <InputGroup>
<FormSelect value={selectedYear} onChange={(e) => setSelectedYear(e.target.value)}> <FormSelect value={selectedYear} onChange={(e) => setSelectedYear(e.target.value)}>
<option value="">Seleccione un año:</option> <option value="">Seleccione un año:</option>
{years.map((y, i) => ( {years.map((y, i) => (
<option key={i} value={y}> <option key={i} value={y}>
{y} {y}
</option> </option>
))} ))}
</FormSelect> </FormSelect>
</InputGroup> </InputGroup>
</FormGroup> </FormGroup>
@@ -82,10 +109,10 @@ export default function Cuestionario({ years, admin, updateIsLoading }: Props) {
<div className="d-flex gap-2 mt-4 mb-3"> <div className="d-flex gap-2 mt-4 mb-3">
<Button <Button
disabled={!selectedYear || !selectedCuestionario || !version} //disabled={!selectedYear || !selectedCuestionario || !version}
onClick={downloadExcel} onClick={downloadExcel}
> >
Enviar archivo Descragar Excel
</Button> </Button>
</div> </div>
</div> </div>
@@ -100,62 +100,64 @@ export default function EditarResponsable({
return ( return (
<div className="space-y-4 mt-6"> <div className="sspace-y-4 mt-6">
<h3 className="text-2xl font-semibold mb-4">Editar información del responsable</h3> <h2 className="text-2xl mb-4 fw-semibold">Editar información del responsable</h2>
{/* 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>
{/* Correo */} {/* Nombre */}
<div> <div className="mb-3">
<label className="block mb-1 font-medium">Correo electrónico</label> <label className="form-label fw-semibold block mb-1 font-medium">Nombre</label>
<input <input
type="email" type="text"
placeholder={responsable.usuario} placeholder={responsable.nombre}
value={nuevo.correo || ""} value={nuevo.nombre || ""}
onChange={(e) => setNuevo({ ...nuevo, correo: e.target.value })} onChange={(e) => setNuevo({ ...nuevo, nombre: e.target.value })}
className="border rounded p-2 w-full" className="form-control border rounded p-2 w-full"
/> />
</div> </div>
{/* Botones */} {/* Correo */}
<div className="pt-5 flex gap-4"> <div>
<button <label className="form-label block mb-1 font-medium">Correo electrónico</label>
disabled={mostrarBoton()} <input
onClick={() => type="email"
imprimirWarning( placeholder={responsable.usuario}
"¿Seguro(a) que quiere actualizar la información de este usuario?", value={nuevo.correo || ""}
actualizar onChange={(e) => setNuevo({ ...nuevo, correo: e.target.value })}
) className="form-control border rounded p-2 w-full"
} />
className={`px-4 py-2 rounded text-white ${ </div>
mostrarBoton() ? "bg-gray-400 cursor-not-allowed" : "bg-blue-600 hover:bg-blue-700"
}`}
>
Guardar Cambios
</button>
<button {/* Botones */}
onClick={() => <div className="pt-5 flex gap-4">
imprimirWarning( <button
"¿Seguro(a) que quiere cambiar la contraseña para este usuario?", disabled={mostrarBoton()}
password onClick={() =>
) imprimirWarning(
} "¿Seguro(a) que quiere actualizar la información de este usuario?",
className="px-4 py-2 rounded bg-blue-500 hover:bg-blue-600 text-white" actualizar
> )
Cambiar contraseña }
</button> className={`mb-4 px-4 py-2 rounded text-white ${
</div> mostrarBoton() ? "bg-gray-400 cursor-not-allowed" : "bg-blue-600 hover:bg-blue-700"
}`}
>
Guardar Cambios
</button>
<button
onClick={() =>
imprimirWarning(
"¿Seguro(a) que quiere cambiar la contraseña para este usuario?",
password
)
}
className="ms-3 px-4 py-2 rounded bg-morado hover:bg-blue-600 text-white"
>
Cambiar contraseña
</button>
</div>
</div> </div>
); );
} }
+225 -132
View File
@@ -8,6 +8,8 @@ import validator from "validator";
import DatePicker from "react-datepicker"; import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css"; import "react-datepicker/dist/react-datepicker.css";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { Col, FormGroup, FormLabel, InputGroup } from "react-bootstrap";
import { FaRegCalendarAlt, FaUpload } from "react-icons/fa";
interface Servicio { interface Servicio {
Status: { idStatus?: number }; Status: { idStatus?: number };
@@ -87,7 +89,7 @@ export default function EditarServicio({
// Redirección si el estado no es válido // Redirección si el estado no es válido
if (data.Status.idStatus === 6 || data.Status.idStatus === 10) { if (data.Status.idStatus === 6 || data.Status.idStatus === 10) {
router.push("/admin/servicio"); router.push("/administrador/servicio");
} }
setFechaInicio(new Date(data.fechaInicio)); setFechaInicio(new Date(data.fechaInicio));
@@ -146,13 +148,18 @@ export default function EditarServicio({
const password = async () => { const password = async () => {
const data = { servicioid }; const data = { servicioid };
console.log("Id del servicio para nueva password", data);
console.log("Id servicio entrando a la info", data.servicioid)
try { try {
console.log("Si entro par mandar la solicitud")
updateIsLoading(true); updateIsLoading(true);
const res = await axiosInstance.put(`/usuario/new_password_alumno`, data); const res = await axiosInstance.put(`/usuario/new_password_alumno`, { idServicio: servicioid });
imprimirMensaje(res.data.message); imprimirMensaje(res.data.message);
updateIsLoading(false); updateIsLoading(false);
router.push("/admin"); router.push("/administrador");
} catch (err: unknown) { } catch (err: unknown) {
console.log("No esta mandando la solicitud")
updateIsLoading(false); updateIsLoading(false);
if (isAxiosError(err)) imprimirError(err.response?.data || err.message); if (isAxiosError(err)) imprimirError(err.response?.data || err.message);
else if (err instanceof Error) imprimirError(err.message); else if (err instanceof Error) imprimirError(err.message);
@@ -211,7 +218,7 @@ export default function EditarServicio({
useEffect(() => { useEffect(() => {
const id = Number(localStorage.getItem("idServicio")); const id = Number(localStorage.getItem("idServicio"));
console.log("ID Servicio para editar:", id); console.log("ID Servicio para editar:", id);
if (!id) router.push("/admin"); if (!id) router.push("/administrador");
else { else {
setServicioid(id); setServicioid(id);
console.log("idServicio seteado:", id); console.log("idServicio seteado:", id);
@@ -223,143 +230,229 @@ export default function EditarServicio({
return ( return (
<div className="space-y-4 mt-6"> <div className="space-y-4 mt-6">
<h3 className="text-2xl font-semibold mb-4"> <h2 className="fw-bold mb-4">
Editar información del alumno Editar información del alumno
</h3> </h2>
{/* Correo */} {/* Correo */}
<div> <div className="mb-3">
<label className="block font-medium mb-1"> <label className="form-label fw-semibold block font-medium mb-1">
Correo electrónico del alumno Correo electrónico del alumno
</label> </label>
<input <input
type="email" type="email"
placeholder={servicio.correo || "Correo"} placeholder={servicio.correo || "Correo"}
value={correo} value={correo}
onChange={(e) => setCorreo(e.target.value)} onChange={(e) => setCorreo(e.target.value)}
className="border rounded p-2 w-full" className="form-control 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> </div>
)}
{/* Teléfono */} {/* Fecha inicio */}
{servicio.telefono && ( <Col>
<div> <FormGroup>
<label className="block font-medium mb-1">Teléfono</label> <FormLabel>Fecha de inicio</FormLabel>
<input <InputGroup>
type="tel" <InputGroup.Text>
maxLength={10} <FaRegCalendarAlt />
placeholder={servicio.telefono} </InputGroup.Text>
value={telefono} <DatePicker
onChange={(e) => setTelefono(e.target.value)} onChange={(date: Date | null ) => {
className="border rounded p-2 w-full" if (date) setFechaInicio(date);
/> }}
placeholderText="Selecciona una fecha de inicio"
minDate={fechaInicio}
dateFormat={'dd-MM-yyyy'}
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
</InputGroup>
</FormGroup>
</Col>
<Col>
<FormGroup>
<FormLabel>Fecha de fin</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
onChange={(date: Date | null) => {
if (date) setFechaFin(date);
}}
placeholderText="Selecciona una fecha de fin"
dateFormat={'dd-MM-yyyy'}
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
</InputGroup>
</FormGroup>
</Col>
<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"
/>
</div> </div>
)}
{/* Fecha nacimiento */} {/* Fecha fin */}
{servicio.fechaNacimiento && ( <div className="mb-3">
<div> <label className="form-labeñ fw-semibold block font-medium mb-1">Fecha de fin</label>
<label className="block font-medium mb-1"> <DatePicker
Fecha de nacimiento selected={fechaInicio}
</label> onChange={(date: Date | null) => {
<DatePicker if (date) setFechaInicio(date);
selected={fechaFin} }}
onChange={(date: Date | null) => { minDate={new Date("2020-01-01")}
if (date) setFechaFin(date); className="border rounded p-2 w-full"
}} />
minDate={fechaInicio}
className="border rounded p-2 w-full"
/>
</div> </div>
)}
{/* Subir archivos */} {/* Dirección */}
<div className="space-y-2"> {servicio.direccion && (
<label className="block font-medium mb-1">Carta de aceptación</label> <div className="mb-3">
<input <label className="form-label fw-semibold block font-medium mb-1">Dirección</label>
type="file" <input
accept="application/pdf" type="text"
onChange={(e) => placeholder={servicio.direccion}
setCartaAceptacion(e.target.files ? e.target.files[0] : null) value={direccion}
} onChange={(e) => setDireccion(e.target.value)}
className="border p-2 rounded w-full" className="form-control border rounded p-2 w-full"
/> />
</div> </div>
)}
{/* Botones */} {/* Teléfono */}
<div className="mt-6 flex gap-4"> {servicio.telefono && (
<button <div className="mb-3">
disabled={mostrar()} <label className="form-label fw-semibold block font-medium mb-1">Teléfono</label>
onClick={() => <input
imprimirWarning( type="tel"
"¿Seguro(a) que quiere actualizar estos datos?", maxLength={10}
actualizar placeholder={servicio.telefono}
) value={telefono}
} onChange={(e) => setTelefono(e.target.value)}
className={`px-4 py-2 rounded text-white ${ className="form-control border rounded p-2 w-full"
mostrar() />
? "bg-gray-400 cursor-not-allowed" </div>
: "bg-blue-600 hover:bg-blue-700" )}
}`}
>
Actualizar Datos
</button>
<button {servicio.fechaNacimiento && (
disabled={ <Col>
servicio.Status.idStatus === 1 || servicio.Status.idStatus === 7 <FormGroup>
} <FormLabel>Fecha de nacimiento</FormLabel>
onClick={() => <InputGroup>
imprimirWarning( <InputGroup.Text>
"¿Seguro(a) que quieres cambiar/reenviar la contraseña de este alumno?",
password </InputGroup.Text>
) </InputGroup>
} </FormGroup>
className="px-4 py-2 rounded bg-blue-500 hover:bg-blue-600 text-white" </Col>
> )}
Cambiar/Reenviar contraseña
</button> {/* Fecha nacimiento */}
</div> {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) setFechaFin(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("application/pdf")?.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=".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={() =>
imprimirWarning(
"¿Seguro(a) que quiere actualizar estos datos?",
actualizar
)
}
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={() =>
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> </div>
); );
} }
@@ -33,6 +33,25 @@ 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);
//fileDownload(res.data, `${selectedYear}_gustavo_baz_prada.csv`); //fileDownload(res.data, `${selectedYear}_gustavo_baz_prada.csv`);
setSelectedYear(""); setSelectedYear("");
@@ -51,20 +70,20 @@ export default function GustavoBazPrada({
<Col> <Col>
<FormGroup> <FormGroup>
<FormLabel>Selecciona un año:</FormLabel> <FormLabel>Selecciona un año:</FormLabel>
<InputGroup> <InputGroup>
<FormSelect <FormSelect
value={selectedYear} value={selectedYear}
onChange={(e) => setSelectedYear(e.target.value)} onChange={(e) => setSelectedYear(e.target.value)}
> >
<option value="">Seleccione un año:</option> <option value="">Seleccione un año:</option>
{(years || []).map((y, i) => ( {(years || []).map((y, i) => (
<option key={i} value={y}> <option key={i} value={y}>
{y} {y}
</option> </option>
))} ))}
</FormSelect> </FormSelect>
</InputGroup> </InputGroup>
</FormGroup> </FormGroup>
</Col> </Col>
@@ -56,11 +56,100 @@ export default function InformacionCasoEspecial({ alumno }: Props) {
return ( return (
<div className="mt-4"> <div className="mt-4">
{/* Título */} {/* Título */}
<h3 className="text-primary fw-bold mb-3">{alumno.Status?.status}</h3> <h2 className="fw-bold mb-3">{alumno.Status?.status}</h2>
<h4 className="mb-4">Datos personales</h4> <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 */} {/* Campos */}
{/*
// Informacino de antes
<Form.Group as={Row} className="mb-2"> <Form.Group as={Row} className="mb-2">
<Form.Label column sm={3}>Número de Cuenta:</Form.Label> <Form.Label column sm={3}>Número de Cuenta:</Form.Label>
<Col sm={9}> <Col sm={9}>
@@ -117,7 +206,7 @@ export default function InformacionCasoEspecial({ alumno }: Props) {
</Col> </Col>
</Form.Group> </Form.Group>
{alumno.motivo && ( {alumno.motivo && (
<Form.Group as={Row} className="mb-2"> <Form.Group as={Row} className="mb-2">
<Form.Label column sm={3}>Motivo:</Form.Label> <Form.Label column sm={3}>Motivo:</Form.Label>
<Col sm={9}> <Col sm={9}>
@@ -174,6 +263,7 @@ export default function InformacionCasoEspecial({ alumno }: Props) {
<Form.Control plaintext readOnly value={fecha(alumno.createdAt)} /> <Form.Control plaintext readOnly value={fecha(alumno.createdAt)} />
</Col> </Col>
</Form.Group> </Form.Group>
*/}
{(alumno.Status?.idStatus === 11 || alumno.Status?.idStatus === 12) && ( {(alumno.Status?.idStatus === 11 || alumno.Status?.idStatus === 12) && (
<div className="text-end mt-4"> <div className="text-end mt-4">
@@ -132,7 +132,7 @@ export default function InformacionServicio({ admin, imprimirError, updateIsLoad
return ( return (
<div className="container mv-5"> <div className="container mv-5">
<div className="mb-5"> <div className="mb-5">
<h3 className="fw-bold mb-4">Datos del programa</h3> <h3 className="mt-3 mb-2">Datos del programa</h3>
<div className="mb-3"> <div className="mb-3">
<label className="form-label fw-semibold">Institucion:</label> <label className="form-label fw-semibold">Institucion:</label>
@@ -178,11 +178,13 @@ export default function InformacionServicio({ admin, imprimirError, updateIsLoad
<p className="form-control">{datos.Usuario?.nombre || '-'}</p> <p className="form-control">{datos.Usuario?.nombre || '-'}</p>
</div> </div>
<div className="mb-3"> {datos.fechaNacimiento && (
<label className="form-label fw-semibold">Fecha de nacimiento: <div className="mb-3">
<p className="form-control">{datos.fechaNacimiento}</p> <label className="form-label fw-semibold">Fecha de nacimiento:
</label> <p className="form-control">{formatDate(datos.fechaNacimiento)}</p>
</div> </label>
</div>
)}
<div className="mb-3"> <div className="mb-3">
<label className="form-label fw-semibold">Carrera:</label> <label className="form-label fw-semibold">Carrera:</label>
@@ -194,50 +196,63 @@ export default function InformacionServicio({ admin, imprimirError, updateIsLoad
<p className="form-control">{datos.creditos || '-'}</p> <p className="form-control">{datos.creditos || '-'}</p>
</div> </div>
<div className="mb-3"> {datos.telefono && (
<label className="form-label fw-semibold">Telèfono:</label> <div className="mb-3">
<p className="form-control">{datos.telefono}</p> <label className="form-label fw-semibold">Telèfono:</label>
</div> <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">Direcciòn:</label>
<p className="form-control">{datos.direccion}</p>
</div>
<div className="mb-3"> <div className="mb-3">
<label className="form-label fw-semibold">Email:</label> <label className="form-label fw-semibold">Email:</label>
<p className="form-control">{datos.correo || '-'}</p> <p className="form-control">{datos.correo || '-'}</p>
</div> </div>
<div className="mb-3"> {datos.programaInterno && (
<label className="form-label fw-semibold">Programa Interno:</label> <div className="mb-3">
<p className="form-control">{datos.programaInterno}</p> <label className="form-label fw-semibold">Programa Interno:</label>
</div> <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"> <div className="mb-3">
<label className="form-label fw-semibold">Fecha de registro:</label> <label className="form-label fw-semibold">Fecha de registro:</label>
<p className="form-control">{datos.createdAt || '-'}</p> <p className="form-control">{formatDate(datos.createdAt) || '-'}</p>
</div> </div>
<div className="mb-3"> <div className="mb-3">
<label className="form-label fw-semibold">fecha de inicio:</label> <label className="form-label fw-semibold">fecha de inicio:</label>
<p className="form-control">{datos.fechaInicio || '-'}</p> <p className="form-control">{formatDate(datos.fechaInicio)|| '-'}</p>
</div> </div>
<div className="mb-3"> <div className="mb-3">
<label className="form-label fw-semibold">Fecha de termino:</label> <label className="form-label fw-semibold">Fecha de termino:</label>
<p className="form-control">{datos.fechaFin || '-'}</p> <p className="form-control">{formatDate(datos.fechaFin) || '-'}</p>
</div> </div>
<div className="mb-3"> {datos.fechaLiberacion && (
<label className="form-label fw-semibold">Fecha de liberaciòn:</label> <div className="mb-3">
<p className="form-control">{datos.fechaLiberacion}</p> <label className="form-label fw-semibold">Fecha de liberaciòn:</label>
</div> <p className="form-control">{formatDate(datos.fechaLiberacion)}</p>
</div>
)}
</div> </div>
<button className="btn btn-primary" <button className="btn btn-primary"
@@ -247,4 +262,23 @@ export default function InformacionServicio({ admin, imprimirError, updateIsLoad
</button> </button>
</div> </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}`;
} }
@@ -58,7 +58,7 @@ export default function ReasignacionProgramas({
return ( return (
<div className="mt-6"> <div className="mt-6">
<h3 className="text-2xl font-semibold mb-3">Reasignación de programas</h3> <h3 className="fw-semibold text-2xl font-semibold mb-3">Reasignación de programas</h3>
<div className="text-base mb-4"> <div className="text-base mb-4">
<p> <p>
@@ -69,21 +69,21 @@ export default function ReasignacionProgramas({
</div> </div>
<div className="mb-4"> <div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="form-label fw-semibold block text-sm font-medium text-gray-700 mb-2">
Usuario/Correo electrónico Usuario/Correo electrónico
</label> </label>
<input <input
type="email" type="email"
placeholder="Usuario" placeholder="Usuario"
value={correoOtroResponsable} value={correoOtroResponsable}
onChange={(e) => setCorreoOtroResponsable(e.target.value)} onChange={(e) => setCorreoOtroResponsable(e.target.value)}
className="border border-gray-300 rounded-lg p-2 w-full" className="form-control border border-gray-300 rounded-lg p-2 w-full"
/> />
</div> </div>
<div className="pt-5"> <div className="pt-3">
<button <button
className={`px-4 py-2 rounded-md text-white ${ className={`px-4 py-2 rounded-1 text-white ${
mostrarBoton() mostrarBoton()
? "bg-gray-400 cursor-not-allowed" ? "bg-gray-400 cursor-not-allowed"
: "bg-blue-600 hover:bg-blue-700" : "bg-blue-600 hover:bg-blue-700"
+23 -5
View File
@@ -34,15 +34,31 @@ export default function Reporte({ admin }: Props) {
const res = await axiosInstance.get("/servicio/reporte", { const res = await axiosInstance.get("/servicio/reporte", {
params: { params: {
inicio: moment(selectedInicio).format("YYYY-MM-DD"), inicio: moment(selectedInicio).format("YYYY-MM-DD"),
fin: moment(selectedFin).format("YYYY-MM-DD"), fin: moment(selectedFin).format("YYYY-MM-DD"),
},
headers: {
Authorization: `Bearer ${admin.token}`,
}, },
responseType: "blob", 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)
//saveAs(res.data, "reporte.csv"); //saveAs(res.data, "reporte.csv");
// reset // reset
@@ -53,6 +69,8 @@ export default function Reporte({ admin }: Props) {
//updateIsLoading(false); //updateIsLoading(false);
// optional: handle error // optional: handle error
} }
}; };
return ( return (
@@ -8,6 +8,7 @@ import { Button, Col, Form, FormGroup, FormLabel, FormSelect, InputGroup, Row, S
import ServicioSocialTabla from "../servicio-social-tabla"; import ServicioSocialTabla from "../servicio-social-tabla";
import { ServicioSocialResponse } from "@/types/responses"; import { ServicioSocialResponse } from "@/types/responses";
import { FaInfoCircle, FaSchool, FaUser } from "react-icons/fa"; import { FaInfoCircle, FaSchool, FaUser } from "react-icons/fa";
import { useRouter } from "next/navigation";
interface Admin { interface Admin {
idTipoUsuario?: number; idTipoUsuario?: number;
@@ -45,6 +46,9 @@ interface CasosEspecialesResponse {
} }
export default function TablaCasosEspeciales({ admin, imprimirError }: Props) { export default function TablaCasosEspeciales({ admin, imprimirError }: Props) {
// Para el redirecionamiento
const router = useRouter();
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [total, setTotal] = useState(0); const [total, setTotal] = useState(0);
@@ -130,9 +134,12 @@ export default function TablaCasosEspeciales({ admin, imprimirError }: Props) {
useEffect(() => { useEffect(() => {
console.log("Admin recibido:", admin); console.log("Admin recibido:", admin);
obtenerCatalogoStatus();
// Asegurar que se ejecute la petición si el usuario tiene permisos // Asegurar que se ejecute la petición si el usuario tiene permisos
if (admin && Number(admin.idTipoUsuario) === 1) { if (admin && Number(admin.idTipoUsuario) === 1) {
obtenerCatalogoStatus(); obtenerCatalogoStatus();
console.log("entro para traer la informacion de los usuarios")
} else if (!admin) { } else if (!admin) {
console.warn("Admin no definido, no se cargaron los casos especiales."); console.warn("Admin no definido, no se cargaron los casos especiales.");
} }
@@ -244,15 +251,27 @@ export default function TablaCasosEspeciales({ admin, imprimirError }: Props) {
{/* Tabla {/* Tabla
<ServicioSocialTabla /> <ServicioSocialTabla />
onPageChange={onPageChange}
columnasResponsable={admin?.idTipoUsuario === 1}
*/} */}
<ServicioSocialTabla <ServicioSocialTabla
data={data} data={data}
total={total} total={total}
columnaFechaRegistro={true} columnaFechaRegistro={true}
columnasResponsable={false} idTipoUsuario={admin?.idTipoUsuario}
columnasResponsable={admin?.idTipoUsuario === 1}
onPageChange={onPageChange} onPageChange={onPageChange}
columnaFechaInicio={false} columnaFechaInicio={false}
columnaFechaFin={false} columnaFechaFin={false}
onRowAction={(row, path) => {
try {
router.push(`/responsable/${path}`)
} catch (error) {
console.error('Error al manejar accion de fila', error)
}
}}
/> />
</section> </section>
@@ -1,8 +1,18 @@
"use client"; "use client";
import React, { useEffect, useRef, useState } from "react"; import React, { useEffect, useRef, useState } from "react";
import { useRouter } from 'next/navigation'; import { useRouter } from "next/navigation";
import { Form, FormGroup, FormLabel, FormControl, FormSelect, Button, InputGroup, Row, Col } from "react-bootstrap"; import {
Form,
FormGroup,
FormLabel,
FormControl,
FormSelect,
Button,
InputGroup,
Row,
Col,
} from "react-bootstrap";
import { FaUser, FaSchool, FaInfoCircle } from "react-icons/fa"; import { FaUser, FaSchool, FaInfoCircle } from "react-icons/fa";
import ServicioSocialTabla from "../servicio-social-tabla"; import ServicioSocialTabla from "../servicio-social-tabla";
import { axiosInstance } from "@/api/config"; import { axiosInstance } from "@/api/config";
@@ -11,197 +21,240 @@ import { Prev } from "react-bootstrap/esm/PageItem";
import { AxiosError } from "axios"; import { AxiosError } from "axios";
interface Admin { interface Admin {
idTipoUsuario: number; idTipoUsuario: number;
token?: string; token?: string;
} }
interface Props { interface Props {
admin: Admin; admin: Admin;
imprimirError: (mensaje: string) => void; imprimirError: (mensaje: string) => void;
} }
type StatusItem = { idStatus: number; status: string }; type StatusItem = { idStatus: number; status: string };
export default function TablaServicioSocial({ admin, imprimirError }: Props) { export default function TablaServicioSocial({ admin, imprimirError }: Props) {
const router = useRouter(); const router = useRouter();
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [total, setTotal] = useState(0); const [total, setTotal] = useState(0);
const [data, setData] = useState<ServicioSocialResponse[]>([]); const [data, setData] = useState<ServicioSocialResponse[]>([]);
const [status, setStatus] = useState<StatusItem[]>([]); const [status, setStatus] = useState<StatusItem[]>([]);
const [search, setSearch] = useState({ numeroCuenta: '', nombre: '', idStatus: '' }); const [search, setSearch] = useState({
const searchAnterior = useRef({ numeroCuenta: '', nombre: '', idStatus: '' }); numeroCuenta: "",
nombre: "",
idStatus: "",
});
const searchAnterior = useRef({ numeroCuenta: "", nombre: "", idStatus: "" });
useEffect(() => { useEffect(() => {
if (admin?.idTipoUsuario === 1) { if (admin?.idTipoUsuario === 1) {
obtenerCatalogoStatus(); obtenerCatalogoStatus();
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [admin?.idTipoUsuario]); }, [admin?.idTipoUsuario]);
function onPageChange(newPage: number) { function onPageChange(newPage: number) {
setPage(newPage); setPage(newPage);
obtenerServicios(newPage); obtenerServicios(newPage);
}
async function obtenerServicios(pagina?: number) {
const paginaActual = pagina ?? page;
let query = "";
setIsLoading(true);
if (
search.numeroCuenta !== searchAnterior.current.numeroCuenta ||
search.nombre !== searchAnterior.current.nombre ||
search.idStatus !== searchAnterior.current.idStatus
) {
// resetear a primera página si cambió la búsqueda
setPage(1);
searchAnterior.current = { ...search };
} }
async function obtenerServicios(pagina?: number) { if (search.idStatus)
const paginaActual = pagina ?? page; query += `&idStatus=${encodeURIComponent(search.idStatus)}`;
let query = ''; if (search.nombre) query += `&nombre=${encodeURIComponent(search.nombre)}`;
if (search.numeroCuenta)
query += `&numeroCuenta=${encodeURIComponent(search.numeroCuenta)}`;
setIsLoading(true); try {
// asegurar que enviamos Authorization si existe en admin, y loggear token para debug
if ( const config = admin?.token
search.numeroCuenta !== searchAnterior.current.numeroCuenta || ? { headers: { Authorization: `Bearer ${admin.token}` } }
search.nombre !== searchAnterior.current.nombre || : undefined;
search.idStatus !== searchAnterior.current.idStatus console.debug(
) { "Obtener servicios - localStorage token:",
// resetear a primera página si cambió la búsqueda typeof window !== "undefined"
setPage(1); ? localStorage.getItem("token")
searchAnterior.current = { ...search }; : undefined,
} "admin.token:",
admin?.token
if (search.idStatus) query += `&idStatus=${encodeURIComponent(search.idStatus)}`; );
if (search.nombre) query += `&nombre=${encodeURIComponent(search.nombre)}`; const res = await axiosInstance.get(
if (search.numeroCuenta) query += `&numeroCuenta=${encodeURIComponent(search.numeroCuenta)}`; `/servicio/admin?pagina=${paginaActual}${query}`,
config
try { );
// asegurar que enviamos Authorization si existe en admin, y loggear token para debug console.debug("Respuesta servicios_admin:", res.data);
const config = admin?.token ? { headers: { Authorization: `Bearer ${admin.token}` } } : undefined; setData(res.data.serviciosAdmin || []);
console.debug('Obtener servicios - localStorage token:', typeof window !== 'undefined' ? localStorage.getItem('token') : undefined, 'admin.token:', admin?.token); setTotal(res.data.count ?? 0);
const res = await axiosInstance.get(`/servicio/servicios_admin?pagina=${paginaActual}${query}`, config); } catch (err: unknown) {
console.debug('Respuesta servicios_admin:', res.data); // manejar error
setData(res.data.serviciosAdmin || []); console.error("Error obtenerServicios", err);
setTotal(res.data.count ?? 0); //const mensaje = (err as any)?.response?.data ?? String(err); Falta imprimir error
} catch (err: unknown) { //imprimirError(mensaje);
// manejar error } finally {
console.error('Error obtenerServicios', err); setIsLoading(false);
//const mensaje = (err as any)?.response?.data ?? String(err); Falta imprimir error
//imprimirError(mensaje);
} finally {
setIsLoading(false);
}
} }
}
const obtenerCatalogoStatus = async () => { const obtenerCatalogoStatus = async () => {
try { try {
//const headers = responsable?.token ? { Authorization: `Bearer ${responsable.token}` } : undefined; //const headers = responsable?.token ? { Authorization: `Bearer ${responsable.token}` } : undefined;
const res = await axiosInstance.get<StatusItem[]>('/status'); const res = await axiosInstance.get<StatusItem[]>("/status");
setStatus(res.data); setStatus(res.data);
console.log(res.data); console.log(res.data);
//console.log('Status obtenidos', status); //console.log('Status obtenidos', status);
obtenerServicios(); obtenerServicios();
} catch (err: unknown) { } catch (err: unknown) {
const axiosErr = err as AxiosError; const axiosErr = err as AxiosError;
console.log('Error al obtener catálogo de status:', err); console.log("Error al obtener catálogo de status:", err);
} }
}; };
return (
<section>
<div className="columns">
<h2 className="title">Servicios Sociales</h2>
return ( <section className="container-fluid my-4">
<section> <Form
<div className="columns"> onSubmit={(e) => {
<h2 className="title">Servicios Sociales</h2> e.preventDefault();
obtenerServicios(1);
}}
>
<Row className="g-3">
<Col md={3}>
<FormGroup>
<FormLabel>Número de Cuenta</FormLabel>
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaSchool />
</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") obtenerServicios(1);
}}
className="rounded-4"
/>
</InputGroup>
</FormGroup>
</Col>
<section className="container-fluid my-4"> <Col md={3}>
<Form onSubmit={(e) => { e.preventDefault(); obtenerServicios(1); }}> <FormGroup>
<Row className="g-3"> <FormLabel>Nombre</FormLabel>
<Col md={3}> <InputGroup>
<FormGroup> <InputGroup.Text className="rounded-4">
<FormLabel>Número de Cuenta</FormLabel> <FaUser />
<InputGroup> </InputGroup.Text>
<InputGroup.Text className="rounded-4"> <FormControl
<FaSchool /> type="text"
</InputGroup.Text> placeholder="Nombre"
<FormControl value={search.nombre}
type="text" onChange={(e) =>
placeholder="No.Cuenta" setSearch((prev) => ({
maxLength={9} ...prev,
value={search.numeroCuenta} nombre: e.target.value,
onChange={(e) => setSearch(prev => ({ ...prev, numeroCuenta: e.target.value }))} }))
onKeyDown={(e) => { if (e.key === 'Enter') obtenerServicios(1); }} }
className="rounded-4" onKeyDown={(e) => {
/> if (e.key === "Enter") obtenerServicios(1);
</InputGroup> }}
</FormGroup> className="rounded-4"
</Col> />
</InputGroup>
</FormGroup>
</Col>
<Col md={3}> <Col md={3}>
<FormGroup> <FormGroup>
<FormLabel>Nombre</FormLabel> <FormLabel>Status</FormLabel>
<InputGroup> <InputGroup>
<InputGroup.Text className="rounded-4"> <InputGroup.Text className="rounded-4">
<FaUser /> <FaInfoCircle />
</InputGroup.Text> </InputGroup.Text>
<FormControl <FormSelect
type="text" value={search.idStatus}
placeholder="Nombre" onChange={(e) =>
value={search.nombre} setSearch((Prev) => ({
onChange={(e) => setSearch(prev => ({ ...prev, nombre: e.target.value }))} ...Prev,
onKeyDown={(e) => { if (e.key === 'Enter') obtenerServicios(1); }} idStatus: e.target.value,
className="rounded-4" }))
/> }
</InputGroup> className="rounded-4"
</FormGroup> >
</Col> <option value="">Status</option>
{status.slice(0, 10).map((s) => (
<option key={s.idStatus} value={s.idStatus}>
{s.status}
</option>
))}
</FormSelect>
</InputGroup>
</FormGroup>
</Col>
<Col md={3}> <Col md={3} className="d-flex align-items-end">
<FormGroup> <Button
<FormLabel>Status</FormLabel> type="submit"
<InputGroup> className="w-100 rounded-5"
<InputGroup.Text className="rounded-4"> disabled={isLoading}
<FaInfoCircle /> >
</InputGroup.Text> {isLoading ? "Buscando..." : "Buscar"}
<FormSelect </Button>
value={search.idStatus} </Col>
onChange={(e) => setSearch(Prev => ({ ...Prev, idStatus: e.target.value }))} </Row>
className="rounded-4" </Form>
>
<option value="">Status</option>
{status.slice(0, 10).map((s) => (
<option key={s.idStatus} value={s.idStatus}>{s.status}</option>
))}
</FormSelect>
</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>
<ServicioSocialTabla
data={data}
total={total}
columnaFechaFin={true}
columnaFechaInicio={true}
onPageChange={onPageChange}
idTipoUsuario={admin?.idTipoUsuario}
columnasResponsable={admin?.idTipoUsuario === 1}
columnaFechaRegistro={true}
columnaCuestionario={false}
columnaCartaTermino={false}
columnaCuestionarioCompleto={false}
onRowAction={(row, path) => {
try {
// Falta corregir este parte de codigo para poder subirlo
//if ((row as any)?.idServicio) localStorage.setItem('idServicio', String((row as any).idServicio));
router.push(`/responsable/${path}`);
} catch (e) {
console.error('Error al manejar acción de fila', e);
}
}}
/>
</section> </section>
) </div>
<ServicioSocialTabla
data={data}
total={total}
columnaFechaFin={true}
columnaFechaInicio={true}
onPageChange={onPageChange}
idTipoUsuario={admin?.idTipoUsuario}
columnasResponsable={admin?.idTipoUsuario === 1}
columnaFechaRegistro={true}
columnaCuestionario={false}
columnaCartaTermino={false}
columnaCuestionarioCompleto={false}
onRowAction={(row, path) => {
try {
// Falta corregir este parte de codigo para poder subirlo
//if ((row as any)?.idServicio) localStorage.setItem('idServicio', String((row as any).idServicio));
router.push(`/responsable/${path}`);
} catch (e) {
console.error("Error al manejar acción de fila", e);
}
}}
/>
</section>
);
} }
@@ -14,10 +14,10 @@ export default function TituloStatus({ status }: Props) {
console.log('idStatus en TituloStatus:', idStatus); console.log('idStatus en TituloStatus:', idStatus);
return ( return (
<div className="mb-5"> <div className="mb-4">
{idStatus === 1 && <h3 className="title">Confirmar el pre-registro del alumno</h3>} {idStatus === 1 && <h2 className="fw-bold mt-4">Confirmar el pre-registro del alumno</h2>}
{idStatus === 4 && <h3 className="title">Término</h3>} {idStatus === 4 && <h2 className="fw-bold mt-4">Término</h2>}
{idStatus === 5 && <h3 className="title">Validar el término del alumno</h3>} {idStatus === 5 && <h2 className="fw-bold mt-4">Validar el término del alumno</h2>}
</div> </div>
); );
} }
+55 -55
View File
@@ -1,73 +1,73 @@
"use client"; "use client";
import React from "react"; import React from "react";
import { FaUserPlus, FaUserCheck, FaUser, FaUsers, FaUserClock, FaUserTie } from "react-icons/fa";
interface Props { interface Props {
idStatus: number; 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) { export default function BarraProgreso({ idStatus }: Props) {
const steps: Step[] = [ const steps = [
{ label: "Pre Registro", icon: "account-plus", visible: idStatus < 6 }, { label: "Pre Registro", icon: <FaUserPlus />, id: 1 },
{ label: "Pre Registro Validado", icon: "account-clock", visible: idStatus < 6 }, { label: "Pre Registro Validado", icon: <FaUserCheck />, id: 2 },
{ label: "Registro", icon: "account", visible: idStatus < 6 }, { label: "Registro", icon: <FaUser />, id: 3 },
{ label: "Pre Termino", icon: "account-details", visible: idStatus < 6 }, { label: "Pre Termino", icon: <FaUsers />, id: 4 },
{ label: "Termino", icon: "account-clock", visible: idStatus < 6 }, { label: "Termino", icon: <FaUserClock />, id: 5 },
{ label: "Liberacion", icon: "account-check", visible: idStatus < 6 }, { label: "Liberacion", icon: <FaUserTie />, id: 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 ( return (
<div className="b-steps-container mt-6 mb-5"> <div className="container mt-5">
<div className="b-steps"> <div className="d-flex justify-content-center align-items-center position-relative">
{steps.filter(s => s.visible).map((step, idx) => ( {steps.map((step, index) => {
<div key={idx} className={`step-item ${step.type === 'danger' ? 'is-danger' : 'is-info'}`} aria-hidden> const isActive = idStatus >= step.id;
<div className="step-marker"> const isCurrent = idStatus === step.id;
{/* 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>
<style jsx>{` return (
.b-steps { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: stretch; } <div key={index} className="text-center position-relative flex-fill">
.step-item { display: flex; align-items: center; padding: 0.6rem 0.9rem; border-radius: 6px; background: #e6f2ff; color: #0b66b2; min-width: 220px; } {/* Línea de conexión */}
.step-item.is-info { background: #ecf8ff; color: #0b66b2; } {index > 0 && (
.step-item.is-danger { background: #ffecec; color: #a10b0b; } <div
.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); } className={`position-absolute top-50 start-0 translate-middle-y w-100 border-top ${
.step-item.is-info .step-marker { background: #dff4ff; } isActive ? "border-primary" : "border-secondary opacity-25"
.step-item.is-danger .step-marker { background: #ffdede; } }`}
.icon { font-size: 1.1rem; } style={{ zIndex: 0 }}
.step-title { font-weight: 600; font-size: 0.95rem; } ></div>
)}
/* Mimic the Buefy rule to hide ::before/::after on danger steps */ {/* Círculo del paso */}
.b-steps .step-item.is-danger::before, <div
.b-steps .step-item.is-danger::after { className={`rounded-circle d-flex justify-content-center align-items-center mx-auto mb-2 border ${
display: none !important; isActive
content: none !important; ? "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>
@media (max-width: 768px) { {/* Título */}
.step-item { min-width: 140px; padding: 0.45rem 0.6rem; } <div
.step-title { font-size: 0.85rem; } className={`fw-semibold ${
} isActive || isCurrent ? "text-dark" : "text-secondary"
`}</style> }`}
style={{ fontSize: "0.95rem" }}
>
{step.label}
</div>
</div>
);
})}
</div>
</div> </div>
); );
} }
@@ -7,6 +7,8 @@ import { axiosInstance } from '@/api/config';
import { isAxiosError } from 'axios'; import { isAxiosError } from 'axios';
import type { AxiosResponse } from 'axios'; import type { AxiosResponse } from 'axios';
import moment from "moment"; import moment from "moment";
import { Col, FormGroup, FormLabel, InputGroup } from "react-bootstrap";
import { FaRegCalendarAlt } from "react-icons/fa";
interface Alumno { interface Alumno {
token: { headers: Record<string, string> }; token: { headers: Record<string, string> };
@@ -74,64 +76,89 @@ export default function CompletarDatosPersonales({
}; };
return ( return (
<div className="space-y-4"> <div className="container space-y-4">
<h3 className="text-xl font-semibold">Formulario Pre-registro</h3> <h2 className="text-xl fw-semibold">Formulario Pre-registro</h2>
{/* Fecha de nacimiento */} {/* Fecha de nacimiento */}
<div> <Col>
<label className="block mb-1 font-medium">Fecha de nacimiento</label> <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>
<DatePicker <DatePicker
selected={nacimiento} selected={nacimiento}
onChange={(date: Date | null) => { onChange={(date: Date | null) => {
if (date) setNacimiento(date); if (date) setNacimiento(date);
}} }}
maxDate={maxDate} maxDate={maxDate}
className="border border-gray-300 rounded px-3 py-2 w-full" className="border border-gray-300 rounded px-3 py-2 form-control"
placeholderText="Fecha de nacimiento" placeholderText="Fecha de nacimiento"
/> wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
</div> </div>
*/}
{/* Teléfono */} {/* Teléfono */}
<div> <div className="mb-3">
<label className="block mb-1 font-medium">Teléfono</label> <label className="block mb-1 form-label fw-semibold mb-2">Teléfono</label>
<input <input
type="tel" type="tel"
placeholder="Teléfono" placeholder="Teléfono"
maxLength={10} maxLength={10}
value={telefono} value={telefono}
onChange={(e) => setTelefono(e.target.value)} onChange={(e) => setTelefono(e.target.value)}
className="border border-gray-300 rounded px-3 py-2 w-full" className="border border-gray-300 rounded px-3 py-2 form-control"
/> />
</div> </div>
{/* Dirección */} {/* Dirección */}
<div> <div className="mb-3">
<label className="block mb-1 font-medium">Dirección</label> <label className="block mb-1 form-label fw-semibold mb-2">Dirección</label>
<input <input
type="text" type="text"
placeholder="Dirección" placeholder="Dirección"
maxLength={200} maxLength={200}
value={direccion} value={direccion}
onChange={(e) => setDireccion(e.target.value)} onChange={(e) => setDireccion(e.target.value)}
className="border border-gray-300 rounded px-3 py-2 w-full" className="border border-gray-300 rounded px-3 py-2 form-control"
/> />
</div> </div>
{/* Botón enviar */} {/* Botón enviar */}
<div className="text-center mt-4"> <div className="text-center mt-4">
<button <button
disabled={!telefono || !direccion} disabled={!telefono || !direccion}
onClick={() => onClick={() =>
imprimirWarning( imprimirWarning(
"¿Estas seguro(a) que tus datos son correctos?", "¿Estas seguro(a) que tus datos son correctos?",
terminarPreRegistro terminarPreRegistro
) )
} }
className={`px-4 py-2 rounded text-white ${ className={`px-4 py-2 rounded text-white mb-5 ${
!telefono || !direccion !telefono || !direccion
? "bg-gray-400 cursor-not-allowed" ? "bg-gray-400 cursor-not-allowed"
: "bg-green-600 hover:bg-green-700" : "bg-green-600 hover:bg-green-700"
}`} }`}
> >
Enviar Enviar
</button> </button>
@@ -300,7 +300,7 @@ export default function FullCuestionario() {
<div> <div>
{p.opciones?.map((op) => ( {p.opciones?.map((op) => (
<div key={op} className="SINO"> <div key={op} className="SINO">
<label> <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)} /> {op} <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> </label>
</div> </div>
@@ -309,7 +309,7 @@ export default function FullCuestionario() {
)} )}
{p.tipo === 'texto' && ( {p.tipo === 'texto' && (
<div> <div>
<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í" /> <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í" />
</div> </div>
)} )}
</div> </div>
@@ -327,18 +327,18 @@ export default function FullCuestionario() {
<table className="table table-bordered text-center"> <table className="table table-bordered text-center">
<thead> <thead>
<tr> <tr>
<th></th> <th className="bg-transparent"></th>
{t.renglones[0].opciones.map((op) => ( {t.renglones[0].opciones.map((op) => (
<th key={op}>{op}</th> <th className="bg-transparent" key={op}>{op}</th>
))} ))}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{t.renglones.map((r) => ( {t.renglones.map((r) => (
<tr key={r.idRenglon}> <tr key={r.idRenglon}>
<td>{r.textoRenglon}</td> <td className="bg-transparent">{r.textoRenglon}</td>
{r.opciones.map((op) => ( {r.opciones.map((op) => (
<td key={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)} /> <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> </td>
))} ))}
@@ -351,12 +351,10 @@ export default function FullCuestionario() {
); );
})} })}
<div className="my-6 mb-6 has-text-centered"> <div className="my-6 mb-6 text-center mb-5">
<div> <button className="btn btn-success is-medium" onClick={submitForm} disabled={!isFormComplete}>
<button className="button is-success is-medium" onClick={submitForm} disabled={!isFormComplete}>
{isLoading ? 'Enviando...' : 'Enviar'} {isLoading ? 'Enviando...' : 'Enviar'}
</button> </button>
</div>
</div> </div>
</div> </div>
+93 -93
View File
@@ -50,116 +50,116 @@ export default function InformacinoServicio({ servicio }: Props) {
}; };
return ( return (
<div> <div className="container">
{/* Datos del programa */} {/* Datos del programa */}
<div className="mb-5"> <div className="mb-5">
<h4 className="is-size-4 pb-2">Datos del programa</h4> <h4 className="is-size-4 pb-2">Datos del programa</h4>
<div className="mb-2"> <div className="mb-2">
<label>Institución:</label> <label className="form-label fw-semibold">Institución:</label>
<p className="input">{servicio.Programa.institucion}</p> <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> </div>
<div className="mb-2"> {/* Datos personales */}
<label>Dependencia:</label> <div className="mb-5">
<p className="input">{servicio.Programa.dependencia}</p> <h4 className="is-size-4 pb-2">Datos personales</h4>
</div>
<div className="mb-2"> <div className="mb-2">
<label>Programa:</label> <label className="form-label fw-semibold">Número de cuenta:</label>
<p className="input">{servicio.Programa.programa}</p> <p className="form-control">{servicio.Usuario.usuario}</p>
</div> </div>
<div className="mb-2"> <div className="mb-2">
<label>Clave de programa:</label> <label className="form-label fw-semibold">Nombre:</label>
<p className="input">{servicio.Programa.clavePrograma}</p> <p className="form-control">{servicio.Usuario.nombre}</p>
</div> </div>
</div>
{/* Datos personales */} <div className="mb-2">
<div className="mb-5"> <label className="form-label fw-semibold">Carrera:</label>
<h4 className="is-size-4 pb-2">Datos personales</h4> <p className="form-control">{servicio.Carrera.carrera}</p>
</div>
<div className="mb-2"> <div className="mb-2">
<label>Número de cuenta:</label> <label className="form-label fw-semibold">Créditos:</label>
<p className="input">{servicio.Usuario.usuario}</p> <p className="form-control">
</div> {servicio.creditos ? parseInt(servicio.creditos) : ""}
{servicio.creditos && "%"}
</p>
</div>
<div className="mb-2"> {servicio.telefono && (
<label>Nombre:</label> <div className="mb-2">
<p className="input">{servicio.Usuario.nombre}</p> <label className="form-label">Teléfono:</label>
</div> <p className="form-control">{servicio.telefono}</p>
</div>
)}
<div className="mb-2"> {servicio.direccion && (
<label>Carrera:</label> <div className="mb-2">
<p className="input">{servicio.Carrera.carrera}</p> <label className="form-label fw-semibold">Dirección:</label>
</div> <p className="form-control">{servicio.direccion}</p>
</div>
)}
<div className="mb-2"> <div className="mb-2">
<label>Créditos:</label> <label className="form-label fw-semibold">Email:</label>
<p className="input"> <p className="form-control">{servicio.correo}</p>
{servicio.creditos ? parseInt(servicio.creditos) : ""} </div>
{servicio.creditos && "%"}
</p>
</div>
{servicio.telefono && ( {servicio.programaInterno && (
<div className="mb-2"> <div className="mb-2">
<label>Teléfono:</label> <label className="form-label fw-semibold">Programa Interno:</label>
<p className="input">{servicio.telefono}</p> <p className="form-control">{servicio.programaInterno}</p>
</div> </div>
)} )}
{servicio.direccion && ( {servicio.profesor && (
<div className="mb-2"> <div className="mb-2">
<label>Dirección:</label> <label className="form-label fw-semibold">Profesor:</label>
<p className="input">{servicio.direccion}</p> <p className="form-control">{servicio.profesor}</p>
</div> </div>
)} )}
<div className="mb-2"> {servicio.createdAt && (
<label>Email:</label> <div className="mb-2">
<p className="input">{servicio.correo}</p> <label className="form-label fw-semibold">Fecha de registro:</label>
</div> <p className="form-control">{fecha(servicio.createdAt)}</p>
</div>
)}
{servicio.programaInterno && ( <div className="mb-2">
<div className="mb-2"> <label className="form-label fw-semibold">Fecha de inicio:</label>
<label>Programa Interno:</label> <p className="form-control">{fecha(servicio.fechaInicio)}</p>
<p className="input">{servicio.programaInterno}</p> </div>
</div>
)}
{servicio.profesor && ( <div className="mb-2">
<div className="mb-2"> <label className="form-label fw-semibold">Fecha de término:</label>
<label>Profesor:</label> <p className="form-control">{fecha(servicio.fechaFin)}</p>
<p className="input">{servicio.profesor}</p> </div>
</div>
)}
{servicio.createdAt && ( {servicio.fechaLiberacion && (
<div className="mb-2"> <div className="mb-2">
<label>Fecha de registro:</label> <label className="form-label fw-semibold">Fecha de liberación:</label>
<p className="input">{fecha(servicio.createdAt)}</p> <p className="form-control">{fecha(servicio.fechaLiberacion)}</p>
</div>
)}
</div> </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> </div>
); );
} }
+2 -2
View File
@@ -10,8 +10,8 @@ interface Props {
export default function MensajeAlumno({ Status }: Props) { export default function MensajeAlumno({ Status }: Props) {
return ( return (
<div className="mb-6"> <div className="container mb-6">
<h4 className="block is-size-4">Estimado alumno(a):</h4> <h4 className="block is-size-4 mb-4">Estimado alumno(a):</h4>
<p className="has-text-justified is-size-6"> <p className="has-text-justified is-size-6">
{Status.idStatus === 2 && ( {Status.idStatus === 2 && (
+110 -64
View File
@@ -3,6 +3,8 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { axiosInstance } from "@/api/config"; import { axiosInstance } from "@/api/config";
import { isAxiosError } from 'axios'; import { isAxiosError } from 'axios';
import { Button, FormGroup, FormLabel } from "react-bootstrap";
import { FaUpload } from "react-icons/fa";
interface Alumno { interface Alumno {
tokenArchivo: string; tokenArchivo: string;
@@ -53,95 +55,139 @@ export default function PreTermino({
formData.append("informeGlobal", file); formData.append("informeGlobal", file);
try { try {
updateIsLoading(true); updateIsLoading(true);
const res = await axiosInstance.put(`/servicio/informe_global`, formData, { headers: { "Content-Type": "multipart/form-data", Authorization: alumno.tokenArchivo } }); const res = await axiosInstance.put(`/servicio/informe_global`, formData, { headers: { "Content-Type": "multipart/form-data", Authorization: alumno.tokenArchivo } });
imprimirMensaje(res.data.message); imprimirMensaje(res.data.message);
obtenerServicio(); obtenerServicio();
setFile(null); setFile(null);
} catch (err: unknown) { } catch (err: unknown) {
if (isAxiosError(err)) { if (isAxiosError(err)) {
imprimirError((err.response?.data) || err.message); imprimirError((err.response?.data) || err.message);
} else { } else {
imprimirError(err); imprimirError(err);
} }
} finally { } finally {
updateIsLoading(false); updateIsLoading(false);
} }
}; };
return ( return (
<div> <div className="container">
<h3 className="label"> <h6 className="label fw-semibold">
Cuestionario de evaluación del programa de servicio social. Cuestionario de evaluación del programa de servicio social.
</h3> </h6>
{!servicio.idCuestionarioAlumno && !servicio.idCuestionarioAlumno2 ? ( {!servicio.idCuestionarioAlumno && !servicio.idCuestionarioAlumno2 ? (
<div className="mb-6"> <div className="mb-5">
<a <a
href="/alumno/cuestionario" href="/alumno/cuestionario"
className="button is-info is-light" className="btn btn-outline-primary is-info is-light"
> >
<span className="icon"> <span className="icon">
<i className="fas fa-book-open"></i> <i className="fas fa-book-open"></i>
</span> </span>
<span>Cuestionario</span> <span>Cuestionario</span>
</a> </a>
</div> </div>
) : ( ) : (
<div className="mb-6"> <div className="mb-6">
<p className="block is-size-6"> <p className="block is-size-6">
Cuestionario contestado{" "} Cuestionario contestado{" "}
<span className="icon has-text-success"> <span className="icon has-text-success">
<i className="fas fa-check-bold"></i> <i className="fas fa-check-bold"></i>
</span> </span>
</p> </p>
</div> </div>
)} )}
<h3 className="label"> <h6 className="label fw-semibold">
Informe global de actividades{" "} Informe global de actividades{" "}
{!servicio.informeGlobal && ( {!servicio.informeGlobal && (
<span>(en formato .PDF. No se aceptan fotos)</span> <span>(en formato .PDF. No se aceptan fotos)</span>
)} )}
. .
</h3> </h6>
{/*
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
)
}
>
Enviar
</button>
</div>
</div>
*/}
{!servicio.informeGlobal ? ( {!servicio.informeGlobal ? (
<div> <div className="ph-5">
<div className="field"> <FormGroup>
<input <div className="border p-4 text-center rounded"
type="file" style={{ cursor: 'pointer'}}
accept="application/pdf" onClick={() => document.getElementById("fileInput")?.click()}
onChange={(e) => { >
if (e.target.files && e.target.files[0]) { <FaUpload size={40} className="mb-2"/>
setFile(e.target.files[0]); <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-6">Tamaño máximo 20MB</p> <p className="is-size-7">Sia 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="field has-text-centered my-5"> <input
<button id="fileInput"
className="button is-success" type="file"
disabled={!file} accept="application/pdf"
onClick={() => onChange={(e) => {
imprimirWarning( if (e.target.files && e.target.files[0]) {
"¿Estas seguro(a) de querer subir este informe global?", setFile(e.target.files[0]);
enviarInformeGlobal }
) }}
} hidden
> />
Enviar </FormGroup>
</button>
</div> <div className="d-flex gap-2 justify-content-center mb-5">
<Button
className=""
disabled={!file}
onClick={() =>
imprimirWarning(
"¿Estas seguro(a) de querer subir este informe global?",
enviarInformeGlobal
)
}
>Enviar</Button>
</div>
</div> </div>
) : ( ) : (
<p className="block is-size-6"> <p className="block is-size-6 mb-5">
Informe Global enviado.{" "} Informe Global enviado.{" "}
<span className="icon has-text-success"> <span className="icon has-text-success">
<i className="fas fa-check-bold"></i> <i className="fas fa-check-bold"></i>
</span> </span>
</p> </p>
)} )}
</div> </div>
+271 -245
View File
@@ -55,6 +55,19 @@ export default function CasoEspecialForm({
const [minDate, setMinDate] = useState<Date>(new Date('2020-01-02')); const [minDate, setMinDate] = useState<Date>(new Date('2020-01-02'));
const [minDate2, setMinDate2] = useState<Date>(new Date()); const [minDate2, setMinDate2] = useState<Date>(new Date());
// 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 || new Date())
}
// Función para resetear campos // Función para resetear campos
const resetear = (): void => { const resetear = (): void => {
setDependencia(''); setDependencia('');
@@ -213,276 +226,289 @@ export default function CasoEspecialForm({
// Render // Render
return ( return (
<div className="container mt-4"> <div className="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>
<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">Carrera</label>
<p className="form-control">{alumno.carrera || ''}</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>
<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>
<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>
<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>
<Col>
<FormGroup>
<FormLabel>Fecha de inicio</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
selected={fechaInicio}
minDate={minDate}
dateFormat="dd-MM-yyyy"
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
</InputGroup>
</FormGroup>
</Col>
<Col>
<FormGroup>
<FormLabel>Fecha de fin</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
selected={fechaFin}
value={fechaFin.toISOString().substring(0,10)}
minDate={minDate2}
dateFormat="dd-MM-yyyy"
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
</InputGroup>
</FormGroup>
</Col>
<Col>
<FormGroup>
<FormLabel>Fecha de nacimiento</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
selected={fechaNacimiento}
maxDate={new Date()}
dateFormat="dd-MM-yyyy"
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
</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"> <div className="mb-3">
<label className="form-label">Fecha de fin</label> <label className="form-label">Número de Cuenta</label>
<input <div className="input-group">
type="date" <input
className="form-control" type="text"
value={fechaFin.toISOString().substring(0, 10)} className="form-control"
onChange={(e) => setFechaFin(new Date(e.target.value))} placeholder="Número de Cuenta"
min={minDate2.toISOString().substring(0, 10)} 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> </div>
)}
<div className="mb-3"> {alumno.nombre && (
<label className="form-label">Fecha de nacimiento</label> <div className="mb-3">
<input <label className="form-label">Nombre</label>
type="date" <p className="form-control">{alumno.nombre || ''}</p>
className="form-control" </div>
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"> {alumno.carrera && (
<label className="form-label">Artículo</label> <div className="mb-3">
<select <label className="form-label">Carrera</label>
className="form-select" <p className="form-control">{alumno.carrera || ''}</p>
value={idStatus} </div>
onChange={(e) => setIdStatus(e.target.value)} )}
>
<option value="">Selecciona una opción</option> {alumno.creditos && (
<option value="11">Artículo 52</option> <div className="mb-3">
<option value="12">Artículo 91</option> <label className="form-label">Créditos</label>
</select> <p className="form-control">
</div> {alumno.creditos ? parseInt(alumno.creditos) : ''}{' '}
{alumno.creditos ? '%' : ''}
</p>
</div>
)}
{idStatus === '11' && (
<div className="mb-3"> <div className="mb-3">
<label className="form-label">Motivo</label> <label className="form-label">Dirección</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 <input
type="text" type="text"
className="form-control" className="form-control"
maxLength={250} maxLength={200}
value={institucion} value={direccion}
onChange={(e) => setInstitucion(e.target.value)} onChange={(e) => setDireccion(e.target.value)}
/> />
</div> </div>
<div className="mb-3"> <div className="mb-3">
<label className="form-label">Dependencia</label> <label className="form-label">Teléfono</label>
<input <input
type="text" type="tel"
className="form-control" className="form-control"
maxLength={250} maxLength={10}
value={dependencia} value={telefono}
onChange={(e) => setDependencia(e.target.value)} onChange={(e) => setTelefono(e.target.value)}
/> />
</div> </div>
</>
)}
<FormGroup className='mb-3'> <div className="mb-3">
<FormLabel>Archivo (.zip, .rar)</FormLabel> <label className="form-label">Correo electrónico</label>
<div <input
className='border p-4 text-center rounded' type="email"
style={{ cursor: 'pointer' }} className="form-control"
onClick={() => document.getElementById('fileInput')?.click()} value={correo}
> onChange={(e) => setCorreo(e.target.value)}
<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'>Tmañ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> </div>
<input
id='fileInput' <Col>
type="file" <FormGroup>
style={{ display: 'none'}} <FormLabel>Fecha de inicio</FormLabel>
onChange={e => setFile(e.target.files ? e.target.files[0] : null)} <InputGroup>
/> <InputGroup.Text>
</FormGroup> <FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
selected={fechaInicio}
onChange={handleFechaInicioChange}
minDate={minDate}
dateFormat="dd-MM-yyyy"
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
</InputGroup>
</FormGroup>
</Col>
{fechaInicio && (
<Col>
<FormGroup>
<FormLabel>Fecha de fin</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
selected={fechaFin}
onChange={handleFechaFinChange}
value={fechaFin.toISOString().substring(0,10)}
minDate={minDate2}
dateFormat="dd-MM-yyyy"
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
</InputGroup>
</FormGroup>
</Col>
)}
<Col>
<FormGroup>
<FormLabel>Fecha de nacimiento</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
selected={fechaNacimiento}
maxDate={new Date()}
onChange={handleFechaNacimientoChange}
dateFormat="dd-MM-yyyy"
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
</InputGroup>
</FormGroup>
</Col>
{/* {/*
<div className="mb-3"> <div className="mb-3">
<label className="form-label">Archivo (.zip, .rar)</label> <label className="form-label">Fecha de inicio</label>
<input <input
type="file" type="date"
accept=".zip,.rar" className="form-control"
className="form-control" value={fechaInicio.toISOString().substring(0, 10)}
onChange={(e) => setFile(e.target.files ? e.target.files[0] : null)} onChange={(e) => setFechaInicio(new Date(e.target.value))}
/> min={minDate.toISOString().substring(0, 10)}
<div className="form-text">Tamaño máximo 20MB</div> />
</div> </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="text-center mt-4 mb-3"> <div className="mb-3">
<button <label className="form-label">Artículo</label>
className="btn btn-success" <select
disabled={mostrarBoton()} className="form-select"
onClick={() => value={idStatus}
imprimirWarning( onChange={(e) => setIdStatus(e.target.value)}
'¿Estás seguro(a) de querer crear un nuevo caso especial?', >
enviar <option value="">Selecciona una opción</option>
) <option value="11">Artículo 52</option>
} <option value="12">Artículo 91</option>
> </select>
Enviar </div>
</button>
</div>
<BotonRegresar /> {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'>Tmañ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"
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
)
}
>
Enviar
</button>
</div>
<BotonRegresar />
</div> </div>
); );
} }
@@ -120,14 +120,14 @@ export default function LiberarCasoEspecial({ responsable, imprimirError }: Prop
return ( return (
<div className="mb-6 container mt-4"> <div className="mb-6 container mt-4">
<h3 className="h4 mb-4">Casos Especiales</h3> <h2 className="mb-4 fw-bold">Casos Especiales</h2>
<section className="container-fluid my-3"> <section className="my-3">
<Form onSubmit={(e) => { e.preventDefault(); obtenerCasosEspeciales(); }}> <Form onSubmit={(e) => { e.preventDefault(); obtenerCasosEspeciales(); }}>
<Row className="g-3"> <Row className="g-3">
<Col md={3}> <Col md={3}>
<FormGroup> <FormGroup>
<FormLabel>Número de Cuenta</FormLabel> <FormLabel className="fw-semibold">Número de Cuenta</FormLabel>
<InputGroup> <InputGroup>
<InputGroup.Text className="rounded-4"> <InputGroup.Text className="rounded-4">
<FaSchool /> <FaSchool />
@@ -147,7 +147,7 @@ export default function LiberarCasoEspecial({ responsable, imprimirError }: Prop
<Col md={3}> <Col md={3}>
<FormGroup> <FormGroup>
<FormLabel>Nombre</FormLabel> <FormLabel className="fw-semibold">Nombre</FormLabel>
<InputGroup> <InputGroup>
<InputGroup.Text className="rounded-4"> <InputGroup.Text className="rounded-4">
<FaUser /> <FaUser />
@@ -166,7 +166,7 @@ export default function LiberarCasoEspecial({ responsable, imprimirError }: Prop
<Col md={3}> <Col md={3}>
<FormGroup> <FormGroup>
<FormLabel>Status</FormLabel> <FormLabel className="fw-semibold">Status</FormLabel>
<InputGroup> <InputGroup>
<InputGroup.Text className="rounded-4"> <InputGroup.Text className="rounded-4">
<FaInfoCircle /> <FaInfoCircle />
@@ -291,12 +291,12 @@ export default function CuestionarioResponsbale2({
const p = item as Pregunta; const p = item as Pregunta;
return ( return (
<div key={p.id} className="mb-6 mt-6"> <div key={p.id} className="mb-6 mt-6">
<label htmlFor={p.id} className="form-label">{p.numeroPregunta}. {p.texto}</label> <label htmlFor={p.id} className="form-label mb-3">{p.numeroPregunta}. {p.texto}</label>
{p.tipo === 'seleccionUnica' && p.opciones && ( {p.tipo === 'seleccionUnica' && p.opciones && (
<div> <div className="mb-2">
{p.opciones.map((op) => ( {p.opciones.map((op) => (
<div key={op} className="SINO"> <div key={op} className="SINO">
<label> <label className="mb-3">
<input type="radio" name={p.id} checked={respuestas[p.id] === op} onChange={() => setSingle(p.id, op)} /> {op} <input type="radio" name={p.id} checked={respuestas[p.id] === op} onChange={() => setSingle(p.id, op)} /> {op}
</label> </label>
</div> </div>
@@ -316,7 +316,7 @@ export default function CuestionarioResponsbale2({
)} )}
{p.tipo === 'texto' && ( {p.tipo === 'texto' && (
<div> <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" /> <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" />
</div> </div>
)} )}
</div> </div>
@@ -326,22 +326,22 @@ export default function CuestionarioResponsbale2({
return ( return (
<div key={t.idTabla} className="mb-6 table-responsive"> <div key={t.idTabla} className="mb-6 table-responsive">
{(index === 0 || t.numeroPregunta !== (sortedItems[index - 1] as Tabla).numeroPregunta) && ( {(index === 0 || t.numeroPregunta !== (sortedItems[index - 1] as Tabla).numeroPregunta) && (
<h3 className="mb-3">{t.numeroPregunta}. {t.preguntaTabla}</h3> <h5 className="mb-3 mt-4">{t.numeroPregunta}. {t.preguntaTabla}</h5>
)} )}
{t.subPreguntaTabla && <h5 className="mb-3">{t.subPreguntaTabla}</h5>} {t.subPreguntaTabla && <h5 className="mb-3">{t.subPreguntaTabla}</h5>}
<table className="table table-bordered text-center"> <table className="table table-bordered text-center">
<thead> <thead>
<tr> <tr>
<th></th> <th className="bg-transparent"></th>
{(t.renglones[0].opciones || []).map((op) => <th key={op}>{op}</th>)} {(t.renglones[0].opciones || []).map((op) => <th key={op} className="bg-transparent">{op}</th>)}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{t.renglones.map((r) => ( {t.renglones.map((r) => (
<tr key={r.idRenglon}> <tr key={r.idRenglon}>
<td>{r.textoRenglon}</td> <td className="bg-transparent">{r.textoRenglon}</td>
{(r.opciones || []).map((op) => ( {(r.opciones || []).map((op) => (
<td key={op}> <td key={op} className="bg-transparent">
<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)} /> <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> </td>
))} ))}
+64 -38
View File
@@ -60,6 +60,14 @@ export default function NuevoServicio({
const [minDate2, setMinDate2] = useState<Date>(new Date()); const [minDate2, setMinDate2] = useState<Date>(new Date());
const [programa, setPrograma] = useState<Programa>({}); const [programa, setPrograma] = useState<Programa>({});
const handleFechaInicioChange = (date: Date | null) => {
setFechaInicio(date || new Date());
}
const handleFechaFinChange = (date: Date | null) => {
setFechaFin(date || new Date());
}
const resetar = () => { const resetar = () => {
setCorreo(""); setCorreo("");
setProfesor(""); setProfesor("");
@@ -187,7 +195,7 @@ export default function NuevoServicio({
return ( return (
<div> <div>
<div className="mb-4"> <div className="mb-4">
<label className="form-label">Número de Cuenta</label> <label className="form-label fw-bolder">Número de Cuenta</label>
<div className="d-flex gap-2"> <div className="d-flex gap-2">
<InputGroup.Text> <InputGroup.Text>
<FaSchool /> <FaSchool />
@@ -205,23 +213,29 @@ export default function NuevoServicio({
</div> </div>
</div> </div>
<div className="mb-3"> {alumno.nombre && (
<label className="form-label">Nombre</label> <div className="mb-3">
<p className="form-control">{alumno.nombre}</p> <label className="form-label fw-bolder">Nombre</label>
</div> <p className="form-control">{alumno.nombre}</p>
</div>
)}
{alumno.carrera && (
<div className="mb-3">
<label className="form-label fw-bolder">Carrera</label>
<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>
<p className="form-control">{alumno.creditos ? parseInt(String(alumno.creditos)) : ''}{alumno.creditos ? '%' : ''}</p>
</div>
)}
<div className="mb-3"> <div className="mb-3">
<label className="form-label">Carrera</label> <label className="form-label fw-bolder">Seleccione programa</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({}); } }}> <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> <option value={JSON.stringify({})} disabled>Seleccionar</option>
{programas.map((p, i) => ( {programas.map((p, i) => (
@@ -230,30 +244,36 @@ export default function NuevoServicio({
</select> </select>
</div> </div>
<div className="mb-3"> {programa.institucion && (
<label className="form-label">Institución</label> <div className="mb-3">
<p className="form-control">{programa.institucion}</p> <label className="form-label fw-bolder">Institución</label>
</div> <p className="form-control">{programa.institucion}</p>
</div>
)}
<div className="mb-3"> {programa.dependencia && (
<label className="form-label">Dependencia</label> <div className="mb-3">
<p className="form-control">{programa.dependencia}</p> <label className="form-label fw-bolder">Dependencia</label>
</div> <p className="form-control">{programa.dependencia}</p>
</div>
)}
<div className="mb-3"> {programa.clavePrograma && (
<label className="form-label">Clave del programa</label> <div className="mb-3">
<p className="form-control">{programa.clavePrograma}</p> <label className="form-label fw-bolder">Clave del programa</label>
</div> <p className="form-control">{programa.clavePrograma}</p>
</div>
)}
{programa.acatlan && ( {programa.acatlan && (
<> <>
<div className="mb-3"> <div className="mb-3">
<label className="form-label">Programa interno</label> <label className="form-label fw-bolder">Programa interno</label>
<input className="form-control" placeholder="Programa interno" value={programaInterno} onChange={(e) => setProgramaInterno(e.target.value)} /> <input className="form-control" placeholder="Programa interno" value={programaInterno} onChange={(e) => setProgramaInterno(e.target.value)} />
</div> </div>
<div className="mb-3"> <div className="mb-3">
<label className="form-label">Profesor/Responsable de Programa</label> <label className="form-label fw-bolder">Profesor/Responsable de Programa</label>
<input className="form-control" placeholder="Profesor/Responsable de Programa" value={profesor} onChange={(e) => setProfesor(e.target.value)} /> <input className="form-control" placeholder="Profesor/Responsable de Programa" value={profesor} onChange={(e) => setProfesor(e.target.value)} />
</div> </div>
</> </>
@@ -309,7 +329,7 @@ export default function NuevoServicio({
<Col> <Col>
<FormGroup> <FormGroup>
<FormLabel>Fecha de inicio</FormLabel> <FormLabel className="fw-bolder">Fecha de inicio</FormLabel>
<InputGroup> <InputGroup>
<InputGroup.Text> <InputGroup.Text>
<FaRegCalendarAlt /> <FaRegCalendarAlt />
@@ -317,6 +337,7 @@ export default function NuevoServicio({
<DatePicker <DatePicker
selected={fechaInicio} selected={fechaInicio}
onChange={handleFechaInicioChange}
minDate={minDate} minDate={minDate}
dateFormat="dd-MM-yyyy" dateFormat="dd-MM-yyyy"
className="form-control" className="form-control"
@@ -329,7 +350,7 @@ export default function NuevoServicio({
<Col> <Col>
<FormGroup> <FormGroup>
<FormLabel>Fecha de fin</FormLabel> <FormLabel className="fw-bolder">Fecha de fin</FormLabel>
<InputGroup> <InputGroup>
<InputGroup.Text> <InputGroup.Text>
<FaRegCalendarAlt /> <FaRegCalendarAlt />
@@ -337,6 +358,7 @@ export default function NuevoServicio({
<DatePicker <DatePicker
selected={fechaFin} selected={fechaFin}
onChange={handleFechaFinChange}
value={fechaFin.toISOString().substring(0,10)} value={fechaFin.toISOString().substring(0,10)}
minDate={minDate2} minDate={minDate2}
dateFormat="dd-MM-yyyy" dateFormat="dd-MM-yyyy"
@@ -348,7 +370,8 @@ export default function NuevoServicio({
</FormGroup> </FormGroup>
</Col> </Col>
<div className="mb-3"> {/*
<div className="mb-3">
<label className="form-label">Fecha de inicio</label> <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))} /> <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> </div>
@@ -359,14 +382,15 @@ 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))} /> <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>
)} )}
*/}
<div className="mb-3"> <div className="mb-3">
<label className="form-label">Correo electrónico del Alumno</label> <label className="form-label fw-bolder">Correo electrónico del Alumno</label>
<input type="email" className="form-control" placeholder="Email" value={correo} onChange={(e) => setCorreo(e.target.value)} /> <input type="email" className="form-control" placeholder="Email" value={correo} onChange={(e) => setCorreo(e.target.value)} />
</div> </div>
<FormGroup className="mb-3"> <FormGroup className="mb-3">
<FormLabel>Carta de aceptación (en formato .PDF. No se aceptan fotos).</FormLabel> <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("fileInput")?.click()}> <div className="border p-4 text-center rounded" style={{ cursor: "pointer" }} onClick={() => document.getElementById("fileInput")?.click()}>
<FaUpload size={40} className="mb-2"/> <FaUpload size={40} className="mb-2"/>
<p className="mb-1"> <p className="mb-1">
@@ -384,7 +408,8 @@ export default function NuevoServicio({
/> />
</FormGroup> </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> <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])} /> <input type="file" accept="application/pdf" className="form-control" onChange={(e) => onFileChange(e.target.files?.[0])} />
<div className="mt-2 text-muted"> <div className="mt-2 text-muted">
@@ -393,9 +418,10 @@ 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> <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> </div>
*/}
<div className="has-text-centered mt-4"> <div className="text-center mt-4 mb-3">
<button disabled={mostrarBoton()} onClick={() => imprimirWarning('¿Esta seguro(a) de querer Pre-Registrar a este alumno?', preRegistrar)}> <button className="btn btn-success" disabled={mostrarBoton()} onClick={() => imprimirWarning('¿Esta seguro(a) de querer Pre-Registrar a este alumno?', preRegistrar)}>
Enviar archivo Enviar archivo
</button> </button>
</div> </div>
+243 -230
View File
@@ -2,96 +2,107 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { axiosInstance } from "@/api/config"; import { axiosInstance } from "@/api/config";
import { ServicioSocialConCasoEspecial, ServicioSocialResponse } from "@/types/responses"; import {
ServicioSocialConCasoEspecial,
ServicioSocialResponse,
} from "@/types/responses";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
interface Props { interface Props {
data?: ServicioSocialResponse[]; data?: ServicioSocialResponse[];
total?: number; total?: number;
onPageChange?: (newPage: number) => void; onPageChange?: (newPage: number) => void;
columnaFechaInicio?: boolean; columnaFechaInicio?: boolean;
columnaFechaFin?: boolean; columnaFechaFin?: boolean;
idTipoUsuario?: number; // 1=admin, 2=responsable, 3=alumno, 4=caso especial idTipoUsuario?: number; // 1=admin, 2=responsable, 3=alumno, 4=caso especial
columnasResponsable?: boolean; columnasResponsable?: boolean;
onRowAction?: (row: ServicioSocialResponse, path: string) => void; onRowAction?: (row: ServicioSocialResponse, path: string) => void;
columnaCuestionario?: boolean; columnaCuestionario?: boolean;
columnaCartaTermino?: boolean; columnaCartaTermino?: boolean;
columnaCuestionarioCompleto?: boolean; columnaCuestionarioCompleto?: boolean;
columnaFechaRegistro?: boolean; columnaFechaRegistro?: boolean;
} }
export default function ServicioSocialTabla({ export default function ServicioSocialTabla({
data, data,
total = 0, total = 0,
onPageChange, onPageChange,
idTipoUsuario, idTipoUsuario,
columnasResponsable = false, columnasResponsable = false,
columnaFechaInicio = false, columnaFechaInicio = false,
columnaFechaFin = false, columnaFechaFin = false,
columnaCuestionario = false, columnaCuestionario = false,
columnaCartaTermino = false, columnaCartaTermino = false,
columnaCuestionarioCompleto = false, columnaCuestionarioCompleto = false,
columnaFechaRegistro = false, columnaFechaRegistro = false,
onRowAction, onRowAction,
}: Props) { }: Props) {
const [info, setInfo] = useState<ServicioSocialResponse[]>(data || []); const [info, setInfo] = useState<ServicioSocialResponse[]>(data || []);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const router = useRouter(); const router = useRouter();
//const columnaFechaInicio = true; //const columnaFechaInicio = true;
//const columnaFechaFin = true; //const columnaFechaFin = true;
// 🔹 Cargar datos del padre si existen // 🔹 Cargar datos del padre si existen
useEffect(() => { useEffect(() => {
if (data && data.length > 0) { if (data && data.length > 0) {
setInfo(data); setInfo(data);
}
}, [data]);
// 🔹 Paginación simple
function handlePrev() {
if (currentPage > 1) {
const np = currentPage - 1;
setCurrentPage(np);
onPageChange?.(np);
}
} }
}, [data]);
function handleNext() { // 🔹 Paginación simple
const lastPage = Math.max(1, Math.ceil(total / 10)); function handlePrev() {
if (currentPage < lastPage) { if (currentPage > 1) {
const np = currentPage + 1; const np = currentPage - 1;
setCurrentPage(np); setCurrentPage(np);
onPageChange?.(np); onPageChange?.(np);
}
} }
}
const servicioSelected = (row: ServicioSocialResponse | ServicioSocialConCasoEspecial) => { function handleNext() {
if (!row) return; 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) { if (idTipoUsuario === 1) {
localStorage.setItem("idServicio", String(row.idServicio)); if (row.idServicio) {
} localStorage.setItem("idServicio", String(row.idServicio));
}
if ("idCasoEspecial" in row && row.idCasoEspecial) { // esta mal esta logica
localStorage.setItem("idCasoEspecial", String(row.idCasoEspecial)); if ("idCasoEspecial" in row && row.idCasoEspecial) {
} localStorage.setItem("idCasoEspecial", String(row.idCasoEspecial));
}
if (row.idServicio) {
router.push("/administrador/servicio"); router.push("/administrador/servicio");
} else if ("idCasoEspecial" in row && row.idCasoEspecial) {
} else if ( router.push("/administrador/casos_especiales/caso_especial");
row.idServicio || }
("idCasoEspecial" in row && row.idCasoEspecial) // Hacer validacion para saber que id fue y redireccionar
) { //router.push("/administrador/servicio");
console.log("Limpiando selección (no admin)"); //router.push('/administrador/casos_especiales/caso_especial')
} } else if (
row.idServicio ||
("idCasoEspecial" in row && row.idCasoEspecial)
) {
console.log("Limpiando selección (no admin)");
} }
};
/* /*
const servicioSelected = (row: ServicioSocialResponse | ServicioSocialConCasoEspecial) => { const servicioSelected = (row: ServicioSocialResponse | ServicioSocialConCasoEspecial) => {
if (!row) return; if (!row) return;
@@ -138,177 +149,179 @@ const servicioSelected = (row: ServicioSocialResponse | ServicioSocialConCasoEsp
*/ */
return ( return (
<section> <section>
{loading ? ( {loading ? (
<div>Cargando...</div> <div>Cargando...</div>
) : ( ) : (
<> <>
<div className="table-responsive"> <div className="table-responsive">
<table className="table table-striped"> <table className="table table-striped">
<thead> <thead>
<tr> <tr>
{columnaFechaRegistro && <th>Fecha Registro</th>} {columnaFechaRegistro && <th>Fecha Registro</th>}
<th>Número de Cuenta</th> <th>Número de Cuenta</th>
<th>Nombre</th> <th>Nombre</th>
<th>Carrera</th> <th>Carrera</th>
{columnaFechaInicio && <th>Fecha Inicio</th>} {columnaFechaInicio && <th>Fecha Inicio</th>}
{columnaFechaFin && <th>Fecha Fin</th>} {columnaFechaFin && <th>Fecha Fin</th>}
<th>Status</th> <th>Status</th>
{columnaCuestionarioCompleto && <th>Cuestionario Completo</th>} {columnaCuestionarioCompleto && (
{columnaCartaTermino && <th>Carta Termino</th>} <th>Cuestionario Completo</th>
{columnaCuestionario && <th>Cuestionario</th>} )}
</tr> {columnaCartaTermino && <th>Carta Termino</th>}
</thead> {columnaCuestionario && <th>Cuestionario</th>}
<tbody> </tr>
{info.map((item, index) => ( </thead>
<tr <tbody>
key={index} {info.map((item, index) => (
style={{ <tr
cursor: idTipoUsuario === 1 ? "pointer" : "default", key={index}
}} style={{
onClick={() => ( cursor: idTipoUsuario === 1 ? "pointer" : "default",
idTipoUsuario === 1 ? servicioSelected(item) : undefined }}
)} onClick={() =>
//onClick={() => Falta corregir idTipoUsuario === 1 ? servicioSelected(item) : undefined
//idTipoUsuario === 1 ? servicioSelected(item) : undefined
//}
>
{columnaFechaRegistro && <td>{formatDate(item.createdAt)}</td>}
<td>{item.Usuario?.usuario}</td>
<td>{item.Usuario?.nombre}</td>
<td>{item.Carrera?.carrera}</td>
{columnaFechaInicio && (
<td>{formatDate(item.fechaInicio)}</td>
)}
{columnaFechaFin && <td>{formatDate(item.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))
} }
> //onClick={() => Falta corregir
Siguiente //idTipoUsuario === 1 ? servicioSelected(item) : undefined
</button> //}
</div> >
{columnaFechaRegistro && (
<td>{formatDate(item.createdAt)}</td>
)}
<td>{item.usuario?.usuario}</td>
<td>{item.usuario?.nombre}</td>
<td>{item.carrera?.carrera}</td>
{columnaFechaInicio && (
<td>{formatDate(item.fechaInicio)}</td>
)}
{columnaFechaFin && <td>{formatDate(item.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>
</> </div>
)} </>
</section> )}
); </section>
);
} }
function formatDate(value?: string | null): string { 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 // 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())) { if (isNaN(d.getTime())) {
console.warn("⚠️ Fecha inválida:", value); console.warn("⚠️ Fecha inválida:", value);
return ""; return "";
} }
const day = String(d.getDate()).padStart(2, "0"); const day = String(d.getDate()).padStart(2, "0");
const month = String(d.getMonth() + 1).padStart(2, "0"); const month = String(d.getMonth() + 1).padStart(2, "0");
const year = d.getFullYear(); const year = d.getFullYear();
return `${day}/${month}/${year}`; return `${day}/${month}/${year}`;
} }
function statusClass(id?: number) { function statusClass(id?: number) {
switch (id) { switch (id) {
case 1: case 1:
return "dark"; return "dark";
case 2: case 2:
return "info"; return "info";
case 3: case 3:
return "warning"; return "warning";
case 4: case 4:
return "primary"; return "primary";
case 5: case 5:
case 6: case 6:
return "success"; return "success";
case 7: case 7:
case 8: case 8:
case 9: case 9:
case 10: case 10:
return "danger"; return "danger";
default: default:
return "secondary"; return "secondary";
} }
} }
+36 -36
View File
@@ -1,60 +1,60 @@
import { AxiosRequestConfig } from "axios"; import { AxiosRequestConfig } from "axios";
export interface ServicioSocialResponse { export interface ServicioSocialResponse {
idServicio?: number; idServicio?: number;
id?: number; id?: number;
createdAt?: string; createdAt?: string;
fecha_creacion?: string | null; fecha_creacion?: string | null;
fechaFin?: string | null; fechaFin?: string | null;
fechaInicio?: string | null; fechaInicio?: string | null;
fecha_registro?: string | null; fecha_registro?: string | null;
Usuario?: Usuario; usuario?: Usuario;
Carrera?: Carrera; carrera?: Carrera;
Status?: Status; status?: Status;
cartaTermino?: boolean; cartaTermino?: boolean;
idCuestionarioPrograma?: number | null; idCuestionarioPrograma?: number | null;
idCuestionarioPrograma2?: number | null; idCuestionarioPrograma2?: number | null;
cuestionarioCompletado?: boolean; cuestionarioCompletado?: boolean;
} }
export interface Usuario { export interface Usuario {
idUsuario?: number; idUsuario?: number;
usuario: string | number; usuario: string | number;
nombre: string; nombre: string;
idservicio?: number; idservicio?: number;
} }
export interface OtraTabla { export interface OtraTabla {
title: string; title: string;
admin: Record<string, unknown>; admin: Record<string, unknown>;
alumno: Record<string, unknown>; alumno: Record<string, unknown>;
imprimirError?: (...args: unknown[]) => void; imprimirError?: (...args: unknown[]) => void;
imprimirMensaje?: (...args: unknown[]) => void; imprimirMensaje?: (...args: unknown[]) => void;
imprimirWairning?: (...args: unknown[]) => void; imprimirWairning?: (...args: unknown[]) => void;
updateIsLoading?: (...args: unknown[]) => void; updateIsLoading?: (...args: unknown[]) => void;
} }
export interface Carrera { export interface Carrera {
idCarrera?: number; idCarrera?: number;
carrera: string; carrera: string;
} }
export interface Status { export interface Status {
idStatus: number; idStatus: number;
status: string; status: string;
} }
export interface CargaMasivaProps { export interface CargaMasivaProps {
admin: { tokenArchivo: string }; admin: { tokenArchivo: string };
} }
export interface Responsables { export interface Responsables {
idUsuario: number; idUsuario: number;
usuario: string; usuario: string;
nombre: string; nombre: string;
activo: boolean; activo: boolean;
} }
interface ServicioSocialConCasoEspecial extends ServicioSocialResponse { interface ServicioSocialConCasoEspecial extends ServicioSocialResponse {
idCasoEspecial?: number; idCasoEspecial?: number;
} }