95 Commits

Author SHA1 Message Date
stern 51aa3121bc Add deploy.yaml
Despliegue Automatizado Universal / get-context (push) Successful in 1s
Despliegue Automatizado Universal / build (push) Successful in 1m27s
Despliegue Automatizado Universal / deploy (push) Successful in 40s
2026-07-02 03:27:35 -06:00
Patriots01 4b7516f9ec Se agrego la opcion de descargar reportes de caso especial 2026-03-11 22:11:39 +00:00
Patriots01 ef43cf238c Modificaciones 2026-03-07 01:41:29 +00:00
santiago 5fbdf59fb2 Se agrego la opcion de editar casos especiales asi como la opcion de liberar los casos especiales 2026-01-29 12:40:06 -06:00
santiago 3f1412a911 Se puso la opcion de buscar con el enter en casos especiales 2026-01-29 12:37:03 -06:00
santiago c1eead5a3e Modificaciones 2026-01-29 12:20:30 -06:00
santiago 4fdf6429ec Se agrego la vista de editar casos especiales 2026-01-29 11:08:12 -06:00
santiago cb7beef846 Modificaciones 2026-01-28 11:59:16 -06:00
santiago 2146daf642 modificaciones 2026-01-27 10:37:03 -06:00
santiago 1ad4163319 Se modifico la parte del envio de cuestionario y mensajes de alerta 2026-01-27 10:31:36 -06:00
santiago 870d47326a Se actualizo el envio de cuestionario de alumno y se modifico el mensaje 2026-01-26 15:28:15 -06:00
santiago 64d8509363 Modificaciones en el envio de cuestionario alumno 2026-01-26 15:25:54 -06:00
santiago b77726cd36 Modificaciones en el mensaje del envio de cuestionario 2026-01-26 13:41:22 -06:00
santiago 20fa308877 Se modificaron cosas optimizacion del sistema, se agregaron mensajes de alerta y correcion de UX/UI 2026-01-26 10:39:23 -06:00
santiago 40f0f38f61 Se optimizaron varias cosas de la interfas asi como se añadieron mensajes de alerta y cambios en la ui/ux 2026-01-26 09:53:03 -06:00
santiago e2e8793420 Se modificaron cosas de la importacion para un mejor rendimiento asi como los mensajes al hacer peticiones al back, asu como tambien se modificaron los mejos de errores 2026-01-25 13:08:32 -06:00
santiago 4b49987cf5 Modificaciones 2026-01-24 22:12:48 -06:00
santiago 828e56f36f Se quitaron los console.log 2026-01-24 16:32:26 -06:00
santiago 578b3f4c52 Modificaciones 2026-01-23 11:49:25 -06:00
santiago dcb2544bd9 Modificaciones 2026-01-23 11:48:13 -06:00
santiago e0cb71ee45 Modificaciones 2026-01-23 11:38:39 -06:00
santiago a3a9f03025 Modificaciones en el nombre del servicio 2026-01-23 10:42:37 -06:00
santiago 4647014dcd Modificaciones en la forma de registro de un caso especial, y modificaciones en las fechas a es 2026-01-23 10:36:44 -06:00
santiago c50f3ea9f3 Se agregaron las claves del programa de acatlan contigo 2026-01-21 13:54:53 -06:00
santiago fbcde608ed Modificaciones 2026-01-21 11:12:57 -06:00
santiago c291c66fef Modificaciones 2026-01-20 17:34:13 -06:00
santiago 08ac1f064b Modificaciones 2026-01-20 10:49:46 -06:00
santiago 72f27183c4 Modificaciones 2026-01-20 10:46:07 -06:00
santiago 9efbe3851c Modificaciones 2026-01-20 10:42:08 -06:00
santiago cb5889143e Modificaciones 2026-01-19 21:53:31 -06:00
santiago dfb9a0d1d7 Modificaciones 2026-01-17 13:28:51 -06:00
santiago 600741652c Modificaciones 2026-01-16 16:02:24 -06:00
santiago 7bfac7afb1 Modificaciones 2026-01-16 15:50:38 -06:00
santiago 2861751e6a Modificaciones 2026-01-16 15:40:57 -06:00
santiago 7c256e4c2f Modificaciones 2026-01-16 15:23:00 -06:00
santiago 38d6cca656 Modificaciones 2026-01-16 15:21:38 -06:00
santiago 7877d2f8dd Modificaciones 2026-01-16 15:07:59 -06:00
santiago 1c869bd092 Modificaciones 2026-01-16 14:56:55 -06:00
santiago 595fdddd52 Modificaciones 2026-01-16 14:52:19 -06:00
santiago 97f251aa69 Modificaciones 2026-01-16 14:00:32 -06:00
santiago 0147cbf34c Modificaciones 2026-01-16 13:30:16 -06:00
santiago 863783307e Modificaciones 2026-01-16 12:26:49 -06:00
santiago d8276b3c07 Modificaciones 2026-01-16 12:20:50 -06:00
santiago ea4fa4a8b6 Modificaciones 2026-01-16 12:17:58 -06:00
santiago fcfa2d2c3b Modificaciones 2026-01-16 12:14:02 -06:00
santiago 513e8dab8d Merge branch 'develop' 2026-01-16 10:08:45 -06:00
santiago 25eaba5b9c Modificaciones en la fecha 2026-01-16 09:53:15 -06:00
santiago 5bd61a39ad Se cambio el titulo del servicio al de produccion 2026-01-15 14:27:52 -06:00
santiago 28d2ba31aa Modificaciones 2026-01-15 12:00:23 -06:00
santiago f913419122 Se corrigieron errores en los mensajes de alert 2026-01-15 11:46:45 -06:00
santiago f25856a068 Se modifico el mensaje de error en el login 2026-01-15 11:43:35 -06:00
santiago d21ea10b0a Se corrigio un error de sintaxis 2026-01-15 11:41:41 -06:00
santiago 8e6d1c6e80 Se agregaron mensajes de alerta perzonalizados en la vistas de alumno y responsable 2026-01-15 11:39:30 -06:00
santiago ea5b94f48b Modificaciones en los mensajes de alerta 2026-01-15 11:22:20 -06:00
Patriots01 44f9486f45 Modificacines 2026-01-15 00:44:48 +00:00
Patriots01 51d647c751 Modificacinoes 2026-01-14 19:10:56 +00:00
Patriots01 de7d09bad5 Modificaciones 2026-01-14 18:36:49 +00:00
Patriots01 a9ccaf120e Modificaciones 2026-01-14 18:32:51 +00:00
Patriots01 cfe05cb966 Modificaciones 2026-01-14 18:07:53 +00:00
Patriots01 cc47077705 Modificaciones 2026-01-14 17:53:19 +00:00
Patriots01 de3dfe06ca Modificaciones 2026-01-14 01:46:23 +00:00
Patriots01 ef42e30dfa Modificaciones 2026-01-14 01:33:50 +00:00
Patriots01 d297d7474e Modificaciones 2026-01-14 01:20:00 +00:00
Patriots01 39e47e9262 Modificaciones 2026-01-14 01:06:28 +00:00
Patriots01 70143c0fc5 Modificaciones 2026-01-14 00:36:33 +00:00
Patriots01 50aacba98e Modificaciones 2026-01-13 23:46:43 +00:00
Patriots01 b8a279d2b3 Modificaciones 2026-01-13 23:30:02 +00:00
Patriots01 dc431e0df1 Se modifico la vista del usuario para poder habilitar y desabilitar el cuestionario e el infrome 2026-01-13 22:11:08 +00:00
santiago 57c314a085 Modificaciones 2026-01-13 15:45:53 -06:00
santiago 3f9978a43e Modificaiones 2026-01-13 15:21:53 -06:00
Patriots01 922a3bd8de Modificaciones 2026-01-13 21:01:03 +00:00
santiago 7633905fd2 Se modifico el titulo del servicio 2026-01-13 11:30:58 -06:00
santiago 0342f94f5a Modificaciones 2026-01-12 15:12:57 -06:00
santiago 7446782446 Se modifico la vista de responsable para desabilitar en caso de que no cumpla con el status 2026-01-12 10:38:37 -06:00
santiago da2cad15b9 Se soluciono un problema de bucle 2026-01-12 10:23:48 -06:00
santiago c71738333f Se Agregaron cosas de la UI/UX asi como se arregaron algunos errores 2026-01-12 10:03:59 -06:00
santiago 4765105d90 Modificaciones en algunas vistas y en el envio de archivos y un error de dedo 2026-01-08 13:01:54 -06:00
santiago 4dc5256071 Modificaciones en algunas vistas y en el envio de archivos 2026-01-08 12:59:07 -06:00
santiago d3ba08a3cf Se modifico la logia en el envio de archivos falta corregir el endpoint de servicio/update 2026-01-07 19:53:59 -06:00
santiago bd7b34c59c Se modificaron cosas para que funcione con la nueva api, se modifico la funcionalidad asi como el fetching de los datos 2025-12-09 09:19:26 -06:00
santiago feee742080 Merge branch 'pruebas_api_nueva' of https://repositorio.acatlan.unam.mx/smoreno/new_servicio_social_front into develop 2025-12-09 09:12:11 -06:00
santiago 801e1b4205 Se modificaron varias cosas en el fetching de los datos asi como los nombres y estilos de algunas vistas 2025-12-09 09:05:57 -06:00
santiago 3e28503718 Modifificaciones 2025-12-07 09:34:14 -06:00
Patriots01 3de2f08ed2 Se modificaron las rutas de las peticiones 2025-11-27 23:31:06 +00:00
Patriots01 bbb0b0f795 Modificaiones en la fomrma de renderizar la info 2025-11-27 20:29:44 +00:00
Patriots01 6ff9641761 Se modifico el fechind de los datos en la vista de responsable 2025-11-26 22:00:35 +00:00
Patriots01 99adfdebb5 Modificaciones 2025-11-26 21:13:28 +00:00
Patriots01 ac1fd05638 Modificacinoes en los endpoint asi como los metodos 2025-11-25 22:00:20 +00:00
Patriots01 8b27cbd425 Se modificaron cosas de nombres a la hora de renderizar la informacion y se cambiaron los losnmbres de los endpoint asi como tambien las direcciones de algunos endpoint 2025-11-25 21:54:49 +00:00
Patriots01 82b5b1fc50 Modificaciones 2025-11-25 19:54:57 +00:00
Patriots01 e27b1fb643 Modificaciones 2025-11-25 19:03:57 +00:00
Patriots01 da73df3e7d Modificaciones 2025-11-25 17:55:39 +00:00
Patriots01 5a0fe4edde errores corregidos en login 2025-11-24 22:21:57 +00:00
santiago b1bafec1be Modificacines 2025-11-14 21:23:32 +01:00
santiago 1cc2637b25 Cambios en el login 2025-10-20 19:17:37 +02:00
67 changed files with 3805 additions and 1331 deletions
+85
View File
@@ -0,0 +1,85 @@
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,6 +19,10 @@ 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',
},
},
];
+925 -503
View File
File diff suppressed because it is too large Load Diff
+5 -3
View File
@@ -11,17 +11,19 @@
"dependencies": {
"axios": "^1.12.2",
"bootstrap": "^5.3.8",
"date-fns": "^4.1.0",
"file-saver": "^2.0.5",
"moment": "^2.30.1",
"next": "15.5.3",
"next": "^16.0.7",
"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",
"sass": "^1.92.1",
"sweetalert2": "^11.26.2",
"react-toastify": "^11.0.5",
"sass": "^1.95.0",
"sweetalert2": "^11.26.17",
"sweetalert2-react-content": "^5.1.0",
"validator": "^13.15.15"
},
+7
View File
@@ -0,0 +1,7 @@
<!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"/>

After

Width:  |  Height:  |  Size: 1.1 KiB

+7
View File
@@ -0,0 +1,7 @@
<!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"/>

After

Width:  |  Height:  |  Size: 1.3 KiB

+4
View File
@@ -0,0 +1,4 @@
<?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>

After

Width:  |  Height:  |  Size: 491 B

+1
View File
@@ -0,0 +1 @@
correo,nombre,institucion,dependencia,programa,clave
1 correo nombre institucion dependencia programa clave
+23 -5
View File
@@ -2,11 +2,11 @@ import axios from 'axios';
// Crea una instancia base de Axios
export const axiosInstance = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000/api', // poner la url
baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000', // poner la url
headers: {
'Content-Type': 'application/json',
},
timeout: 10000, // tiempo máximo de espera (10 segundos)
timeout: 20000, // tiempo máximo de espera (20 segundos)
});
// Interceptor para agregar el token automáticamente
@@ -19,9 +19,9 @@ axiosInstance.interceptors.request.use(
config.headers.Authorization = `Bearer ${token}`;
//Quitar estas lienas
config.headers['token'] = token;
config.headers['x-access-token'] = token;
config.headers['token-v2'] = token;
// config.headers['token'] = token;
// config.headers['x-access-token'] = token;
// config.headers['token-v2'] = token;
}
}
@@ -29,6 +29,23 @@ axiosInstance.interceptors.request.use(
},
(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(
@@ -38,6 +55,7 @@ axiosInstance.interceptors.response.use(
// 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
@@ -0,0 +1,144 @@
'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,13 +3,14 @@
import { useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { axiosInstance } from '@/api/config';
import { isAxiosError } from 'axios';
import axios, { AxiosError, 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';
@@ -27,9 +28,10 @@ interface Status {
}
interface Alumno {
Usuario?: object;
Carrera?: object;
Status?: Status;
idCasoEspecial: number;
usuario?: object;
carrera?: object;
status?: Status;
}
export default function CasoEspecialPage() {
@@ -38,7 +40,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>({ Usuario: {}, Carrera: {}, Status: {} });
const [alumno, setAlumno] = useState<Alumno>({ idCasoEspecial: 0, usuario: {}, carrera: {}, status: {} });
// Funciones de dialogo
const imprimirError = (err: unknown = {}, title = '¡Hubo un error!', onConfirm = () => {}) => {
@@ -58,7 +60,7 @@ export default function CasoEspecialPage() {
const anyErr = err as { err?: string };
if (anyErr.err === 'token error') {
localStorage.clear();
router.push('/');
router.replace('/');
}
} catch (_) {}
};
@@ -78,7 +80,7 @@ export default function CasoEspecialPage() {
title = '¡Espera un minuto!',
onCancel = () => {}
) => {
MySwal.fire({
Swal.fire({
title,
text: message,
icon: 'warning',
@@ -111,14 +113,27 @@ export default function CasoEspecialPage() {
updateIsLoading(true);
try {
const res = await axiosInstance.get(`/caso_especial?idCasoEspecial=${idCasoEspecial}`, { headers: { token: admin.token } });
setAlumno(res.data);
// 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)
} catch (err: unknown) {
if (isAxiosError(err)) imprimirError(err.response?.data || err.message);
else if (err instanceof Error) imprimirError(err.message);
else imprimirError();
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();
} finally {
updateIsLoading(false);
updateIsLoading(false);
}
};
@@ -129,9 +144,9 @@ export default function CasoEspecialPage() {
useEffect(() => {
if (admin.idTipoUsuario) {
if (admin.idTipoUsuario === 2) router.push('/responsable');
if (admin.idTipoUsuario === 3) router.push('/alumno');
if (admin.idTipoUsuario === 4) router.push('/casoEspecial');
if (admin.idTipoUsuario === 2) router.replace('/responsable');
if (admin.idTipoUsuario === 3) router.replace('/alumno');
if (admin.idTipoUsuario === 4) router.replace('/casoEspecial');
}
if (idCasoEspecial) {
@@ -148,7 +163,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! }}
@@ -160,7 +175,7 @@ export default function CasoEspecialPage() {
)}
{isLoading && (
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-30 z-50">
<div className="fixed inset-0 flex items-center justify-center bg-transparent bg-opacity-30 z-50">
<div className="loader">Cargando...</div>
</div>
)}
+2 -5
View File
@@ -1,18 +1,15 @@
"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,6 +11,9 @@ 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) {
+10 -3
View File
@@ -2,13 +2,14 @@
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 } from "react";
import { useEffect, useState, useMemo } from "react";
export default function Registro() {
//const years = [2020, 2021, 2022, 2023, 2024, 2025];
const admin = { token: "mi_token_de_prueba" };
const [admin, setAdmin] = useState({ token: "" });
const obtenerYears = () => {
const years1 = [];
@@ -20,11 +21,15 @@ export default function Registro() {
return years1
}
const years = obtenerYears()
const years = useMemo(() => obtenerYears(), [])
useEffect(() => {
setAdmin({ token: localStorage.getItem("token") || "" });
console.log("Estos son los datos de years", years)
console.log("Este es el tipo de datos de years", typeof(years))
console.log("Estos son los datos de admin", admin)
console.log("Este es el tipo de datos de admin", typeof(admin))
}, [years]);
return (
@@ -42,6 +47,8 @@ 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=${idUsuario}`)
const res = await axiosInstance.get(`usuario/responsable/${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,9 +48,8 @@ export default function Servicio() {
return (
<section className="container">
<BotonRegresar />
{/* <EditarServicio admin={}/> */}
<EditarServicio
admin={admin.token || ''}
admin={{ token: admin.token || '' }}
imprimirError={imprimirError}
imprimirMensaje={imprimirMensajeError}
imprimirWarning={imprimirWarning}
+138 -52
View File
@@ -10,6 +10,9 @@ 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
@@ -78,10 +81,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;
@@ -98,10 +101,10 @@ interface Datos {
cartaTermino?: string;
informeGlobal?: string;
idCuestionarioPrograma?: number;
idCuestionarioPrograma2?: number;
idCuestionarioAlumno?: number;
idCuestionarioAlumno2?: number;
cuestionarioPrograma?: {idCuestionarioPrograma: number};
cuestionarioPrograma2?: {idCuestionarioPrograma2: number};
cuestionarioAlumno?: {idCuestionarioAlumno: number};
cuestionarioAlumno2?: {idCuestionarioAlumno2: number};
}
export default function Servicio() {
@@ -130,22 +133,44 @@ 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) => {
alert(`❌ Error: ${JSON.stringify(error)}`);
Swal.fire('Error', JSON.stringify(error), 'error')
// alert(`❌ Error: ${JSON.stringify(error)}`);
}
const imprimirMensaje = (message: string) => {
alert(`${message}`);
Swal.fire('Exito', message, 'success')
// alert(`✅ ${message}`);
};
const imprimirWarning = (message: string, onConfirm: () => void) => {
if (confirm(`⚠️ ${message}\n¿Deseas continuar?`)) {
onConfirm();
}
confirmar(message, onConfirm)
// if (confirm(`
// ${Swal.fire('Aviso', message, 'warning')}
// `)) {
// onConfirm();
// }
};
// Función para actualizar el estado de carga
// Función para acualizar el estado de carga
const updateIsLoading = (value: boolean) => {
setIsLoading(value);
};
@@ -168,42 +193,71 @@ 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;
const adminInfo: AdminInfo = {
idUsuario,
idTipoUsuario,
tipoUsuario,
token: {
headers: {
return {
idUsuario,
idTipoUsuario,
tipoUsuario,
token: {
headers: {
token,
},
},
};
},
};
//setAdmin(adminInfo);
const idServisio = setIdServicio(Number(localStorage.getItem('idServicio')));
console.log('ID Servicio obtenido:', idServisio);
return adminInfo;
// 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;
};
const obtenerRegistro = async (adminInfo: AdminInfo, idServicioVal: number) => {
try {
updateIsLoading(true);
const res = await axiosInstance.get(`/servicio/admin?idServicio=${idServicioVal}`); //, adminInfo.token
// await new Promise(resolve => setTimeout(resolve, 3000));
// throw new Error('Api caida')
const res = await axiosInstance.get(`/servicio/admin/${idServicioVal}`); //, adminInfo.token
console.log('Respuesta del servicio:', res.data);
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) {
@@ -239,16 +293,29 @@ export default function Servicio() {
<section className="container px-2 pb-6">
<BotonRegresar />
<h3 className="container fw-bold title">{datos.Status?.status}</h3>
<InformacionServicio
datos={datos}
admin={admin}
imprimirError={imprimirError}
updateIsLoading={updateIsLoading}
/>
<h3 className="container fw-bold title">{datos.status?.status}</h3>
<TituloStatus status={datos.Status}/>
{isLoading ? (
<Spinner />
) : datos ? (
<InformacionServicio
datos={datos}
admin={admin}
imprimirError={imprimirError}
updateIsLoading={updateIsLoading}
/>
) : null}
{/* {!isLoading && datos && (
<InformacionServicio
datos={datos}
admin={admin}
imprimirError={imprimirError}
updateIsLoading={updateIsLoading}
/>
)} */}
<TituloStatus status={datos.status}/>
{/* Para mostrar si tiene la carta y poder ver los diferentes archivos */}
{datos.cartaAceptacion && (
@@ -287,31 +354,48 @@ export default function Servicio() {
/>
)}
<div className='container'>
{datos.idCuestionarioPrograma || datos.idCuestionarioPrograma2 ? (
<div className="container">
{tieneCuestionarioPrograma && (
<p className="my-4">
<strong>Cuenta con cuestionario de programa resuelto.</strong>
</p>
)}
{tieneCuestionarioAlumno && (
<p className="my-4">
<strong>Cuenta con cuestionario de alumno resuelto.</strong>
</p>
)}
</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.idCuestionarioAlumno || datos.idCuestionarioAlumno2 ? (
{datos.cuestionarioAlumno2?.idCuestionarioAlumno2 != null || datos.cuestionarioAlumno?.idCuestionarioAlumno != null ?(
<p className="my-4">
<strong>Cuenta con cuestionario de alumno resuelto.</strong>
</p>
) : null}
</div>
</div> */}
<ConfirmarServicio
idServicio={idServicio ?? 0}
admin={admin}
datos={datos}
imprimirMensaje={imprimirMensaje}
imprimirWarning={imprimirWarning}
imprimirError={imprimirError}
updateIsLoading={updateIsLoading}
/>
<CancelarServicio
{idServicio && datos.status && (
<ConfirmarServicio
idServicio={idServicio ?? 0}
admin={admin}
datos={datos}
imprimirMensaje={imprimirMensaje}
imprimirWarning={imprimirWarning}
imprimirError={imprimirError}
updateIsLoading={updateIsLoading}
/>
)}
{datos.status?.idStatus != 5 && datos.status?.idStatus != 6 && (
<CancelarServicio
idServicio={idServicio ?? 0}
admin={admin}
datos={datos}
@@ -320,6 +404,8 @@ export default function Servicio() {
imprimirError={imprimirError}
updateIsLoading={updateIsLoading}
/>
)}
{/*
@@ -5,8 +5,20 @@ 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'>
@@ -28,7 +40,7 @@ web: <a
</p>
</div>
<FullCuestionario />
<FullCuestionario idServicio={idServicio}/>
</div>
);
}
+21 -10
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,6 +57,7 @@ interface Programa {
export default function Home() {
const [data, setData] = useState<InfoAlumnoExtendido>();
// const [servicio, setServicio] = useState<InfoAlumno>();
const handleInfo = async () => {
const idUsuario = localStorage.getItem('idUsuario');
@@ -80,7 +81,13 @@ 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)
@@ -88,17 +95,21 @@ 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: 2 }} />
<MensajeAlumno status={{ idStatus: data?.status.idStatus ?? 6 }} />
</div>
{/* servicio={{
@@ -123,9 +134,9 @@ export default function Home() {
/>
)}
{ data?.Status.idStatus === 2 && (
{ data?.status.idStatus === 2 && (
<CompletarDatosPersonales
idServicio={123}
idServicio={data.idServicio}
alumno={{ token: { headers: { Authorization: "Bearer ..." } }, tokenArchivo: { headers: { Authorization: "Bearer ..." } } }}
imprimirMensaje={(msg) => console.log(msg)}
imprimirWarning={(msg, callback) => { if (confirm(msg)) callback(); }}
@@ -135,9 +146,9 @@ export default function Home() {
/>
)}
{ data?.Status.idStatus !== undefined && data?.Status.idStatus >= 4 && (
{ data?.status.idStatus !== undefined && data?.status.idStatus >= 4 && data?.status.idStatus !== 8 && (
<PreTermino
alumno={{ tokenArchivo: "token123" }}
alumno={{tokenAlumno: localStorage.getItem('token') || ""}}
servicio={data}
imprimirMensaje={(msg) => console.log(msg)}
imprimirWarning={(msg, callback) => {
+22 -4
View File
@@ -2,9 +2,13 @@
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)}`)
@@ -14,10 +18,24 @@ export default function Nuevo() {
alert(`${message}`);
}
const imprimirWarning = (message: string, onConfirm: () => void) => {
if (confirm(`${message}\n¿Desea continuar?`)) {
onConfirm();
}
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 updateIsLoading = (value: boolean) => {
+115 -4
View File
@@ -1,3 +1,4 @@
@import "react-datepicker/dist/react-datepicker.css";
:root {
--background: #ffffff;
--foreground: #171717;
@@ -10,9 +11,6 @@
}
}
@import "react-datepicker/dist/react-datepicker.css";
html,
body {
max-width: 100vw;
@@ -20,7 +18,7 @@ body {
}
body {
color: var(--foreground);
/* Do not override text color globally so Bootstrap utilities and components keep their intended colors */
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
-webkit-font-smoothing: antialiased;
@@ -60,6 +58,17 @@ 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%;
@@ -138,3 +147,105 @@ 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;
}
+2 -1
View File
@@ -17,13 +17,14 @@ 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>
+55 -11
View File
@@ -1,18 +1,36 @@
'use client'
import { axiosInstance } from "@/api/config";
import Image from "next/image";
import { useRouter } from "next/navigation";
import React from "react";
import React, { useState } from "react";
import Swal from "sweetalert2";
export default function Home() {
const [showPassword, setShowPassword] = useState(false);
const router = useRouter();
const [error, setError] = React.useState('');
const [loadingData, setLoadingData] = React.useState({
usuario: '',
password: '',
});
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setLoadingData((prevData) => ({ ...prevData, [name]: value}));
if (error) setError('');
}
const handleOnSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const object = Object.fromEntries(formData);
// const formData = new FormData(e.currentTarget);
// const object = Object.fromEntries(formData);
try {
const res = await axiosInstance.post('/usuario/login', object);
const res = await axiosInstance.post('/auth/login', {
usuario: loadingData.usuario,
password: loadingData.password,
});
// Extraer datos del backend
const token = res?.data?.token ?? '';
@@ -31,6 +49,7 @@ export default function Home() {
localStorage.setItem('nombre', String(nombre));
localStorage.setItem('idTipoUsuario', String(idTipoUsuario));
// Validar y redirigir según el idTipoUsuario
if (token && idUsuario && idTipoUsuario) {
switch (idTipoUsuario) {
@@ -48,19 +67,26 @@ export default function Home() {
break;
default:
console.warn(`Tipo de usuario desconocido: ${idTipoUsuario}`);
localStorage.clear();
//localStorage.clear();
break;
}
} else {
localStorage.clear();
//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();
//localStorage.clear();
}
};
const togglePassword = () => {
setShowPassword(prev => !prev)
}
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}>
@@ -68,12 +94,30 @@ export default function Home() {
<div className="mb-3">
<label htmlFor="usuario" className="form-label">Usuario</label>
<input type="text" name="usuario" id="usuario" className="form-control" required />
<input type="text" name="usuario" id="usuario" className="form-control" onChange={handleChange} 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 />
<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">
@@ -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}
/>
)}
+32 -10
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; usuario?: string };
type Alumno = { nombre?: string; idUsuario?: number };
export default function Page() {
const router = useRouter();
@@ -16,6 +16,8 @@ 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);
@@ -54,9 +56,17 @@ 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 () => {
@@ -64,8 +74,12 @@ 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=${idServicio}`, { headers });
setAlumno(res.data.Usuario ?? {});
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 ?? {});
updateIsLoading(false);
const s = res.data.Status?.idStatus ?? 0;
// Se comennto para hacer pruebas
@@ -82,20 +96,28 @@ export default function Page() {
useEffect(() => { getLocalhostInfo(); }, []);
useEffect(() => {
if (responsable.idTipoUsuario === 1) router.push('/admin');
if (responsable.idTipoUsuario === 3) router.push('/alumno');
if (responsable.idTipoUsuario === 4) router.push('/casoEspecial');
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')
)
// 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
}, [responsable, idServicio]);
}, [isReady, responsable, idServicio]);
return (
<section className="container px-2 pb-6">
<BotonRegresar />
<h3 className="title">{alumno.nombre} {alumno.usuario}</h3>
<h3 className="title">{alumno.nombre}</h3>
{idServicio !== null && (
<UploadArchivo
+23 -6
View File
@@ -19,6 +19,10 @@ 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);
@@ -37,7 +41,7 @@ export default function Page() {
const anyErr = err as { err?: unknown };
if (anyErr.err === 'token error') {
try { localStorage.clear(); } catch (_) {}
router.push('/');
router.replace('/');
}
}
};
@@ -59,17 +63,24 @@ 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=${idServicio}`, { headers });
//const headers = responsable.token ? { Authorization: `Bearer ${responsable.token}` } : undefined;
const res = await axiosInstance.get<{ Usuario?: Record<string, unknown>; idCuestionarioPrograma?: number }>(`/servicio/admin/${idServicio}`,);
setAlumno(res.data.Usuario ?? null);
updateIsLoading(false);
if (res.data.idCuestionarioPrograma) router.push('/responsable');
@@ -90,14 +101,20 @@ 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
else if (idServicio !== null) obtenerRegistro();
if (idServicio !== null) {
obtenerRegistro();
} else router.push('/responsable');
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [responsable, idServicio]);
}, [isReady, 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.push('/');
router.replace('/');
}
}
};
@@ -59,7 +59,7 @@ export default function Page() {
}, []);
useEffect(() => {
if (responsable.idTipoUsuario === 1) router.push('/admin');
if (responsable.idTipoUsuario === 1) router.push('/administrador');
if (responsable.idTipoUsuario === 3) router.push('/alumno');
if (responsable.idTipoUsuario === 4) router.push('/casoEspecial');
// eslint-disable-next-line react-hooks/exhaustive-deps
+11 -8
View File
@@ -53,17 +53,20 @@ 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');
@@ -75,8 +78,8 @@ export default function Page() {
), [responsable]);
return (
<section className="container px-2 pb-6">
<div className="pt-6 pb-4">
<section className="container px-2 pb-6 mb-2">
<div className="pb-3">
<p className="is-size-4 block mt-5 h4 mb-4">Estimado(a) responsable del programa de servicio social:</p>
<p className="block">
+76 -26
View File
@@ -4,7 +4,8 @@ import { useState } from "react";
import { useRouter } from "next/navigation";
import { axiosInstance } from '@/api/config';
import { isAxiosError } from 'axios';
import type { AxiosResponse } from 'axios';
import type { AxiosError, AxiosResponse } from 'axios';
import Swal from "sweetalert2";
interface Status {
idStatus: number;
@@ -25,7 +26,7 @@ interface Datos {
//Programa?: Programa
//Carrera?: Carrera;
//Usuario?: Usuario;
Status?: Status;
status?: Status;
creditos?: string;
correo?: string;
fechaRegistro?: string;
@@ -57,7 +58,7 @@ interface Props {
}
interface ApiResponse {
data: { message: string };
message: string;
}
interface RechazarData {
@@ -97,6 +98,11 @@ 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);
@@ -118,18 +124,36 @@ export default function Archivo({
mensaje: mensajeRechazo,
};
console.log("Datos para rechazar:", data);
try {
updateIsLoading(true);
const res = await funcRechazar(data);
localStorage.removeItem("idServicio");
imprimirMensaje(res.data.data.message);
router.push("/administrador");
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");
} catch (err: unknown) {
if (isAxiosError(err)) {
imprimirError(err.response?.data || err.message);
} else {
imprimirError(err);
}
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);
// }
} finally {
updateIsLoading(false);
}
@@ -146,19 +170,40 @@ export default function Archivo({
return (
<div className="container mb-4">
<div className="flex items-center">
<h5 className="pr-4 text-lg">
Ver {title}
<a
className="btn btn-link btn-light text-decoration-none ms-3 text-morado"
target="_blank"
rel="noopener noreferrer"
href={`https://drive.google.com/file/d/${archivo()}/view?usp=sharing`}
style={{ background: '#eae4f8'}}
>
Ver
</a>
</h5>
<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="pr-4">
@@ -173,8 +218,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"
@@ -183,11 +228,16 @@ export default function Archivo({
Rechazar
</button>
)}
</div> */}
</div>
{rechazar && (
<div className="mt-2">
<label>Razón del rechazo:</label>
<label>Razón del rechazo (No olvide indicar el documento rechazado):</label>
<textarea
maxLength={500}
value={mensajeRechazo}
@@ -2,8 +2,9 @@
import { useState } from "react";
import { axiosInstance } from '@/api/config';
import { isAxiosError } from 'axios';
import { AxiosError } from 'axios';
import { useRouter } from "next/navigation";
import Swal from "sweetalert2";
interface Status {
idStatus: number;
@@ -80,12 +81,22 @@ export default function CancelarServicio({
updateIsLoading(true);
const res = await axiosInstance.put(`/servicio/cancelar`, data);
localStorage.removeItem("idServicio");
imprimirMensaje(res.data.message);
router.push("/admin");
Swal.fire('Exito', res.data.message, 'success')
// imprimirMensaje(res.data.message);
router.push("/administrador");
} catch (err: unknown) {
if (isAxiosError(err)) imprimirError(err.response?.data || err.message);
else if (err instanceof Error) imprimirError(err.message);
else imprimirError(err);
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);
} finally {
updateIsLoading(false);
}
+45 -11
View File
@@ -1,19 +1,30 @@
'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/fa";
import { FaUpload } from "react-icons/fa6";
import Swal from "sweetalert2";
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 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 [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
@@ -38,18 +49,40 @@ export default function CargaMasiva({datos}: {datos: CargaMasivaProps}) {
//updateIsLoading(true)
try {
const res = await axiosInstance.post("/programa/carga_masiva", formData,{
console.log("Este es el archivo csv", formData)
// Cambioamos a variable de entorno
const res = await axiosInstance.post(`${process.env.NEXT_PUBLIC_API_URL}/programa/carga_masiva`, formData,{
headers: {
Authorization: `Bearer ${datos.admin.tokenArchivo}`,
'Content-Type': 'multipart/form-data',
},
})
console.log('Respuesta de carga masiva:', res.data)
//updateIsLoading(false)
//ImprimirMensaje(res.data.message)
router.push("/administrador/responsables")
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")
} 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)
}
}
@@ -86,16 +119,17 @@ export default function CargaMasiva({datos}: {datos: CargaMasivaProps}) {
disabled={!csv}
onClick={enviarCargaMasiva}>
Enviar archivo
</Button>
</Button>
<Button
href={link}
target="_blank"
rel="noopener noreferrer"
<Button
type="button"
onClick={handleDowload}
// target="_blank"
// rel="noopener noreferrer"
className="bg-morado border-morado"
>
Descargar plantilla
</Button>
</Button>
</div>
</div>
);
@@ -1,9 +1,10 @@
import { useState } from "react";
import { isAxiosError } from 'axios';
import { axiosInstance } from '@/api/config';
import type { AxiosResponse } from 'axios';
import { AxiosError, AxiosResponse } from 'axios';
import { useRouter } from "next/navigation";
import { Carrera, Usuario } from "@/types/responses";
import Swal from "sweetalert2";
interface Status {
idStatus: number;
@@ -11,6 +12,8 @@ interface Status {
interface Programa {
acatlan?: boolean;
clavePrograma?: string;
programa?: string;
}
interface Alumno {
@@ -24,10 +27,10 @@ interface Admin {
}
interface Datos {
Programa?: Programa;
programa?: Programa;
//Carrera?: Carrera;
//Usuario?: Usuario;
Status?: Status;
status?: Status;
creditos?: string;
correo?: string;
fechaRegistro?: string;
@@ -83,9 +86,11 @@ 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 (
@@ -97,20 +102,33 @@ export default function ConfirmarServicio({
updateIsLoading(true);
const res = await funcConfirmar(data);
// ✅ TS sabe que res.data.message existe
imprimirMensaje(res.data.message);
router.push("/admin");
Swal.fire({
icon: 'success',
title: 'Exito',
text: res.data.message,
});
// imprimirMensaje(res.data.message);
router.replace("/administrador");
} catch (err: unknown) {
if (isAxiosError(err)) {
imprimirError(err.response?.data || err.message);
} else {
imprimirError(err);
}
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);
// }
} finally {
updateIsLoading(false);
}
};
const confirmarPreRegistro = (data: ConfirmarData) =>
const confirmarPreRegistro = (data: ConfirmarData) =>
axiosInstance.put<ApiResponse>(`/servicio/registro`, data);
const confirmarLiberacion = (data: ConfirmarData) => {
@@ -118,9 +136,29 @@ 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 (
<div className="container space-y-4">
{datos.Status?.idStatus === 5 && datos.Programa?.acatlan && (
// datos.Programa?.acatlan && datos.programa?.programa === clavesPermitidas &&
<div className="container space-y-4">
{datos.status?.idStatus === 5 && clavesPermitidas.includes(datos?.programa?.clavePrograma || '') && (
<div className="flex items-center space-x-2">
<input
type="checkbox"
@@ -129,28 +167,23 @@ 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">
<label htmlFor="vistoBuenoAcatlan" className="text-gray-700 mb-4 me-2 ms-2">
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={() =>
imprimirWarning(
"¿Seguro(a) que quieres confirmar este servicio?",
confirmarServicio
)
}
onClick={confirma}
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"
+44 -5
View File
@@ -3,6 +3,7 @@ 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[];
@@ -19,8 +20,33 @@ export default function Cuestionario({ years, admin, updateIsLoading }: Props) {
try {
//updateIsLoading(true);
//const res = await axiosInstance.get(`/cuestionario_alumno?year=${selectedCuestionario}&version=${version}`, {
const res = await axiosInstance.get(`/cuestionario_alumno`, {
params: { year: selectedYear, 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 },
responseType: "blob",
});
@@ -48,12 +74,25 @@ 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: unknown) {
} catch (err) {
Swal.fire({
title: 'Error',
text: 'Error al descargar el cuestionario.',
icon: 'error',
})
console.error("Error en descargar el formulario", err)
//updateIsLoading(false);
// optional: handle error, e.g. console.error(err)
@@ -71,8 +110,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_alumno">Cuestionario de Alumnos</option>
<option value="cuestionario_programa">Cuestionario de Programas</option>
<option value="cuestionario-alumno2">Cuestionario de Alumnos</option>
<option value="cuestionario-programa2">Cuestionario de Programas</option>
</FormSelect>
</InputGroup>
</FormGroup>
@@ -8,6 +8,12 @@ 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;
@@ -22,7 +28,9 @@ interface Viejo {
}
interface Admin {
token: { headers: Record<string, string> };
idTipoUsuario: number;
tipoUsuario?: string;
token: string;
}
interface Props {
@@ -84,7 +92,12 @@ export default function EditarAlumno({
};
const actualizar = async () => {
const data: Record<string, unknown> = { idCasoEspecial };
const data: Record<string, unknown> = { idCasoEspecial };
if (!data.idCasoEspecial) {
console.log('No tiene el id')
return;
}
if (nuevo.direccion) data.direccion = nuevo.direccion;
if (nuevo.correo) data.correo = nuevo.correo;
@@ -92,16 +105,18 @@ 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 = moment(nuevo.fechaInicio).toDate();
if (nuevo.fechaFin) data.fechaFin = moment(nuevo.fechaFin).toDate();
if (nuevo.fechaInicio) data.fechaInicio = formatDate(nuevo.fechaInicio);
if (nuevo.fechaFin) data.fechaFin = formatDate(nuevo.fechaFin);
if (nuevo.fechaNacimiento)
data.fechaNacimiento = moment(nuevo.fechaNacimiento).toDate();
data.fechaNacimiento = formatDate(nuevo.fechaNacimiento);
console.log('Esta es la info', data);
try {
updateIsLoading(true);
const res = await axiosInstance.put(`/caso_especial/update`, data, admin.token);
const res = await axiosInstance.put(`/caso-especial/update/${idCasoEspecial}`, data);
imprimirMensaje(res.data.message);
router.push("/admin/casos_especiales/caso_especial");
router.push("/administrador/casos_especiales");
} catch (err: unknown) {
let msg: unknown = err;
if (isAxiosError(err) && err.response?.data) {
@@ -110,65 +125,139 @@ 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">
<h3 className="text-2xl font-semibold mb-4">Editar información del alumno</h3>
<h2 className="fw-bold mb-4">Editar información del alumno</h2>
{/* Correo */}
<div>
<label className="block mb-1 font-medium">Correo electrónico</label>
<div className="mb-3">
<label className="form-label fw-semibold 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="border rounded p-2 w-full"
className="form-control border rounded p-2 w-full"
/>
</div>
{/* Teléfono */}
<div>
<label className="block mb-1 font-medium">Teléfono</label>
<div className="mb-3">
<label className="form-label fw-semibold 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="border rounded p-2 w-full"
className="form-control border rounded p-2 w-full"
/>
</div>
{/* Dirección */}
<div>
<label className="block mb-1 font-medium">Dirección</label>
<div className="mb-3">
<label className="form-label fw-semibold 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="border rounded p-2 w-full"
className="form-control 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="block mb-1 font-medium">Fecha de inicio</label>
{/* <div>
<label className="form-label fw-semibold 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="border rounded p-2 w-full"
/>
</div>
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"
{/* Fecha fin */}
showMonthDropdown
showYearDropdown
scrollableYearDropdown
yearDropdownItemNumber={60}
dropdownMode="select"
locale="es"
/>
</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
{nuevo.fechaInicio && (
<div>
<label className="block mb-1 font-medium">Fecha de fin</label>
@@ -178,29 +267,61 @@ 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="border rounded p-2 w-full"
className="form-control border rounded p-2 w-full"
locale='es'
/>
</div>
</div> */}
{/* Motivo */}
{viejo.motivo && (
<div>
<label className="block mb-1 font-medium">Motivo</label>
<div className="mb-3">
<label className="form-label fw-semibold block mb-1 font-medium">Motivo</label>
<select
value={nuevo.motivo || ""}
onChange={(e) => setNuevo({ ...nuevo, motivo: e.target.value })}
className="border rounded p-2 w-full my-2"
className="form-control border rounded p-2 w-full my-2"
>
<option value="">Seleccione...</option>
<option value="1">Tercera edad</option>
@@ -211,28 +332,28 @@ export default function EditarAlumno({
{/* Dependencia */}
{viejo.dependencia && (
<div>
<label className="block mb-1 font-medium">Dependencia</label>
<div className="mb-3">
<label className="form-label fw-semibold 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="border rounded p-2 w-full"
className="form-control border rounded p-2 w-full"
/>
</div>
)}
{/* Institución */}
{viejo.institucion && (
<div>
<label className="block mb-1 font-medium">Institución</label>
<div className="mb-3">
<label className="form-label fw-semibold 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="border rounded p-2 w-full"
className="form-control border rounded p-2 w-full"
/>
</div>
)}
@@ -5,6 +5,7 @@ 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;
@@ -26,6 +27,11 @@ 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,
@@ -52,25 +58,81 @@ 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 data: Record<string, unknown> = { idUsuario: responsable.idUsuario };
const idResponsable = localStorage.getItem("idResponsable");
if (!idResponsable) return
const data: Record<string, unknown> = { idUsuario: responsable.idUsuario };
if (nuevo.correo) data.correo = nuevo.correo;
if (nuevo.nombre) data.nombre = nuevo.nombre;
// Hacemos el parseo de la fecha
//data.fechaNacimiento = convertirFecha(data.fechaNacimiento);
try {
updateIsLoading(true);
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);
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);
} finally {
updateIsLoading(false);
}
@@ -80,21 +142,39 @@ 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.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);
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);
} finally {
updateIsLoading(false);
updateIsLoading(false);
}
};
@@ -133,12 +213,7 @@ export default function EditarResponsable({
<div className="pt-5 flex gap-4">
<button
disabled={mostrarBoton()}
onClick={() =>
imprimirWarning(
"¿Seguro(a) que quiere actualizar la información de este usuario?",
actualizar
)
}
onClick={confirmarActualizacion}
className={`mb-4 px-4 py-2 rounded text-white ${
mostrarBoton() ? "bg-gray-400 cursor-not-allowed" : "bg-blue-600 hover:bg-blue-700"
}`}
@@ -147,12 +222,7 @@ export default function EditarResponsable({
</button>
<button
onClick={() =>
imprimirWarning(
"¿Seguro(a) que quiere cambiar la contraseña para este usuario?",
password
)
}
onClick={confirmarCambioPassword}
className="ms-3 px-4 py-2 rounded bg-morado hover:bg-blue-600 text-white"
>
Cambiar contraseña
+157 -48
View File
@@ -2,17 +2,22 @@
import { useState, useEffect } from "react";
import { axiosInstance } from "@/api/config";
import { isAxiosError } from 'axios';
import axios, { 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 { FaRegCalendarAlt, FaUpload } from "react-icons/fa";
import { FaRegCalendar, FaUpload } from "react-icons/fa6";
import Swal from "sweetalert2";
import { registerLocale } from "react-datepicker";
import { es } from "date-fns/locale/es";
registerLocale("es", es);
interface Servicio {
Status: { idStatus?: number };
status: { idStatus?: number };
correo?: string;
direccion?: string;
telefono?: string;
@@ -30,7 +35,7 @@ interface AdminToken {
tokenArchivo?: { headers: Record<string, string> };
}; */}
interface Props {
admin: string;
admin: AdminToken;
imprimirError: (err: unknown) => void;
imprimirMensaje: (msg: string) => void;
imprimirWarning: (msg: string, onConfirm: () => void) => void;
@@ -44,8 +49,11 @@ 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("");
@@ -62,6 +70,14 @@ 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) {
@@ -82,13 +98,16 @@ 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?idServicio=${servicioid}`);
const res = await axiosInstance.get(`/servicio/admin/${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");
}
@@ -122,25 +141,61 @@ export default function EditarServicio({
if (direccion) data.direccion = direccion;
if (correo) data.correo = correo;
if (telefono) data.telefono = telefono;
if (fechaInicio) data.fechaInicio = fechaInicio;
if (fechaFin) data.fechaFin = fechaFin;
if (fechaNacimiento) data.fechaNacimiento = fechaNacimiento;
// 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");
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);
const res = await axiosInstance.put(`/servicio/update`, formData); //, admin.tokenArchivo
imprimirMensaje(res.data.message);
// 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);
updateIsLoading(false);
router.push("/administrador/servicio");
router.replace("/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);
}
};
@@ -151,19 +206,25 @@ 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.put(`/usuario/new_password_alumno`, { idServicio: servicioid });
imprimirMensaje(res.data.message);
const res = await axiosInstance.post(`/usuario/new-password-alumno/${data.servicioid}`);
// imprimirMensaje(res.data.message);
Swal.fire('Exito', 'Se actualizo la contraseña', 'success')
updateIsLoading(false);
router.push("/administrador");
router.replace("/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);
}
};
@@ -197,8 +258,44 @@ 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))
@@ -218,7 +315,7 @@ export default function EditarServicio({
useEffect(() => {
const id = Number(localStorage.getItem("idServicio"));
console.log("ID Servicio para editar:", id);
if (!id) router.push("/administrador");
if (!id) router.replace("/administrador");
else {
setServicioid(id);
console.log("idServicio seteado:", id);
@@ -254,18 +351,27 @@ export default function EditarServicio({
<FormLabel>Fecha de inicio</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
<FaRegCalendar />
</InputGroup.Text>
<DatePicker
selected={selectedInicio}
onChange={(date: Date | null ) => {
if (date) setFechaInicio(date);
if (date) setSelectedInicio(date);
}}
placeholderText="Selecciona una fecha de inicio"
minDate={fechaInicio}
// minDate={fechaInicio} Quitamos la restricción de fecha mínima
dateFormat={'dd-MM-yyyy'}
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
showMonthDropdown
showYearDropdown
scrollableYearDropdown
yearDropdownItemNumber={60}
dropdownMode="select"
locale='es'
/>
</InputGroup>
</FormGroup>
@@ -276,22 +382,32 @@ export default function EditarServicio({
<FormLabel>Fecha de fin</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
<FaRegCalendar />
</InputGroup.Text>
<DatePicker
selected={selectFechaFin}
onChange={(date: Date | null) => {
if (date) setFechaFin(date);
if (date) setSelectedFechaFin(date);
}}
placeholderText="Selecciona una fecha de fin"
dateFormat={'dd-MM-yyyy'}
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
showMonthDropdown
showYearDropdown
scrollableYearDropdown
yearDropdownItemNumber={60}
dropdownMode="select"
locale='es'
/>
</InputGroup>
</FormGroup>
</Col>
{/*
Fecha inicio
<div className="mb-3">
<label className="form-label fw-semibold block font-medium mb-1">Fecha de inicio</label>
<DatePicker
@@ -304,7 +420,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
@@ -316,6 +432,7 @@ export default function EditarServicio({
className="border rounded p-2 w-full"
/>
</div>
*/}
{/* Dirección */}
{servicio.direccion && (
@@ -346,7 +463,8 @@ export default function EditarServicio({
</div>
)}
{servicio.fechaNacimiento && (
{/*
{servicio.fechaNacimiento && (
<Col>
<FormGroup>
<FormLabel>Fecha de nacimiento</FormLabel>
@@ -359,7 +477,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">
@@ -368,13 +486,14 @@ export default function EditarServicio({
<DatePicker
selected={fechaFin}
onChange={(date: Date | null) => {
if (date) setFechaFin(date);
if (date) setFechaNacimiento(date);
}}
minDate={fechaInicio}
className="border rounded p-2 w-full"
/>
</div>
)}
*/}
<FormGroup className="mb-4">
<FormLabel>Carta de aceptaciòn</FormLabel>
@@ -382,7 +501,7 @@ export default function EditarServicio({
<div
className="border p-4 text-center rounded"
style={{ cursor: "pointer" }}
onClick={() => document.getElementById("application/pdf")?.click()}
onClick={() => document.getElementById("pdfInput")?.click()}
>
<FaUpload size={40} className="mb-2"/>
<p className="mb-1">
@@ -395,7 +514,7 @@ export default function EditarServicio({
<input
id="pdfInput"
type="file"
accept=".pdf"
accept="application/pdf"
style={{ display: 'none'}}
onChange={(e) => {
setCartaAceptacion(e.target.files ? e.target.files[0] : null)
@@ -423,12 +542,7 @@ export default function EditarServicio({
<div className="mt-6 flex gap-4 mb-5">
<button
disabled={mostrar()}
onClick={() =>
imprimirWarning(
"¿Seguro(a) que quiere actualizar estos datos?",
actualizar
)
}
onClick={confirmarActualizarDatos}
className={`me-2 px-4 py-2 rounded text-white ${
mostrar()
? "bg-gray-400 cursor-not-allowed"
@@ -440,14 +554,9 @@ export default function EditarServicio({
<button
disabled={
servicio.Status.idStatus === 1 || servicio.Status.idStatus === 7
}
onClick={() =>
imprimirWarning(
"¿Seguro(a) que quieres cambiar/reenviar la contraseña de este alumno?",
password
)
servicio.status.idStatus === 1 || servicio.status.idStatus === 7
}
onClick={confirmarEnvioPassword}
className="px-4 py-2 rounded bg-blue-500 hover:bg-blue-600 text-white"
>
Cambiar/Reenviar contraseña
@@ -3,6 +3,7 @@ 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 {
@@ -52,15 +53,28 @@ 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,10 +43,17 @@ export default function InformacionResponsable({ admin, imprimirError, updateIsL
const obtenerResponsable = async () => {
try {
updateIsLoading(true);
const res = await axiosInstance.get(`/usuario/responsable?idUsuario=${idResponsable}`, {
console.log("Este es el id del responsable", idResponsable)
const res = await axiosInstance.get(`/usuario/responsable/${idResponsable}`, {
headers: { Authorization: admin.token },
});
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');
@@ -60,21 +67,26 @@ export default function InformacionResponsable({ admin, imprimirError, updateIsL
// Funcion para obtener programas
const obtenerProgramas = async () => {
try {
const res = await axiosInstance.get(`/programa/programas_admin?idUsuario=${idResponsable}`, {
headers: { Authorization: admin.token },
});
setProgramas(res.data);
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);
} catch (err: unknown) {
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');
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');
}
};
// Cargar al montar
useEffect(() => {
const id = Number(localStorage.getItem('idResponsable'));
if (!id) {
router.push('/administrador/responsables');
} else {
@@ -109,18 +121,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,6 +5,7 @@ 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;
@@ -21,9 +22,10 @@ interface Status {
}
interface Alumno {
Usuario?: Usuario;
Carrera?: Carrera;
Status?: Status;
idCasoEspecial: number;
usuario?: Usuario;
carrera?: Carrera;
status?: Status;
creditos?: number;
correo?: string;
fechaNacimiento?: string;
@@ -56,23 +58,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">
@@ -265,14 +267,15 @@ export default function InformacionCasoEspecial({ alumno }: Props) {
</Form.Group>
*/}
{(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")}
{(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"
>
Editar información
</Button>
<span>Editar información</span>
</Link>
</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,6 +47,7 @@ interface Datos {
fechaLiberacion?: string;
telefono?: string;
direccion?: string;
responsableIntermo?: string;
/*
cartaAceptacion?: boolean;
cartaTermino?: boolean;
@@ -75,7 +76,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();
@@ -97,11 +98,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
@@ -136,32 +137,37 @@ 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:</label>
<p className="form-control">{datos.Programa?.programa}</p>
<label className="form-label fw-semibold">Programa interno:</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>
<p className="form-control">{datos.programa?.usuario?.nombre}</p>
</div>
<div className="mb-3">
<label className="form-label fw-semibold">Profesor:</label>
<p className="form-control">{datos?.responsableIntermo || '-'}</p>
</div>
<div 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>
@@ -170,30 +176,58 @@ 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 className="form-label fw-semibold">Fecha de nacimiento:</label>
<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 || '-'}</p>
<p className="form-control">{datos.creditos?.replace(/^0+/, '') + '%' || '-'}</p>
</div>
{datos.telefono && (
@@ -230,7 +264,6 @@ 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>
@@ -272,7 +305,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/liberacion`, data, { headers: { Authorization: `Bearer ${admin.token}` } });
imprimirMensaje(res.data.message);
router.push('/admin/casos_especiales');
updateIsLoading(true);
const res = await axiosInstance.put(`/caso-especial/liberaciony/${idCasoEspecial}`, data);
imprimirMensaje(res.data.message);
router.replace('/administrador/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">
<div className="mt-2 mb-4">
<button
className="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600"
onClick={() =>
@@ -4,6 +4,7 @@ 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 } } };
@@ -35,24 +36,48 @@ 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,
correoOtroResponsable,
//idUsuario: responsable.idUsuario,
correo: 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`, data);
const res = await axiosInstance.put(`/programa/reasignar_programas/${responsable.idUsuario}`, data);
Swal.fire('Exito', 'Se reasignaron los programas con exito', 'success')
updateIsLoading(false);
imprimirMensaje(res.data.message);
router.push("/admin/responsables/responsable");
// imprimirMensaje(res.data.message);
router.replace("/administrador/responsables");
} 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);
}
};
@@ -89,12 +114,7 @@ export default function ReasignacionProgramas({
: "bg-blue-600 hover:bg-blue-700"
}`}
disabled={mostrarBoton()}
onClick={() =>
imprimirWarning(
`¿Seguro(a) que quiere eliminar a ${correoOtroResponsable} y pasar todos los programas a ${responsable.usuario}?`,
reasignarProgramas
)
}
onClick={confirmarActualizacion}
>
Reasignar
</button>
@@ -0,0 +1,87 @@
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>
)
}
+45 -6
View File
@@ -1,10 +1,15 @@
"use client";
import { useState } from "react";
import { axiosInstance } from "@/api/config";
import { Button, Col, FormGroup, FormLabel, InputGroup, Row } from "react-bootstrap";
import { Button, Col, FormGroup, FormLabel, InputGroup } from "react-bootstrap";
import DatePicker from "react-datepicker";
import moment from "moment";
import { FaCalendarAlt, FaRegCalendarAlt } from "react-icons/fa";
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 { saveAs } from "file-saver";
interface Props {
@@ -32,10 +37,15 @@ 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"),
fin: moment(selectedFin).format("YYYY-MM-DD"),
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
},
responseType: "blob",
});
@@ -59,6 +69,12 @@ 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
@@ -66,6 +82,13 @@ 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
}
@@ -83,7 +106,7 @@ export default function Reporte({ admin }: Props) {
<FormLabel>Seleccione una fecha de inicio:</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
<FaRegCalendar />
</InputGroup.Text>
<DatePicker
selected={selectedInicio}
@@ -95,6 +118,14 @@ 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>
@@ -105,7 +136,7 @@ export default function Reporte({ admin }: Props) {
<FormLabel>Seleccione una fecha de fin:</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
<FaRegCalendar />
</InputGroup.Text>
<DatePicker
selected={selectedFin}
@@ -117,6 +148,14 @@ 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,8 +7,9 @@ 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 { FaInfoCircle, FaSchool, FaUser } from "react-icons/fa";
import { FaCircleInfo, FaUser } from "react-icons/fa6";
import { useRouter } from "next/navigation";
import Image from "next/image";
interface Admin {
idTipoUsuario?: number;
@@ -92,11 +93,12 @@ 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}`, config);
const res = await axiosInstance.get(`/caso-especial/servicios_especiales?pagina=${pagina}${query}`);
console.debug('Respuesta casos especiales:', res.data);
setData(res.data.serviciosEspeciales || []);
setData(res.data.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)) {
@@ -162,7 +164,7 @@ export default function TablaCasosEspeciales({ admin, imprimirError }: Props) {
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaSchool />
<Image src="/image/numCuenta.svg" width={24} height={24} alt="Icono de escuela" />
</InputGroup.Text>
<Form.Control
@@ -194,7 +196,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>
@@ -206,13 +208,14 @@ export default function TablaCasosEspeciales({ admin, imprimirError }: Props) {
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaInfoCircle />
<FaCircleInfo />
</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/fa";
import { FaEnvelope, FaUser } from "react-icons/fa6";
export default function TablaResponsables() {
const [info, setInfo] = useState<Responsables[]>([]);
@@ -68,6 +68,7 @@ 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>
@@ -80,6 +81,7 @@ 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,14 +1,14 @@
"use client";
import React, { useEffect, useRef, useState } from "react";
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, FaSchool, FaInfoCircle } from "react-icons/fa";
import { FaUser, FaCircleInfo } from "react-icons/fa6";
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;
@@ -38,6 +38,8 @@ export default function TablaServicioSocial({ admin, imprimirError }: Props) {
if (admin?.idTipoUsuario === 1) {
obtenerCatalogoStatus();
}
console.log("Esta es la info que tiene al guardar en la state", data)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [admin?.idTipoUsuario]);
@@ -70,10 +72,14 @@ export default function TablaServicioSocial({ admin, imprimirError }: Props) {
// asegurar que enviamos Authorization si existe en admin, y loggear token para debug
const config = admin?.token ? { headers: { Authorization: `Bearer ${admin.token}` } } : undefined;
console.debug('Obtener servicios - localStorage token:', typeof window !== 'undefined' ? localStorage.getItem('token') : undefined, 'admin.token:', admin?.token);
const res = await axiosInstance.get(`/servicio/servicios_admin?pagina=${paginaActual}${query}`, config);
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);
@@ -98,7 +104,6 @@ export default function TablaServicioSocial({ admin, imprimirError }: Props) {
}
};
return (
<section>
<div className="columns">
@@ -107,12 +112,12 @@ export default function TablaServicioSocial({ admin, imprimirError }: Props) {
<section className="container-fluid my-4">
<Form onSubmit={(e) => { e.preventDefault(); obtenerServicios(1); }}>
<Row className="g-3">
<Col md={3}>
<Col xs={12} sm={6} lg={3}>
<FormGroup>
<FormLabel>Número de Cuenta</FormLabel>
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaSchool />
<Image src="/image/numCuenta.svg" width={24} height={24} alt="Icono de escuela" />
</InputGroup.Text>
<FormControl
type="text"
@@ -127,7 +132,7 @@ export default function TablaServicioSocial({ admin, imprimirError }: Props) {
</FormGroup>
</Col>
<Col md={3}>
<Col xs={12} sm={6} lg={3}>
<FormGroup>
<FormLabel>Nombre</FormLabel>
<InputGroup>
@@ -146,17 +151,19 @@ export default function TablaServicioSocial({ admin, imprimirError }: Props) {
</FormGroup>
</Col>
<Col md={3}>
<Col xs={12} sm={6} lg={3}>
<FormGroup>
<FormLabel>Status</FormLabel>
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaInfoCircle />
<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) => (
@@ -167,7 +174,7 @@ export default function TablaServicioSocial({ admin, imprimirError }: Props) {
</FormGroup>
</Col>
<Col md={3} className="d-flex align-items-end">
<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>
@@ -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>
);
}
+7 -4
View File
@@ -1,13 +1,16 @@
"use client";
import React from "react";
import { FaUserPlus, FaUserCheck, FaUser, FaUsers, FaUserClock, FaUserTie } from "react-icons/fa";
import { FaUserPlus, FaUserCheck, FaUser, FaUsers, FaUserClock, FaUserTie } from "react-icons/fa6";
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 },
@@ -21,8 +24,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 = idStatus >= step.id;
const isCurrent = idStatus === step.id;
const isActive = statusMapeado >= step.id;
const isCurrent = statusMapeado === step.id;
return (
<div key={index} className="text-center position-relative flex-fill">
@@ -1,14 +1,11 @@
"use client";
import React, { useState } from "react";
import DatePicker from "react-datepicker";
import { useState } from "react";
import "react-datepicker/dist/react-datepicker.css";
import { axiosInstance } from '@/api/config';
import { isAxiosError } from 'axios';
import type { AxiosResponse } from 'axios';
import moment from "moment";
import { Col, FormGroup, FormLabel, InputGroup } from "react-bootstrap";
import { FaRegCalendarAlt } from "react-icons/fa";
import { useRouter } from "next/navigation";
import Swal from "sweetalert2";
interface Alumno {
token: { headers: Record<string, string> };
@@ -19,7 +16,7 @@ interface Alumno {
interface Props {
idServicio: number;
alumno: Alumno;
imprimirMensaje: (msg: string) => void;
imprimirMensaje: (message: string) => void;
imprimirWarning: (msg: string, onConfirm: () => void) => void;
imprimirError: (err: unknown) => void;
obtenerServicio: () => void;
@@ -30,11 +27,11 @@ interface PreRegistroData {
idServicio: number;
direccion: string;
telefono: string;
fechaNacimiento: string;
// fechaNacimiento: string;
}
interface ApiResponse {
data: { message: string };
message: string ;
}
export default function CompletarDatosPersonales({
@@ -51,28 +48,62 @@ 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);
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 {
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);
// }
}
// } finally {
// updateIsLoading(false);
// }
};
return (
@@ -80,7 +111,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>
@@ -100,7 +131,7 @@ export default function CompletarDatosPersonales({
/>
</InputGroup>
</FormGroup>
</Col>
</Col> */}
{/*
<div className="mb-3">
<label className="block mb-1 fw-semibold">Fecha de nacimiento</label>
@@ -133,10 +164,10 @@ export default function CompletarDatosPersonales({
{/* Dirección */}
<div className="mb-3">
<label className="block mb-1 form-label fw-semibold mb-2">Dirección</label>
<label className="block mb-1 form-label fw-semibold mb-2">Domicilio</label>
<input
type="text"
placeholder="Dirección"
placeholder="Domicilio"
maxLength={200}
value={direccion}
onChange={(e) => setDireccion(e.target.value)}
@@ -148,12 +179,7 @@ export default function CompletarDatosPersonales({
<div className="text-center mt-4">
<button
disabled={!telefono || !direccion}
onClick={() =>
imprimirWarning(
"¿Estas seguro(a) que tus datos son correctos?",
terminarPreRegistro
)
}
onClick={confirmarDatos}
className={`px-4 py-2 rounded text-white mb-5 ${
!telefono || !direccion
? "bg-gray-400 cursor-not-allowed"
@@ -2,6 +2,9 @@
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;
@@ -25,9 +28,16 @@ type Tabla = {
type Formulario = { titulo?: string; descripcion?: string; preguntas: Pregunta[]; tablas: Tabla[] };
export default function FullCuestionario() {
interface Props{
idServicio: number;
}
export default function FullCuestionario({ idServicio}: Props) {
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(
() => ({
@@ -97,6 +107,29 @@ export default function FullCuestionario() {
// 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(() => {
@@ -242,20 +275,41 @@ export default function FullCuestionario() {
if (!validateResponses()) return;
setIsLoading(true);
try {
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');
// 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');
if (typeof window !== 'undefined') {
localStorage.removeItem('idCuestionarioAlumno');
window.location.href = '/alumno';
// window.location.href = '/alumno';
router.replace('/alumno');
}
} 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);
} 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);
} finally {
setIsLoading(false);
}
@@ -290,7 +344,7 @@ export default function FullCuestionario() {
{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)} /> {op}
<input type="radio" name={p.id} checked={respuestas[p.id] === op} onChange={() => updateRespuestas(p.id, op)} disabled={isFormDisabled} /> {op}
</label>
</div>
))}
@@ -301,7 +355,7 @@ export default function FullCuestionario() {
{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)} /> {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)} disabled={isFormDisabled} /> {op}
</label>
</div>
))}
@@ -309,7 +363,7 @@ export default function FullCuestionario() {
)}
{p.tipo === 'texto' && (
<div>
<input id={p.id} className="form-control bg-transparent" type="text" value={(respuestas[p.id] as string) || ''} onChange={(e) => updateRespuestas(p.id, e.target.value)} maxLength={p.limite || 200} placeholder="Escribe tu respuesta aquí" />
<input id={p.id} className="form-control 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} />
</div>
)}
</div>
@@ -339,7 +393,7 @@ export default function FullCuestionario() {
<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)} />
<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} />
</td>
))}
</tr>
@@ -352,9 +406,14 @@ export default function FullCuestionario() {
})}
<div className="my-6 mb-6 text-center mb-5">
<button className="btn btn-success is-medium" onClick={submitForm} disabled={!isFormComplete}>
<button className="btn btn-success is-medium" onClick={submitForm} disabled={!isFormComplete || isFormDisabled || isLoading}>
{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>
+12 -11
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,19 +80,20 @@ 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
+75 -28
View File
@@ -1,19 +1,23 @@
"use client";
import { useState, useEffect } from "react";
import { axiosInstance } from "@/api/config";
import { isAxiosError } from 'axios';
import { Button, FormGroup, FormLabel } from "react-bootstrap";
import { FaUpload } from "react-icons/fa";
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";
interface Alumno {
tokenArchivo: string;
tokenAlumno: string;
}
interface Servicio {
idServicio: number;
idCuestionarioAlumno?: number;
idCuestionarioAlumno2?: number;
//idCuestionarioAlumno?: number;
cuestionarioAlumno?: object;
//idCuestionarioAlumno2?: number;
cuestionarioAlumno2?: object;
informeGlobal?: string;
}
@@ -37,14 +41,39 @@ 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;
@@ -54,18 +83,39 @@ 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);
const res = await axiosInstance.put(`/servicio/informe_global`, formData, { headers: { "Content-Type": "multipart/form-data", Authorization: alumno.tokenArchivo } });
imprimirMensaje(res.data.message);
obtenerServicio();
// 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();
setFile(null);
} catch (err: unknown) {
if (isAxiosError(err)) {
imprimirError((err.response?.data) || err.message);
} else {
imprimirError(err);
}
// setTimeout(() => {
// router.refresh();
// }, 1000);
//router.refresh(); // Para recargar la pagina y valide si tiene el informe.
} catch (err) {
Swal.fire('Error', 'No se pudo enviar el archivo, por favor intentelo mas tarde.', 'error')
// if (isAxiosError(err)) {
// imprimirError((err.response?.data) || err.message);
// } else {
// imprimirError(err);
// }
} finally {
updateIsLoading(false);
}
@@ -77,17 +127,19 @@ export default function PreTermino({
Cuestionario de evaluación del programa de servicio social.
</h6>
{!servicio.idCuestionarioAlumno && !servicio.idCuestionarioAlumno2 ? (
{/* !servicio.idCuestionarioAlumno && !servicio.idCuestionarioAlumno2 ? */}
{servicio.cuestionarioAlumno2 == null && servicio.cuestionarioAlumno == null ? (
<div className="mb-5">
<a
href="/alumno/cuestionario"
<Link
href={`/alumno/cuestionario/${servicio.idServicio}`}
className="btn btn-outline-primary is-info is-light"
>
<span className="icon">
<i className="fas fa-book-open"></i>
</span>
<span>Cuestionario</span>
</a>
</Link>
</div>
) : (
<div className="mb-6">
@@ -102,7 +154,7 @@ export default function PreTermino({
<h6 className="label fw-semibold">
Informe global de actividades{" "}
{!servicio.informeGlobal && (
{servicioLocal.informeGlobal && (
<span>(en formato .PDF. No se aceptan fotos)</span>
)}
.
@@ -141,7 +193,7 @@ Codigo anterior para el envio del archivo
</div>
*/}
{!servicio.informeGlobal ? (
{!servicioLocal.informeGlobal ? (
<div className="ph-5">
<FormGroup>
<div className="border p-4 text-center rounded"
@@ -173,12 +225,7 @@ Codigo anterior para el envio del archivo
<Button
className=""
disabled={!file}
onClick={() =>
imprimirWarning(
"¿Estas seguro(a) de querer subir este informe global?",
enviarInformeGlobal
)
}
onClick={confirmarInforme}
>Enviar</Button>
</div>
</div>
@@ -1,15 +1,22 @@
'use client';
import React, { useState, useEffect } from 'react';
import { useState, useEffect } from 'react';
import { axiosInstance } from '@/api/config';
import moment from 'moment';
import 'bootstrap/dist/css/bootstrap.min.css';
import { Col, FormGroup, FormLabel, InputGroup } from 'react-bootstrap';
import { FaRegCalendarAlt, FaUpload } from 'react-icons/fa';
import { FaRegCalendar, FaUpload } from 'react-icons/fa6';
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';
// 🔹 Tipos estrictos
registerLocale("es", es);
5
interface Alumno {
idUsuario?: number;
idCarrera?: number;
@@ -18,21 +25,29 @@ 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: { headers: Record<string, string> };
tokenArchivo: string;
}
interface Props {
//responsable: Responsable;
// responsable: Responsable;
imprimirError: (msg: { message: string }) => void;
imprimirMensaje: (msg: string) => void;
imprimirWarning: (msg: string, callback: () => void) => void;
imprimirWarning: (msg: string, callback: () => void, title: string) => void;
updateIsLoading: (value: boolean) => void;
}
export default function CasoEspecialForm({
//responsable,
// responsable,
imprimirError,
imprimirMensaje,
imprimirWarning,
@@ -50,11 +65,19 @@ 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>(new Date());
const [fechaNacimiento, setFechaNacimiento] = useState<Date | null>(null);
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());
@@ -65,7 +88,7 @@ export default function CasoEspecialForm({
}
const handleFechaNacimientoChange = (date: Date | null) => {
setFechaNacimiento(date || new Date())
setFechaNacimiento(date)
}
// Función para resetear campos
@@ -80,7 +103,7 @@ export default function CasoEspecialForm({
setAlumno({});
setFechaInicio(new Date());
setFechaFin(new Date());
setFechaNacimiento(new Date());
setFechaNacimiento(null);
setFile(null);
};
@@ -114,19 +137,27 @@ export default function CasoEspecialForm({
const buscarAlumno = async (): Promise<void> => {
try {
updateIsLoading(true);
const res = await axiosInstance.get(`/usuario/escolares?numeroCuenta=${numeroCuenta}`);
const res = await axiosInstance.post(`/usuario/escolares/${numeroCuenta}`);
resetear();
console.log('Esta es la res:' , res)
setAlumno(res.data);
} catch (err: unknown) {
resetear();
setNumeroCuenta('');
let msg = { message: 'Error al buscar alumno.' };
if (typeof err === 'object' && err !== null && 'response' in err) {
const anyErr = err as { response?: { data?: { message?: unknown } } };
const m = anyErr.response?.data as { message?: unknown } | undefined;
msg = { message: m?.message ? String(m.message) : msg.message };
} else if (err instanceof Error) msg = { message: err.message };
imprimirError(msg);
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);
} finally {
updateIsLoading(false);
}
@@ -136,52 +167,179 @@ 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: 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,
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 }),
};
console.log("Datos a enviar en caso especial:", data);
const formData = new FormData();
formData.append('alumno', JSON.stringify(data));
formData.append('archivos', file);
// 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);
try {
updateIsLoading(true);
const res = await axiosInstance.post(`/caso_especial/nuevo`, formData);
// 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}` : '',
},
});
resetear();
setNumeroCuenta('');
imprimirMensaje(res.data.message);
const mensaje = res?.data?.message || 'Se creo el servicio social con exito.'
Swal.fire({
icon: 'success',
title: 'Exito',
text: mensaje,
})
// imprimirMensaje(res.data.message);
} catch (err: unknown) {
let msg: { message: string } = { message: 'Error al enviar formulario.' };
if (typeof err === 'object' && err !== null && 'response' in err) {
const anyErr = err as { response?: { data?: unknown } };
if (anyErr.response && typeof anyErr.response.data === 'object' && anyErr.response.data !== null) {
const d = anyErr.response.data as { message?: unknown } | unknown;
if (d && typeof d === 'object' && 'message' in d && typeof (d as { message?: unknown }).message === 'string') {
msg = { message: (d as { message?: unknown }).message as string };
}
}
} else if (err instanceof Error) {
msg = { message: err.message };
}
imprimirError(msg);
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);
} finally {
updateIsLoading(false);
}
};
const formatDate = (date: Date) => {
return date.toISOString().split("T")[0];
};
// Actualizar fechas automáticamente
const updateFechas = (): void => {
const nuevaFin = new Date(
@@ -201,11 +359,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]);
@@ -220,9 +378,12 @@ export default function CasoEspecialForm({
updateFechas();
}, []);
const confirmarCasoEspecial = () => {
Swal.fire({
})
}
{/* Validaciones del cuestionario */}
// Render
return (
@@ -230,18 +391,21 @@ export default function CasoEspecialForm({
<div className="mb-3">
<label className="form-label">Número de Cuenta</label>
<div className="input-group">
<input
type="text"
className="form-control"
placeholder="Número de Cuenta"
maxLength={9}
value={numeroCuenta}
onChange={(e) => setNumeroCuenta(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && buscarAlumno()}
/>
<button className="text-white rounded-1" onClick={buscarAlumno}>
Buscar
</button>
<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>
</div>
</div>
@@ -269,6 +433,36 @@ 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
@@ -306,7 +500,7 @@ export default function CasoEspecialForm({
<FormLabel>Fecha de inicio</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
<FaRegCalendar />
</InputGroup.Text>
<DatePicker
@@ -317,6 +511,14 @@ 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>
@@ -328,45 +530,31 @@ export default function CasoEspecialForm({
<FormLabel>Fecha de fin</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
<FaRegCalendar />
</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>
@@ -469,12 +657,13 @@ 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'>Tmaño máximo 20MB</p>
<p className='is-size-6'>Tamañ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)}
/>
@@ -500,14 +689,13 @@ export default function CasoEspecialForm({
onClick={() =>
imprimirWarning(
'¿Estás seguro(a) de querer crear un nuevo caso especial?',
enviar
)
}
enviar,
'Aviso'
)}
>
Enviar
</button>
</div>
<BotonRegresar />
</div>
);
@@ -4,7 +4,8 @@ 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 { FaInfoCircle, FaSchool, FaUser } from "react-icons/fa";
import { FaCircleInfo, FaUser } from "react-icons/fa6";
import Image from "next/image";
interface Responsable {
idTipoUsuario: number;
@@ -20,7 +21,7 @@ interface ServicioEspecial {
idServicio: number;
nombre: string;
numeroCuenta: string;
status: string;
Status: string;
// agrega más campos según lo que devuelva tu API
}
@@ -71,13 +72,9 @@ 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}`, {
headers: {
Authorization: `Bearer ${responsable.token}`,
},
});
const res = await axiosInstance.get(`/caso-especial/servicios_especiales?pagina=${page}${query}`);
setData(res.data.serviciosEspeciales);
setData(res.data.data.serviciosEspeciales);
setTotal(res.data.count);
} catch (err: unknown) {
let msg = 'Error al obtener los casos especiales';
@@ -130,7 +127,7 @@ export default function LiberarCasoEspecial({ responsable, imprimirError }: Prop
<FormLabel className="fw-semibold">Número de Cuenta</FormLabel>
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaSchool />
<Image src="/image/numCuenta.svg" width={24} height={24} alt="Icono de escuela" />
</InputGroup.Text>
<FormControl
type="text"
@@ -169,7 +166,7 @@ export default function LiberarCasoEspecial({ responsable, imprimirError }: Prop
<FormLabel className="fw-semibold">Status</FormLabel>
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaInfoCircle />
<FaCircleInfo />
</InputGroup.Text>
<Form.Select
className="rounded-4"
+2
View File
@@ -1,3 +1,4 @@
'use client'
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
@@ -14,6 +15,7 @@ export default function Logout() {
const handleLogout = () => {
localStorage.removeItem("token");
localStorage.removeItem('usuario');
localStorage.clear();
router.push("/");
}
@@ -2,6 +2,9 @@
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 };
@@ -44,6 +47,8 @@ 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).`,
@@ -153,12 +158,16 @@ export default function CuestionarioResponsbale2({
else answered = answer !== null && String(answer) !== '';
if (!answered) missing.push(`Pregunta ${p.numeroPregunta}`);
});
formulario.tablas.forEach((t) => {
formulario.tablas.forEach((t, index) => {
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) missing.push(`Tabla ${t.numeroPregunta}`);
if (!allAnswered) {
const letra = String.fromCharCode(65 + index);
missing.push(`Tabla ${t.numeroPregunta}.${letra}`);
}
});
return missing;
}, [formulario, respuestas]);
@@ -204,6 +213,7 @@ 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;
@@ -235,12 +245,32 @@ export default function CuestionarioResponsbale2({
const valor = respuestasTabla[idR as unknown as number];
let clave = `p${numPregunta}_${idR}`;
if (subLetra) clave = `p${numPregunta}_${subLetra}_${idR}`;
resultado[clave] = valor ?? '';
// antes resultado[clave] = valor ?? '';
// Modifique esta linea
if (valor !== null && valor !== '') {
resultado[clave] = valor;
}
}
} else {
const preguntaDef = formulario.preguntas.find((p) => p.id === key);
if (!preguntaDef) continue;
resultado[`p${preguntaDef.numeroPregunta}`] = resps[key] as string;
// VALIDAR CONDICIONAL (p6)
if (preguntaDef.condicional) {
const cond = preguntaDef.condicional;
if (resps[cond.preguntaId] !== cond.valor) {
continue; // NO mandar esta pregunta
}
}
const valor = resps[key];
if (valor !== null && String(valor).trim() !== '') {
resultado[`p${preguntaDef.numeroPregunta}`] = valor as string;
}
//resultado[`p${preguntaDef.numeroPregunta}`] = resps[key] as string;
}
}
return resultado;
@@ -251,21 +281,36 @@ export default function CuestionarioResponsbale2({
updateIsLoading(true);
setIsLoading(true);
try {
const data = { ...formatearRespuestas(respuestas), idServicio: localStorage.getItem('idServicio') };
const data = { ...formatearRespuestas(respuestas), idServicio: Number(localStorage.getItem('idServicio')) };
console.log('Datos a enviar:', data);
const headers = responsable?.token ? { Authorization: `Bearer ${responsable.token}` } : undefined;
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');
}
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');
// }
} finally {
updateIsLoading(false);
setIsLoading(false);
+164 -39
View File
@@ -1,12 +1,19 @@
"use client";
import React, { useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { axiosInstance } from "@/api/config";
import type { AxiosError } from "axios";
import { Button, Col, Form, FormGroup, FormLabel, InputGroup } from "react-bootstrap";
import { FaRegCalendarAlt, FaSchool, FaUpload } from "react-icons/fa";
import { Col, FormGroup, FormLabel, InputGroup } from "react-bootstrap";
import { FaRegCalendar, FaUpload } from "react-icons/fa6";
import DatePicker from "react-datepicker";
import { PiDotFill } from "react-icons/pi";
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);
type Responsable = {
idUsuario?: number;
@@ -61,11 +68,13 @@ export default function NuevoServicio({
const [programa, setPrograma] = useState<Programa>({});
const handleFechaInicioChange = (date: Date | null) => {
setFechaInicio(date || new Date());
// setFechaInicio(date || new Date());
if (date) setFechaInicio(date);
}
const handleFechaFinChange = (date: Date | null) => {
setFechaFin(date || new Date());
// setFechaFin(date || new Date());
if (date) setFechaFin(date);
}
const resetar = () => {
@@ -79,6 +88,17 @@ 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;
@@ -94,18 +114,29 @@ export default function NuevoServicio({
updateIsLoading(true);
try {
const headers = responsable?.token ? { Authorization: `Bearer ${responsable.token}` } : undefined;
const res = await axiosInstance.get<Alumno>(`/usuario/escolares?numeroCuenta=${encodeURIComponent(numeroCuenta)}`, { headers });
// Enviar headers como tercer parámetro (config), no como body
const res = await axiosInstance.post<Alumno>(`/usuario/escolares/${numeroCuenta}`);
resetar();
setAlumno(res.data);
obtenerProgramas();
updateIsLoading(false);
} catch (err: unknown) {
setNumeroCuenta("");
resetar();
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 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" });
}
};
@@ -122,35 +153,76 @@ export default function NuevoServicio({
correo,
programaInterno,
profesor,
fechaInicio: fechaInicio.toISOString(),
fechaFin: fechaFin.toISOString(),
fechaInicio: formatDate(fechaInicio),
fechaFin: formatDate(fechaFin),
} as const;
formData.append("alumno", JSON.stringify(data));
if (file) formData.append("cartaAceptacion", file, file.name);
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);
updateIsLoading(true);
try {
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 });
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}`,
}),
},
});
setNumeroCuenta("");
resetar();
updateIsLoading(false);
imprimirMensaje(res.data?.message || "Registrado");
Swal.fire('Exito', 'El alumno se registro en el servicio social', 'success')
// imprimirMensaje(res.data?.message || "Registrado");
resetar();
} 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" });
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 obtenerProgramas = async () => {
try {
const headers = responsable?.token ? { Authorization: `Bearer ${responsable.token}` } : undefined;
const res = await axiosInstance.get<Programa[]>(`/programa/programas_responsable?idUsuario=${responsable.idUsuario}`, { headers });
setProgramas(res.data);
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([]);
}
} catch (err: unknown) {
const axiosErr = err as AxiosError;
if (axiosErr?.response?.data) imprimirError(axiosErr.response.data as object);
@@ -182,34 +254,46 @@ 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 gap-2">
<div className="d-flex">
<InputGroup.Text>
<FaSchool />
<Image src="/image/numCuenta.svg" width={24} height={24} alt="Icono de escuela" />
</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 className="" onClick={() => buscarAlumno()}>Buscar</button>
<button type="button" className="" onClick={() => buscarAlumno()}>Buscar</button>
</div>
</div>
@@ -217,12 +301,14 @@ 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>
)}
@@ -230,20 +316,43 @@ 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={JSON.stringify(programa) } onChange={(e) => { try { setPrograma(JSON.parse(e.target.value)); } catch { setPrograma({}); } }}>
<option value={JSON.stringify({})} disabled>Seleccionar</option>
{programas.map((p, i) => (
<option key={i} value={JSON.stringify(p)}>{p.programa}</option>
<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) => (
<option key={i} value={JSON.stringify(p)}>{p.programa}</option>
))}
</select>
</div> */}
{programa.institucion && (
<div className="mb-3">
<label className="form-label fw-bolder">Institución</label>
@@ -332,7 +441,7 @@ export default function NuevoServicio({
<FormLabel className="fw-bolder">Fecha de inicio</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
<FaRegCalendar />
</InputGroup.Text>
<DatePicker
@@ -343,6 +452,14 @@ 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>
@@ -353,18 +470,26 @@ export default function NuevoServicio({
<FormLabel className="fw-bolder">Fecha de fin</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
<FaRegCalendar />
</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>
@@ -391,16 +516,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("fileInput")?.click()}>
<div className="border p-4 text-center rounded" style={{ cursor: "pointer" }} onClick={() => document.getElementById("pdfInput")?.click()}>
<FaUpload size={40} className="mb-2"/>
<p className="mb-1">
{file?.name || 'Arrastra aquí tu archivo o da click aquí para buscar'}
</p>
<p className="is-size-6">Tamaño máximo 200</p>
<p className="is-size-6">Tamaño máximo 20MB</p>
<p className="is-size-7">Si al momento de elegir un archivo este no se selecciona, haga click en cancelar en la ventana emergente e intente de nuevo.</p>
</div>
<input
id="fileInput"
id="pdfInput"
type="file"
style={{ display: 'none'}}
accept="application/pdf"
@@ -1,13 +1,12 @@
"use client";
import React, { useEffect, useState } from "react";
import { 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 { FaClipboardList, FaInfoCircle, FaSchool, FaUser } from "react-icons/fa";
import { Prev } from "react-bootstrap/esm/PageItem";
import { FaCircleInfo, FaUser } from "react-icons/fa6";
import Image from "next/image";
import { useRouter } from "next/navigation";
type Responsable = { idUsuario?: number; token?: string; idTipoUsuario?: number };
type StatusItem = { idStatus: number; status: string };
@@ -23,14 +22,26 @@ 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 }>({ idStatus: '' });
const [search, setSearch] = useState<{ numeroCuenta?: string; nombre?: string; idStatus?: string }>({ numeroCuenta: '', nombre: '', 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);
@@ -49,14 +60,21 @@ 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 idUsuario = localStorage.getItem('idUsuario') ?? '';
const idUsuario1 = 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<{ serviciosResponsable: ServicioSocialResponse[]; count: number }>(`/servicio/servicios_responsable?idUsuario=${idUsuario}&pagina=${pagina}${query}`);
const res = await axiosInstance.get(`/servicio/servicios_responsable?idUsuario=${idUsuario1}&pagina=${pagina}${query}`);
//const res = await axiosInstance.get(`/servicio/servicios_responsable?idUsuario=${idUsuario1}&pagina=${pagina}${query}`);
setData(res.data.serviciosResponsable);
setTotal(res.data.count);
setIsLoading(false);
@@ -87,6 +105,8 @@ 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
}, []);
@@ -99,7 +119,7 @@ export default function TablaServiciosResponsable({ responsable, imprimirError }
<FormLabel>Número de cuenta</FormLabel>
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaSchool />
<Image src="/image/numCuenta.svg" width={24} height={24} alt="Icono de escuela" />
</InputGroup.Text>
<FormControl
type="text"
@@ -133,21 +153,23 @@ 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">
<FaInfoCircle />
<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, 9).map((s) => (
{mounted && status.slice(0, 9).map((s) => (
<option key={s.idStatus} value={s.idStatus}>
{s.status}
</option>
@@ -158,10 +180,13 @@ export default function TablaServiciosResponsable({ responsable, imprimirError }
</Col>
<Col md={3} className="d-flex align-items-end">
<Button
type="button"
<Button
type="submit"
className="w-100 rounded-5"
disabled={isLoading}>{isLoading ? 'Buscando...' : 'Buscar'}
disabled={isLoading}
onClick={() => obtenerServicios(1)}
>
{isLoading ? 'Buscando...' : 'Buscar'}
</Button>
</Col>
</Row>
@@ -184,6 +209,7 @@ export default function TablaServiciosResponsable({ responsable, imprimirError }
columnasResponsable={true}
columnaCuestionario={true}
columnaCartaTermino={true}
onRowAction={onRowAction}
/>
</div>
);
+93 -17
View File
@@ -1,8 +1,11 @@
"use client";
import React, { useEffect, useState } from "react";
import { axiosInstance } from "@/api/config";
import type { AxiosError } from "axios";
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";
type Responsable = { tokenArchivo?: string };
@@ -31,6 +34,26 @@ 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();
@@ -38,20 +61,39 @@ 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 {
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' });
// 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' });
}
};
@@ -65,20 +107,54 @@ 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={() => imprimirWarning(`¿Estas seguro(a) de querer subir esta carta de ${tipoCarta}?`, subir)}>
<button className="btn btn-success" disabled={!file?.name} onClick={confirmarEnvioArchivo}>
Enviar
</button>
</div>
</div>
);
}
+109 -41
View File
@@ -46,25 +46,49 @@ export default function ServicioSocialTabla({
// 🔹 Cargar datos del padre si existen
useEffect(() => {
if (data && data.length > 0) {
setInfo(data);
setInfo(data);
}
}, [data]);
// 🔹 Paginación simple
function handlePrev() {
if (currentPage > 1) {
const np = currentPage - 1;
setCurrentPage(np);
onPageChange?.(np);
const np = currentPage - 1;
setCurrentPage(np);
onPageChange?.(np);
}
}
function handleNext() {
const lastPage = Math.max(1, Math.ceil(total / 10));
if (currentPage < lastPage) {
const np = currentPage + 1;
setCurrentPage(np);
onPageChange?.(np);
const 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");
}
}
const servicioSelectedResponsableCartaTermino = (row: ServicioSocialResponse | ServicioSocialConCasoEspecial) => {
if (!row) return;
if (idTipoUsuario === 2) {
console.log("Responsable seleccionó un servicio social para carta de termino:", row);
if (row.idServicio) {
localStorage.setItem("idServicio", String(row.idServicio));
}
router.push("/responsable/carta_termino");
}
}
@@ -91,6 +115,8 @@ export default function ServicioSocialTabla({
//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)
@@ -153,19 +179,19 @@ const servicioSelected = (row: ServicioSocialResponse | ServicioSocialConCasoEsp
) : (
<>
<div className="table-responsive">
<table className="table table-striped">
<table className="table table-striped table-fixed">
<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>}
{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>
@@ -182,18 +208,24 @@ const servicioSelected = (row: ServicioSocialResponse | ServicioSocialConCasoEsp
//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>
{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>{formatDate(item.fechaInicio)}</td>
<td className="text-center align-middle">{formatDate(item.fechaInicio)}</td>
)}
{columnaFechaFin && <td>{formatDate(item.fechaFin)}</td>}
<td>
{columnasResponsable && item.Status?.idStatus === 7 ? (
{columnaFechaFin && <td className="text-center align-middle">{formatDate(item.fechaFin)}</td>}
<td className="text-center align-middle">
{columnasResponsable && item.status?.idStatus === 7 ? (
<button
className="btn btn-sm btn-danger"
className="badge bg-danger border-0"
onClick={() =>
onRowAction?.(item, "carta_aceptacion")
}
@@ -203,36 +235,71 @@ const servicioSelected = (row: ServicioSocialResponse | ServicioSocialConCasoEsp
) : (
<span
className={`badge bg-${statusClass(
item.Status?.idStatus
item.status?.idStatus
)}`}
>
{item.Status?.status}
{item.status?.status}
</span>
)}
</td>
{columnaCartaTermino && (
<td>{item.cartaTermino ? (
<span className="badge bg-success">Completado</span>
) : (
<span className="badge bg-danger">No Completado</span>
)}</td>
<td 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>{item.cuestionarioCompletado ? (
<td className="text-center align-middle"
>{item.cuestionarioCompletado ? (
<span className="badge bg-success">Completado</span>
) : (
<span className="badge bg-danger">No Completado</span>
<span className="badge text-morado bg-light-morado">No Completado</span>
)}</td>
)}
{columnaCuestionario && (
<td>
{item.cuestionarioCompletado ? (
<span className="badge bg-success">Completado</span>
<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 bg-danger">No Completado</span>
<span className="badge text-secondary"> </span>
)}
</td>
)}
@@ -277,6 +344,7 @@ const servicioSelected = (row: ServicioSocialResponse | ServicioSocialConCasoEsp
);
}
function formatDate(value?: string | null): string {
if (!value) return "";
@@ -284,7 +352,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 "";
}
+27
View File
@@ -0,0 +1,27 @@
// 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";
+36 -14
View File
@@ -1,4 +1,4 @@
@import 'bootstrap/scss/functions';
@import 'bootstrap/dist/css/bootstrap.min.css';
$azul: #003d79;
$dorado: #bd8c01;
@@ -19,8 +19,7 @@ $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,
@@ -65,7 +64,7 @@ $custom-colors: (
}
$theme-colors: map-merge($theme-colors, $custom-colors);
$theme-colors: if(variable-exists("theme-colors"), map-merge($theme-colors, $custom-colors), $custom-colors);
.form-registro {
.form-floating {
@@ -93,9 +92,6 @@ $theme-colors: map-merge($theme-colors, $custom-colors);
}
}
@import 'bootstrap/scss/maps';
@import 'bootstrap/scss/mixins';
@import 'bootstrap/scss/bootstrap';
.aviso {
@@ -137,7 +133,7 @@ $theme-colors: map-merge($theme-colors, $custom-colors);
height: 2px;
background-color: $azul;
@include media-breakpoint-up(md) {
@media (min-width: 768px) {
left: 10rem;
}
}
@@ -208,12 +204,12 @@ $theme-colors: map-merge($theme-colors, $custom-colors);
}
.full-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;
/* Equivalent of Bootstrap utility classes without using @extend */
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.minNavbar {
@@ -268,3 +264,29 @@ $theme-colors: map-merge($theme-colors, $custom-colors);
.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
@@ -0,0 +1,8 @@
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
}
+16 -3
View File
@@ -8,9 +8,9 @@ export interface ServicioSocialResponse {
fechaFin?: string | null;
fechaInicio?: string | null;
fecha_registro?: string | null;
Usuario?: Usuario;
Carrera?: Carrera;
Status?: Status;
usuario?: Usuario;
carrera?: Carrera;
status?: Status;
cartaTermino?: boolean;
idCuestionarioPrograma?: number | null;
idCuestionarioPrograma2?: number | null;
@@ -57,4 +57,17 @@ export interface Responsables {
interface ServicioSocialConCasoEspecial extends ServicioSocialResponse {
idCasoEspecial?: number;
}
interface CasoEspecial {
idCasoEspecial: number;
correo?: string;
telefono?: string;
direccion?: string;
fechaInicio?: Date;
fechaFin?: Date;
fechaNacimiento?: Date;
motivo?: string;
dependencia?: string;
institucion?: string;
}
+19 -5
View File
@@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -11,7 +15,7 @@
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
@@ -19,9 +23,19 @@
}
],
"paths": {
"@/*": ["./src/*"]
"@/*": [
"./src/*"
]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}