Se agregaron modificaciones en los componentes

This commit is contained in:
santiago
2025-11-02 23:15:53 -06:00
parent 7919324bf1
commit 5e603aaf7c
13 changed files with 355 additions and 179 deletions
@@ -6,7 +6,7 @@ export default function CasosEspeciales() {
<section className="container px-2 pb-5">
<BotonRegresar />
<TablaCasosEspeciales />
<TablaCasosEspeciales />
</section>
);
}
+1 -1
View File
@@ -19,7 +19,7 @@ export default function Administrador() {
return (
<section className="container px-2 pb-5">
<div className="pb-4 pt-3">
<div className="pb-4 pt-3 mt-4">
<Link href="/administrador/responsables" className="me-3">
<button >Ver responsable</button>
</Link>
+3 -3
View File
@@ -12,9 +12,9 @@ export default function Registro() {
//};
return (
<main>
<section className="container px-2 pb-5">
<BotonRegresar />
<h3 className="title">Reportes</h3>
<h2 className="title mb-4">Reportes</h2>
<Cuestionario
years={years}
admin={admin}
@@ -24,6 +24,6 @@ export default function Registro() {
<Reporte admin={admin}/>
<GustavoBazPrada />
</main>
</section>
);
}
+2 -2
View File
@@ -56,14 +56,14 @@ export default function Page() {
return (
<section className="container px-2 pb-6">
<div className="pt-6 pb-5">
<p className="is-size-4 block">Estimado(a) responsable del programa de servicio social:</p>
<p className="is-size-4 block mt-5">Estimado(a) responsable del programa de servicio social:</p>
<p className="block">
Para registrar a un alumno de servicio social pulsa el botón <strong> Agregar Alumno </strong> y sigue las instrucciones.
</p>
</div>
<div className="pb-5">
<div className="pb-3">
<button className="button is-info" onClick={() => router.push('/responsable/nuevo')}>Agregar alumno</button>
</div>
@@ -80,9 +80,9 @@ export default function Cuestionario({ years, admin, updateIsLoading }: Props) {
</FormGroup>
</Col>
<div>
<div className="d-flex gap-2 mt-4 mb-3">
<Button
variant="info"
disabled={!selectedYear || !selectedCuestionario || !version}
onClick={downloadExcel}
>
Enviar archivo
@@ -46,37 +46,37 @@ export default function GustavoBazPrada({
};
return (
<div className="mb-2">
<h4 className="mb-2">Gustavo Baz Prada</h4>
<div className="mt-4 mb-2">
<h4 className="mb-3">Gustavo Baz Prada</h4>
<Col>
<FormGroup>
<FormLabel>Selecciona un año:</FormLabel>
<InputGroup>
<FormSelect
value={selectedYear}
onChange={(e) => setSelectedYear(e.target.value)}
>
<option value="">Seleccione un año:</option>
{(years || []).map((y, i) => (
<option key={i} value={y}>
{y}
</option>
))}
</FormSelect>
</InputGroup>
</FormGroup>
</Col>
<Col>
<FormGroup>
<FormLabel>Selecciona un año:</FormLabel>
<InputGroup>
<FormSelect
value={selectedYear}
onChange={(e) => setSelectedYear(e.target.value)}
>
<option value="">Seleccione un año:</option>
{(years || []).map((y, i) => (
<option key={i} value={y}>
{y}
</option>
))}
</FormSelect>
</InputGroup>
</FormGroup>
</Col>
<Col className="pt-3 pb-5">
<Button
variant="info"
onClick={descargar}
disabled={!selectedYear}
>
Descargar Excel
</Button>
</Col>
<Col className="pt-3 pb-5">
<Button
className="mt-2"
onClick={descargar}
disabled={!selectedYear}
>
Descargar Excel
</Button>
</Col>
</div>
);
}
+49 -36
View File
@@ -1,9 +1,10 @@
"use client";
import { useState } from "react";
import { axiosInstance } from "@/api/config";
import { Button, Col, FormGroup, FormLabel, InputGroup } from "react-bootstrap";
import { Button, Col, FormGroup, FormLabel, InputGroup, Row } from "react-bootstrap";
import DatePicker from "react-datepicker";
import moment from "moment";
import { FaCalendarAlt, FaRegCalendarAlt } from "react-icons/fa";
//import { saveAs } from "file-saver";
interface Props {
@@ -55,42 +56,54 @@ export default function Reporte({ admin }: Props) {
};
return (
<div>
<h4 className="mb-2">Descargar reporte</h4>
<Col>
<FormGroup>
<FormLabel>Seleccione una fecha de inicio:</FormLabel>
<InputGroup>
<DatePicker
selected={selectedInicio}
onChange={(date) => setSelectedInicio(date)}
minDate={minDate}
maxDate={maxDate}
placeholderText="Seleccione una fecha de inicio"
dateFormat="dd-MM-yyyy"
className="form-control"
/>
</InputGroup>
</FormGroup>
</Col>
<div className="columns">
<h4 className="mb-3">Descargar reporte</h4>
<Col>
<FormGroup>
<FormLabel>Seleccione una fecha de fin:</FormLabel>
<InputGroup>
<DatePicker
selected={selectedFin}
onChange={(date) => setSelectedFin(date)}
minDate={minDate}
maxDate={maxDate}
placeholderText="Seleccione una fecha de fin"
dateFormat="yyyy-MM-dd"
className="form-control"
/>
</InputGroup>
<section>
<Col>
<FormGroup className="mb-3">
<FormLabel>Seleccione una fecha de inicio:</FormLabel>
<InputGroup>
<InputGroup.Text>
<FaRegCalendarAlt />
</InputGroup.Text>
<DatePicker
selected={selectedInicio}
onChange={(date) => setSelectedInicio(date)}
minDate={minDate}
maxDate={maxDate}
placeholderText="Seleccione una fecha de inicio"
dateFormat="dd-MM-yyyy"
className="form-control"
wrapperClassName="flex-grow-1"
calendarClassName="mi-calendario"
/>
</InputGroup>
</FormGroup>
</Col>
<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>
</section>
{/*
<label htmlFor="fecha" className="text-sm font-medium">
Selecciona una fecha de inicio
@@ -142,8 +155,8 @@ export default function Reporte({ admin }: Props) {
</FormGroup>
</Col>
*/}
<div>
<Button variant="info" onClick={descargar} disabled={validarFechas()}>
<div className="mt-4 mb-3">
<Button onClick={descargar} disabled={validarFechas()}>
Descargar Excel
</Button>
</div>
@@ -4,9 +4,10 @@ import { useEffect, useState } from "react";
import { axiosInstance } from '@/api/config';
import { isAxiosError } from 'axios';
import type { AxiosResponse } from 'axios';
import { Button, Form, Spinner } from "react-bootstrap";
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";
interface Admin {
idTipoUsuario?: number;
@@ -138,84 +139,121 @@ export default function TablaCasosEspeciales({ admin, imprimirError }: Props) {
}, [admin]);
return (
<div>
<h3 className="title">Casos Especiales</h3>
<section>
<div className="columns">
<h3 className="title">Casos Especiales</h3>
<div className="row mb-3">
{/* Número de Cuenta */}
<div className="col">
<Form.Label>Número de Cuenta</Form.Label>
<Form.Control
type="text"
placeholder="No. Cuenta"
maxLength={9}
value={search.numeroCuenta}
onChange={(e) =>
setSearch({ ...search, numeroCuenta: e.target.value })
}
onKeyDown={(e) => e.key === "Enter" && obtenerCasosEspeciales()}
/>
</div>
<section className="container-fluid my-4">
<Form onSubmit={(e) => { e.preventDefault();
obtenerCatalogoStatus();
}}>
<Row className="g-3">
<Col md={3}>
<FormGroup>
<FormLabel>Número de Cuenta</FormLabel>
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaSchool />
</InputGroup.Text>
{/* Nombre */}
<div className="col">
<Form.Label>Nombre</Form.Label>
<Form.Control
type="text"
placeholder="Nombre"
value={search.nombre}
onChange={(e) => setSearch({ ...search, nombre: e.target.value })}
onKeyDown={(e) => e.key === "Enter" && obtenerCasosEspeciales()}
/>
</div>
<Form.Control
type="text"
className="rounded-4"
placeholder="No. Cuenta"
maxLength={9}
value={search.numeroCuenta}
onChange={(e) =>
setSearch({ ...search, numeroCuenta: e.target.value })
}
onKeyDown={(e) => e.key === "Enter" && obtenerCasosEspeciales()}
/>
</InputGroup>
</FormGroup>
</Col>
{/* Status */}
<div className="col">
<Form.Label>Status</Form.Label>
<Form.Select
value={search.idStatus}
onChange={(e) =>
setSearch({ ...search, idStatus: e.target.value })
}
>
<option value="">Status</option>
<option value="articulo 52">Articulo 52</option>
<option value="articulo 91">Articulo 91</option>
<option value="liberaciòn articulo 52">Liberaciòn Articulo 52</option>
<option value="liberaciòn articulo 91">Liberaciòn Articulo 91</option>
{status.map((s) => (
<option key={s.idStatus} value={s.idStatus}>
{s.status}
</option>
))}
</Form.Select>
</div>
<Col md={3}>
<FormGroup>
<FormLabel>Nombre</FormLabel>
{/* Botón Buscar */}
<div className="col d-flex align-items-end">
<Button
variant="info"
onClick={() => obtenerCasosEspeciales()}
className="w-100"
>
{isLoading ? <Spinner size="sm" animation="border" /> : "Buscar"}
</Button>
</div>
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaUser />
</InputGroup.Text>
<Form.Control
type="text"
className="rounded-4"
placeholder="Nombre"
value={search.nombre}
onChange={(e) => setSearch({ ...search, nombre: e.target.value })}
onKeyDown={(e) => e.key === "Enter" && obtenerCasosEspeciales()}
/>
</InputGroup>
</FormGroup>
</Col>
<Col md={3}>
<FormGroup>
<FormLabel>Status</FormLabel>
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaInfoCircle />
</InputGroup.Text>
<FormSelect
value={search.idStatus}
onChange={(e) => setSearch(prev => ({ ...prev, idStatus: e.target.value }))}
className="rounded-4"
>
<option value="">Status</option>
{status.map((s) => (
<option key={s.idStatus} value={s.idStatus}>{s.status}</option>
))}
</FormSelect>
</InputGroup>
</FormGroup>
</Col>
<Col md={3} className="d-flex align-items-end">
<Button type="submit" className="w-100 rounded-5" disabled={isLoading}>
{isLoading ? <Spinner size="sm" animation="border" /> : "Buscar"}
</Button>
</Col>
</Row>
</Form>
</section>
</div>
{/* Tabla
{/* Botón Buscar
<div className="col d-flex align-items-end">
<Button
variant="info"
onClick={() => obtenerCasosEspeciales()}
className="w-100"
>
{isLoading ? <Spinner size="sm" animation="border" /> : "Buscar"}
</Button>
</div>
*/}
{/* Tabla
<ServicioSocialTabla />
*/}
<ServicioSocialTabla
data={data}
total={total}
columnasResponsable={false}
onPageChange={onPageChange}
columnaFechaInicio={false}
columnaFechaFin={false}
/>
<ServicioSocialTabla />
*/}
<ServicioSocialTabla
data={data}
total={total}
onPageChange={onPageChange}
columnaFechaInicio={false}
columnaFechaFin={false}
/>
</div>
</section>
);
}
@@ -99,7 +99,7 @@ export default function TablaServicioSocial({ admin, imprimirError }: Props) {
return (
<section>
<div className="columns">
<h3 className="title">Servicios Sociales</h3>
<h2 className="title">Servicios Sociales</h2>
<section className="container-fluid my-4">
<Form onSubmit={(e) => { e.preventDefault(); obtenerServicios(1); }}>
@@ -178,6 +178,8 @@ export default function TablaServicioSocial({ admin, imprimirError }: Props) {
<ServicioSocialTabla
data={data}
total={total}
columnaFechaFin={true}
columnaFechaInicio={true}
onPageChange={onPageChange}
idTipoUsuario={admin?.idTipoUsuario}
columnasResponsable={admin?.idTipoUsuario === 1}
@@ -5,6 +5,9 @@ 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";
type Responsable = { idUsuario?: number; token?: string; idTipoUsuario?: number };
type StatusItem = { idStatus: number; status: string };
@@ -84,29 +87,81 @@ export default function TablaServiciosResponsable({ responsable, imprimirError }
return (
<div>
<div className="row mb-3">
<div className="col">
<label className="form-label">Número de Cuenta</label>
<input type="text" className="form-control" placeholder="No.Cuenta" maxLength={9} value={search.numeroCuenta || ''} onChange={(e) => setSearch(s => ({ ...s, numeroCuenta: e.target.value }))} onKeyDown={(e) => e.key === 'Enter' && obtenerServicios()} />
</div>
<div className="col">
<label className="form-label">Nombre</label>
<input type="text" className="form-control" placeholder="Nombre" value={search.nombre || ''} onChange={(e) => setSearch(s => ({ ...s, nombre: e.target.value }))} onKeyDown={(e) => e.key === 'Enter' && obtenerServicios()} />
</div>
<section className="container-fluid my-4">
<Row className="g-3">
<Col md={3}>
<FormGroup>
<FormLabel>Número de cuenta</FormLabel>
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaSchool />
</InputGroup.Text>
<FormControl
type="text"
placeholder="No.Cuenta"
maxLength={9}
value={search.numeroCuenta}
onChange={(e) => setSearch(Prev => ({ ...Prev, numeroCuenta: e.target.value }))}
onKeyDown={(e) => { if (e.key === 'Enter') obtenerServicios(1); }}
className="rounded-4"
/>
</InputGroup>
</FormGroup>
</Col>
<div className="col">
<label className="form-label">Status</label>
<select className="form-select" value={search.idStatus || ''} onChange={(e) => setSearch(s => ({ ...s, idStatus: e.target.value }))}>
<option value="">Status</option>
{status.map((s, i) => i < 9 ? <option key={s.idStatus} value={String(s.idStatus)}>{s.status}</option> : null)}
</select>
</div>
<Col md={3}>
<FormGroup>
<FormLabel>Nombre</FormLabel>
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaUser />
</InputGroup.Text>
<FormControl
type="text"
placeholder="Nombre"
value={search.nombre}
onChange={(e) => setSearch(Prev => ({ ...Prev, nombre: e.target.value}))}
onKeyDown={(e) => { if (e.key === 'Enter')
obtenerServicios(1);}}
className="rounded-4"
/>
</InputGroup>
</FormGroup>
</Col>
<div className="col align-self-center">
<button className="btn btn-info w-100" onClick={() => obtenerServicios()}>Buscar</button>
</div>
</div>
<Col md={3}>
<FormGroup>
<FormLabel>Status</FormLabel>
<InputGroup>
<InputGroup.Text className="rounded-4">
<FaInfoCircle />
</InputGroup.Text>
<FormSelect
value={search.idStatus}
onChange={(e) => setSearch(Prev => ({ ...Prev, idStatus: e.target.value}))}
className="rounded-4"
>
<option value="">Status</option>
{status.map((s) => (
<option key={s.idStatus} value={s.idStatus}>
{s.status}
</option>
))}
</FormSelect>
</InputGroup>
</FormGroup>
</Col>
<Col md={3} className="d-flex align-items-end">
<Button
type="button"
className="w-100 rounded-5"
disabled={isLoading}>{isLoading ? 'Buscando...' : 'Buscar'}
</Button>
</Col>
</Row>
</section>
{/*
columnaFechaCreado={false}
@@ -116,13 +171,14 @@ export default function TablaServiciosResponsable({ responsable, imprimirError }
*/}
<ServicioSocialTabla
idTipoUsuario={responsable.idTipoUsuario ?? 0}
total={total}
data={data}
onPageChange={onPageChange}
columnaFechaInicio={true}
columnaFechaFin={true}
columnasResponsable={true}
columnaCuestionario={true}
/>
</div>
);
+44 -11
View File
@@ -14,6 +14,7 @@ interface Props {
idTipoUsuario?: number; // 1=admin, 2=responsable, 3=alumno, 4=caso especial
columnasResponsable?: boolean;
onRowAction?: (row: ServicioSocialResponse, path: string) => void;
columnaCuestionario?: boolean;
}
export default function ServicioSocialTabla({
@@ -22,6 +23,9 @@ export default function ServicioSocialTabla({
onPageChange,
idTipoUsuario,
columnasResponsable = false,
columnaFechaInicio = false,
columnaFechaFin = false,
columnaCuestionario = false,
onRowAction,
}: Props) {
const [info, setInfo] = useState<ServicioSocialResponse[]>(data || []);
@@ -30,8 +34,8 @@ export default function ServicioSocialTabla({
const router = useRouter();
const columnaFechaInicio = true;
const columnaFechaFin = true;
//const columnaFechaInicio = true;
//const columnaFechaFin = true;
// 🔹 Cargar datos del padre si existen
useEffect(() => {
@@ -128,9 +132,6 @@ const servicioSelected = (row: ServicioSocialResponse | ServicioSocialConCasoEsp
*/
return (
<section>
{loading ? (
@@ -141,13 +142,14 @@ const servicioSelected = (row: ServicioSocialResponse | ServicioSocialConCasoEsp
<table className="table table-striped">
<thead>
<tr>
<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>
<th>Carta Termino</th>
<th>Cuestionario</th>
</tr>
</thead>
<tbody>
@@ -164,7 +166,6 @@ const servicioSelected = (row: ServicioSocialResponse | ServicioSocialConCasoEsp
//idTipoUsuario === 1 ? servicioSelected(item) : undefined
//}
>
<td>{formatDate(item.fecha_registro)}</td>
<td>{item.Usuario?.usuario}</td>
<td>{item.Usuario?.nombre}</td>
<td>{item.Carrera?.carrera}</td>
@@ -192,6 +193,28 @@ const servicioSelected = (row: ServicioSocialResponse | ServicioSocialConCasoEsp
</span>
)}
</td>
<td>{item.cartaTermino ? (
<span className="badge bg-success">Completado</span>
) : (
<span className="badge bg-danger">No Completado</span>
)}</td>
<td>{item.cuestionarioCompletado ? (
<span className="badge bg-success">Completado</span>
) : (
<span className="badge bg-danger">No Completado</span>
)}</td>
{columnaCuestionario && (
<td>
{item.cuestionarioCompletado ? (
<span className="badge bg-success">Completado</span>
) : (
<span className="badge bg-danger">No Completado</span>
)}
</td>
)}
</tr>
))}
{info.length === 0 && (
@@ -233,17 +256,27 @@ const servicioSelected = (row: ServicioSocialResponse | ServicioSocialConCasoEsp
);
}
// 🔧 Funciones auxiliares
function formatDate(value?: string | null) {
function formatDate(value?: string | null): string {
if (!value) return "";
const d = new Date(value);
if (isNaN(d.getTime())) return String(value);
// Si ya viene como fecha ISO, forzamos a hora local sin modificar el día
const d = new Date(value.includes("T") ? value : `${value}T00:00:00`);
if (isNaN(d.getTime())) {
console.warn("⚠️ Fecha inválida:", value);
return "";
}
const day = String(d.getDate()).padStart(2, "0");
const month = String(d.getMonth() + 1).padStart(2, "0");
const year = d.getFullYear();
return `${day}/${month}/${year}`;
}
function statusClass(id?: number) {
switch (id) {
case 1:
+33
View File
@@ -32,6 +32,39 @@ $custom-colors: (
'yellow-guapa': $yellow-guapa,
);
// Para el datapicker
.mi-calendario {
font-family: "Poppins", sans-serif;
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
border: 1px solid #dee2e6; // color tipo Bootstrap
background-color: #fff;
.react-datepicker__header {
background-color: #756e6e; // azul Bootstrap
color: white;
border-bottom: none;
}
.react-datepicker__current-month,
.react-datepicker__day-name {
color: white;
}
.react-datepicker__day--selected,
.react-datepicker__day--keyboard-selected {
background-color: #0d6efd;
color: white;
border-radius: 50%;
}
.react-datepicker__day:hover {
background-color: #e9ecef;
border-radius: 50%;
}
}
$theme-colors: map-merge($theme-colors, $custom-colors);
.form-registro {
+1
View File
@@ -14,6 +14,7 @@ export interface ServicioSocialResponse {
cartaTermino?: boolean;
idCuestionarioPrograma?: number | null;
idCuestionarioPrograma2?: number | null;
cuestionarioCompletado?: boolean;
}
export interface Usuario {