386 lines
15 KiB
TypeScript
386 lines
15 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState } from "react";
|
|
import {
|
|
// TransferAlt,
|
|
Check,
|
|
X,
|
|
// Error as ErrorIcon,
|
|
// ErrorCircle,
|
|
CheckCircle,
|
|
} from "@boxicons/react";
|
|
|
|
// Interfaz para los datos del reemplazo
|
|
interface ReemplazoItem {
|
|
idCursoAbierto: string;
|
|
Curso: string;
|
|
FechaAsistencia: string;
|
|
HoraInicio: string;
|
|
HoraSalida: string;
|
|
rutProfesor: string;
|
|
Profesor: string;
|
|
Estado: string;
|
|
idAsistenciaProfesor: string;
|
|
Email?: string;
|
|
}
|
|
|
|
// Interfaz para la configuración de la confirmación
|
|
interface ModalConfirmacionState {
|
|
show: boolean;
|
|
accion: "autorizar" | "rechazar" | null;
|
|
item: ReemplazoItem | null;
|
|
}
|
|
|
|
export default function ReemplazosPendientesPage() {
|
|
// Estado con datos de ejemplo
|
|
const [reemplazos, setReemplazos] = useState<ReemplazoItem[]>([
|
|
{
|
|
idCursoAbierto: "CUR-101",
|
|
Curso: "Inglés Básico A1",
|
|
FechaAsistencia: "15-02-2026",
|
|
HoraInicio: "09:00",
|
|
HoraSalida: "10:30",
|
|
rutProfesor: "12.345.678-9",
|
|
Profesor: "Juan Pérez",
|
|
Estado: "Pendiente",
|
|
idAsistenciaProfesor: "SES-001",
|
|
Email: "jperez@correo.cl",
|
|
},
|
|
{
|
|
idCursoAbierto: "CUR-102",
|
|
Curso: "Inglés Intermedio B2",
|
|
FechaAsistencia: "16-02-2026",
|
|
HoraInicio: "11:00",
|
|
HoraSalida: "12:30",
|
|
rutProfesor: "15.987.654-3",
|
|
Profesor: "María González",
|
|
Estado: "Pendiente",
|
|
idAsistenciaProfesor: "SES-002",
|
|
Email: "mgonzalez@correo.cl",
|
|
},
|
|
]);
|
|
|
|
// Estados para diálogos y procesamientos
|
|
const [modalConfirm, setModalConfirm] = useState<ModalConfirmacionState>({
|
|
show: false,
|
|
accion: null,
|
|
item: null,
|
|
});
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [modalRespuesta, setModalRespuesta] = useState<{
|
|
show: boolean;
|
|
errorMsg: string;
|
|
okMsg: string;
|
|
}>({ show: false, errorMsg: "", okMsg: "" });
|
|
|
|
// Abrir modal customizado de confirmación
|
|
const abrirConfirmacion = (item: ReemplazoItem, accion: "autorizar" | "rechazar") => {
|
|
setModalConfirm({
|
|
show: true,
|
|
accion,
|
|
item,
|
|
});
|
|
};
|
|
|
|
// Procesar la autorización / rechazo
|
|
const handleConfirmar = () => {
|
|
if (!modalConfirm.item || !modalConfirm.accion) return;
|
|
|
|
const { idAsistenciaProfesor } = modalConfirm.item;
|
|
const accion = modalConfirm.accion;
|
|
|
|
// Cerrar modal de confirmación y mostrar loader
|
|
setModalConfirm({ show: false, accion: null, item: null });
|
|
setIsLoading(true);
|
|
|
|
setTimeout(() => {
|
|
setIsLoading(false);
|
|
// Eliminar el ítem procesado de la lista
|
|
setReemplazos((prev) => prev.filter((r) => r.idAsistenciaProfesor !== idAsistenciaProfesor));
|
|
setModalRespuesta({
|
|
show: true,
|
|
errorMsg: "",
|
|
okMsg: `El reemplazo se ha ${
|
|
accion === "autorizar" ? "autorizado" : "rechazado"
|
|
} correctamente.`,
|
|
});
|
|
}, 1000);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<div className="row">
|
|
{/* ENCABEZADO */}
|
|
<div className="col-md-12">
|
|
<div className="alert alert-light text-dark shadow" role="alert">
|
|
<div className="row align-items-center">
|
|
<div className="col-md-1 text-center">
|
|
{/* <TransferAlt className="text-warning fs-3 border rounded p-1" /> */}
|
|
</div>
|
|
<div className="col-md-9">
|
|
<span className="text-muted">
|
|
<strong>Autorización</strong>
|
|
</span>
|
|
<h5 className="mb-0">
|
|
<strong>Reemplazos Pendientes</strong>
|
|
</h5>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* TABLA O ALERTA */}
|
|
{reemplazos.length > 0 ? (
|
|
<div className="col-md-12" id="divTablaReemplazos">
|
|
<div className="alert alert-light text-dark shadow" role="alert">
|
|
<div className="table-responsive">
|
|
<table className="table table-sm mb-0" style={{ fontSize: "14px" }}>
|
|
<thead>
|
|
<tr>
|
|
<th className="text-start" style={{ width: "18%" }}>Curso</th>
|
|
<th className="text-center" style={{ width: "10%" }}>Sesión</th>
|
|
<th className="text-center" style={{ width: "8%" }}>Inicio</th>
|
|
<th className="text-center" style={{ width: "8%" }}>Salida</th>
|
|
<th className="text-center" style={{ width: "14%" }}>Run</th>
|
|
<th className="text-center" style={{ width: "14%" }}>Reemplazante</th>
|
|
<th className="text-center" style={{ width: "10%" }}>Estado Sesion</th>
|
|
<th className="text-center" style={{ width: "18%" }}>Acción</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{reemplazos.map((item) => (
|
|
<tr key={item.idAsistenciaProfesor}>
|
|
<td className="text-start" style={{ width: "18%" }}>
|
|
<strong>{item.Curso}</strong>
|
|
<span className="badge bg-secondary ms-1">
|
|
{item.idCursoAbierto}
|
|
</span>
|
|
</td>
|
|
<td className="text-center" style={{ width: "10%" }}>
|
|
{item.FechaAsistencia}
|
|
</td>
|
|
<td className="text-center" style={{ width: "8%" }}>
|
|
{item.HoraInicio}
|
|
</td>
|
|
<td className="text-center" style={{ width: "8%" }}>
|
|
{item.HoraSalida}
|
|
</td>
|
|
<td className="text-center" style={{ width: "14%" }}>
|
|
{item.rutProfesor}
|
|
</td>
|
|
<td className="text-center" style={{ width: "14%" }}>
|
|
{item.Profesor}
|
|
</td>
|
|
<td className="text-center" style={{ width: "10%" }}>
|
|
<span className="badge bg-warning text-dark">
|
|
{item.Estado}
|
|
</span>
|
|
</td>
|
|
<td className="text-center" style={{ width: "18%" }}>
|
|
<button
|
|
type="button"
|
|
className="btn btn-success btn-sm me-1"
|
|
onClick={() => abrirConfirmacion(item, "autorizar")}
|
|
>
|
|
<Check className="me-1" /> Autorizar
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className="btn btn-danger btn-sm"
|
|
onClick={() => abrirConfirmacion(item, "rechazar")}
|
|
>
|
|
<X className="me-1" /> Rechazar
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div className="col-md-12" id="divSinReemplazos">
|
|
<div className="alert alert-light text-dark shadow text-center py-4" role="alert">
|
|
<div className="row">
|
|
<div className="col-md-3"></div>
|
|
<div className="col-md-6 text-center">
|
|
<div className="alert alert-light text-dark mt-5" role="alert">
|
|
<strong>
|
|
{/* <ErrorIcon className="text-primary" style={{ fontSize: "90px" }} /> */}
|
|
</strong>
|
|
<br />
|
|
<br />
|
|
<h5>
|
|
<strong>No hay reemplazos pendientes de autorización</strong>
|
|
</h5>
|
|
<hr />
|
|
<strong className="text-muted">
|
|
Si cree que esto es un error, contáctenos.{" "}
|
|
<a
|
|
target="_blank"
|
|
href="https://norteamericano.freshdesk.com/support/tickets/new"
|
|
rel="noreferrer"
|
|
>
|
|
Generar Ticket
|
|
</a>
|
|
</strong>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* MODAL PERSONALIZADO DE CONFIRMACIÓN (REEMPLAZO DE SWEETALERT2) */}
|
|
{modalConfirm.show && modalConfirm.item && (
|
|
<div className="modal fade show d-block bg-dark bg-opacity-50" tabIndex={-1} role="dialog">
|
|
<div className="modal-dialog modal-dialog-centered" role="document">
|
|
<div className="modal-content rounded-4 p-3">
|
|
<div className="modal-body text-center">
|
|
{modalConfirm.accion === "autorizar" ? (
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#198754" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
|
|
<polyline points="22 4 12 14.01 9 11.01" />
|
|
</svg>
|
|
) : (
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#dc3545" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<circle cx="12" cy="12" r="10" />
|
|
<line x1="15" y1="9" x2="9" y2="15" />
|
|
<line x1="9" y1="9" x2="15" y2="15" />
|
|
</svg>
|
|
)}
|
|
|
|
<h4 className="mt-2 mb-1 fw-bold" style={{ color: "#1f2937" }}>
|
|
{modalConfirm.accion === "autorizar" ? "Autorizar Reemplazo" : "Rechazar Reemplazo"}
|
|
</h4>
|
|
<p className="text-muted mb-3" style={{ fontSize: "14px" }}>
|
|
{modalConfirm.accion === "autorizar"
|
|
? "Se asignará al profesor reemplazante"
|
|
: "Se rechazará al profesor reemplazante"}
|
|
</p>
|
|
|
|
<div
|
|
className="p-3 text-start rounded-3"
|
|
style={{
|
|
backgroundColor: modalConfirm.accion === "autorizar" ? "#f0fdf4" : "#fef2f2",
|
|
border: `1px solid ${modalConfirm.accion === "autorizar" ? "#a3cfbb" : "#f1aeb5"}`,
|
|
fontSize: "13px",
|
|
color: "#374151",
|
|
}}
|
|
>
|
|
<div className="d-flex justify-content-between mb-2">
|
|
<span className="fw-bold">Curso</span>
|
|
<span>
|
|
{modalConfirm.item.Curso}{" "}
|
|
<span className="badge bg-secondary ms-1">
|
|
{modalConfirm.item.idCursoAbierto}
|
|
</span>
|
|
</span>
|
|
</div>
|
|
<div className="d-flex justify-content-between mb-2">
|
|
<span className="fw-bold">Sesión</span>
|
|
<span>
|
|
{modalConfirm.item.FechaAsistencia} | {modalConfirm.item.HoraInicio} - {modalConfirm.item.HoraSalida}
|
|
</span>
|
|
</div>
|
|
<div className="d-flex justify-content-between mb-2">
|
|
<span className="fw-bold">Profesor</span>
|
|
<span>{modalConfirm.item.rutProfesor}</span>
|
|
</div>
|
|
<div className="d-flex justify-content-between">
|
|
<span className="fw-bold">Reemplazante</span>
|
|
<span
|
|
className="fw-bold"
|
|
style={{
|
|
color: modalConfirm.accion === "autorizar" ? "#198754" : "#dc3545",
|
|
}}
|
|
>
|
|
{modalConfirm.item.Profesor}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="d-flex justify-content-end gap-2 mt-4">
|
|
<button
|
|
type="button"
|
|
className="btn btn-secondary"
|
|
onClick={() => setModalConfirm({ show: false, accion: null, item: null })}
|
|
>
|
|
Cancelar
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className={`btn ${modalConfirm.accion === "autorizar" ? "btn-success" : "btn-danger"}`}
|
|
onClick={handleConfirmar}
|
|
>
|
|
{modalConfirm.accion === "autorizar" ? "Sí, autorizar" : "Sí, rechazar"}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* MODAL RESPUESTA */}
|
|
{modalRespuesta.show && (
|
|
<div className="modal fade show d-block bg-dark bg-opacity-50" tabIndex={-1} role="dialog">
|
|
<div className="modal-dialog modal-dialog-centered" role="document">
|
|
<div className="modal-content">
|
|
<div className="modal-header text-white text-center" style={{ backgroundColor: "#373193" }}>
|
|
<h5 className="modal-title">Reemplazos</h5>
|
|
</div>
|
|
<div className="modal-body">
|
|
{modalRespuesta.errorMsg && (
|
|
<div>
|
|
<strong style={{ color: "#e22523" }}>
|
|
{/* <ErrorCircle className="me-1" /> */}
|
|
<span>{modalRespuesta.errorMsg}</span>
|
|
</strong>
|
|
</div>
|
|
)}
|
|
{modalRespuesta.okMsg && (
|
|
<div>
|
|
<strong style={{ color: "#00285d" }}>
|
|
<CheckCircle className="me-1" />
|
|
<span>{modalRespuesta.okMsg}</span>
|
|
</strong>
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div className="modal-footer">
|
|
<button
|
|
type="button"
|
|
className="btn btn-secondary"
|
|
onClick={() => setModalRespuesta({ show: false, errorMsg: "", okMsg: "" })}
|
|
>
|
|
Cerrar
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* MODAL CARGANDO */}
|
|
{isLoading && (
|
|
<div className="modal fade show d-block bg-dark bg-opacity-50" tabIndex={-1} role="dialog">
|
|
<div className="modal-dialog modal-dialog-centered">
|
|
<div className="modal-content">
|
|
<div className="modal-body">
|
|
<div className="col-md-12 text-center py-3">
|
|
<div className="spinner-border text-info" role="status">
|
|
<span className="visually-hidden">Procesando...</span>
|
|
</div>
|
|
<p className="mt-2 mb-0">Procesando la información...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
} |