3 Commits

44 changed files with 1809 additions and 2404 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

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