11 Commits

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

After

Width:  |  Height:  |  Size: 6.9 KiB

+45 -43
View File
@@ -1,59 +1,61 @@
import axios from 'axios';
import axios from "axios";
// Crea una instancia base de Axios
export const axiosInstance = axios.create({
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)
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)
});
// 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}`;
//Quitar estas lienas
config.headers['token'] = token;
config.headers['x-access-token'] = token;
config.headers['token-v2'] = 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}`;
}
return config;
},
(error) => Promise.reject(error)
//Quitar estas lienas
config.headers["token"] = token;
config.headers["x-access-token"] = token;
config.headers["token-v2"] = token;
}
}
return config;
},
(error) => Promise.reject(error)
);
// Interceptor para manejar respuestas y errores globales
axiosInstance.interceptors.response.use(
(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);
}
return Promise.reject(error);
// 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);
}
);
@@ -1,12 +1,23 @@
'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 />
<TablaCasosEspeciales admin={admin ?? undefined}/>
</section>
);
}
+24 -6
View File
@@ -1,20 +1,36 @@
'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 updateIsLoading = (loading: boolean) => {
// console.log("Loading:", loading);
//};
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]);
return (
<section className="container px-2 pb-5">
<BotonRegresar />
<h2 className="title mb-4">Reportes</h2>
<h2 className="title mb-4 fw-bold">Reportes</h2>
<Cuestionario
years={years}
admin={admin}
@@ -23,7 +39,9 @@ export default function Registro() {
<Reporte admin={admin}/>
<GustavoBazPrada />
<GustavoBazPrada
years={years}
/>
</section>
);
}
@@ -63,33 +63,35 @@ export default function Editar() {
}, []);
return (
<section>
<BotonRegresar />
<section className="container px-2 pb-5">
<div className="pt-4">
<BotonRegresar />
</div>
<EditarResponsable
<EditarResponsable
responsable={data}
imprimirError={imprimirError}
imprimirMensaje={imprimirMensaje}
imprimirWarning={imprimirWarning}
updateIsLoading={updateIsLoading}
/>
<ReasignacionProgramas
responsable={data}
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}
/>
*/}
{/*
<EditarResponsable
token={token ?? undefined}
imprimirError={imprimirError}
imprimirMensaje={imprimirMensaje}
imprimirWarning={imprimirWarning}
updateIsLoading={updateIsLoading}
/>
*/}
</section>
);
}
@@ -46,7 +46,7 @@ export default function Servicio() {
}, []);
return (
<section>
<section className="container">
<BotonRegresar />
{/* <EditarServicio admin={}/> */}
<EditarServicio
+6 -4
View File
@@ -239,7 +239,7 @@ export default function Servicio() {
<section className="container px-2 pb-6">
<BotonRegresar />
<h3 className="title">{datos.Status?.status}</h3>
<h3 className="container fw-bold title">{datos.Status?.status}</h3>
<InformacionServicio
datos={datos}
@@ -265,7 +265,7 @@ export default function Servicio() {
{datos.cartaTermino && (
<Archivo
title={datos.cartaTermino ? "carta de término" : ""}
title={datos.cartaTermino ? "carta de termino" : ""}
datos={datos}
admin={admin}
imprimirMensaje={imprimirMensaje}
@@ -287,7 +287,7 @@ export default function Servicio() {
/>
)}
<div>
<div className='container'>
{datos.idCuestionarioPrograma || datos.idCuestionarioPrograma2 ? (
<p className="my-4">
<strong>Cuenta con cuestionario de programa resuelto.</strong>
@@ -373,7 +373,9 @@ export default function Servicio() {
*/}
<BotonRegresar />
<div className='container mt-4'>
<BotonRegresar />
</div>
{isLoading && (
<div className="loading-overlay">
+20 -2
View File
@@ -8,8 +8,26 @@ import FullCuestionario from '@/components/alumno/cuestionario2/full-cuestionari
export default function Page() {
return (
<div className='bg-light'>
<BotonRegresar />
<div className='container bg-light'>
<div className='mt-4'>
<BotonRegresar />
</div>
<div className='my-1'>
<h1 className='is-size-2'>2025 evaluación del universitario(a) sobre su servicio social</h1>
<p>
Te invitamos a compartir tu experiencia respecto del programa de servicio social en el que
participaste, a efecto de mejorar la oferta de programas disponibles para los alumnos que
desean liberar este requisito. Te recordamos que tus respuestas son confidenciales.
Los datos están sujetos al aviso de privacidad integral que se puede consultar en el sitio
web: <a
href="https://www.acatlan.unam.mx/normatividad"
className="column text-decoration-none text-morado"
target="_blank"> www.acatlan.unam.mx/normatividad </a>
</p>
</div>
<FullCuestionario />
</div>
);
+122 -27
View File
@@ -1,27 +1,107 @@
"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>
<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)}
/>
{ data?.Status.idStatus && (
<BarraProgreso idStatus={data?.Status.idStatus}/>
)}
<InformacinoServicio
servicio={{
<div className="my-5">
<MensajeAlumno Status={{ idStatus: 2 }} />
</div>
{/* servicio={{
Programa: {
institucion: "UNAM",
dependencia: "Académicos",
@@ -35,25 +115,40 @@ export default function Home() {
fechaInicio: new Date().toISOString(),
fechaFin: new Date().toISOString(),
createdAt: new Date().toISOString(),
}}
/>
}} */}
{ data && (
<InformacinoServicio
servicio={data}
/>
)}
<MensajeAlumno Status={{ idStatus: 2 }} />
{ data?.Status.idStatus === 2 && (
<CompletarDatosPersonales
idServicio={123}
alumno={{ token: { headers: { Authorization: "Bearer ..." } }, tokenArchivo: { headers: { Authorization: "Bearer ..." } } }}
imprimirMensaje={(msg) => console.log(msg)}
imprimirWarning={(msg, callback) => { if (confirm(msg)) callback(); }}
imprimirError={(err) => console.error(err)}
obtenerServicio={() => console.log("obtener servicio")}
updateIsLoading={(loading) => console.log("loading", loading)}
/>
)}
<PreTermino
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)}
/>
{ 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)}
/>
)}
</div>
);
}
+2 -2
View File
@@ -25,10 +25,10 @@ export default function Nuevo() {
}
return (
<div>
<div className="container">
<BotonRegresar />
<h2>Agregar un Servicio Social</h2>
<h2 className="fw-bold">Agregar un Servicio Social</h2>
<CasoEspecialForm
imprimirError={imprimirError}
+2 -2
View File
@@ -49,8 +49,8 @@ export default function Page() {
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 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>
{responsable.idTipoUsuario !== undefined && responsable.token ? (
+9
View File
@@ -3,6 +3,15 @@ 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,
+93 -69
View File
@@ -1,85 +1,109 @@
'use client'
"use client";
import { axiosInstance } from "@/api/config";
import { useRouter } from "next/navigation";
import React from "react";
export default function Home() {
const router = useRouter();
const router = useRouter();
const handleOnSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const object = Object.fromEntries(formData);
const handleOnSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const object = Object.fromEntries(formData);
try {
const res = await axiosInstance.post('/usuario/login', object);
try {
const res = await axiosInstance.post("/auth/login", object);
// Extraer datos del backend
const token = res?.data?.token ?? '';
const usuarioObj = res?.data?.Usuario ?? res?.data ?? {};
const idUsuario = usuarioObj.idUsuario ?? '';
const usuario = usuarioObj.usuario ?? '';
const nombre = usuarioObj.nombre ?? '';
const idTipoUsuario = Number(usuarioObj.TipoUsuario?.idTipoUsuario ?? 0);
//const idTipoUsuario = Number((usuarioObj as any).TipoUsuario?.idTipoUsuario ?? 0);
// Extraer datos del backend
const token = res?.data?.token ?? "";
const usuarioObj = res?.data?.Usuario ?? res?.data ?? {};
const idUsuario = usuarioObj.idUsuario ?? "";
const usuario = usuarioObj.usuario ?? "";
const nombre = usuarioObj.nombre ?? "";
const idTipoUsuario = Number(usuarioObj.TipoUsuario?.idTipoUsuario ?? 0);
//const idTipoUsuario = Number((usuarioObj as any).TipoUsuario?.idTipoUsuario ?? 0);
// Guardar en localStorage
localStorage.setItem("token", String(token));
localStorage.setItem("idUsuario", String(idUsuario));
localStorage.setItem("usuario", String(usuario));
localStorage.setItem("nombre", String(nombre));
localStorage.setItem("idTipoUsuario", String(idTipoUsuario));
// Guardar en localStorage
localStorage.setItem('token', String(token));
localStorage.setItem('idUsuario', String(idUsuario));
localStorage.setItem('usuario', String(usuario));
localStorage.setItem('nombre', String(nombre));
localStorage.setItem('idTipoUsuario', String(idTipoUsuario));
// Validar y redirigir según el idTipoUsuario
if (token && idUsuario && idTipoUsuario) {
switch (idTipoUsuario) {
case 1:
router.push('/administrador');
break;
case 2:
router.push('/responsable');
break;
case 3:
router.push('/alumno');
break;
case 4:
router.push('/casoEspecial');
break;
default:
console.warn(`Tipo de usuario desconocido: ${idTipoUsuario}`);
localStorage.clear();
break;
}
} else {
localStorage.clear();
console.error('Error: datos de usuario incompletos');
}
} catch (error) {
console.error('Error en el inicio de sesión:', error);
// Validar y redirigir según el idTipoUsuario
if (token && idUsuario && idTipoUsuario) {
switch (idTipoUsuario) {
case 1:
router.push("/administrador");
break;
case 2:
router.push("/responsable");
break;
case 3:
router.push("/alumno");
break;
case 4:
router.push("/casoEspecial");
break;
default:
console.warn(`Tipo de usuario desconocido: ${idTipoUsuario}`);
localStorage.clear();
break;
}
};
} else {
localStorage.clear();
console.error("Error: datos de usuario incompletos");
}
} catch (error) {
console.error("Error en el inicio de sesión:", error);
localStorage.clear();
}
};
return (
<div className="d-flex justify-content-center align-items-center bg-light" style={{ minHeight: 'calc(100vh - 200px)' }}>
<form className="p-4 shadow rounded bg-white w-100" style={{ maxWidth: '400px' }} onSubmit={handleOnSubmit}>
<h2 className="text-center mb-4 fw-bold">IRIS</h2>
return (
<div
className="d-flex justify-content-center align-items-center bg-light"
style={{ minHeight: "calc(100vh - 200px)" }}
>
<form
className="p-4 shadow rounded bg-white w-100"
style={{ maxWidth: "400px" }}
onSubmit={handleOnSubmit}
>
<h2 className="text-center mb-4 fw-bold">IRIS</h2>
<div className="mb-3">
<label htmlFor="usuario" className="form-label">Usuario</label>
<input type="text" name="usuario" id="usuario" className="form-control" required />
</div>
<div className="mb-4">
<label htmlFor="password" className="form-label">Contraseña</label>
<input type="password" name="password" id="password" className="form-control" required />
</div>
<div className="d-grid">
<button type="submit" className="btn btn-primary btn-lg">Iniciar Sesión</button>
</div>
</form>
<div className="mb-3">
<label htmlFor="usuario" className="form-label">
Usuario
</label>
<input
type="text"
name="usuario"
id="usuario"
className="form-control"
required
/>
</div>
);
<div className="mb-4">
<label htmlFor="password" className="form-label">
Contraseña
</label>
<input
type="password"
name="password"
id="password"
className="form-control"
required
/>
</div>
<div className="d-grid">
<button type="submit" className="btn btn-primary btn-lg">
Iniciar Sesión
</button>
</div>
</form>
</div>
);
}
+1 -1
View File
@@ -110,7 +110,7 @@ export default function Page() {
</p>
<p>
<a href="https://www.acatlan.unam.mx/normatividad" target="_blank" rel="noreferrer"> www.acatlan.unam.mx/normatividad</a>
<a href="https://www.acatlan.unam.mx/normatividad" target="_blank" rel="noreferrer" className="text-decoration-none text-morado"> www.acatlan.unam.mx/normatividad</a>
</p>
</div>
+1 -1
View File
@@ -81,7 +81,7 @@ export default function Page() {
</p>
</div>
<h2 className="title">Añadir Servicio Social</h2>
<h2 className="title fw-bold mt-4 mb-4">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" onClick={() => router.push('/responsable/nuevo')}>Agregar alumno</button>
<button className="button is-info rounded-2" onClick={() => router.push('/responsable/nuevo')}>Agregar alumno</button>
</div>
{tabla}
+18 -5
View File
@@ -145,13 +145,25 @@ export default function Archivo({
axiosInstance.put(`/servicio/rechazar_informe`, data);
return (
<div className="mb-4">
<div className="container mb-4">
<div className="flex items-center">
<h6 className="pr-4 text-lg">Ver {title}</h6>
<div className="pr-4">
<h5 className="pr-4 text-lg">
Ver {title}
<a
className="btn btn-link btn-light"
className="btn btn-link btn-light text-decoration-none ms-3 text-morado"
target="_blank"
rel="noopener noreferrer"
href={`https://drive.google.com/file/d/${archivo()}/view?usp=sharing`}
style={{ background: '#eae4f8'}}
>
Ver
</a>
</h5>
{/*
<div className="pr-4">
<a
className="btn btn-link btn-light text-decoration-none"
target="_blank"
rel="noopener noreferrer"
href={`https://drive.google.com/file/d/${archivo()}/view?usp=sharing`}
@@ -159,6 +171,7 @@ 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="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"
/>
<div className="container mt-5 space-y-4">
{/* Botón de Cancelar */}
<button
disabled={!mensajeCancelar}
onClick={() =>
imprimirWarning(
"¿Seguro(a) que quiere cancelar este servicio?",
cancelarServicio
)
}
className={`px-4 py-2 rounded text-white ${
!mensajeCancelar
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
? "bg-gray-400 cursor-not-allowed"
: "bg-blue-600 hover:bg-blue-700"
: "bg-red-600 hover:bg-red-700"
}`}
>
Cancelar servicio
Cancelar
</button>
</div>
)}
{/* Área de mensaje */}
{cancelar && (
<div className="space-y-2 mt-3">
<label className="block font-medium mb-2">Razón de la cancelación:</label>
<textarea
maxLength={500}
value={mensajeCancelar}
onChange={(e) => setMensajeCancelar(e.target.value)}
className="form-control border rounded p-2 w-full"
/>
<button
disabled={!mensajeCancelar}
onClick={() =>
imprimirWarning(
"¿Seguro(a) que quiere cancelar este servicio?",
cancelarServicio
)
}
className={`bg-morado mt-3 mb-4 px-4 py-2 rounded text-white ${
!mensajeCancelar
? "bg-gray-400 cursor-not-allowed"
: "bg-blue-600 hover:bg-blue-700"
}`}
>
Cancelar servicio
</button>
</div>
)}
</div>
);
}
@@ -119,7 +119,7 @@ export default function ConfirmarServicio({
};
return (
<div className="space-y-4">
<div className="container space-y-4">
{datos.Status?.idStatus === 5 && datos.Programa?.acatlan && (
<div className="flex items-center space-x-2">
<input
+42 -15
View File
@@ -1,5 +1,6 @@
"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";
@@ -10,26 +11,50 @@ 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(`/${selectedCuestionario}`, {
//const res = await axiosInstance.get(`/cuestionario_alumno?year=${selectedCuestionario}&version=${version}`, {
const res = await axiosInstance.get(`/cuestionario_alumno`, {
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)
}
@@ -42,9 +67,10 @@ export default function Cuestionario({ years, admin, updateIsLoading }: Props) {
<div>
<Col>
<FormGroup>
<FormLabel>Cuestionario:</FormLabel>
<FormLabel className="fw-semibold">Cuestionario:</FormLabel>
<InputGroup>
<FormSelect>
<FormSelect onChange={(e) => setSelectedCuestionario(e.target.value)}>
<option value="">Seleccione un cuestionario:</option>
<option value="cuestionario_alumno">Cuestionario de Alumnos</option>
<option value="cuestionario_programa">Cuestionario de Programas</option>
</FormSelect>
@@ -54,9 +80,10 @@ export default function Cuestionario({ years, admin, updateIsLoading }: Props) {
<Col>
<FormGroup>
<FormLabel>Version:</FormLabel>
<FormLabel className="fw-semibold">Version:</FormLabel>
<InputGroup>
<FormSelect>
<FormSelect value={version} onChange={(e) => setVersion(e.target.value)}>
<option value="">Seleccione una version:</option>
<option value="v1">V1</option>
<option value="v2">V2</option>
</FormSelect>
@@ -66,15 +93,15 @@ export default function Cuestionario({ years, admin, updateIsLoading }: Props) {
<Col>
<FormGroup>
<FormLabel>Año:</FormLabel>
<FormLabel className="fw-semibold">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>
@@ -82,10 +109,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}
>
Enviar archivo
Descragar Excel
</Button>
</div>
</div>
@@ -100,62 +100,64 @@ export default function EditarResponsable({
return (
<div className="space-y-4 mt-6">
<h3 className="text-2xl font-semibold mb-4">Editar información del responsable</h3>
<div className="sspace-y-4 mt-6">
<h2 className="text-2xl mb-4 fw-semibold">Editar información del responsable</h2>
{/* Nombre */}
<div>
<label className="block mb-1 font-medium">Nombre</label>
<input
type="text"
placeholder={responsable.nombre}
value={nuevo.nombre || ""}
onChange={(e) => setNuevo({ ...nuevo, nombre: e.target.value })}
className="border rounded p-2 w-full"
/>
</div>
{/* Correo */}
<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>
{/* 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>
{/* 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>
{/* 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>
<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>
{/* 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>
</div>
);
}
+225 -132
View File
@@ -8,6 +8,8 @@ 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 };
@@ -87,7 +89,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("/admin/servicio");
router.push("/administrador/servicio");
}
setFechaInicio(new Date(data.fechaInicio));
@@ -146,13 +148,18 @@ 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`, data);
const res = await axiosInstance.put(`/usuario/new_password_alumno`, { idServicio: servicioid });
imprimirMensaje(res.data.message);
updateIsLoading(false);
router.push("/admin");
router.push("/administrador");
} 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);
@@ -211,7 +218,7 @@ export default function EditarServicio({
useEffect(() => {
const id = Number(localStorage.getItem("idServicio"));
console.log("ID Servicio para editar:", id);
if (!id) router.push("/admin");
if (!id) router.push("/administrador");
else {
setServicioid(id);
console.log("idServicio seteado:", id);
@@ -223,143 +230,229 @@ export default function EditarServicio({
return (
<div className="space-y-4 mt-6">
<h3 className="text-2xl font-semibold mb-4">
Editar información del alumno
</h3>
<h2 className="fw-bold mb-4">
Editar información del alumno
</h2>
{/* 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"
/>
{/* 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"
/>
</div>
)}
{/* 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"
/>
{/* 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"
/>
</div>
)}
{/* 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"
/>
{/* 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"
/>
</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>
{/* 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>
)}
{/* 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>
{/* 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>
)}
<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>
{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>
</div>
);
}
@@ -33,6 +33,25 @@ export default function GustavoBazPrada({
}
);
// Crear blob manualmente
const blob = new Blob([res.data], { type: "text/csv" });
// Crear URL temporal para descargar
const url = window.URL.createObjectURL(blob);
// Crear link "virtual"
const link = document.createElement("a");
link.href = url;
link.download = `${selectedYear}_gustavo_baz_prada.csv`;
// Disparar descarga
document.body.appendChild(link);
link.click();
// limpiar URL temporal
link.remove();
window.URL.revokeObjectURL(url);
//fileDownload(res.data, `${selectedYear}_gustavo_baz_prada.csv`);
setSelectedYear("");
@@ -51,20 +70,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,11 +56,100 @@ export default function InformacionCasoEspecial({ alumno }: Props) {
return (
<div className="mt-4">
{/* Título */}
<h3 className="text-primary fw-bold mb-3">{alumno.Status?.status}</h3>
<h2 className="fw-bold mb-3">{alumno.Status?.status}</h2>
<h4 className="mb-4">Datos personales</h4>
<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}>
@@ -117,7 +206,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}>
@@ -174,6 +263,7 @@ 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="fw-bold mb-4">Datos del programa</h3>
<h3 className="mt-3 mb-2">Datos del programa</h3>
<div className="mb-3">
<label className="form-label fw-semibold">Institucion:</label>
@@ -178,11 +178,13 @@ export default function InformacionServicio({ admin, imprimirError, updateIsLoad
<p className="form-control">{datos.Usuario?.nombre || '-'}</p>
</div>
<div className="mb-3">
<label className="form-label fw-semibold">Fecha de nacimiento:
<p className="form-control">{datos.fechaNacimiento}</p>
</label>
</div>
{datos.fechaNacimiento && (
<div className="mb-3">
<label className="form-label fw-semibold">Fecha de nacimiento:
<p className="form-control">{formatDate(datos.fechaNacimiento)}</p>
</label>
</div>
)}
<div className="mb-3">
<label className="form-label fw-semibold">Carrera:</label>
@@ -194,50 +196,63 @@ export default function InformacionServicio({ admin, imprimirError, updateIsLoad
<p className="form-control">{datos.creditos || '-'}</p>
</div>
<div className="mb-3">
<label className="form-label fw-semibold">Telèfono:</label>
<p className="form-control">{datos.telefono}</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">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>
<div className="mb-3">
<label className="form-label fw-semibold">Programa Interno:</label>
<p className="form-control">{datos.programaInterno}</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">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">{datos.createdAt || '-'}</p>
<p className="form-control">{formatDate(datos.createdAt) || '-'}</p>
</div>
<div className="mb-3">
<label className="form-label fw-semibold">fecha de inicio:</label>
<p className="form-control">{datos.fechaInicio || '-'}</p>
<p className="form-control">{formatDate(datos.fechaInicio)|| '-'}</p>
</div>
<div className="mb-3">
<label className="form-label fw-semibold">Fecha de termino:</label>
<p className="form-control">{datos.fechaFin || '-'}</p>
<p className="form-control">{formatDate(datos.fechaFin) || '-'}</p>
</div>
<div className="mb-3">
<label className="form-label fw-semibold">Fecha de liberaciòn:</label>
<p className="form-control">{datos.fechaLiberacion}</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>
<button className="btn btn-primary"
@@ -247,4 +262,23 @@ 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="text-2xl font-semibold mb-3">Reasignación de programas</h3>
<h3 className="fw-semibold text-2xl font-semibold mb-3">Reasignación de programas</h3>
<div className="text-base mb-4">
<p>
@@ -69,21 +69,21 @@ export default function ReasignacionProgramas({
</div>
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-1">
<label className="form-label fw-semibold block text-sm font-medium text-gray-700 mb-2">
Usuario/Correo electrónico
</label>
<input
type="email"
placeholder="Usuario"
value={correoOtroResponsable}
onChange={(e) => setCorreoOtroResponsable(e.target.value)}
className="border border-gray-300 rounded-lg p-2 w-full"
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"
/>
</div>
<div className="pt-5">
<div className="pt-3">
<button
className={`px-4 py-2 rounded-md text-white ${
className={`px-4 py-2 rounded-1 text-white ${
mostrarBoton()
? "bg-gray-400 cursor-not-allowed"
: "bg-blue-600 hover:bg-blue-700"
+23 -5
View File
@@ -34,15 +34,31 @@ export default function Reporte({ admin }: Props) {
const res = await axiosInstance.get("/servicio/reporte", {
params: {
inicio: moment(selectedInicio).format("YYYY-MM-DD"),
fin: moment(selectedFin).format("YYYY-MM-DD"),
},
headers: {
Authorization: `Bearer ${admin.token}`,
inicio: moment(selectedInicio).format("YYYY-MM-DD"),
fin: moment(selectedFin).format("YYYY-MM-DD"),
},
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
@@ -53,6 +69,8 @@ export default function Reporte({ admin }: Props) {
//updateIsLoading(false);
// optional: handle error
}
};
return (
@@ -8,6 +8,7 @@ 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;
@@ -45,6 +46,9 @@ 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);
@@ -130,9 +134,12 @@ 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.");
}
@@ -244,15 +251,27 @@ export default function TablaCasosEspeciales({ admin, imprimirError }: Props) {
{/* Tabla
<ServicioSocialTabla />
onPageChange={onPageChange}
columnasResponsable={admin?.idTipoUsuario === 1}
*/}
<ServicioSocialTabla
data={data}
total={total}
columnaFechaRegistro={true}
columnasResponsable={false}
idTipoUsuario={admin?.idTipoUsuario}
columnasResponsable={admin?.idTipoUsuario === 1}
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,8 +1,18 @@
"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";
@@ -11,197 +21,240 @@ 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);
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 };
}
async function obtenerServicios(pagina?: number) {
const paginaActual = pagina ?? page;
let query = '';
if (search.idStatus)
query += `&idStatus=${encodeURIComponent(search.idStatus)}`;
if (search.nombre) query += `&nombre=${encodeURIComponent(search.nombre)}`;
if (search.numeroCuenta)
query += `&numeroCuenta=${encodeURIComponent(search.numeroCuenta)}`;
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);
}
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);
}
}
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>
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>
<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}>
<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>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}>
<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);
}
}}
/>
<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>
);
}
@@ -14,10 +14,10 @@ export default function TituloStatus({ status }: Props) {
console.log('idStatus en TituloStatus:', idStatus);
return (
<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 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>
);
}
+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: 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" },
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 },
];
return (
<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>
<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;
<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; }
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>
)}
/* 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;
}
{/* 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>
@media (max-width: 768px) {
.step-item { min-width: 140px; padding: 0.45rem 0.6rem; }
.step-title { font-size: 0.85rem; }
}
`}</style>
{/* Título */}
<div
className={`fw-semibold ${
isActive || isCurrent ? "text-dark" : "text-secondary"
}`}
style={{ fontSize: "0.95rem" }}
>
{step.label}
</div>
</div>
);
})}
</div>
</div>
);
}
@@ -7,6 +7,8 @@ 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> };
@@ -74,64 +76,89 @@ export default function CompletarDatosPersonales({
};
return (
<div className="space-y-4">
<h3 className="text-xl font-semibold">Formulario Pre-registro</h3>
<div className="container space-y-4">
<h2 className="text-xl fw-semibold">Formulario Pre-registro</h2>
{/* Fecha de nacimiento */}
<div>
<label className="block mb-1 font-medium">Fecha de nacimiento</label>
<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>
<DatePicker
selected={nacimiento}
onChange={(date: Date | null) => {
if (date) setNacimiento(date);
}}
maxDate={maxDate}
className="border border-gray-300 rounded px-3 py-2 w-full"
className="border border-gray-300 rounded px-3 py-2 form-control"
placeholderText="Fecha de nacimiento"
/>
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
</div>
*/}
{/* Teléfono */}
<div>
<label className="block mb-1 font-medium">Teléfono</label>
<div className="mb-3">
<label className="block mb-1 form-label fw-semibold mb-2">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 w-full"
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"
/>
</div>
{/* Dirección */}
<div>
<label className="block mb-1 font-medium">Dirección</label>
<div className="mb-3">
<label className="block mb-1 form-label fw-semibold mb-2">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 w-full"
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"
/>
</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 ${
!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 mb-5 ${
!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>
<label className="mt-2">
<input className="checkb" type="checkbox" value={op} checked={Array.isArray(respuestas[p.id]) && (respuestas[p.id] as string[]).includes(op)} onChange={(e) => toggleSelection(p.id, op, e)} /> {op}
</label>
</div>
@@ -309,7 +309,7 @@ export default function FullCuestionario() {
)}
{p.tipo === 'texto' && (
<div>
<input id={p.id} className="form-control" type="text" value={(respuestas[p.id] as string) || ''} onChange={(e) => updateRespuestas(p.id, e.target.value)} maxLength={p.limite || 200} placeholder="Escribe tu respuesta aquí" />
<input id={p.id} className="form-control bg-transparent" type="text" value={(respuestas[p.id] as string) || ''} onChange={(e) => updateRespuestas(p.id, e.target.value)} maxLength={p.limite || 200} placeholder="Escribe tu respuesta aquí" />
</div>
)}
</div>
@@ -327,18 +327,18 @@ export default function FullCuestionario() {
<table className="table table-bordered text-center">
<thead>
<tr>
<th></th>
<th className="bg-transparent"></th>
{t.renglones[0].opciones.map((op) => (
<th key={op}>{op}</th>
<th className="bg-transparent" key={op}>{op}</th>
))}
</tr>
</thead>
<tbody>
{t.renglones.map((r) => (
<tr key={r.idRenglon}>
<td>{r.textoRenglon}</td>
<td className="bg-transparent">{r.textoRenglon}</td>
{r.opciones.map((op) => (
<td key={op}>
<td className="bg-transparent" key={op}>
<input type="radio" name={`tabla-${t.idTabla}-renglon-${r.idRenglon}`} value={op} checked={Boolean(tablaResp && tablaResp[r.idRenglon] === op)} onChange={() => handleTableResponse(t.idTabla, r.idRenglon, op)} />
</td>
))}
@@ -351,12 +351,10 @@ export default function FullCuestionario() {
);
})}
<div className="my-6 mb-6 has-text-centered">
<div>
<button className="button is-success is-medium" onClick={submitForm} disabled={!isFormComplete}>
<div className="my-6 mb-6 text-center mb-5">
<button className="btn btn-success is-medium" onClick={submitForm} disabled={!isFormComplete}>
{isLoading ? 'Enviando...' : 'Enviar'}
</button>
</div>
</div>
</div>
+93 -93
View File
@@ -50,116 +50,116 @@ export default function InformacinoServicio({ servicio }: Props) {
};
return (
<div>
{/* Datos del programa */}
<div className="mb-5">
<h4 className="is-size-4 pb-2">Datos del programa</h4>
<div className="container">
{/* Datos del programa */}
<div className="mb-5">
<h4 className="is-size-4 pb-2">Datos del programa</h4>
<div className="mb-2">
<label>Institución:</label>
<p className="input">{servicio.Programa.institucion}</p>
<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>
<div className="mb-2">
<label>Dependencia:</label>
<p className="input">{servicio.Programa.dependencia}</p>
</div>
{/* Datos personales */}
<div className="mb-5">
<h4 className="is-size-4 pb-2">Datos personales</h4>
<div className="mb-2">
<label>Programa:</label>
<p className="input">{servicio.Programa.programa}</p>
</div>
<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>Clave de programa:</label>
<p className="input">{servicio.Programa.clavePrograma}</p>
</div>
</div>
<div className="mb-2">
<label className="form-label fw-semibold">Nombre:</label>
<p className="form-control">{servicio.Usuario.nombre}</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">Carrera:</label>
<p className="form-control">{servicio.Carrera.carrera}</p>
</div>
<div className="mb-2">
<label>Número de cuenta:</label>
<p className="input">{servicio.Usuario.usuario}</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>
<div className="mb-2">
<label>Nombre:</label>
<p className="input">{servicio.Usuario.nombre}</p>
</div>
{servicio.telefono && (
<div className="mb-2">
<label className="form-label">Teléfono:</label>
<p className="form-control">{servicio.telefono}</p>
</div>
)}
<div className="mb-2">
<label>Carrera:</label>
<p className="input">{servicio.Carrera.carrera}</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>Créditos:</label>
<p className="input">
{servicio.creditos ? parseInt(servicio.creditos) : ""}
{servicio.creditos && "%"}
</p>
</div>
<div className="mb-2">
<label className="form-label fw-semibold">Email:</label>
<p className="form-control">{servicio.correo}</p>
</div>
{servicio.telefono && (
<div className="mb-2">
<label>Teléfono:</label>
<p className="input">{servicio.telefono}</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.direccion && (
<div className="mb-2">
<label>Dirección:</label>
<p className="input">{servicio.direccion}</p>
</div>
)}
{servicio.profesor && (
<div className="mb-2">
<label className="form-label fw-semibold">Profesor:</label>
<p className="form-control">{servicio.profesor}</p>
</div>
)}
<div className="mb-2">
<label>Email:</label>
<p className="input">{servicio.correo}</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>
)}
{servicio.programaInterno && (
<div className="mb-2">
<label>Programa Interno:</label>
<p className="input">{servicio.programaInterno}</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>
{servicio.profesor && (
<div className="mb-2">
<label>Profesor:</label>
<p className="input">{servicio.profesor}</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.createdAt && (
<div className="mb-2">
<label>Fecha de registro:</label>
<p className="input">{fecha(servicio.createdAt)}</p>
{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>
)}
<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="mb-6">
<h4 className="block is-size-4">Estimado alumno(a):</h4>
<div className="container mb-6">
<h4 className="block is-size-4 mb-4">Estimado alumno(a):</h4>
<p className="has-text-justified is-size-6">
{Status.idStatus === 2 && (
+110 -64
View File
@@ -3,6 +3,8 @@
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;
@@ -53,95 +55,139 @@ 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>
<h3 className="label">
<div className="container">
<h6 className="label fw-semibold">
Cuestionario de evaluación del programa de servicio social.
</h3>
</h6>
{!servicio.idCuestionarioAlumno && !servicio.idCuestionarioAlumno2 ? (
<div className="mb-6">
<a
href="/alumno/cuestionario"
className="button is-info is-light"
>
<span className="icon">
<i className="fas fa-book-open"></i>
</span>
<span>Cuestionario</span>
</a>
<div 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>
) : (
<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>
)}
<h3 className="label">
<h6 className="label fw-semibold">
Informe global de actividades{" "}
{!servicio.informeGlobal && (
<span>(en formato .PDF. No se aceptan fotos)</span>
)}
.
</h3>
</h6>
{/*
Codigo anterior para el envio del archivo
<div>
<div className="field">
<input
type="file"
accept="application/pdf"
onChange={(e) => {
if (e.target.files && e.target.files[0]) {
setFile(e.target.files[0]);
}
}}
/>
<p className="is-size-6">Tamaño máximo 20MB</p>
</div>
<div className="field has-text-centered my-5">
<button
className="button is-success"
disabled={!file}
onClick={() =>
imprimirWarning(
"¿Estas seguro(a) de querer subir este informe global?",
enviarInformeGlobal
)
}
>
Enviar
</button>
</div>
</div>
*/}
{!servicio.informeGlobal ? (
<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="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 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>
<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>
) : (
<p className="block is-size-6">
Informe Global enviado.{" "}
<span className="icon has-text-success">
<i className="fas fa-check-bold"></i>
</span>
<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>
)}
</div>
+271 -245
View File
@@ -55,6 +55,19 @@ export default function CasoEspecialForm({
const [minDate, setMinDate] = useState<Date>(new Date('2020-01-02'));
const [minDate2, setMinDate2] = useState<Date>(new Date());
// Para mostrar las fechas
const handleFechaInicioChange = (date: Date | null) => {
setFechaInicio(date || new Date());
}
const handleFechaFinChange = (date: Date | null) => {
setFechaFin(date || new Date());
}
const handleFechaNacimientoChange = (date: Date | null) => {
setFechaNacimiento(date || new Date())
}
// Función para resetear campos
const resetear = (): void => {
setDependencia('');
@@ -213,276 +226,289 @@ export default function CasoEspecialForm({
// 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">{alumno.nombre || ''}</p>
</div>
<div className="mb-3">
<label className="form-label">Carrera</label>
<p className="form-control">{alumno.carrera || ''}</p>
</div>
<div className="mb-3">
<label className="form-label">Créditos</label>
<p className="form-control">
{alumno.creditos ? parseInt(alumno.creditos) : ''}{' '}
{alumno.creditos ? '%' : ''}
</p>
</div>
<div className="mb-3">
<label className="form-label">Dirección</label>
<input
type="text"
className="form-control"
maxLength={200}
value={direccion}
onChange={(e) => setDireccion(e.target.value)}
/>
</div>
<div className="mb-3">
<label className="form-label">Teléfono</label>
<input
type="tel"
className="form-control"
maxLength={10}
value={telefono}
onChange={(e) => setTelefono(e.target.value)}
/>
</div>
<div className="mb-3">
<label className="form-label">Correo electrónico</label>
<input
type="email"
className="form-control"
value={correo}
onChange={(e) => setCorreo(e.target.value)}
/>
</div>
<Col>
<FormGroup>
<FormLabel>Fecha de inicio</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
selected={fechaInicio}
minDate={minDate}
dateFormat="dd-MM-yyyy"
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
</InputGroup>
</FormGroup>
</Col>
<Col>
<FormGroup>
<FormLabel>Fecha de fin</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
selected={fechaFin}
value={fechaFin.toISOString().substring(0,10)}
minDate={minDate2}
dateFormat="dd-MM-yyyy"
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
</InputGroup>
</FormGroup>
</Col>
<Col>
<FormGroup>
<FormLabel>Fecha de nacimiento</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
selected={fechaNacimiento}
maxDate={new Date()}
dateFormat="dd-MM-yyyy"
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
</InputGroup>
</FormGroup>
</Col>
<div className="mb-3">
<label className="form-label">Fecha de inicio</label>
<input
type="date"
className="form-control"
value={fechaInicio.toISOString().substring(0, 10)}
onChange={(e) => setFechaInicio(new Date(e.target.value))}
min={minDate.toISOString().substring(0, 10)}
/>
</div>
{fechaInicio && (
<div className="mt-4">
<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)}
/>
<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>
)}
<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>
{alumno.nombre && (
<div className="mb-3">
<label className="form-label">Nombre</label>
<p className="form-control">{alumno.nombre || ''}</p>
</div>
)}
<div className="mb-3">
<label className="form-label">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>
{alumno.carrera && (
<div className="mb-3">
<label className="form-label">Carrera</label>
<p className="form-control">{alumno.carrera || ''}</p>
</div>
)}
{alumno.creditos && (
<div className="mb-3">
<label className="form-label">Créditos</label>
<p className="form-control">
{alumno.creditos ? parseInt(alumno.creditos) : ''}{' '}
{alumno.creditos ? '%' : ''}
</p>
</div>
)}
{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>
<label className="form-label">Dirección</label>
<input
type="text"
className="form-control"
maxLength={250}
value={institucion}
onChange={(e) => setInstitucion(e.target.value)}
maxLength={200}
value={direccion}
onChange={(e) => setDireccion(e.target.value)}
/>
</div>
<div className="mb-3">
<label className="form-label">Dependencia</label>
<label className="form-label">Teléfono</label>
<input
type="text"
type="tel"
className="form-control"
maxLength={250}
value={dependencia}
onChange={(e) => setDependencia(e.target.value)}
maxLength={10}
value={telefono}
onChange={(e) => setTelefono(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 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>
<input
id='fileInput'
type="file"
style={{ display: 'none'}}
onChange={e => setFile(e.target.files ? e.target.files[0] : null)}
/>
</FormGroup>
<Col>
<FormGroup>
<FormLabel>Fecha de inicio</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
selected={fechaInicio}
onChange={handleFechaInicioChange}
minDate={minDate}
dateFormat="dd-MM-yyyy"
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
</InputGroup>
</FormGroup>
</Col>
{fechaInicio && (
<Col>
<FormGroup>
<FormLabel>Fecha de fin</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
selected={fechaFin}
onChange={handleFechaFinChange}
value={fechaFin.toISOString().substring(0,10)}
minDate={minDate2}
dateFormat="dd-MM-yyyy"
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
</InputGroup>
</FormGroup>
</Col>
)}
<Col>
<FormGroup>
<FormLabel>Fecha de nacimiento</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
selected={fechaNacimiento}
maxDate={new Date()}
onChange={handleFechaNacimientoChange}
dateFormat="dd-MM-yyyy"
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
</InputGroup>
</FormGroup>
</Col>
{/*
<div className="mb-3">
<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>
<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="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>
<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>
<BotonRegresar />
{idStatus === '11' && (
<div className="mb-3">
<label className="form-label">Motivo</label>
<select
className="form-select"
value={motivo}
onChange={(e) => setMotivo(e.target.value)}
>
<option value="">Selecciona una opción</option>
<option value="1">Tercera edad</option>
<option value="2">Capacidades diferentes</option>
</select>
</div>
)}
{idStatus === '12' && (
<>
<div className="mb-3">
<label className="form-label">Institución</label>
<input
type="text"
className="form-control"
maxLength={250}
value={institucion}
onChange={(e) => setInstitucion(e.target.value)}
/>
</div>
<div className="mb-3">
<label className="form-label">Dependencia</label>
<input
type="text"
className="form-control"
maxLength={250}
value={dependencia}
onChange={(e) => setDependencia(e.target.value)}
/>
</div>
</>
)}
<FormGroup className='mb-3'>
<FormLabel>Archivo (.zip, .rar)</FormLabel>
<div
className='border p-4 text-center rounded'
style={{ cursor: 'pointer' }}
onClick={() => document.getElementById('fileInput')?.click()}
>
<FaUpload size={40} className='mb-2'/>
<p className='mb-1'>
{file?.name || 'Arrastra aquí tu archivo o da click aquí para buscar'}
</p>
<p className='is-size-6'>Tmaño máximo 20MB</p>
<p className='is-size-6'>Si al momento de elegir un archivo este no se selecciona, haga click en cancelar en la ventana emergente e intente de nuevo.</p>
</div>
<input
id='fileInput'
type="file"
style={{ display: 'none'}}
onChange={e => setFile(e.target.files ? e.target.files[0] : null)}
/>
</FormGroup>
{/*
<div className="mb-3">
<label className="form-label">Archivo (.zip, .rar)</label>
<input
type="file"
accept=".zip,.rar"
className="form-control"
onChange={(e) => setFile(e.target.files ? e.target.files[0] : null)}
/>
<div className="form-text">Tamaño máximo 20MB</div>
</div>
*/}
<div className="text-center mt-4 mb-3">
<button
className="btn btn-success"
disabled={mostrarBoton()}
onClick={() =>
imprimirWarning(
'¿Estás seguro(a) de querer crear un nuevo caso especial?',
enviar
)
}
>
Enviar
</button>
</div>
<BotonRegresar />
</div>
);
}
@@ -120,14 +120,14 @@ export default function LiberarCasoEspecial({ responsable, imprimirError }: Prop
return (
<div className="mb-6 container mt-4">
<h3 className="h4 mb-4">Casos Especiales</h3>
<h2 className="mb-4 fw-bold">Casos Especiales</h2>
<section className="container-fluid my-3">
<section className="my-3">
<Form onSubmit={(e) => { e.preventDefault(); obtenerCasosEspeciales(); }}>
<Row className="g-3">
<Col md={3}>
<FormGroup>
<FormLabel>Número de Cuenta</FormLabel>
<FormLabel className="fw-semibold">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>Nombre</FormLabel>
<FormLabel className="fw-semibold">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>Status</FormLabel>
<FormLabel className="fw-semibold">Status</FormLabel>
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaInfoCircle />
@@ -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">{p.numeroPregunta}. {p.texto}</label>
<label htmlFor={p.id} className="form-label mb-3">{p.numeroPregunta}. {p.texto}</label>
{p.tipo === 'seleccionUnica' && p.opciones && (
<div>
<div className="mb-2">
{p.opciones.map((op) => (
<div key={op} className="SINO">
<label>
<label className="mb-3">
<input type="radio" name={p.id} checked={respuestas[p.id] === op} onChange={() => setSingle(p.id, op)} /> {op}
</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" />
<textarea id={p.id} maxLength={p.limite} placeholder="Escribe tu respuesta aquí" value={String(respuestas[p.id] ?? '')} onChange={(e) => setSingle(p.id, e.target.value)} className="form-control mb-3 bg-transparent" />
</div>
)}
</div>
@@ -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) && (
<h3 className="mb-3">{t.numeroPregunta}. {t.preguntaTabla}</h3>
<h5 className="mb-3 mt-4">{t.numeroPregunta}. {t.preguntaTabla}</h5>
)}
{t.subPreguntaTabla && <h5 className="mb-3">{t.subPreguntaTabla}</h5>}
<table className="table table-bordered text-center">
<thead>
<tr>
<th></th>
{(t.renglones[0].opciones || []).map((op) => <th key={op}>{op}</th>)}
<th className="bg-transparent"></th>
{(t.renglones[0].opciones || []).map((op) => <th key={op} className="bg-transparent">{op}</th>)}
</tr>
</thead>
<tbody>
{t.renglones.map((r) => (
<tr key={r.idRenglon}>
<td>{r.textoRenglon}</td>
<td className="bg-transparent">{r.textoRenglon}</td>
{(r.opciones || []).map((op) => (
<td key={op}>
<td key={op} className="bg-transparent">
<input type="radio" name={`tabla-${t.idTabla}-renglon-${r.idRenglon}`} value={op} checked={((respuestas[String(t.idTabla)] as Record<number, string | null>) || {})[r.idRenglon] === op} onChange={() => setTableAnswer(t.idTabla, r.idRenglon, op)} />
</td>
))}
+64 -38
View File
@@ -60,6 +60,14 @@ 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("");
@@ -187,7 +195,7 @@ export default function NuevoServicio({
return (
<div>
<div className="mb-4">
<label className="form-label">Número de Cuenta</label>
<label className="form-label fw-bolder">Número de Cuenta</label>
<div className="d-flex gap-2">
<InputGroup.Text>
<FaSchool />
@@ -205,23 +213,29 @@ export default function NuevoServicio({
</div>
</div>
<div className="mb-3">
<label className="form-label">Nombre</label>
<p className="form-control">{alumno.nombre}</p>
</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">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>
<label className="form-label fw-bolder">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) => (
@@ -230,30 +244,36 @@ export default function NuevoServicio({
</select>
</div>
<div className="mb-3">
<label className="form-label">Institución</label>
<p className="form-control">{programa.institucion}</p>
</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">Dependencia</label>
<p className="form-control">{programa.dependencia}</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">Clave del programa</label>
<p className="form-control">{programa.clavePrograma}</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>
)}
{programa.acatlan && (
<>
<div className="mb-3">
<label className="form-label">Programa interno</label>
<label className="form-label fw-bolder">Programa interno</label>
<input className="form-control" placeholder="Programa interno" value={programaInterno} onChange={(e) => setProgramaInterno(e.target.value)} />
</div>
<div className="mb-3">
<label className="form-label">Profesor/Responsable de Programa</label>
<label className="form-label fw-bolder">Profesor/Responsable de Programa</label>
<input className="form-control" placeholder="Profesor/Responsable de Programa" value={profesor} onChange={(e) => setProfesor(e.target.value)} />
</div>
</>
@@ -309,7 +329,7 @@ export default function NuevoServicio({
<Col>
<FormGroup>
<FormLabel>Fecha de inicio</FormLabel>
<FormLabel className="fw-bolder">Fecha de inicio</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
@@ -317,6 +337,7 @@ export default function NuevoServicio({
<DatePicker
selected={fechaInicio}
onChange={handleFechaInicioChange}
minDate={minDate}
dateFormat="dd-MM-yyyy"
className="form-control"
@@ -329,7 +350,7 @@ export default function NuevoServicio({
<Col>
<FormGroup>
<FormLabel>Fecha de fin</FormLabel>
<FormLabel className="fw-bolder">Fecha de fin</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
@@ -337,6 +358,7 @@ export default function NuevoServicio({
<DatePicker
selected={fechaFin}
onChange={handleFechaFinChange}
value={fechaFin.toISOString().substring(0,10)}
minDate={minDate2}
dateFormat="dd-MM-yyyy"
@@ -348,7 +370,8 @@ 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>
@@ -359,14 +382,15 @@ export default function NuevoServicio({
<input type="date" className="form-control" value={fechaFin.toISOString().substring(0,10)} min={minDate2.toISOString().substring(0,10)} onChange={(e) => setFechaFin(new Date(e.target.value))} />
</div>
)}
*/}
<div className="mb-3">
<label className="form-label">Correo electrónico del Alumno</label>
<label className="form-label fw-bolder">Correo electrónico del Alumno</label>
<input type="email" className="form-control" placeholder="Email" value={correo} onChange={(e) => setCorreo(e.target.value)} />
</div>
<FormGroup className="mb-3">
<FormLabel>Carta de aceptación (en formato .PDF. No se aceptan fotos).</FormLabel>
<FormLabel className="fw-bolder">Carta de aceptación (en formato .PDF. No se aceptan fotos).</FormLabel>
<div className="border p-4 text-center rounded" style={{ cursor: "pointer" }} onClick={() => document.getElementById("fileInput")?.click()}>
<FaUpload size={40} className="mb-2"/>
<p className="mb-1">
@@ -384,7 +408,8 @@ 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">
@@ -393,9 +418,10 @@ export default function NuevoServicio({
<p>Si al momento de elegir un archivo este no se selecciona, haga click en cancelar en la ventana emergente e intente de nuevo.</p>
</div>
</div>
*/}
<div className="has-text-centered mt-4">
<button disabled={mostrarBoton()} onClick={() => imprimirWarning('¿Esta seguro(a) de querer Pre-Registrar a este alumno?', preRegistrar)}>
<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)}>
Enviar archivo
</button>
</div>
+243 -230
View File
@@ -2,96 +2,107 @@
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);
}
}, [data]);
// 🔹 Paginación simple
function handlePrev() {
if (currentPage > 1) {
const np = currentPage - 1;
setCurrentPage(np);
onPageChange?.(np);
}
// 🔹 Cargar datos del padre si existen
useEffect(() => {
if (data && data.length > 0) {
setInfo(data);
}
}, [data]);
function handleNext() {
const lastPage = Math.max(1, Math.ceil(total / 10));
if (currentPage < lastPage) {
const np = currentPage + 1;
setCurrentPage(np);
onPageChange?.(np);
}
// 🔹 Paginación simple
function handlePrev() {
if (currentPage > 1) {
const np = currentPage - 1;
setCurrentPage(np);
onPageChange?.(np);
}
}
const servicioSelected = (row: ServicioSocialResponse | ServicioSocialConCasoEspecial) => {
if (!row) return;
function handleNext() {
const lastPage = Math.max(1, Math.ceil(total / 10));
if (currentPage < lastPage) {
const np = currentPage + 1;
setCurrentPage(np);
onPageChange?.(np);
}
}
if (idTipoUsuario === 1) {
const servicioSelected = (
row: ServicioSocialResponse | ServicioSocialConCasoEspecial
) => {
if (!row) return;
if (row.idServicio) {
localStorage.setItem("idServicio", String(row.idServicio));
}
if (idTipoUsuario === 1) {
if (row.idServicio) {
localStorage.setItem("idServicio", String(row.idServicio));
}
if ("idCasoEspecial" in row && row.idCasoEspecial) {
localStorage.setItem("idCasoEspecial", String(row.idCasoEspecial));
}
// esta mal esta logica
if ("idCasoEspecial" in row && row.idCasoEspecial) {
localStorage.setItem("idCasoEspecial", String(row.idCasoEspecial));
}
if (row.idServicio) {
router.push("/administrador/servicio");
} else if (
row.idServicio ||
("idCasoEspecial" in row && row.idCasoEspecial)
) {
console.log("Limpiando selección (no admin)");
}
} 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)");
}
};
/*
/*
const servicioSelected = (row: ServicioSocialResponse | ServicioSocialConCasoEspecial) => {
if (!row) return;
@@ -138,177 +149,179 @@ 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>
)}</td>
)}
{columnaCuestionarioCompleto && (
<td>{item.cuestionarioCompletado ? (
<span className="badge bg-success">Completado</span>
) : (
<span className="badge bg-danger">No Completado</span>
)}</td>
)}
{columnaCuestionario && (
<td>
{item.cuestionarioCompletado ? (
<span className="badge bg-success">Completado</span>
) : (
<span className="badge bg-danger">No Completado</span>
)}
</td>
)}
</tr>
))}
{info.length === 0 && (
<tr>
<td colSpan={7} className="text-center">
No hay registros
</td>
</tr>
)}
</tbody>
</table>
</div>
<div className="d-flex justify-content-between align-items-center mt-2">
<div>Total: {total}</div>
<div>
<button
className="btn btn-sm btn-outline-primary me-2"
onClick={handlePrev}
disabled={currentPage <= 1}
>
Anterior
</button>
<span>Página {currentPage}</span>
<button
className="btn btn-sm btn-outline-primary ms-2"
onClick={handleNext}
disabled={
currentPage >= Math.max(1, Math.ceil(total / 10))
return (
<section>
{loading ? (
<div>Cargando...</div>
) : (
<>
<div className="table-responsive">
<table className="table table-striped">
<thead>
<tr>
{columnaFechaRegistro && <th>Fecha Registro</th>}
<th>Número de Cuenta</th>
<th>Nombre</th>
<th>Carrera</th>
{columnaFechaInicio && <th>Fecha Inicio</th>}
{columnaFechaFin && <th>Fecha Fin</th>}
<th>Status</th>
{columnaCuestionarioCompleto && (
<th>Cuestionario Completo</th>
)}
{columnaCartaTermino && <th>Carta Termino</th>}
{columnaCuestionario && <th>Cuestionario</th>}
</tr>
</thead>
<tbody>
{info.map((item, index) => (
<tr
key={index}
style={{
cursor: idTipoUsuario === 1 ? "pointer" : "default",
}}
onClick={() =>
idTipoUsuario === 1 ? servicioSelected(item) : undefined
}
>
Siguiente
</button>
</div>
//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>
)}
</td>
)}
{columnaCuestionarioCompleto && (
<td>
{item.cuestionarioCompletado ? (
<span className="badge bg-success">Completado</span>
) : (
<span className="badge bg-danger">No Completado</span>
)}
</td>
)}
{columnaCuestionario && (
<td>
{item.cuestionarioCompletado ? (
<span className="badge bg-success">Completado</span>
) : (
<span className="badge bg-danger">No Completado</span>
)}
</td>
)}
</tr>
))}
{info.length === 0 && (
<tr>
<td colSpan={7} className="text-center">
No hay registros
</td>
</tr>
)}
</tbody>
</table>
</div>
<div className="d-flex justify-content-between align-items-center mt-2">
<div>Total: {total}</div>
<div>
<button
className="btn btn-sm btn-outline-primary me-2"
onClick={handlePrev}
disabled={currentPage <= 1}
>
Anterior
</button>
<span>Página {currentPage}</span>
<button
className="btn btn-sm btn-outline-primary ms-2"
onClick={handleNext}
disabled={currentPage >= Math.max(1, Math.ceil(total / 10))}
>
Siguiente
</button>
</div>
</>
)}
</section>
);
</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;
}