2 Commits

72 changed files with 2518 additions and 5503 deletions
-85
View File
@@ -1,85 +0,0 @@
name: Despliegue Automatizado Universal
on:
push:
branches:
- develop
- master
jobs:
get-context:
runs-on: host
outputs:
node_version: ${{ steps.setup.outputs.version }}
target_app: ${{ steps.setup.outputs.app }}
steps:
- name: Resolver Aplicación desde apps.conf
id: setup
run: |
# 1. Obtener el nombre del repositorio actual en Gitea (ej: front-Censo)
REPO_NAME="${{ github.event.repository.name }}"
# 2. Determinar si buscamos un entorno -dev (develop) o producción (master)
if [ "${{ github.ref_name }}" = "develop" ]; then
SUFFIX="-dev"
else
SUFFIX=""
fi
echo "Buscando qué sección de apps.conf tiene REPO=$REPO_NAME y termina en '$SUFFIX'..."
# 3. Buscar en /etc/apps.conf el bloque que contenga REPO=nombre_del_repo
TARGET_APP=$(awk -v repo="REPO=$REPO_NAME" -v suf="$SUFFIX" '
/^\[/ { gsub(/[\[\]]/, "", $0); current_box=$0; next }
$0 == repo {
if ((suf == "-dev" && current_box ~ /-dev$/) || (suf == "" && current_box !~ /-dev$/)) {
print current_box;
exit
}
}
' /etc/apps.conf)
if [ -z "$TARGET_APP" ]; then
echo "ERROR: No se encontró ninguna sección en /etc/apps.conf para el repositorio $REPO_NAME con el sufijo correcto."
exit 1
fi
# 4. Leer la versión de Node del bloque encontrado
VERSION=$(awk -v app="$TARGET_APP" '
$0=="["app"]" { found=1; next }
/^\[/ && found { exit }
found && /^NODE=/ { split($0, a, "="); print a[2]; exit }
' /etc/apps.conf)
MAJOR_VERSION=$(echo "$VERSION" | tr -d '[:space:]' | grep -oE '[0-9]+' | head -n1)
echo "-> ID de Aplicación encontrado: $TARGET_APP"
echo "-> Versión Mayor de Node resuelta: $MAJOR_VERSION"
echo "app=${TARGET_APP}" >> $GITHUB_OUTPUT
echo "version=${MAJOR_VERSION}" >> $GITHUB_OUTPUT
build:
needs: get-context
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configurar Node.js dinámicamente
uses: actions/setup-node@v4
with:
node-version: ${{ needs.get-context.outputs.node_version }}
- run: npm ci
- run: npm run build
deploy:
needs: [get-context, build]
runs-on: host
steps:
- name: Ejecutar Despliegue en Servidor Físico
run: |
TARGET_APP="${{ needs.get-context.outputs.target_app }}"
BRANCH_NAME="${{ github.ref_name }}"
sudo -u deploy /home/deploy/scripts/deploy-front.sh "$TARGET_APP" "$BRANCH_NAME"
-4
View File
@@ -19,10 +19,6 @@ const eslintConfig = [
"build/**", "build/**",
"next-env.d.ts", "next-env.d.ts",
], ],
rules: {
// permit using `any` in the codebase, otherwise Next's defaults treat it as an error
'@typescript-eslint/no-explicit-any': 'off',
},
}, },
]; ];
+503 -925
View File
File diff suppressed because it is too large Load Diff
+3 -5
View File
@@ -11,19 +11,17 @@
"dependencies": { "dependencies": {
"axios": "^1.12.2", "axios": "^1.12.2",
"bootstrap": "^5.3.8", "bootstrap": "^5.3.8",
"date-fns": "^4.1.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"moment": "^2.30.1", "moment": "^2.30.1",
"next": "^16.0.7", "next": "15.5.3",
"react": "19.1.0", "react": "19.1.0",
"react-bootstrap": "^2.10.10", "react-bootstrap": "^2.10.10",
"react-datepicker": "^8.7.0", "react-datepicker": "^8.7.0",
"react-dom": "19.1.0", "react-dom": "19.1.0",
"react-hot-toast": "^2.6.0", "react-hot-toast": "^2.6.0",
"react-icons": "^5.5.0", "react-icons": "^5.5.0",
"react-toastify": "^11.0.5", "sass": "^1.92.1",
"sass": "^1.95.0", "sweetalert2": "^11.26.2",
"sweetalert2": "^11.26.17",
"sweetalert2-react-content": "^5.1.0", "sweetalert2-react-content": "^5.1.0",
"validator": "^13.15.15" "validator": "^13.15.15"
}, },
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

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

Before

Width:  |  Height:  |  Size: 1.1 KiB

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

Before

Width:  |  Height:  |  Size: 1.3 KiB

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

Before

Width:  |  Height:  |  Size: 491 B

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