1 Commits

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