Se agregaron nuevos componentes
This commit is contained in:
@@ -5,9 +5,7 @@ import React from "react";
|
||||
export default function Layout({ children }: { children: React.ReactNode}) {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
{children}
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,8 +1,58 @@
|
||||
"use client"
|
||||
import BarraProgreso from "@/components/alumno/barra-progreso";
|
||||
import CompletarDatosPersonales from "@/components/alumno/completar-datos-personales";
|
||||
import InformacinoServicio from "@/components/alumno/informacion-servicio";
|
||||
import MensajeAlumno from "@/components/alumno/mensajes-alumno";
|
||||
import PreTermino from "@/components/alumno/pre-termino";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div>
|
||||
Hola Mundo
|
||||
<BarraProgreso idStatus={5}/>
|
||||
<CompletarDatosPersonales
|
||||
idServicio={123}
|
||||
alumno={{ token: { headers: { Authorization: "Bearer ..." } }, tokenArchivo: { headers: { Authorization: "Bearer ..." } } }}
|
||||
imprimirMensaje={(msg) => console.log(msg)}
|
||||
imprimirWarning={(msg, callback) => { if (confirm(msg)) callback(); }}
|
||||
imprimirError={(err) => console.error(err)}
|
||||
obtenerServicio={() => console.log("obtener servicio")}
|
||||
updateIsLoading={(loading) => console.log("loading", loading)}
|
||||
/>
|
||||
|
||||
<InformacinoServicio
|
||||
servicio={{
|
||||
Programa: {
|
||||
institucion: "UNAM",
|
||||
dependencia: "Académicos",
|
||||
programa: "Servicio Social",
|
||||
clavePrograma: "SS123",
|
||||
},
|
||||
Usuario: { usuario: "123456", nombre: "Juan Pérez" },
|
||||
Carrera: { carrera: "Matemáticas" },
|
||||
creditos: "80",
|
||||
correo: "juan@correo.com",
|
||||
fechaInicio: new Date().toISOString(),
|
||||
fechaFin: new Date().toISOString(),
|
||||
createdAt: new Date().toISOString(),
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
<MensajeAlumno Status={{ idStatus: 2 }} />
|
||||
|
||||
<PreTermino
|
||||
alumno={{ tokenArchivo: "token123" }}
|
||||
servicio={{ idServicio: 123, informeGlobal: undefined }}
|
||||
imprimirMensaje={(msg) => console.log(msg)}
|
||||
imprimirWarning={(msg, callback) => {
|
||||
if (confirm(msg)) callback();
|
||||
}}
|
||||
imprimirError={(err) => console.error(err)}
|
||||
obtenerServicio={() => console.log("obtener servicio")}
|
||||
updateIsLoading={(loading) => console.log("loading", loading)}
|
||||
/>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import axios, { AxiosResponse } from "axios";
|
||||
|
||||
interface Status {
|
||||
idStatus: number;
|
||||
}
|
||||
|
||||
interface Alumno {
|
||||
idServicio: number;
|
||||
Status: Status;
|
||||
cartaAceptacion?: string;
|
||||
cartaTermino?: string;
|
||||
informeGlobal?: string;
|
||||
}
|
||||
|
||||
interface Admin {
|
||||
token: { headers: Record<string, string> };
|
||||
}
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
admin: Admin;
|
||||
alumno: Alumno;
|
||||
imprimirError: (err: unknown) => void;
|
||||
imprimirMensaje: (msg: string) => void;
|
||||
imprimirWarning: (msg: string, onConfirm: () => void) => void;
|
||||
updateIsLoading: (value: boolean) => void;
|
||||
}
|
||||
|
||||
interface ApiResponse {
|
||||
data: { message: string };
|
||||
}
|
||||
|
||||
interface RechazarData {
|
||||
idServicio: number;
|
||||
mensaje: string;
|
||||
}
|
||||
|
||||
export default function VerDocumento({
|
||||
title,
|
||||
admin,
|
||||
alumno,
|
||||
imprimirError,
|
||||
imprimirMensaje,
|
||||
imprimirWarning,
|
||||
updateIsLoading,
|
||||
}: Props) {
|
||||
const [rechazar, setRechazar] = useState(false);
|
||||
const [mensajeRechazo, setMensajeRechazo] = useState("");
|
||||
const router = useRouter();
|
||||
|
||||
const updateRechazar = () => {
|
||||
if (rechazar) setMensajeRechazo("");
|
||||
setRechazar(!rechazar);
|
||||
};
|
||||
|
||||
const archivo = (): string | undefined => {
|
||||
switch (title) {
|
||||
case "carta de aceptación":
|
||||
return alumno.cartaAceptacion;
|
||||
case "carta de termino":
|
||||
return alumno.cartaTermino;
|
||||
case "informe global":
|
||||
return alumno.informeGlobal;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const rechazarArchivo = () => {
|
||||
switch (title) {
|
||||
case "carta de aceptación":
|
||||
rechazarFunc(rechazarCartaAceptacion);
|
||||
break;
|
||||
case "carta de termino":
|
||||
rechazarFunc(rechazarCartaTermino);
|
||||
break;
|
||||
case "informe global":
|
||||
rechazarFunc(rechazarInformeGlobal);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const rechazarFunc = async (
|
||||
funcRechazar: (data: RechazarData) => Promise<AxiosResponse<ApiResponse>>
|
||||
) => {
|
||||
const data: RechazarData = {
|
||||
idServicio: alumno.idServicio,
|
||||
mensaje: mensajeRechazo,
|
||||
};
|
||||
|
||||
try {
|
||||
updateIsLoading(true);
|
||||
const res = await funcRechazar(data);
|
||||
localStorage.removeItem("idServicio");
|
||||
imprimirMensaje(res.data.data.message);
|
||||
router.push("/administrador");
|
||||
} catch (err: unknown) {
|
||||
if (axios.isAxiosError(err)) {
|
||||
imprimirError(err.response?.data || err.message);
|
||||
} else {
|
||||
imprimirError(err);
|
||||
}
|
||||
} finally {
|
||||
updateIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const rechazarCartaAceptacion = (data: RechazarData) =>
|
||||
axios.put(`${process.env.NEXT_PUBLIC_API}/servicio/rechazar_aceptacion`, data, admin.token);
|
||||
|
||||
const rechazarCartaTermino = (data: RechazarData) =>
|
||||
axios.put(`${process.env.NEXT_PUBLIC_API}/servicio/rechazar_termino`, data, admin.token);
|
||||
|
||||
const rechazarInformeGlobal = (data: RechazarData) =>
|
||||
axios.put(`${process.env.NEXT_PUBLIC_API}/servicio/rechazar_informe`, data, admin.token);
|
||||
|
||||
return (
|
||||
<div className="mb-4">
|
||||
<div className="flex items-center">
|
||||
<h6 className="pr-4 text-lg">Ver {title}</h6>
|
||||
|
||||
<div className="pr-4">
|
||||
<a
|
||||
className="btn btn-link btn-light"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={`https://drive.google.com/file/d/${archivo()}/view?usp=sharing`}
|
||||
>
|
||||
Ver
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{((alumno.Status.idStatus === 1 && title === "carta de aceptación") ||
|
||||
(alumno.Status.idStatus === 5 &&
|
||||
(title === "carta de termino" || title === "informe global"))) && (
|
||||
<button
|
||||
className="btn btn-danger"
|
||||
onClick={updateRechazar}
|
||||
>
|
||||
Rechazar
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{rechazar && (
|
||||
<div className="mt-2">
|
||||
<label>Razón del rechazo:</label>
|
||||
<textarea
|
||||
maxLength={500}
|
||||
value={mensajeRechazo}
|
||||
onChange={(e) => setMensajeRechazo(e.target.value)}
|
||||
className="form-control"
|
||||
/>
|
||||
|
||||
<button
|
||||
className="btn btn-link"
|
||||
disabled={!mensajeRechazo}
|
||||
onClick={() =>
|
||||
imprimirWarning(
|
||||
"¿Seguro(a) que quiere rechazar este documento?",
|
||||
rechazarArchivo
|
||||
)
|
||||
}
|
||||
>
|
||||
Rechazar {title}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
|
||||
interface Props {
|
||||
idStatus: number;
|
||||
}
|
||||
|
||||
interface Step {
|
||||
label: string;
|
||||
icon: string; // Puedes usar iconos de heroicons o font-awesome
|
||||
visible: boolean;
|
||||
type?: "default" | "danger";
|
||||
}
|
||||
|
||||
export default function BarraProgreso({ idStatus }: Props) {
|
||||
const steps: Step[] = [
|
||||
{ label: "Pre Registro", icon: "account-plus", visible: idStatus < 6 },
|
||||
{ label: "Pre Registro Validado", icon: "account-clock", visible: idStatus < 6 },
|
||||
{ label: "Registro", icon: "account", visible: idStatus < 6 },
|
||||
{ label: "Pre Termino", icon: "account-details", visible: idStatus < 6 },
|
||||
{ label: "Termino", icon: "account-clock", visible: idStatus < 6 },
|
||||
{ label: "Liberacion", icon: "account-check", visible: idStatus < 6 },
|
||||
{ label: "Carta Aceptación Rechazada", icon: "file", visible: idStatus === 6, type: "danger" },
|
||||
{ label: "Carta Termino Rechazada", icon: "file", visible: idStatus === 7, type: "danger" },
|
||||
{ label: "Informe Global Rechazado", icon: "file", visible: idStatus === 8, type: "danger" },
|
||||
{ label: "Cancelado", icon: "account-cancel", visible: idStatus === 9, type: "danger" },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap gap-2 mt-6 mb-5">
|
||||
{steps
|
||||
.filter((step) => step.visible)
|
||||
.map((step, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`flex items-center px-4 py-2 rounded ${
|
||||
step.type === "danger"
|
||||
? "bg-red-500 text-white"
|
||||
: "bg-blue-500 text-white"
|
||||
}`}
|
||||
>
|
||||
<span className="mr-2">
|
||||
{/* Aquí puedes usar un icono según step.icon */}
|
||||
<i className={`fa fa-${step.icon}`}></i>
|
||||
</span>
|
||||
<span>{step.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import axios, { AxiosResponse } from "axios";
|
||||
import moment from "moment";
|
||||
|
||||
interface Alumno {
|
||||
token: { headers: Record<string, string> };
|
||||
tokenArchivo?: { headers: Record<string, string> }; // opcional si algunas llamadas lo usan
|
||||
}
|
||||
|
||||
|
||||
interface Props {
|
||||
idServicio: number;
|
||||
alumno: Alumno;
|
||||
imprimirMensaje: (msg: string) => void;
|
||||
imprimirWarning: (msg: string, onConfirm: () => void) => void;
|
||||
imprimirError: (err: unknown) => void;
|
||||
obtenerServicio: () => void;
|
||||
updateIsLoading: (value: boolean) => void;
|
||||
}
|
||||
|
||||
interface PreRegistroData {
|
||||
idServicio: number;
|
||||
direccion: string;
|
||||
telefono: string;
|
||||
fechaNacimiento: string;
|
||||
}
|
||||
|
||||
interface ApiResponse {
|
||||
data: { message: string };
|
||||
}
|
||||
|
||||
export default function CompletarDatosPersonales({
|
||||
idServicio,
|
||||
alumno,
|
||||
imprimirMensaje,
|
||||
imprimirWarning,
|
||||
imprimirError,
|
||||
obtenerServicio,
|
||||
updateIsLoading,
|
||||
}: Props) {
|
||||
const [direccion, setDireccion] = useState("");
|
||||
const [telefono, setTelefono] = useState("");
|
||||
const [nacimiento, setNacimiento] = useState<Date>(new Date());
|
||||
const maxDate = new Date();
|
||||
|
||||
const terminarPreRegistro = async () => {
|
||||
const data: PreRegistroData = {
|
||||
idServicio,
|
||||
direccion,
|
||||
telefono,
|
||||
fechaNacimiento: moment(nacimiento).format("YYYY-MM-DD"),
|
||||
};
|
||||
|
||||
try {
|
||||
updateIsLoading(true);
|
||||
const res: AxiosResponse<ApiResponse> = await axios.put(
|
||||
`${process.env.NEXT_PUBLIC_API}/servicio/registro_validado`,
|
||||
data,
|
||||
alumno.token
|
||||
);
|
||||
imprimirMensaje(res.data.data.message);
|
||||
obtenerServicio();
|
||||
} catch (err: unknown) {
|
||||
if (axios.isAxiosError(err)) {
|
||||
imprimirError(err.response?.data || err.message);
|
||||
} else {
|
||||
imprimirError(err);
|
||||
}
|
||||
} finally {
|
||||
updateIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-xl font-semibold">Formulario Pre-registro</h3>
|
||||
|
||||
{/* Fecha de nacimiento */}
|
||||
<div>
|
||||
<label className="block mb-1 font-medium">Fecha de nacimiento</label>
|
||||
<DatePicker
|
||||
selected={nacimiento}
|
||||
onChange={(date: Date | null) => {
|
||||
if (date) setNacimiento(date);
|
||||
}}
|
||||
maxDate={maxDate}
|
||||
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||
placeholderText="Fecha de nacimiento"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Teléfono */}
|
||||
<div>
|
||||
<label className="block mb-1 font-medium">Teléfono</label>
|
||||
<input
|
||||
type="tel"
|
||||
placeholder="Teléfono"
|
||||
maxLength={10}
|
||||
value={telefono}
|
||||
onChange={(e) => setTelefono(e.target.value)}
|
||||
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Dirección */}
|
||||
<div>
|
||||
<label className="block mb-1 font-medium">Dirección</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Dirección"
|
||||
maxLength={200}
|
||||
value={direccion}
|
||||
onChange={(e) => setDireccion(e.target.value)}
|
||||
className="border border-gray-300 rounded px-3 py-2 w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Botón enviar */}
|
||||
<div className="text-center mt-4">
|
||||
<button
|
||||
disabled={!telefono || !direccion}
|
||||
onClick={() =>
|
||||
imprimirWarning(
|
||||
"¿Estas seguro(a) que tus datos son correctos?",
|
||||
terminarPreRegistro
|
||||
)
|
||||
}
|
||||
className={`px-4 py-2 rounded text-white ${
|
||||
!telefono || !direccion
|
||||
? "bg-gray-400 cursor-not-allowed"
|
||||
: "bg-green-600 hover:bg-green-700"
|
||||
}`}
|
||||
>
|
||||
Enviar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
"use client";
|
||||
|
||||
import moment from "moment";
|
||||
|
||||
interface Programa {
|
||||
institucion: string;
|
||||
dependencia: string;
|
||||
programa: string;
|
||||
clavePrograma: string;
|
||||
}
|
||||
|
||||
interface Usuario {
|
||||
usuario: string;
|
||||
nombre: string;
|
||||
}
|
||||
|
||||
interface Carrera {
|
||||
carrera: string;
|
||||
}
|
||||
|
||||
export interface Servicio {
|
||||
Programa: Programa;
|
||||
Usuario: Usuario;
|
||||
Carrera: Carrera;
|
||||
creditos?: string;
|
||||
telefono?: string;
|
||||
direccion?: string;
|
||||
correo: string;
|
||||
programaInterno?: string;
|
||||
profesor?: string;
|
||||
createdAt?: string;
|
||||
fechaInicio: string;
|
||||
fechaFin: string;
|
||||
fechaLiberacion?: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
servicio: Servicio;
|
||||
}
|
||||
|
||||
export default function InformacinoServicio({ servicio }: Props) {
|
||||
// Función para formatear fechas
|
||||
const fecha = (date?: string) => {
|
||||
if (!date) return "";
|
||||
const f = moment(date);
|
||||
const day = f.date() < 10 ? "0" + f.date() : f.date();
|
||||
const month = f.month() < 9 ? "0" + (f.month() + 1) : f.month() + 1;
|
||||
const year = f.year();
|
||||
return `${day}/${month}/${year}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Datos del programa */}
|
||||
<div className="mb-5">
|
||||
<h4 className="is-size-4 pb-2">Datos del programa</h4>
|
||||
|
||||
<div className="mb-2">
|
||||
<label>Institución:</label>
|
||||
<p className="input">{servicio.Programa.institucion}</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-2">
|
||||
<label>Dependencia:</label>
|
||||
<p className="input">{servicio.Programa.dependencia}</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-2">
|
||||
<label>Programa:</label>
|
||||
<p className="input">{servicio.Programa.programa}</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-2">
|
||||
<label>Clave de programa:</label>
|
||||
<p className="input">{servicio.Programa.clavePrograma}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Datos personales */}
|
||||
<div className="mb-5">
|
||||
<h4 className="is-size-4 pb-2">Datos personales</h4>
|
||||
|
||||
<div className="mb-2">
|
||||
<label>Número de cuenta:</label>
|
||||
<p className="input">{servicio.Usuario.usuario}</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-2">
|
||||
<label>Nombre:</label>
|
||||
<p className="input">{servicio.Usuario.nombre}</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-2">
|
||||
<label>Carrera:</label>
|
||||
<p className="input">{servicio.Carrera.carrera}</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-2">
|
||||
<label>Créditos:</label>
|
||||
<p className="input">
|
||||
{servicio.creditos ? parseInt(servicio.creditos) : ""}
|
||||
{servicio.creditos && "%"}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{servicio.telefono && (
|
||||
<div className="mb-2">
|
||||
<label>Teléfono:</label>
|
||||
<p className="input">{servicio.telefono}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{servicio.direccion && (
|
||||
<div className="mb-2">
|
||||
<label>Dirección:</label>
|
||||
<p className="input">{servicio.direccion}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mb-2">
|
||||
<label>Email:</label>
|
||||
<p className="input">{servicio.correo}</p>
|
||||
</div>
|
||||
|
||||
{servicio.programaInterno && (
|
||||
<div className="mb-2">
|
||||
<label>Programa Interno:</label>
|
||||
<p className="input">{servicio.programaInterno}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{servicio.profesor && (
|
||||
<div className="mb-2">
|
||||
<label>Profesor:</label>
|
||||
<p className="input">{servicio.profesor}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{servicio.createdAt && (
|
||||
<div className="mb-2">
|
||||
<label>Fecha de registro:</label>
|
||||
<p className="input">{fecha(servicio.createdAt)}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mb-2">
|
||||
<label>Fecha de inicio:</label>
|
||||
<p className="input">{fecha(servicio.fechaInicio)}</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-2">
|
||||
<label>Fecha de término:</label>
|
||||
<p className="input">{fecha(servicio.fechaFin)}</p>
|
||||
</div>
|
||||
|
||||
{servicio.fechaLiberacion && (
|
||||
<div className="mb-2">
|
||||
<label>Fecha de liberación:</label>
|
||||
<p className="input">{fecha(servicio.fechaLiberacion)}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
"use client";
|
||||
|
||||
interface Status {
|
||||
idStatus: number;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
Status: Status;
|
||||
}
|
||||
|
||||
export default function MensajeAlumno({ Status }: Props) {
|
||||
return (
|
||||
<div className="mb-6">
|
||||
<h4 className="block is-size-4">Estimado alumno(a):</h4>
|
||||
|
||||
<p className="has-text-justified is-size-6">
|
||||
{Status.idStatus === 2 && (
|
||||
<>
|
||||
Estimado alumno(a): Para poder terminar con el registro de tu trámite
|
||||
de servicio social te pedimos que verifiques que tus datos sean
|
||||
correctos, de no ser así puedes acudir a las ventanillas de servicio
|
||||
social de la Secretaría de Asuntos Académicos Estudiantiles, en la
|
||||
planta baja del Edificio A-8 de lunes a viernes de 09:00 a 15:00 hrs.
|
||||
y de 17:00 a 19:00 hrs. o bien, comunicarte al 5623 1686 o al correo
|
||||
tramites.ss@acatlan.unam.mx y también contesta el formulario que se
|
||||
encuentra hasta el final de esta página.
|
||||
</>
|
||||
)}
|
||||
|
||||
{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
|
||||
social por lo que el siguiente trámite lo realizarás hasta que
|
||||
concluyas 480 horas en un periodo de mínimo 6 meses y obtengas tu
|
||||
carta de término por parte de la institución, quien se encargará de
|
||||
subir dicho archivo a este sistema, además de responder un
|
||||
cuestionario sobre tu desempeño. A su vez, deberás responder el
|
||||
siguiente cuestionario de evaluación del programa de servicio social
|
||||
en donde participaste y enviar el informe global de actividades
|
||||
elaborado por ti, con firma y sello de visto bueno de tu jefe
|
||||
inmediato. En cuanto el Área de Registro y Control de Servicio Social
|
||||
valide tu solicitud, podrás revisar las indicaciones del siguiente
|
||||
paso en el panel llamado “Liberación”. Cualquier duda puedes acudir a
|
||||
las ventanillas de servicio social de la Secretaría de Asuntos
|
||||
Académicos Estudiantiles, en la planta baja del Edificio A-8 de lunes
|
||||
a viernes de 09:00 a 15:00 hrs. y de 17:00 a 19:00 hrs. o bien,
|
||||
comunicarte al 5623 1686 o al correo tramites.ss@acatlan.unam.mx
|
||||
</>
|
||||
)}
|
||||
|
||||
{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
|
||||
cuestionario que se encuentra hasta el final de esta página.
|
||||
</>
|
||||
)}
|
||||
|
||||
{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 && (
|
||||
<>
|
||||
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
|
||||
sólo queda esperar a que en máximo 15 días hábiles se te envíe por
|
||||
correo tu carta de liberación. Cualquier duda puedes acudir a las
|
||||
ventanillas de servicio social la Secretaría de Asuntos Académicos
|
||||
Estudiantiles, en la planta baja del Edificio A-8 de lunes a viernes
|
||||
de 09:00 a 15:00 hrs. y de 17:00 a 19:00 hrs. o bien, comunicarte al
|
||||
5623 1686 o al correo tramites.ss@acatlan.unam.mx
|
||||
</>
|
||||
)}
|
||||
|
||||
{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
|
||||
la vuelvan a subir al sistema para volver a verificarla.
|
||||
</>
|
||||
)}
|
||||
|
||||
{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
|
||||
los requisitos.
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import axios, { AxiosResponse } from "axios";
|
||||
|
||||
interface Alumno {
|
||||
tokenArchivo: string;
|
||||
}
|
||||
|
||||
interface Servicio {
|
||||
idServicio: number;
|
||||
idCuestionarioAlumno?: number;
|
||||
idCuestionarioAlumno2?: number;
|
||||
informeGlobal?: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
alumno: Alumno;
|
||||
servicio: Servicio;
|
||||
imprimirMensaje: (msg: string) => void;
|
||||
imprimirWarning: (msg: string, callback: () => void) => void;
|
||||
imprimirError: (err: any) => void;
|
||||
obtenerServicio: () => void;
|
||||
updateIsLoading: (loading: boolean) => void;
|
||||
}
|
||||
|
||||
export default function PreTermino({
|
||||
alumno,
|
||||
servicio,
|
||||
imprimirMensaje,
|
||||
imprimirWarning,
|
||||
imprimirError,
|
||||
obtenerServicio,
|
||||
updateIsLoading,
|
||||
}: Props) {
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (file && file.size >= 20000000) {
|
||||
imprimirError({ message: "El tamaño del archivo excede los 20MB" });
|
||||
setFile(null);
|
||||
}
|
||||
}, [file, imprimirError]);
|
||||
|
||||
const enviarInformeGlobal = async () => {
|
||||
if (!file) return;
|
||||
|
||||
const formData = new FormData();
|
||||
const data = { idServicio: servicio.idServicio };
|
||||
|
||||
formData.append("data", JSON.stringify(data));
|
||||
formData.append("informeGlobal", file);
|
||||
|
||||
try {
|
||||
updateIsLoading(true);
|
||||
const res: AxiosResponse<{ message: string }> = await axios.put(
|
||||
`${process.env.api}/servicio/informe_global`,
|
||||
formData,
|
||||
{ headers: { "Content-Type": "multipart/form-data", Authorization: alumno.tokenArchivo } }
|
||||
);
|
||||
imprimirMensaje(res.data.message);
|
||||
obtenerServicio();
|
||||
setFile(null);
|
||||
} catch (err: unknown) {
|
||||
if (axios.isAxiosError(err)) {
|
||||
imprimirError(err.response?.data || err.message);
|
||||
} else {
|
||||
imprimirError(err);
|
||||
}
|
||||
} finally {
|
||||
updateIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3 className="label">
|
||||
Cuestionario de evaluación del programa de servicio social.
|
||||
</h3>
|
||||
|
||||
{!servicio.idCuestionarioAlumno && !servicio.idCuestionarioAlumno2 ? (
|
||||
<div className="mb-6">
|
||||
<a
|
||||
href="/alumno/cuestionario"
|
||||
className="button is-info is-light"
|
||||
>
|
||||
<span className="icon">
|
||||
<i className="fas fa-book-open"></i>
|
||||
</span>
|
||||
<span>Cuestionario</span>
|
||||
</a>
|
||||
</div>
|
||||
) : (
|
||||
<div className="mb-6">
|
||||
<p className="block is-size-6">
|
||||
Cuestionario contestado{" "}
|
||||
<span className="icon has-text-success">
|
||||
<i className="fas fa-check-bold"></i>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<h3 className="label">
|
||||
Informe global de actividades{" "}
|
||||
{!servicio.informeGlobal && (
|
||||
<span>(en formato .PDF. No se aceptan fotos)</span>
|
||||
)}
|
||||
.
|
||||
</h3>
|
||||
|
||||
{!servicio.informeGlobal ? (
|
||||
<div>
|
||||
<div className="field">
|
||||
<input
|
||||
type="file"
|
||||
accept="application/pdf"
|
||||
onChange={(e) => {
|
||||
if (e.target.files && e.target.files[0]) {
|
||||
setFile(e.target.files[0]);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<p className="is-size-6">Tamaño máximo 20MB</p>
|
||||
</div>
|
||||
|
||||
<div className="field has-text-centered my-5">
|
||||
<button
|
||||
className="button is-success"
|
||||
disabled={!file}
|
||||
onClick={() =>
|
||||
imprimirWarning(
|
||||
"¿Estas seguro(a) de querer subir este informe global?",
|
||||
enviarInformeGlobal
|
||||
)
|
||||
}
|
||||
>
|
||||
Enviar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<p className="block is-size-6">
|
||||
Informe Global enviado.{" "}
|
||||
<span className="icon has-text-success">
|
||||
<i className="fas fa-check-bold"></i>
|
||||
</span>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user