349 lines
13 KiB
TypeScript
Executable File
349 lines
13 KiB
TypeScript
Executable File
"use client";
|
|
|
|
import React, { useState } from "react";
|
|
import Link from "next/link";
|
|
import { User } from "@boxicons/react";
|
|
|
|
// Interface para mapear la estructura de la lista de contratos anulados
|
|
interface ContratoAnuladoItem {
|
|
idEmpresas: string;
|
|
empresa: string;
|
|
Contrato: string;
|
|
Fecha: string;
|
|
DescripcionActividad: string;
|
|
usuarioResponsable: string;
|
|
}
|
|
|
|
export default function ContratosHistorialPage() {
|
|
// --- ESTADOS DE CONTROLES Y SECCIONES ---
|
|
const [mes, setMes] = useState("");
|
|
const [agno, setAgno] = useState("");
|
|
const [showSearch, setShowSearch] = useState(false);
|
|
const [mensajeLabel, setMensajeLabel] = useState("");
|
|
|
|
// --- ESTADOS DE MODALES Y CARGA ---
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [modalAlerta, setModalAlerta] = useState({ show: false, message: "" });
|
|
|
|
// --- DATOS SIMULADOS (DataList / Grid) ---
|
|
const [contratosAnulados, setContratosAnulados] = useState<ContratoAnuladoItem[]>([
|
|
{
|
|
idEmpresas: "76.123.456-7",
|
|
empresa: "Empresa Ejemplo S.A.",
|
|
Contrato: "1002",
|
|
Fecha: "2026-02-10",
|
|
DescripcionActividad: "Anulación solicitada por el cliente por cambio de presupuesto",
|
|
usuarioResponsable: "AdminUser",
|
|
},
|
|
]);
|
|
|
|
// --- MANEJADORES DE EVENTOS ---
|
|
const handleBuscar = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setIsLoading(true);
|
|
|
|
// Simulación de respuesta de backend
|
|
setTimeout(() => {
|
|
setIsLoading(false);
|
|
if (!mes || !agno) {
|
|
setModalAlerta({
|
|
show: true,
|
|
message: "Por favor seleccione un mes e ingrese un año válido.",
|
|
});
|
|
return;
|
|
}
|
|
setShowSearch(true);
|
|
setMensajeLabel("");
|
|
}, 800);
|
|
};
|
|
|
|
const handleExportarExcel = (e: React.MouseEvent) => {
|
|
e.preventDefault();
|
|
setIsLoading(true);
|
|
|
|
setTimeout(() => {
|
|
setIsLoading(false);
|
|
setModalAlerta({
|
|
show: true,
|
|
message: "Informe mensual exportado correctamente a Excel.",
|
|
});
|
|
}, 1000);
|
|
};
|
|
|
|
return (
|
|
<div className="container-fluid py-3">
|
|
<div className="row">
|
|
<div className="col-md-12">
|
|
<div className="row">
|
|
{/* CABECERA IZQUIERDA */}
|
|
<div className="col-md-5">
|
|
<div className="alert alert-light text-dark shadow-sm" role="alert">
|
|
<div className="row align-items-center">
|
|
<div className="col-8 d-flex align-items-center">
|
|
<strong>
|
|
<User style={{ fontSize: "24px" }} />
|
|
</strong>
|
|
<strong>
|
|
<span className="ms-2">Contratos anulados por mes</span>
|
|
</strong>
|
|
</div>
|
|
<div className="col text-end">
|
|
<Link
|
|
href="/Clientes/Index"
|
|
className="btn btn-warning text-dark btn-sm fw-bold"
|
|
>
|
|
Volver
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* CABECERA DERECHA / FILTROS DE BÚSQUEDA */}
|
|
<div className="col-md-7">
|
|
<div className="alert alert-light text-dark shadow-sm" role="alert">
|
|
<form onSubmit={handleBuscar} className="row align-items-center">
|
|
<div className="col text-end">
|
|
<span className="fw-bold">Mes</span>
|
|
</div>
|
|
<div className="col">
|
|
<select
|
|
className="form-select form-select-sm"
|
|
value={mes}
|
|
onChange={(e) => setMes(e.target.value)}
|
|
>
|
|
<option value="">-- Seleccione --</option>
|
|
<option value="1">Enero</option>
|
|
<option value="2">Febrero</option>
|
|
<option value="3">Marzo</option>
|
|
<option value="4">Abril</option>
|
|
<option value="5">Mayo</option>
|
|
<option value="6">Junio</option>
|
|
<option value="7">Julio</option>
|
|
<option value="8">Agosto</option>
|
|
<option value="9">Septiembre</option>
|
|
<option value="10">Octubre</option>
|
|
<option value="11">Noviembre</option>
|
|
<option value="12">Diciembre</option>
|
|
</select>
|
|
</div>
|
|
<div className="col text-end">
|
|
<span className="fw-bold">Agno</span>
|
|
</div>
|
|
<div className="col">
|
|
<input
|
|
type="number"
|
|
className="form-control form-control-sm"
|
|
value={agno}
|
|
onChange={(e) => setAgno(e.target.value)}
|
|
placeholder="AAAA"
|
|
/>
|
|
</div>
|
|
<div className="col">
|
|
<button
|
|
type="submit"
|
|
className="btn btn-primary btn-sm d-grid w-100"
|
|
>
|
|
Buscar
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
{/* BOTÓN EXPORTAR EXCEL */}
|
|
<div className="col-md-12 mx-auto">
|
|
<button
|
|
type="button"
|
|
className="btn p-0 text-start w-100 text-decoration-none border-0 bg-transparent"
|
|
onClick={handleExportarExcel}
|
|
>
|
|
<div className="alert alert-success shadow" role="alert">
|
|
<div className="row align-items-center">
|
|
<div className="col-md-3">
|
|
<h1 className="mb-0">
|
|
{/* <Export /> */}
|
|
</h1>
|
|
</div>
|
|
<div className="col text-center">
|
|
<span className="text-muted">
|
|
<strong>Excel</strong>
|
|
</span>
|
|
<h5 className="mb-0">
|
|
<strong>Exportar Mensual</strong>
|
|
</h5>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</button>
|
|
</div>
|
|
|
|
{/* TABLA DE RESULTADOS */}
|
|
{showSearch && (
|
|
<div className="col-md-12 mt-3" id="div_search">
|
|
<div className="alert alert-light text-dark shadow-sm" role="alert">
|
|
<div className="table-responsive">
|
|
<table
|
|
className="table table-sm mb-0"
|
|
style={{ fontSize: "12px" }}
|
|
>
|
|
<thead>
|
|
<tr>
|
|
<th className="text-start" style={{ width: "3%" }}></th>
|
|
<th className="text-start" style={{ width: "7%" }}>
|
|
RUT Empresa
|
|
</th>
|
|
<th className="text-start" style={{ width: "20%" }}>
|
|
Empresa
|
|
</th>
|
|
<th className="text-start" style={{ width: "7%" }}>
|
|
Contrato
|
|
</th>
|
|
<th className="text-start" style={{ width: "12%" }}>
|
|
Fecha
|
|
</th>
|
|
<th className="text-start" style={{ width: "40%" }}>
|
|
Observacion
|
|
</th>
|
|
<th className="text-start" style={{ width: "11%" }}>
|
|
Responsable
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{contratosAnulados.map((item, index) => (
|
|
<tr key={index}>
|
|
<td className="text-start" style={{ width: "3%" }}></td>
|
|
<td className="text-start" style={{ width: "7%" }}>
|
|
{item.idEmpresas}
|
|
</td>
|
|
<td className="text-start" style={{ width: "20%" }}>
|
|
{item.empresa}
|
|
</td>
|
|
<td className="text-start" style={{ width: "7%" }}>
|
|
{item.Contrato}
|
|
</td>
|
|
<td className="text-start" style={{ width: "12%" }}>
|
|
{item.Fecha}
|
|
</td>
|
|
<td className="text-start" style={{ width: "40%" }}>
|
|
{item.DescripcionActividad}
|
|
</td>
|
|
<td className="text-start" style={{ width: "11%" }}>
|
|
{item.usuarioResponsable}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
|
|
{mensajeLabel && (
|
|
<h4 className="mt-2">
|
|
<span>{mensajeLabel}</span>
|
|
</h4>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* --- MODAL ALERTA / ERRORES --- */}
|
|
{modalAlerta.show && (
|
|
<div
|
|
className="modal fade show d-block bg-dark bg-opacity-50"
|
|
tabIndex={-1}
|
|
role="dialog"
|
|
>
|
|
<div className="modal-dialog modal-dialog-centered modal-dialog-scrollable">
|
|
<div className="modal-content text-dark">
|
|
<div className="modal-header alert alert-secondary mb-0">
|
|
<h5 className="float-start mt-2">
|
|
<strong>Módulo Académico - Empresa</strong>
|
|
</h5>
|
|
<button
|
|
type="button"
|
|
className="btn-close float-end mt-1"
|
|
onClick={() => setModalAlerta({ show: false, message: "" })}
|
|
aria-label="Close"
|
|
></button>
|
|
</div>
|
|
<div
|
|
className="modal-body alert mb-0"
|
|
style={{ textAlign: "justify" }}
|
|
>
|
|
<div className="row mb-2">
|
|
<div className="col-md-12 text-center">
|
|
<h5>
|
|
<strong>{modalAlerta.message}</strong>
|
|
</h5>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="modal-footer">
|
|
<button
|
|
type="button"
|
|
className="btn btn-primary"
|
|
onClick={() => setModalAlerta({ show: false, message: "" })}
|
|
>
|
|
Cerrar
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* --- MODAL CARGANDO (LOADING SPINNER) --- */}
|
|
{isLoading && (
|
|
<div
|
|
className="modal fade show d-block bg-dark bg-opacity-50"
|
|
tabIndex={-1}
|
|
role="dialog"
|
|
>
|
|
<div className="modal-dialog modal-dialog-centered modal-dialog-scrollable">
|
|
<div className="modal-content text-dark">
|
|
<div className="modal-header alert alert-secondary mb-0">
|
|
<h5 className="float-start mt-2">
|
|
<strong>Procesando</strong>
|
|
</h5>
|
|
</div>
|
|
<div
|
|
className="modal-body alert mb-0"
|
|
style={{ textAlign: "justify" }}
|
|
>
|
|
<div className="row mb-2">
|
|
<div className="col-md-12 text-center">
|
|
<h5>
|
|
<strong>Espere un momento, por favor...</strong>
|
|
</h5>
|
|
<div className="d-flex justify-content-center gap-1 mt-3">
|
|
<div
|
|
className="spinner-grow spinner-grow-sm text-primary"
|
|
role="status"
|
|
></div>
|
|
<div
|
|
className="spinner-grow spinner-grow-sm text-danger"
|
|
role="status"
|
|
></div>
|
|
<div
|
|
className="spinner-grow spinner-grow-sm text-primary"
|
|
role="status"
|
|
></div>
|
|
<div
|
|
className="spinner-grow spinner-grow-sm text-danger"
|
|
role="status"
|
|
></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
} |